iosm-cli 0.2.16 → 0.3.0

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 (56) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +10 -2
  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 +26 -1
  9. package/dist/core/settings-manager.js.map +1 -1
  10. package/dist/core/settings.schema.json +92 -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/modes/interactive/components/assistant-message.d.ts +7 -0
  18. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  19. package/dist/modes/interactive/components/assistant-message.js +77 -5
  20. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  21. package/dist/modes/interactive/components/custom-editor.d.ts +7 -0
  22. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  23. package/dist/modes/interactive/components/custom-editor.js +95 -10
  24. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  25. package/dist/modes/interactive/components/footer.d.ts +2 -0
  26. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  27. package/dist/modes/interactive/components/footer.js +49 -32
  28. package/dist/modes/interactive/components/footer.js.map +1 -1
  29. package/dist/modes/interactive/components/message-frame-border.d.ts +22 -0
  30. package/dist/modes/interactive/components/message-frame-border.d.ts.map +1 -0
  31. package/dist/modes/interactive/components/message-frame-border.js +51 -0
  32. package/dist/modes/interactive/components/message-frame-border.js.map +1 -0
  33. package/dist/modes/interactive/components/message-window.d.ts +25 -0
  34. package/dist/modes/interactive/components/message-window.d.ts.map +1 -0
  35. package/dist/modes/interactive/components/message-window.js +66 -0
  36. package/dist/modes/interactive/components/message-window.js.map +1 -0
  37. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  38. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  39. package/dist/modes/interactive/components/settings-selector.js +12 -0
  40. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  41. package/dist/modes/interactive/components/subagent-message.d.ts +12 -9
  42. package/dist/modes/interactive/components/subagent-message.d.ts.map +1 -1
  43. package/dist/modes/interactive/components/subagent-message.js +138 -136
  44. package/dist/modes/interactive/components/subagent-message.js.map +1 -1
  45. package/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  46. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  47. package/dist/modes/interactive/components/tool-execution.js +33 -28
  48. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  49. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  50. package/dist/modes/interactive/components/user-message.js +10 -3
  51. package/dist/modes/interactive/components/user-message.js.map +1 -1
  52. package/dist/modes/interactive/interactive-mode.d.ts +11 -1
  53. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  54. package/dist/modes/interactive/interactive-mode.js +509 -155
  55. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  56. package/package.json +3 -3
