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
|
@@ -11,7 +11,7 @@ import { spawn, spawnSync } from "child_process";
|
|
|
11
11
|
import { APP_NAME, CHANGELOG_URL, ENV_SESSION_TRACE, ENV_OFFLINE, ENV_SKIP_VERSION_CHECK, getAgentDir, getAuthPath, getDebugLogPath, getModelsPath, getSessionTracePath, getShareViewerUrl, getUpdateInstruction, isSessionTraceEnabled, PACKAGE_NAME, VERSION, } from "../../config.js";
|
|
12
12
|
import { AuthStorage } from "../../core/auth-storage.js";
|
|
13
13
|
import { getAgentProfile, getMainProfileNames, getProfileNames, isReadOnlyProfileName, isValidProfileName, } from "../../core/agent-profiles.js";
|
|
14
|
-
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
14
|
+
import { parseSkillBlock, } from "../../core/agent-session.js";
|
|
15
15
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
16
16
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
17
17
|
import { createCompactionSummaryMessage, INTERNAL_UI_META_CUSTOM_TYPE, isInternalUiMetaDetails, } from "../../core/messages.js";
|
|
@@ -119,6 +119,211 @@ function extractTaskToolErrorText(result) {
|
|
|
119
119
|
return candidate.output.trim();
|
|
120
120
|
return undefined;
|
|
121
121
|
}
|
|
122
|
+
const MAX_TOOL_PROTOCOL_REPAIR_ATTEMPTS = 2;
|
|
123
|
+
const MAX_ASSISTANT_CONTINUATION_PROMPTS_PER_TURN = 1;
|
|
124
|
+
function extractAssistantProtocolText(message) {
|
|
125
|
+
const parts = [];
|
|
126
|
+
for (const content of message.content) {
|
|
127
|
+
if (content.type === "text" && typeof content.text === "string" && content.text.trim()) {
|
|
128
|
+
parts.push(content.text.trim());
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (content.type !== "thinking")
|
|
132
|
+
continue;
|
|
133
|
+
const record = content;
|
|
134
|
+
const thinking = record.thinking;
|
|
135
|
+
if (typeof thinking === "string" && thinking.trim()) {
|
|
136
|
+
parts.push(thinking.trim());
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return parts.join("\n\n").trim();
|
|
140
|
+
}
|
|
141
|
+
function extractAssistantVisibleText(message) {
|
|
142
|
+
return message.content
|
|
143
|
+
.filter((content) => content.type === "text")
|
|
144
|
+
.map((content) => content.text.trim())
|
|
145
|
+
.filter((text) => text.length > 0)
|
|
146
|
+
.join("\n\n")
|
|
147
|
+
.trim();
|
|
148
|
+
}
|
|
149
|
+
function isNonActionableVisibleAssistantText(text) {
|
|
150
|
+
const trimmed = text.trim();
|
|
151
|
+
if (trimmed.length === 0)
|
|
152
|
+
return true;
|
|
153
|
+
return /^\[\s*output\s+truncated[^\]]*\]?\s*\.?$/i.test(trimmed);
|
|
154
|
+
}
|
|
155
|
+
function detectRawToolProtocolIssue(text) {
|
|
156
|
+
const trimmed = text.trim();
|
|
157
|
+
if (!trimmed)
|
|
158
|
+
return undefined;
|
|
159
|
+
const hasToolCallOpenBlock = /(^|\n)\s*<\s*tool_call\b/i.test(trimmed);
|
|
160
|
+
const hasToolCallCloseTag = /<\/\s*tool_call\s*>/i.test(trimmed);
|
|
161
|
+
const hasFunctionBlock = /(^|\n)\s*<\s*function\s*=\s*[A-Za-z0-9._:-]+/i.test(trimmed);
|
|
162
|
+
const hasParameterBlock = /(^|\n)\s*<\s*parameter\s*=\s*[A-Za-z0-9._:-]+\s*>/i.test(trimmed);
|
|
163
|
+
// Treat markup as protocol issue only when it resembles an executable pseudo-call structure.
|
|
164
|
+
// Plain inline mentions like "raw <tool_call>/<function=...> markup" should not trigger repair.
|
|
165
|
+
const hasToolCallMarkup = (hasToolCallOpenBlock && (hasToolCallCloseTag || hasFunctionBlock || hasParameterBlock)) ||
|
|
166
|
+
(hasFunctionBlock && hasParameterBlock);
|
|
167
|
+
const hasDelegateTaskMarkup = /<\s*delegate_task\b/i.test(trimmed) && /<\/\s*delegate_task\s*>/i.test(trimmed);
|
|
168
|
+
if (!hasToolCallMarkup && !hasDelegateTaskMarkup) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
hasToolCallMarkup,
|
|
173
|
+
hasDelegateTaskMarkup,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function detectAssistantResumeReason(message) {
|
|
177
|
+
if (message.stopReason === "aborted")
|
|
178
|
+
return "user_aborted";
|
|
179
|
+
if (message.stopReason === "error")
|
|
180
|
+
return "interrupted_error";
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
function buildAssistantResumePrompt(input) {
|
|
184
|
+
const originalPrompt = input.originalPrompt.trim();
|
|
185
|
+
const boundedOriginalPrompt = originalPrompt.length > 2_000 ? `${originalPrompt.slice(0, 2_000).trimEnd()}...` : originalPrompt;
|
|
186
|
+
const reasonLine = input.reason === "user_aborted"
|
|
187
|
+
? "Previous assistant turn was cancelled before completion."
|
|
188
|
+
: "Previous assistant turn ended with a tool/model interruption before completion.";
|
|
189
|
+
return [
|
|
190
|
+
"[ASSISTANT_RESUME_REQUEST]",
|
|
191
|
+
reasonLine,
|
|
192
|
+
"Continue the same user request from current in-memory state.",
|
|
193
|
+
"Do not repeat completed steps unless required.",
|
|
194
|
+
"Use only real structured tool calls when tools are needed.",
|
|
195
|
+
"Do not emit pseudo XML-like tool markup.",
|
|
196
|
+
"<original_user_request>",
|
|
197
|
+
boundedOriginalPrompt,
|
|
198
|
+
"</original_user_request>",
|
|
199
|
+
"[/ASSISTANT_RESUME_REQUEST]",
|
|
200
|
+
].join("\n");
|
|
201
|
+
}
|
|
202
|
+
function buildAssistantContinuationSelectorTitle(reason) {
|
|
203
|
+
if (reason === "user_aborted") {
|
|
204
|
+
return "You stopped the current run.\nChoose what to do next:";
|
|
205
|
+
}
|
|
206
|
+
return "Tool invocation failed on the model side.\nChoose what to do next:";
|
|
207
|
+
}
|
|
208
|
+
function buildRawToolProtocolCorrectionPrompt(input) {
|
|
209
|
+
const reasons = [
|
|
210
|
+
input.issue.hasToolCallMarkup ? "raw <tool_call>/<function=...> markup" : undefined,
|
|
211
|
+
input.issue.hasDelegateTaskMarkup ? "raw <delegate_task> blocks" : undefined,
|
|
212
|
+
].filter((item) => typeof item === "string");
|
|
213
|
+
const originalPrompt = input.originalPrompt.trim();
|
|
214
|
+
const boundedOriginalPrompt = originalPrompt.length > 2_000 ? `${originalPrompt.slice(0, 2_000).trimEnd()}...` : originalPrompt;
|
|
215
|
+
return [
|
|
216
|
+
"[TOOL_PROTOCOL_CORRECTION]",
|
|
217
|
+
`Previous assistant output included ${reasons.join(" and ")} in plain text.`,
|
|
218
|
+
"These XML-like blocks are not executable tool calls and are ignored by the runtime.",
|
|
219
|
+
"Retry now and follow this protocol exactly:",
|
|
220
|
+
"1) Do not output XML/pseudo-call tags (<tool_call>, <function=...>, <delegate_task>).",
|
|
221
|
+
"2) If a tool is needed, emit real structured tool calls only.",
|
|
222
|
+
"3) Prefer structured built-ins (db_run, typecheck_run, test_run, lint_run) when applicable.",
|
|
223
|
+
"4) If no tool is needed, return a normal direct answer.",
|
|
224
|
+
input.hasPriorToolActivity
|
|
225
|
+
? "5) Continue from the current in-memory state; avoid repeating already completed tool steps unless necessary."
|
|
226
|
+
: undefined,
|
|
227
|
+
"Execute the original user request now.",
|
|
228
|
+
"<original_user_request>",
|
|
229
|
+
boundedOriginalPrompt,
|
|
230
|
+
"</original_user_request>",
|
|
231
|
+
"[/TOOL_PROTOCOL_CORRECTION]",
|
|
232
|
+
]
|
|
233
|
+
.filter((line) => typeof line === "string")
|
|
234
|
+
.join("\n");
|
|
235
|
+
}
|
|
236
|
+
function buildRawToolSilentStopRecoveryPrompt(input) {
|
|
237
|
+
const originalPrompt = input.originalPrompt.trim();
|
|
238
|
+
const boundedOriginalPrompt = originalPrompt.length > 2_000 ? `${originalPrompt.slice(0, 2_000).trimEnd()}...` : originalPrompt;
|
|
239
|
+
return [
|
|
240
|
+
"[ASSISTANT_STALL_RECOVERY]",
|
|
241
|
+
"Previous assistant output ended with stop but produced no visible text and no executable tool calls.",
|
|
242
|
+
"Retry now and continue the same request.",
|
|
243
|
+
"1) Do not return an empty response.",
|
|
244
|
+
"2) If a tool is needed, emit real structured tool calls.",
|
|
245
|
+
"3) If no tool is needed, return a normal direct answer.",
|
|
246
|
+
input.hasPriorToolActivity
|
|
247
|
+
? "4) Continue from the current in-memory state; avoid repeating already completed tool steps unless necessary."
|
|
248
|
+
: undefined,
|
|
249
|
+
"Execute the original user request now.",
|
|
250
|
+
"<original_user_request>",
|
|
251
|
+
boundedOriginalPrompt,
|
|
252
|
+
"</original_user_request>",
|
|
253
|
+
"[/ASSISTANT_STALL_RECOVERY]",
|
|
254
|
+
]
|
|
255
|
+
.filter((line) => typeof line === "string")
|
|
256
|
+
.join("\n");
|
|
257
|
+
}
|
|
258
|
+
async function promptWithRawToolProtocolRepair(input) {
|
|
259
|
+
let currentPrompt = input.promptText;
|
|
260
|
+
let currentOptions = {
|
|
261
|
+
...(input.promptOptions ?? {}),
|
|
262
|
+
skipProtocolAutoRepair: true,
|
|
263
|
+
};
|
|
264
|
+
for (let repairAttempt = 0; repairAttempt <= MAX_TOOL_PROTOCOL_REPAIR_ATTEMPTS; repairAttempt += 1) {
|
|
265
|
+
let toolCallsStarted = 0;
|
|
266
|
+
let latestAssistantProtocolText = "";
|
|
267
|
+
let latestAssistantStopReason;
|
|
268
|
+
let latestAssistantVisibleText = "";
|
|
269
|
+
let latestAssistantHasInlineToolCalls = false;
|
|
270
|
+
const unsubscribe = typeof input.session.subscribe === "function"
|
|
271
|
+
? input.session.subscribe((event) => {
|
|
272
|
+
if (event.type === "tool_execution_start") {
|
|
273
|
+
toolCallsStarted += 1;
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (event.type === "message_end" && event.message.role === "assistant") {
|
|
277
|
+
latestAssistantStopReason = event.message.stopReason;
|
|
278
|
+
const assistantMessage = event.message;
|
|
279
|
+
latestAssistantProtocolText = extractAssistantProtocolText(assistantMessage);
|
|
280
|
+
latestAssistantVisibleText = extractAssistantVisibleText(assistantMessage);
|
|
281
|
+
latestAssistantHasInlineToolCalls = assistantMessage.content.some((part) => part.type === "toolCall");
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
: undefined;
|
|
285
|
+
try {
|
|
286
|
+
await input.session.prompt(currentPrompt, currentOptions);
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
unsubscribe?.();
|
|
290
|
+
}
|
|
291
|
+
const issue = detectRawToolProtocolIssue(latestAssistantProtocolText);
|
|
292
|
+
const silentStopWithoutOutput = !issue &&
|
|
293
|
+
latestAssistantStopReason === "stop" &&
|
|
294
|
+
!latestAssistantHasInlineToolCalls &&
|
|
295
|
+
isNonActionableVisibleAssistantText(latestAssistantVisibleText);
|
|
296
|
+
if (!issue && !silentStopWithoutOutput) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
if (repairAttempt >= MAX_TOOL_PROTOCOL_REPAIR_ATTEMPTS) {
|
|
300
|
+
await input.onRepairExhausted?.(issue ? "raw_markup" : "silent_stop");
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const hasPriorToolActivity = toolCallsStarted > 0 || repairAttempt > 0;
|
|
304
|
+
if (issue) {
|
|
305
|
+
input.onRepairApplied?.("raw_markup");
|
|
306
|
+
currentPrompt = buildRawToolProtocolCorrectionPrompt({
|
|
307
|
+
originalPrompt: input.promptText,
|
|
308
|
+
issue,
|
|
309
|
+
hasPriorToolActivity,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
input.onRepairApplied?.("silent_stop");
|
|
314
|
+
currentPrompt = buildRawToolSilentStopRecoveryPrompt({
|
|
315
|
+
originalPrompt: input.promptText,
|
|
316
|
+
hasPriorToolActivity,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
currentOptions = {
|
|
320
|
+
expandPromptTemplates: false,
|
|
321
|
+
skipOrchestrationDirective: true,
|
|
322
|
+
skipProtocolAutoRepair: true,
|
|
323
|
+
source: "interactive",
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
122
327
|
function buildMetaParallelismCorrection(input) {
|
|
123
328
|
const validationFailure = input.taskToolError && /Validation failed for tool "task"/i.test(input.taskToolError)
|
|
124
329
|
? input.taskToolError.split("\n").slice(0, 3).join("\n")
|
|
@@ -444,6 +649,81 @@ const DOCTOR_CLI_TOOL_SPECS = [
|
|
|
444
649
|
candidates: ["semgrep"],
|
|
445
650
|
hint: "Install semgrep (pipx install semgrep or pip install semgrep).",
|
|
446
651
|
},
|
|
652
|
+
{
|
|
653
|
+
tool: "vitest",
|
|
654
|
+
candidates: ["vitest"],
|
|
655
|
+
hint: "Install vitest (npm/pnpm/yarn add -D vitest).",
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
tool: "jest",
|
|
659
|
+
candidates: ["jest"],
|
|
660
|
+
hint: "Install jest (npm/pnpm/yarn add -D jest).",
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
tool: "pytest",
|
|
664
|
+
candidates: ["python3", "pytest"],
|
|
665
|
+
hint: "Install pytest (python3 -m pip install pytest or pipx install pytest).",
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
tool: "eslint",
|
|
669
|
+
candidates: ["eslint"],
|
|
670
|
+
hint: "Install eslint (npm/pnpm/yarn add -D eslint).",
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
tool: "prettier",
|
|
674
|
+
candidates: ["prettier"],
|
|
675
|
+
hint: "Install prettier (npm/pnpm/yarn add -D prettier).",
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
tool: "stylelint",
|
|
679
|
+
candidates: ["stylelint"],
|
|
680
|
+
hint: "Install stylelint (npm/pnpm/yarn add -D stylelint).",
|
|
681
|
+
},
|
|
682
|
+
{
|
|
683
|
+
tool: "tsc",
|
|
684
|
+
candidates: ["tsc"],
|
|
685
|
+
hint: "Install TypeScript compiler (npm/pnpm/yarn add -D typescript).",
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
tool: "vue_tsc",
|
|
689
|
+
candidates: ["vue-tsc"],
|
|
690
|
+
hint: "Install vue-tsc (npm/pnpm/yarn add -D vue-tsc).",
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
tool: "pyright",
|
|
694
|
+
candidates: ["pyright"],
|
|
695
|
+
hint: "Install pyright (npm i -D pyright or pipx install pyright).",
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
tool: "mypy",
|
|
699
|
+
candidates: ["mypy", "python3"],
|
|
700
|
+
hint: "Install mypy (python3 -m pip install mypy or pipx install mypy).",
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
tool: "psql",
|
|
704
|
+
candidates: ["psql"],
|
|
705
|
+
hint: "Install PostgreSQL CLI (psql).",
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
tool: "mysql",
|
|
709
|
+
candidates: ["mysql"],
|
|
710
|
+
hint: "Install MySQL CLI client (mysql).",
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
tool: "sqlite3",
|
|
714
|
+
candidates: ["sqlite3"],
|
|
715
|
+
hint: "Install sqlite3 CLI.",
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
tool: "mongosh",
|
|
719
|
+
candidates: ["mongosh"],
|
|
720
|
+
hint: "Install MongoDB shell (mongosh).",
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
tool: "redis-cli",
|
|
724
|
+
candidates: ["redis-cli"],
|
|
725
|
+
hint: "Install Redis CLI (redis-cli).",
|
|
726
|
+
},
|
|
447
727
|
{
|
|
448
728
|
tool: "sed",
|
|
449
729
|
candidates: ["sed"],
|
|
@@ -1349,6 +1629,31 @@ export class InteractiveMode {
|
|
|
1349
1629
|
}
|
|
1350
1630
|
return null;
|
|
1351
1631
|
}
|
|
1632
|
+
getUltrathinkArgumentCompletions(prefix) {
|
|
1633
|
+
const hasTrailingSpace = /\\s$/.test(prefix);
|
|
1634
|
+
const tokens = this.parseSlashArgs(prefix);
|
|
1635
|
+
const queryToken = hasTrailingSpace ? "" : (tokens[tokens.length - 1] ?? "");
|
|
1636
|
+
if (tokens.length === 0) {
|
|
1637
|
+
return [
|
|
1638
|
+
{ value: "-q", label: "-q", description: `Iterations (default 5, max 12)` },
|
|
1639
|
+
{ value: "--iterations", label: "--iterations", description: "Same as -q" },
|
|
1640
|
+
];
|
|
1641
|
+
}
|
|
1642
|
+
const previousToken = tokens[tokens.length - 2];
|
|
1643
|
+
if (previousToken === "-q" || previousToken === "--iterations") {
|
|
1644
|
+
const values = ["3", "5", "7", "10", "12"];
|
|
1645
|
+
return values
|
|
1646
|
+
.filter((value) => value.startsWith(queryToken))
|
|
1647
|
+
.map((value) => ({ value, label: value, description: "iteration count" }));
|
|
1648
|
+
}
|
|
1649
|
+
if (queryToken.startsWith("-")) {
|
|
1650
|
+
return [
|
|
1651
|
+
{ value: "-q", label: "-q", description: `Iterations (default 5, max 12)` },
|
|
1652
|
+
{ value: "--iterations", label: "--iterations", description: "Same as -q" },
|
|
1653
|
+
].filter((item) => item.value.startsWith(queryToken));
|
|
1654
|
+
}
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1352
1657
|
setupAutocomplete(fdPath) {
|
|
1353
1658
|
// Define commands for autocomplete
|
|
1354
1659
|
const builtinCommands = BUILTIN_SLASH_COMMANDS.filter((command) => this.activeProfileName === "iosm" || !IOSM_PROFILE_ONLY_COMMANDS.has(command.name));
|
|
@@ -1406,6 +1711,10 @@ export class InteractiveMode {
|
|
|
1406
1711
|
if (swarmCommand) {
|
|
1407
1712
|
swarmCommand.getArgumentCompletions = (prefix) => this.getSwarmArgumentCompletions(prefix);
|
|
1408
1713
|
}
|
|
1714
|
+
const ultrathinkCommand = slashCommands.find((command) => command.name === "ultrathink");
|
|
1715
|
+
if (ultrathinkCommand) {
|
|
1716
|
+
ultrathinkCommand.getArgumentCompletions = (prefix) => this.getUltrathinkArgumentCompletions(prefix);
|
|
1717
|
+
}
|
|
1409
1718
|
// Convert prompt templates to SlashCommand format for autocomplete
|
|
1410
1719
|
const templateCommands = this.session.promptTemplates.map((cmd) => ({
|
|
1411
1720
|
name: cmd.name,
|
|
@@ -1784,7 +2093,25 @@ export class InteractiveMode {
|
|
|
1784
2093
|
while (true) {
|
|
1785
2094
|
const userInput = await this.getUserInput();
|
|
1786
2095
|
try {
|
|
1787
|
-
|
|
2096
|
+
let promptText = userInput;
|
|
2097
|
+
let continuationPrompts = 0;
|
|
2098
|
+
while (true) {
|
|
2099
|
+
const turnStartMessageCount = this.session.messages.length;
|
|
2100
|
+
await this.promptWithTaskFallback(promptText);
|
|
2101
|
+
if (continuationPrompts >= MAX_ASSISTANT_CONTINUATION_PROMPTS_PER_TURN) {
|
|
2102
|
+
break;
|
|
2103
|
+
}
|
|
2104
|
+
const continuationDecision = await this.maybeRequestAgentContinuation(userInput, turnStartMessageCount);
|
|
2105
|
+
if (!continuationDecision || continuationDecision.action === "stay") {
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2108
|
+
if (continuationDecision.action === "new_session") {
|
|
2109
|
+
await this.handleClearCommand();
|
|
2110
|
+
break;
|
|
2111
|
+
}
|
|
2112
|
+
continuationPrompts += 1;
|
|
2113
|
+
promptText = continuationDecision.promptText;
|
|
2114
|
+
}
|
|
1788
2115
|
}
|
|
1789
2116
|
catch (error) {
|
|
1790
2117
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -5342,7 +5669,7 @@ export class InteractiveMode {
|
|
|
5342
5669
|
transport: this.settingsManager.getTransport(),
|
|
5343
5670
|
thinkingLevel: this.session.thinkingLevel,
|
|
5344
5671
|
availableThinkingLevels: this.session.getAvailableThinkingLevels(),
|
|
5345
|
-
currentTheme: this.settingsManager.getTheme() || "
|
|
5672
|
+
currentTheme: this.settingsManager.getTheme() || "universal",
|
|
5346
5673
|
availableThemes: getAvailableThemes(),
|
|
5347
5674
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
5348
5675
|
collapseChangelog: this.settingsManager.getCollapseChangelog(),
|
|
@@ -5506,7 +5833,7 @@ export class InteractiveMode {
|
|
|
5506
5833
|
this.settingsManager.setTheme(themeName);
|
|
5507
5834
|
this.ui.invalidate();
|
|
5508
5835
|
if (!result.success) {
|
|
5509
|
-
this.showError(`Failed to load theme "${themeName}": ${result.error}\nFell back to
|
|
5836
|
+
this.showError(`Failed to load theme "${themeName}": ${result.error}\nFell back to universal theme.`);
|
|
5510
5837
|
}
|
|
5511
5838
|
},
|
|
5512
5839
|
onThemePreview: (themeName) => {
|
|
@@ -8729,7 +9056,9 @@ export class InteractiveMode {
|
|
|
8729
9056
|
const mcpConnected = mcpStatuses.filter((status) => status.state === "connected").length;
|
|
8730
9057
|
const mcpErrored = mcpStatuses.filter((status) => status.state === "error").length;
|
|
8731
9058
|
const mcpDisabled = mcpStatuses.filter((status) => !status.enabled).length;
|
|
8732
|
-
const
|
|
9059
|
+
const resolveCliToolStatuses = this.resolveDoctorCliToolStatuses ??
|
|
9060
|
+
resolveDoctorCliToolStatuses;
|
|
9061
|
+
const cliToolStatuses = resolveCliToolStatuses();
|
|
8733
9062
|
const missingCliTools = cliToolStatuses.filter((status) => !status.available).map((status) => status.tool);
|
|
8734
9063
|
let semanticStatus;
|
|
8735
9064
|
let semanticStatusError;
|
|
@@ -9195,6 +9524,41 @@ export class InteractiveMode {
|
|
|
9195
9524
|
return !(message.stopReason === "aborted" && message.content.length === 0);
|
|
9196
9525
|
});
|
|
9197
9526
|
}
|
|
9527
|
+
async maybeRequestAgentContinuation(originalUserInput, turnStartMessageCount) {
|
|
9528
|
+
const recentAssistant = this.session.messages
|
|
9529
|
+
.slice(turnStartMessageCount)
|
|
9530
|
+
.reverse()
|
|
9531
|
+
.find((message) => message.role === "assistant");
|
|
9532
|
+
if (!recentAssistant) {
|
|
9533
|
+
return undefined;
|
|
9534
|
+
}
|
|
9535
|
+
const resumeReason = detectAssistantResumeReason(recentAssistant);
|
|
9536
|
+
if (!resumeReason) {
|
|
9537
|
+
return undefined;
|
|
9538
|
+
}
|
|
9539
|
+
const selected = await this.showExtensionSelector(buildAssistantContinuationSelectorTitle(resumeReason), [
|
|
9540
|
+
"1. Yes, continue agent work",
|
|
9541
|
+
"2. Repeat my original request",
|
|
9542
|
+
"3. No, keep this session",
|
|
9543
|
+
"4. Start a new session",
|
|
9544
|
+
]);
|
|
9545
|
+
if (selected === "4. Start a new session") {
|
|
9546
|
+
return { action: "new_session" };
|
|
9547
|
+
}
|
|
9548
|
+
if (selected === "2. Repeat my original request") {
|
|
9549
|
+
return { action: "repeat_request", promptText: originalUserInput };
|
|
9550
|
+
}
|
|
9551
|
+
if (selected !== "1. Yes, continue agent work") {
|
|
9552
|
+
return { action: "stay" };
|
|
9553
|
+
}
|
|
9554
|
+
return {
|
|
9555
|
+
action: "resume",
|
|
9556
|
+
promptText: buildAssistantResumePrompt({
|
|
9557
|
+
reason: resumeReason,
|
|
9558
|
+
originalPrompt: originalUserInput,
|
|
9559
|
+
}),
|
|
9560
|
+
};
|
|
9561
|
+
}
|
|
9198
9562
|
sanitizeAssistantDisplayMessage(message) {
|
|
9199
9563
|
const hideAllTextForOrchestration = this.activeAssistantOrchestrationContext;
|
|
9200
9564
|
let changed = false;
|
|
@@ -9237,7 +9601,157 @@ export class InteractiveMode {
|
|
|
9237
9601
|
normalized === "/capabilities" ||
|
|
9238
9602
|
normalized === "capabilities");
|
|
9239
9603
|
}
|
|
9604
|
+
async showProtocolRepairRecoverySelector(reason) {
|
|
9605
|
+
const title = reason === "silent_stop"
|
|
9606
|
+
? "Model returned empty/non-actionable responses after auto-repair.\nChoose what to do next:"
|
|
9607
|
+
: "Model emitted pseudo tool-call markup after auto-repair.\nChoose what to do next:";
|
|
9608
|
+
const retryNow = "1. Retry now (Recommended)";
|
|
9609
|
+
const repeatOriginal = "2. Repeat original request";
|
|
9610
|
+
const switchModel = "3. Switch model and retry";
|
|
9611
|
+
const keepSession = "4. Keep session";
|
|
9612
|
+
const selected = await this.showExtensionSelector(title, [retryNow, repeatOriginal, switchModel, keepSession]);
|
|
9613
|
+
if (!selected || selected === keepSession)
|
|
9614
|
+
return "keep_session";
|
|
9615
|
+
if (selected === repeatOriginal)
|
|
9616
|
+
return "repeat_original";
|
|
9617
|
+
if (selected === switchModel)
|
|
9618
|
+
return "switch_model_retry";
|
|
9619
|
+
return "retry_now";
|
|
9620
|
+
}
|
|
9621
|
+
async showModelSelectorForImmediateRetry(initialSearchInput, providerFilter) {
|
|
9622
|
+
return await new Promise((resolve) => {
|
|
9623
|
+
let settled = false;
|
|
9624
|
+
const settle = (model) => {
|
|
9625
|
+
if (settled)
|
|
9626
|
+
return;
|
|
9627
|
+
settled = true;
|
|
9628
|
+
resolve(model);
|
|
9629
|
+
};
|
|
9630
|
+
this.showSelector((done) => {
|
|
9631
|
+
const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRegistry, this.session.scopedModels, async (model) => {
|
|
9632
|
+
try {
|
|
9633
|
+
await this.session.setModel(model);
|
|
9634
|
+
this.footer.invalidate();
|
|
9635
|
+
this.updateEditorBorderColor();
|
|
9636
|
+
this.refreshBuiltInHeader();
|
|
9637
|
+
done();
|
|
9638
|
+
this.showStatus(`Model: ${model.provider}/${model.id}`);
|
|
9639
|
+
this.checkDaxnutsEasterEgg(model);
|
|
9640
|
+
settle(model);
|
|
9641
|
+
}
|
|
9642
|
+
catch (error) {
|
|
9643
|
+
done();
|
|
9644
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
9645
|
+
settle(undefined);
|
|
9646
|
+
}
|
|
9647
|
+
}, () => {
|
|
9648
|
+
done();
|
|
9649
|
+
this.ui.requestRender();
|
|
9650
|
+
settle(undefined);
|
|
9651
|
+
}, initialSearchInput, providerFilter);
|
|
9652
|
+
return { component: selector, focus: selector };
|
|
9653
|
+
});
|
|
9654
|
+
});
|
|
9655
|
+
}
|
|
9656
|
+
async selectModelForImmediateRetry(preferredProvider) {
|
|
9657
|
+
await this.hydrateMissingProviderModelsForSavedAuth();
|
|
9658
|
+
this.session.modelRegistry.refresh();
|
|
9659
|
+
let models = [];
|
|
9660
|
+
try {
|
|
9661
|
+
models = await this.session.modelRegistry.getAvailable();
|
|
9662
|
+
}
|
|
9663
|
+
catch {
|
|
9664
|
+
models = [];
|
|
9665
|
+
}
|
|
9666
|
+
if (models.length === 0) {
|
|
9667
|
+
this.showStatus("No models available");
|
|
9668
|
+
return undefined;
|
|
9669
|
+
}
|
|
9670
|
+
const providerCounts = new Map();
|
|
9671
|
+
for (const model of models) {
|
|
9672
|
+
providerCounts.set(model.provider, (providerCounts.get(model.provider) ?? 0) + 1);
|
|
9673
|
+
}
|
|
9674
|
+
const providers = Array.from(providerCounts.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
9675
|
+
if (providers.length === 0) {
|
|
9676
|
+
this.showStatus("No providers available");
|
|
9677
|
+
return undefined;
|
|
9678
|
+
}
|
|
9679
|
+
if (preferredProvider) {
|
|
9680
|
+
const preferred = providers.find(([provider]) => provider.toLowerCase() === preferredProvider.toLowerCase());
|
|
9681
|
+
if (preferred) {
|
|
9682
|
+
return await this.showModelSelectorForImmediateRetry(undefined, preferred[0]);
|
|
9683
|
+
}
|
|
9684
|
+
}
|
|
9685
|
+
if (providers.length === 1) {
|
|
9686
|
+
return await this.showModelSelectorForImmediateRetry(undefined, providers[0]?.[0]);
|
|
9687
|
+
}
|
|
9688
|
+
const optionMap = new Map();
|
|
9689
|
+
const options = ["All providers"];
|
|
9690
|
+
for (const [provider, count] of providers) {
|
|
9691
|
+
const optionLabel = `${provider} (${count})`;
|
|
9692
|
+
optionMap.set(optionLabel, provider);
|
|
9693
|
+
options.push(optionLabel);
|
|
9694
|
+
}
|
|
9695
|
+
const selected = await this.showExtensionSelector("/model: choose provider for retry", options);
|
|
9696
|
+
if (!selected)
|
|
9697
|
+
return undefined;
|
|
9698
|
+
if (selected === "All providers") {
|
|
9699
|
+
return await this.showModelSelectorForImmediateRetry();
|
|
9700
|
+
}
|
|
9701
|
+
const provider = optionMap.get(selected);
|
|
9702
|
+
if (!provider) {
|
|
9703
|
+
this.showWarning("Provider selection is no longer available.");
|
|
9704
|
+
return undefined;
|
|
9705
|
+
}
|
|
9706
|
+
return await this.showModelSelectorForImmediateRetry(undefined, provider);
|
|
9707
|
+
}
|
|
9240
9708
|
async promptWithTaskFallback(userInput) {
|
|
9709
|
+
const handleProtocolRepairApplied = (reason) => {
|
|
9710
|
+
const showWarning = this.showWarning;
|
|
9711
|
+
if (typeof showWarning === "function") {
|
|
9712
|
+
const message = reason === "silent_stop"
|
|
9713
|
+
? "Protocol auto-repair: model returned an empty stop response; retrying once."
|
|
9714
|
+
: "Protocol auto-repair: model emitted raw tool-call markup; retrying once.";
|
|
9715
|
+
showWarning.call(this, message);
|
|
9716
|
+
}
|
|
9717
|
+
};
|
|
9718
|
+
const runPromptWithProtocolRecovery = async (promptText, promptOptions) => {
|
|
9719
|
+
let nextPrompt = promptText;
|
|
9720
|
+
let nextOptions = promptOptions;
|
|
9721
|
+
for (let recoveryAttempt = 0; recoveryAttempt < 3; recoveryAttempt += 1) {
|
|
9722
|
+
let exhaustedReason;
|
|
9723
|
+
await promptWithRawToolProtocolRepair({
|
|
9724
|
+
session: this.session,
|
|
9725
|
+
promptText: nextPrompt,
|
|
9726
|
+
promptOptions: nextOptions,
|
|
9727
|
+
onRepairApplied: handleProtocolRepairApplied,
|
|
9728
|
+
onRepairExhausted: async (reason) => {
|
|
9729
|
+
exhaustedReason = reason;
|
|
9730
|
+
},
|
|
9731
|
+
});
|
|
9732
|
+
if (!exhaustedReason) {
|
|
9733
|
+
return;
|
|
9734
|
+
}
|
|
9735
|
+
const selectedAction = await this.showProtocolRepairRecoverySelector(exhaustedReason);
|
|
9736
|
+
if (selectedAction === "keep_session") {
|
|
9737
|
+
return;
|
|
9738
|
+
}
|
|
9739
|
+
if (selectedAction === "repeat_original") {
|
|
9740
|
+
nextPrompt = userInput;
|
|
9741
|
+
nextOptions = undefined;
|
|
9742
|
+
continue;
|
|
9743
|
+
}
|
|
9744
|
+
if (selectedAction === "switch_model_retry") {
|
|
9745
|
+
const selectedModel = await this.selectModelForImmediateRetry();
|
|
9746
|
+
if (!selectedModel) {
|
|
9747
|
+
return;
|
|
9748
|
+
}
|
|
9749
|
+
continue;
|
|
9750
|
+
}
|
|
9751
|
+
// retry_now: rerun current prompt/options
|
|
9752
|
+
}
|
|
9753
|
+
this.showWarning("Protocol auto-repair exhausted repeatedly. Keep session as-is and retry with a different model or a simpler prompt.");
|
|
9754
|
+
};
|
|
9241
9755
|
const mentionedAgent = this.resolveMentionedAgent(userInput);
|
|
9242
9756
|
if (mentionedAgent) {
|
|
9243
9757
|
const cleaned = userInput.replace(/(?:^|\s)@[^\s]+/g, " ").trim();
|
|
@@ -9263,7 +9777,7 @@ export class InteractiveMode {
|
|
|
9263
9777
|
"Answer normally and concisely in plain language. Do not run task tool for this query.",
|
|
9264
9778
|
"</agent_capability_query>",
|
|
9265
9779
|
].join("\n");
|
|
9266
|
-
await
|
|
9780
|
+
await runPromptWithProtocolRecovery(capabilityPrompt, {
|
|
9267
9781
|
expandPromptTemplates: false,
|
|
9268
9782
|
source: "interactive",
|
|
9269
9783
|
});
|
|
@@ -9294,7 +9808,7 @@ export class InteractiveMode {
|
|
|
9294
9808
|
: []),
|
|
9295
9809
|
"</orchestrate>",
|
|
9296
9810
|
].join("\n");
|
|
9297
|
-
await
|
|
9811
|
+
await runPromptWithProtocolRecovery(mentionPrompt, {
|
|
9298
9812
|
expandPromptTemplates: false,
|
|
9299
9813
|
source: "interactive",
|
|
9300
9814
|
});
|
|
@@ -9327,7 +9841,7 @@ export class InteractiveMode {
|
|
|
9327
9841
|
});
|
|
9328
9842
|
return;
|
|
9329
9843
|
}
|
|
9330
|
-
await
|
|
9844
|
+
await runPromptWithProtocolRecovery(userInput);
|
|
9331
9845
|
}
|
|
9332
9846
|
createIosmVerificationEventBridge(options) {
|
|
9333
9847
|
const loaderMessage = options?.loaderMessage ?? `Verifying workspace... (${appKey(this.keybindings, "interrupt")} to interrupt)`;
|
|
@@ -12920,7 +13434,7 @@ The agent will automatically receive IOSM context on every turn.`;
|
|
|
12920
13434
|
const themeName = this.settingsManager.getTheme();
|
|
12921
13435
|
const themeResult = themeName ? setTheme(themeName, true) : { success: true };
|
|
12922
13436
|
if (!themeResult.success) {
|
|
12923
|
-
this.showError(`Failed to load theme "${themeName}": ${themeResult.error}\nFell back to
|
|
13437
|
+
this.showError(`Failed to load theme "${themeName}": ${themeResult.error}\nFell back to universal theme.`);
|
|
12924
13438
|
}
|
|
12925
13439
|
const editorPaddingX = this.settingsManager.getEditorPaddingX();
|
|
12926
13440
|
const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
|