iosm-cli 0.2.2 → 0.2.4

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 (54) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +32 -5
  3. package/dist/cli/args.d.ts +1 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +1 -1
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-profiles.d.ts +1 -1
  8. package/dist/core/agent-profiles.d.ts.map +1 -1
  9. package/dist/core/agent-profiles.js +9 -0
  10. package/dist/core/agent-profiles.js.map +1 -1
  11. package/dist/core/agent-session.d.ts +7 -0
  12. package/dist/core/agent-session.d.ts.map +1 -1
  13. package/dist/core/agent-session.js +58 -13
  14. package/dist/core/agent-session.js.map +1 -1
  15. package/dist/core/extensions/types.d.ts +1 -1
  16. package/dist/core/extensions/types.d.ts.map +1 -1
  17. package/dist/core/extensions/types.js.map +1 -1
  18. package/dist/core/orchestration-limits.d.ts +6 -0
  19. package/dist/core/orchestration-limits.d.ts.map +1 -0
  20. package/dist/core/orchestration-limits.js +6 -0
  21. package/dist/core/orchestration-limits.js.map +1 -0
  22. package/dist/core/sdk.d.ts +6 -1
  23. package/dist/core/sdk.d.ts.map +1 -1
  24. package/dist/core/sdk.js +96 -6
  25. package/dist/core/sdk.js.map +1 -1
  26. package/dist/core/subagents.d.ts.map +1 -1
  27. package/dist/core/subagents.js +21 -7
  28. package/dist/core/subagents.js.map +1 -1
  29. package/dist/core/tools/edit.d.ts +8 -4
  30. package/dist/core/tools/edit.d.ts.map +1 -1
  31. package/dist/core/tools/edit.js +18 -3
  32. package/dist/core/tools/edit.js.map +1 -1
  33. package/dist/core/tools/index.d.ts +9 -7
  34. package/dist/core/tools/index.d.ts.map +1 -1
  35. package/dist/core/tools/task.d.ts +10 -3
  36. package/dist/core/tools/task.d.ts.map +1 -1
  37. package/dist/core/tools/task.js +404 -149
  38. package/dist/core/tools/task.js.map +1 -1
  39. package/dist/core/tools/todo.d.ts +10 -10
  40. package/dist/core/tools/todo.d.ts.map +1 -1
  41. package/dist/core/tools/todo.js +135 -17
  42. package/dist/core/tools/todo.js.map +1 -1
  43. package/dist/modes/interactive/components/footer.d.ts +1 -1
  44. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  45. package/dist/modes/interactive/components/footer.js +8 -8
  46. package/dist/modes/interactive/components/footer.js.map +1 -1
  47. package/dist/modes/interactive/interactive-mode.d.ts +10 -0
  48. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  49. package/dist/modes/interactive/interactive-mode.js +243 -15
  50. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  51. package/docs/cli-reference.md +4 -0
  52. package/docs/configuration.md +5 -1
  53. package/docs/interactive-mode.md +5 -1
  54. package/package.json +1 -1
@@ -15,6 +15,7 @@ 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";
18
+ import { MAX_ORCHESTRATION_AGENTS, MAX_ORCHESTRATION_PARALLEL, MAX_SUBAGENT_DELEGATE_PARALLEL, } from "../../core/orchestration-limits.js";
18
19
  import { loadModelsDevProviderCatalog, } from "../../core/models-dev-provider-catalog.js";
19
20
  import { ModelRegistry } from "../../core/model-registry.js";
20
21
  import { MODELS_DEV_PROVIDERS } from "../../core/models-dev-providers.js";
@@ -75,6 +76,152 @@ import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMar
75
76
  function isExpandable(obj) {
76
77
  return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
77
78
  }