@@ -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;
@@ -1031,6 +1156,7 @@ export class InteractiveMode {
1031
1156
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
1032
1157
  this.headerContainer = new Container();
1033
1158
  this.chatContainer = new Container();
1159
+ this.orchestrationContainer = new Container();
1034
1160
  this.pendingMessagesContainer = new Container();
1035
1161
  this.statusContainer = new Container();
1036
1162
  this.widgetContainerAbove = new Container();
@@ -1048,6 +1174,7 @@ export class InteractiveMode {
1048
1174
  this.footerDataProvider = new FooterDataProvider();
1049
1175
  this.footer = new FooterComponent(session, this.footerDataProvider);
1050
1176
  this.footer.setAutoCompactEnabled(session.autoCompactionEnabled);
1177
+ this.footer.setCompactModeEnabled(this.getCompactFooterSetting());
1051
1178
  const profile = getAgentProfile(options.profile);
1052
1179
  this.activeProfileName = profile.name;
1053
1180
  this.profilePromptSuffix = profile.systemPromptAppend || undefined;
@@ -1967,6 +2094,7 @@ export class InteractiveMode {
1967
2094
  this.ui.addChild(this.chatContainer);
1968
2095
  this.ui.addChild(this.pendingMessagesContainer);
1969
2096
  this.ui.addChild(this.statusContainer);
2097
+ this.ui.addChild(this.orchestrationContainer);
1970
2098
  this.renderWidgets(); // Initialize with default spacer
1971
2099
  this.ui.addChild(this.widgetContainerAbove);
1972
2100
  this.ui.addChild(this.editorContainer);
@@ -3653,31 +3781,210 @@ export class InteractiveMode {
3653
3781
  this.editor.addToHistory?.(text);
3654
3782
  };
3655
3783
  }
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
- });
3784
+ resetOrchestrationSummary() {
3785
+ this.orchestrationCompletedSubagents = [];
3786
+ this.orchestrationArchivedForTurn = false;
3787
+ this.refreshOrchestrationSummaryDisplay();
3788
+ }
3789
+ refreshOrchestrationSummaryDisplay() {
3790
+ this.orchestrationContainer.clear();
3791
+ const runningStates = [...this.subagentComponents.values()].sort((a, b) => a.startTime - b.startTime);
3792
+ const completedStates = [...this.orchestrationCompletedSubagents].sort((a, b) => b.finishedAt - a.finishedAt);
3793
+ const now = Date.now();
3794
+ const sectionTaskLimit = Number.POSITIVE_INFINITY;
3795
+ const runningRows = [];
3796
+ const queuedRows = [];
3797
+ const completedRows = [];
3798
+ const failedRows = [];
3799
+ const criticalPath = [];
3800
+ const sectionCounters = {
3801
+ running: { shown: 0, hidden: 0 },
3802
+ queued: { shown: 0, hidden: 0 },
3803
+ completed: { shown: 0, hidden: 0 },
3804
+ failed: { shown: 0, hidden: 0 },
3805
+ };
3806
+ const pushTaskBlock = (target, lines, counter) => {
3807
+ if (counter.shown >= sectionTaskLimit) {
3808
+ counter.hidden += 1;
3809
+ return;
3810
+ }
3811
+ target.push(...lines);
3812
+ counter.shown += 1;
3813
+ };
3814
+ for (const state of runningStates) {
3815
+ const elapsed = formatElapsedClock(now - state.startTime);
3816
+ const phase = compactOneLineText(state.phase?.trim() || "running", 56);
3817
+ const toolProgress = formatOrchestrationToolProgress(state.toolCallsStarted, state.toolCallsCompleted);
3818
+ const msgCount = Math.max(0, state.assistantMessages ?? 0);
3819
+ const isQueuedTask = state.phaseState === "queued";
3820
+ const delegateRows = [];
3821
+ appendDelegateTreeRows(delegateRows, state, { indent: " " });
3822
+ if (isQueuedTask) {
3823
+ pushTaskBlock(queuedRows, [
3824
+ `• ${state.profile} ${compactOneLineText(state.description, 52)} reason: ${phase}`,
3825
+ ...delegateRows,
3826
+ ], sectionCounters.queued);
3827
+ }
3828
+ else {
3829
+ if (criticalPath.length < 3) {
3830
+ criticalPath.push(state.profile);
3831
+ }
3832
+ pushTaskBlock(runningRows, [
3833
+ `▶ ${state.profile} ${compactOneLineText(state.description, 52)} ${elapsed} ${toolProgress} msgs ${msgCount} last: ${phase}`,
3834
+ ...delegateRows,
3835
+ ], sectionCounters.running);
3836
+ }
3837
+ }
3838
+ for (const state of completedStates) {
3839
+ const elapsed = formatElapsedClock(state.durationMs);
3840
+ const toolProgress = formatOrchestrationToolProgress(state.toolCallsStarted, state.toolCallsCompleted);
3841
+ const msgCount = Math.max(0, state.assistantMessages ?? 0);
3842
+ const delegateRows = [];
3843
+ appendDelegateTreeRows(delegateRows, state, { indent: " " });
3844
+ if (state.status === "error") {
3845
+ const row = `[!] ${state.profile} ${compactOneLineText(state.description, 46)} ${elapsed} ${toolProgress} msgs ${msgCount} error: ${compactOneLineText(state.errorMessage || "error", 46)}`;
3846
+ pushTaskBlock(failedRows, [row, ...delegateRows], sectionCounters.failed);
3847
+ continue;
3848
+ }
3849
+ const outputText = typeof state.outputLength === "number" && Number.isFinite(state.outputLength) && state.outputLength >= 0
3850
+ ? `${(state.outputLength / 1024).toFixed(1)}KB`
3851
+ : "-";
3852
+ const queueText = typeof state.waitMs === "number" && Number.isFinite(state.waitMs) && state.waitMs >= 0
3853
+ ? `queue ${Math.max(0, Math.floor(state.waitMs))}ms`
3854
+ : "queue -";
3855
+ const row = `[x] ${state.profile} ${compactOneLineText(state.description, 46)} ${elapsed} ${toolProgress} msgs ${msgCount} out ${outputText} ${queueText}`;
3856
+ pushTaskBlock(completedRows, [row, ...delegateRows], sectionCounters.completed);
3857
+ }
3858
+ if (sectionCounters.running.hidden > 0) {
3859
+ runningRows.push(`• … +${sectionCounters.running.hidden} more task(s)`);
3860
+ }
3861
+ if (sectionCounters.queued.hidden > 0) {
3862
+ queuedRows.push(`• … +${sectionCounters.queued.hidden} more task(s)`);
3863
+ }
3864
+ if (sectionCounters.completed.hidden > 0) {
3865
+ completedRows.push(`• … +${sectionCounters.completed.hidden} more task(s)`);
3866
+ }
3867
+ if (sectionCounters.failed.hidden > 0) {
3868
+ failedRows.push(`• … +${sectionCounters.failed.hidden} more task(s)`);
3869
+ }
3870
+ const runningCount = runningStates.filter((state) => state.phaseState !== "queued").length;
3871
+ const queuedCount = runningStates.filter((state) => state.phaseState === "queued").length;
3872
+ const doneCount = completedStates.filter((state) => state.status === "done").length;
3873
+ const failedCount = completedStates.filter((state) => state.status === "error").length;
3874
+ const totalCount = doneCount + runningCount + queuedCount + failedCount;
3875
+ if (totalCount === 0) {
3876
+ return;
3877
+ }
3878
+ const etaText = runningCount > 0 || queuedCount > 0 ? "--:--" : "done";
3879
+ const header = theme.fg("warning", "ORCH") +
3880
+ theme.fg("customMessageText", ` ${doneCount}/${totalCount} done`) +
3881
+ theme.fg("dim", " | ") +
3882
+ theme.fg("customMessageText", `${runningCount} running`) +
3883
+ theme.fg("dim", " | ") +
3884
+ theme.fg("customMessageText", `${queuedCount} queued`) +
3885
+ theme.fg("dim", " | ") +
3886
+ (failedCount > 0
3887
+ ? theme.fg("warning", `${failedCount} failed`)
3888
+ : theme.fg("customMessageText", `${failedCount} failed`)) +
3889
+ theme.fg("dim", " | ") +
3890
+ theme.fg("muted", `ETA ${etaText}`);
3891
+ const panelContent = new Container();
3892
+ panelContent.addChild(new Text(header, 0, 0));
3893
+ if (criticalPath.length > 0) {
3894
+ panelContent.addChild(new Text(theme.fg("muted", `CP: ${criticalPath.join(" -> ")}`), 0, 0));
3895
+ }
3896
+ const renderSection = (title, rows, color = "customMessageText") => {
3897
+ panelContent.addChild(new Spacer(1));
3898
+ panelContent.addChild(new Text(theme.fg("warning", title), 0, 0));
3899
+ if (rows.length === 0) {
3900
+ panelContent.addChild(new Text(theme.fg("muted", "• none"), 0, 0));
3901
+ return;
3902
+ }
3903
+ for (const row of rows) {
3904
+ panelContent.addChild(new Text(theme.fg(color, row), 0, 0));
3905
+ }
3906
+ };
3907
+ renderSection("RUNNING", runningRows);
3908
+ renderSection("QUEUED", queuedRows);
3909
+ renderSection("COMPLETED", completedRows, "success");
3910
+ renderSection("FAILED", failedRows, "warning");
3911
+ this.orchestrationContainer.addChild(new MessageWindow(panelContent, {
3912
+ label: "IOSM Orchestration",
3913
+ lineColor: "warning",
3914
+ labelColor: "warning",
3915
+ paddingY: 1,
3916
+ }));
3917
+ this.orchestrationContainer.addChild(new Spacer(1));
3918
+ }
3919
+ pushOrchestrationCompletedSubagent(state) {
3920
+ this.orchestrationCompletedSubagents.push(state);
3921
+ }
3922
+ archiveOrchestrationPanelToChatIfComplete() {
3923
+ if (this.orchestrationArchivedForTurn)
3924
+ return;
3925
+ if (this.subagentComponents.size > 0)
3926
+ return;
3927
+ if (this.orchestrationCompletedSubagents.length === 0)
3928
+ return;
3929
+ if (this.orchestrationContainer.children.length === 0)
3930
+ return;
3931
+ const snapshot = [...this.orchestrationContainer.children];
3932
+ this.orchestrationContainer.clear();
3933
+ const trailingNode = this.chatContainer.children.length > 0
3934
+ ? this.chatContainer.children[this.chatContainer.children.length - 1]
3935
+ : undefined;
3936
+ if (trailingNode) {
3937
+ this.chatContainer.removeChild(trailingNode);
3938
+ }
3939
+ this.chatContainer.addChild(new Spacer(1));
3940
+ for (const child of snapshot) {
3941
+ this.chatContainer.addChild(child);
3942
+ }
3943
+ if (trailingNode) {
3944
+ this.chatContainer.addChild(trailingNode);
3945
+ }
3946
+ this.orchestrationArchivedForTurn = true;
3947
+ }
3948
+ hasCompletedSubagentForToolCall(toolCallId) {
3949
+ return this.orchestrationCompletedSubagents.some((item) => item.toolCallId === toolCallId);
3950
+ }
3951
+ queuePendingTaskCallsFromAssistantMessage(message) {
3952
+ let changed = false;
3953
+ for (const content of message.content) {
3954
+ if (content.type !== "toolCall" || content.name !== "task")
3955
+ continue;
3956
+ if (typeof content.id !== "string" || content.id.trim().length === 0)
3957
+ continue;
3958
+ const toolCallId = content.id;
3959
+ const alreadyCompleted = typeof this.hasCompletedSubagentForToolCall === "function"
3960
+ ? this.hasCompletedSubagentForToolCall(toolCallId)
3961
+ : Array.isArray(this.orchestrationCompletedSubagents)
3962
+ ? this.orchestrationCompletedSubagents.some((item) => item?.toolCallId === toolCallId)
3963
+ : false;
3964
+ if (this.subagentComponents.has(toolCallId) || alreadyCompleted) {
3965
+ continue;
3966
+ }
3967
+ const args = content.arguments;
3968
+ this.subagentComponents.set(toolCallId, {
3969
+ startTime: Date.now(),
3970
+ profile: args.profile ?? "explore",
3971
+ description: args.description ?? "Running subtask",
3972
+ cwd: args.cwd,
3973
+ agent: args.agent,
3974
+ lockKey: args.lock_key,
3975
+ isolation: args.isolation,
3976
+ phase: "queued for execution",
3977
+ phaseState: "queued",
3978
+ toolCallsStarted: 0,
3979
+ toolCallsCompleted: 0,
3980
+ assistantMessages: 0,
3981
+ });
3982
+ changed = true;
3983
+ }
3984
+ if (!changed)
3985
+ return;
3986
+ this.ensureSubagentElapsedTimer();
3987
+ this.refreshOrchestrationSummaryDisplay();
3681
3988
  }
