ace-swarm 2.2.0 → 2.3.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 (77) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/README.md +86 -40
  3. package/assets/.github/hooks/ace-copilot.json +16 -16
  4. package/assets/agent-state/MODULES/schemas/VERICIFY_PROCESS_POST_LOG.schema.json +1 -0
  5. package/assets/scripts/ace-hook-dispatch.mjs +447 -0
  6. package/assets/scripts/copilot-hook-dispatch.mjs +1 -303
  7. package/assets/scripts/render-mcp-configs.sh +328 -1
  8. package/dist/ace-context.d.ts +29 -0
  9. package/dist/ace-context.d.ts.map +1 -0
  10. package/dist/ace-context.js +240 -0
  11. package/dist/ace-context.js.map +1 -0
  12. package/dist/ace-internal-tools.d.ts +8 -0
  13. package/dist/ace-internal-tools.d.ts.map +1 -0
  14. package/dist/ace-internal-tools.js +76 -0
  15. package/dist/ace-internal-tools.js.map +1 -0
  16. package/dist/ace-server-instructions.d.ts +12 -0
  17. package/dist/ace-server-instructions.d.ts.map +1 -0
  18. package/dist/ace-server-instructions.js +299 -0
  19. package/dist/ace-server-instructions.js.map +1 -0
  20. package/dist/helpers.d.ts.map +1 -1
  21. package/dist/helpers.js +90 -0
  22. package/dist/helpers.js.map +1 -1
  23. package/dist/internal-tool-runtime.d.ts +21 -0
  24. package/dist/internal-tool-runtime.d.ts.map +1 -0
  25. package/dist/internal-tool-runtime.js +136 -0
  26. package/dist/internal-tool-runtime.js.map +1 -0
  27. package/dist/local-model-runtime.d.ts +36 -0
  28. package/dist/local-model-runtime.d.ts.map +1 -0
  29. package/dist/local-model-runtime.js +161 -0
  30. package/dist/local-model-runtime.js.map +1 -0
  31. package/dist/model-bridge.d.ts +54 -0
  32. package/dist/model-bridge.d.ts.map +1 -0
  33. package/dist/model-bridge.js +587 -0
  34. package/dist/model-bridge.js.map +1 -0
  35. package/dist/orchestrator-supervisor.d.ts +100 -0
  36. package/dist/orchestrator-supervisor.d.ts.map +1 -0
  37. package/dist/orchestrator-supervisor.js +399 -0
  38. package/dist/orchestrator-supervisor.js.map +1 -0
  39. package/dist/runtime-executor.d.ts.map +1 -1
  40. package/dist/runtime-executor.js +5 -42
  41. package/dist/runtime-executor.js.map +1 -1
  42. package/dist/schemas.js +1 -1
  43. package/dist/schemas.js.map +1 -1
  44. package/dist/server.d.ts +5 -1
  45. package/dist/server.d.ts.map +1 -1
  46. package/dist/server.js +9 -1
  47. package/dist/server.js.map +1 -1
  48. package/dist/shared.d.ts +3 -3
  49. package/dist/tools-agent.d.ts +1 -0
  50. package/dist/tools-agent.d.ts.map +1 -1
  51. package/dist/tools-agent.js +456 -1
  52. package/dist/tools-agent.js.map +1 -1
  53. package/dist/tui/agent-runner.d.ts +6 -0
  54. package/dist/tui/agent-runner.d.ts.map +1 -1
  55. package/dist/tui/agent-runner.js +15 -1
  56. package/dist/tui/agent-runner.js.map +1 -1
  57. package/dist/tui/agent-worker.d.ts +3 -1
  58. package/dist/tui/agent-worker.d.ts.map +1 -1
  59. package/dist/tui/agent-worker.js +117 -9
  60. package/dist/tui/agent-worker.js.map +1 -1
  61. package/dist/tui/chat.d.ts +19 -0
  62. package/dist/tui/chat.d.ts.map +1 -1
  63. package/dist/tui/chat.js +108 -0
  64. package/dist/tui/chat.js.map +1 -1
  65. package/dist/tui/index.d.ts +1 -0
  66. package/dist/tui/index.d.ts.map +1 -1
  67. package/dist/tui/index.js +3 -0
  68. package/dist/tui/index.js.map +1 -1
  69. package/dist/vericify-bridge.d.ts +5 -1
  70. package/dist/vericify-bridge.d.ts.map +1 -1
  71. package/dist/vericify-bridge.js +10 -0
  72. package/dist/vericify-bridge.js.map +1 -1
  73. package/dist/vericify-context.d.ts +10 -0
  74. package/dist/vericify-context.d.ts.map +1 -0
  75. package/dist/vericify-context.js +72 -0
  76. package/dist/vericify-context.js.map +1 -0
  77. package/package.json +2 -1
