@voybio/ace-swarm 0.2.4 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/README.md +20 -13
  3. package/assets/.agents/skills/eval-harness/SKILL.md +14 -0
  4. package/assets/.agents/skills/handoff-lint/SKILL.md +14 -0
  5. package/assets/.agents/skills/incident-commander/SKILL.md +14 -0
  6. package/assets/.agents/skills/memory-curator/SKILL.md +14 -0
  7. package/assets/.agents/skills/release-sentry/SKILL.md +14 -0
  8. package/assets/.agents/skills/risk-quant/SKILL.md +14 -0
  9. package/assets/.agents/skills/schema-forge/SKILL.md +14 -0
  10. package/assets/.agents/skills/state-auditor/SKILL.md +14 -0
  11. package/assets/agent-state/EVIDENCE_LOG.md +1 -1
  12. package/assets/agent-state/MODULES/gates/gate-correctness.json +1 -1
  13. package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
  14. package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
  15. package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
  16. package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
  17. package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
  18. package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
  19. package/assets/agent-state/STATUS.md +2 -2
  20. package/assets/scripts/ace-hook-dispatch.mjs +70 -6
  21. package/assets/scripts/render-mcp-configs.sh +19 -5
  22. package/dist/ace-context.js +22 -1
  23. package/dist/ace-server-instructions.js +3 -3
  24. package/dist/ace-state-resolver.js +5 -3
  25. package/dist/astgrep-index.d.ts +9 -1
  26. package/dist/astgrep-index.js +14 -3
  27. package/dist/cli.js +52 -20
  28. package/dist/handoff-registry.js +5 -5
  29. package/dist/helpers/artifacts.d.ts +19 -0
  30. package/dist/helpers/artifacts.js +152 -0
  31. package/dist/helpers/bootstrap.d.ts +24 -0
  32. package/dist/helpers/bootstrap.js +894 -0
  33. package/dist/helpers/constants.d.ts +53 -0
  34. package/dist/helpers/constants.js +288 -0
  35. package/dist/helpers/drift.d.ts +13 -0
  36. package/dist/helpers/drift.js +45 -0
  37. package/dist/helpers/path-utils.d.ts +17 -0
  38. package/dist/helpers/path-utils.js +104 -0
  39. package/dist/helpers/store-resolution.d.ts +19 -0
  40. package/dist/helpers/store-resolution.js +301 -0
  41. package/dist/helpers/workspace-root.d.ts +3 -0
  42. package/dist/helpers/workspace-root.js +80 -0
  43. package/dist/helpers.d.ts +8 -123
  44. package/dist/helpers.js +8 -1747
  45. package/dist/job-scheduler.js +3 -3
  46. package/dist/local-model-runtime.js +12 -1
  47. package/dist/model-bridge.d.ts +7 -0
  48. package/dist/model-bridge.js +75 -5
  49. package/dist/orchestrator-supervisor.d.ts +14 -0
  50. package/dist/orchestrator-supervisor.js +72 -1
  51. package/dist/run-ledger.js +3 -3
  52. package/dist/runtime-command.d.ts +8 -0
  53. package/dist/runtime-command.js +38 -6
  54. package/dist/runtime-executor.d.ts +14 -0
  55. package/dist/runtime-executor.js +669 -171
  56. package/dist/runtime-profile.d.ts +32 -0
  57. package/dist/runtime-profile.js +89 -13
  58. package/dist/runtime-tool-specs.d.ts +21 -0
  59. package/dist/runtime-tool-specs.js +78 -3
  60. package/dist/safe-edit.d.ts +7 -0
  61. package/dist/safe-edit.js +163 -37
  62. package/dist/schemas.js +19 -0
  63. package/dist/shared.d.ts +2 -2
  64. package/dist/status-events.js +9 -6
  65. package/dist/store/ace-packed-store.d.ts +3 -2
  66. package/dist/store/ace-packed-store.js +188 -110
  67. package/dist/store/bootstrap-store.d.ts +1 -1
  68. package/dist/store/bootstrap-store.js +94 -81
  69. package/dist/store/cache-workspace.d.ts +22 -0
  70. package/dist/store/cache-workspace.js +149 -0
  71. package/dist/store/materializers/context-snapshot-materializer.js +6 -7
  72. package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
  73. package/dist/store/materializers/hook-context-materializer.js +11 -21
  74. package/dist/store/materializers/host-file-materializer.js +6 -0
  75. package/dist/store/materializers/projection-manager.d.ts +0 -1
  76. package/dist/store/materializers/projection-manager.js +5 -13
  77. package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
  78. package/dist/store/materializers/vericify-projector.d.ts +7 -7
  79. package/dist/store/materializers/vericify-projector.js +11 -11
  80. package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
  81. package/dist/store/repositories/local-model-runtime-repository.js +242 -6
  82. package/dist/store/skills-install.d.ts +4 -0
  83. package/dist/store/skills-install.js +21 -12
  84. package/dist/store/state-reader.d.ts +2 -0
  85. package/dist/store/state-reader.js +20 -0
  86. package/dist/store/store-artifacts.d.ts +7 -0
  87. package/dist/store/store-artifacts.js +27 -1
  88. package/dist/store/store-authority-audit.d.ts +18 -1
  89. package/dist/store/store-authority-audit.js +115 -5
  90. package/dist/store/store-snapshot.d.ts +3 -0
  91. package/dist/store/store-snapshot.js +22 -2
  92. package/dist/store/workspace-store-paths.d.ts +39 -0
  93. package/dist/store/workspace-store-paths.js +94 -0
  94. package/dist/store/write-coordinator.d.ts +65 -0
  95. package/dist/store/write-coordinator.js +386 -0
  96. package/dist/todo-state.js +5 -5
  97. package/dist/tools-agent.js +319 -34
  98. package/dist/tools-discovery.js +1 -1
  99. package/dist/tools-files.d.ts +7 -0
  100. package/dist/tools-files.js +299 -10
  101. package/dist/tools-framework.js +107 -27
  102. package/dist/tools-handoff.js +2 -2
  103. package/dist/tools-lifecycle.js +4 -4
  104. package/dist/tools-memory.js +6 -6
  105. package/dist/tools-todo.js +2 -2
  106. package/dist/tracker-adapters.d.ts +1 -1
  107. package/dist/tracker-adapters.js +13 -18
  108. package/dist/tracker-sync.js +5 -3
  109. package/dist/tui/agent-runner.js +3 -1
  110. package/dist/tui/chat.js +103 -7
  111. package/dist/tui/dashboard.d.ts +1 -0
  112. package/dist/tui/dashboard.js +43 -0
  113. package/dist/tui/layout.d.ts +20 -0
  114. package/dist/tui/layout.js +31 -1
  115. package/dist/tui/local-model-contract.d.ts +6 -2
  116. package/dist/tui/local-model-contract.js +16 -3
  117. package/dist/vericify-bridge.d.ts +5 -0
  118. package/dist/vericify-bridge.js +27 -3
  119. package/dist/workspace-manager.d.ts +30 -3
  120. package/dist/workspace-manager.js +257 -27
  121. package/package.json +1 -2
  122. package/dist/internal-tool-runtime.d.ts +0 -21
  123. package/dist/internal-tool-runtime.js +0 -136
  124. package/dist/store/workspace-snapshot.d.ts +0 -26
  125. package/dist/store/workspace-snapshot.js +0 -107
