mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-21 16:01:05 +00:00
Merge remote-tracking branch 'origin/main' into feat/model-cycling-enhancements
This commit is contained in:
commit
df3af27288
18 changed files with 264 additions and 173 deletions
30
package-lock.json
generated
30
package-lock.json
generated
|
|
@ -3847,11 +3847,11 @@
|
||||||
},
|
},
|
||||||
"packages/agent": {
|
"packages/agent": {
|
||||||
"name": "@mariozechner/pi-agent",
|
"name": "@mariozechner/pi-agent",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-ai": "^0.8.3",
|
"@mariozechner/pi-ai": "^0.8.4",
|
||||||
"@mariozechner/pi-tui": "^0.8.3"
|
"@mariozechner/pi-tui": "^0.8.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.3.0",
|
"@types/node": "^24.3.0",
|
||||||
|
|
@ -3881,7 +3881,7 @@
|
||||||
},
|
},
|
||||||
"packages/ai": {
|
"packages/ai": {
|
||||||
"name": "@mariozechner/pi-ai",
|
"name": "@mariozechner/pi-ai",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/sdk": "^0.61.0",
|
"@anthropic-ai/sdk": "^0.61.0",
|
||||||
|
|
@ -3922,12 +3922,12 @@
|
||||||
},
|
},
|
||||||
"packages/coding-agent": {
|
"packages/coding-agent": {
|
||||||
"name": "@mariozechner/pi-coding-agent",
|
"name": "@mariozechner/pi-coding-agent",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-agent": "^0.8.3",
|
"@mariozechner/pi-agent": "^0.8.4",
|
||||||
"@mariozechner/pi-ai": "^0.8.3",
|
"@mariozechner/pi-ai": "^0.8.4",
|
||||||
"@mariozechner/pi-tui": "^0.8.3",
|
"@mariozechner/pi-tui": "^0.8.4",
|
||||||
"chalk": "^5.5.0",
|
"chalk": "^5.5.0",
|
||||||
"diff": "^8.0.2",
|
"diff": "^8.0.2",
|
||||||
"glob": "^11.0.3"
|
"glob": "^11.0.3"
|
||||||
|
|
@ -3964,10 +3964,10 @@
|
||||||
},
|
},
|
||||||
"packages/pods": {
|
"packages/pods": {
|
||||||
"name": "@mariozechner/pi",
|
"name": "@mariozechner/pi",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-agent": "^0.8.3",
|
"@mariozechner/pi-agent": "^0.8.4",
|
||||||
"chalk": "^5.5.0"
|
"chalk": "^5.5.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -3980,7 +3980,7 @@
|
||||||
},
|
},
|
||||||
"packages/proxy": {
|
"packages/proxy": {
|
||||||
"name": "@mariozechner/pi-proxy",
|
"name": "@mariozechner/pi-proxy",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hono/node-server": "^1.14.0",
|
"@hono/node-server": "^1.14.0",
|
||||||
"hono": "^4.6.16"
|
"hono": "^4.6.16"
|
||||||
|
|
@ -3996,7 +3996,7 @@
|
||||||
},
|
},
|
||||||
"packages/tui": {
|
"packages/tui": {
|
||||||
"name": "@mariozechner/pi-tui",
|
"name": "@mariozechner/pi-tui",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/mime-types": "^2.1.4",
|
"@types/mime-types": "^2.1.4",
|
||||||
|
|
@ -4031,12 +4031,12 @@
|
||||||
},
|
},
|
||||||
"packages/web-ui": {
|
"packages/web-ui": {
|
||||||
"name": "@mariozechner/pi-web-ui",
|
"name": "@mariozechner/pi-web-ui",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lmstudio/sdk": "^1.5.0",
|
"@lmstudio/sdk": "^1.5.0",
|
||||||
"@mariozechner/pi-ai": "^0.8.3",
|
"@mariozechner/pi-ai": "^0.8.4",
|
||||||
"@mariozechner/pi-tui": "^0.8.3",
|
"@mariozechner/pi-tui": "^0.8.4",
|
||||||
"docx-preview": "^0.3.7",
|
"docx-preview": "^0.3.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"lucide": "^0.544.0",
|
"lucide": "^0.544.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mariozechner/pi-agent",
|
"name": "@mariozechner/pi-agent",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
"prepublishOnly": "npm run clean && npm run build"
|
"prepublishOnly": "npm run clean && npm run build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-ai": "^0.8.4",
|
"@mariozechner/pi-ai": "^0.8.5",
|
||||||
"@mariozechner/pi-tui": "^0.8.4"
|
"@mariozechner/pi-tui": "^0.8.5"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"ai",
|
"ai",
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,8 @@ export class Agent {
|
||||||
private messageTransformer: (messages: AppMessage[]) => Message[] | Promise<Message[]>;
|
private messageTransformer: (messages: AppMessage[]) => Message[] | Promise<Message[]>;
|
||||||
private messageQueue: Array<QueuedMessage<AppMessage>> = [];
|
private messageQueue: Array<QueuedMessage<AppMessage>> = [];
|
||||||
private queueMode: "all" | "one-at-a-time";
|
private queueMode: "all" | "one-at-a-time";
|
||||||
|
private runningPrompt?: Promise<void>;
|
||||||
|
private resolveRunningPrompt?: () => void;
|
||||||
|
|
||||||
constructor(opts: AgentOptions) {
|
constructor(opts: AgentOptions) {
|
||||||
this._state = { ...this._state, ...opts.initialState };
|
this._state = { ...this._state, ...opts.initialState };
|
||||||
|
|
@ -148,12 +150,37 @@ export class Agent {
|
||||||
this.abortController?.abort();
|
this.abortController?.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise that resolves when the current prompt completes.
|
||||||
|
* Returns immediately resolved promise if no prompt is running.
|
||||||
|
*/
|
||||||
|
waitForIdle(): Promise<void> {
|
||||||
|
return this.runningPrompt ?? Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear all messages and state. Call abort() first if a prompt is in flight.
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
this._state.messages = [];
|
||||||
|
this._state.isStreaming = false;
|
||||||
|
this._state.streamMessage = null;
|
||||||
|
this._state.pendingToolCalls = new Set<string>();
|
||||||
|
this._state.error = undefined;
|
||||||
|
this.messageQueue = [];
|
||||||
|
}
|
||||||
|
|
||||||
async prompt(input: string, attachments?: Attachment[]) {
|
async prompt(input: string, attachments?: Attachment[]) {
|
||||||
const model = this._state.model;
|
const model = this._state.model;
|
||||||
if (!model) {
|
if (!model) {
|
||||||
throw new Error("No model configured");
|
throw new Error("No model configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up running prompt tracking
|
||||||
|
this.runningPrompt = new Promise<void>((resolve) => {
|
||||||
|
this.resolveRunningPrompt = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
// Build user message with attachments
|
// Build user message with attachments
|
||||||
const content: Array<TextContent | ImageContent> = [{ type: "text", text: input }];
|
const content: Array<TextContent | ImageContent> = [{ type: "text", text: input }];
|
||||||
if (attachments?.length) {
|
if (attachments?.length) {
|
||||||
|
|
@ -322,6 +349,9 @@ export class Agent {
|
||||||
this._state.streamMessage = null;
|
this._state.streamMessage = null;
|
||||||
this._state.pendingToolCalls = new Set<string>();
|
this._state.pendingToolCalls = new Set<string>();
|
||||||
this.abortController = undefined;
|
this.abortController = undefined;
|
||||||
|
this.resolveRunningPrompt?.();
|
||||||
|
this.runningPrompt = undefined;
|
||||||
|
this.resolveRunningPrompt = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mariozechner/pi-ai",
|
"name": "@mariozechner/pi-ai",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
|
|
|
||||||
|
|
@ -5102,23 +5102,6 @@ export const MODELS = {
|
||||||
contextWindow: 131072,
|
contextWindow: 131072,
|
||||||
maxTokens: 16384,
|
maxTokens: 16384,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
"meta-llama/llama-3.1-405b-instruct": {
|
|
||||||
id: "meta-llama/llama-3.1-405b-instruct",
|
|
||||||
name: "Meta: Llama 3.1 405B Instruct",
|
|
||||||
api: "openai-completions",
|
|
||||||
provider: "openrouter",
|
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
|
||||||
reasoning: false,
|
|
||||||
input: ["text"],
|
|
||||||
cost: {
|
|
||||||
input: 3.5,
|
|
||||||
output: 3.5,
|
|
||||||
cacheRead: 0,
|
|
||||||
cacheWrite: 0,
|
|
||||||
},
|
|
||||||
contextWindow: 130815,
|
|
||||||
maxTokens: 4096,
|
|
||||||
} satisfies Model<"openai-completions">,
|
|
||||||
"meta-llama/llama-3.1-70b-instruct": {
|
"meta-llama/llama-3.1-70b-instruct": {
|
||||||
id: "meta-llama/llama-3.1-70b-instruct",
|
id: "meta-llama/llama-3.1-70b-instruct",
|
||||||
name: "Meta: Llama 3.1 70B Instruct",
|
name: "Meta: Llama 3.1 70B Instruct",
|
||||||
|
|
@ -5136,6 +5119,23 @@ export const MODELS = {
|
||||||
contextWindow: 131072,
|
contextWindow: 131072,
|
||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
|
"meta-llama/llama-3.1-405b-instruct": {
|
||||||
|
id: "meta-llama/llama-3.1-405b-instruct",
|
||||||
|
name: "Meta: Llama 3.1 405B Instruct",
|
||||||
|
api: "openai-completions",
|
||||||
|
provider: "openrouter",
|
||||||
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: {
|
||||||
|
input: 3.5,
|
||||||
|
output: 3.5,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
},
|
||||||
|
contextWindow: 130815,
|
||||||
|
maxTokens: 4096,
|
||||||
|
} satisfies Model<"openai-completions">,
|
||||||
"mistralai/mistral-nemo": {
|
"mistralai/mistral-nemo": {
|
||||||
id: "mistralai/mistral-nemo",
|
id: "mistralai/mistral-nemo",
|
||||||
name: "Mistral: Mistral Nemo",
|
name: "Mistral: Mistral Nemo",
|
||||||
|
|
@ -5153,9 +5153,9 @@ export const MODELS = {
|
||||||
contextWindow: 131072,
|
contextWindow: 131072,
|
||||||
maxTokens: 16384,
|
maxTokens: 16384,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
"openai/gpt-4o-mini-2024-07-18": {
|
"openai/gpt-4o-mini": {
|
||||||
id: "openai/gpt-4o-mini-2024-07-18",
|
id: "openai/gpt-4o-mini",
|
||||||
name: "OpenAI: GPT-4o-mini (2024-07-18)",
|
name: "OpenAI: GPT-4o-mini",
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
provider: "openrouter",
|
provider: "openrouter",
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
|
|
@ -5170,9 +5170,9 @@ export const MODELS = {
|
||||||
contextWindow: 128000,
|
contextWindow: 128000,
|
||||||
maxTokens: 16384,
|
maxTokens: 16384,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
"openai/gpt-4o-mini": {
|
"openai/gpt-4o-mini-2024-07-18": {
|
||||||
id: "openai/gpt-4o-mini",
|
id: "openai/gpt-4o-mini-2024-07-18",
|
||||||
name: "OpenAI: GPT-4o-mini",
|
name: "OpenAI: GPT-4o-mini (2024-07-18)",
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
provider: "openrouter",
|
provider: "openrouter",
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
|
|
@ -5272,23 +5272,6 @@ export const MODELS = {
|
||||||
contextWindow: 128000,
|
contextWindow: 128000,
|
||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
"openai/gpt-4o-2024-05-13": {
|
|
||||||
id: "openai/gpt-4o-2024-05-13",
|
|
||||||
name: "OpenAI: GPT-4o (2024-05-13)",
|
|
||||||
api: "openai-completions",
|
|
||||||
provider: "openrouter",
|
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
|
||||||
reasoning: false,
|
|
||||||
input: ["text", "image"],
|
|
||||||
cost: {
|
|
||||||
input: 5,
|
|
||||||
output: 15,
|
|
||||||
cacheRead: 0,
|
|
||||||
cacheWrite: 0,
|
|
||||||
},
|
|
||||||
contextWindow: 128000,
|
|
||||||
maxTokens: 4096,
|
|
||||||
} satisfies Model<"openai-completions">,
|
|
||||||
"openai/gpt-4o": {
|
"openai/gpt-4o": {
|
||||||
id: "openai/gpt-4o",
|
id: "openai/gpt-4o",
|
||||||
name: "OpenAI: GPT-4o",
|
name: "OpenAI: GPT-4o",
|
||||||
|
|
@ -5323,6 +5306,23 @@ export const MODELS = {
|
||||||
contextWindow: 128000,
|
contextWindow: 128000,
|
||||||
maxTokens: 64000,
|
maxTokens: 64000,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
|
"openai/gpt-4o-2024-05-13": {
|
||||||
|
id: "openai/gpt-4o-2024-05-13",
|
||||||
|
name: "OpenAI: GPT-4o (2024-05-13)",
|
||||||
|
api: "openai-completions",
|
||||||
|
provider: "openrouter",
|
||||||
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text", "image"],
|
||||||
|
cost: {
|
||||||
|
input: 5,
|
||||||
|
output: 15,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
},
|
||||||
|
contextWindow: 128000,
|
||||||
|
maxTokens: 4096,
|
||||||
|
} satisfies Model<"openai-completions">,
|
||||||
"meta-llama/llama-3-70b-instruct": {
|
"meta-llama/llama-3-70b-instruct": {
|
||||||
id: "meta-llama/llama-3-70b-instruct",
|
id: "meta-llama/llama-3-70b-instruct",
|
||||||
name: "Meta: Llama 3 70B Instruct",
|
name: "Meta: Llama 3 70B Instruct",
|
||||||
|
|
@ -5442,23 +5442,6 @@ export const MODELS = {
|
||||||
contextWindow: 128000,
|
contextWindow: 128000,
|
||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
"openai/gpt-3.5-turbo-0613": {
|
|
||||||
id: "openai/gpt-3.5-turbo-0613",
|
|
||||||
name: "OpenAI: GPT-3.5 Turbo (older v0613)",
|
|
||||||
api: "openai-completions",
|
|
||||||
provider: "openrouter",
|
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
|
||||||
reasoning: false,
|
|
||||||
input: ["text"],
|
|
||||||
cost: {
|
|
||||||
input: 1,
|
|
||||||
output: 2,
|
|
||||||
cacheRead: 0,
|
|
||||||
cacheWrite: 0,
|
|
||||||
},
|
|
||||||
contextWindow: 4095,
|
|
||||||
maxTokens: 4096,
|
|
||||||
} satisfies Model<"openai-completions">,
|
|
||||||
"openai/gpt-4-turbo-preview": {
|
"openai/gpt-4-turbo-preview": {
|
||||||
id: "openai/gpt-4-turbo-preview",
|
id: "openai/gpt-4-turbo-preview",
|
||||||
name: "OpenAI: GPT-4 Turbo Preview",
|
name: "OpenAI: GPT-4 Turbo Preview",
|
||||||
|
|
@ -5476,6 +5459,23 @@ export const MODELS = {
|
||||||
contextWindow: 128000,
|
contextWindow: 128000,
|
||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
|
"openai/gpt-3.5-turbo-0613": {
|
||||||
|
id: "openai/gpt-3.5-turbo-0613",
|
||||||
|
name: "OpenAI: GPT-3.5 Turbo (older v0613)",
|
||||||
|
api: "openai-completions",
|
||||||
|
provider: "openrouter",
|
||||||
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: {
|
||||||
|
input: 1,
|
||||||
|
output: 2,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
},
|
||||||
|
contextWindow: 4095,
|
||||||
|
maxTokens: 4096,
|
||||||
|
} satisfies Model<"openai-completions">,
|
||||||
"mistralai/mistral-small": {
|
"mistralai/mistral-small": {
|
||||||
id: "mistralai/mistral-small",
|
id: "mistralai/mistral-small",
|
||||||
name: "Mistral Small",
|
name: "Mistral Small",
|
||||||
|
|
@ -5578,23 +5578,6 @@ export const MODELS = {
|
||||||
contextWindow: 8191,
|
contextWindow: 8191,
|
||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
"openai/gpt-4": {
|
|
||||||
id: "openai/gpt-4",
|
|
||||||
name: "OpenAI: GPT-4",
|
|
||||||
api: "openai-completions",
|
|
||||||
provider: "openrouter",
|
|
||||||
baseUrl: "https://openrouter.ai/api/v1",
|
|
||||||
reasoning: false,
|
|
||||||
input: ["text"],
|
|
||||||
cost: {
|
|
||||||
input: 30,
|
|
||||||
output: 60,
|
|
||||||
cacheRead: 0,
|
|
||||||
cacheWrite: 0,
|
|
||||||
},
|
|
||||||
contextWindow: 8191,
|
|
||||||
maxTokens: 4096,
|
|
||||||
} satisfies Model<"openai-completions">,
|
|
||||||
"openai/gpt-3.5-turbo": {
|
"openai/gpt-3.5-turbo": {
|
||||||
id: "openai/gpt-3.5-turbo",
|
id: "openai/gpt-3.5-turbo",
|
||||||
name: "OpenAI: GPT-3.5 Turbo",
|
name: "OpenAI: GPT-3.5 Turbo",
|
||||||
|
|
@ -5612,6 +5595,23 @@ export const MODELS = {
|
||||||
contextWindow: 16385,
|
contextWindow: 16385,
|
||||||
maxTokens: 4096,
|
maxTokens: 4096,
|
||||||
} satisfies Model<"openai-completions">,
|
} satisfies Model<"openai-completions">,
|
||||||
|
"openai/gpt-4": {
|
||||||
|
id: "openai/gpt-4",
|
||||||
|
name: "OpenAI: GPT-4",
|
||||||
|
api: "openai-completions",
|
||||||
|
provider: "openrouter",
|
||||||
|
baseUrl: "https://openrouter.ai/api/v1",
|
||||||
|
reasoning: false,
|
||||||
|
input: ["text"],
|
||||||
|
cost: {
|
||||||
|
input: 30,
|
||||||
|
output: 60,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
},
|
||||||
|
contextWindow: 8191,
|
||||||
|
maxTokens: 4096,
|
||||||
|
} satisfies Model<"openai-completions">,
|
||||||
"openrouter/auto": {
|
"openrouter/auto": {
|
||||||
id: "openrouter/auto",
|
id: "openrouter/auto",
|
||||||
name: "OpenRouter: Auto Router",
|
name: "OpenRouter: Auto Router",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,21 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **`/clear` Command**: New slash command to reset the conversation context and start a fresh session. Aborts any in-flight agent work, clears all messages, and creates a new session file. ([#48](https://github.com/badlogic/pi-mono/pull/48))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Markdown Link Rendering**: Fixed links with identical text and href (e.g., `https://github.com/badlogic/pi-mono/pull/48/files`) being rendered twice. Now correctly compares raw text instead of styled text (which contains ANSI codes) when determining if link text matches href.
|
||||||
|
|
||||||
|
## [0.8.5] - 2025-11-21
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Path Completion Hanging**: Fixed catastrophic regex backtracking in path completion that caused the terminal to hang when text contained many `/` characters (e.g., URLs). Replaced complex regex with simple string operations. ([#18](https://github.com/badlogic/pi-mono/issues/18))
|
||||||
|
- **Autocomplete Arrow Keys**: Fixed issue where arrow keys would move both the autocomplete selection and the editor cursor simultaneously when the file selector list was shown.
|
||||||
|
|
||||||
## [0.8.4] - 2025-11-21
|
## [0.8.4] - 2025-11-21
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
|
|
@ -445,6 +445,16 @@ Logout from OAuth providers:
|
||||||
|
|
||||||
Shows a list of logged-in providers to logout from.
|
Shows a list of logged-in providers to logout from.
|
||||||
|
|
||||||
|
### /clear
|
||||||
|
|
||||||
|
Clear the conversation context and start a fresh session:
|
||||||
|
|
||||||
|
```
|
||||||
|
/clear
|
||||||
|
```
|
||||||
|
|
||||||
|
Aborts any in-flight agent work, clears all messages, and creates a new session file.
|
||||||
|
|
||||||
## Editor Features
|
## Editor Features
|
||||||
|
|
||||||
The interactive input editor includes several productivity features:
|
The interactive input editor includes several productivity features:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mariozechner/pi-coding-agent",
|
"name": "@mariozechner/pi-coding-agent",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -22,9 +22,9 @@
|
||||||
"prepublishOnly": "npm run clean && npm run build"
|
"prepublishOnly": "npm run clean && npm run build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-agent": "^0.8.4",
|
"@mariozechner/pi-agent": "^0.8.5",
|
||||||
"@mariozechner/pi-ai": "^0.8.4",
|
"@mariozechner/pi-ai": "^0.8.5",
|
||||||
"@mariozechner/pi-tui": "^0.8.4",
|
"@mariozechner/pi-tui": "^0.8.5",
|
||||||
"chalk": "^5.5.0",
|
"chalk": "^5.5.0",
|
||||||
"diff": "^8.0.2",
|
"diff": "^8.0.2",
|
||||||
"glob": "^11.0.3"
|
"glob": "^11.0.3"
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,6 @@ const __dirname = dirname(__filename);
|
||||||
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
const packageJson = JSON.parse(readFileSync(join(__dirname, "../package.json"), "utf-8"));
|
||||||
const VERSION = packageJson.version;
|
const VERSION = packageJson.version;
|
||||||
|
|
||||||
const envApiKeyMap: Record<KnownProvider, string[]> = {
|
|
||||||
google: ["GEMINI_API_KEY"],
|
|
||||||
openai: ["OPENAI_API_KEY"],
|
|
||||||
anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
|
|
||||||
xai: ["XAI_API_KEY"],
|
|
||||||
groq: ["GROQ_API_KEY"],
|
|
||||||
cerebras: ["CEREBRAS_API_KEY"],
|
|
||||||
openrouter: ["OPENROUTER_API_KEY"],
|
|
||||||
zai: ["ZAI_API_KEY"],
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultModelPerProvider: Record<KnownProvider, string> = {
|
const defaultModelPerProvider: Record<KnownProvider, string> = {
|
||||||
anthropic: "claude-sonnet-4-5",
|
anthropic: "claude-sonnet-4-5",
|
||||||
openai: "gpt-5.1-codex",
|
openai: "gpt-5.1-codex",
|
||||||
|
|
@ -478,14 +467,9 @@ async function runInteractiveMode(
|
||||||
scopedModels,
|
scopedModels,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Initialize TUI
|
// Initialize TUI (subscribes to agent events internally)
|
||||||
await renderer.init();
|
await renderer.init();
|
||||||
|
|
||||||
// Set interrupt callback
|
|
||||||
renderer.setInterruptCallback(() => {
|
|
||||||
agent.abort();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render any existing messages (from --continue mode)
|
// Render any existing messages (from --continue mode)
|
||||||
renderer.renderInitialMessages(agent.state);
|
renderer.renderInitialMessages(agent.state);
|
||||||
|
|
||||||
|
|
@ -494,12 +478,6 @@ async function runInteractiveMode(
|
||||||
renderer.showWarning(modelFallbackMessage);
|
renderer.showWarning(modelFallbackMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to agent events
|
|
||||||
agent.subscribe(async (event) => {
|
|
||||||
// Pass all events to the renderer
|
|
||||||
await renderer.handleEvent(event, agent.state);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Interactive loop
|
// Interactive loop
|
||||||
while (true) {
|
while (true) {
|
||||||
const userInput = await renderer.getUserInput();
|
const userInput = await renderer.getUserInput();
|
||||||
|
|
@ -718,11 +696,6 @@ export async function main(args: string[]) {
|
||||||
// Load previous messages if continuing or resuming
|
// Load previous messages if continuing or resuming
|
||||||
// This may update initialModel if restoring from session
|
// This may update initialModel if restoring from session
|
||||||
if (parsed.continue || parsed.resume) {
|
if (parsed.continue || parsed.resume) {
|
||||||
const messages = sessionManager.loadMessages();
|
|
||||||
if (messages.length > 0 && shouldPrintMessages) {
|
|
||||||
console.log(chalk.dim(`Loaded ${messages.length} messages from previous session`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load and restore model (overrides initialModel if found and has API key)
|
// Load and restore model (overrides initialModel if found and has API key)
|
||||||
const savedModel = sessionManager.loadModel();
|
const savedModel = sessionManager.loadModel();
|
||||||
if (savedModel) {
|
if (savedModel) {
|
||||||
|
|
@ -871,9 +844,6 @@ export async function main(args: string[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Session will be started lazily after first user+assistant message exchange
|
|
||||||
// (unless continuing/resuming, in which case it's already initialized)
|
|
||||||
|
|
||||||
// Log loaded context files (they're already in the system prompt)
|
// Log loaded context files (they're already in the system prompt)
|
||||||
if (shouldPrintMessages && !parsed.continue && !parsed.resume) {
|
if (shouldPrintMessages && !parsed.continue && !parsed.resume) {
|
||||||
const contextFiles = loadProjectContextFiles();
|
const contextFiles = loadProjectContextFiles();
|
||||||
|
|
@ -885,19 +855,6 @@ export async function main(args: string[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subscribe to agent events to save messages
|
|
||||||
agent.subscribe((event) => {
|
|
||||||
// Save messages on completion
|
|
||||||
if (event.type === "message_end") {
|
|
||||||
sessionManager.saveMessage(event.message);
|
|
||||||
|
|
||||||
// Check if we should initialize session now (after first user+assistant exchange)
|
|
||||||
if (sessionManager.shouldInitializeSession(agent.state.messages)) {
|
|
||||||
sessionManager.startSession(agent.state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Route to appropriate mode
|
// Route to appropriate mode
|
||||||
if (mode === "rpc") {
|
if (mode === "rpc") {
|
||||||
// RPC mode - headless operation
|
// RPC mode - headless operation
|
||||||
|
|
@ -930,8 +887,6 @@ export async function main(args: string[]) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Parse current and last versions
|
// Parse current and last versions
|
||||||
const currentParts = VERSION.split(".").map(Number);
|
|
||||||
const current = { major: currentParts[0] || 0, minor: currentParts[1] || 0, patch: currentParts[2] || 0 };
|
|
||||||
const changelogPath = getChangelogPath();
|
const changelogPath = getChangelogPath();
|
||||||
const entries = parseChangelog(changelogPath);
|
const entries = parseChangelog(changelogPath);
|
||||||
const newEntries = getNewEntries(entries, lastVersion);
|
const newEntries = getNewEntries(entries, lastVersion);
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,13 @@ export class SessionManager {
|
||||||
this.sessionFile = join(this.sessionDir, `${timestamp}_${this.sessionId}.jsonl`);
|
this.sessionFile = join(this.sessionDir, `${timestamp}_${this.sessionId}.jsonl`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Reset to a fresh session. Clears pending messages and starts a new session file. */
|
||||||
|
reset(): void {
|
||||||
|
this.pendingMessages = [];
|
||||||
|
this.sessionInitialized = false;
|
||||||
|
this.initNewSession();
|
||||||
|
}
|
||||||
|
|
||||||
private findMostRecentlyModifiedSession(): string | null {
|
private findMostRecentlyModifiedSession(): string | null {
|
||||||
try {
|
try {
|
||||||
const files = readdirSync(this.sessionDir)
|
const files = readdirSync(this.sessionDir)
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export class TuiRenderer {
|
||||||
private isInitialized = false;
|
private isInitialized = false;
|
||||||
private onInputCallback?: (text: string) => void;
|
private onInputCallback?: (text: string) => void;
|
||||||
private loadingAnimation: Loader | null = null;
|
private loadingAnimation: Loader | null = null;
|
||||||
private onInterruptCallback?: () => void;
|
|
||||||
private lastSigintTime = 0;
|
private lastSigintTime = 0;
|
||||||
private changelogMarkdown: string | null = null;
|
private changelogMarkdown: string | null = null;
|
||||||
private newVersion: string | null = null;
|
private newVersion: string | null = null;
|
||||||
|
|
@ -97,6 +97,9 @@ export class TuiRenderer {
|
||||||
// Tool output expansion state
|
// Tool output expansion state
|
||||||
private toolOutputExpanded = false;
|
private toolOutputExpanded = false;
|
||||||
|
|
||||||
|
// Agent subscription unsubscribe function
|
||||||
|
private unsubscribe?: () => void;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
agent: Agent,
|
agent: Agent,
|
||||||
sessionManager: SessionManager,
|
sessionManager: SessionManager,
|
||||||
|
|
@ -173,6 +176,11 @@ export class TuiRenderer {
|
||||||
description: "Select color theme (opens selector UI)",
|
description: "Select color theme (opens selector UI)",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clearCommand: SlashCommand = {
|
||||||
|
name: "clear",
|
||||||
|
description: "Clear context and start a fresh session",
|
||||||
|
};
|
||||||
|
|
||||||
// Setup autocomplete for file paths and slash commands
|
// Setup autocomplete for file paths and slash commands
|
||||||
const autocompleteProvider = new CombinedAutocompleteProvider(
|
const autocompleteProvider = new CombinedAutocompleteProvider(
|
||||||
[
|
[
|
||||||
|
|
@ -186,6 +194,7 @@ export class TuiRenderer {
|
||||||
loginCommand,
|
loginCommand,
|
||||||
logoutCommand,
|
logoutCommand,
|
||||||
queueCommand,
|
queueCommand,
|
||||||
|
clearCommand,
|
||||||
],
|
],
|
||||||
process.cwd(),
|
process.cwd(),
|
||||||
);
|
);
|
||||||
|
|
@ -268,7 +277,7 @@ export class TuiRenderer {
|
||||||
// Set up custom key handlers on the editor
|
// Set up custom key handlers on the editor
|
||||||
this.editor.onEscape = () => {
|
this.editor.onEscape = () => {
|
||||||
// Intercept Escape key when processing
|
// Intercept Escape key when processing
|
||||||
if (this.loadingAnimation && this.onInterruptCallback) {
|
if (this.loadingAnimation) {
|
||||||
// Get all queued messages
|
// Get all queued messages
|
||||||
const queuedText = this.queuedMessages.join("\n\n");
|
const queuedText = this.queuedMessages.join("\n\n");
|
||||||
|
|
||||||
|
|
@ -289,7 +298,7 @@ export class TuiRenderer {
|
||||||
this.agent.clearMessageQueue();
|
this.agent.clearMessageQueue();
|
||||||
|
|
||||||
// Abort
|
// Abort
|
||||||
this.onInterruptCallback();
|
this.agent.abort();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -386,6 +395,13 @@ export class TuiRenderer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for /clear command
|
||||||
|
if (text === "/clear") {
|
||||||
|
this.handleClearCommand();
|
||||||
|
this.editor.setText("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Normal message submission - validate model and API key first
|
// Normal message submission - validate model and API key first
|
||||||
const currentModel = this.agent.state.model;
|
const currentModel = this.agent.state.model;
|
||||||
if (!currentModel) {
|
if (!currentModel) {
|
||||||
|
|
@ -439,6 +455,9 @@ export class TuiRenderer {
|
||||||
this.ui.start();
|
this.ui.start();
|
||||||
this.isInitialized = true;
|
this.isInitialized = true;
|
||||||
|
|
||||||
|
// Subscribe to agent events for UI updates and session saving
|
||||||
|
this.subscribeToAgent();
|
||||||
|
|
||||||
// Set up theme file watcher for live reload
|
// Set up theme file watcher for live reload
|
||||||
onThemeChange(() => {
|
onThemeChange(() => {
|
||||||
this.ui.invalidate();
|
this.ui.invalidate();
|
||||||
|
|
@ -447,7 +466,24 @@ export class TuiRenderer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleEvent(event: AgentEvent, state: AgentState): Promise<void> {
|
private subscribeToAgent(): void {
|
||||||
|
this.unsubscribe = this.agent.subscribe(async (event) => {
|
||||||
|
// Handle UI updates
|
||||||
|
await this.handleEvent(event, this.agent.state);
|
||||||
|
|
||||||
|
// Save messages to session
|
||||||
|
if (event.type === "message_end") {
|
||||||
|
this.sessionManager.saveMessage(event.message);
|
||||||
|
|
||||||
|
// Check if we should initialize session now (after first user+assistant exchange)
|
||||||
|
if (this.sessionManager.shouldInitializeSession(this.agent.state.messages)) {
|
||||||
|
this.sessionManager.startSession(this.agent.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleEvent(event: AgentEvent, state: AgentState): Promise<void> {
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
await this.init();
|
await this.init();
|
||||||
}
|
}
|
||||||
|
|
@ -713,10 +749,6 @@ export class TuiRenderer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterruptCallback(callback: () => void): void {
|
|
||||||
this.onInterruptCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleCtrlC(): void {
|
private handleCtrlC(): void {
|
||||||
// Handle Ctrl+C double-press logic
|
// Handle Ctrl+C double-press logic
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
@ -1435,6 +1467,45 @@ export class TuiRenderer {
|
||||||
this.ui.requestRender();
|
this.ui.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async handleClearCommand(): Promise<void> {
|
||||||
|
// Unsubscribe first to prevent processing abort events
|
||||||
|
this.unsubscribe?.();
|
||||||
|
|
||||||
|
// Abort and wait for completion
|
||||||
|
this.agent.abort();
|
||||||
|
await this.agent.waitForIdle();
|
||||||
|
|
||||||
|
// Stop loading animation
|
||||||
|
if (this.loadingAnimation) {
|
||||||
|
this.loadingAnimation.stop();
|
||||||
|
this.loadingAnimation = null;
|
||||||
|
}
|
||||||
|
this.statusContainer.clear();
|
||||||
|
|
||||||
|
// Reset agent and session
|
||||||
|
this.agent.reset();
|
||||||
|
this.sessionManager.reset();
|
||||||
|
|
||||||
|
// Resubscribe to agent
|
||||||
|
this.subscribeToAgent();
|
||||||
|
|
||||||
|
// Clear UI state
|
||||||
|
this.chatContainer.clear();
|
||||||
|
this.pendingMessagesContainer.clear();
|
||||||
|
this.queuedMessages = [];
|
||||||
|
this.streamingComponent = null;
|
||||||
|
this.pendingTools.clear();
|
||||||
|
this.isFirstUserMessage = true;
|
||||||
|
|
||||||
|
// Show confirmation
|
||||||
|
this.chatContainer.addChild(new Spacer(1));
|
||||||
|
this.chatContainer.addChild(
|
||||||
|
new Text(theme.fg("accent", "✓ Context cleared") + "\n" + theme.fg("muted", "Started fresh session"), 1, 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.ui.requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
private updatePendingMessagesDisplay(): void {
|
private updatePendingMessagesDisplay(): void {
|
||||||
this.pendingMessagesContainer.clear();
|
this.pendingMessagesContainer.clear();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mariozechner/pi",
|
"name": "@mariozechner/pi",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"description": "CLI tool for managing vLLM deployments on GPU pods",
|
"description": "CLI tool for managing vLLM deployments on GPU pods",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
"node": ">=20.0.0"
|
"node": ">=20.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mariozechner/pi-agent": "^0.8.4",
|
"@mariozechner/pi-agent": "^0.8.5",
|
||||||
"chalk": "^5.5.0"
|
"chalk": "^5.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mariozechner/pi-proxy",
|
"name": "@mariozechner/pi-proxy",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "CORS and authentication proxy for pi-ai",
|
"description": "CORS and authentication proxy for pi-ai",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mariozechner/pi-tui",
|
"name": "@mariozechner/pi-tui",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
"description": "Terminal User Interface library with differential rendering for efficient text-based applications",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
|
||||||
|
|
@ -283,21 +283,17 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
return atMatch[0]; // Return the full @path pattern
|
return atMatch[0]; // Return the full @path pattern
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match paths - including those ending with /, ~/, or any word at end for forced extraction
|
// Simple approach: find the last whitespace/delimiter and extract the word after it
|
||||||
// This regex captures:
|
// This avoids catastrophic backtracking from nested quantifiers
|
||||||
// - Paths starting from beginning of line or after space/quote/equals
|
const lastDelimiterIndex = Math.max(
|
||||||
// - Absolute paths starting with /
|
text.lastIndexOf(" "),
|
||||||
// - Relative paths with ./ or ../
|
text.lastIndexOf("\t"),
|
||||||
// - Home directory paths with ~/
|
text.lastIndexOf('"'),
|
||||||
// - The path itself (can include / in the middle)
|
text.lastIndexOf("'"),
|
||||||
// - For forced extraction, capture any word at the end
|
text.lastIndexOf("="),
|
||||||
const matches = text.match(/(?:^|[\s"'=])((?:\/|~\/|\.{1,2}\/)?(?:[^\s"'=]*\/?)*[^\s"'=]*)$/);
|
);
|
||||||
if (!matches) {
|
|
||||||
// If forced extraction and no matches, return empty string to trigger from current dir
|
|
||||||
return forceExtract ? "" : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathPrefix = matches[1] || "";
|
const pathPrefix = lastDelimiterIndex === -1 ? text : text.slice(lastDelimiterIndex + 1);
|
||||||
|
|
||||||
// For forced extraction (Tab key), always return something
|
// For forced extraction (Tab key), always return something
|
||||||
if (forceExtract) {
|
if (forceExtract) {
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,7 @@ export class Editor implements Component {
|
||||||
// Only pass arrow keys to the list, not Enter/Tab (we handle those directly)
|
// Only pass arrow keys to the list, not Enter/Tab (we handle those directly)
|
||||||
if (data === "\x1b[A" || data === "\x1b[B") {
|
if (data === "\x1b[A" || data === "\x1b[B") {
|
||||||
this.autocompleteList.handleInput(data);
|
this.autocompleteList.handleInput(data);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Tab was pressed, always apply the selection
|
// If Tab was pressed, always apply the selection
|
||||||
|
|
@ -832,6 +833,11 @@ export class Editor implements Component {
|
||||||
this.tryTriggerAutocomplete(true);
|
this.tryTriggerAutocomplete(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/559322883
|
||||||
|
17 this job fails with https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19
|
||||||
|
536643416/job/55932288317 havea look at .gi
|
||||||
|
*/
|
||||||
private forceFileAutocomplete(): void {
|
private forceFileAutocomplete(): void {
|
||||||
if (!this.autocompleteProvider) return;
|
if (!this.autocompleteProvider) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,8 @@ export class Markdown implements Component {
|
||||||
case "link": {
|
case "link": {
|
||||||
const linkText = this.renderInlineTokens(token.tokens || []);
|
const linkText = this.renderInlineTokens(token.tokens || []);
|
||||||
// If link text matches href, only show the link once
|
// If link text matches href, only show the link once
|
||||||
if (linkText === token.href) {
|
// Compare raw text (token.text) not styled text (linkText) since linkText has ANSI codes
|
||||||
|
if (token.text === token.href) {
|
||||||
result += this.theme.link(this.theme.underline(linkText)) + this.applyDefaultStyle("");
|
result += this.theme.link(this.theme.underline(linkText)) + this.applyDefaultStyle("");
|
||||||
} else {
|
} else {
|
||||||
result +=
|
result +=
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@mariozechner/pi-web-ui",
|
"name": "@mariozechner/pi-web-ui",
|
||||||
"version": "0.8.4",
|
"version": "0.8.5",
|
||||||
"description": "Reusable web UI components for AI chat interfaces powered by @mariozechner/pi-ai",
|
"description": "Reusable web UI components for AI chat interfaces powered by @mariozechner/pi-ai",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@lmstudio/sdk": "^1.5.0",
|
"@lmstudio/sdk": "^1.5.0",
|
||||||
"@mariozechner/pi-ai": "^0.8.4",
|
"@mariozechner/pi-ai": "^0.8.5",
|
||||||
"@mariozechner/pi-tui": "^0.8.4",
|
"@mariozechner/pi-tui": "^0.8.5",
|
||||||
"docx-preview": "^0.3.7",
|
"docx-preview": "^0.3.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"lucide": "^0.544.0",
|
"lucide": "^0.544.0",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue