@posthog/agent 2.3.727 → 2.3.736

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.
Files changed (63) hide show
  1. package/dist/adapters/claude/conversion/tool-use-to-acp.d.ts +2 -10
  2. package/dist/adapters/claude/conversion/tool-use-to-acp.js +24 -10
  3. package/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
  4. package/dist/adapters/claude/permissions/permission-options.js +6 -4
  5. package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
  6. package/dist/adapters/claude/session/jsonl-hydration.js +1 -0
  7. package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -1
  8. package/dist/adapters/claude/session/models.js +20 -1
  9. package/dist/adapters/claude/session/models.js.map +1 -1
  10. package/dist/adapters/claude/tools.js +4 -2
  11. package/dist/adapters/claude/tools.js.map +1 -1
  12. package/dist/adapters/reasoning-effort.js +3 -1
  13. package/dist/adapters/reasoning-effort.js.map +1 -1
  14. package/dist/agent.js +544 -74
  15. package/dist/agent.js.map +1 -1
  16. package/dist/posthog-api.js +4 -4
  17. package/dist/posthog-api.js.map +1 -1
  18. package/dist/server/agent-server.js +544 -74
  19. package/dist/server/agent-server.js.map +1 -1
  20. package/dist/server/bin.cjs +544 -74
  21. package/dist/server/bin.cjs.map +1 -1
  22. package/package.json +4 -4
  23. package/src/adapters/claude/UPSTREAM.md +81 -3
  24. package/src/adapters/claude/claude-agent.slash-command.test.ts +22 -2
  25. package/src/adapters/claude/claude-agent.ts +232 -19
  26. package/src/adapters/claude/conversion/sdk-to-acp.ts +182 -16
  27. package/src/adapters/claude/conversion/task-state.test.ts +338 -0
  28. package/src/adapters/claude/conversion/task-state.ts +178 -0
  29. package/src/adapters/claude/conversion/tool-use-to-acp.ts +29 -19
  30. package/src/adapters/claude/hooks.test.ts +162 -0
  31. package/src/adapters/claude/hooks.ts +44 -3
  32. package/src/adapters/claude/permissions/permission-options.ts +7 -2
  33. package/src/adapters/claude/session/commands.ts +1 -0
  34. package/src/adapters/claude/session/mcp-config.ts +23 -7
  35. package/src/adapters/claude/session/model-config.test.ts +60 -0
  36. package/src/adapters/claude/session/model-config.ts +56 -0
  37. package/src/adapters/claude/session/models.test.ts +119 -0
  38. package/src/adapters/claude/session/models.ts +31 -0
  39. package/src/adapters/claude/session/options.test.ts +1 -0
  40. package/src/adapters/claude/session/options.ts +29 -1
  41. package/src/adapters/claude/session/settings.test.ts +102 -1
  42. package/src/adapters/claude/session/settings.ts +33 -6
  43. package/src/adapters/claude/tools.ts +4 -2
  44. package/src/adapters/claude/types.ts +15 -0
  45. package/src/adapters/codex/codex-agent.ts +2 -2
  46. package/src/server/agent-server.test.ts +173 -0
  47. package/src/test/mocks/claude-sdk.ts +4 -0
  48. package/src/test/native-binary.test.ts +27 -0
  49. package/dist/claude-cli/cli.js +0 -18412
  50. package/dist/claude-cli/vendor/audio-capture/arm64-darwin/audio-capture.node +0 -0
  51. package/dist/claude-cli/vendor/audio-capture/arm64-linux/audio-capture.node +0 -0
  52. package/dist/claude-cli/vendor/audio-capture/arm64-win32/audio-capture.node +0 -0
  53. package/dist/claude-cli/vendor/audio-capture/x64-darwin/audio-capture.node +0 -0
  54. package/dist/claude-cli/vendor/audio-capture/x64-linux/audio-capture.node +0 -0
  55. package/dist/claude-cli/vendor/audio-capture/x64-win32/audio-capture.node +0 -0
  56. package/dist/claude-cli/vendor/ripgrep/COPYING +0 -3
  57. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
  58. package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
  59. package/dist/claude-cli/vendor/ripgrep/arm64-win32/rg.exe +0 -0
  60. package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
  61. package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
  62. package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
  63. package/dist/claude-cli/yoga.wasm +0 -0
package/dist/agent.js CHANGED
@@ -4674,7 +4674,7 @@ import { v7 as uuidv7 } from "uuid";
4674
4674
  // package.json
4675
4675
  var package_default = {
4676
4676
  name: "@posthog/agent",
4677
- version: "2.3.727",
4677
+ version: "2.3.736",
4678
4678
  repository: "https://github.com/PostHog/code",
4679
4679
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
4680
4680
  exports: {
@@ -4794,9 +4794,9 @@ var package_default = {
4794
4794
  vitest: "^2.1.8"
4795
4795
  },
4796
4796
  dependencies: {
4797
- "@agentclientprotocol/sdk": "0.19.0",
4798
- "@anthropic-ai/claude-agent-sdk": "0.2.112",
4799
- "@anthropic-ai/sdk": "0.89.0",
4797
+ "@agentclientprotocol/sdk": "0.22.1",
4798
+ "@anthropic-ai/claude-agent-sdk": "0.3.154",
4799
+ "@anthropic-ai/sdk": "0.100.0",
4800
4800
  "@hono/node-server": "^1.19.9",
4801
4801
  "@opentelemetry/api-logs": "^0.208.0",
4802
4802
  "@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
@@ -10183,6 +10183,30 @@ var registerHookCallback = (toolUseID, {
10183
10183
  onPostToolUseHook
10184
10184
  };
10185
10185
  };
10186
+ var createTaskHook = (taskState, onChange) => async (input) => {
10187
+ const taskId = "task_id" in input && typeof input.task_id === "string" ? input.task_id : void 0;
10188
+ if (!taskId) return { continue: true };
10189
+ let mutated = false;
10190
+ if (input.hook_event_name === "TaskCreated") {
10191
+ if (!input.task_subject) return { continue: true };
10192
+ if (taskState.has(taskId)) return { continue: true };
10193
+ taskState.set(taskId, {
10194
+ subject: input.task_subject,
10195
+ status: "pending",
10196
+ description: input.task_description
10197
+ });
10198
+ mutated = true;
10199
+ } else if (input.hook_event_name === "TaskCompleted") {
10200
+ const existing = taskState.get(taskId);
10201
+ if (!existing || existing.status === "completed") {
10202
+ return { continue: true };
10203
+ }
10204
+ taskState.set(taskId, { ...existing, status: "completed" });
10205
+ mutated = true;
10206
+ }
10207
+ if (mutated && onChange) await onChange();
10208
+ return { continue: true };
10209
+ };
10186
10210
  var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
10187
10211
  if (input.hook_event_name === "PostToolUse") {
10188
10212
  const toolName = input.tool_name;
@@ -10197,10 +10221,8 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
10197
10221
  input.tool_input,
10198
10222
  input.tool_response
10199
10223
  );
10200
- delete toolUseCallbacks[toolUseID];
10201
- } else {
10202
- delete toolUseCallbacks[toolUseID];
10203
10224
  }
10225
+ delete toolUseCallbacks[toolUseID];
10204
10226
  }
10205
10227
  }