@@ -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
@@ -1,6 +1,8 @@
1
1
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
2
2
  import { dirname } from "node:path";
3
- import { getWorkspaceStorePath, readStoreBlobSync, toVirtualStorePath } from "./store-snapshot.js";
3
+ import { openStore } from "./ace-packed-store.js";
4
+ import { getWorkspaceStorePath, invalidateStoreSnapshotCache, readStoreBlobSync, toVirtualStorePath, } from "./store-snapshot.js";
5
+ import { withStoreWriteCoordinator } from "./write-coordinator.js";
4
6
  import { HEADER_SIZE, HEADER_SIZE_V1, STORE_VERSION, MAGIC } from "./types.js";
5
7
  // ── v2 header constants (mirrors ace-packed-store.ts) ─────────────────────────
6
8
  const H_KV_IDX_OFF = 16;
@@ -191,6 +193,7 @@ export function writeStoreBlobsSync(workspaceRoot, blobs) {
191
193
  const tmpPath = `${storePath}.${process.pid}.${Date.now()}.tmp`;
192
194
  writeFileSync(tmpPath, output);
193
195
  renameSync(tmpPath, storePath);
196
+ invalidateStoreSnapshotCache(storePath);
194
197
  return blobs.map(({ key }) => toVirtualStorePath(storePath, key));
195
198
  }
