@voybio/ace-swarm 0.2.5 → 2.4.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 (144) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/README.md +21 -13
  3. package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
  4. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  5. package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
  6. package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
  7. package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
  8. package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
  9. package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
  10. package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
  11. package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
  12. package/assets/agent-state/STATUS.md +2 -2
  13. package/assets/agent-state/runtime-tool-specs.json +70 -2
  14. package/assets/instructions/ACE_Coder.instructions.md +13 -0
  15. package/assets/instructions/ACE_UI.instructions.md +11 -0
  16. package/assets/scripts/ace-hook-dispatch.mjs +70 -6
  17. package/assets/scripts/render-mcp-configs.sh +19 -5
  18. package/dist/ace-context.js +91 -11
  19. package/dist/ace-internal-tools.d.ts +3 -1
  20. package/dist/ace-internal-tools.js +10 -2
  21. package/dist/ace-server-instructions.js +3 -3
  22. package/dist/ace-state-resolver.js +5 -3
  23. package/dist/agent-runtime/role-adapters.d.ts +18 -1
  24. package/dist/agent-runtime/role-adapters.js +49 -5
  25. package/dist/astgrep-index.d.ts +57 -1
  26. package/dist/astgrep-index.js +140 -4
  27. package/dist/cli.js +232 -35
  28. package/dist/discovery-runtime-wrappers.d.ts +108 -0
  29. package/dist/discovery-runtime-wrappers.js +615 -0
  30. package/dist/handoff-registry.js +5 -5
  31. package/dist/helpers/artifacts.d.ts +19 -0
  32. package/dist/helpers/artifacts.js +152 -0
  33. package/dist/helpers/bootstrap.d.ts +24 -0
  34. package/dist/helpers/bootstrap.js +894 -0
  35. package/dist/helpers/constants.d.ts +53 -0
  36. package/dist/helpers/constants.js +295 -0
  37. package/dist/helpers/drift.d.ts +13 -0
  38. package/dist/helpers/drift.js +45 -0
  39. package/dist/helpers/path-utils.d.ts +24 -0
  40. package/dist/helpers/path-utils.js +123 -0
  41. package/dist/helpers/store-resolution.d.ts +19 -0
  42. package/dist/helpers/store-resolution.js +305 -0
  43. package/dist/helpers/workspace-root.d.ts +3 -0
  44. package/dist/helpers/workspace-root.js +80 -0
  45. package/dist/helpers.d.ts +8 -125
  46. package/dist/helpers.js +8 -1768
  47. package/dist/job-scheduler.js +33 -7
  48. package/dist/json-sanitizer.d.ts +16 -0
  49. package/dist/json-sanitizer.js +26 -0
  50. package/dist/local-model-policy.d.ts +27 -0
  51. package/dist/local-model-policy.js +84 -0
  52. package/dist/local-model-runtime.d.ts +6 -0
  53. package/dist/local-model-runtime.js +33 -21
  54. package/dist/model-bridge.d.ts +13 -1
  55. package/dist/model-bridge.js +410 -23
  56. package/dist/orchestrator-supervisor.d.ts +56 -0
  57. package/dist/orchestrator-supervisor.js +179 -1
  58. package/dist/plan-proposal.d.ts +115 -0
  59. package/dist/plan-proposal.js +1073 -0
  60. package/dist/run-ledger.js +3 -3
  61. package/dist/runtime-command.d.ts +8 -0
  62. package/dist/runtime-command.js +38 -6
  63. package/dist/runtime-executor.d.ts +20 -1
  64. package/dist/runtime-executor.js +737 -172
  65. package/dist/runtime-profile.d.ts +32 -0
  66. package/dist/runtime-profile.js +89 -13
  67. package/dist/runtime-tool-specs.d.ts +39 -0
  68. package/dist/runtime-tool-specs.js +144 -28
  69. package/dist/safe-edit.d.ts +7 -0
  70. package/dist/safe-edit.js +163 -37
  71. package/dist/schemas.js +48 -1
  72. package/dist/server.js +51 -0
  73. package/dist/shared.d.ts +3 -2
  74. package/dist/shared.js +2 -0
  75. package/dist/status-events.js +9 -6
  76. package/dist/store/ace-packed-store.d.ts +3 -2
  77. package/dist/store/ace-packed-store.js +188 -110
  78. package/dist/store/bootstrap-store.d.ts +2 -1
  79. package/dist/store/bootstrap-store.js +102 -83
  80. package/dist/store/cache-workspace.js +11 -5
  81. package/dist/store/materializers/context-snapshot-materializer.js +6 -2
  82. package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
  83. package/dist/store/materializers/hook-context-materializer.js +11 -21
  84. package/dist/store/materializers/host-file-materializer.js +6 -0
  85. package/dist/store/materializers/projection-manager.d.ts +0 -1
  86. package/dist/store/materializers/projection-manager.js +5 -13
  87. package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
  88. package/dist/store/materializers/vericify-projector.d.ts +7 -7
  89. package/dist/store/materializers/vericify-projector.js +11 -11
  90. package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
  91. package/dist/store/repositories/local-model-runtime-repository.js +242 -6
  92. package/dist/store/repositories/vericify-repository.d.ts +1 -1
  93. package/dist/store/skills-install.d.ts +4 -0
  94. package/dist/store/skills-install.js +21 -12
  95. package/dist/store/state-reader.d.ts +2 -0
  96. package/dist/store/state-reader.js +20 -0
  97. package/dist/store/store-artifacts.d.ts +7 -0
  98. package/dist/store/store-artifacts.js +27 -1
  99. package/dist/store/store-authority-audit.d.ts +18 -1
  100. package/dist/store/store-authority-audit.js +115 -5
  101. package/dist/store/store-snapshot.d.ts +3 -0
  102. package/dist/store/store-snapshot.js +22 -2
  103. package/dist/store/workspace-store-paths.d.ts +39 -0
  104. package/dist/store/workspace-store-paths.js +94 -0
  105. package/dist/store/write-coordinator.d.ts +65 -0
  106. package/dist/store/write-coordinator.js +386 -0
  107. package/dist/todo-state.js +5 -5
  108. package/dist/tools-agent.d.ts +20 -0
  109. package/dist/tools-agent.js +789 -25
  110. package/dist/tools-discovery.js +136 -1
  111. package/dist/tools-files.d.ts +7 -0
  112. package/dist/tools-files.js +1002 -11
  113. package/dist/tools-framework.js +105 -66
  114. package/dist/tools-handoff.js +2 -2
  115. package/dist/tools-lifecycle.js +4 -4
  116. package/dist/tools-memory.js +6 -6
  117. package/dist/tools-todo.js +2 -2
  118. package/dist/tracker-adapters.d.ts +1 -1
  119. package/dist/tracker-adapters.js +13 -18
  120. package/dist/tracker-sync.js +5 -3
  121. package/dist/tui/agent-runner.js +3 -1
  122. package/dist/tui/chat.js +103 -7
  123. package/dist/tui/dashboard.d.ts +1 -0
  124. package/dist/tui/dashboard.js +43 -0
  125. package/dist/tui/index.js +10 -1
  126. package/dist/tui/layout.d.ts +20 -0
  127. package/dist/tui/layout.js +31 -1
  128. package/dist/tui/local-model-contract.d.ts +6 -2
  129. package/dist/tui/local-model-contract.js +16 -3
  130. package/dist/tui/ollama.d.ts +8 -1
  131. package/dist/tui/ollama.js +53 -12
  132. package/dist/tui/openai-compatible.d.ts +13 -0
  133. package/dist/tui/openai-compatible.js +305 -5
  134. package/dist/tui/provider-discovery.d.ts +1 -0
  135. package/dist/tui/provider-discovery.js +35 -11
  136. package/dist/vericify-bridge.d.ts +6 -1
  137. package/dist/vericify-bridge.js +27 -3
  138. package/dist/workspace-manager.d.ts +30 -3
  139. package/dist/workspace-manager.js +257 -27
  140. package/package.json +1 -2
  141. package/dist/internal-tool-runtime.d.ts +0 -21
  142. package/dist/internal-tool-runtime.js +0 -136
  143. package/dist/store/workspace-snapshot.d.ts +0 -26
  144. package/dist/store/workspace-snapshot.js +0 -107
