@posthog/agent 2.3.735 → 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 (62) 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 +524 -72
  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 +524 -72
  19. package/dist/server/agent-server.js.map +1 -1
  20. package/dist/server/bin.cjs +524 -72
  21. package/dist/server/bin.cjs.map +1 -1
  22. package/package.json +6 -6
  23. package/src/adapters/claude/UPSTREAM.md +80 -3
  24. package/src/adapters/claude/claude-agent.ts +201 -18
  25. package/src/adapters/claude/conversion/sdk-to-acp.ts +182 -16
  26. package/src/adapters/claude/conversion/task-state.test.ts +338 -0
  27. package/src/adapters/claude/conversion/task-state.ts +178 -0
  28. package/src/adapters/claude/conversion/tool-use-to-acp.ts +29 -19
  29. package/src/adapters/claude/hooks.test.ts +162 -0
  30. package/src/adapters/claude/hooks.ts +44 -3
  31. package/src/adapters/claude/permissions/permission-options.ts +7 -2
  32. package/src/adapters/claude/session/commands.ts +1 -0
  33. package/src/adapters/claude/session/mcp-config.ts +23 -7
  34. package/src/adapters/claude/session/model-config.test.ts +60 -0
  35. package/src/adapters/claude/session/model-config.ts +56 -0
  36. package/src/adapters/claude/session/models.test.ts +119 -0
  37. package/src/adapters/claude/session/models.ts +31 -0
  38. package/src/adapters/claude/session/options.test.ts +1 -0
  39. package/src/adapters/claude/session/options.ts +29 -1
  40. package/src/adapters/claude/session/settings.test.ts +102 -1
  41. package/src/adapters/claude/session/settings.ts +33 -6
  42. package/src/adapters/claude/tools.ts +4 -2
  43. package/src/adapters/claude/types.ts +8 -0
  44. package/src/adapters/codex/codex-agent.ts +2 -2
  45. package/src/server/agent-server.test.ts +173 -0
  46. package/src/test/mocks/claude-sdk.ts +4 -0
  47. package/src/test/native-binary.test.ts +27 -0
  48. package/dist/claude-cli/cli.js +0 -18412
  49. package/dist/claude-cli/vendor/audio-capture/arm64-darwin/audio-capture.node +0 -0
  50. package/dist/claude-cli/vendor/audio-capture/arm64-linux/audio-capture.node +0 -0
  51. package/dist/claude-cli/vendor/audio-capture/arm64-win32/audio-capture.node +0 -0
  52. package/dist/claude-cli/vendor/audio-capture/x64-darwin/audio-capture.node +0 -0
  53. package/dist/claude-cli/vendor/audio-capture/x64-linux/audio-capture.node +0 -0
  54. package/dist/claude-cli/vendor/audio-capture/x64-win32/audio-capture.node +0 -0
  55. package/dist/claude-cli/vendor/ripgrep/COPYING +0 -3
  56. package/dist/claude-cli/vendor/ripgrep/arm64-darwin/rg +0 -0
  57. package/dist/claude-cli/vendor/ripgrep/arm64-linux/rg +0 -0
  58. package/dist/claude-cli/vendor/ripgrep/arm64-win32/rg.exe +0 -0
  59. package/dist/claude-cli/vendor/ripgrep/x64-darwin/rg +0 -0
  60. package/dist/claude-cli/vendor/ripgrep/x64-linux/rg +0 -0
  61. package/dist/claude-cli/vendor/ripgrep/x64-win32/rg.exe +0 -0
  62. 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.735",
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;
10831
+ return {
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":
10702
10844
  return {
10703
- title: Array.isArray(input?.todos) ? `Update TODOs: ${input.todos.map((todo) => todo.content).join(", ")}` : "Update TODOs",
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);
@@ -18259,6 +18581,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18259
18581
  },
18260
18582
  loadSession: true,
18261
18583
  sessionCapabilities: {
18584
+ additionalDirectories: {},
18262
18585
  list: {},
18263
18586
  fork: {},
18264
18587
  resume: {}
@@ -18284,10 +18607,18 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18284
18607
  if (fs10.existsSync(path14.resolve(os6.homedir(), ".claude.json.backup")) && !fs10.existsSync(path14.resolve(os6.homedir(), ".claude.json"))) {
18285
18608
  throw RequestError2.authRequired();
18286
18609
  }
18287
- const response = await this.createSession(params, {
18288
- // Revisit these meta values once we support resume
18289
- resume: params._meta?.claudeCode?.options?.resume
18290
- });
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
+ );
18291
18622
  return response;