3682
3989
  getSwarmSubagentKey(runId, taskId) {
3683
3990
  return `swarm:${runId}:${taskId}`;
@@ -3688,14 +3995,13 @@ export class InteractiveMode {
3688
3995
  if (existing) {
3689
3996
  return existing;
3690
3997
  }
3691
- const profile = (input.profile?.trim() || this.resolveSwarmTaskProfile(input.task)).trim();
3692
- const info = {
3998
+ const state = {
3999
+ startTime: Date.now(),
4000
+ profile: (input.profile?.trim() || this.resolveSwarmTaskProfile(input.task)).trim(),
3693
4001
  description: input.task.brief || input.task.id,
3694
- profile,
3695
- status: "running",
4002
+ cwd: this.sessionManager.getCwd(),
3696
4003
  phase: "starting subagent",
3697
4004
  phaseState: "starting",
3698
- cwd: this.sessionManager.getCwd(),
3699
4005
  toolCallsStarted: 0,
3700
4006
  toolCallsCompleted: 0,
3701
4007
  assistantMessages: 0,
@@ -3703,34 +4009,9 @@ export class InteractiveMode {
3703
4009
  delegatedSucceeded: 0,
3704
4010
  delegatedFailed: 0,
3705
4011
  };
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
4012
  this.subagentComponents.set(key, state);
3733
4013
  this.ensureSubagentElapsedTimer();
4014
+ this.refreshOrchestrationSummaryDisplay();
3734
4015
  this.ui.requestRender();
3735
4016
  return state;
3736
4017
  }
@@ -3800,9 +4081,12 @@ export class InteractiveMode {
3800
4081
  : undefined;
3801
4082
  }
3802
4083
  if ("delegateItems" in progress) {
3803
- state.delegateItems = Array.isArray(progress.delegateItems) ? progress.delegateItems : undefined;
4084
+ const parsed = parseSubagentDelegateItems(progress.delegateItems);
4085
+ if (Array.isArray(parsed) && parsed.length > 0) {
4086
+ state.delegateItems = parsed;
4087
+ }
3804
4088
  }
3805
- this.updateRunningSubagentDisplay(state);
4089
+ this.refreshOrchestrationSummaryDisplay();
3806
4090
  this.ui.requestRender();
3807
4091
  }
3808
4092
  finalizeSwarmSubagentDisplay(input) {
@@ -3811,26 +4095,35 @@ export class InteractiveMode {
3811
4095
  if (!state)
3812
4096
  return;
3813
4097
  const durationMs = Date.now() - state.startTime;
3814
- state.component.update({
3815
- description: state.description,
3816
- profile: state.profile,
4098
+ this.pushOrchestrationCompletedSubagent({
4099
+ toolCallId: key,
4100
+ finishedAt: Date.now(),
3817
4101
  status: input.status,
4102
+ profile: state.profile,
4103
+ description: state.description,
3818
4104
  durationMs,
3819
- phaseState: state.phaseState,
3820
4105
  cwd: state.cwd,
3821
4106
  agent: state.agent,
3822
4107
  lockKey: state.lockKey,
3823
4108
  isolation: state.isolation,
4109
+ phase: state.phase,
4110
+ phaseState: state.phaseState,
3824
4111
  toolCallsStarted: state.toolCallsStarted,
3825
4112
  toolCallsCompleted: state.toolCallsCompleted,
3826
4113
  assistantMessages: state.assistantMessages,
3827
4114
  delegatedTasks: state.delegatedTasks,
3828
4115
  delegatedSucceeded: state.delegatedSucceeded,
3829
4116
  delegatedFailed: state.delegatedFailed,
4117
+ delegateIndex: state.delegateIndex,
4118
+ delegateTotal: state.delegateTotal,
4119
+ delegateDescription: state.delegateDescription,
4120
+ delegateProfile: state.delegateProfile,
4121
+ delegateItems: state.delegateItems,
3830
4122
  errorMessage: input.status === "error" ? input.errorMessage ?? "error" : undefined,
3831
4123
  });
3832
4124
  this.subagentComponents.delete(key);
3833
4125
  this.stopSubagentElapsedTimerIfIdle();
4126
+ this.refreshOrchestrationSummaryDisplay();
3834
4127
  this.ui.requestRender();
3835
4128
  }
3836
4129
  finalizeSwarmRunSubagentDisplays(runId, errorMessage) {
@@ -3855,9 +4148,7 @@ export class InteractiveMode {
3855
4148
  this.clearSubagentElapsedTimer();
3856
4149
  return;
3857
4150
  }
3858
- for (const subagent of this.subagentComponents.values()) {
3859
- this.updateRunningSubagentDisplay(subagent);
3860
- }
4151
+ this.refreshOrchestrationSummaryDisplay();
3861
4152
  this.ui.requestRender();
3862
4153
  }, 1000);
3863
4154
  }
@@ -3888,6 +4179,19 @@ export class InteractiveMode {
3888
4179
  this.currentTurnSawAssistantMessage = false;
3889
4180
  this.currentTurnSawTaskToolCall = false;
3890
4181
  this.turnAllowedToolSignatures.clear();
4182
+ this.orchestrationArchivedForTurn = false;
4183
+ if (typeof this.resetOrchestrationSummary === "function") {
4184
+ this.resetOrchestrationSummary();
4185
+ }
4186
+ else {
4187
+ this.orchestrationCompletedSubagents = [];
4188
+ if (typeof this.refreshOrchestrationSummaryDisplay === "function") {
4189
+ this.refreshOrchestrationSummaryDisplay();
4190
+ }
4191
+ else if (this.orchestrationContainer?.clear) {
4192
+ this.orchestrationContainer.clear();
4193
+ }
4194
+ }
3891
4195
  // Restore main escape handler if retry handler is still active
3892
4196
  // (retry success event fires later, but we need main handler now)
3893
4197
  if (this.retryEscapeHandler) {
@@ -3930,6 +4234,8 @@ export class InteractiveMode {
3930
4234
  this.pendingAssistantOrchestrationContexts -= 1;
3931
4235
  }
3932
4236
  this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
4237
+ this.streamingComponent.setExpanded(this.toolOutputExpanded);
4238
+ this.streamingComponent.setStreaming(true);
3933
4239
  this.streamingMessage = event.message;
3934
4240
  this.chatContainer.addChild(this.streamingComponent);
3935
4241
  this.streamingComponent.updateContent(this.sanitizeAssistantDisplayMessage(this.streamingMessage));
@@ -3974,6 +4280,10 @@ export class InteractiveMode {
3974
4280
  break;
3975
4281
  if (this.streamingComponent && event.message.role === "assistant") {
3976
4282
  this.streamingMessage = event.message;
4283
+ this.streamingComponent.setStreaming(false);
4284
+ if (typeof this.queuePendingTaskCallsFromAssistantMessage === "function") {
4285
+ this.queuePendingTaskCallsFromAssistantMessage(event.message);
4286
+ }
3977
4287
  let errorMessage;
3978
4288
  let interruptedStopReason;
3979
4289
  if (this.streamingMessage.stopReason === "aborted") {
@@ -4021,55 +4331,42 @@ export class InteractiveMode {
4021
4331
  if (event.toolName === "task") {
4022
4332
  this.currentTurnSawTaskToolCall = true;
4023
4333
  }
4024
- if (event.toolName === "task" && !this.subagentComponents.has(event.toolCallId)) {
4334
+ if (event.toolName === "task") {
4025
4335
  const staleTaskComponent = this.pendingTools.get(event.toolCallId);
4026
4336
  if (staleTaskComponent) {
4027
4337
  this.chatContainer.removeChild(staleTaskComponent);
4028
4338
  this.pendingTools.delete(event.toolCallId);
4029
4339
  }
4030
4340
  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
- });
4341
+ const existing = this.subagentComponents.get(event.toolCallId);
4342
+ if (existing) {
4343
+ existing.profile = args.profile ?? existing.profile;
4344
+ existing.description = args.description ?? existing.description;
4345
+ existing.cwd = args.cwd ?? existing.cwd;
4346
+ existing.agent = args.agent ?? existing.agent;
4347
+ existing.lockKey = args.lock_key ?? existing.lockKey;
4348
+ existing.isolation = args.isolation ?? existing.isolation;
4349
+ existing.phase = "starting subagent";
4350
+ existing.phaseState = "starting";
4351
+ }
4352
+ else {
4353
+ this.subagentComponents.set(event.toolCallId, {
4354
+ startTime: Date.now(),
4355
+ profile: args.profile ?? "explore",
4356
+ description: args.description ?? "Running subtask",
4357
+ cwd: args.cwd,
4358
+ agent: args.agent,
4359
+ lockKey: args.lock_key,
4360
+ isolation: args.isolation,
4361
+ phase: "starting subagent",
4362
+ phaseState: "starting",
4363
+ toolCallsStarted: 0,
4364
+ toolCallsCompleted: 0,
4365
+ assistantMessages: 0,
4366
+ });
4367
+ }
4072
4368
  this.ensureSubagentElapsedTimer();
4369
+ this.refreshOrchestrationSummaryDisplay();
4073
4370
  this.ui.requestRender();
4074
4371
  }
4075
4372
  else if (event.toolName !== "task" && !this.pendingTools.has(event.toolCallId)) {
@@ -4121,6 +4418,15 @@ export class InteractiveMode {
4121
4418
  if (typeof progress.assistantMessages === "number") {
4122
4419
  subagent.assistantMessages = progress.assistantMessages;
4123
4420
  }
4421
+ if (typeof progress.delegatedTasks === "number") {
4422
+ subagent.delegatedTasks = Math.max(0, progress.delegatedTasks);
4423
+ }
4424
+ if (typeof progress.delegatedSucceeded === "number") {
4425
+ subagent.delegatedSucceeded = Math.max(0, progress.delegatedSucceeded);
4426
+ }
4427
+ if (typeof progress.delegatedFailed === "number") {
4428
+ subagent.delegatedFailed = Math.max(0, progress.delegatedFailed);
4429
+ }
4124
4430
  if ("delegateIndex" in progress) {
4125
4431
  subagent.delegateIndex =
4126
4432
  typeof progress.delegateIndex === "number" && progress.delegateIndex > 0
@@ -4146,7 +4452,10 @@ export class InteractiveMode {
4146
4452
  : undefined;
4147
4453
  }
4148
4454
  if ("delegateItems" in progress) {
4149
- subagent.delegateItems = parseSubagentDelegateItems(progress.delegateItems);
4455
+ const parsedDelegateItems = parseSubagentDelegateItems(progress.delegateItems);
4456
+ if (Array.isArray(parsedDelegateItems) && parsedDelegateItems.length > 0) {
4457
+ subagent.delegateItems = parsedDelegateItems;
4458
+ }
4150
4459
  }
4151
4460
  }
4152
4461
  else {
@@ -4155,7 +4464,7 @@ export class InteractiveMode {
4155
4464
  subagent.phase = text.trim();
4156
4465
  }
4157
4466
  }
4158
- this.updateRunningSubagentDisplay(subagent);
4467
+ this.refreshOrchestrationSummaryDisplay();
4159
4468
  this.ui.requestRender();
4160
4469
  break;
4161
4470
  }
@@ -4191,24 +4500,32 @@ export class InteractiveMode {
4191
4500
  subagent.delegatedTasks = delegatedTasks;
4192
4501
  subagent.delegatedSucceeded = delegatedSucceeded;
4193
4502
  subagent.delegatedFailed = delegatedFailed;
4194
- subagent.component.update({
4195
- description: subagent.description,
4196
- profile: subagent.profile,
4503
+ this.pushOrchestrationCompletedSubagent({
4504
+ toolCallId: event.toolCallId,
4505
+ finishedAt: Date.now(),
4197
4506
  status: event.isError ? "error" : "done",
4198
- outputLength,
4507
+ profile: subagent.profile,
4508
+ description: subagent.description,
4199
4509
  durationMs,
4200
- waitMs,
4201
- phaseState: subagent.phaseState,
4202
4510
  cwd: subagent.cwd,
4203
4511
  agent: subagent.agent,
4204
4512
  lockKey: subagent.lockKey,
4205
4513
  isolation: subagent.isolation,
4514
+ phase: subagent.phase,
4515
+ phaseState: subagent.phaseState,
4206
4516
  toolCallsStarted,
4207
4517
  toolCallsCompleted,
4208
4518
  assistantMessages,
4209
4519
  delegatedTasks,
4210
4520
  delegatedSucceeded,
4211
4521
  delegatedFailed,
4522
+ delegateIndex: subagent.delegateIndex,
4523
+ delegateTotal: subagent.delegateTotal,
4524
+ delegateDescription: subagent.delegateDescription,
4525
+ delegateProfile: subagent.delegateProfile,
4526
+ delegateItems: subagent.delegateItems,
4527
+ outputLength,
4528
+ waitMs,
4212
4529
  errorMessage: event.isError
4213
4530
  ? event.result?.content?.[0]?.text ?? "error"
4214
4531
  : undefined,
@@ -4220,6 +4537,7 @@ export class InteractiveMode {
4220
4537
  }
4221
4538
  this.subagentComponents.delete(event.toolCallId);
4222
4539
  this.stopSubagentElapsedTimerIfIdle();
4540
+ this.refreshOrchestrationSummaryDisplay();
4223
4541
  this.ui.requestRender();
4224
4542
  break;
4225
4543
  }
@@ -4248,6 +4566,15 @@ export class InteractiveMode {
4248
4566
  this.pendingTools.clear();
4249
4567
  this.subagentComponents.clear();
4250
4568
  this.clearSubagentElapsedTimer();
4569
+ if (typeof this.refreshOrchestrationSummaryDisplay === "function") {
4570
+ this.refreshOrchestrationSummaryDisplay();
4571
+ if (typeof this.archiveOrchestrationPanelToChatIfComplete === "function") {
4572
+ this.archiveOrchestrationPanelToChatIfComplete();
4573
+ }
4574
+ }
4575
+ else if (this.orchestrationContainer?.clear) {
4576
+ this.orchestrationContainer.clear();
4577
+ }
4251
4578
  this.currentTurnSawAssistantMessage = false;
4252
4579
  await this.checkShutdownRequested();
4253
4580
  this.ui.requestRender();
@@ -4515,6 +4842,7 @@ export class InteractiveMode {
4515
4842
  const displayMessage = this.sanitizeAssistantDisplayMessage(message);
4516
4843
  this.activeAssistantOrchestrationContext = previousOrchestrationContext;
4517
4844
  const assistantComponent = new AssistantMessageComponent(displayMessage, this.hideThinkingBlock, this.getMarkdownThemeWithSettings());
4845
+ assistantComponent.setExpanded(this.toolOutputExpanded);
4518
4846
  this.chatContainer.addChild(assistantComponent);
4519
4847
  break;
4520
4848
  }
@@ -4535,6 +4863,7 @@ export class InteractiveMode {
4535
4863
  */
4536
4864
  renderSessionContext(sessionContext, options = {}) {
4537
4865
  this.pendingTools.clear();
4866
+ this.resetOrchestrationSummary();
4538
4867
  this.pendingInternalUserDisplayAliases = [];
4539
4868
  this.pendingAssistantOrchestrationContexts = 0;
4540
4869
  this.activeAssistantOrchestrationContext = false;
@@ -4552,17 +4881,14 @@ export class InteractiveMode {
4552
4881
  if (content.type === "toolCall") {
4553
4882
  if (content.name === "task") {
4554
4883
  const args = content.arguments;
4555
- const subagent = new SubagentMessageComponent({
4884
+ pendingSubagentHistory.set(content.id, {
4556
4885
  description: args.description ?? "Running subtask",
4557
4886
  profile: args.profile ?? "explore",
4558
- status: "running",
4559
4887
  cwd: args.cwd,
4560
4888
  agent: args.agent,
4561
4889
  lockKey: args.lock_key,
4562
4890
  isolation: args.isolation,
4563
4891
  });
4564
- this.chatContainer.addChild(subagent);
4565
- pendingSubagentHistory.set(content.id, subagent);
4566
4892
  continue;
4567
4893
  }
4568
4894
  const component = new ToolExecutionComponent(content.name, content.arguments, { showImages: this.settingsManager.getShowImages() }, this.getRegisteredToolDefinition(content.name), this.ui);
@@ -4592,18 +4918,36 @@ export class InteractiveMode {
4592
4918
  const subagent = pendingSubagentHistory.get(message.toolCallId);
4593
4919
  if (subagent) {
4594
4920
  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",
4921
+ this.pushOrchestrationCompletedSubagent({
4922
+ toolCallId: message.toolCallId,
4923
+ finishedAt: Date.now(),
4598
4924
  status: message.isError ? "error" : "done",
4925
+ description: typeof details?.description === "string"
4926
+ ? details.description
4927
+ : subagent.description ?? "Running subtask",
4928
+ profile: typeof details?.profile === "string"
4929
+ ? details.profile
4930
+ : subagent.profile ?? "explore",
4931
+ durationMs: typeof details?.durationMs === "number" && Number.isFinite(details.durationMs)
4932
+ ? Math.max(0, Math.floor(details.durationMs))
4933
+ : 0,
4934
+ cwd: subagent.cwd,
4935
+ agent: subagent.agent,
4936
+ lockKey: subagent.lockKey,
4937
+ isolation: subagent.isolation,
4599
4938
  outputLength: typeof details?.outputLength === "number" ? details.outputLength : undefined,
4600
4939
  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,
4940
+ toolCallsStarted: typeof details?.toolCallsStarted === "number" ? details.toolCallsStarted : 0,
4941
+ toolCallsCompleted: typeof details?.toolCallsCompleted === "number" ? details.toolCallsCompleted : 0,
4942
+ assistantMessages: typeof details?.assistantMessages === "number" ? details.assistantMessages : 0,
4604
4943
  delegatedTasks: typeof details?.delegatedTasks === "number" ? details.delegatedTasks : undefined,
4605
4944
  delegatedSucceeded: typeof details?.delegatedSucceeded === "number" ? details.delegatedSucceeded : undefined,
4606
4945
  delegatedFailed: typeof details?.delegatedFailed === "number" ? details.delegatedFailed : undefined,
4946
+ delegateIndex: typeof details?.delegateIndex === "number" ? details.delegateIndex : undefined,
4947
+ delegateTotal: typeof details?.delegateTotal === "number" ? details.delegateTotal : undefined,
4948
+ delegateDescription: typeof details?.delegateDescription === "string" ? details.delegateDescription : undefined,
4949
+ delegateProfile: typeof details?.delegateProfile === "string" ? details.delegateProfile : undefined,
4950
+ delegateItems: parseSubagentDelegateItems(details?.delegateItems),
4607
4951
  errorMessage: message.isError
4608
4952
  ? message.content?.[0]?.text ?? "error"
4609
4953
  : undefined,
@@ -5795,6 +6139,7 @@ export class InteractiveMode {
5795
6139
  autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
5796
6140
  quietStartup: this.settingsManager.getQuietStartup(),
5797
6141
  clearOnShrink: this.settingsManager.getClearOnShrink(),
6142
+ compactFooter: this.getCompactFooterSetting(),
5798
6143
  webSearchEnabled: this.settingsManager.getWebSearchEnabled(),
5799
6144
  webSearchProviderMode: this.settingsManager.getWebSearchProviderMode(),
5800
6145
  webSearchFallbackMode: this.settingsManager.getWebSearchFallbackMode(),
@@ -6003,6 +6348,11 @@ export class InteractiveMode {
6003
6348
  this.settingsManager.setClearOnShrink(enabled);
6004
6349
  this.ui.setClearOnShrink(enabled);
6005
6350
  },
6351
+ onCompactFooterChange: (enabled) => {
6352
+ this.setCompactFooterSetting(enabled);
6353
+ this.footer.setCompactModeEnabled?.(enabled);
6354
+ this.ui.requestRender();
6355
+ },
6006
6356
  onCancel: () => {
6007
6357
  done();
6008
6358
  this.ui.requestRender();
@@ -9975,9 +10325,8 @@ export class InteractiveMode {
9975
10325
  const mentionTask = cleaned.length > 0 ? cleaned : userInput;
9976
10326
  const orchestrationAwareAgent = /orchestrator/i.test(mentionedAgent);
9977
10327
  const mentionMode = orchestrationAwareAgent ? "parallel" : "sequential";
9978
- const mentionMaxParallel = orchestrationAwareAgent ? MAX_ORCHESTRATION_PARALLEL : undefined;
9979
10328
  const mentionPrompt = [
9980
- `<orchestrate mode="${mentionMode}" agents="1"${mentionMaxParallel ? ` max_parallel="${mentionMaxParallel}"` : ""}>`,
10329
+ `<orchestrate mode="${mentionMode}" agents="1">`,
9981
10330
  `- agent 1: profile=${this.activeProfileName} cwd=${this.sessionManager.getCwd()} agent=${mentionedAgent}`,
9982
10331
  `task: ${mentionTask}`,
9983
10332
  "constraints:",
@@ -9986,8 +10335,8 @@ export class InteractiveMode {
9986
10335
  ...(orchestrationAwareAgent
9987
10336
  ? [
9988
10337
  "- 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}.`,
10338
+ "- If user explicitly requested an agent count, set delegate_parallel_hint to that count.",
10339
+ "- Otherwise set delegate_parallel_hint based on complexity: simple=1, medium=3-6, complex/risky=7+.",
9991
10340
  "- For non-trivial tasks, prefer delegate_parallel_hint >= 2 and split into independent <delegate_task> workstreams.",
9992
10341
  '- Prefer existing custom agents for delegated work when suitable (use <delegate_task agent="name" ...>).',
9993
10342
  "- If no existing custom agent fits, create focused delegate streams via profile-based <delegate_task> blocks.",
@@ -10019,10 +10368,10 @@ export class InteractiveMode {
10019
10368
  }
10020
10369
  const explicitRequested = parseRequestedParallelAgentCount(userInput);
10021
10370
  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 ??
10371
+ const fallbackParallel = Math.max(1, explicitRequested ??
10023
10372
  (hasComplexSignal
10024
10373
  ? Math.max(details.requiredTopLevelTasks, 6)
10025
- : Math.max(details.requiredTopLevelTasks, 3))));
10374
+ : Math.max(details.requiredTopLevelTasks, 3)));
10026
10375
  this.showWarning(`META enforcement fallback: orchestration contract not satisfied (${details.launchedTopLevelTasks}/${details.requiredTopLevelTasks} task calls). Launching /swarm run.`);
10027
10376
  await this.runSwarmFromTask(userInput, { maxParallel: fallbackParallel });
10028
10377
  },
@@ -10050,6 +10399,8 @@ export class InteractiveMode {
10050
10399
  case "message_start":
10051
10400
  if (event.message.role === "assistant" && !hideAssistantText) {
10052
10401
  verifyStreamingComponent = new AssistantMessageComponent(undefined, false, this.getMarkdownThemeWithSettings());
10402
+ verifyStreamingComponent.setExpanded(this.toolOutputExpanded);
10403
+ verifyStreamingComponent.setStreaming(true);
10053
10404
  verifyStreamingMessage = event.message;
10054
10405
  this.chatContainer.addChild(verifyStreamingComponent);
10055
10406
  verifyStreamingComponent.updateContent(verifyStreamingMessage);
@@ -10072,6 +10423,7 @@ export class InteractiveMode {
10072
10423
  case "message_end":
10073
10424
  if (verifyStreamingComponent && event.message.role === "assistant") {
10074
10425
  verifyStreamingMessage = event.message;
10426
+ verifyStreamingComponent.setStreaming(false);
10075
10427
  verifyStreamingComponent.updateContent(verifyStreamingMessage);
10076
10428
  verifyStreamingComponent = undefined;
10077
10429
  verifyStreamingMessage = undefined;
@@ -10278,7 +10630,7 @@ export class InteractiveMode {
10278
10630
  const highRisk = /\b(refactor|rewrite|migration|migrate|breaking|rollback|security|auth|authentication|authorization|permission|payment|billing|schema|database|critical)\b/i.test(normalizedTask);
10279
10631
  const mediumRisk = /\b(cross[-\s]?module|architecture|infra|platform|multi[-\s]?file|integration|audit|hardening)\b/i.test(normalizedTask);
10280
10632
  let hint = input.mode === "parallel"
10281
- ? Math.max(2, Math.min(MAX_SUBAGENT_DELEGATE_PARALLEL, input.maxParallel ?? input.agents))
10633
+ ? Math.max(2, input.maxParallel ?? input.agents)
10282
10634
  : 1;
10283
10635
  if (highRisk) {
10284
10636
  hint = Math.max(hint, 7);
@@ -10290,12 +10642,12 @@ export class InteractiveMode {
10290
10642
  hint = Math.max(hint, 6);
10291
10643
  }
10292
10644
  if (input.hasDependencies) {
10293
- hint = Math.max(2, Math.min(hint, 6));
10645
+ hint = Math.max(2, hint);
10294
10646
  }
10295
10647
  if (input.hasLock) {
10296
- hint = Math.max(1, Math.min(hint, 4));
10648
+ hint = Math.max(1, hint);
10297
10649
  }
10298
- return Math.max(1, Math.min(MAX_SUBAGENT_DELEGATE_PARALLEL, hint));
10650
+ return Math.max(1, hint);
10299
10651
  }
10300
10652
  isEffectiveContractReady(contract) {
10301
10653
  const hasText = (value) => typeof value === "string" && value.trim().length > 0;
@@ -10518,8 +10870,8 @@ export class InteractiveMode {
10518
10870
  if (token === "--max-parallel") {
10519
10871
  const next = rest[index + 1];
10520
10872
  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}).`);
10873
+ if (!Number.isInteger(parsed) || parsed < 1) {
10874
+ this.showWarning("Invalid --max-parallel value (expected integer >= 1).");
10523
10875
  return undefined;
10524
10876
  }
10525
10877
  maxParallel = parsed;
@@ -10692,16 +11044,15 @@ export class InteractiveMode {
10692
11044
  resolveSwarmMaxParallel(input) {
10693
11045
  const requested = input.requested;
10694
11046
  if (typeof requested === "number" && Number.isFinite(requested)) {
10695
- return Math.max(1, Math.min(MAX_ORCHESTRATION_PARALLEL, Math.floor(requested)));
11047
+ return Math.max(1, Math.floor(requested));
10696
11048
  }
10697
11049
  const totalTasks = Math.max(1, input.plan.tasks.length);
10698
11050
  const initialFanout = Math.max(1, input.plan.tasks.filter((task) => task.depends_on.length === 0).length);
10699
11051
  const parallelizable = input.plan.tasks.filter((task) => task.concurrency_class === "implementation" || task.concurrency_class === "tests")
10700
11052
  .length;
10701
11053
  const sourceFloor = input.source === "singular" ? 4 : 3;
10702
- const autoCap = Math.min(MAX_ORCHESTRATION_PARALLEL, 10);
10703
11054
  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));
11055
+ return Math.max(1, heuristic);
10705
11056
  }
10706
11057
  resolveSwarmDispatchTimeoutMs() {
10707
11058
  const dispatchTimeoutRaw = Number.parseInt(process.env.IOSM_SWARM_DISPATCH_TIMEOUT_MS ?? "", 10);
@@ -11803,8 +12154,8 @@ export class InteractiveMode {
11803
12154
  if (arg === "--agents") {
11804
12155
  const value = args[index + 1];
11805
12156
  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}).`);
12157
+ if (!Number.isInteger(parsed) || parsed < 1) {
12158
+ this.showWarning("Invalid --agents value (expected integer >= 1).");
11808
12159
  return undefined;
11809
12160
  }
11810
12161
  agents = parsed;
@@ -11814,8 +12165,8 @@ export class InteractiveMode {
11814
12165
  if (arg === "--max-parallel") {
11815
12166
  const value = args[index + 1];
11816
12167
  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}).`);
12168
+ if (!Number.isInteger(parsed) || parsed < 1) {
12169
+ this.showWarning("Invalid --max-parallel value (expected integer >= 1).");
11819
12170
  return undefined;
11820
12171
  }
11821
12172
  maxParallel = parsed;
@@ -11935,7 +12286,7 @@ export class InteractiveMode {
11935
12286
  return undefined;
11936
12287
  }
11937
12288
  if (mode === "parallel" && maxParallel === undefined) {
11938
- maxParallel = Math.max(1, Math.min(MAX_ORCHESTRATION_PARALLEL, agents));
12289
+ maxParallel = Math.max(1, agents);
11939
12290
  }
11940
12291
  if (maxParallel !== undefined && maxParallel > agents) {
11941
12292
  maxParallel = agents;
@@ -12068,6 +12419,8 @@ export class InteractiveMode {
12068
12419
  "- when assignment lines include depends_on, still emit one task call per assignment; runtime enforces dependency gating",
12069
12420
  "- include delegate_parallel_hint from each assignment/task_call hint in every corresponding task tool call",
12070
12421
  "- for delegate_parallel_hint >= 2, split child work into nested <delegate_task> streams unless impossible",
12422
+ "- treat delegates strictly as child task calls; do not count plain tool invocations as delegated agents",
12423
+ "- assign explicit ownership domains per top-level agent to minimize overlap and duplicate findings",
12071
12424
  "- if nested split is impossible for a non-trivial stream, emit one line: DELEGATION_IMPOSSIBLE: <reason>",
12072
12425
  "- keep required orchestration task calls in foreground; do not set background=true unless user explicitly requested detached async runs",
12073
12426
  "- do not poll .iosm/subagents/background via bash/read during orchestration; wait for task results and then synthesize",
@@ -14573,6 +14926,7 @@ The agent will automatically receive IOSM context on every turn.`;
14573
14926
  }
14574
14927
  setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
14575
14928
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
14929
+ this.footer.setCompactModeEnabled(this.getCompactFooterSetting());
14576
14930
  const themeName = this.settingsManager.getTheme();
14577
14931
  const themeResult = themeName ? setTheme(themeName, true) : { success: true };
14578
14932
  if (!themeResult.success) {
@@ -14911,7 +15265,7 @@ The agent will automatically receive IOSM context on every turn.`;
14911
15265
  | \`${cycleThinkingLevel}\` | Cycle thinking level |
14912
15266
  | \`${cycleModelForward}\` | Cycle models |
14913
15267
  | \`${selectModel}\` | Open model selector |
14914
- | \`${expandTools}\` | Toggle tool output expansion |
15268
+ | \`${expandTools}\` | Toggle tool/reasoning output expansion |
14915
15269
  | \`${toggleThinking}\` | Toggle thinking block visibility |
14916
15270
  | \`${externalEditor}\` | Edit message in external editor |
14917
15271
  | \`${steer}\` | Queue steer message |