Five independent items from the post-review backlog. F1: Stop on an external agent task now aborts the running child via a per-task AbortController registry reachable from the cancel route, and finalizes the assistant message as cancelled (fixing two latent bugs — catch blocks left the message streaming, and warm success-paths wrote complete on an aborted turn); warm pools/worktrees are preserved and the native path is unchanged. F2/F3: prune the tool-call parser to its two load-bearing exports (unexport eight zero-caller symbols, add a gate test for the <invoke>-as-text fallback) and route placeholder-rejection logging through pino. F6: a 90s per-chunk stall-timeout wraps native inference's fullStream via AbortSignal.any so a hung stream finalizes the message instead of hanging — no retry (a pure classifyStreamError helper is added). F7: a read-only view_session_history MCP tool (newest-N, chronological). F9: retire the unused apps/coder/web :9502 fallback SPA, keeping every API/WS/health/MCP route. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
/**
|
|
* F1 — per-task abort registry. A Stop on an external-agent task must reach the
|
|
* in-flight run and abort its child / prompt. Each external run-function registers
|
|
* its per-turn AbortController here keyed by task id; the cancel route calls
|
|
* `cancel(taskId)` to fire it; the run-function's `.finally` deletes the entry.
|
|
*
|
|
* Idempotent by construction:
|
|
* - `cancel()` on an already-aborted controller no-ops (AbortController.abort()
|
|
* is idempotent) → a rapid double-Stop is safe.
|
|
* - `cancel()` on an unknown / already-finished task returns false → a
|
|
* cancel-after-natural-exit (entry already deleted) and a Stop on a native
|
|
* boocode task (never registered) are both safe no-ops.
|
|
*
|
|
* Pure (no DB / child / IO) so the abort wiring + idempotency contract is
|
|
* unit-testable in isolation — mirrors the turn-guard / lifecycle-decisions
|
|
* pure-helper precedent.
|
|
*/
|
|
export interface CancelRegistry {
|
|
/** Create + store an AbortController for this task, returning it for the run. */
|
|
register(taskId: string): AbortController;
|
|
/** Abort the task's in-flight run. Returns false when no controller is registered. */
|
|
cancel(taskId: string): boolean;
|
|
/** Drop the task's entry (called from the run's `.finally`). No-op if absent. */
|
|
delete(taskId: string): void;
|
|
/** Whether a controller is currently registered for this task. */
|
|
has(taskId: string): boolean;
|
|
}
|
|
|
|
export function createCancelRegistry(): CancelRegistry {
|
|
const controllers = new Map<string, AbortController>();
|
|
return {
|
|
register(taskId) {
|
|
const ac = new AbortController();
|
|
controllers.set(taskId, ac);
|
|
return ac;
|
|
},
|
|
cancel(taskId) {
|
|
const ac = controllers.get(taskId);
|
|
if (!ac) return false;
|
|
ac.abort();
|
|
return true;
|
|
},
|
|
delete(taskId) {
|
|
controllers.delete(taskId);
|
|
},
|
|
has(taskId) {
|
|
return controllers.has(taskId);
|
|
},
|
|
};
|
|
}
|