iosm-cli 0.2.17 → 0.3.1

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 (72) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +7 -4
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +3 -0
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/settings-manager.d.ts +3 -0
  7. package/dist/core/settings-manager.d.ts.map +1 -1
  8. package/dist/core/settings-manager.js +11 -0
  9. package/dist/core/settings-manager.js.map +1 -1
  10. package/dist/core/settings.schema.json +21 -0
  11. package/dist/core/subagents.d.ts.map +1 -1
  12. package/dist/core/subagents.js +4 -0
  13. package/dist/core/subagents.js.map +1 -1
  14. package/dist/core/tools/task.d.ts.map +1 -1
  15. package/dist/core/tools/task.js +48 -26
  16. package/dist/core/tools/task.js.map +1 -1
  17. package/dist/iosm/guide.d.ts +14 -0
  18. package/dist/iosm/guide.d.ts.map +1 -1
  19. package/dist/iosm/guide.js +72 -1
  20. package/dist/iosm/guide.js.map +1 -1
  21. package/dist/iosm/index.d.ts +1 -1
  22. package/dist/iosm/index.d.ts.map +1 -1
  23. package/dist/iosm/index.js +1 -1
  24. package/dist/iosm/index.js.map +1 -1
  25. package/dist/iosm/init.d.ts.map +1 -1
  26. package/dist/iosm/init.js +20 -2
  27. package/dist/iosm/init.js.map +1 -1
  28. package/dist/main.d.ts.map +1 -1
  29. package/dist/main.js +16 -0
  30. package/dist/main.js.map +1 -1
  31. package/dist/modes/interactive/components/assistant-message.d.ts +7 -0
  32. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  33. package/dist/modes/interactive/components/assistant-message.js +77 -5
  34. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  35. package/dist/modes/interactive/components/custom-editor.d.ts +7 -0
  36. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  37. package/dist/modes/interactive/components/custom-editor.js +95 -10
  38. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  39. package/dist/modes/interactive/components/footer.d.ts +2 -0
  40. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  41. package/dist/modes/interactive/components/footer.js +49 -32
  42. package/dist/modes/interactive/components/footer.js.map +1 -1
  43. package/dist/modes/interactive/components/message-frame-border.d.ts +22 -0
  44. package/dist/modes/interactive/components/message-frame-border.d.ts.map +1 -0
  45. package/dist/modes/interactive/components/message-frame-border.js +51 -0
  46. package/dist/modes/interactive/components/message-frame-border.js.map +1 -0
  47. package/dist/modes/interactive/components/message-window.d.ts +25 -0
  48. package/dist/modes/interactive/components/message-window.d.ts.map +1 -0
  49. package/dist/modes/interactive/components/message-window.js +66 -0
  50. package/dist/modes/interactive/components/message-window.js.map +1 -0
  51. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  52. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  53. package/dist/modes/interactive/components/settings-selector.js +12 -0
  54. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  55. package/dist/modes/interactive/components/subagent-message.d.ts +12 -9
  56. package/dist/modes/interactive/components/subagent-message.d.ts.map +1 -1
  57. package/dist/modes/interactive/components/subagent-message.js +138 -136
  58. package/dist/modes/interactive/components/subagent-message.js.map +1 -1
  59. package/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  60. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  61. package/dist/modes/interactive/components/tool-execution.js +33 -28
  62. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  63. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  64. package/dist/modes/interactive/components/user-message.js +10 -3
  65. package/dist/modes/interactive/components/user-message.js.map +1 -1
  66. package/dist/modes/interactive/interactive-mode.d.ts +12 -1
  67. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  68. package/dist/modes/interactive/interactive-mode.js +551 -161
  69. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  70. package/docs/getting-started.md +3 -3
  71. package/docs/interactive-mode.md +1 -1
  72. package/package.json +1 -1
@@ -15,7 +15,6 @@ 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";
19
18
  import { loadModelsDevProviderCatalog, } from "../../core/models-dev-provider-catalog.js";
20
19
  import { ModelRegistry } from "../../core/model-registry.js";
21
20
  import { MODELS_DEV_PROVIDERS } from "../../core/models-dev-providers.js";
@@ -67,6 +66,7 @@ import { ExtensionSelectorComponent } from "./components/extension-selector.js";
67
66
  import { FooterComponent } from "./components/footer.js";
68
67
  import { appKey, appKeyHint, editorKey } from "./components/keybinding-hints.js";
69
68
  import { LoginDialogComponent } from "./components/login-dialog.js";
69
+ import { MessageWindow } from "./components/message-window.js";
70
70
  import { McpSelectorComponent } from "./components/mcp-selector.js";
71
71
  import { ModelSelectorComponent } from "./components/model-selector.js";
72
72
  import { OAuthSelectorComponent } from "./components/oauth-selector.js";
@@ -74,7 +74,6 @@ import { ScopedModelsSelectorComponent } from "./components/scoped-models-select
74
74
  import { SessionSelectorComponent } from "./components/session-selector.js";
75
75
  import { SettingsSelectorComponent } from "./components/settings-selector.js";
76
76
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
77
- import { SubagentMessageComponent, } from "./components/subagent-message.js";
78
77
  import { TaskPlanMessageComponent } from "./components/task-plan-message.js";
79
78
  import { ToolExecutionComponent } from "./components/tool-execution.js";
80
79
  import { TreeSelectorComponent } from "./components/tree-selector.js";
@@ -94,16 +93,16 @@ export function resolveStreamingSubmissionMode(input) {
94
93
  }
