@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
@@ -0,0 +1,615 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { isIP } from "node:net";
3
+ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
4
+ import { dirname } from "node:path";
5
+ import { isReadError, normalizePathForValidation, resolveWorkspaceArtifactPath, resolveWorkspaceRoot, safeRead, safeWriteAsync, wsPath, } from "./helpers.js";
6
+ import { appendRunLedgerEntrySafe } from "./run-ledger.js";
7
+ import { runShellCommand } from "./runtime-command.js";
8
+ import { executeRuntimeTool, loadRuntimeToolRegistry, persistRuntimeToolInvocationArtifacts, } from "./runtime-tool-specs.js";
9
+ import { refreshAstgrepIndex } from "./astgrep-index.js";
10
+ const BLOCKED_HOSTNAMES = new Set(["localhost", "localhost.localdomain"]);
11
+ const SAFE_EXTERNAL_ENV_KEYS = new Set(["PATH", "HOME", "TMPDIR", "TEMP", "TMP", "USER", "LANG", "LC_ALL"]);
12
+ const DEFAULT_WEB_RESEARCH_TOOL = "web_research_packet";
13
+ const DEFAULT_CODEMUNCH_TOOL = "codemunch_snapshot";
14
+ const DEFAULT_CODEMUNCH_COMMAND = "npx -y codemunch";
15
+ const WEB_RESEARCH_MAX_CITATIONS = 12;
16
+ const WEB_RESEARCH_MAX_SEARCH_RESULTS = 20;
17
+ const WEB_RESEARCH_MAX_FETCH_DOCS = 10;
18
+ const WEB_RESEARCH_MAX_SNIPPET_CHARS = 280;
19
+ const WEB_RESEARCH_MAX_FETCH_CHARS = 6000;
20
+ const defaultDeps = {
21
+ executeRuntimeTool,
22
+ loadRuntimeToolRegistry,
23
+ persistRuntimeToolInvocationArtifacts,
24
+ runShellCommand,
25
+ refreshAstgrepIndex,
26
+ now: () => new Date(),
27
+ randomId: () => randomUUID(),
28
+ };
29
+ function sha256(content) {
30
+ return `sha256:${createHash("sha256").update(content, "utf8").digest("hex")}`;
31
+ }
32
+ function isRecord(value) {
33
+ return !!value && typeof value === "object" && !Array.isArray(value);
34
+ }
35
+ function pickString(record, keys) {
36
+ for (const key of keys) {
37
+ const value = record[key];
38
+ if (typeof value === "string" && value.trim().length > 0) {
39
+ return value.trim();
40
+ }
41
+ }
42
+ return undefined;
43
+ }
44
+ function pickNumber(record, keys) {
45
+ for (const key of keys) {
46
+ const value = record[key];
47
+ if (typeof value === "number" && Number.isFinite(value))
48
+ return value;
49
+ }
50
+ return undefined;
51
+ }
52
+ function truncate(value, maxChars) {
53
+ if (!value)
54
+ return undefined;
55
+ const trimmed = value.trim();
56
+ if (trimmed.length <= maxChars)
57
+ return trimmed;
58
+ return `${trimmed.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;
59
+ }
60
+ function slugify(value, fallback = "artifact") {
61
+ const slug = value
62
+ .toLowerCase()
63
+ .replace(/https?:\/\//g, "")
64
+ .replace(/[^a-z0-9]+/g, "-")
65
+ .replace(/^-+|-+$/g, "")
66
+ .slice(0, 48);
67
+ return slug || fallback;
68
+ }
69
+ function artifactStamp(now, id, seed) {
70
+ return `${now.toISOString().replace(/[:.]/g, "-")}-${slugify(seed)}-${id.slice(0, 8)}`;
71
+ }
72
+ function runtimeToolConfigured(name, deps) {
73
+ const registry = deps.loadRuntimeToolRegistry();
74
+ return Boolean(registry.ok && registry.registry?.tools.some((tool) => tool.name === name));
75
+ }
76
+ function extractArrays(output, keys) {
77
+ const rows = [];
78
+ for (const key of keys) {
79
+ const value = output[key];
80
+ if (!Array.isArray(value))
81
+ continue;
82
+ for (const entry of value) {
83
+ if (isRecord(entry))
84
+ rows.push(entry);
85
+ }
86
+ }
87
+ return rows;
88
+ }
89
+ function normalizeCitations(output) {
90
+ const rows = extractArrays(output, ["citations", "results", "search_results", "sources"]);
91
+ const seen = new Set();
92
+ const citations = [];
93
+ for (const row of rows) {
94
+ const url = pickString(row, ["url", "link", "source_url"]);
95
+ if (!url || seen.has(url))
96
+ continue;
97
+ seen.add(url);
98
+ citations.push({
99
+ title: pickString(row, ["title", "name", "label"]) ?? url,
100
+ url,
101
+ snippet: truncate(pickString(row, ["snippet", "summary", "excerpt", "description"]), WEB_RESEARCH_MAX_SNIPPET_CHARS),
102
+ });
103
+ if (citations.length >= WEB_RESEARCH_MAX_CITATIONS)
104
+ break;
105
+ }
106
+ return citations;
107
+ }
108
+ function normalizeFetchDocs(output) {
109
+ const rows = extractArrays(output, ["fetches", "fetched", "pages", "documents", "fetched_pages"]);
110
+ return rows.slice(0, WEB_RESEARCH_MAX_FETCH_DOCS).map((row) => ({
111
+ url: pickString(row, ["url", "source_url", "link"]),
112
+ title: pickString(row, ["title", "name"]),
113
+ content: truncate(pickString(row, ["content", "text", "excerpt", "markdown", "body", "html"]), WEB_RESEARCH_MAX_FETCH_CHARS),
114
+ }));
115
+ }
116
+ function readWorkspaceContent(path) {
117
+ const raw = safeRead(path);
118
+ if (isReadError(raw)) {
119
+ throw new Error(raw);
120
+ }
121
+ return raw;
122
+ }
123
+ function resolveOutputCxml(output) {
124
+ const inline = pickString(output, ["cxml", "snapshot_cxml"]);
125
+ if (inline)
126
+ return { content: inline };
127
+ const candidatePath = pickString(output, ["cxml_path", "snapshot_path", "output_path", "path"]);
128
+ if (!candidatePath) {
129
+ throw new Error("Codemunch wrapper expected 'cxml' or 'cxml_path' in the tool output.");
130
+ }
131
+ const resolved = resolveWorkspaceArtifactPath(candidatePath, "read");
132
+ if (!existsSync(resolved)) {
133
+ throw new Error(`Codemunch output path not found: ${candidatePath}`);
134
+ }
135
+ return { content: readFileSync(resolved, "utf-8"), source_path: resolved };
136
+ }
137
+ function shellQuote(value) {
138
+ return `'${value.replace(/'/g, `'\"'\"'`)}'`;
139
+ }
140
+ function isPrivateIp(hostname) {
141
+ const kind = isIP(hostname);
142
+ if (kind === 0)
143
+ return false;
144
+ if (kind === 6) {
145
+ const lower = hostname.toLowerCase();
146
+ return lower === "::1" || lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
147
+ }
148
+ const parts = hostname.split(".").map((part) => Number(part));
149
+ const [a, b] = parts;
150
+ return (a === 10 ||
151
+ a === 127 ||
152
+ (a === 169 && b === 254) ||
153
+ (a === 172 && b >= 16 && b <= 31) ||
154
+ (a === 192 && b === 168) ||
155
+ a === 0);
156
+ }
157
+ function assertPublicHttpUrl(raw, label) {
158
+ let parsed;
159
+ try {
160
+ parsed = new URL(raw);
161
+ }
162
+ catch {
163
+ throw new Error(`${label} must be a valid URL.`);
164
+ }
165
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
166
+ throw new Error(`${label} must use http or https.`);
167
+ }
168
+ const hostname = parsed.hostname.toLowerCase();
169
+ if (BLOCKED_HOSTNAMES.has(hostname) || isPrivateIp(hostname)) {
170
+ throw new Error(`${label} targets a private or local address.`);
171
+ }
172
+ }
173
+ function assertGitHubUrl(raw) {
174
+ assertPublicHttpUrl(raw, "repo_url");
175
+ const parsed = new URL(raw);
176
+ if (parsed.hostname.toLowerCase() !== "github.com") {
177
+ throw new Error("repo_url must be a GitHub URL for codemunch snapshots.");
178
+ }
179
+ }
180
+ function safeExternalEnv(extra = {}) {
181
+ const env = {};
182
+ for (const key of SAFE_EXTERNAL_ENV_KEYS) {
183
+ const value = process.env[key];
184
+ if (typeof value === "string")
185
+ env[key] = value;
186
+ }
187
+ for (const [key, value] of Object.entries(extra)) {
188
+ env[key] = value;
189
+ }
190
+ return env;
191
+ }
192
+ async function appendWrapperLedgerEntry(tool, message, artifacts, metadata) {
193
+ await appendRunLedgerEntrySafe({
194
+ tool,
195
+ category: "info",
196
+ message,
197
+ artifacts: artifacts.filter((artifact) => Boolean(artifact)).map((artifact) => normalizePathForValidation(artifact)),
198
+ metadata,
199
+ }).catch(() => undefined);
200
+ }
201
+ export async function createWebResearchPacket(input, deps = {}) {
202
+ const resolved = { ...defaultDeps, ...deps };
203
+ try {
204
+ for (const url of input.fetch_urls ?? []) {
205
+ assertPublicHttpUrl(url, "fetch_urls");
206
+ }
207
+ }
208
+ catch (error) {
209
+ return {
210
+ ok: false,
211
+ summary: "Web research request failed SSRF guard validation.",
212
+ tool_name: DEFAULT_WEB_RESEARCH_TOOL,
213
+ error: error instanceof Error ? error.message : String(error),
214
+ };
215
+ }
216
+ if (!runtimeToolConfigured(DEFAULT_WEB_RESEARCH_TOOL, resolved)) {
217
+ return {
218
+ ok: false,
219
+ summary: `Runtime tool '${DEFAULT_WEB_RESEARCH_TOOL}' is not configured.`,
220
+ tool_name: DEFAULT_WEB_RESEARCH_TOOL,
221
+ error: `Unknown runtime tool spec: ${DEFAULT_WEB_RESEARCH_TOOL}`,
222
+ };
223
+ }
224
+ const runtimeResult = await resolved.executeRuntimeTool(DEFAULT_WEB_RESEARCH_TOOL, {
225
+ query: input.query,
226
+ sources_required: input.sources_required,
227
+ fetch_urls: input.fetch_urls,
228
+ }, {
229
+ session_id: input.session_id,
230
+ turn_number: input.turn_number,
231
+ workspace_path: input.workspace_path,
232
+ });
233
+ if (!runtimeResult.ok || !isRecord(runtimeResult.output)) {
234
+ return {
235
+ ok: false,
236
+ summary: runtimeResult.summary,
237
+ tool_name: DEFAULT_WEB_RESEARCH_TOOL,
238
+ request_path: runtimeResult.request_path,
239
+ response_path: runtimeResult.response_path,
240
+ error: runtimeResult.error ? JSON.stringify(runtimeResult.error) : "Invalid runtime tool output.",
241
+ };
242
+ }
243
+ const now = resolved.now();
244
+ const id = artifactStamp(now, resolved.randomId(), input.query);
245
+ const researchDir = `agent-state/research/${id}`;
246
+ const citations = normalizeCitations(runtimeResult.output);
247
+ const searchResultsPath = await safeWriteAsync(`${researchDir}/search-results.json`, JSON.stringify({
248
+ query: input.query,
249
+ generated_at: now.toISOString(),
250
+ sources_required: input.sources_required,
251
+ results: citations.slice(0, WEB_RESEARCH_MAX_SEARCH_RESULTS),
252
+ }, null, 2));
253
+ const fetchedPaths = [];
254
+ const fetchedDocs = normalizeFetchDocs(runtimeResult.output);
255
+ for (const [index, doc] of fetchedDocs.entries()) {
256
+ if (!doc.content)
257
+ continue;
258
+ const fetchPath = await safeWriteAsync(`${researchDir}/fetch-${String(index + 1).padStart(2, "0")}-${slugify(doc.title ?? doc.url ?? "page")}.txt`, doc.content);
259
+ fetchedPaths.push(fetchPath);
260
+ const match = doc.url ? citations.find((citation) => citation.url === doc.url) : undefined;
261
+ if (match)
262
+ match.fetched_path = fetchPath;
263
+ }
264
+ const packetPath = await safeWriteAsync(`${researchDir}/packet.json`, JSON.stringify({
265
+ schema: "ace-web-research-packet@1",
266
+ generated_at: now.toISOString(),
267
+ query: input.query,
268
+ sources_required: input.sources_required,
269
+ fetch_urls: input.fetch_urls ?? [],
270
+ summary: pickString(runtimeResult.output, ["summary", "answer", "overview"]) ?? runtimeResult.summary,
271
+ citations,
272
+ fetched_documents: fetchedDocs.map((doc, index) => ({
273
+ ...doc,
274
+ artifact_path: fetchedPaths[index],
275
+ })),
276
+ runtime_tool: {
277
+ name: DEFAULT_WEB_RESEARCH_TOOL,
278
+ summary: runtimeResult.summary,
279
+ request_path: runtimeResult.request_path,
280
+ response_path: runtimeResult.response_path,
281
+ },
282
+ }, null, 2));
283
+ const summary = pickString(runtimeResult.output, ["summary", "answer", "overview"]) ??
284
+ `Stored ${citations.length} citations and ${fetchedPaths.length} fetched pages for '${input.query}'.`;
285
+ await appendWrapperLedgerEntry("web_research_packet", summary, [packetPath, searchResultsPath, ...fetchedPaths, runtimeResult.request_path, runtimeResult.response_path], {
286
+ query: input.query,
287
+ citations: citations.length,
288
+ fetched_documents: fetchedPaths.length,
289
+ runtime_tool: DEFAULT_WEB_RESEARCH_TOOL,
290
+ });
291
+ return {
292
+ ok: true,
293
+ summary,
294
+ tool_name: DEFAULT_WEB_RESEARCH_TOOL,
295
+ packet_path: packetPath,
296
+ search_results_path: searchResultsPath,
297
+ fetched_paths: fetchedPaths,
298
+ request_path: runtimeResult.request_path,
299
+ response_path: runtimeResult.response_path,
300
+ citations: citations.slice(0, 5),
301
+ };
302
+ }
303
+ export async function createCodemunchSnapshot(input, deps = {}) {
304
+ const resolved = { ...defaultDeps, ...deps };
305
+ try {
306
+ assertGitHubUrl(input.repo_url);
307
+ }
308
+ catch (error) {
309
+ return {
310
+ ok: false,
311
+ summary: "Codemunch snapshot request failed repository guard validation.",
312
+ mode: "runtime_tool",
313
+ error: error instanceof Error ? error.message : String(error),
314
+ };
315
+ }
316
+ const now = resolved.now();
317
+ const id = artifactStamp(now, resolved.randomId(), input.repo_url);
318
+ const researchDir = `agent-state/research/codemunch/${id}`;
319
+ const snapshotPath = resolveWorkspaceArtifactPath(`${researchDir}/snapshot.cxml`, "write");
320
+ mkdirSync(dirname(snapshotPath), { recursive: true });
321
+ let mode = "runtime_tool";
322
+ let requestPath;
323
+ let responsePath;
324
+ let runtimeSummary = "";
325
+ let sourceContent = "";
326
+ let sourcePath;
327
+ let provenanceSource = {};
328
+ if (runtimeToolConfigured(DEFAULT_CODEMUNCH_TOOL, resolved)) {
329
+ const runtimeResult = await resolved.executeRuntimeTool(DEFAULT_CODEMUNCH_TOOL, {
330
+ repo_url: input.repo_url,
331
+ branch: input.branch,
332
+ max_bytes: input.max_bytes,
333
+ }, {
334
+ session_id: input.session_id,
335
+ turn_number: input.turn_number,
336
+ workspace_path: input.workspace_path,
337
+ });
338
+ requestPath = runtimeResult.request_path;
339
+ responsePath = runtimeResult.response_path;
340
+ runtimeSummary = runtimeResult.summary;
341
+ if (!runtimeResult.ok || !isRecord(runtimeResult.output)) {
342
+ return {
343
+ ok: false,
344
+ summary: runtimeResult.summary,
345
+ mode,
346
+ request_path: requestPath,
347
+ response_path: responsePath,
348
+ error: runtimeResult.error ? JSON.stringify(runtimeResult.error) : "Invalid runtime tool output.",
349
+ };
350
+ }
351
+ const resolvedOutput = resolveOutputCxml(runtimeResult.output);
352
+ sourceContent = resolvedOutput.content;
353
+ sourcePath = resolvedOutput.source_path;
354
+ provenanceSource = {
355
+ tool_name: DEFAULT_CODEMUNCH_TOOL,
356
+ cache_path: pickString(runtimeResult.output, ["cache_path", "cache_dir"]),
357
+ generated_at: pickString(runtimeResult.output, ["generated_at", "created_at"]),
358
+ source_path: sourcePath,
359
+ };
360
+ }
361
+ else {
362
+ mode = "external_cli";
363
+ const commandBase = process.env.ACE_CODEMUNCH_COMMAND?.trim() || DEFAULT_CODEMUNCH_COMMAND;
364
+ const command = [
365
+ commandBase,
366
+ shellQuote(input.repo_url),
367
+ "--out",
368
+ shellQuote(snapshotPath),
369
+ typeof input.max_bytes === "number" ? `--max-bytes ${Math.trunc(input.max_bytes)}` : "",
370
+ input.branch ? `--branch ${shellQuote(input.branch)}` : "",
371
+ ]
372
+ .filter(Boolean)
373
+ .join(" ");
374
+ const requestPayload = JSON.stringify({
375
+ tool: { name: DEFAULT_CODEMUNCH_TOOL, mode: "external_cli", command_base: commandBase },
376
+ input: {
377
+ repo_url: input.repo_url,
378
+ branch: input.branch,
379
+ max_bytes: input.max_bytes,
380
+ },
381
+ workspace_path: input.workspace_path ?? resolveWorkspaceRoot(),
382
+ timestamp: now.toISOString(),
383
+ }, null, 2);
384
+ const shellResult = await resolved.runShellCommand(command, {
385
+ cwd: resolveWorkspaceRoot(),
386
+ env: safeExternalEnv({
387
+ ACE_CODEMUNCH_REPO_URL: input.repo_url,
388
+ ACE_CODEMUNCH_BRANCH: input.branch ?? "",
389
+ ACE_CODEMUNCH_MAX_BYTES: typeof input.max_bytes === "number" ? String(Math.trunc(input.max_bytes)) : "",
390
+ ACE_CODEMUNCH_OUT_PATH: snapshotPath,
391
+ }),
392
+ timeout_ms: 120_000,
393
+ });
394
+ const responsePayload = JSON.stringify({
395
+ ok: shellResult.exit_code === 0 && existsSync(snapshotPath),
396
+ summary: shellResult.exit_code === 0 && existsSync(snapshotPath)
397
+ ? `Codemunch snapshot generated for ${input.repo_url}`
398
+ : `Codemunch CLI failed for ${input.repo_url}`,
399
+ output: shellResult.exit_code === 0 && existsSync(snapshotPath)
400
+ ? {
401
+ cxml_path: snapshotPath,
402
+ repo_url: input.repo_url,
403
+ branch: input.branch,
404
+ max_bytes: input.max_bytes,
405
+ command,
406
+ }
407
+ : undefined,
408
+ error: shellResult.exit_code === 0 && existsSync(snapshotPath)
409
+ ? undefined
410
+ : {
411
+ exit_code: shellResult.exit_code,
412
+ stdout: shellResult.stdout,
413
+ stderr: shellResult.stderr,
414
+ },
415
+ }, null, 2);
416
+ const persisted = await resolved.persistRuntimeToolInvocationArtifacts(resolved.randomId(), requestPayload, responsePayload);
417
+ requestPath = persisted.requestPath;
418
+ responsePath = persisted.responsePath;
419
+ runtimeSummary =
420
+ shellResult.exit_code === 0 && existsSync(snapshotPath)
421
+ ? `Codemunch snapshot generated for ${input.repo_url}`
422
+ : `Codemunch CLI failed for ${input.repo_url}`;
423
+ if (shellResult.exit_code !== 0 || !existsSync(snapshotPath)) {
424
+ return {
425
+ ok: false,
426
+ summary: runtimeSummary,
427
+ mode,
428
+ request_path: requestPath,
429
+ response_path: responsePath,
430
+ error: shellResult.stderr || shellResult.stdout || "Codemunch CLI did not produce snapshot output.",
431
+ };
432
+ }
433
+ sourceContent = readFileSync(snapshotPath, "utf-8");
434
+ sourcePath = snapshotPath;
435
+ provenanceSource = {
436
+ command,
437
+ exit_code: shellResult.exit_code,
438
+ duration_ms: shellResult.duration_ms,
439
+ source_path: snapshotPath,
440
+ };
441
+ }
442
+ const persistedSnapshotPath = await safeWriteAsync(`${researchDir}/snapshot.cxml`, sourceContent);
443
+ const checksum = sha256(sourceContent);
444
+ const provenancePath = await safeWriteAsync(`${researchDir}/provenance.json`, JSON.stringify({
445
+ schema: "ace-codemunch-snapshot@1",
446
+ generated_at: now.toISOString(),
447
+ repo_url: input.repo_url,
448
+ branch: input.branch ?? null,
449
+ max_bytes: typeof input.max_bytes === "number" ? Math.trunc(input.max_bytes) : null,
450
+ snapshot_path: persistedSnapshotPath,
451
+ checksum,
452
+ request_path: requestPath,
453
+ response_path: responsePath,
454
+ mode,
455
+ source: provenanceSource,
456
+ }, null, 2));
457
+ await appendWrapperLedgerEntry("codemunch_snapshot", runtimeSummary, [persistedSnapshotPath, provenancePath, requestPath, responsePath], {
458
+ repo_url: input.repo_url,
459
+ branch: input.branch ?? null,
460
+ checksum,
461
+ mode,
462
+ source_path: sourcePath ? normalizePathForValidation(sourcePath) : null,
463
+ });
464
+ return {
465
+ ok: true,
466
+ summary: runtimeSummary,
467
+ mode,
468
+ snapshot_path: persistedSnapshotPath,
469
+ checksum,
470
+ provenance_path: provenancePath,
471
+ request_path: requestPath,
472
+ response_path: responsePath,
473
+ };
474
+ }
475
+ export async function createMcpIngestionProbe(input, deps = {}) {
476
+ const resolved = { ...defaultDeps, ...deps };
477
+ const callId = resolved.randomId();
478
+ const now = resolved.now();
479
+ const requestPayload = JSON.stringify({
480
+ schema: "ace-mcp-ingestion-probe@1",
481
+ generated_at: now.toISOString(),
482
+ name: input.name,
483
+ transport: input.transport,
484
+ command: input.transport === "stdio" ? input.command : undefined,
485
+ args: input.transport === "stdio" ? input.args ?? [] : undefined,
486
+ url: input.transport === "http" ? input.url : undefined,
487
+ env_keys: Object.keys(input.env ?? {}).sort(),
488
+ }, null, 2);
489
+ let error;
490
+ let reasonCode;
491
+ if (!input.name.trim()) {
492
+ error = "MCP server name is required.";
493
+ reasonCode = "invalid_name";
494
+ }
495
+ else if (input.transport === "stdio" && !input.command?.trim()) {
496
+ error = "stdio MCP ingestion requires command.";
497
+ reasonCode = "command_missing";
498
+ }
499
+ else if (input.transport === "http") {
500
+ try {
501
+ if (!input.url)
502
+ throw new Error("http MCP ingestion requires url.");
503
+ assertPublicHttpUrl(input.url, "mcp_server.url");
504
+ }
505
+ catch (caught) {
506
+ error = caught instanceof Error ? caught.message : String(caught);
507
+ reasonCode = "ssrf_blocked";
508
+ }
509
+ }
510
+ if (!error) {
511
+ const forbiddenEnv = Object.keys(input.env ?? {}).filter((key) => /(SECRET|TOKEN|KEY|PASSWORD|PASS|CREDENTIAL|AUTH|COOKIE|SESSION)/i.test(key));
512
+ if (forbiddenEnv.length > 0) {
513
+ error = `MCP server env contains secret-like keys: ${forbiddenEnv.join(", ")}`;
514
+ reasonCode = "secret_env_blocked";
515
+ }
516
+ }
517
+ const tools = (input.declared_tools ?? []).map((tool) => ({
518
+ name: tool.name,
519
+ description: tool.description,
520
+ input_schema: tool.input_schema,
521
+ })).filter((tool) => tool.name?.trim());
522
+ const responsePayload = JSON.stringify({
523
+ ok: !error,
524
+ summary: error
525
+ ? `MCP ingestion probe blocked for ${input.name}: ${error}`
526
+ : `MCP ingestion probe configured ${tools.length} declared tool(s) for ${input.name}.`,
527
+ output: !error
528
+ ? {
529
+ health: "configured",
530
+ tools,
531
+ transport: input.transport,
532
+ }
533
+ : undefined,
534
+ error: error ? { reason_code: reasonCode, message: error } : undefined,
535
+ }, null, 2);
536
+ const persisted = await resolved.persistRuntimeToolInvocationArtifacts(callId, requestPayload, responsePayload);
537
+ return {
538
+ ok: !error,
539
+ summary: error
540
+ ? `MCP ingestion probe blocked for ${input.name}: ${error}`
541
+ : `MCP ingestion probe configured ${tools.length} declared tool(s) for ${input.name}.`,
542
+ server_name: input.name,
543
+ transport: input.transport,
544
+ health: error ? "blocked" : "configured",
545
+ tools,
546
+ request_path: persisted.requestPath,
547
+ response_path: persisted.responsePath,
548
+ error,
549
+ reason_code: reasonCode,
550
+ };
551
+ }
552
+ export async function createCodemunchIndex(input, deps = {}) {
553
+ const resolved = { ...defaultDeps, ...deps };
554
+ const now = resolved.now();
555
+ const id = artifactStamp(now, resolved.randomId(), input.cxml_path);
556
+ const researchDir = `agent-state/research/codemunch/${id}`;
557
+ const sourceContent = readWorkspaceContent(input.cxml_path);
558
+ const checksum = sha256(sourceContent);
559
+ const repPath = wsPath("rep_astgrep.cxml");
560
+ const priorRep = existsSync(repPath) ? readFileSync(repPath, "utf-8") : undefined;
561
+ mkdirSync(resolveWorkspaceArtifactPath(researchDir, "write"), { recursive: true });
562
+ try {
563
+ const routedRepPath = await safeWriteAsync("rep_astgrep.cxml", sourceContent);
564
+ const discoveryResult = await resolved.refreshAstgrepIndex({
565
+ scope: researchDir,
566
+ append_evidence: input.append_evidence ?? false,
567
+ emit_event: input.emit_event ?? false,
568
+ include_rep_corpus: true,
569
+ });
570
+ const indexJsonContent = readWorkspaceContent(discoveryResult.output_json_path);
571
+ const indexMdContent = readWorkspaceContent(discoveryResult.output_md_path);
572
+ const summaryJsonPath = await safeWriteAsync(`${researchDir}/structural-summary.json`, indexJsonContent);
573
+ const summaryMdPath = await safeWriteAsync(`${researchDir}/structural-summary.md`, indexMdContent);
574
+ const parsedSummary = JSON.parse(indexJsonContent);
575
+ const routingHints = Array.isArray(parsedSummary.routing_hints)
576
+ ? parsedSummary.routing_hints.filter((value) => typeof value === "string").slice(0, 5)
577
+ : [];
578
+ const provenancePath = await safeWriteAsync(`${researchDir}/index-provenance.json`, JSON.stringify({
579
+ schema: "ace-codemunch-index@1",
580
+ generated_at: now.toISOString(),
581
+ source_cxml_path: input.cxml_path,
582
+ checksum,
583
+ routed_rep_corpus_path: routedRepPath,
584
+ discovery_output_json_path: discoveryResult.output_json_path,
585
+ discovery_output_md_path: discoveryResult.output_md_path,
586
+ copied_summary_json_path: summaryJsonPath,
587
+ copied_summary_md_path: summaryMdPath,
588
+ }, null, 2));
589
+ await appendWrapperLedgerEntry("codemunch_index", `Indexed codemunch snapshot ${normalizePathForValidation(input.cxml_path)}`, [summaryJsonPath, summaryMdPath, provenancePath, routedRepPath], {
590
+ checksum,
591
+ rep_sources: discoveryResult.stats.rep_sources,
592
+ hotspot_files: discoveryResult.stats.hotspot_files,
593
+ });
594
+ return {
595
+ ok: true,
596
+ summary: `Indexed codemunch snapshot with ${discoveryResult.stats.rep_sources} rep sources.`,
597
+ source_cxml_path: input.cxml_path,
598
+ checksum,
599
+ rep_corpus_path: routedRepPath,
600
+ structural_summary_json_path: summaryJsonPath,
601
+ structural_summary_md_path: summaryMdPath,
602
+ provenance_path: provenancePath,
603
+ routing_hints: routingHints,
604
+ };
605
+ }
606
+ finally {
607
+ if (typeof priorRep === "string") {
608
+ await safeWriteAsync("rep_astgrep.cxml", priorRep);
609
+ }
610
+ else if (existsSync(repPath)) {
611
+ rmSync(repPath, { force: true });
612
+ }
613
+ }
614
+ }
615
+ //# sourceMappingURL=discovery-runtime-wrappers.js.map
@@ -4,7 +4,7 @@ import { openStore } from "./store/ace-packed-store.js";
4
4
  import { ProjectionManager } from "./store/materializers/projection-manager.js";
