@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
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { z } from "zod";
6
6
  import { bootstrapStoreWorkspace } from "./store/bootstrap-store.js";
7
- import { ACE_ROOT_REL, ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_LLM_PROVIDERS, ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, WORKSPACE_ROOT, classifyPathSource, detectAssetDrift, getAllMcpServerConfigSnippets, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, getMcpClientInstallHint, getMcpServerConfigSnippet, getTaskArtifactPath, isSwarmRole, listAvailableSkills, normalizePathForValidation, resolveWorkspaceRoot, safeRead, safeWrite, withFileLock, wsPath, } from "./helpers.js";
7
+ import { ACE_ROOT_REL, ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_LLM_PROVIDERS, ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, WORKSPACE_ROOT, classifyPathSource, detectAssetDrift, getAllMcpServerConfigSnippets, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, getMcpClientInstallHint, getMcpServerConfigSnippet, getTaskArtifactPath, isSwarmRole, listAvailableSkills, normalizePathForValidation, resolveWorkspaceRoot, safeRead, safeWriteAsync, withFileLock, wsPath, } from "./helpers.js";
8
8
  import { getRoleTitle, MCP_CLIENT_ENUM, scoreDomains, } from "./shared.js";
9
9
  import { defaultModelForProvider, } from "./tui/provider-discovery.js";
10
10
  import { refreshAstgrepIndex } from "./astgrep-index.js";
@@ -21,6 +21,21 @@ import { auditStoreAuthority, writeStoreAuthorityAuditReport, } from "./store/st
21
21
  import { PROVENANCE_CRITICAL_EVENT_TYPES, validateArtifactManifestPayload, validateProvenanceLogContent, validateTealConfigContent, } from "./schemas.js";
22
22
  import { readAceTaskContractAssessment } from "./ace-autonomy.js";
23
23
  import { listStoreKeysSync, readStoreBlobSync } from "./store/store-snapshot.js";
24
+ import { parseJsonLikeText } from "./json-sanitizer.js";
25
+ function executionRoleForDomain(domain) {
26
+ switch (domain) {
27
+ case "venture":
28
+ return "vos";
29
+ case "ux":
30
+ return "ui";
31
+ case "engineering":
32
+ return "coders";
33
+ case "research":
34
+ return "research";
35
+ default:
36
+ return "orchestrator";
37
+ }
38
+ }
24
39
  function getArtifactManifestEntries(payload) {
25
40
  if (!payload || typeof payload !== "object" || Array.isArray(payload))
26
41
  return [];
@@ -61,6 +76,14 @@ function parseGateManifest(raw, sourceRef) {
61
76
  return undefined;
62
77
  }
63
78
  }
79
+ function parseJsonForValidation(raw) {
80
+ return parseJsonLikeText(raw);
81
+ }
82
+ function jsonValidationDetail(parsed, validDetail = "valid JSON") {
83
+ return parsed.sanitized.removed_control_bytes > 0
84
+ ? `control bytes removed before parse (${parsed.sanitized.removed_control_bytes}); source requires repair`
85
+ : validDetail;
86
+ }
64
87
  function readGateManifests(gatesDir) {
65
88
  const files = readdirSync(gatesDir).filter((f) => f.endsWith(".json"));
66
89
  const allGates = [];
@@ -287,7 +310,7 @@ async function appendEvidenceLogEntrySafe(review) {
287
310
  const timestamp = new Date().toISOString();
288
311
  const anchor = `#ts:${timestamp}`;
289
312
  const evidenceRef = `agent-state/EVIDENCE_LOG.md${anchor}`;
290
- return withFileLock("agent-state/EVIDENCE_LOG.md", () => {
313
+ return withFileLock("agent-state/EVIDENCE_LOG.md", async () => {
291
314
  const existing = safeRead("agent-state/EVIDENCE_LOG.md");
292
315
  const seed = existing.startsWith("[FILE NOT FOUND]") || existing.startsWith("[ACCESS DENIED]")
293
316
  ? "# EVIDENCE LOG\n\nAppend-only validation evidence.\n\n## Entries\n"
@@ -319,7 +342,7 @@ async function appendEvidenceLogEntrySafe(review) {
319
342
  })
320
343
  : [" - none"]),
321
344
  ];
322
- const path = safeWrite("agent-state/EVIDENCE_LOG.md", `${seed}${lines.join("\n")}\n`);
345
+ const path = await safeWriteAsync("agent-state/EVIDENCE_LOG.md", `${seed}${lines.join("\n")}\n`);
323
346
  return { path, evidenceRef };
324
347
  });
325
348
  }
