From 575c87547578c9083257f400782647726b67a72f Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sun, 28 Dec 2025 16:54:43 +0100 Subject: [PATCH] Remove allowDuringStreaming flag - commands always run immediately Hook commands now always execute immediately, even during streaming. If a command needs to interact with the LLM, it uses pi.sendMessage() which handles queueing automatically. This simplifies the API and eliminates the issue of queued slash commands being sent to the LLM instead of executing. --- .pi/hooks/test-command.ts | 8 -- packages/ai/src/models.generated.ts | 114 +++++++++--------- packages/coding-agent/examples/hooks/snake.ts | 2 +- packages/coding-agent/src/core/hooks/types.ts | 10 +- .../src/modes/interactive/interactive-mode.ts | 10 +- 5 files changed, 66 insertions(+), 78 deletions(-) diff --git a/.pi/hooks/test-command.ts b/.pi/hooks/test-command.ts index a9bdc16e..52382d85 100644 --- a/.pi/hooks/test-command.ts +++ b/.pi/hooks/test-command.ts @@ -29,14 +29,6 @@ export default function (pi: HookAPI) { return box; }); - pi.registerCommand("no-stream", { - description: "Send a message without streaming", - handler: async (ctx) => { - ctx.ui.notify("Sending message after streaming is done..."); - }, - allowDuringStreaming: true, - }) - // Register /test-msg command pi.registerCommand("test-msg", { description: "Send a test custom message", diff --git a/packages/ai/src/models.generated.ts b/packages/ai/src/models.generated.ts index 6392308c..2df269d3 100644 --- a/packages/ai/src/models.generated.ts +++ b/packages/ai/src/models.generated.ts @@ -6104,9 +6104,9 @@ export const MODELS = { contextWindow: 32768, maxTokens: 4096, } satisfies Model<"openai-completions">, - "anthropic/claude-3.5-haiku": { - id: "anthropic/claude-3.5-haiku", - name: "Anthropic: Claude 3.5 Haiku", + "anthropic/claude-3.5-haiku-20241022": { + id: "anthropic/claude-3.5-haiku-20241022", + name: "Anthropic: Claude 3.5 Haiku (2024-10-22)", api: "openai-completions", provider: "openrouter", baseUrl: "https://openrouter.ai/api/v1", @@ -6121,9 +6121,9 @@ export const MODELS = { contextWindow: 200000, maxTokens: 8192, } satisfies Model<"openai-completions">, - "anthropic/claude-3.5-haiku-20241022": { - id: "anthropic/claude-3.5-haiku-20241022", - name: "Anthropic: Claude 3.5 Haiku (2024-10-22)", + "anthropic/claude-3.5-haiku": { + id: "anthropic/claude-3.5-haiku", + name: "Anthropic: Claude 3.5 Haiku", api: "openai-completions", provider: "openrouter", baseUrl: "https://openrouter.ai/api/v1", @@ -6359,6 +6359,23 @@ export const MODELS = { contextWindow: 128000, maxTokens: 16384, } satisfies Model<"openai-completions">, + "meta-llama/llama-3.1-8b-instruct": { + id: "meta-llama/llama-3.1-8b-instruct", + name: "Meta: Llama 3.1 8B Instruct", + api: "openai-completions", + provider: "openrouter", + baseUrl: "https://openrouter.ai/api/v1", + reasoning: false, + input: ["text"], + cost: { + input: 0.02, + output: 0.03, + cacheRead: 0, + cacheWrite: 0, + }, + contextWindow: 131072, + maxTokens: 16384, + } 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", @@ -6393,23 +6410,6 @@ export const MODELS = { contextWindow: 131072, maxTokens: 4096, } satisfies Model<"openai-completions">, - "meta-llama/llama-3.1-8b-instruct": { - id: "meta-llama/llama-3.1-8b-instruct", - name: "Meta: Llama 3.1 8B Instruct", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - reasoning: false, - input: ["text"], - cost: { - input: 0.02, - output: 0.03, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 131072, - maxTokens: 16384, - } satisfies Model<"openai-completions">, "mistralai/mistral-nemo": { id: "mistralai/mistral-nemo", name: "Mistral: Mistral Nemo", @@ -6546,6 +6546,23 @@ export const MODELS = { contextWindow: 128000, maxTokens: 4096, } 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": { id: "openai/gpt-4o", name: "OpenAI: GPT-4o", @@ -6580,23 +6597,6 @@ export const MODELS = { contextWindow: 128000, maxTokens: 64000, } 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": { id: "meta-llama/llama-3-70b-instruct", name: "Meta: Llama 3 70B Instruct", @@ -6835,23 +6835,6 @@ export const MODELS = { contextWindow: 8191, maxTokens: 4096, } satisfies Model<"openai-completions">, - "openai/gpt-3.5-turbo": { - id: "openai/gpt-3.5-turbo", - name: "OpenAI: GPT-3.5 Turbo", - api: "openai-completions", - provider: "openrouter", - baseUrl: "https://openrouter.ai/api/v1", - reasoning: false, - input: ["text"], - cost: { - input: 0.5, - output: 1.5, - cacheRead: 0, - cacheWrite: 0, - }, - contextWindow: 16385, - maxTokens: 4096, - } satisfies Model<"openai-completions">, "openai/gpt-4": { id: "openai/gpt-4", name: "OpenAI: GPT-4", @@ -6869,6 +6852,23 @@ export const MODELS = { contextWindow: 8191, maxTokens: 4096, } satisfies Model<"openai-completions">, + "openai/gpt-3.5-turbo": { + id: "openai/gpt-3.5-turbo", + name: "OpenAI: GPT-3.5 Turbo", + api: "openai-completions", + provider: "openrouter", + baseUrl: "https://openrouter.ai/api/v1", + reasoning: false, + input: ["text"], + cost: { + input: 0.5, + output: 1.5, + cacheRead: 0, + cacheWrite: 0, + }, + contextWindow: 16385, + maxTokens: 4096, + } satisfies Model<"openai-completions">, "openrouter/auto": { id: "openrouter/auto", name: "OpenRouter: Auto Router", diff --git a/packages/coding-agent/examples/hooks/snake.ts b/packages/coding-agent/examples/hooks/snake.ts index ea7031da..3b5ffe8d 100644 --- a/packages/coding-agent/examples/hooks/snake.ts +++ b/packages/coding-agent/examples/hooks/snake.ts @@ -309,7 +309,7 @@ const SNAKE_SAVE_TYPE = "snake-save"; export default function (pi: HookAPI) { pi.registerCommand("snake", { description: "Play Snake!", - allowDuringStreaming: true, // Run even during streaming, not queued + handler: async (ctx) => { if (!ctx.hasUI) { ctx.ui.notify("Snake requires interactive mode", "error"); diff --git a/packages/coding-agent/src/core/hooks/types.ts b/packages/coding-agent/src/core/hooks/types.ts index 45943521..fa753b7e 100644 --- a/packages/coding-agent/src/core/hooks/types.ts +++ b/packages/coding-agent/src/core/hooks/types.ts @@ -442,8 +442,7 @@ export interface HookCommandContext { export interface RegisteredCommand { name: string; description?: string; - /** If true, command runs during streaming instead of being queued */ - allowDuringStreaming?: boolean; + handler: (ctx: HookCommandContext) => Promise; } @@ -456,9 +455,9 @@ export interface HookAPI { on(event: "session", handler: HookHandler): void; // biome-ignore lint/suspicious/noConfusingVoidType: void allows handlers to not return anything on(event: "context", handler: HookHandler): void; - // biome-ignore lint/suspicious/noConfusingVoidType: void allows handlers to not return anything on( event: "before_agent_start", + // biome-ignore lint/suspicious/noConfusingVoidType: void allows handlers to not return anything handler: HookHandler, ): void; on(event: "agent_start", handler: HookHandler): void; @@ -527,10 +526,7 @@ export interface HookAPI { * Register a custom slash command. * Handler receives HookCommandContext. */ - registerCommand( - name: string, - options: { description?: string; allowDuringStreaming?: boolean; handler: RegisteredCommand["handler"] }, - ): void; + registerCommand(name: string, options: { description?: string; handler: RegisteredCommand["handler"] }): void; /** * Execute a shell command and return stdout/stderr/code. diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 68681cb9..d1e12f6c 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -744,13 +744,13 @@ export class InteractiveMode { return; } - // Check if this hook command can run during streaming (not queued) - if (text.startsWith("/") && this.session.hookRunner && this.session.isStreaming) { + // Hook commands always run immediately, even during streaming + // (if they need to interact with LLM, they use pi.sendMessage which handles queueing) + if (text.startsWith("/") && this.session.hookRunner) { const spaceIndex = text.indexOf(" "); const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex); const command = this.session.hookRunner.getCommand(commandName); - if (command?.allowDuringStreaming) { - // Execute hook command right away + if (command) { this.editor.addToHistory(text); this.editor.setText(""); await this.session.prompt(text); @@ -758,7 +758,7 @@ export class InteractiveMode { } } - // Queue message if agent is streaming + // Queue regular messages if agent is streaming if (this.session.isStreaming) { await this.session.queueMessage(text); this.updatePendingMessagesDisplay();