@getpaseo/server 0.1.97 → 0.1.98

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 (69) hide show
  1. package/dist/server/server/agent/agent-manager.d.ts +11 -3
  2. package/dist/server/server/agent/agent-manager.js +94 -22
  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 +9 -3
  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 +2 -0
  14. package/dist/server/server/agent/provider-registry.js +10 -3
  15. package/dist/server/server/agent/provider-snapshot-manager.d.ts +3 -0
  16. package/dist/server/server/agent/provider-snapshot-manager.js +11 -2
  17. package/dist/server/server/agent/providers/claude/agent.js +257 -143
  18. package/dist/server/server/agent/providers/claude/models.js +7 -3
  19. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +4 -3
  20. package/dist/server/server/agent/providers/codex-app-server-agent.js +43 -1
  21. package/dist/server/server/agent/providers/copilot-acp-agent.js +4 -1
  22. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +9 -0
  23. package/dist/server/server/agent/providers/diagnostic-utils.js +188 -0
  24. package/dist/server/server/agent/providers/mock-slow-provider.js +1 -1
  25. package/dist/server/server/agent/providers/opencode/server-manager.d.ts +29 -2
  26. package/dist/server/server/agent/providers/opencode/server-manager.js +83 -17
  27. package/dist/server/server/agent/providers/opencode-agent.d.ts +2 -0
  28. package/dist/server/server/agent/providers/opencode-agent.js +14 -9
  29. package/dist/server/server/agent/providers/pi/agent.js +27 -14
  30. package/dist/server/server/bootstrap.d.ts +2 -0
  31. package/dist/server/server/bootstrap.js +32 -2
  32. package/dist/server/server/managed-processes/managed-processes.d.ts +76 -0
  33. package/dist/server/server/managed-processes/managed-processes.js +326 -0
  34. package/dist/server/server/resolve-worktree-creation-intent.d.ts +3 -0
  35. package/dist/server/server/resolve-worktree-creation-intent.js +3 -3
  36. package/dist/server/server/session.d.ts +12 -1
  37. package/dist/server/server/session.js +230 -40
  38. package/dist/server/server/speech/providers/openai/runtime.js +3 -4
  39. package/dist/server/server/websocket-server.d.ts +1 -0
  40. package/dist/server/server/websocket-server.js +11 -0
  41. package/dist/server/server/workspace-archive-service.js +2 -3
  42. package/dist/server/server/workspace-directory.js +5 -5
  43. package/dist/server/server/workspace-reconciliation-service.js +2 -2
  44. package/dist/server/server/worktree-core.d.ts +1 -0
  45. package/dist/server/server/worktree-core.js +5 -1
  46. package/dist/server/services/quota-fetcher/manifest.d.ts +4 -0
  47. package/dist/server/services/quota-fetcher/manifest.js +47 -0
  48. package/dist/server/services/quota-fetcher/provider.d.ts +17 -0
  49. package/dist/server/services/quota-fetcher/provider.js +2 -0
  50. package/dist/server/services/quota-fetcher/providers/claude.d.ts +26 -0
  51. package/dist/server/services/quota-fetcher/providers/claude.js +217 -0
  52. package/dist/server/services/quota-fetcher/providers/codex.d.ts +23 -0
  53. package/dist/server/services/quota-fetcher/providers/codex.js +211 -0
  54. package/dist/server/services/quota-fetcher/providers/copilot.d.ts +17 -0
  55. package/dist/server/services/quota-fetcher/providers/copilot.js +75 -0
  56. package/dist/server/services/quota-fetcher/providers/cursor.d.ts +17 -0
  57. package/dist/server/services/quota-fetcher/providers/cursor.js +123 -0
  58. package/dist/server/services/quota-fetcher/providers/grok.d.ts +18 -0
  59. package/dist/server/services/quota-fetcher/providers/grok.js +89 -0
  60. package/dist/server/services/quota-fetcher/providers/kimi.d.ts +20 -0
  61. package/dist/server/services/quota-fetcher/providers/kimi.js +89 -0
  62. package/dist/server/services/quota-fetcher/providers/zai.d.ts +17 -0
  63. package/dist/server/services/quota-fetcher/providers/zai.js +58 -0
  64. package/dist/server/services/quota-fetcher/service.d.ts +28 -0
  65. package/dist/server/services/quota-fetcher/service.js +58 -0
  66. package/dist/server/services/quota-fetcher/usage.d.ts +22 -0
  67. package/dist/server/services/quota-fetcher/usage.js +49 -0
  68. package/dist/server/utils/directory-suggestions.js +98 -2
  69. 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, formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, toDiagnosticErrorMessage, } 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;
@@ -1099,6 +1103,9 @@ export class ClaudeAgentClient {
1099
1103
  }
1100
1104
  return {
1101
1105
  diagnostic: formatProviderDiagnostic("Claude Code", [
1106
+ ...(await buildCommandResolutionDiagnosticRows(launch, {
1107
+ knownBinaryNames: ["claude"],
1108
+ })),
1102
1109
  ...(await buildBinaryDiagnosticRows(launch, availability)),
1103
1110
  ...(auth ? [{ label: "Auth", value: auth }] : []),
1104
1111
  { label: "Models", value: modelsValue },
@@ -1180,21 +1187,21 @@ function extractContextWindowSize(modelUsage) {
1180
1187
  }
1181
1188
  return maxContextWindow;
1182
1189
  }
1183
- function readUsageTotalTokens(usage) {
1184
- if (!usage || typeof usage !== "object") {
1190
+ function resolveInitialContextWindowSize(modelId) {
1191
+ const normalized = typeof modelId === "string" ? modelId.trim().toLowerCase() : "";
1192
+ if (!normalized) {
1185
1193
  return undefined;
1186
1194
  }
1187
- const totalTokens = usage.total_tokens;
1188
- if (typeof totalTokens !== "number" || !Number.isFinite(totalTokens) || totalTokens < 0) {
1189
- return undefined;
1195
+ if (normalized.includes("[1m]") || normalized.includes("context-1m")) {
1196
+ return 1000000;
1190
1197
  }
1191
- return totalTokens;
1192
- }
1193
- function readContextWindowUsedTokensFromTaskProgress(message) {
1194
- return readUsageTotalTokens(message.usage);
1195
- }
1196
- function readUsageFromTaskNotification(message) {
1197
- return readUsageTotalTokens(message.usage);
1198
+ if (normalized.includes("claude-fable-5")) {
1199
+ return 1000000;
1200
+ }
1201
+ if (/(?:^|[~/_-])(?:claude[-_ ]*)?(opus|sonnet|haiku)(?:$|[-_ ./])/.test(normalized)) {
1202
+ return 200000;
1203
+ }
1204
+ return undefined;
1198
1205
  }
1199
1206
  function readStreamRequestInputTokens(event) {
1200
1207
  const messageUsage = toObjectRecord(toObjectRecord(event.message)?.usage);
@@ -1225,6 +1232,172 @@ function readStreamRequestOutputTokens(event) {
1225
1232
  }
1226
1233
  return outputTokens;
1227
1234
  }
1235
+ function readLastUsageIteration(usage) {
1236
+ const iterations = toObjectRecord(usage)?.iterations;
1237
+ if (!Array.isArray(iterations)) {
1238
+ return undefined;
1239
+ }
1240
+ for (let index = iterations.length - 1; index >= 0; index -= 1) {
1241
+ const candidate = toObjectRecord(iterations[index]);
1242
+ if (candidate) {
1243
+ return candidate;
1244
+ }
1245
+ }
1246
+ return undefined;
1247
+ }
1248
+ function readUsageTokenTotal(usage) {
1249
+ const usageWithCacheCreation = usage;
1250
+ const inputTokens = typeof usage.input_tokens === "number" && Number.isFinite(usage.input_tokens)
1251
+ ? usage.input_tokens
1252
+ : 0;
1253
+ const cacheCreationInputTokens = typeof usageWithCacheCreation.cache_creation_input_tokens === "number" &&
1254
+ Number.isFinite(usageWithCacheCreation.cache_creation_input_tokens)
1255
+ ? usageWithCacheCreation.cache_creation_input_tokens
1256
+ : 0;
1257
+ const cacheReadInputTokens = typeof usage.cache_read_input_tokens === "number" &&
1258
+ Number.isFinite(usage.cache_read_input_tokens)
1259
+ ? usage.cache_read_input_tokens
1260
+ : 0;
1261
+ const outputTokens = typeof usage.output_tokens === "number" && Number.isFinite(usage.output_tokens)
1262
+ ? usage.output_tokens
1263
+ : 0;
1264
+ const total = inputTokens + cacheCreationInputTokens + cacheReadInputTokens + outputTokens;
1265
+ return total > 0 ? total : undefined;
1266
+ }
1267
+ function readActiveUsageTokens(usage) {
1268
+ const activeUsage = readLastUsageIteration(usage);
1269
+ return activeUsage ? readUsageTokenTotal(activeUsage) : undefined;
1270
+ }
1271
+ function readLegacyResultUsageTokens(usage) {
1272
+ const usageRecord = toObjectRecord(usage);
1273
+ return usageRecord ? readUsageTokenTotal(usageRecord) : undefined;
1274
+ }
1275
+ function readCurrentContextUsage(value) {
1276
+ const record = toObjectRecord(value);
1277
+ if (!record) {
1278
+ return undefined;
1279
+ }
1280
+ const totalTokens = record.totalTokens;
1281
+ if (typeof totalTokens !== "number" || !Number.isFinite(totalTokens) || totalTokens < 0) {
1282
+ return undefined;
1283
+ }
1284
+ const maxTokens = record.maxTokens;
1285
+ return {
1286
+ totalTokens,
1287
+ ...(typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0
1288
+ ? { maxTokens }
1289
+ : {}),
1290
+ };
1291
+ }
1292
+ function isClaudeSubagentToolName(name) {
1293
+ return name === "Task" || name === "Agent";
1294
+ }
1295
+ class ClaudeContextUsageState {
1296
+ constructor(initialContextWindowMaxTokens) {
1297
+ this.completedResultTurns = 0;
1298
+ this.contextWindowMaxTokens = initialContextWindowMaxTokens;
1299
+ }
1300
+ beginTurn() {
1301
+ this.streamRequestInputTokens = undefined;
1302
+ this.streamRequestOutputTokens = undefined;
1303
+ }
1304
+ setInitialContextWindowMaxTokens(contextWindowMaxTokens) {
1305
+ this.contextWindowMaxTokens = contextWindowMaxTokens;
1306
+ }
1307
+ recordModelUsage(modelUsage) {
1308
+ const contextWindowMaxTokens = extractContextWindowSize(modelUsage);
1309
+ if (contextWindowMaxTokens !== undefined) {
1310
+ this.contextWindowMaxTokens = contextWindowMaxTokens;
1311
+ }
1312
+ return this.contextWindowMaxTokens;
1313
+ }
1314
+ recordCurrentContextUsage(usage) {
1315
+ if (usage?.maxTokens !== undefined) {
1316
+ this.contextWindowMaxTokens = usage.maxTokens;
1317
+ }
1318
+ }
1319
+ buildStreamUsageEvent(event) {
1320
+ const streamEvent = toObjectRecord(event);
1321
+ if (!streamEvent) {
1322
+ return null;
1323
+ }
1324
+ const eventType = readTrimmedString(streamEvent.type);
1325
+ if (eventType === "message_start") {
1326
+ const inputTokens = readStreamRequestInputTokens(streamEvent);
1327
+ if (typeof inputTokens !== "number") {
1328
+ return null;
1329
+ }
1330
+ this.streamRequestInputTokens = inputTokens;
1331
+ this.streamRequestOutputTokens = 0;
1332
+ }
1333
+ else if (eventType === "message_delta") {
1334
+ const outputTokens = readStreamRequestOutputTokens(streamEvent);
1335
+ if (typeof outputTokens !== "number") {
1336
+ return null;
1337
+ }
1338
+ this.streamRequestOutputTokens = outputTokens;
1339
+ }
1340
+ else {
1341
+ return null;
1342
+ }
1343
+ const usedTokens = this.streamUsedTokens();
1344
+ if (usedTokens === undefined) {
1345
+ return null;
1346
+ }
1347
+ return this.createUsageUpdatedEvent(usedTokens);
1348
+ }
1349
+ buildResultUsage(message, modelUsage, currentContextUsage) {
1350
+ try {
1351
+ if (!message.usage) {
1352
+ return undefined;
1353
+ }
1354
+ const usage = {
1355
+ inputTokens: message.usage.input_tokens,
1356
+ cachedInputTokens: message.usage.cache_read_input_tokens,
1357
+ outputTokens: message.usage.output_tokens,
1358
+ totalCostUsd: message.total_cost_usd,
1359
+ };
1360
+ const modelContextWindowMaxTokens = this.recordModelUsage(modelUsage ?? message.modelUsage);
1361
+ this.recordCurrentContextUsage(currentContextUsage);
1362
+ if (this.contextWindowMaxTokens !== undefined) {
1363
+ usage.contextWindowMaxTokens = this.contextWindowMaxTokens;
1364
+ }
1365
+ else if (modelContextWindowMaxTokens !== undefined) {
1366
+ usage.contextWindowMaxTokens = modelContextWindowMaxTokens;
1367
+ }
1368
+ const activeResultUsageTokens = readActiveUsageTokens(message.usage) ??
1369
+ (this.completedResultTurns === 0 ? readLegacyResultUsageTokens(message.usage) : undefined);
1370
+ const usedTokens = currentContextUsage?.totalTokens ?? this.streamUsedTokens() ?? activeResultUsageTokens;
1371
+ if (usedTokens !== undefined) {
1372
+ usage.contextWindowUsedTokens = usedTokens;
1373
+ }
1374
+ return usage;
1375
+ }
1376
+ finally {
1377
+ this.completedResultTurns += 1;
1378
+ }
1379
+ }
1380
+ streamUsedTokens() {
1381
+ if (typeof this.streamRequestInputTokens !== "number" ||
1382
+ typeof this.streamRequestOutputTokens !== "number") {
1383
+ return undefined;
1384
+ }
1385
+ return this.streamRequestInputTokens + this.streamRequestOutputTokens;
1386
+ }
1387
+ createUsageUpdatedEvent(contextWindowUsedTokens) {
1388
+ const usage = {
1389
+ contextWindowUsedTokens,
1390
+ };
1391
+ if (this.contextWindowMaxTokens !== undefined) {
1392
+ usage.contextWindowMaxTokens = this.contextWindowMaxTokens;
1393
+ }
1394
+ return {
1395
+ type: "usage_updated",
1396
+ provider: "claude",
1397
+ usage,
1398
+ };
1399
+ }
1400
+ }
1228
1401
  class ClaudeAgentSession {
1229
1402
  constructor(config, options) {
1230
1403
  this.provider = "claude";
@@ -1344,6 +1517,7 @@ class ClaudeAgentSession {
1344
1517
  this.logger = options.logger.child({ agentId: this.agentId });
1345
1518
  this.queryFactory = options.queryFactory;
1346
1519
  this.resolveBinary = options.resolveBinary;
1520
+ this.contextUsage = new ClaudeContextUsageState(resolveInitialContextWindowSize(this.config.model));
1347
1521
  const handle = options.handle;
1348
1522
  if (handle) {
1349
1523
  if (!handle.sessionId) {
@@ -1441,6 +1615,7 @@ class ClaudeAgentSession {
1441
1615
  this.activeForegroundTurnId = turnId;
1442
1616
  this.foregroundHasVisibleActivity = false;
1443
1617
  this.activeTurnHasAssistantText = false;
1618
+ this.contextUsage.beginTurn();
1444
1619
  this.transitionTurnState("foreground", "foreground turn started");
1445
1620
  this.clearRecentStderr();
1446
1621
  let cancelIssued = false;
@@ -1550,6 +1725,7 @@ class ClaudeAgentSession {
1550
1725
  if (!claudeModelSupportsFastMode(this.config.model) && this.config.featureValues?.fast_mode) {
1551
1726
  await this.applyFastModeFeature(false, activeQuery);
1552
1727
  }
1728
+ this.contextUsage.setInitialContextWindowMaxTokens(resolveInitialContextWindowSize(this.config.model));
1553
1729
  this.lastOptionsModel = normalizedModelId ?? this.lastOptionsModel;
1554
1730
  this.lastRuntimeModel = null;
1555
1731
  this.cachedRuntimeInfo = null;
@@ -1563,13 +1739,16 @@ class ClaudeAgentSession {
1563
1739
  if (!normalizedThinkingOptionId || normalizedThinkingOptionId === "default") {
1564
1740
  this.config.thinkingOptionId = undefined;
1565
1741
  }
1566
- else if (isClaudeThinkingEffort(normalizedThinkingOptionId)) {
1742
+ else if (isClaudeThinkingOption(normalizedThinkingOptionId)) {
1567
1743
  this.config.thinkingOptionId = normalizedThinkingOptionId;
1568
1744
  }
1569
1745
  else {
1570
1746
  throw new Error(`Unknown thinking option: ${normalizedThinkingOptionId}`);
1571
1747
  }
1572
1748
  this.queryRestartNeeded = true;
1749
+ if (this.activeForegroundTurnId || this.autonomousTurn) {
1750
+ return SETTING_APPLIES_NEXT_TURN_NOTICE;
1751
+ }
1573
1752
  }
1574
1753
  async setFeature(featureId, value) {
1575
1754
  if (featureId !== "fast_mode") {
@@ -2096,10 +2275,13 @@ class ClaudeAgentSession {
2096
2275
  const thinkingOptionId = this.config.thinkingOptionId && this.config.thinkingOptionId !== "default"
2097
2276
  ? this.config.thinkingOptionId
2098
2277
  : undefined;
2278
+ if (thinkingOptionId === "ultracode") {
2279
+ return { thinking: { type: "adaptive" }, effort: "xhigh", ultracode: true };
2280
+ }
2099
2281
  if (thinkingOptionId && isClaudeThinkingEffort(thinkingOptionId)) {
2100
- return { thinking: { type: "adaptive" }, effort: thinkingOptionId };
2282
+ return { thinking: { type: "adaptive" }, effort: thinkingOptionId, ultracode: false };
2101
2283
  }
2102
- return { thinking: undefined, effort: undefined };
2284
+ return { thinking: undefined, effort: undefined, ultracode: false };
2103
2285
  }
2104
2286
  buildAppendedSystemPrompt() {
2105
2287
  return (composeSystemPromptParts(this.config.systemPrompt, this.config.daemonAppendSystemPrompt) ?? "");
@@ -2120,10 +2302,10 @@ class ClaudeAgentSession {
2120
2302
  });
2121
2303
  }
2122
2304
  async buildOptions() {
2123
- const { thinking, effort } = this.resolveThinkingConfig();
2305
+ const { thinking, effort, ultracode } = this.resolveThinkingConfig();
2124
2306
  const appendedSystemPrompt = this.buildAppendedSystemPrompt();
2125
2307
  const extraClaudeOptions = this.config.extra?.claude;
2126
- const fastModeOptions = this.buildFastModeOptions(extraClaudeOptions);
2308
+ const settingsOptions = this.buildSettingsOptions(extraClaudeOptions, { ultracode });
2127
2309
  const sdkEnv = this.buildSdkEnv(extraClaudeOptions);
2128
2310
  assertClaudeAutoModeEligible(this.currentMode, sdkEnv);
2129
2311
  const claudeBinary = await this.resolveBinary();
@@ -2172,7 +2354,7 @@ class ClaudeAgentSession {
2172
2354
  ...(thinking ? { thinking } : {}),
2173
2355
  ...(effort ? { effort } : {}),
2174
2356
  ...extraClaudeOptions,
2175
- ...fastModeOptions,
2357
+ ...settingsOptions,
2176
2358
  ...(this.persistSession === undefined ? {} : { persistSession: this.persistSession }),
2177
2359
  env: sdkEnv,
2178
2360
  };
@@ -2194,12 +2376,17 @@ class ClaudeAgentSession {
2194
2376
  }
2195
2377
  return base;
2196
2378
  }
2197
- buildFastModeOptions(extraClaudeOptions) {
2379
+ buildSettingsOptions(extraClaudeOptions, input) {
2198
2380
  const fastMode = this.resolveFastModeSetting();
2199
- if (fastMode === null) {
2381
+ if (fastMode === null && !input.ultracode) {
2200
2382
  return {};
2201
2383
  }
2202
- return { settings: mergeClaudeSettings(extraClaudeOptions?.settings, { fastMode }) };
2384
+ return {
2385
+ settings: mergeClaudeSettings(extraClaudeOptions?.settings, {
2386
+ ...(fastMode === null ? {} : { fastMode }),
2387
+ ...(input.ultracode ? { ultracode: true } : {}),
2388
+ }),
2389
+ };
2203
2390
  }
2204
2391
  resolveFastModeSetting() {
2205
2392
  if (!claudeModelSupportsFastMode(this.config.model)) {
@@ -2413,6 +2600,7 @@ class ClaudeAgentSession {
2413
2600
  id: this.createTurnId("autonomous"),
2414
2601
  };
2415
2602
  this.activeTurnHasAssistantText = false;
2603
+ this.contextUsage.beginTurn();
2416
2604
  this.notifySubscribers({ type: "turn_started", provider: "claude" });
2417
2605
  this.syncTurnState("autonomous turn started");
2418
2606
  }
@@ -2491,7 +2679,7 @@ class ClaudeAgentSession {
2491
2679
  if (await this.handleMissingResumedConversation(message, activeQuery)) {
2492
2680
  return true;
2493
2681
  }
2494
- this.routeSdkMessageFromPump(message);
2682
+ await this.routeSdkMessageFromPump(message, activeQuery);
2495
2683
  return false;
2496
2684
  };
2497
2685
  const drainActiveQuery = async () => {
@@ -2559,7 +2747,7 @@ class ClaudeAgentSession {
2559
2747
  message.type === "tool_progress" ||
2560
2748
  (message.type === "system" && message.subtype === "task_notification"));
2561
2749
  }
2562
- routeSdkMessageFromPump(message) {
2750
+ async routeSdkMessageFromPump(message, activeQuery) {
2563
2751
  if (this.shouldSuppressStaleResult(message)) {
2564
2752
  return;
2565
2753
  }
@@ -2582,22 +2770,7 @@ class ClaudeAgentSession {
2582
2770
  identifiers,
2583
2771
  rawEvent: message,
2584
2772
  }, "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];
2773
+ const events = await this.buildPumpedMessageEvents(message, activeQuery, identifiers.messageId, turnId);
2601
2774
  if (events.length === 0) {
2602
2775
  return;
2603
2776
  }
@@ -2620,6 +2793,38 @@ class ClaudeAgentSession {
2620
2793
  }
2621
2794
  this.dispatchEvents(events);
2622
2795
  }
2796
+ async buildPumpedMessageEvents(message, activeQuery, messageIdHint, turnId) {
2797
+ const currentContextUsage = message.type === "result" && message.subtype === "success"
2798
+ ? await this.queryCurrentContextUsage(activeQuery)
2799
+ : undefined;
2800
+ const messageEvents = this.translateMessageToEvents(message, {
2801
+ suppressAssistantText: true,
2802
+ suppressReasoning: true,
2803
+ currentContextUsage,
2804
+ });
2805
+ const assistantTimelineEvents = this.timelineAssembler
2806
+ .consume({
2807
+ message,
2808
+ runId: turnId,
2809
+ messageIdHint,
2810
+ })
2811
+ .map((item) => ({
2812
+ type: "timeline",
2813
+ item,
2814
+ provider: "claude",
2815
+ }));
2816
+ return [...messageEvents, ...assistantTimelineEvents];
2817
+ }
2818
+ async queryCurrentContextUsage(activeQuery) {
2819
+ try {
2820
+ const usage = await withTimeout(activeQuery.getContextUsage(), 3000, "timeout");
2821
+ return readCurrentContextUsage(usage);
2822
+ }
2823
+ catch (error) {
2824
+ this.logger.debug({ err: error }, "Claude context usage query failed");
2825
+ return undefined;
2826
+ }
2827
+ }
2623
2828
  async handleMissingResumedConversation(message, activeQuery) {
2624
2829
  const staleResumeError = this.readMissingResumedConversationError(message);
2625
2830
  if (!staleResumeError) {
@@ -2710,7 +2915,9 @@ class ClaudeAgentSession {
2710
2915
  this.appendStreamEventEvents(message, events, options);
2711
2916
  break;
2712
2917
  case "result":
2713
- this.appendResultEvents(message, events);
2918
+ this.appendResultEvents(message, events, {
2919
+ currentContextUsage: options?.currentContextUsage,
2920
+ });
2714
2921
  break;
2715
2922
  default:
2716
2923
  break;
@@ -2783,22 +2990,18 @@ class ClaudeAgentSession {
2783
2990
  return;
2784
2991
  }
2785
2992
  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
- }
2993
+ return;
2791
2994
  }
2792
2995
  }
2793
2996
  appendTaskNotificationEvents(message, events) {
2794
2997
  // TODO: subagent timelines are best-effort. Subagent task_notifications
2795
2998
  // 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
2999
+ // the parent's subagent tool call, so they slip past the sidechain router and pollute
2797
3000
  // the parent timeline. Drop them here; eventually thread them into the
2798
- // parent Task tool call's sub_agent log instead.
3001
+ // parent tool call's sub_agent log instead.
2799
3002
  const taskUseId = message.tool_use_id;
2800
3003
  const cachedTool = taskUseId ? this.toolUseCache.get(taskUseId) : undefined;
2801
- if (cachedTool?.name === "Task") {
3004
+ if (isClaudeSubagentToolName(cachedTool?.name)) {
2802
3005
  return;
2803
3006
  }
2804
3007
  const taskNotificationItem = mapTaskNotificationSystemRecordToToolCall(message);
@@ -2809,11 +3012,6 @@ class ClaudeAgentSession {
2809
3012
  provider: "claude",
2810
3013
  });
2811
3014
  }
2812
- const usage = readUsageFromTaskNotification(message);
2813
- if (typeof usage === "number") {
2814
- this.lastContextWindowUsedTokens = usage;
2815
- events.push(this.createUsageUpdatedEvent(usage));
2816
- }
2817
3015
  }
2818
3016
  appendUserMessageEvents(message, events) {
2819
3017
  if (isSyntheticUserEntry(message)) {
@@ -2877,7 +3075,7 @@ class ClaudeAgentSession {
2877
3075
  }
2878
3076
  }
2879
3077
  appendStreamEventEvents(message, events, options) {
2880
- const usageUpdatedEvent = this.trackStreamEventUsage(message.event);
3078
+ const usageUpdatedEvent = this.contextUsage.buildStreamUsageEvent(message.event);
2881
3079
  if (usageUpdatedEvent) {
2882
3080
  events.push(usageUpdatedEvent);
2883
3081
  }
@@ -2889,8 +3087,8 @@ class ClaudeAgentSession {
2889
3087
  events.push({ type: "timeline", item, provider: "claude" });
2890
3088
  }
2891
3089
  }
2892
- appendResultEvents(message, events) {
2893
- const usage = this.convertUsage(message, message.modelUsage);
3090
+ appendResultEvents(message, events, options) {
3091
+ const usage = this.convertUsage(message, message.modelUsage, options?.currentContextUsage);
2894
3092
  if (message.subtype === "success") {
2895
3093
  // Built-in slash commands (e.g. /voice, /usage, "Unknown command: …")
2896
3094
  // run client-side in the Claude CLI with no model turn — output_tokens
@@ -3034,92 +3232,8 @@ class ClaudeAgentSession {
3034
3232
  }
3035
3233
  return null;
3036
3234
  }
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);
3235
+ convertUsage(message, modelUsage, currentContextUsage) {
3236
+ return this.contextUsage.buildResultUsage(message, modelUsage, currentContextUsage);
3123
3237
  }
3124
3238
  enqueueTimeline(item) {
3125
3239
  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",
@@ -1,4 +1,4 @@
1
- import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPersistenceHandle, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPermissionResult, type AgentPromptContentBlock, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentTimelineItem, type ToolCallTimelineItem, type AgentUsage, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ListModelsOptions } from "../agent-sdk-types.js";
1
+ import { type AgentCapabilityFlags, type AgentClient, type AgentCreateSessionOptions, type AgentFeature, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPersistenceHandle, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPermissionResult, type AgentProviderNotice, type AgentPromptContentBlock, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type AgentTimelineItem, type ToolCallTimelineItem, type AgentUsage, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ListModelsOptions } from "../agent-sdk-types.js";
2
2
  import type { Logger } from "pino";
3
3
  import type { ChildProcessWithoutNullStreams } from "node:child_process";
4
4
  import { type ProviderRuntimeSettings } from "../provider-launch-config.js";
@@ -196,9 +196,9 @@ export declare class CodexAppServerAgentSession implements AgentSession {
196
196
  getRuntimeInfo(): Promise<AgentRuntimeInfo>;
197
197
  getAvailableModes(): Promise<AgentMode[]>;
198
198
  getCurrentMode(): Promise<string | null>;
199
- setMode(modeId: string): Promise<void>;
199
+ setMode(modeId: string): Promise<void | AgentProviderNotice>;
200
200
  setModel(modelId: string | null): Promise<void>;
201
- setThinkingOption(thinkingOptionId: string | null): Promise<void>;
201
+ setThinkingOption(thinkingOptionId: string | null): Promise<void | AgentProviderNotice>;
202
202
  setFeature(featureId: string, value: unknown): Promise<void>;
203
203
  getPendingPermissions(): AgentPermissionRequest[];
204
204
  respondToPermission(requestId: string, response: AgentPermissionResponse): Promise<AgentPermissionResult | void>;
@@ -297,6 +297,7 @@ export declare class CodexAppServerAgentClient implements AgentClient {
297
297
  importSession(input: ImportProviderSessionInput, context: ImportProviderSessionContext): Promise<import("../agent-sdk-types.js").ImportedProviderSession>;
298
298
  listModels(_options: ListModelsOptions): Promise<AgentModelDefinition[]>;
299
299
  archiveNativeSession(handle: AgentPersistenceHandle): Promise<void>;
300
+ unarchiveNativeSession(handle: AgentPersistenceHandle): Promise<void>;
300
301
  isAvailable(): Promise<boolean>;
301
302
  getDiagnostic(): Promise<{
302
303
  diagnostic: string;