@@ -534,6 +557,12 @@ export function registerFrameworkTools(server) {
534
557
  recommendedSubagents.add("memory");
535
558
  }
536
559
  return {
560
+ structuredContent: {
561
+ detected_domain: detected,
562
+ suggested_execution_role: executionRoleForDomain(detected),
563
+ default_entry_agent: "orchestrator",
564
+ task_contract_ok: taskContract.ok,
565
+ },
537
566
  content: [
538
567
  {
539
568
  type: "text",
@@ -635,7 +664,7 @@ export function registerFrameworkTools(server) {
635
664
  model: resolvedLlmModel ?? undefined,
636
665
  baseUrl: resolvedLlmBaseUrl ?? undefined,
637
666
  });
638
- const astIndex = refreshAstgrepIndex({
667
+ const astIndex = await refreshAstgrepIndex({
639
668
  scope: ".",
640
669
  append_evidence: true,
641
670
  emit_event: true,
@@ -662,7 +691,7 @@ export function registerFrameworkTools(server) {
662
691
  category: "major_update",
663
692
  message: "Bootstrapped ACE workspace state",
664
693
  artifacts: [
665
- `${ACE_ROOT_REL}/ace-state.ace`,
694
+ storeResult.storePath,
666
695
  `${ACE_TASKS_ROOT_REL}/todo.md`,
667
696
  normalizePathForValidation(delta.index_path),
668
697
  "agent-state/AST_GREP_INDEX.json",
@@ -717,7 +746,10 @@ export function registerFrameworkTools(server) {
717
746
  "",
718
747
  "## LLM Runtime Profile",
719
748
  `- provider: ${llm_provider}`,
720
- `- model: ${resolvedLlmModel ?? defaultModelForProvider(llm_provider)}`,
749
+ `- model: ${resolvedLlmModel ??
750
+ (llm_provider === "llama.cpp"
751
+ ? "(set via ace connect or ace doctor --scan)"
752
+ : defaultModelForProvider(llm_provider))}`,
721
753
  `- base_url: ${resolvedLlmBaseUrl ??
722
754
  (llm_provider === "ollama" || llm_provider === "llama.cpp"
723
755
  ? "discover via ace doctor --scan or set explicitly"
@@ -990,58 +1022,60 @@ export function registerFrameworkTools(server) {
990
1022
  // Check: handoff registry parseable
991
1023
  const hrRaw = safeRead("agent-state/handoff-registry.json");
992
1024
  const hrOk = !hrRaw.startsWith("[FILE NOT FOUND]");
1025
+ let hrParsed;
993
1026
  if (hrOk) {
994
- try {
995
- JSON.parse(hrRaw);
1027
+ const parsed = parseJsonForValidation(hrRaw);
1028
+ if (parsed.ok) {
1029
+ hrParsed = parsed.value;
996
1030
  checks.push({
997
1031
  name: "handoff-registry:parse",
998
- ok: true,
999
- detail: "valid JSON",
1032
+ ok: parsed.sanitized.removed_control_bytes === 0,
1033
+ detail: jsonValidationDetail(parsed),
1000
1034
  });
1001
1035
  }
1002
- catch {
1036
+ else {
1003
1037
  checks.push({
1004
1038
  name: "handoff-registry:parse",
1005
1039
  ok: false,
1006
- detail: "CORRUPT JSON",
1040
+ detail: `agent-state/handoff-registry.json: ${parsed.error}`,
1007
1041
  });
1008
1042
  }
1009
1043
  }
1010
1044
  // Check: run-ledger parseable
1011
1045
  const rlRaw = safeRead("agent-state/run-ledger.json");
1012
1046
  if (!rlRaw.startsWith("[FILE NOT FOUND]")) {
1013
- try {
1014
- JSON.parse(rlRaw);
1047
+ const parsed = parseJsonForValidation(rlRaw);
1048
+ if (parsed.ok) {
1015
1049
  checks.push({
1016
1050
  name: "run-ledger:parse",
1017
- ok: true,
1018
- detail: "valid JSON",
1051
+ ok: parsed.sanitized.removed_control_bytes === 0,
1052
+ detail: jsonValidationDetail(parsed),
1019
1053
  });
1020
1054
  }
1021
- catch {
1055
+ else {
1022
1056
  checks.push({
1023
1057
  name: "run-ledger:parse",
1024
1058
  ok: false,
1025
- detail: "CORRUPT JSON",
1059
+ detail: `agent-state/run-ledger.json: ${parsed.error}`,
1026
1060
  });
1027
1061
  }
1028
1062
  }
1029
1063
  // Check: todo-state parseable
1030
1064
  const tsRaw = safeRead("agent-state/todo-state.json");
1031
1065
  if (!tsRaw.startsWith("[FILE NOT FOUND]")) {
1032
- try {
1033
- JSON.parse(tsRaw);
1066
+ const parsed = parseJsonForValidation(tsRaw);
1067
+ if (parsed.ok) {
1034
1068
  checks.push({
1035
1069
  name: "todo-state:parse",
1036
- ok: true,
1037
- detail: "valid JSON",
1070
+ ok: parsed.sanitized.removed_control_bytes === 0,
1071
+ detail: jsonValidationDetail(parsed),
1038
1072
  });
1039
1073
  }
1040
- catch {
1074
+ else {
1041
1075
  checks.push({
1042
1076
  name: "todo-state:parse",
1043
1077
  ok: false,
1044
- detail: "CORRUPT JSON",
1078
+ detail: `agent-state/todo-state.json: ${parsed.error}`,
1045
1079
  });
1046
1080
  }
1047
1081
  }
@@ -1061,19 +1095,19 @@ export function registerFrameworkTools(server) {
1061
1095
  });
1062
1096
  continue;
1063
1097
  }
1064
- try {
1065
- JSON.parse(raw);
1098
+ const parsed = parseJsonForValidation(raw);
1099
+ if (parsed.ok) {
1066
1100
  checks.push({
1067
1101
  name: check.name,
1068
- ok: true,
1069
- detail: "valid JSON",
1102
+ ok: parsed.sanitized.removed_control_bytes === 0,
1103
+ detail: jsonValidationDetail(parsed),
1070
1104
  });
1071
1105
  }
1072
- catch {
1106
+ else {
1073
1107
  checks.push({
1074
1108
  name: check.name,
1075
1109
  ok: false,
1076
- detail: "CORRUPT JSON",
1110
+ detail: `${check.rel}: ${parsed.error}`,
1077
1111
  });
1078
1112
  }
1079
1113
  }
@@ -1127,25 +1161,28 @@ export function registerFrameworkTools(server) {
1127
1161
  const artifactManifestRaw = safeRead("agent-state/ARTIFACT_MANIFEST.json");
1128
1162
  let artifactManifestEntries = [];
1129
1163
  if (!artifactManifestRaw.startsWith("[FILE NOT FOUND]")) {
1130
- try {
1131
- const parsed = JSON.parse(artifactManifestRaw);
1132
- const validation = validateArtifactManifestPayload(parsed);
1164
+ const parsed = parseJsonForValidation(artifactManifestRaw);
1165
+ if (parsed.ok) {
1166
+ const validation = validateArtifactManifestPayload(parsed.value);
1167
+ const controlBytesOk = parsed.sanitized.removed_control_bytes === 0;
1133
1168
  checks.push({
1134
1169
  name: "artifact-manifest:schema",
1135
- ok: validation.ok,
1136
- detail: validation.ok
1137
- ? `valid (${validation.schema})`
1138
- : validation.errors.join("; "),
1170
+ ok: validation.ok && controlBytesOk,
1171
+ detail: !controlBytesOk
1172
+ ? jsonValidationDetail(parsed)
1173
+ : validation.ok
1174
+ ? `valid (${validation.schema})`
1175
+ : validation.errors.join("; "),
1139
1176
  });
1140
1177
  if (validation.ok) {
1141
- artifactManifestEntries = getArtifactManifestEntries(parsed);
1178
+ artifactManifestEntries = getArtifactManifestEntries(parsed.value);
1142
1179
  }
1143
1180
  }
1144
- catch {
1181
+ else {
1145
1182
  checks.push({
1146
1183
  name: "artifact-manifest:parse",
1147
1184
  ok: false,
1148
- detail: "CORRUPT JSON",
1185
+ detail: `agent-state/ARTIFACT_MANIFEST.json: ${parsed.error}`,
1149
1186
  });
1150
1187
  }
1151
1188
  }
@@ -1183,8 +1220,16 @@ export function registerFrameworkTools(server) {
1183
1220
  // Check: module registry role coverage vs event emitters
1184
1221
  const registryRaw = safeRead("agent-state/MODULES/registry.json");
1185
1222
  if (!registryRaw.startsWith("[FILE NOT FOUND]")) {
1186
- try {
1187
- const registry = JSON.parse(registryRaw);
1223
+ const parsed = parseJsonForValidation(registryRaw);
1224
+ if (parsed.ok) {
1225
+ if (parsed.sanitized.removed_control_bytes > 0) {
1226
+ checks.push({
1227
+ name: "registry:parse",
1228
+ ok: false,
1229
+ detail: jsonValidationDetail(parsed),
1230
+ });
1231
+ }
1232
+ const registry = parsed.value;
1188
1233
  const registeredRoles = new Set(registry.roles ?? []);
1189
1234
  const expectedEmitters = [
1190
1235
  "capability-safety",
@@ -1203,11 +1248,11 @@ export function registerFrameworkTools(server) {
1203
1248
  });
1204
1249
  }
1205
1250
  }
1206
- catch {
1251
+ else {
1207
1252
  checks.push({
1208
1253
  name: "registry:parse",
1209
1254
  ok: false,
1210
- detail: "registry.json is corrupt",
1255
+ detail: `agent-state/MODULES/registry.json: ${parsed.error}`,
1211
1256
  });
1212
1257
  }
1213
1258
  }
@@ -1228,30 +1273,24 @@ export function registerFrameworkTools(server) {
1228
1273
  });
1229
1274
  }
1230
1275
  // Check: handoff consistency (open handoffs have source files)
1231
- if (hrOk) {
1232
- try {
1233
- const registry = JSON.parse(hrRaw);
1234
- const handoffs = Object.values(registry.handoffs ?? {});
1235
- let openWithoutFile = 0;
1236
- for (const h of handoffs) {
1237
- if ((h.status === "open" || h.status === "accepted") &&
1238
- h.source_file) {
1239
- const fileContent = safeRead(h.source_file);
1240
- if (fileContent.startsWith("[FILE NOT FOUND]"))
1241
- openWithoutFile++;
1242
- }
1276
+ if (hrOk && hrParsed) {
1277
+ const handoffs = Object.values(hrParsed.handoffs ?? {});
1278
+ let openWithoutFile = 0;
1279
+ for (const h of handoffs) {
1280
+ if ((h.status === "open" || h.status === "accepted") &&
1281
+ h.source_file) {
1282
+ const fileContent = safeRead(h.source_file);
1283
+ if (fileContent.startsWith("[FILE NOT FOUND]"))
1284
+ openWithoutFile++;
1243
1285
  }
1244
- checks.push({
1245
- name: "handoff:source-file-integrity",
1246
- ok: openWithoutFile === 0,
1247
- detail: openWithoutFile === 0
1248
- ? "all open handoffs have source files"
1249
- : `${openWithoutFile} open handoffs reference missing source files`,
1250
- });
1251
- }
1252
- catch {
1253
- /* already caught above */
1254
1286
  }
1287
+ checks.push({
1288
+ name: "handoff:source-file-integrity",
1289
+ ok: openWithoutFile === 0,
1290
+ detail: openWithoutFile === 0
1291
+ ? "all open handoffs have source files"
1292
+ : `${openWithoutFile} open handoffs reference missing source files`,
1293
+ });
1255
1294
  }
1256
1295
  // Check: event provenance gaps in recent events.
1257
1296
  // Scans for provenance-critical event types that are missing
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { z } from "zod";
6
- import { ACE_TASKS_ROOT_REL, normalizePathForValidation, readTaskArtifact, safeWrite, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
6
+ import { ACE_TASKS_ROOT_REL, normalizePathForValidation, readTaskArtifact, safeWriteAsync, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
7
7
  import { ROLE_ENUM, HANDOFF_VALIDATION_MODE, HANDOFF_LIFECYCLE_STATUS_ENUM, } from "./shared.js";
8
8
  import { validateHandoffPayload, validateSwarmHandoffPayload, lintHandoffPayload, } from "./schemas.js";
9
9
  import { acknowledgeHandoffSafe, getHandoffRegistryPath, readHandoffRegistry, registerHandoffSafe, } from "./handoff-registry.js";
@@ -247,7 +247,7 @@ export function registerHandoffTools(server) {
247
247
  .replace("T", "-")
248
248
  .slice(0, 15);
249
249
  const filePath = wsPath("tasks", `SWARM_HANDOFF.${from}_to_${to}_${timeStr}_${shortId}.json`);
250
- safeWrite(filePath, JSON.stringify(handoff, null, 2));
250
+ await safeWriteAsync(filePath, JSON.stringify(handoff, null, 2));
251
251
  const registry = await registerHandoffSafe({
252
252
  handoff_id: handoff.handoff_id,
253
253
  from: handoff.router.from,
@@ -2,8 +2,8 @@
2
2
  * Lifecycle tool registrations: status events, circuit breaker, and kanban.
3
3
  */
4
4
  import { z } from "zod";
5
- import { safeRead, safeWrite } from "./helpers.js";
6
- import { appendStatusEventSafe, getStatusEventsPath, readStatusEvents, } from "./status-events.js";
5
+ import { safeRead, safeWriteAsync } from "./helpers.js";
6
+ import { appendStatusEventSafe, getStatusEventsPath, readStatusEvents } from "./status-events.js";
7
7
  import { refreshKanbanArtifacts } from "./kanban.js";
8
8
  export function registerLifecycleTools(server) {
9
9
  server.tool("emit_status_event", "Append a schema-valid lifecycle event into agent-state/STATUS_EVENTS.ndjson", {
@@ -119,7 +119,7 @@ export function registerLifecycleTools(server) {
119
119
  ? "# STATUS\n\n"
120
120
  : current.trimEnd();
121
121
  const next = `${seed}\n- [${timestamp}] CIRCUIT_BREAKER_OPENED: ${reason}${owner ? ` (owner: ${owner})` : ""}\n`;
122
- const statusPath = safeWrite("agent-state/STATUS.md", next);
122
+ const statusPath = await safeWriteAsync("agent-state/STATUS.md", next);
123
123
  const event = await appendStatusEventSafe({
124
124
  source_module: "capability-ops",
125
125
  event_type: "CIRCUIT_BREAKER_OPENED",
@@ -158,7 +158,7 @@ export function registerLifecycleTools(server) {
158
158
  ? "# STATUS\n\n"
159
159
  : current.trimEnd();
160
160
  const next = `${seed}\n- [${timestamp}] CIRCUIT_BREAKER_CLOSED: ${reason}\n`;
161
- const statusPath = safeWrite("agent-state/STATUS.md", next);
161
+ const statusPath = await safeWriteAsync("agent-state/STATUS.md", next);
162
162
  const event = await appendStatusEventSafe({
163
163
  source_module: "capability-ops",
164
164
  event_type: "CIRCUIT_BREAKER_CLOSED",
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { existsSync } from "node:fs";
5
5
  import { z } from "zod";
6
- import { resolveWorkspaceRoot, safeRead, safeWrite, wsPath } from "./helpers.js";
6
+ import { resolveWorkspaceRoot, safeRead, safeWriteAsync, wsPath } from "./helpers.js";
7
7
  import { isReadError } from "./shared.js";
8
8
  import { appendRunLedgerEntrySafe } from "./run-ledger.js";
9
9
  import { appendStatusEventSafe } from "./status-events.js";
@@ -13,7 +13,7 @@ import { openStore } from "./store/ace-packed-store.js";
13
13
  import { ProjectionManager } from "./store/materializers/projection-manager.js";
14
14
  import { ContextSnapshotRepository } from "./store/repositories/context-snapshot-repository.js";
15
15
  import { getWorkspaceStorePath, storeExistsSync, toVirtualStorePath, } from "./store/store-snapshot.js";
16
- import { withStoreWriteQueue } from "./store/write-queue.js";
16
+ import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
17
17
  const CONTEXT_STORE_REL = "agent-state/context-snapshots";
18
18
  export function registerMemoryTools(server) {
19
19
  server.tool("context_snapshot", "Capture a named context snapshot for later retrieval (decisions, progress, state)", {
@@ -41,7 +41,7 @@ export function registerMemoryTools(server) {
41
41
  let path = wsPath(CONTEXT_STORE_REL, `${name}.json`);
42
42
  if (storeExistsSync(root) && existsSync(getWorkspaceStorePath(root))) {
43
43
  const storePath = getWorkspaceStorePath(root);
44
- const saved = await withStoreWriteQueue(storePath, async () => {
44
+ const saved = await withStoreWriteCoordinator(storePath, async () => {
45
45
  const store = await openStore(storePath);
46
46
  try {
47
47
  const repo = new ContextSnapshotRepository(store);
@@ -62,7 +62,7 @@ export function registerMemoryTools(server) {
62
62
  finally {
63
63
  await store.close();
64
64
  }
65
- });
65
+ }, { operation_label: "contextSnapshotSave" });
66
66
  path = toVirtualStorePath(storePath, `state/memory/context_snapshots/${saved.file}`);
67
67
  }
68
68
  else {
@@ -82,7 +82,7 @@ export function registerMemoryTools(server) {
82
82
  .slice(0, 48);
83
83
  const filename = `${slug}.json`;
84
84
  path = wsPath(CONTEXT_STORE_REL, filename);
85
- safeWrite(path, JSON.stringify(snapshot, null, 2));
85
+ await safeWriteAsync(path, JSON.stringify(snapshot, null, 2));
86
86
  const indexPath = wsPath(CONTEXT_STORE_REL, "index.json");
87
87
  const indexRaw = safeRead(indexPath);
88
88
  let index = { snapshots: [] };
@@ -97,7 +97,7 @@ export function registerMemoryTools(server) {
97
97
  index.snapshots = index.snapshots.filter((s) => s.name !== name);
98
98
  index.snapshots.push({ name, file: filename, timestamp, summary });
99
99
  index.snapshots.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
100
- safeWrite(indexPath, JSON.stringify(index, null, 2));
100
+ await safeWriteAsync(indexPath, JSON.stringify(index, null, 2));
101
101
  }
102
102
  const ledger = await appendRunLedgerEntrySafe({
103
103
  tool: "context_snapshot",
@@ -2,7 +2,7 @@
2
2
  * TODO, task management, and run-ledger tool registrations.
3
3
  */
4
4
  import { z } from "zod";
5
- import { ACE_TASKS_ROOT_REL, readTaskArtifact, resolveWritableTaskPath, safeWrite, } from "./helpers.js";
5
+ import { ACE_TASKS_ROOT_REL, readTaskArtifact, resolveWritableTaskPath, safeWriteAsync, } from "./helpers.js";
6
6
  import { TODO_STATUS_ENUM } from "./shared.js";
7
7
  import { getTodoStatePath, readTodoState, syncTodoStateSafe, transitionTodoNodeSafe, } from "./todo-state.js";
8
8
  import { appendRunLedgerEntrySafe, getRunLedgerPath, readRunLedger, } from "./run-ledger.js";
@@ -33,7 +33,7 @@ export function registerTodoTools(server) {
33
33
  }, async ({ new_content, node_id, status, note }) => {
34
34
  if (typeof new_content === "string") {
35
35
  const synced = await syncTodoStateSafe(new_content);
36
- const path = safeWrite(resolveWritableTaskPath("todo"), new_content);
36
+ const path = await safeWriteAsync(resolveWritableTaskPath("todo"), new_content);
37
37
  const runLedger = await appendRunLedgerEntrySafe({
38
38
  tool: "update_todo",
39
39
  category: "major_update",
@@ -67,7 +67,7 @@ export declare function validateTrackerSnapshotContent(raw: string): ValidationR
67
67
  export declare function loadTrackerSnapshot(explicitPath?: string): TrackerSnapshotResult;
68
68
  export declare function readTrackerSnapshot(): TrackerSnapshot;
69
69
  export declare function getTrackerSnapshotPath(): string;
70
- export declare function writeTrackerSnapshot(snapshot: TrackerSnapshot): string;
70
+ export declare function writeTrackerSnapshot(snapshot: TrackerSnapshot): Promise<string>;
71
71
  export declare function listTrackerAdapterKinds(): string[];
72
72
  export declare function getTrackerAdapter(kind?: string): IssueTrackerAdapter;
73
73
  export declare function buildDefaultTrackerSnapshot(kind?: TrackerAdapterKind): TrackerSnapshot;
@@ -1,11 +1,12 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { spawn } from "node:child_process";
3
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
4
- import { dirname, resolve } from "node:path";
3
+ import { existsSync, readFileSync, } from "node:fs";
4
+ import { resolve } from "node:path";
5
5
  import { DEFAULTS_ROOT, resolveWorkspaceArtifactPath as resolveWorkspaceArtifactPathHelper, resolveWorkspaceRoot, } from "./helpers.js";
6
6
  import { validateTrackerSnapshotPayload } from "./schemas.js";
7
- import { appendStatusEvent } from "./status-events.js";
7
+ import { appendStatusEventSafe } from "./status-events.js";
8
8
  import { readRuntimeProfile } from "./runtime-profile.js";
9
+ import { safeWriteAsync } from "./helpers.js";
9
10
  export const TRACKER_SNAPSHOT_REL_PATH = "agent-state/tracker-snapshot.json";
10
11
  export const TRACKER_SNAPSHOT_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/TRACKER_SNAPSHOT.schema.json";
11
12
  export const TRACKER_SNAPSHOT_SCHEMA_NAME = "tracker-snapshot@1.0.0";
@@ -40,14 +41,6 @@ function resolveTrackerSnapshotTarget(explicitPath) {
40
41
  function resolveWorkspaceArtifactPath(filePath) {
41
42
  return resolveWorkspaceArtifactPathHelper(filePath, "write");
42
43
  }
43
- function safeWriteWorkspaceFile(filePath, content) {
44
- const abs = resolveWorkspaceArtifactPath(filePath);
45
- mkdirSync(dirname(abs), { recursive: true });
46
- const tmpPath = `${abs}.${process.pid}.${Date.now()}.tmp`;
47
- writeFileSync(tmpPath, content, "utf-8");
48
- renameSync(tmpPath, abs);
49
- return abs;
50
- }
51
44
  function normalizeTrackerItem(item) {
52
45
  return {
53
46
  ...item,
@@ -150,12 +143,14 @@ function filterTrackerItems(items, filter) {
150
143
  }
151
144
  function emitTrackerEvent(eventType, summary, payload) {
152
145
  try {
153
- appendStatusEvent({
146
+ void appendStatusEventSafe({
154
147
  source_module: "capability-framework",
155
148
  event_type: eventType,
156
149
  status: eventType === "TRACKER_SNAPSHOT_REFRESH_FAILED" ? "fail" : "done",
157
150
  summary,
158
151
  payload,
152
+ }).catch(() => {
153
+ // Tracker operations must not fail because event logging failed.
159
154
  });
160
155
  }
161
156
  catch {
@@ -455,7 +450,7 @@ async function githubCreateComment(itemId, body) {
455
450
  }),
456
451
  ];
457
452
  nextSnapshot.updated_at = now;
458
- writeTrackerSnapshot(nextSnapshot);
453
+ await writeTrackerSnapshot(nextSnapshot);
459
454
  emitTrackerEvent("TRACKER_COMMENT_CREATED", `Tracker comment created for ${issue.item_id}.`, {
460
455
  adapter_kind: "external",
461
456
  provider: "github",
@@ -502,7 +497,7 @@ async function githubUpdateItemState(itemId, state) {
502
497
  try {
503
498
  const issue = (await githubGetIssuesById([itemId]))[0];
504
499
  const snapshot = upsertTrackerItem(readOrBuildExternalSnapshot(), issue);
505
- writeTrackerSnapshot(snapshot);
500
+ await writeTrackerSnapshot(snapshot);
506
501
  emitTrackerEvent("TRACKER_ITEM_STATE_UPDATED", `Tracker item ${issue.item_id} moved to ${issue.state}.`, {
507
502
  adapter_kind: "external",
508
503
  provider: "github",
@@ -569,13 +564,13 @@ export function readTrackerSnapshot() {
569
564
  export function getTrackerSnapshotPath() {
570
565
  return resolveTrackerSnapshotTarget().path;
571
566
  }
572
- export function writeTrackerSnapshot(snapshot) {
567
+ export async function writeTrackerSnapshot(snapshot) {
573
568
  const normalized = normalizeTrackerSnapshot(snapshot);
574
569
  const validation = validateTrackerSnapshotPayload(normalized);
575
570
  if (!validation.ok) {
576
571
  throw new Error(`Tracker snapshot failed validation (${validation.schema}): ${validation.errors.join("; ")}`);
577
572
  }
578
- return safeWriteWorkspaceFile(TRACKER_SNAPSHOT_REL_PATH, JSON.stringify(normalized, null, 2));
573
+ return safeWriteAsync(TRACKER_SNAPSHOT_REL_PATH, JSON.stringify(normalized, null, 2));
579
574
  }
580
575
  const noneTrackerAdapter = {
581
576
  kind: "none",
@@ -700,7 +695,7 @@ const memoryTrackerAdapter = {
700
695
  updated_at: now,
701
696
  comments: [...snapshot.comments, comment],
702
697
  };
703
- writeTrackerSnapshot(nextSnapshot);
698
+ await writeTrackerSnapshot(nextSnapshot);
704
699
  emitTrackerEvent("TRACKER_COMMENT_CREATED", `Tracker comment created for ${comment.item_id}.`, {
705
700
  adapter_kind: "memory",
706
701
  item_id: comment.item_id,
@@ -743,7 +738,7 @@ const memoryTrackerAdapter = {
743
738
  updated_at: now,
744
739
  items,
745
740
  };
746
- writeTrackerSnapshot(nextSnapshot);
741
+ await writeTrackerSnapshot(nextSnapshot);
747
742
  emitTrackerEvent("TRACKER_ITEM_STATE_UPDATED", `Tracker item ${items[itemIndex].item_id} moved to ${nextState}.`, {
748
743
  adapter_kind: "memory",
749
744
  item_id: items[itemIndex].item_id,
@@ -2,15 +2,17 @@ import { resolve } from "node:path";
2
2
  import { resolveWorkspaceRoot } from "./helpers.js";
3
3
  import { readRuntimeProfile } from "./runtime-profile.js";
4
4
  import { buildDefaultTrackerSnapshot, getTrackerAdapter, writeTrackerSnapshot, readTrackerSnapshot, TRACKER_SNAPSHOT_REL_PATH, } from "./tracker-adapters.js";
5
- import { appendStatusEvent } from "./status-events.js";
5
+ import { appendStatusEventSafe } from "./status-events.js";
6
6
  function emitTrackerSyncEvent(eventType, summary, payload) {
7
7
  try {
8
- appendStatusEvent({
8
+ void appendStatusEventSafe({
9
9
  source_module: "capability-framework",
10
10
  event_type: eventType,
11
11
  status: eventType === "TRACKER_SNAPSHOT_REFRESH_FAILED" ? "fail" : "done",
12
12
  summary,
13
13
  payload,
14
+ }).catch(() => {
15
+ // Snapshot refresh must not fail because observability logging failed.
14
16
  });
15
17
  }
16
18
  catch {
@@ -52,7 +54,7 @@ export async function refreshTrackerSnapshot(kind) {
52
54
  items,
53
55
  };
54
56
  }
55
- const writtenPath = writeTrackerSnapshot(snapshot);
57
+ const writtenPath = await writeTrackerSnapshot(snapshot);
56
58
  emitTrackerSyncEvent("TRACKER_SNAPSHOT_REFRESHED", `Tracker snapshot refreshed using ${snapshot.adapter_kind}.`, {
57
59
  adapter_kind: snapshot.adapter_kind,
58
60
  snapshot_path: writtenPath,
@@ -17,7 +17,9 @@ import { readAgentInstructions } from "../helpers.js";
17
17
  export class AgentRunner extends EventEmitter {
18
18
  agents = new Map();
19
19
  workspaceRoot;
20
- startupTimeoutMs = 5000;
20
+ // Full-suite test runs can heavily contend CPU/IO while workers bootstrap.
21
+ // Keep startup resilient to transient contention before declaring failure.
22
+ startupTimeoutMs = 12000;
21
23
  constructor(workspaceRoot) {
22
24
  super();
23
25
  this.workspaceRoot = workspaceRoot;