@voybio/ace-swarm 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +69 -29
  2. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  3. package/assets/agent-state/STATUS.md +2 -2
  4. package/assets/scripts/ace-hook-dispatch.mjs +1 -1
  5. package/dist/ace-autonomy.js +38 -1
  6. package/dist/ace-context.js +8 -0
  7. package/dist/ace-server-instructions.js +55 -19
  8. package/dist/ace-state-resolver.d.ts +18 -0
  9. package/dist/ace-state-resolver.js +106 -0
  10. package/dist/cli.js +74 -7
  11. package/dist/handoff-registry.js +11 -7
  12. package/dist/helpers.js +75 -9
  13. package/dist/job-scheduler.js +94 -44
  14. package/dist/run-ledger.js +3 -4
  15. package/dist/server.d.ts +1 -1
  16. package/dist/server.js +1 -1
  17. package/dist/shared.d.ts +1 -1
  18. package/dist/status-events.js +12 -14
  19. package/dist/store/ace-packed-store.d.ts +65 -26
  20. package/dist/store/ace-packed-store.js +448 -261
  21. package/dist/store/bootstrap-store.d.ts +1 -1
  22. package/dist/store/bootstrap-store.js +24 -13
  23. package/dist/store/catalog-builder.js +3 -3
  24. package/dist/store/importer.d.ts +2 -2
  25. package/dist/store/importer.js +2 -2
  26. package/dist/store/materializers/context-snapshot-materializer.d.ts +10 -0
  27. package/dist/store/materializers/context-snapshot-materializer.js +51 -0
  28. package/dist/store/materializers/hook-context-materializer.d.ts +1 -1
  29. package/dist/store/materializers/hook-context-materializer.js +1 -1
  30. package/dist/store/materializers/host-file-materializer.d.ts +6 -0
  31. package/dist/store/materializers/host-file-materializer.js +14 -1
  32. package/dist/store/materializers/projection-manager.d.ts +14 -0
  33. package/dist/store/materializers/projection-manager.js +73 -0
  34. package/dist/store/materializers/scheduler-projection-materializer.d.ts +16 -0
  35. package/dist/store/materializers/scheduler-projection-materializer.js +48 -0
  36. package/dist/store/repositories/context-snapshot-repository.d.ts +46 -0
  37. package/dist/store/repositories/context-snapshot-repository.js +105 -0
  38. package/dist/store/repositories/local-model-runtime-repository.d.ts +98 -0
  39. package/dist/store/repositories/local-model-runtime-repository.js +165 -0
  40. package/dist/store/repositories/scheduler-repository.d.ts +21 -39
  41. package/dist/store/repositories/scheduler-repository.js +123 -93
  42. package/dist/store/repositories/todo-repository.d.ts +4 -0
  43. package/dist/store/repositories/todo-repository.js +50 -0
  44. package/dist/store/skills-install.d.ts +1 -1
  45. package/dist/store/skills-install.js +3 -3
  46. package/dist/store/state-reader.d.ts +8 -1
  47. package/dist/store/state-reader.js +19 -13
  48. package/dist/store/store-artifacts.js +105 -41
  49. package/dist/store/store-authority-audit.d.ts +30 -0
  50. package/dist/store/store-authority-audit.js +448 -0
  51. package/dist/store/store-snapshot.js +3 -3
  52. package/dist/store/types.d.ts +6 -2
  53. package/dist/store/types.js +5 -2
  54. package/dist/todo-state.js +179 -11
  55. package/dist/tools-files.js +2 -1
  56. package/dist/tools-framework.js +62 -2
  57. package/dist/tools-memory.js +69 -34
  58. package/dist/tools-todo.js +1 -1
  59. package/dist/tui/agent-worker.d.ts +1 -1
  60. package/dist/tui/agent-worker.js +5 -3
  61. package/dist/tui/chat.d.ts +19 -0
  62. package/dist/tui/chat.js +275 -9
  63. package/dist/tui/commands.d.ts +2 -0
  64. package/dist/tui/commands.js +62 -0
  65. package/dist/tui/dashboard.d.ts +6 -1
  66. package/dist/tui/dashboard.js +44 -3
  67. package/dist/tui/index.d.ts +5 -0
  68. package/dist/tui/index.js +154 -0
  69. package/dist/tui/input.js +5 -0
  70. package/dist/tui/layout.d.ts +24 -0
  71. package/dist/tui/layout.js +76 -2
  72. package/dist/tui/local-model-contract.d.ts +50 -0
  73. package/dist/tui/local-model-contract.js +272 -0
  74. package/dist/vericify-bridge.js +3 -4
  75. package/dist/vericify-context.js +18 -6
  76. package/package.json +4 -6
