@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
@@ -908,7 +908,7 @@ import { Hono } from "hono";
908
908
  // package.json
909
909
  var package_default = {
910
910
  name: "@posthog/agent",
911
- version: "2.3.67",
911
+ version: "2.3.73",
912
912
  repository: "https://github.com/PostHog/code",
913
913
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
914
914
  exports: {
@@ -995,8 +995,8 @@ var package_default = {
995
995
  vitest: "^2.1.8"
996
996
  },
997
997
  dependencies: {
998
- "@agentclientprotocol/sdk": "0.15.0",
999
- "@anthropic-ai/claude-agent-sdk": "0.2.71",
998
+ "@agentclientprotocol/sdk": "0.16.1",
999
+ "@anthropic-ai/claude-agent-sdk": "0.2.76",
1000
1000
  "@anthropic-ai/sdk": "^0.78.0",
1001
1001
  "@hono/node-server": "^1.19.9",
1002
1002
  "@opentelemetry/api-logs": "^0.208.0",
@@ -1382,12 +1382,14 @@ function formatGatewayModelName(model) {
1382
1382
  }
1383
1383
 
1384
1384
  // src/adapters/base-acp-agent.ts
1385
+ var DEFAULT_CONTEXT_WINDOW = 2e5;
1385
1386
  var BaseAcpAgent = class {
1386
1387
  session;
1387
1388
  sessionId;
1388
1389
  client;
1389
1390
  logger;
1390
1391
  fileContentCache = {};
1392
+ gatewayModels = [];
1391
1393
  constructor(client) {
1392
1394
  this.client = client;
1393
1395
  this.logger = new Logger({ debug: true, prefix: "[BaseAcpAgent]" });
@@ -1440,8 +1442,8 @@ var BaseAcpAgent = class {
1440
1442
  throw new Error("Method not implemented.");
1441
1443
  }
1442
1444
  async getModelConfigOptions(currentModelOverride) {
1443
- const gatewayModels = await fetchGatewayModels();
1444
- const options = gatewayModels.filter((model) => isAnthropicModel(model)).map((model) => ({
1445
+ this.gatewayModels = await fetchGatewayModels();
1446
+ const options = this.gatewayModels.filter((model) => isAnthropicModel(model)).map((model) => ({
1445
1447
  value: model.id,
1446
1448
  name: formatGatewayModelName(model),
1447
1449
  description: `Context: ${model.context_window.toLocaleString()} tokens`
@@ -1462,6 +1464,10 @@ var BaseAcpAgent = class {
1462
1464
  }
1463
1465
  return { currentModelId, options };
1464
1466
  }
1467
+ getContextWindowForModel(modelId) {
1468
+ const match = this.gatewayModels.find((m) => m.id === modelId);
1469
+ return match?.context_window ?? DEFAULT_CONTEXT_WINDOW;
1470
+ }
1465
1471
  };
1466
1472
 
1467
1473
  // src/adapters/claude/conversion/acp-to-sdk.ts
@@ -2637,7 +2643,7 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
2637
2643
  }
2638
2644
  }
2639
2645
  async function handleSystemMessage(message, context) {
2640
- const { sessionId, client, logger } = context;
2646
+ const { session, sessionId, client, logger } = context;
2641
2647
  switch (message.subtype) {
2642
2648
  case "init":
2643
2649
  break;
@@ -2645,7 +2651,8 @@ async function handleSystemMessage(message, context) {
2645
2651
  await client.extNotification("_posthog/compact_boundary", {
2646
2652
  sessionId,
2647
2653
  trigger: message.compact_metadata.trigger,
2648
- preTokens: message.compact_metadata.pre_tokens
2654
+ preTokens: message.compact_metadata.pre_tokens,
2655
+ contextSize: session.contextSize
2649
2656
  });
2650
2657
  break;
2651
2658
  case "hook_response":
@@ -2817,21 +2824,6 @@ function filterMessageContent(content) {
2817
2824
  async function handleUserAssistantMessage(message, context) {
2818
2825
  const { session, sessionId, client, toolUseCache, fileContentCache, logger } = context;
2819
2826
  if (shouldSkipUserAssistantMessage(message)) {
2820
- const content2 = message.message.content;
2821
- if (typeof content2 === "string" && hasLocalCommandStdout(content2) && content2.includes("Context Usage")) {
2822
- const stripped = content2.replace("<local-command-stdout>", "").replace("</local-command-stdout>", "");
2823
- for (const notification of toAcpNotifications(
2824
- stripped,
2825
- "assistant",
2826
- sessionId,
2827
- toolUseCache,
2828
- fileContentCache,
2829
- client,
2830
- logger
2831
- )) {
2832
- await client.sessionUpdate(notification);
2833
- }
2834
- }
2835
2827
  logSpecialMessages(message, logger);
2836
2828
  if (isLoginRequiredMessage(message)) {
2837
2829
  return { shouldStop: true, error: RequestError.authRequired() };
@@ -3091,8 +3083,17 @@ function buildPermissionOptions(toolName, toolInput, cwd, suggestions) {
3091
3083
  }
3092
3084
  return permissionOptions("Yes, always allow");
3093
3085
  }
3086
+ var ALLOW_BYPASS2 = !IS_ROOT || !!process.env.IS_SANDBOX;
3094
3087
  function buildExitPlanModePermissionOptions() {
3095
- return [
3088
+ const options = [];
3089
+ if (ALLOW_BYPASS2) {
3090
+ options.push({
3091
+ kind: "allow_always",
3092
+ name: "Yes, bypass all permissions",
3093
+ optionId: "bypassPermissions"
3094
+ });
3095
+ }
3096
+ options.push(
3096
3097
  {
3097
3098
  kind: "allow_always",
3098
3099
  name: "Yes, and auto-accept edits",
@@ -3109,7 +3110,8 @@ function buildExitPlanModePermissionOptions() {
3109
3110
  optionId: "reject_with_feedback",
3110
3111
  _meta: { customInput: true }
3111
3112
  }
3112
- ];
3113
+ );
3114
+ return options;
3113
3115
  }
3114
3116
 
3115
3117
  // src/adapters/claude/permissions/permission-handlers.ts
@@ -3183,7 +3185,7 @@ async function requestPlanApproval(context, updatedInput) {
3183
3185
  }
3184
3186
  async function applyPlanApproval(response, context, updatedInput) {
3185
3187
  const { session } = context;
3186
- if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits")) {
3188
+ if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "default" || response.outcome.optionId === "acceptEdits" || response.outcome.optionId === "bypassPermissions")) {
3187
3189
  session.permissionMode = response.outcome.optionId;
3188
3190
  await session.query.setPermissionMode(response.outcome.optionId);
3189
3191
  await context.client.sessionUpdate({
@@ -3256,8 +3258,7 @@ async function handleAskUserQuestionTool(context) {
3256
3258
  context.logger.warn("[AskUserQuestion] No questions found in input");
3257
3259
  return {
3258
3260
  behavior: "deny",
3259
- message: "No questions provided",
3260
- interrupt: true
3261
+ message: "No questions provided"
3261
3262
  };
3262
3263
  }
3263
3264
  const { client, sessionId, toolUseID, toolInput } = context;
@@ -3288,16 +3289,14 @@ async function handleAskUserQuestionTool(context) {
3288
3289
  const customMessage = response._meta?.message;
3289
3290
  return {
3290
3291
  behavior: "deny",
3291
- message: typeof customMessage === "string" ? customMessage : "User cancelled the questions",
3292
- interrupt: true
3292
+ message: typeof customMessage === "string" ? customMessage : "User cancelled the questions"
3293
3293
  };
3294
3294
  }
3295
3295
  const answers = response._meta?.answers;
3296
3296
  if (!answers || Object.keys(answers).length === 0) {
3297
3297
  return {
3298
3298
  behavior: "deny",
3299
- message: "User did not provide answers",
3300
- interrupt: true
3299
+ message: "User did not provide answers"
3301
3300
  };
3302
3301
  }
3303
3302
  return {
@@ -3364,8 +3363,7 @@ async function handleDefaultPermissionFlow(context) {
3364
3363
  await emitToolDenial(context, message);
3365
3364
  return {
3366
3365
  behavior: "deny",
3367
- message,
3368
- interrupt: true
3366
+ message
3369
3367
  };
3370
3368
  }
3371
3369
  }
@@ -3479,16 +3477,6 @@ var GATEWAY_TO_SDK_MODEL = {
3479
3477
  function toSdkModelId(modelId) {
3480
3478
  return GATEWAY_TO_SDK_MODEL[modelId] ?? modelId;
3481
3479
  }
3482
- var MODELS_WITH_1M_CONTEXT = /* @__PURE__ */ new Set([
3483
- "claude-opus-4-6",
3484
- "claude-sonnet-4-6"
3485
- ]);
3486
- function supports1MContext(modelId) {
3487
- return MODELS_WITH_1M_CONTEXT.has(modelId);
3488
- }
3489
- function getDefaultContextWindow(modelId) {
3490
- return supports1MContext(modelId) ? 1e6 : 2e5;
3491
- }
3492
3480
  var MODELS_WITH_EFFORT = /* @__PURE__ */ new Set([
3493
3481
  "claude-opus-4-5",
3494
3482
  "claude-opus-4-6",
@@ -3513,6 +3501,56 @@ function getEffortOptions(modelId) {
3513
3501
  }
3514
3502
  return options;
3515
3503
  }
3504
+ var MODEL_CONTEXT_HINT_PATTERN = /\[(\d+m)\]$/i;
3505
+ function tokenizeModelPreference(model) {
3506
+ const lower = model.trim().toLowerCase();
3507
+ const contextHint = lower.match(MODEL_CONTEXT_HINT_PATTERN)?.[1]?.toLowerCase();
3508
+ const normalized = lower.replace(MODEL_CONTEXT_HINT_PATTERN, " $1 ");
3509
+ const rawTokens = normalized.split(/[^a-z0-9]+/).filter(Boolean);
3510
+ const tokens = rawTokens.map((token) => {
3511
+ if (token === "opusplan") return "opus";
3512
+ if (token === "best" || token === "default") return "";
3513
+ return token;
3514
+ }).filter((token) => token && token !== "claude").filter((token) => /[a-z]/.test(token) || token.endsWith("m"));
3515
+ return { tokens, contextHint };
3516
+ }
3517
+ function scoreModelMatch(model, tokens, contextHint) {
3518
+ const haystack = `${model.value} ${model.name ?? ""}`.toLowerCase();
3519
+ let score = 0;
3520
+ for (const token of tokens) {
3521
+ if (haystack.includes(token)) {
3522
+ score += token === contextHint ? 3 : 1;
3523
+ }
3524
+ }
3525
+ return score;
3526
+ }
3527
+ function resolveModelPreference(preference, options) {
3528
+ const trimmed2 = preference.trim();
3529
+ if (!trimmed2) return null;
3530
+ const lower = trimmed2.toLowerCase();
3531
+ const directMatch = options.find(
3532
+ (o) => o.value === trimmed2 || o.value.toLowerCase() === lower || o.name && o.name.toLowerCase() === lower
3533
+ );
3534
+ if (directMatch) return directMatch.value;
3535
+ const includesMatch = options.find((o) => {
3536
+ const value = o.value.toLowerCase();
3537
+ const display = (o.name ?? "").toLowerCase();
3538
+ return value.includes(lower) || display.includes(lower) || lower.includes(value);
3539
+ });
3540
+ if (includesMatch) return includesMatch.value;
3541
+ const { tokens, contextHint } = tokenizeModelPreference(trimmed2);
3542
+ if (tokens.length === 0) return null;
3543
+ let bestMatch = null;
3544
+ let bestScore = 0;
3545
+ for (const model of options) {
3546
+ const score = scoreModelMatch(model, tokens, contextHint);
3547
+ if (0 < score && (!bestMatch || bestScore < score)) {
3548
+ bestMatch = model;
3549
+ bestScore = score;
3550
+ }
3551
+ }
3552
+ return bestMatch?.value ?? null;
3553
+ }
3516
3554
 
3517
3555
  // src/adapters/claude/session/options.ts
3518
3556
  import { spawn } from "child_process";
@@ -3993,6 +4031,7 @@ var SettingsManager = class {
3993
4031
  // src/adapters/claude/claude-agent.ts
3994
4032
  var SESSION_VALIDATION_TIMEOUT_MS = 1e4;
3995
4033
  var MAX_TITLE_LENGTH = 256;
4034
+ var LOCAL_ONLY_COMMANDS = /* @__PURE__ */ new Set(["/context", "/heapdump", "/extra-usage"]);
3996
4035
  function sanitizeTitle(text2) {
3997
4036
  const sanitized = text2.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
3998
4037
  if (sanitized.length <= MAX_TITLE_LENGTH) {
@@ -4029,7 +4068,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4029
4068
  sessionCapabilities: {
4030
4069
  list: {},
4031
4070
  fork: {},
4032
- resume: {}
4071
+ resume: {},
4072
+ close: {}
4033
4073
  },
4034
4074
  _meta: {
4035
4075
  posthog: {
@@ -4069,6 +4109,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4069
4109
  );
4070
4110
  }
4071
4111
  async unstable_resumeSession(params) {
4112
+ const existing = this.getExistingSessionState(params.sessionId);
4113
+ if (existing) return existing;
4072
4114
  const response = await this.createSession(
4073
4115
  {
4074
4116
  cwd: params.cwd,
@@ -4082,6 +4124,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4082
4124
  return response;
4083
4125
  }
4084
4126
  async loadSession(params) {
4127
+ const existing = this.getExistingSessionState(params.sessionId);
4128
+ if (existing) return existing;
4085
4129
  const response = await this.createSession(
4086
4130
  {
4087
4131
  cwd: params.cwd,
@@ -4098,7 +4142,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4098
4142
  configOptions: response.configOptions
4099
4143
  };
4100
4144
  }
4101
- async unstable_listSessions(params) {
4145
+ async listSessions(params) {
4102
4146
  const sdkSessions = await listSessions({ dir: params.cwd ?? void 0 });
4103
4147
  const sessions = [];
4104
4148
  for (const session of sdkSessions) {
@@ -4114,6 +4158,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4114
4158
  sessions
4115
4159
  };
4116
4160
  }
4161
+ async unstable_listSessions(params) {
4162
+ return this.listSessions(params);
4163
+ }
4117
4164
  async prompt(params) {
4118
4165
  this.session.cancelled = false;
4119
4166
  this.session.interruptReason = void 0;
@@ -4124,17 +4171,37 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4124
4171
  cachedWriteTokens: 0
4125
4172
  };
4126
4173
  const userMessage = promptToClaude(params);
4174
+ const promptUuid = randomUUID();
4175
+ userMessage.uuid = promptUuid;
4176
+ let promptReplayed = false;
4177
+ let isLocalOnlyCommand = false;
4178
+ const msgContent = userMessage.message.content;
4179
+ let firstTextPart = "";
4180
+ if (typeof msgContent === "string") {
4181
+ firstTextPart = msgContent;
4182
+ } else if (Array.isArray(msgContent)) {
4183
+ for (const block of msgContent) {
4184
+ if ("type" in block && block.type === "text" && "text" in block) {
4185
+ firstTextPart = block.text;
4186
+ break;
4187
+ }
4188
+ }
4189
+ }
4190
+ const commandMatch = firstTextPart.match(/^(\/\S+)/);
4191
+ if (commandMatch && LOCAL_ONLY_COMMANDS.has(commandMatch[1])) {
4192
+ isLocalOnlyCommand = true;
4193
+ promptReplayed = true;
4194
+ }
4127
4195
  if (this.session.promptRunning) {
4128
- const uuid = randomUUID();
4129
- userMessage.uuid = uuid;
4130
4196
  this.session.input.push(userMessage);
4131
4197
  const order = this.session.nextPendingOrder++;
4132
4198
  const cancelled = await new Promise((resolve4) => {
4133
- this.session.pendingMessages.set(uuid, { resolve: resolve4, order });
4199
+ this.session.pendingMessages.set(promptUuid, { resolve: resolve4, order });
4134
4200
  });
4135
4201
  if (cancelled) {
4136
4202
  return { stopReason: "cancelled" };
4137
4203
  }
4204
+ promptReplayed = true;
4138
4205
  } else {
4139
4206
  this.session.input.push(userMessage);
4140
4207
  }
@@ -4142,6 +4209,16 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4142
4209
  this.session.promptRunning = true;
4143
4210
  let handedOff = false;
4144
4211
  let lastAssistantTotalUsage = null;
4212
+ if (this.session.lastContextWindowSize == null) {
4213
+ this.session.lastContextWindowSize = this.getContextWindowForModel(
4214
+ this.session.modelId ?? ""
4215
+ );
4216
+ this.logger.debug("Initial context window size from gateway", {
4217
+ modelId: this.session.modelId,
4218
+ contextWindowSize: this.session.lastContextWindowSize
4219
+ });
4220
+ }
4221
+ let lastContextWindowSize = this.session.lastContextWindowSize;
4145
4222
  const supportsTerminalOutput = this.clientCapabilities?._meta?.terminal_output === true;
4146
4223
  const context = {
4147
4224
  session: this.session,
@@ -4168,10 +4245,21 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4168
4245
  case "system":
4169
4246
  if (message.subtype === "compact_boundary") {
4170
4247
  lastAssistantTotalUsage = 0;
4248
+ promptReplayed = true;
4249
+ }
4250
+ if (message.subtype === "local_command_output") {
4251
+ promptReplayed = true;
4171
4252
  }
4172
4253
  await handleSystemMessage(message, context);
4173
4254
  break;
4174
4255
  case "result": {
4256
+ if (!promptReplayed) {
4257
+ this.logger.debug(
4258
+ "Skipping background task result before prompt replay",
4259
+ { sessionId: params.sessionId }
4260
+ );
4261
+ break;
4262
+ }
4175
4263
  if (this.session.cancelled) {
4176
4264
  return { stopReason: "cancelled" };
4177
4265
  }
@@ -4182,8 +4270,19 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4182
4270
  const contextWindows = Object.values(message.modelUsage).map(
4183
4271
  (m) => m.contextWindow
4184
4272
  );
4185
- const contextWindowSize = contextWindows.length > 0 ? Math.min(...contextWindows) : getDefaultContextWindow(this.session.modelId ?? "");
4186
- this.session.contextSize = contextWindowSize;
4273
+ if (contextWindows.length > 0) {
4274
+ const sdkContextWindow = Math.min(...contextWindows);
4275
+ if (sdkContextWindow > lastContextWindowSize) {
4276
+ lastContextWindowSize = sdkContextWindow;
4277
+ }
4278
+ }
4279
+ this.session.lastContextWindowSize = lastContextWindowSize;
4280
+ this.logger.debug("Context window size from result", {
4281
+ sdkReported: contextWindows,
4282
+ resolved: lastContextWindowSize,
4283
+ modelId: this.session.modelId
4284
+ });
4285
+ this.session.contextSize = lastContextWindowSize;
4187
4286
  if (lastAssistantTotalUsage !== null) {
4188
4287
  this.session.contextUsed = lastAssistantTotalUsage;
4189
4288
  }
@@ -4193,7 +4292,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4193
4292
  update: {
4194
4293
  sessionUpdate: "usage_update",
4195
4294
  used: lastAssistantTotalUsage,
4196
- size: contextWindowSize,
4295
+ size: lastContextWindowSize,
4197
4296
  cost: {
4198
4297
  amount: message.total_cost_usd,
4199
4298
  currency: "USD"
@@ -4220,6 +4319,15 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4220
4319
  };
4221
4320
  const result = handleResultMessage(message);
4222
4321
  if (result.error) throw result.error;
4322
+ if (isLocalOnlyCommand && message.subtype === "success" && message.result) {
4323
+ await this.client.sessionUpdate({
4324
+ sessionId: params.sessionId,
4325
+ update: {
4326
+ sessionUpdate: "agent_message_chunk",
4327
+ content: { type: "text", text: message.result }
4328
+ }
4329
+ });
4330
+ }
4223
4331
  return { stopReason: result.stopReason ?? "end_turn", usage };
4224
4332
  }
4225
4333
  case "stream_event":
@@ -4231,6 +4339,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4231
4339
  break;
4232
4340
  }
4233
4341
  if (message.type === "user" && "uuid" in message && message.uuid) {
4342
+ if (message.uuid === promptUuid) {
4343
+ promptReplayed = true;
4344
+ break;
4345
+ }
4234
4346
  const pending = this.session.pendingMessages.get(
4235
4347
  message.uuid
4236
4348
  );
@@ -4246,7 +4358,16 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4246
4358
  }
4247
4359
  if ("usage" in message.message && message.parent_tool_use_id === null) {
4248
4360
  const usage = message.message.usage;
4249
- lastAssistantTotalUsage = usage.input_tokens + usage.output_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
4361
+ lastAssistantTotalUsage = usage.input_tokens + usage.cache_read_input_tokens + usage.cache_creation_input_tokens;
4362
+ await this.client.sessionUpdate({
4363
+ sessionId: params.sessionId,
4364
+ update: {
4365
+ sessionUpdate: "usage_update",
4366
+ used: lastAssistantTotalUsage,
4367
+ size: lastContextWindowSize,
4368
+ cost: null
4369
+ }
4370
+ });
4250
4371
  }
4251
4372
  const result = await handleUserAssistantMessage(message, context);
4252
4373
  if (result.error) throw result.error;
@@ -4276,6 +4397,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4276
4397
  this.logger.error(`Process died: ${msg}`, {
4277
4398
  sessionId: this.sessionId
4278
4399
  });
4400
+ this.session.settingsManager.dispose();
4279
4401
  this.session.input.end();
4280
4402
  throw RequestError2.internalError(
4281
4403
  void 0,
@@ -4303,9 +4425,11 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4303
4425
  await this.session.query.interrupt();
4304
4426
  }
4305
4427
  async unstable_setSessionModel(params) {
4306
- const sdkModelId = toSdkModelId(params.modelId);
4307
- await this.session.query.setModel(sdkModelId);
4428
+ await this.session.query.setModel(toSdkModelId(params.modelId));
4308
4429
  this.session.modelId = params.modelId;
4430
+ this.session.lastContextWindowSize = this.getContextWindowForModel(
4431
+ params.modelId
4432
+ );
4309
4433
  this.rebuildEffortConfigOption(params.modelId);
4310
4434
  await this.updateConfigOption("model", params.modelId);
4311
4435
  return {};
@@ -4322,42 +4446,55 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4322
4446
  if (!option) {
4323
4447
  throw new Error(`Unknown config option: ${params.configId}`);
4324
4448
  }
4449
+ if (typeof params.value !== "string") {
4450
+ throw new Error(
4451
+ `Invalid value type for config option ${params.configId}`
4452
+ );
4453
+ }
4325
4454
  const allValues = "options" in option && Array.isArray(option.options) ? option.options.flatMap(
4326
4455
  (o) => "options" in o && Array.isArray(o.options) ? o.options : [o]
4327
4456
  ) : [];
4328
- const validValue = allValues.find((o) => o.value === params.value);
4457
+ let validValue = allValues.find((o) => o.value === params.value);
4458
+ if (!validValue && params.configId === "model") {
4459
+ const resolved = resolveModelPreference(params.value, allValues);
4460
+ if (resolved) {
4461
+ validValue = allValues.find((o) => o.value === resolved);
4462
+ }
4463
+ }
4329
4464
  if (!validValue) {
4330
4465
  throw new Error(
4331
4466
  `Invalid value for config option ${params.configId}: ${params.value}`
4332
4467
  );
4333
4468
  }
4469
+ const resolvedValue = validValue.value;
4334
4470
  if (params.configId === "mode") {
4335
- await this.applySessionMode(params.value);
4471
+ await this.applySessionMode(resolvedValue);
4336
4472
  await this.client.sessionUpdate({
4337
4473
  sessionId: this.sessionId,
4338
4474
  update: {
4339
4475
  sessionUpdate: "current_mode_update",
4340
- currentModeId: params.value
4476
+ currentModeId: resolvedValue
4341
4477
  }
4342
4478
  });
4343
4479
  } else if (params.configId === "model") {
4344
- const sdkModelId = toSdkModelId(params.value);
4480
+ const sdkModelId = toSdkModelId(resolvedValue);
4345
4481
  await this.session.query.setModel(sdkModelId);
4346
- this.session.modelId = params.value;
4347
- this.rebuildEffortConfigOption(params.value);
4482
+ this.session.modelId = resolvedValue;
4483
+ this.session.lastContextWindowSize = this.getContextWindowForModel(resolvedValue);
4484
+ this.rebuildEffortConfigOption(resolvedValue);
4348
4485
  } else if (params.configId === "effort") {
4349
- const newEffort = params.value;
4486
+ const newEffort = resolvedValue;
4350
4487
  this.session.effort = newEffort;
4351
4488
  this.session.queryOptions.effort = newEffort;
4352
4489
  }
4353
4490
  this.session.configOptions = this.session.configOptions.map(
4354
- (o) => o.id === params.configId ? { ...o, currentValue: params.value } : o
4491
+ (o) => o.id === params.configId && typeof o.currentValue === "string" ? { ...o, currentValue: resolvedValue } : o
4355
4492
  );
4356
4493
  return { configOptions: this.session.configOptions };
4357
4494
  }
4358
4495
  async updateConfigOption(configId, value) {
4359
4496
  this.session.configOptions = this.session.configOptions.map(
4360
- (o) => o.id === configId ? { ...o, currentValue: value } : o
4497
+ (o) => o.id === configId && typeof o.currentValue === "string" ? { ...o, currentValue: value } : o
4361
4498
  );
4362
4499
  await this.client.sessionUpdate({
4363
4500
  sessionId: this.sessionId,
@@ -4424,7 +4561,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4424
4561
  sessionId,
4425
4562
  isResume,
4426
4563
  forkSession,
4427
- additionalDirectories: meta?.claudeCode?.options?.additionalDirectories,
4564
+ additionalDirectories: [
4565
+ ...meta?.claudeCode?.options?.additionalDirectories ?? [],
4566
+ ...meta?.additionalRoots ?? []
4567
+ ],
4428
4568
  disableBuiltInTools: meta?.disableBuiltInTools,
4429
4569
  settingsManager,
4430
4570
  onModeChange: this.createOnModeChange(),
@@ -4501,11 +4641,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4501
4641
  const modelOptions = await this.getModelConfigOptions();
4502
4642
  const resolvedModelId = settingsModel || modelOptions.currentModelId;
4503
4643
  session.modelId = resolvedModelId;
4504
- if (!isResume) {
4505
- const resolvedSdkModel = toSdkModelId(resolvedModelId);
4506
- if (resolvedSdkModel !== DEFAULT_MODEL) {
4507
- await this.session.query.setModel(resolvedSdkModel);
4508
- }
4644
+ session.lastContextWindowSize = this.getContextWindowForModel(resolvedModelId);
4645
+ const resolvedSdkModel = toSdkModelId(resolvedModelId);
4646
+ if (!isResume && resolvedSdkModel !== DEFAULT_MODEL) {
4647
+ await this.session.query.setModel(resolvedSdkModel);
4509
4648
  }
4510
4649
  const availableModes2 = getAvailableModes();
4511
4650
  const modes = {
@@ -4568,6 +4707,35 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4568
4707
  await this.updateConfigOption("mode", newMode);
4569
4708
  };
4570
4709
  }
4710
+ getExistingSessionState(sessionId) {
4711
+ if (this.sessionId !== sessionId || !this.session) return null;
4712
+ const availableModes2 = getAvailableModes();
4713
+ const modes = {
4714
+ currentModeId: this.session.permissionMode,
4715
+ availableModes: availableModes2.map((mode) => ({
4716
+ id: mode.id,
4717
+ name: mode.name,
4718
+ description: mode.description ?? void 0
4719
+ }))
4720
+ };
4721
+ const modelOptions = this.session.configOptions.find(
4722
+ (o) => o.id === "model"
4723
+ );
4724
+ const models = {
4725
+ currentModelId: this.session.modelId ?? DEFAULT_MODEL,
4726
+ availableModels: modelOptions && "options" in modelOptions ? modelOptions.options.map((opt) => ({
4727
+ modelId: opt.value,
4728
+ name: opt.name,
4729
+ description: opt.description
4730
+ })) : []
4731
+ };
4732
+ return {
4733
+ sessionId,
4734
+ modes,
4735
+ models,
4736
+ configOptions: this.session.configOptions
4737
+ };
4738
+ }
4571
4739
  buildConfigOptions(currentModeId, modelOptions, currentEffort = "high") {
4572
4740
  const modeOptions = getAvailableModes().map((mode) => ({
4573
4741
  value: mode.id,
@@ -4623,7 +4791,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
4623
4791
  }
4624
4792
  return;
4625
4793
  }
4626
- const currentValue = existingEffort?.currentValue ?? "high";
4794
+ const rawCurrentValue = existingEffort?.currentValue;
4795
+ const currentValue = typeof rawCurrentValue === "string" ? rawCurrentValue : "high";
4627
4796
  const isValidValue = effortOptions.some((o) => o.value === currentValue);
4628
4797
  const resolvedValue = isValidValue ? currentValue : "high";
4629
4798
  if (resolvedValue !== currentValue && this.session.effort) {