@posthog/agent 2.3.67 → 2.3.73

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 (33) hide show
  1. package/dist/adapters/claude/permissions/permission-options.js +12 -2
  2. package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
  3. package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -1
  4. package/dist/agent.js +239 -70
  5. package/dist/agent.js.map +1 -1
  6. package/dist/claude-cli/cli.js +4002 -2916
  7. package/dist/claude-cli/vendor/audio-capture/arm64-darwin/audio-capture.node +0 -0
  8. package/dist/claude-cli/vendor/audio-capture/arm64-linux/audio-capture.node +0 -0
  9. package/dist/claude-cli/vendor/audio-capture/arm64-win32/audio-capture.node +0 -0
  10. package/dist/claude-cli/vendor/audio-capture/x64-darwin/audio-capture.node +0 -0
  11. package/dist/claude-cli/vendor/audio-capture/x64-linux/audio-capture.node +0 -0
  12. package/dist/claude-cli/vendor/audio-capture/x64-win32/audio-capture.node +0 -0
  13. package/dist/claude-cli/vendor/tree-sitter-bash/arm64-darwin/tree-sitter-bash.node +0 -0
  14. package/dist/claude-cli/vendor/tree-sitter-bash/arm64-linux/tree-sitter-bash.node +0 -0
  15. package/dist/claude-cli/vendor/tree-sitter-bash/arm64-win32/tree-sitter-bash.node +0 -0
  16. package/dist/claude-cli/vendor/tree-sitter-bash/x64-darwin/tree-sitter-bash.node +0 -0
  17. package/dist/claude-cli/vendor/tree-sitter-bash/x64-linux/tree-sitter-bash.node +0 -0
  18. package/dist/claude-cli/vendor/tree-sitter-bash/x64-win32/tree-sitter-bash.node +0 -0
  19. package/dist/posthog-api.js +3 -3
  20. package/dist/posthog-api.js.map +1 -1
  21. package/dist/server/agent-server.js +239 -70
  22. package/dist/server/agent-server.js.map +1 -1
  23. package/dist/server/bin.cjs +239 -70
  24. package/dist/server/bin.cjs.map +1 -1
  25. package/package.json +3 -3
  26. package/src/adapters/base-acp-agent.ts +11 -2
  27. package/src/adapters/claude/UPSTREAM.md +3 -4
  28. package/src/adapters/claude/claude-agent.ts +217 -35
  29. package/src/adapters/claude/conversion/sdk-to-acp.ts +2 -25
  30. package/src/adapters/claude/permissions/permission-handlers.ts +5 -7
  31. package/src/adapters/claude/permissions/permission-options.ts +17 -2
  32. package/src/adapters/claude/session/models.ts +94 -4
  33. package/src/adapters/claude/types.ts +3 -0
@@ -904,7 +904,7 @@ var import_hono = require("hono");
904
904
  // package.json