18292
18623
  }
18293
18624
  async unstable_forkSession(params) {
@@ -18295,24 +18626,27 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18295
18626
  {
18296
18627
  cwd: params.cwd,
18297
18628
  mcpServers: params.mcpServers ?? [],
18629
+ additionalDirectories: params.additionalDirectories,
18298
18630
  _meta: params._meta
18299
18631
  },
18300
18632
  { resume: params.sessionId, forkSession: true }
18301
18633
  );
18302
18634
  }
18303
- async unstable_resumeSession(params) {
18635
+ async resumeSession(params) {
18304
18636
  const existing = this.getExistingSessionState(params.sessionId);
18305
18637
  if (existing) return existing;
18306
18638
  const response = await this.createSession(
18307
18639
  {
18308
18640
  cwd: params.cwd,
18309
18641
  mcpServers: params.mcpServers ?? [],
18642
+ additionalDirectories: params.additionalDirectories,
18310
18643
  _meta: params._meta
18311
18644
  },
18312
18645
  {
18313
18646
  resume: params.sessionId
18314
18647
  }
18315
18648
  );
18649
+ await this.rehydrateTaskStateFromJsonl(params.sessionId);
18316
18650
  return response;
18317
18651
  }
18318
18652
  async loadSession(params) {
@@ -18322,6 +18656,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18322
18656
  {
18323
18657
  cwd: params.cwd,
18324
18658
  mcpServers: params.mcpServers ?? [],
18659
+ additionalDirectories: params.additionalDirectories,
18325
18660
  _meta: params._meta
18326
18661
  },
18327
18662
  { resume: params.sessionId, skipBackgroundFetches: true }
@@ -18400,6 +18735,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18400
18735
  await this.broadcastUserMessage(params);
18401
18736
  this.session.promptRunning = true;
18402
18737
  let handedOff = false;
18738
+ let errored = false;
18403
18739
  let lastAssistantTotalUsage = null;
18404
18740
  let lastStreamUsage = {
18405
18741
  input_tokens: 0,
@@ -18407,6 +18743,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18407
18743
  cache_read_input_tokens: 0,
18408
18744
  cache_creation_input_tokens: 0
18409
18745
  };
18746
+ let compactionInProgress = false;
18410
18747
  if (this.session.lastContextWindowSize == null) {
18411
18748
  this.session.lastContextWindowSize = this.getContextWindowForModel(
18412
18749
  this.session.modelId ?? ""
@@ -18464,6 +18801,40 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18464
18801
  if (message.subtype === "local_command_output") {
18465
18802
  promptReplayed = true;
18466
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
+ }
18467
18838
  if (message.subtype === "session_state_changed" && message.state === "idle") {
18468
18839
  if (!promptReplayed) {
18469
18840
  const cmdName = commandMatch?.[1].slice(1);
@@ -18503,7 +18874,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18503
18874
  size: lastContextWindowSize
18504
18875
  }
18505
18876
  });
18506
- return { stopReason: "end_turn" };
18877
+ return {
18878
+ stopReason: this.session.cancelled ? "cancelled" : "end_turn"
18879
+ };
18507
18880
  }
18508
18881
  await handleSystemMessage(message, context);
18509
18882
  break;
@@ -18686,6 +19059,28 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18686
19059
  }
18687
19060
  throw new Error("Session did not end in result");
18688
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
+ }
18689
19084
  if (error instanceof RequestError2 || !(error instanceof Error)) {
18690
19085
  throw error;
18691
19086
  }
@@ -18706,9 +19101,19 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18706
19101
  this.toolUseStreamCache.clear();
18707
19102
  if (!handedOff) {
18708
19103
  this.session.promptRunning = false;
18709
- for (const [key, pending] of this.session.pendingMessages) {
18710
- pending.resolve(true);
18711
- 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
+ }
18712
19117
  }
18713
19118
  }
18714
19119
  }
@@ -18759,7 +19164,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18759
19164
  );
18760
19165
  }
18761
19166
  const mcpServers = parseMcpServers(
18762
- params
19167
+ params,
19168
+ this.logger
18763
19169
  );