10206
10228
  return { continue: true };
@@ -10347,6 +10369,104 @@ var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID
10347
10369
  }
10348
10370
  };
10349
10371
 
10372
+ // src/adapters/claude/conversion/task-state.ts
10373
+ function parseTaskCreateOutput(content) {
10374
+ const tryParse = (text2) => {
10375
+ try {
10376
+ const parsed = JSON.parse(text2);
10377
+ if (parsed && typeof parsed === "object" && parsed.task && typeof parsed.task.id === "string") {
10378
+ return parsed;
10379
+ }
10380
+ } catch {
10381
+ }
10382
+ return void 0;
10383
+ };
10384
+ if (typeof content === "string") {
10385
+ return tryParse(content);
10386
+ }
10387
+ if (Array.isArray(content)) {
10388
+ for (const block of content) {
10389
+ if (block && typeof block === "object" && "type" in block && block.type === "text") {
10390
+ const text2 = block.text;
10391
+ if (typeof text2 === "string") {
10392
+ const parsed = tryParse(text2);
10393
+ if (parsed) return parsed;
10394
+ }
10395
+ }
10396
+ }
10397
+ }
10398
+ return void 0;
10399
+ }
10400
+ function applyTaskCreate(state, input, output) {
10401
+ const taskId = output?.task?.id;
10402
+ if (!taskId || !input) return;
10403
+ state.set(taskId, {
10404
+ subject: input.subject,
10405
+ status: "pending",
10406
+ activeForm: input.activeForm,
10407
+ description: input.description
10408
+ });
10409
+ }
10410
+ function applyTaskUpdate(state, input) {
10411
+ if (!input?.taskId) return;
10412
+ if (input.status === "deleted") {
10413
+ state.delete(input.taskId);
10414
+ return;
10415
+ }
10416
+ const existing = state.get(input.taskId);
10417
+ const subject = input.subject ?? existing?.subject;
10418
+ if (!subject) return;
10419
+ state.set(input.taskId, {
10420
+ subject,
10421
+ status: input.status ?? existing?.status ?? "pending",
10422
+ activeForm: input.activeForm ?? existing?.activeForm,
10423
+ description: input.description ?? existing?.description
10424
+ });
10425
+ }
10426
+ function taskStateToPlanEntries(state) {
10427
+ return Array.from(state.values()).map((task) => ({
10428
+ content: task.subject,
10429
+ status: task.status,
10430
+ priority: "medium"
10431
+ }));
10432
+ }
10433
+ function isToolUseBlock(block) {
10434
+ return !!block && typeof block === "object" && block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string";
10435
+ }
10436
+ function isToolResultBlock(block) {
10437
+ return !!block && typeof block === "object" && block.type === "tool_result" && typeof block.tool_use_id === "string";
10438
+ }
10439
+ function rehydrateTaskState(messages, state) {
10440
+ const pendingInputs = /* @__PURE__ */ new Map();
10441
+ for (const msg of messages) {
10442
+ const content = msg.message?.content;
10443
+ if (!Array.isArray(content)) continue;
10444
+ if (msg.type === "assistant") {
10445
+ for (const block of content) {
10446
+ if (isToolUseBlock(block) && (block.name === "TaskCreate" || block.name === "TaskUpdate")) {
10447
+ pendingInputs.set(block.id, { name: block.name, input: block.input });
10448
+ }
10449
+ }
10450
+ } else if (msg.type === "user") {
10451
+ for (const block of content) {
10452
+ if (!isToolResultBlock(block) || block.is_error) continue;
10453
+ const cached = pendingInputs.get(block.tool_use_id);
10454
+ if (!cached) continue;
10455
+ if (cached.name === "TaskCreate") {
10456
+ applyTaskCreate(
10457
+ state,
10458
+ cached.input,
10459
+ parseTaskCreateOutput(block.content)
10460
+ );
10461
+ } else if (cached.name === "TaskUpdate") {
10462
+ applyTaskUpdate(state, cached.input);
10463
+ }
10464
+ pendingInputs.delete(block.tool_use_id);
10465
+ }
10466
+ }
10467
+ }
10468
+ }
10469
+
10350
10470
  // src/adapters/claude/conversion/tool-use-to-acp.ts
10351
10471
  import fs4 from "fs";
10352
10472
  import path7 from "path";
@@ -10698,9 +10818,31 @@ function toolInfoFromToolUse(toolUse, options) {
10698
10818
  content: []
10699
10819
  };
10700
10820
  }
10701
- case "TodoWrite":
10821
+ case "TaskCreate": {
10822
+ const subject = typeof input?.subject === "string" ? input.subject : void 0;
10823
+ return {
10824
+ title: subject ? `Create task: ${subject}` : "Create task",
10825
+ kind: "think",
10826
+ content: []
10827
+ };
10828
+ }
10829
+ case "TaskUpdate": {
10830
+ const subject = typeof input?.subject === "string" ? input.subject : void 0;
10702
10831
  return {
10703
- title: Array.isArray(input?.todos) ? `Update TODOs: ${input.todos.map((todo) => todo.content).join(", ")}` : "Update TODOs",
10832
+ title: subject ? `Update task: ${subject}` : "Update task",
10833
+ kind: "think",
10834
+ content: []
10835
+ };
10836
+ }
10837
+ case "TaskList":
10838
+ return {
10839
+ title: "List tasks",
10840
+ kind: "think",
10841
+ content: []
10842
+ };
10843
+ case "TaskGet":
10844
+ return {
10845
+ title: "Get task",
10704
10846
  kind: "think",
10705
10847
  content: []
10706
10848
  };
@@ -10984,13 +11126,6 @@ ${content}
10984
11126
  }
10985
11127
  return {};
10986
11128
  }
