@lucascouts/claude-agent-tui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/NOTICE +14 -0
- package/README.md +50 -0
- package/dist/acp-agent.d.ts +594 -0
- package/dist/acp-agent.d.ts.map +1 -0
- package/dist/acp-agent.js +2139 -0
- package/dist/ansi-mirror.d.ts +42 -0
- package/dist/ansi-mirror.d.ts.map +1 -0
- package/dist/ansi-mirror.js +61 -0
- package/dist/besteffort.d.ts +44 -0
- package/dist/besteffort.d.ts.map +1 -0
- package/dist/besteffort.js +100 -0
- package/dist/billing/entrypoint-guard.d.ts +97 -0
- package/dist/billing/entrypoint-guard.d.ts.map +1 -0
- package/dist/billing/entrypoint-guard.js +166 -0
- package/dist/claude-path.d.ts +12 -0
- package/dist/claude-path.d.ts.map +1 -0
- package/dist/claude-path.js +61 -0
- package/dist/diff-enriched-reader.d.ts +41 -0
- package/dist/diff-enriched-reader.d.ts.map +1 -0
- package/dist/diff-enriched-reader.js +106 -0
- package/dist/diff-source.d.ts +104 -0
- package/dist/diff-source.d.ts.map +1 -0
- package/dist/diff-source.js +164 -0
- package/dist/end-of-turn.d.ts +172 -0
- package/dist/end-of-turn.d.ts.map +1 -0
- package/dist/end-of-turn.js +415 -0
- package/dist/engine-lifecycle.d.ts +222 -0
- package/dist/engine-lifecycle.d.ts.map +1 -0
- package/dist/engine-lifecycle.js +236 -0
- package/dist/engine-pty.d.ts +143 -0
- package/dist/engine-pty.d.ts.map +1 -0
- package/dist/engine-pty.js +222 -0
- package/dist/engine-watcher.d.ts +83 -0
- package/dist/engine-watcher.d.ts.map +1 -0
- package/dist/engine-watcher.js +173 -0
- package/dist/engine.d.ts +30 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +34 -0
- package/dist/event-switch.d.ts +164 -0
- package/dist/event-switch.d.ts.map +1 -0
- package/dist/event-switch.js +206 -0
- package/dist/gate/port.d.ts +38 -0
- package/dist/gate/port.d.ts.map +1 -0
- package/dist/gate/port.js +126 -0
- package/dist/gate/settings-writer.d.ts +130 -0
- package/dist/gate/settings-writer.d.ts.map +1 -0
- package/dist/gate/settings-writer.js +349 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +106 -0
- package/dist/jsonl.d.ts +267 -0
- package/dist/jsonl.d.ts.map +1 -0
- package/dist/jsonl.js +527 -0
- package/dist/lib.d.ts +6 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +5 -0
- package/dist/linearize.d.ts +219 -0
- package/dist/linearize.d.ts.map +1 -0
- package/dist/linearize.js +444 -0
- package/dist/live-diff-env.d.ts +7 -0
- package/dist/live-diff-env.d.ts.map +1 -0
- package/dist/live-diff-env.js +18 -0
- package/dist/live-subagent-env.d.ts +7 -0
- package/dist/live-subagent-env.d.ts.map +1 -0
- package/dist/live-subagent-env.js +19 -0
- package/dist/permissions/allow-inject.d.ts +67 -0
- package/dist/permissions/allow-inject.d.ts.map +1 -0
- package/dist/permissions/allow-inject.js +85 -0
- package/dist/permissions/deny.d.ts +60 -0
- package/dist/permissions/deny.d.ts.map +1 -0
- package/dist/permissions/deny.js +81 -0
- package/dist/permissions/gate-wiring.d.ts +112 -0
- package/dist/permissions/gate-wiring.d.ts.map +1 -0
- package/dist/permissions/gate-wiring.js +350 -0
- package/dist/permissions/hook-server.d.ts +72 -0
- package/dist/permissions/hook-server.d.ts.map +1 -0
- package/dist/permissions/hook-server.js +179 -0
- package/dist/permissions/permission-mode.d.ts +67 -0
- package/dist/permissions/permission-mode.d.ts.map +1 -0
- package/dist/permissions/permission-mode.js +100 -0
- package/dist/permissions/request-permission.d.ts +102 -0
- package/dist/permissions/request-permission.d.ts.map +1 -0
- package/dist/permissions/request-permission.js +124 -0
- package/dist/settings.d.ts +68 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +182 -0
- package/dist/stop-reason-map.d.ts +17 -0
- package/dist/stop-reason-map.d.ts.map +1 -0
- package/dist/stop-reason-map.js +33 -0
- package/dist/subagent-source.d.ts +63 -0
- package/dist/subagent-source.d.ts.map +1 -0
- package/dist/subagent-source.js +132 -0
- package/dist/subagent-watcher.d.ts +40 -0
- package/dist/subagent-watcher.d.ts.map +1 -0
- package/dist/subagent-watcher.js +108 -0
- package/dist/tools.d.ts +119 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +729 -0
- package/dist/usage-env.d.ts +7 -0
- package/dist/usage-env.d.ts.map +1 -0
- package/dist/usage-env.js +16 -0
- package/dist/usage.d.ts +54 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +53 -0
- package/dist/utils.d.ts +16 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +83 -0
- package/dist/zed-register.d.ts +26 -0
- package/dist/zed-register.d.ts.map +1 -0
- package/dist/zed-register.js +106 -0
- package/package.json +79 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { SessionMessage } from "./engine-watcher.js";
|
|
2
|
+
/**
|
|
3
|
+
* Injectable seam matching the SDK `listSubagents` signature: returns the agentIds spawned in this
|
|
4
|
+
* session. Async. Defaults to {@link defaultListSubagents}.
|
|
5
|
+
*/
|
|
6
|
+
export type ListSubagents = (sessionId: string, opts?: {
|
|
7
|
+
dir?: string;
|
|
8
|
+
}) => Promise<string[]>;
|
|
9
|
+
/**
|
|
10
|
+
* Injectable seam matching the SDK `getSubagentMessages` signature: returns one agent's sidechain
|
|
11
|
+
* messages in the reduced {@link SessionMessage} shape. Async. Defaults to
|
|
12
|
+
* {@link defaultGetSubagentMessages}.
|
|
13
|
+
*/
|
|
14
|
+
export type GetSubagentMessages = (sessionId: string, agentId: string, opts?: {
|
|
15
|
+
dir?: string;
|
|
16
|
+
}) => Promise<SessionMessage[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Default {@link ListSubagents}: the SDK's pure `listSubagents`. Imported lazily (dynamic import) so
|
|
19
|
+
* the module never force-loads the SDK at eval time. Mirrors linearize.ts `defaultGetMessages`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const defaultListSubagents: ListSubagents;
|
|
22
|
+
/**
|
|
23
|
+
* Default {@link GetSubagentMessages}: the SDK's pure `getSubagentMessages`. Imported lazily (dynamic
|
|
24
|
+
* import) so the module never force-loads the SDK at eval time. Mirrors linearize.ts
|
|
25
|
+
* `defaultGetMessages`.
|
|
26
|
+
*/
|
|
27
|
+
export declare const defaultGetSubagentMessages: GetSubagentMessages;
|
|
28
|
+
/**
|
|
29
|
+
* True when ANY message in `mainChain` carries a `Task`/`Agent` `tool_use` block — the only signal
|
|
30
|
+
* that a sidechain may exist (and thus the only justification for paying the `listSubagents` probe).
|
|
31
|
+
* Scans each message's `message.content` array for a block with `type === "tool_use"` whose `name`
|
|
32
|
+
* is `"Task"` or `"Agent"`. Tolerant of the reduced shape: missing/odd `content` is simply skipped.
|
|
33
|
+
* Story 044 exports it for the watcher/pump arm decision (reuse over re-implementation).
|
|
34
|
+
*/
|
|
35
|
+
export declare function hasSubagentSpawn(mainChain: SessionMessage[]): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* True iff at least one `Task`/`Agent` `tool_use` id in `mainChain` has NO matching `tool_result`
|
|
38
|
+
* block (`tool_use_id === id`) anywhere in the same chain; false when every spawn id is closed or
|
|
39
|
+
* no spawn exists. Story 044 / R2.2 — the pump's teardown gate: the sub-agent watcher stays armed
|
|
40
|
+
* only while a spawn is open. Single tolerant pass in the {@link hasSubagentSpawn} shape-reading
|
|
41
|
+
* style (skip non-object rows/messages/non-array content); `tool_result` blocks are read off ANY
|
|
42
|
+
* row type, mirroring the end-of-turn detector's `closedToolUseIds`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function spawnIdsOpen(mainChain: SessionMessage[]): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Source the merged subagent (sidechain) rows for a session, GUARDED by a main-chain `Task`/`Agent`
|
|
47
|
+
* `tool_use` probe (R1.2, R5.2).
|
|
48
|
+
*
|
|
49
|
+
* - If the main chain carries no `Task`/`Agent` `tool_use`, return `[]` WITHOUT calling
|
|
50
|
+
* `listSubagents` — the R5.2 cheap path (no disk probe on the common no-subagent turn).
|
|
51
|
+
* - Otherwise call `listSubagents(sessionId, { dir })` for the agentIds, then
|
|
52
|
+
* `getSubagentMessages(sessionId, agentId, { dir })` for each, and return the concatenation of all
|
|
53
|
+
* rows in agentId order.
|
|
54
|
+
*
|
|
55
|
+
* Both readers are injected via `opts` (the lazy defaults are {@link defaultListSubagents} /
|
|
56
|
+
* {@link defaultGetSubagentMessages}); the deterministic unit tests pass stubs/spies.
|
|
57
|
+
*/
|
|
58
|
+
export declare function sourceSubagentRows(sessionId: string, mainChain: SessionMessage[], opts: {
|
|
59
|
+
dir?: string;
|
|
60
|
+
listSubagents: ListSubagents;
|
|
61
|
+
getSubagentMessages: GetSubagentMessages;
|
|
62
|
+
}): Promise<SessionMessage[]>;
|
|
63
|
+
//# sourceMappingURL=subagent-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent-source.d.ts","sourceRoot":"","sources":["../src/subagent-source.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAK1D;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AAE9F;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,KACpB,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;AAE/B;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,aAGlC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,mBAGxC,CAAC;AAOF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,OAAO,CAmBrE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,OAAO,CA2BjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,cAAc,EAAE,EAC3B,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,aAAa,CAAC;IAAC,mBAAmB,EAAE,mBAAmB,CAAA;CAAE,GAC7F,OAAO,CAAC,cAAc,EAAE,CAAC,CAa3B"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// === Story 041 / §sidechain LIVE: guarded subagent-row sourcing (task 1.1) ====================
|
|
2
|
+
//
|
|
3
|
+
// Renders nested subagents (the `Task`/`Agent` tool spawns a sidechain conversation) at the LIVE
|
|
4
|
+
// read path. The SDK exposes two pure, billing-free readers for that sidechain:
|
|
5
|
+
// - `listSubagents(sessionId, { dir })` → the agentIds spawned in this session
|
|
6
|
+
// - `getSubagentMessages(sessionId, id, { dir })` → that agent's reduced-shape SessionMessage[]
|
|
7
|
+
//
|
|
8
|
+
// R5.2 CHEAP PATH: `listSubagents` touches disk (it scans the session's subagent transcripts). When
|
|
9
|
+
// the main chain carries NO `Task`/`Agent` `tool_use` block there can be no sidechain, so we MUST
|
|
10
|
+
// skip `listSubagents` entirely and return `[]` — never pay the probe for the common no-subagent
|
|
11
|
+
// turn. This guard is the contract pinned by the unit test (a spy asserts 0 invocations).
|
|
12
|
+
//
|
|
13
|
+
// Both readers are LAZY-imported defaults (dynamic import at call time, mirroring linearize.ts
|
|
14
|
+
// `defaultGetMessages`) so this module — and the deterministic unit tests, which always inject
|
|
15
|
+
// stubs — never force-load the SDK at module-eval time. The two seams are fully injectable.
|
|
16
|
+
/** The `tool_use` block `name`s whose presence in the main chain proves a sidechain may exist. */
|
|
17
|
+
const SUBAGENT_TOOL_NAMES = new Set(["Task", "Agent"]);
|
|
18
|
+
/**
|
|
19
|
+
* Default {@link ListSubagents}: the SDK's pure `listSubagents`. Imported lazily (dynamic import) so
|
|
20
|
+
* the module never force-loads the SDK at eval time. Mirrors linearize.ts `defaultGetMessages`.
|
|
21
|
+
*/
|
|
22
|
+
export const defaultListSubagents = async (sessionId, opts) => {
|
|
23
|
+
const sdk = await import("@anthropic-ai/claude-agent-sdk");
|
|
24
|
+
return sdk.listSubagents(sessionId, opts);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Default {@link GetSubagentMessages}: the SDK's pure `getSubagentMessages`. Imported lazily (dynamic
|
|
28
|
+
* import) so the module never force-loads the SDK at eval time. Mirrors linearize.ts
|
|
29
|
+
* `defaultGetMessages`.
|
|
30
|
+
*/
|
|
31
|
+
export const defaultGetSubagentMessages = async (sessionId, agentId, opts) => {
|
|
32
|
+
const sdk = await import("@anthropic-ai/claude-agent-sdk");
|
|
33
|
+
return sdk.getSubagentMessages(sessionId, agentId, opts);
|
|
34
|
+
};
|
|
35
|
+
/** Narrowing helper: a non-null plain object (so we can read string-keyed props safely). */
|
|
36
|
+
function isObject(value) {
|
|
37
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* True when ANY message in `mainChain` carries a `Task`/`Agent` `tool_use` block — the only signal
|
|
41
|
+
* that a sidechain may exist (and thus the only justification for paying the `listSubagents` probe).
|
|
42
|
+
* Scans each message's `message.content` array for a block with `type === "tool_use"` whose `name`
|
|
43
|
+
* is `"Task"` or `"Agent"`. Tolerant of the reduced shape: missing/odd `content` is simply skipped.
|
|
44
|
+
* Story 044 exports it for the watcher/pump arm decision (reuse over re-implementation).
|
|
45
|
+
*/
|
|
46
|
+
export function hasSubagentSpawn(mainChain) {
|
|
47
|
+
for (const msg of mainChain) {
|
|
48
|
+
if (!isObject(msg))
|
|
49
|
+
continue;
|
|
50
|
+
const inner = msg.message;
|
|
51
|
+
if (!isObject(inner))
|
|
52
|
+
continue;
|
|
53
|
+
const content = inner.content;
|
|
54
|
+
if (!Array.isArray(content))
|
|
55
|
+
continue;
|
|
56
|
+
for (const block of content) {
|
|
57
|
+
if (isObject(block) &&
|
|
58
|
+
block.type === "tool_use" &&
|
|
59
|
+
typeof block.name === "string" &&
|
|
60
|
+
SUBAGENT_TOOL_NAMES.has(block.name)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* True iff at least one `Task`/`Agent` `tool_use` id in `mainChain` has NO matching `tool_result`
|
|
69
|
+
* block (`tool_use_id === id`) anywhere in the same chain; false when every spawn id is closed or
|
|
70
|
+
* no spawn exists. Story 044 / R2.2 — the pump's teardown gate: the sub-agent watcher stays armed
|
|
71
|
+
* only while a spawn is open. Single tolerant pass in the {@link hasSubagentSpawn} shape-reading
|
|
72
|
+
* style (skip non-object rows/messages/non-array content); `tool_result` blocks are read off ANY
|
|
73
|
+
* row type, mirroring the end-of-turn detector's `closedToolUseIds`.
|
|
74
|
+
*/
|
|
75
|
+
export function spawnIdsOpen(mainChain) {
|
|
76
|
+
const spawnIds = new Set();
|
|
77
|
+
const closedIds = new Set();
|
|
78
|
+
for (const msg of mainChain) {
|
|
79
|
+
if (!isObject(msg))
|
|
80
|
+
continue;
|
|
81
|
+
const inner = msg.message;
|
|
82
|
+
if (!isObject(inner))
|
|
83
|
+
continue;
|
|
84
|
+
const content = inner.content;
|
|
85
|
+
if (!Array.isArray(content))
|
|
86
|
+
continue;
|
|
87
|
+
for (const block of content) {
|
|
88
|
+
if (!isObject(block))
|
|
89
|
+
continue;
|
|
90
|
+
if (block.type === "tool_use" &&
|
|
91
|
+
typeof block.id === "string" &&
|
|
92
|
+
typeof block.name === "string" &&
|
|
93
|
+
SUBAGENT_TOOL_NAMES.has(block.name)) {
|
|
94
|
+
spawnIds.add(block.id);
|
|
95
|
+
}
|
|
96
|
+
else if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
|
|
97
|
+
closedIds.add(block.tool_use_id);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const id of spawnIds) {
|
|
102
|
+
if (!closedIds.has(id))
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Source the merged subagent (sidechain) rows for a session, GUARDED by a main-chain `Task`/`Agent`
|
|
109
|
+
* `tool_use` probe (R1.2, R5.2).
|
|
110
|
+
*
|
|
111
|
+
* - If the main chain carries no `Task`/`Agent` `tool_use`, return `[]` WITHOUT calling
|
|
112
|
+
* `listSubagents` — the R5.2 cheap path (no disk probe on the common no-subagent turn).
|
|
113
|
+
* - Otherwise call `listSubagents(sessionId, { dir })` for the agentIds, then
|
|
114
|
+
* `getSubagentMessages(sessionId, agentId, { dir })` for each, and return the concatenation of all
|
|
115
|
+
* rows in agentId order.
|
|
116
|
+
*
|
|
117
|
+
* Both readers are injected via `opts` (the lazy defaults are {@link defaultListSubagents} /
|
|
118
|
+
* {@link defaultGetSubagentMessages}); the deterministic unit tests pass stubs/spies.
|
|
119
|
+
*/
|
|
120
|
+
export async function sourceSubagentRows(sessionId, mainChain, opts) {
|
|
121
|
+
// R5.2 cheap path: no main-chain Task/Agent → no sidechain possible → skip the disk probe.
|
|
122
|
+
if (!hasSubagentSpawn(mainChain))
|
|
123
|
+
return [];
|
|
124
|
+
const { dir, listSubagents, getSubagentMessages } = opts;
|
|
125
|
+
const agentIds = await listSubagents(sessionId, { dir });
|
|
126
|
+
const rows = [];
|
|
127
|
+
for (const agentId of agentIds) {
|
|
128
|
+
const agentRows = await getSubagentMessages(sessionId, agentId, { dir });
|
|
129
|
+
rows.push(...agentRows);
|
|
130
|
+
}
|
|
131
|
+
return rows;
|
|
132
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SessionMessage } from "./engine-watcher.js";
|
|
2
|
+
import { type ListSubagents, type GetSubagentMessages } from "./subagent-source.js";
|
|
3
|
+
/** Default poll interval (ms) between sub-agent reader probes. */
|
|
4
|
+
export declare const SUBAGENT_POLL_MS = 1000;
|
|
5
|
+
export interface SubagentWatcherOptions {
|
|
6
|
+
sessionId: string;
|
|
7
|
+
/** session.cwd — passed to the readers as `{ dir }`. */
|
|
8
|
+
dir?: string;
|
|
9
|
+
/** Latest main chain (for the hasSubagentSpawn guard); refreshed by the pump via update(). */
|
|
10
|
+
mainChain: SessionMessage[];
|
|
11
|
+
listSubagents: ListSubagents;
|
|
12
|
+
getSubagentMessages: GetSubagentMessages;
|
|
13
|
+
/** Fired once per poll that observed >=1 NEW sub-agent row (new uuid). Never on an empty/no-change poll. */
|
|
14
|
+
onActivity: () => void | Promise<void>;
|
|
15
|
+
/** Injected for tests; defaults to a setTimeout/clearTimeout-backed one-shot scheduler. */
|
|
16
|
+
schedule?: (fn: () => void, ms: number) => () => void;
|
|
17
|
+
/** Poll interval (ms); default {@link SUBAGENT_POLL_MS}. */
|
|
18
|
+
pollMs?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface SubagentWatcher {
|
|
21
|
+
/** Refresh the cached main chain so the spawn guard tracks the current turn. */
|
|
22
|
+
update(mainChain: SessionMessage[]): void;
|
|
23
|
+
/** Stop the poll, drop the seen-uuid set; idempotent. */
|
|
24
|
+
stop(): void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a sub-agent activity watcher: poll the story-041 readers every `pollMs` while armed, fire
|
|
28
|
+
* `onActivity` only on observed progress (a new-uuid row), stop cleanly.
|
|
29
|
+
*
|
|
30
|
+
* SCHEDULING: `schedule` is ONE-SHOT — the same `DetectorSchedule` semantics as end-of-turn.ts
|
|
31
|
+
* (schedule(fn, ms) fires fn once after ms, returns a cancel) — and the watcher CHAINS one-shots:
|
|
32
|
+
* the first tick is armed at construction (`pollMs` from now); after each async poll body completes
|
|
33
|
+
* the next tick is re-armed, unless stopped. Why one-shot chaining instead of setInterval:
|
|
34
|
+
* (a) one shared semantics with the rest of the fork's timer seams, so the SAME fake clock
|
|
35
|
+
* drives detector + watcher in tests;
|
|
36
|
+
* (b) re-arming AFTER the poll completes prevents overlapping polls when a read outlasts the
|
|
37
|
+
* interval (setInterval would overlap).
|
|
38
|
+
*/
|
|
39
|
+
export declare function createSubagentWatcher(opts: SubagentWatcherOptions): SubagentWatcher;
|
|
40
|
+
//# sourceMappingURL=subagent-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent-watcher.d.ts","sourceRoot":"","sources":["../src/subagent-watcher.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,sBAAsB,CAAC;AAE9B,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,mBAAmB,EAAE,mBAAmB,CAAC;IACzC,4GAA4G;IAC5G,UAAU,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,IAAI,CAAC;IACtD,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,MAAM,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAC1C,yDAAyD;IACzD,IAAI,IAAI,IAAI,CAAC;CACd;AAYD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,sBAAsB,GAAG,eAAe,CAwEnF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// === Story 044 / Option B: poll-based sub-agent activity watcher (task 3.2) ======================
|
|
2
|
+
//
|
|
3
|
+
// While a `Task`/`Agent` spawn is in flight on the main chain, the turn's real progress happens on
|
|
4
|
+
// the SIDECHAIN (`subagents/*.jsonl`) — which the story-023 tail does not observe, so a long
|
|
5
|
+
// sub-agent looks like a stall and trips the turn watchdog (the story-041 R4.2 live gap). This
|
|
6
|
+
// watcher feeds that missing liveness: it POLLS the story-041 SDK readers (`listSubagents` +
|
|
7
|
+
// `getSubagentMessages`, via `sourceSubagentRows`) instead of tailing the raw `subagents/*.jsonl`
|
|
8
|
+
// files. Design rationale (Option B as planned):
|
|
9
|
+
// - reuses the 041 data path verbatim — same readers, same reduced shape, same R5.2 guard;
|
|
10
|
+
// - dissolves the class-028 late-discovery gap: before the sidechain materializes the readers
|
|
11
|
+
// simply return `[]`, so polling is harmless and needs no fs.watch arming dance (R2.1);
|
|
12
|
+
// - keeps engine-watcher.ts frozen — no second tail, no new fs seam.
|
|
13
|
+
//
|
|
14
|
+
// Liveness is PROGRESS-GATED (R5.2): `onActivity` fires at most once per poll, and only when the
|
|
15
|
+
// poll observed at least one NEW row (a `uuid` not seen before). An empty poll, a no-change poll,
|
|
16
|
+
// or a reader error fires NOTHING — a hung sub-agent must still let the stall watchdog trip.
|
|
17
|
+
import { hasSubagentSpawn, sourceSubagentRows, } from "./subagent-source.js";
|
|
18
|
+
/** Default poll interval (ms) between sub-agent reader probes. */
|
|
19
|
+
export const SUBAGENT_POLL_MS = 1000;
|
|
20
|
+
/**
|
|
21
|
+
* Default one-shot scheduler: `setTimeout`/`clearTimeout` with `.unref?.()` so an armed watcher
|
|
22
|
+
* never keeps the process alive on its own (mirrors `defaultSchedule` in end-of-turn.ts).
|
|
23
|
+
*/
|
|
24
|
+
const defaultSchedule = (fn, ms) => {
|
|
25
|
+
const handle = setTimeout(fn, ms);
|
|
26
|
+
handle.unref?.();
|
|
27
|
+
return () => clearTimeout(handle);
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Create a sub-agent activity watcher: poll the story-041 readers every `pollMs` while armed, fire
|
|
31
|
+
* `onActivity` only on observed progress (a new-uuid row), stop cleanly.
|
|
32
|
+
*
|
|
33
|
+
* SCHEDULING: `schedule` is ONE-SHOT — the same `DetectorSchedule` semantics as end-of-turn.ts
|
|
34
|
+
* (schedule(fn, ms) fires fn once after ms, returns a cancel) — and the watcher CHAINS one-shots:
|
|
35
|
+
* the first tick is armed at construction (`pollMs` from now); after each async poll body completes
|
|
36
|
+
* the next tick is re-armed, unless stopped. Why one-shot chaining instead of setInterval:
|
|
37
|
+
* (a) one shared semantics with the rest of the fork's timer seams, so the SAME fake clock
|
|
38
|
+
* drives detector + watcher in tests;
|
|
39
|
+
* (b) re-arming AFTER the poll completes prevents overlapping polls when a read outlasts the
|
|
40
|
+
* interval (setInterval would overlap).
|
|
41
|
+
*/
|
|
42
|
+
export function createSubagentWatcher(opts) {
|
|
43
|
+
const { sessionId, dir, listSubagents, getSubagentMessages, onActivity, schedule = defaultSchedule, pollMs = SUBAGENT_POLL_MS, } = opts;
|
|
44
|
+
let mainChain = opts.mainChain;
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
let stopped = false;
|
|
47
|
+
/** Cancel for the currently pending one-shot, if any (cleared when it fires). */
|
|
48
|
+
let cancelPending;
|
|
49
|
+
/** Arm the next one-shot tick — never after stop(). */
|
|
50
|
+
const arm = () => {
|
|
51
|
+
if (stopped)
|
|
52
|
+
return;
|
|
53
|
+
cancelPending = schedule(() => {
|
|
54
|
+
cancelPending = undefined;
|
|
55
|
+
void poll();
|
|
56
|
+
}, pollMs);
|
|
57
|
+
};
|
|
58
|
+
/** One poll body; always re-arms afterwards unless stopped (errors included — next poll retries). */
|
|
59
|
+
const poll = async () => {
|
|
60
|
+
try {
|
|
61
|
+
// R5.2 cheap path: no main-chain Task/Agent spawn → no sidechain possible → skip the
|
|
62
|
+
// readers entirely (zero sub-agent IO on the common no-subagent turn).
|
|
63
|
+
if (hasSubagentSpawn(mainChain)) {
|
|
64
|
+
const rows = await sourceSubagentRows(sessionId, mainChain, {
|
|
65
|
+
dir,
|
|
66
|
+
listSubagents,
|
|
67
|
+
getSubagentMessages,
|
|
68
|
+
});
|
|
69
|
+
// Progress = rows with a NEW non-empty-string uuid. A uuid-less row is skipped — it never
|
|
70
|
+
// counts as progress and never crashes the poll.
|
|
71
|
+
const fresh = [];
|
|
72
|
+
for (const row of rows) {
|
|
73
|
+
const uuid = row.uuid;
|
|
74
|
+
if (typeof uuid !== "string" || uuid === "")
|
|
75
|
+
continue;
|
|
76
|
+
if (!seen.has(uuid))
|
|
77
|
+
fresh.push(uuid);
|
|
78
|
+
}
|
|
79
|
+
if (fresh.length > 0 && !stopped) {
|
|
80
|
+
for (const uuid of fresh)
|
|
81
|
+
seen.add(uuid);
|
|
82
|
+
await onActivity();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// A reader rejection (transient FS/SDK error) must not kill the chain: swallow and re-arm —
|
|
88
|
+
// the NEXT poll retries. Errors never fire onActivity (no liveness from failure).
|
|
89
|
+
}
|
|
90
|
+
if (!stopped)
|
|
91
|
+
arm();
|
|
92
|
+
};
|
|
93
|
+
// Arm the first tick at construction: the first probe lands `pollMs` from now.
|
|
94
|
+
arm();
|
|
95
|
+
return {
|
|
96
|
+
update(next) {
|
|
97
|
+
mainChain = next;
|
|
98
|
+
},
|
|
99
|
+
stop() {
|
|
100
|
+
if (stopped)
|
|
101
|
+
return;
|
|
102
|
+
stopped = true;
|
|
103
|
+
cancelPending?.();
|
|
104
|
+
cancelPending = undefined;
|
|
105
|
+
seen.clear();
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { PlanEntry, ToolCallContent, ToolCallLocation, ToolKind } from "@agentclientprotocol/sdk";
|
|
2
|
+
import { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
3
|
+
import { TaskCreateInput, TaskCreateOutput, TaskUpdateInput } from "@anthropic-ai/claude-agent-sdk/sdk-tools.js";
|
|
4
|
+
import { ToolResultBlockParam, WebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources";
|
|
5
|
+
import { BetaBashCodeExecutionToolResultBlockParam, BetaCodeExecutionToolResultBlockParam, BetaRequestMCPToolResultBlockParam, BetaTextEditorCodeExecutionToolResultBlockParam, BetaToolResultBlockParam, BetaToolSearchToolResultBlockParam, BetaWebFetchToolResultBlockParam, BetaWebSearchToolResultBlockParam } from "@anthropic-ai/sdk/resources/beta.mjs";
|
|
6
|
+
import { Logger } from "./acp-agent.js";
|
|
7
|
+
interface ToolInfo {
|
|
8
|
+
title: string;
|
|
9
|
+
kind: ToolKind;
|
|
10
|
+
content: ToolCallContent[];
|
|
11
|
+
locations?: ToolCallLocation[];
|
|
12
|
+
}
|
|
13
|
+
interface ToolUpdate {
|
|
14
|
+
title?: string;
|
|
15
|
+
content?: ToolCallContent[];
|
|
16
|
+
locations?: ToolCallLocation[];
|
|
17
|
+
_meta?: {
|
|
18
|
+
terminal_info?: {
|
|
19
|
+
terminal_id: string;
|
|
20
|
+
};
|
|
21
|
+
terminal_output?: {
|
|
22
|
+
terminal_id: string;
|
|
23
|
+
data: string;
|
|
24
|
+
};
|
|
25
|
+
terminal_exit?: {
|
|
26
|
+
terminal_id: string;
|
|
27
|
+
exit_code: number;
|
|
28
|
+
signal: string | null;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert an absolute file path to a project-relative path for display.
|
|
34
|
+
* Returns the original path if it's outside the project directory or if no cwd is provided.
|
|
35
|
+
*/
|
|
36
|
+
export declare function toDisplayPath(filePath: string, cwd?: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Story 025 / Task 2.2 (R2.1, R2.3) — derive the ACP `toolCallId` from a JSONL
|
|
39
|
+
* `tool_use.id`. Pure, total and side-effect-free, and STABLE across the live and
|
|
40
|
+
* replay paths so a loaded thread re-derives identical ids.
|
|
41
|
+
*
|
|
42
|
+
* Default (`namespaced` omitted/false): returns the raw `tool_use.id` VERBATIM — the
|
|
43
|
+
* §7 reuse contract stories 017-021 assume, and the behaviour the live-Zed probe
|
|
44
|
+
* (Task 2.1, FORK.md) confirms or overturns. When the probe shows the user's Zed
|
|
45
|
+
* requires per-session uniqueness, callers pass `{ namespaced: true }` to get a
|
|
46
|
+
* deterministic `${sessionId}:${toolUseId}` id — identical for the same
|
|
47
|
+
* (toolUseId, sessionId) and distinct across sessions. The raw id stays embedded so
|
|
48
|
+
* the original `tool_use.id` remains recoverable.
|
|
49
|
+
*/
|
|
50
|
+
export declare function toolCallIdFor(toolUseId: string, sessionId: string, options?: {
|
|
51
|
+
namespaced?: boolean;
|
|
52
|
+
}): string;
|
|
53
|
+
export declare function toolInfoFromToolUse(toolUse: any, supportsTerminalOutput?: boolean, cwd?: string): ToolInfo;
|
|
54
|
+
export declare function toolUpdateFromToolResult(toolResult: ToolResultBlockParam | BetaToolResultBlockParam | BetaWebSearchToolResultBlockParam | BetaWebFetchToolResultBlockParam | WebSearchToolResultBlockParam | BetaCodeExecutionToolResultBlockParam | BetaBashCodeExecutionToolResultBlockParam | BetaTextEditorCodeExecutionToolResultBlockParam | BetaRequestMCPToolResultBlockParam | BetaToolSearchToolResultBlockParam, toolUse: any | undefined, supportsTerminalOutput?: boolean): ToolUpdate;
|
|
55
|
+
export type ClaudePlanEntry = {
|
|
56
|
+
content: string;
|
|
57
|
+
status: "pending" | "in_progress" | "completed";
|
|
58
|
+
activeForm: string;
|
|
59
|
+
};
|
|
60
|
+
export declare function planEntries(input: {
|
|
61
|
+
todos: ClaudePlanEntry[];
|
|
62
|
+
} | undefined): PlanEntry[];
|
|
63
|
+
/**
|
|
64
|
+
* Per-session task list accumulated from Task* tool calls (TaskCreate /
|
|
65
|
+
* TaskUpdate). The headless/SDK session emits these as incremental tool
|
|
66
|
+
* calls keyed by task ID, replacing the snapshot-style TodoWrite tool.
|
|
67
|
+
* Iteration order is insertion order (Map semantics), matching the order
|
|
68
|
+
* tasks are created.
|
|
69
|
+
*/
|
|
70
|
+
export type TaskEntry = {
|
|
71
|
+
subject: string;
|
|
72
|
+
status: "pending" | "in_progress" | "completed";
|
|
73
|
+
activeForm?: string;
|
|
74
|
+
description?: string;
|
|
75
|
+
};
|
|
76
|
+
export type TaskState = Map<string, TaskEntry>;
|
|
77
|
+
/**
|
|
78
|
+
* Best-effort parse of a TaskCreate tool_result content into the structured
|
|
79
|
+
* TaskCreateOutput. The SDK delivers tool outputs either as a string or as
|
|
80
|
+
* an array of TextBlockParam-like blocks containing JSON text; try both.
|
|
81
|
+
*/
|
|
82
|
+
export declare function parseTaskCreateOutput(content: unknown): TaskCreateOutput | undefined;
|
|
83
|
+
export declare function applyTaskCreate(state: TaskState, input: TaskCreateInput | undefined, output: TaskCreateOutput | undefined): void;
|
|
84
|
+
export declare function applyTaskUpdate(state: TaskState, input: TaskUpdateInput | undefined): void;
|
|
85
|
+
export declare function taskStateToPlanEntries(state: TaskState): PlanEntry[];
|
|
86
|
+
export declare function markdownEscape(text: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Builds diff ToolUpdate content from the structured toolResponse provided by
|
|
89
|
+
* the PostToolUse hook for diff-producing tools (Edit, Write). Unlike parsing
|
|
90
|
+
* the plain unified diff string, this uses the pre-parsed structuredPatch
|
|
91
|
+
* which supports multiple replacement sites (replaceAll) and always includes
|
|
92
|
+
* context lines for better readability.
|
|
93
|
+
*/
|
|
94
|
+
export declare function toolUpdateFromDiffToolResponse(toolResponse: unknown): {
|
|
95
|
+
content?: ToolCallContent[];
|
|
96
|
+
locations?: ToolCallLocation[];
|
|
97
|
+
};
|
|
98
|
+
export declare const registerHookCallback: (toolUseID: string, { onPostToolUseHook, }: {
|
|
99
|
+
onPostToolUseHook?: (toolUseID: string, toolInput: unknown, toolResponse: unknown) => Promise<void>;
|
|
100
|
+
}) => void;
|
|
101
|
+
export declare const createPostToolUseHook: (logger?: Logger, options?: {
|
|
102
|
+
onEnterPlanMode?: () => Promise<void>;
|
|
103
|
+
}) => HookCallback;
|
|
104
|
+
/**
|
|
105
|
+
* Hook callback for `TaskCreated` / `TaskCompleted` events. The SDK fires
|
|
106
|
+
* these for both user-facing TaskCreate tool calls and subagent task
|
|
107
|
+
* creation, giving us `task_id` + `task_subject` without having to parse
|
|
108
|
+
* tool_result payloads.
|
|
109
|
+
*
|
|
110
|
+
* Populating `taskState` from the hook means a later `TaskUpdate` (which
|
|
111
|
+
* typically only carries `taskId` + `status`) finds an existing entry with
|
|
112
|
+
* a real subject, instead of synthesizing a placeholder with empty content.
|
|
113
|
+
*/
|
|
114
|
+
export declare const createTaskHook: (options: {
|
|
115
|
+
taskState: TaskState;
|
|
116
|
+
onChange?: () => Promise<void>;
|
|
117
|
+
}) => HookCallback;
|
|
118
|
+
export {};
|
|
119
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|