196
199
  export function writeStoreBlobSync(workspaceRoot, key, content) {
@@ -199,4 +202,27 @@ export function writeStoreBlobSync(workspaceRoot, key, content) {
199
202
  export function writeOperationalArtifactSync(workspaceRoot, relPath, content) {
200
203
  return writeStoreBlobSync(workspaceRoot, operationalArtifactKey(relPath), content);
201
204
  }
205
+ export async function writeStoreBlobsQueued(workspaceRoot, blobs, options = {}) {
206
+ const storePath = getWorkspaceStorePath(workspaceRoot);
207
+ return withStoreWriteCoordinator(storePath, async () => {
208
+ const store = await openStore(storePath);
209
+ try {
210
+ for (const { key, content } of blobs) {
211
+ await store.setBlob(key, content);
212
+ }
213
+ await store.commit();
214
+ return blobs.map(({ key }) => toVirtualStorePath(storePath, key));
215
+ }
216
+ finally {
217
+ await store.close();
218
+ }
219
+ }, { ...options, operation_label: options.operation_label ?? "writeStoreBlobsQueued" });
220
+ }
221
+ export async function writeStoreBlobQueued(workspaceRoot, key, content, options = {}) {
222
+ const [path] = await writeStoreBlobsQueued(workspaceRoot, [{ key, content }], options);
223
+ return path;
224
+ }
225
+ export async function writeOperationalArtifactQueued(workspaceRoot, relPath, content, options = {}) {
226
+ return writeStoreBlobQueued(workspaceRoot, operationalArtifactKey(relPath), content, options);
227
+ }
202
228
  //# sourceMappingURL=store-artifacts.js.map
@@ -1,22 +1,39 @@
1
1
  export declare const STORE_AUTHORITY_AUDIT_REPORT_REL_PATH = "agent-state/STORE_AUTHORITY_AUDIT.md";
2
2
  export type StoreAuthorityClassification = "canonical knowledge artifact" | "canonical runtime state" | "projection only";
3
+ export type StoreAuthorityTopologyClassification = "runtime_hot_path_sync_writer" | "async_handler_sync_writer" | "allowed_offline_sync_writer" | "store_backed_possible" | "non_blocking";
4
+ export type StoreAuthorityCallName = "safeWrite" | "safeWriteWorkspaceFile" | "safeWriteReport" | "withStoreWriteCoordinatorSync" | "writeStoreBlobSync" | "writeStoreBlobsSync" | "appendStatusEvent" | "createWorkspaceSession";
5
+ export interface StoreAuthorityTopologyFlags {
6
+ runtime_hot_path: boolean;
7
+ async_handler: boolean;
8
+ store_backed_possible: boolean;
9
+ allowed_offline_sync: boolean;
10
+ must_migrate_to_async: boolean;
11
+ }
3
12
  export interface StoreAuthoritySafeWriteSite {
4
- call: "safeWrite" | "safeWriteWorkspaceFile" | "safeWriteReport";
13
+ call: StoreAuthorityCallName;
5
14
  classification: StoreAuthorityClassification;
6
15
  file: string;
7
16
  line: number;
8
17
  column: number;
18
+ enclosing_function?: string;
19
+ enclosing_function_is_async: boolean;
9
20
  target_expression: string;
10
21
  target_path?: string;
11
22
  fallback_keys: string[];
12
23
  bypasses_acepack: boolean;
13
24
  regenerable_from_store: boolean;
25
+ topology: StoreAuthorityTopologyFlags;
26
+ topology_classification: StoreAuthorityTopologyClassification;
27
+ blocking: boolean;
28
+ failure_condition: string;
14
29
  note: string;
15
30
  }
16
31
  export interface StoreAuthorityAuditResult {
17
32
  bypassing_sites: StoreAuthoritySafeWriteSite[];
33
+ blocking_sites: StoreAuthoritySafeWriteSite[];
18
34
  classifications: Record<StoreAuthorityClassification, StoreAuthoritySafeWriteSite[]>;
19
35
  generated_at: string;
36
+ topology_failure: boolean;
20
37
  non_regenerable_projections: StoreAuthoritySafeWriteSite[];
21
38
  report_path: string;
22
39
  runtime_files_without_store_fallback_keys: StoreAuthoritySafeWriteSite[];
@@ -7,8 +7,32 @@ import { OPERATIONAL_ARTIFACT_REL_PATHS, operationalArtifactKey } from "./store-
7
7
  export const STORE_AUTHORITY_AUDIT_REPORT_REL_PATH = "agent-state/STORE_AUTHORITY_AUDIT.md";
8
8
  const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
9
9
  const SRC_ROOT = resolve(PACKAGE_ROOT, "src");
10
- const TARGET_CALLS = new Set(["safeWrite", "safeWriteWorkspaceFile", "safeWriteReport"]);
10
+ const TARGET_CALLS = new Set([
11
+ "safeWrite",
12
+ "safeWriteWorkspaceFile",
13
+ "safeWriteReport",
14
+ "withStoreWriteCoordinatorSync",
15
+ "writeStoreBlobSync",
16
+ "writeStoreBlobsSync",
17
+ "appendStatusEvent",
18
+ "createWorkspaceSession",
19
+ ]);
11
20
  const SKIPPED_INTERNAL_FUNCTIONS = new Set(["safeWriteWorkspaceFile", "safeWriteReport"]);
21
+ const OFFLINE_SYNC_HELPER_SOURCES = new Set([
22
+ "src/helpers/store-resolution.ts",
23
+ "src/store/store-artifacts.ts",
24
+ "src/store/write-coordinator.ts",
25
+ "src/runtime-executor.ts",
26
+ "src/status-events.ts",
27
+ "src/run-ledger.ts",
28
+ "src/handoff-registry.ts",
29
+ "src/workspace-manager.ts",
30
+ "src/job-scheduler.ts",
31
+ "src/astgrep-index.ts",
32
+ "src/index-store.ts",
33
+ "src/tools-files.ts",
34
+ ]);
35
+ const TOOL_RUNTIME_SOURCE_HINT = /(?:^|\/)(?:tools-[^/]+|tools-framework|runtime-[^/]+|tracker-[^/]+|workspace-manager|status-events|job-scheduler|run-ledger|runtime-profile|handoff-registry|astgrep-index)\.ts$/;
12
36
  const TASK_KEY_TO_FILE = {
13
37
  todo: "todo.md",
14
38
  role_tasks: "role_tasks.md",
@@ -74,6 +98,9 @@ function functionName(node) {
74
98
  return undefined;
75
99
  return undefined;
76
100
  }
101
+ function hasAsyncModifier(node) {
102
+ return node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
103
+ }
77
104
  function callName(node) {
78
105
  const expr = node.expression;
79
106
  if (ts.isIdentifier(expr))
@@ -82,6 +109,70 @@ function callName(node) {
82
109
  return expr.name.text;
83
110
  return undefined;
84
111
  }
112
+ function isRuntimeHotPathSource(relativeSource) {
113
+ return TOOL_RUNTIME_SOURCE_HINT.test(relativeSource);
114
+ }
115
+ function isOfflineSyncAllowedSource(relativeSource) {
116
+ return OFFLINE_SYNC_HELPER_SOURCES.has(relativeSource);
117
+ }
118
+ function classifyTopology(sourcePath, currentFunction, currentFunctionIsAsync, callName, resolved) {
119
+ const relativeSource = normalizeAuditPath(relative(PACKAGE_ROOT, sourcePath));
120
+ const runtimeHotPath = isRuntimeHotPathSource(relativeSource) ||
121
+ (currentFunction ? /(?:runtime|session|workspace|status|tracker|tool|handler|executor|lifecycle|scheduler|ledger)/i.test(currentFunction) : false) ||
122
+ currentFunctionIsAsync ||
123
+ callName === "createWorkspaceSession" ||
124
+ callName === "appendStatusEvent";
125
+ const asyncHandler = currentFunctionIsAsync &&
126
+ (isServerToolHandlerFromSource(sourcePath, currentFunction) || /(?:handler|tool|callback|command|action|task)/i.test(currentFunction ?? ""));
127
+ const allowedOfflineSync = isOfflineSyncAllowedSource(relativeSource) ||
128
+ (relativeSource === "src/status-events.ts" && currentFunction === "appendStatusEventSafe");
129
+ const storeBackedPossible = callName === "safeWrite" ||
130
+ callName === "safeWriteWorkspaceFile" ||
131
+ callName === "safeWriteReport" ||
132
+ callName === "withStoreWriteCoordinatorSync" ||
133
+ callName === "writeStoreBlobSync" ||
134
+ callName === "writeStoreBlobsSync" ||
135
+ callName === "appendStatusEvent" ||
136
+ callName === "createWorkspaceSession" ||
137
+ resolved.fallbackKeys.length > 0;
138
+ const mustMigrateToAsync = storeBackedPossible && !allowedOfflineSync && (runtimeHotPath || asyncHandler);
139
+ const blocking = runtimeHotPath && mustMigrateToAsync;
140
+ const classification = blocking
141
+ ? "runtime_hot_path_sync_writer"
142
+ : asyncHandler && mustMigrateToAsync
143
+ ? "async_handler_sync_writer"
144
+ : allowedOfflineSync
145
+ ? "allowed_offline_sync_writer"
146
+ : storeBackedPossible
147
+ ? "store_backed_possible"
148
+ : "non_blocking";
149
+ return {
150
+ flags: {
151
+ runtime_hot_path: runtimeHotPath,
152
+ async_handler: asyncHandler,
153
+ store_backed_possible: storeBackedPossible,
154
+ allowed_offline_sync: allowedOfflineSync,
155
+ must_migrate_to_async: mustMigrateToAsync,
156
+ },
157
+ classification,
158
+ blocking,
159
+ failure_condition: mustMigrateToAsync
160
+ ? "runtime_hot_path && store_backed_possible && !allowed_offline_sync"
161
+ : "no blocking migration required",
162
+ note: blocking
163
+ ? "production runtime hot path still reaches a sync store-backed writer"
164
+ : allowedOfflineSync
165
+ ? "sync writer is an allowed offline/library-local helper"
166
+ : storeBackedPossible
167
+ ? "store-backed sync surface is present but not currently blocking"
168
+ : "non-store topology site",
169
+ };
170
+ }
171
+ function isServerToolHandlerFromSource(sourcePath, currentFunction) {
172
+ if (!currentFunction)
173
+ return false;
174
+ return /(?:tools-|runtime-|tracker-|workspace-manager|status-events|job-scheduler|run-ledger|runtime-profile|handoff-registry|astgrep-index)/.test(normalizeAuditPath(relative(PACKAGE_ROOT, sourcePath)));
175
+ }
85
176
  function collectResolvableConstants(root, sourceFile, inherited) {
86
177
  const constants = new Map(inherited);
87
178
  const pending = [];
@@ -325,18 +416,20 @@ function scanSourceFile(sourcePath) {
325
416
  const raw = readFileSync(sourcePath, "utf-8");
326
417
  const sourceFile = ts.createSourceFile(sourcePath, raw, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
327
418
  const sites = [];
328
- const scanNode = (node, inheritedConstants, currentFunction) => {
419
+ const scanNode = (node, inheritedConstants, currentFunction, currentFunctionIsAsync = false) => {
329
420
  if (node !== sourceFile && isFunctionLike(node)) {
330
421
  const name = functionName(node) ?? currentFunction;
331
422
  const constants = collectResolvableConstants(node, sourceFile, inheritedConstants);
423
+ const isAsync = hasAsyncModifier(node);
332
424
  if (node.body) {
333
- ts.forEachChild(node.body, (child) => scanNode(child, constants, name));
425
+ ts.forEachChild(node.body, (child) => scanNode(child, constants, name, isAsync));
334
426
  }
335
427
  return;
336
428
  }
337
429
  if (ts.isCallExpression(node)) {
338
430
  const name = callName(node);
339
431
  if (name && TARGET_CALLS.has(name)) {
432
+ const call = name;
340
433
  if (currentFunction && SKIPPED_INTERNAL_FUNCTIONS.has(currentFunction) && name === "safeWrite") {
341
434
  return;
342
435
  }
@@ -344,22 +437,29 @@ function scanSourceFile(sourcePath) {
344
437
  const targetPath = firstArg ? resolveExpression(firstArg, sourceFile, inheritedConstants) : undefined;
345
438
  const resolved = classifyTarget(targetPath);
346
439
  const location = formatLocation(sourceFile, node);
440
+ const topology = classifyTopology(sourcePath, currentFunction, currentFunctionIsAsync, call, resolved);
347
441
  sites.push({
348
- call: name,
442
+ call,
349
443
  classification: resolved.classification,
350
444
  file: normalizeAuditPath(relative(PACKAGE_ROOT, sourcePath)),
351
445
  line: location.line,
352
446
  column: location.column,
447
+ enclosing_function: currentFunction,
448
+ enclosing_function_is_async: currentFunctionIsAsync,
353
449
  target_expression: firstArg ? firstArg.getText(sourceFile) : "",
354
450
  target_path: resolved.canonicalTarget,
355
451
  fallback_keys: resolved.fallbackKeys,
356
452
  bypasses_acepack: resolved.bypassesAcepack,
357
453
  regenerable_from_store: resolved.regenerableFromStore,
454
+ topology: topology.flags,
455
+ topology_classification: topology.classification,
456
+ blocking: topology.blocking,
457
+ failure_condition: topology.failure_condition,
358
458
  note: resolved.note,
359
459
  });
360
460
  }
361
461
  }
362
- ts.forEachChild(node, (child) => scanNode(child, inheritedConstants, currentFunction));
462
+ ts.forEachChild(node, (child) => scanNode(child, inheritedConstants, currentFunction, currentFunctionIsAsync));
363
463
  };
364
464
  const fileConstants = collectResolvableConstants(sourceFile, sourceFile, new Map());
365
465
  ts.forEachChild(sourceFile, (child) => scanNode(child, fileConstants));
@@ -377,11 +477,14 @@ export function auditStoreAuthority() {
377
477
  const classifications = groupByClassification(safeWriteSites);
378
478
  const runtimeFilesWithoutStoreFallbackKeys = safeWriteSites.filter((site) => site.bypasses_acepack);
379
479
  const nonRegenerableProjections = safeWriteSites.filter((site) => site.classification === "projection only" && !site.regenerable_from_store);
480
+ const blockingSites = safeWriteSites.filter((site) => site.blocking);
380
481
  const report_path = STORE_AUTHORITY_AUDIT_REPORT_REL_PATH;
381
482
  return {
382
483
  bypassing_sites: runtimeFilesWithoutStoreFallbackKeys,
484
+ blocking_sites: blockingSites,
383
485
  classifications,
384
486
  generated_at: new Date().toISOString(),
487
+ topology_failure: blockingSites.length > 0,
385
488
  non_regenerable_projections: nonRegenerableProjections,
386
489
  report_path,
387
490
  runtime_files_without_store_fallback_keys: runtimeFilesWithoutStoreFallbackKeys,
@@ -408,6 +511,9 @@ function renderSites(label, sites) {
408
511
  lines.push(` - target: ${site.target_path ? `\`${site.target_path}\`` : "unresolved"}`);
409
512
  lines.push(` - fallback keys: ${site.fallback_keys.length > 0 ? site.fallback_keys.map((key) => `\`${key}\``).join(", ") : "none"}`);
410
513
  lines.push(` - classification: \`${site.classification}\``);
514
+ lines.push(` - topology: \`${site.topology_classification}\``);
515
+ lines.push(` - flags: runtime_hot_path=${site.topology.runtime_hot_path}, async_handler=${site.topology.async_handler}, store_backed_possible=${site.topology.store_backed_possible}, allowed_offline_sync=${site.topology.allowed_offline_sync}, must_migrate_to_async=${site.topology.must_migrate_to_async}`);
516
+ lines.push(` - failure condition: ${site.failure_condition}`);
411
517
  lines.push(` - note: ${site.note}`);
412
518
  }
413
519
  return lines;
@@ -422,12 +528,16 @@ export function renderStoreAuthorityAuditReport(result) {
422
528
  "",
423
529
  "## Summary",
424
530
  `- bypassing ACEPACK: ${result.bypassing_sites.length}`,
531
+ `- blocking runtime hot-path writers: ${result.blocking_sites.length}`,
532
+ `- topology failure: ${result.topology_failure ? "yes" : "no"}`,
425
533
  `- runtime files without store fallback keys: ${result.runtime_files_without_store_fallback_keys.length}`,
426
534
  `- non-regenerable projections: ${result.non_regenerable_projections.length}`,
427
535
  `- canonical knowledge artifacts: ${result.classifications["canonical knowledge artifact"].length}`,
428
536
  `- canonical runtime state writers: ${result.classifications["canonical runtime state"].length}`,
429
537
  `- projection-only writers: ${result.classifications["projection only"].length}`,
430
538
  "",
539
+ ...renderSites("Blocking Runtime Hot-Path Writers", result.blocking_sites),
540
+ "",
431
541
  ...renderSites("Writers Bypassing ACEPACK", result.bypassing_sites),
432
542
  "",
433
543
  ...renderSites("Runtime Files Without Store Fallback Keys", result.runtime_files_without_store_fallback_keys),
@@ -1,3 +1,6 @@
1
+ export declare function invalidateStoreSnapshotCache(storePath?: string): void;
2
+ export declare function getCanonicalWorkspaceStorePath(workspaceRoot: string): string;
3
+ export declare function getLegacyWorkspaceStorePath(workspaceRoot: string): string;
1
4
  export declare function getWorkspaceStorePath(workspaceRoot: string): string;
2
5
  export declare function storeExistsSync(workspaceRoot: string): boolean;
3
6
  export declare function readStoreBlobSync(workspaceRoot: string, key: string): string | undefined;
@@ -3,6 +3,13 @@ import { resolve } from "node:path";
3
3
  import { HEADER_SIZE, MAGIC } from "./types.js";
4
4
  const DECODE = new TextDecoder();
5
5
  let cached;
6
+ export function invalidateStoreSnapshotCache(storePath) {
7
+ if (!cached)
8
+ return;
9
+ if (!storePath || cached.path === storePath) {
10
+ cached = undefined;
11
+ }
12
+ }
6
13
  function bytesToUint32(bytes, offset = 0) {
7
14
  return new DataView(bytes.buffer, bytes.byteOffset).getUint32(offset, false);
8
15
  }
@@ -47,12 +54,25 @@ function loadSnapshot(storePath) {
47
54
  return cached;
48
55
  }
49
56
  catch {
50
- return cached?.path === storePath ? cached : undefined;
57
+ invalidateStoreSnapshotCache(storePath);
58
+ return undefined;
51
59
  }
52
60
  }
53
- export function getWorkspaceStorePath(workspaceRoot) {
61
+ export function getCanonicalWorkspaceStorePath(workspaceRoot) {
62
+ return resolve(workspaceRoot, "agent-state", "ace-state.ace");
63
+ }
64
+ export function getLegacyWorkspaceStorePath(workspaceRoot) {
54
65
  return resolve(workspaceRoot, ".agents", "ACE", "ace-state.ace");
55
66
  }
67
+ export function getWorkspaceStorePath(workspaceRoot) {
68
+ const canonicalPath = getCanonicalWorkspaceStorePath(workspaceRoot);
69
+ if (existsSync(canonicalPath))
70
+ return canonicalPath;
71
+ const legacyPath = getLegacyWorkspaceStorePath(workspaceRoot);
72
+ if (existsSync(legacyPath))
73
+ return legacyPath;
74
+ return canonicalPath;
75
+ }
56
76
  export function storeExistsSync(workspaceRoot) {
57
77
  return existsSync(getWorkspaceStorePath(workspaceRoot));
58
78
  }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * workspace-store-paths.ts
3
+ *
4
+ * Central resolver and adoption helper for the ACE canonical/legacy store transition.
5
+ *
6
+ * Contract:
7
+ * - canonical: agent-state/ace-state.ace (new and adopted workspaces)
8
+ * - legacy: .agents/ACE/ace-state.ace (older workspaces; supported input, not a write target)
9
+ *
10
+ * Every command that opens ACEPACK must use this module. No command should
11
+ * assemble .agents/ACE/ace-state.ace by hand.
12
+ */
13
+ export interface WorkspaceStoreResolution {
14
+ storePath: string;
15
+ canonicalPath: string;
16
+ legacyPath: string;
17
+ mode: "canonical" | "legacy_fallback" | "adopted_legacy" | "dual_store_conflict" | "missing";
18
+ warnings: string[];
19
+ }
20
+ /**
21
+ * Read-safe, non-mutating resolver. Reports the active store path and whether
22
+ * legacy also exists. Does not copy or seed any files.
23
+ */
24
+ export declare function resolveWorkspaceStorePath(workspaceRoot: string): WorkspaceStoreResolution;
25
+ /**
26
+ * Mutating resolver. Use before any command that writes to the store.
27
+ *
28
+ * Adoption behaviour:
29
+ * - canonical only → return canonical
30
+ * - canonical + legacy identical → return canonical with low-severity warning
31
+ * - canonical + legacy differ → return dual_store_conflict (caller decides how to handle)
32
+ * - legacy only → copy legacy to canonical atomically, return adopted_legacy
33
+ * - neither → return missing (caller seeds a fresh store)
34
+ */
35
+ export declare function ensureCanonicalWorkspaceStore(workspaceRoot: string, options?: {
36
+ operationLabel?: string;
37
+ allowDualStoreConflict?: boolean;
38
+ }): Promise<WorkspaceStoreResolution>;
39
+ //# sourceMappingURL=workspace-store-paths.d.ts.map
@@ -0,0 +1,94 @@
1
+ /**
2
+ * workspace-store-paths.ts
3
+ *
4
+ * Central resolver and adoption helper for the ACE canonical/legacy store transition.
5
+ *
6
+ * Contract:
7
+ * - canonical: agent-state/ace-state.ace (new and adopted workspaces)
8
+ * - legacy: .agents/ACE/ace-state.ace (older workspaces; supported input, not a write target)
9
+ *
10
+ * Every command that opens ACEPACK must use this module. No command should
11
+ * assemble .agents/ACE/ace-state.ace by hand.
12
+ */
13
+ import { existsSync, readFileSync, copyFileSync, mkdirSync } from "node:fs";
14
+ import { dirname } from "node:path";
15
+ import { getCanonicalWorkspaceStorePath, getLegacyWorkspaceStorePath, } from "./store-snapshot.js";
16
+ import { withStoreWriteCoordinator } from "./write-coordinator.js";
17
+ /**
18
+ * Read-safe, non-mutating resolver. Reports the active store path and whether
19
+ * legacy also exists. Does not copy or seed any files.
20
+ */
21
+ export function resolveWorkspaceStorePath(workspaceRoot) {
22
+ const canonicalPath = getCanonicalWorkspaceStorePath(workspaceRoot);
23
+ const legacyPath = getLegacyWorkspaceStorePath(workspaceRoot);
24
+ const hasCanonical = existsSync(canonicalPath);
25
+ const hasLegacy = existsSync(legacyPath);
26
+ const warnings = [];
27
+ if (hasCanonical && hasLegacy) {
28
+ const canonicalBytes = readFileSync(canonicalPath);
29
+ const legacyBytes = readFileSync(legacyPath);
30
+ const identical = canonicalBytes.equals(legacyBytes);
31
+ if (identical) {
32
+ warnings.push(`Legacy store ${legacyPath} is byte-identical to canonical store. Legacy is redundant and can be removed.`);
33
+ return { storePath: canonicalPath, canonicalPath, legacyPath, mode: "canonical", warnings };
34
+ }
35
+ warnings.push(`Dual ACE stores detected. Canonical: ${canonicalPath} | Legacy: ${legacyPath}. They differ — use 'ace repair' to resolve.`);
36
+ return {
37
+ storePath: canonicalPath,
38
+ canonicalPath,
39
+ legacyPath,
40
+ mode: "dual_store_conflict",
41
+ warnings,
42
+ };
43
+ }
44
+ if (hasCanonical) {
45
+ return { storePath: canonicalPath, canonicalPath, legacyPath, mode: "canonical", warnings };
46
+ }
47
+ if (hasLegacy) {
48
+ return {
49
+ storePath: legacyPath,
50
+ canonicalPath,
51
+ legacyPath,
52
+ mode: "legacy_fallback",
53
+ warnings,
54
+ };
55
+ }
56
+ return { storePath: canonicalPath, canonicalPath, legacyPath, mode: "missing", warnings };
57
+ }
58
+ /**
59
+ * Mutating resolver. Use before any command that writes to the store.
60
+ *
61
+ * Adoption behaviour:
62
+ * - canonical only → return canonical
63
+ * - canonical + legacy identical → return canonical with low-severity warning
64
+ * - canonical + legacy differ → return dual_store_conflict (caller decides how to handle)
65
+ * - legacy only → copy legacy to canonical atomically, return adopted_legacy
66
+ * - neither → return missing (caller seeds a fresh store)
67
+ */
68
+ export async function ensureCanonicalWorkspaceStore(workspaceRoot, options) {
69
+ const label = options?.operationLabel ?? "ensureCanonicalWorkspaceStore";
70
+ const resolution = resolveWorkspaceStorePath(workspaceRoot);
71
+ if (resolution.mode === "legacy_fallback") {
72
+ // Adopt: copy legacy into canonical atomically under write coordinator.
73
+ const { canonicalPath, legacyPath } = resolution;
74
+ await withStoreWriteCoordinator(canonicalPath, async () => {
75
+ // Re-check after acquiring the write lock — another process may have adopted already.
76
+ if (!existsSync(canonicalPath)) {
77
+ mkdirSync(dirname(canonicalPath), { recursive: true });
78
+ copyFileSync(legacyPath, canonicalPath);
79
+ }
80
+ }, { operation_label: `${label}:adopt_legacy` });
81
+ const warnings = [
82
+ `Legacy store adopted: copied ${legacyPath} → ${canonicalPath}. Legacy file retained as fallback.`,
83
+ ];
84
+ return {
85
+ storePath: canonicalPath,
86
+ canonicalPath,
87
+ legacyPath,
88
+ mode: "adopted_legacy",
89
+ warnings,
90
+ };
91
+ }
92
+ return resolution;
93
+ }
94
+ //# sourceMappingURL=workspace-store-paths.js.map
@@ -0,0 +1,65 @@
1
+ export interface StoreWriteCoordinatorOptions {
2
+ agent_id?: string;
3
+ emit_lifecycle_events?: boolean;
4
+ lifecycle_sink?: (event: StoreWriteLifecycleEvent) => void;
5
+ operation_label?: string;
6
+ wait_ms?: number;
7
+ poll_interval_ms?: number;
8
+ lease_ttl_ms?: number;
9
+ stale_grace_ms?: number;
10
+ }
11
+ export interface StoreWriteLeaseInfo {
12
+ token: string;
13
+ storePath: string;
14
+ lockPath: string;
15
+ metadataPath: string;
16
+ pid: number;
17
+ hostname: string;
18
+ agentId?: string;
19
+ operationLabel?: string;
20
+ acquiredAt: string;
21
+ expiresAt: string;
22
+ ttlMs: number;
23
+ waitedMs: number;
24
+ }
25
+ export type StoreWriteLifecycleEventType = "store-write-requested" | "store-write-accepted" | "store-write-started" | "store-write-committed" | "store-write-blocked" | "store-write-failed";
26
+ export interface StoreWriteLifecycleEvent {
27
+ event: StoreWriteLifecycleEventType;
28
+ storePath: string;
29
+ operationLabel?: string;
30
+ hostname: string;
31
+ pid: number;
32
+ timestamp: string;
33
+ waitedMs?: number;
34
+ currentHolder?: StoreWriteLeaseInfo;
35
+ error?: string;
36
+ asyncOwnership?: {
37
+ count: number;
38
+ operationLabels: string[];
39
+ };
40
+ }
41
+ export declare class StoreWriteLeaseBusyError extends Error {
42
+ readonly storePath: string;
43
+ readonly lockPath: string;
44
+ readonly waitedMs: number;
45
+ readonly currentHolder?: StoreWriteLeaseInfo;
46
+ constructor(input: {
47
+ storePath: string;
48
+ lockPath: string;
49
+ waitedMs: number;
50
+ currentHolder?: StoreWriteLeaseInfo;
51
+ });
52
+ }
53
+ export declare class StoreSyncWriteBlockedByAsyncLeaseError extends Error {
54
+ readonly code = "STORE_SYNC_WRITE_BLOCKED_BY_ASYNC_LEASE";
55
+ readonly storePath: string;
56
+ readonly currentAsyncOperationLabels: string[];
57
+ constructor(input: {
58
+ storePath: string;
59
+ currentAsyncOperationLabels: string[];
60
+ });
61
+ }
62
+ export declare function withStoreWriteCoordinator<T>(storePath: string, fn: () => Promise<T>, options?: StoreWriteCoordinatorOptions): Promise<T>;
63
+ export declare function withStoreWriteCoordinatorSync<T>(storePath: string, fn: () => T, options?: StoreWriteCoordinatorOptions): T;
64
+ export declare function resolveStoreWriteLeasePath(workspaceRoot: string): string;
65
+ //# sourceMappingURL=write-coordinator.d.ts.map