5
5
  import { HandoffRepository } from "./store/repositories/handoff-repository.js";
6
6
  import { getWorkspaceStorePath, listStoreKeysSync, readStoreJsonSync, storeExistsSync, } from "./store/store-snapshot.js";
7
- import { withStoreWriteQueue } from "./store/write-queue.js";
7
+ import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
8
8
  import { waitForPendingStatusEventMirrors } from "./status-events.js";
9
9
  import { waitForTodoStoreMirror } from "./todo-state.js";
10
10
  const HANDOFF_REGISTRY_REL = "agent-state/handoff-registry.json";
@@ -299,7 +299,7 @@ export async function registerHandoffSafe(input) {
299
299
  return withFileLock(HANDOFF_REGISTRY_REL, () => registerHandoff(input));
300
300
  }
301
301
  return withFileLock(HANDOFF_REGISTRY_REL, async () => {
302
- return withStoreWriteQueue(storePath, async () => {
302
+ return withStoreWriteCoordinator(storePath, async () => {
303
303
  const store = await openStore(storePath);
304
304
  try {
305
305
  const repo = new HandoffRepository(store);
@@ -346,7 +346,7 @@ export async function registerHandoffSafe(input) {
346
346
  finally {
347
347
  await store.close();
348
348
  }
349
- });
349
+ }, { operation_label: "registerHandoffSafe" });
350
350
  });
351
351
  }
352
352
  /**
@@ -361,7 +361,7 @@ export async function acknowledgeHandoffSafe(input) {
361
361
  return withFileLock(HANDOFF_REGISTRY_REL, () => acknowledgeHandoff(input));
362
362
  }
363
363
  return withFileLock(HANDOFF_REGISTRY_REL, async () => {
364
- return withStoreWriteQueue(storePath, async () => {
364
+ return withStoreWriteCoordinator(storePath, async () => {
365
365
  const store = await openStore(storePath);
366
366
  try {
367
367
  const repo = new HandoffRepository(store);
@@ -420,7 +420,7 @@ export async function acknowledgeHandoffSafe(input) {
420
420
  finally {
421
421
  await store.close();
422
422
  }
423
- });
423
+ }, { operation_label: "acknowledgeHandoffSafe" });
424
424
  });
425
425
  }
426
426
  //# sourceMappingURL=handoff-registry.js.map
@@ -0,0 +1,19 @@
1
+ import { type AgentRole, type KernelKey, type TaskKey } from "./constants.js";
2
+ export interface SkillReference {
3
+ name: string;
4
+ path: string;
5
+ source: "workspace" | "package-defaults" | "store";
6
+ }
7
+ export declare function getAgentInstructionPath(role: AgentRole): string | undefined;
8
+ export declare function getAgentManifestPath(role: AgentRole): string | undefined;
9
+ export declare function readAgentInstructions(role: AgentRole): string;
10
+ export declare function readAgentManifest(role: AgentRole): string;
11
+ export declare function getTaskArtifactPath(key: TaskKey): string | undefined;
12
+ export declare function readTaskArtifact(key: TaskKey): string;
13
+ export declare function getKernelArtifactPath(key: KernelKey): string | undefined;
14
+ export declare function readKernelArtifact(key: KernelKey): string;
15
+ export declare function resolveWritableTaskPath(key: TaskKey): string;
16
+ export declare function listAvailableSkills(): SkillReference[];
17
+ export declare function getSkillPath(name: string): string | undefined;
18
+ export declare function readSkillInstructions(name: string): string;
19
+ //# sourceMappingURL=artifacts.d.ts.map