79
+ export function resolveStreamingSubmissionMode(input) {
80
+ if (input.configuredMode === "meta" &&
81
+ input.activeProfileName === "meta" &&
82
+ (input.activeSubagentCount > 0 || input.activeAssistantOrchestrationContext)) {
83
+ return "followUp";
84
+ }
85
+ return input.configuredMode;
86
+ }
87
+ function parseRequestedParallelAgentCount(text) {
88
+ const patterns = [
89
+ /(\d+)\s+(?:parallel|concurrent)\s+agents?/i,
90
+ /(\d+)\s+agents?/i,
91
+ /(\d+)\s+паралл\w*\s+агент\w*/i,
92
+ /(\d+)\s+агент\w*/i,
93
+ ];
94
+ for (const pattern of patterns) {
95
+ const match = text.match(pattern);
96
+ const parsed = match?.[1] ? Number.parseInt(match[1], 10) : Number.NaN;
97
+ if (Number.isInteger(parsed) && parsed >= 1) {
98
+ return Math.max(1, Math.min(MAX_ORCHESTRATION_AGENTS, parsed));
99
+ }
100
+ }
101
+ return undefined;
102
+ }
103
+ function deriveMetaRequiredTopLevelTaskCalls(userInput, taskPlanSnapshot) {
104
+ const requested = parseRequestedParallelAgentCount(userInput);
105
+ if (requested)
106
+ return requested;
107
+ if (!taskPlanSnapshot)
108
+ return undefined;
109
+ return Math.max(2, Math.min(3, taskPlanSnapshot.totalSteps));
110
+ }
111
+ function extractTaskToolErrorText(result) {
112
+ if (!result || typeof result !== "object")
113
+ return undefined;
114
+ const candidate = result;
115
+ if (typeof candidate.error === "string" && candidate.error.trim())
116
+ return candidate.error.trim();
117
+ if (typeof candidate.output === "string" && candidate.output.trim())
118
+ return candidate.output.trim();
119
+ return undefined;
120
+ }
121
+ function buildMetaParallelismCorrection(input) {
122
+ const validationFailure = input.taskToolError && /Validation failed for tool "task"/i.test(input.taskToolError)
123
+ ? input.taskToolError.split("\n").slice(0, 3).join("\n")
124
+ : undefined;
125
+ return [
126
+ "[META_PARALLELISM_CORRECTION]",
127
+ `Meta runtime correction: this run currently has ${input.launchedTopLevelTasks} top-level task call(s), but it should have at least ${input.requiredTopLevelTasks}.`,
128
+ "Stop manual sequential execution in the main agent and convert the execution graph into parallel task calls now.",
129
+ "Emit the missing top-level task calls in the same assistant response when branches are independent.",
130
+ "Each task tool call MUST include description and prompt.",
131
+ 'If you want a custom subagent, pass it via agent="name"; keep profile set to the capability profile, not the custom subagent name.',
132
+ input.rawRootDelegateBlocks && input.rawRootDelegateBlocks > 0
133
+ ? `You emitted ${input.rawRootDelegateBlocks} raw root-level <delegate_task> block(s). Those are not executed automatically in the parent session. Convert each one into an actual top-level task tool call now.`
134
+ : undefined,
135
+ "If a child workstream is still broad, require nested <delegate_task> fan-out instead of letting one child do everything alone.",
136
+ input.taskPlanSnapshot
137
+ ? `You already produced a complex plan with ${input.taskPlanSnapshot.totalSteps} steps. Turn that plan into parallel execution now.`
138
+ : undefined,
139
+ validationFailure ? `Previous task validation failure:\n${validationFailure}` : undefined,
140
+ "If you truly cannot split safely, output exactly one line: DELEGATION_IMPOSSIBLE: <precise reason>.",
141
+ "[/META_PARALLELISM_CORRECTION]",
142
+ ]
143
+ .filter((line) => typeof line === "string" && line.trim().length > 0)
144
+ .join("\n");
145
+ }
146
+ async function promptMetaWithParallelismGuard(input) {
147
+ let taskToolCalls = 0;
148
+ let nonTaskToolCalls = 0;
149
+ let taskPlanSnapshot;
150
+ let taskToolError;
151
+ let rawRootDelegateBlocks = 0;
152
+ let delegationImpossibleDeclared = false;
153
+ let correctionText;
154
+ const maybeScheduleCorrection = () => {
155
+ const requiredTopLevelTasks = deriveMetaRequiredTopLevelTaskCalls(input.userInput, taskPlanSnapshot);
156
+ if (!requiredTopLevelTasks || taskToolCalls >= requiredTopLevelTasks || delegationImpossibleDeclared) {
157
+ return;
158
+ }
159
+ const hasComplexPlan = !!taskPlanSnapshot;
160
+ const hasTaskFailure = !!taskToolError;
161
+ const hasRawRootDelegates = rawRootDelegateBlocks > 0;
162
+ if (!hasTaskFailure && !hasRawRootDelegates && nonTaskToolCalls < 1) {
163
+ return;
164
+ }
165
+ if (!hasComplexPlan && !hasTaskFailure && !hasRawRootDelegates && nonTaskToolCalls < 2) {
166
+ return;
167
+ }
168
+ correctionText = buildMetaParallelismCorrection({
169
+ requiredTopLevelTasks,
170
+ launchedTopLevelTasks: taskToolCalls,
171
+ taskPlanSnapshot,
172
+ taskToolError,
173
+ rawRootDelegateBlocks,
174
+ });
175
+ };
176
+ const unsubscribe = input.session.subscribe((event) => {
177
+ if (event.type === "message_end" && event.message.role === "custom") {
178
+ if (event.message.customType === TASK_PLAN_CUSTOM_TYPE && isTaskPlanSnapshot(event.message.details)) {
179
+ taskPlanSnapshot = event.message.details;
180
+ maybeScheduleCorrection();
181
+ }
182
+ return;
183
+ }
184
+ if (event.type === "message_end" && event.message.role === "assistant") {
185
+ const rawText = extractAssistantText(event.message);
186
+ rawRootDelegateBlocks += (rawText.match(/<delegate_task\b/gi) ?? []).length;
187
+ delegationImpossibleDeclared =
188
+ delegationImpossibleDeclared || /^\s*DELEGATION_IMPOSSIBLE\s*:/im.test(rawText);
189
+ maybeScheduleCorrection();
190
+ return;
191
+ }
192
+ if (event.type === "tool_execution_start") {
193
+ if (event.toolName === "task") {
194
+ taskToolCalls += 1;
195
+ }
196
+ else {
197
+ nonTaskToolCalls += 1;
198
+ }
199
+ maybeScheduleCorrection();
200
+ return;
201
+ }
202
+ if (event.type === "tool_execution_end" && event.toolName === "task" && event.isError) {
203
+ taskToolError = extractTaskToolErrorText(event.result) ?? taskToolError;
204
+ maybeScheduleCorrection();
205
+ }
206
+ });
207
+ try {
208
+ await input.session.prompt(input.userInput);
209
+ const requiredTopLevelTasks = deriveMetaRequiredTopLevelTaskCalls(input.userInput, taskPlanSnapshot);
210
+ if (correctionText &&
211
+ requiredTopLevelTasks &&
212
+ taskToolCalls < requiredTopLevelTasks &&
213
+ !delegationImpossibleDeclared) {
214
+ await input.session.prompt(correctionText, {
215
+ expandPromptTemplates: false,
216
+ skipOrchestrationDirective: true,
217
+ source: "interactive",
218
+ });
219
+ }
220
+ }
221
+ finally {
222
+ unsubscribe();
223
+ }
224
+ }
78
225
  const IOSM_PROFILE_ONLY_COMMANDS = new Set(["iosm", "cycle-list", "cycle-plan", "cycle-status", "cycle-report"]);
