@voybio/ace-swarm 0.2.0 → 0.2.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.
Files changed (64) hide show
  1. package/README.md +15 -15
  2. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  3. package/assets/agent-state/STATUS.md +2 -2
  4. package/dist/ace-autonomy.js +38 -1
  5. package/dist/ace-context.js +8 -0
  6. package/dist/ace-server-instructions.js +55 -19
  7. package/dist/ace-state-resolver.d.ts +18 -0
  8. package/dist/ace-state-resolver.js +106 -0
  9. package/dist/cli.js +67 -0
  10. package/dist/handoff-registry.js +11 -7
  11. package/dist/helpers.js +74 -8
  12. package/dist/job-scheduler.js +94 -44
  13. package/dist/run-ledger.js +3 -4
  14. package/dist/server.d.ts +1 -1
  15. package/dist/server.js +1 -1
  16. package/dist/shared.d.ts +1 -1
  17. package/dist/status-events.js +12 -14
  18. package/dist/store/bootstrap-store.js +20 -9
  19. package/dist/store/materializers/context-snapshot-materializer.d.ts +10 -0
  20. package/dist/store/materializers/context-snapshot-materializer.js +51 -0
  21. package/dist/store/materializers/host-file-materializer.d.ts +6 -0
  22. package/dist/store/materializers/host-file-materializer.js +13 -0
  23. package/dist/store/materializers/projection-manager.d.ts +14 -0
  24. package/dist/store/materializers/projection-manager.js +73 -0
  25. package/dist/store/materializers/scheduler-projection-materializer.d.ts +16 -0
  26. package/dist/store/materializers/scheduler-projection-materializer.js +48 -0
  27. package/dist/store/repositories/context-snapshot-repository.d.ts +46 -0
  28. package/dist/store/repositories/context-snapshot-repository.js +105 -0
  29. package/dist/store/repositories/local-model-runtime-repository.d.ts +98 -0
  30. package/dist/store/repositories/local-model-runtime-repository.js +165 -0
  31. package/dist/store/repositories/scheduler-repository.d.ts +21 -39
  32. package/dist/store/repositories/scheduler-repository.js +123 -93
  33. package/dist/store/repositories/todo-repository.d.ts +4 -0
  34. package/dist/store/repositories/todo-repository.js +50 -0
  35. package/dist/store/state-reader.d.ts +8 -1
  36. package/dist/store/state-reader.js +12 -1
  37. package/dist/store/store-artifacts.js +31 -5
  38. package/dist/store/store-authority-audit.d.ts +30 -0
  39. package/dist/store/store-authority-audit.js +448 -0
  40. package/dist/store/types.d.ts +2 -0
  41. package/dist/store/types.js +1 -0
  42. package/dist/todo-state.js +179 -11
  43. package/dist/tools-files.js +2 -1
  44. package/dist/tools-framework.js +60 -0
  45. package/dist/tools-memory.js +69 -34
  46. package/dist/tools-todo.js +1 -1
  47. package/dist/tui/agent-worker.d.ts +1 -1
  48. package/dist/tui/agent-worker.js +5 -3
  49. package/dist/tui/chat.d.ts +19 -0
  50. package/dist/tui/chat.js +275 -9
  51. package/dist/tui/commands.d.ts +2 -0
  52. package/dist/tui/commands.js +62 -0
  53. package/dist/tui/dashboard.d.ts +5 -0
  54. package/dist/tui/dashboard.js +38 -2
  55. package/dist/tui/index.d.ts +5 -0
  56. package/dist/tui/index.js +146 -2
  57. package/dist/tui/input.js +5 -0
  58. package/dist/tui/layout.d.ts +24 -0
  59. package/dist/tui/layout.js +76 -2
  60. package/dist/tui/local-model-contract.d.ts +50 -0
  61. package/dist/tui/local-model-contract.js +272 -0
  62. package/dist/vericify-bridge.js +3 -4
  63. package/dist/vericify-context.js +18 -6
  64. package/package.json +1 -1
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,17 +222,19 @@ 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();
225
231
  });
226
232
  // Dashboard: discovered providers from the store → feed into TUI provider controls
