@lucascouts/claude-agent-acp-plus 0.33.0 → 0.34.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/README.md +32 -2
- package/dist/acp-agent.d.ts +39 -0
- package/dist/acp-agent.d.ts.map +1 -1
- package/dist/acp-agent.js +352 -16
- package/dist/acp-subagents.d.ts +20 -0
- package/dist/acp-subagents.d.ts.map +1 -0
- package/dist/acp-subagents.js +14 -0
- package/dist/child-session-history.d.ts +26 -0
- package/dist/child-session-history.d.ts.map +1 -0
- package/dist/child-session-history.js +42 -0
- package/dist/child-sessions.d.ts +95 -0
- package/dist/child-sessions.d.ts.map +1 -0
- package/dist/child-sessions.js +247 -0
- package/dist/task-feed.d.ts +3 -0
- package/dist/task-feed.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { findTranscript } from "./transcript-path.js";
|
|
4
|
+
const META_FILE = /^agent-(.+)\.meta\.json$/;
|
|
5
|
+
/** The subagents of `sessionId`, keyed by the tool use id that launched each. */
|
|
6
|
+
export async function readSubagentLaunches(configDir, sessionId) {
|
|
7
|
+
const launches = new Map();
|
|
8
|
+
const transcript = await findTranscript(configDir, sessionId);
|
|
9
|
+
if (!transcript)
|
|
10
|
+
return launches;
|
|
11
|
+
const directory = path.join(path.dirname(transcript), sessionId, "subagents");
|
|
12
|
+
let names;
|
|
13
|
+
try {
|
|
14
|
+
names = await readdir(directory);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return launches;
|
|
18
|
+
}
|
|
19
|
+
const text = (value) => (typeof value === "string" && value ? value : undefined);
|
|
20
|
+
await Promise.all(names.map(async (name) => {
|
|
21
|
+
const agentId = name.match(META_FILE)?.[1];
|
|
22
|
+
if (!agentId)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
const meta = JSON.parse(await readFile(path.join(directory, name), "utf8"));
|
|
26
|
+
if (typeof meta?.toolUseId !== "string")
|
|
27
|
+
return;
|
|
28
|
+
launches.set(meta.toolUseId, {
|
|
29
|
+
agentId,
|
|
30
|
+
name: text(meta.name),
|
|
31
|
+
agentType: text(meta.agentType),
|
|
32
|
+
description: text(meta.description),
|
|
33
|
+
launcherAgentId: text(meta.parentAgentId),
|
|
34
|
+
depth: typeof meta.spawnDepth === "number" ? meta.spawnDepth : 1,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// A damaged metadata file leaves its subagent nested, as without the RFD.
|
|
39
|
+
}
|
|
40
|
+
}));
|
|
41
|
+
return launches;
|
|
42
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { SessionNotification } from "@agentclientprotocol/sdk/unstable";
|
|
2
|
+
/**
|
|
3
|
+
* Story 065 — each Agent-tool subagent as a child session under the ACP
|
|
4
|
+
* subagents RFD (agentclientprotocol/agent-client-protocol#1992).
|
|
5
|
+
*
|
|
6
|
+
* The fork's tool-call conversion is unchanged: a subagent's updates still
|
|
7
|
+
* leave it stamped with `_meta.claudeCode.parentToolUseId`, and `route` only
|
|
8
|
+
* swaps their `sessionId` for the child's. The child's id is the SDK agent id
|
|
9
|
+
* (`task_started.task_id`, the name of its transcript
|
|
10
|
+
* `subagents/agent-<id>.jsonl`), never a fresh one, so a replay or a restore
|
|
11
|
+
* names the same child (design.md, Data Models).
|
|
12
|
+
*
|
|
13
|
+
* Zed `a5c4ca8b`'s v1 client has no early-traffic buffer: a child's traffic
|
|
14
|
+
* before its `subagent_update` is dropped, so `launch` is awaited before the
|
|
15
|
+
* stream that follows `task_started` is converted.
|
|
16
|
+
*/
|
|
17
|
+
/** Why a child stopped, in the RFD's `IdleStateUpdate.stopReason` vocabulary. */
|
|
18
|
+
export type ChildStopReason = "end_turn" | "cancelled" | "error";
|
|
19
|
+
export type ChildLaunch = {
|
|
20
|
+
parentSessionId: string;
|
|
21
|
+
/** `task_started.parent_task_id`: the subagent whose agent launched this one, if any. */
|
|
22
|
+
launcherTaskId?: string;
|
|
23
|
+
/** The Agent call's tool use id: what the child's stream names as its parent. */
|
|
24
|
+
toolUseId: string;
|
|
25
|
+
/** The SDK agent id — the child's session id. */
|
|
26
|
+
agentId: string;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
prompt?: string;
|
|
30
|
+
/** True when the SDK can stop this subagent on its own (`Query.stopTask`). */
|
|
31
|
+
cancellable: boolean;
|
|
32
|
+
};
|
|
33
|
+
type Send = (notification: SessionNotification) => Promise<void>;
|
|
34
|
+
/** A subagent's SDK task status mapped onto the RFD's idle stop reason. */
|
|
35
|
+
export declare function childStopReasonOf(status: string | undefined): ChildStopReason | undefined;
|
|
36
|
+
export declare class ChildSessionRouter {
|
|
37
|
+
private readonly send;
|
|
38
|
+
private readonly debug;
|
|
39
|
+
private readonly byChild;
|
|
40
|
+
/** Keyed by parent and tool use id: tool use ids are unique only within a session. */
|
|
41
|
+
private readonly byToolUse;
|
|
42
|
+
/** Tool calls already placed — on a child, or (`null`) nested in the parent
|
|
43
|
+
* because they went out before their launch was known. A tool call never
|
|
44
|
+
* moves once the client has seen it, and an update the conversion did not
|
|
45
|
+
* stamp (a progress beat, an edit diff) follows its call, even after its
|
|
46
|
+
* child settled. Keyed like `byToolUse`; bounded by `place`. */
|
|
47
|
+
private readonly toolCalls;
|
|
48
|
+
constructor(send: Send, debug: (line: string) => void);
|
|
49
|
+
isChild(sessionId: string): boolean;
|
|
50
|
+
/** The parent session a child belongs to. */
|
|
51
|
+
parentOf(childId: string): string | undefined;
|
|
52
|
+
/** The child a parent's Agent call launched, if that call launched one. */
|
|
53
|
+
childFor(parentSessionId: string, toolUseId: string | null | undefined): string | undefined;
|
|
54
|
+
/** The child whose traffic a notification belongs to, if any. */
|
|
55
|
+
private childOf;
|
|
56
|
+
/** Record where a tool call went; the oldest entries go first past the cap,
|
|
57
|
+
* so a long connection does not keep every tool call it ever saw. */
|
|
58
|
+
private place;
|
|
59
|
+
/**
|
|
60
|
+
* Announce a child on its parent — `subagent_update` with title,
|
|
61
|
+
* description, capabilities and `running` — then the parent's outgoing
|
|
62
|
+
* `session_message` carrying the prompt (R2.1, R2.2). A second launch of a
|
|
63
|
+
* known child (a resumed subagent) only reports it running again.
|
|
64
|
+
*/
|
|
65
|
+
launch(launch: ChildLaunch): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Register a child read back from its parent's transcript, under the id it
|
|
68
|
+
* had live (R4.1, R4.3). With `announce`, a replay: title, description and
|
|
69
|
+
* prompt go out with no state — replayed history is not live work (R4.2).
|
|
70
|
+
* Without it, a resume: nothing is sent until the child runs again. A child
|
|
71
|
+
* already registered for another parent is left to that parent.
|
|
72
|
+
*/
|
|
73
|
+
restore(launch: ChildLaunch, options: {
|
|
74
|
+
announce: boolean;
|
|
75
|
+
}): Promise<void>;
|
|
76
|
+
private announce;
|
|
77
|
+
private sendPrompt;
|
|
78
|
+
/**
|
|
79
|
+
* The notification as it should cross the wire: a subagent's update moves
|
|
80
|
+
* to its child's session; everything else is returned unchanged. A
|
|
81
|
+
* `parentToolUseId` naming no launch stays nested in the parent (R2.3).
|
|
82
|
+
*/
|
|
83
|
+
route(notification: SessionNotification): SessionNotification;
|
|
84
|
+
/** A permission request or elicitation opened on the child (R2.4). */
|
|
85
|
+
askOpened(childId: string): Promise<void>;
|
|
86
|
+
/** A permission request or elicitation on the child was answered (R2.4). */
|
|
87
|
+
askClosed(childId: string): Promise<void>;
|
|
88
|
+
/** True once the child was cancelled, until it is resumed. */
|
|
89
|
+
isCancelled(childId: string): boolean;
|
|
90
|
+
/** The subagent ended: `idle` with its stop reason, sent once (R2.4). */
|
|
91
|
+
settle(childId: string, stopReason: ChildStopReason): Promise<void>;
|
|
92
|
+
private setState;
|
|
93
|
+
}
|
|
94
|
+
export {};
|
|
95
|
+
//# sourceMappingURL=child-sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child-sessions.d.ts","sourceRoot":"","sources":["../src/child-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E;;;;;;;;;;;;;;GAcG;AAEH,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,OAAO,CAAC;AAoBjE,MAAM,MAAM,WAAW,GAAG;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,KAAK,IAAI,GAAG,CAAC,YAAY,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEjE,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,eAAe,GAAG,SAAS,CAYzF;AASD,qBAAa,kBAAkB;IAY3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAZxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,sFAAsF;IACtF,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D;;;;qEAIiE;IACjE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;gBAG3C,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI;IAGhD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAInC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C,2EAA2E;IAC3E,QAAQ,CAAC,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS;IAK3F,iEAAiE;IACjE,OAAO,CAAC,OAAO;IA2Bf;0EACsE;IACtE,OAAO,CAAC,KAAK;IAOb;;;;;OAKG;IACG,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA4ChD;;;;;;OAMG;IACG,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YA6BnE,QAAQ;YAoBR,UAAU;IAgBxB;;;;OAIG;IACH,KAAK,CAAC,YAAY,EAAE,mBAAmB,GAAG,mBAAmB;IAM7D,sEAAsE;IAChE,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/C,4EAA4E;IACtE,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS/C,8DAA8D;IAC9D,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIrC,yEAAyE;IACnE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;YAQ3D,QAAQ;CAYvB"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/** A subagent's SDK task status mapped onto the RFD's idle stop reason. */
|
|
2
|
+
export function childStopReasonOf(status) {
|
|
3
|
+
switch (status) {
|
|
4
|
+
case "completed":
|
|
5
|
+
return "end_turn";
|
|
6
|
+
case "failed":
|
|
7
|
+
return "error";
|
|
8
|
+
case "stopped":
|
|
9
|
+
case "killed":
|
|
10
|
+
return "cancelled";
|
|
11
|
+
default:
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Far above the tool calls one turn keeps in flight; only the oldest, long
|
|
16
|
+
* finished placements are ever evicted. */
|
|
17
|
+
const TOOL_CALL_PLACEMENT_CAP = 4096;
|
|
18
|
+
const launchKey = (parentSessionId, toolUseId) => `${parentSessionId}\0${toolUseId}`;
|
|
19
|
+
export class ChildSessionRouter {
|
|
20
|
+
send;
|
|
21
|
+
debug;
|
|
22
|
+
byChild = new Map();
|
|
23
|
+
/** Keyed by parent and tool use id: tool use ids are unique only within a session. */
|
|
24
|
+
byToolUse = new Map();
|
|
25
|
+
/** Tool calls already placed — on a child, or (`null`) nested in the parent
|
|
26
|
+
* because they went out before their launch was known. A tool call never
|
|
27
|
+
* moves once the client has seen it, and an update the conversion did not
|
|
28
|
+
* stamp (a progress beat, an edit diff) follows its call, even after its
|
|
29
|
+
* child settled. Keyed like `byToolUse`; bounded by `place`. */
|
|
30
|
+
toolCalls = new Map();
|
|
31
|
+
constructor(send, debug) {
|
|
32
|
+
this.send = send;
|
|
33
|
+
this.debug = debug;
|
|
34
|
+
}
|
|
35
|
+
isChild(sessionId) {
|
|
36
|
+
return this.byChild.has(sessionId);
|
|
37
|
+
}
|
|
38
|
+
/** The parent session a child belongs to. */
|
|
39
|
+
parentOf(childId) {
|
|
40
|
+
return this.byChild.get(childId)?.parentSessionId;
|
|
41
|
+
}
|
|
42
|
+
/** The child a parent's Agent call launched, if that call launched one. */
|
|
43
|
+
childFor(parentSessionId, toolUseId) {
|
|
44
|
+
if (!toolUseId)
|
|
45
|
+
return undefined;
|
|
46
|
+
return this.byToolUse.get(launchKey(parentSessionId, toolUseId))?.childId;
|
|
47
|
+
}
|
|
48
|
+
/** The child whose traffic a notification belongs to, if any. */
|
|
49
|
+
childOf(notification) {
|
|
50
|
+
const update = notification.update;
|
|
51
|
+
const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : undefined;
|
|
52
|
+
const callKey = toolCallId ? launchKey(notification.sessionId, toolCallId) : undefined;
|
|
53
|
+
if (callKey && this.toolCalls.has(callKey))
|
|
54
|
+
return this.toolCalls.get(callKey) ?? undefined;
|
|
55
|
+
const parentToolUseId = update._meta?.claudeCode
|
|
56
|
+
?.parentToolUseId;
|
|
57
|
+
if (typeof parentToolUseId !== "string") {
|
|
58
|
+
// An unstamped call is the parent's own — or a subagent's, sent before
|
|
59
|
+
// its `task_started` was consumed (`canUseTool` racing the stream).
|
|
60
|
+
// Either way the client saw it on the parent, so it stays there.
|
|
61
|
+
if (callKey && notification.update.sessionUpdate === "tool_call")
|
|
62
|
+
this.place(callKey, null);
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const childId = this.childFor(notification.sessionId, parentToolUseId);
|
|
66
|
+
if (!childId) {
|
|
67
|
+
this.debug(`[subagents/route] outcome=nested reason=unknown_launch sessionId=${notification.sessionId} parentToolUseId=${parentToolUseId}`);
|
|
68
|
+
}
|
|
69
|
+
if (callKey)
|
|
70
|
+
this.place(callKey, childId ?? null);
|
|
71
|
+
return childId;
|
|
72
|
+
}
|
|
73
|
+
/** Record where a tool call went; the oldest entries go first past the cap,
|
|
74
|
+
* so a long connection does not keep every tool call it ever saw. */
|
|
75
|
+
place(callKey, childId) {
|
|
76
|
+
this.toolCalls.set(callKey, childId);
|
|
77
|
+
if (this.toolCalls.size > TOOL_CALL_PLACEMENT_CAP) {
|
|
78
|
+
this.toolCalls.delete(this.toolCalls.keys().next().value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Announce a child on its parent — `subagent_update` with title,
|
|
83
|
+
* description, capabilities and `running` — then the parent's outgoing
|
|
84
|
+
* `session_message` carrying the prompt (R2.1, R2.2). A second launch of a
|
|
85
|
+
* known child (a resumed subagent) only reports it running again.
|
|
86
|
+
*/
|
|
87
|
+
async launch(launch) {
|
|
88
|
+
const known = this.byChild.get(launch.agentId);
|
|
89
|
+
if (known?.unannounced) {
|
|
90
|
+
// Known from the transcript of a resumed parent, never shown on this
|
|
91
|
+
// connection: the client gets the whole announcement now (R4.3).
|
|
92
|
+
known.unannounced = false;
|
|
93
|
+
known.cancelled = false;
|
|
94
|
+
known.state = "running";
|
|
95
|
+
// A launcher the client was never shown cannot carry it: Zed v1 drops
|
|
96
|
+
// updates for a session it does not know, so it goes on the root.
|
|
97
|
+
if (this.byChild.get(known.announcedOn)?.unannounced) {
|
|
98
|
+
known.announcedOn = known.parentSessionId;
|
|
99
|
+
}
|
|
100
|
+
this.byToolUse.set(launchKey(known.parentSessionId, launch.toolUseId), known);
|
|
101
|
+
await this.announce(known, launch, { state: "running" });
|
|
102
|
+
await this.sendPrompt(known, launch);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (known) {
|
|
106
|
+
// A resumed subagent keeps its task id but runs under a new Agent call
|
|
107
|
+
// (sdk.d.ts SDKTaskStartedMessage): its stream names that call.
|
|
108
|
+
this.byToolUse.set(launchKey(known.parentSessionId, launch.toolUseId), known);
|
|
109
|
+
known.cancelled = false;
|
|
110
|
+
await this.setState(known, { state: "running" });
|
|
111
|
+
await this.sendPrompt(known, launch);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const launcher = launch.launcherTaskId ? this.byChild.get(launch.launcherTaskId) : undefined;
|
|
115
|
+
const record = {
|
|
116
|
+
childId: launch.agentId,
|
|
117
|
+
parentSessionId: launch.parentSessionId,
|
|
118
|
+
announcedOn: launcher?.parentSessionId === launch.parentSessionId
|
|
119
|
+
? launcher.childId
|
|
120
|
+
: launch.parentSessionId,
|
|
121
|
+
state: "running",
|
|
122
|
+
openAsks: 0,
|
|
123
|
+
};
|
|
124
|
+
this.byChild.set(record.childId, record);
|
|
125
|
+
this.byToolUse.set(launchKey(record.parentSessionId, launch.toolUseId), record);
|
|
126
|
+
await this.announce(record, launch, { state: "running" });
|
|
127
|
+
await this.sendPrompt(record, launch);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Register a child read back from its parent's transcript, under the id it
|
|
131
|
+
* had live (R4.1, R4.3). With `announce`, a replay: title, description and
|
|
132
|
+
* prompt go out with no state — replayed history is not live work (R4.2).
|
|
133
|
+
* Without it, a resume: nothing is sent until the child runs again. A child
|
|
134
|
+
* already registered for another parent is left to that parent.
|
|
135
|
+
*/
|
|
136
|
+
async restore(launch, options) {
|
|
137
|
+
const known = this.byChild.get(launch.agentId);
|
|
138
|
+
if (known && known.parentSessionId !== launch.parentSessionId) {
|
|
139
|
+
this.debug(`[subagents/restore] outcome=skipped reason=other_parent childSessionId=${launch.agentId} sessionId=${launch.parentSessionId} owner=${known.parentSessionId}`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const launcher = launch.launcherTaskId ? this.byChild.get(launch.launcherTaskId) : undefined;
|
|
143
|
+
const record = known ?? {
|
|
144
|
+
childId: launch.agentId,
|
|
145
|
+
parentSessionId: launch.parentSessionId,
|
|
146
|
+
announcedOn: launcher?.parentSessionId === launch.parentSessionId
|
|
147
|
+
? launcher.childId
|
|
148
|
+
: launch.parentSessionId,
|
|
149
|
+
openAsks: 0,
|
|
150
|
+
};
|
|
151
|
+
this.byChild.set(record.childId, record);
|
|
152
|
+
this.byToolUse.set(launchKey(record.parentSessionId, launch.toolUseId), record);
|
|
153
|
+
if (!options.announce) {
|
|
154
|
+
if (!known)
|
|
155
|
+
record.unannounced = true;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
record.unannounced = false;
|
|
159
|
+
await this.announce(record, launch);
|
|
160
|
+
await this.sendPrompt(record, launch);
|
|
161
|
+
}
|
|
162
|
+
async announce(record, launch, state) {
|
|
163
|
+
await this.send({
|
|
164
|
+
sessionId: record.announcedOn,
|
|
165
|
+
update: {
|
|
166
|
+
sessionUpdate: "subagent_update",
|
|
167
|
+
sessionId: record.childId,
|
|
168
|
+
title: launch.title,
|
|
169
|
+
description: launch.description,
|
|
170
|
+
// `SubagentSessionCapabilities.cancel` is an object when supported;
|
|
171
|
+
// absent, Zed disables Stop for the child (design.md, Security).
|
|
172
|
+
...(launch.cancellable ? { capabilities: { cancel: {} } } : {}),
|
|
173
|
+
...(state ? { state } : {}),
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async sendPrompt(record, launch) {
|
|
178
|
+
if (!launch.prompt)
|
|
179
|
+
return;
|
|
180
|
+
await this.send({
|
|
181
|
+
sessionId: record.announcedOn,
|
|
182
|
+
update: {
|
|
183
|
+
sessionUpdate: "session_message",
|
|
184
|
+
// Derived, not random: a replay of the same launch upserts the same
|
|
185
|
+
// message, and each run of a resumed child gets its own.
|
|
186
|
+
messageId: `subagent-prompt-${record.childId}-${launch.toolUseId}`,
|
|
187
|
+
senderSessionId: record.announcedOn,
|
|
188
|
+
recipientSessionId: record.childId,
|
|
189
|
+
content: [{ type: "text", text: launch.prompt }],
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* The notification as it should cross the wire: a subagent's update moves
|
|
195
|
+
* to its child's session; everything else is returned unchanged. A
|
|
196
|
+
* `parentToolUseId` naming no launch stays nested in the parent (R2.3).
|
|
197
|
+
*/
|
|
198
|
+
route(notification) {
|
|
199
|
+
if (this.byChild.has(notification.sessionId))
|
|
200
|
+
return notification;
|
|
201
|
+
const childId = this.childOf(notification);
|
|
202
|
+
return childId ? { ...notification, sessionId: childId } : notification;
|
|
203
|
+
}
|
|
204
|
+
/** A permission request or elicitation opened on the child (R2.4). */
|
|
205
|
+
async askOpened(childId) {
|
|
206
|
+
const record = this.byChild.get(childId);
|
|
207
|
+
if (!record)
|
|
208
|
+
return;
|
|
209
|
+
record.openAsks += 1;
|
|
210
|
+
if (record.state !== "idle")
|
|
211
|
+
await this.setState(record, { state: "requires_action" });
|
|
212
|
+
}
|
|
213
|
+
/** A permission request or elicitation on the child was answered (R2.4). */
|
|
214
|
+
async askClosed(childId) {
|
|
215
|
+
const record = this.byChild.get(childId);
|
|
216
|
+
if (!record)
|
|
217
|
+
return;
|
|
218
|
+
record.openAsks = Math.max(0, record.openAsks - 1);
|
|
219
|
+
if (record.openAsks === 0 && record.state === "requires_action") {
|
|
220
|
+
await this.setState(record, { state: "running" });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** True once the child was cancelled, until it is resumed. */
|
|
224
|
+
isCancelled(childId) {
|
|
225
|
+
return this.byChild.get(childId)?.cancelled === true;
|
|
226
|
+
}
|
|
227
|
+
/** The subagent ended: `idle` with its stop reason, sent once (R2.4). */
|
|
228
|
+
async settle(childId, stopReason) {
|
|
229
|
+
const record = this.byChild.get(childId);
|
|
230
|
+
if (!record)
|
|
231
|
+
return;
|
|
232
|
+
if (stopReason === "cancelled")
|
|
233
|
+
record.cancelled = true;
|
|
234
|
+
if (record.state === "idle")
|
|
235
|
+
return;
|
|
236
|
+
await this.setState(record, { state: "idle", stopReason });
|
|
237
|
+
}
|
|
238
|
+
async setState(record, state) {
|
|
239
|
+
if (record.state === state.state && state.state !== "idle")
|
|
240
|
+
return;
|
|
241
|
+
record.state = state.state;
|
|
242
|
+
await this.send({
|
|
243
|
+
sessionId: record.announcedOn,
|
|
244
|
+
update: { sessionUpdate: "subagent_update", sessionId: record.childId, state },
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
package/dist/task-feed.d.ts
CHANGED
|
@@ -66,6 +66,9 @@ export type TaskEntry = {
|
|
|
66
66
|
hasOutput: boolean;
|
|
67
67
|
/** A running shell's last lines, sanitized; null otherwise (version 2). */
|
|
68
68
|
tail: string[] | null;
|
|
69
|
+
/** The subagent's child session under the subagents RFD (version 3, story
|
|
70
|
+
* 065). Never set by the feed: added at send time, so v2 bytes cannot change. */
|
|
71
|
+
childSessionId?: string;
|
|
69
72
|
};
|
|
70
73
|
export type TasksSnapshot = {
|
|
71
74
|
tasks: TaskEntry[];
|
package/dist/task-feed.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-feed.d.ts","sourceRoot":"","sources":["../src/task-feed.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,KAAK,EACV,0BAA0B,EAC1B,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,gCAAgC,CAAC;AAGxC,2EAA2E;AAC3E,eAAO,MAAM,cAAc,kBAAkB,CAAC;AAE9C;2DAC2D;AAC3D,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;AAExF,MAAM,MAAM,SAAS,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,0EAA0E;AAC1E,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"task-feed.d.ts","sourceRoot":"","sources":["../src/task-feed.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.34.0",
|
|
7
7
|
"description": "Claude Code in Zed and other ACP clients with the VS Code extension's UX — multi-select questions, refusal consent, live quota windows, background tasks, Ultracode effort and session notices. Fork of claude-agent-acp.",
|
|
8
8
|
"main": "dist/lib.js",
|
|
9
9
|
"types": "dist/lib.d.ts",
|