@agentclientprotocol/claude-agent-acp 0.99.2-preview.5 → 0.99.2-preview.7

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.
@@ -1,4 +1,6 @@
1
1
  import { RequestError, } from "@agentclientprotocol/sdk/unstable";
2
+ import { realpath } from "node:fs/promises";
3
+ import path from "node:path";
2
4
  import { AIR_SUBAGENT_KEY, airExtensionMeta } from "./air-extension.js";
3
5
  import { MODEL_CONFIG_ID } from "./session-model.js";
4
6
  import { isFinalToolCallStatus } from "./tool-calls/renderer.js";
@@ -28,6 +30,11 @@ export class NativeSubagentRuntime {
28
30
  identityByToolUse = new Map();
29
31
  controlByToolUse = new Map();
30
32
  configBySession = new Map();
33
+ /**
34
+ * The RFD form: the working directory of each child, by session id, as its
35
+ * hooks last reported it. A hook can come before the child is announced.
36
+ */
37
+ cwdBySession = new Map();
31
38
  childByParentToolUse = new Map();
32
39
  /**
33
40
  * The child session of each tool call that went to a child session. A later
@@ -224,6 +231,85 @@ export class NativeSubagentRuntime {
224
231
  config.publishing = publishing.catch(() => { });
225
232
  await publishing;
226
233
  }
234
+ /**
235
+ * The RFD form: takes the working directory of the child `agentId`, the
236
+ * `cwd` of one of its hooks. A child that Claude Code runs in its own
237
+ * worktree (Agent `isolation: "worktree"`) reports the worktree. The client
238
+ * gets the `cwd` only when it differs from the parent's, in the
239
+ * announcement or in a `subagent_update` right after it. Claude Code runs a
240
+ * child's SubagentStart hook before its first model request, so the `cwd`
241
+ * reaches the client before the child's first tool call.
242
+ */
243
+ async reportCwd(agentId, cwd) {
244
+ if (this.form !== "rfd" || agentId === this.rootSessionId || !path.isAbsolute(cwd))
245
+ return;
246
+ const known = this.children.get(agentId) ?? this.childOfSession(agentId);
247
+ const sessionId = known?.sessionId ?? agentId;
248
+ if (!known && !this.cwdBySession.has(sessionId)) {
249
+ const pending = [...this.cwdBySession.keys()].filter((id) => !this.childOfSession(id)).length;
250
+ if (pending >= MAX_PENDING_PARENTS) {
251
+ this.logger.log(`Session ${this.rootSessionId}: dropping cwd for unannounced subagent ${agentId}; pending buffer limit reached`);
252
+ return;
253
+ }
254
+ }
255
+ this.cwdBySession.set(sessionId, cwd);
256
+ if (!known)
257
+ return;
258
+ // An announcement in flight may have been built without this `cwd`.
259
+ if (known.announcePromise)
260
+ await known.announcePromise.catch(() => { });
261
+ if (known.announced)
262
+ await this.publishCwd(known);
263
+ }
264
+ /**
265
+ * The `cwd` of `child` that the client does not hold yet: its working
266
+ * directory when that differs from its parent's, `null` when it is back in
267
+ * the parent's after it reported another, else undefined.
268
+ */
269
+ async unreportedCwd(child) {
270
+ const cwd = this.cwdBySession.get(child.sessionId);
271
+ if (cwd === undefined)
272
+ return undefined;
273
+ const inherited = this.effectiveCwd(child.parentSessionId);
274
+ if (inherited !== undefined && (await sameDirectory(cwd, inherited))) {
275
+ return child.reportedCwd ? null : undefined;
276
+ }
277
+ return cwd === child.reportedCwd ? undefined : cwd;
278
+ }
279
+ /**
280
+ * The working directory of the session `sessionId` as the client holds it:
281
+ * the root's from session setup, a child's reported `cwd`, else its
282
+ * parent's.
283
+ */
284
+ effectiveCwd(sessionId) {
285
+ const seen = new Set();
286
+ let child = this.childOfSession(sessionId);
287
+ while (child && !seen.has(child.sessionId)) {
288
+ if (child.reportedCwd)
289
+ return child.reportedCwd;
290
+ seen.add(child.sessionId);
291
+ child = this.childOfSession(child.parentSessionId);
292
+ }
293
+ return this.session.cwd;
294
+ }
295
+ /** The RFD form: sends the `cwd` of the announced `child` that the client does not hold. */
296
+ async publishCwd(child) {
297
+ if (this.form !== "rfd")
298
+ return;
299
+ // Concurrent hooks of one child report the same `cwd` once.
300
+ const publishing = (child.cwdPublishing ?? Promise.resolve()).then(async () => {
301
+ const cwd = await this.unreportedCwd(child);
302
+ if (cwd === undefined)
303
+ return;
304
+ await this.publish({
305
+ sessionId: child.parentSessionId,
306
+ update: { sessionUpdate: "subagent_update", sessionId: child.sessionId, cwd },
307
+ });
308
+ child.reportedCwd = cwd;
309
+ });
310
+ child.cwdPublishing = publishing.catch(() => { });
311
+ await publishing;
312
+ }
227
313
  async routeUpdate(notification, deliver, forcedSessionId) {
228
314
  const { update } = notification;
229
315
  const claudeMeta = update._meta?.claudeCode;
@@ -686,10 +772,15 @@ export class NativeSubagentRuntime {
686
772
  }
687
773
  if (child.announced)
688
774
  continue;
775
+ // A hook of the resumed agent may have reported a newer `cwd`.
776
+ if (entry.cwd && !this.cwdBySession.has(child.sessionId)) {
777
+ this.cwdBySession.set(child.sessionId, entry.cwd);
778
+ }
689
779
  if (entry.agentId === agentId) {
690
780
  await this.announce(child);
691
781
  }
692
782
  else {
783
+ const cwd = await this.unreportedCwd(child);
693
784
  await this.publish({
694
785
  sessionId: child.parentSessionId,
695
786
  update: {
@@ -697,8 +788,11 @@ export class NativeSubagentRuntime {
697
788
  sessionId: child.sessionId,
698
789
  ...(child.title ? { title: child.title } : {}),
699
790
  ...(child.description ? { description: child.description } : {}),
791
+ ...(cwd ? { cwd } : {}),
700
792
  },
701
793
  });
794
+ if (cwd)
795
+ child.reportedCwd = cwd;
702
796
  child.announced = true;
703
797
  }
704
798
  for (const pending of this.takePending(entry.launchId))
@@ -846,6 +940,7 @@ export class NativeSubagentRuntime {
846
940
  withdraw(taskId, child) {
847
941
  this.children.delete(taskId);
848
942
  this.configBySession.delete(child.sessionId);
943
+ this.cwdBySession.delete(child.sessionId);
849
944
  if (child.parentToolUseId) {
850
945
  this.takePending(child.parentToolUseId);
851
946
  this.cleanupControl(child.parentToolUseId);
@@ -863,13 +958,16 @@ export class NativeSubagentRuntime {
863
958
  await this.reportState(child, rfdEndState("failed", { error }));
864
959
  }
865
960
  /**
866
- * Announces `child` in the client's form, then its configuration. A runtime
867
- * without a form never does.
961
+ * Announces `child` in the client's form, with its `cwd` in the RFD form,
962
+ * then its configuration. A runtime without a form never does.
868
963
  */
869
964
  async announce(child) {
870
965
  if (!this.form)
871
966
  return;
872
- await announceNativeSubagent(child, this.publish, this.form);
967
+ const cwd = this.form === "rfd" && !child.announced ? await this.unreportedCwd(child) : undefined;
968
+ await announceNativeSubagent(child, this.publish, this.form, cwd ?? undefined);
969
+ // A hook that reported a `cwd` while the announcement was being built.
970
+ await this.publishCwd(child);
873
971
  await this.publishConfiguration(child);
874
972
  }
875
973
  /** The RFD form: publishes the work state of `child` on its parent. */
@@ -1105,6 +1203,7 @@ export class NativeSubagentRuntime {
1105
1203
  this.identityByToolUse.clear();
1106
1204
  this.controlByToolUse.clear();
1107
1205
  this.configBySession.clear();
1206
+ this.cwdBySession.clear();
1108
1207
  this.childByParentToolUse.clear();
1109
1208
  this.taskFinishPromises.clear();
1110
1209
  this.senderByToolUse.clear();
@@ -1272,7 +1371,7 @@ export function responseModelEntry(model, models) {
1272
1371
  * `NativeSubagentRuntime.cancelChild`), and nothing else. AIR's draft grants
1273
1372
  * no capabilities.
1274
1373
  */
1275
- export async function announceNativeSubagent(child, publish, form) {
1374
+ export async function announceNativeSubagent(child, publish, form, cwd) {
1276
1375
  if (child.announced)
1277
1376
  return;
1278
1377
  if (child.announcePromise)
@@ -1287,10 +1386,13 @@ export async function announceNativeSubagent(child, publish, form) {
1287
1386
  sessionId: child.sessionId,
1288
1387
  ...(child.title ? { title: child.title } : {}),
1289
1388
  ...(child.description ? { description: child.description } : {}),
1389
+ ...(cwd ? { cwd } : {}),
1290
1390
  capabilities: { cancel: {} },
1291
1391
  state: { state: "running" },
1292
1392
  },
1293
1393
  });
1394
+ if (cwd)
1395
+ child.reportedCwd = cwd;
1294
1396
  child.announced = true;
1295
1397
  // The immediate parent made the Agent/Task call that launched the child.
1296
1398
  if (child.prompt) {
@@ -1723,3 +1825,15 @@ function promptField(prompt) {
1723
1825
  function nonBlankString(value) {
1724
1826
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
1725
1827
  }
1828
+ /**
1829
+ * Whether `left` and `right` are the same directory, also when one is
1830
+ * spelled through a symlink, e.g. `/tmp/project` and `/private/tmp/project`
1831
+ * on macOS. A path that does not exist is compared as written.
1832
+ */
1833
+ export async function sameDirectory(left, right) {
1834
+ if (path.resolve(left) === path.resolve(right))
1835
+ return true;
1836
+ const real = (dir) => realpath(dir).catch(() => path.resolve(dir));
1837
+ const [realLeft, realRight] = await Promise.all([real(left), real(right)]);
1838
+ return realLeft === realRight;
1839
+ }
@@ -77,7 +77,9 @@ export declare function deleteSessionIfStored(sessionId: string, deleteSession?:
77
77
  /**
78
78
  * `session/delete` of an AIR client without `sessionIndex`, which uses delete
79
79
  * to mark a session done: archives it instead, so the transcript survives. A
80
- * session without a transcript fails as the SDK delete did.
80
+ * session without a transcript has nothing to archive and succeeds, as ACP
81
+ * asks of a delete of a session that never existed (see
82
+ * {@link deleteSessionIfStored}).
81
83
  */
82
84
  export declare function archiveInsteadOfDelete(sessionId: string, service: SessionIndexService): Promise<void>;
83
85
  /** How {@link SessionIndexService.retitle} reaches the session. */
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/session-index/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/session-index/service.ts"],"names":[],"mappings":"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"}
@@ -270,13 +270,6 @@ async function isTranscriptOf(transcript, paths) {
270
270
  function isSdkNotFound(error) {
271
271
  return error instanceof Error && /^Session \S+ not found in /.test(error.message);
272
272
  }
273
- /** The error the SDK `deleteSession` gives for a session id it rejects or
274
- * does not find, which a client without `sessionIndex` got before. */
275
- function sdkDeleteError(sessionId) {
276
- return new Error(isSessionId(sessionId)
277
- ? `Session ${sessionId} not found in any project directory`
278
- : `Invalid sessionId: ${sessionId}`);
279
- }
280
273
  /**
281
274
  * `session/delete` of a client that is neither AIR nor a `sessionIndex` client:
282
275
  * the SDK delete of the session's transcript. A session without one succeeds,
@@ -301,15 +294,17 @@ export async function deleteSessionIfStored(sessionId, deleteSession = sdkDelete
301
294
  /**
302
295
  * `session/delete` of an AIR client without `sessionIndex`, which uses delete
303
296
  * to mark a session done: archives it instead, so the transcript survives. A
304
- * session without a transcript fails as the SDK delete did.
297
+ * session without a transcript has nothing to archive and succeeds, as ACP
298
+ * asks of a delete of a session that never existed (see
299
+ * {@link deleteSessionIfStored}).
305
300
  */
306
301
  export async function archiveInsteadOfDelete(sessionId, service) {
307
302
  if (!isSessionId(sessionId))
308
- throw sdkDeleteError(sessionId);
303
+ return;
309
304
  // The SDK delete skips empty transcripts, so they do not count.
310
305
  const found = await service.index.findTranscripts(sessionId);
311
306
  if (found.length === 0)
312
- throw sdkDeleteError(sessionId);
307
+ return;
313
308
  await service.retitle(sessionId, archiveTo(true), { sidecar: "existing" });
314
309
  }
315
310
  export class SessionIndexService {
@@ -14,6 +14,11 @@ export interface SubagentRecord {
14
14
  background: boolean;
15
15
  /** Whether the user stopped it. */
16
16
  stoppedByUser: boolean;
17
+ /**
18
+ * Its working directory, when Claude Code placed it outside its parent's:
19
+ * in a worktree of its own or of an ancestor, or in a `cwd` of its own.
20
+ */
21
+ cwd?: string;
17
22
  }
18
23
  /** Where the subagent transcripts of a session are. */
19
24
  export interface SubagentHistory {
@@ -31,7 +36,9 @@ export interface SubagentHistory {
31
36
  * Claude keeps the history of a subagent in its own transcript,
32
37
  * `<session>/subagents/agent-<id>.jsonl`, next to the session transcript.
33
38
  * The `agent-<id>.meta.json` file beside it names the launching tool use.
34
- * The ids are empty for a session without a local transcript.
39
+ * For a child that Claude Code placed elsewhere, it has `worktreePath`,
40
+ * `inheritedWorktreePath` or `cwd`; then the child's working directory is the
41
+ * `cwd` of its transcript's records. The ids are empty for a session without a local transcript.
35
42
  */
36
43
  export declare function subagentHistory(sessionId: string): Promise<SubagentHistory>;
37
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-history.d.ts","sourceRoot":"","sources":["../src/subagent-history.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,gCAAgC,CAAC;AAKxC,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACpC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAwCjF;AAOD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE;IAAE,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,GAC1C,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAmD/B;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAW5D;AA0DD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE;IAAE,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,GAC1C,OAAO,CAAC,cAAc,EAAE,CAAC,CAW3B;AAyBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAkB5E"}
1
+ {"version":3,"file":"subagent-history.d.ts","sourceRoot":"","sources":["../src/subagent-history.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,gCAAgC,CAAC;AAKxC,OAAO,EAA+B,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,0DAA0D;AAC1D,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,UAAU,EAAE,OAAO,CAAC;IACpB,mCAAmC;IACnC,aAAa,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACpC;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA+CjF;AAgCD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE;IAAE,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,GAC1C,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAoD/B;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAW5D;AA0DD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE;IAAE,KAAK,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,GAC1C,OAAO,CAAC,cAAc,EAAE,CAAC,CAW3B;AAyBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAkB5E"}
@@ -11,7 +11,9 @@ import { findTranscript } from "./resumed-session.js";
11
11
  * Claude keeps the history of a subagent in its own transcript,
12
12
  * `<session>/subagents/agent-<id>.jsonl`, next to the session transcript.
13
13
  * The `agent-<id>.meta.json` file beside it names the launching tool use.
14
- * The ids are empty for a session without a local transcript.
14
+ * For a child that Claude Code placed elsewhere, it has `worktreePath`,
15
+ * `inheritedWorktreePath` or `cwd`; then the child's working directory is the
16
+ * `cwd` of its transcript's records. The ids are empty for a session without a local transcript.
15
17
  */
16
18
  export async function subagentHistory(sessionId) {
17
19
  const agents = new Map();
@@ -35,6 +37,12 @@ export async function subagentHistory(sessionId) {
35
37
  if (typeof meta?.toolUseId !== "string")
36
38
  return;
37
39
  const text = (value) => (typeof value === "string" ? value : undefined);
40
+ const placement = [meta.cwd, meta.worktreePath, meta.inheritedWorktreePath]
41
+ .map(text)
42
+ .find((value) => value && path.isAbsolute(value));
43
+ const cwd = placement
44
+ ? ((await recordedCwd(path.join(directory, `agent-${match[1]}.jsonl`))) ?? placement)
45
+ : undefined;
38
46
  agents.set(meta.toolUseId, {
39
47
  agentId: match[1],
40
48
  name: text(meta.name),
@@ -42,6 +50,7 @@ export async function subagentHistory(sessionId) {
42
50
  description: text(meta.description),
43
51
  background: meta.requestShape === "background",
44
52
  stoppedByUser: meta.stoppedByUser === true,
53
+ ...(cwd ? { cwd } : {}),
45
54
  });
46
55
  }
47
56
  catch {
@@ -55,6 +64,35 @@ export async function subagentHistory(sessionId) {
55
64
  const dir = (await getSessionInfo(sessionId).catch(() => undefined))?.cwd;
56
65
  return dir ? { agents, dir } : { agents };
57
66
  }
67
+ /**
68
+ * The `cwd` of the first record of the transcript `file` that has one, or
69
+ * undefined when the file cannot be read. Claude Code writes the working
70
+ * directory of the session or subagent on each record.
71
+ */
72
+ async function recordedCwd(file) {
73
+ const stream = createReadStream(file, { encoding: "utf8" });
74
+ try {
75
+ for await (const line of readline.createInterface({ input: stream, crlfDelay: Infinity })) {
76
+ if (!line.includes('"cwd"'))
77
+ continue;
78
+ try {
79
+ const cwd = JSON.parse(line)?.cwd;
80
+ if (typeof cwd === "string" && path.isAbsolute(cwd))
81
+ return cwd;
82
+ }
83
+ catch {
84
+ // A damaged record; a later one can have the `cwd`.
85
+ }
86
+ }
87
+ }
88
+ catch {
89
+ // An unreadable transcript; the caller falls back to the metadata's path.
90
+ }
91
+ finally {
92
+ stream.destroy();
93
+ }
94
+ return undefined;
95
+ }
58
96
  /** The folder of the subagent transcripts, next to the session transcript `transcript`. */
59
97
  function subagentsDirectory(transcript, sessionId) {
60
98
  return path.join(path.dirname(transcript), sessionId, "subagents");
@@ -121,6 +159,7 @@ export async function subagentLineage(sessionId, agentId, logger) {
121
159
  agentId: current,
122
160
  launchId: launch.launchId,
123
161
  ...(launcher !== null ? { parentAgentId: launcher } : {}),
162
+ ...(launch.record.cwd ? { cwd: launch.record.cwd } : {}),
124
163
  ...(launch.record.name ? { name: launch.record.name } : {}),
125
164
  ...(launch.record.agentType ? { agentType: launch.record.agentType } : {}),
126
165
  ...(launch.record.description ? { description: launch.record.description } : {}),
@@ -54,6 +54,8 @@ export type ReplayChild = {
54
54
  /** The state of the child's last persisted task notification. */
55
55
  notified?: SubagentState;
56
56
  announced: boolean;
57
+ /** The `cwd` that the replay reported for it; undefined while it inherits its parent's. */
58
+ cwd?: string;
57
59
  promptSent: boolean;
58
60
  /** Whether it handed a report to its parent with a SubagentHandback call. */
59
61
  handedBack?: boolean;
@@ -72,6 +74,8 @@ export declare class RfdSubagentReplay {
72
74
  * {@link currentStates}, after the load response.
73
75
  */
74
76
  private readonly mapsDuringReplay;
77
+ /** The working directory of the root session, which a child inherits unless it reports its own. */
78
+ private readonly rootCwd?;
75
79
  /** The children by the id of the tool use that launched each. */
76
80
  private readonly children;
77
81
  private readonly byAgentId;
@@ -88,7 +92,9 @@ export declare class RfdSubagentReplay {
88
92
  * A load lacks the list until the CLI started, so it maps only in
89
93
  * {@link currentStates}, after the load response.
90
94
  */
91
- mapsDuringReplay?: boolean);
95
+ mapsDuringReplay?: boolean,
96
+ /** The working directory of the root session, which a child inherits unless it reports its own. */
97
+ rootCwd?: string | undefined);
92
98
  /**
93
99
  * Registers the launches, the launch results, and the SendMessage calls in
94
100
  * `messages`, the transcript of the session `ownerSessionId`.
@@ -98,10 +104,16 @@ export declare class RfdSubagentReplay {
98
104
  childOfLaunch(toolUseId: string): ReplayChild | undefined;
99
105
  /**
100
106
  * Announces `child` on its parent, as live: running, with the parent's
101
- * labels. Then it sends `firstModel`, the model of the child's first
102
- * response, when the transcript has one.
107
+ * labels, and with its recorded `cwd` when that differs from its parent's.
108
+ * Then it sends `firstModel`, the model of the child's first response, when
109
+ * the transcript has one.
103
110
  */
104
111
  announce(child: ReplayChild, firstModel?: string): Promise<void>;
112
+ /**
113
+ * The working directory of the session `sessionId` as the client holds it:
114
+ * a child's reported `cwd`, else its parent's, up to the root's.
115
+ */
116
+ private effectiveCwd;
105
117
  /**
106
118
  * Takes the model of a replayed response of the child `owner`. A child
107
119
  * whose model changes gets its `model` option again.
@@ -156,6 +168,10 @@ export declare class RfdSubagentReplay {
156
168
  * when a live session is loaded again. Such a child reports that state and
157
169
  * can be cancelled, as live. Any other child cannot be running, because the
158
170
  * process that ran it is gone, so its replayed end is its current state.
171
+ *
172
+ * A replayed `cwd` whose directory is gone, such as a worktree that Claude
173
+ * Code removed when the child finished, is cleared with `cwd: null`, as the
174
+ * RFD requires after a reconnection.
159
175
  */
160
176
  currentStates(live: (sessionId: string) => WorkState["state"] | undefined, liveConfiguration?: (sessionId: string) => SessionConfigOption[] | undefined): AcpSessionNotification[];
161
177
  private registerLaunch;
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-replay.d.ts","sourceRoot":"","sources":["../src/subagent-replay.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subagent-replay.d.ts","sourceRoot":"","sources":["../src/subagent-replay.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,12 @@
1
- import { childModelOption, HANDBACK_TOOL, handbackMessage, isNativeSubagentControlTool, launchReply, linkedSessionMessages, nativeSubagentState, responseModelEntry, rfdEndState, rfdLabels, } from "./native-subagents.js";
1
+ import { statSync } from "node:fs";
2
+ import { childModelOption, HANDBACK_TOOL, handbackMessage, isNativeSubagentControlTool, launchReply, linkedSessionMessages, nativeSubagentState, responseModelEntry, rfdEndState, rfdLabels, sameDirectory, } from "./native-subagents.js";
2
3
  import { noticeUpdate } from "./session-notices.js";
3
4
  export class RfdSubagentReplay {
4
5
  history;
5
6
  publish;
6
7
  models;
7
8
  mapsDuringReplay;
9
+ rootCwd;
8
10
  /** The children by the id of the tool use that launched each. */
9
11
  children = new Map();
10
12
  byAgentId = new Map();
@@ -21,11 +23,14 @@ export class RfdSubagentReplay {
21
23
  * A load lacks the list until the CLI started, so it maps only in
22
24
  * {@link currentStates}, after the load response.
23
25
  */
24
- mapsDuringReplay = true) {
26
+ mapsDuringReplay = true,
27
+ /** The working directory of the root session, which a child inherits unless it reports its own. */
28
+ rootCwd) {
25
29
  this.history = history;
26
30
  this.publish = publish;
27
31
  this.models = models;
28
32
  this.mapsDuringReplay = mapsDuringReplay;
33
+ this.rootCwd = rootCwd;
29
34
  }
30
35
  /**
31
36
  * Registers the launches, the launch results, and the SendMessage calls in
@@ -76,13 +81,19 @@ export class RfdSubagentReplay {
76
81
  }
77
82
  /**
78
83
  * Announces `child` on its parent, as live: running, with the parent's
79
- * labels. Then it sends `firstModel`, the model of the child's first
80
- * response, when the transcript has one.
84
+ * labels, and with its recorded `cwd` when that differs from its parent's.
85
+ * Then it sends `firstModel`, the model of the child's first response, when
86
+ * the transcript has one.
81
87
  */
82
88
  async announce(child, firstModel) {
83
89
  if (child.announced)
84
90
  return;
85
91
  const labels = rfdLabels(child.record.name ?? child.input.name, child.record.agentType ?? child.input.subagent_type, child.record.description ?? child.input.description);
92
+ const inherited = this.effectiveCwd(child.parentSessionId);
93
+ const cwd = child.record.cwd &&
94
+ (inherited === undefined || !(await sameDirectory(child.record.cwd, inherited)))
95
+ ? child.record.cwd
96
+ : undefined;
86
97
  await this.publish({
87
98
  sessionId: child.parentSessionId,
88
99
  update: {
@@ -90,12 +101,29 @@ export class RfdSubagentReplay {
90
101
  sessionId: child.sessionId,
91
102
  ...(labels.title ? { title: labels.title } : {}),
92
103
  ...(labels.description ? { description: labels.description } : {}),
104
+ ...(cwd ? { cwd } : {}),
93
105
  state: { state: "running" },
94
106
  },
95
107
  });
108
+ child.cwd = cwd;
96
109
  child.announced = true;
97
110
  await this.responseModel(child, firstModel);
98
111
  }
112
+ /**
113
+ * The working directory of the session `sessionId` as the client holds it:
114
+ * a child's reported `cwd`, else its parent's, up to the root's.
115
+ */
116
+ effectiveCwd(sessionId) {
117
+ const seen = new Set();
118
+ let child = this.byAgentId.get(sessionId);
119
+ while (child && !seen.has(child.sessionId)) {
120
+ if (child.cwd)
121
+ return child.cwd;
122
+ seen.add(child.sessionId);
123
+ child = this.byAgentId.get(child.parentSessionId);
124
+ }
125
+ return this.rootCwd;
126
+ }
99
127
  /**
100
128
  * Takes the model of a replayed response of the child `owner`. A child
101
129
  * whose model changes gets its `model` option again.
@@ -258,6 +286,10 @@ export class RfdSubagentReplay {
258
286
  * when a live session is loaded again. Such a child reports that state and
259
287
  * can be cancelled, as live. Any other child cannot be running, because the
260
288
  * process that ran it is gone, so its replayed end is its current state.
289
+ *
290
+ * A replayed `cwd` whose directory is gone, such as a worktree that Claude
291
+ * Code removed when the child finished, is cleared with `cwd: null`, as the
292
+ * RFD requires after a reconnection.
261
293
  */
262
294
  currentStates(live, liveConfiguration) {
263
295
  return [...this.children.values()]
@@ -278,6 +310,7 @@ export class RfdSubagentReplay {
278
310
  sessionUpdate: "subagent_update",
279
311
  sessionId: child.sessionId,
280
312
  ...(liveState !== undefined ? { capabilities: { cancel: {} } } : {}),
313
+ ...(child.cwd && !isDirectory(child.cwd) ? { cwd: null } : {}),
281
314
  state,
282
315
  },
283
316
  },
@@ -427,6 +460,15 @@ function textOf(content) {
427
460
  .filter((text) => typeof text === "string")
428
461
  .join("\n");
429
462
  }
463
+ /** Whether `dir` is an existing directory. */
464
+ function isDirectory(dir) {
465
+ try {
466
+ return statSync(dir).isDirectory();
467
+ }
468
+ catch {
469
+ return false;
470
+ }
471
+ }
430
472
  /** A message that a SendMessage call delivered to the child. */
431
473
  function isDelivery(message) {
432
474
  return message.origin?.kind === "coordinator";
@@ -15,7 +15,22 @@ export declare function v1InitializeRequest(request: v2.InitializeRequest): Init
15
15
  * The v2 `initialize` response for the v1 response of the agent.
16
16
  *
17
17
  * It does not advertise what the v2 surface does not serve yet: the
18
- * extensions of {@link UNSERVED_EXTENSION_KEYS}.
18
+ * extensions of {@link UNSERVED_EXTENSION_KEYS}, and the AIR extensions of
19
+ * {@link withoutUnservedAirExtensions}.
19
20
  */
20
21
  export declare function v2InitializeResponse(response: InitializeResponse): v2.InitializeResponse;
22
+ /**
23
+ * The session capabilities of the v2 surface, which serves each through a v1
24
+ * method of the agent.
25
+ *
26
+ * - The session baseline: `session/new`, `list`, `resume` (with replay),
27
+ * `close`, `prompt`, `cancel`, and `update`.
28
+ * - `session/delete`, `session/fork` (which returns a session the client can
29
+ * prompt at once), and `additionalDirectories`.
30
+ * - The prompt content and MCP transports that the agent's v1 `initialize`
31
+ * lists: images, embedded context, and HTTP servers. Stdio servers are in the
32
+ * v1 baseline; v2 has no SSE transport.
33
+ * - `systemPrompt`, in `append` and `override` mode.
34
+ */
35
+ export declare const V2_SESSION_CAPABILITIES: v2.SessionCapabilities;
21
36
  //# sourceMappingURL=initialize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../src/v2/initialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,mDAAmD,CAAC;AAGxE,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,CAmCpF;AASD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,EAAE,CAAC,kBAAkB,CAuBxF"}
1
+ {"version":3,"file":"initialize.d.ts","sourceRoot":"","sources":["../../src/v2/initialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,MAAM,mDAAmD,CAAC;AAIxE,0DAA0D;AAC1D,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,GAAG,iBAAiB,CAmCpF;AASD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,EAAE,CAAC,kBAAkB,CA2BxF;AA0BD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,EAAE,EAAE,CAAC,mBAOxC,CAAC"}