@getpaseo/server 0.1.97 → 0.1.99

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 (101) hide show
  1. package/dist/server/server/agent/agent-manager.d.ts +11 -3
  2. package/dist/server/server/agent/agent-manager.js +96 -24
  3. package/dist/server/server/agent/agent-prompt.d.ts +1 -1
  4. package/dist/server/server/agent/agent-prompt.js +3 -10
  5. package/dist/server/server/agent/agent-sdk-types.d.ts +20 -9
  6. package/dist/server/server/agent/create-agent/create.d.ts +2 -0
  7. package/dist/server/server/agent/create-agent/create.js +8 -7
  8. package/dist/server/server/agent/lifecycle-command.d.ts +15 -1
  9. package/dist/server/server/agent/lifecycle-command.js +9 -2
  10. package/dist/server/server/agent/mcp-server.js +254 -115
  11. package/dist/server/server/agent/provider-notices.d.ts +3 -0
  12. package/dist/server/server/agent/provider-notices.js +5 -0
  13. package/dist/server/server/agent/provider-registry.d.ts +8 -3
  14. package/dist/server/server/agent/provider-registry.js +58 -25
  15. package/dist/server/server/agent/provider-snapshot-manager.d.ts +3 -0
  16. package/dist/server/server/agent/provider-snapshot-manager.js +37 -16
  17. package/dist/server/server/agent/providers/acp-agent.d.ts +5 -3
  18. package/dist/server/server/agent/providers/acp-agent.js +32 -19
  19. package/dist/server/server/agent/providers/claude/agent.d.ts +2 -2
  20. package/dist/server/server/agent/providers/claude/agent.js +261 -167
  21. package/dist/server/server/agent/providers/claude/models.js +7 -3
  22. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +6 -4
  23. package/dist/server/server/agent/providers/codex-app-server-agent.js +48 -25
  24. package/dist/server/server/agent/providers/copilot-acp-agent.js +4 -31
  25. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +9 -0
  26. package/dist/server/server/agent/providers/diagnostic-utils.js +188 -0
  27. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +0 -1
  28. package/dist/server/server/agent/providers/generic-acp-agent.js +2 -108
  29. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +2 -3
  30. package/dist/server/server/agent/providers/mock-load-test-agent.js +5 -5
  31. package/dist/server/server/agent/providers/mock-slow-provider.d.ts +2 -3
  32. package/dist/server/server/agent/providers/mock-slow-provider.js +3 -6
  33. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +29 -2
  34. package/dist/server/server/agent/providers/opencode/server-manager.js +83 -17
  35. package/dist/server/server/agent/providers/opencode-agent.d.ts +6 -3
  36. package/dist/server/server/agent/providers/opencode-agent.js +61 -107
  37. package/dist/server/server/agent/providers/pi/agent.d.ts +2 -3
  38. package/dist/server/server/agent/providers/pi/agent.js +11 -63
  39. package/dist/server/server/agent/providers/pi/cli-runtime.js +2 -2
  40. package/dist/server/server/agent/providers/pi/runtime.d.ts +1 -1
  41. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +1 -1
  42. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +1 -1
  43. package/dist/server/server/bootstrap.d.ts +2 -0
  44. package/dist/server/server/bootstrap.js +32 -2
  45. package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
  46. package/dist/server/server/managed-processes/managed-processes.js +326 -0
  47. package/dist/server/server/resolve-worktree-creation-intent.d.ts +3 -0
  48. package/dist/server/server/resolve-worktree-creation-intent.js +3 -3
  49. package/dist/server/server/session/agent-config/agent-config-session.d.ts +50 -0
  50. package/dist/server/server/session/agent-config/agent-config-session.js +98 -0
  51. package/dist/server/server/session/chat/chat-schedule-loop-session.d.ts +120 -0
  52. package/dist/server/server/session/chat/chat-schedule-loop-session.js +489 -0
  53. package/dist/server/server/session/checkout/checkout-session.d.ts +142 -0
  54. package/dist/server/server/session/checkout/checkout-session.js +925 -0
  55. package/dist/server/server/session/daemon/daemon-session.d.ts +50 -0
  56. package/dist/server/server/session/daemon/daemon-session.js +98 -0
  57. package/dist/server/server/session/files/workspace-files-session.d.ts +43 -0
  58. package/dist/server/server/session/files/workspace-files-session.js +218 -0
  59. package/dist/server/server/session/project-config/project-config-session.d.ts +34 -0
  60. package/dist/server/server/session/project-config/project-config-session.js +125 -0
  61. package/dist/server/server/session/provider/provider-catalog-session.d.ts +74 -0
  62. package/dist/server/server/session/provider/provider-catalog-session.js +339 -0
  63. package/dist/server/server/session/voice/voice-session.d.ts +166 -0
  64. package/dist/server/server/session/voice/voice-session.js +893 -0
  65. package/dist/server/server/{voice → session/voice}/voice-turn-controller.d.ts +2 -2
  66. package/dist/server/server/{voice → session/voice}/voice-turn-controller.js +2 -2
  67. package/dist/server/server/session.d.ts +23 -207
  68. package/dist/server/server/session.js +2319 -5102
  69. package/dist/server/server/speech/providers/openai/runtime.js +3 -4
  70. package/dist/server/server/websocket-server.d.ts +1 -0
  71. package/dist/server/server/websocket-server.js +11 -0
  72. package/dist/server/server/workspace-archive-service.js +2 -3
  73. package/dist/server/server/workspace-directory.js +5 -5
  74. package/dist/server/server/workspace-reconciliation-service.js +2 -2
  75. package/dist/server/server/worktree-core.d.ts +1 -0
  76. package/dist/server/server/worktree-core.js +5 -1
  77. package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
  78. package/dist/server/services/quota-fetcher/manifest.js +47 -0
  79. package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
  80. package/dist/server/services/quota-fetcher/provider.js +2 -0
  81. package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
  82. package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
  83. package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
  84. package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
  85. package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
  86. package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
  87. package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
  88. package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
  89. package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
  90. package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
  91. package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
  92. package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
  93. package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
  94. package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
  95. package/dist/server/services/quota-fetcher/service.d.ts +28 -0
  96. package/dist/server/services/quota-fetcher/service.js +58 -0
  97. package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
  98. package/dist/server/services/quota-fetcher/usage.js +49 -0
  99. package/dist/server/utils/checkout-git.d.ts +6 -0
  100. package/dist/server/utils/directory-suggestions.js +98 -2
  101. package/package.json +5 -5