@@ -0,0 +1,447 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { resolve } from "node:path";
5
+
6
+ const DESTRUCTIVE_COMMAND_PATTERNS = [
7
+ /\brm\s+-rf\b/i,
8
+ /\bgit\s+reset\s+--hard\b/i,
9
+ /\bgit\s+checkout\s+--\b/i,
10
+ /\bgit\s+clean\s+-fdx\b/i,
11
+ /\bdel\s+\/s\s+\/q\b/i,
12
+ /\bdrop\s+table\b/i,
13
+ /\btruncate\s+table\b/i,
14
+ /\bmkfs\b/i,
15
+ ];
16
+
17
+ const PROTECTED_PATH_FRAGMENTS = [
18
+ ".github/hooks/",
19
+ ".github\\hooks\\",
20
+ "scripts/ace/ace-hook-dispatch.mjs",
21
+ "scripts\\ace\\ace-hook-dispatch.mjs",
22
+ "scripts/ace/copilot-hook-dispatch.mjs",
23
+ "scripts\\ace\\copilot-hook-dispatch.mjs",
24
+ ".vscode/settings.json",
25
+ ".vscode\\settings.json",
26
+ ];
27
+
28
+ const ROLE_TOOL_HINTS = {
29
+ orchestrator: ["recall_context", "route_task", "create_handoff", "execute_gates", "emit_status_event"],
30
+ coders: ["read_workspace_file", "safe_edit_file", "run_tests", "git_diff", "execute_gates"],
31
+ qa: ["read_workspace_file", "run_tests", "execute_gates", "git_diff", "append_vericify_process_post"],
32
+ docs: ["read_workspace_file", "write_workspace_file", "git_diff", "append_vericify_process_post"],
33
+ research: ["recall_context", "read_workspace_file", "build_continuity_packet", "append_vericify_process_post"],
34
+ spec: ["read_workspace_file", "write_workspace_file", "execute_gates", "append_vericify_process_post"],
35
+ };
36
+
37
+ function readStdin() {
38
+ return new Promise((resolveStdin, reject) => {
39
+ let input = "";
40
+ process.stdin.setEncoding("utf8");
41
+ process.stdin.on("data", (chunk) => {
42
+ input += chunk;
43
+ });
44
+ process.stdin.on("end", () => resolveStdin(input));
45
+ process.stdin.on("error", reject);
46
+ });
47
+ }
48
+
49
+ function writeJson(payload) {
50
+ process.stdout.write(JSON.stringify(payload));
51
+ }
52
+
53
+ function toLowerText(value) {
54
+ return typeof value === "string" ? value.trim().toLowerCase() : "";
55
+ }
56
+
57
+ function safeJson(value) {
58
+ try {
59
+ return JSON.stringify(value ?? {});
60
+ } catch {
61
+ return "";
62
+ }
63
+ }
64
+
65
+ function resolveWorkspaceRoot(payload) {
66
+ const cwd =
67
+ typeof payload.cwd === "string" && payload.cwd.trim() ? payload.cwd.trim() : process.cwd();
68
+ return resolve(cwd);
69
+ }
70
+
71
+ function readIfPresent(workspaceRoot, relativePath) {
72
+ const absolute = resolve(workspaceRoot, relativePath);
73
+ if (!existsSync(absolute)) return undefined;
74
+ try {
75
+ return readFileSync(absolute, "utf8");
76
+ } catch {
77
+ return undefined;
78
+ }
79
+ }
80
+
81
+ function oneLine(value) {
82
+ return String(value ?? "")
83
+ .replace(/\s+/g, " ")
84
+ .trim();
85
+ }
86
+
87
+ function truncate(value, max = 160) {
88
+ const text = oneLine(value);
89
+ if (text.length <= max) return text;
90
+ return `${text.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
91
+ }
92
+
93
+ function extractTaskObjective(workspaceRoot) {
94
+ const raw = readIfPresent(workspaceRoot, "agent-state/TASK.md");
95
+ if (!raw) return undefined;
96
+ const match = raw.match(/## Objective\s+([\s\S]*?)(?:\n## |\n# |$)/);
97
+ if (match?.[1]) {
98
+ return truncate(match[1]);
99
+ }
100
+
101
+ const bodyLine = raw
102
+ .split(/\r?\n/)
103
+ .map((line) => line.trim())
104
+ .find((line) => line && !line.startsWith("#"));
105
+ if (bodyLine) {
106
+ return truncate(bodyLine);
107
+ }
108
+
109
+ const heading = raw.match(/^#\s+(.+)$/m)?.[1];
110
+ return heading ? truncate(heading) : undefined;
111
+ }
112
+
113
+ function extractStatusLine(workspaceRoot, label) {
114
+ const raw = readIfPresent(workspaceRoot, "agent-state/STATUS.md");
115
+ if (!raw) return undefined;
116
+ const pattern = new RegExp(`^- ${label}:\\s*(.+)$`, "m");
117
+ const match = raw.match(pattern);
118
+ return match?.[1] ? truncate(match[1]) : undefined;
119
+ }
120
+
121
+ function extractScopeReminder(workspaceRoot) {
122
+ const raw = readIfPresent(workspaceRoot, "agent-state/SCOPE.md");
123
+ if (!raw) return undefined;
124
+ const lines = raw
125
+ .split(/\r?\n/)
126
+ .map((line) => line.trim())
127
+ .filter(Boolean)
128
+ .filter((line) => !line.startsWith("#"));
129
+ if (lines.length === 0) {
130
+ return "ACE scope reminder: review agent-state/SCOPE.md before mutating files or state.";
131
+ }
132
+ return `ACE scope reminder: ${truncate(lines.slice(0, 4).join(" "), 220)}`;
133
+ }
134
+
135
+ function extractRoleHint(payload) {
136
+ const candidates = [
137
+ payload.role,
138
+ payload.agentRole,
139
+ payload.agent_role,
140
+ payload.subagentRole,
141
+ payload.subagent_role,
142
+ payload?.subagent?.role,
143
+ payload?.tool_input?.role,
144
+ ];
145
+ for (const candidate of candidates) {
146
+ if (typeof candidate !== "string") continue;
147
+ const normalized = candidate.trim().toLowerCase().replace(/^agent-/, "");
148
+ if (normalized) return normalized;
149
+ }
150
+ return undefined;
151
+ }
152
+
153
+ function buildRoleToolHint(role) {
154
+ const tools = ROLE_TOOL_HINTS[role];
155
+ if (!tools || tools.length === 0) return undefined;
156
+ return `Suggested ACE tools for ${role}: ${tools.join(", ")}.`;
157
+ }
158
+
159
+ function aceContext(workspaceRoot) {
160
+ const stateDirs = [
161
+ "agent-state",
162
+ "global-state",
163
+ "venture-state",
164
+ "brand-state",
165
+ "engineering-state",
166
+ ].filter((relPath) => existsSync(resolve(workspaceRoot, relPath)));
167
+ const coreFiles = [
168
+ "agent-state/TASK.md",
169
+ "agent-state/STATUS.md",
170
+ "agent-state/SCOPE.md",
171
+ "agent-state/EVIDENCE_LOG.md",
172
+ ].filter((relPath) => existsSync(resolve(workspaceRoot, relPath)));
173
+
174
+ const taskObjective = extractTaskObjective(workspaceRoot);
175
+ const currentPhase = extractStatusLine(workspaceRoot, "Current phase");
176
+ const blockers = extractStatusLine(workspaceRoot, "Blockers");
177
+ const hasSkills = existsSync(resolve(workspaceRoot, ".agents", "skills"));
178
+ const hasAgentState = existsSync(resolve(workspaceRoot, "agent-state"));
179
+ const scopeReminder = extractScopeReminder(workspaceRoot);
180
+
181
+ return {
182
+ coreFiles,
183
+ stateDirs,
184
+ taskObjective,
185
+ currentPhase,
186
+ blockers,
187
+ hasSkills,
188
+ hasAgentState,
189
+ scopeReminder,
190
+ };
191
+ }
192
+
193
+ function readAutonomyHint(workspaceRoot) {
194
+ const workflowPath = resolve(workspaceRoot, "agent-state", "ACE_WORKFLOW.md");
195
+ const defaultHint =
196
+ "Runtime autonomy defaults are active: orchestrator preflight, context recall.";
197
+ if (!existsSync(workflowPath)) return defaultHint;
198
+
199
+ let raw = "";
200
+ try {
201
+ raw = readFileSync(workflowPath, "utf8");
202
+ } catch {
203
+ return defaultHint;
204
+ }
205
+
206
+ const signals = [];
207
+ if (/^\s*orchestrator_preflight:\s*true\s*$/m.test(raw)) {
208
+ signals.push("orchestrator preflight");
209
+ }
210
+ if (/^\s*recall_context:\s*true\s*$/m.test(raw)) {
211
+ signals.push("context recall");
212
+ }
213
+ const reviewMode = raw.match(/^\s*review_mode:\s*["']?([A-Za-z0-9_-]+)["']?\s*$/m)?.[1];
214
+ if (reviewMode) {
215
+ signals.push(`review mode ${reviewMode}`);
216
+ }
217
+
218
+ return signals.length > 0
219
+ ? `Runtime autonomy policy is active in agent-state/ACE_WORKFLOW.md: ${signals.join(", ")}.`
220
+ : defaultHint;
221
+ }
222
+
223
+ function buildAceMessage(workspaceRoot, options = {}) {
224
+ const context = aceContext(workspaceRoot);
225
+ if (!context.hasAgentState && !context.hasSkills) return "";
226
+
227
+ const parts = [];
228
+ if (context.coreFiles.length > 0) {
229
+ parts.push(`ACE workspace detected. Ground truth lives in ${context.coreFiles.join(", ")}.`);
230
+ } else if (context.hasAgentState) {
231
+ parts.push("ACE workspace detected. Ground truth lives in agent-state/*.");
232
+ }
233
+ if (context.taskObjective) {
234
+ parts.push(`Current task: ${context.taskObjective}.`);
235
+ }
236
+ if (context.currentPhase) {
237
+ parts.push(`Current phase: ${context.currentPhase}.`);
238
+ }
239
+ if (context.blockers) {
240
+ parts.push(`Blockers: ${context.blockers}.`);
241
+ }
242
+ if (context.stateDirs.length > 0) {
243
+ parts.push(
244
+ `Persist durable artifacts under ${context.stateDirs.map((dir) => `${dir}/`).join(", ")} rather than editor session caches such as workspaceStorage/.../chat-session-resources/.`
245
+ );
246
+ }
247
+ parts.push(
248
+ "Before major edits, read task, scope, and status artifacts rather than relying on chat history."
249
+ );
250
+ if (context.scopeReminder) {
251
+ parts.push(context.scopeReminder);
252
+ }
253
+ if (context.hasSkills) {
254
+ parts.push("Workspace skills are available under .agents/skills/.");
255
+ }
256
+ const autonomyHint = readAutonomyHint(workspaceRoot);
257
+ if (autonomyHint) {
258
+ parts.push(autonomyHint);
259
+ }
260
+ const roleHint = extractRoleHint(options.payload ?? {});
261
+ if (roleHint && (options.eventName === "SubagentStart" || options.eventName === "UserPromptSubmit")) {
262
+ parts.push(`Assigned ACE role: ${roleHint}.`);
263
+ const toolHint = buildRoleToolHint(roleHint);
264
+ if (toolHint) {
265
+ parts.push(toolHint);
266
+ }
267
+ }
268
+ const promptText =
269
+ typeof options.payload?.prompt === "string" ? options.payload.prompt.trim() : "";
270
+ if (promptText && options.eventName === "UserPromptSubmit") {
271
+ parts.push(`Latest prompt: ${truncate(promptText, 180)}.`);
272
+ }
273
+ parts.push(
274
+ "ACE hook policy protects .github/hooks, .vscode/settings.json, and scripts/ace/*hook-dispatch.mjs from silent self-modification."
275
+ );
276
+ return parts.join(" ");
277
+ }
278
+
279
+ function isCommandTool(toolName) {
280
+ return ["terminal", "shell", "bash", "command", "execute", "run"].some((token) =>
281
+ toolName.includes(token)
282
+ );
283
+ }
284
+
285
+ function isMutationTool(toolName) {
286
+ return [
287
+ "edit",
288
+ "write",
289
+ "create",
290
+ "replace",
291
+ "delete",
292
+ "rename",
293
+ "move",
294
+ "file",
295
+ "commit",
296
+ "dispatch",
297
+ "update",
298
+ "append",
299
+ ].some((token) => toolName.includes(token));
300
+ }
301
+
302
+ function containsProtectedPath(text) {
303
+ const normalized = toLowerText(text);
304
+ return PROTECTED_PATH_FRAGMENTS.some((fragment) => normalized.includes(fragment.toLowerCase()));
305
+ }
306
+
307
+ function containsDestructiveCommand(text) {
308
+ return DESTRUCTIVE_COMMAND_PATTERNS.some((pattern) => pattern.test(text));
309
+ }
310
+
311
+ function resolvePreToolUse(payload, workspaceRoot) {
312
+ const toolName = toLowerText(payload.tool_name);
313
+ const serializedInput = safeJson(payload.tool_input);
314
+
315
+ if (isCommandTool(toolName) && containsDestructiveCommand(serializedInput)) {
316
+ return {
317
+ continue: true,
318
+ hookSpecificOutput: {
319
+ hookEventName: "PreToolUse",
320
+ permissionDecision: "deny",
321
+ permissionDecisionReason:
322
+ "Blocked by ACE hook policy: destructive shell command detected.",
323
+ additionalContext:
324
+ "Use a non-destructive alternative or ask for explicit confirmation before destructive terminal actions.",
325
+ },
326
+ };
327
+ }
328
+
329
+ if (isMutationTool(toolName) && containsProtectedPath(serializedInput)) {
330
+ return {
331
+ continue: true,
332
+ hookSpecificOutput: {
333
+ hookEventName: "PreToolUse",
334
+ permissionDecision: "ask",
335
+ permissionDecisionReason:
336
+ "Editing hook or control-plane files requires confirmation.",
337
+ additionalContext:
338
+ "This workspace treats hook files, dispatcher scripts, and VS Code settings as protected control-plane artifacts.",
339
+ },
340
+ };
341
+ }
342
+
343
+ const message = buildAceMessage(workspaceRoot, {
344
+ payload,
345
+ eventName: "PreToolUse",
346
+ });
347
+ if (isMutationTool(toolName) && message) {
348
+ return {
349
+ continue: true,
350
+ hookSpecificOutput: {
351
+ hookEventName: "PreToolUse",
352
+ permissionDecision: "allow",
353
+ additionalContext: `${message} Confirm the target stays within scope before mutating files or state, and persist evidence after the change.`,
354
+ },
355
+ };
356
+ }
357
+
358
+ return { continue: true };
359
+ }
360
+
361
+ function resolvePostToolUse(payload, workspaceRoot) {
362
+ const toolName = toLowerText(payload.tool_name);
363
+ if (!isMutationTool(toolName)) {
364
+ return { continue: true };
365
+ }
366
+
367
+ const message = buildAceMessage(workspaceRoot, {
368
+ payload,
369
+ eventName: "PostToolUse",
370
+ });
371
+ return {
372
+ continue: true,
373
+ hookSpecificOutput: {
374
+ hookEventName: "PostToolUse",
375
+ additionalContext: message
376
+ ? `${message} If project files or state changed, reconcile evidence, run the smallest relevant verification, and call execute_gates before concluding.`
377
+ : "If project files or state changed, run the smallest relevant verification and call execute_gates before concluding.",
378
+ },
379
+ };
380
+ }
381
+
382
+ function resolveSessionContext(payload, workspaceRoot, eventName) {
383
+ const message = buildAceMessage(workspaceRoot, {
384
+ payload,
385
+ eventName,
386
+ });
387
+ if (!message) return { continue: true };
388
+
389
+ return {
390
+ continue: true,
391
+ hookSpecificOutput: {
392
+ hookEventName: eventName,
393
+ additionalContext: message,
394
+ },
395
+ };
396
+ }
397
+
398
+ function resolveHook(payload) {
399
+ const eventName = toLowerText(payload.hookEventName);
400
+ const workspaceRoot = resolveWorkspaceRoot(payload);
401
+
402
+ if (eventName === "sessionstart") {
403
+ return resolveSessionContext(payload, workspaceRoot, "SessionStart");
404
+ }
405
+
406
+ if (eventName === "subagentstart") {
407
+ return resolveSessionContext(payload, workspaceRoot, "SubagentStart");
408
+ }
409
+
410
+ if (eventName === "userpromptsubmit") {
411
+ return resolveSessionContext(payload, workspaceRoot, "UserPromptSubmit");
412
+ }
413
+
414
+ if (eventName === "pretooluse") {
415
+ return resolvePreToolUse(payload, workspaceRoot);
416
+ }
417
+
418
+ if (eventName === "posttooluse") {
419
+ return resolvePostToolUse(payload, workspaceRoot);
420
+ }
421
+
422
+ if (eventName === "precompact") {
423
+ return resolveSessionContext(payload, workspaceRoot, "PreCompact");
424
+ }
425
+
426
+ if (eventName === "subagentstop" || eventName === "stop") {
427
+ return { continue: true };
428
+ }
429
+
430
+ return { continue: true };
431
+ }
432
+
433
+ async function main() {
434
+ try {
435
+ const raw = await readStdin();
436
+ const payload = raw.trim() ? JSON.parse(raw) : {};
437
+ writeJson(resolveHook(payload));
438
+ } catch (error) {
439
+ const message = error instanceof Error ? error.message : String(error);
440
+ writeJson({
441
+ continue: true,
442
+ systemMessage: `ACE hook warning: ${message}`,
443
+ });
444
+ }
445
+ }
446
+
447
+ main();