227
233
  this.dashboard.on("providers", (discovered) => {
228
234
  for (const d of discovered) {
229
235
  this.ensureProvider(d.provider);
230
- if (d.base_url)
231
- this.setProviderBaseUrl(d.provider, d.base_url);
236
+ if (d.endpoint)
237
+ this.setProviderBaseUrl(d.provider, d.endpoint);
232
238
  if (d.models && d.models.length > 0)
233
239
  this.setProviderModels(d.provider, d.models, false);
234
240
  }
@@ -286,6 +292,9 @@ export class AceTui {
286
292
  case "l":
287
293
  this.clearView();
288
294
  return;
295
+ case "w":
296
+ this.closeCurrentTab();
297
+ return;
289
298
  }
290
299
  }
291
300
  // Alt+number → switch tab
@@ -479,6 +488,7 @@ export class AceTui {
479
488
  renderStatusBar() {
480
489
  const snap = this.telemetry.getSnapshot();
481
490
  const agentTokens = this.agentRunner.getTotalTokens();
491
+ const runtimeStatus = this.getActiveRuntimeStatus();
482
492
  const state = {
483
493
  taskState: this.getTaskState(),
484
494
  provider: this.provider,
@@ -486,6 +496,29 @@ export class AceTui {
486
496
  tokensIn: snap.tokensIn + agentTokens.input,
487
497
  tokensOut: snap.tokensOut + agentTokens.output,
488
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,
489
522
  };
490
523
  this.layout.renderStatusBar(state);
491
524
  }
@@ -495,6 +528,17 @@ export class AceTui {
495
528
  this.layout.renderInputLine(this.input.getBuffer(), this.input.getCursorPos(), mode, prompt);
496
529
  }
497
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
+ }
498
542
  if (this.activeChatSession?.isStreaming())
499
543
  return "running";
500
544
  if (this.agentRunner.getActiveCount() > 0)
@@ -503,6 +547,7 @@ export class AceTui {
503
547
  }
504
548
  syncTabState() {
505
549
  const tab = this.tabs.getActiveTab();
550
+ this.pendingCloseTabId = null;
506
551
  // Update active chat session pointer
507
552
  if (tab.type === "chat") {
508
553
  this.activeChatSession = this.chatSessions.get(tab.id) ?? null;
@@ -527,6 +572,13 @@ export class AceTui {
527
572
  const now = Date.now();
528
573
  this.messageQueue = this.messageQueue.filter(m => m.expiry > now);
529
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
+ }
530
582
  normalizeProvider(provider) {
531
583
  if (!provider)
532
584
  return undefined;
@@ -901,6 +953,25 @@ export class AceTui {
901
953
  this.renderStatusBar();
902
954
  this.renderInputArea();
903
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
+ });
904
975
  session.on("error", (msg) => {
905
976
  this.showMessage(msg, "error");
906
977
  });
@@ -917,6 +988,19 @@ export class AceTui {
917
988
  }
918
989
  closeCurrentTab() {
919
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;
920
1004
  // Clean up chat session if closing a chat tab
921
1005
  if (tab.type === "chat") {
922
1006
  const session = this.chatSessions.get(tab.id);
@@ -933,6 +1017,51 @@ export class AceTui {
933
1017
  this.syncTabState();
934
1018
  this.fullRender();
935
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
+ }
936
1065
  showMessage(text, level = "info") {
937
1066
  const colorFn = level === "error" ? fg.red : level === "warn" ? fg.yellow : fg.gray;
938
1067
  const prefix = level === "error" ? "ERROR" : level === "warn" ? "WARN" : "INFO";
@@ -967,6 +1096,20 @@ export class AceTui {
967
1096
  this.layout.fullRedraw();
968
1097
  this.fullRender();
969
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
+ }
970
1113
  getStatus() {
971
1114
  const snap = this.telemetry.getSnapshot();
972
1115
  return {
@@ -979,6 +1122,7 @@ export class AceTui {
979
1122
  requests: snap.requestCount,
980
1123
  activeAgents: this.agentRunner.getActiveCount(),
981
1124
  tabs: this.tabs.getTabs().length,
1125
+ bridgeStatus: this.latestRuntimeStatus?.bridge_status,
982
1126
  };
983
1127
  }
984
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