10987
- function planEntries(input) {
10988
- return input.todos.map((input2) => ({
10989
- content: input2.content,
10990
- status: input2.status,
10991
- priority: "medium"
10992
- }));
10993
- }
10994
11129
  function resolveFileContent(filePath, oldText, cachedFileContent) {
10995
11130
  if (cachedFileContent && filePath in cachedFileContent) {
10996
11131
  const cached = cachedFileContent[filePath];
@@ -11076,14 +11211,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
11076
11211
  function handleToolUseChunk(chunk, ctx) {
11077
11212
  const alreadyCached = chunk.id in ctx.toolUseCache;
11078
11213
  ctx.toolUseCache[chunk.id] = chunk;
11079
- if (chunk.name === "TodoWrite") {
11080
- const input = chunk.input;
11081
- if (Array.isArray(input.todos)) {
11082
- return {
11083
- sessionUpdate: "plan",
11084
- entries: planEntries(chunk.input)
11085
- };
11086
- }
11214
+ if (chunk.name === "TaskCreate" || chunk.name === "TaskUpdate" || chunk.name === "TaskList" || chunk.name === "TaskGet") {
11087
11215
  return null;
11088
11216
  }
11089
11217
  if (!alreadyCached && ctx.registerHooks !== false) {
@@ -11091,7 +11219,7 @@ function handleToolUseChunk(chunk, ctx) {
11091
11219
  onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
11092
11220
  const toolUse = ctx.toolUseCache[toolUseId];
11093
11221
  if (toolUse) {
11094
- const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
11222
+ const editUpdate = toolUse.name === "Edit" || toolUse.name === "Write" ? toolUpdateFromEditToolResponse(toolResponse) : null;
11095
11223
  await ctx.client.sessionUpdate({
11096
11224
  sessionId: ctx.sessionId,
11097
11225
  update: {
@@ -11203,7 +11331,28 @@ function handleToolResultChunk(chunk, ctx) {
11203
11331
  );
11204
11332
  return [];
11205
11333
  }
11206
- if (toolUse.name === "TodoWrite") {
11334
+ if (toolUse.name === "TaskCreate" || toolUse.name === "TaskUpdate" || toolUse.name === "TaskList" || toolUse.name === "TaskGet") {
11335
+ if (chunk.is_error || !ctx.taskState) return [];
11336
+ if (toolUse.name === "TaskCreate") {
11337
+ applyTaskCreate(
11338
+ ctx.taskState,
11339
+ toolUse.input,
11340
+ parseTaskCreateOutput(chunk.content)
11341
+ );
11342
+ } else if (toolUse.name === "TaskUpdate") {
11343
+ applyTaskUpdate(
11344
+ ctx.taskState,
11345
+ toolUse.input
11346
+ );
11347
+ }
11348
+ if (toolUse.name === "TaskCreate" || toolUse.name === "TaskUpdate") {
11349
+ return [
11350
+ {
11351
+ sessionUpdate: "plan",
11352
+ entries: taskStateToPlanEntries(ctx.taskState)
11353
+ }
11354
+ ];
11355
+ }
11207
11356
  return [];
11208
11357
  }
11209
11358
  if (!chunk.is_error) {
@@ -11300,13 +11449,15 @@ function processContentChunk(chunk, role, ctx) {
11300
11449
  case "container_upload":
11301
11450
  case "compaction":
11302
11451
  case "compaction_delta":
11452
+ case "advisor_tool_result":
11453
+ case "mid_conv_system":
11303
11454
  return [];
11304
11455
  default:
11305
11456
  unreachable(chunk, ctx.logger);
11306
11457
  return [];
11307
11458
  }
11308
11459
  }
11309
- function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, mcpToolUseResult, enrichedReadCache) {
11460
+ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, mcpToolUseResult, enrichedReadCache, taskState) {
11310
11461
  if (typeof content === "string") {
11311
11462
  const update = {
11312
11463
  sessionUpdate: messageUpdateType(role),
@@ -11332,7 +11483,8 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
11332
11483
  registerHooks,
11333
11484
  supportsTerminalOutput,
11334
11485
  cwd,
11335
- mcpToolUseResult
11486
+ mcpToolUseResult,
11487
+ taskState
11336
11488
  };
11337
11489
  const output = [];
11338
11490
  for (const chunk of content) {
@@ -11342,7 +11494,7 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
11342
11494
  }
11343
11495
  return output;
11344
11496
  }
11345
- function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUseStreamCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, enrichedReadCache) {
11497
+ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUseStreamCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, enrichedReadCache, taskState) {
11346
11498
  const event = message.event;
11347
11499
  switch (event.type) {
11348
11500
  case "content_block_start": {
@@ -11366,7 +11518,8 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUse
11366
11518
  supportsTerminalOutput,
11367
11519
  cwd,
11368
11520
  void 0,
11369
- enrichedReadCache
11521
+ enrichedReadCache,
11522
+ taskState
11370
11523
  );
11371
11524
  }
11372
11525
  case "content_block_delta": {
@@ -11391,12 +11544,18 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUse
11391
11544
  supportsTerminalOutput,
11392
11545
  cwd,
11393
11546
  void 0,
11394
- enrichedReadCache
11547
+ enrichedReadCache,
11548
+ taskState
11395
11549
  );
11396
11550
  }
11397
11551
  case "content_block_stop":
11398
11552
  toolUseStreamCache.delete(event.index);
11399
11553
  return [];
11554
+ // `ping` is a Messages-API keep-alive event that the SDK's
11555
+ // `BetaRawMessageStreamEvent` union doesn't include even though the
11556
+ // wire format emits it; the `as never` cast lets us no-op it here
11557
+ // instead of falling through to `unreachable`.
11558
+ case "ping":
11400
11559
  case "message_start":
11401
11560
  case "message_delta":
11402
11561
  case "message_stop":
@@ -11467,6 +11626,38 @@ async function handleSystemMessage(message, context) {
11467
11626
  });
11468
11627
  break;
11469
11628
  }
11629
+ case "memory_recall": {
11630
+ const isSynthesis = message.mode === "synthesize";
11631
+ if (!isSynthesis && message.memories.length === 0) break;
11632
+ const locations = isSynthesis ? [] : message.memories.map((m) => ({ path: m.path }));
11633
+ const content = isSynthesis ? message.memories.filter(
11634
+ (m) => typeof m.content === "string"
11635
+ ).map((m) => ({
11636
+ type: "content",
11637
+ content: { type: "text", text: m.content }
11638
+ })) : [];
11639
+ const count = message.memories.length;
11640
+ const title = isSynthesis ? "Recalled synthesized memory" : `Recalled ${count} ${count === 1 ? "memory" : "memories"}`;
11641
+ await client.sessionUpdate({
11642
+ sessionId: message.session_id,
11643
+ update: {
11644
+ sessionUpdate: "tool_call",
11645
+ toolCallId: message.uuid,
11646
+ title,
11647
+ kind: "read",
11648
+ status: "completed",
11649
+ ...locations.length > 0 && { locations },
11650
+ ...content.length > 0 && { content },
11651
+ _meta: {
11652
+ claudeCode: {
11653
+ toolName: "memory_recall",
11654
+ toolResponse: { mode: message.mode }
11655
+ }
11656
+ }
11657
+ }
11658
+ });
11659
+ break;
11660
+ }
11470
11661
  default:
11471
11662
  break;
11472
11663
  }
@@ -11576,7 +11767,8 @@ async function handleStreamEvent(message, context) {
11576
11767
  context.registerHooks,
11577
11768
  context.supportsTerminalOutput,
11578
11769
  context.session.cwd,
11579
- context.enrichedReadCache
11770
+ context.enrichedReadCache,
11771
+ context.session.taskState
11580
11772
  )) {
11581
11773
  await client.sessionUpdate(notification);
11582
11774
  context.session.notificationHistory.push(notification);
@@ -11588,6 +11780,17 @@ function hasLocalCommandStdout(content) {
11588
11780
  function hasLocalCommandStderr(content) {
11589
11781
  return typeof content === "string" && content.includes("<local-command-stderr>");
11590
11782
  }
11783
+ function isSdkLocalCommandMessage(content) {
11784
+ return typeof content === "string" && content.includes("<command-name>") && (content.includes("<local-command-stdout>") || content.includes("<local-command-stderr>"));
11785
+ }
11786
+ var LOCAL_COMMAND_TAG_PATTERN = /<(command-name|command-message|command-args|local-command-stdout|local-command-stderr)>[\s\S]*?<\/\1>/g;
11787
+ function stripMarkerTags(text2) {
11788
+ return text2.replace(LOCAL_COMMAND_TAG_PATTERN, "");
11789
+ }
11790
+ function stripLocalCommandMetadata(content) {
11791
+ const stripped = stripMarkerTags(content);
11792
+ return stripped.trim() === "" ? null : stripped;
11793
+ }
11591
11794
  function isLoginRequiredMessage(message) {
11592
11795
  return message.type === "assistant" && message.message.model === "<synthetic>" && Array.isArray(message.message.content) && message.message.content.length === 1 && message.message.content[0].type === "text" && message.message.content[0].text?.includes("Please run /login") === true;
11593
11796
  }
@@ -11596,7 +11799,7 @@ function isPlainTextUserMessage(message) {
11596
11799
  return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
11597
11800
  }
11598
11801
  function shouldSkipUserAssistantMessage(message) {
11599
- return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
11802
+ return isSdkLocalCommandMessage(message.message.content) || isLoginRequiredMessage(message);
11600
11803
  }
11601
11804
  function logSpecialMessages(message, logger) {
11602
11805
  const content = message.message.content;
@@ -11617,11 +11820,39 @@ function filterMessageContent(content) {
11617
11820
  }
11618
11821
  async function handleUserAssistantMessage(message, context) {
11619
11822
  const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
11823
+ if (message.message.role === "system") {
11824
+ return {};
11825
+ }
11620
11826
  if (shouldSkipUserAssistantMessage(message)) {
11621
11827
  logSpecialMessages(message, logger);
11622
11828
  if (isLoginRequiredMessage(message)) {
11623
11829
  return { shouldStop: true, error: RequestError.authRequired() };
11624
11830
  }
11831
+ const rawContent = message.message.content;
11832
+ if (typeof rawContent === "string") {
11833
+ const stripped = stripLocalCommandMetadata(rawContent);
11834
+ if (stripped !== null) {
11835
+ for (const notification of toAcpNotifications(
11836
+ stripped,
11837
+ message.message.role,
11838
+ sessionId,
11839
+ toolUseCache,
11840
+ fileContentCache,
11841
+ client,
11842
+ logger,
11843
+ void 0,
11844
+ context.registerHooks,
11845
+ context.supportsTerminalOutput,
11846
+ session.cwd,
11847
+ void 0,
11848
+ context.enrichedReadCache,
11849
+ session.taskState
11850
+ )) {
11851
+ await client.sessionUpdate(notification);
11852
+ session.notificationHistory.push(notification);
11853
+ }
11854
+ }
11855
+ }
11625
11856
  return {};
11626
11857
  }
11627
11858
  if (isPlainTextUserMessage(message)) {
@@ -11644,7 +11875,8 @@ async function handleUserAssistantMessage(message, context) {
11644
11875
  context.supportsTerminalOutput,
11645
11876
  session.cwd,
11646
11877
  mcpToolUseResult,
11647
- context.enrichedReadCache
11878
+ context.enrichedReadCache,
11879
+ session.taskState
11648
11880
  )) {
11649
11881
  await client.sessionUpdate(notification);
11650
11882
  session.notificationHistory.push(notification);
@@ -11840,8 +12072,10 @@ var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
11840
12072
  var AGENT_TOOLS = /* @__PURE__ */ new Set([
11841
12073
  "Task",
11842
12074
  "Agent",
11843
- "TodoWrite",
11844
- "Skill"
12075
+ "TaskCreate",
12076
+ "TaskUpdate",
12077
+ "TaskGet",
12078
+ "TaskList"
11845
12079
  ]);
11846
12080
  var BASE_ALLOWED_TOOLS = [
11847
12081
  ...READ_TOOLS,
@@ -11925,8 +12159,8 @@ function buildPermissionOptions(toolName, toolInput, repoRoot, suggestions) {
11925
12159
  if (toolName === "Task") {
11926
12160
  return permissionOptions("Yes, allow all sub-tasks");
11927
12161
  }
11928
- if (toolName === "TodoWrite") {
11929
- return permissionOptions("Yes, allow all todo updates");
12162
+ if (toolName === "TaskCreate" || toolName === "TaskUpdate" || toolName === "TaskGet" || toolName === "TaskList") {
12163
+ return permissionOptions("Yes, allow all task updates");
11930
12164
  }
11931
12165
  return permissionOptions("Yes, always allow");
11932
12166
  }
@@ -12478,6 +12712,7 @@ async function canUseTool(context) {
12478
12712
 
12479
12713
  // src/adapters/claude/session/commands.ts
12480
12714
  var UNSUPPORTED_COMMANDS = [
12715
+ "clear",
12481
12716
  "context",
12482
12717
  "cost",
12483
12718
  "keybindings-help",
@@ -12532,18 +12767,30 @@ function loadUserClaudeJsonMcpServers(cwd, logger, homeDir = os3.homedir()) {
12532
12767
  const projectScoped = project?.mcpServers && typeof project.mcpServers === "object" ? project.mcpServers : {};
12533
12768
  return { ...topLevel, ...projectScoped };
12534
12769
  }
12535
- function parseMcpServers(params) {
12770
+ function parseMcpServers(params, logger) {
12536
12771
  const mcpServers = {};
12537
12772
  if (!Array.isArray(params.mcpServers)) {
12538
12773
  return mcpServers;
12539
12774
  }
12540
12775
  for (const server of params.mcpServers) {
12541
12776
  if ("type" in server) {
12542
- mcpServers[server.name] = {
12543
- type: server.type,
12544
- url: server.url,
12545
- headers: server.headers ? Object.fromEntries(server.headers.map((e) => [e.name, e.value])) : void 0
12546
- };
12777
+ if (server.type === "http" || server.type === "sse") {
12778
+ mcpServers[server.name] = {
12779
+ type: server.type,
12780
+ url: server.url,
12781
+ headers: server.headers ? Object.fromEntries(
12782
+ server.headers.map((e) => [
12783
+ e.name,
12784
+ e.value
12785
+ ])
12786
+ ) : void 0
12787
+ };
12788
+ } else {
12789
+ logger?.warn("parseMcpServers: dropping unsupported MCP server type", {
12790
+ name: server.name,
12791
+ type: server.type
12792
+ });
12793
+ }
12547
12794
  } else {
12548
12795
  mcpServers[server.name] = {
12549
12796
  type: "stdio",
@@ -12556,12 +12803,43 @@ function parseMcpServers(params) {
12556
12803
  return mcpServers;
12557
12804
  }
12558
12805
 
12806
+ // src/adapters/claude/session/model-config.ts
12807
+ function applyAvailableModelsAllowlist(modelOptions, allowlist) {
12808
+ const filtered = [];
12809
+ const seen = /* @__PURE__ */ new Set();
12810
+ for (const entry of allowlist) {
12811
+ const trimmed2 = entry.trim();
12812
+ if (!trimmed2 || seen.has(trimmed2)) continue;
12813
+ const match = modelOptions.options.find((o) => o.value === trimmed2);
12814
+ if (match) {
12815
+ filtered.push(match);
12816
+ seen.add(trimmed2);
12817
+ }
12818
+ }
12819
+ if (filtered.length === 0) return modelOptions;
12820
+ const currentModelId = filtered.some(
12821
+ (o) => o.value === modelOptions.currentModelId
12822
+ ) ? modelOptions.currentModelId : filtered[0].value;
12823
+ return { currentModelId, options: filtered };
12824
+ }
12825
+ function resolveInitialModelId(modelOptions, preferredModelIds) {
12826
+ const allowedModelIds = new Set(modelOptions.options.map((opt) => opt.value));
12827
+ for (const candidate of preferredModelIds) {
12828
+ const trimmed2 = candidate?.trim();
12829
+ if (trimmed2 && allowedModelIds.has(trimmed2)) {
12830
+ return trimmed2;
12831
+ }
12832
+ }
12833
+ return modelOptions.currentModelId;
12834
+ }
12835
+
12559
12836
  // src/adapters/claude/session/models.ts
12560
12837
  var DEFAULT_MODEL = "opus";
12561
12838
  var GATEWAY_TO_SDK_MODEL = {
12562
12839
  "claude-opus-4-5": "opus",
12563
12840
  "claude-opus-4-6": "opus",
12564
12841
  "claude-opus-4-7": "opus",
12842
+ "claude-opus-4-8": "opus",
12565
12843
  "claude-sonnet-4-5": "sonnet",
12566
12844
  "claude-sonnet-4-6": "sonnet",
12567
12845
  "claude-haiku-4-5": "haiku"
@@ -12572,6 +12850,7 @@ function toSdkModelId(modelId) {
12572
12850
  var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
12573
12851
  "claude-opus-4-6",
12574
12852
  "claude-opus-4-7",
12853
+ "claude-opus-4-8",
12575
12854
  "claude-sonnet-4-6"
12576
12855
  ]);
12577
12856
  function supports1MContext(modelId) {
@@ -12581,11 +12860,13 @@ var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
12581
12860
  "claude-opus-4-5",
12582
12861
  "claude-opus-4-6",
12583
12862
  "claude-opus-4-7",
12863
+ "claude-opus-4-8",
12584
12864
  "claude-sonnet-4-6"
12585
12865
  ]);
12586
12866
  var MODELS_WITH_XHIGH_EFFORT = /* @__PURE__ */ new Set([
12587
12867
  "claude-opus-4-6",
12588
- "claude-opus-4-7"
12868
+ "claude-opus-4-7",
12869
+ "claude-opus-4-8"
12589
12870
  ]);
12590
12871
  function supportsEffort(modelId) {
12591
12872
  return MODELS_WITH_EFFORT.has(modelId);
@@ -12625,6 +12906,19 @@ function tokenizeModelPreference(model) {
12625
12906
  }).filter((token) => token && token !== "claude").filter((token) => /[a-z]/.test(token) || token.endsWith("m"));
12626
12907
  return { tokens, contextHint };
12627
12908
  }
12909
+ var MODEL_FAMILY_VERSION_PATTERN = /\b(\d+)[-.](\d+)\b/;
12910
+ function extractModelFamilyVersion(s) {
12911
+ if (!s) return null;
12912
+ const match = s.match(MODEL_FAMILY_VERSION_PATTERN);
12913
+ return match ? `${match[1]}.${match[2]}` : null;
12914
+ }
12915
+ function modelVersionsCompatible(preference, candidate) {
12916
+ const preferred = extractModelFamilyVersion(preference);
12917
+ if (!preferred) return true;
12918
+ const candidateVersion = extractModelFamilyVersion(candidate.value) ?? extractModelFamilyVersion(candidate.name) ?? extractModelFamilyVersion(candidate.description);
12919
+ if (!candidateVersion) return true;
12920
+ return preferred === candidateVersion;
12921
+ }
12628
12922
  function scoreModelMatch(model, tokens, contextHint) {
12629
12923
  const haystack = `${model.value} ${model.name ?? ""}`.toLowerCase();
12630
12924
  let score = 0;
@@ -12644,6 +12938,7 @@ function resolveModelPreference(preference, options) {
12644
12938
  );
12645
12939
  if (directMatch) return directMatch.value;
12646
12940
  const includesMatch = options.find((o) => {
12941
+ if (!modelVersionsCompatible(trimmed2, o)) return false;
12647
12942
  const value = o.value.toLowerCase();
12648
12943
  const display = (o.name ?? "").toLowerCase();
12649
12944
  return value.includes(lower) || display.includes(lower) || lower.includes(value);
@@ -12654,6 +12949,7 @@ function resolveModelPreference(preference, options) {
12654
12949
  let bestMatch = null;
12655
12950
  let bestScore = 0;
12656
12951
  for (const model of options) {
12952
+ if (!modelVersionsCompatible(trimmed2, model)) continue;
12657
12953
  const score = scoreModelMatch(model, tokens, contextHint);
12658
12954
  if (0 < score && (!bestMatch || bestScore < score)) {
12659
12955
  bestMatch = model;
@@ -12730,6 +13026,7 @@ function buildEnvironment() {
12730
13026
  const existingCustomHeaders = process.env.ANTHROPIC_CUSTOM_HEADERS;
12731
13027
  const customHeaders = existingCustomHeaders ? `${existingCustomHeaders}
12732
13028
  ${bedrockFallbackHeader}` : bedrockFallbackHeader;
13029
+ const mcpNonblocking = process.env.MCP_CONNECTION_NONBLOCKING;
12733
13030
  return {
12734
13031
  ...process.env,
12735
13032
  ELECTRON_RUN_AS_NODE: "1",
@@ -12738,11 +13035,14 @@ ${bedrockFallbackHeader}` : bedrockFallbackHeader;
12738
13035
  ENABLE_TOOL_SEARCH: "auto:0",
12739
13036
  // Enable idle state as end-of-turn signal (required for SDK 0.2.114+)
12740
13037
  CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: "1",
13038
+ ...mcpNonblocking !== void 0 && {
13039
+ MCP_CONNECTION_NONBLOCKING: mcpNonblocking
13040
+ },
12741
13041
  // Route to AWS Bedrock as a fallback when Anthropic returns 5xx
12742
13042
  ANTHROPIC_CUSTOM_HEADERS: customHeaders
12743
13043
  };
12744
13044
  }
12745
- function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache, registeredAgents, cloudMode) {
13045
+ function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache, registeredAgents, cloudMode, taskState, onTaskStateChange) {
12746
13046
  const postToolUseHooks = [createPostToolUseHook({ onModeChange })];
12747
13047
  if (enrichmentDeps && enrichedReadCache) {
12748
13048
  postToolUseHooks.push(
@@ -12756,13 +13056,16 @@ function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichment
12756
13056
  if (cloudMode) {
12757
13057
  preToolUseHooks.push(createSignedCommitGuardHook(logger));
12758
13058
  }
13059
+ const taskHook = createTaskHook(taskState, onTaskStateChange);
12759
13060
  return {
12760
13061
  ...userHooks,
12761
13062
  PostToolUse: [
12762
13063
  ...userHooks?.PostToolUse || [],
12763
13064
  { hooks: postToolUseHooks }
12764
13065
  ],
12765
- PreToolUse: [...userHooks?.PreToolUse || [], { hooks: preToolUseHooks }]
13066
+ PreToolUse: [...userHooks?.PreToolUse || [], { hooks: preToolUseHooks }],
13067
+ TaskCreated: [...userHooks?.TaskCreated || [], { hooks: [taskHook] }],
13068
+ TaskCompleted: [...userHooks?.TaskCompleted || [], { hooks: [taskHook] }]
12766
13069
  };
12767
13070
  }
12768
13071
  var PH_EXPLORE_AGENT = {
@@ -12793,7 +13096,10 @@ Rules:
12793
13096
  "WebFetch",
12794
13097
  "WebSearch",
12795
13098
  "NotebookRead",
12796
- "TodoWrite"
13099
+ "TaskCreate",
13100
+ "TaskUpdate",
13101
+ "TaskGet",
13102
+ "TaskList"
12797
13103
  ]
12798
13104
  };
12799
13105
  function buildAgents(userAgents) {
@@ -12921,7 +13227,9 @@ function buildSessionOptions(params) {
12921
13227
  params.enrichmentDeps,
12922
13228
  params.enrichedReadCache,
12923
13229
  registeredAgentNames,
12924
- params.cloudMode ?? false
13230
+ params.cloudMode ?? false,
13231
+ params.taskState,
13232
+ params.onTaskStateChange
12925
13233
  ),
12926
13234
  outputFormat: params.outputFormat,
12927
13235
  abortController: getAbortController(
@@ -17957,6 +18265,15 @@ function getManagedSettingsPath() {
17957
18265
  return "/etc/claude-code/managed-settings.json";
17958
18266
  }
17959
18267
  }
18268
+ function mergeAvailableModels(existing, incoming, layer) {
18269
+ if (incoming === void 0) {
18270
+ return existing;
18271
+ }
18272
+ if (layer === "enterprise") {
18273
+ return Array.from(new Set(incoming));
18274
+ }
18275
+ return Array.from(/* @__PURE__ */ new Set([...existing ?? [], ...incoming]));
18276
+ }
17960
18277
  var SettingsManager = class {
17961
18278
  cwd;
17962
18279
  repoRoot;
@@ -18012,10 +18329,10 @@ var SettingsManager = class {
18012
18329
  }
18013
18330
  mergeAllSettings() {
18014
18331
  const allSettings = [
18015
- this.userSettings,
18016
- this.projectSettings,
18017
- this.localSettings,
18018
- this.enterpriseSettings
18332
+ { layer: "user", settings: this.userSettings },
18333
+ { layer: "project", settings: this.projectSettings },
18334
+ { layer: "local", settings: this.localSettings },
18335
+ { layer: "enterprise", settings: this.enterpriseSettings }
18019
18336
  ];
18020
18337
  const permissions = {
18021
18338
  allow: [],
@@ -18024,7 +18341,7 @@ var SettingsManager = class {
18024
18341
  };
18025
18342
  const merged = { permissions };
18026
18343
  const posthogApprovedExecTools = /* @__PURE__ */ new Set();
18027
- for (const settings of allSettings) {
18344
+ for (const { layer, settings } of allSettings) {
18028
18345
  if (settings.permissions) {
18029
18346
  if (settings.permissions.allow) {
18030
18347
  permissions.allow?.push(...settings.permissions.allow);
@@ -18051,6 +18368,11 @@ var SettingsManager = class {
18051
18368
  if (settings.model) {
18052
18369
  merged.model = settings.model;
18053
18370
  }
18371
+ merged.availableModels = mergeAvailableModels(
18372
+ merged.availableModels,
18373
+ settings.availableModels,
18374
+ layer
18375
+ );
18054
18376
  if (settings.posthogApprovedExecTools) {
18055
18377
  for (const tool2 of settings.posthogApprovedExecTools) {
18056
18378
  posthogApprovedExecTools.add(tool2);
@@ -18193,6 +18515,14 @@ function readClaudeMdQuietly(cwd, logger) {
18193
18515
  return void 0;
18194
18516
  }
18195
18517
  }
18518
+ function collectKnownSlashCommands(commands) {
18519
+ const names = /* @__PURE__ */ new Set();
18520
+ if (!commands) return names;
18521
+ for (const cmd of commands) {
18522
+ if (cmd.name) names.add(cmd.name);
18523
+ }
18524
+ return names;
18525
+ }
18196
18526
  function sanitizeTitle(text2) {
18197
18527
  const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
18198
18528
  if (sanitized.length <= MAX_TITLE_LENGTH) {
@@ -18251,6 +18581,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18251
18581
  },
18252
18582
  loadSession: true,
18253
18583
  sessionCapabilities: {
18584
+ additionalDirectories: {},
18254
18585
  list: {},
18255
18586
  fork: {},
18256
18587
  resume: {}
@@ -18276,10 +18607,18 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18276
18607
  if (fs10.existsSync(path14.resolve(os6.homedir(), ".claude.json.backup")) && !fs10.existsSync(path14.resolve(os6.homedir(), ".claude.json"))) {
18277
18608
  throw RequestError2.authRequired();
18278
18609
  }
18279
- const response = await this.createSession(params, {
18280
- // Revisit these meta values once we support resume
18281
- resume: params._meta?.claudeCode?.options?.resume
18282
- });
18610
+ const response = await this.createSession(
18611
+ {
18612
+ cwd: params.cwd,
18613
+ mcpServers: params.mcpServers ?? [],
18614
+ additionalDirectories: params.additionalDirectories,
18615
+ _meta: params._meta
18616
+ },
18617
+ {
18618
+ // Revisit these meta values once we support resume
18619
+ resume: params._meta?.claudeCode?.options?.resume
18620
+ }
18621
+ );
18283
18622
  return response;
18284
18623
  }
18285
18624
  async unstable_forkSession(params) {
@@ -18287,24 +18626,27 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18287
18626
  {
18288
18627
  cwd: params.cwd,
18289
18628
  mcpServers: params.mcpServers ?? [],
18629
+ additionalDirectories: params.additionalDirectories,
18290
18630
  _meta: params._meta
18291
18631
  },
18292
18632
  { resume: params.sessionId, forkSession: true }
18293
18633
  );
18294
18634
  }
18295
- async unstable_resumeSession(params) {
18635
+ async resumeSession(params) {
18296
18636
  const existing = this.getExistingSessionState(params.sessionId);
18297
18637
  if (existing) return existing;
18298
18638
  const response = await this.createSession(
18299
18639
  {
18300
18640
  cwd: params.cwd,
18301
18641
  mcpServers: params.mcpServers ?? [],
18642
+ additionalDirectories: params.additionalDirectories,
18302
18643
  _meta: params._meta
18303
18644
  },
18304
18645
  {
18305
18646
  resume: params.sessionId
18306
18647
  }
18307
18648
  );
18649
+ await this.rehydrateTaskStateFromJsonl(params.sessionId);
18308
18650
  return response;
18309
18651
  }
18310
18652
  async loadSession(params) {
@@ -18314,6 +18656,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18314
18656
  {
18315
18657
  cwd: params.cwd,
18316
18658
  mcpServers: params.mcpServers ?? [],
18659
+ additionalDirectories: params.additionalDirectories,
18317
18660
  _meta: params._meta
18318
18661
  },
18319
18662
  { resume: params.sessionId, skipBackgroundFetches: true }
@@ -18392,6 +18735,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18392
18735
  await this.broadcastUserMessage(params);
18393
18736
  this.session.promptRunning = true;
18394
18737
  let handedOff = false;
18738
+ let errored = false;
18395
18739
  let lastAssistantTotalUsage = null;
18396
18740
  let lastStreamUsage = {
18397
18741
  input_tokens: 0,
@@ -18399,6 +18743,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18399
18743
  cache_read_input_tokens: 0,
18400
18744
  cache_creation_input_tokens: 0
18401
18745
  };
18746
+ let compactionInProgress = false;
18402
18747
  if (this.session.lastContextWindowSize == null) {
18403
18748
  this.session.lastContextWindowSize = this.getContextWindowForModel(
18404
18749
  this.session.modelId ?? ""
@@ -18456,9 +18801,45 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18456
18801
  if (message.subtype === "local_command_output") {
18457
18802
  promptReplayed = true;
18458
18803
  }
18804
+ if (message.subtype === "status") {
18805
+ if (message.status === "compacting") {
18806
+ compactionInProgress = true;
18807
+ } else if (message.compact_result === "success" && compactionInProgress) {
18808
+ compactionInProgress = false;
18809
+ await this.client.sessionUpdate({
18810
+ sessionId: params.sessionId,
18811
+ update: {
18812
+ sessionUpdate: "agent_message_chunk",
18813
+ content: {
18814
+ type: "text",
18815
+ text: "\n\nCompacting completed."
18816
+ }
18817
+ }
18818
+ });
18819
+ break;
18820
+ } else if (message.compact_result === "failed" && compactionInProgress) {
18821
+ compactionInProgress = false;
18822
+ const reason = message.compact_error ? `: ${message.compact_error}` : ".";
18823
+ await this.client.sessionUpdate({
18824
+ sessionId: params.sessionId,
18825
+ update: {
18826
+ sessionUpdate: "agent_message_chunk",
18827
+ content: {
18828
+ type: "text",
18829
+ text: `
18830
+
18831
+ Compacting failed${reason}`
18832
+ }
18833
+ }
18834
+ });
18835
+ break;
18836
+ }
18837
+ }
18459
18838
  if (message.subtype === "session_state_changed" && message.state === "idle") {
18460
18839
  if (!promptReplayed) {
18461
- if (commandMatch) {
18840
+ const cmdName = commandMatch?.[1].slice(1);
18841
+ const known = cmdName !== void 0 && this.session.knownSlashCommands?.has(cmdName) === true;
18842
+ if (commandMatch && !known) {
18462
18843
  const cmd = commandMatch[1];
18463
18844
  this.logger.warn(
18464
18845
  "Slash command produced no output; treating as unsupported",
@@ -18477,7 +18858,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18477
18858
  return { stopReason: "end_turn" };
18478
18859
  }
18479
18860
  this.logger.debug("Skipping idle state before prompt replay", {
18480
- sessionId: params.sessionId
18861
+ sessionId: params.sessionId,
18862
+ command: commandMatch?.[1],
18863
+ known
18481
18864
  });
18482
18865
  break;
18483
18866
  }
@@ -18491,7 +18874,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18491
18874
  size: lastContextWindowSize
18492
18875
  }
18493
18876
  });
18494
- return { stopReason: "end_turn" };
18877
+ return {
18878
+ stopReason: this.session.cancelled ? "cancelled" : "end_turn"
18879
+ };
18495
18880
  }
18496
18881
  await handleSystemMessage(message, context);
18497
18882
  break;
@@ -18674,6 +19059,28 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18674
19059
  }
18675
19060
  throw new Error("Session did not end in result");
18676
19061
  } catch (error) {
19062
+ errored = true;
19063
+ try {
19064
+ await this.session.query.interrupt();
19065
+ const MAX_DRAIN = 100;
19066
+ for (let i2 = 0; i2 < MAX_DRAIN; i2++) {
19067
+ const { value: m, done } = await this.session.query.next();
19068
+ if (done || !m) break;
19069
+ if (m.type === "system" && m.subtype === "session_state_changed" && m.state === "idle") {
19070
+ break;
19071
+ }
19072
+ if (i2 === MAX_DRAIN - 1) {
19073
+ this.logger.error(
19074
+ `Session ${params.sessionId}: drained ${MAX_DRAIN} messages after error without observing idle`
19075
+ );
19076
+ }
19077
+ }
19078
+ } catch (drainErr) {
19079
+ this.logger.error(
19080
+ `Session ${params.sessionId}: failed to drain query after prompt error`,
19081
+ { error: drainErr }
19082
+ );
19083
+ }
18677
19084
  if (error instanceof RequestError2 || !(error instanceof Error)) {
18678
19085
  throw error;
18679
19086
  }
@@ -18694,9 +19101,19 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18694
19101
  this.toolUseStreamCache.clear();
18695
19102
  if (!handedOff) {
18696
19103
  this.session.promptRunning = false;
18697
- for (const [key, pending] of this.session.pendingMessages) {
18698
- pending.resolve(true);
18699
- this.session.pendingMessages.delete(key);
19104
+ if (errored) {
19105
+ for (const pending of this.session.pendingMessages.values()) {
19106
+ pending.resolve(true);
19107
+ }
19108
+ this.session.pendingMessages.clear();
19109
+ } else if (this.session.pendingMessages.size > 0) {
19110
+ const next = [...this.session.pendingMessages.entries()].sort(
19111
+ (a, b) => a[1].order - b[1].order
19112
+ )[0];
19113
+ if (next) {
19114
+ next[1].resolve(false);
19115
+ this.session.pendingMessages.delete(next[0]);
19116
+ }
18700
19117
  }
18701
19118
  }
18702
19119
  }
@@ -18747,7 +19164,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18747
19164
  );
18748
19165
  }
18749
19166
  const mcpServers = parseMcpServers(
18750
- params
19167
+ params,
19168
+ this.logger
18751
19169
  );
18752
19170
  await this.refreshSession(mcpServers);
18753
19171
  return { refreshed: true };
@@ -18932,7 +19350,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18932
19350
  const settingsManager = new SettingsManager(cwd);
18933
19351
  await settingsManager.initialize();
18934
19352
  const earlyModelId = settingsManager.getSettings().model || meta?.model || "";
18935
- const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params) : {};
19353
+ const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params, this.logger) : {};
18936
19354
  const localToolsServer = createLocalToolsMcpServer(
18937
19355
  { cwd, token: resolveGithubToken(), taskId },
18938
19356
  meta
@@ -18956,6 +19374,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18956
19374
  cwd
18957
19375
  });
18958
19376
  const permissionMode = meta?.permissionMode && CODE_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
19377
+ const taskState = /* @__PURE__ */ new Map();
18959
19378
  const options = buildSessionOptions({
18960
19379
  cwd,
18961
19380
  mcpServers,
@@ -18969,7 +19388,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18969
19388
  forkSession,
18970
19389
  additionalDirectories: [
18971
19390
  ...meta?.claudeCode?.options?.additionalDirectories ?? [],
18972
- ...meta?.additionalRoots ?? []
19391
+ // Prefer the official ACP `additionalDirectories` field. Fall back
19392
+ // to the legacy `_meta.additionalRoots` extension for clients that
19393
+ // haven't been updated yet.
19394
+ ...params.additionalDirectories ?? meta?.additionalRoots ?? []
18973
19395
  ],
18974
19396
  disableBuiltInTools: meta?.disableBuiltInTools,
18975
19397
  outputFormat,
@@ -18980,7 +19402,17 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18980
19402
  effort,
18981
19403
  enrichmentDeps: this.enrichment?.deps,
18982
19404
  enrichedReadCache: this.enrichedReadCache,
18983
- cloudMode: cloudRun
19405
+ cloudMode: cloudRun,
19406
+ taskState,
19407
+ onTaskStateChange: async () => {
19408
+ await this.client.sessionUpdate({
19409
+ sessionId,
19410
+ update: {
19411
+ sessionUpdate: "plan",
19412
+ entries: taskStateToPlanEntries(taskState)
19413
+ }
19414
+ });
19415
+ }
18984
19416
  });
18985
19417
  const abortController = options.abortController;
18986
19418
  const q = query({ prompt: input, options });
@@ -19009,6 +19441,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19009
19441
  systemPrompt: estimateSystemPrompt(systemPrompt),
19010
19442
  rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
19011
19443
  },
19444
+ taskState,
19012
19445
  // Custom properties
19013
19446
  cwd,
19014
19447
  notificationHistory: [],
@@ -19027,6 +19460,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19027
19460
  `Session ${forkSession ? "fork" : "resumption"} timed out for sessionId=${sessionId}`
19028
19461
  );
19029
19462
  }
19463
+ session.knownSlashCommands = collectKnownSlashCommands(
19464
+ result.value.commands
19465
+ );
19030
19466
  } catch (err2) {
19031
19467
  settingsManager.dispose();
19032
19468
  if (err2 instanceof Error && err2.message === "Query closed before response received") {
@@ -19045,7 +19481,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19045
19481
  }
19046
19482
  }
19047
19483
  const initPromise2 = !isResume ? withTimeout(q.initializationResult(), SESSION_VALIDATION_TIMEOUT_MS) : void 0;
19048
- const [modelOptions] = await Promise.all([
19484
+ const [rawModelOptions] = await Promise.all([
19049
19485
  this.getModelConfigOptions(
19050
19486
  settingsManager.getSettings().model || meta?.model || void 0
19051
19487
  ),
@@ -19057,6 +19493,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19057
19493
  })
19058
19494
  ] : []
19059
19495
  ]);
19496
+ const settingsAvailableModels = settingsManager.getSettings().availableModels;
19497
+ const modelOptions = Array.isArray(settingsAvailableModels) ? applyAvailableModelsAllowlist(rawModelOptions, settingsAvailableModels) : rawModelOptions;
19060
19498
  if (initPromise2) {
19061
19499
  try {
19062
19500
  const initResult = await initPromise2;
@@ -19066,6 +19504,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19066
19504
  `Session initialization timed out for sessionId=${sessionId}`
19067
19505
  );
19068
19506
  }
19507
+ session.knownSlashCommands = collectKnownSlashCommands(
19508
+ initResult.value.commands
19509
+ );
19069
19510
  } catch (err2) {
19070
19511
  settingsManager.dispose();
19071
19512
  this.logger.error("Session initialization failed", {
@@ -19077,9 +19518,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19077
19518
  throw err2;
19078
19519
  }
19079
19520
  }
19080
- const settingsModel = settingsManager.getSettings().model;
19081
- const metaModel = meta?.model;
19082
- const resolvedModelId = settingsModel || metaModel || modelOptions.currentModelId;
19521
+ const resolvedModelId = resolveInitialModelId(modelOptions, [
19522
+ settingsManager.getSettings().model,
19523
+ meta?.model
19524
+ ]);
19083
19525
  session.modelId = resolvedModelId;
19084
19526
  session.lastContextWindowSize = this.getContextWindowForModel(resolvedModelId);
19085
19527
  const resolvedSdkModel = toSdkModelId(resolvedModelId);
@@ -19287,6 +19729,34 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19287
19729
  [key]: tokens
19288
19730
  };
19289
19731
  }
19732
+ /**
19733
+ * Rebuild the in-memory taskState from JSONL and push a plan update so the
19734
+ * client's plan panel reflects pre-resume tasks. `loadSession` already covers
19735
+ * this via the full `replaySessionHistory` notification stream; resume
19736
+ * deliberately stays quiet (the client keeps its own message history) so we
19737
+ * walk the transcript here for state only.
19738
+ */
19739
+ async rehydrateTaskStateFromJsonl(sessionId) {
19740
+ try {
19741
+ const messages = await getSessionMessages(sessionId, {
19742
+ dir: this.session.cwd
19743
+ });
19744
+ rehydrateTaskState(messages, this.session.taskState);
19745
+ if (this.session.taskState.size === 0) return;
19746
+ await this.client.sessionUpdate({
19747
+ sessionId,
19748
+ update: {
19749
+ sessionUpdate: "plan",
19750
+ entries: taskStateToPlanEntries(this.session.taskState)
19751
+ }
19752
+ });
19753
+ } catch (err2) {
19754
+ this.logger.warn("Failed to rehydrate task state", {
19755
+ sessionId,
19756
+ error: err2 instanceof Error ? err2.message : String(err2)
19757
+ });
19758
+ }
19759
+ }
19290
19760
  async replaySessionHistory(sessionId) {
19291
19761
  try {
19292
19762
  const messages = await getSessionMessages(sessionId, {
@@ -20077,7 +20547,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20077
20547
  }
20078
20548
  return response;
20079
20549
  }
20080
- async unstable_resumeSession(params) {
20550
+ async resumeSession(params) {
20081
20551
  const meta = params._meta;
20082
20552
  const injectedParams = this.applyLocalTools(
20083
20553
  this.applyStructuredOutput(