Skip to content

Commit 365c5b9

Browse files
aorumbayevclaude
andauthored
refactor: typed AgentEvent + edit-diff + tool streaming + pi-coding-agent backend + web renderer registry (#138)
* feat(core/events): extract events_common.py shared base variants (Pi Step 1a) Pydantic discriminated union variants — MessageStart/Update/End, ToolExecutionStart/Update/End — that both ChatEvent and the upcoming AgentEvent compose. ChatEvent migrated to import these from events_common; chat-specific naming aliases retained for back-compat. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(core): typed AgentEvent union for agent task sessions (Pi Step 1b) core/agent_events.py defines AgentStart, AgentEnd, TurnStart, TurnEnd, CompactionOccurred plus shared variants from events_common, and all legacy SessionEventType-mapped variants. core/_events.py gains emit_typed() method accepting AgentEvent instances, settlement-rule wait_idle() / register_agent_end_subscriber() / notify_agent_end_handled() APIs, and dual-mode coalescing/terminal detection for both legacy uppercase and new lowercase kind strings. SessionEvent.event_type relaxed from SessionEventType enum to str so new kind-based events persist without coercion. All is-identity comparisons on event_type replaced with equality comparisons. Alembic revision 5041f8573a34 drops and recreates task_events with CASCADE FK (alpha hard cut). AGENT_STATUS retained — zero-cost removal deferred until all TUI/server consumers migrate in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(events): emit AgentEvent through wire generator (Pi Step 1c) scripts/generate_wire_types.py extended with static AgentEvent TS section (33 interface definitions + AgentEvent discriminated union type). Wire output regenerated to packages/shared/api-client/src/wire.ts. Wire-drift gate confirms sync. Settlement rule wired into Events aggregate: register_agent_end_subscriber / notify_agent_end_handled / wait_idle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(mcp/fs): port edit-diff CRLF/BOM/overlap handling from pi-coding-agent (Pi Step 7) Direct port of references/pi-mono/packages/coding-agent/src/core/tools/edit-diff.ts. New module mcp/toolsets/_edit_diff.py provides normalize_line_endings, detect_bom, reapply_line_endings, merge_overlapping_edits, plus the Edit dataclass and EditConflict exception. Also includes apply_edits_to_normalized_content (core text-hunk replacement engine with fuzzy matching), and LineEdit for line-range overlap merging. mcp/toolsets/fs.py is a new toolset with three tools: - fs_read_file (WORKER+): read file bytes, detect BOM/EOL, return metadata - fs_write_file (ORCHESTRATOR): fresh create/overwrite, no BOM preservation - fs_edit_file (ORCHESTRATOR): old->new text replacement preserving BOM + EOL All file-mutation tools route through apply_edits() which: 1. Reads bytes; detects BOM (UTF-8-sig, UTF-16-LE/BE, UTF-32-LE/BE). 2. Decodes using detected encoding (UTF-8 fallback). 3. Normalizes EOLs to LF; records original eol_style. 4. Applies edits to LF-normalized content (fuzzy match for smart quotes/ dashes/special spaces -- same algorithm as pi-mono). 5. Reapplies original EOL style. 6. Re-encodes and prepends original BOM if present. Audit of existing write_text calls: - src/kagan/core/_launchers.py: writes .mcp.json, startup prompt, attach context -- generated tool configs, not user-edited content. Left alone. - src/kagan/core/_agent.py: writes .mcp.json -- same reason. Left alone. - src/kagan/core/chat/_factories.py: writes .mcp.json. Left alone. - src/kagan/core/_preflight.py: probe write. Left alone. - src/kagan/core/_prompt_export.py: writes exported prompt YAML. Not user-edited content (export artifact). Left alone. No existing naïve write_text calls touch user-edited source files, so no migration was needed beyond the new fs toolset. Policy: fs_read_file -> WORKER+; fs_write_file, fs_edit_file -> ORCHESTRATOR. Contract tests (41): CRLF round-trip, BOM preservation, overlapping-edit merge correctness, fuzzy match, all error paths. Behavioral tests (15): full MCP tool protocol round-trips with real files in tmp_path -- BOM+CRLF compound round-trips, missing-file error, overlapping error, multi-edit, trailing-newline edge case. Pi-mono integration step 7 of 6 (Steps 1, 7, 2, 4, 3, 5 land on this branch before one consolidated PR). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: pre-sweep — remove legacy / dead / backcompat / shim code (pi-mono) Before remaining pi-mono steps (2, 4, R6a, 5) land, sweep soft-cut residue from R1-R5 and Pi Step 1. B — Settlement-rule subscribers wired in core/_sessions.py: register_agent_end_subscriber() called before ACP agent spawns; notify_agent_end_handled() released in _handle_acp_done() finally block so wait_idle() unblocks on both success and error paths. New unit test file tests/core/unit/test_settlement_rule.py (7 tests) covering: immediate return, timeout, blocking until notify, multi-subscriber drain, error-path release. _FakeEvents in tests/unit/test_sessions_shutdown.py extended with publish_board / notify_agent_end_handled / register_agent_end_subscriber stubs to match the expanded Events protocol. C — R3 thin re-export shim TS files deleted: packages/shared/api-client/src/types.ts — 461-LOC hand-written file that was never imported by anything (not in index.ts); dead since R3 promoted everything to wire.ts. packages/vscode/src/api/types.ts — 153-LOC re-export shim deleted; all 22 vscode consumers (providers/, commands/, extension.ts, api/client.ts, api/sse.ts, api/local.ts) now import directly from @kagan/shared-api-client. packages/web/src/lib/api/types.ts — 168-LOC re-export shim deleted; all 61 web consumers now import directly from @kagan/shared-api-client. DoctorReportResponse added to web/src/lib/api/client.ts top-level import block (was an inline import type referencing the deleted shim). Deferrals (reported): A — AGENT_STATUS / SessionEventType full retirement: enum has 20 active values; migration touches _acp.py, _sessions.py, _events.py and TUI screens (not in eng-core lane). Deferred to pi-step-2 or dedicated sweep task. D — _transitions.py deletion: validate_move() is still called from Tasks.set_status() (line 619) and validate_merge_move() from merge_task(); Tasks.set_status() is still an active code path for reject_review and detached-agent completion. Deleting _transitions.py requires first migrating Tasks.set_status() to the transitions.py funnel. Deferred. F — Comment debt: "legacy" comments in _events.py describe real dual-format DB behaviour (uppercase enum vs lowercase kind strings for historical rows). Not stale — legitimately deferred until the DB migration that rewrites old rows runs. H — Vulture: clean (0 findings). I — Test fixtures: _session_picker module is still live (phase 6 extraction intact), so fixture imports are valid. Net: 91 files changed, +109 / -870 LOC. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: complete pi-mono pre-sweep — retire SessionEventType + delete _transitions.py Round-2 sweep finishes the two categories deferred in dae507d: A. SessionEventType enum deleted from core/enums.py and removed from core/__init__.py public surface. All ~25 call sites across core/_acp, core/_sessions, core/_events, core/_reviews, core/_tasks, core/transitions, core/_agent_monitor, server/mcp/toolsets/sessions, cli/doctor, cli/chat/controller, plus 4 TUI screen files (session_dashboard, _chat_runner, task_screen, doctor_modal) and task_event_handler + task_workspace_helpers migrated to kind string comparisons (e.g. "output_chunk", "task_status_changed"). The SESSION_EVENT_TYPE_TO_KIND bridge map in agent_events.py is deleted. _events.emit() signature changed from SessionEventType to str. wire.ts regenerated with EVENT_TYPE constants now derived from AgentEvent kind strings instead of the removed enum. D. core/_transitions.py deleted. validate_move / validate_merge_move guards folded into callers: - Tasks.set_status now enforces the direct-move matrix inline (REVIEW->DONE excluded, same invariant as before). - merge_task uses an inline InvalidTransitionError guard instead of validate_merge_move. - reject_review emits "task_status_changed" kind string directly. tests/core/unit/test_transitions.py rewritten to test the same invariants without importing the deleted module. Net delete: ~459 LOC (130 from _transitions.py, 23 from SessionEventType enum body, 27 from SESSION_EVENT_TYPE_TO_KIND map, rest call site cleanups). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(mcp): opt-in tool partial-result streaming for bash_exec + terminal_run (Pi Step 2) bash_exec and terminal_run toolsets are introduced with a keyword-only on_update callback. When the MCP server has a bound task context (task_id + session_id), the dispatcher wires the callback to emit typed ToolExecutionUpdate events on the per-task stream for each stdout/stderr line, correlated with the tool_id from ToolExecutionStart. Without task context the callback degrades gracefully (output buffered, no events). Other ~28 toolsets are sub-second one-shots and stay unchanged. The run_bash() helper in bash.py is shared by terminal_run and provides the asyncio subprocess execution primitive with a streaming _merge_streams coroutine that drains stdout and stderr concurrently. terminal_run adds a max_output_lines cap for high-frequency output and a 10-minute default timeout (vs 5 minutes for bash_exec). Both tools are ORCHESTRATOR-role only in _policy.py. Contract tests (13 new) verify: - on_update callback receives each stdout/stderr line during execution - Default None callback buffers output unchanged - Timeout fires correctly (timed_out=True) - Non-zero exit codes propagate - MCP tool registration and result shape for both tools - No error when no task context is available (graceful degradation) Spinner placeholder rendering for bash_exec/terminal_run: N/A — tools are brand new so no placeholder rendering existed. Pi Step 5 will add the renderer registry that consumes ToolExecutionUpdate.partial_result. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(core): add pi-coding-agent as 15th backend (R6a) New BackendSpec("pi-coding-agent", ...) entry uses pi's JSONL-framed RPC mode instead of ACP. New module core/adapters/pi_rpc.py provides PiRpcClient + translate_pi_rpc_message (JSONL → AgentEvent). BackendCapability.PI_RPC_STREAMING added as a sibling to ACP_STREAMING. Backend selection can identify pi-rpc backends via has_capability(). Node >= 20.6 gated via check_node_version() + _parse_node_version() in _environment_checks.py. Translator splits into per-domain helpers dispatched via a static dict to keep McCabe complexity within the project limit (≤ 20). Smoke test asserts subprocess lifecycle (start + close) when npx is on PATH with Node >= 20.6; skipped otherwise. Unit test for the message translator covers each pi RPC frame shape (36 assertions across 41 test cases). Net add: 627 LOC core/adapters/pi_rpc.py + 21 LOC _agent.py + 36 LOC _environment_checks.py + 235 LOC tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(web): tool renderer registry + artifacts panel + RAF-batched streaming (Pi Step 5) packages/web/src/lib/tool-renderers/ holds a plain Map<string, React.FC> of tool renderers. getToolRenderer(name) falls back to DefaultRenderer for unregistered tools. Hardcoded StreamToolPill replaced by StreamToolCard which delegates to the registry. ArtifactsPanel.tsx renders sandboxed iframes (sandbox="", srcDoc) for HTML / SVG / Markdown artifacts. PDF/DOCX/XLSX deferred. jotai artifactsAtom backs the panel. streaming-message.ts ports pi-web-ui's RAF-batching pattern to React 19 + jotai. Per-token CHAT_CHUNK SSE frames coalesce to one setState per frame, fixing per-token rendering jank. No runtime registerToolRenderer() API — YAGNI. Net: 6 new source files, 3 new test files. Registered tool renderers: bash_exec, bash, terminal_run, js_repl, edit_file, str_replace_editor, read_file, apply_diff, patch. vitest: 351 passed (54 test files, +20 new test cases). Build: clean (pnpm run build, tsc --noEmit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: scrub internal pi-mono references from Kagan code User-facing docs naming pi-coding-agent as a backend stay. Internal module docstrings and code comments that referenced the sister project as a heritage source are rewritten to be self-describing. Affected files: - server/mcp/toolsets/_edit_diff.py -- module docstring (removed "Ported from references/pi-mono/...") replaced with a description of the functionality; inline comment "mirrors pi-mono detectLineEnding" removed from normalize_line_endings docstring The package constant PI_CODING_AGENT_BACKEND = "pi-coding-agent" and the @mariozechner/pi-coding-agent npm name are unchanged (they describe the external tool, not internal heritage). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: address Greptile P1 + 3 P2 on consolidated pi-mono PR P1 (event ordering — bash_exec / terminal_run): Fire-and-forget on_update emit tasks were not awaited before the tool_execution_end event shipped. Clients saw end → late updates, breaking the start→updates→end ordering renderers depend on. Both tools now collect pending update Tasks and asyncio.gather them before emitting end. P2 (_DIRECT_MOVES rebuilt every set_status call): Moved the frozenset literal from inside Tasks.set_status to module- level in _tasks.py. Built once at import; same invariant. P2 (turn_index hardcoded to 0): PiRpcClient now maintains a per-prompt _turn_counter incremented at the start of each _run_prompt. translate_pi_rpc_message accepts a turn_index kwarg and prefers a pi-supplied frame value when present; falls back to the caller's counter. All translator helpers gain the kwarg for dispatch consistency. P2 (_cumulative_bytes not reset between prompt() calls): Counter now resets at the start of _run_prompt. The 500 MB cap is a per-prompt safety bound, not a session-lifetime budget — long-lived clients across many prompts no longer drift toward the ceiling. 1687 tests pass (was 1687 — flat). Lint, typecheck, deadcode clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: boundary typing — kill dict[str,Any] + isinstance ladders Pattern across the codebase: external JSON-shaped data crossed the Python boundary as dict[str, Any] and every consumer did raw = obj.get("field"); if isinstance(raw, T): ... Replace with Pydantic models at the boundary so downstream code reads typed attributes. Sites swept: 1. core/adapters/pi_rpc.py — 13 _translate_X helpers + _TRANSLATOR_DISPATCH dict consolidated into one match statement over typed PiRpcMessage variants. New pi_rpc_messages.py adds boundary models for all 19 known pi frame types with extra="allow" and frozen=True. parse_pi_rpc_message does O(1) type-tag lookup + model_validate instead of a union traversal. Per-handler turn_index pollution gone; pi's real protocol has no turn_index field on turn_start/turn_end frames (verified against references/pi-mono/packages/agent/src/types.ts). _update_assembled_text and _track_message_state merged into one pass per message. 2. server/_chat_routes.py::_parse_attachments — Attachment + AttachmentBody models replace hand-rolled isinstance(a, dict) + str(a.get(...)) loop. Attachment lives in core/_io/sessions.py and is re-exported from kagan.core. Downstream dict consumers (SpawnPerTurnACPFactory, engine.push_user) receive .model_dump() dicts; wire shape unchanged. 3. MCP toolset arg shaping — audit complete; all toolsets (tasks, sessions, projects, review, bash, terminal_run, _edit_diff) already use typed Python signatures or core/_io/ models from R2. No changes needed. 4. server _session_to_wire / chat_session_to_legacy_dict — kept as-is; both involve explicit field selection / datetime formatting logic that legitimately belongs in a function, not a mechanical model_validate. 5. TUI event consumers — verified clean post Pi Step 1. No leftover event.payload[X] dict reads on the pi RPC path. Net: pi_rpc.py -207 LOC, _chat_routes.py +20 LOC (clearer intent), +2 new files (pi_rpc_messages.py, test_pi_rpc_messages.py, test_attachment_model.py). Tests: +60 new assertions across 3 files. All 1727 existing tests pass; 0 new failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent dce2635 commit 365c5b9

163 files changed

Lines changed: 6345 additions & 1321 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/shared/api-client/src/types.ts

Lines changed: 0 additions & 460 deletions
This file was deleted.

packages/shared/api-client/src/wire.ts

Lines changed: 227 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,41 @@
88
export type TaskStatus = "BACKLOG" | "IN_PROGRESS" | "REVIEW" | "DONE";
99
export type SessionStatus = "PENDING" | "RUNNING" | "COMPLETED" | "FAILED" | "CANCELLED";
1010
export type Priority = "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
11-
export type SessionEventType = "OUTPUT_CHUNK" | "AGENT_STATUS" | "TOOL_CALL_START" | "TOOL_CALL_UPDATE" | "AGENT_COMPLETED" | "AGENT_FAILED" | "PLAN_UPDATE" | "TASK_STATUS_CHANGED" | "MERGE_COMPLETED" | "MERGE_FAILED" | "CRITERION_VERDICT" | "AUTO_REVIEW_STARTED" | "INSIGHT_EXTRACTED" | "STEP_VERIFIED" | "CHECKPOINT_CREATED" | "SESSION_REWOUND" | "HOOK_BLOCKED" | "COMPACTION_TRIGGERED" | "DOCTOR_WARNED" | "FIRST_SESSION_SUCCESS" | "BACKEND_AUTO_PROMOTED";
1211

13-
// ── Event type constants (derived from src/kagan/core/enums.py) ─────────────
12+
// ── Event type constants (derived from src/kagan/core/agent_events.py) ───────
1413
export const EVENT_TYPE = {
15-
OUTPUT_CHUNK: "OUTPUT_CHUNK",
16-
AGENT_STATUS: "AGENT_STATUS",
17-
TOOL_CALL_START: "TOOL_CALL_START",
18-
TOOL_CALL_UPDATE: "TOOL_CALL_UPDATE",
19-
AGENT_COMPLETED: "AGENT_COMPLETED",
20-
AGENT_FAILED: "AGENT_FAILED",
21-
PLAN_UPDATE: "PLAN_UPDATE",
22-
TASK_STATUS_CHANGED: "TASK_STATUS_CHANGED",
23-
MERGE_COMPLETED: "MERGE_COMPLETED",
24-
MERGE_FAILED: "MERGE_FAILED",
25-
CRITERION_VERDICT: "CRITERION_VERDICT",
26-
AUTO_REVIEW_STARTED: "AUTO_REVIEW_STARTED",
27-
INSIGHT_EXTRACTED: "INSIGHT_EXTRACTED",
28-
STEP_VERIFIED: "STEP_VERIFIED",
29-
CHECKPOINT_CREATED: "CHECKPOINT_CREATED",
30-
SESSION_REWOUND: "SESSION_REWOUND",
31-
HOOK_BLOCKED: "HOOK_BLOCKED",
32-
COMPACTION_TRIGGERED: "COMPACTION_TRIGGERED",
33-
DOCTOR_WARNED: "DOCTOR_WARNED",
34-
FIRST_SESSION_SUCCESS: "FIRST_SESSION_SUCCESS",
35-
BACKEND_AUTO_PROMOTED: "BACKEND_AUTO_PROMOTED",
14+
AGENT_START: "agent_start",
15+
AGENT_END: "agent_end",
16+
TURN_START: "turn_start",
17+
TURN_END: "turn_end",
18+
MESSAGE_START: "message_start",
19+
MESSAGE_UPDATE: "message_update",
20+
MESSAGE_END: "message_end",
21+
TOOL_EXECUTION_START: "tool_execution_start",
22+
TOOL_EXECUTION_UPDATE: "tool_execution_update",
23+
TOOL_EXECUTION_END: "tool_execution_end",
24+
COMPACTION_OCCURRED: "compaction_occurred",
25+
OUTPUT_CHUNK: "output_chunk",
26+
AGENT_STATUS: "agent_status",
27+
TOOL_CALL_START: "tool_call_start",
28+
TOOL_CALL_UPDATE: "tool_call_update",
29+
PLAN_UPDATE: "plan_update",
30+
TASK_STATUS_CHANGED: "task_status_changed",
31+
AGENT_COMPLETED: "agent_completed",
32+
AGENT_FAILED: "agent_failed",
33+
MERGE_COMPLETED: "merge_completed",
34+
MERGE_FAILED: "merge_failed",
35+
CRITERION_VERDICT: "criterion_verdict",
36+
AUTO_REVIEW_STARTED: "auto_review_started",
37+
INSIGHT_EXTRACTED: "insight_extracted",
38+
STEP_VERIFIED: "step_verified",
39+
CHECKPOINT_CREATED: "checkpoint_created",
40+
SESSION_REWOUND: "session_rewound",
41+
HOOK_BLOCKED: "hook_blocked",
42+
COMPACTION_TRIGGERED: "compaction_triggered",
43+
DOCTOR_WARNED: "doctor_warned",
44+
FIRST_SESSION_SUCCESS: "first_session_success",
45+
BACKEND_AUTO_PROMOTED: "backend_auto_promoted",
3646
} as const;
3747

3848
export type EventType = (typeof EVENT_TYPE)[keyof typeof EVENT_TYPE];
@@ -318,6 +328,200 @@ export interface ReviewDecideRequest {
318328

319329
// ── Static wire sections (maintained in scripts/generate_wire_types.py) ──────
320330

331+
// ── AgentEvent typed union (from src/kagan/core/agent_events.py) ─────────────
332+
// Discriminated on the ``kind`` field. All task session events use these
333+
// shapes.
334+
335+
export interface AgentEventAgentStart {
336+
kind: "agent_start";
337+
session_id: string;
338+
agent_backend: string;
339+
}
340+
export interface AgentEventAgentEnd {
341+
kind: "agent_end";
342+
session_id: string;
343+
stop_reason: "completed" | "error" | "aborted" | "compacted";
344+
}
345+
export interface AgentEventTurnStart {
346+
kind: "turn_start";
347+
turn_index: number;
348+
}
349+
export interface AgentEventTurnEnd {
350+
kind: "turn_end";
351+
turn_index: number;
352+
}
353+
export interface AgentEventMessageStart {
354+
kind: "message_start";
355+
message_id: string;
356+
}
357+
export interface AgentEventMessageUpdate {
358+
kind: "message_update";
359+
message_id: string;
360+
delta: string;
361+
}
362+
export interface AgentEventMessageEnd {
363+
kind: "message_end";
364+
message_id: string;
365+
full_text: string;
366+
}
367+
export interface AgentEventToolExecutionStart {
368+
kind: "tool_execution_start";
369+
tool_id: string;
370+
name: string;
371+
args?: Record<string, unknown> | null;
372+
}
373+
export interface AgentEventToolExecutionUpdate {
374+
kind: "tool_execution_update";
375+
tool_id: string;
376+
partial_result: string;
377+
}
378+
export interface AgentEventToolExecutionEnd {
379+
kind: "tool_execution_end";
380+
tool_id: string;
381+
status: "success" | "error" | "cancelled";
382+
result?: string | null;
383+
}
384+
export interface AgentEventCompactionOccurred {
385+
kind: "compaction_occurred";
386+
backend: string;
387+
threshold?: number | null;
388+
}
389+
export interface AgentEventOutputChunk {
390+
kind: "output_chunk";
391+
text: string;
392+
thought?: boolean;
393+
acp?: Record<string, unknown> | null;
394+
}
395+
export interface AgentEventAgentStatus {
396+
kind: "agent_status";
397+
usage?: Record<string, unknown> | null;
398+
acp?: Record<string, unknown> | null;
399+
}
400+
export interface AgentEventToolCallStart {
401+
kind: "tool_call_start";
402+
acp: Record<string, unknown>;
403+
}
404+
export interface AgentEventToolCallUpdate {
405+
kind: "tool_call_update";
406+
acp: Record<string, unknown>;
407+
}
408+
export interface AgentEventPlanUpdate {
409+
kind: "plan_update";
410+
acp: Record<string, unknown>;
411+
}
412+
export interface AgentEventTaskStatusChanged {
413+
kind: "task_status_changed";
414+
from_status: string;
415+
to_status: string;
416+
}
417+
export interface AgentEventAgentCompleted {
418+
kind: "agent_completed";
419+
message?: string | null;
420+
}
421+
export interface AgentEventAgentFailed {
422+
kind: "agent_failed";
423+
message?: string | null;
424+
}
425+
export interface AgentEventMergeCompleted {
426+
kind: "merge_completed";
427+
message?: string | null;
428+
}
429+
export interface AgentEventMergeFailed {
430+
kind: "merge_failed";
431+
message?: string | null;
432+
}
433+
export interface AgentEventCriterionVerdict {
434+
kind: "criterion_verdict";
435+
verdict: "pass" | "fail" | "skip";
436+
reason: string;
437+
criterion_index?: number | null;
438+
}
439+
export interface AgentEventAutoReviewStarted {
440+
kind: "auto_review_started";
441+
}
442+
export interface AgentEventInsightExtracted {
443+
kind: "insight_extracted";
444+
content: string;
445+
category?: string | null;
446+
}
447+
export interface AgentEventStepVerified {
448+
kind: "step_verified";
449+
step_index: number;
450+
step_description: string;
451+
verdict: string;
452+
reason: string;
453+
}
454+
export interface AgentEventCheckpointCreated {
455+
kind: "checkpoint_created";
456+
step_index: number;
457+
commit_sha: string;
458+
tag_name: string;
459+
description?: string | null;
460+
}
461+
export interface AgentEventSessionRewound {
462+
kind: "session_rewound";
463+
step_index: number;
464+
commit_sha: string;
465+
}
466+
export interface AgentEventHookBlocked {
467+
kind: "hook_blocked";
468+
hook: string;
469+
details?: string | null;
470+
}
471+
export interface AgentEventCompactionTriggered {
472+
kind: "compaction_triggered";
473+
backend: string;
474+
threshold?: number | null;
475+
}
476+
export interface AgentEventDoctorWarned {
477+
kind: "doctor_warned";
478+
message: string;
479+
check?: string | null;
480+
}
481+
export interface AgentEventFirstSessionSuccess {
482+
kind: "first_session_success";
483+
}
484+
export interface AgentEventBackendAutoPromoted {
485+
kind: "backend_auto_promoted";
486+
from_backend: string;
487+
to_backend: string;
488+
reason?: string | null;
489+
}
490+
491+
export type AgentEvent =
492+
| AgentEventAgentStart
493+
| AgentEventAgentEnd
494+
| AgentEventTurnStart
495+
| AgentEventTurnEnd
496+
| AgentEventMessageStart
497+
| AgentEventMessageUpdate
498+
| AgentEventMessageEnd
499+
| AgentEventToolExecutionStart
500+
| AgentEventToolExecutionUpdate
501+
| AgentEventToolExecutionEnd
502+
| AgentEventCompactionOccurred
503+
| AgentEventOutputChunk
504+
| AgentEventAgentStatus
505+
| AgentEventToolCallStart
506+
| AgentEventToolCallUpdate
507+
| AgentEventPlanUpdate
508+
| AgentEventTaskStatusChanged
509+
| AgentEventAgentCompleted
510+
| AgentEventAgentFailed
511+
| AgentEventMergeCompleted
512+
| AgentEventMergeFailed
513+
| AgentEventCriterionVerdict
514+
| AgentEventAutoReviewStarted
515+
| AgentEventInsightExtracted
516+
| AgentEventStepVerified
517+
| AgentEventCheckpointCreated
518+
| AgentEventSessionRewound
519+
| AgentEventHookBlocked
520+
| AgentEventCompactionTriggered
521+
| AgentEventDoctorWarned
522+
| AgentEventFirstSessionSuccess
523+
| AgentEventBackendAutoPromoted;
524+
321525
// ── Envelope ─────────────────────────────────────────────────────────────────
322526

323527
/**

packages/vscode/src/api/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import type {
2828
WireRepository,
2929
WireTask,
3030
WireTaskSession,
31-
} from "./types.js";
31+
} from "@kagan/shared-api-client";
3232

3333
export class ApiError extends Error {
3434
constructor(

packages/vscode/src/api/local.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* no equivalent in the shared wire surface (@kagan/shared-api-client).
66
*/
77

8-
import type { Priority, TaskStatus } from "./types.js";
8+
import type { Priority, TaskStatus } from "@kagan/shared-api-client";
99

1010
/** Launcher backends supported by the VS Code extension. */
1111
export type LauncherBackend =

packages/vscode/src/api/sse.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// "Readability counts." — async generator, no callbacks, no event bus.
33

44
import * as vscode from "vscode";
5-
import type { SSEMessage } from "./types.js";
5+
import type { SSEMessage } from "@kagan/shared-api-client";
66

77
export class SSEStream implements vscode.Disposable {
88
private controller: AbortController | null = null;

0 commit comments

Comments
 (0)