iosm-cli 0.2.6 → 0.2.8

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 (105) hide show
  1. package/CHANGELOG.md +54 -1
  2. package/README.md +7 -7
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +9 -2
  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 +12 -1
  8. package/dist/core/agent-profiles.js.map +1 -1
  9. package/dist/core/agent-session.d.ts.map +1 -1
  10. package/dist/core/agent-session.js +40 -1
  11. package/dist/core/agent-session.js.map +1 -1
  12. package/dist/core/sdk.d.ts +2 -2
  13. package/dist/core/sdk.d.ts.map +1 -1
  14. package/dist/core/sdk.js +3 -3
  15. package/dist/core/sdk.js.map +1 -1
  16. package/dist/core/settings-manager.d.ts +39 -0
  17. package/dist/core/settings-manager.d.ts.map +1 -1
  18. package/dist/core/settings-manager.js +168 -0
  19. package/dist/core/settings-manager.js.map +1 -1
  20. package/dist/core/shadow-guard.js +1 -1
  21. package/dist/core/shadow-guard.js.map +1 -1
  22. package/dist/core/system-prompt.d.ts.map +1 -1
  23. package/dist/core/system-prompt.js +91 -2
  24. package/dist/core/system-prompt.js.map +1 -1
  25. package/dist/core/tools/fetch.d.ts +56 -0
  26. package/dist/core/tools/fetch.d.ts.map +1 -0
  27. package/dist/core/tools/fetch.js +272 -0
  28. package/dist/core/tools/fetch.js.map +1 -0
  29. package/dist/core/tools/fs-ops.d.ts +54 -0
  30. package/dist/core/tools/fs-ops.d.ts.map +1 -0
  31. package/dist/core/tools/fs-ops.js +206 -0
  32. package/dist/core/tools/fs-ops.js.map +1 -0
  33. package/dist/core/tools/git-common.d.ts +45 -0
  34. package/dist/core/tools/git-common.d.ts.map +1 -0
  35. package/dist/core/tools/git-common.js +185 -0
  36. package/dist/core/tools/git-common.js.map +1 -0
  37. package/dist/core/tools/git-read.d.ts +62 -0
  38. package/dist/core/tools/git-read.d.ts.map +1 -0
  39. package/dist/core/tools/git-read.js +215 -0
  40. package/dist/core/tools/git-read.js.map +1 -0
  41. package/dist/core/tools/git-write.d.ts +75 -0
  42. package/dist/core/tools/git-write.d.ts.map +1 -0
  43. package/dist/core/tools/git-write.js +298 -0
  44. package/dist/core/tools/git-write.js.map +1 -0
  45. package/dist/core/tools/index.d.ts +90 -0
  46. package/dist/core/tools/index.d.ts.map +1 -1
  47. package/dist/core/tools/index.js +32 -0
  48. package/dist/core/tools/index.js.map +1 -1
  49. package/dist/core/tools/task.js +1 -1
  50. package/dist/core/tools/task.js.map +1 -1
  51. package/dist/core/tools/web-search.d.ts +72 -0
  52. package/dist/core/tools/web-search.d.ts.map +1 -0
  53. package/dist/core/tools/web-search.js +702 -0
  54. package/dist/core/tools/web-search.js.map +1 -0
  55. package/dist/index.d.ts +2 -2
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +1 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  60. package/dist/modes/interactive/components/config-selector.js +7 -2
  61. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  62. package/dist/modes/interactive/components/mcp-selector.d.ts.map +1 -1
  63. package/dist/modes/interactive/components/mcp-selector.js +3 -1
  64. package/dist/modes/interactive/components/mcp-selector.js.map +1 -1
  65. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  66. package/dist/modes/interactive/components/model-selector.js +12 -2
  67. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  68. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/oauth-selector.js +11 -0
  70. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  71. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  72. package/dist/modes/interactive/components/scoped-models-selector.js +16 -5
  73. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  74. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  75. package/dist/modes/interactive/components/session-selector.js +4 -2
  76. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  77. package/dist/modes/interactive/components/settings-selector.d.ts +25 -0
  78. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  79. package/dist/modes/interactive/components/settings-selector.js +182 -2
  80. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  81. package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
  82. package/dist/modes/interactive/components/show-images-selector.js +7 -2
  83. package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
  84. package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
  85. package/dist/modes/interactive/components/theme-selector.js +7 -2
  86. package/dist/modes/interactive/components/theme-selector.js.map +1 -1
  87. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  88. package/dist/modes/interactive/components/thinking-selector.js +7 -2
  89. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  90. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  91. package/dist/modes/interactive/components/tree-selector.js +18 -3
  92. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  93. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  94. package/dist/modes/interactive/components/user-message-selector.js +8 -0
  95. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  96. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  97. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  98. package/dist/modes/interactive/interactive-mode.js +184 -24
  99. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  100. package/docs/cli-reference.md +19 -1
  101. package/docs/configuration.md +11 -2
  102. package/docs/development-and-testing.md +1 -1
  103. package/docs/interactive-mode.md +2 -2
  104. package/docs/rpc-json-sdk.md +1 -1
  105. package/package.json +1 -1