package/dist/tui/index.js CHANGED
@@ -18,6 +18,8 @@ import { detectColorLevel, write, cursor, screen, fg, style } from "./renderer.j
18
18
  import { ALL_AGENTS, WORKSPACE_ROOT } from "../helpers.js";
19
19
  import { backfillHandoffsIntoScheduler } from "../tools-handoff.js";
20
20
  import { DEFAULT_OLLAMA_MODEL, inferProviderFromModel, normalizeLocalBaseUrl, } from "./provider-discovery.js";
21
+ import { resolveAceStateLayout } from "../ace-state-resolver.js";
22
+ import { withLocalModelRuntimeRepository, } from "../store/repositories/local-model-runtime-repository.js";
21
23
  const DASHBOARD_CONTROLS = ["provider", "model", "chat", "logs", "refresh"];
22
24
  // ── TUI Application ─────────────────────────────────────────────────
23
25
  export class AceTui {
@@ -48,6 +50,8 @@ export class AceTui {
48
50
  messageQueue = [];
49
51
  workspaceRoot;
50
52
  providerBaseUrls = new Map();
53
+ latestRuntimeStatus = null;
54
+ pendingCloseTabId = null;
51
55
  constructor(options = {}) {
52
56
  const workspaceRoot = options.workspaceRoot ?? WORKSPACE_ROOT;
53
57
  this.workspaceRoot = workspaceRoot;
@@ -218,10 +222,22 @@ export class AceTui {
218
222
  });
219
223
  // Dashboard: state updated
220
224
  this.dashboard.on("updated", () => {
225
+ this.latestRuntimeStatus = this.dashboard.getLatestRuntimeStatus() ?? null;
221
226
  const active = this.tabs.getActiveTab();
222
227
  if (active.type === "dashboard") {
223
228
  this.renderActiveView();
224
229
  }
230
+ this.renderStatusBar();
231
+ });
232
+ // Dashboard: discovered providers from the store → feed into TUI provider controls
233
+ this.dashboard.on("providers", (discovered) => {
234
+ for (const d of discovered) {
235
+ this.ensureProvider(d.provider);
236
+ if (d.endpoint)
237
+ this.setProviderBaseUrl(d.provider, d.endpoint);
238
+ if (d.models && d.models.length > 0)
239
+ this.setProviderModels(d.provider, d.models, false);
240
+ }
225
241
  });
226
242
  // Agent runner: agent output
227
243
  this.agentRunner.on("agent_output", (role, text) => {
@@ -276,6 +292,9 @@ export class AceTui {
276
292
  case "l":
277
293
  this.clearView();
278
294
  return;
295
+ case "w":
296
+ this.closeCurrentTab();
297
+ return;
279
298
  }
280
299
  }
281
300
  // Alt+number → switch tab
@@ -469,6 +488,7 @@ export class AceTui {
469
488
  renderStatusBar() {
470
489
  const snap = this.telemetry.getSnapshot();
471
490
  const agentTokens = this.agentRunner.getTotalTokens();
491
+ const runtimeStatus = this.getActiveRuntimeStatus();
472
492
  const state = {
473
493
  taskState: this.getTaskState(),
474
494
  provider: this.provider,
@@ -476,6 +496,29 @@ export class AceTui {
476
496
  tokensIn: snap.tokensIn + agentTokens.input,
477
497
  tokensOut: snap.tokensOut + agentTokens.output,
478
498
  startTime: this.startTime,
499
+ activeTabLabel: this.tabs.getActiveTab().label,
500
+ sessionId: runtimeStatus?.session_id,
501
+ turnCount: runtimeStatus?.turn_count,
502
+ preflightState: runtimeStatus?.preflight_state,
503
+ agentName: runtimeStatus?.role,
504
+ taskName: runtimeStatus?.current_task,
505
+ bridgeStatus: runtimeStatus?.bridge_status,
506
+ activeTool: runtimeStatus?.active_tool,
507
+ blockedReason: runtimeStatus?.blocked_reason,
508
+ nextAction: runtimeStatus?.recommended_next_action,
509
+ approvalState: runtimeStatus?.approval_state,
510
+ retryState: runtimeStatus?.retry_state
511
+ ? `attempt ${runtimeStatus.retry_state.attempt}${typeof runtimeStatus.retry_state.max_attempts === "number"
512
+ ? `/${runtimeStatus.retry_state.max_attempts}`
513
+ : ""}`
514
+ : undefined,
515
+ pollState: runtimeStatus?.poll_state
516
+ ? runtimeStatus.poll_state.active
517
+ ? `active${typeof runtimeStatus.poll_state.interval_ms === "number"
518
+ ? `/${runtimeStatus.poll_state.interval_ms}ms`
519
+ : ""}`
520
+ : "paused"
521
+ : undefined,
479
522
  };
480
523
  this.layout.renderStatusBar(state);
481
524
  }
@@ -485,6 +528,17 @@ export class AceTui {
485
528
  this.layout.renderInputLine(this.input.getBuffer(), this.input.getCursorPos(), mode, prompt);
486
529
  }
487
530
  getTaskState() {
531
+ const runtimeStatus = this.getActiveRuntimeStatus();
532
+ if (runtimeStatus &&
533
+ ["blocked", "failed", "needs_input", "approval_pending"].includes(runtimeStatus.bridge_status)) {
534
+ return "blocked";
535
+ }
536
+ if (runtimeStatus &&
537
+ (runtimeStatus.bridge_status === "retrying" ||
538
+ runtimeStatus.bridge_status === "running" ||
539
+ Boolean(runtimeStatus.active_tool))) {
540
+ return "running";
541
+ }
488
542
  if (this.activeChatSession?.isStreaming())
489
543
  return "running";
490
544
  if (this.agentRunner.getActiveCount() > 0)
@@ -493,6 +547,7 @@ export class AceTui {
493
547
  }
494
548
  syncTabState() {
495
549
  const tab = this.tabs.getActiveTab();
550
+ this.pendingCloseTabId = null;
496
551
  // Update active chat session pointer
497
552
  if (tab.type === "chat") {
498
553
  this.activeChatSession = this.chatSessions.get(tab.id) ?? null;
@@ -517,6 +572,13 @@ export class AceTui {
517
572
  const now = Date.now();
518
573
  this.messageQueue = this.messageQueue.filter(m => m.expiry > now);
519
574
  }
575
+ getActiveRuntimeStatus() {
576
+ const activeTab = this.tabs.getActiveTab();
577
+ if (activeTab.type === "chat") {
578
+ return this.chatSessions.get(activeTab.id)?.getCurrentRuntimeStatus() ?? this.latestRuntimeStatus;
579
+ }
580
+ return this.latestRuntimeStatus;
581
+ }
520
582
  normalizeProvider(provider) {
521
583
  if (!provider)
522
584
  return undefined;
@@ -891,6 +953,25 @@ export class AceTui {
891
953
  this.renderStatusBar();
892
954
  this.renderInputArea();
893
955
  });
956
+ session.on("runtime_status", (status) => {
957
+ this.latestRuntimeStatus = status;
958
+ if (this.tabs.getActiveTab().id === tab.id || this.tabs.getActiveTab().type === "dashboard") {
959
+ this.renderActiveView();
960
+ }
961
+ this.renderStatusBar();
962
+ });
963
+ session.on("tool_call", (tool) => {
964
+ this.dashboard.addEvent("bridge", `tool call → ${tool}`);
965
+ if (this.tabs.getActiveTab().type === "dashboard") {
966
+ this.renderActiveView();
967
+ }
968
+ });
969
+ session.on("tool_result", (tool, toolResult) => {
970
+ this.dashboard.addEvent("bridge", `${toolResult.ok ? "tool ok" : "tool error"} → ${tool}: ${toolResult.summary}`);
971
+ if (this.tabs.getActiveTab().type === "dashboard") {
972
+ this.renderActiveView();
973
+ }
974
+ });
894
975
  session.on("error", (msg) => {
895
976
  this.showMessage(msg, "error");
896
977
  });
@@ -907,6 +988,19 @@ export class AceTui {
907
988
  }
908
989
  closeCurrentTab() {
909
990
  const tab = this.tabs.getActiveTab();
991
+ if (!tab.closeable) {
992
+ this.showMessage(`Tab '${tab.label}' cannot be closed.`, "warn");
993
+ return;
994
+ }
995
+ if (tab.type === "chat") {
996
+ const session = this.chatSessions.get(tab.id);
997
+ if (session?.hasMeaningfulTranscript() && this.pendingCloseTabId !== tab.id) {
998
+ this.pendingCloseTabId = tab.id;
999
+ this.showMessage("Chat has transcript state. Use /archive to persist it, or close again to discard.", "warn");
1000
+ return;
1001
+ }
1002
+ }
1003
+ this.pendingCloseTabId = null;
910
1004
  // Clean up chat session if closing a chat tab
911
1005
  if (tab.type === "chat") {
912
1006
  const session = this.chatSessions.get(tab.id);
@@ -923,6 +1017,51 @@ export class AceTui {
923
1017
  this.syncTabState();
924
1018
  this.fullRender();
925
1019
  }
1020
+ async archiveCurrentTab() {
1021
+ const tab = this.tabs.getActiveTab();
1022
+ if (tab.type !== "chat") {
1023
+ this.showMessage("Archive is available only for chat tabs.", "warn");
1024
+ return;
1025
+ }
1026
+ const session = this.chatSessions.get(tab.id);
1027
+ if (!session) {
1028
+ this.showMessage("Chat session not found for archive.", "error");
1029
+ return;
1030
+ }
1031
+ if (!session.hasMeaningfulTranscript()) {
1032
+ this.showMessage("Chat is empty. Closing without archive.", "info");
1033
+ this.pendingCloseTabId = tab.id;
1034
+ this.closeCurrentTab();
1035
+ return;
1036
+ }
1037
+ const runtimeStatus = session.getCurrentRuntimeStatus();
1038
+ await withLocalModelRuntimeRepository(this.workspaceRoot, async (repo) => {
1039
+ await repo.archiveChat({
1040
+ session_id: session.getSessionId(),
1041
+ tab_id: tab.id,
1042
+ label: tab.label,
1043
+ provider: session.getProvider(),
1044
+ model: session.getModel(),
1045
+ created_at: session.getCreatedAt(),
1046
+ last_active_at: session.getLastActiveAt(),
1047
+ transcript_excerpt: session.getTranscriptExcerpt(),
1048
+ transcript_summary: session.getTranscriptSummary(),
1049
+ ace_context: {
1050
+ resolved_layout_mode: resolveAceStateLayout(this.workspaceRoot).physicalMode,
1051
+ role: runtimeStatus?.role,
1052
+ bridge_status: runtimeStatus?.bridge_status ?? "done",
1053
+ active_tool: runtimeStatus?.active_tool,
1054
+ blocked_reason: runtimeStatus?.blocked_reason,
1055
+ approval_state: runtimeStatus?.approval_state,
1056
+ retry_state: runtimeStatus?.retry_state,
1057
+ recommended_next_action: runtimeStatus?.recommended_next_action,
1058
+ },
1059
+ });
1060
+ });
1061
+ this.showMessage(`Archived chat '${tab.label}'.`, "info");
1062
+ this.pendingCloseTabId = tab.id;
1063
+ this.closeCurrentTab();
1064
+ }
926
1065
  showMessage(text, level = "info") {
927
1066
  const colorFn = level === "error" ? fg.red : level === "warn" ? fg.yellow : fg.gray;
928
1067
  const prefix = level === "error" ? "ERROR" : level === "warn" ? "WARN" : "INFO";
@@ -957,6 +1096,20 @@ export class AceTui {
957
1096
  this.layout.fullRedraw();
958
1097
  this.fullRender();
959
1098
  }
1099
+ setApprovalState(state) {
1100
+ const session = this.activeChatSession;
1101
+ if (!session) {
1102
+ this.showMessage("No active chat runtime to update approval state.", "warn");
1103
+ return false;
1104
+ }
1105
+ const ok = session.setApprovalState(state);
1106
+ if (!ok) {
1107
+ this.showMessage("No active runtime packet to update approval state.", "warn");
1108
+ return false;
1109
+ }
1110
+ this.renderStatusBar();
1111
+ return true;
1112
+ }
960
1113
  getStatus() {
961
1114
  const snap = this.telemetry.getSnapshot();
962
1115
  return {
@@ -969,6 +1122,7 @@ export class AceTui {
969
1122
  requests: snap.requestCount,
970
1123
  activeAgents: this.agentRunner.getActiveCount(),
971
1124
  tabs: this.tabs.getTabs().length,
1125
+ bridgeStatus: this.latestRuntimeStatus?.bridge_status,
972
1126
  };
973
1127
  }
974
1128
  quit() {
package/dist/tui/input.js CHANGED
@@ -101,6 +101,11 @@ export class InputHandler extends EventEmitter {
101
101
  this.emit("key", { name: "l", sequence: ch, ctrl: true, meta: false, shift: false });
102
102
  continue;
103
103
  }
104
+ // Ctrl+W — close/archive current tab
105
+ if (code === 23) {
106
+ this.emit("key", { name: "w", sequence: ch, ctrl: true, meta: false, shift: false });
107
+ continue;
108
+ }
104
109
  // Tab
105
110
  if (code === 9) {
106
111
  if ((this.mode === "command" || this.mode === "chat") && this.buffer.length > 0) {
@@ -24,6 +24,17 @@ export interface StatusBarState {
24
24
  tokensIn: number;
25
25
  tokensOut: number;
26
26
  startTime: number;
27
+ activeTabLabel?: string;
28
+ sessionId?: string;
29
+ turnCount?: number;
30
+ preflightState?: string;
31
+ bridgeStatus?: string;
32
+ activeTool?: string;
33
+ blockedReason?: string;
34
+ nextAction?: string;
35
+ approvalState?: string;
36
+ retryState?: string;
37
+ pollState?: string;
27
38
  taskName?: string;
28
39
  agentName?: string;
29
40
  }
@@ -44,6 +55,17 @@ export interface DashboardControlState {
44
55
  selectedIndex: number;
45
56
  items: DashboardControlItem[];
46
57
  }
58
+ export declare function formatRuntimeStatusSummary(state: Pick<StatusBarState, "sessionId" | "turnCount" | "preflightState" | "taskName" | "agentName" | "approvalState" | "retryState" | "pollState">): string | undefined;
59
+ interface RuntimeStatusSummary {
60
+ bridge_status: string;
61
+ preflight_state: string;
62
+ role?: string;
63
+ approval_state?: string;
64
+ active_tool?: string;
65
+ active_tool_role?: string;
66
+ recommended_next_action?: string;
67
+ blocked_reason?: string;
68
+ }
47
69
  export declare class LayoutManager {
48
70
  private zones;
49
71
  private cols;
@@ -94,6 +116,7 @@ export interface DashboardData {
94
116
  tokensIn: number;
95
117
  tokensOut: number;
96
118
  tokensPerSec: number;
119
+ runtimeStatus?: RuntimeStatusSummary;
97
120
  tasks: TaskItem[];
98
121
  events: ActivityEvent[];
99
122
  }
@@ -113,4 +136,5 @@ export interface ChatMessage {
113
136
  timestamp?: number;
114
137
  tokens?: number;
115
138
  }
139
+ export {};
116
140
  //# sourceMappingURL=layout.d.ts.map
@@ -5,6 +5,34 @@
5
5
  * Arttime-inspired: defined zones for each UI element.
6
6
  */
7
7
  import { getTermSize, write, writeAt, drawBox, clearRegion, cursor, screen, style, fg, bg, padRight, truncate, visibleWidth, box, symbols, formatTime, formatElapsed, formatNumber, hline, } from "./renderer.js";
8
+ export function formatRuntimeStatusSummary(state) {
9
+ const parts = [];
10
+ if (state.sessionId) {
11
+ parts.push(`sid:${truncate(state.sessionId, 10)}`);
12
+ }
13
+ if (typeof state.turnCount === "number") {
14
+ parts.push(`turn:${state.turnCount}`);
15
+ }
16
+ if (state.agentName) {
17
+ parts.push(`role:${truncate(state.agentName, 12)}`);
18
+ }
19
+ if (state.taskName) {
20
+ parts.push(`task:${truncate(state.taskName, 18)}`);
21
+ }
22
+ if (state.preflightState) {
23
+ parts.push(`pf:${state.preflightState}`);
24
+ }
25
+ if (state.approvalState) {
26
+ parts.push(`approval:${state.approvalState}`);
27
+ }
28
+ if (state.retryState) {
29
+ parts.push(`retry:${state.retryState}`);
30
+ }
31
+ if (state.pollState) {
32
+ parts.push(`poll:${state.pollState}`);
33
+ }
34
+ return parts.length > 0 ? parts.join(" ") : undefined;
35
+ }
8
36
  // ── Layout manager ───────────────────────────────────────────────────
9
37
  export class LayoutManager {
10
38
  zones;
@@ -116,12 +144,39 @@ export class LayoutManager {
116
144
  stateIndicator = `${fg.red}${style.bold}${symbols.bullet} OVERDUE${style.reset}`;
117
145
  break;
118
146
  }
147
+ if (state.bridgeStatus === "approval_pending") {
148
+ stateIndicator = `${fg.yellow}${symbols.bullet} approval${style.reset}`;
149
+ }
150
+ else if (state.bridgeStatus === "retrying") {
151
+ stateIndicator = `${fg.yellow}${symbols.bullet} retrying${style.reset}`;
152
+ }
153
+ else if (state.bridgeStatus === "needs_input") {
154
+ stateIndicator = `${fg.yellow}${symbols.bullet} needs_input${style.reset}`;
155
+ }
156
+ if (state.activeTool) {
157
+ stateIndicator = `${fg.green}${symbols.bullet} ${state.activeTool}${style.reset}`;
158
+ }
159
+ const tabSeg = state.activeTabLabel
160
+ ? `${fg.gray}tab: ${fg.white}${state.activeTabLabel}${style.reset}`
161
+ : undefined;
119
162
  const providerSeg = `${fg.cyan}provider: ${fg.brightCyan}${state.provider}${style.reset}`;
120
163
  const modelSeg = `${fg.cyan}model: ${fg.brightCyan}${state.model}${style.reset}`;
164
+ const runtimeSummary = formatRuntimeStatusSummary(state);
165
+ const runtimeSeg = runtimeSummary
166
+ ? `${fg.gray}${runtimeSummary}${style.reset}`
167
+ : undefined;
121
168
  const tokenSeg = `${fg.gray}tokens: ${fg.white}${formatNumber(state.tokensIn)}/${formatNumber(state.tokensOut)}${style.reset}`;
122
169
  const timeSeg = `${fg.gray}${elapsed}${style.reset}`;
123
170
  const clockSeg = `${fg.gray}${style.underline}${time}${style.reset}`;
124
- const segments = [stateIndicator, providerSeg, modelSeg, tokenSeg, timeSeg, clockSeg];
171
+ const contextSeg = state.blockedReason
172
+ ? `${fg.red}${truncate(state.blockedReason, Math.max(18, Math.floor(this.cols * 0.22)))}${style.reset}`
173
+ : state.nextAction
174
+ ? `${fg.gray}next: ${fg.white}${truncate(state.nextAction, Math.max(18, Math.floor(this.cols * 0.18)))}${style.reset}`
175
+ : state.bridgeStatus
176
+ ? `${fg.gray}${state.bridgeStatus}${style.reset}`
177
+ : undefined;
178
+ const segments = [stateIndicator, tabSeg, providerSeg, modelSeg, runtimeSeg, contextSeg, tokenSeg, timeSeg, clockSeg]
179
+ .filter((segment) => Boolean(segment));
125
180
  const sep = ` ${fg.gray}│${style.reset} `;
126
181
  const line = ` ${segments.join(sep)} `;
127
182
  // Clear and write
@@ -198,13 +253,32 @@ export class LayoutManager {
198
253
  renderStatusPanel(x, y, w, h, data) {
199
254
  const lines = [
200
255
  `${fg.gray}Phase: ${fg.brightWhite}${data.phase}`,
256
+ data.runtimeStatus?.bridge_status
257
+ ? `${fg.gray}Bridge: ${fg.brightYellow}${data.runtimeStatus.bridge_status}`
258
+ : undefined,
259
+ data.runtimeStatus?.preflight_state
260
+ ? `${fg.gray}Preflt: ${data.runtimeStatus.preflight_state === "blocked"
261
+ ? fg.brightRed
262
+ : data.runtimeStatus.preflight_state === "attention_required"
263
+ ? fg.yellow
264
+ : fg.brightGreen}${data.runtimeStatus.preflight_state}`
265
+ : undefined,
266
+ data.runtimeStatus?.role
267
+ ? `${fg.gray}Role: ${fg.brightCyan}${data.runtimeStatus.role}`
268
+ : undefined,
269
+ data.runtimeStatus?.blocked_reason
270
+ ? `${fg.gray}Blocker: ${fg.brightRed}${data.runtimeStatus.blocked_reason}`
271
+ : undefined,
272
+ data.runtimeStatus?.recommended_next_action
273
+ ? `${fg.gray}Next: ${fg.white}${data.runtimeStatus.recommended_next_action}`
274
+ : undefined,
201
275
  `${fg.gray}Provider:${fg.brightCyan} ${padRight(data.provider, 8)}`,
202
276
  `${fg.gray}Model: ${fg.brightCyan}${data.model}`,
203
277
  `${fg.gray}Agents: ${fg.brightGreen}${data.activeAgents}${fg.gray}/${data.totalAgents} active`,
204
278
  `${fg.gray}Uptime: ${fg.white}${formatElapsed(Date.now() - data.startTime)}`,
205
279
  `${fg.gray}Tokens: ${fg.white}${formatNumber(data.tokensIn)} in / ${formatNumber(data.tokensOut)} out`,
206
280
  `${fg.gray}Speed: ${fg.white}${data.tokensPerSec.toFixed(0)} tok/s`,
207
- ];
281
+ ].filter((line) => Boolean(line));
208
282
  for (let i = 0; i < Math.min(lines.length, h); i++) {
209
283
  writeAt(x, y + i, truncate(lines[i] + style.reset, w));
210
284
  }
@@ -0,0 +1,50 @@
1
+ import { type AceStateResolution } from "../ace-state-resolver.js";
2
+ import type { AceRuntimeStatusPacket, AceSessionActivationLedger, AceSessionContinuityRecord } from "../store/repositories/local-model-runtime-repository.js";
3
+ export interface AcePreflightPacket {
4
+ session_id: string;
5
+ workspace_root: string;
6
+ state_resolution: AceStateResolution;
7
+ preflight_state: "ready" | "attention_required" | "blocked";
8
+ bridge_status: "running" | "needs_input" | "approval_pending" | "retrying" | "blocked" | "failed" | "done";
9
+ quartet_health: "healthy" | "thin" | "incomplete" | "contradictory";
10
+ task_contract_health: "healthy" | "thin" | "blocked";
11
+ recommended_role?: string;
12
+ recommended_next_action?: string;
13
+ blockers: string[];
14
+ warnings: string[];
15
+ recall_summary?: string;
16
+ should_synthesize_plan: boolean;
17
+ }
18
+ export interface StartupNudge {
19
+ id: string;
20
+ text: string;
21
+ recommended_action: string;
22
+ }
23
+ export declare function shouldSynthesizeShortPlan(task: string): boolean;
24
+ export declare function buildAcePreflightPacket(input: {
25
+ sessionId: string;
26
+ workspaceRoot: string;
27
+ task: string;
28
+ preferredRole?: string;
29
+ }): AcePreflightPacket;
30
+ export declare function nextActivationLedger(sessionId: string, current: AceSessionActivationLedger | undefined, recommendedAction: string | undefined): AceSessionActivationLedger;
31
+ export declare function buildStartupNudge(preflight: AcePreflightPacket, ledger: AceSessionActivationLedger): StartupNudge | undefined;
32
+ export declare function buildBridgeTaskInput(conversation: string, preflight: AcePreflightPacket): string;
33
+ export declare function mapBridgeResultToRuntimeStatus(input: {
34
+ current: AceRuntimeStatusPacket;
35
+ summary: string;
36
+ toolNames: string[];
37
+ }): AceRuntimeStatusPacket;
38
+ export declare function buildContinuityRecord(input: {
39
+ preflight: AcePreflightPacket;
40
+ role: string;
41
+ activeTool?: string;
42
+ bridgeStatus: AceRuntimeStatusPacket["bridge_status"];
43
+ evidenceRefs?: string[];
44
+ }): AceSessionContinuityRecord;
45
+ export declare function containsVerificationLanguage(text: string): boolean;
46
+ export declare function hasEvidenceLikeTool(toolNames: readonly string[]): boolean;
47
+ export declare function applyEvidenceGuardrail(summary: string, toolNames: readonly string[]): string;
48
+ export declare function readRuntimeWorkflowHint(workspaceRoot: string): string | undefined;
49
+ export declare function readStatusHint(workspaceRoot: string): string | undefined;
50
+ //# sourceMappingURL=local-model-contract.d.ts.map