@dotobokuri/fleet-cli 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -18998,6 +18998,7 @@ var models_default = {
18998
18998
  { modelId: "gemini-3-flash", name: "Gemini 3 Flash", effort: { supported: false } },
18999
18999
  { modelId: "gemini-3.5-flash", name: "Gemini 3.5 Flash", effort: { supported: false } },
19000
19000
  { modelId: "claude-opus-4-7-thinking", name: "Claude Opus 4.7 Thinking", spawnModelTemplate: "claude-opus-4-7-thinking-{effort}", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "xhigh" } },
19001
+ { modelId: "claude-opus-4-8-thinking", name: "Claude Opus 4.8 Thinking", spawnModelTemplate: "claude-opus-4-8-thinking-{effort}", effort: { supported: true, levels: ["low", "medium", "high", "xhigh", "max"], default: "xhigh" } },
19001
19002
  { modelId: "claude-4.6-sonnet-medium-thinking", name: "Claude Sonnet 4.6 Medium Thinking", effort: { supported: false } }
19002
19003
  ]
19003
19004
  }
@@ -31862,12 +31863,20 @@ async function runTaskForceBackend(registry3, cliType, carrierId, requestKey, re
31862
31863
  appendBlock(jobId, toArchiveBlock("thought", carrierId, text, cliType));
31863
31864
  emitStreamEvent(registry3, { type: "track:thought", jobId, trackId, text: cleanText });
31864
31865
  },
31865
- onToolCall: (title, status, _rawOutput, toolCallId) => {
31866
+ onToolCall: (title, status, rawOutput, toolCallId) => {
31866
31867
  progress.status = "streaming";
31867
31868
  progress.toolCallCount++;
31868
31869
  const cleanTitle = sanitizeToolLabel(title);
31869
31870
  const cleanStatus = sanitizeToolLabel(status);
31870
- emitStreamEvent(registry3, { type: "track:tool", jobId, trackId, title: cleanTitle, status: cleanStatus, toolCallId });
31871
+ emitStreamEvent(registry3, {
31872
+ type: "track:tool",
31873
+ jobId,
31874
+ trackId,
31875
+ detailChars: rawOutput?.length ?? 0,
31876
+ title: cleanTitle,
31877
+ status: cleanStatus,
31878
+ toolCallId
31879
+ });
31871
31880
  }
31872
31881
  });
31873
31882
  progress.status = result.status === "done" ? "done" : "error";
@@ -32043,14 +32052,7 @@ function buildCarrierDispatchToolSpec(registry3) {
32043
32052
  });
32044
32053
  }
32045
32054
  const metadata = config2.carrierMetadata;
32046
- if (isCarrierAgentModeSubagent(carrierId, config2.defaultAgentMode)) {
32047
- const displayName = resolveCarrierDisplayName(registry3, carrierId);
32048
- return launchResponseResult({
32049
- job_id: jobId,
32050
- accepted: false,
32051
- error: `Carrier "${carrierId}" is in native subagent mode and is unreachable via carrier_dispatch. Invoke it directly as the native subagent "${displayName}".`
32052
- });
32053
- }
32055
+ const isNativeSubagentMode = isCarrierAgentModeSubagent(carrierId, config2.defaultAgentMode);
32054
32056
  if (metadata) {
32055
32057
  const blockValidation = validateRequiredRequestBlocks(metadata, request, carrierId);
32056
32058
  if (!blockValidation.ok) {
@@ -32061,7 +32063,7 @@ function buildCarrierDispatchToolSpec(registry3) {
32061
32063
  });
32062
32064
  }
32063
32065
  }