95
94
  function parseRequestedParallelAgentCount(text) {
96
95
  const patterns = [
97
- /(\d+)\s+(?:parallel|concurrent)\s+agents?/i,
98
- /(\d+)\s+agents?/i,
99
- /(\d+)\s+паралл[\p{L}\p{N}_-]*\s+агент[\p{L}\p{N}_-]*/iu,
100
- /(\d+)\s+агент[\p{L}\p{N}_-]*/iu,
96
+ /--agents\s+(\d+)\b/i,
97
+ /(?:^|\s)agents\s*[:=]\s*(\d+)\b/i,
98
+ /<orchestrate[^>]*\bagents\s*=\s*["']?(\d+)["']?/i,
99
+ /"agents"\s*:\s*(\d+)/i,
101
100
  ];
102
101
  for (const pattern of patterns) {
103
102
  const match = text.match(pattern);
104
103
  const parsed = match?.[1] ? Number.parseInt(match[1], 10) : Number.NaN;
105
104
  if (Number.isInteger(parsed) && parsed >= 1) {
106
- return Math.max(1, Math.min(MAX_ORCHESTRATION_AGENTS, parsed));
105
+ return parsed;
107
106
  }
108
107
  }
109
108
  return undefined;
@@ -871,11 +870,125 @@ function parseSubagentDelegateItems(value) {
871
870
  if (!index || !description || !profile || !status)
872
871
  continue;
873
872
  parsed.push({ index, description, profile, status });
874
- if (parsed.length >= 20)
875
- break;
876
873
  }
877
874
  return parsed;
878
875
  }
876
+ function summarizeDelegateProgress(input) {
877
+ if (Array.isArray(input.delegateItems) && input.delegateItems.length > 0) {
878
+ let done = 0;
879
+ let failed = 0;
880
+ let running = 0;
881
+ let pending = 0;
882
+ for (const item of input.delegateItems) {
883
+ switch (item.status) {
884
+ case "done":
885
+ done += 1;
886
+ break;
887
+ case "failed":
888
+ failed += 1;
889
+ break;
890
+ case "running":
891
+ running += 1;
892
+ break;
893
+ default:
894
+ pending += 1;
895
+ break;
896
+ }
897
+ }
898
+ return { total: input.delegateItems.length, done, failed, running, pending };
899
+ }
900
+ if (typeof input.delegatedTasks === "number" && Number.isFinite(input.delegatedTasks) && input.delegatedTasks > 0) {
901
+ const total = Math.max(0, Math.floor(input.delegatedTasks));
902
+ const done = typeof input.delegatedSucceeded === "number" && Number.isFinite(input.delegatedSucceeded)
903
+ ? Math.max(0, Math.floor(input.delegatedSucceeded))
904
+ : 0;
905
+ const failed = typeof input.delegatedFailed === "number" && Number.isFinite(input.delegatedFailed)
906
+ ? Math.max(0, Math.floor(input.delegatedFailed))
907
+ : 0;
908
+ const running = Math.max(0, total - done - failed);
909
+ return { total, done, failed, running, pending: 0 };
910
+ }
911
+ return { total: 0, done: 0, failed: 0, running: 0, pending: 0 };
912
+ }
913
+ function formatOrchestrationToolProgress(started, completed) {
914
+ if (started <= 0)
915
+ return "tools -";
916
+ return `tools ${Math.max(0, completed)}/${Math.max(0, started)}`;
917
+ }
918
+ function formatDelegateStatusMarker(status) {
919
+ switch (status) {
920
+ case "done":
921
+ return "[x]";
922
+ case "running":
923
+ return "[>]";
924
+ case "failed":
925
+ return "[!]";
926
+ default:
927
+ return "[ ]";
928
+ }
929
+ }
930
+ function compactOneLineText(text, maxLength = 96) {
931
+ const normalized = text.replace(/\s+/g, " ").trim();
932
+ if (normalized.length <= maxLength)
933
+ return normalized;
934
+ return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;
935
+ }
936
+ function appendDelegateTreeRows(rows, input, options = {}) {
937
+ const indent = options.indent ?? " ";
938
+ if (Array.isArray(input.delegateItems) && input.delegateItems.length > 0) {
939
+ const hasRowLimit = typeof options.maxRows === "number" && Number.isFinite(options.maxRows) && options.maxRows > 0;
940
+ const visibleItems = hasRowLimit
941
+ ? input.delegateItems.slice(0, Math.max(1, Math.floor(options.maxRows)))
942
+ : input.delegateItems;
943
+ for (const [index, item] of visibleItems.entries()) {
944
+ const isLastVisible = index === visibleItems.length - 1;
945
+ const hasMoreHidden = input.delegateItems.length > visibleItems.length;
946
+ const branch = isLastVisible && !hasMoreHidden ? "└─" : "├─";
947
+ rows.push(`${indent}${branch} ${formatDelegateStatusMarker(item.status)} #${item.index} ${compactOneLineText(item.description, 56)} (${item.profile})`);
948
+ }
949
+ if (input.delegateItems.length > visibleItems.length) {
950
+ rows.push(`${indent}└─ … +${input.delegateItems.length - visibleItems.length} more delegate(s)`);
951
+ }
952
+ return;
953
+ }
954
+ const summary = summarizeDelegateProgress({
955
+ delegateItems: input.delegateItems,
956
+ delegatedTasks: input.delegatedTasks,
957
+ delegatedSucceeded: input.delegatedSucceeded,
958
+ delegatedFailed: input.delegatedFailed,
959
+ });
960
+ if (summary.total === 0)
961
+ return;
962
+ const summaryParts = [`${summary.done}/${summary.total} done`];
963
+ if (summary.failed > 0)
964
+ summaryParts.push(`${summary.failed} failed`);
965
+ if (summary.running > 0)
966
+ summaryParts.push(`${summary.running} running`);
967
+ if (summary.pending > 0)
968
+ summaryParts.push(`${summary.pending} pending`);
969
+ rows.push(`${indent}└─ delegates ${summaryParts.join(", ")}`);
970
+ if (typeof input.delegateIndex === "number" &&
971
+ input.delegateIndex > 0 &&
972
+ typeof input.delegateDescription === "string" &&
973
+ input.delegateDescription.trim().length > 0) {
974
+ const totalText = typeof input.delegateTotal === "number" && input.delegateTotal > 0 ? `/${Math.floor(input.delegateTotal)}` : "";
975
+ const profileText = typeof input.delegateProfile === "string" && input.delegateProfile.trim().length > 0
976
+ ? ` (${input.delegateProfile.trim()})`
977
+ : "";
978
+ rows.push(`${indent} └─ [>] active #${Math.floor(input.delegateIndex)}${totalText} ${compactOneLineText(input.delegateDescription, 56)}${profileText}`);
979
+ }
980
+ }
981
+ function formatElapsedClock(ms) {
982
+ const totalSeconds = Math.max(0, Math.floor(ms / 1000));
983
+ const hours = Math.floor(totalSeconds / 3600);
984
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
985
+ const seconds = totalSeconds % 60;
986
+ const pad2 = (value) => value.toString().padStart(2, "0");
987
+ if (hours > 0) {
988
+ return `${pad2(hours)}:${pad2(minutes)}:${pad2(seconds)}`;
989
+ }
990
+ return `${pad2(minutes)}:${pad2(seconds)}`;
991
+ }
879
992
  function hasDependencyCycle(agents, dependencies) {
880
993
  const graph = new Map();
881
994
  for (let agent = 1; agent <= agents; agent++) {
@@ -925,6 +1038,16 @@ export class InteractiveMode {
925
1038
  get settingsManager() {
926
1039
  return this.session.settingsManager;
927
1040
  }
1041
+ getCompactFooterSetting() {
1042
+ const manager = this.settingsManager;
1043
+ return typeof manager.getCompactFooter === "function" ? manager.getCompactFooter() : false;
1044
+ }
1045
+ setCompactFooterSetting(enabled) {
1046
+ const manager = this.settingsManager;
1047
+ if (typeof manager.setCompactFooter === "function") {
1048
+ manager.setCompactFooter(enabled);
1049
+ }
1050
+ }
928
1051
  constructor(session, options = {}) {
929
1052
  this.options = options;
930
1053
  this.isInitialized = false;
@@ -959,6 +1082,8 @@ export class InteractiveMode {
959
1082
  // Subagent execution tracking with live progress/metadata for task tool calls.
960
1083
  this.subagentComponents = new Map();
961
1084
  this.subagentElapsedTimer = undefined;
1085
+ this.orchestrationCompletedSubagents = [];
1086
+ this.orchestrationArchivedForTurn = false;
962
1087
  // Internal UI metadata emitted by runtime for orchestration rendering.
963
1088
  this.pendingInternalUserDisplayAliases = [];
964
1089
  this.pendingAssistantOrchestrationContexts = 0;
@@ -986,6 +1111,7 @@ export class InteractiveMode {
986
1111
  // IOSM automation state
987
1112
  this.iosmAutomationRun = undefined;
988
1113
  this.iosmVerificationSession = undefined;
1114
+ this.standardInitSession = undefined;
989
1115
  this.singularAnalysisSession = undefined;
990
1116
  // Extension UI state
991
1117
  this.extensionSelector = undefined;
@@ -1031,6 +1157,7 @@ export class InteractiveMode {
1031
1157
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
1032
1158
  this.headerContainer = new Container();
1033
1159
  this.chatContainer = new Container();
1160
+ this.orchestrationContainer = new Container();
1034
1161
  this.pendingMessagesContainer = new Container();
1035
1162
  this.statusContainer = new Container();
1036
1163
  this.widgetContainerAbove = new Container();
@@ -1048,6 +1175,7 @@ export class InteractiveMode {
1048
1175
  this.footerDataProvider = new FooterDataProvider();
1049
1176
  this.footer = new FooterComponent(session, this.footerDataProvider);
1050
1177
  this.footer.setAutoCompactEnabled(session.autoCompactionEnabled);
1178
+ this.footer.setCompactModeEnabled(this.getCompactFooterSetting());
1051
1179
  const profile = getAgentProfile(options.profile);
1052
1180
  this.activeProfileName = profile.name;
1053
1181
  this.profilePromptSuffix = profile.systemPromptAppend || undefined;
@@ -1967,6 +2095,7 @@ export class InteractiveMode {
1967
2095
  this.ui.addChild(this.chatContainer);
1968
2096
  this.ui.addChild(this.pendingMessagesContainer);
1969
2097
  this.ui.addChild(this.statusContainer);
2098
+ this.ui.addChild(this.orchestrationContainer);
1970
2099
  this.renderWidgets(); // Initialize with default spacer
1971
2100
  this.ui.addChild(this.widgetContainerAbove);
1972
2101
  this.ui.addChild(this.editorContainer);
@@ -3261,6 +3390,7 @@ export class InteractiveMode {
3261
3390
  this.session.isRetrying ||
3262
3391
  this.iosmAutomationRun !== undefined ||
3263
3392
  this.iosmVerificationSession !== undefined ||
3393
+ this.standardInitSession !== undefined ||
3264
3394
  this.swarmActiveRunId !== undefined ||
3265
3395
  this.singularAnalysisSession !== undefined ||
3266
3396
  queuedMessages.steering.length > 0 ||
@@ -3653,31 +3783,210 @@ export class InteractiveMode {
3653
3783
  this.editor.addToHistory?.(text);
3654
3784
  };
3655
3785
  }
3656
- updateRunningSubagentDisplay(subagent) {
3657
- subagent.component.update({
3658
- description: subagent.description,
3659
- profile: subagent.profile,
3660
- status: "running",
3661
- phase: subagent.phase ?? "running",
3662
- phaseState: subagent.phaseState,
3663
- cwd: subagent.cwd,
3664
- agent: subagent.agent,
3665
- lockKey: subagent.lockKey,
3666
- isolation: subagent.isolation,
3667
- activeTool: subagent.activeTool,
3668
- toolCallsStarted: subagent.toolCallsStarted,
3669
- toolCallsCompleted: subagent.toolCallsCompleted,
3670
- assistantMessages: subagent.assistantMessages,
3671
- delegatedTasks: subagent.delegatedTasks,
3672
- delegatedSucceeded: subagent.delegatedSucceeded,
3673
- delegatedFailed: subagent.delegatedFailed,
3674
- delegateIndex: subagent.delegateIndex,
3675
- delegateTotal: subagent.delegateTotal,
3676
- delegateDescription: subagent.delegateDescription,
3677
- delegateProfile: subagent.delegateProfile,
3678
- delegateItems: subagent.delegateItems,
3679
- durationMs: Date.now() - subagent.startTime,
3680
- });
3786
+ resetOrchestrationSummary() {
3787
+ this.orchestrationCompletedSubagents = [];
3788
+ this.orchestrationArchivedForTurn = false;
3789
+ this.refreshOrchestrationSummaryDisplay();
3790
+ }
3791
+ refreshOrchestrationSummaryDisplay() {
3792
+ this.orchestrationContainer.clear();
3793
+ const runningStates = [...this.subagentComponents.values()].sort((a, b) => a.startTime - b.startTime);
3794
+ const completedStates = [...this.orchestrationCompletedSubagents].sort((a, b) => b.finishedAt - a.finishedAt);
3795
+ const now = Date.now();
3796
+ const sectionTaskLimit = Number.POSITIVE_INFINITY;
3797
+ const runningRows = [];
3798
+ const queuedRows = [];
3799
+ const completedRows = [];
3800
+ const failedRows = [];
3801
+ const criticalPath = [];
3802
+ const sectionCounters = {
3803
+ running: { shown: 0, hidden: 0 },
3804
+ queued: { shown: 0, hidden: 0 },
3805
+ completed: { shown: 0, hidden: 0 },
3806
+ failed: { shown: 0, hidden: 0 },
3807
+ };
3808
+ const pushTaskBlock = (target, lines, counter) => {
3809
+ if (counter.shown >= sectionTaskLimit) {
3810
+ counter.hidden += 1;
3811
+ return;
3812
+ }
3813
+ target.push(...lines);
3814
+ counter.shown += 1;
3815
+ };
3816
+ for (const state of runningStates) {
3817
+ const elapsed = formatElapsedClock(now - state.startTime);
3818
+ const phase = compactOneLineText(state.phase?.trim() || "running", 56);
3819
+ const toolProgress = formatOrchestrationToolProgress(state.toolCallsStarted, state.toolCallsCompleted);
3820
+ const msgCount = Math.max(0, state.assistantMessages ?? 0);
3821
+ const isQueuedTask = state.phaseState === "queued";
3822
+ const delegateRows = [];
3823
+ appendDelegateTreeRows(delegateRows, state, { indent: " " });
3824
+ if (isQueuedTask) {
3825
+ pushTaskBlock(queuedRows, [
3826
+ `• ${state.profile} ${compactOneLineText(state.description, 52)} reason: ${phase}`,
3827
+ ...delegateRows,
3828
+ ], sectionCounters.queued);
3829
+ }
3830
+ else {
3831
+ if (criticalPath.length < 3) {
3832
+ criticalPath.push(state.profile);
3833
+ }
3834
+ pushTaskBlock(runningRows, [
3835
+ `▶ ${state.profile} ${compactOneLineText(state.description, 52)} ${elapsed} ${toolProgress} msgs ${msgCount} last: ${phase}`,
3836
+ ...delegateRows,
3837
+ ], sectionCounters.running);
3838
+ }
3839
+ }
3840
+ for (const state of completedStates) {
3841
+ const elapsed = formatElapsedClock(state.durationMs);
3842
+ const toolProgress = formatOrchestrationToolProgress(state.toolCallsStarted, state.toolCallsCompleted);
3843
+ const msgCount = Math.max(0, state.assistantMessages ?? 0);
3844
+ const delegateRows = [];
3845
+ appendDelegateTreeRows(delegateRows, state, { indent: " " });
3846
+ if (state.status === "error") {
3847
+ const row = `[!] ${state.profile} ${compactOneLineText(state.description, 46)} ${elapsed} ${toolProgress} msgs ${msgCount} error: ${compactOneLineText(state.errorMessage || "error", 46)}`;
3848
+ pushTaskBlock(failedRows, [row, ...delegateRows], sectionCounters.failed);
3849
+ continue;
3850
+ }
3851
+ const outputText = typeof state.outputLength === "number" && Number.isFinite(state.outputLength) && state.outputLength >= 0
3852
+ ? `${(state.outputLength / 1024).toFixed(1)}KB`
3853
+ : "-";
3854
+ const queueText = typeof state.waitMs === "number" && Number.isFinite(state.waitMs) && state.waitMs >= 0
3855
+ ? `queue ${Math.max(0, Math.floor(state.waitMs))}ms`
3856
+ : "queue -";
3857
+ const row = `[x] ${state.profile} ${compactOneLineText(state.description, 46)} ${elapsed} ${toolProgress} msgs ${msgCount} out ${outputText} ${queueText}`;
3858
+ pushTaskBlock(completedRows, [row, ...delegateRows], sectionCounters.completed);
3859
+ }
3860
+ if (sectionCounters.running.hidden > 0) {
3861
+ runningRows.push(`• … +${sectionCounters.running.hidden} more task(s)`);
3862
+ }
3863
+ if (sectionCounters.queued.hidden > 0) {
3864
+ queuedRows.push(`• … +${sectionCounters.queued.hidden} more task(s)`);
3865
+ }
3866
+ if (sectionCounters.completed.hidden > 0) {
3867
+ completedRows.push(`• … +${sectionCounters.completed.hidden} more task(s)`);
3868
+ }
3869
+ if (sectionCounters.failed.hidden > 0) {
3870
+ failedRows.push(`• … +${sectionCounters.failed.hidden} more task(s)`);
3871
+ }
3872
+ const runningCount = runningStates.filter((state) => state.phaseState !== "queued").length;
3873
+ const queuedCount = runningStates.filter((state) => state.phaseState === "queued").length;
3874
+ const doneCount = completedStates.filter((state) => state.status === "done").length;
3875
+ const failedCount = completedStates.filter((state) => state.status === "error").length;
3876
+ const totalCount = doneCount + runningCount + queuedCount + failedCount;
3877
+ if (totalCount === 0) {
3878
+ return;
3879
+ }
3880
+ const etaText = runningCount > 0 || queuedCount > 0 ? "--:--" : "done";
3881
+ const header = theme.fg("warning", "ORCH") +
3882
+ theme.fg("customMessageText", ` ${doneCount}/${totalCount} done`) +
3883
+ theme.fg("dim", " | ") +
3884
+ theme.fg("customMessageText", `${runningCount} running`) +
3885
+ theme.fg("dim", " | ") +
3886
+ theme.fg("customMessageText", `${queuedCount} queued`) +
3887
+ theme.fg("dim", " | ") +
3888
+ (failedCount > 0
3889
+ ? theme.fg("warning", `${failedCount} failed`)
3890
+ : theme.fg("customMessageText", `${failedCount} failed`)) +
3891
+ theme.fg("dim", " | ") +
3892
+ theme.fg("muted", `ETA ${etaText}`);
3893
+ const panelContent = new Container();
3894
+ panelContent.addChild(new Text(header, 0, 0));
3895
+ if (criticalPath.length > 0) {
3896
+ panelContent.addChild(new Text(theme.fg("muted", `CP: ${criticalPath.join(" -> ")}`), 0, 0));
3897
+ }
3898
+ const renderSection = (title, rows, color = "customMessageText") => {
3899
+ panelContent.addChild(new Spacer(1));
3900
+ panelContent.addChild(new Text(theme.fg("warning", title), 0, 0));
3901
+ if (rows.length === 0) {
3902
+ panelContent.addChild(new Text(theme.fg("muted", "• none"), 0, 0));
3903
+ return;
3904
+ }
3905
+ for (const row of rows) {
3906
+ panelContent.addChild(new Text(theme.fg(color, row), 0, 0));
3907
+ }
3908
+ };
3909
+ renderSection("RUNNING", runningRows);
3910
+ renderSection("QUEUED", queuedRows);
3911
+ renderSection("COMPLETED", completedRows, "success");
3912
+ renderSection("FAILED", failedRows, "warning");
3913
+ this.orchestrationContainer.addChild(new MessageWindow(panelContent, {
3914
+ label: "IOSM Orchestration",
3915
+ lineColor: "warning",
3916
+ labelColor: "warning",
3917
+ paddingY: 1,
3918
+ }));
3919
+ this.orchestrationContainer.addChild(new Spacer(1));
3920
+ }
3921
+ pushOrchestrationCompletedSubagent(state) {
3922
+ this.orchestrationCompletedSubagents.push(state);
3923
+ }
3924
+ archiveOrchestrationPanelToChatIfComplete() {
3925
+ if (this.orchestrationArchivedForTurn)
3926
+ return;
3927
+ if (this.subagentComponents.size > 0)
3928
+ return;
3929
+ if (this.orchestrationCompletedSubagents.length === 0)
3930
+ return;
3931
+ if (this.orchestrationContainer.children.length === 0)
3932
+ return;
3933
+ const snapshot = [...this.orchestrationContainer.children];
3934
+ this.orchestrationContainer.clear();
3935
+ const trailingNode = this.chatContainer.children.length > 0
3936
+ ? this.chatContainer.children[this.chatContainer.children.length - 1]
3937
+ : undefined;
3938
+ if (trailingNode) {
3939
+ this.chatContainer.removeChild(trailingNode);
3940
+ }
3941
+ this.chatContainer.addChild(new Spacer(1));
3942
+ for (const child of snapshot) {
3943
+ this.chatContainer.addChild(child);
3944
+ }
3945
+ if (trailingNode) {
3946
+ this.chatContainer.addChild(trailingNode);
3947
+ }
3948
+ this.orchestrationArchivedForTurn = true;
3949
+ }
3950
+ hasCompletedSubagentForToolCall(toolCallId) {
3951
+ return this.orchestrationCompletedSubagents.some((item) => item.toolCallId === toolCallId);
3952
+ }
3953
+ queuePendingTaskCallsFromAssistantMessage(message) {
3954
+ let changed = false;
3955
+ for (const content of message.content) {
3956
+ if (content.type !== "toolCall" || content.name !== "task")
3957
+ continue;
3958
+ if (typeof content.id !== "string" || content.id.trim().length === 0)
3959
+ continue;
3960
+ const toolCallId = content.id;
3961
+ const alreadyCompleted = typeof this.hasCompletedSubagentForToolCall === "function"
3962
+ ? this.hasCompletedSubagentForToolCall(toolCallId)
3963
+ : Array.isArray(this.orchestrationCompletedSubagents)
3964
+ ? this.orchestrationCompletedSubagents.some((item) => item?.toolCallId === toolCallId)
3965
+ : false;
3966
+ if (this.subagentComponents.has(toolCallId) || alreadyCompleted) {
3967
+ continue;
3968
+ }
3969
+ const args = content.arguments;
3970
+ this.subagentComponents.set(toolCallId, {
3971
+ startTime: Date.now(),
3972
+ profile: args.profile ?? "explore",
3973
+ description: args.description ?? "Running subtask",
3974
+ cwd: args.cwd,
3975
+ agent: args.agent,
3976
+ lockKey: args.lock_key,
3977
+ isolation: args.isolation,
3978
+ phase: "queued for execution",
3979
+ phaseState: "queued",
3980
+ toolCallsStarted: 0,
3981
+ toolCallsCompleted: 0,
3982
+ assistantMessages: 0,
3983
+ });
3984
+ changed = true;
3985
+ }
3986
+ if (!changed)
3987
+ return;
3988
+ this.ensureSubagentElapsedTimer();
3989
+ this.refreshOrchestrationSummaryDisplay();
3681
3990
  }
3682
3991
  getSwarmSubagentKey(runId, taskId) {
3683
3992
  return `swarm:${runId}:${taskId}`;
@@ -3688,14 +3997,13 @@ export class InteractiveMode {
3688
3997
  if (existing) {
3689
3998
  return existing;
3690
3999
  }
3691
- const profile = (input.profile?.trim() || this.resolveSwarmTaskProfile(input.task)).trim();
3692
- const info = {
4000
+ const state = {
4001
+ startTime: Date.now(),
4002
+ profile: (input.profile?.trim() || this.resolveSwarmTaskProfile(input.task)).trim(),
3693
4003
  description: input.task.brief || input.task.id,
3694
- profile,
3695
- status: "running",
4004
+ cwd: this.sessionManager.getCwd(),
3696
4005
  phase: "starting subagent",
3697
4006
  phaseState: "starting",
3698
- cwd: this.sessionManager.getCwd(),
3699
4007
  toolCallsStarted: 0,
3700
4008
  toolCallsCompleted: 0,
3701
4009
  assistantMessages: 0,
@@ -3703,34 +4011,9 @@ export class InteractiveMode {
3703
4011
  delegatedSucceeded: 0,
3704
4012
  delegatedFailed: 0,
3705
4013
  };
3706
- const component = new SubagentMessageComponent(info);
3707
- this.chatContainer.addChild(component);
3708
- const state = {
3709
- component,
3710
- startTime: Date.now(),
3711
- profile: info.profile,
3712
- description: info.description,
3713
- cwd: info.cwd,
3714
- agent: info.agent,
3715
- lockKey: info.lockKey,
3716
- isolation: info.isolation,
3717
- phase: info.phase,
3718
- phaseState: info.phaseState,
3719
- activeTool: info.activeTool,
3720
- toolCallsStarted: info.toolCallsStarted ?? 0,
3721
- toolCallsCompleted: info.toolCallsCompleted ?? 0,
3722
- assistantMessages: info.assistantMessages ?? 0,
3723
- delegatedTasks: info.delegatedTasks,
3724
- delegatedSucceeded: info.delegatedSucceeded,
3725
- delegatedFailed: info.delegatedFailed,
3726
- delegateIndex: info.delegateIndex,
3727
- delegateTotal: info.delegateTotal,
3728
- delegateDescription: info.delegateDescription,
3729
- delegateProfile: info.delegateProfile,
3730
- delegateItems: info.delegateItems,
3731
- };
3732
4014
  this.subagentComponents.set(key, state);
3733
4015
  this.ensureSubagentElapsedTimer();
4016
+ this.refreshOrchestrationSummaryDisplay();
3734
4017
  this.ui.requestRender();
3735
4018
  return state;
3736
4019
  }
@@ -3800,9 +4083,12 @@ export class InteractiveMode {
3800
4083
  : undefined;
3801
4084
  }
3802
4085
  if ("delegateItems" in progress) {
3803
- state.delegateItems = Array.isArray(progress.delegateItems) ? progress.delegateItems : undefined;
4086
+ const parsed = parseSubagentDelegateItems(progress.delegateItems);
4087
+ if (Array.isArray(parsed) && parsed.length > 0) {
4088
+ state.delegateItems = parsed;
4089
+ }
3804
4090
  }
3805
- this.updateRunningSubagentDisplay(state);
4091
+ this.refreshOrchestrationSummaryDisplay();
3806
4092
  this.ui.requestRender();
3807
4093
  }
3808
4094
  finalizeSwarmSubagentDisplay(input) {
@@ -3811,26 +4097,35 @@ export class InteractiveMode {
3811
4097
  if (!state)
3812
4098
  return;
3813
4099
  const durationMs = Date.now() - state.startTime;
3814
- state.component.update({
3815
- description: state.description,
3816
- profile: state.profile,
4100
+ this.pushOrchestrationCompletedSubagent({
4101
+ toolCallId: key,
4102
+ finishedAt: Date.now(),
3817
4103
  status: input.status,
4104
+ profile: state.profile,
4105
+ description: state.description,
3818
4106
  durationMs,
3819
- phaseState: state.phaseState,
3820
4107
  cwd: state.cwd,
3821
4108
  agent: state.agent,
3822
4109
  lockKey: state.lockKey,
3823
4110
  isolation: state.isolation,
4111
+ phase: state.phase,
4112
+ phaseState: state.phaseState,
3824
4113
  toolCallsStarted: state.toolCallsStarted,
3825
4114
  toolCallsCompleted: state.toolCallsCompleted,
3826
4115
  assistantMessages: state.assistantMessages,
3827
4116
  delegatedTasks: state.delegatedTasks,
3828
4117
  delegatedSucceeded: state.delegatedSucceeded,
3829
4118
  delegatedFailed: state.delegatedFailed,
4119
+ delegateIndex: state.delegateIndex,
4120
+ delegateTotal: state.delegateTotal,
4121
+ delegateDescription: state.delegateDescription,
4122
+ delegateProfile: state.delegateProfile,
4123
+ delegateItems: state.delegateItems,
3830
4124
  errorMessage: input.status === "error" ? input.errorMessage ?? "error" : undefined,
3831
4125
  });
3832
4126
  this.subagentComponents.delete(key);
3833
4127
  this.stopSubagentElapsedTimerIfIdle();
4128
+ this.refreshOrchestrationSummaryDisplay();
3834
4129
  this.ui.requestRender();
3835
4130
  }
3836
4131
  finalizeSwarmRunSubagentDisplays(runId, errorMessage) {
@@ -3855,9 +4150,7 @@ export class InteractiveMode {
3855
4150
  this.clearSubagentElapsedTimer();
3856
4151
  return;
3857
4152
  }
3858
- for (const subagent of this.subagentComponents.values()) {
3859
- this.updateRunningSubagentDisplay(subagent);
3860
- }
4153
+ this.refreshOrchestrationSummaryDisplay();
3861
4154
  this.ui.requestRender();
3862
4155
  }, 1000);
3863
4156
  }
@@ -3888,6 +4181,19 @@ export class InteractiveMode {
3888
4181
  this.currentTurnSawAssistantMessage = false;
3889
4182
  this.currentTurnSawTaskToolCall = false;
3890
4183
  this.turnAllowedToolSignatures.clear();
4184
+ this.orchestrationArchivedForTurn = false;
4185
+ if (typeof this.resetOrchestrationSummary === "function") {
4186
+ this.resetOrchestrationSummary();
4187
+ }
4188
+ else {
4189
+ this.orchestrationCompletedSubagents = [];
4190
+ if (typeof this.refreshOrchestrationSummaryDisplay === "function") {
4191
+ this.refreshOrchestrationSummaryDisplay();
4192
+ }
4193
+ else if (this.orchestrationContainer?.clear) {
4194
+ this.orchestrationContainer.clear();
4195
+ }
4196
+ }
3891
4197
  // Restore main escape handler if retry handler is still active
3892
4198
  // (retry success event fires later, but we need main handler now)
3893
4199
  if (this.retryEscapeHandler) {
@@ -3930,6 +4236,8 @@ export class InteractiveMode {
3930
4236
  this.pendingAssistantOrchestrationContexts -= 1;
3931
4237
  }
3932
4238
  this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
4239
+ this.streamingComponent.setExpanded(this.toolOutputExpanded);
4240
+ this.streamingComponent.setStreaming(true);
3933
4241
  this.streamingMessage = event.message;
3934
4242
  this.chatContainer.addChild(this.streamingComponent);
3935
4243
  this.streamingComponent.updateContent(this.sanitizeAssistantDisplayMessage(this.streamingMessage));
@@ -3974,6 +4282,10 @@ export class InteractiveMode {
3974
4282
  break;
3975
4283
  if (this.streamingComponent && event.message.role === "assistant") {
3976
4284
  this.streamingMessage = event.message;
4285
+ this.streamingComponent.setStreaming(false);
4286
+ if (typeof this.queuePendingTaskCallsFromAssistantMessage === "function") {
4287
+ this.queuePendingTaskCallsFromAssistantMessage(event.message);
4288
+ }
3977
4289
  let errorMessage;
3978
4290
  let interruptedStopReason;
3979
4291
  if (this.streamingMessage.stopReason === "aborted") {
@@ -4021,55 +4333,42 @@ export class InteractiveMode {
4021
4333
  if (event.toolName === "task") {
4022
4334
  this.currentTurnSawTaskToolCall = true;
4023
4335
  }
4024
- if (event.toolName === "task" && !this.subagentComponents.has(event.toolCallId)) {
4336
+ if (event.toolName === "task") {
4025
4337
  const staleTaskComponent = this.pendingTools.get(event.toolCallId);
4026
4338
  if (staleTaskComponent) {
4027
4339
  this.chatContainer.removeChild(staleTaskComponent);
4028
4340
  this.pendingTools.delete(event.toolCallId);
4029
4341
  }
4030
4342
  const args = event.args;
4031
- const description = args.description ?? "Running subtask";
4032
- const info = {
4033
- description,
4034
- profile: args.profile ?? "explore",
4035
- status: "running",
4036
- phase: "starting subagent",
4037
- phaseState: "starting",
4038
- cwd: args.cwd,
4039
- agent: args.agent,
4040
- lockKey: args.lock_key,
4041
- isolation: args.isolation,
4042
- toolCallsStarted: 0,
4043
- toolCallsCompleted: 0,
4044
- assistantMessages: 0,
4045
- };
4046
- const component = new SubagentMessageComponent(info);
4047
- this.chatContainer.addChild(component);
4048
- this.subagentComponents.set(event.toolCallId, {
4049
- component,
4050
- startTime: Date.now(),
4051
- profile: info.profile,
4052
- description: info.description,
4053
- cwd: info.cwd,
4054
- agent: info.agent,
4055
- lockKey: info.lockKey,
4056
- isolation: info.isolation,
4057
- phase: info.phase,
4058
- phaseState: info.phaseState,
4059
- activeTool: info.activeTool,
4060
- toolCallsStarted: info.toolCallsStarted ?? 0,
4061
- toolCallsCompleted: info.toolCallsCompleted ?? 0,
4062
- assistantMessages: info.assistantMessages ?? 0,
4063
- delegatedTasks: info.delegatedTasks,
4064
- delegatedSucceeded: info.delegatedSucceeded,
4065
- delegatedFailed: info.delegatedFailed,
4066
- delegateIndex: info.delegateIndex,
4067
- delegateTotal: info.delegateTotal,
4068
- delegateDescription: info.delegateDescription,
4069
- delegateProfile: info.delegateProfile,
4070
- delegateItems: info.delegateItems,
4071
- });
4343
+ const existing = this.subagentComponents.get(event.toolCallId);
4344
+ if (existing) {
4345
+ existing.profile = args.profile ?? existing.profile;
4346
+ existing.description = args.description ?? existing.description;
4347
+ existing.cwd = args.cwd ?? existing.cwd;
4348
+ existing.agent = args.agent ?? existing.agent;
4349
+ existing.lockKey = args.lock_key ?? existing.lockKey;
4350
+ existing.isolation = args.isolation ?? existing.isolation;
4351
+ existing.phase = "starting subagent";
4352
+ existing.phaseState = "starting";
4353
+ }
4354
+ else {
4355
+ this.subagentComponents.set(event.toolCallId, {
4356
+ startTime: Date.now(),
4357
+ profile: args.profile ?? "explore",
4358
+ description: args.description ?? "Running subtask",
4359
+ cwd: args.cwd,
4360
+ agent: args.agent,
4361
+ lockKey: args.lock_key,
4362
+ isolation: args.isolation,
4363
+ phase: "starting subagent",
4364
+ phaseState: "starting",
4365
+ toolCallsStarted: 0,
4366
+ toolCallsCompleted: 0,
4367
+ assistantMessages: 0,
4368
+ });
4369
+ }
4072
4370
  this.ensureSubagentElapsedTimer();
4371
+ this.refreshOrchestrationSummaryDisplay();
4073
4372
  this.ui.requestRender();
4074
4373
  }
4075
4374
  else if (event.toolName !== "task" && !this.pendingTools.has(event.toolCallId)) {
@@ -4121,6 +4420,15 @@ export class InteractiveMode {
4121
4420
  if (typeof progress.assistantMessages === "number") {
4122
4421
  subagent.assistantMessages = progress.assistantMessages;
4123
4422
  }
4423
+ if (typeof progress.delegatedTasks === "number") {
4424
+ subagent.delegatedTasks = Math.max(0, progress.delegatedTasks);
4425
+ }
4426
+ if (typeof progress.delegatedSucceeded === "number") {
4427
+ subagent.delegatedSucceeded = Math.max(0, progress.delegatedSucceeded);
4428
+ }
4429
+ if (typeof progress.delegatedFailed === "number") {
4430
+ subagent.delegatedFailed = Math.max(0, progress.delegatedFailed);
4431
+ }
4124
4432
  if ("delegateIndex" in progress) {
4125
4433
  subagent.delegateIndex =
4126
4434
  typeof progress.delegateIndex === "number" && progress.delegateIndex > 0
@@ -4146,7 +4454,10 @@ export class InteractiveMode {
4146
4454
  : undefined;
4147
4455
  }
4148
4456
  if ("delegateItems" in progress) {
4149
- subagent.delegateItems = parseSubagentDelegateItems(progress.delegateItems);
4457
+ const parsedDelegateItems = parseSubagentDelegateItems(progress.delegateItems);
4458
+ if (Array.isArray(parsedDelegateItems) && parsedDelegateItems.length > 0) {
4459
+ subagent.delegateItems = parsedDelegateItems;
4460
+ }
4150
4461
  }
4151
4462
  }
4152
4463
  else {
@@ -4155,7 +4466,7 @@ export class InteractiveMode {
4155
4466
  subagent.phase = text.trim();
4156
4467
  }
4157
4468
  }
4158
- this.updateRunningSubagentDisplay(subagent);
4469
+ this.refreshOrchestrationSummaryDisplay();
4159
4470
  this.ui.requestRender();
4160
4471
  break;
4161
4472
  }
@@ -4191,24 +4502,32 @@ export class InteractiveMode {
4191
4502
  subagent.delegatedTasks = delegatedTasks;
4192
4503
  subagent.delegatedSucceeded = delegatedSucceeded;
4193
4504
  subagent.delegatedFailed = delegatedFailed;
4194
- subagent.component.update({
4195
- description: subagent.description,
4196
- profile: subagent.profile,
4505
+ this.pushOrchestrationCompletedSubagent({
4506
+ toolCallId: event.toolCallId,
4507
+ finishedAt: Date.now(),
4197
4508
  status: event.isError ? "error" : "done",
4198
- outputLength,
4509
+ profile: subagent.profile,
4510
+ description: subagent.description,
4199
4511
  durationMs,
4200
- waitMs,
4201
- phaseState: subagent.phaseState,
4202
4512
  cwd: subagent.cwd,
4203
4513
  agent: subagent.agent,
4204
4514
  lockKey: subagent.lockKey,
4205
4515
  isolation: subagent.isolation,
4516
+ phase: subagent.phase,
4517
+ phaseState: subagent.phaseState,
4206
4518
  toolCallsStarted,
4207
4519
  toolCallsCompleted,
4208
4520
  assistantMessages,
4209
4521
  delegatedTasks,
4210
4522
  delegatedSucceeded,
4211
4523
  delegatedFailed,
4524
+ delegateIndex: subagent.delegateIndex,
4525
+ delegateTotal: subagent.delegateTotal,
4526
+ delegateDescription: subagent.delegateDescription,
4527
+ delegateProfile: subagent.delegateProfile,
4528
+ delegateItems: subagent.delegateItems,
4529
+ outputLength,
4530
+ waitMs,
4212
4531
  errorMessage: event.isError
4213
4532
  ? event.result?.content?.[0]?.text ?? "error"
4214
4533
  : undefined,
@@ -4220,6 +4539,7 @@ export class InteractiveMode {
4220
4539
  }
4221
4540
  this.subagentComponents.delete(event.toolCallId);
4222
4541
  this.stopSubagentElapsedTimerIfIdle();
4542
+ this.refreshOrchestrationSummaryDisplay();
4223
4543
  this.ui.requestRender();
4224
4544
  break;
4225
4545
  }
@@ -4248,6 +4568,15 @@ export class InteractiveMode {
4248
4568
  this.pendingTools.clear();
4249
4569
  this.subagentComponents.clear();
4250
4570
  this.clearSubagentElapsedTimer();
4571
+ if (typeof this.refreshOrchestrationSummaryDisplay === "function") {
4572
+ this.refreshOrchestrationSummaryDisplay();
4573
+ if (typeof this.archiveOrchestrationPanelToChatIfComplete === "function") {
4574
+ this.archiveOrchestrationPanelToChatIfComplete();
4575
+ }
4576
+ }
4577
+ else if (this.orchestrationContainer?.clear) {
4578
+ this.orchestrationContainer.clear();
4579
+ }
4251
4580
  this.currentTurnSawAssistantMessage = false;
4252
4581
  await this.checkShutdownRequested();
4253
4582
  this.ui.requestRender();
@@ -4515,6 +4844,7 @@ export class InteractiveMode {
4515
4844
  const displayMessage = this.sanitizeAssistantDisplayMessage(message);
4516
4845
  this.activeAssistantOrchestrationContext = previousOrchestrationContext;
4517
4846
  const assistantComponent = new AssistantMessageComponent(displayMessage, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
4847
+ assistantComponent.setExpanded(this.toolOutputExpanded);
4518
4848
  this.chatContainer.addChild(assistantComponent);
4519
4849
  break;
4520
4850
  }
@@ -4535,6 +4865,7 @@ export class InteractiveMode {
4535
4865
  */
4536
4866
  renderSessionContext(sessionContext, options = {}) {
4537
4867
  this.pendingTools.clear();
4868
+ this.resetOrchestrationSummary();
4538
4869
  this.pendingInternalUserDisplayAliases = [];
4539
4870
  this.pendingAssistantOrchestrationContexts = 0;
4540
4871
  this.activeAssistantOrchestrationContext = false;
@@ -4552,17 +4883,14 @@ export class InteractiveMode {
4552
4883
  if (content.type === "toolCall") {
4553
4884
  if (content.name === "task") {
4554
4885
  const args = content.arguments;
4555
- const subagent = new SubagentMessageComponent({
4886
+ pendingSubagentHistory.set(content.id, {
4556
4887
  description: args.description ?? "Running subtask",
4557
4888
  profile: args.profile ?? "explore",
4558
- status: "running",
4559
4889
  cwd: args.cwd,
4560
4890
  agent: args.agent,
4561
4891
  lockKey: args.lock_key,
4562
4892
  isolation: args.isolation,
4563
4893
  });
4564
- this.chatContainer.addChild(subagent);
4565
- pendingSubagentHistory.set(content.id, subagent);
4566
4894
  continue;
4567
4895
  }
4568
4896
  const component = new ToolExecutionComponent(content.name, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui);
@@ -4592,18 +4920,36 @@ export class InteractiveMode {
4592
4920
  const subagent = pendingSubagentHistory.get(message.toolCallId);
4593
4921
  if (subagent) {
4594
4922
  const details = message.details;
4595
- subagent.update({
4596
- description: typeof details?.description === "string" ? details.description : "Running subtask",
4597
- profile: typeof details?.profile === "string" ? details.profile : "explore",
4923
+ this.pushOrchestrationCompletedSubagent({
4924
+ toolCallId: message.toolCallId,
4925
+ finishedAt: Date.now(),
4598
4926
  status: message.isError ? "error" : "done",
4927
+ description: typeof details?.description === "string"
4928
+ ? details.description
4929
+ : subagent.description ?? "Running subtask",
4930
+ profile: typeof details?.profile === "string"
4931
+ ? details.profile
4932
+ : subagent.profile ?? "explore",
4933
+ durationMs: typeof details?.durationMs === "number" && Number.isFinite(details.durationMs)
4934
+ ? Math.max(0, Math.floor(details.durationMs))
4935
+ : 0,
4936
+ cwd: subagent.cwd,
4937
+ agent: subagent.agent,
4938
+ lockKey: subagent.lockKey,
4939
+ isolation: subagent.isolation,
4599
4940
  outputLength: typeof details?.outputLength === "number" ? details.outputLength : undefined,
4600
4941
  waitMs: typeof details?.waitMs === "number" ? details.waitMs : undefined,
4601
- toolCallsStarted: typeof details?.toolCallsStarted === "number" ? details.toolCallsStarted : undefined,
4602
- toolCallsCompleted: typeof details?.toolCallsCompleted === "number" ? details.toolCallsCompleted : undefined,
4603
- assistantMessages: typeof details?.assistantMessages === "number" ? details.assistantMessages : undefined,
4942
+ toolCallsStarted: typeof details?.toolCallsStarted === "number" ? details.toolCallsStarted : 0,
4943
+ toolCallsCompleted: typeof details?.toolCallsCompleted === "number" ? details.toolCallsCompleted : 0,
4944
+ assistantMessages: typeof details?.assistantMessages === "number" ? details.assistantMessages : 0,
4604
4945
  delegatedTasks: typeof details?.delegatedTasks === "number" ? details.delegatedTasks : undefined,
4605
4946
  delegatedSucceeded: typeof details?.delegatedSucceeded === "number" ? details.delegatedSucceeded : undefined,
4606
4947
  delegatedFailed: typeof details?.delegatedFailed === "number" ? details.delegatedFailed : undefined,
4948
+ delegateIndex: typeof details?.delegateIndex === "number" ? details.delegateIndex : undefined,
4949
+ delegateTotal: typeof details?.delegateTotal === "number" ? details.delegateTotal : undefined,
4950
+ delegateDescription: typeof details?.delegateDescription === "string" ? details.delegateDescription : undefined,
4951
+ delegateProfile: typeof details?.delegateProfile === "string" ? details.delegateProfile : undefined,
4952
+ delegateItems: parseSubagentDelegateItems(details?.delegateItems),
4607
4953
  errorMessage: message.isError
4608
4954
  ? message.content?.[0]?.text ?? "error"
4609
4955
  : undefined,
@@ -4659,6 +5005,8 @@ export class InteractiveMode {
4659
5005
  // =========================================================================
4660
5006
  handleCtrlC() {
4661
5007
  const now = Date.now();
5008
+ const queuedMessages = this.getAllQueuedMessages();
5009
+ const queuedMeta = queuedMessages.meta ?? [];
4662
5010
  if (this.swarmActiveRunId) {
4663
5011
  if (this.swarmStopRequested && now - this.lastSigintTime < 500) {
4664
5012
  void this.shutdown();
@@ -4677,6 +5025,21 @@ export class InteractiveMode {
4677
5025
  void this.interruptCurrentWork();
4678
5026
  return;
4679
5027
  }
5028
+ const hasInterruptibleWork = this.session.isStreaming ||
5029
+ this.session.isBashRunning ||
5030
+ this.session.isCompacting ||
5031
+ this.session.isRetrying ||
5032
+ this.iosmVerificationSession !== undefined ||
5033
+ this.standardInitSession !== undefined ||
5034
+ this.singularAnalysisSession !== undefined ||
5035
+ queuedMessages.steering.length > 0 ||
5036
+ queuedMessages.followUp.length > 0 ||
5037
+ queuedMeta.length > 0;
5038
+ if (hasInterruptibleWork) {
5039
+ this.lastSigintTime = now;
5040
+ void this.interruptCurrentWork();
5041
+ return;
5042
+ }
4680
5043
  if (now - this.lastSigintTime < 500) {
4681
5044
  void this.shutdown();
4682
5045
  }
@@ -5075,6 +5438,8 @@ export class InteractiveMode {
5075
5438
  const hasAutomationWork = this.iosmAutomationRun !== undefined;
5076
5439
  const verificationSession = this.iosmVerificationSession;
5077
5440
  const hasVerificationWork = verificationSession !== undefined;
5441
+ const standardInitSession = this.standardInitSession;
5442
+ const hasStandardInitWork = standardInitSession !== undefined;
5078
5443
  const hasSwarmWork = this.swarmActiveRunId !== undefined;
5079
5444
  const singularSession = this.singularAnalysisSession;
5080
5445
  const hasSingularWork = singularSession !== undefined;
@@ -5085,6 +5450,7 @@ export class InteractiveMode {
5085
5450
  if (!hasPendingQueuedMessages &&
5086
5451
  !hasAutomationWork &&
5087
5452
  !hasVerificationWork &&
5453
+ !hasStandardInitWork &&
5088
5454
  !hasSwarmWork &&
5089
5455
  !hasSingularWork &&
5090
5456
  !hasMainStreaming &&
@@ -5123,11 +5489,13 @@ export class InteractiveMode {
5123
5489
  ? "Stopping IOSM automation..."
5124
5490
  : hasVerificationWork
5125
5491
  ? "Stopping IOSM verification..."
5126
- : hasSwarmWork
5127
- ? "Stopping swarm run..."
5128
- : hasSingularWork
5129
- ? "Stopping /singular analysis..."
5130
- : "Stopping current run...");
5492
+ : hasStandardInitWork
5493
+ ? "Stopping /init..."
5494
+ : hasSwarmWork
5495
+ ? "Stopping swarm run..."
5496
+ : hasSingularWork
5497
+ ? "Stopping /singular analysis..."
5498
+ : "Stopping current run...");
5131
5499
  const abortPromises = [];
5132
5500
  if (hasMainStreaming) {
5133
5501
  abortPromises.push(this.session.abort());
@@ -5135,6 +5503,9 @@ export class InteractiveMode {
5135
5503
  if (verificationSession) {
5136
5504
  abortPromises.push(verificationSession.abort());
5137
5505
  }
5506
+ if (standardInitSession) {
5507
+ abortPromises.push(standardInitSession.abort());
5508
+ }
5138
5509
  if (singularSession) {
5139
5510
  abortPromises.push(singularSession.abort());
5140
5511
  }
@@ -5795,6 +6166,7 @@ export class InteractiveMode {
5795
6166
  autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
5796
6167
  quietStartup: this.settingsManager.getQuietStartup(),
5797
6168
  clearOnShrink: this.settingsManager.getClearOnShrink(),
6169
+ compactFooter: this.getCompactFooterSetting(),
5798
6170
  webSearchEnabled: this.settingsManager.getWebSearchEnabled(),
5799
6171
  webSearchProviderMode: this.settingsManager.getWebSearchProviderMode(),
5800
6172
  webSearchFallbackMode: this.settingsManager.getWebSearchFallbackMode(),
@@ -6003,6 +6375,11 @@ export class InteractiveMode {
6003
6375
  this.settingsManager.setClearOnShrink(enabled);
6004
6376
  this.ui.setClearOnShrink(enabled);
6005
6377
  },
6378
+ onCompactFooterChange: (enabled) => {
6379
+ this.setCompactFooterSetting(enabled);
6380
+ this.footer.setCompactModeEnabled?.(enabled);
6381
+ this.ui.requestRender();
6382
+ },
6006
6383
  onCancel: () => {
6007
6384
  done();
6008
6385
  this.ui.requestRender();
@@ -8595,6 +8972,7 @@ export class InteractiveMode {
8595
8972
  profile: "plan",
8596
8973
  enableTaskTool: false,
8597
8974
  });
8975
+ this.standardInitSession = session;
8598
8976
  let toolCallsStarted = 0;
8599
8977
  const chunks = [];
8600
8978
  const eventBridge = this.createIosmVerificationEventBridge({
@@ -9975,9 +10353,8 @@ export class InteractiveMode {
9975
10353
  const mentionTask = cleaned.length > 0 ? cleaned : userInput;
9976
10354
  const orchestrationAwareAgent = /orchestrator/i.test(mentionedAgent);
9977
10355
  const mentionMode = orchestrationAwareAgent ? "parallel" : "sequential";
9978
- const mentionMaxParallel = orchestrationAwareAgent ? MAX_ORCHESTRATION_PARALLEL : undefined;
9979
10356
  const mentionPrompt = [
9980
- `<orchestrate mode="${mentionMode}" agents="1"${mentionMaxParallel ? ` max_parallel="${mentionMaxParallel}"` : ""}>`,
10357
+ `<orchestrate mode="${mentionMode}" agents="1">`,
9981
10358
  `- agent 1: profile=${this.activeProfileName} cwd=${this.sessionManager.getCwd()} agent=${mentionedAgent}`,
9982
10359
  `task: ${mentionTask}`,
9983
10360
  "constraints:",
@@ -9986,8 +10363,8 @@ export class InteractiveMode {
9986
10363
  ...(orchestrationAwareAgent
9987
10364
  ? [
9988
10365
  "- Include delegate_parallel_hint in the task call.",
9989
- `- If user explicitly requested an agent count, set delegate_parallel_hint to that count (clamp 1..${MAX_SUBAGENT_DELEGATE_PARALLEL}).`,
9990
- `- Otherwise set delegate_parallel_hint based on complexity: simple=1, medium=3-6, complex/risky=7-${MAX_SUBAGENT_DELEGATE_PARALLEL}.`,
10366
+ "- If user explicitly requested an agent count, set delegate_parallel_hint to that count.",
10367
+ "- Otherwise set delegate_parallel_hint based on complexity: simple=1, medium=3-6, complex/risky=7+.",
9991
10368
  "- For non-trivial tasks, prefer delegate_parallel_hint >= 2 and split into independent <delegate_task> workstreams.",
9992
10369
  '- Prefer existing custom agents for delegated work when suitable (use <delegate_task agent="name" ...>).',
9993
10370
  "- If no existing custom agent fits, create focused delegate streams via profile-based <delegate_task> blocks.",
@@ -10019,10 +10396,10 @@ export class InteractiveMode {
10019
10396
  }
10020
10397
  const explicitRequested = parseRequestedParallelAgentCount(userInput);
10021
10398
  const hasComplexSignal = /\b(audit|security|hardening|refactor|migration|orchestrat|parallel|delegate|multi[-\s]?agent)\b/i.test(userInput);
10022
- const fallbackParallel = Math.max(1, Math.min(MAX_ORCHESTRATION_PARALLEL, explicitRequested ??
10399
+ const fallbackParallel = Math.max(1, explicitRequested ??
10023
10400
  (hasComplexSignal
10024
10401
  ? Math.max(details.requiredTopLevelTasks, 6)
10025
- : Math.max(details.requiredTopLevelTasks, 3))));
10402
+ : Math.max(details.requiredTopLevelTasks, 3)));
10026
10403
  this.showWarning(`META enforcement fallback: orchestration contract not satisfied (${details.launchedTopLevelTasks}/${details.requiredTopLevelTasks} task calls). Launching /swarm run.`);
10027
10404
  await this.runSwarmFromTask(userInput, { maxParallel: fallbackParallel });
10028
10405
  },
@@ -10050,6 +10427,8 @@ export class InteractiveMode {
10050
10427
  case "message_start":
10051
10428
  if (event.message.role === "assistant" && !hideAssistantText) {
10052
10429
  verifyStreamingComponent = new AssistantMessageComponent(undefined, false, this.getMarkdownThemeWithSettings());
10430
+ verifyStreamingComponent.setExpanded(this.toolOutputExpanded);
10431
+ verifyStreamingComponent.setStreaming(true);
10053
10432
  verifyStreamingMessage = event.message;
10054
10433
  this.chatContainer.addChild(verifyStreamingComponent);
10055
10434
  verifyStreamingComponent.updateContent(verifyStreamingMessage);
@@ -10072,6 +10451,7 @@ export class InteractiveMode {
10072
10451
  case "message_end":
10073
10452
  if (verifyStreamingComponent && event.message.role === "assistant") {
10074
10453
  verifyStreamingMessage = event.message;
10454
+ verifyStreamingComponent.setStreaming(false);
10075
10455
  verifyStreamingComponent.updateContent(verifyStreamingMessage);
10076
10456
  verifyStreamingComponent = undefined;
10077
10457
  verifyStreamingMessage = undefined;
@@ -10278,7 +10658,7 @@ export class InteractiveMode {
10278
10658
  const highRisk = /\b(refactor|rewrite|migration|migrate|breaking|rollback|security|auth|authentication|authorization|permission|payment|billing|schema|database|critical)\b/i.test(normalizedTask);
10279
10659
  const mediumRisk = /\b(cross[-\s]?module|architecture|infra|platform|multi[-\s]?file|integration|audit|hardening)\b/i.test(normalizedTask);
10280
10660
  let hint = input.mode === "parallel"
10281
- ? Math.max(2, Math.min(MAX_SUBAGENT_DELEGATE_PARALLEL, input.maxParallel ?? input.agents))
10661
+ ? Math.max(2, input.maxParallel ?? input.agents)
10282
10662
  : 1;
10283
10663
  if (highRisk) {
10284
10664
  hint = Math.max(hint, 7);
@@ -10290,12 +10670,12 @@ export class InteractiveMode {
10290
10670
  hint = Math.max(hint, 6);
10291
10671
  }
10292
10672
  if (input.hasDependencies) {
10293
- hint = Math.max(2, Math.min(hint, 6));
10673
+ hint = Math.max(2, hint);
10294
10674
  }
10295
10675
  if (input.hasLock) {
10296
- hint = Math.max(1, Math.min(hint, 4));
10676
+ hint = Math.max(1, hint);
10297
10677
  }
10298
- return Math.max(1, Math.min(MAX_SUBAGENT_DELEGATE_PARALLEL, hint));
10678
+ return Math.max(1, hint);
10299
10679
  }
10300
10680
  isEffectiveContractReady(contract) {
10301
10681
  const hasText = (value) => typeof value === "string" && value.trim().length > 0;
@@ -10518,8 +10898,8 @@ export class InteractiveMode {
10518
10898
  if (token === "--max-parallel") {
10519
10899
  const next = rest[index + 1];
10520
10900
  const parsed = next ? Number.parseInt(next, 10) : Number.NaN;
10521
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_ORCHESTRATION_PARALLEL) {
10522
- this.showWarning(`Invalid --max-parallel value (expected 1..${MAX_ORCHESTRATION_PARALLEL}).`);
10901
+ if (!Number.isInteger(parsed) || parsed < 1) {
10902
+ this.showWarning("Invalid --max-parallel value (expected integer >= 1).");
10523
10903
  return undefined;
10524
10904
  }
10525
10905
  maxParallel = parsed;
@@ -10692,16 +11072,15 @@ export class InteractiveMode {
10692
11072
  resolveSwarmMaxParallel(input) {
10693
11073
  const requested = input.requested;
10694
11074
  if (typeof requested === "number" && Number.isFinite(requested)) {
10695
- return Math.max(1, Math.min(MAX_ORCHESTRATION_PARALLEL, Math.floor(requested)));
11075
+ return Math.max(1, Math.floor(requested));
10696
11076
  }
10697
11077
  const totalTasks = Math.max(1, input.plan.tasks.length);
10698
11078
  const initialFanout = Math.max(1, input.plan.tasks.filter((task) => task.depends_on.length === 0).length);
10699
11079
  const parallelizable = input.plan.tasks.filter((task) => task.concurrency_class === "implementation" || task.concurrency_class === "tests")
10700
11080
  .length;
10701
11081
  const sourceFloor = input.source === "singular" ? 4 : 3;
10702
- const autoCap = Math.min(MAX_ORCHESTRATION_PARALLEL, 10);
10703
11082
  const heuristic = Math.max(sourceFloor, Math.ceil(totalTasks / 2), initialFanout, parallelizable >= 4 ? Math.ceil(parallelizable / 2) : 1);
10704
- return Math.max(1, Math.min(autoCap, heuristic));
11083
+ return Math.max(1, heuristic);
10705
11084
  }
10706
11085
  resolveSwarmDispatchTimeoutMs() {
10707
11086
  const dispatchTimeoutRaw = Number.parseInt(process.env.IOSM_SWARM_DISPATCH_TIMEOUT_MS ?? "", 10);
@@ -11803,8 +12182,8 @@ export class InteractiveMode {
11803
12182
  if (arg === "--agents") {
11804
12183
  const value = args[index + 1];
11805
12184
  const parsed = value ? Number.parseInt(value, 10) : Number.NaN;
11806
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_ORCHESTRATION_AGENTS) {
11807
- this.showWarning(`Invalid --agents value (expected 1..${MAX_ORCHESTRATION_AGENTS}).`);
12185
+ if (!Number.isInteger(parsed) || parsed < 1) {
12186
+ this.showWarning("Invalid --agents value (expected integer >= 1).");
11808
12187
  return undefined;
11809
12188
  }
11810
12189
  agents = parsed;
@@ -11814,8 +12193,8 @@ export class InteractiveMode {
11814
12193
  if (arg === "--max-parallel") {
11815
12194
  const value = args[index + 1];
11816
12195
  const parsed = value ? Number.parseInt(value, 10) : Number.NaN;
11817
- if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_ORCHESTRATION_PARALLEL) {
11818
- this.showWarning(`Invalid --max-parallel value (expected 1..${MAX_ORCHESTRATION_PARALLEL}).`);
12196
+ if (!Number.isInteger(parsed) || parsed < 1) {
12197
+ this.showWarning("Invalid --max-parallel value (expected integer >= 1).");
11819
12198
  return undefined;
11820
12199
  }
11821
12200
  maxParallel = parsed;
@@ -11935,7 +12314,7 @@ export class InteractiveMode {
11935
12314
  return undefined;
11936
12315
  }
11937
12316
  if (mode === "parallel" && maxParallel === undefined) {
11938
- maxParallel = Math.max(1, Math.min(MAX_ORCHESTRATION_PARALLEL, agents));
12317
+ maxParallel = Math.max(1, agents);
11939
12318
  }
11940
12319
  if (maxParallel !== undefined && maxParallel > agents) {
11941
12320
  maxParallel = agents;
@@ -12068,6 +12447,8 @@ export class InteractiveMode {
12068
12447
  "- when assignment lines include depends_on, still emit one task call per assignment; runtime enforces dependency gating",
12069
12448
  "- include delegate_parallel_hint from each assignment/task_call hint in every corresponding task tool call",
12070
12449
  "- for delegate_parallel_hint >= 2, split child work into nested <delegate_task> streams unless impossible",
12450
+ "- treat delegates strictly as child task calls; do not count plain tool invocations as delegated agents",
12451
+ "- assign explicit ownership domains per top-level agent to minimize overlap and duplicate findings",
12071
12452
  "- if nested split is impossible for a non-trivial stream, emit one line: DELEGATION_IMPOSSIBLE: <reason>",
12072
12453
  "- keep required orchestration task calls in foreground; do not set background=true unless user explicitly requested detached async runs",
12073
12454
  "- do not poll .iosm/subagents/background via bash/read during orchestration; wait for task results and then synthesize",
@@ -14148,6 +14529,9 @@ export class InteractiveMode {
14148
14529
  };
14149
14530
  }
14150
14531
  finally {
14532
+ if (this.standardInitSession === session) {
14533
+ this.standardInitSession = undefined;
14534
+ }
14151
14535
  unsubscribe();
14152
14536
  session.dispose();
14153
14537
  }
@@ -14173,7 +14557,7 @@ export class InteractiveMode {
14173
14557
  : "Mode: standard overwrite (existing guidance merged by agent when applicable).",
14174
14558
  `.iosm/agents: ready`,
14175
14559
  options.agentVerify
14176
- ? "Agent verification skipped in standard mode (available in profile `iosm`)."
14560
+ ? "Agent verification skipped in standard mode. Switch to profile `iosm` for IOSM artifacts."
14177
14561
  : "Verification: disabled.",
14178
14562
  ].join("\n"));
14179
14563
  }
@@ -14194,6 +14578,10 @@ export class InteractiveMode {
14194
14578
  this.showWarning("Cannot run /init while IOSM verification is already running.");
14195
14579
  return;
14196
14580
  }
14581
+ if (this.standardInitSession) {
14582
+ this.showWarning("Cannot run /init while another /init run is already in progress.");
14583
+ return;
14584
+ }
14197
14585
  const args = this.parseSlashArgs(text).slice(1);
14198
14586
  let force = false;
14199
14587
  let agentVerify = true;
@@ -14343,6 +14731,7 @@ ${priorityLines}
14343
14731
 
14344
14732
  ### Key workspace files
14345
14733
 
14734
+ - \`AGENTS.md\` — contributor/agent collaboration guide (auto-synced on init)
14346
14735
  - \`IOSM.md\` — agent playbook (auto-loaded as context each session)
14347
14736
  - \`iosm.yaml\` — cycle configuration${result.cycle ? `\n- \`.iosm/cycles/${cycleId}/cycle-report.json\`` : ""}
14348
14737
  - \`${shortPath(guidePath)}\`
@@ -14573,6 +14962,7 @@ The agent will automatically receive IOSM context on every turn.`;
14573
14962
  }
14574
14963
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
14575
14964
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
14965
+ this.footer.setCompactModeEnabled(this.getCompactFooterSetting());
14576
14966
  const themeName = this.settingsManager.getTheme();
14577
14967
  const themeResult = themeName ? setTheme(themeName, true) : { success: true };
14578
14968
  if (!themeResult.success) {
@@ -14911,7 +15301,7 @@ The agent will automatically receive IOSM context on every turn.`;
14911
15301
  | \`${cycleThinkingLevel}\` | Cycle thinking level |
14912
15302
  | \`${cycleModelForward}\` | Cycle models |
14913
15303
  | \`${selectModel}\` | Open model selector |
14914
- | \`${expandTools}\` | Toggle tool output expansion |
15304
+ | \`${expandTools}\` | Toggle tool/reasoning output expansion |
14915
15305
  | \`${toggleThinking}\` | Toggle thinking block visibility |
14916
15306
  | \`${externalEditor}\` | Edit message in external editor |
14917
15307
  | \`${steer}\` | Queue steer message |