@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
@@ -4433,6 +4433,7 @@ var GATEWAY_TO_SDK_MODEL = {
4433
4433
  "claude-opus-4-5": "opus",
4434
4434
  "claude-opus-4-6": "opus",
4435
4435
  "claude-opus-4-7": "opus",
4436
+ "claude-opus-4-8": "opus",
4436
4437
  "claude-sonnet-4-5": "sonnet",
4437
4438
  "claude-sonnet-4-6": "sonnet",
4438
4439
  "claude-haiku-4-5": "haiku"
@@ -4443,6 +4444,7 @@ function toSdkModelId(modelId) {
4443
4444
  var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
4444
4445
  "claude-opus-4-6",
4445
4446
  "claude-opus-4-7",
4447
+ "claude-opus-4-8",
4446
4448
  "claude-sonnet-4-6"
4447
4449
  ]);
4448
4450
  function supports1MContext(modelId) {
@@ -4452,11 +4454,13 @@ var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
4452
4454
  "claude-opus-4-5",
4453
4455
  "claude-opus-4-6",
4454
4456
  "claude-opus-4-7",
4457
+ "claude-opus-4-8",
4455
4458
  "claude-sonnet-4-6"
4456
4459
  ]);
4457
4460
  var MODELS_WITH_XHIGH_EFFORT = /* @__PURE__ */ new Set([
4458
4461
  "claude-opus-4-6",
4459
- "claude-opus-4-7"
4462
+ "claude-opus-4-7",
4463
+ "claude-opus-4-8"
4460
4464
  ]);
4461
4465
  function supportsEffort(modelId) {
4462
4466
  return MODELS_WITH_EFFORT.has(modelId);
@@ -4496,6 +4500,19 @@ function tokenizeModelPreference(model) {
4496
4500
  }).filter((token) => token && token !== "claude").filter((token) => /[a-z]/.test(token) || token.endsWith("m"));
4497
4501
  return { tokens, contextHint };
4498
4502
  }
4503
+ var MODEL_FAMILY_VERSION_PATTERN = /\b(\d+)[-.](\d+)\b/;
4504
+ function extractModelFamilyVersion(s) {
4505
+ if (!s) return null;
4506
+ const match = s.match(MODEL_FAMILY_VERSION_PATTERN);
4507
+ return match ? `${match[1]}.${match[2]}` : null;
4508
+ }
4509
+ function modelVersionsCompatible(preference, candidate) {
4510
+ const preferred = extractModelFamilyVersion(preference);
4511
+ if (!preferred) return true;
4512
+ const candidateVersion = extractModelFamilyVersion(candidate.value) ?? extractModelFamilyVersion(candidate.name) ?? extractModelFamilyVersion(candidate.description);
4513
+ if (!candidateVersion) return true;
4514
+ return preferred === candidateVersion;
4515
+ }
4499
4516
  function scoreModelMatch(model, tokens, contextHint) {
4500
4517
  const haystack = `${model.value} ${model.name ?? ""}`.toLowerCase();
4501
4518
  let score = 0;
@@ -4515,6 +4532,7 @@ function resolveModelPreference(preference, options) {
4515
4532
  );
4516
4533
  if (directMatch) return directMatch.value;
4517
4534
  const includesMatch = options.find((o) => {
4535
+ if (!modelVersionsCompatible(trimmed2, o)) return false;
4518
4536
  const value = o.value.toLowerCase();
4519
4537
  const display = (o.name ?? "").toLowerCase();
4520
4538
  return value.includes(lower) || display.includes(lower) || lower.includes(value);
@@ -4525,6 +4543,7 @@ function resolveModelPreference(preference, options) {
4525
4543
  let bestMatch = null;
4526
4544
  let bestScore = 0;
4527
4545
  for (const model of options) {
4546
+ if (!modelVersionsCompatible(trimmed2, model)) continue;
4528
4547
  const score = scoreModelMatch(model, tokens, contextHint);
4529
4548
  if (0 < score && (!bestMatch || bestScore < score)) {
4530
4549
  bestMatch = model;
@@ -9471,7 +9490,7 @@ var import_zod4 = require("zod");
9471
9490
  // package.json
9472
9491
  var package_default = {
9473
9492
  name: "@posthog/agent",
9474
- version: "2.3.735",
9493
+ version: "2.3.736",
9475
9494
  repository: "https://github.com/PostHog/code",
9476
9495
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
9477
9496
  exports: {
@@ -9591,9 +9610,9 @@ var package_default = {
9591
9610
  vitest: "^2.1.8"
9592
9611
  },
9593
9612
  dependencies: {
9594
- "@agentclientprotocol/sdk": "0.19.0",
9595
- "@anthropic-ai/claude-agent-sdk": "0.2.112",
9596
- "@anthropic-ai/sdk": "0.89.0",
9613
+ "@agentclientprotocol/sdk": "0.22.1",
9614
+ "@anthropic-ai/claude-agent-sdk": "0.3.154",
9615
+ "@anthropic-ai/sdk": "0.100.0",
9597
9616
  "@hono/node-server": "^1.19.9",
9598
9617
  "@opentelemetry/api-logs": "^0.208.0",
9599
9618
  "@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
@@ -15308,6 +15327,30 @@ var registerHookCallback = (toolUseID, {
15308
15327
  onPostToolUseHook
15309
15328
  };
15310
15329
  };
15330
+ var createTaskHook = (taskState, onChange) => async (input) => {
15331
+ const taskId = "task_id" in input && typeof input.task_id === "string" ? input.task_id : void 0;
15332
+ if (!taskId) return { continue: true };
15333
+ let mutated = false;
15334
+ if (input.hook_event_name === "TaskCreated") {
15335
+ if (!input.task_subject) return { continue: true };
15336
+ if (taskState.has(taskId)) return { continue: true };
15337
+ taskState.set(taskId, {
15338
+ subject: input.task_subject,
15339
+ status: "pending",
15340
+ description: input.task_description
15341
+ });
15342
+ mutated = true;
15343
+ } else if (input.hook_event_name === "TaskCompleted") {
15344
+ const existing = taskState.get(taskId);
15345
+ if (!existing || existing.status === "completed") {
15346
+ return { continue: true };
15347
+ }
15348
+ taskState.set(taskId, { ...existing, status: "completed" });
15349
+ mutated = true;
15350
+ }
15351
+ if (mutated && onChange) await onChange();
15352
+ return { continue: true };
15353
+ };
15311
15354
  var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
15312
15355
  if (input.hook_event_name === "PostToolUse") {
15313
15356
  const toolName = input.tool_name;
@@ -15322,10 +15365,8 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
15322
15365
  input.tool_input,
15323
15366
  input.tool_response
15324
15367
  );
15325
- delete toolUseCallbacks[toolUseID];
15326
- } else {
15327
- delete toolUseCallbacks[toolUseID];
15328
15368
  }
15369
+ delete toolUseCallbacks[toolUseID];
15329
15370
  }
15330
15371
  }
15331
15372
  return { continue: true };
@@ -15472,6 +15513,104 @@ var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID
15472
15513
  }
15473
15514
  };
15474
15515
 
15516
+ // src/adapters/claude/conversion/task-state.ts
15517
+ function parseTaskCreateOutput(content) {
15518
+ const tryParse = (text2) => {
15519
+ try {
15520
+ const parsed = JSON.parse(text2);
15521
+ if (parsed && typeof parsed === "object" && parsed.task && typeof parsed.task.id === "string") {
15522
+ return parsed;
15523
+ }
15524
+ } catch {
15525
+ }
15526
+ return void 0;
15527
+ };
15528
+ if (typeof content === "string") {
15529
+ return tryParse(content);
15530
+ }
15531
+ if (Array.isArray(content)) {
15532
+ for (const block of content) {
15533
+ if (block && typeof block === "object" && "type" in block && block.type === "text") {
15534
+ const text2 = block.text;
15535
+ if (typeof text2 === "string") {
15536
+ const parsed = tryParse(text2);
15537
+ if (parsed) return parsed;
15538
+ }
15539
+ }
15540
+ }
15541
+ }
15542
+ return void 0;
15543
+ }
15544
+ function applyTaskCreate(state, input, output) {
15545
+ const taskId = output?.task?.id;
15546
+ if (!taskId || !input) return;
15547
+ state.set(taskId, {
15548
+ subject: input.subject,
15549
+ status: "pending",
15550
+ activeForm: input.activeForm,
15551
+ description: input.description
15552
+ });
15553
+ }
15554
+ function applyTaskUpdate(state, input) {
15555
+ if (!input?.taskId) return;
15556
+ if (input.status === "deleted") {
15557
+ state.delete(input.taskId);
15558
+ return;
15559
+ }
15560
+ const existing = state.get(input.taskId);
15561
+ const subject = input.subject ?? existing?.subject;
15562
+ if (!subject) return;
15563
+ state.set(input.taskId, {
15564
+ subject,
15565
+ status: input.status ?? existing?.status ?? "pending",
15566
+ activeForm: input.activeForm ?? existing?.activeForm,
15567
+ description: input.description ?? existing?.description
15568
+ });
15569
+ }
15570
+ function taskStateToPlanEntries(state) {
15571
+ return Array.from(state.values()).map((task) => ({
15572
+ content: task.subject,
15573
+ status: task.status,
15574
+ priority: "medium"
15575
+ }));
15576
+ }
15577
+ function isToolUseBlock(block) {
15578
+ return !!block && typeof block === "object" && block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string";
15579
+ }
15580
+ function isToolResultBlock(block) {
15581
+ return !!block && typeof block === "object" && block.type === "tool_result" && typeof block.tool_use_id === "string";
15582
+ }
15583
+ function rehydrateTaskState(messages, state) {
15584
+ const pendingInputs = /* @__PURE__ */ new Map();
15585
+ for (const msg of messages) {
15586
+ const content = msg.message?.content;
15587
+ if (!Array.isArray(content)) continue;
15588
+ if (msg.type === "assistant") {
15589
+ for (const block of content) {
15590
+ if (isToolUseBlock(block) && (block.name === "TaskCreate" || block.name === "TaskUpdate")) {
15591
+ pendingInputs.set(block.id, { name: block.name, input: block.input });
15592
+ }
15593
+ }
15594
+ } else if (msg.type === "user") {
15595
+ for (const block of content) {
15596
+ if (!isToolResultBlock(block) || block.is_error) continue;
15597
+ const cached = pendingInputs.get(block.tool_use_id);
15598
+ if (!cached) continue;
15599
+ if (cached.name === "TaskCreate") {
15600
+ applyTaskCreate(
15601
+ state,
15602
+ cached.input,
15603
+ parseTaskCreateOutput(block.content)
15604
+ );
15605
+ } else if (cached.name === "TaskUpdate") {
15606
+ applyTaskUpdate(state, cached.input);
15607
+ }
15608
+ pendingInputs.delete(block.tool_use_id);
15609
+ }
15610
+ }
15611
+ }
15612
+ }
15613
+
15475
15614
  // src/adapters/claude/conversion/tool-use-to-acp.ts
15476
15615
  var import_node_fs2 = __toESM(require("fs"), 1);
15477
15616
  var import_node_path3 = __toESM(require("path"), 1);
@@ -15823,9 +15962,31 @@ function toolInfoFromToolUse(toolUse, options) {
15823
15962
  content: []
15824
15963
  };
15825
15964
  }
15826
- case "TodoWrite":
15965
+ case "TaskCreate": {
15966
+ const subject = typeof input?.subject === "string" ? input.subject : void 0;
15827
15967
  return {
15828
- title: Array.isArray(input?.todos) ? `Update TODOs: ${input.todos.map((todo) => todo.content).join(", ")}` : "Update TODOs",
15968
+ title: subject ? `Create task: ${subject}` : "Create task",
15969
+ kind: "think",
15970
+ content: []
15971
+ };
15972
+ }
15973
+ case "TaskUpdate": {
15974
+ const subject = typeof input?.subject === "string" ? input.subject : void 0;
15975
+ return {
15976
+ title: subject ? `Update task: ${subject}` : "Update task",
15977
+ kind: "think",
15978
+ content: []
15979
+ };
15980
+ }
15981
+ case "TaskList":
15982
+ return {
15983
+ title: "List tasks",
15984
+ kind: "think",
15985
+ content: []
15986
+ };
15987
+ case "TaskGet":
15988
+ return {
15989
+ title: "Get task",
15829
15990
  kind: "think",
15830
15991
  content: []
15831
15992
  };
@@ -16109,13 +16270,6 @@ ${content}
16109
16270
  }
16110
16271
  return {};
16111
16272
  }
16112
- function planEntries(input) {
16113
- return input.todos.map((input2) => ({
16114
- content: input2.content,
16115
- status: input2.status,
16116
- priority: "medium"
16117
- }));
16118
- }
16119
16273
  function resolveFileContent(filePath, oldText, cachedFileContent) {
16120
16274
  if (cachedFileContent && filePath in cachedFileContent) {
16121
16275
  const cached = cachedFileContent[filePath];
@@ -16201,14 +16355,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
16201
16355
  function handleToolUseChunk(chunk, ctx) {
16202
16356
  const alreadyCached = chunk.id in ctx.toolUseCache;
16203
16357
  ctx.toolUseCache[chunk.id] = chunk;
16204
- if (chunk.name === "TodoWrite") {
16205
- const input = chunk.input;
16206
- if (Array.isArray(input.todos)) {
16207
- return {
16208
- sessionUpdate: "plan",
16209
- entries: planEntries(chunk.input)
16210
- };
16211
- }
16358
+ if (chunk.name === "TaskCreate" || chunk.name === "TaskUpdate" || chunk.name === "TaskList" || chunk.name === "TaskGet") {
16212
16359
  return null;
16213
16360
  }
16214
16361
  if (!alreadyCached && ctx.registerHooks !== false) {
@@ -16216,7 +16363,7 @@ function handleToolUseChunk(chunk, ctx) {
16216
16363
  onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
16217
16364
  const toolUse = ctx.toolUseCache[toolUseId];
16218
16365
  if (toolUse) {
16219
- const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
16366
+ const editUpdate = toolUse.name === "Edit" || toolUse.name === "Write" ? toolUpdateFromEditToolResponse(toolResponse) : null;
16220
16367
  await ctx.client.sessionUpdate({
16221
16368
  sessionId: ctx.sessionId,
16222
16369
  update: {
@@ -16328,7 +16475,28 @@ function handleToolResultChunk(chunk, ctx) {
16328
16475
  );
16329
16476
  return [];
16330
16477
  }
16331
- if (toolUse.name === "TodoWrite") {
16478
+ if (toolUse.name === "TaskCreate" || toolUse.name === "TaskUpdate" || toolUse.name === "TaskList" || toolUse.name === "TaskGet") {
16479
+ if (chunk.is_error || !ctx.taskState) return [];
16480
+ if (toolUse.name === "TaskCreate") {
16481
+ applyTaskCreate(
16482
+ ctx.taskState,
16483
+ toolUse.input,
16484
+ parseTaskCreateOutput(chunk.content)
16485
+ );
16486
+ } else if (toolUse.name === "TaskUpdate") {
16487
+ applyTaskUpdate(
16488
+ ctx.taskState,
16489
+ toolUse.input
16490
+ );
16491
+ }
16492
+ if (toolUse.name === "TaskCreate" || toolUse.name === "TaskUpdate") {
16493
+ return [
16494
+ {
16495
+ sessionUpdate: "plan",
16496
+ entries: taskStateToPlanEntries(ctx.taskState)
16497
+ }
16498
+ ];
16499
+ }
16332
16500
  return [];
16333
16501
  }
16334
16502
  if (!chunk.is_error) {
@@ -16425,13 +16593,15 @@ function processContentChunk(chunk, role, ctx) {
16425
16593
  case "container_upload":
16426
16594
  case "compaction":
16427
16595
  case "compaction_delta":
16596
+ case "advisor_tool_result":
16597
+ case "mid_conv_system":
16428
16598
  return [];
16429
16599
  default:
16430
16600
  unreachable(chunk, ctx.logger);
16431
16601
  return [];
16432
16602
  }
16433
16603
  }
16434
- function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, mcpToolUseResult, enrichedReadCache) {
16604
+ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, mcpToolUseResult, enrichedReadCache, taskState) {
16435
16605
  if (typeof content === "string") {
16436
16606
  const update = {
16437
16607
  sessionUpdate: messageUpdateType(role),
@@ -16457,7 +16627,8 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
16457
16627
  registerHooks,
16458
16628
  supportsTerminalOutput,
16459
16629
  cwd,
16460
- mcpToolUseResult
16630
+ mcpToolUseResult,
16631
+ taskState
16461
16632
  };
16462
16633
  const output = [];
16463
16634
  for (const chunk of content) {
@@ -16467,7 +16638,7 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
16467
16638
  }
16468
16639
  return output;
16469
16640
  }
16470
- function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUseStreamCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, enrichedReadCache) {
16641
+ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUseStreamCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, enrichedReadCache, taskState) {
16471
16642
  const event = message.event;
16472
16643
  switch (event.type) {
16473
16644
  case "content_block_start": {
@@ -16491,7 +16662,8 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUse
16491
16662
  supportsTerminalOutput,
16492
16663
  cwd,
16493
16664
  void 0,
16494
- enrichedReadCache
16665
+ enrichedReadCache,
16666
+ taskState
16495
16667
  );
16496
16668
  }
16497
16669
  case "content_block_delta": {
@@ -16516,12 +16688,18 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUse
16516
16688
  supportsTerminalOutput,
16517
16689
  cwd,
16518
16690
  void 0,
16519
- enrichedReadCache
16691
+ enrichedReadCache,
16692
+ taskState
16520
16693
  );
16521
16694
  }
16522
16695
  case "content_block_stop":
16523
16696
  toolUseStreamCache.delete(event.index);
16524
16697
  return [];
16698
+ // `ping` is a Messages-API keep-alive event that the SDK's
16699
+ // `BetaRawMessageStreamEvent` union doesn't include even though the
16700
+ // wire format emits it; the `as never` cast lets us no-op it here
16701
+ // instead of falling through to `unreachable`.
16702
+ case "ping":
16525
16703
  case "message_start":
16526
16704
  case "message_delta":
16527
16705
  case "message_stop":
@@ -16592,6 +16770,38 @@ async function handleSystemMessage(message, context) {
16592
16770
  });
16593
16771
  break;
16594
16772
  }
16773
+ case "memory_recall": {
16774
+ const isSynthesis = message.mode === "synthesize";
16775
+ if (!isSynthesis && message.memories.length === 0) break;
16776
+ const locations = isSynthesis ? [] : message.memories.map((m) => ({ path: m.path }));
16777
+ const content = isSynthesis ? message.memories.filter(
16778
+ (m) => typeof m.content === "string"
16779
+ ).map((m) => ({
16780
+ type: "content",
16781
+ content: { type: "text", text: m.content }
16782
+ })) : [];
16783
+ const count = message.memories.length;
16784
+ const title = isSynthesis ? "Recalled synthesized memory" : `Recalled ${count} ${count === 1 ? "memory" : "memories"}`;
16785
+ await client.sessionUpdate({
16786
+ sessionId: message.session_id,
16787
+ update: {
16788
+ sessionUpdate: "tool_call",
16789
+ toolCallId: message.uuid,
16790
+ title,
16791
+ kind: "read",
16792
+ status: "completed",
16793
+ ...locations.length > 0 && { locations },
16794
+ ...content.length > 0 && { content },
16795
+ _meta: {
16796
+ claudeCode: {
16797
+ toolName: "memory_recall",
16798
+ toolResponse: { mode: message.mode }
16799
+ }
16800
+ }
16801
+ }
16802
+ });
16803
+ break;
16804
+ }
16595
16805
  default:
16596
16806
  break;
16597
16807
  }
@@ -16701,7 +16911,8 @@ async function handleStreamEvent(message, context) {
16701
16911
  context.registerHooks,
16702
16912
  context.supportsTerminalOutput,
16703
16913
  context.session.cwd,
16704
- context.enrichedReadCache
16914
+ context.enrichedReadCache,
16915
+ context.session.taskState
16705
16916
  )) {
16706
16917
  await client.sessionUpdate(notification);
16707
16918
  context.session.notificationHistory.push(notification);
@@ -16713,6 +16924,17 @@ function hasLocalCommandStdout(content) {
16713
16924
  function hasLocalCommandStderr(content) {
16714
16925
  return typeof content === "string" && content.includes("<local-command-stderr>");
16715
16926
  }
16927
+ function isSdkLocalCommandMessage(content) {
16928
+ return typeof content === "string" && content.includes("<command-name>") && (content.includes("<local-command-stdout>") || content.includes("<local-command-stderr>"));
16929
+ }
16930
+ var LOCAL_COMMAND_TAG_PATTERN = /<(command-name|command-message|command-args|local-command-stdout|local-command-stderr)>[\s\S]*?<\/\1>/g;
16931
+ function stripMarkerTags(text2) {
16932
+ return text2.replace(LOCAL_COMMAND_TAG_PATTERN, "");
16933
+ }
16934
+ function stripLocalCommandMetadata(content) {
16935
+ const stripped = stripMarkerTags(content);
16936
+ return stripped.trim() === "" ? null : stripped;
16937
+ }
16716
16938
  function isLoginRequiredMessage(message) {
16717
16939
  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;
16718
16940
  }
@@ -16721,7 +16943,7 @@ function isPlainTextUserMessage(message) {
16721
16943
  return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
16722
16944
  }
16723
16945
  function shouldSkipUserAssistantMessage(message) {
16724
- return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
16946
+ return isSdkLocalCommandMessage(message.message.content) || isLoginRequiredMessage(message);
16725
16947
  }
16726
16948
  function logSpecialMessages(message, logger) {
16727
16949
  const content = message.message.content;
@@ -16742,11 +16964,39 @@ function filterMessageContent(content) {
16742
16964
  }
16743
16965
  async function handleUserAssistantMessage(message, context) {
16744
16966
  const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
16967
+ if (message.message.role === "system") {
16968
+ return {};
16969
+ }
16745
16970
  if (shouldSkipUserAssistantMessage(message)) {
16746
16971
  logSpecialMessages(message, logger);
16747
16972
  if (isLoginRequiredMessage(message)) {
16748
16973
  return { shouldStop: true, error: import_sdk.RequestError.authRequired() };
16749
16974
  }
16975
+ const rawContent = message.message.content;
16976
+ if (typeof rawContent === "string") {
16977
+ const stripped = stripLocalCommandMetadata(rawContent);
16978
+ if (stripped !== null) {
16979
+ for (const notification of toAcpNotifications(
16980
+ stripped,
16981
+ message.message.role,
16982
+ sessionId,
16983
+ toolUseCache,
16984
+ fileContentCache,
16985
+ client,
16986
+ logger,
16987
+ void 0,
16988
+ context.registerHooks,
16989
+ context.supportsTerminalOutput,
16990
+ session.cwd,
16991
+ void 0,
16992
+ context.enrichedReadCache,
16993
+ session.taskState
16994
+ )) {
16995
+ await client.sessionUpdate(notification);
16996
+ session.notificationHistory.push(notification);
16997
+ }
16998
+ }
16999
+ }
16750
17000
  return {};
16751
17001
  }
16752
17002
  if (isPlainTextUserMessage(message)) {
@@ -16769,7 +17019,8 @@ async function handleUserAssistantMessage(message, context) {
16769
17019
  context.supportsTerminalOutput,
16770
17020
  session.cwd,
16771
17021
  mcpToolUseResult,
16772
- context.enrichedReadCache
17022
+ context.enrichedReadCache,
17023
+ session.taskState
16773
17024
  )) {
16774
17025
  await client.sessionUpdate(notification);
16775
17026
  session.notificationHistory.push(notification);
@@ -16962,8 +17213,10 @@ var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
16962
17213
  var AGENT_TOOLS = /* @__PURE__ */ new Set([
16963
17214
  "Task",
16964
17215
  "Agent",
16965
- "TodoWrite",
16966
- "Skill"
17216
+ "TaskCreate",
17217
+ "TaskUpdate",
17218
+ "TaskGet",
17219
+ "TaskList"
16967
17220
  ]);
16968
17221
  var BASE_ALLOWED_TOOLS = [
16969
17222
  ...READ_TOOLS,
@@ -17047,8 +17300,8 @@ function buildPermissionOptions(toolName, toolInput, repoRoot, suggestions) {
17047
17300
  if (toolName === "Task") {
17048
17301
  return permissionOptions("Yes, allow all sub-tasks");
17049
17302
  }
17050
- if (toolName === "TodoWrite") {
17051
- return permissionOptions("Yes, allow all todo updates");
17303
+ if (toolName === "TaskCreate" || toolName === "TaskUpdate" || toolName === "TaskGet" || toolName === "TaskList") {
17304
+ return permissionOptions("Yes, allow all task updates");
17052
17305
  }
17053
17306
  return permissionOptions("Yes, always allow");
17054
17307
  }
@@ -17600,6 +17853,7 @@ async function canUseTool(context) {
17600
17853
 
17601
17854
  // src/adapters/claude/session/commands.ts
17602
17855
  var UNSUPPORTED_COMMANDS = [
17856
+ "clear",
17603
17857
  "context",
17604
17858
  "cost",
17605
17859
  "keybindings-help",
@@ -17654,18 +17908,30 @@ function loadUserClaudeJsonMcpServers(cwd, logger, homeDir = os3.homedir()) {
17654
17908
  const projectScoped = project?.mcpServers && typeof project.mcpServers === "object" ? project.mcpServers : {};
17655
17909
  return { ...topLevel, ...projectScoped };
17656
17910
  }
17657
- function parseMcpServers(params) {
17911
+ function parseMcpServers(params, logger) {
17658
17912
  const mcpServers = {};
17659
17913
  if (!Array.isArray(params.mcpServers)) {
17660
17914
  return mcpServers;
17661
17915
  }
17662
17916
  for (const server of params.mcpServers) {
17663
17917
  if ("type" in server) {
17664
- mcpServers[server.name] = {
17665
- type: server.type,
17666
- url: server.url,
17667
- headers: server.headers ? Object.fromEntries(server.headers.map((e) => [e.name, e.value])) : void 0
17668
- };
17918
+ if (server.type === "http" || server.type === "sse") {
17919
+ mcpServers[server.name] = {
17920
+ type: server.type,
17921
+ url: server.url,
17922
+ headers: server.headers ? Object.fromEntries(
17923
+ server.headers.map((e) => [
17924
+ e.name,
17925
+ e.value
17926
+ ])
17927
+ ) : void 0
17928
+ };
17929
+ } else {
17930
+ logger?.warn("parseMcpServers: dropping unsupported MCP server type", {
17931
+ name: server.name,
17932
+ type: server.type
17933
+ });
17934
+ }
17669
17935
  } else {
17670
17936
  mcpServers[server.name] = {
17671
17937
  type: "stdio",
@@ -17678,6 +17944,36 @@ function parseMcpServers(params) {
17678
17944
  return mcpServers;
17679
17945
  }
17680
17946
 
17947
+ // src/adapters/claude/session/model-config.ts
17948
+ function applyAvailableModelsAllowlist(modelOptions, allowlist) {
17949
+ const filtered = [];
17950
+ const seen = /* @__PURE__ */ new Set();
17951
+ for (const entry of allowlist) {
17952
+ const trimmed2 = entry.trim();
17953
+ if (!trimmed2 || seen.has(trimmed2)) continue;
17954
+ const match = modelOptions.options.find((o) => o.value === trimmed2);
17955
+ if (match) {
17956
+ filtered.push(match);
17957
+ seen.add(trimmed2);
17958
+ }
17959
+ }
17960
+ if (filtered.length === 0) return modelOptions;
17961
+ const currentModelId = filtered.some(
17962
+ (o) => o.value === modelOptions.currentModelId
17963
+ ) ? modelOptions.currentModelId : filtered[0].value;
17964
+ return { currentModelId, options: filtered };
17965
+ }
17966
+ function resolveInitialModelId(modelOptions, preferredModelIds) {
17967
+ const allowedModelIds = new Set(modelOptions.options.map((opt) => opt.value));
17968
+ for (const candidate of preferredModelIds) {
17969
+ const trimmed2 = candidate?.trim();
17970
+ if (trimmed2 && allowedModelIds.has(trimmed2)) {
17971
+ return trimmed2;
17972
+ }
17973
+ }
17974
+ return modelOptions.currentModelId;
17975
+ }
17976
+
17681
17977
  // src/adapters/claude/session/options.ts
17682
17978
  var import_node_child_process3 = require("child_process");
17683
17979
  var fs8 = __toESM(require("fs"), 1);
@@ -17745,6 +18041,7 @@ function buildEnvironment() {
17745
18041
  const existingCustomHeaders = process.env.ANTHROPIC_CUSTOM_HEADERS;
17746
18042
  const customHeaders = existingCustomHeaders ? `${existingCustomHeaders}
17747
18043
  ${bedrockFallbackHeader}` : bedrockFallbackHeader;
18044
+ const mcpNonblocking = process.env.MCP_CONNECTION_NONBLOCKING;
17748
18045
  return {
17749
18046
  ...process.env,
17750
18047
  ELECTRON_RUN_AS_NODE: "1",
@@ -17753,11 +18050,14 @@ ${bedrockFallbackHeader}` : bedrockFallbackHeader;
17753
18050
  ENABLE_TOOL_SEARCH: "auto:0",
17754
18051
  // Enable idle state as end-of-turn signal (required for SDK 0.2.114+)
17755
18052
  CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: "1",
18053
+ ...mcpNonblocking !== void 0 && {
18054
+ MCP_CONNECTION_NONBLOCKING: mcpNonblocking
18055
+ },
17756
18056
  // Route to AWS Bedrock as a fallback when Anthropic returns 5xx
17757
18057
  ANTHROPIC_CUSTOM_HEADERS: customHeaders
17758
18058
  };
17759
18059
  }
17760
- function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache, registeredAgents, cloudMode) {
18060
+ function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache, registeredAgents, cloudMode, taskState, onTaskStateChange) {
17761
18061
  const postToolUseHooks = [createPostToolUseHook({ onModeChange })];
17762
18062
  if (enrichmentDeps && enrichedReadCache) {
17763
18063
  postToolUseHooks.push(
@@ -17771,13 +18071,16 @@ function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichment
17771
18071
  if (cloudMode) {
17772
18072
  preToolUseHooks.push(createSignedCommitGuardHook(logger));
17773
18073
  }
18074
+ const taskHook = createTaskHook(taskState, onTaskStateChange);
17774
18075
  return {
17775
18076
  ...userHooks,
17776
18077
  PostToolUse: [
17777
18078
  ...userHooks?.PostToolUse || [],
17778
18079
  { hooks: postToolUseHooks }
17779
18080
  ],
17780
- PreToolUse: [...userHooks?.PreToolUse || [], { hooks: preToolUseHooks }]
18081
+ PreToolUse: [...userHooks?.PreToolUse || [], { hooks: preToolUseHooks }],
18082
+ TaskCreated: [...userHooks?.TaskCreated || [], { hooks: [taskHook] }],
18083
+ TaskCompleted: [...userHooks?.TaskCompleted || [], { hooks: [taskHook] }]
17781
18084
  };
17782
18085
  }
17783
18086
  var PH_EXPLORE_AGENT = {
@@ -17808,7 +18111,10 @@ Rules:
17808
18111
  "WebFetch",
17809
18112
  "WebSearch",
17810
18113
  "NotebookRead",
17811
- "TodoWrite"
18114
+ "TaskCreate",
18115
+ "TaskUpdate",
18116
+ "TaskGet",
18117
+ "TaskList"
17812
18118
  ]
17813
18119
  };
17814
18120
  function buildAgents(userAgents) {
@@ -17936,7 +18242,9 @@ function buildSessionOptions(params) {
17936
18242
  params.enrichmentDeps,
17937
18243
  params.enrichedReadCache,
17938
18244
  registeredAgentNames,
17939
- params.cloudMode ?? false
18245
+ params.cloudMode ?? false,
18246
+ params.taskState,
18247
+ params.onTaskStateChange
17940
18248
  ),
17941
18249
  outputFormat: params.outputFormat,
17942
18250
  abortController: getAbortController(
@@ -18165,6 +18473,15 @@ function getManagedSettingsPath() {
18165
18473
  return "/etc/claude-code/managed-settings.json";
18166
18474
  }
18167
18475
  }
18476
+ function mergeAvailableModels(existing, incoming, layer) {
18477
+ if (incoming === void 0) {
18478
+ return existing;
18479
+ }
18480
+ if (layer === "enterprise") {
18481
+ return Array.from(new Set(incoming));
18482
+ }
18483
+ return Array.from(/* @__PURE__ */ new Set([...existing ?? [], ...incoming]));
18484
+ }
18168
18485
  var SettingsManager = class {
18169
18486
  cwd;
18170
18487
  repoRoot;
@@ -18220,10 +18537,10 @@ var SettingsManager = class {
18220
18537
  }
18221
18538
  mergeAllSettings() {
18222
18539
  const allSettings = [
18223
- this.userSettings,
18224
- this.projectSettings,
18225
- this.localSettings,
18226
- this.enterpriseSettings
18540
+ { layer: "user", settings: this.userSettings },
18541
+ { layer: "project", settings: this.projectSettings },
18542
+ { layer: "local", settings: this.localSettings },
18543
+ { layer: "enterprise", settings: this.enterpriseSettings }
18227
18544
  ];
18228
18545
  const permissions = {
18229
18546
  allow: [],
@@ -18232,7 +18549,7 @@ var SettingsManager = class {
18232
18549
  };
18233
18550
  const merged = { permissions };
18234
18551
  const posthogApprovedExecTools = /* @__PURE__ */ new Set();
18235
- for (const settings of allSettings) {
18552
+ for (const { layer, settings } of allSettings) {
18236
18553
  if (settings.permissions) {
18237
18554
  if (settings.permissions.allow) {
18238
18555
  permissions.allow?.push(...settings.permissions.allow);
@@ -18259,6 +18576,11 @@ var SettingsManager = class {
18259
18576
  if (settings.model) {
18260
18577
  merged.model = settings.model;
18261
18578
  }
18579
+ merged.availableModels = mergeAvailableModels(
18580
+ merged.availableModels,
18581
+ settings.availableModels,
18582
+ layer
18583
+ );
18262
18584
  if (settings.posthogApprovedExecTools) {
18263
18585
  for (const tool2 of settings.posthogApprovedExecTools) {
18264
18586
  posthogApprovedExecTools.add(tool2);
@@ -18467,6 +18789,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18467
18789
  },
18468
18790
  loadSession: true,
18469
18791
  sessionCapabilities: {
18792
+ additionalDirectories: {},
18470
18793
  list: {},
18471
18794
  fork: {},
18472
18795
  resume: {}
@@ -18492,10 +18815,18 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18492
18815
  if (fs10.existsSync(path12.resolve(os6.homedir(), ".claude.json.backup")) && !fs10.existsSync(path12.resolve(os6.homedir(), ".claude.json"))) {
18493
18816
  throw import_sdk2.RequestError.authRequired();
18494
18817
  }
18495
- const response = await this.createSession(params, {
18496
- // Revisit these meta values once we support resume
18497
- resume: params._meta?.claudeCode?.options?.resume
18498
- });
18818
+ const response = await this.createSession(
18819
+ {
18820
+ cwd: params.cwd,
18821
+ mcpServers: params.mcpServers ?? [],
18822
+ additionalDirectories: params.additionalDirectories,
18823
+ _meta: params._meta
18824
+ },
18825
+ {
18826
+ // Revisit these meta values once we support resume
18827
+ resume: params._meta?.claudeCode?.options?.resume
18828
+ }
18829
+ );
18499
18830
  return response;
18500
18831
  }
18501
18832
  async unstable_forkSession(params) {
@@ -18503,24 +18834,27 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18503
18834
  {
18504
18835
  cwd: params.cwd,
18505
18836
  mcpServers: params.mcpServers ?? [],
18837
+ additionalDirectories: params.additionalDirectories,
18506
18838
  _meta: params._meta
18507
18839
  },
18508
18840
  { resume: params.sessionId, forkSession: true }
18509
18841
  );
18510
18842
  }
18511
- async unstable_resumeSession(params) {
18843
+ async resumeSession(params) {
18512
18844
  const existing = this.getExistingSessionState(params.sessionId);
18513
18845
  if (existing) return existing;
18514
18846
  const response = await this.createSession(
18515
18847
  {
18516
18848
  cwd: params.cwd,
18517
18849
  mcpServers: params.mcpServers ?? [],
18850
+ additionalDirectories: params.additionalDirectories,
18518
18851
  _meta: params._meta
18519
18852
  },
18520
18853
  {
18521
18854
  resume: params.sessionId
18522
18855
  }
18523
18856
  );
18857
+ await this.rehydrateTaskStateFromJsonl(params.sessionId);
18524
18858
  return response;
18525
18859
  }
18526
18860
  async loadSession(params) {
@@ -18530,6 +18864,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18530
18864
  {
18531
18865
  cwd: params.cwd,
18532
18866
  mcpServers: params.mcpServers ?? [],
18867
+ additionalDirectories: params.additionalDirectories,
18533
18868
  _meta: params._meta
18534
18869
  },
18535
18870
  { resume: params.sessionId, skipBackgroundFetches: true }
@@ -18608,6 +18943,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18608
18943
  await this.broadcastUserMessage(params);
18609
18944
  this.session.promptRunning = true;
18610
18945
  let handedOff = false;
18946
+ let errored = false;
18611
18947
  let lastAssistantTotalUsage = null;
18612
18948
  let lastStreamUsage = {
18613
18949
  input_tokens: 0,
@@ -18615,6 +18951,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18615
18951
  cache_read_input_tokens: 0,
18616
18952
  cache_creation_input_tokens: 0
18617
18953
  };
18954
+ let compactionInProgress = false;
18618
18955
  if (this.session.lastContextWindowSize == null) {
18619
18956
  this.session.lastContextWindowSize = this.getContextWindowForModel(
18620
18957
  this.session.modelId ?? ""
@@ -18672,6 +19009,40 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18672
19009
  if (message.subtype === "local_command_output") {
18673
19010
  promptReplayed = true;
18674
19011
  }
19012
+ if (message.subtype === "status") {
19013
+ if (message.status === "compacting") {
19014
+ compactionInProgress = true;
19015
+ } else if (message.compact_result === "success" && compactionInProgress) {
19016
+ compactionInProgress = false;
19017
+ await this.client.sessionUpdate({
19018
+ sessionId: params.sessionId,
19019
+ update: {
19020
+ sessionUpdate: "agent_message_chunk",
19021
+ content: {
19022
+ type: "text",
19023
+ text: "\n\nCompacting completed."
19024
+ }
19025
+ }
19026
+ });
19027
+ break;
19028
+ } else if (message.compact_result === "failed" && compactionInProgress) {
19029
+ compactionInProgress = false;
19030
+ const reason = message.compact_error ? `: ${message.compact_error}` : ".";
19031
+ await this.client.sessionUpdate({
19032
+ sessionId: params.sessionId,
19033
+ update: {
19034
+ sessionUpdate: "agent_message_chunk",
19035
+ content: {
19036
+ type: "text",
19037
+ text: `
19038
+
19039
+ Compacting failed${reason}`
19040
+ }
19041
+ }
19042
+ });
19043
+ break;
19044
+ }
19045
+ }
18675
19046
  if (message.subtype === "session_state_changed" && message.state === "idle") {
18676
19047
  if (!promptReplayed) {
18677
19048
  const cmdName = commandMatch?.[1].slice(1);
@@ -18711,7 +19082,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18711
19082
  size: lastContextWindowSize
18712
19083
  }
18713
19084
  });
18714
- return { stopReason: "end_turn" };
19085
+ return {
19086
+ stopReason: this.session.cancelled ? "cancelled" : "end_turn"
19087
+ };
18715
19088
  }
18716
19089
  await handleSystemMessage(message, context);
18717
19090
  break;
@@ -18894,6 +19267,28 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18894
19267
  }
18895
19268
  throw new Error("Session did not end in result");
18896
19269
  } catch (error) {
19270
+ errored = true;
19271
+ try {
19272
+ await this.session.query.interrupt();
19273
+ const MAX_DRAIN = 100;
19274
+ for (let i2 = 0; i2 < MAX_DRAIN; i2++) {
19275
+ const { value: m, done } = await this.session.query.next();
19276
+ if (done || !m) break;
19277
+ if (m.type === "system" && m.subtype === "session_state_changed" && m.state === "idle") {
19278
+ break;
19279
+ }
19280
+ if (i2 === MAX_DRAIN - 1) {
19281
+ this.logger.error(
19282
+ `Session ${params.sessionId}: drained ${MAX_DRAIN} messages after error without observing idle`
19283
+ );
19284
+ }
19285
+ }
19286
+ } catch (drainErr) {
19287
+ this.logger.error(
19288
+ `Session ${params.sessionId}: failed to drain query after prompt error`,
19289
+ { error: drainErr }
19290
+ );
19291
+ }
18897
19292
  if (error instanceof import_sdk2.RequestError || !(error instanceof Error)) {
18898
19293
  throw error;
18899
19294
  }
@@ -18914,9 +19309,19 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18914
19309
  this.toolUseStreamCache.clear();
18915
19310
  if (!handedOff) {
18916
19311
  this.session.promptRunning = false;
18917
- for (const [key, pending] of this.session.pendingMessages) {
18918
- pending.resolve(true);
18919
- this.session.pendingMessages.delete(key);
19312
+ if (errored) {
19313
+ for (const pending of this.session.pendingMessages.values()) {
19314
+ pending.resolve(true);
19315
+ }
19316
+ this.session.pendingMessages.clear();
19317
+ } else if (this.session.pendingMessages.size > 0) {
19318
+ const next = [...this.session.pendingMessages.entries()].sort(
19319
+ (a, b) => a[1].order - b[1].order
19320
+ )[0];
19321
+ if (next) {
19322
+ next[1].resolve(false);
19323
+ this.session.pendingMessages.delete(next[0]);
19324
+ }
18920
19325
  }
18921
19326
  }
18922
19327
  }
@@ -18967,7 +19372,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18967
19372
  );
18968
19373
  }
18969
19374
  const mcpServers = parseMcpServers(
18970
- params
19375
+ params,
19376
+ this.logger
18971
19377
  );
18972
19378
  await this.refreshSession(mcpServers);
18973
19379
  return { refreshed: true };
@@ -19152,7 +19558,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19152
19558
  const settingsManager = new SettingsManager(cwd);
19153
19559
  await settingsManager.initialize();
19154
19560
  const earlyModelId = settingsManager.getSettings().model || meta?.model || "";
19155
- const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params) : {};
19561
+ const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params, this.logger) : {};
19156
19562
  const localToolsServer = createLocalToolsMcpServer(
19157
19563
  { cwd, token: resolveGithubToken(), taskId },
19158
19564
  meta
@@ -19176,6 +19582,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19176
19582
  cwd
19177
19583
  });
19178
19584
  const permissionMode = meta?.permissionMode && CODE_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
19585
+ const taskState = /* @__PURE__ */ new Map();
19179
19586
  const options = buildSessionOptions({
19180
19587
  cwd,
19181
19588
  mcpServers,
@@ -19189,7 +19596,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19189
19596
  forkSession,
19190
19597
  additionalDirectories: [
19191
19598
  ...meta?.claudeCode?.options?.additionalDirectories ?? [],
19192
- ...meta?.additionalRoots ?? []
19599
+ // Prefer the official ACP `additionalDirectories` field. Fall back
19600
+ // to the legacy `_meta.additionalRoots` extension for clients that
19601
+ // haven't been updated yet.
19602
+ ...params.additionalDirectories ?? meta?.additionalRoots ?? []
19193
19603
  ],
19194
19604
  disableBuiltInTools: meta?.disableBuiltInTools,
19195
19605
  outputFormat,
@@ -19200,7 +19610,17 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19200
19610
  effort,
19201
19611
  enrichmentDeps: this.enrichment?.deps,
19202
19612
  enrichedReadCache: this.enrichedReadCache,
19203
- cloudMode: cloudRun
19613
+ cloudMode: cloudRun,
19614
+ taskState,
19615
+ onTaskStateChange: async () => {
19616
+ await this.client.sessionUpdate({
19617
+ sessionId,
19618
+ update: {
19619
+ sessionUpdate: "plan",
19620
+ entries: taskStateToPlanEntries(taskState)
19621
+ }
19622
+ });
19623
+ }
19204
19624
  });
19205
19625
  const abortController = options.abortController;
19206
19626
  const q = (0, import_claude_agent_sdk2.query)({ prompt: input, options });
@@ -19229,6 +19649,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19229
19649
  systemPrompt: estimateSystemPrompt(systemPrompt),
19230
19650
  rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
19231
19651
  },
19652
+ taskState,
19232
19653
  // Custom properties
19233
19654
  cwd,
19234
19655
  notificationHistory: [],
@@ -19268,7 +19689,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19268
19689
  }
19269
19690
  }
19270
19691
  const initPromise2 = !isResume ? withTimeout(q.initializationResult(), SESSION_VALIDATION_TIMEOUT_MS) : void 0;
19271
- const [modelOptions] = await Promise.all([
19692
+ const [rawModelOptions] = await Promise.all([
19272
19693
  this.getModelConfigOptions(
19273
19694
  settingsManager.getSettings().model || meta?.model || void 0
19274
19695
  ),
@@ -19280,6 +19701,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19280
19701
  })
19281
19702
  ] : []
19282
19703
  ]);
19704
+ const settingsAvailableModels = settingsManager.getSettings().availableModels;
19705
+ const modelOptions = Array.isArray(settingsAvailableModels) ? applyAvailableModelsAllowlist(rawModelOptions, settingsAvailableModels) : rawModelOptions;
19283
19706
  if (initPromise2) {
19284
19707
  try {
19285
19708
  const initResult = await initPromise2;
@@ -19303,9 +19726,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19303
19726
  throw err2;
19304
19727
  }
19305
19728
  }
19306
- const settingsModel = settingsManager.getSettings().model;
19307
- const metaModel = meta?.model;
19308
- const resolvedModelId = settingsModel || metaModel || modelOptions.currentModelId;
19729
+ const resolvedModelId = resolveInitialModelId(modelOptions, [
19730
+ settingsManager.getSettings().model,
19731
+ meta?.model
19732
+ ]);
19309
19733
  session.modelId = resolvedModelId;
19310
19734
  session.lastContextWindowSize = this.getContextWindowForModel(resolvedModelId);
19311
19735
  const resolvedSdkModel = toSdkModelId(resolvedModelId);
@@ -19513,6 +19937,34 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19513
19937
  [key]: tokens
19514
19938
  };
19515
19939
  }
19940
+ /**
19941
+ * Rebuild the in-memory taskState from JSONL and push a plan update so the
19942
+ * client's plan panel reflects pre-resume tasks. `loadSession` already covers
19943
+ * this via the full `replaySessionHistory` notification stream; resume
19944
+ * deliberately stays quiet (the client keeps its own message history) so we
19945
+ * walk the transcript here for state only.
19946
+ */
19947
+ async rehydrateTaskStateFromJsonl(sessionId) {
19948
+ try {
19949
+ const messages = await (0, import_claude_agent_sdk2.getSessionMessages)(sessionId, {
19950
+ dir: this.session.cwd
19951
+ });
19952
+ rehydrateTaskState(messages, this.session.taskState);
19953
+ if (this.session.taskState.size === 0) return;
19954
+ await this.client.sessionUpdate({
19955
+ sessionId,
19956
+ update: {
19957
+ sessionUpdate: "plan",
19958
+ entries: taskStateToPlanEntries(this.session.taskState)
19959
+ }
19960
+ });
19961
+ } catch (err2) {
19962
+ this.logger.warn("Failed to rehydrate task state", {
19963
+ sessionId,
19964
+ error: err2 instanceof Error ? err2.message : String(err2)
19965
+ });
19966
+ }
19967
+ }
19516
19968
  async replaySessionHistory(sessionId) {
19517
19969
  try {
19518
19970
  const messages = await (0, import_claude_agent_sdk2.getSessionMessages)(sessionId, {
@@ -20266,7 +20718,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20266
20718
  }
20267
20719
  return response;
20268
20720
  }
20269
- async unstable_resumeSession(params) {
20721
+ async resumeSession(params) {
20270
20722
  const meta = params._meta;
20271
20723
  const injectedParams = this.applyLocalTools(
20272
20724
  this.applyStructuredOutput(