@@ -692,6 +692,8 @@ export class InteractiveMode {
692
692
  // Extension UI state
693
693
  this.extensionSelector = undefined;
694
694
  this.extensionInput = undefined;
695
+ this.extensionInputRestoreComponent = undefined;
696
+ this.extensionInputRestoreFocus = undefined;
695
697
  this.extensionEditor = undefined;
696
698
  this.extensionTerminalInputUnsubscribers = new Set();
697
699
  // Extension widgets (components rendered above/below the editor)
@@ -716,6 +718,9 @@ export class InteractiveMode {
716
718
  this.modelsDevProviderCatalogRefreshPromise = undefined;
717
719
  // Custom header from extension (undefined = use built-in header)
718
720
  this.customHeader = undefined;
721
+ // Active selector shown in editor container (used to restore UI after temporary dialogs)
722
+ this.activeSelectorComponent = undefined;
723
+ this.activeSelectorFocus = undefined;
719
724
  /**
720
725
  * Gracefully shutdown the agent.
721
726
  * Emits shutdown event to extensions, then exits.
@@ -2595,6 +2600,12 @@ export class InteractiveMode {
2595
2600
  resolve(undefined);
2596
2601
  };
2597
2602
  opts?.signal?.addEventListener("abort", onAbort, { once: true });
2603
+ const canRestoreSelector = this.activeSelectorComponent !== undefined &&
2604
+ this.editorContainer.children.includes(this.activeSelectorComponent);
2605
+ this.extensionInputRestoreComponent = canRestoreSelector ? this.activeSelectorComponent : this.editor;
2606
+ this.extensionInputRestoreFocus = canRestoreSelector
2607
+ ? (this.activeSelectorFocus ?? this.activeSelectorComponent)
2608
+ : this.editor;
2598
2609
  this.extensionInput = new ExtensionInputComponent(title, placeholder, (value) => {
2599
2610
  opts?.signal?.removeEventListener("abort", onAbort);
2600
2611
  this.hideExtensionInput();
@@ -2616,9 +2627,13 @@ export class InteractiveMode {
2616
2627
  hideExtensionInput() {
2617
2628
  this.extensionInput?.dispose();
2618
2629
  this.editorContainer.clear();
2619
- this.editorContainer.addChild(this.editor);
2630
+ const restoreComponent = this.extensionInputRestoreComponent ?? this.editor;
2631
+ const restoreFocus = this.extensionInputRestoreFocus ?? restoreComponent;
2632
+ this.editorContainer.addChild(restoreComponent);
2620
2633
  this.extensionInput = undefined;
2621
- this.ui.setFocus(this.editor);
2634
+ this.extensionInputRestoreComponent = undefined;
2635
+ this.extensionInputRestoreFocus = undefined;
2636
+ this.ui.setFocus(restoreFocus);
2622
2637
  this.ui.requestRender();
2623
2638
  }
2624
2639
  /**
@@ -4837,8 +4852,12 @@ export class InteractiveMode {
4837
4852
  this.editorContainer.clear();
4838
4853
  this.editorContainer.addChild(this.editor);
4839
4854
  this.ui.setFocus(this.editor);
4855
+ this.activeSelectorComponent = undefined;
4856
+ this.activeSelectorFocus = undefined;
4840
4857
  };
4841
4858
  const { component, focus } = create(done);
4859
+ this.activeSelectorComponent = component;
4860
+ this.activeSelectorFocus = focus;
4842
4861
  this.editorContainer.clear();
4843
4862
  this.editorContainer.addChild(component);
4844
4863
  this.ui.setFocus(focus);
@@ -5334,6 +5353,16 @@ export class InteractiveMode {
5334
5353
  autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
5335
5354
  quietStartup: this.settingsManager.getQuietStartup(),
5336
5355
  clearOnShrink: this.settingsManager.getClearOnShrink(),
5356
+ webSearchEnabled: this.settingsManager.getWebSearchEnabled(),
5357
+ webSearchProviderMode: this.settingsManager.getWebSearchProviderMode(),
5358
+ webSearchFallbackMode: this.settingsManager.getWebSearchFallbackMode(),
5359
+ webSearchSafeSearch: this.settingsManager.getWebSearchSafeSearch(),
5360
+ webSearchMaxResults: this.settingsManager.getWebSearchMaxResults(),
5361
+ webSearchTimeoutSeconds: this.settingsManager.getWebSearchTimeoutSeconds(),
5362
+ webSearchTavilyApiKeyConfigured: this.settingsManager.isWebSearchTavilyApiKeyConfigured(),
5363
+ webSearchSearxngUrlConfigured: this.settingsManager.isWebSearchSearxngUrlConfigured(),
5364
+ githubToolsNetworkEnabled: this.settingsManager.getGithubToolsNetworkEnabled(),
5365
+ githubToolsTokenConfigured: this.settingsManager.isGithubToolsTokenConfigured(),
5337
5366
  }, {
5338
5367
  onAutoCompactChange: (enabled) => {
5339
5368
  this.session.setAutoCompactionEnabled(enabled);
@@ -5370,6 +5399,103 @@ export class InteractiveMode {
5370
5399
  this.settingsManager.setTransport(transport);
5371
5400
  this.session.agent.setTransport(transport);
5372
5401
  },
5402
+ onWebSearchEnabledChange: (enabled) => {
5403
+ this.settingsManager.setWebSearchEnabled(enabled);
5404
+ },
5405
+ onWebSearchProviderModeChange: (mode) => {
5406
+ this.settingsManager.setWebSearchProviderMode(mode);
5407
+ },
5408
+ onWebSearchFallbackModeChange: (mode) => {
5409
+ this.settingsManager.setWebSearchFallbackMode(mode);
5410
+ },
5411
+ onWebSearchSafeSearchChange: (mode) => {
5412
+ this.settingsManager.setWebSearchSafeSearch(mode);
5413
+ },
5414
+ onWebSearchMaxResultsChange: (maxResults) => {
5415
+ this.settingsManager.setWebSearchMaxResults(maxResults);
5416
+ },
5417
+ onWebSearchTimeoutSecondsChange: (timeoutSeconds) => {
5418
+ this.settingsManager.setWebSearchTimeoutSeconds(timeoutSeconds);
5419
+ },
5420
+ onWebSearchTavilyApiKeyAction: async (action) => {
5421
+ if (action === "clear") {
5422
+ this.settingsManager.setWebSearchTavilyApiKey(undefined);
5423
+ await this.settingsManager.flush();
5424
+ this.showStatus("Web Search Tool: Tavily API key cleared.");
5425
+ return "not configured";
5426
+ }
5427
+ const current = this.settingsManager.getWebSearchTavilyApiKey();
5428
+ const entered = await this.showExtensionInput("Web Search Tool: Tavily API key", current ?? "tvly-...");
5429
+ if (entered === undefined) {
5430
+ return this.settingsManager.isWebSearchTavilyApiKeyConfigured() ? "configured" : "not configured";
5431
+ }
5432
+ const normalized = entered.trim();
5433
+ if (!normalized) {
5434
+ this.showWarning("Tavily API key cannot be empty.");
5435
+ return this.settingsManager.isWebSearchTavilyApiKeyConfigured() ? "configured" : "not configured";
5436
+ }
5437
+ this.settingsManager.setWebSearchTavilyApiKey(normalized);
5438
+ await this.settingsManager.flush();
5439
+ this.showStatus("Web Search Tool: Tavily API key saved.");
5440
+ return "configured";
5441
+ },
5442
+ onWebSearchSearxngUrlAction: async (action) => {
5443
+ if (action === "clear") {
5444
+ this.settingsManager.setWebSearchSearxngUrl(undefined);
5445
+ await this.settingsManager.flush();
5446
+ this.showStatus("Web Search Tool: SearXNG base URL cleared.");
5447
+ return "not configured";
5448
+ }
5449
+ const current = this.settingsManager.getWebSearchSearxngUrl();
5450
+ const entered = await this.showExtensionInput("Web Search Tool: SearXNG base URL", current ?? "https://searx.example");
5451
+ if (entered === undefined) {
5452
+ return this.settingsManager.isWebSearchSearxngUrlConfigured() ? "configured" : "not configured";
5453
+ }
5454
+ const normalized = entered.trim();
5455
+ if (!normalized) {
5456
+ this.showWarning("SearXNG base URL cannot be empty.");
5457
+ return this.settingsManager.isWebSearchSearxngUrlConfigured() ? "configured" : "not configured";
5458
+ }
5459
+ try {
5460
+ const parsed = new URL(normalized);
5461
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
5462
+ throw new Error("SearXNG URL must use http or https.");
5463
+ }
5464
+ }
5465
+ catch (error) {
5466
+ this.showWarning(error instanceof Error ? error.message : "Invalid SearXNG URL.");
5467
+ return this.settingsManager.isWebSearchSearxngUrlConfigured() ? "configured" : "not configured";
5468
+ }
5469
+ this.settingsManager.setWebSearchSearxngUrl(normalized);
5470
+ await this.settingsManager.flush();
5471
+ this.showStatus("Web Search Tool: SearXNG base URL saved.");
5472
+ return "configured";
5473
+ },
5474
+ onGithubToolsNetworkEnabledChange: (enabled) => {
5475
+ this.settingsManager.setGithubToolsNetworkEnabled(enabled);
5476
+ },
5477
+ onGithubToolsTokenAction: async (action) => {
5478
+ if (action === "clear") {
5479
+ this.settingsManager.setGithubToolsToken(undefined);
5480
+ await this.settingsManager.flush();
5481
+ this.showStatus("Github tools: token cleared.");
5482
+ return "not configured";
5483
+ }
5484
+ const current = this.settingsManager.getGithubToolsToken();
5485
+ const entered = await this.showExtensionInput("Github tools: token", current ?? "ghp_...");
5486
+ if (entered === undefined) {
5487
+ return this.settingsManager.isGithubToolsTokenConfigured() ? "configured" : "not configured";
5488
+ }
5489
+ const normalized = entered.trim();
5490
+ if (!normalized) {
5491
+ this.showWarning("GitHub token cannot be empty.");
5492
+ return this.settingsManager.isGithubToolsTokenConfigured() ? "configured" : "not configured";
5493
+ }
5494
+ this.settingsManager.setGithubToolsToken(normalized);
5495
+ await this.settingsManager.flush();
5496
+ this.showStatus("Github tools: token saved.");
5497
+ return "configured";
5498
+ },
5373
5499
  onThinkingLevelChange: (level) => {
5374
5500
  this.session.setThinkingLevel(level);
5375
5501
  this.footer.invalidate();
@@ -10016,8 +10142,6 @@ export class InteractiveMode {
10016
10142
  let activeTool;
10017
10143
  const dispatchTimeoutMs = this.resolveSwarmDispatchTimeoutMs();
10018
10144
  let timedOut = false;
10019
- let timeoutHandle;
10020
- let detachStopListener;
10021
10145
  const delegatedFailureCauses = new Map();
10022
10146
  const accumulateFailureCauses = (raw) => {
10023
10147
  if (!raw || typeof raw !== "object")
@@ -10147,21 +10271,24 @@ export class InteractiveMode {
10147
10271
  });
10148
10272
  }
10149
10273
  });
10150
- try {
10151
- emitProgress({
10152
- phase: "booting subagent",
10153
- phaseState: "starting",
10154
- });
10155
- if (input.stopSignal?.aborted) {
10156
- return {
10157
- taskId: input.task.id,
10158
- status: "blocked",
10159
- error: "Swarm run interrupted.",
10160
- failureCause: "interrupted",
10161
- costUsd: this.estimateSwarmTaskCostUsd(input.task),
10162
- };
10163
- }
10164
- const promptPromise = swarmSession.prompt(prompt, {
10274
+ const protocolCorrectionPrompt = [
10275
+ "[SWARM_PROTOCOL_CORRECTION]",
10276
+ `Previous response for run_id="${input.meta.runId}" task_id="${input.task.id}" executed zero task tool calls.`,
10277
+ "Execute the required task tool call now. Do not return prose-only output.",
10278
+ `Required task call args: description="${safeDescription || input.task.id}", profile="${profile}", delegate_parallel_hint=${delegateParallelHint}, run_id="${input.meta.runId}", task_id="${input.task.id}".`,
10279
+ minDelegatesRequired > 0
10280
+ ? `Inside this single task call, emit at least ${minDelegatesRequired} independent <delegate_task> subtasks (target parallel fan-out up to ${delegateParallelHint}).`
10281
+ : "Keep execution focused in a single task call unless decomposition is clearly beneficial.",
10282
+ minDelegatesRequired > 0
10283
+ ? 'If safe decomposition is impossible, output exactly one line: DELEGATION_IMPOSSIBLE: <reason>.'
10284
+ : "If decomposition is not beneficial, continue with single-agent execution in the task call.",
10285
+ "If blocked, output exactly one line: BLOCKED: <reason>",
10286
+ "[/SWARM_PROTOCOL_CORRECTION]",
10287
+ ].join("\n");
10288
+ const runPromptAttempt = async (attemptPrompt) => {
10289
+ let timeoutHandle;
10290
+ let detachStopListener;
10291
+ const promptPromise = swarmSession.prompt(attemptPrompt, {
10165
10292
  expandPromptTemplates: false,
10166
10293
  skipIosmAutopilot: true,
10167
10294
  skipOrchestrationDirective: true,
@@ -10204,7 +10331,44 @@ export class InteractiveMode {
10204
10331
  input.stopSignal.addEventListener("abort", onAbort, { once: true });
10205
10332
  detachStopListener = () => input.stopSignal?.removeEventListener("abort", onAbort);
10206
10333
  });
10207
- await Promise.race([promptPromise, timeoutPromise, stopPromise]);
10334
+ try {
10335
+ await Promise.race([promptPromise, timeoutPromise, stopPromise]);
10336
+ }
10337
+ finally {
10338
+ detachStopListener?.();
10339
+ if (timeoutHandle) {
10340
+ clearTimeout(timeoutHandle);
10341
+ }
10342
+ }
10343
+ };
10344
+ try {
10345
+ emitProgress({
10346
+ phase: "booting subagent",
10347
+ phaseState: "starting",
10348
+ });
10349
+ if (input.stopSignal?.aborted) {
10350
+ return {
10351
+ taskId: input.task.id,
10352
+ status: "blocked",
10353
+ error: "Swarm run interrupted.",
10354
+ failureCause: "interrupted",
10355
+ costUsd: this.estimateSwarmTaskCostUsd(input.task),
10356
+ };
10357
+ }
10358
+ await runPromptAttempt(prompt);
10359
+ const firstAttemptOutput = chunks.join("\n\n").trim();
10360
+ const firstAttemptBlocked = /^\s*BLOCKED\s*:/im.test(firstAttemptOutput);
10361
+ const needsProtocolCorrection = !firstAttemptBlocked &&
10362
+ !input.stopSignal?.aborted &&
10363
+ taskToolCalls === 0 &&
10364
+ taskErrors.length === 0;
10365
+ if (needsProtocolCorrection) {
10366
+ emitProgress({
10367
+ phase: "protocol correction",
10368
+ phaseState: "running",
10369
+ });
10370
+ await runPromptAttempt(protocolCorrectionPrompt);
10371
+ }
10208
10372
  }
10209
10373
  catch (error) {
10210
10374
  return {
@@ -10220,10 +10384,6 @@ export class InteractiveMode {
10220
10384
  };
10221
10385
  }
10222
10386
  finally {
10223
- detachStopListener?.();
10224
- if (timeoutHandle) {
10225
- clearTimeout(timeoutHandle);
10226
- }
10227
10387
  unsubscribe();
10228
10388
  if (timedOut || swarmSession.isStreaming) {
10229
10389
  await swarmSession.abort().catch(() => {