@@ -9,6 +9,9 @@ export interface AceSessionActivationLedger {
9
9
  activated_tools: string[];
10
10
  activated_roles: string[];
11
11
  last_recommended_action?: string;
12
+ auto_executed_nudges?: string[];
13
+ ignored_nudges?: string[];
14
+ last_transition_id?: string;
12
15
  }
13
16
  export interface AceSessionContinuityRecord {
14
17
  session_id: string;
@@ -24,6 +27,7 @@ export interface AceSessionContinuityRecord {
24
27
  recent_decisions: string[];
25
28
  recommended_next_action?: string;
26
29
  evidence_refs: string[];
30
+ last_transition_id?: string;
27
31
  }
28
32
  export interface AceRuntimeStatusPacket {
29
33
  session_id: string;
@@ -52,6 +56,25 @@ export interface AceRuntimeStatusPacket {
52
56
  tokens_in?: number;
53
57
  tokens_out?: number;
54
58
  updated_at: number;
59
+ surface_kind?: "tui_interactive" | "unattended_runtime" | "scheduled_job" | "bridge_resumed" | "supervised_step";
60
+ model_class?: "frontier" | "mid" | "small_local";
61
+ waiting_on?: string;
62
+ resume_condition?: string;
63
+ resume_after?: number;
64
+ sleep_state?: "awake" | "sleeping" | "approval_pending" | "dependency_wait" | "stalled";
65
+ escalation_policy?: {
66
+ silent_until?: string;
67
+ escalate_after_ms?: number;
68
+ route_to?: string[];
69
+ };
70
+ last_transition_id?: string;
71
+ last_progress_at?: number;
72
+ turn_budget_ms?: number;
73
+ stall_window_ms?: number;
74
+ stall_restart_count?: number;
75
+ active_workspace_path?: string;
76
+ active_workspace_session_id?: string;
77
+ workspace_hook_health?: "ok" | "degraded" | "failed";
55
78
  }
56
79
  export interface AceArchivedChatRecord {
57
80
  session_id: string;
@@ -75,19 +98,112 @@ export interface AceArchivedChatRecord {
75
98
  recommended_next_action?: string;
76
99
  };
77
100
  }
101
+ export interface TransitionRecord {
102
+ transition_id: string;
103
+ session_id?: string;
104
+ subject_kind: "runtime_status" | "continuity" | "plan_step" | "plan_proposal" | "nudge" | "capability" | "workspace_session" | "turn" | "activation";
105
+ subject_id: string;
106
+ from?: string;
107
+ to: string;
108
+ reason: string;
109
+ reason_code?: string;
110
+ evidence_refs: string[];
111
+ at: number;
112
+ }
113
+ export interface TurnSnapshotRecord {
114
+ snapshot_id: string;
115
+ session_id: string;
116
+ turn_number: number;
117
+ captured_at: number;
118
+ preflight: Record<string, unknown>;
119
+ recall: Record<string, unknown> | null;
120
+ continuity: Record<string, unknown> | null;
121
+ capability_snapshot_id: string | null;
122
+ tool_choice_audit_id?: string | null;
123
+ surface_kind: AceRuntimeStatusPacket["surface_kind"];
124
+ }
125
+ export type ToolCostClass = "cheap" | "moderate" | "heavy";
126
+ export interface ToolAvailabilityEntry {
127
+ name: string;
128
+ reason: string;
129
+ reason_code: "runtime_disabled" | "workspace_disabled" | "policy_disabled" | "missing_credentials" | "unhealthy";
130
+ }
131
+ export interface CapabilitySnapshotRecord {
132
+ snapshot_id: string;
133
+ session_id: string;
134
+ turn_number: number;
135
+ captured_at: number;
136
+ model_class: AceRuntimeStatusPacket["model_class"];
137
+ allowed_tools: string[];
138
+ unavailable_tools: ToolAvailabilityEntry[];
139
+ tool_cost_class: Record<string, ToolCostClass>;
140
+ tool_choice_audit_id?: string;
141
+ }
142
+ export interface ToolChoiceAuditRecord {
143
+ audit_id: string;
144
+ session_id: string;
145
+ turn_number: number;
146
+ captured_at: number;
147
+ model_class: AceRuntimeStatusPacket["model_class"];
148
+ priority_tools: string[];
149
+ demoted_tools: Array<{
150
+ name: string;
151
+ reason_code: "heavy_for_small_local" | "policy_disabled" | "workspace_disabled";
152
+ }>;
153
+ selected_budget: {
154
+ read_file_lines_max_lines: number | null;
155
+ };
156
+ capability_snapshot_id: string;
157
+ }
158
+ type TransitionMetadata = {
159
+ from?: string;
160
+ reason: string;
161
+ reason_code?: string;
162
+ evidence_refs?: string[];
163
+ };
78
164
  export declare class LocalModelRuntimeRepository {
79
165
  private store;
80
166
  constructor(store: AcePackedStore);
167
+ private static normalizeTransitionMetadata;
81
168
  private activationKey;
82
169
  private continuityKey;
83
170
  private statusKey;
84
171
  private archiveKey;
85
172
  getActivationLedger(sessionId: string): Promise<AceSessionActivationLedger | undefined>;
86
- upsertActivationLedger(input: Partial<AceSessionActivationLedger> & Pick<AceSessionActivationLedger, "session_id">): Promise<AceSessionActivationLedger>;
173
+ private writeActivationLedger;
174
+ upsertActivationLedger(input: Partial<AceSessionActivationLedger> & Pick<AceSessionActivationLedger, "session_id">, transition: TransitionMetadata): Promise<AceSessionActivationLedger>;
175
+ upsertActivationLedgerForTestSetup(input: Partial<AceSessionActivationLedger> & Pick<AceSessionActivationLedger, "session_id">): Promise<AceSessionActivationLedger>;
176
+ upsertActivationLedgerWithTransition(input: Partial<AceSessionActivationLedger> & Pick<AceSessionActivationLedger, "session_id">, transition: TransitionMetadata): Promise<AceSessionActivationLedger>;
87
177
  getContinuityRecord(sessionId: string): Promise<AceSessionContinuityRecord | undefined>;
88
- upsertContinuityRecord(input: Omit<AceSessionContinuityRecord, "updated_at" | "created_at"> & Partial<Pick<AceSessionContinuityRecord, "created_at">>): Promise<AceSessionContinuityRecord>;
178
+ private writeContinuityRecord;
179
+ upsertContinuityRecord(input: Omit<AceSessionContinuityRecord, "updated_at" | "created_at"> & Partial<Pick<AceSessionContinuityRecord, "created_at">>, transition: TransitionMetadata): Promise<AceSessionContinuityRecord>;
180
+ upsertContinuityRecordWithTransition(input: Omit<AceSessionContinuityRecord, "updated_at" | "created_at"> & Partial<Pick<AceSessionContinuityRecord, "created_at">>, transition: TransitionMetadata): Promise<AceSessionContinuityRecord>;
181
+ upsertContinuityRecordForTestSetup(input: Omit<AceSessionContinuityRecord, "updated_at" | "created_at"> & Partial<Pick<AceSessionContinuityRecord, "created_at">>): Promise<AceSessionContinuityRecord>;
89
182
  getRuntimeStatus(sessionId: string): Promise<AceRuntimeStatusPacket | undefined>;
90
- upsertRuntimeStatus(input: Omit<AceRuntimeStatusPacket, "updated_at">): Promise<AceRuntimeStatusPacket>;
183
+ private writeRuntimeStatus;
184
+ upsertRuntimeStatus(input: Omit<AceRuntimeStatusPacket, "updated_at">, transition: TransitionMetadata): Promise<AceRuntimeStatusPacket>;
185
+ upsertRuntimeStatusWithTransition(input: Omit<AceRuntimeStatusPacket, "updated_at">, transition: TransitionMetadata): Promise<AceRuntimeStatusPacket>;
186
+ upsertRuntimeStatusForTestSetup(input: Omit<AceRuntimeStatusPacket, "updated_at">): Promise<AceRuntimeStatusPacket>;
187
+ private transitionLogKey;
188
+ private transitionSessionIndexKey;
189
+ private deriveTransitionSessionId;
190
+ appendTransitionRecord(record: Omit<TransitionRecord, "transition_id" | "at"> & Partial<Pick<TransitionRecord, "transition_id" | "at" | "session_id">>): Promise<TransitionRecord>;
191
+ getTransitionLog(subjectKind: TransitionRecord["subject_kind"], subjectId: string): Promise<TransitionRecord[]>;
192
+ getTransitionLog(sessionId: string, limit: number): Promise<TransitionRecord[]>;
193
+ private turnSnapshotKey;
194
+ saveTurnSnapshot(snapshot: TurnSnapshotRecord): Promise<void>;
195
+ getTurnSnapshot(sessionId: string, turnNumber: number): Promise<TurnSnapshotRecord | undefined>;
196
+ private capabilitySnapshotKey;
197
+ private capabilitySnapshotIdKey;
198
+ private toolChoiceAuditKey;
199
+ private toolChoiceAuditIdKey;
200
+ saveCapabilitySnapshot(snapshot: CapabilitySnapshotRecord): Promise<void>;
201
+ getCapabilitySnapshot(sessionId: string, turnNumber: number): Promise<CapabilitySnapshotRecord | undefined>;
202
+ getCapabilitySnapshotById(sessionId: string, snapshotId: string): Promise<CapabilitySnapshotRecord | undefined>;
203
+ getTurnCapabilitySnapshot(sessionId: string, turnNumber: number): Promise<CapabilitySnapshotRecord | undefined>;
204
+ saveToolChoiceAudit(audit: ToolChoiceAuditRecord): Promise<ToolChoiceAuditRecord>;
205
+ getToolChoiceAudit(sessionId: string, turnNumber: number): Promise<ToolChoiceAuditRecord | undefined>;
206
+ getToolChoiceAuditById(sessionId: string, auditId: string): Promise<ToolChoiceAuditRecord | undefined>;
91
207
  listRuntimeStatuses(): Promise<AceRuntimeStatusPacket[]>;
92
208
  archiveChat(input: Omit<AceArchivedChatRecord, "archived_at"> & Partial<Pick<AceArchivedChatRecord, "archived_at">>): Promise<AceArchivedChatRecord>;
93
209
  listArchivedChats(): Promise<AceArchivedChatRecord[]>;
@@ -95,4 +211,5 @@ export declare class LocalModelRuntimeRepository {
95
211
  private appendLifecycleEntry;
96
212
  }
97
213
  export declare function withLocalModelRuntimeRepository<T>(workspaceRoot: string, fn: (repo: LocalModelRuntimeRepository) => Promise<T>): Promise<T | undefined>;
214
+ export {};
98
215
  //# sourceMappingURL=local-model-runtime-repository.d.ts.map
@@ -2,19 +2,52 @@ import { randomUUID } from "node:crypto";
2
2
  import { openStore } from "../ace-packed-store.js";
3
3
  import { ContentSource, EntityKind } from "../types.js";
4
4
  import { getWorkspaceStorePath } from "../store-snapshot.js";
5
- import { withStoreWriteQueue } from "../write-queue.js";
5
+ import { withStoreWriteCoordinator } from "../write-coordinator.js";
6
6
  const ACTIVATION_INDEX_KEY = "state/ui/session_activation/index";
7
7
  const CONTINUITY_INDEX_KEY = "state/runtime/session_continuity/index";
8
8
  const STATUS_INDEX_KEY = "state/runtime/ui_status/index";
9
9
  const ARCHIVE_INDEX_KEY = "state/ui/chat_archives/index";
10
+ const TRANSITION_LOG_PREFIX = "state/runtime/transitions";
11
+ const TRANSITION_SESSION_INDEX_PREFIX = "state/runtime/transitions/by_session";
12
+ const TURN_SNAPSHOT_PREFIX = "state/runtime/turn_snapshots";
13
+ const CAPABILITY_SNAPSHOT_PREFIX = "state/runtime/capability_snapshots";
14
+ const TOOL_CHOICE_AUDIT_PREFIX = "state/runtime/tool_choice_audits";
10
15
  function uniqueStrings(values) {
11
16
  return Array.from(new Set((values ?? []).filter((value) => typeof value === "string" && value.length > 0)));
12
17
  }
18
+ function normalizeTransitionField(name, value) {
19
+ if (typeof value !== "string")
20
+ return undefined;
21
+ const trimmed = value.trim();
22
+ if (!trimmed || trimmed === "(unspecified)" || trimmed === "placeholder" || trimmed === "(placeholder)") {
23
+ throw new Error(`Transition metadata requires a non-placeholder ${name}.`);
24
+ }
25
+ return trimmed;
26
+ }
13
27
  export class LocalModelRuntimeRepository {
14
28
  store;
15
29
  constructor(store) {
16
30
  this.store = store;
17
31
  }
32
+ static normalizeTransitionMetadata(transition) {
33
+ const reason = normalizeTransitionField("reason", transition.reason);
34
+ if (!reason) {
35
+ throw new Error("Transition metadata requires a non-placeholder reason.");
36
+ }
37
+ const from = normalizeTransitionField("from", transition.from);
38
+ const reasonCode = normalizeTransitionField("reason_code", transition.reason_code);
39
+ const evidenceRefs = uniqueStrings(transition.evidence_refs ?? []);
40
+ if (evidenceRefs.some((entry) => entry === "placeholder" || entry === "(placeholder)")) {
41
+ throw new Error("Transition metadata evidence_refs cannot use placeholder values.");
42
+ }
43
+ return {
44
+ ...transition,
45
+ ...(from ? { from } : {}),
46
+ reason,
47
+ ...(reasonCode ? { reason_code: reasonCode } : {}),
48
+ evidence_refs: evidenceRefs,
49
+ };
50
+ }
18
51
  activationKey(sessionId) {
19
52
  return `state/ui/session_activation/${sessionId}`;
20
53
  }
@@ -30,7 +63,7 @@ export class LocalModelRuntimeRepository {
30
63
  async getActivationLedger(sessionId) {
31
64
  return this.store.getJSON(this.activationKey(sessionId));
32
65
  }
33
- async upsertActivationLedger(input) {
66
+ async writeActivationLedger(input, transition) {
34
67
  const current = await this.getActivationLedger(input.session_id);
35
68
  const now = Date.now();
36
69
  const record = {
@@ -43,7 +76,27 @@ export class LocalModelRuntimeRepository {
43
76
  activated_tools: uniqueStrings(input.activated_tools ?? current?.activated_tools ?? []),
44
77
  activated_roles: uniqueStrings(input.activated_roles ?? current?.activated_roles ?? []),
45
78
  last_recommended_action: input.last_recommended_action ?? current?.last_recommended_action,
79
+ auto_executed_nudges: input.auto_executed_nudges != null
80
+ ? uniqueStrings(input.auto_executed_nudges)
81
+ : current?.auto_executed_nudges,
82
+ ignored_nudges: input.ignored_nudges != null
83
+ ? uniqueStrings(input.ignored_nudges)
84
+ : current?.ignored_nudges,
46
85
  };
86
+ if (transition) {
87
+ const normalizedTransition = LocalModelRuntimeRepository.normalizeTransitionMetadata(transition);
88
+ const tr = await this.appendTransitionRecord({
89
+ session_id: input.session_id,
90
+ subject_kind: "activation",
91
+ subject_id: input.session_id,
92
+ from: normalizedTransition.from ?? (current ? "updated" : "created"),
93
+ to: "updated",
94
+ reason: normalizedTransition.reason,
95
+ reason_code: normalizedTransition.reason_code,
96
+ evidence_refs: normalizedTransition.evidence_refs ?? [],
97
+ });
98
+ record.last_transition_id = tr.transition_id;
99
+ }
47
100
  await this.store.setJSON(this.activationKey(record.session_id), record);
48
101
  await this.appendLifecycleEntry(this.activationKey(record.session_id), {
49
102
  op: current ? "activation_update" : "activation_create",
@@ -52,10 +105,22 @@ export class LocalModelRuntimeRepository {
52
105
  await this.addToIndex(ACTIVATION_INDEX_KEY, record.session_id);
53
106
  return record;
54
107
  }
108
+ async upsertActivationLedger(input, transition) {
109
+ if (!transition) {
110
+ throw new Error("upsertActivationLedger requires transition metadata. Use upsertActivationLedgerForTestSetup for seeding.");
111
+ }
112
+ return this.writeActivationLedger(input, transition);
113
+ }
114
+ async upsertActivationLedgerForTestSetup(input) {
115
+ return this.writeActivationLedger(input);
116
+ }
117
+ async upsertActivationLedgerWithTransition(input, transition) {
118
+ return this.upsertActivationLedger(input, transition);
119
+ }
55
120
  async getContinuityRecord(sessionId) {
56
121
  return this.store.getJSON(this.continuityKey(sessionId));
57
122
  }
58
- async upsertContinuityRecord(input) {
123
+ async writeContinuityRecord(input, transition) {
59
124
  const current = await this.getContinuityRecord(input.session_id);
60
125
  const now = Date.now();
61
126
  const record = {
@@ -66,6 +131,20 @@ export class LocalModelRuntimeRepository {
66
131
  recent_decisions: uniqueStrings(input.recent_decisions ?? current?.recent_decisions ?? []),
67
132
  evidence_refs: uniqueStrings(input.evidence_refs ?? current?.evidence_refs ?? []),
68
133
  };
134
+ if (transition) {
135
+ const normalizedTransition = LocalModelRuntimeRepository.normalizeTransitionMetadata(transition);
136
+ const tr = await this.appendTransitionRecord({
137
+ session_id: input.session_id,
138
+ subject_kind: "continuity",
139
+ subject_id: input.session_id,
140
+ from: normalizedTransition.from ?? current?.last_bridge_status,
141
+ to: input.last_bridge_status ?? "running",
142
+ reason: normalizedTransition.reason,
143
+ reason_code: normalizedTransition.reason_code,
144
+ evidence_refs: normalizedTransition.evidence_refs ?? [],
145
+ });
146
+ record.last_transition_id = tr.transition_id;
147
+ }
69
148
  await this.store.setJSON(this.continuityKey(record.session_id), record);
70
149
  await this.appendLifecycleEntry(this.continuityKey(record.session_id), {
71
150
  op: current ? "continuity_update" : "continuity_create",
@@ -75,14 +154,42 @@ export class LocalModelRuntimeRepository {
75
154
  await this.addToIndex(CONTINUITY_INDEX_KEY, record.session_id);
76
155
  return record;
77
156
  }
157
+ async upsertContinuityRecord(input, transition) {
158
+ if (!transition) {
159
+ throw new Error("upsertContinuityRecord requires transition metadata. Use upsertContinuityRecordForTestSetup for seeding.");
160
+ }
161
+ return this.writeContinuityRecord(input, transition);
162
+ }
163
+ async upsertContinuityRecordWithTransition(input, transition) {
164
+ return this.upsertContinuityRecord(input, transition);
165
+ }
166
+ async upsertContinuityRecordForTestSetup(input) {
167
+ return this.writeContinuityRecord(input);
168
+ }
78
169
  async getRuntimeStatus(sessionId) {
79
170
  return this.store.getJSON(this.statusKey(sessionId));
80
171
  }
81
- async upsertRuntimeStatus(input) {
172
+ async writeRuntimeStatus(input, transition) {
173
+ const current = await this.getRuntimeStatus(input.session_id);
82
174
  const record = {
83
175
  ...input,
84
176
  updated_at: Date.now(),
85
177
  };
178
+ if (transition) {
179
+ const normalizedTransition = LocalModelRuntimeRepository.normalizeTransitionMetadata(transition);
180
+ const tr = await this.appendTransitionRecord({
181
+ session_id: input.session_id,
182
+ subject_kind: "runtime_status",
183
+ subject_id: input.session_id,
184
+ from: normalizedTransition.from ?? current?.bridge_status,
185
+ to: input.bridge_status,
186
+ reason: normalizedTransition.reason,
187
+ reason_code: normalizedTransition.reason_code,
188
+ evidence_refs: normalizedTransition.evidence_refs ?? [],
189
+ });
190
+ record.last_transition_id = tr.transition_id;
191
+ record.last_progress_at = tr.at;
192
+ }
86
193
  await this.store.setJSON(this.statusKey(record.session_id), record);
87
194
  await this.appendLifecycleEntry(this.statusKey(record.session_id), {
88
195
  op: "runtime_status",
@@ -93,6 +200,135 @@ export class LocalModelRuntimeRepository {
93
200
  await this.addToIndex(STATUS_INDEX_KEY, record.session_id);
94
201
  return record;
95
202
  }
203
+ async upsertRuntimeStatus(input, transition) {
204
+ if (!transition) {
205
+ throw new Error("upsertRuntimeStatus requires transition metadata. Use upsertRuntimeStatusForTestSetup for seeding.");
206
+ }
207
+ return this.writeRuntimeStatus(input, transition);
208
+ }
209
+ async upsertRuntimeStatusWithTransition(input, transition) {
210
+ return this.upsertRuntimeStatus(input, transition);
211
+ }
212
+ async upsertRuntimeStatusForTestSetup(input) {
213
+ return this.writeRuntimeStatus(input);
214
+ }
215
+ transitionLogKey(subjectKind, subjectId) {
216
+ return `${TRANSITION_LOG_PREFIX}/${subjectKind}/${subjectId}`;
217
+ }
218
+ transitionSessionIndexKey(sessionId) {
219
+ return `${TRANSITION_SESSION_INDEX_PREFIX}/${sessionId}`;
220
+ }
221
+ deriveTransitionSessionId(record) {
222
+ if (typeof record.session_id === "string" && record.session_id.trim().length > 0) {
223
+ return record.session_id.trim();
224
+ }
225
+ const [ownerId] = record.subject_id.split("/", 1);
226
+ return ownerId?.trim() || record.subject_id;
227
+ }
228
+ async appendTransitionRecord(record) {
229
+ const normalizedTransition = LocalModelRuntimeRepository.normalizeTransitionMetadata(record);
230
+ const sessionId = this.deriveTransitionSessionId(record);
231
+ const full = {
232
+ ...record,
233
+ session_id: sessionId,
234
+ reason: normalizedTransition.reason,
235
+ evidence_refs: normalizedTransition.evidence_refs ?? [],
236
+ transition_id: record.transition_id ?? randomUUID(),
237
+ at: record.at ?? Date.now(),
238
+ };
239
+ const key = this.transitionLogKey(full.subject_kind, full.subject_id);
240
+ const existing = await this.store.getJSON(key) ?? [];
241
+ existing.push(full);
242
+ await this.store.setJSON(key, existing);
243
+ const sessionIndexKey = this.transitionSessionIndexKey(sessionId);
244
+ const sessionIndex = await this.store.getJSON(sessionIndexKey) ?? [];
245
+ sessionIndex.push(full);
246
+ await this.store.setJSON(sessionIndexKey, sessionIndex);
247
+ return full;
248
+ }
249
+ async getTransitionLog(subjectKindOrSessionId, subjectIdOrLimit) {
250
+ if (typeof subjectIdOrLimit === "number") {
251
+ const sessionId = subjectKindOrSessionId;
252
+ const deduped = new Map();
253
+ const indexed = await this.store.getJSON(this.transitionSessionIndexKey(sessionId)) ?? [];
254
+ for (const record of indexed) {
255
+ deduped.set(record.transition_id, record);
256
+ }
257
+ const kinds = [
258
+ "runtime_status", "continuity", "turn", "plan_step", "plan_proposal", "capability", "workspace_session", "nudge", "activation",
259
+ ];
260
+ for (const kind of kinds) {
261
+ const records = await this.store.getJSON(this.transitionLogKey(kind, sessionId)) ?? [];
262
+ for (const record of records) {
263
+ deduped.set(record.transition_id, record);
264
+ }
265
+ }
266
+ return Array.from(deduped.values()).sort((a, b) => b.at - a.at).slice(0, subjectIdOrLimit);
267
+ }
268
+ const key = this.transitionLogKey(subjectKindOrSessionId, subjectIdOrLimit);
269
+ return await this.store.getJSON(key) ?? [];
270
+ }
271
+ turnSnapshotKey(sessionId, turnNumber) {
272
+ return `${TURN_SNAPSHOT_PREFIX}/${sessionId}/${turnNumber}`;
273
+ }
274
+ async saveTurnSnapshot(snapshot) {
275
+ await this.store.setJSON(this.turnSnapshotKey(snapshot.session_id, snapshot.turn_number), snapshot);
276
+ }
277
+ async getTurnSnapshot(sessionId, turnNumber) {
278
+ return this.store.getJSON(this.turnSnapshotKey(sessionId, turnNumber));
279
+ }
280
+ capabilitySnapshotKey(sessionId, turnNumber) {
281
+ return `${CAPABILITY_SNAPSHOT_PREFIX}/${sessionId}/${turnNumber}`;
282
+ }
283
+ capabilitySnapshotIdKey(sessionId, snapshotId) {
284
+ return `${CAPABILITY_SNAPSHOT_PREFIX}/by_id/${sessionId}/${snapshotId}`;
285
+ }
286
+ toolChoiceAuditKey(sessionId, turnNumber) {
287
+ return `${TOOL_CHOICE_AUDIT_PREFIX}/${sessionId}/${turnNumber}`;
288
+ }
289
+ toolChoiceAuditIdKey(sessionId, auditId) {
290
+ return `${TOOL_CHOICE_AUDIT_PREFIX}/by_id/${sessionId}/${auditId}`;
291
+ }
292
+ async saveCapabilitySnapshot(snapshot) {
293
+ await this.store.setJSON(this.capabilitySnapshotKey(snapshot.session_id, snapshot.turn_number), snapshot);
294
+ await this.store.setJSON(this.capabilitySnapshotIdKey(snapshot.session_id, snapshot.snapshot_id), {
295
+ turn_number: snapshot.turn_number,
296
+ });
297
+ }
298
+ async getCapabilitySnapshot(sessionId, turnNumber) {
299
+ return this.store.getJSON(this.capabilitySnapshotKey(sessionId, turnNumber));
300
+ }
301
+ async getCapabilitySnapshotById(sessionId, snapshotId) {
302
+ const pointer = await this.store.getJSON(this.capabilitySnapshotIdKey(sessionId, snapshotId));
303
+ if (!pointer)
304
+ return undefined;
305
+ return this.getCapabilitySnapshot(sessionId, pointer.turn_number);
306
+ }
307
+ async getTurnCapabilitySnapshot(sessionId, turnNumber) {
308
+ const turnSnapshot = await this.getTurnSnapshot(sessionId, turnNumber);
309
+ if (!turnSnapshot)
310
+ return undefined;
311
+ if (!turnSnapshot.capability_snapshot_id) {
312
+ return this.getCapabilitySnapshot(sessionId, turnNumber);
313
+ }
314
+ return this.getCapabilitySnapshotById(sessionId, turnSnapshot.capability_snapshot_id);
315
+ }
316
+ async saveToolChoiceAudit(audit) {
317
+ await this.store.setJSON(this.toolChoiceAuditKey(audit.session_id, audit.turn_number), audit);
318
+ await this.store.setJSON(this.toolChoiceAuditIdKey(audit.session_id, audit.audit_id), {
319
+ turn_number: audit.turn_number,
320
+ });
321
+ return audit;
322
+ }
323
+ async getToolChoiceAudit(sessionId, turnNumber) {
324
+ return this.store.getJSON(this.toolChoiceAuditKey(sessionId, turnNumber));
325
+ }
326
+ async getToolChoiceAuditById(sessionId, auditId) {
327
+ const pointer = await this.store.getJSON(this.toolChoiceAuditIdKey(sessionId, auditId));
328
+ if (!pointer)
329
+ return undefined;
330
+ return this.getToolChoiceAudit(sessionId, pointer.turn_number);
331
+ }
96
332
  async listRuntimeStatuses() {
97
333
  const index = await this.store.getJSON(STATUS_INDEX_KEY) ?? [];
98
334
  const statuses = [];
@@ -149,7 +385,7 @@ export class LocalModelRuntimeRepository {
149
385
  }
150
386
  export async function withLocalModelRuntimeRepository(workspaceRoot, fn) {
151
387
  const storePath = getWorkspaceStorePath(workspaceRoot);
152
- return withStoreWriteQueue(storePath, async () => {
388
+ return withStoreWriteCoordinator(storePath, async () => {
153
389
  const store = await openStore(storePath);
154
390
  try {
155
391
  const repo = new LocalModelRuntimeRepository(store);
@@ -160,6 +396,6 @@ export async function withLocalModelRuntimeRepository(workspaceRoot, fn) {
160
396
  finally {
161
397
  await store.close();
162
398
  }
163
- });
399
+ }, { operation_label: "withLocalModelRuntimeRepository" });
164
400
  }
165
401
  //# sourceMappingURL=local-model-runtime-repository.js.map
@@ -9,7 +9,7 @@ export interface VericifyPost {
9
9
  id: string;
10
10
  run_id: string;
11
11
  agent_id: string;
12
- kind: "intent" | "progress" | "completion" | "blocker" | "handoff_note" | "stale_ack";
12
+ kind: "intent" | "progress" | "completion" | "blocker" | "handoff_note" | "stale_ack" | "plan_proposal" | "plan_quality_assessment";
13
13
  summary: string;
14
14
  ts: number;
15
15
  metadata?: Record<string, unknown>;
@@ -20,6 +20,10 @@ export interface InstallResult {
20
20
  export declare function installSkillPack(workspaceRoot: string, skillName: string): Promise<InstallResult>;
21
21
  export declare function installAllSkills(workspaceRoot: string): Promise<InstallResult[]>;
22
22
  export declare function listInstalledSkills(workspaceRoot: string): Promise<string[]>;
23
+ export declare function listInstalledSkillsDetailed(workspaceRoot: string): Promise<{
24
+ skills: string[];
25
+ warnings: string[];
26
+ }>;
23
27
  export declare function listAvailableSkillPacks(workspaceRoot: string): Promise<{
24
28
  installed: string[];
25
29
  available: string[];
@@ -13,10 +13,11 @@
13
13
  * 4. Compact if the install materially expands the store (>50% dead space)
14
14
  */
15
15
  import { existsSync } from "fs";
16
- import { join, dirname, resolve } from "path";
16
+ import { dirname, resolve } from "path";
17
17
  import { fileURLToPath } from "url";
18
18
  import { openStore } from "./ace-packed-store.js";
19
19
  import { bakeSkillPack, listAvailableSkills } from "./knowledge-bake.js";
20
+ import { ensureCanonicalWorkspaceStore, resolveWorkspaceStorePath, } from "./workspace-store-paths.js";
20
21
  const __dirname = dirname(fileURLToPath(import.meta.url));
21
22
  function getAssetsRoot() {
22
23
  const candidate = resolve(__dirname, "..", "..", "assets");
@@ -25,26 +26,29 @@ function getAssetsRoot() {
25
26
  return resolve(__dirname, "..", "..", "..", "assets");
26
27
  }
27
28
  export async function installSkillPack(workspaceRoot, skillName) {
28
- const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
29
- if (!existsSync(storePath)) {
30
- throw new Error(`No store found at ${storePath}. Run 'ace init' first.`);
29
+ const resolved = await ensureCanonicalWorkspaceStore(workspaceRoot, {
30
+ operationLabel: "installSkillPack",
31
+ });
32
+ if (resolved.mode === "missing") {
33
+ throw new Error(`No ACE store found at ${resolved.canonicalPath} or ${resolved.legacyPath}. Run 'ace init' first.`);
34
+ }
35
+ for (const w of resolved.warnings) {
36
+ console.warn(`[skills-install] ${w}`);
31
37
  }
32
38
  const assetsRoot = getAssetsRoot();
33
39
  const available = await listAvailableSkills(assetsRoot);
34
40
  if (!available.includes(skillName)) {
35
41
  throw new Error(`Skill pack '${skillName}' not found. Available: ${available.join(", ")}`);
36
42
  }
37
- const store = await openStore(storePath);
43
+ const store = await openStore(resolved.storePath);
38
44
  let compacted = false;
39
45
  try {
40
- // Check if already installed
41
46
  const existing = await store.listSkills();
42
47
  if (existing.includes(skillName)) {
43
48
  console.log(`[skills-install] '${skillName}' already installed — reinstalling`);
44
49
  }
45
50
  const files = await bakeSkillPack(store, assetsRoot, skillName);
46
51
  await store.commit();
47
- // Compact if dead space > 50%
48
52
  if (store.deadSpaceRatio > 0.5) {
49
53
  await store.compact();
50
54
  compacted = true;
@@ -65,12 +69,17 @@ export async function installAllSkills(workspaceRoot) {
65
69
  return results;
66
70
  }
67
71
  export async function listInstalledSkills(workspaceRoot) {
68
- const storePath = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
69
- if (!existsSync(storePath))
70
- return [];
71
- const store = await openStore(storePath);
72
+ const { skills } = await listInstalledSkillsDetailed(workspaceRoot);
73
+ return skills;
74
+ }
75
+ export async function listInstalledSkillsDetailed(workspaceRoot) {
76
+ const resolution = resolveWorkspaceStorePath(workspaceRoot);
77
+ if (resolution.mode === "missing")
78
+ return { skills: [], warnings: [] };
79
+ const store = await openStore(resolution.storePath);
72
80
  try {
73
- return await store.listSkills();
81
+ const skills = await store.listSkills();
82
+ return { skills, warnings: resolution.warnings };
74
83
  }
75
84
  finally {
76
85
  await store.close();
@@ -15,6 +15,7 @@ import { TrackerRepository } from "./repositories/tracker-repository.js";
15
15
  import { DiscoveryRepository } from "./repositories/discovery-repository.js";
16
16
  import { VericifyRepository } from "./repositories/vericify-repository.js";
17
17
  import { LocalModelRuntimeRepository } from "./repositories/local-model-runtime-repository.js";
18
+ import type { TransitionRecord } from "./repositories/local-model-runtime-repository.js";
18
19
  export interface DashboardSnapshot {
19
20
  handoffs: Awaited<ReturnType<HandoffRepository["list"]>>;
20
21
  todos: Awaited<ReturnType<TodoRepository["getAll"]>>;
@@ -27,6 +28,7 @@ export interface DashboardSnapshot {
27
28
  providers: Awaited<ReturnType<DiscoveryRepository["listAll"]>>;
28
29
  runtimeStatuses: Awaited<ReturnType<LocalModelRuntimeRepository["listRuntimeStatuses"]>>;
29
30
  posts: Awaited<ReturnType<VericifyRepository["list"]>>;
31
+ recentTransitions: TransitionRecord[];
30
32
  snapped_at: number;
31
33
  }
32
34
  export declare class StoreStateReader {
@@ -58,6 +58,25 @@ export class StoreStateReader {
58
58
  r.localModelRuntime.listRuntimeStatuses(),
59
59
  r.vericify.list(),
60
60
  ]);
61
+ // Phase 2: fetch transition log for the live session (needs runtimeStatuses from phase 1)
62
+ const liveStatus = runtimeStatuses.find((s) => {
63
+ try {
64
+ process.kill(s.process_id, 0);
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ });
71
+ let recentTransitions = [];
72
+ if (liveStatus?.session_id) {
73
+ try {
74
+ recentTransitions = await r.localModelRuntime.getTransitionLog(liveStatus.session_id, 15);
75
+ }
76
+ catch {
77
+ recentTransitions = [];
78
+ }
79
+ }
61
80
  return {
62
81
  handoffs,
63
82
  todos,
@@ -70,6 +89,7 @@ export class StoreStateReader {
70
89
  providers,
71
90
  runtimeStatuses,
72
91
  posts,
92
+ recentTransitions,
73
93
  snapped_at: Date.now(),
74
94
  };
75
95
  }
@@ -1,3 +1,4 @@
1
+ import { type StoreWriteCoordinatorOptions } from "./write-coordinator.js";
1
2
  export declare const OPERATIONAL_ARTIFACT_REL_PATHS: Set<string>;
2
3
  export declare function operationalArtifactKey(relPath: string): string;
3
4
  export declare function isOperationalArtifactPath(relPath: string): boolean;
@@ -9,4 +10,10 @@ export declare function writeStoreBlobsSync(workspaceRoot: string, blobs: Readon
9
10
  }>): string[];
10
11
  export declare function writeStoreBlobSync(workspaceRoot: string, key: string, content: string): string;
11
12
  export declare function writeOperationalArtifactSync(workspaceRoot: string, relPath: string, content: string): string;
13
+ export declare function writeStoreBlobsQueued(workspaceRoot: string, blobs: ReadonlyArray<{
14
+ key: string;
15
+ content: string;
16
+ }>, options?: StoreWriteCoordinatorOptions): Promise<string[]>;
17
+ export declare function writeStoreBlobQueued(workspaceRoot: string, key: string, content: string, options?: StoreWriteCoordinatorOptions): Promise<string>;
18
+ export declare function writeOperationalArtifactQueued(workspaceRoot: string, relPath: string, content: string, options?: StoreWriteCoordinatorOptions): Promise<string>;
12
19
  //# sourceMappingURL=store-artifacts.d.ts.map