mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-20 20:01:06 +00:00
feat(hooks): add text_delta event for streaming text monitoring
- New text_delta hook event fires for each chunk of streaming text - Enables real-time monitoring of agent output - Plan-mode hook now updates todo progress as [DONE:id] tags stream in - Each todo item has unique ID for reliable tracking
This commit is contained in:
parent
7a03f57fbe
commit
d1eea3ac4e
5 changed files with 57 additions and 5 deletions
|
|
@ -42,6 +42,7 @@
|
||||||
- Hook API: `pi.registerShortcut(shortcut, options)` for hooks to register custom keyboard shortcuts (e.g., `shift+p`, `ctrl+shift+x`)
|
- Hook API: `pi.registerShortcut(shortcut, options)` for hooks to register custom keyboard shortcuts (e.g., `shift+p`, `ctrl+shift+x`)
|
||||||
- Hook API: `ctx.ui.setWidget(key, lines)` for multi-line status displays above the editor (todo lists, progress tracking)
|
- Hook API: `ctx.ui.setWidget(key, lines)` for multi-line status displays above the editor (todo lists, progress tracking)
|
||||||
- Hook API: `theme.strikethrough(text)` for strikethrough text styling
|
- Hook API: `theme.strikethrough(text)` for strikethrough text styling
|
||||||
|
- Hook API: `text_delta` event for monitoring streaming assistant text in real-time
|
||||||
- `/hotkeys` command now shows hook-registered shortcuts in a separate "Hooks" section
|
- `/hotkeys` command now shows hook-registered shortcuts in a separate "Hooks" section
|
||||||
- New example hook: `plan-mode.ts` - Claude Code-style read-only exploration mode:
|
- New example hook: `plan-mode.ts` - Claude Code-style read-only exploration mode:
|
||||||
- Toggle via `/plan` command, `Shift+P` shortcut, or `--plan` CLI flag
|
- Toggle via `/plan` command, `Shift+P` shortcut, or `--plan` CLI flag
|
||||||
|
|
@ -49,6 +50,8 @@
|
||||||
- Bash commands restricted to non-destructive operations (blocks `rm`, `mv`, `git commit`, `npm install`, etc.)
|
- Bash commands restricted to non-destructive operations (blocks `rm`, `mv`, `git commit`, `npm install`, etc.)
|
||||||
- Interactive prompt after each response: execute plan, stay in plan mode, or refine
|
- Interactive prompt after each response: execute plan, stay in plan mode, or refine
|
||||||
- Todo list widget showing progress with checkboxes and strikethrough for completed items
|
- Todo list widget showing progress with checkboxes and strikethrough for completed items
|
||||||
|
- Each todo has a unique ID; agent marks items done by outputting `[DONE:id]`
|
||||||
|
- Real-time progress updates via streaming text monitoring
|
||||||
- `/todos` command to view current plan progress
|
- `/todos` command to view current plan progress
|
||||||
- Shows `⏸ plan` indicator in footer when in plan mode, `📋 2/5` when executing
|
- Shows `⏸ plan` indicator in footer when in plan mode, `📋 2/5` when executing
|
||||||
- State persists across sessions (including todo progress)
|
- State persists across sessions (including todo progress)
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,21 @@ pi.on("turn_end", async (event, ctx) => {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### text_delta
|
||||||
|
|
||||||
|
Fired for each chunk of streaming text from the assistant. Useful for real-time monitoring of agent output.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
pi.on("text_delta", async (event, ctx) => {
|
||||||
|
// event.text - the new text chunk
|
||||||
|
|
||||||
|
// Example: watch for specific patterns in streaming output
|
||||||
|
if (event.text.includes("[DONE:")) {
|
||||||
|
// Handle completion marker
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
#### context
|
#### context
|
||||||
|
|
||||||
Fired before each LLM call. Modify messages non-destructively (session unchanged).
|
Fired before each LLM call. Modify messages non-destructively (session unchanged).
|
||||||
|
|
|
||||||
|
|
@ -291,12 +291,25 @@ export default function planModeHook(pi: HookAPI) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check for [DONE:id] tags after each tool result (agent may output them in tool-related text)
|
// Watch for [DONE:id] tags in streaming text
|
||||||
pi.on("tool_result", async (_event, ctx) => {
|
pi.on("text_delta", async (event, ctx) => {
|
||||||
if (!executionMode || todoItems.length === 0) return;
|
if (!executionMode || todoItems.length === 0) return;
|
||||||
// The actual checking happens in agent_end when we have the full message
|
|
||||||
// But we update status here to keep UI responsive
|
const doneIds = findDoneTags(event.text);
|
||||||
updateStatus(ctx);
|
if (doneIds.length === 0) return;
|
||||||
|
|
||||||
|
let changed = false;
|
||||||
|
for (const id of doneIds) {
|
||||||
|
const item = todoItems.find((t) => t.id === id);
|
||||||
|
if (item && !item.completed) {
|
||||||
|
item.completed = true;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
updateStatus(ctx);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Inject plan mode context
|
// Inject plan mode context
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,15 @@ export class AgentSession {
|
||||||
|
|
||||||
/** Internal handler for agent events - shared by subscribe and reconnect */
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
||||||
private _handleAgentEvent = async (event: AgentEvent): Promise<void> => {
|
private _handleAgentEvent = async (event: AgentEvent): Promise<void> => {
|
||||||
|
// Emit text_delta events to hooks for streaming text monitoring
|
||||||
|
if (
|
||||||
|
event.type === "message_update" &&
|
||||||
|
event.assistantMessageEvent.type === "text_delta" &&
|
||||||
|
this._hookRunner
|
||||||
|
) {
|
||||||
|
await this._hookRunner.emit({ type: "text_delta", text: event.assistantMessageEvent.delta });
|
||||||
|
}
|
||||||
|
|
||||||
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
||||||
// This ensures the UI sees the updated queue state
|
// This ensures the UI sees the updated queue state
|
||||||
if (event.type === "message_start" && event.message.role === "user") {
|
if (event.type === "message_start" && event.message.role === "user") {
|
||||||
|
|
|
||||||
|
|
@ -392,6 +392,16 @@ export interface AgentEndEvent {
|
||||||
messages: AgentMessage[];
|
messages: AgentMessage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event data for text_delta event.
|
||||||
|
* Fired when new text is streamed from the assistant.
|
||||||
|
*/
|
||||||
|
export interface TextDeltaEvent {
|
||||||
|
type: "text_delta";
|
||||||
|
/** The new text chunk */
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event data for turn_start event.
|
* Event data for turn_start event.
|
||||||
*/
|
*/
|
||||||
|
|
@ -535,6 +545,7 @@ export type HookEvent =
|
||||||
| BeforeAgentStartEvent
|
| BeforeAgentStartEvent
|
||||||
| AgentStartEvent
|
| AgentStartEvent
|
||||||
| AgentEndEvent
|
| AgentEndEvent
|
||||||
|
| TextDeltaEvent
|
||||||
| TurnStartEvent
|
| TurnStartEvent
|
||||||
| TurnEndEvent
|
| TurnEndEvent
|
||||||
| ToolCallEvent
|
| ToolCallEvent
|
||||||
|
|
@ -701,6 +712,7 @@ export interface HookAPI {
|
||||||
on(event: "turn_end", handler: HookHandler<TurnEndEvent>): void;
|
on(event: "turn_end", handler: HookHandler<TurnEndEvent>): void;
|
||||||
on(event: "tool_call", handler: HookHandler<ToolCallEvent, ToolCallEventResult>): void;
|
on(event: "tool_call", handler: HookHandler<ToolCallEvent, ToolCallEventResult>): void;
|
||||||
on(event: "tool_result", handler: HookHandler<ToolResultEvent, ToolResultEventResult>): void;
|
on(event: "tool_result", handler: HookHandler<ToolResultEvent, ToolResultEventResult>): void;
|
||||||
|
on(event: "text_delta", handler: HookHandler<TextDeltaEvent>): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a custom message to the session. Creates a CustomMessageEntry that
|
* Send a custom message to the session. Creates a CustomMessageEntry that
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue