@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
@@ -9315,7 +9315,7 @@ import { z as z5 } from "zod";
9315
9315
  // package.json
9316
9316
  var package_default = {
9317
9317
  name: "@posthog/agent",
9318
- version: "2.3.735",
9318
+ version: "2.3.736",
9319
9319
  repository: "https://github.com/PostHog/code",
9320
9320
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
9321
9321
  exports: {
@@ -9435,9 +9435,9 @@ var package_default = {
9435
9435
  vitest: "^2.1.8"
9436
9436
  },
9437
9437
  dependencies: {
9438
- "@agentclientprotocol/sdk": "0.19.0",
9439
- "@anthropic-ai/claude-agent-sdk": "0.2.112",
9440
- "@anthropic-ai/sdk": "0.89.0",
9438
+ "@agentclientprotocol/sdk": "0.22.1",
9439
+ "@anthropic-ai/claude-agent-sdk": "0.3.154",
9440
+ "@anthropic-ai/sdk": "0.100.0",
9441
9441
  "@hono/node-server": "^1.19.9",
9442
9442
  "@opentelemetry/api-logs": "^0.208.0",
9443
9443
  "@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
@@ -15157,6 +15157,30 @@ var registerHookCallback = (toolUseID, {
15157
15157
  onPostToolUseHook
15158
15158
  };
15159
15159
  };
15160
+ var createTaskHook = (taskState, onChange) => async (input) => {
15161
+ const taskId = "task_id" in input && typeof input.task_id === "string" ? input.task_id : void 0;
15162
+ if (!taskId) return { continue: true };
15163
+ let mutated = false;
15164
+ if (input.hook_event_name === "TaskCreated") {
15165
+ if (!input.task_subject) return { continue: true };
15166
+ if (taskState.has(taskId)) return { continue: true };
15167
+ taskState.set(taskId, {
15168
+ subject: input.task_subject,
15169
+ status: "pending",
15170
+ description: input.task_description
15171
+ });
15172
+ mutated = true;
15173
+ } else if (input.hook_event_name === "TaskCompleted") {
15174
+ const existing = taskState.get(taskId);
15175
+ if (!existing || existing.status === "completed") {
15176
+ return { continue: true };
15177
+ }
15178
+ taskState.set(taskId, { ...existing, status: "completed" });
15179
+ mutated = true;
15180
+ }
15181
+ if (mutated && onChange) await onChange();
15182
+ return { continue: true };
15183
+ };
15160
15184
  var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
15161
15185
  if (input.hook_event_name === "PostToolUse") {
15162
15186
  const toolName = input.tool_name;
@@ -15171,10 +15195,8 @@ var createPostToolUseHook = ({ onModeChange }) => async (input, toolUseID) => {
15171
15195
  input.tool_input,
15172
15196
  input.tool_response
15173
15197
  );
15174
- delete toolUseCallbacks[toolUseID];
15175
- } else {
15176
- delete toolUseCallbacks[toolUseID];
15177
15198
  }
15199
+ delete toolUseCallbacks[toolUseID];
15178
15200
  }
15179
15201
  }
15180
15202
  return { continue: true };
@@ -15321,6 +15343,104 @@ var createPreToolUseHook = (settingsManager, logger) => async (input, _toolUseID
15321
15343
  }
15322
15344
  };
15323
15345
 
15346
+ // src/adapters/claude/conversion/task-state.ts
15347
+ function parseTaskCreateOutput(content) {
15348
+ const tryParse = (text2) => {
15349
+ try {
15350
+ const parsed = JSON.parse(text2);
15351
+ if (parsed && typeof parsed === "object" && parsed.task && typeof parsed.task.id === "string") {
15352
+ return parsed;
15353
+ }
15354
+ } catch {
15355
+ }
15356
+ return void 0;
15357
+ };
15358
+ if (typeof content === "string") {
15359
+ return tryParse(content);
15360
+ }
15361
+ if (Array.isArray(content)) {
15362
+ for (const block of content) {
15363
+ if (block && typeof block === "object" && "type" in block && block.type === "text") {
15364
+ const text2 = block.text;
15365
+ if (typeof text2 === "string") {
15366
+ const parsed = tryParse(text2);
15367
+ if (parsed) return parsed;
15368
+ }
15369
+ }
15370
+ }
15371
+ }
15372
+ return void 0;
15373
+ }
15374
+ function applyTaskCreate(state, input, output) {
15375
+ const taskId = output?.task?.id;
15376
+ if (!taskId || !input) return;
15377
+ state.set(taskId, {
15378
+ subject: input.subject,
15379
+ status: "pending",
15380
+ activeForm: input.activeForm,
15381
+ description: input.description
15382
+ });
15383
+ }
15384
+ function applyTaskUpdate(state, input) {
15385
+ if (!input?.taskId) return;
15386
+ if (input.status === "deleted") {
15387
+ state.delete(input.taskId);
15388
+ return;
15389
+ }
15390
+ const existing = state.get(input.taskId);
15391
+ const subject = input.subject ?? existing?.subject;
15392
+ if (!subject) return;
15393
+ state.set(input.taskId, {
15394
+ subject,
15395
+ status: input.status ?? existing?.status ?? "pending",
15396
+ activeForm: input.activeForm ?? existing?.activeForm,
15397
+ description: input.description ?? existing?.description
15398
+ });
15399
+ }
15400
+ function taskStateToPlanEntries(state) {
15401
+ return Array.from(state.values()).map((task) => ({
15402
+ content: task.subject,
15403
+ status: task.status,
15404
+ priority: "medium"
15405
+ }));
15406
+ }
15407
+ function isToolUseBlock(block) {
15408
+ return !!block && typeof block === "object" && block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string";
15409
+ }
15410
+ function isToolResultBlock(block) {
15411
+ return !!block && typeof block === "object" && block.type === "tool_result" && typeof block.tool_use_id === "string";
15412
+ }
15413
+ function rehydrateTaskState(messages, state) {
15414
+ const pendingInputs = /* @__PURE__ */ new Map();
15415
+ for (const msg of messages) {
15416
+ const content = msg.message?.content;
15417
+ if (!Array.isArray(content)) continue;
15418
+ if (msg.type === "assistant") {
15419
+ for (const block of content) {
15420
+ if (isToolUseBlock(block) && (block.name === "TaskCreate" || block.name === "TaskUpdate")) {
15421
+ pendingInputs.set(block.id, { name: block.name, input: block.input });
15422
+ }
15423
+ }
15424
+ } else if (msg.type === "user") {
15425
+ for (const block of content) {
15426
+ if (!isToolResultBlock(block) || block.is_error) continue;
15427
+ const cached = pendingInputs.get(block.tool_use_id);
15428
+ if (!cached) continue;
15429
+ if (cached.name === "TaskCreate") {
15430
+ applyTaskCreate(
15431
+ state,
15432
+ cached.input,
15433
+ parseTaskCreateOutput(block.content)
15434
+ );
15435
+ } else if (cached.name === "TaskUpdate") {
15436
+ applyTaskUpdate(state, cached.input);
15437
+ }
15438
+ pendingInputs.delete(block.tool_use_id);
15439
+ }
15440
+ }
15441
+ }
15442
+ }
15443
+
15324
15444
  // src/adapters/claude/conversion/tool-use-to-acp.ts
15325
15445
  import fs6 from "fs";
15326
15446
  import path7 from "path";
@@ -15672,9 +15792,31 @@ function toolInfoFromToolUse(toolUse, options) {
15672
15792
  content: []
15673
15793
  };
15674
15794
  }
15675
- case "TodoWrite":
15795
+ case "TaskCreate": {
15796
+ const subject = typeof input?.subject === "string" ? input.subject : void 0;
15676
15797
  return {
15677
- title: Array.isArray(input?.todos) ? `Update TODOs: ${input.todos.map((todo) => todo.content).join(", ")}` : "Update TODOs",
15798
+ title: subject ? `Create task: ${subject}` : "Create task",
15799
+ kind: "think",
15800
+ content: []
15801
+ };
15802
+ }
15803
+ case "TaskUpdate": {
15804
+ const subject = typeof input?.subject === "string" ? input.subject : void 0;
15805
+ return {
15806
+ title: subject ? `Update task: ${subject}` : "Update task",
15807
+ kind: "think",
15808
+ content: []
15809
+ };
15810
+ }
15811
+ case "TaskList":
15812
+ return {
15813
+ title: "List tasks",
15814
+ kind: "think",
15815
+ content: []
15816
+ };
15817
+ case "TaskGet":
15818
+ return {
15819
+ title: "Get task",
15678
15820
  kind: "think",
15679
15821
  content: []
15680
15822
  };
@@ -15958,13 +16100,6 @@ ${content}
15958
16100
  }
15959
16101
  return {};
15960
16102
  }