79
226
  const CHECKPOINT_LABEL_PREFIX = "checkpoint:";
80
227
  const INTERRUPT_ABORT_TIMEOUT_MS = 8_000;
@@ -411,6 +558,7 @@ export class InteractiveMode {
411
558
  // Streaming message tracking
412
559
  this.streamingComponent = undefined;
413
560
  this.streamingMessage = undefined;
561
+ this.currentTurnSawAssistantMessage = false;
414
562
  // Tool execution tracking: toolCallId -> component
415
563
  this.pendingTools = new Map();
416
564
  // Subagent execution tracking with live progress/metadata for task tool calls.
@@ -1245,6 +1393,8 @@ export class InteractiveMode {
1245
1393
  // Both are needed: fd for autocomplete, rg for grep tool and bash commands
1246
1394
  const [fdPath] = await Promise.all([ensureTool("fd"), ensureTool("rg")]);
1247
1395
  this.fdPath = fdPath;
1396
+ // Restore saved default model early so startup header/session are consistent after restart.
1397
+ await this.restoreSavedModelSelectionOnStartup();
1248
1398
  // Add header container as first child
1249
1399
  this.ui.addChild(this.headerContainer);
1250
1400
  // Add header with keybindings from config (unless silenced)
@@ -1481,7 +1631,10 @@ export class InteractiveMode {
1481
1631
  this.showError(`models.json error: ${modelsJsonError}`);
1482
1632
  }
1483
1633
  if (modelFallbackMessage) {
1484
- this.showWarning(modelFallbackMessage);
1634
+ const staleNoModelsWarning = this.session.model && modelFallbackMessage.startsWith("No models available.");
1635
+ if (!staleNoModelsWarning) {
1636
+ this.showWarning(modelFallbackMessage);
1637
+ }
1485
1638
  }
1486
1639
  if (!this.session.model) {
1487
1640
  this.showWarning("No model selected. Choose a model to start.");
@@ -2912,9 +3065,18 @@ export class InteractiveMode {
2912
3065
  // If streaming, use configured stream input mode (meta/followUp/steer)
2913
3066
  // This handles extension commands (execute immediately), prompt template expansion, and queueing
2914
3067
  if (this.session.isStreaming) {
3068
+ const streamingBehavior = resolveStreamingSubmissionMode({
3069
+ configuredMode: this.session.streamInputMode,
3070
+ activeProfileName: this.activeProfileName,
3071
+ activeSubagentCount: this.subagentComponents.size,
3072
+ activeAssistantOrchestrationContext: this.activeAssistantOrchestrationContext,
3073
+ });
2915
3074
  this.editor.addToHistory?.(text);
2916
3075
  this.editor.setText("");
2917
- await this.session.prompt(text, { streamingBehavior: this.session.streamInputMode });
3076
+ await this.session.prompt(text, { streamingBehavior });
3077
+ if (streamingBehavior !== this.session.streamInputMode) {
3078
+ this.showStatus("Queued follow-up until meta orchestration completes");
3079
+ }
2918
3080
  this.updatePendingMessagesDisplay();
2919
3081
  this.ui.requestRender();
2920
3082
  return;
@@ -2993,6 +3155,7 @@ export class InteractiveMode {
2993
3155
  this.footer.invalidate();
2994
3156
  switch (event.type) {
2995
3157
  case "agent_start":
3158
+ this.currentTurnSawAssistantMessage = false;
2996
3159
  // Restore main escape handler if retry handler is still active
2997
3160
  // (retry success event fires later, but we need main handler now)
2998
3161
  if (this.retryEscapeHandler) {
@@ -3029,6 +3192,7 @@ export class InteractiveMode {
3029
3192
  this.ui.requestRender();
3030
3193
  }
3031
3194
  else if (event.message.role === "assistant") {
3195
+ this.currentTurnSawAssistantMessage = true;
3032
3196
  this.activeAssistantOrchestrationContext = this.pendingAssistantOrchestrationContexts > 0;
3033
3197
  if (this.activeAssistantOrchestrationContext) {
3034
3198
  this.pendingAssistantOrchestrationContexts -= 1;
@@ -3079,6 +3243,7 @@ export class InteractiveMode {
3079
3243
  if (this.streamingComponent && event.message.role === "assistant") {
3080
3244
  this.streamingMessage = event.message;
3081
3245
  let errorMessage;
3246
+ let interruptedStopReason;
3082
3247
  if (this.streamingMessage.stopReason === "aborted") {
3083
3248
  const retryAttempt = this.session.retryAttempt;
3084
3249
  errorMessage =
@@ -3086,9 +3251,13 @@ export class InteractiveMode {
3086
3251
  ? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
3087
3252
  : "Operation aborted";
3088
3253
  this.streamingMessage.errorMessage = errorMessage;
3254
+ interruptedStopReason = "aborted";
3089
3255
  }
3090
3256
  this.streamingComponent.updateContent(this.sanitizeAssistantDisplayMessage(this.streamingMessage));
3091
3257
  if (this.streamingMessage.stopReason === "aborted" || this.streamingMessage.stopReason === "error") {
3258
+ if (this.streamingMessage.stopReason === "error") {
3259
+ interruptedStopReason = "error";
3260
+ }
3092
3261
  if (!errorMessage) {
3093
3262
  errorMessage = this.streamingMessage.errorMessage || "Error";
3094
3263
  }
@@ -3106,6 +3275,9 @@ export class InteractiveMode {
3106
3275
  component.setArgsComplete();
3107
3276
  }
3108
3277
  }
3278
+ if (interruptedStopReason) {
3279
+ this.showMetaModeInterruptionHint(interruptedStopReason);
3280
+ }
3109
3281
  this.streamingComponent = undefined;
3110
3282
  this.streamingMessage = undefined;
3111
3283
  this.activeAssistantOrchestrationContext = false;
@@ -3325,6 +3497,9 @@ export class InteractiveMode {
3325
3497
  break;
3326
3498
  }
3327
3499
  case "agent_end":
3500
+ if (this.activeProfileName === "meta" && !this.currentTurnSawAssistantMessage) {
3501
+ this.showMetaModeInterruptionHint("error");
3502
+ }
3328
3503
  if (this.loadingAnimation) {
3329
3504
  this.loadingAnimation.stop();
3330
3505
  this.loadingAnimation = undefined;
@@ -3338,6 +3513,7 @@ export class InteractiveMode {
3338
3513
  this.pendingTools.clear();
3339
3514
  this.subagentComponents.clear();
3340
3515
  this.clearSubagentElapsedTimer();
3516
+ this.currentTurnSawAssistantMessage = false;
3341
3517
  await this.checkShutdownRequested();
3342
3518
  this.ui.requestRender();
3343
3519
  break;
@@ -3454,7 +3630,12 @@ export class InteractiveMode {
3454
3630
  return;
3455
3631
  if (message.details.kind !== "orchestration_context")
3456
3632
  return;
3457
- this.pendingAssistantOrchestrationContexts += 1;
3633
+ // Hide assistant prose only for explicit legacy orchestration contracts.
3634
+ // META profile guidance should not suppress normal chat/task responses.
3635
+ const rawPrompt = message.details.rawPrompt ?? "";
3636
+ if (rawPrompt.includes("[ORCHESTRATION_DIRECTIVE]")) {
3637
+ this.pendingAssistantOrchestrationContexts += 1;
3638
+ }
3458
3639
  if (message.details.rawPrompt && message.details.displayText) {
3459
3640
  this.pendingInternalUserDisplayAliases.push({
3460
3641
  rawPrompt: message.details.rawPrompt,
@@ -3485,6 +3666,21 @@ export class InteractiveMode {
3485
3666
  this.lastStatusText = text;
3486
3667
  this.ui.requestRender();
3487
3668
  }
3669
+ showMetaModeInterruptionHint(reason) {
3670
+ if (this.activeProfileName !== "meta")
3671
+ return;
3672
+ const reasonText = reason === "error" ? "response failed unexpectedly" : "response was interrupted";
3673
+ this.showWarning(`META mode ${reasonText}. ` +
3674
+ "Please repeat your request following META profile rules: concrete repository task (goal + scope + constraints + expected output). " +
3675
+ "For conversational chat, switch profile to `full` (Shift+Tab).");
3676
+ }
3677
+ showMetaModeProfileHint() {
3678
+ if (this.activeProfileName !== "meta")
3679
+ return;
3680
+ this.showWarning("META mode is orchestration-first. " +
3681
+ "Send concrete repository tasks (goal + scope + constraints + expected output). " +
3682
+ "For conversational chat, switch profile to `full` (Shift+Tab).");
3683
+ }
3488
3684
  showProgressLine(message) {
3489
3685
  this.chatContainer.addChild(new Spacer(1));
3490
3686
  this.chatContainer.addChild(new Text(theme.fg("dim", message), 1, 0));
@@ -3885,6 +4081,7 @@ export class InteractiveMode {
3885
4081
  const nextActiveTools = this.getProfileToolNames(profile.name);
3886
4082
  this.session.setActiveToolsByName(nextActiveTools);
3887
4083
  this.session.setThinkingLevel(profile.thinkingLevel);
4084
+ this.session.setProfileName(profile.name);
3888
4085
  this.profilePromptSuffix = profile.systemPromptAppend || undefined;
3889
4086
  this.syncRuntimePromptSuffix();
3890
4087
  this.session.setIosmAutopilotEnabled(profile.name === "iosm");
@@ -3896,6 +4093,7 @@ export class InteractiveMode {
3896
4093
  this.updateEditorBorderColor();
3897
4094
  this.refreshBuiltInHeader();
3898
4095
  this.showStatus(`Profile: ${profile.name}`);
4096
+ this.showMetaModeProfileHint();
3899
4097
  }
3900
4098
  cycleProfile(direction) {
3901
4099
  if (this.session.isStreaming || this.session.isCompacting || this.iosmAutomationRun || this.iosmVerificationSession) {
@@ -5533,6 +5731,29 @@ export class InteractiveMode {
5533
5731
  await this.hydrateProviderModelsFromModelsDev(providerId);
5534
5732
  }
5535
5733
  }
5734
+ async restoreSavedModelSelectionOnStartup() {
5735
+ if (this.session.model)
5736
+ return;
5737
+ const defaultProvider = this.settingsManager.getDefaultProvider();
5738
+ const defaultModelId = this.settingsManager.getDefaultModel();
5739
+ if (!defaultProvider || !defaultModelId)
5740
+ return;
5741
+ if (!this.hasRegisteredProviderModels(defaultProvider)) {
5742
+ await this.hydrateProviderModelsFromModelsDev(defaultProvider);
5743
+ }
5744
+ const model = this.session.modelRegistry.find(defaultProvider, defaultModelId);
5745
+ if (!model)
5746
+ return;
5747
+ try {
5748
+ const apiKey = await this.session.modelRegistry.getApiKey(model);
5749
+ if (!apiKey)
5750
+ return;
5751
+ }
5752
+ catch {
5753
+ return;
5754
+ }
5755
+ this.session.agent.setModel(model);
5756
+ }
5536
5757
  getApiKeyLoginProviders(modelsDevProviders) {
5537
5758
  const providerNames = new Map();
5538
5759
  this.apiKeyProviderDisplayNames.clear();
@@ -8627,7 +8848,7 @@ export class InteractiveMode {
8627
8848
  const mentionTask = cleaned.length > 0 ? cleaned : userInput;
8628
8849
  const orchestrationAwareAgent = /orchestrator/i.test(mentionedAgent);
8629
8850
  const mentionMode = orchestrationAwareAgent ? "parallel" : "sequential";
8630
- const mentionMaxParallel = orchestrationAwareAgent ? 20 : undefined;
8851
+ const mentionMaxParallel = orchestrationAwareAgent ? MAX_ORCHESTRATION_PARALLEL : undefined;
8631
8852
  const mentionPrompt = [
8632
8853
  `<orchestrate mode="${mentionMode}" agents="1"${mentionMaxParallel ? ` max_parallel="${mentionMaxParallel}"` : ""}>`,
8633
8854
  `- agent 1: profile=${this.activeProfileName} cwd=${this.sessionManager.getCwd()} agent=${mentionedAgent}`,
@@ -8638,8 +8859,8 @@ export class InteractiveMode {
8638
8859
  ...(orchestrationAwareAgent
8639
8860
  ? [
8640
8861
  "- Include delegate_parallel_hint in the task call.",
8641
- "- If user explicitly requested an agent count, set delegate_parallel_hint to that count (clamp 1..10).",
8642
- "- Otherwise set delegate_parallel_hint based on complexity: simple=1, medium=3-6, complex/risky=7-10.",
8862
+ `- If user explicitly requested an agent count, set delegate_parallel_hint to that count (clamp 1..${MAX_SUBAGENT_DELEGATE_PARALLEL}).`,
8863
+ `- Otherwise set delegate_parallel_hint based on complexity: simple=1, medium=3-6, complex/risky=7-${MAX_SUBAGENT_DELEGATE_PARALLEL}.`,
8643
8864
  "- For non-trivial tasks, prefer delegate_parallel_hint >= 2 and split into independent <delegate_task> workstreams.",
8644
8865
  '- Prefer existing custom agents for delegated work when suitable (use <delegate_task agent="name" ...>).',
8645
8866
  "- If no existing custom agent fits, create focused delegate streams via profile-based <delegate_task> blocks.",
@@ -8654,6 +8875,13 @@ export class InteractiveMode {
8654
8875
  });
8655
8876
  return;
8656
8877
  }
8878
+ if (this.activeProfileName === "meta") {
8879
+ await promptMetaWithParallelismGuard({
8880
+ session: this.session,
8881
+ userInput,
8882
+ });
8883
+ return;
8884
+ }
8657
8885
  await this.session.prompt(userInput);
8658
8886
  }
8659
8887
  createIosmVerificationEventBridge(options) {
@@ -9109,8 +9337,8 @@ export class InteractiveMode {
9109
9337
  if (token === "--max-parallel") {
9110
9338
  const next = rest[index + 1];
9111
9339
  const parsed = next ? Number.parseInt(next, 10) : Number.NaN;
9112
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > 20) {
9113
- this.showWarning("Invalid --max-parallel value (expected 1..20).");
9340
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_ORCHESTRATION_PARALLEL) {
9341
+ this.showWarning(`Invalid --max-parallel value (expected 1..${MAX_ORCHESTRATION_PARALLEL}).`);
9114
9342
  return undefined;
9115
9343
  }
9116
9344
  maxParallel = parsed;
@@ -9627,7 +9855,7 @@ export class InteractiveMode {
9627
9855
  index: indexInfo.index,
9628
9856
  });
9629
9857
  const runId = `swarm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
9630
- const maxParallel = Math.max(1, Math.min(20, options.maxParallel ?? 3));
9858
+ const maxParallel = Math.max(1, Math.min(MAX_ORCHESTRATION_PARALLEL, options.maxParallel ?? 3));
9631
9859
  const meta = this.buildSwarmRunMeta({
9632
9860
  runId,
9633
9861
  source: "plain",
@@ -9671,7 +9899,7 @@ export class InteractiveMode {
9671
9899
  index: indexInfo.index,
9672
9900
  });
9673
9901
  const runId = `swarm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
9674
- const maxParallel = Math.max(1, Math.min(20, input.maxParallel ?? 3));
9902
+ const maxParallel = Math.max(1, Math.min(MAX_ORCHESTRATION_PARALLEL, input.maxParallel ?? 3));
9675
9903
  const meta = this.buildSwarmRunMeta({
9676
9904
  runId,
9677
9905
  source: "singular",
@@ -9941,8 +10169,8 @@ export class InteractiveMode {
9941
10169
  if (arg === "--agents") {
9942
10170
  const value = args[index + 1];
9943
10171
  const parsed = value ? Number.parseInt(value, 10) : Number.NaN;
9944
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > 20) {
9945
- this.showWarning("Invalid --agents value (expected 1..20).");
10172
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_ORCHESTRATION_AGENTS) {
10173
+ this.showWarning(`Invalid --agents value (expected 1..${MAX_ORCHESTRATION_AGENTS}).`);
9946
10174
  return undefined;
9947
10175
  }
9948
10176
  agents = parsed;
@@ -9952,8 +10180,8 @@ export class InteractiveMode {
9952
10180
  if (arg === "--max-parallel") {
9953
10181
  const value = args[index + 1];
9954
10182
  const parsed = value ? Number.parseInt(value, 10) : Number.NaN;
9955
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > 20) {
9956
- this.showWarning("Invalid --max-parallel value (expected 1..20).");
10183
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_ORCHESTRATION_PARALLEL) {
10184
+ this.showWarning(`Invalid --max-parallel value (expected 1..${MAX_ORCHESTRATION_PARALLEL}).`);
9957
10185
  return undefined;
9958
10186
  }
9959
10187
  maxParallel = parsed;
@@ -10484,7 +10712,7 @@ export class InteractiveMode {
10484
10712
  "You are generating a custom IOSM subagent specification.",
10485
10713
  "Return ONLY a single JSON object and no additional text.",
10486
10714
  'Allowed JSON keys: name, description, profile, tools, disallowed_tools, system_prompt, cwd, background, instructions.',
10487
- 'profile must be one of: full, plan, iosm, explore, iosm_analyst, iosm_verifier, cycle_planner.',
10715
+ 'profile must be one of: full, plan, iosm, meta, explore, iosm_analyst, iosm_verifier, cycle_planner.',
10488
10716
  "name must be short snake/kebab case, suitable for @mention.",
10489
10717
  "tools/disallowed_tools must be arrays of tool names when present.",
10490
10718
  "instructions must be a concise markdown body for the agent file.",