905
905
  var package_default = {
906
906
  name: "@posthog/agent",
907
- version: "2.3.67",
907
+ version: "2.3.73",
908
908
  repository: "https://github.com/PostHog/code",
909
909
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
910
910
  exports: {
@@ -991,8 +991,8 @@ var package_default = {
991
991
  vitest: "^2.1.8"
992
992
  },
993
993
  dependencies: {
994
- "@agentclientprotocol/sdk": "0.15.0",
995
- "@anthropic-ai/claude-agent-sdk": "0.2.71",
994
+ "@agentclientprotocol/sdk": "0.16.1",
995
+ "@anthropic-ai/claude-agent-sdk": "0.2.76",
996
996
  "@anthropic-ai/sdk": "^0.78.0",
997
997
  "@hono/node-server": "^1.19.9",
998
998
  "@opentelemetry/api-logs": "^0.208.0",
@@ -1372,12 +1372,14 @@ function formatGatewayModelName(model) {
1372
1372
  }
1373
1373
 
1374
1374
  // src/adapters/base-acp-agent.ts
1375
+ var DEFAULT_CONTEXT_WINDOW = 2e5;
1375
1376
  var BaseAcpAgent = class {
1376
1377
  session;
1377
1378
  sessionId;
1378
1379
  client;
1379
1380
  logger;
1380
1381
  fileContentCache = {};
1382
+ gatewayModels = [];
1381
1383
  constructor(client) {
1382
1384
  this.client = client;
1383
1385
  this.logger = new Logger({ debug: true, prefix: "[BaseAcpAgent]" });
@@ -1430,8 +1432,8 @@ var BaseAcpAgent = class {
1430
1432
  throw new Error("Method not implemented.");
1431
1433
  }
1432
1434
  async getModelConfigOptions(currentModelOverride) {
1433
- const gatewayModels = await fetchGatewayModels();
1434
- const options = gatewayModels.filter((model) => isAnthropicModel(model)).map((model) => ({
1435
+ this.gatewayModels = await fetchGatewayModels();
1436
+ const options = this.gatewayModels.filter((model) => isAnthropicModel(model)).map((model) => ({
1435
1437
  value: model.id,
1436
1438
  name: formatGatewayModelName(model),
1437
1439
  description: `Context: ${model.context_window.toLocaleString()} tokens`
@@ -1452,6 +1454,10 @@ var BaseAcpAgent = class {
1452
1454
  }
1453
1455
  return { currentModelId, options };
1454
1456
  }
1457
+ getContextWindowForModel(modelId) {
1458
+ const match = this.gatewayModels.find((m) => m.id === modelId);
1459
+ return match?.context_window ?? DEFAULT_CONTEXT_WINDOW;
1460
+ }
1455
1461
  };
1456
1462
 
1457
1463
  // src/adapters/claude/conversion/acp-to-sdk.ts
@@ -2627,7 +2633,7 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
2627
2633
  }
2628
2634
  }
2629
2635
  async function handleSystemMessage(message, context) {
2630
- const { sessionId, client, logger } = context;
2636
+ const { session, sessionId, client, logger } = context;
2631
2637
  switch (message.subtype) {
2632
2638
  case "init":
2633
2639
  break;
@@ -2635,7 +2641,8 @@ async function handleSystemMessage(message, context) {
2635
2641
  await client.extNotification("_posthog/compact_boundary", {
2636
2642
  sessionId,
2637
2643
  trigger: message.compact_metadata.trigger,
2638
- preTokens: message.compact_metadata.pre_tokens
2644
+ preTokens: message.compact_metadata.pre_tokens,
2645
+ contextSize: session.contextSize
2639
2646
  });
2640
2647
  break;
2641
2648
  case "hook_response":
@@ -2807,21 +2814,6 @@ function filterMessageContent(content) {
2807
2814
  async function handleUserAssistantMessage(message, context) {
2808
2815
  const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
2809
2816
  if (shouldSkipUserAssistantMessage(message)) {
2810
- const content2 = message.message.content;
2811
- if (typeof content2 === "string" && hasLocalCommandStdout(content2) && content2.includes("Context Usage")) {
2812
- const stripped = content2.replace("<local-command-stdout>", "").replace("</local-command-stdout>", "");
2813
- for (const notification of toAcpNotifications(
2814
- stripped,
2815
- "assistant",
2816
- sessionId,
2817
- toolUseCache,
2818
- fileContentCache,
2819
- client,
2820
- logger
2821
- )) {
2822
- await client.sessionUpdate(notification);
2823
- }
2824
- }
2825
2817
  logSpecialMessages(message, logger);
2826
2818
  if (isLoginRequiredMessage(message)) {
2827
2819
  return { shouldStop: true, error: import_sdk.RequestError.authRequired() };
@@ -3081,8 +3073,17 @@ function buildPermissionOptions(toolName, toolInput, cwd, suggestions) {
3081
3073
  }
3082
3074
  return permissionOptions("Yes, always allow");
3083
3075
  }
3076
+ var ALLOW_BYPASS2 = !IS_ROOT || !!process.env.IS_SANDBOX;
3084
3077
  function buildExitPlanModePermissionOptions() {
3085
- return [
3078
+ const options = [];
3079
+ if (ALLOW_BYPASS2) {
3080
+ options.push({
3081
+ kind: "allow_always",
3082
+ name: "Yes, bypass all permissions",
3083
+ optionId: "bypassPermissions"
3084
+ });
3085
+ }
3086
+ options.push(
3086
3087
  {
3087
3088
  kind: "allow_always",
3088
3089
  name: "Yes, and auto-accept edits",
@@ -3099,7 +3100,8 @@ function buildExitPlanModePermissionOptions() {
3099
3100
  optionId: "reject_with_feedback",
3100
3101
  _meta: { customInput: true }
3101
3102
  }
3102
- ];
3103
+ );
3104
+ return options;
3103
3105
  }
3104
3106
 
3105
3107
  // src/adapters/claude/permissions/permission-handlers.ts
@@ -3173,7 +3175,7 @@ async function requestPlanApproval(context, updatedInput) {
3173
3175
  }
3174
3176
  async function applyPlanApproval(response, context, updatedInput) {
3175
3177
  const { session } = context;
3176
- if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits")) {
3178
+ if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits" || response.outcome.optionId === "bypassPermissions")) {
3177
3179
  session.permissionMode = response.outcome.optionId;
3178
3180
  await session.query.setPermissionMode(response.outcome.optionId);
3179
3181
  await context.client.sessionUpdate({
@@ -3246,8 +3248,7 @@ async function handleAskUserQuestionTool(context) {
3246
3248
  context.logger.warn("[AskUserQuestion] No questions found in input");
3247
3249
  return {
3248
3250
  behavior: "deny",
3249
- message: "No questions provided",
3250
- interrupt: true
3251
+ message: "No questions provided"
3251
3252
  };
3252
3253
  }
3253
3254
  const { client, sessionId, toolUseID, toolInput } = context;
@@ -3278,16 +3279,14 @@ async function handleAskUserQuestionTool(context) {
3278
3279
  const customMessage = response._meta?.message;
3279
3280
  return {
3280
3281
  behavior: "deny",
3281
- message: typeof customMessage === "string" ? customMessage : "User cancelled the questions",
3282
- interrupt: true
3282
+ message: typeof customMessage === "string" ? customMessage : "User cancelled the questions"
3283
3283
  };
3284
3284
  }
3285
3285
  const answers = response._meta?.answers;
3286
3286
  if (!answers || Object.keys(answers).length === 0) {
3287
3287
  return {
3288
3288
  behavior: "deny",
3289
- message: "User did not provide answers",
3290
- interrupt: true
3289
+ message: "User did not provide answers"
3291
3290
  };
3292
3291
  }
3293
3292
  return {
@@ -3354,8 +3353,7 @@ async function handleDefaultPermissionFlow(context) {
3354
3353
  await emitToolDenial(context, message);
3355
3354
  return {
3356
3355
  behavior: "deny",
3357
- message,
3358
- interrupt: true
3356
+ message
3359
3357
  };
3360
3358
  }
3361
3359
  }
@@ -3469,16 +3467,6 @@ var GATEWAY_TO_SDK_MODEL = {
3469
3467
  function toSdkModelId(modelId) {
3470
3468
  return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;
3471
3469
  }
3472
- var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
3473
- "claude-opus-4-6",
3474
- "claude-sonnet-4-6"
3475
- ]);
3476
- function supports1MContext(modelId) {
3477
- return MODELS_WITH_1M_CONTEXT.has(modelId);
3478
- }
3479
- function getDefaultContextWindow(modelId) {
3480
- return supports1MContext(modelId) ? 1e6 : 2e5;
3481
- }
3482
3470
  var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
3483
3471
  "claude-opus-4-5",
3484
3472
  "claude-opus-4-6",
@@ -3503,6 +3491,56 @@ function getEffortOptions(modelId) {
3503
3491
  }
3504
3492
  return options;
3505
3493
  }
3494
+ var MODEL_CONTEXT_HINT_PATTERN = /\[(\d+m)\]$/i;
3495
+ function tokenizeModelPreference(model) {
3496
+ const lower = model.trim().toLowerCase();
3497
+ const contextHint = lower.match(MODEL_CONTEXT_HINT_PATTERN)?.[1]?.toLowerCase();
3498
+ const normalized = lower.replace(MODEL_CONTEXT_HINT_PATTERN, " $1 ");
3499
+ const rawTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);
3500
+ const tokens = rawTokens.map((token) => {
3501
+ if (token === "opusplan") return "opus";
3502
+ if (token === "best" || token === "default") return "";
3503
+ return token;
3504
+ }).filter((token) => token && token !== "claude").filter((token) => /[a-z]/.test(token) || token.endsWith("m"));
3505
+ return { tokens, contextHint };
3506
+ }
3507
+ function scoreModelMatch(model, tokens, contextHint) {
3508
+ const haystack = `${model.value} ${model.name ?? ""}`.toLowerCase();
3509
+ let score = 0;
3510
+ for (const token of tokens) {
3511
+ if (haystack.includes(token)) {
3512
+ score += token === contextHint ? 3 : 1;
3513
+ }
3514
+ }
3515
+ return score;
3516
+ }
3517
+ function resolveModelPreference(preference, options) {
3518
+ const trimmed2 = preference.trim();
3519
+ if (!trimmed2) return null;
3520
+ const lower = trimmed2.toLowerCase();
3521
+ const directMatch = options.find(
3522
+ (o) => o.value === trimmed2 || o.value.toLowerCase() === lower || o.name && o.name.toLowerCase() === lower
3523
+ );
3524
+ if (directMatch) return directMatch.value;
3525
+ const includesMatch = options.find((o) => {
3526
+ const value = o.value.toLowerCase();
3527
+ const display = (o.name ?? "").toLowerCase();
3528
+ return value.includes(lower) || display.includes(lower) || lower.includes(value);
3529
+ });
3530
+ if (includesMatch) return includesMatch.value;
3531
+ const { tokens, contextHint } = tokenizeModelPreference(trimmed2);
3532
+ if (tokens.length === 0) return null;
3533
+ let bestMatch = null;
3534
+ let bestScore = 0;
3535
+ for (const model of options) {
3536
+ const score = scoreModelMatch(model, tokens, contextHint);
3537
+ if (0 < score && (!bestMatch || bestScore < score)) {
3538
+ bestMatch = model;
3539
+ bestScore = score;
3540
+ }
3541
+ }
3542
+ return bestMatch?.value ?? null;
3543
+ }
3506
3544
 
3507
3545
  // src/adapters/claude/session/options.ts
3508
3546
  var import_node_child_process = require("child_process");
@@ -3983,6 +4021,7 @@ var SettingsManager = class {
3983
4021
  // src/adapters/claude/claude-agent.ts
3984
4022
  var SESSION_VALIDATION_TIMEOUT_MS = 1e4;
3985
4023
  var MAX_TITLE_LENGTH = 256;
4024
+ var LOCAL_ONLY_COMMANDS = /* @__PURE__ */ new Set(["/context", "/heapdump", "/extra-usage"]);
3986
4025
  function sanitizeTitle(text2) {
3987
4026
  const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
3988
4027
  if (sanitized.length <= MAX_TITLE_LENGTH) {
@@ -4019,7 +4058,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4019
4058
  sessionCapabilities: {
4020
4059
  list: {},
4021
4060
  fork: {},
4022
- resume: {}
4061
+ resume: {},
4062
+ close: {}
4023
4063
  },
4024
4064
  _meta: {
4025
4065
  posthog: {
@@ -4059,6 +4099,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4059
4099
  );
4060
4100
  }
4061
4101
  async unstable_resumeSession(params) {
4102
+ const existing = this.getExistingSessionState(params.sessionId);
4103
+ if (existing) return existing;
4062
4104
  const response = await this.createSession(
4063
4105
  {
4064
4106
  cwd: params.cwd,
@@ -4072,6 +4114,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4072
4114
  return response;
4073
4115
  }
4074
4116
  async loadSession(params) {
4117
+ const existing = this.getExistingSessionState(params.sessionId);
4118
+ if (existing) return existing;
4075
4119
  const response = await this.createSession(
4076
4120
  {
4077
4121
  cwd: params.cwd,
@@ -4088,7 +4132,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4088
4132
  configOptions: response.configOptions
4089
4133
  };
4090
4134
  }
4091
- async unstable_listSessions(params) {
4135
+ async listSessions(params) {
4092
4136
  const sdkSessions = await (0, import_claude_agent_sdk.listSessions)({ dir: params.cwd ?? void 0 });
4093
4137
  const sessions = [];
4094
4138
  for (const session of sdkSessions) {
@@ -4104,6 +4148,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4104
4148
  sessions
4105
4149
  };
4106
4150
  }
4151
+ async unstable_listSessions(params) {
4152
+ return this.listSessions(params);
4153
+ }
4107
4154
  async prompt(params) {
4108
4155
  this.session.cancelled = false;
4109
4156
  this.session.interruptReason = void 0;
@@ -4114,17 +4161,37 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4114
4161
  cachedWriteTokens: 0
4115
4162
  };
4116
4163
  const userMessage = promptToClaude(params);
4164
+ const promptUuid = (0, import_node_crypto.randomUUID)();
4165
+ userMessage.uuid = promptUuid;
4166
+ let promptReplayed = false;
4167
+ let isLocalOnlyCommand = false;
4168
+ const msgContent = userMessage.message.content;
4169
+ let firstTextPart = "";
4170
+ if (typeof msgContent === "string") {
4171
+ firstTextPart = msgContent;
4172
+ } else if (Array.isArray(msgContent)) {
4173
+ for (const block of msgContent) {
4174
+ if ("type" in block && block.type === "text" && "text" in block) {
4175
+ firstTextPart = block.text;
4176
+ break;
4177
+ }
4178
+ }
4179
+ }
4180
+ const commandMatch = firstTextPart.match(/^(\/\S+)/);
4181
+ if (commandMatch && LOCAL_ONLY_COMMANDS.has(commandMatch[1])) {
4182
+ isLocalOnlyCommand = true;
4183
+ promptReplayed = true;
4184
+ }
4117
4185
  if (this.session.promptRunning) {
4118
- const uuid = (0, import_node_crypto.randomUUID)();
4119
- userMessage.uuid = uuid;
4120
4186
  this.session.input.push(userMessage);
4121
4187
  const order = this.session.nextPendingOrder++;
4122
4188
  const cancelled = await new Promise((resolve4) => {
4123
- this.session.pendingMessages.set(uuid, { resolve: resolve4, order });
4189
+ this.session.pendingMessages.set(promptUuid, { resolve: resolve4, order });
4124
4190
  });
4125
4191
  if (cancelled) {
4126
4192
  return { stopReason: "cancelled" };
4127
4193
  }
4194
+ promptReplayed = true;
4128
4195
  } else {
4129
4196
  this.session.input.push(userMessage);
4130
4197
  }
@@ -4132,6 +4199,16 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4132
4199
  this.session.promptRunning = true;
4133
4200
  let handedOff = false;
4134
4201
  let lastAssistantTotalUsage = null;
4202
+ if (this.session.lastContextWindowSize == null) {
4203
+ this.session.lastContextWindowSize = this.getContextWindowForModel(
4204
+ this.session.modelId ?? ""
4205
+ );
4206
+ this.logger.debug("Initial context window size from gateway", {
4207
+ modelId: this.session.modelId,
4208
+ contextWindowSize: this.session.lastContextWindowSize
4209
+ });
4210
+ }
4211
+ let lastContextWindowSize = this.session.lastContextWindowSize;
4135
4212
  const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
4136
4213
  const context = {
4137
4214
  session: this.session,
@@ -4158,10 +4235,21 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4158
4235
  case "system":
4159
4236
  if (message.subtype === "compact_boundary") {
4160
4237
  lastAssistantTotalUsage = 0;
4238
+ promptReplayed = true;
4239
+ }
4240
+ if (message.subtype === "local_command_output") {
4241
+ promptReplayed = true;
4161
4242
  }
4162
4243
  await handleSystemMessage(message, context);
4163
4244
  break;
4164
4245
  case "result": {
4246
+ if (!promptReplayed) {
4247
+ this.logger.debug(
4248
+ "Skipping background task result before prompt replay",
4249
+ { sessionId: params.sessionId }
4250
+ );
4251
+ break;
4252
+ }
4165
4253
  if (this.session.cancelled) {
4166
4254
  return { stopReason: "cancelled" };
4167
4255
  }
@@ -4172,8 +4260,19 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4172
4260
  const contextWindows = Object.values(message.modelUsage).map(
4173
4261
  (m) => m.contextWindow
4174
4262
  );
4175
- const contextWindowSize = contextWindows.length > 0 ? Math.min(...contextWindows) : getDefaultContextWindow(this.session.modelId ?? "");
4176
- this.session.contextSize = contextWindowSize;
4263
+ if (contextWindows.length > 0) {
4264
+ const sdkContextWindow = Math.min(...contextWindows);
4265
+ if (sdkContextWindow > lastContextWindowSize) {
4266
+ lastContextWindowSize = sdkContextWindow;
4267
+ }
4268
+ }
4269
+ this.session.lastContextWindowSize = lastContextWindowSize;
4270
+ this.logger.debug("Context window size from result", {
4271
+ sdkReported: contextWindows,
4272
+ resolved: lastContextWindowSize,
4273
+ modelId: this.session.modelId
4274
+ });
4275
+ this.session.contextSize = lastContextWindowSize;
4177
4276
  if (lastAssistantTotalUsage !== null) {
4178
4277
  this.session.contextUsed = lastAssistantTotalUsage;
4179
4278
  }
@@ -4183,7 +4282,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4183
4282
  update: {
4184
4283
  sessionUpdate: "usage_update",
4185
4284
  used: lastAssistantTotalUsage,
4186
- size: contextWindowSize,
4285
+ size: lastContextWindowSize,
4187
4286
  cost: {
4188
4287
  amount: message.total_cost_usd,
4189
4288
  currency: "USD"
@@ -4210,6 +4309,15 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4210
4309
  };
4211
4310
  const result = handleResultMessage(message);
4212
4311
  if (result.error) throw result.error;
4312
+ if (isLocalOnlyCommand && message.subtype === "success" && message.result) {
4313
+ await this.client.sessionUpdate({
4314
+ sessionId: params.sessionId,
4315
+ update: {
4316
+ sessionUpdate: "agent_message_chunk",
4317
+ content: { type: "text", text: message.result }
4318
+ }
4319
+ });
4320
+ }
4213
4321
  return { stopReason: result.stopReason ?? "end_turn", usage };
4214
4322
  }
4215
4323
  case "stream_event":
@@ -4221,6 +4329,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4221
4329
  break;
4222
4330
  }
4223
4331
  if (message.type === "user" && "uuid" in message && message.uuid) {
4332
+ if (message.uuid === promptUuid) {
4333
+ promptReplayed = true;
4334
+ break;
4335
+ }
4224
4336
  const pending = this.session.pendingMessages.get(
4225
4337
  message.uuid
4226
4338
  );
@@ -4236,7 +4348,16 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4236
4348
  }
4237
4349
  if ("usage" in message.message && message.parent_tool_use_id === null) {
4238
4350
  const usage = message.message.usage;
4239
- lastAssistantTotalUsage = usage.input_tokens + usage.output_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
4351
+ lastAssistantTotalUsage = usage.input_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
4352
+ await this.client.sessionUpdate({
4353
+ sessionId: params.sessionId,
4354
+ update: {
4355
+ sessionUpdate: "usage_update",
4356
+ used: lastAssistantTotalUsage,
4357
+ size: lastContextWindowSize,
4358
+ cost: null
4359
+ }
4360
+ });
4240
4361
  }
4241
4362
  const result = await handleUserAssistantMessage(message, context);
4242
4363
  if (result.error) throw result.error;
@@ -4266,6 +4387,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4266
4387
  this.logger.error(`Process died: ${msg}`, {
4267
4388
  sessionId: this.sessionId
4268
4389
  });
4390
+ this.session.settingsManager.dispose();
4269
4391
  this.session.input.end();
4270
4392
  throw import_sdk2.RequestError.internalError(
4271
4393
  void 0,
@@ -4293,9 +4415,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4293
4415
  await this.session.query.interrupt();
4294
4416
  }
4295
4417
  async unstable_setSessionModel(params) {
4296
- const sdkModelId = toSdkModelId(params.modelId);
4297
- await this.session.query.setModel(sdkModelId);
4418
+ await this.session.query.setModel(toSdkModelId(params.modelId));
4298
4419
  this.session.modelId = params.modelId;
4420
+ this.session.lastContextWindowSize = this.getContextWindowForModel(
4421
+ params.modelId
4422
+ );
4299
4423
  this.rebuildEffortConfigOption(params.modelId);
4300
4424
  await this.updateConfigOption("model", params.modelId);
4301
4425
  return {};
@@ -4312,42 +4436,55 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4312
4436
  if (!option) {
4313
4437
  throw new Error(`Unknown config option: ${params.configId}`);
4314
4438
  }
4439
+ if (typeof params.value !== "string") {
4440
+ throw new Error(
4441
+ `Invalid value type for config option ${params.configId}`
4442
+ );
4443
+ }
4315
4444
  const allValues = "options" in option && Array.isArray(option.options) ? option.options.flatMap(
4316
4445
  (o) => "options" in o && Array.isArray(o.options) ? o.options : [o]
4317
4446
  ) : [];
4318
- const validValue = allValues.find((o) => o.value === params.value);
4447
+ let validValue = allValues.find((o) => o.value === params.value);
4448
+ if (!validValue && params.configId === "model") {
4449
+ const resolved = resolveModelPreference(params.value, allValues);
4450
+ if (resolved) {
4451
+ validValue = allValues.find((o) => o.value === resolved);
4452
+ }
4453
+ }
4319
4454
  if (!validValue) {
4320
4455
  throw new Error(
4321
4456
  `Invalid value for config option ${params.configId}: ${params.value}`
4322
4457
  );
4323
4458
  }
4459
+ const resolvedValue = validValue.value;
4324
4460
  if (params.configId === "mode") {
4325
- await this.applySessionMode(params.value);
4461
+ await this.applySessionMode(resolvedValue);
4326
4462
  await this.client.sessionUpdate({
4327
4463
  sessionId: this.sessionId,
4328
4464
  update: {
4329
4465
  sessionUpdate: "current_mode_update",
4330
- currentModeId: params.value
4466
+ currentModeId: resolvedValue
4331
4467
  }
4332
4468
  });
4333
4469
  } else if (params.configId === "model") {
4334
- const sdkModelId = toSdkModelId(params.value);
4470
+ const sdkModelId = toSdkModelId(resolvedValue);
4335
4471
  await this.session.query.setModel(sdkModelId);
4336
- this.session.modelId = params.value;
4337
- this.rebuildEffortConfigOption(params.value);
4472
+ this.session.modelId = resolvedValue;
4473
+ this.session.lastContextWindowSize = this.getContextWindowForModel(resolvedValue);
4474
+ this.rebuildEffortConfigOption(resolvedValue);
4338
4475
  } else if (params.configId === "effort") {
4339
- const newEffort = params.value;
4476
+ const newEffort = resolvedValue;
4340
4477
  this.session.effort = newEffort;
4341
4478
  this.session.queryOptions.effort = newEffort;
4342
4479
  }
4343
4480
  this.session.configOptions = this.session.configOptions.map(
4344
- (o) => o.id === params.configId ? { ...o, currentValue: params.value } : o
4481
+ (o) => o.id === params.configId && typeof o.currentValue === "string" ? { ...o, currentValue: resolvedValue } : o
4345
4482
  );
4346
4483
  return { configOptions: this.session.configOptions };
4347
4484
  }
4348
4485
  async updateConfigOption(configId, value) {
4349
4486
  this.session.configOptions = this.session.configOptions.map(
4350
- (o) => o.id === configId ? { ...o, currentValue: value } : o
4487
+ (o) => o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o
4351
4488
  );
4352
4489
  await this.client.sessionUpdate({
4353
4490
  sessionId: this.sessionId,
@@ -4414,7 +4551,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4414
4551
  sessionId,
4415
4552
  isResume,
4416
4553
  forkSession,
4417
- additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
4554
+ additionalDirectories: [
4555
+ ...meta?.claudeCode?.options?.additionalDirectories ?? [],
4556
+ ...meta?.additionalRoots ?? []
4557
+ ],
4418
4558
  disableBuiltInTools: meta?.disableBuiltInTools,
4419
4559
  settingsManager,
4420
4560
  onModeChange: this.createOnModeChange(),
@@ -4491,11 +4631,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4491
4631
  const modelOptions = await this.getModelConfigOptions();
4492
4632
  const resolvedModelId = settingsModel || modelOptions.currentModelId;
4493
4633
  session.modelId = resolvedModelId;
4494
- if (!isResume) {
4495
- const resolvedSdkModel = toSdkModelId(resolvedModelId);
4496
- if (resolvedSdkModel !== DEFAULT_MODEL) {
4497
- await this.session.query.setModel(resolvedSdkModel);
4498
- }
4634
+ session.lastContextWindowSize = this.getContextWindowForModel(resolvedModelId);
4635
+ const resolvedSdkModel = toSdkModelId(resolvedModelId);
4636
+ if (!isResume && resolvedSdkModel !== DEFAULT_MODEL) {
4637
+ await this.session.query.setModel(resolvedSdkModel);
4499
4638
  }
4500
4639
  const availableModes2 = getAvailableModes();
4501
4640
  const modes = {
@@ -4558,6 +4697,35 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4558
4697
  await this.updateConfigOption("mode", newMode);
4559
4698
  };
4560
4699
  }
4700
+ getExistingSessionState(sessionId) {
4701
+ if (this.sessionId !== sessionId || !this.session) return null;
4702
+ const availableModes2 = getAvailableModes();
4703
+ const modes = {
4704
+ currentModeId: this.session.permissionMode,
4705
+ availableModes: availableModes2.map((mode) => ({
4706
+ id: mode.id,
4707
+ name: mode.name,
4708
+ description: mode.description ?? void 0
4709
+ }))
4710
+ };
4711
+ const modelOptions = this.session.configOptions.find(
4712
+ (o) => o.id === "model"
4713
+ );
4714
+ const models = {
4715
+ currentModelId: this.session.modelId ?? DEFAULT_MODEL,
4716
+ availableModels: modelOptions && "options" in modelOptions ? modelOptions.options.map((opt) => ({
4717
+ modelId: opt.value,
4718
+ name: opt.name,
4719
+ description: opt.description
4720
+ })) : []
4721
+ };
4722
+ return {
4723
+ sessionId,
4724
+ modes,
4725
+ models,
4726
+ configOptions: this.session.configOptions
4727
+ };
4728
+ }
4561
4729
  buildConfigOptions(currentModeId, modelOptions, currentEffort = "high") {
4562
4730
  const modeOptions = getAvailableModes().map((mode) => ({
4563
4731
  value: mode.id,
@@ -4613,7 +4781,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4613
4781
  }
4614
4782
  return;
4615
4783
  }
4616
- const currentValue = existingEffort?.currentValue ?? "high";
4784
+ const rawCurrentValue = existingEffort?.currentValue;
4785
+ const currentValue = typeof rawCurrentValue === "string" ? rawCurrentValue : "high";
4617
4786
  const isValidValue = effortOptions.some((o) => o.value === currentValue);
4618
4787
  const resolvedValue = isValidValue ? currentValue : "high";
4619
4788
  if (resolvedValue !== currentValue && this.session.effort) {