15961
- function planEntries(input) {
15962
- return input.todos.map((input2) => ({
15963
- content: input2.content,
15964
- status: input2.status,
15965
- priority: "medium"
15966
- }));
15967
- }
15968
16103
  function resolveFileContent(filePath, oldText, cachedFileContent) {
15969
16104
  if (cachedFileContent && filePath in cachedFileContent) {
15970
16105
  const cached = cachedFileContent[filePath];
@@ -16050,14 +16185,7 @@ function handleThinkingChunk(chunk, parentToolCallId) {
16050
16185
  function handleToolUseChunk(chunk, ctx) {
16051
16186
  const alreadyCached = chunk.id in ctx.toolUseCache;
16052
16187
  ctx.toolUseCache[chunk.id] = chunk;
16053
- if (chunk.name === "TodoWrite") {
16054
- const input = chunk.input;
16055
- if (Array.isArray(input.todos)) {
16056
- return {
16057
- sessionUpdate: "plan",
16058
- entries: planEntries(chunk.input)
16059
- };
16060
- }
16188
+ if (chunk.name === "TaskCreate" || chunk.name === "TaskUpdate" || chunk.name === "TaskList" || chunk.name === "TaskGet") {
16061
16189
  return null;
16062
16190
  }
16063
16191
  if (!alreadyCached && ctx.registerHooks !== false) {
@@ -16065,7 +16193,7 @@ function handleToolUseChunk(chunk, ctx) {
16065
16193
  onPostToolUseHook: async (toolUseId, _toolInput, toolResponse) => {
16066
16194
  const toolUse = ctx.toolUseCache[toolUseId];
16067
16195
  if (toolUse) {
16068
- const editUpdate = toolUse.name === "Edit" ? toolUpdateFromEditToolResponse(toolResponse) : null;
16196
+ const editUpdate = toolUse.name === "Edit" || toolUse.name === "Write" ? toolUpdateFromEditToolResponse(toolResponse) : null;
16069
16197
  await ctx.client.sessionUpdate({
16070
16198
  sessionId: ctx.sessionId,
16071
16199
  update: {
@@ -16177,7 +16305,28 @@ function handleToolResultChunk(chunk, ctx) {
16177
16305
  );
16178
16306
  return [];
16179
16307
  }
16180
- if (toolUse.name === "TodoWrite") {
16308
+ if (toolUse.name === "TaskCreate" || toolUse.name === "TaskUpdate" || toolUse.name === "TaskList" || toolUse.name === "TaskGet") {
16309
+ if (chunk.is_error || !ctx.taskState) return [];
16310
+ if (toolUse.name === "TaskCreate") {
16311
+ applyTaskCreate(
16312
+ ctx.taskState,
16313
+ toolUse.input,
16314
+ parseTaskCreateOutput(chunk.content)
16315
+ );
16316
+ } else if (toolUse.name === "TaskUpdate") {
16317
+ applyTaskUpdate(
16318
+ ctx.taskState,
16319
+ toolUse.input
16320
+ );
16321
+ }
16322
+ if (toolUse.name === "TaskCreate" || toolUse.name === "TaskUpdate") {
16323
+ return [
16324
+ {
16325
+ sessionUpdate: "plan",
16326
+ entries: taskStateToPlanEntries(ctx.taskState)
16327
+ }
16328
+ ];
16329
+ }
16181
16330
  return [];
16182
16331
  }
16183
16332
  if (!chunk.is_error) {
@@ -16274,13 +16423,15 @@ function processContentChunk(chunk, role, ctx) {
16274
16423
  case "container_upload":
16275
16424
  case "compaction":
16276
16425
  case "compaction_delta":
16426
+ case "advisor_tool_result":
16427
+ case "mid_conv_system":
16277
16428
  return [];
16278
16429
  default:
16279
16430
  unreachable(chunk, ctx.logger);
16280
16431
  return [];
16281
16432
  }
16282
16433
  }
16283
- function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, mcpToolUseResult, enrichedReadCache) {
16434
+ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, mcpToolUseResult, enrichedReadCache, taskState) {
16284
16435
  if (typeof content === "string") {
16285
16436
  const update = {
16286
16437
  sessionUpdate: messageUpdateType(role),
@@ -16306,7 +16457,8 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
16306
16457
  registerHooks,
16307
16458
  supportsTerminalOutput,
16308
16459
  cwd,
16309
- mcpToolUseResult
16460
+ mcpToolUseResult,
16461
+ taskState
16310
16462
  };
16311
16463
  const output = [];
16312
16464
  for (const chunk of content) {
@@ -16316,7 +16468,7 @@ function toAcpNotifications(content, role, sessionId, toolUseCache, fileContentC
16316
16468
  }
16317
16469
  return output;
16318
16470
  }
16319
- function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUseStreamCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, enrichedReadCache) {
16471
+ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUseStreamCache, fileContentCache, client, logger, parentToolCallId, registerHooks, supportsTerminalOutput, cwd, enrichedReadCache, taskState) {
16320
16472
  const event = message.event;
16321
16473
  switch (event.type) {
16322
16474
  case "content_block_start": {
@@ -16340,7 +16492,8 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUse
16340
16492
  supportsTerminalOutput,
16341
16493
  cwd,
16342
16494
  void 0,
16343
- enrichedReadCache
16495
+ enrichedReadCache,
16496
+ taskState
16344
16497
  );
16345
16498
  }
16346
16499
  case "content_block_delta": {
@@ -16365,12 +16518,18 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, toolUse
16365
16518
  supportsTerminalOutput,
16366
16519
  cwd,
16367
16520
  void 0,
16368
- enrichedReadCache
16521
+ enrichedReadCache,
16522
+ taskState
16369
16523
  );
16370
16524
  }
16371
16525
  case "content_block_stop":
16372
16526
  toolUseStreamCache.delete(event.index);
16373
16527
  return [];
16528
+ // `ping` is a Messages-API keep-alive event that the SDK's
16529
+ // `BetaRawMessageStreamEvent` union doesn't include even though the
16530
+ // wire format emits it; the `as never` cast lets us no-op it here
16531
+ // instead of falling through to `unreachable`.
16532
+ case "ping":
16374
16533
  case "message_start":
16375
16534
  case "message_delta":
16376
16535
  case "message_stop":
@@ -16441,6 +16600,38 @@ async function handleSystemMessage(message, context) {
16441
16600
  });
16442
16601
  break;
16443
16602
  }
16603
+ case "memory_recall": {
16604
+ const isSynthesis = message.mode === "synthesize";
16605
+ if (!isSynthesis && message.memories.length === 0) break;
16606
+ const locations = isSynthesis ? [] : message.memories.map((m) => ({ path: m.path }));
16607
+ const content = isSynthesis ? message.memories.filter(
16608
+ (m) => typeof m.content === "string"
16609
+ ).map((m) => ({
16610
+ type: "content",
16611
+ content: { type: "text", text: m.content }
16612
+ })) : [];
16613
+ const count = message.memories.length;
16614
+ const title = isSynthesis ? "Recalled synthesized memory" : `Recalled ${count} ${count === 1 ? "memory" : "memories"}`;
16615
+ await client.sessionUpdate({
16616
+ sessionId: message.session_id,
16617
+ update: {
16618
+ sessionUpdate: "tool_call",
16619
+ toolCallId: message.uuid,
16620
+ title,
16621
+ kind: "read",
16622
+ status: "completed",
16623
+ ...locations.length > 0 && { locations },
16624
+ ...content.length > 0 && { content },
16625
+ _meta: {
16626
+ claudeCode: {
16627
+ toolName: "memory_recall",
16628
+ toolResponse: { mode: message.mode }
16629
+ }
16630
+ }
16631
+ }
16632
+ });
16633
+ break;
16634
+ }
16444
16635
  default:
16445
16636
  break;
16446
16637
  }
@@ -16550,7 +16741,8 @@ async function handleStreamEvent(message, context) {
16550
16741
  context.registerHooks,
16551
16742
  context.supportsTerminalOutput,
16552
16743
  context.session.cwd,
16553
- context.enrichedReadCache
16744
+ context.enrichedReadCache,
16745
+ context.session.taskState
16554
16746
  )) {
16555
16747
  await client.sessionUpdate(notification);
16556
16748
  context.session.notificationHistory.push(notification);
@@ -16562,6 +16754,17 @@ function hasLocalCommandStdout(content) {
16562
16754
  function hasLocalCommandStderr(content) {
16563
16755
  return typeof content === "string" && content.includes("<local-command-stderr>");
16564
16756
  }
16757
+ function isSdkLocalCommandMessage(content) {
16758
+ return typeof content === "string" && content.includes("<command-name>") && (content.includes("<local-command-stdout>") || content.includes("<local-command-stderr>"));
16759
+ }
16760
+ var LOCAL_COMMAND_TAG_PATTERN = /<(command-name|command-message|command-args|local-command-stdout|local-command-stderr)>[\s\S]*?<\/\1>/g;
16761
+ function stripMarkerTags(text2) {
16762
+ return text2.replace(LOCAL_COMMAND_TAG_PATTERN, "");
16763
+ }
16764
+ function stripLocalCommandMetadata(content) {
16765
+ const stripped = stripMarkerTags(content);
16766
+ return stripped.trim() === "" ? null : stripped;
16767
+ }
16565
16768
  function isLoginRequiredMessage(message) {
16566
16769
  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;
16567
16770
  }
@@ -16570,7 +16773,7 @@ function isPlainTextUserMessage(message) {
16570
16773
  return message.type === "user" && (typeof content === "string" || Array.isArray(content) && content.length === 1 && content[0].type === "text");
16571
16774
  }
16572
16775
  function shouldSkipUserAssistantMessage(message) {
16573
- return hasLocalCommandStdout(message.message.content) || hasLocalCommandStderr(message.message.content) || isLoginRequiredMessage(message);
16776
+ return isSdkLocalCommandMessage(message.message.content) || isLoginRequiredMessage(message);
16574
16777
  }
16575
16778
  function logSpecialMessages(message, logger) {
16576
16779
  const content = message.message.content;
@@ -16591,11 +16794,39 @@ function filterMessageContent(content) {
16591
16794
  }
16592
16795
  async function handleUserAssistantMessage(message, context) {
16593
16796
  const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
16797
+ if (message.message.role === "system") {
16798
+ return {};
16799
+ }
16594
16800
  if (shouldSkipUserAssistantMessage(message)) {
16595
16801
  logSpecialMessages(message, logger);
16596
16802
  if (isLoginRequiredMessage(message)) {
16597
16803
  return { shouldStop: true, error: RequestError.authRequired() };
16598
16804
  }
16805
+ const rawContent = message.message.content;
16806
+ if (typeof rawContent === "string") {
16807
+ const stripped = stripLocalCommandMetadata(rawContent);
16808
+ if (stripped !== null) {
16809
+ for (const notification of toAcpNotifications(
16810
+ stripped,
16811
+ message.message.role,
16812
+ sessionId,
16813
+ toolUseCache,
16814
+ fileContentCache,
16815
+ client,
16816
+ logger,
16817
+ void 0,
16818
+ context.registerHooks,
16819
+ context.supportsTerminalOutput,
16820
+ session.cwd,
16821
+ void 0,
16822
+ context.enrichedReadCache,
16823
+ session.taskState
16824
+ )) {
16825
+ await client.sessionUpdate(notification);
16826
+ session.notificationHistory.push(notification);
16827
+ }
16828
+ }
16829
+ }
16599
16830
  return {};
16600
16831
  }
16601
16832
  if (isPlainTextUserMessage(message)) {
@@ -16618,7 +16849,8 @@ async function handleUserAssistantMessage(message, context) {
16618
16849
  context.supportsTerminalOutput,
16619
16850
  session.cwd,
16620
16851
  mcpToolUseResult,
16621
- context.enrichedReadCache
16852
+ context.enrichedReadCache,
16853
+ session.taskState
16622
16854
  )) {
16623
16855
  await client.sessionUpdate(notification);
16624
16856
  session.notificationHistory.push(notification);
@@ -16814,8 +17046,10 @@ var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
16814
17046
  var AGENT_TOOLS = /* @__PURE__ */ new Set([
16815
17047
  "Task",
16816
17048
  "Agent",
16817
- "TodoWrite",
16818
- "Skill"
17049
+ "TaskCreate",
17050
+ "TaskUpdate",
17051
+ "TaskGet",
17052
+ "TaskList"
16819
17053
  ]);
16820
17054
  var BASE_ALLOWED_TOOLS = [
16821
17055
  ...READ_TOOLS,
@@ -16899,8 +17133,8 @@ function buildPermissionOptions(toolName, toolInput, repoRoot, suggestions) {
16899
17133
  if (toolName === "Task") {
16900
17134
  return permissionOptions("Yes, allow all sub-tasks");
16901
17135
  }
16902
- if (toolName === "TodoWrite") {
16903
- return permissionOptions("Yes, allow all todo updates");
17136
+ if (toolName === "TaskCreate" || toolName === "TaskUpdate" || toolName === "TaskGet" || toolName === "TaskList") {
17137
+ return permissionOptions("Yes, allow all task updates");
16904
17138
  }
16905
17139
  return permissionOptions("Yes, always allow");
16906
17140
  }
@@ -17452,6 +17686,7 @@ async function canUseTool(context) {
17452
17686
 
17453
17687
  // src/adapters/claude/session/commands.ts
17454
17688
  var UNSUPPORTED_COMMANDS = [
17689
+ "clear",
17455
17690
  "context",
17456
17691
  "cost",
17457
17692
  "keybindings-help",
@@ -17506,18 +17741,30 @@ function loadUserClaudeJsonMcpServers(cwd, logger, homeDir = os3.homedir()) {
17506
17741
  const projectScoped = project?.mcpServers && typeof project.mcpServers === "object" ? project.mcpServers : {};
17507
17742
  return { ...topLevel, ...projectScoped };
17508
17743
  }
17509
- function parseMcpServers(params) {
17744
+ function parseMcpServers(params, logger) {
17510
17745
  const mcpServers = {};
17511
17746
  if (!Array.isArray(params.mcpServers)) {
17512
17747
  return mcpServers;
17513
17748
  }
17514
17749
  for (const server of params.mcpServers) {
17515
17750
  if ("type" in server) {
17516
- mcpServers[server.name] = {
17517
- type: server.type,
17518
- url: server.url,
17519
- headers: server.headers ? Object.fromEntries(server.headers.map((e) => [e.name, e.value])) : void 0
17520
- };
17751
+ if (server.type === "http" || server.type === "sse") {
17752
+ mcpServers[server.name] = {
17753
+ type: server.type,
17754
+ url: server.url,
17755
+ headers: server.headers ? Object.fromEntries(
17756
+ server.headers.map((e) => [
17757
+ e.name,
17758
+ e.value
17759
+ ])
17760
+ ) : void 0
17761
+ };
17762
+ } else {
17763
+ logger?.warn("parseMcpServers: dropping unsupported MCP server type", {
17764
+ name: server.name,
17765
+ type: server.type
17766
+ });
17767
+ }
17521
17768
  } else {
17522
17769
  mcpServers[server.name] = {
17523
17770
  type: "stdio",
@@ -17530,12 +17777,43 @@ function parseMcpServers(params) {
17530
17777
  return mcpServers;
17531
17778
  }
17532
17779
 
17780
+ // src/adapters/claude/session/model-config.ts
17781
+ function applyAvailableModelsAllowlist(modelOptions, allowlist) {
17782
+ const filtered = [];
17783
+ const seen = /* @__PURE__ */ new Set();
17784
+ for (const entry of allowlist) {
17785
+ const trimmed2 = entry.trim();
17786
+ if (!trimmed2 || seen.has(trimmed2)) continue;
17787
+ const match = modelOptions.options.find((o) => o.value === trimmed2);
17788
+ if (match) {
17789
+ filtered.push(match);
17790
+ seen.add(trimmed2);
17791
+ }
17792
+ }
17793
+ if (filtered.length === 0) return modelOptions;
17794
+ const currentModelId = filtered.some(
17795
+ (o) => o.value === modelOptions.currentModelId
17796
+ ) ? modelOptions.currentModelId : filtered[0].value;
17797
+ return { currentModelId, options: filtered };
17798
+ }
17799
+ function resolveInitialModelId(modelOptions, preferredModelIds) {
17800
+ const allowedModelIds = new Set(modelOptions.options.map((opt) => opt.value));
17801
+ for (const candidate of preferredModelIds) {
17802
+ const trimmed2 = candidate?.trim();
17803
+ if (trimmed2 && allowedModelIds.has(trimmed2)) {
17804
+ return trimmed2;
17805
+ }
17806
+ }
17807
+ return modelOptions.currentModelId;
17808
+ }
17809
+
17533
17810
  // src/adapters/claude/session/models.ts
17534
17811
  var DEFAULT_MODEL = "opus";
17535
17812
  var GATEWAY_TO_SDK_MODEL = {
17536
17813
  "claude-opus-4-5": "opus",
17537
17814
  "claude-opus-4-6": "opus",
17538
17815
  "claude-opus-4-7": "opus",
17816
+ "claude-opus-4-8": "opus",
17539
17817
  "claude-sonnet-4-5": "sonnet",
17540
17818
  "claude-sonnet-4-6": "sonnet",
17541
17819
  "claude-haiku-4-5": "haiku"
@@ -17546,6 +17824,7 @@ function toSdkModelId(modelId) {
17546
17824
  var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
17547
17825
  "claude-opus-4-6",
17548
17826
  "claude-opus-4-7",
17827
+ "claude-opus-4-8",
17549
17828
  "claude-sonnet-4-6"
17550
17829
  ]);
17551
17830
  function supports1MContext(modelId) {
@@ -17555,11 +17834,13 @@ var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
17555
17834
  "claude-opus-4-5",
17556
17835
  "claude-opus-4-6",
17557
17836
  "claude-opus-4-7",
17837
+ "claude-opus-4-8",
17558
17838
  "claude-sonnet-4-6"
17559
17839
  ]);
17560
17840
  var MODELS_WITH_XHIGH_EFFORT = /* @__PURE__ */ new Set([
17561
17841
  "claude-opus-4-6",
17562
- "claude-opus-4-7"
17842
+ "claude-opus-4-7",
17843
+ "claude-opus-4-8"
17563
17844
  ]);
17564
17845
  function supportsEffort(modelId) {
17565
17846
  return MODELS_WITH_EFFORT.has(modelId);
@@ -17599,6 +17880,19 @@ function tokenizeModelPreference(model) {
17599
17880
  }).filter((token) => token && token !== "claude").filter((token) => /[a-z]/.test(token) || token.endsWith("m"));
17600
17881
  return { tokens, contextHint };
17601
17882
  }
17883
+ var MODEL_FAMILY_VERSION_PATTERN = /\b(\d+)[-.](\d+)\b/;
17884
+ function extractModelFamilyVersion(s) {
17885
+ if (!s) return null;
17886
+ const match = s.match(MODEL_FAMILY_VERSION_PATTERN);
17887
+ return match ? `${match[1]}.${match[2]}` : null;
17888
+ }
17889
+ function modelVersionsCompatible(preference, candidate) {
17890
+ const preferred = extractModelFamilyVersion(preference);
17891
+ if (!preferred) return true;
17892
+ const candidateVersion = extractModelFamilyVersion(candidate.value) ?? extractModelFamilyVersion(candidate.name) ?? extractModelFamilyVersion(candidate.description);
17893
+ if (!candidateVersion) return true;
17894
+ return preferred === candidateVersion;
17895
+ }
17602
17896
  function scoreModelMatch(model, tokens, contextHint) {
17603
17897
  const haystack = `${model.value} ${model.name ?? ""}`.toLowerCase();
17604
17898
  let score = 0;
@@ -17618,6 +17912,7 @@ function resolveModelPreference(preference, options) {
17618
17912
  );
17619
17913
  if (directMatch) return directMatch.value;
17620
17914
  const includesMatch = options.find((o) => {
17915
+ if (!modelVersionsCompatible(trimmed2, o)) return false;
17621
17916
  const value = o.value.toLowerCase();
17622
17917
  const display = (o.name ?? "").toLowerCase();
17623
17918
  return value.includes(lower) || display.includes(lower) || lower.includes(value);
@@ -17628,6 +17923,7 @@ function resolveModelPreference(preference, options) {
17628
17923
  let bestMatch = null;
17629
17924
  let bestScore = 0;
17630
17925
  for (const model of options) {
17926
+ if (!modelVersionsCompatible(trimmed2, model)) continue;
17631
17927
  const score = scoreModelMatch(model, tokens, contextHint);
17632
17928
  if (0 < score && (!bestMatch || bestScore < score)) {
17633
17929
  bestMatch = model;
@@ -17704,6 +18000,7 @@ function buildEnvironment() {
17704
18000
  const existingCustomHeaders = process.env.ANTHROPIC_CUSTOM_HEADERS;
17705
18001
  const customHeaders = existingCustomHeaders ? `${existingCustomHeaders}
17706
18002
  ${bedrockFallbackHeader}` : bedrockFallbackHeader;
18003
+ const mcpNonblocking = process.env.MCP_CONNECTION_NONBLOCKING;
17707
18004
  return {
17708
18005
  ...process.env,
17709
18006
  ELECTRON_RUN_AS_NODE: "1",
@@ -17712,11 +18009,14 @@ ${bedrockFallbackHeader}` : bedrockFallbackHeader;
17712
18009
  ENABLE_TOOL_SEARCH: "auto:0",
17713
18010
  // Enable idle state as end-of-turn signal (required for SDK 0.2.114+)
17714
18011
  CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS: "1",
18012
+ ...mcpNonblocking !== void 0 && {
18013
+ MCP_CONNECTION_NONBLOCKING: mcpNonblocking
18014
+ },
17715
18015
  // Route to AWS Bedrock as a fallback when Anthropic returns 5xx
17716
18016
  ANTHROPIC_CUSTOM_HEADERS: customHeaders
17717
18017
  };
17718
18018
  }
17719
- function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache, registeredAgents, cloudMode) {
18019
+ function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichmentDeps, enrichedReadCache, registeredAgents, cloudMode, taskState, onTaskStateChange) {
17720
18020
  const postToolUseHooks = [createPostToolUseHook({ onModeChange })];
17721
18021
  if (enrichmentDeps && enrichedReadCache) {
17722
18022
  postToolUseHooks.push(
@@ -17730,13 +18030,16 @@ function buildHooks(userHooks, onModeChange, settingsManager, logger, enrichment
17730
18030
  if (cloudMode) {
17731
18031
  preToolUseHooks.push(createSignedCommitGuardHook(logger));
17732
18032
  }
18033
+ const taskHook = createTaskHook(taskState, onTaskStateChange);
17733
18034
  return {
17734
18035
  ...userHooks,
17735
18036
  PostToolUse: [
17736
18037
  ...userHooks?.PostToolUse || [],
17737
18038
  { hooks: postToolUseHooks }
17738
18039
  ],
17739
- PreToolUse: [...userHooks?.PreToolUse || [], { hooks: preToolUseHooks }]
18040
+ PreToolUse: [...userHooks?.PreToolUse || [], { hooks: preToolUseHooks }],
18041
+ TaskCreated: [...userHooks?.TaskCreated || [], { hooks: [taskHook] }],
18042
+ TaskCompleted: [...userHooks?.TaskCompleted || [], { hooks: [taskHook] }]
17740
18043
  };
17741
18044
  }
17742
18045
  var PH_EXPLORE_AGENT = {
@@ -17767,7 +18070,10 @@ Rules:
17767
18070
  "WebFetch",
17768
18071
  "WebSearch",
17769
18072
  "NotebookRead",
17770
- "TodoWrite"
18073
+ "TaskCreate",
18074
+ "TaskUpdate",
18075
+ "TaskGet",
18076
+ "TaskList"
17771
18077
  ]
17772
18078
  };
17773
18079
  function buildAgents(userAgents) {
@@ -17895,7 +18201,9 @@ function buildSessionOptions(params) {
17895
18201
  params.enrichmentDeps,
17896
18202
  params.enrichedReadCache,
17897
18203
  registeredAgentNames,
17898
- params.cloudMode ?? false
18204
+ params.cloudMode ?? false,
18205
+ params.taskState,
18206
+ params.onTaskStateChange
17899
18207
  ),
17900
18208
  outputFormat: params.outputFormat,
17901
18209
  abortController: getAbortController(
@@ -18124,6 +18432,15 @@ function getManagedSettingsPath() {
18124
18432
  return "/etc/claude-code/managed-settings.json";
18125
18433
  }
18126
18434
  }
18435
+ function mergeAvailableModels(existing, incoming, layer) {
18436
+ if (incoming === void 0) {
18437
+ return existing;
18438
+ }
18439
+ if (layer === "enterprise") {
18440
+ return Array.from(new Set(incoming));
18441
+ }
18442
+ return Array.from(/* @__PURE__ */ new Set([...existing ?? [], ...incoming]));
18443
+ }
18127
18444
  var SettingsManager = class {
18128
18445
  cwd;
18129
18446
  repoRoot;
@@ -18179,10 +18496,10 @@ var SettingsManager = class {
18179
18496
  }
18180
18497
  mergeAllSettings() {
18181
18498
  const allSettings = [
18182
- this.userSettings,
18183
- this.projectSettings,
18184
- this.localSettings,
18185
- this.enterpriseSettings
18499
+ { layer: "user", settings: this.userSettings },
18500
+ { layer: "project", settings: this.projectSettings },
18501
+ { layer: "local", settings: this.localSettings },
18502
+ { layer: "enterprise", settings: this.enterpriseSettings }
18186
18503
  ];
18187
18504
  const permissions = {
18188
18505
  allow: [],
@@ -18191,7 +18508,7 @@ var SettingsManager = class {
18191
18508
  };
18192
18509
  const merged = { permissions };
18193
18510
  const posthogApprovedExecTools = /* @__PURE__ */ new Set();
18194
- for (const settings of allSettings) {
18511
+ for (const { layer, settings } of allSettings) {
18195
18512
  if (settings.permissions) {
18196
18513
  if (settings.permissions.allow) {
18197
18514
  permissions.allow?.push(...settings.permissions.allow);
@@ -18218,6 +18535,11 @@ var SettingsManager = class {
18218
18535
  if (settings.model) {
18219
18536
  merged.model = settings.model;
18220
18537
  }
18538
+ merged.availableModels = mergeAvailableModels(
18539
+ merged.availableModels,
18540
+ settings.availableModels,
18541
+ layer
18542
+ );
18221
18543
  if (settings.posthogApprovedExecTools) {
18222
18544
  for (const tool2 of settings.posthogApprovedExecTools) {
18223
18545
  posthogApprovedExecTools.add(tool2);
@@ -18426,6 +18748,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18426
18748
  },
18427
18749
  loadSession: true,
18428
18750
  sessionCapabilities: {
18751
+ additionalDirectories: {},
18429
18752
  list: {},
18430
18753
  fork: {},
18431
18754
  resume: {}
@@ -18451,10 +18774,18 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18451
18774
  if (fs10.existsSync(path12.resolve(os6.homedir(), ".claude.json.backup")) && !fs10.existsSync(path12.resolve(os6.homedir(), ".claude.json"))) {
18452
18775
  throw RequestError2.authRequired();
18453
18776
  }
18454
- const response = await this.createSession(params, {
18455
- // Revisit these meta values once we support resume
18456
- resume: params._meta?.claudeCode?.options?.resume
18457
- });
18777
+ const response = await this.createSession(
18778
+ {
18779
+ cwd: params.cwd,
18780
+ mcpServers: params.mcpServers ?? [],
18781
+ additionalDirectories: params.additionalDirectories,
18782
+ _meta: params._meta
18783
+ },
18784
+ {
18785
+ // Revisit these meta values once we support resume
18786
+ resume: params._meta?.claudeCode?.options?.resume
18787
+ }
18788
+ );
18458
18789
  return response;
18459
18790
  }
18460
18791
  async unstable_forkSession(params) {
@@ -18462,24 +18793,27 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18462
18793
  {
18463
18794
  cwd: params.cwd,
18464
18795
  mcpServers: params.mcpServers ?? [],
18796
+ additionalDirectories: params.additionalDirectories,
18465
18797
  _meta: params._meta
18466
18798
  },
18467
18799
  { resume: params.sessionId, forkSession: true }
18468
18800
  );
18469
18801
  }
18470
- async unstable_resumeSession(params) {
18802
+ async resumeSession(params) {
18471
18803
  const existing = this.getExistingSessionState(params.sessionId);
18472
18804
  if (existing) return existing;
18473
18805
  const response = await this.createSession(
18474
18806
  {
18475
18807
  cwd: params.cwd,
18476
18808
  mcpServers: params.mcpServers ?? [],
18809
+ additionalDirectories: params.additionalDirectories,
18477
18810
  _meta: params._meta
18478
18811
  },
18479
18812
  {
18480
18813
  resume: params.sessionId
18481
18814
  }
18482
18815
  );
18816
+ await this.rehydrateTaskStateFromJsonl(params.sessionId);
18483
18817
  return response;
18484
18818
  }
18485
18819
  async loadSession(params) {
@@ -18489,6 +18823,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18489
18823
  {
18490
18824
  cwd: params.cwd,
18491
18825
  mcpServers: params.mcpServers ?? [],
18826
+ additionalDirectories: params.additionalDirectories,
18492
18827
  _meta: params._meta
18493
18828
  },
18494
18829
  { resume: params.sessionId, skipBackgroundFetches: true }
@@ -18567,6 +18902,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18567
18902
  await this.broadcastUserMessage(params);
18568
18903
  this.session.promptRunning = true;
18569
18904
  let handedOff = false;
18905
+ let errored = false;
18570
18906
  let lastAssistantTotalUsage = null;
18571
18907
  let lastStreamUsage = {
18572
18908
  input_tokens: 0,
@@ -18574,6 +18910,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18574
18910
  cache_read_input_tokens: 0,
18575
18911
  cache_creation_input_tokens: 0
18576
18912
  };
18913
+ let compactionInProgress = false;
18577
18914
  if (this.session.lastContextWindowSize == null) {
18578
18915
  this.session.lastContextWindowSize = this.getContextWindowForModel(
18579
18916
  this.session.modelId ?? ""
@@ -18631,6 +18968,40 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18631
18968
  if (message.subtype === "local_command_output") {
18632
18969
  promptReplayed = true;
18633
18970
  }
18971
+ if (message.subtype === "status") {
18972
+ if (message.status === "compacting") {
18973
+ compactionInProgress = true;
18974
+ } else if (message.compact_result === "success" && compactionInProgress) {
18975
+ compactionInProgress = false;
18976
+ await this.client.sessionUpdate({
18977
+ sessionId: params.sessionId,
18978
+ update: {
18979
+ sessionUpdate: "agent_message_chunk",
18980
+ content: {
18981
+ type: "text",
18982
+ text: "\n\nCompacting completed."
18983
+ }
18984
+ }
18985
+ });
18986
+ break;
18987
+ } else if (message.compact_result === "failed" && compactionInProgress) {
18988
+ compactionInProgress = false;
18989
+ const reason = message.compact_error ? `: ${message.compact_error}` : ".";
18990
+ await this.client.sessionUpdate({
18991
+ sessionId: params.sessionId,
18992
+ update: {
18993
+ sessionUpdate: "agent_message_chunk",
18994
+ content: {
18995
+ type: "text",
18996
+ text: `
18997
+
18998
+ Compacting failed${reason}`
18999
+ }
19000
+ }
19001
+ });
19002
+ break;
19003
+ }
19004
+ }
18634
19005
  if (message.subtype === "session_state_changed" && message.state === "idle") {
18635
19006
  if (!promptReplayed) {
18636
19007
  const cmdName = commandMatch?.[1].slice(1);
@@ -18670,7 +19041,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18670
19041
  size: lastContextWindowSize
18671
19042
  }
18672
19043
  });
18673
- return { stopReason: "end_turn" };
19044
+ return {
19045
+ stopReason: this.session.cancelled ? "cancelled" : "end_turn"
19046
+ };
18674
19047
  }
18675
19048
  await handleSystemMessage(message, context);
18676
19049
  break;
@@ -18853,6 +19226,28 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18853
19226
  }
18854
19227
  throw new Error("Session did not end in result");
18855
19228
  } catch (error) {
19229
+ errored = true;
19230
+ try {
19231
+ await this.session.query.interrupt();
19232
+ const MAX_DRAIN = 100;
19233
+ for (let i2 = 0; i2 < MAX_DRAIN; i2++) {
19234
+ const { value: m, done } = await this.session.query.next();
19235
+ if (done || !m) break;
19236
+ if (m.type === "system" && m.subtype === "session_state_changed" && m.state === "idle") {
19237
+ break;
19238
+ }
19239
+ if (i2 === MAX_DRAIN - 1) {
19240
+ this.logger.error(
19241
+ `Session ${params.sessionId}: drained ${MAX_DRAIN} messages after error without observing idle`
19242
+ );
19243
+ }
19244
+ }
19245
+ } catch (drainErr) {
19246
+ this.logger.error(
19247
+ `Session ${params.sessionId}: failed to drain query after prompt error`,
19248
+ { error: drainErr }
19249
+ );
19250
+ }
18856
19251
  if (error instanceof RequestError2 || !(error instanceof Error)) {
18857
19252
  throw error;
18858
19253
  }
@@ -18873,9 +19268,19 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18873
19268
  this.toolUseStreamCache.clear();
18874
19269
  if (!handedOff) {
18875
19270
  this.session.promptRunning = false;
18876
- for (const [key, pending] of this.session.pendingMessages) {
18877
- pending.resolve(true);
18878
- this.session.pendingMessages.delete(key);
19271
+ if (errored) {
19272
+ for (const pending of this.session.pendingMessages.values()) {
19273
+ pending.resolve(true);
19274
+ }
19275
+ this.session.pendingMessages.clear();
19276
+ } else if (this.session.pendingMessages.size > 0) {
19277
+ const next = [...this.session.pendingMessages.entries()].sort(
19278
+ (a, b) => a[1].order - b[1].order
19279
+ )[0];
19280
+ if (next) {
19281
+ next[1].resolve(false);
19282
+ this.session.pendingMessages.delete(next[0]);
19283
+ }
18879
19284
  }
18880
19285
  }
18881
19286
  }
@@ -18926,7 +19331,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
18926
19331
  );
18927
19332
  }
18928
19333
  const mcpServers = parseMcpServers(
18929
- params
19334
+ params,
19335
+ this.logger
18930
19336
  );
18931
19337
  await this.refreshSession(mcpServers);
18932
19338
  return { refreshed: true };
@@ -19111,7 +19517,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19111
19517
  const settingsManager = new SettingsManager(cwd);
19112
19518
  await settingsManager.initialize();
19113
19519
  const earlyModelId = settingsManager.getSettings().model || meta?.model || "";
19114
- const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params) : {};
19520
+ const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params, this.logger) : {};
19115
19521
  const localToolsServer = createLocalToolsMcpServer(
19116
19522
  { cwd, token: resolveGithubToken(), taskId },
19117
19523
  meta
@@ -19135,6 +19541,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19135
19541
  cwd
19136
19542
  });
19137
19543
  const permissionMode = meta?.permissionMode && CODE_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
19544
+ const taskState = /* @__PURE__ */ new Map();
19138
19545
  const options = buildSessionOptions({
19139
19546
  cwd,
19140
19547
  mcpServers,
@@ -19148,7 +19555,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19148
19555
  forkSession,
19149
19556
  additionalDirectories: [
19150
19557
  ...meta?.claudeCode?.options?.additionalDirectories ?? [],
19151
- ...meta?.additionalRoots ?? []
19558
+ // Prefer the official ACP `additionalDirectories` field. Fall back
19559
+ // to the legacy `_meta.additionalRoots` extension for clients that
19560
+ // haven't been updated yet.
19561
+ ...params.additionalDirectories ?? meta?.additionalRoots ?? []
19152
19562
  ],
19153
19563
  disableBuiltInTools: meta?.disableBuiltInTools,
19154
19564
  outputFormat,
@@ -19159,7 +19569,17 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19159
19569
  effort,
19160
19570
  enrichmentDeps: this.enrichment?.deps,
19161
19571
  enrichedReadCache: this.enrichedReadCache,
19162
- cloudMode: cloudRun
19572
+ cloudMode: cloudRun,
19573
+ taskState,
19574
+ onTaskStateChange: async () => {
19575
+ await this.client.sessionUpdate({
19576
+ sessionId,
19577
+ update: {
19578
+ sessionUpdate: "plan",
19579
+ entries: taskStateToPlanEntries(taskState)
19580
+ }
19581
+ });
19582
+ }
19163
19583
  });
19164
19584
  const abortController = options.abortController;
19165
19585
  const q = query({ prompt: input, options });
@@ -19188,6 +19608,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19188
19608
  systemPrompt: estimateSystemPrompt(systemPrompt),
19189
19609
  rules: estimateRulesTokens(readClaudeMdQuietly(cwd, this.logger))
19190
19610
  },
19611
+ taskState,
19191
19612
  // Custom properties
19192
19613
  cwd,
19193
19614
  notificationHistory: [],
@@ -19227,7 +19648,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19227
19648
  }
19228
19649
  }
19229
19650
  const initPromise2 = !isResume ? withTimeout(q.initializationResult(), SESSION_VALIDATION_TIMEOUT_MS) : void 0;
19230
- const [modelOptions] = await Promise.all([
19651
+ const [rawModelOptions] = await Promise.all([
19231
19652
  this.getModelConfigOptions(
19232
19653
  settingsManager.getSettings().model || meta?.model || void 0
19233
19654
  ),
@@ -19239,6 +19660,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19239
19660
  })
19240
19661
  ] : []
19241
19662
  ]);
19663
+ const settingsAvailableModels = settingsManager.getSettings().availableModels;
19664
+ const modelOptions = Array.isArray(settingsAvailableModels) ? applyAvailableModelsAllowlist(rawModelOptions, settingsAvailableModels) : rawModelOptions;
19242
19665
  if (initPromise2) {
19243
19666
  try {
19244
19667
  const initResult = await initPromise2;
@@ -19262,9 +19685,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19262
19685
  throw err2;
19263
19686
  }
19264
19687
  }
19265
- const settingsModel = settingsManager.getSettings().model;
19266
- const metaModel = meta?.model;
19267
- const resolvedModelId = settingsModel || metaModel || modelOptions.currentModelId;
19688
+ const resolvedModelId = resolveInitialModelId(modelOptions, [
19689
+ settingsManager.getSettings().model,
19690
+ meta?.model
19691
+ ]);
19268
19692
  session.modelId = resolvedModelId;
19269
19693
  session.lastContextWindowSize = this.getContextWindowForModel(resolvedModelId);
19270
19694
  const resolvedSdkModel = toSdkModelId(resolvedModelId);
@@ -19472,6 +19896,34 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
19472
19896
  [key]: tokens
19473
19897
  };
19474
19898
  }
19899
+ /**
19900
+ * Rebuild the in-memory taskState from JSONL and push a plan update so the
19901
+ * client's plan panel reflects pre-resume tasks. `loadSession` already covers
19902
+ * this via the full `replaySessionHistory` notification stream; resume
19903
+ * deliberately stays quiet (the client keeps its own message history) so we
19904
+ * walk the transcript here for state only.
19905
+ */
19906
+ async rehydrateTaskStateFromJsonl(sessionId) {
19907
+ try {
19908
+ const messages = await getSessionMessages(sessionId, {
19909
+ dir: this.session.cwd
19910
+ });
19911
+ rehydrateTaskState(messages, this.session.taskState);
19912
+ if (this.session.taskState.size === 0) return;
19913
+ await this.client.sessionUpdate({
19914
+ sessionId,
19915
+ update: {
19916
+ sessionUpdate: "plan",
19917
+ entries: taskStateToPlanEntries(this.session.taskState)
19918
+ }
19919
+ });
19920
+ } catch (err2) {
19921
+ this.logger.warn("Failed to rehydrate task state", {
19922
+ sessionId,
19923
+ error: err2 instanceof Error ? err2.message : String(err2)
19924
+ });
19925
+ }
19926
+ }
19475
19927
  async replaySessionHistory(sessionId) {
19476
19928
  try {
19477
19929
  const messages = await getSessionMessages(sessionId, {
@@ -20262,7 +20714,7 @@ var CodexAcpAgent = class extends BaseAcpAgent {
20262
20714
  }
20263
20715
  return response;
20264
20716
  }
20265
- async unstable_resumeSession(params) {
20717
+ async resumeSession(params) {
20266
20718
  const meta = params._meta;
20267
20719
  const injectedParams = this.applyLocalTools(
20268
20720
  this.applyStructuredOutput(