32064
- if (getConfiguredTaskForceBackends(carrierId).length >= 2) {
32066
+ if (!isNativeSubagentMode && getConfiguredTaskForceBackends(carrierId).length >= 2) {
32065
32067
  return launchTaskForceJob({
32066
32068
  registry: registry3,
32067
32069
  carrierId,
@@ -32198,10 +32200,18 @@ async function runSingleCarrier(opts) {
32198
32200
  appendBlock(opts.jobId, toArchiveBlock("thought", opts.carrierId, text));
32199
32201
  emitStreamEvent(opts.registry, { type: "track:thought", jobId: opts.jobId, trackId: opts.carrierId, text: cleanText });
32200
32202
  },
32201
- onToolCall: (toolTitle, toolStatus, _rawOutput, toolCallId) => {
32203
+ onToolCall: (toolTitle, toolStatus, rawOutput, toolCallId) => {
32202
32204
  const title = sanitizeToolLabel(toolTitle);
32203
32205
  const status = sanitizeToolLabel(toolStatus);
32204
- emitStreamEvent(opts.registry, { type: "track:tool", jobId: opts.jobId, trackId: opts.carrierId, title, status, toolCallId });
32206
+ emitStreamEvent(opts.registry, {
32207
+ type: "track:tool",
32208
+ jobId: opts.jobId,
32209
+ trackId: opts.carrierId,
32210
+ detailChars: rawOutput?.length ?? 0,
32211
+ title,
32212
+ status,
32213
+ toolCallId
32214
+ });
32205
32215
  }
32206
32216
  });
32207
32217
  const finalStatus = toCarrierJobStatus(execResult.status);
@@ -33722,7 +33732,6 @@ function createBoundCarrierJobs(registry3, boundStream = createBoundCarrierStrea
33722
33732
  }
33723
33733
 
33724
33734
  // ../../packages/fleet-admiral/dist/index.js
33725
- var FLEET_ACTION_LABEL = "Fleet Action Protocol";
33726
33735
  var FLEET_ACTION_PROMPT = String.raw`Every task progresses through the following phases **in order**. Phases marked *conditional* may be skipped when the task is trivially small or the condition is not met.
33727
33736
 
33728
33737
  **Deep Dive rule:** After **every phase** that produces analytical results, evaluate whether the Deep Dive Standing Order should be triggered before advancing to the next phase. This applies to all phases — not just analysis phases.
@@ -33875,21 +33884,16 @@ Resolve technical trade-offs first; never delegate unresolved decisions to a pla
33875
33884
  ### Tool Selection
33876
33885
  | Intent | Tool |
33877
33886
  |---|---|
33878
- | Delegate to a roster Carrier (listed in <fleet section="roster">) | carrier_dispatch |
33879
- | Delegate to a subagent-mode Carrier (listed in <fleet section="subagents">) | its native Claude subagent path — never carrier_dispatch |
33880
- | Parallel subtasks on one Carrier | multiple carrier_dispatch calls |
33887
+ | Delegate to a roster Carrier (listed in <fleet section="roster">) | native subagent path (spawn_agent / agent) when available; otherwise carrier_dispatch |
33888
+ | Parallel subtasks on one Carrier | multiple invocations, same response |
33881
33889
  | Lookup/control detached jobs | carrier_jobs (never for delegation) |
33882
33890
  | Synthesis, strategic advice | (no tool) |
33883
33891
 
33884
- ### Routing by Carrier Mode
33885
- A Carrier's section determines its delegation route check it before every delegation, and do not default to carrier_dispatch reflexively.
33886
- - carrier_dispatch reaches ONLY carriers listed in <fleet section="roster">.
33887
- - A Carrier listed under <fleet section="subagents"> is in subagent mode: it is intentionally absent from the roster and is unreachable via carrier_dispatch. Invoke it through its native Claude subagent path exactly as that section names it; never reroute it to carrier_dispatch.
33888
- - Native subagent results are NOT recovered into carrier_jobs, JobArchive, or [carrier:result] pushes — after invoking a subagent-mode Carrier, do not wait for a completion push.
33889
- - If no <fleet section="subagents"> block is present, every Carrier is a roster Carrier and this rule is inert.
33892
+ ### Carrier Invocation
33893
+ Every Carrier lives in <fleet section="roster">. For any Carrier, prefer the native subagent path (spawn_agent / agent); if the Carrier cannot be invoked that way in this session, fall back to carrier_dispatch. If neither path accepts the Carrier, report it unavailable per Dispatch rules — never silently substitute. Native invocations return inline and do NOT emit a [carrier:result] push — do not wait for one. Only carrier_dispatch jobs push completion via [carrier:result].
33890
33894
 
33891
33895
  ### Parallel Default
33892
- When the same phase or step calls multiple Captain-led Carriers, dispatch them in parallel — one tool call per carrier, same response. Sequence only when:
33896
+ When the same phase or step calls multiple Captain-led Carriers, invoke them in parallel — one tool call per carrier, same response. Sequence only when:
33893
33897
  - a later Carrier's work depends on an earlier Carrier's output,
33894
33898
  - carriers share a mutable resource (same files, generated artifacts, lock files, singleton test environment), or
33895
33899
  - a recon Carrier must complete before a specialist Carrier can be selected.
@@ -33901,7 +33905,7 @@ When the same phase or step calls multiple Captain-led Carriers, dispatch them i
33901
33905
  - Splitting a parallel launch into sequential calls.
33902
33906
  - Sortieing a planning carrier for single-carrier work.
33903
33907
  - Falling back to direct work when delegation is appropriate.
33904
- - Dispatching a subagent-mode Carrier (one listed under <fleet section="subagents">) via carrier_dispatch instead of its native subagent path.`
33908
+ - Treating native subagent availability as exclusive; prefer the native path, but carrier_dispatch remains allowed when operationally useful.`
33905
33909
  };
33906
33910
  var CONTEXT_CONFIDENCE = {
33907
33911
  id: "context-confidence",
@@ -34103,12 +34107,12 @@ Tool results and user messages may include ${"`"}<system-reminder>${"`"} tags. T
34103
34107
  `;
34104
34108
  function createSystemPromptBuilder(deps) {
34105
34109
  return {
34106
- build(injectTone, nativeSubagents = []) {
34107
- return buildSystemPromptWithSubagents(deps, injectTone, nativeSubagents);
34110
+ build(injectTone) {
34111
+ return buildSystemPromptFromDeps(deps, injectTone);
34108
34112
  }
34109
34113
  };
34110
34114
  }
34111
- function buildSystemPromptWithSubagents(deps, injectTone, nativeSubagents) {
34115
+ function buildSystemPromptFromDeps(deps, injectTone) {
34112
34116
  const parts = [];
34113
34117
  parts.push(FLEET_PREAMBLE.trim());
34114
34118
  parts.push(`<fleet section="role">
@@ -34124,25 +34128,11 @@ ${FLEET_TONE_PROMPT.trim()}
34124
34128
  }
34125
34129
  const carrierRuntime = deps.carrierRuntime;
34126
34130
  const carrierIds = getRegisteredOrder(carrierRuntime.registry);
34127
- const subagentCarrierIds = nativeSubagents.map((subagent) => subagent.carrierId);
34128
- const rosterCarrierIds = carrierIds.filter((carrierId) => !subagentCarrierIds.includes(carrierId));
34129
- if (rosterCarrierIds.length > 0) {
34131
+ if (carrierIds.length > 0) {
34130
34132
  parts.push(`<fleet section="roster">
34131
34133
  ${buildCarrierRoster(carrierRuntime.registry, carrierIds, {
34132
- excludeCarrierIds: subagentCarrierIds,
34133
34134
  heading: "# Available Carriers"
34134
34135
  })}
34135
- </fleet>`);
34136
- }
34137
- if (nativeSubagents.length > 0) {
34138
- const metadataByCarrierId = new Map(
34139
- nativeSubagents.map((subagent) => [
34140
- subagent.carrierId,
34141
- getRegisteredCarrierConfig(carrierRuntime.registry, subagent.carrierId)?.carrierMetadata
34142
- ]).filter((entry) => Boolean(entry[1]))
34143
- );
34144
- parts.push(`<fleet section="subagents">
34145
- ${buildNativeSubagentSection(nativeSubagents, metadataByCarrierId)}
34146
34136
  </fleet>`);
34147
34137
  }
34148
34138
  const protocolBody = `# Fleet Action Protocol \u2014 Operational Doctrine
@@ -34167,34 +34157,6 @@ ${ordersBody}
34167
34157
  }
34168
34158
  return parts.join("\n\n");
34169
34159
  }
34170
- function buildNativeSubagentSection(subagents, metadataByCarrierId) {
34171
- const host = isCodexSubagent(subagents[0]) ? "Codex" : "Claude";
34172
- const invocationKind = host === "Codex" ? "native Codex roles" : "native Claude subagents";
34173
- const lines = [
34174
- `# Native ${host} Subagents`,
34175
- `The following Fleet carriers are exposed to this ${host}-family dedicated CLI session as ${invocationKind}.`,
34176
- "This path coexists with Fleet `carrier_dispatch`; use either route according to the task.",
34177
- "Native subagent output is not recovered into Fleet `carrier_jobs`, JobArchive, stream events, or `[carrier:result]` reminders.",
34178
- "When invoking a native subagent, use the carrier's structured request blocks below and keep the request concise.",
34179
- ""
34180
- ];
34181
- for (const subagent of subagents) {
34182
- lines.push(`- ${subagent.carrierId}: invoke as \`${getNativeSubagentInvocationName(subagent)}\` \u2014 ${subagent.description}`);
34183
- const metadata = metadataByCarrierId.get(subagent.carrierId);
34184
- const blockLines = metadata ? formatRequestBlocksGuide(metadata) : [];
34185
- if (blockLines.length > 0) {
34186
- lines.push(" Request blocks \u2014 wrap content in these (? = optional):");
34187
- lines.push(...blockLines);
34188
- }
34189
- }
34190
- return lines.join("\n");
34191
- }
34192
- function getNativeSubagentInvocationName(subagent) {
34193
- return isCodexSubagent(subagent) ? subagent.roleKey : subagent.name;
34194
- }
34195
- function isCodexSubagent(subagent) {
34196
- return Boolean(subagent && "roleKey" in subagent);
34197
- }
34198
34160
  var CARRIER_MCP_SERVER_NAME = "fleet-carriers";
34199
34161
  var WIKI_MCP_SERVER_NAME = "fleet-wiki";
34200
34162
  var CARRIER_EXECUTOR_MCP_TOOL_IDS = ["carrier_jobs"];
@@ -35633,7 +35595,7 @@ async function injectAgentCliProfile(profile, options2) {
35633
35595
  const codexSubagents = startupSubagents.host === "codex" ? startupSubagents.definitions.map((definition) => ensureCodexSubagentRoleFile(definition)).filter((role) => role !== void 0) : [];
35634
35596
  const systemPromptFile = writeSystemPromptFile(
35635
35597
  profile.id,
35636
- options2.buildSystemPrompt(injectTone, startupSubagents.definitions),
35598
+ options2.buildSystemPrompt(injectTone),
35637
35599
  options2.onCleanup
35638
35600
  );
35639
35601
  const context = {
@@ -35999,11 +35961,6 @@ var ASCII_FLEET_BANNER = [
35999
35961
  "\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D "
36000
35962
  ];
36001
35963
 
36002
- // src/styles/hud.ts
36003
- function getFleetActionHudColor() {
36004
- return "\x1B[38;2;100;180;255m";
36005
- }
36006
-
36007
35964
  // src/styles/carriers.ts
36008
35965
  var PROVIDER_RGBS = {
36009
35966
  claude: [255, 149, 0],
@@ -36146,9 +36103,11 @@ var SELECTED_MARKER = "\u25B8";
36146
36103
  var IDLE_MARKER = " ";
36147
36104
  var CHOICE_INDENT = 4;
36148
36105
  var COUNT_SEPARATOR = " \xB7 ";
36106
+ var SELECTED_BG = "\x1B[48;2;45;55;70m";
36107
+ var DEFAULT_BG = "\x1B[48;2;28;28;36m";
36149
36108
  var MISSION_CONTROL_THEME = {
36150
36109
  accent: (text) => paint2(FLEET_ACCENT, text),
36151
- bg: (name, text) => paint2(name === "selected" ? "\x1B[48;2;45;55;70m" : "\x1B[48;2;28;28;36m", text),
36110
+ bg: (name, text) => paintBackground(name === "selected" ? SELECTED_BG : DEFAULT_BG, text),
36152
36111
  bold: (text) => paint2("\x1B[1m", text),
36153
36112
  border: (text) => paint2("\x1B[38;5;244m", text),
36154
36113
  dim: (text) => paint2("\x1B[38;5;244m", text),
@@ -36295,6 +36254,9 @@ function computeChoiceWidth(cliOptions) {
36295
36254
  function paint2(code, text) {
36296
36255
  return paint(code, text, true);
36297
36256
  }
36257
+ function paintBackground(code, text) {
36258
+ return `${code}${text.replaceAll(ANSI_RESET3, `${ANSI_RESET3}${code}`)}${ANSI_RESET3}`;
36259
+ }
36298
36260
 
36299
36261
  // src/mission-control/carrier-roster/model-flow.ts
36300
36262
  function buildModelEffortTransition(input) {
@@ -36372,8 +36334,8 @@ function handleCarrierStatusOverlayInput(data, inputState, controller) {
36372
36334
 
36373
36335
  // src/mission-bridge/job-bar/constants.ts
36374
36336
  var ANSI_RESET4 = "\x1B[0m";
36375
- var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
36376
36337
  var ANIM_INTERVAL_MS = 100;
36338
+ var BREATHING_CYCLE_FRAMES = 10;
36377
36339
  var PANEL_DIM_COLOR = "\x1B[38;2;160;150;180m";
36378
36340
  var TASKFORCE_BADGE_RGB = [100, 180, 255];
36379
36341
  var TASKFORCE_BADGE_COLOR2 = `\x1B[38;2;${TASKFORCE_BADGE_RGB[0]};${TASKFORCE_BADGE_RGB[1]};${TASKFORCE_BADGE_RGB[2]}m`;
@@ -36389,11 +36351,11 @@ var NAME_WIDTH = 12;
36389
36351
  var MIN_CELL_WIDTH = 40;
36390
36352
  var ROSTER_ACTIONS_ID = "__roster_actions__";
36391
36353
  var CARRIER_ACTION_LABELS = [
36392
- "Edit Model",
36393
- "Change CLI Type",
36394
- "Rename",
36395
- "Toggle Native(SubAgent)",
36396
- "Open TaskForce",
36354
+ "Agent CLI",
36355
+ "Model",
36356
+ "Configure TaskForce",
36357
+ "Configure SubAgent",
36358
+ "Rename Carrier",
36397
36359
  "Toggle Details"
36398
36360
  ];
36399
36361
  var ROSTER_ACTION_LABELS = [
@@ -36416,7 +36378,7 @@ function renderCarrierStatusOverlay(width, model, deps) {
36416
36378
  body.push({
36417
36379
  kind: "cell",
36418
36380
  line: {
36419
- selected: isSelected,
36381
+ bg: isSelected ? getEntryBgColorForEntry(entry) : void 0,
36420
36382
  text: withIndent(renderEntryLine(entry, isSelected, deps))
36421
36383
  }
36422
36384
  });
@@ -36427,7 +36389,7 @@ function renderCarrierStatusOverlay(width, model, deps) {
36427
36389
  body.push(...buildRenameEditorLines(model.renameState, deps.theme));
36428
36390
  }
36429
36391
  if (isSelected && model.expandedCarrierId === entry.carrierId) {
36430
- body.push(...buildDetailRows(entry, Math.max(20, width - 8), deps).map((line) => toIndentedCellLine(line)));
36392
+ body.push(...buildDetailRows(entry, getDetailInnerWidth(width), deps).map((line) => toIndentedCellLine(line)));
36431
36393
  }
36432
36394
  }
36433
36395
  if (gi < model.viewModel.groupedEntries.length - 1) {
@@ -36435,10 +36397,7 @@ function renderCarrierStatusOverlay(width, model, deps) {
36435
36397
  }
36436
36398
  }
36437
36399
  body.push({ kind: "blank" });
36438
- body.push(toCellLine(
36439
- renderRosterActionsRow(model.viewModel.selectedCarrierId === ROSTER_ACTIONS_ID, deps),
36440
- model.viewModel.selectedCarrierId === ROSTER_ACTIONS_ID
36441
- ));
36400
+ body.push(toCellLine(renderRosterActionsRow(model.viewModel.selectedCarrierId === ROSTER_ACTIONS_ID, deps)));
36442
36401
  if (model.state.kind === "carrierActions") {
36443
36402
  body.push(...buildActionMenuLines("Carrier Actions", CARRIER_ACTION_LABELS, model.state.cursor, deps));
36444
36403
  }
@@ -36451,7 +36410,7 @@ function renderCarrierStatusOverlay(width, model, deps) {
36451
36410
  body.push({ kind: "center", text: tone(model.feedbackMessage) }, { kind: "blank" });
36452
36411
  }
36453
36412
  body.push({ kind: "center", text: deps.theme.dim(getFooterHint(model)) });
36454
- return renderDisplayLines(body, width, deps.theme);
36413
+ return renderDisplayLines(body, width);
36455
36414
  }
36456
36415
  function estimateCarrierStatusRows(model, deps) {
36457
36416
  let rows = 0;
@@ -36475,9 +36434,78 @@ function estimateCarrierStatusRows(model, deps) {
36475
36434
  function clampCarrierStatusOverlayRows(maxRows, cardRows) {
36476
36435
  return Math.min(Math.max(0, maxRows), Math.max(0, cardRows));
36477
36436
  }
36437
+ function getCarrierStatusFocusLine(width, model, deps) {
36438
+ let line = 2;
36439
+ if (model.state.kind === "batchFrom" || model.state.kind === "batchTo") {
36440
+ return line + getBatchChoiceLineOffset(buildBatchCliPanelLines(model.state, deps), model.state);
36441
+ }
36442
+ for (let gi = 0; gi < model.viewModel.groupedEntries.length; gi++) {
36443
+ const group = model.viewModel.groupedEntries[gi];
36444
+ line += 2;
36445
+ for (const entry of group.entries) {
36446
+ const entryLine = line;
36447
+ line++;
36448
+ if (entry.carrierId === model.viewModel.selectedCarrierId) {
36449
+ const entryFocusLine = getSelectedEntryFocusLine(entryLine, entry, model, deps);
36450
+ if (entryFocusLine !== void 0) return entryFocusLine;
36451
+ line += getSelectedEntryExtraRows(width, entry, model, deps);
36452
+ }
36453
+ }
36454
+ if (gi < model.viewModel.groupedEntries.length - 1) {
36455
+ line++;
36456
+ }
36457
+ }
36458
+ line++;
36459
+ const rosterActionsLine = line;
36460
+ if (model.viewModel.selectedCarrierId === ROSTER_ACTIONS_ID && model.state.kind === "browse") {
36461
+ return rosterActionsLine;
36462
+ }
36463
+ line++;
36464
+ if (model.state.kind === "carrierActions") {
36465
+ return line + 1 + clampCursor(model.state.cursor, CARRIER_ACTION_LABELS.length);
36466
+ }
36467
+ if (model.state.kind === "rosterActions") {
36468
+ return line + 1 + clampCursor(model.state.cursor, ROSTER_ACTION_LABELS.length);
36469
+ }
36470
+ return void 0;
36471
+ }
36478
36472
  function getModelLabel(provider, modelId) {
36479
36473
  return provider.models.find((model) => model.modelId === modelId)?.name ?? modelId;
36480
36474
  }
36475
+ function getBatchChoiceLineOffset(lines, state) {
36476
+ const choiceStart = Math.max(0, lines.length - state.choices.length);
36477
+ return choiceStart + clampCursor(state.cursor, state.choices.length);
36478
+ }
36479
+ function getSelectedEntryFocusLine(entryLine, entry, model, deps) {
36480
+ const state = model.state;
36481
+ if (shouldRenderEntryEditor(state, entry.carrierId) && "cursor" in state) {
36482
+ return entryLine + 1 + clampCursor(state.cursor, getEntryEditorOptions(entry, state, deps).length);
36483
+ }
36484
+ if (model.renameState?.carrierId === entry.carrierId) {
36485
+ return entryLine + 2;
36486
+ }
36487
+ if (state.kind === "browse" || state.kind === "saving") {
36488
+ return entryLine;
36489
+ }
36490
+ return void 0;
36491
+ }
36492
+ function getSelectedEntryExtraRows(width, entry, model, deps) {
36493
+ let rows = 0;
36494
+ if (shouldRenderEntryEditor(model.state, entry.carrierId)) {
36495
+ rows += getEntryEditorOptions(entry, model.state, deps).length;
36496
+ }
36497
+ if (model.renameState?.carrierId === entry.carrierId) {
36498
+ rows += buildRenameEditorLines(model.renameState, deps.theme).length;
36499
+ }
36500
+ if (model.expandedCarrierId === entry.carrierId) {
36501
+ rows += buildDetailRows(entry, getDetailInnerWidth(width), deps).length;
36502
+ }
36503
+ return rows;
36504
+ }
36505
+ function clampCursor(cursor, length) {
36506
+ if (length <= 0) return 0;
36507
+ return Math.max(0, Math.min(length - 1, cursor));
36508
+ }
36481
36509
  function buildBatchCliPanelLines(state, deps) {
36482
36510
  if (state.kind !== "batchFrom" && state.kind !== "batchTo") return [];
36483
36511
  const title = state.kind === "batchFrom" ? " Batch CLI: FROM \uC120\uD0DD" : " Batch CLI: TO \uC120\uD0DD";
@@ -36488,16 +36516,15 @@ function buildBatchCliPanelLines(state, deps) {
36488
36516
  }
36489
36517
  for (let i = 0; i < state.choices.length; i++) {
36490
36518
  const choice = state.choices[i];
36491
- const cursor = i === state.cursor ? "\u25B8" : " ";
36519
+ const cursor = i === state.cursor ? deps.theme.accent("\u25B8") : " ";
36492
36520
  const content = ` ${cursor} \u25CB ${choice.label}`;
36493
36521
  lines.push(toCellLine(
36494
- choice.carrierCount === 0 && state.kind === "batchFrom" ? deps.theme.dim(content) : content,
36495
- i === state.cursor
36522
+ choice.carrierCount === 0 && state.kind === "batchFrom" ? deps.theme.dim(content) : content
36496
36523
  ));
36497
36524
  }
36498
36525
  return lines;
36499
36526
  }
36500
- function renderDisplayLines(lines, width, theme) {
36527
+ function renderDisplayLines(lines, width) {
36501
36528
  const cellWidth = resolveCellWidth(lines);
36502
36529
  return lines.map((line) => {
36503
36530
  if (line.kind === "blank") {
@@ -36506,28 +36533,25 @@ function renderDisplayLines(lines, width, theme) {
36506
36533
  if (line.kind === "center") {
36507
36534
  return centerText(line.text, width);
36508
36535
  }
36509
- return centerText(renderCellLine(line.line, cellWidth, theme), width);
36536
+ return renderCellLine(line.line, cellWidth, width);
36510
36537
  });
36511
36538
  }
36512
36539
  function resolveCellWidth(lines) {
36513
36540
  const lineWidths = lines.filter((line) => line.kind === "cell").map((line) => visibleWidth(line.line.text));
36514
36541
  return Math.max(MIN_CELL_WIDTH, ...lineWidths);
36515
36542
  }
36516
- function renderCellLine(line, cellWidth, theme) {
36543
+ function renderCellLine(line, cellWidth, width) {
36517
36544
  const padded = padEndVisible(truncateToWidth(line.text, cellWidth), cellWidth);
36518
- if (line.selected) {
36519
- return theme.bg("selected", padded);
36520
- }
36521
- return applyLineBg(padded, line.bg);
36545
+ return centerText(applyLineBg(padded, line.bg), width);
36522
36546
  }
36523
- function toCellLine(text, selected = false) {
36547
+ function toCellLine(text) {
36524
36548
  return {
36525
36549
  kind: "cell",
36526
- line: { selected, text }
36550
+ line: { text }
36527
36551
  };
36528
36552
  }
36529
- function toIndentedCellLine(text, selected = false) {
36530
- return toCellLine(withIndent(text), selected);
36553
+ function toIndentedCellLine(text) {
36554
+ return toCellLine(withIndent(text));
36531
36555
  }
36532
36556
  function withIndent(text) {
36533
36557
  return `${INDENT}${text}`;
@@ -36535,6 +36559,9 @@ function withIndent(text) {
36535
36559
  function padEndVisible(text, width) {
36536
36560
  return `${text}${" ".repeat(Math.max(0, width - visibleWidth(text)))}`;
36537
36561
  }
36562
+ function getDetailInnerWidth(width) {
36563
+ return Math.max(20, width - 8);
36564
+ }
36538
36565
  function buildDetailRows(entry, innerWidth, deps) {
36539
36566
  const provider = deps.getAvailableModels(entry.cliType);
36540
36567
  const modelLabel = getModelLabel(provider, entry.model);
@@ -36560,20 +36587,23 @@ function buildEntryEditorLines(entry, state, deps) {
36560
36587
  return options2.map((option3, index) => {
36561
36588
  const cursorToken = index === cursor ? `${getCliEntryColor(entry.cliType)}\u25B8${ANSI_RESET5}` : " ";
36562
36589
  const marker = option3.value === currentValue ? "\u25CF" : "\u25CB";
36563
- return toIndentedCellLine(` ${cursorToken} ${marker} ${option3.label}`, index === cursor);
36590
+ return toIndentedCellLine(` ${cursorToken} ${marker} ${option3.label}`);
36564
36591
  });
36565
36592
  }
36566
36593
  function buildActionMenuLines(title, labels, cursor, deps) {
36567
36594
  return [
36568
36595
  toIndentedCellLine(deps.theme.accent(` ${title}`)),
36569
- ...labels.map((label, index) => toIndentedCellLine(` ${index === cursor ? "\u25B8" : " "} ${label}`, index === cursor))
36596
+ ...labels.map((label, index) => {
36597
+ const marker = index === cursor ? deps.theme.accent("\u25B8") : " ";
36598
+ return toIndentedCellLine(` ${marker} ${label}`);
36599
+ })
36570
36600
  ];
36571
36601
  }
36572
36602
  function buildRenameEditorLines(renameState, theme) {
36573
36603
  const draft = renameState.draft.length > 0 ? renameState.draft : theme.dim("(empty resets default)");
36574
36604
  return [
36575
36605
  toIndentedCellLine(theme.accent(" \uC774\uB984 \uBCC0\uACBD")),
36576
- toIndentedCellLine(` \u25B8 ${draft}`, true)
36606
+ toIndentedCellLine(` ${theme.accent("\u25B8")} ${draft}`)
36577
36607
  ];
36578
36608
  }
36579
36609
  function getEntryEditorCurrentValue(entry, state, deps) {
@@ -36617,10 +36647,16 @@ function isWarningFeedback(message) {
36617
36647
  return message.startsWith("\uC800\uC7A5 \uC2E4\uD328") || message.startsWith("\uACBD\uACE0:");
36618
36648
  }
36619
36649
  function getEntryColor(entry) {
36620
- if (entry.subagentMode) return getSubagentSignatureColor();
36650
+ if (entry.subagentMode) return PROVIDER_ANSI_COLORS[entry.cliType] ?? "";
36621
36651
  if (entry.taskForceBackendCount >= 2) return TASKFORCE_BADGE_COLOR2;
36622
36652
  return PROVIDER_ANSI_COLORS[entry.cliType] ?? "";
36623
36653
  }
36654
+ function getEntryBgColor(cliType) {
36655
+ return PROVIDER_BG_ANSI_COLORS[cliType];
36656
+ }
36657
+ function getEntryBgColorForEntry(entry) {
36658
+ return getEntryBgColor(entry.cliType);
36659
+ }
36624
36660
  function getCliEntryColor(cliType) {
36625
36661
  return PROVIDER_ANSI_COLORS[cliType] ?? "";
36626
36662
  }
@@ -36654,7 +36690,7 @@ function renderEntryLine(entry, isSelected, deps) {
36654
36690
  const effortSupported = deps.getModelEffortLevels(entry.cliType, entry.model).length > 0;
36655
36691
  const effortStr = effortSupported && entry.effort ? `${dim3(" \xB7 ")}${entry.effort}` : "";
36656
36692
  const roleStr = entry.role ? dim3(` (${entry.role})`) : "";
36657
- const taskForceTag = entry.taskForceBackendCount >= 2 ? ` ${TASKFORCE_BADGE_COLOR2}[TF:${entry.taskForceBackendCount}]${ANSI_RESET5}` : "";
36693
+ const taskForceTag = !entry.subagentMode && entry.taskForceBackendCount >= 2 ? ` ${TASKFORCE_BADGE_COLOR2}[TF:${entry.taskForceBackendCount}]${ANSI_RESET5}` : "";
36658
36694
  const subagentTag = entry.subagentMode ? ` ${getSubagentSignatureColor()}[SA]${ANSI_RESET5}` : "";
36659
36695
  return `${selectedPrefix} ${dim3(slotStr)}${slotPad}${coloredName}${namePad}${modelStr}${effortStr}${roleStr}${subagentTag}${taskForceTag}`;
36660
36696
  }
@@ -36856,11 +36892,11 @@ function sanitizeCarrierMetadataText(value) {
36856
36892
  // src/mission-control/carrier-roster/panel.ts
36857
36893
  var ROSTER_ACTIONS_ID2 = "__roster_actions__";
36858
36894
  var CARRIER_ACTION_LABELS2 = [
36859
- "Edit Model",
36860
- "Change CLI Type",
36861
- "Rename",
36862
- "Toggle Native(SubAgent)",
36863
- "Open TaskForce",
36895
+ "Agent CLI",
36896
+ "Model",
36897
+ "Configure TaskForce",
36898
+ "Configure SubAgent",
36899
+ "Rename Carrier",
36864
36900
  "Toggle Details"
36865
36901
  ];
36866
36902
  var ROSTER_ACTION_LABELS2 = [
@@ -36917,6 +36953,9 @@ var CarrierStatusOverlay = class {
36917
36953
  render(width) {
36918
36954
  return renderCarrierStatusOverlay(width, this.getRenderModel(), this.getRenderDeps());
36919
36955
  }
36956
+ getFocusLine(width) {
36957
+ return getCarrierStatusFocusLine(width, this.getRenderModel(), this.getRenderDeps());
36958
+ }
36920
36959
  getEntries() {
36921
36960
  return buildStatusEntries(this.options.carrierRuntime);
36922
36961
  }
@@ -37014,19 +37053,19 @@ var CarrierStatusOverlay = class {
37014
37053
  this.state = { kind: "browse" };
37015
37054
  switch (index) {
37016
37055
  case 0:
37017
- this.startModelEdit();
37056
+ this.startCliTypeEdit();
37018
37057
  return;
37019
37058
  case 1:
37020
- this.startCliTypeEdit();
37059
+ this.startModelEdit();
37021
37060
  return;
37022
37061
  case 2:
37023
- this.startRenameEdit();
37062
+ this.openTaskForce();
37024
37063
  return;
37025
37064
  case 3:
37026
37065
  this.toggleSubagentMode();
37027
37066
  return;
37028
37067
  case 4:
37029
- this.openTaskForce();
37068
+ this.startRenameEdit();
37030
37069
  return;
37031
37070
  case 5:
37032
37071
  this.toggleDetails();
@@ -37582,7 +37621,6 @@ var RosterTaskForcePanelSurface = class {
37582
37621
  body.push({
37583
37622
  kind: "cell",
37584
37623
  line: {
37585
- selected: isSelected,
37586
37624
  text: withIndent2(this.renderEntryLine(entry, isSelected))
37587
37625
  }
37588
37626
  });
@@ -37592,7 +37630,7 @@ var RosterTaskForcePanelSurface = class {
37592
37630
  const model = models[j];
37593
37631
  const cursor = j === this.editCursor ? `${entry.color}\u25B8${ANSI_RESET6}` : " ";
37594
37632
  const marker = model.modelId === entry.model ? "\u25CF" : "\u25CB";
37595
- body.push(toIndentedCellLine2(` ${cursor} ${marker} ${model.name ?? model.modelId}`, j === this.editCursor));
37633
+ body.push(toIndentedCellLine2(` ${cursor} ${marker} ${model.name ?? model.modelId}`));
37596
37634
  }
37597
37635
  }
37598
37636
  if (isSelected && this.mode === "effort") {
@@ -37601,7 +37639,7 @@ var RosterTaskForcePanelSurface = class {
37601
37639
  const level = effortLevels[j];
37602
37640
  const cursor = j === this.editCursor ? `${entry.color}\u25B8${ANSI_RESET6}` : " ";
37603
37641
  const marker = level === (entry.effort ?? "") ? "\u25CF" : "\u25CB";
37604
- body.push(toIndentedCellLine2(` ${cursor} ${marker} ${level}`, j === this.editCursor));
37642
+ body.push(toIndentedCellLine2(` ${cursor} ${marker} ${level}`));
37605
37643
  }
37606
37644
  }
37607
37645
  if (isSelected && this.mode === "actions") {
@@ -37610,7 +37648,7 @@ var RosterTaskForcePanelSurface = class {
37610
37648
  for (let j = 0; j < actions.length; j++) {
37611
37649
  const action = actions[j];
37612
37650
  const cursor = j === this.editCursor ? `${entry.color}\u25B8${ANSI_RESET6}` : " ";
37613
- body.push(toIndentedCellLine2(` ${cursor} ${action}`, j === this.editCursor));
37651
+ body.push(toIndentedCellLine2(` ${cursor} ${action}`));
37614
37652
  }
37615
37653
  }
37616
37654
  }
@@ -37620,7 +37658,7 @@ var RosterTaskForcePanelSurface = class {
37620
37658
  body.push({ kind: "center", text: color(this.feedbackMessage) }, { kind: "blank" });
37621
37659
  }
37622
37660
  body.push({ kind: "center", text: this.options.theme.dim(this.getFooterHint()) });
37623
- return renderDisplayLines2(body, width, this.options.theme);
37661
+ return renderDisplayLines2(body, width);
37624
37662
  }
37625
37663
  buildBackendEntries() {
37626
37664
  return this.getTaskForceCliTypes().map((cliType) => {
@@ -37923,7 +37961,7 @@ function syncConfiguredTaskForceCarriers(carrierRuntime) {
37923
37961
  function clampOverlayRows(maxRows, cardRows) {
37924
37962
  return Math.min(Math.max(0, maxRows), Math.max(0, cardRows));
37925
37963
  }
37926
- function renderDisplayLines2(lines, width, theme) {
37964
+ function renderDisplayLines2(lines, width) {
37927
37965
  const cellWidth = resolveCellWidth2(lines);
37928
37966
  return lines.map((line) => {
37929
37967
  if (line.kind === "blank") {
@@ -37932,28 +37970,25 @@ function renderDisplayLines2(lines, width, theme) {
37932
37970
  if (line.kind === "center") {
37933
37971
  return centerText(line.text, width);
37934
37972
  }
37935
- return centerText(renderCellLine2(line.line, cellWidth, theme), width);
37973
+ return renderCellLine2(line.line, cellWidth, width);
37936
37974
  });
37937
37975
  }
37938
37976
  function resolveCellWidth2(lines) {
37939
37977
  const lineWidths = lines.filter((line) => line.kind === "cell").map((line) => visibleWidth(line.line.text));
37940
37978
  return Math.max(MIN_CELL_WIDTH2, ...lineWidths);
37941
37979
  }
37942
- function renderCellLine2(line, cellWidth, theme) {
37980
+ function renderCellLine2(line, cellWidth, width) {
37943
37981
  const padded = padEndVisible2(truncateToWidth(line.text, cellWidth), cellWidth);
37944
- if (line.selected) {
37945
- return theme.bg("selected", padded);
37946
- }
37947
- return applyLineBg2(padded, line.bg);
37982
+ return centerText(applyLineBg2(padded, line.bg), width);
37948
37983
  }
37949
- function toCellLine2(text, selected = false) {
37984
+ function toCellLine2(text) {
37950
37985
  return {
37951
37986
  kind: "cell",
37952
- line: { selected, text }
37987
+ line: { text }
37953
37988
  };
37954
37989
  }
37955
- function toIndentedCellLine2(text, selected = false) {
37956
- return toCellLine2(withIndent2(text), selected);
37990
+ function toIndentedCellLine2(text) {
37991
+ return toCellLine2(withIndent2(text));
37957
37992
  }
37958
37993
  function withIndent2(text) {
37959
37994
  return `${INDENT2}${text}`;
@@ -38001,6 +38036,9 @@ function createCarrierRosterPanel(options2) {
38001
38036
  component.handleInput(data);
38002
38037
  return true;
38003
38038
  },
38039
+ getFocusLine({ width }) {
38040
+ return component.getFocusLine?.(width);
38041
+ },
38004
38042
  render({ width }) {
38005
38043
  return component.render(width);
38006
38044
  }
@@ -38028,6 +38066,9 @@ function createPanelStack(options2) {
38028
38066
  },
38029
38067
  invalidate() {
38030
38068
  },
38069
+ getFocusLine(width) {
38070
+ return api.current().getFocusLine?.({ width });
38071
+ },
38031
38072
  render(width) {
38032
38073
  return [...api.current().render({ width })];
38033
38074
  }
@@ -38137,6 +38178,12 @@ function createActionListPanel(options2) {
38137
38178
  }
38138
38179
  return false;
38139
38180
  },
38181
+ getFocusLine({ width }) {
38182
+ const actions = resolveActions(options2.actions);
38183
+ selected = clampSelected(selected, actions.length);
38184
+ if (actions.length === 0) return void 0;
38185
+ return getActionFocusLine(width, options2, selected);
38186
+ },
38140
38187
  render({ width }) {
38141
38188
  const actions = resolveActions(options2.actions);
38142
38189
  selected = clampSelected(selected, actions.length);
@@ -38159,6 +38206,14 @@ function createActionListPanel(options2) {
38159
38206
  }
38160
38207
  };
38161
38208
  }
38209
+ function getActionFocusLine(width, options2, selected) {
38210
+ const breadcrumbs = options2.breadcrumbs?.() ?? [];
38211
+ const breadcrumbLines = breadcrumbs.length <= 1 ? [] : [
38212
+ "",
38213
+ centerText(MISSION_CONTROL_THEME.dim(renderBreadcrumbs(breadcrumbs)), width)
38214
+ ];
38215
+ return breadcrumbLines.length + 2 + selected;
38216
+ }
38162
38217
  function resolveActions(actions) {
38163
38218
  const items = typeof actions === "function" ? actions() : actions;
38164
38219
  return items.filter((item) => item !== false && item !== void 0);
@@ -39619,11 +39674,6 @@ function formatError3(error51) {
39619
39674
  }
39620
39675
 
39621
39676
  // src/mission-control/controller.ts
39622
- var EMPTY_KEYBOARD_PROTOCOL_STATE = {
39623
- childRequested: false,
39624
- effectiveMode: "passthrough",
39625
- outerEnabled: false
39626
- };
39627
39677
  var EMPTY_MOUSE_PROTOCOL_STATE = {
39628
39678
  activeEncoding: "default",
39629
39679
  activeProtocol: "none",
@@ -39677,16 +39727,18 @@ function createMissionControlController(options2) {
39677
39727
  openLauncherRoot();
39678
39728
  }
39679
39729
  if (activePanel !== void 0) {
39680
- return fitMissionControlRows(renderMissionControl(width, {
39730
+ const panelLines = activePanel.component.render(width);
39731
+ const rendered = renderMissionControl(width, {
39681
39732
  bannerPhase: getBannerPhase(),
39682
39733
  cliOptions,
39683
39734
  lastExit,
39684
39735
  loadedCounts: options2.loadedCounts,
39685
- panelLines: activePanel.component.render(width),
39736
+ panelLines,
39686
39737
  release: release2,
39687
39738
  selectedCliId,
39688
39739
  state
39689
- }), rows);
39740
+ });
39741
+ return fitMissionControlRows(rendered, rows, getMissionControlPanelFocusLine(activePanel.component, width, rendered, panelLines));
39690
39742
  }
39691
39743
  return fitMissionControlRows(renderMissionControl(width, {
39692
39744
  bannerPhase: getBannerPhase(),
@@ -39701,7 +39753,6 @@ function createMissionControlController(options2) {
39701
39753
  resize(nextCols, nextRows) {
39702
39754
  cols = nextCols;
39703
39755
  rows = nextRows;
39704
- activePanel?.component.desiredHeight?.(nextRows);
39705
39756
  active?.view.resize(nextCols, nextRows);
39706
39757
  },
39707
39758
  scrollLines(delta) {
@@ -39712,7 +39763,6 @@ function createMissionControlController(options2) {
39712
39763
  }
39713
39764
  };
39714
39765
  const ptyHost = {
39715
- getKeyboardProtocol: () => active?.host.getKeyboardProtocol?.() ?? EMPTY_KEYBOARD_PROTOCOL_STATE,
39716
39766
  getMouseProtocol: () => active?.host.getMouseProtocol?.() ?? EMPTY_MOUSE_PROTOCOL_STATE,
39717
39767
  kill() {
39718
39768
  suppressNextExit = true;
@@ -40008,6 +40058,10 @@ function createStartPanel(options2) {
40008
40058
  }
40009
40059
  return false;
40010
40060
  },
40061
+ getFocusLine() {
40062
+ selected = clampIndex(selected, options2.cliOptions.length);
40063
+ return options2.cliOptions.length === 0 ? void 0 : 4 + selected;
40064
+ },
40011
40065
  render({ width }) {
40012
40066
  selected = clampIndex(selected, options2.cliOptions.length);
40013
40067
  return [
@@ -40251,10 +40305,11 @@ function normalizeRenderedRows(lines, rows) {
40251
40305
  }
40252
40306
  return normalized;
40253
40307
  }
40254
- function fitMissionControlRows(lines, rows) {
40308
+ function fitMissionControlRows(lines, rows, focusLine) {
40255
40309
  const targetRows = Math.max(0, Math.floor(rows));
40256
40310
  if (lines.length >= targetRows) {
40257
- return lines.slice(0, targetRows);
40311
+ const start = getFitStartLine(lines.length, targetRows, focusLine);
40312
+ return lines.slice(start, start + targetRows);
40258
40313
  }
40259
40314
  const topPadding = Math.floor((targetRows - lines.length) / 2);
40260
40315
  const normalized = [
@@ -40266,6 +40321,18 @@ function fitMissionControlRows(lines, rows) {
40266
40321
  }
40267
40322
  return normalized;
40268
40323
  }
40324
+ function getFitStartLine(totalRows, targetRows, focusLine) {
40325
+ if (targetRows <= 0 || focusLine === void 0 || !Number.isFinite(focusLine)) return 0;
40326
+ const normalizedFocusLine = Math.max(0, Math.min(totalRows - 1, Math.floor(focusLine)));
40327
+ const maxStart = Math.max(0, totalRows - targetRows);
40328
+ if (normalizedFocusLine < targetRows) return 0;
40329
+ return Math.min(maxStart, normalizedFocusLine - targetRows + 1);
40330
+ }
40331
+ function getMissionControlPanelFocusLine(component, width, rendered, panelLines) {
40332
+ const panelFocusLine = component.getFocusLine?.(width);
40333
+ if (panelFocusLine === void 0) return void 0;
40334
+ return rendered.length - panelLines.length + panelFocusLine;
40335
+ }
40269
40336
  function formatSaveError(error51) {
40270
40337
  if (error51 instanceof Error && error51.message.length > 0) {
40271
40338
  return error51.message;
@@ -46361,7 +46428,6 @@ function countMarkdownFilesRecursively(dir, isWikiRoot) {
46361
46428
 
46362
46429
  // src/mission-bridge/fleet-status-section.ts
46363
46430
  var BORDER_CHAR = "\u2500";
46364
- var PROTOCOL_ICON = "\u2693";
46365
46431
  var FleetStatusSection = class {
46366
46432
  constructor(options2) {
46367
46433
  this.options = options2;
@@ -46370,22 +46436,10 @@ var FleetStatusSection = class {
46370
46436
  invalidate() {
46371
46437
  }
46372
46438
  render(width) {
46373
- if (this.options.getNative?.() ?? this.options.native) {
46374
- return [renderBorder(width, DIM_COLOR)];
46375
- }
46376
- return [renderStatusLine2(width, getFleetActionHudColor(), FLEET_ACTION_LABEL)];
46439
+ this.options.getNative?.();
46440
+ return [renderBorder(width, DIM_COLOR)];
46377
46441
  }
46378
46442
  };
46379
- function renderStatusLine2(width, protocolColor, protocolLabel) {
46380
- if (width <= 0) return "";
46381
- const centerBlock = paint(protocolColor, ` ${PROTOCOL_ICON} ${protocolLabel} `, true);
46382
- const centerWidth = visibleWidth(centerBlock);
46383
- if (centerWidth >= width) return truncateToWidth(centerBlock, width);
46384
- const remainingWidth = width - centerWidth;
46385
- const leftWidth = Math.floor(remainingWidth / 2);
46386
- const rightWidth = remainingWidth - leftWidth;
46387
- return renderBorder(leftWidth, protocolColor) + centerBlock + renderBorder(rightWidth, protocolColor);
46388
- }
46389
46443
  function renderBorder(width, color) {
46390
46444
  if (width <= 0) return "";
46391
46445
  return paint(color, BORDER_CHAR.repeat(width), true);
@@ -46449,14 +46503,13 @@ function buildPanelTrackViewModel(track, runs, maxTrackBlocks = DEFAULT_MAX_TRAC
46449
46503
  blocks: blockTail,
46450
46504
  displayCli: track.displayCli,
46451
46505
  displayName: track.displayName,
46452
- isComplete: status === "done",
46506
+ estimatedTokenCount: stats.estimatedTokenCount,
46507
+ isComplete: status === "done" || status === "err",
46453
46508
  kind: track.kind,
46454
46509
  runId: run?.runId ?? track.runId,
46455
46510
  status,
46456
46511
  streamKey: track.streamKey,
46457
46512
  subtitle: track.subtitle,
46458
- textLineCount: stats.textLineCount,
46459
- toolCallCount: stats.toolCallCount,
46460
46513
  trackId: track.trackId
46461
46514
  };
46462
46515
  }
@@ -46496,16 +46549,18 @@ function resolveRun(track, runs) {
46496
46549
  return runs.get(track.streamKey) ?? runs.get(track.trackId);
46497
46550
  }
46498
46551
  function collectBlockStats(blocks) {
46499
- let textLineCount = 0;
46500
- let toolCallCount = 0;
46552
+ let charCount = 0;
46501
46553
  for (const block of blocks) {
46502
46554
  if (block.type === "tool") {
46503
- toolCallCount++;
46555
+ charCount += block.title.length;
46556
+ if (block.status) charCount += block.status.length;
46557
+ charCount += block.detailChars ?? 0;
46504
46558
  continue;
46505
46559
  }
46506
- textLineCount += block.text.split("\n").filter((line) => line.trim()).length;
46560
+ charCount += block.text.length;
46507
46561
  }
46508
- return { textLineCount, toolCallCount };
46562
+ const estimatedTokenCount = charCount === 0 ? 0 : Math.max(1, Math.round(charCount / 4));
46563
+ return { estimatedTokenCount };
46509
46564
  }
46510
46565
 
46511
46566
  // src/mission-bridge/job-bar/renderer.ts
@@ -46528,8 +46583,9 @@ var KIND_LABELS = {
46528
46583
  function renderCarrierJobHud(options2) {
46529
46584
  const lines = [];
46530
46585
  const jobs2 = buildActiveJobViewModels(options2.jobs, options2.runs);
46586
+ const now = options2.now ?? Date.now();
46531
46587
  if (jobs2.length === 0) return [];
46532
- appendWidgetJobSummary(options2.carrierRuntime, lines, options2.width, jobs2, options2.frame, options2.theme);
46588
+ appendWidgetJobSummary(options2.carrierRuntime, lines, options2.width, jobs2, options2.frame, options2.theme, now);
46533
46589
  return lines.slice(0, MAX_WIDGET_LINES).map((line) => truncateToWidth(line, options2.width));
46534
46590
  }
46535
46591
  function renderCarrierJobHudStrip(options2) {
@@ -46540,7 +46596,6 @@ function renderCarrierJobHudStrip(options2) {
46540
46596
  carriers,
46541
46597
  options2.frame,
46542
46598
  options2.theme,
46543
- options2.keyboardProtocol,
46544
46599
  options2.pendingExitWarning === true
46545
46600
  );
46546
46601
  }
@@ -46616,13 +46671,12 @@ function renderBlockLines(blocks) {
46616
46671
  }
46617
46672
  return lines;
46618
46673
  }
46619
- function renderCarrierHudStrip(width, carriers, frame, theme, keyboardProtocol, pendingExitWarning) {
46674
+ function renderCarrierHudStrip(width, carriers, frame, theme, pendingExitWarning) {
46620
46675
  const tiles = carriers.map((carrier) => formatCarrierTile(carrier, frame));
46621
46676
  const line = centerLine2(tiles.join(tileSeparator(theme)), width);
46622
- const warnedLine = prependExitWarning(line, width, pendingExitWarning);
46623
- return [appendProtocolIndicator(warnedLine, width, keyboardProtocol)];
46677
+ return [prependExitWarning(line, width, pendingExitWarning)];
46624
46678
  }
46625
- function appendWidgetJobSummary(carrierRuntime, lines, width, jobs2, frame, theme) {
46679
+ function appendWidgetJobSummary(carrierRuntime, lines, width, jobs2, frame, theme, now) {
46626
46680
  const subagentModes = readCarrierAgentModeSnapshot(buildCarrierDefaults(carrierRuntime)).agentModes;
46627
46681
  const groups = buildCarrierJobGroups(
46628
46682
  jobs2,
@@ -46635,7 +46689,7 @@ function appendWidgetJobSummary(carrierRuntime, lines, width, jobs2, frame, them
46635
46689
  const taskForceBackendCount = getConfiguredTaskForceBackendsFromSnapshot(readCarriersSnapshot(), group.carrierId).length;
46636
46690
  const groupColor = resolveCarrierPresentationColor(carrierRuntime, group.carrierId, subagentModes, taskForceBackendCount);
46637
46691
  lines.push(truncateToWidth(
46638
- `${STREAM_PREFIX}${groupColor}Carrier ${group.displayName}${ANSI_RESET4}`,
46692
+ `${STREAM_PREFIX}${groupColor}${group.displayName}${ANSI_RESET4}`,
46639
46693
  width
46640
46694
  ));
46641
46695
  for (let jobIndex = 0; jobIndex < group.jobs.length && lines.length < MAX_WIDGET_LINES; jobIndex++) {
@@ -46645,9 +46699,10 @@ function appendWidgetJobSummary(carrierRuntime, lines, width, jobs2, frame, them
46645
46699
  const jobBranch = isLastJob ? "\u2514\u2500" : "\u251C\u2500";
46646
46700
  const jobColor = resolveJobRowColor(carrierRuntime, job);
46647
46701
  const inline = shouldInlineSingleTrack(job) && job.tracks[0] && !job.tracks[0].isComplete ? trackInlineBlock(job.tracks[0]) : "";
46702
+ const elapsed = widgetJobElapsed(job, now);
46648
46703
  const stats = shouldInlineSingleTrack(job) && job.tracks[0] ? widgetTrackStats(job.tracks[0]) : "";
46649
46704
  lines.push(truncateToWidth(
46650
- `${STREAM_PREFIX} ${border(theme, jobBranch)} ${jobIcon(job, frame, jobColor)} ${jobColor}${jobDisplayLabel(job)}${ANSI_RESET4}${stats}${inline}`,
46705
+ `${STREAM_PREFIX} ${border(theme, jobBranch)} ${jobIcon(job, frame, jobColor)} ${jobColor}${jobDisplayLabel(job)}${ANSI_RESET4}${elapsed}${stats}${inline}`,
46651
46706
  width
46652
46707
  ));
46653
46708
  if (shouldInlineSingleTrack(job)) continue;
@@ -46678,7 +46733,6 @@ function buildCarrierTiles(carrierRuntime, activeJobs) {
46678
46733
  const subagentMode = subagentModes[carrierId] === "subagent";
46679
46734
  return {
46680
46735
  activeJobCount: activeCarrierJobs.length,
46681
- activeTrackCount: activeCarrierJobs.reduce((sum, job) => sum + job.tracks.length, 0),
46682
46736
  carrierId,
46683
46737
  color: resolveCarrierPresentationColor(carrierRuntime, carrierId, subagentModes, taskForceBackendCount),
46684
46738
  displayName: resolveCarrierDisplayName2(carrierRuntime.registry, carrierId),
@@ -46709,14 +46763,6 @@ function centerLine2(line, width) {
46709
46763
  function centerPadding(line, width) {
46710
46764
  return Math.max(0, Math.floor((width - visibleWidth(line)) / 2));
46711
46765
  }
46712
- function appendProtocolIndicator(line, width, state) {
46713
- if (!state) return line;
46714
- const indicator = formatProtocolIndicator(state);
46715
- const indicatorWidth = visibleWidth(indicator);
46716
- const content = truncateToWidth(line, Math.max(0, width - indicatorWidth));
46717
- const padding = Math.max(0, width - visibleWidth(content) - indicatorWidth);
46718
- return truncateToWidth(`${content}${" ".repeat(padding)}${indicator}`, width);
46719
- }
46720
46766
  function prependExitWarning(line, width, pending) {
46721
46767
  if (!pending) return line;
46722
46768
  const warning = formatExitWarning();
@@ -46736,18 +46782,10 @@ function prependExitWarning(line, width, pending) {
46736
46782
  function formatExitWarning() {
46737
46783
  return `${PANEL_DIM_COLOR}${EXIT_WARNING_TEXT}${ANSI_RESET4}`;
46738
46784
  }
46739
- function formatProtocolIndicator(state) {
46740
- const suffix = protocolIndicatorSuffix(state);
46741
- return `${PANEL_DIM_COLOR}\u2328${suffix}${ANSI_RESET4}`;
46742
- }
46743
- function protocolIndicatorSuffix(state) {
46744
- if (!state.outerEnabled) return "S";
46745
- return state.effectiveMode === "transform" ? "T" : "E";
46746
- }
46747
46785
  function formatCarrierTile(carrier, frame) {
46748
46786
  const icon = carrierStatusIcon(carrier, frame, carrier.color);
46749
46787
  const hasActiveJob = carrier.activeJobCount > 0;
46750
- const suffix = `${carrierBadges(carrier)}${carrierActivityBadge(carrier)}`;
46788
+ const suffix = carrierBadges(carrier);
46751
46789
  const prefix = `${icon} `;
46752
46790
  if (hasActiveJob) {
46753
46791
  return `${prefix}${waveText(carrier.displayName, carrier.rgb, frame)}${suffix}${ANSI_RESET4}`;
@@ -46756,9 +46794,7 @@ function formatCarrierTile(carrier, frame) {
46756
46794
  }
46757
46795
  function carrierStatusIcon(carrier, frame, color) {
46758
46796
  if (carrier.activeJobCount > 0) {
46759
- const frames = SPINNER_FRAMES;
46760
- const spinner = frames[frame % frames.length] ?? "\u25CB";
46761
- return color ? `${color}${spinner}${ANSI_RESET4}` : spinner;
46797
+ return activeBreathingIcon(frame, color);
46762
46798
  }
46763
46799
  return color ? `${color}\u25CB${ANSI_RESET4}` : "\u25CB";
46764
46800
  }
@@ -46769,11 +46805,6 @@ function carrierBadges(carrier) {
46769
46805
  const tfBadge = carrier.taskForceBackendCount >= 2 ? ` ${TASKFORCE_BADGE_COLOR2}[TF:${carrier.taskForceBackendCount}]${ANSI_RESET4}` : "";
46770
46806
  return tfBadge;
46771
46807
  }
46772
- function carrierActivityBadge(carrier) {
46773
- if (carrier.activeJobCount <= 0) return "";
46774
- const trackSuffix = carrier.activeTrackCount > 0 ? `:${carrier.activeTrackCount}` : "";
46775
- return ` ${PANEL_DIM_COLOR}[${carrier.activeJobCount}${trackSuffix}]${ANSI_RESET4}`;
46776
- }
46777
46808
  function kindDisplayName(kind) {
46778
46809
  return KIND_LABELS[kind] ?? capitalize(kind);
46779
46810
  }
@@ -46783,9 +46814,7 @@ function capitalize(text) {
46783
46814
  }
46784
46815
  function jobIcon(job, frame, color) {
46785
46816
  if (job.status === "active") {
46786
- const frames = SPINNER_FRAMES;
46787
- const spinner = frames[frame % frames.length] ?? "\u25CB";
46788
- return color ? `${color}${spinner}${ANSI_RESET4}` : spinner;
46817
+ return activeBreathingIcon(frame, color);
46789
46818
  }
46790
46819
  if (job.status === "done") return `${COLOR_DONE}${SYM_INDICATOR}${ANSI_RESET4}`;
46791
46820
  if (job.status === "error" || job.status === "aborted") return `${COLOR_ERROR}${SYM_INDICATOR}${ANSI_RESET4}`;
@@ -46813,14 +46842,12 @@ function buildCarrierDefaults(carrierRuntime) {
46813
46842
  );
46814
46843
  }
46815
46844
  function resolveCarrierPresentationColor(carrierRuntime, carrierId, subagentModes, taskForceBackendCount) {
46816
- if (subagentModes[carrierId] === "subagent") return SUBAGENT_PRESENTATION_ANSI;
46845
+ if (subagentModes[carrierId] === "subagent") return resolveCarrierColor(carrierRuntime.registry, carrierId);
46817
46846
  if (taskForceBackendCount >= 2) return TASKFORCE_BADGE_COLOR2;
46818
46847
  return resolveCarrierColor(carrierRuntime.registry, carrierId);
46819
46848
  }
46820
46849
  function resolveCarrierPresentationRgb(carrierRuntime, carrierId, subagentModes, taskForceBackendCount) {
46821
- if (subagentModes[carrierId] === "subagent") {
46822
- return [...SUBAGENT_PRESENTATION_RGB];
46823
- }
46850
+ if (subagentModes[carrierId] === "subagent") return resolveCarrierRgb(carrierRuntime.registry, carrierId);
46824
46851
  if (taskForceBackendCount >= 2) return TASKFORCE_BADGE_RGB;
46825
46852
  return resolveCarrierRgb(carrierRuntime.registry, carrierId);
46826
46853
  }
@@ -46836,9 +46863,7 @@ function trackStatusIcon(track, frame, color) {
46836
46863
  if (track.status === "err") return `${COLOR_ERROR}${SYM_INDICATOR}${ANSI_RESET4}`;
46837
46864
  return `${COLOR_DONE}${SYM_INDICATOR}${ANSI_RESET4}`;
46838
46865
  }
46839
- const frames = SPINNER_FRAMES;
46840
- const spinner = frames[frame % frames.length] ?? "\u25CB";
46841
- return color ? `${color}${spinner}${ANSI_RESET4}` : spinner;
46866
+ return activeBreathingIcon(frame, color);
46842
46867
  }
46843
46868
  function trackDisplayName(track) {
46844
46869
  if (track.kind === "backend") {
@@ -46931,10 +46956,31 @@ function isHudEscIntermediate(code) {
46931
46956
  return code >= 32 && code <= 47;
46932
46957
  }
46933
46958
  function widgetTrackStats(track) {
46934
- const parts = [];
46935
- if (track.toolCallCount > 0) parts.push(`${track.toolCallCount}T`);
46936
- if (track.textLineCount > 0) parts.push(`${track.textLineCount}L`);
46937
- return parts.length > 0 ? ` ${PANEL_DIM_COLOR}[${parts.join("\xB7")}]${ANSI_RESET4}` : "";
46959
+ const label = formatTokenEstimate(track.estimatedTokenCount);
46960
+ return label ? ` ${PANEL_DIM_COLOR}${label}${ANSI_RESET4}` : "";
46961
+ }
46962
+ function widgetJobElapsed(job, now) {
46963
+ return ` ${PANEL_DIM_COLOR}${formatElapsedDuration((job.finishedAt ?? now) - job.startedAt)}${ANSI_RESET4}`;
46964
+ }
46965
+ function activeBreathingIcon(frame, color) {
46966
+ const cycleFrame = (frame % BREATHING_CYCLE_FRAMES + BREATHING_CYCLE_FRAMES) % BREATHING_CYCLE_FRAMES;
46967
+ const eased = (Math.sin(cycleFrame / BREATHING_CYCLE_FRAMES * Math.PI * 2 - Math.PI / 2) + 1) / 2;
46968
+ const icon = eased >= 0.5 ? "\u25CF" : "\u25CB";
46969
+ return color ? `${color}${icon}${ANSI_RESET4}` : icon;
46970
+ }
46971
+ function formatTokenEstimate(tokenCount) {
46972
+ if (tokenCount <= 0) return "";
46973
+ if (tokenCount < 1e3) return `~${tokenCount} tokens`;
46974
+ const scaled = tokenCount / 1e3;
46975
+ const rounded = scaled.toFixed(1).replace(/\.0$/, "");
46976
+ return `~${rounded}k tokens`;
46977
+ }
46978
+ function formatElapsedDuration(elapsedMs) {
46979
+ const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
46980
+ if (totalSeconds < 60) return `${totalSeconds}s`;
46981
+ const minutes = Math.floor(totalSeconds / 60);
46982
+ const seconds = totalSeconds % 60;
46983
+ return `${minutes}m ${seconds}s`;
46938
46984
  }
46939
46985
 
46940
46986
  // src/mission-bridge/job-bar/section.ts
@@ -46963,7 +47009,6 @@ var JobBarStripSection = class {
46963
47009
  carrierRuntime: this.jobBarState.carrierRuntime,
46964
47010
  frame: state.frame,
46965
47011
  jobs: this.jobBarState.getActiveJobs(),
46966
- keyboardProtocol: this.jobBarState.getKeyboardProtocol?.(),
46967
47012
  pendingExitWarning: this.jobBarState.getPendingExitWarning(),
46968
47013
  runs: this.jobBarState.getPanelRuns(),
46969
47014
  width
@@ -47012,7 +47057,6 @@ function createJobBarState(options2) {
47012
47057
  dispose: disposeJobBarState,
47013
47058
  ensurePanelAnimTimer,
47014
47059
  getActiveJobs: getActiveJobs2,
47015
- getKeyboardProtocol: options2.getKeyboardProtocol,
47016
47060
  getGrandFleetStreamStoreState,
47017
47061
  getJobById,
47018
47062
  getPanelJobs,
@@ -47188,7 +47232,7 @@ function createJobBarState(options2) {
47188
47232
  return;
47189
47233
  case "track:tool":
47190
47234
  updateRunBlocks(event.jobId, event.trackId, (run) => {
47191
- upsertToolBlock(run.blocks, event.toolCallId, event.title, event.status);
47235
+ upsertToolBlock(run.blocks, event.toolCallId, event.title, event.status, event.detailChars);
47192
47236
  if (run.status === "wait" || run.status === "conn") run.status = "stream";
47193
47237
  });
47194
47238
  schedulePanelRender(true);
@@ -47461,7 +47505,7 @@ function createJobBarState(options2) {
47461
47505
  }
47462
47506
  blocks.push({ text, type: "thought" });
47463
47507
  }
47464
- function upsertToolBlock(blocks, toolCallId, title, status) {
47508
+ function upsertToolBlock(blocks, toolCallId, title, status, detailChars) {
47465
47509
  const existingIndex = blocks.findIndex(
47466
47510
  (block) => block.type === "tool" && (toolCallId ? block.toolCallId === toolCallId : block.title === title)
47467
47511
  );
@@ -47470,12 +47514,19 @@ function createJobBarState(options2) {
47470
47514
  if (existing?.type !== "tool") return;
47471
47515
  blocks[existingIndex] = {
47472
47516
  ...existing,
47517
+ ...detailChars !== void 0 ? { detailChars } : {},
47473
47518
  status: status || existing.status,
47474
47519
  title: title || existing.title
47475
47520
  };
47476
47521
  return;
47477
47522
  }
47478
- blocks.push({ status, title, toolCallId, type: "tool" });
47523
+ blocks.push({
47524
+ ...detailChars !== void 0 ? { detailChars } : {},
47525
+ status,
47526
+ title,
47527
+ toolCallId,
47528
+ type: "tool"
47529
+ });
47479
47530
  }
47480
47531
  function toReadonlyPanelJob(job) {
47481
47532
  return {
@@ -47504,7 +47555,6 @@ function createJobBarState(options2) {
47504
47555
  function createMissionBridgeController(options2) {
47505
47556
  const jobBarState = createJobBarState({
47506
47557
  carrierRuntime: options2.carrierRuntime,
47507
- getKeyboardProtocol: options2.getKeyboardProtocol,
47508
47558
  onCarrierResultReminder: options2.onCarrierResultReminder === void 0 ? void 0 : (text) => options2.onCarrierResultReminder?.(sanitizeCarrierResultReminder(text)),
47509
47559
  onRenderRequest: options2.onJobBarRenderRequest
47510
47560
  });
@@ -47942,11 +47992,6 @@ var DEFAULT_HOST_KEYBINDINGS = [
47942
47992
  { action: "host-interrupt", key: "", label: "Ctrl+C" },
47943
47993
  { action: "mode-toggle", key: "", label: "Ctrl+T" }
47944
47994
  ];
47945
- var STANDARD_KEYBOARD_PROTOCOL_STATE = {
47946
- outerEnabled: false,
47947
- childRequested: false,
47948
- effectiveMode: "passthrough"
47949
- };
47950
47995
  function createMissionControlProfileConfig(options2) {
47951
47996
  return {
47952
47997
  cliOptions: getAgentCliMetadata(),
@@ -48041,7 +48086,6 @@ async function runApp(options2 = {}) {
48041
48086
  addInputListener: (listener) => ui.addInputListener(listener),
48042
48087
  carrierRuntime: runtime.carrierRuntime,
48043
48088
  getColumns: () => ui.columns,
48044
- getKeyboardProtocol: () => missionControl.ptyHost.getKeyboardProtocol?.() ?? STANDARD_KEYBOARD_PROTOCOL_STATE,
48045
48089
  getNative: () => sessionOptionsRuntime.getDraft().native,
48046
48090
  getRows: () => ptyManager?.getCurrentRequest().fleetRows ?? Math.max(0, ui.rows - missionControl.ptyView.maxRows),
48047
48091
  onCarrierResultReminder: (text) => sendCarrierResultReminder(text),