18764
19170
  await this.refreshSession(mcpServers);
18765
19171
  return { refreshed: true };
@@ -18944,7 +19350,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18944
19350
  const settingsManager = new SettingsManager(cwd);
18945
19351
  await settingsManager.initialize();
18946
19352
  const earlyModelId = settingsManager.getSettings().model || meta?.model || "";
18947
- const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params) : {};
19353
+ const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params, this.logger) : {};
18948
19354
  const localToolsServer = createLocalToolsMcpServer(
18949
19355
  { cwd, token: resolveGithubToken(), taskId },
18950
19356
  meta
@@ -18968,6 +19374,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18968
19374
  cwd
18969
19375
  });
18970
19376
  const permissionMode = meta?.permissionMode && CODE_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
19377
+ const taskState = /* @__PURE__ */ new Map();
18971
19378
  const options = buildSessionOptions({
18972
19379
  cwd,
18973
19380
  mcpServers,
@@ -18981,7 +19388,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18981
19388
  forkSession,
18982
19389
  additionalDirectories: [
18983
19390
  ...meta?.claudeCode?.options?.additionalDirectories ?? [],
18984
- ...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 ?? []
18985
19395
  ],
18986
19396
  disableBuiltInTools: meta?.disableBuiltInTools,
18987
19397
  outputFormat,
@@ -18992,7 +19402,17 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18992
19402
  effort,
18993
19403
  enrichmentDeps: this.enrichment?.deps,
18994
19404
  enrichedReadCache: this.enrichedReadCache,
18995
- 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
+ }
18996
19416
  });
18997
19417
  const abortController = options.abortController;
18998
19418
  const q = query({ prompt: input, options });
@@ -19021,6 +19441,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19021
19441
  systemPrompt: estimateSystemPrompt(systemPrompt),
19022
19442
  rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
19023
19443
  },
19444
+ taskState,
19024
19445
  // Custom properties
19025
19446
  cwd,
19026
19447
  notificationHistory: [],
@@ -19060,7 +19481,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19060
19481
  }
19061
19482
  }
19062
19483
  const initPromise2 = !isResume ? withTimeout(q.initializationResult(), SESSION_VALIDATION_TIMEOUT_MS) : void 0;
19063
- const [modelOptions] = await Promise.all([
19484
+ const [rawModelOptions] = await Promise.all([
19064
19485
  this.getModelConfigOptions(
19065
19486
  settingsManager.getSettings().model || meta?.model || void 0
19066
19487
  ),
@@ -19072,6 +19493,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19072
19493
  })
19073
19494
  ] : []
19074
19495
  ]);
19496
+ const settingsAvailableModels = settingsManager.getSettings().availableModels;
19497
+ const modelOptions = Array.isArray(settingsAvailableModels) ? applyAvailableModelsAllowlist(rawModelOptions, settingsAvailableModels) : rawModelOptions;
19075
19498
  if (initPromise2) {
19076
19499
  try {
19077
19500
  const initResult = await initPromise2;
@@ -19095,9 +19518,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19095
19518
  throw err2;
19096
19519
  }
19097
19520
  }
19098
- const settingsModel = settingsManager.getSettings().model;
19099
- const metaModel = meta?.model;
19100
- const resolvedModelId = settingsModel || metaModel || modelOptions.currentModelId;
19521
+ const resolvedModelId = resolveInitialModelId(modelOptions, [
19522
+ settingsManager.getSettings().model,
19523
+ meta?.model
19524
+ ]);
19101
19525
  session.modelId = resolvedModelId;
19102
19526
  session.lastContextWindowSize = this.getContextWindowForModel(resolvedModelId);
19103
19527
  const resolvedSdkModel = toSdkModelId(resolvedModelId);
@@ -19305,6 +19729,34 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19305
19729
  [key]: tokens
19306
19730
  };
19307
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
+ }
19308
19760
  async replaySessionHistory(sessionId) {
19309
19761
  try {
19310
19762
  const messages = await getSessionMessages(sessionId, {
@@ -20095,7 +20547,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20095
20547
  }
20096
20548
  return response;
20097
20549
  }
20098
- async unstable_resumeSession(params) {
20550
+ async resumeSession(params) {
20099
20551
  const meta = params._meta;
20100
20552
  const injectedParams = this.applyLocalTools(
20101
20553
  this.applyStructuredOutput(