iosm-cli 0.2.8 → 0.2.10

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 (100) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +3 -3
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +7 -3
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/core/agent-profiles.d.ts.map +1 -1
  7. package/dist/core/agent-profiles.js +5 -1
  8. package/dist/core/agent-profiles.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +8 -0
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +490 -3
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/sdk.d.ts +2 -2
  14. package/dist/core/sdk.d.ts.map +1 -1
  15. package/dist/core/sdk.js +7 -4
  16. package/dist/core/sdk.js.map +1 -1
  17. package/dist/core/settings-manager.d.ts +18 -0
  18. package/dist/core/settings-manager.d.ts.map +1 -1
  19. package/dist/core/settings-manager.js +29 -0
  20. package/dist/core/settings-manager.js.map +1 -1
  21. package/dist/core/shadow-guard.d.ts.map +1 -1
  22. package/dist/core/shadow-guard.js +12 -1
  23. package/dist/core/shadow-guard.js.map +1 -1
  24. package/dist/core/slash-commands.d.ts.map +1 -1
  25. package/dist/core/slash-commands.js +4 -0
  26. package/dist/core/slash-commands.js.map +1 -1
  27. package/dist/core/system-prompt.d.ts.map +1 -1
  28. package/dist/core/system-prompt.js +32 -1
  29. package/dist/core/system-prompt.js.map +1 -1
  30. package/dist/core/tools/db-run.d.ts +84 -0
  31. package/dist/core/tools/db-run.d.ts.map +1 -0
  32. package/dist/core/tools/db-run.js +690 -0
  33. package/dist/core/tools/db-run.js.map +1 -0
  34. package/dist/core/tools/index.d.ts +44 -0
  35. package/dist/core/tools/index.d.ts.map +1 -1
  36. package/dist/core/tools/index.js +16 -0
  37. package/dist/core/tools/index.js.map +1 -1
  38. package/dist/core/tools/lint-run.d.ts +42 -0
  39. package/dist/core/tools/lint-run.d.ts.map +1 -0
  40. package/dist/core/tools/lint-run.js +276 -0
  41. package/dist/core/tools/lint-run.js.map +1 -0
  42. package/dist/core/tools/test-run.d.ts +36 -0
  43. package/dist/core/tools/test-run.d.ts.map +1 -0
  44. package/dist/core/tools/test-run.js +255 -0
  45. package/dist/core/tools/test-run.js.map +1 -0
  46. package/dist/core/tools/typecheck-run.d.ts +44 -0
  47. package/dist/core/tools/typecheck-run.d.ts.map +1 -0
  48. package/dist/core/tools/typecheck-run.js +343 -0
  49. package/dist/core/tools/typecheck-run.js.map +1 -0
  50. package/dist/core/tools/verification-runner.d.ts +53 -0
  51. package/dist/core/tools/verification-runner.d.ts.map +1 -0
  52. package/dist/core/tools/verification-runner.js +235 -0
  53. package/dist/core/tools/verification-runner.js.map +1 -0
  54. package/dist/core/ultrathink.d.ts +122 -0
  55. package/dist/core/ultrathink.d.ts.map +1 -0
  56. package/dist/core/ultrathink.js +621 -0
  57. package/dist/core/ultrathink.js.map +1 -0
  58. package/dist/index.d.ts +2 -2
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +1 -1
  61. package/dist/index.js.map +1 -1
  62. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  63. package/dist/modes/interactive/components/branch-summary-message.js +2 -1
  64. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  65. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  66. package/dist/modes/interactive/components/compaction-summary-message.js +2 -1
  67. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  68. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/custom-message.js +2 -1
  70. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  71. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  72. package/dist/modes/interactive/components/skill-invocation-message.js +4 -2
  73. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  74. package/dist/modes/interactive/components/subagent-message.d.ts.map +1 -1
  75. package/dist/modes/interactive/components/subagent-message.js +3 -1
  76. package/dist/modes/interactive/components/subagent-message.js.map +1 -1
  77. package/dist/modes/interactive/components/task-plan-message.d.ts.map +1 -1
  78. package/dist/modes/interactive/components/task-plan-message.js +2 -1
  79. package/dist/modes/interactive/components/task-plan-message.js.map +1 -1
  80. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  81. package/dist/modes/interactive/components/tool-execution.js +25 -7
  82. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  83. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  84. package/dist/modes/interactive/components/user-message.js +2 -1
  85. package/dist/modes/interactive/components/user-message.js.map +1 -1
  86. package/dist/modes/interactive/interactive-mode.d.ts +5 -0
  87. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  88. package/dist/modes/interactive/interactive-mode.js +523 -9
  89. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  90. package/dist/modes/interactive/theme/dark.json +39 -38
  91. package/dist/modes/interactive/theme/light.json +29 -29
  92. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  93. package/dist/modes/interactive/theme/theme.js +16 -25
  94. package/dist/modes/interactive/theme/theme.js.map +1 -1
  95. package/dist/modes/interactive/theme/universal.json +85 -0
  96. package/docs/cli-reference.md +21 -1
  97. package/docs/configuration.md +76 -1
  98. package/docs/development-and-testing.md +1 -1
  99. package/docs/interactive-mode.md +11 -2
  100. package/package.json +1 -1
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
16
16
  import { basename, dirname, join } from "node:path";
17
- import { isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "@mariozechner/pi-ai";
17
+ import { completeSimple, isContextOverflow, modelsAreEqual, resetApiProviders, supportsXhigh } from "@mariozechner/pi-ai";
18
18
  import { getDocsPath, getSessionTracePath, isSessionTraceEnabled } from "../config.js";
19
19
  import { buildIosmRuntimeDirective, prepareIosmRuntimeContext } from "../iosm/runtime-context.js";
20
20
  import { theme } from "../modes/interactive/theme/theme.js";
@@ -35,6 +35,7 @@ import { isReadOnlyProfileName } from "./agent-profiles.js";
35
35
  import { applyPostToolUseHooks, applyPreToolUseHooks, applyStopHooks, applyUserPromptSubmitHooks, emptyHooksConfig, } from "./hooks.js";
36
36
  import { extractTaskPlanFromAssistantMessage, formatTaskPlanMessageContent, taskPlanSignature, TASK_PLAN_CUSTOM_TYPE, } from "./task-plan.js";
37
37
  import { createAllTools, getAllowedFetchMethodsForProfile } from "./tools/index.js";
38
+ import { ULTRATHINK_CHECKPOINT_COMPRESSION_SYSTEM_PROMPT, ULTRATHINK_MAX_CHECKPOINT_CHARS, ULTRATHINK_MAX_ITERATION_INPUT_TOKENS, ULTRATHINK_MAX_RUN_COST, ULTRATHINK_MAX_RUN_INPUT_TOKENS, ULTRATHINK_MAX_RUN_TOTAL_TOKENS, ULTRATHINK_STAGNATION_LIMIT, ULTRATHINK_VISIBLE_PROMPT_PREFIX, buildUltrathinkCheckpointCompressionPrompt, buildUltrathinkBudgetStatusLine, buildUltrathinkComplianceRepairPrompt, buildUltrathinkContextTail, buildUltrathinkEvidenceCatalog, buildUltrathinkIterationPrompt, buildUltrathinkToolGroundingPrompt, buildUltrathinkVisibleIterationPrompt, createInitialUltrathinkCheckpoint, evaluateUltrathinkEvidencePolicy, extractUltrathinkCheckpoint, extractUltrathinkIterationSummary, extractUltrathinkToolEvidence, findLastMeaningfulUserIntent, getUltrathinkPhase, hasUltrathinkEvidenceViolations, isUltrathinkStagnated, normalizeUltrathinkCheckpoint, parseUltrathinkCommand, resolveUltrathinkReadOnlyTools, shouldUltrathinkForceToolGrounding, truncateUltrathinkCheckpoint, ULTRATHINK_USAGE, } from "./ultrathink.js";
38
39
  /**
39
40
  * Parse a skill block from message text.
40
41
  * Returns null if the text doesn't contain a skill block.
@@ -90,6 +91,108 @@ function deriveOrchestrationDisplayText(promptTextWithoutDirective) {
90
91
  }
91
92
  return task;
92
93
  }
94
+ function extractAssistantProtocolText(message) {
95
+ const parts = [];
96
+ for (const content of message.content) {
97
+ if (content.type === "text" && typeof content.text === "string" && content.text.trim()) {
98
+ parts.push(content.text.trim());
99
+ continue;
100
+ }
101
+ if (content.type !== "thinking")
102
+ continue;
103
+ const record = content;
104
+ const thinking = record.thinking;
105
+ if (typeof thinking === "string" && thinking.trim()) {
106
+ parts.push(thinking.trim());
107
+ }
108
+ }
109
+ return parts.join("\n\n").trim();
110
+ }
111
+ function extractAssistantVisibleText(message) {
112
+ return message.content
113
+ .filter((content) => content.type === "text")
114
+ .map((content) => content.text.trim())
115
+ .filter((text) => text.length > 0)
116
+ .join("\n\n")
117
+ .trim();
118
+ }
119
+ function isNonActionableVisibleAssistantText(text) {
120
+ const trimmed = text.trim();
121
+ if (trimmed.length === 0)
122
+ return true;
123
+ return /^\[\s*output\s+truncated[^\]]*\]?\s*\.?$/i.test(trimmed);
124
+ }
125
+ function detectPromptProtocolIssue(text) {
126
+ const trimmed = text.trim();
127
+ if (!trimmed)
128
+ return undefined;
129
+ const hasToolCallOpenBlock = /(^|\n)\s*<\s*tool_call\b/i.test(trimmed);
130
+ const hasToolCallCloseTag = /<\/\s*tool_call\s*>/i.test(trimmed);
131
+ const hasFunctionBlock = /(^|\n)\s*<\s*function\s*=\s*[A-Za-z0-9._:-]+/i.test(trimmed);
132
+ const hasParameterBlock = /(^|\n)\s*<\s*parameter\s*=\s*[A-Za-z0-9._:-]+\s*>/i.test(trimmed);
133
+ // Flag only executable-looking pseudo markup; don't treat inline explanatory mentions as protocol issues.
134
+ const hasToolCallMarkup = (hasToolCallOpenBlock && (hasToolCallCloseTag || hasFunctionBlock || hasParameterBlock)) ||
135
+ (hasFunctionBlock && hasParameterBlock);
136
+ const hasDelegateTaskMarkup = /<\s*delegate_task\b/i.test(trimmed) && /<\/\s*delegate_task\s*>/i.test(trimmed);
137
+ if (!hasToolCallMarkup && !hasDelegateTaskMarkup)
138
+ return undefined;
139
+ return {
140
+ hasToolCallMarkup,
141
+ hasDelegateTaskMarkup,
142
+ };
143
+ }
144
+ function buildPromptProtocolCorrectionPrompt(input) {
145
+ const reasons = [
146
+ input.issue.hasToolCallMarkup ? "raw <tool_call>/<function=...> markup" : undefined,
147
+ input.issue.hasDelegateTaskMarkup ? "raw <delegate_task> blocks" : undefined,
148
+ ].filter((item) => typeof item === "string");
149
+ const boundedOriginal = input.originalPrompt.length > 2_000
150
+ ? `${input.originalPrompt.slice(0, 2_000).trimEnd()}...`
151
+ : input.originalPrompt;
152
+ return [
153
+ "[TOOL_PROTOCOL_CORRECTION]",
154
+ `Previous assistant output included ${reasons.join(" and ")} in plain text.`,
155
+ "These XML-like blocks are not executable tool calls.",
156
+ "Retry now and follow this protocol exactly:",
157
+ "1) Do not output XML/pseudo-call tags (<tool_call>, <function=...>, <delegate_task>).",
158
+ "2) If a tool is needed, emit real structured tool calls only.",
159
+ "3) Prefer structured tools when available instead of ad-hoc pseudo calls.",
160
+ "4) If no tool is needed, provide a direct normal answer.",
161
+ input.hasPriorToolActivity
162
+ ? "5) Continue from the current in-memory state; avoid repeating already completed tool steps unless necessary."
163
+ : undefined,
164
+ "Execute the original user request now.",
165
+ "<original_user_request>",
166
+ boundedOriginal,
167
+ "</original_user_request>",
168
+ "[/TOOL_PROTOCOL_CORRECTION]",
169
+ ]
170
+ .filter((line) => typeof line === "string")
171
+ .join("\n");
172
+ }
173
+ function buildPromptSilentStopRecoveryPrompt(input) {
174
+ const boundedOriginal = input.originalPrompt.length > 2_000
175
+ ? `${input.originalPrompt.slice(0, 2_000).trimEnd()}...`
176
+ : input.originalPrompt;
177
+ return [
178
+ "[ASSISTANT_STALL_RECOVERY]",
179
+ "Previous assistant output ended with stop but produced no visible text and no executable tool calls.",
180
+ "Retry now and continue the same request.",
181
+ "1) Do not return an empty response.",
182
+ "2) If a tool is needed, emit real structured tool calls.",
183
+ "3) If no tool is needed, provide a direct answer.",
184
+ input.hasPriorToolActivity
185
+ ? "4) Continue from the current in-memory state; avoid repeating already completed tool steps unless necessary."
186
+ : undefined,
187
+ "Execute the original user request now.",
188
+ "<original_user_request>",
189
+ boundedOriginal,
190
+ "</original_user_request>",
191
+ "[/ASSISTANT_STALL_RECOVERY]",
192
+ ]
193
+ .filter((line) => typeof line === "string")
194
+ .join("\n");
195
+ }
93
196
  function buildSubagentOrchestrationDirective(text) {
94
197
  const block = parseOrchestrateBlock(text);
95
198
  if (block) {
@@ -147,6 +250,7 @@ function buildMetaProfileOrchestrationDirective(text) {
147
250
  // ============================================================================
148
251
  /** Standard thinking levels */
149
252
  const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
253
+ const MAX_PROMPT_PROTOCOL_AUTO_REPAIR_ATTEMPTS = 2;
150
254
  /** Thinking levels including xhigh (for supported models) */
151
255
  const THINKING_LEVELS_WITH_XHIGH = ["off", "minimal", "low", "medium", "high", "xhigh"];
152
256
  // ============================================================================
@@ -195,6 +299,8 @@ export class AgentSession {
195
299
  this._hooksConfig = emptyHooksConfig();
196
300
  this._pendingHookNotices = [];
197
301
  this._sessionTraceEnabled = isSessionTraceEnabled();
302
+ this._protocolAutoRepairActive = false;
303
+ this._ultrathinkActive = false;
198
304
  // Track last assistant message for auto-compaction check
199
305
  this._lastAssistantMessage = undefined;
200
306
  this._lastTaskPlanSignature = undefined;
@@ -962,6 +1068,22 @@ export class AgentSession {
962
1068
  source: inputSource,
963
1069
  imageCount: options?.images?.length ?? 0,
964
1070
  });
1071
+ if (!options?.skipUltrathinkCommand) {
1072
+ const ultrathinkParseResult = parseUltrathinkCommand(text);
1073
+ if (ultrathinkParseResult?.kind === "error") {
1074
+ throw new Error(`${ultrathinkParseResult.error}\n\n${ultrathinkParseResult.usage}`);
1075
+ }
1076
+ if (ultrathinkParseResult?.kind === "command") {
1077
+ await this._runUltrathinkCommand(ultrathinkParseResult.command, { source: inputSource });
1078
+ this._appendSessionTrace({
1079
+ type: "prompt_handled_by_command",
1080
+ text,
1081
+ command: "ultrathink",
1082
+ iterations: ultrathinkParseResult.command.iterations,
1083
+ });
1084
+ return;
1085
+ }
1086
+ }
965
1087
  // Handle extension commands first (execute immediately, even during streaming)
966
1088
  // Extension commands manage their own LLM interaction via iosm.sendMessage()
967
1089
  if (expandPromptTemplates && text.startsWith("/")) {
@@ -1126,13 +1248,371 @@ export class AgentSession {
1126
1248
  this.agent.setSystemPrompt(this._baseSystemPrompt);
1127
1249
  }
1128
1250
  }
1129
- await this.agent.prompt(messages);
1130
- await this.waitForRetry();
1251
+ const enableProtocolAutoRepair = !options?.skipProtocolAutoRepair && !this._protocolAutoRepairActive;
1252
+ let protocolToolCallsStarted = 0;
1253
+ let latestAssistantProtocolText = "";
1254
+ let latestAssistantMessage;
1255
+ let latestAssistantVisibleText = "";
1256
+ const unsubscribeProtocolMonitor = enableProtocolAutoRepair
1257
+ ? this.subscribe((event) => {
1258
+ if (event.type === "tool_execution_start") {
1259
+ protocolToolCallsStarted += 1;
1260
+ return;
1261
+ }
1262
+ if (event.type === "message_end" && event.message.role === "assistant") {
1263
+ latestAssistantMessage = event.message;
1264
+ latestAssistantProtocolText = extractAssistantProtocolText(latestAssistantMessage);
1265
+ latestAssistantVisibleText = extractAssistantVisibleText(latestAssistantMessage);
1266
+ }
1267
+ })
1268
+ : undefined;
1269
+ try {
1270
+ await this.agent.prompt(messages);
1271
+ await this.waitForRetry();
1272
+ }
1273
+ finally {
1274
+ unsubscribeProtocolMonitor?.();
1275
+ }
1131
1276
  this._appendSessionTrace({
1132
1277
  type: "prompt_dispatched",
1133
1278
  messageCount: messages.length,
1134
1279
  text: promptText,
1135
1280
  });
1281
+ if (enableProtocolAutoRepair) {
1282
+ let nextIssue = detectPromptProtocolIssue(latestAssistantProtocolText);
1283
+ let nextSilentStopWithoutOutput = !nextIssue &&
1284
+ latestAssistantMessage?.stopReason === "stop" &&
1285
+ !latestAssistantMessage.content.some((part) => part.type === "toolCall") &&
1286
+ isNonActionableVisibleAssistantText(latestAssistantVisibleText);
1287
+ if (nextIssue || nextSilentStopWithoutOutput) {
1288
+ this._protocolAutoRepairActive = true;
1289
+ try {
1290
+ for (let repairAttempt = 1; repairAttempt <= MAX_PROMPT_PROTOCOL_AUTO_REPAIR_ATTEMPTS && (nextIssue || nextSilentStopWithoutOutput); repairAttempt += 1) {
1291
+ const hasPriorToolActivity = protocolToolCallsStarted > 0 || repairAttempt > 1;
1292
+ const correctionPrompt = nextIssue
1293
+ ? buildPromptProtocolCorrectionPrompt({
1294
+ originalPrompt: expandedText,
1295
+ issue: nextIssue,
1296
+ hasPriorToolActivity,
1297
+ })
1298
+ : buildPromptSilentStopRecoveryPrompt({
1299
+ originalPrompt: expandedText,
1300
+ hasPriorToolActivity,
1301
+ });
1302
+ this._appendSessionTrace({
1303
+ type: "prompt_protocol_auto_repair",
1304
+ originalPrompt: expandedText,
1305
+ issue: nextIssue ?? { silentStopWithoutOutput: true },
1306
+ hasPriorToolActivity,
1307
+ repairAttempt,
1308
+ maxRepairAttempts: MAX_PROMPT_PROTOCOL_AUTO_REPAIR_ATTEMPTS,
1309
+ });
1310
+ await this.prompt(correctionPrompt, {
1311
+ expandPromptTemplates: false,
1312
+ skipIosmAutopilot: true,
1313
+ skipOrchestrationDirective: true,
1314
+ skipProtocolAutoRepair: true,
1315
+ source: inputSource,
1316
+ });
1317
+ const repairedAssistant = this._findLastAssistantMessage();
1318
+ const repairedProtocolText = repairedAssistant ? extractAssistantProtocolText(repairedAssistant) : "";
1319
+ const repairedVisibleText = repairedAssistant ? extractAssistantVisibleText(repairedAssistant) : "";
1320
+ nextIssue = detectPromptProtocolIssue(repairedProtocolText);
1321
+ nextSilentStopWithoutOutput =
1322
+ !nextIssue &&
1323
+ repairedAssistant?.stopReason === "stop" &&
1324
+ !repairedAssistant.content.some((part) => part.type === "toolCall") &&
1325
+ isNonActionableVisibleAssistantText(repairedVisibleText);
1326
+ }
1327
+ }
1328
+ finally {
1329
+ this._protocolAutoRepairActive = false;
1330
+ }
1331
+ }
1332
+ }
1333
+ }
1334
+ async _runUltrathinkCommand(command, options) {
1335
+ if (this.isStreaming) {
1336
+ throw new Error("Cannot start /ultrathink while the agent is processing another request.");
1337
+ }
1338
+ if (this.isCompacting) {
1339
+ throw new Error("Cannot start /ultrathink while compaction is running.");
1340
+ }
1341
+ if (this._ultrathinkActive) {
1342
+ throw new Error("An /ultrathink run is already in progress.");
1343
+ }
1344
+ if (!this.model) {
1345
+ throw new Error("No model selected.\n\n" +
1346
+ `Use /login or set an API key environment variable. See ${join(getDocsPath(), "providers.md")}\n\n` +
1347
+ "Then use /model to select a model.");
1348
+ }
1349
+ const apiKey = await this._modelRegistry.getApiKey(this.model);
1350
+ if (!apiKey) {
1351
+ const isOAuth = this._modelRegistry.isUsingOAuth(this.model);
1352
+ if (isOAuth) {
1353
+ throw new Error(`Authentication failed for "${this.model.provider}". ` +
1354
+ `Credentials may have expired or network is unavailable. ` +
1355
+ `Run '/login ${this.model.provider}' to re-authenticate.`);
1356
+ }
1357
+ throw new Error(`No API key found for ${this.model.provider}.\n\n` +
1358
+ `Use /login or set an API key environment variable. See ${join(getDocsPath(), "providers.md")}`);
1359
+ }
1360
+ const objective = command.query?.trim() || findLastMeaningfulUserIntent(this.messages);
1361
+ if (!objective) {
1362
+ throw new Error([
1363
+ "Cannot infer an objective for /ultrathink from session context.",
1364
+ "Provide a query explicitly or send a regular user request first.",
1365
+ "",
1366
+ ULTRATHINK_USAGE,
1367
+ ].join("\n"));
1368
+ }
1369
+ const originalTools = this.getActiveToolNames();
1370
+ const availableToolNames = this.getAllTools().map((tool) => tool.name);
1371
+ const readOnlyTools = resolveUltrathinkReadOnlyTools(availableToolNames);
1372
+ if (readOnlyTools.length === 0) {
1373
+ throw new Error([
1374
+ "Cannot start /ultrathink: no read-only tools are currently active.",
1375
+ "Enable at least one analysis tool (for example read/rg/find/semantic_search/fetch/git_read) and retry.",
1376
+ ].join("\n"));
1377
+ }
1378
+ const contextTail = buildUltrathinkContextTail(this.messages);
1379
+ let checkpoint = createInitialUltrathinkCheckpoint(objective);
1380
+ let previousSummary;
1381
+ let accumulatedInputTokens = 0;
1382
+ let accumulatedTotalTokens = 0;
1383
+ let accumulatedCost = 0;
1384
+ let stagnationCount = 0;
1385
+ let targetIterations = command.iterations;
1386
+ let enforceEvidencePolicy = true;
1387
+ const evidenceById = new Map();
1388
+ const mergeEvidence = (messages) => {
1389
+ const evidence = extractUltrathinkToolEvidence(messages);
1390
+ for (const entry of evidence) {
1391
+ evidenceById.set(entry.toolCallId, entry);
1392
+ }
1393
+ return evidence.length;
1394
+ };
1395
+ const registerUsage = (assistantMessage) => {
1396
+ const usage = assistantMessage?.usage;
1397
+ const inputTokens = Number(usage?.input ?? 0);
1398
+ const totalTokens = Number(usage?.totalTokens ?? inputTokens + Number(usage?.output ?? 0));
1399
+ const costTotal = Number(usage?.cost?.total ?? 0);
1400
+ accumulatedInputTokens += inputTokens;
1401
+ accumulatedTotalTokens += totalTokens;
1402
+ accumulatedCost += costTotal;
1403
+ return { inputTokens, totalTokens, costTotal };
1404
+ };
1405
+ const exceedsBudget = (iterationInputTokens) => iterationInputTokens > ULTRATHINK_MAX_ITERATION_INPUT_TOKENS ||
1406
+ accumulatedInputTokens > ULTRATHINK_MAX_RUN_INPUT_TOKENS ||
1407
+ accumulatedTotalTokens > ULTRATHINK_MAX_RUN_TOTAL_TOKENS ||
1408
+ accumulatedCost > ULTRATHINK_MAX_RUN_COST;
1409
+ const runUltrathinkInternalPrompt = async (rawPrompt, displayText) => {
1410
+ this._appendCustomMessageLocally({
1411
+ customType: INTERNAL_UI_META_CUSTOM_TYPE,
1412
+ content: "",
1413
+ display: false,
1414
+ details: {
1415
+ kind: "orchestration_context",
1416
+ rawPrompt,
1417
+ displayText,
1418
+ },
1419
+ });
1420
+ const messageCountBefore = this.messages.length;
1421
+ await this.prompt(rawPrompt, {
1422
+ expandPromptTemplates: false,
1423
+ skipIosmAutopilot: true,
1424
+ skipOrchestrationDirective: true,
1425
+ skipUltrathinkCommand: true,
1426
+ source: options.source,
1427
+ });
1428
+ return this.messages.slice(messageCountBefore);
1429
+ };
1430
+ this._ultrathinkActive = true;
1431
+ this.setActiveToolsByName(readOnlyTools);
1432
+ try {
1433
+ for (let iteration = 1; iteration <= targetIterations; iteration++) {
1434
+ const phase = iteration === targetIterations ? "Synthesis" : getUltrathinkPhase(iteration, targetIterations);
1435
+ const evidenceCatalog = buildUltrathinkEvidenceCatalog([...evidenceById.values()]);
1436
+ const budgetStatus = buildUltrathinkBudgetStatusLine({
1437
+ accumulatedInputTokens,
1438
+ accumulatedTotalTokens,
1439
+ accumulatedCost,
1440
+ });
1441
+ const iterationPrompt = buildUltrathinkIterationPrompt({
1442
+ iteration,
1443
+ totalIterations: targetIterations,
1444
+ phase,
1445
+ objective,
1446
+ checkpoint,
1447
+ previousSummary,
1448
+ contextTail: iteration === 1 ? contextTail : undefined,
1449
+ evidenceCatalog,
1450
+ budgetStatus,
1451
+ });
1452
+ const visibleIterationPrompt = buildUltrathinkVisibleIterationPrompt({
1453
+ iteration,
1454
+ totalIterations: targetIterations,
1455
+ phase,
1456
+ objective,
1457
+ });
1458
+ const iterationMessages = await runUltrathinkInternalPrompt(iterationPrompt, visibleIterationPrompt);
1459
+ let toolChecksThisIteration = mergeEvidence(iterationMessages);
1460
+ let assistantMessage = this._findLastAssistantMessage();
1461
+ let assistantText = this.getLastAssistantText() ?? "";
1462
+ let iterationUsage = registerUsage(assistantMessage);
1463
+ let iterationInputTokens = iterationUsage.inputTokens;
1464
+ const evaluatePolicy = () => evaluateUltrathinkEvidencePolicy({
1465
+ text: assistantText,
1466
+ phase,
1467
+ toolChecksThisIteration,
1468
+ knownEvidenceIds: [...evidenceById.keys()],
1469
+ });
1470
+ const shouldGround = shouldUltrathinkForceToolGrounding({
1471
+ phase,
1472
+ cumulativeEvidenceCount: evidenceById.size,
1473
+ toolChecksThisIteration,
1474
+ });
1475
+ if (shouldGround) {
1476
+ const groundingPrompt = buildUltrathinkToolGroundingPrompt({
1477
+ iteration,
1478
+ totalIterations: targetIterations,
1479
+ phase,
1480
+ objective,
1481
+ checkpoint,
1482
+ availableReadOnlyTools: readOnlyTools,
1483
+ evidenceCatalog: buildUltrathinkEvidenceCatalog([...evidenceById.values()]),
1484
+ });
1485
+ const groundingDisplayText = `${ULTRATHINK_VISIBLE_PROMPT_PREFIX} ${iteration}/${targetIterations} (${phase}) grounding retry. Performing live workspace probes with read-only tools.`;
1486
+ const groundingMessages = await runUltrathinkInternalPrompt(groundingPrompt, groundingDisplayText);
1487
+ toolChecksThisIteration += mergeEvidence(groundingMessages);
1488
+ assistantMessage = this._findLastAssistantMessage();
1489
+ assistantText = this.getLastAssistantText() ?? "";
1490
+ iterationUsage = registerUsage(assistantMessage);
1491
+ iterationInputTokens += iterationUsage.inputTokens;
1492
+ }
1493
+ if (enforceEvidencePolicy) {
1494
+ let evidencePolicy = evaluatePolicy();
1495
+ if (hasUltrathinkEvidenceViolations(evidencePolicy)) {
1496
+ const policyIssues = [];
1497
+ if (evidencePolicy.missingEvidenceForNumbers) {
1498
+ policyIssues.push("Quantitative claims are missing `[evidence:<toolCallId>]` tags.");
1499
+ }
1500
+ if (evidencePolicy.invalidEvidenceTags.length > 0) {
1501
+ policyIssues.push(`Unknown evidence tags: ${evidencePolicy.invalidEvidenceTags.join(", ")}`);
1502
+ }
1503
+ if (evidencePolicy.needsNoNewEvidenceMarker && !evidencePolicy.hasNoNewEvidenceMarker) {
1504
+ policyIssues.push("Verify/Synthesis response with no new tool checks must include [NO_NEW_EVIDENCE_OK].");
1505
+ }
1506
+ const repairPrompt = buildUltrathinkComplianceRepairPrompt({
1507
+ iteration,
1508
+ totalIterations: targetIterations,
1509
+ phase,
1510
+ objective,
1511
+ originalResponse: assistantText,
1512
+ issues: policyIssues,
1513
+ checkpoint,
1514
+ evidenceCatalog: buildUltrathinkEvidenceCatalog([...evidenceById.values()]),
1515
+ });
1516
+ const repairDisplayText = `${ULTRATHINK_VISIBLE_PROMPT_PREFIX} ${iteration}/${targetIterations} (${phase}) policy repair. Normalizing evidence links and checkpoint format.`;
1517
+ const repairMessages = await runUltrathinkInternalPrompt(repairPrompt, repairDisplayText);
1518
+ toolChecksThisIteration += mergeEvidence(repairMessages);
1519
+ assistantMessage = this._findLastAssistantMessage();
1520
+ assistantText = this.getLastAssistantText() ?? "";
1521
+ iterationUsage = registerUsage(assistantMessage);
1522
+ iterationInputTokens += iterationUsage.inputTokens;
1523
+ evidencePolicy = evaluatePolicy();
1524
+ if (hasUltrathinkEvidenceViolations(evidencePolicy)) {
1525
+ // Do not fail the entire run; keep the latest usable answer and finish gracefully.
1526
+ enforceEvidencePolicy = false;
1527
+ if (iteration < targetIterations) {
1528
+ targetIterations = Math.min(targetIterations, iteration + 1);
1529
+ }
1530
+ }
1531
+ }
1532
+ }
1533
+ previousSummary = extractUltrathinkIterationSummary(assistantText);
1534
+ const checkpointBeforeIteration = checkpoint;
1535
+ const extractedCheckpoint = extractUltrathinkCheckpoint(assistantText);
1536
+ if (extractedCheckpoint && extractedCheckpoint.trim()) {
1537
+ checkpoint = normalizeUltrathinkCheckpoint(extractedCheckpoint, objective);
1538
+ }
1539
+ if (checkpoint.length > ULTRATHINK_MAX_CHECKPOINT_CHARS) {
1540
+ checkpoint = await this._compressUltrathinkCheckpoint(checkpoint, objective, apiKey);
1541
+ }
1542
+ const stagnated = isUltrathinkStagnated({
1543
+ previousCheckpoint: checkpointBeforeIteration,
1544
+ nextCheckpoint: checkpoint,
1545
+ toolChecksThisIteration,
1546
+ });
1547
+ if (stagnated && iteration < targetIterations) {
1548
+ stagnationCount += 1;
1549
+ if (stagnationCount >= ULTRATHINK_STAGNATION_LIMIT) {
1550
+ targetIterations = Math.min(targetIterations, iteration + 1);
1551
+ }
1552
+ }
1553
+ else {
1554
+ stagnationCount = 0;
1555
+ }
1556
+ if (iteration < targetIterations && exceedsBudget(iterationInputTokens)) {
1557
+ targetIterations = Math.min(targetIterations, iteration + 1);
1558
+ }
1559
+ }
1560
+ }
1561
+ finally {
1562
+ this._ultrathinkActive = false;
1563
+ const currentTools = this.getActiveToolNames();
1564
+ const shouldRestore = currentTools.length !== originalTools.length ||
1565
+ currentTools.some((toolName, index) => toolName !== originalTools[index]);
1566
+ if (shouldRestore) {
1567
+ this.setActiveToolsByName(originalTools);
1568
+ }
1569
+ }
1570
+ }
1571
+ async _compressUltrathinkCheckpoint(checkpoint, objective, apiKey) {
1572
+ const model = this.model;
1573
+ const fallback = truncateUltrathinkCheckpoint(normalizeUltrathinkCheckpoint(checkpoint, objective), ULTRATHINK_MAX_CHECKPOINT_CHARS);
1574
+ if (!model)
1575
+ return fallback;
1576
+ const reserveTokens = this.settingsManager.getCompactionReserveTokens();
1577
+ const maxTokens = Math.max(256, Math.min(2048, Math.floor(reserveTokens * 0.4)));
1578
+ try {
1579
+ const response = await completeSimple(model, {
1580
+ systemPrompt: ULTRATHINK_CHECKPOINT_COMPRESSION_SYSTEM_PROMPT,
1581
+ messages: [
1582
+ {
1583
+ role: "user",
1584
+ content: [
1585
+ {
1586
+ type: "text",
1587
+ text: buildUltrathinkCheckpointCompressionPrompt({
1588
+ objective,
1589
+ checkpoint,
1590
+ maxChars: ULTRATHINK_MAX_CHECKPOINT_CHARS,
1591
+ }),
1592
+ },
1593
+ ],
1594
+ timestamp: Date.now(),
1595
+ },
1596
+ ],
1597
+ }, model.reasoning
1598
+ ? { maxTokens, apiKey, reasoning: "high" }
1599
+ : { maxTokens, apiKey });
1600
+ if (response.stopReason === "error") {
1601
+ return fallback;
1602
+ }
1603
+ const text = response.content
1604
+ .filter((part) => part.type === "text")
1605
+ .map((part) => part.text)
1606
+ .join("\n")
1607
+ .trim();
1608
+ if (!text) {
1609
+ return fallback;
1610
+ }
1611
+ return truncateUltrathinkCheckpoint(normalizeUltrathinkCheckpoint(text, objective), ULTRATHINK_MAX_CHECKPOINT_CHARS);
1612
+ }
1613
+ catch {
1614
+ return fallback;
1615
+ }
1136
1616
  }
1137
1617
  /**
1138
1618
  * Try to execute an extension command. Returns true if command was found and executed.
@@ -2411,6 +2891,13 @@ export class AgentSession {
2411
2891
  return this._toolPermissionHandler ? this._toolPermissionHandler(request) : true;
2412
2892
  },
2413
2893
  },
2894
+ dbRun: {
2895
+ resolveRuntimeConfig: () => this.settingsManager.getDbToolsSettings(),
2896
+ permissionGuard: async (request) => {
2897
+ evaluatePreToolHooks(request);
2898
+ return this._toolPermissionHandler ? this._toolPermissionHandler(request) : true;
2899
+ },
2900
+ },
2414
2901
  });
2415
2902
  this._baseToolRegistry = new Map(Object.entries(baseTools).map(([name, tool]) => [name, tool]));
2416
2903
  const extensionsResult = this._resourceLoader.getExtensions();