@@ -9,13 +9,14 @@ import { getClaudeModelsWithSettings, normalizeClaudeRuntimeModelId } from "./mo
9
9
  import { parsePartialJsonObject } from "./partial-json.js";
10
10
  import { ClaudeSidechainTracker } from "./sidechain-tracker.js";
11
11
  import { buildClaudeFeatures, claudeModelSupportsFastMode } from "./feature-definitions.js";
12
- import { buildBinaryDiagnosticRows, formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, toDiagnosticErrorMessage, } from "../diagnostic-utils.js";
12
+ import { buildBinaryDiagnosticRows, buildCommandResolutionDiagnosticRows, formatProviderDiagnostic, formatProviderDiagnosticError, } from "../diagnostic-utils.js";
13
13
  import { appendOrReplaceGrowingAssistantMessage, runProviderTurn } from "../provider-runner.js";
14
14
  import { renderPromptAttachmentAsText } from "../../prompt-attachments.js";
15
15
  import { claudeQuery } from "./query.js";
16
16
  import { realClaudeRewindSdk, revertClaudeConversation, revertClaudeFiles } from "./rewind.js";
17
17
  import { normalizeProviderReplayTimestamp } from "../../provider-history-timestamps.js";
18
18
  import { claudeProjectDirSync } from "./project-dir.js";
19
+ import { SETTING_APPLIES_NEXT_TURN_NOTICE } from "../../provider-notices.js";
19
20
  import { getAgentStreamEventTurnId, } from "../../agent-sdk-types.js";
20
21
  import { importSessionFromPersistence } from "../../provider-session-import.js";
21
22
  import { checkProviderLaunchAvailable, createProviderEnv, createProviderEnvSpec, resolveProviderLaunch, } from "../../provider-launch-config.js";
@@ -228,6 +229,9 @@ function isClaudeThinkingEffort(value) {
228
229
  value === "xhigh" ||
229
230
  value === "max");
230
231
  }
232
+ function isClaudeThinkingOption(value) {
233
+ return value === "ultracode" || isClaudeThinkingEffort(value);
234
+ }
231
235
  const MAX_RECENT_STDERR_CHARS = 4000;
232
236
  const STDERR_FLUSH_WAIT_MS = 150;
233
237
  const STDERR_FLUSH_POLL_INTERVAL_MS = 10;
@@ -1028,9 +1032,10 @@ export class ClaudeAgentClient {
1028
1032
  resolveBinary: this.resolveBinary,
1029
1033
  });
1030
1034
  }
1031
- async listModels(_options) {
1035
+ async fetchCatalog(_options) {
1032
1036
  // Claude exposes a global catalog here; cwd/force are intentionally irrelevant.
1033
- return await getClaudeModelsWithSettings(this.logger, this.configDir);
1037
+ const models = await getClaudeModelsWithSettings(this.logger, this.configDir);
1038
+ return { models, modes: DEFAULT_MODES };
1034
1039
  }
1035
1040
  async listFeatures(config) {
1036
1041
  const claudeConfig = this.assertConfig(config);
@@ -1075,34 +1080,16 @@ export class ClaudeAgentClient {
1075
1080
  defaultBinary: "claude",
1076
1081
  });
1077
1082
  const availability = await checkProviderLaunchAvailable(launch);
1078
- const available = availability.available;
1079
- const auth = available
1083
+ const auth = availability.available
1080
1084
  ? await resolveClaudeAuth(launch, availability, this.runtimeSettings)
1081
1085
  : null;
1082
- let modelsValue = "Not checked";
1083
- let status = formatDiagnosticStatus(available);
1084
- if (available) {
1085
- try {
1086
- const models = await this.listModels({
1087
- cwd: os.homedir(),
1088
- force: false,
1089
- });
1090
- modelsValue = String(models.length);
1091
- }
1092
- catch (error) {
1093
- modelsValue = `Error - ${toDiagnosticErrorMessage(error)}`;
1094
- status = formatDiagnosticStatus(available, {
1095
- source: "model fetch",
1096
- cause: error,
1097
- });
1098
- }
1099
- }
1100
1086
  return {
1101
1087
  diagnostic: formatProviderDiagnostic("Claude Code", [
1088
+ ...(await buildCommandResolutionDiagnosticRows(launch, {
1089
+ knownBinaryNames: ["claude"],
1090
+ })),
1102
1091
  ...(await buildBinaryDiagnosticRows(launch, availability)),
1103
1092
  ...(auth ? [{ label: "Auth", value: auth }] : []),
1104
- { label: "Models", value: modelsValue },
1105
- { label: "Status", value: status },
1106
1093
  ]),
1107
1094
  };
1108
1095
  }
