mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-16 06:02:42 +00:00
Add before/after session events with cancellation support
- Merge branch event into session with before_branch/branch reasons
- Add before_switch, before_clear, shutdown reasons
- before_* events can be cancelled with { cancel: true }
- Update RPC commands to return cancelled status
- Add shutdown event on process exit
- New example hooks: confirm-destructive, dirty-repo-guard, auto-commit-on-exit
fixes #278
This commit is contained in:
parent
99081fce30
commit
42d7d9d9b6
20 changed files with 426 additions and 124 deletions
|
|
@ -1162,8 +1162,7 @@ export class InteractiveMode {
|
|||
private handleCtrlC(): void {
|
||||
const now = Date.now();
|
||||
if (now - this.lastSigintTime < 500) {
|
||||
this.stop();
|
||||
process.exit(0);
|
||||
void this.shutdown();
|
||||
} else {
|
||||
this.clearEditor();
|
||||
this.lastSigintTime = now;
|
||||
|
|
@ -1172,6 +1171,27 @@ export class InteractiveMode {
|
|||
|
||||
private handleCtrlD(): void {
|
||||
// Only called when editor is empty (enforced by CustomEditor)
|
||||
void this.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully shutdown the agent.
|
||||
* Emits shutdown event to hooks, then exits.
|
||||
*/
|
||||
private async shutdown(): Promise<void> {
|
||||
// Emit shutdown event to hooks
|
||||
const hookRunner = this.session.hookRunner;
|
||||
if (hookRunner?.hasHandlers("session")) {
|
||||
const entries = this.sessionManager.loadEntries();
|
||||
await hookRunner.emit({
|
||||
type: "session",
|
||||
entries,
|
||||
sessionFile: this.session.sessionFile,
|
||||
previousSessionFile: null,
|
||||
reason: "shutdown",
|
||||
});
|
||||
}
|
||||
|
||||
this.stop();
|
||||
process.exit(0);
|
||||
}
|
||||
|
|
@ -1496,8 +1516,8 @@ export class InteractiveMode {
|
|||
userMessages.map((m) => ({ index: m.entryIndex, text: m.text })),
|
||||
async (entryIndex) => {
|
||||
const result = await this.session.branch(entryIndex);
|
||||
if (result.skipped) {
|
||||
// Hook requested to skip conversation restore
|
||||
if (result.cancelled) {
|
||||
// Hook cancelled the branch
|
||||
done();
|
||||
this.ui.requestRender();
|
||||
return;
|
||||
|
|
@ -1533,8 +1553,7 @@ export class InteractiveMode {
|
|||
this.ui.requestRender();
|
||||
},
|
||||
() => {
|
||||
this.stop();
|
||||
process.exit(0);
|
||||
void this.shutdown();
|
||||
},
|
||||
);
|
||||
return { component: selector, focus: selector.getSessionList() };
|
||||
|
|
|
|||
|
|
@ -186,9 +186,11 @@ export class RpcClient {
|
|||
|
||||
/**
|
||||
* Reset session (clear all messages).
|
||||
* @returns Object with `cancelled: true` if a hook cancelled the reset
|
||||
*/
|
||||
async reset(): Promise<void> {
|
||||
await this.send({ type: "reset" });
|
||||
async reset(): Promise<{ cancelled: boolean }> {
|
||||
const response = await this.send({ type: "reset" });
|
||||
return this.getData(response);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -311,15 +313,18 @@ export class RpcClient {
|
|||
|
||||
/**
|
||||
* Switch to a different session file.
|
||||
* @returns Object with `cancelled: true` if a hook cancelled the switch
|
||||
*/
|
||||
async switchSession(sessionPath: string): Promise<void> {
|
||||
await this.send({ type: "switch_session", sessionPath });
|
||||
async switchSession(sessionPath: string): Promise<{ cancelled: boolean }> {
|
||||
const response = await this.send({ type: "switch_session", sessionPath });
|
||||
return this.getData(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Branch from a specific message.
|
||||
* @returns Object with `text` (the message text) and `cancelled` (if hook cancelled)
|
||||
*/
|
||||
async branch(entryIndex: number): Promise<{ text: string }> {
|
||||
async branch(entryIndex: number): Promise<{ text: string; cancelled: boolean }> {
|
||||
const response = await this.send({ type: "branch", entryIndex });
|
||||
return this.getData(response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,8 +205,8 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|||
}
|
||||
|
||||
case "reset": {
|
||||
await session.reset();
|
||||
return success(id, "reset");
|
||||
const cancelled = !(await session.reset());
|
||||
return success(id, "reset", { cancelled });
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
|
|
@ -339,13 +339,13 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|||
}
|
||||
|
||||
case "switch_session": {
|
||||
await session.switchSession(command.sessionPath);
|
||||
return success(id, "switch_session");
|
||||
const cancelled = !(await session.switchSession(command.sessionPath));
|
||||
return success(id, "switch_session", { cancelled });
|
||||
}
|
||||
|
||||
case "branch": {
|
||||
const result = await session.branch(command.entryIndex);
|
||||
return success(id, "branch", { text: result.selectedText, skipped: result.skipped });
|
||||
return success(id, "branch", { text: result.selectedText, cancelled: result.cancelled });
|
||||
}
|
||||
|
||||
case "get_branch_messages": {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ export type RpcResponse =
|
|||
| { id?: string; type: "response"; command: "prompt"; success: true }
|
||||
| { id?: string; type: "response"; command: "queue_message"; success: true }
|
||||
| { id?: string; type: "response"; command: "abort"; success: true }
|
||||
| { id?: string; type: "response"; command: "reset"; success: true }
|
||||
| { id?: string; type: "response"; command: "reset"; success: true; data: { cancelled: boolean } }
|
||||
|
||||
// State
|
||||
| { id?: string; type: "response"; command: "get_state"; success: true; data: RpcSessionState }
|
||||
|
|
@ -142,8 +142,8 @@ export type RpcResponse =
|
|||
// Session
|
||||
| { id?: string; type: "response"; command: "get_session_stats"; success: true; data: SessionStats }
|
||||
| { id?: string; type: "response"; command: "export_html"; success: true; data: { path: string } }
|
||||
| { id?: string; type: "response"; command: "switch_session"; success: true }
|
||||
| { id?: string; type: "response"; command: "branch"; success: true; data: { text: string } }
|
||||
| { id?: string; type: "response"; command: "switch_session"; success: true; data: { cancelled: boolean } }
|
||||
| { id?: string; type: "response"; command: "branch"; success: true; data: { text: string; cancelled: boolean } }
|
||||
| {
|
||||
id?: string;
|
||||
type: "response";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue