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.
- package/CHANGELOG.md +66 -0
- package/README.md +3 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +7 -3
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-profiles.d.ts.map +1 -1
- package/dist/core/agent-profiles.js +5 -1
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/agent-session.d.ts +8 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +490 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +7 -4
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +18 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +29 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shadow-guard.d.ts.map +1 -1
- package/dist/core/shadow-guard.js +12 -1
- package/dist/core/shadow-guard.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +4 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +32 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/db-run.d.ts +84 -0
- package/dist/core/tools/db-run.d.ts.map +1 -0
- package/dist/core/tools/db-run.js +690 -0
- package/dist/core/tools/db-run.js.map +1 -0
- package/dist/core/tools/index.d.ts +44 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +16 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/lint-run.d.ts +42 -0
- package/dist/core/tools/lint-run.d.ts.map +1 -0
- package/dist/core/tools/lint-run.js +276 -0
- package/dist/core/tools/lint-run.js.map +1 -0
- package/dist/core/tools/test-run.d.ts +36 -0
- package/dist/core/tools/test-run.d.ts.map +1 -0
- package/dist/core/tools/test-run.js +255 -0
- package/dist/core/tools/test-run.js.map +1 -0
- package/dist/core/tools/typecheck-run.d.ts +44 -0
- package/dist/core/tools/typecheck-run.d.ts.map +1 -0
- package/dist/core/tools/typecheck-run.js +343 -0
- package/dist/core/tools/typecheck-run.js.map +1 -0
- package/dist/core/tools/verification-runner.d.ts +53 -0
- package/dist/core/tools/verification-runner.d.ts.map +1 -0
- package/dist/core/tools/verification-runner.js +235 -0
- package/dist/core/tools/verification-runner.js.map +1 -0
- package/dist/core/ultrathink.d.ts +122 -0
- package/dist/core/ultrathink.d.ts.map +1 -0
- package/dist/core/ultrathink.js +621 -0
- package/dist/core/ultrathink.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +2 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +2 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +2 -1
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +4 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/subagent-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/subagent-message.js +3 -1
- package/dist/modes/interactive/components/subagent-message.js.map +1 -1
- package/dist/modes/interactive/components/task-plan-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-plan-message.js +2 -1
- package/dist/modes/interactive/components/task-plan-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +25 -7
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +523 -9
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +39 -38
- package/dist/modes/interactive/theme/light.json +29 -29
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +16 -25
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/interactive/theme/universal.json +85 -0
- package/docs/cli-reference.md +21 -1
- package/docs/configuration.md +76 -1
- package/docs/development-and-testing.md +1 -1
- package/docs/interactive-mode.md +11 -2
- 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
|
-
|
|
1130
|
-
|
|
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();
|