@@ -1180,21 +1167,21 @@ function extractContextWindowSize(modelUsage) {
1180
1167
  }
1181
1168
  return maxContextWindow;
1182
1169
  }
1183
- function readUsageTotalTokens(usage) {
1184
- if (!usage || typeof usage !== "object") {
1170
+ function resolveInitialContextWindowSize(modelId) {
1171
+ const normalized = typeof modelId === "string" ? modelId.trim().toLowerCase() : "";
1172
+ if (!normalized) {
1185
1173
  return undefined;
1186
1174
  }
1187
- const totalTokens = usage.total_tokens;
1188
- if (typeof totalTokens !== "number" || !Number.isFinite(totalTokens) || totalTokens < 0) {
1189
- return undefined;
1175
+ if (normalized.includes("[1m]") || normalized.includes("context-1m")) {
1176
+ return 1000000;
1190
1177
  }
1191
- return totalTokens;
1192
- }
1193
- function readContextWindowUsedTokensFromTaskProgress(message) {
1194
- return readUsageTotalTokens(message.usage);
1195
- }
1196
- function readUsageFromTaskNotification(message) {
1197
- return readUsageTotalTokens(message.usage);
1178
+ if (normalized.includes("claude-fable-5")) {
1179
+ return 1000000;
1180
+ }
1181
+ if (/(?:^|[~/_-])(?:claude[-_ ]*)?(opus|sonnet|haiku)(?:$|[-_ ./])/.test(normalized)) {
1182
+ return 200000;
1183
+ }
1184
+ return undefined;
1198
1185
  }
1199
1186
  function readStreamRequestInputTokens(event) {
1200
1187
  const messageUsage = toObjectRecord(toObjectRecord(event.message)?.usage);
@@ -1225,6 +1212,172 @@ function readStreamRequestOutputTokens(event) {
1225
1212
  }
1226
1213
  return outputTokens;
1227
1214
  }
1215
+ function readLastUsageIteration(usage) {
1216
+ const iterations = toObjectRecord(usage)?.iterations;
1217
+ if (!Array.isArray(iterations)) {
1218
+ return undefined;
1219
+ }
1220
+ for (let index = iterations.length - 1; index >= 0; index -= 1) {
1221
+ const candidate = toObjectRecord(iterations[index]);
1222
+ if (candidate) {
1223
+ return candidate;
1224
+ }
1225
+ }
1226
+ return undefined;
1227
+ }
1228
+ function readUsageTokenTotal(usage) {
1229
+ const usageWithCacheCreation = usage;
1230
+ const inputTokens = typeof usage.input_tokens === "number" && Number.isFinite(usage.input_tokens)
1231
+ ? usage.input_tokens
1232
+ : 0;
1233
+ const cacheCreationInputTokens = typeof usageWithCacheCreation.cache_creation_input_tokens === "number" &&
1234
+ Number.isFinite(usageWithCacheCreation.cache_creation_input_tokens)
1235
+ ? usageWithCacheCreation.cache_creation_input_tokens
1236
+ : 0;
1237
+ const cacheReadInputTokens = typeof usage.cache_read_input_tokens === "number" &&
1238
+ Number.isFinite(usage.cache_read_input_tokens)
1239
+ ? usage.cache_read_input_tokens
1240
+ : 0;
1241
+ const outputTokens = typeof usage.output_tokens === "number" && Number.isFinite(usage.output_tokens)
1242
+ ? usage.output_tokens
1243
+ : 0;
1244
+ const total = inputTokens + cacheCreationInputTokens + cacheReadInputTokens + outputTokens;
1245
+ return total > 0 ? total : undefined;
1246
+ }
1247
+ function readActiveUsageTokens(usage) {
1248
+ const activeUsage = readLastUsageIteration(usage);
1249
+ return activeUsage ? readUsageTokenTotal(activeUsage) : undefined;
1250
+ }
1251
+ function readLegacyResultUsageTokens(usage) {
1252
+ const usageRecord = toObjectRecord(usage);
1253
+ return usageRecord ? readUsageTokenTotal(usageRecord) : undefined;
1254
+ }
1255
+ function readCurrentContextUsage(value) {
1256
+ const record = toObjectRecord(value);
1257
+ if (!record) {
1258
+ return undefined;
1259
+ }
1260
+ const totalTokens = record.totalTokens;
1261
+ if (typeof totalTokens !== "number" || !Number.isFinite(totalTokens) || totalTokens < 0) {
1262
+ return undefined;
1263
+ }
1264
+ const maxTokens = record.maxTokens;
1265
+ return {
1266
+ totalTokens,
1267
+ ...(typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0
1268
+ ? { maxTokens }
1269
+ : {}),
1270
+ };
1271
+ }
1272
+ function isClaudeSubagentToolName(name) {
1273
+ return name === "Task" || name === "Agent";
1274
+ }
1275
+ class ClaudeContextUsageState {
1276
+ constructor(initialContextWindowMaxTokens) {
1277
+ this.completedResultTurns = 0;
1278
+ this.contextWindowMaxTokens = initialContextWindowMaxTokens;
1279
+ }
1280
+ beginTurn() {
1281
+ this.streamRequestInputTokens = undefined;
1282
+ this.streamRequestOutputTokens = undefined;
1283
+ }
1284
+ setInitialContextWindowMaxTokens(contextWindowMaxTokens) {
1285
+ this.contextWindowMaxTokens = contextWindowMaxTokens;
1286
+ }
1287
+ recordModelUsage(modelUsage) {
1288
+ const contextWindowMaxTokens = extractContextWindowSize(modelUsage);
1289
+ if (contextWindowMaxTokens !== undefined) {
1290
+ this.contextWindowMaxTokens = contextWindowMaxTokens;
1291
+ }
1292
+ return this.contextWindowMaxTokens;
1293
+ }
1294
+ recordCurrentContextUsage(usage) {
1295
+ if (usage?.maxTokens !== undefined) {
1296
+ this.contextWindowMaxTokens = usage.maxTokens;
1297
+ }
1298
+ }
1299
+ buildStreamUsageEvent(event) {
1300
+ const streamEvent = toObjectRecord(event);
1301
+ if (!streamEvent) {
1302
+ return null;
1303
+ }
1304
+ const eventType = readTrimmedString(streamEvent.type);
1305
+ if (eventType === "message_start") {
1306
+ const inputTokens = readStreamRequestInputTokens(streamEvent);
1307
+ if (typeof inputTokens !== "number") {
1308
+ return null;
1309
+ }
1310
+ this.streamRequestInputTokens = inputTokens;
1311
+ this.streamRequestOutputTokens = 0;
1312
+ }
1313
+ else if (eventType === "message_delta") {
1314
+ const outputTokens = readStreamRequestOutputTokens(streamEvent);
1315
+ if (typeof outputTokens !== "number") {
1316
+ return null;
1317
+ }
1318
+ this.streamRequestOutputTokens = outputTokens;
1319
+ }
1320
+ else {
1321
+ return null;
1322
+ }
1323
+ const usedTokens = this.streamUsedTokens();
1324
+ if (usedTokens === undefined) {
1325
+ return null;
1326
+ }
1327
+ return this.createUsageUpdatedEvent(usedTokens);
1328
+ }
1329
+ buildResultUsage(message, modelUsage, currentContextUsage) {
1330
+ try {
1331
+ if (!message.usage) {
1332
+ return undefined;
1333
+ }
1334
+ const usage = {
1335
+ inputTokens: message.usage.input_tokens,
1336
+ cachedInputTokens: message.usage.cache_read_input_tokens,
1337
+ outputTokens: message.usage.output_tokens,
1338
+ totalCostUsd: message.total_cost_usd,
1339
+ };
1340
+ const modelContextWindowMaxTokens = this.recordModelUsage(modelUsage ?? message.modelUsage);
1341
+ this.recordCurrentContextUsage(currentContextUsage);
1342
+ if (this.contextWindowMaxTokens !== undefined) {
1343
+ usage.contextWindowMaxTokens = this.contextWindowMaxTokens;
1344
+ }
1345
+ else if (modelContextWindowMaxTokens !== undefined) {
1346
+ usage.contextWindowMaxTokens = modelContextWindowMaxTokens;
1347
+ }
1348
+ const activeResultUsageTokens = readActiveUsageTokens(message.usage) ??
1349
+ (this.completedResultTurns === 0 ? readLegacyResultUsageTokens(message.usage) : undefined);
1350
+ const usedTokens = currentContextUsage?.totalTokens ?? this.streamUsedTokens() ?? activeResultUsageTokens;
1351
+ if (usedTokens !== undefined) {
1352
+ usage.contextWindowUsedTokens = usedTokens;
1353
+ }
1354
+ return usage;
1355
+ }
1356
+ finally {
1357
+ this.completedResultTurns += 1;
1358
+ }
1359
+ }
1360
+ streamUsedTokens() {
1361
+ if (typeof this.streamRequestInputTokens !== "number" ||
1362
+ typeof this.streamRequestOutputTokens !== "number") {
1363
+ return undefined;
1364
+ }
1365
+ return this.streamRequestInputTokens + this.streamRequestOutputTokens;
1366
+ }
1367
+ createUsageUpdatedEvent(contextWindowUsedTokens) {
1368
+ const usage = {
1369
+ contextWindowUsedTokens,
1370
+ };
1371
+ if (this.contextWindowMaxTokens !== undefined) {
1372
+ usage.contextWindowMaxTokens = this.contextWindowMaxTokens;
1373
+ }
1374
+ return {
1375
+ type: "usage_updated",
1376
+ provider: "claude",
1377
+ usage,
1378
+ };
1379
+ }
1380
+ }
1228
1381
  class ClaudeAgentSession {
1229
1382
  constructor(config, options) {
1230
1383
  this.provider = "claude";
@@ -1344,6 +1497,7 @@ class ClaudeAgentSession {
1344
1497
  this.logger = options.logger.child({ agentId: this.agentId });
1345
1498
  this.queryFactory = options.queryFactory;
1346
1499
  this.resolveBinary = options.resolveBinary;
1500
+ this.contextUsage = new ClaudeContextUsageState(resolveInitialContextWindowSize(this.config.model));
1347
1501
  const handle = options.handle;
1348
1502
  if (handle) {
1349
1503
  if (!handle.sessionId) {
@@ -1441,6 +1595,7 @@ class ClaudeAgentSession {
1441
1595
  this.activeForegroundTurnId = turnId;
1442
1596
  this.foregroundHasVisibleActivity = false;
1443
1597
  this.activeTurnHasAssistantText = false;
1598
+ this.contextUsage.beginTurn();
1444
1599
  this.transitionTurnState("foreground", "foreground turn started");
1445
1600
  this.clearRecentStderr();
1446
1601
  let cancelIssued = false;
@@ -1550,6 +1705,7 @@ class ClaudeAgentSession {
1550
1705
  if (!claudeModelSupportsFastMode(this.config.model) && this.config.featureValues?.fast_mode) {
1551
1706
  await this.applyFastModeFeature(false, activeQuery);
1552
1707
  }
1708
+ this.contextUsage.setInitialContextWindowMaxTokens(resolveInitialContextWindowSize(this.config.model));
1553
1709
  this.lastOptionsModel = normalizedModelId ?? this.lastOptionsModel;
1554
1710
  this.lastRuntimeModel = null;
1555
1711
  this.cachedRuntimeInfo = null;
@@ -1563,13 +1719,16 @@ class ClaudeAgentSession {
1563
1719
  if (!normalizedThinkingOptionId || normalizedThinkingOptionId === "default") {
1564
1720
  this.config.thinkingOptionId = undefined;
1565
1721
  }
1566
- else if (isClaudeThinkingEffort(normalizedThinkingOptionId)) {
1722
+ else if (isClaudeThinkingOption(normalizedThinkingOptionId)) {
1567
1723
  this.config.thinkingOptionId = normalizedThinkingOptionId;
1568
1724
  }
1569
1725
  else {
1570
1726
  throw new Error(`Unknown thinking option: ${normalizedThinkingOptionId}`);
1571
1727
  }
1572
1728
  this.queryRestartNeeded = true;
1729
+ if (this.activeForegroundTurnId || this.autonomousTurn) {
1730
+ return SETTING_APPLIES_NEXT_TURN_NOTICE;
1731
+ }
1573
1732
  }
1574
1733
  async setFeature(featureId, value) {
1575
1734
  if (featureId !== "fast_mode") {
@@ -2096,10 +2255,13 @@ class ClaudeAgentSession {
2096
2255
  const thinkingOptionId = this.config.thinkingOptionId && this.config.thinkingOptionId !== "default"
2097
2256
  ? this.config.thinkingOptionId
2098
2257
  : undefined;
2258
+ if (thinkingOptionId === "ultracode") {
2259
+ return { thinking: { type: "adaptive" }, effort: "xhigh", ultracode: true };
2260
+ }
2099
2261
  if (thinkingOptionId && isClaudeThinkingEffort(thinkingOptionId)) {
2100
- return { thinking: { type: "adaptive" }, effort: thinkingOptionId };
2262
+ return { thinking: { type: "adaptive" }, effort: thinkingOptionId, ultracode: false };
2101
2263
  }
2102
- return { thinking: undefined, effort: undefined };
2264
+ return { thinking: undefined, effort: undefined, ultracode: false };
2103
2265
  }
2104
2266
  buildAppendedSystemPrompt() {
2105
2267
  return (composeSystemPromptParts(this.config.systemPrompt, this.config.daemonAppendSystemPrompt) ?? "");
@@ -2120,10 +2282,10 @@ class ClaudeAgentSession {
2120
2282
  });
2121
2283
  }
2122
2284
  async buildOptions() {
2123
- const { thinking, effort } = this.resolveThinkingConfig();
2285
+ const { thinking, effort, ultracode } = this.resolveThinkingConfig();
2124
2286
  const appendedSystemPrompt = this.buildAppendedSystemPrompt();
2125
2287
  const extraClaudeOptions = this.config.extra?.claude;
2126
- const fastModeOptions = this.buildFastModeOptions(extraClaudeOptions);
2288
+ const settingsOptions = this.buildSettingsOptions(extraClaudeOptions, { ultracode });
2127
2289
  const sdkEnv = this.buildSdkEnv(extraClaudeOptions);
2128
2290
  assertClaudeAutoModeEligible(this.currentMode, sdkEnv);
2129
2291
  const claudeBinary = await this.resolveBinary();
@@ -2172,7 +2334,7 @@ class ClaudeAgentSession {
2172
2334
  ...(thinking ? { thinking } : {}),
2173
2335
  ...(effort ? { effort } : {}),
2174
2336
  ...extraClaudeOptions,
2175
- ...fastModeOptions,
2337
+ ...settingsOptions,
2176
2338
  ...(this.persistSession === undefined ? {} : { persistSession: this.persistSession }),
2177
2339
  env: sdkEnv,
2178
2340
  };
@@ -2194,12 +2356,17 @@ class ClaudeAgentSession {
2194
2356
  }
2195
2357
  return base;
2196
2358
  }
2197
- buildFastModeOptions(extraClaudeOptions) {
2359
+ buildSettingsOptions(extraClaudeOptions, input) {
2198
2360
  const fastMode = this.resolveFastModeSetting();
2199
- if (fastMode === null) {
2361
+ if (fastMode === null && !input.ultracode) {
2200
2362
  return {};
2201
2363
  }
2202
- return { settings: mergeClaudeSettings(extraClaudeOptions?.settings, { fastMode }) };
2364
+ return {
2365
+ settings: mergeClaudeSettings(extraClaudeOptions?.settings, {
2366
+ ...(fastMode === null ? {} : { fastMode }),
2367
+ ...(input.ultracode ? { ultracode: true } : {}),
2368
+ }),
2369
+ };
2203
2370
  }
2204
2371
  resolveFastModeSetting() {
2205
2372
  if (!claudeModelSupportsFastMode(this.config.model)) {
@@ -2413,6 +2580,7 @@ class ClaudeAgentSession {
2413
2580
  id: this.createTurnId("autonomous"),
2414
2581
  };
2415
2582
  this.activeTurnHasAssistantText = false;
2583
+ this.contextUsage.beginTurn();
2416
2584
  this.notifySubscribers({ type: "turn_started", provider: "claude" });
2417
2585
  this.syncTurnState("autonomous turn started");
2418
2586
  }
@@ -2491,7 +2659,7 @@ class ClaudeAgentSession {
2491
2659
  if (await this.handleMissingResumedConversation(message, activeQuery)) {
2492
2660
  return true;
2493
2661
  }
2494
- this.routeSdkMessageFromPump(message);
2662
+ await this.routeSdkMessageFromPump(message, activeQuery);
2495
2663
  return false;
2496
2664
  };
2497
2665
  const drainActiveQuery = async () => {
@@ -2559,7 +2727,7 @@ class ClaudeAgentSession {
2559
2727
  message.type === "tool_progress" ||
2560
2728
  (message.type === "system" && message.subtype === "task_notification"));
2561
2729
  }
2562
- routeSdkMessageFromPump(message) {
2730
+ async routeSdkMessageFromPump(message, activeQuery) {
2563
2731
  if (this.shouldSuppressStaleResult(message)) {
2564
2732
  return;
2565
2733
  }
@@ -2582,22 +2750,7 @@ class ClaudeAgentSession {
2582
2750
  identifiers,
2583
2751
  rawEvent: message,
2584
2752
  }, "provider.claude.parsed_event");
2585
- const messageEvents = this.translateMessageToEvents(message, {
2586
- suppressAssistantText: true,
2587
- suppressReasoning: true,
2588
- });
2589
- const assistantTimelineEvents = this.timelineAssembler
2590
- .consume({
2591
- message,
2592
- runId: turnId,
2593
- messageIdHint: identifiers.messageId,
2594
- })
2595
- .map((item) => ({
2596
- type: "timeline",
2597
- item,
2598
- provider: "claude",
2599
- }));
2600
- const events = [...messageEvents, ...assistantTimelineEvents];
2753
+ const events = await this.buildPumpedMessageEvents(message, activeQuery, identifiers.messageId, turnId);
2601
2754
  if (events.length === 0) {
2602
2755
  return;
2603
2756
  }
@@ -2620,6 +2773,38 @@ class ClaudeAgentSession {
2620
2773
  }
2621
2774
  this.dispatchEvents(events);
2622
2775
  }
2776
+ async buildPumpedMessageEvents(message, activeQuery, messageIdHint, turnId) {
2777
+ const currentContextUsage = message.type === "result" && message.subtype === "success"
2778
+ ? await this.queryCurrentContextUsage(activeQuery)
2779
+ : undefined;
2780
+ const messageEvents = this.translateMessageToEvents(message, {
2781
+ suppressAssistantText: true,
2782
+ suppressReasoning: true,
2783
+ currentContextUsage,
2784
+ });
2785
+ const assistantTimelineEvents = this.timelineAssembler
2786
+ .consume({
2787
+ message,
2788
+ runId: turnId,
2789
+ messageIdHint,
2790
+ })
2791
+ .map((item) => ({
2792
+ type: "timeline",
2793
+ item,
2794
+ provider: "claude",
2795
+ }));
2796
+ return [...messageEvents, ...assistantTimelineEvents];
2797
+ }
2798
+ async queryCurrentContextUsage(activeQuery) {
2799
+ try {
2800
+ const usage = await withTimeout(activeQuery.getContextUsage(), 3000, "timeout");
2801
+ return readCurrentContextUsage(usage);
2802
+ }
2803
+ catch (error) {
2804
+ this.logger.debug({ err: error }, "Claude context usage query failed");
2805
+ return undefined;
2806
+ }
2807
+ }
2623
2808
  async handleMissingResumedConversation(message, activeQuery) {
2624
2809
  const staleResumeError = this.readMissingResumedConversationError(message);
2625
2810
  if (!staleResumeError) {
@@ -2710,7 +2895,9 @@ class ClaudeAgentSession {
2710
2895
  this.appendStreamEventEvents(message, events, options);
2711
2896
  break;
2712
2897
  case "result":
2713
- this.appendResultEvents(message, events);
2898
+ this.appendResultEvents(message, events, {
2899
+ currentContextUsage: options?.currentContextUsage,
2900
+ });
2714
2901
  break;
2715
2902
  default:
2716
2903
  break;
@@ -2783,22 +2970,18 @@ class ClaudeAgentSession {
2783
2970
  return;
2784
2971
  }
2785
2972
  if (message.subtype === "task_progress") {
2786
- this.lastContextWindowUsedTokens =
2787
- readContextWindowUsedTokensFromTaskProgress(message) ?? this.lastContextWindowUsedTokens;
2788
- if (typeof this.lastContextWindowUsedTokens === "number") {
2789
- events.push(this.createUsageUpdatedEvent(this.lastContextWindowUsedTokens));
2790
- }
2973
+ return;
2791
2974
  }
2792
2975
  }
2793
2976
  appendTaskNotificationEvents(message, events) {
2794
2977
  // TODO: subagent timelines are best-effort. Subagent task_notifications
2795
2978
  // arrive without parent_tool_use_id but with tool_use_id pointing at the
2796
- // parent's Task call, so they slip past the sidechain router and pollute
2979
+ // the parent's subagent tool call, so they slip past the sidechain router and pollute
2797
2980
  // the parent timeline. Drop them here; eventually thread them into the
2798
- // parent Task tool call's sub_agent log instead.
2981
+ // parent tool call's sub_agent log instead.
2799
2982
  const taskUseId = message.tool_use_id;
2800
2983
  const cachedTool = taskUseId ? this.toolUseCache.get(taskUseId) : undefined;
2801
- if (cachedTool?.name === "Task") {
2984
+ if (isClaudeSubagentToolName(cachedTool?.name)) {
2802
2985
  return;
2803
2986
  }
2804
2987
  const taskNotificationItem = mapTaskNotificationSystemRecordToToolCall(message);
@@ -2809,11 +2992,6 @@ class ClaudeAgentSession {
2809
2992
  provider: "claude",
2810
2993
  });
2811
2994
  }
2812
- const usage = readUsageFromTaskNotification(message);
2813
- if (typeof usage === "number") {
2814
- this.lastContextWindowUsedTokens = usage;
2815
- events.push(this.createUsageUpdatedEvent(usage));
2816
- }
2817
2995
  }
2818
2996
  appendUserMessageEvents(message, events) {
2819
2997
  if (isSyntheticUserEntry(message)) {
@@ -2877,7 +3055,7 @@ class ClaudeAgentSession {
2877
3055
  }
2878
3056
  }
2879
3057
  appendStreamEventEvents(message, events, options) {
2880
- const usageUpdatedEvent = this.trackStreamEventUsage(message.event);
3058
+ const usageUpdatedEvent = this.contextUsage.buildStreamUsageEvent(message.event);
2881
3059
  if (usageUpdatedEvent) {
2882
3060
  events.push(usageUpdatedEvent);
2883
3061
  }
@@ -2889,8 +3067,8 @@ class ClaudeAgentSession {
2889
3067
  events.push({ type: "timeline", item, provider: "claude" });
2890
3068
  }
2891
3069
  }
2892
- appendResultEvents(message, events) {
2893
- const usage = this.convertUsage(message, message.modelUsage);
3070
+ appendResultEvents(message, events, options) {
3071
+ const usage = this.convertUsage(message, message.modelUsage, options?.currentContextUsage);
2894
3072
  if (message.subtype === "success") {
2895
3073
  // Built-in slash commands (e.g. /voice, /usage, "Unknown command: …")
2896
3074
  // run client-side in the Claude CLI with no model turn — output_tokens
@@ -3034,92 +3212,8 @@ class ClaudeAgentSession {
3034
3212
  }
3035
3213
  return null;
3036
3214
  }
3037
- convertUsage(message, modelUsage) {
3038
- if (!message.usage) {
3039
- return undefined;
3040
- }
3041
- const usage = {
3042
- inputTokens: message.usage.input_tokens,
3043
- cachedInputTokens: message.usage.cache_read_input_tokens,
3044
- outputTokens: message.usage.output_tokens,
3045
- totalCostUsd: message.total_cost_usd,
3046
- };
3047
- const contextWindowMaxTokens = extractContextWindowSize(modelUsage ?? message.modelUsage);
3048
- if (contextWindowMaxTokens !== undefined) {
3049
- this.lastContextWindowMaxTokens = contextWindowMaxTokens;
3050
- usage.contextWindowMaxTokens = contextWindowMaxTokens;
3051
- }
3052
- else if (this.lastContextWindowMaxTokens !== undefined) {
3053
- usage.contextWindowMaxTokens = this.lastContextWindowMaxTokens;
3054
- }
3055
- if (typeof this.lastContextWindowUsedTokens === "number") {
3056
- // task_progress.total_tokens is the accurate context window fill level.
3057
- // Prefer it over result.usage which contains accumulated session totals.
3058
- usage.contextWindowUsedTokens = this.lastContextWindowUsedTokens;
3059
- }
3060
- else if (typeof this.lastStreamRequestInputTokens === "number" &&
3061
- typeof this.lastStreamRequestOutputTokens === "number") {
3062
- usage.contextWindowUsedTokens =
3063
- this.lastStreamRequestInputTokens + this.lastStreamRequestOutputTokens;
3064
- }
3065
- else if (message.usage) {
3066
- // Fallback: derive from result.usage when no task_progress has been
3067
- // received yet. These values are accumulated across all API calls, but
3068
- // for the first turn they equal the per-call values so the estimate is
3069
- // reasonable. Once a task_progress arrives it takes over permanently.
3070
- const usageWithCacheCreation = message.usage;
3071
- const derived = (message.usage.input_tokens ?? 0) +
3072
- (usageWithCacheCreation.cache_creation_input_tokens ?? 0) +
3073
- (message.usage.cache_read_input_tokens ?? 0) +
3074
- (message.usage.output_tokens ?? 0);
3075
- if (Number.isFinite(derived) && derived > 0) {
3076
- usage.contextWindowUsedTokens = derived;
3077
- }
3078
- }
3079
- return usage;
3080
- }
3081
- createUsageUpdatedEvent(contextWindowUsedTokens) {
3082
- const usage = {
3083
- contextWindowUsedTokens,
3084
- };
3085
- if (this.lastContextWindowMaxTokens !== undefined) {
3086
- usage.contextWindowMaxTokens = this.lastContextWindowMaxTokens;
3087
- }
3088
- return {
3089
- type: "usage_updated",
3090
- provider: "claude",
3091
- usage,
3092
- };
3093
- }
3094
- trackStreamEventUsage(event) {
3095
- const streamEvent = toObjectRecord(event);
3096
- if (!streamEvent) {
3097
- return null;
3098
- }
3099
- const eventType = readTrimmedString(streamEvent.type);
3100
- if (eventType === "message_start") {
3101
- const inputTokens = readStreamRequestInputTokens(streamEvent);
3102
- if (typeof inputTokens !== "number") {
3103
- return null;
3104
- }
3105
- this.lastStreamRequestInputTokens = inputTokens;
3106
- this.lastStreamRequestOutputTokens = 0;
3107
- }
3108
- else if (eventType === "message_delta") {
3109
- const outputTokens = readStreamRequestOutputTokens(streamEvent);
3110
- if (typeof outputTokens !== "number") {
3111
- return null;
3112
- }
3113
- this.lastStreamRequestOutputTokens = outputTokens;
3114
- }
3115
- else {
3116
- return null;
3117
- }
3118
- if (typeof this.lastStreamRequestInputTokens !== "number" ||
3119
- typeof this.lastStreamRequestOutputTokens !== "number") {
3120
- return null;
3121
- }
3122
- return this.createUsageUpdatedEvent(this.lastStreamRequestInputTokens + this.lastStreamRequestOutputTokens);
3215
+ convertUsage(message, modelUsage, currentContextUsage) {
3216
+ return this.contextUsage.buildResultUsage(message, modelUsage, currentContextUsage);
3123
3217
  }
3124
3218
  enqueueTimeline(item) {
3125
3219
  this.pushEvent({ type: "timeline", item, provider: "claude" });
@@ -14,20 +14,24 @@ const CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS = [
14
14
  { id: "xhigh", label: "Extra High" },
15
15
  { id: "max", label: "Max" },
16
16
  ];
17
+ const CLAUDE_ULTRACODE_THINKING_OPTIONS = [
18
+ ...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS,
19
+ { id: "ultracode", label: "Ultracode" },
20
+ ];
17
21
  const CLAUDE_MODELS = [
18
22
  {
19
23
  provider: "claude",
20
24
  id: "claude-fable-5",
21
25
  label: "Fable 5",
22
26
  description: "Fable 5 · Most powerful model",
23
- thinkingOptions: [...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS],
27
+ thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS],
24
28
  },
25
29
  {
26
30
  provider: "claude",
27
31
  id: "claude-opus-4-8[1m]",
28
32
  label: "Opus 4.8 1M",
29
33
  description: "Opus 4.8 with 1M context window",
30
- thinkingOptions: [...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS],
34
+ thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS],
31
35
  },
32
36
  {
33
37
  provider: "claude",
@@ -35,7 +39,7 @@ const CLAUDE_MODELS = [
35
39
  label: "Opus 4.8",
36
40
  description: "Opus 4.8 · Latest release",
37
41
  isDefault: true,
38
- thinkingOptions: [...CLAUDE_OPUS_EXTENDED_THINKING_OPTIONS],
42
+ thinkingOptions: [...CLAUDE_ULTRACODE_THINKING_OPTIONS],
39
43
  },
40
44
  {
41
45
  provider: "claude",