@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.
- package/CHANGELOG.md +19 -1
- package/README.md +21 -13
- package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
- package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
- package/assets/agent-state/STATUS.md +2 -2
- package/assets/agent-state/runtime-tool-specs.json +70 -2
- package/assets/instructions/ACE_Coder.instructions.md +13 -0
- package/assets/instructions/ACE_UI.instructions.md +11 -0
- package/assets/scripts/ace-hook-dispatch.mjs +70 -6
- package/assets/scripts/render-mcp-configs.sh +19 -5
- package/dist/ace-context.js +91 -11
- package/dist/ace-internal-tools.d.ts +3 -1
- package/dist/ace-internal-tools.js +10 -2
- package/dist/ace-server-instructions.js +3 -3
- package/dist/ace-state-resolver.js +5 -3
- package/dist/agent-runtime/role-adapters.d.ts +18 -1
- package/dist/agent-runtime/role-adapters.js +49 -5
- package/dist/astgrep-index.d.ts +57 -1
- package/dist/astgrep-index.js +140 -4
- package/dist/cli.js +232 -35
- package/dist/discovery-runtime-wrappers.d.ts +108 -0
- package/dist/discovery-runtime-wrappers.js +615 -0
- package/dist/handoff-registry.js +5 -5
- package/dist/helpers/artifacts.d.ts +19 -0
- package/dist/helpers/artifacts.js +152 -0
- package/dist/helpers/bootstrap.d.ts +24 -0
- package/dist/helpers/bootstrap.js +894 -0
- package/dist/helpers/constants.d.ts +53 -0
- package/dist/helpers/constants.js +295 -0
- package/dist/helpers/drift.d.ts +13 -0
- package/dist/helpers/drift.js +45 -0
- package/dist/helpers/path-utils.d.ts +24 -0
- package/dist/helpers/path-utils.js +123 -0
- package/dist/helpers/store-resolution.d.ts +19 -0
- package/dist/helpers/store-resolution.js +305 -0
- package/dist/helpers/workspace-root.d.ts +3 -0
- package/dist/helpers/workspace-root.js +80 -0
- package/dist/helpers.d.ts +8 -125
- package/dist/helpers.js +8 -1768
- package/dist/job-scheduler.js +33 -7
- package/dist/json-sanitizer.d.ts +16 -0
- package/dist/json-sanitizer.js +26 -0
- package/dist/local-model-policy.d.ts +27 -0
- package/dist/local-model-policy.js +84 -0
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +33 -21
- package/dist/model-bridge.d.ts +13 -1
- package/dist/model-bridge.js +410 -23
- package/dist/orchestrator-supervisor.d.ts +56 -0
- package/dist/orchestrator-supervisor.js +179 -1
- package/dist/plan-proposal.d.ts +115 -0
- package/dist/plan-proposal.js +1073 -0
- package/dist/run-ledger.js +3 -3
- package/dist/runtime-command.d.ts +8 -0
- package/dist/runtime-command.js +38 -6
- package/dist/runtime-executor.d.ts +20 -1
- package/dist/runtime-executor.js +737 -172
- package/dist/runtime-profile.d.ts +32 -0
- package/dist/runtime-profile.js +89 -13
- package/dist/runtime-tool-specs.d.ts +39 -0
- package/dist/runtime-tool-specs.js +144 -28
- package/dist/safe-edit.d.ts +7 -0
- package/dist/safe-edit.js +163 -37
- package/dist/schemas.js +48 -1
- package/dist/server.js +51 -0
- package/dist/shared.d.ts +3 -2
- package/dist/shared.js +2 -0
- package/dist/status-events.js +9 -6
- package/dist/store/ace-packed-store.d.ts +3 -2
- package/dist/store/ace-packed-store.js +188 -110
- package/dist/store/bootstrap-store.d.ts +2 -1
- package/dist/store/bootstrap-store.js +102 -83
- package/dist/store/cache-workspace.js +11 -5
- package/dist/store/materializers/context-snapshot-materializer.js +6 -2
- package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
- package/dist/store/materializers/hook-context-materializer.js +11 -21
- package/dist/store/materializers/host-file-materializer.js +6 -0
- package/dist/store/materializers/projection-manager.d.ts +0 -1
- package/dist/store/materializers/projection-manager.js +5 -13
- package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
- package/dist/store/materializers/vericify-projector.d.ts +7 -7
- package/dist/store/materializers/vericify-projector.js +11 -11
- package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
- package/dist/store/repositories/local-model-runtime-repository.js +242 -6
- package/dist/store/repositories/vericify-repository.d.ts +1 -1
- package/dist/store/skills-install.d.ts +4 -0
- package/dist/store/skills-install.js +21 -12
- package/dist/store/state-reader.d.ts +2 -0
- package/dist/store/state-reader.js +20 -0
- package/dist/store/store-artifacts.d.ts +7 -0
- package/dist/store/store-artifacts.js +27 -1
- package/dist/store/store-authority-audit.d.ts +18 -1
- package/dist/store/store-authority-audit.js +115 -5
- package/dist/store/store-snapshot.d.ts +3 -0
- package/dist/store/store-snapshot.js +22 -2
- package/dist/store/workspace-store-paths.d.ts +39 -0
- package/dist/store/workspace-store-paths.js +94 -0
- package/dist/store/write-coordinator.d.ts +65 -0
- package/dist/store/write-coordinator.js +386 -0
- package/dist/todo-state.js +5 -5
- package/dist/tools-agent.d.ts +20 -0
- package/dist/tools-agent.js +789 -25
- package/dist/tools-discovery.js +136 -1
- package/dist/tools-files.d.ts +7 -0
- package/dist/tools-files.js +1002 -11
- package/dist/tools-framework.js +105 -66
- package/dist/tools-handoff.js +2 -2
- package/dist/tools-lifecycle.js +4 -4
- package/dist/tools-memory.js +6 -6
- package/dist/tools-todo.js +2 -2
- package/dist/tracker-adapters.d.ts +1 -1
- package/dist/tracker-adapters.js +13 -18
- package/dist/tracker-sync.js +5 -3
- package/dist/tui/agent-runner.js +3 -1
- package/dist/tui/chat.js +103 -7
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.js +43 -0
- package/dist/tui/index.js +10 -1
- package/dist/tui/layout.d.ts +20 -0
- package/dist/tui/layout.js +31 -1
- package/dist/tui/local-model-contract.d.ts +6 -2
- package/dist/tui/local-model-contract.js +16 -3
- package/dist/tui/ollama.d.ts +8 -1
- package/dist/tui/ollama.js +53 -12
- package/dist/tui/openai-compatible.d.ts +13 -0
- package/dist/tui/openai-compatible.js +305 -5
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +35 -11
- package/dist/vericify-bridge.d.ts +6 -1
- package/dist/vericify-bridge.js +27 -3
- package/dist/workspace-manager.d.ts +30 -3
- package/dist/workspace-manager.js +257 -27
- package/package.json +1 -2
- package/dist/internal-tool-runtime.d.ts +0 -21
- package/dist/internal-tool-runtime.js +0 -136
- package/dist/store/workspace-snapshot.d.ts +0 -26
- package/dist/store/workspace-snapshot.js +0 -107
package/dist/safe-edit.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Safe-edit module: copy → modify → validate → swap pattern.
|
|
3
3
|
* Prevents bricking by never leaving core files in a broken state.
|
|
4
4
|
*/
|
|
5
|
-
import { copyFileSync, existsSync, mkdirSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
5
|
+
import { copyFileSync, cpSync, existsSync, mkdtempSync, mkdirSync, readFileSync, renameSync, rmSync, symlinkSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
6
6
|
import { createHash } from "node:crypto";
|
|
7
|
-
import { dirname, resolve } from "node:path";
|
|
7
|
+
import { basename, dirname, relative, resolve } from "node:path";
|
|
8
8
|
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
9
10
|
import { WORKSPACE_ROOT, safeRead, wsPath } from "./helpers.js";
|
|
10
11
|
import { isInside, isReadError, normalizeRelPath } from "./shared.js";
|
|
11
12
|
const STAGING_DIR = ".ace-staging";
|
|
@@ -19,14 +20,56 @@ function ensureDir(dirPath) {
|
|
|
19
20
|
if (!existsSync(dirPath))
|
|
20
21
|
mkdirSync(dirPath, { recursive: true });
|
|
21
22
|
}
|
|
22
|
-
function
|
|
23
|
+
function resolveWorkspaceTarget(inputPath) {
|
|
24
|
+
const absPath = resolve(WORKSPACE_ROOT, inputPath);
|
|
25
|
+
if (!isInside(WORKSPACE_ROOT, absPath)) {
|
|
26
|
+
return { error: `Path escapes workspace root: ${inputPath}`, absPath };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
absPath,
|
|
30
|
+
relPath: normalizeRelPath(relative(WORKSPACE_ROOT, absPath)),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function shouldCopyIntoStaging(src) {
|
|
34
|
+
const rel = normalizeRelPath(relative(WORKSPACE_ROOT, src));
|
|
35
|
+
if (!rel || rel === ".")
|
|
36
|
+
return true;
|
|
37
|
+
const first = rel.split("/")[0];
|
|
38
|
+
return !new Set([".ace-staging", ".git", "node_modules", ".npm-cache"]).has(first);
|
|
39
|
+
}
|
|
40
|
+
function prepareStagedWorkspace(stagingDir) {
|
|
41
|
+
const stagedWorkspace = mkdtempSync(resolve(tmpdir(), "ace-staged-workspace-"));
|
|
42
|
+
rmSync(stagedWorkspace, { recursive: true, force: true });
|
|
43
|
+
mkdirSync(stagedWorkspace, { recursive: true });
|
|
44
|
+
cpSync(WORKSPACE_ROOT, stagedWorkspace, {
|
|
45
|
+
recursive: true,
|
|
46
|
+
dereference: false,
|
|
47
|
+
filter: (src) => shouldCopyIntoStaging(src),
|
|
48
|
+
});
|
|
49
|
+
const rootNodeModules = resolve(WORKSPACE_ROOT, "node_modules");
|
|
50
|
+
if (existsSync(rootNodeModules)) {
|
|
51
|
+
symlinkSync(rootNodeModules, resolve(stagedWorkspace, "node_modules"), "dir");
|
|
52
|
+
}
|
|
53
|
+
writeFileSync(resolve(stagingDir, "workspace-path.txt"), stagedWorkspace, "utf-8");
|
|
54
|
+
return stagedWorkspace;
|
|
55
|
+
}
|
|
56
|
+
function workspacePathIn(root, relPath) {
|
|
57
|
+
return resolve(root, relPath);
|
|
58
|
+
}
|
|
59
|
+
function swapTargetContent(absPath, content) {
|
|
60
|
+
ensureDir(dirname(absPath));
|
|
61
|
+
const swapPath = `${absPath}.ace-swap.${process.pid}.${Date.now()}`;
|
|
62
|
+
writeFileSync(swapPath, content, "utf-8");
|
|
63
|
+
renameSync(swapPath, absPath);
|
|
64
|
+
}
|
|
65
|
+
function runValidation(validationCmd, testCmd, cwd = WORKSPACE_ROOT) {
|
|
23
66
|
let validation_passed;
|
|
24
67
|
let validation_output;
|
|
25
68
|
let test_passed;
|
|
26
69
|
let test_output;
|
|
27
70
|
if (validationCmd) {
|
|
28
71
|
const res = spawnSync("sh", ["-c", validationCmd], {
|
|
29
|
-
cwd
|
|
72
|
+
cwd,
|
|
30
73
|
encoding: "utf-8",
|
|
31
74
|
timeout: 60_000,
|
|
32
75
|
maxBuffer: 4 * 1024 * 1024,
|
|
@@ -36,7 +79,7 @@ function runValidation(validationCmd, testCmd) {
|
|
|
36
79
|
}
|
|
37
80
|
if (testCmd && (validation_passed === undefined || validation_passed)) {
|
|
38
81
|
const res = spawnSync("sh", ["-c", testCmd], {
|
|
39
|
-
cwd
|
|
82
|
+
cwd,
|
|
40
83
|
encoding: "utf-8",
|
|
41
84
|
timeout: 120_000,
|
|
42
85
|
maxBuffer: 4 * 1024 * 1024,
|
|
@@ -51,84 +94,80 @@ function runValidation(validationCmd, testCmd) {
|
|
|
51
94
|
// Safe edit (copy → modify → validate → swap)
|
|
52
95
|
// ────────────────────────────────────────────────────────────────────
|
|
53
96
|
export function safeEditFile(input) {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
if (!isInside(WORKSPACE_ROOT, absPath)) {
|
|
97
|
+
const target = resolveWorkspaceTarget(input.path);
|
|
98
|
+
if ("error" in target) {
|
|
57
99
|
return {
|
|
58
100
|
ok: false,
|
|
59
|
-
original_path: absPath,
|
|
101
|
+
original_path: target.absPath,
|
|
60
102
|
staging_path: "",
|
|
61
103
|
original_hash: "none",
|
|
62
104
|
new_hash: "none",
|
|
63
|
-
error:
|
|
105
|
+
error: target.error,
|
|
64
106
|
restored: false,
|
|
65
107
|
};
|
|
66
108
|
}
|
|
67
|
-
const relPath =
|
|
109
|
+
const { absPath, relPath } = target;
|
|
68
110
|
const timestamp = Date.now();
|
|
69
111
|
const originalContent = safeRead(input.path);
|
|
70
112
|
const isNew = isReadError(originalContent);
|
|
71
113
|
const originalHash = isNew ? "none" : fileHash(originalContent);
|
|
72
|
-
const
|
|
114
|
+
const stagedNewHash = fileHash(input.content);
|
|
73
115
|
// Step 1: Create staging directory
|
|
74
116
|
const stagingDir = wsPath(STAGING_DIR, `${timestamp}-${isNew ? "new" : originalHash.slice(0, 8)}`);
|
|
75
117
|
ensureDir(stagingDir);
|
|
76
118
|
const flatName = relPath.replace(/\//g, "__");
|
|
77
119
|
const stagingNew = resolve(stagingDir, `new__${flatName}`);
|
|
78
120
|
writeFileSync(stagingNew, input.content, "utf-8");
|
|
121
|
+
const stagedWorkspace = prepareStagedWorkspace(stagingDir);
|
|
122
|
+
const stagedTarget = workspacePathIn(stagedWorkspace, relPath);
|
|
123
|
+
ensureDir(dirname(stagedTarget));
|
|
124
|
+
writeFileSync(stagedTarget, input.content, "utf-8");
|
|
79
125
|
// Step 2: If file exists, copy original to staging + backup
|
|
80
126
|
let backupPath;
|
|
81
127
|
if (!isNew) {
|
|
82
128
|
const stagingOriginal = resolve(stagingDir, `original__${flatName}`);
|
|
83
129
|
writeFileSync(stagingOriginal, originalContent, "utf-8");
|
|
84
|
-
if (input.backup !== false) {
|
|
85
|
-
backupPath = `${absPath}.ace-backup.${originalHash.slice(0, 8)}`;
|
|
86
|
-
copyFileSync(absPath, backupPath);
|
|
87
|
-
}
|
|
88
130
|
}
|
|
89
|
-
// Step 3:
|
|
90
|
-
|
|
91
|
-
writeFileSync(absPath, input.content, "utf-8");
|
|
92
|
-
// Step 4: Run validation/tests
|
|
131
|
+
// Step 3: Run validation/tests against the staged workspace.
|
|
132
|
+
let check;
|
|
93
133
|
if (input.validation_command || input.test_command) {
|
|
94
|
-
|
|
134
|
+
check = runValidation(input.validation_command, input.test_command, stagedWorkspace);
|
|
95
135
|
if (!check.passed) {
|
|
96
|
-
// ROLLBACK: restore original content
|
|
97
|
-
if (!isNew) {
|
|
98
|
-
writeFileSync(absPath, originalContent, "utf-8");
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
// Remove newly created file
|
|
102
|
-
try {
|
|
103
|
-
unlinkSync(absPath);
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
/* best effort */
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
136
|
return {
|
|
110
137
|
ok: false,
|
|
111
138
|
original_path: absPath,
|
|
112
139
|
staging_path: stagingDir,
|
|
113
140
|
backup_path: backupPath,
|
|
114
141
|
original_hash: originalHash,
|
|
115
|
-
new_hash:
|
|
142
|
+
new_hash: stagedNewHash,
|
|
116
143
|
validation_passed: check.validation_passed,
|
|
117
144
|
validation_output: check.validation_output,
|
|
118
145
|
test_passed: check.test_passed,
|
|
119
146
|
test_output: check.test_output,
|
|
120
|
-
error: "Validation/test failed —
|
|
147
|
+
error: "Validation/test failed — staged change not promoted",
|
|
121
148
|
restored: true,
|
|
122
149
|
};
|
|
123
150
|
}
|
|
124
151
|
}
|
|
152
|
+
// Step 4: Promote staged content to the real target only after checks pass.
|
|
153
|
+
if (!isNew && input.backup !== false) {
|
|
154
|
+
backupPath = `${absPath}.ace-backup.${originalHash.slice(0, 8)}`;
|
|
155
|
+
copyFileSync(absPath, backupPath);
|
|
156
|
+
}
|
|
157
|
+
const promotedContent = readFileSync(stagedTarget, "utf-8");
|
|
158
|
+
const promotedHash = fileHash(promotedContent);
|
|
159
|
+
swapTargetContent(absPath, promotedContent);
|
|
125
160
|
return {
|
|
126
161
|
ok: true,
|
|
127
162
|
original_path: absPath,
|
|
128
163
|
staging_path: stagingDir,
|
|
129
164
|
backup_path: backupPath,
|
|
130
165
|
original_hash: originalHash,
|
|
131
|
-
new_hash:
|
|
166
|
+
new_hash: promotedHash,
|
|
167
|
+
validation_passed: check?.validation_passed,
|
|
168
|
+
validation_output: check?.validation_output,
|
|
169
|
+
test_passed: check?.test_passed,
|
|
170
|
+
test_output: check?.test_output,
|
|
132
171
|
};
|
|
133
172
|
}
|
|
134
173
|
// ────────────────────────────────────────────────────────────────────
|
|
@@ -252,4 +291,91 @@ export function diffFiles(pathA, pathB) {
|
|
|
252
291
|
}
|
|
253
292
|
return diffContents(contentA, contentB);
|
|
254
293
|
}
|
|
294
|
+
export function applyPatch(input) {
|
|
295
|
+
const target = resolveWorkspaceTarget(input.path);
|
|
296
|
+
if ("error" in target) {
|
|
297
|
+
return {
|
|
298
|
+
ok: false,
|
|
299
|
+
original_path: target.absPath,
|
|
300
|
+
staging_path: "",
|
|
301
|
+
original_hash: "none",
|
|
302
|
+
new_hash: "none",
|
|
303
|
+
error: target.error,
|
|
304
|
+
restored: false,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
const { absPath, relPath } = target;
|
|
308
|
+
const originalContent = safeRead(input.path);
|
|
309
|
+
const isNew = isReadError(originalContent);
|
|
310
|
+
const originalHash = isNew ? "none" : fileHash(originalContent);
|
|
311
|
+
const timestamp = Date.now();
|
|
312
|
+
const stagingDir = wsPath(STAGING_DIR, `${timestamp}-patch-${isNew ? "new" : originalHash.slice(0, 8)}`);
|
|
313
|
+
ensureDir(stagingDir);
|
|
314
|
+
const stagedWorkspace = prepareStagedWorkspace(stagingDir);
|
|
315
|
+
const stagedTarget = workspacePathIn(stagedWorkspace, relPath);
|
|
316
|
+
ensureDir(dirname(stagedTarget));
|
|
317
|
+
if (isNew && !existsSync(stagedTarget))
|
|
318
|
+
writeFileSync(stagedTarget, "", "utf-8");
|
|
319
|
+
const tmpPatch = resolve(stagingDir, `${basename(relPath)}.${timestamp}.patch`);
|
|
320
|
+
try {
|
|
321
|
+
writeFileSync(tmpPatch, input.patch, "utf8");
|
|
322
|
+
const patchResult = spawnSync("patch", [stagedTarget, tmpPatch], {
|
|
323
|
+
encoding: "utf8",
|
|
324
|
+
cwd: stagedWorkspace,
|
|
325
|
+
});
|
|
326
|
+
if (patchResult.status !== 0) {
|
|
327
|
+
return {
|
|
328
|
+
ok: false,
|
|
329
|
+
original_path: absPath,
|
|
330
|
+
staging_path: stagingDir,
|
|
331
|
+
original_hash: originalHash,
|
|
332
|
+
new_hash: "none",
|
|
333
|
+
error: `patch command failed: ${patchResult.stderr || patchResult.stdout}`,
|
|
334
|
+
restored: true,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const patchedContent = readFileSync(stagedTarget, "utf-8");
|
|
338
|
+
const newHash = fileHash(patchedContent);
|
|
339
|
+
const check = runValidation(input.validation_command, input.test_command, stagedWorkspace);
|
|
340
|
+
if (!check.passed) {
|
|
341
|
+
return {
|
|
342
|
+
ok: false,
|
|
343
|
+
original_path: absPath,
|
|
344
|
+
staging_path: stagingDir,
|
|
345
|
+
original_hash: originalHash,
|
|
346
|
+
new_hash: newHash,
|
|
347
|
+
validation_passed: check.validation_passed,
|
|
348
|
+
validation_output: check.validation_output,
|
|
349
|
+
test_passed: check.test_passed,
|
|
350
|
+
test_output: check.test_output,
|
|
351
|
+
error: "Validation/test failed — staged patch not promoted",
|
|
352
|
+
restored: true,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
let backupPath;
|
|
356
|
+
if (!isNew) {
|
|
357
|
+
backupPath = `${absPath}.ace-backup.${originalHash.slice(0, 8)}`;
|
|
358
|
+
copyFileSync(absPath, backupPath);
|
|
359
|
+
}
|
|
360
|
+
swapTargetContent(absPath, patchedContent);
|
|
361
|
+
return {
|
|
362
|
+
ok: true,
|
|
363
|
+
original_path: absPath,
|
|
364
|
+
staging_path: stagingDir,
|
|
365
|
+
backup_path: backupPath,
|
|
366
|
+
original_hash: originalHash,
|
|
367
|
+
new_hash: newHash,
|
|
368
|
+
validation_passed: check.validation_passed,
|
|
369
|
+
validation_output: check.validation_output,
|
|
370
|
+
test_passed: check.test_passed,
|
|
371
|
+
test_output: check.test_output,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
finally {
|
|
375
|
+
try {
|
|
376
|
+
unlinkSync(tmpPatch);
|
|
377
|
+
}
|
|
378
|
+
catch { /* ignore */ }
|
|
379
|
+
}
|
|
380
|
+
}
|
|
255
381
|
//# sourceMappingURL=safe-edit.js.map
|
package/dist/schemas.js
CHANGED
|
@@ -186,6 +186,8 @@ const workspaceSessionRecordSchema = z
|
|
|
186
186
|
last_error: z.string().optional(),
|
|
187
187
|
created_at: z.string().datetime({ offset: true }),
|
|
188
188
|
updated_at: z.string().datetime({ offset: true }),
|
|
189
|
+
hook_health: z.enum(["ok", "degraded", "failed"]).optional(),
|
|
190
|
+
hook_summary: z.string().optional(),
|
|
189
191
|
hooks: z
|
|
190
192
|
.object({
|
|
191
193
|
after_create: workspaceHookStateSchema,
|
|
@@ -292,6 +294,32 @@ const runtimeToolExecutorSchema = z
|
|
|
292
294
|
env: z.record(z.string(), z.string()).optional(),
|
|
293
295
|
})
|
|
294
296
|
.strict();
|
|
297
|
+
const runtimeToolMcpServerSchema = z
|
|
298
|
+
.object({
|
|
299
|
+
transport: z.enum(["stdio", "http"]),
|
|
300
|
+
command: z.string().optional(),
|
|
301
|
+
args: z.array(z.string()).optional(),
|
|
302
|
+
url: z.string().url().optional(),
|
|
303
|
+
env: z.record(z.string(), z.string()).optional(),
|
|
304
|
+
tool_allowlist: z.array(z.string()).optional(),
|
|
305
|
+
})
|
|
306
|
+
.strict()
|
|
307
|
+
.superRefine((value, ctx) => {
|
|
308
|
+
if (value.transport === "stdio" && !value.command?.trim()) {
|
|
309
|
+
ctx.addIssue({
|
|
310
|
+
code: z.ZodIssueCode.custom,
|
|
311
|
+
path: ["command"],
|
|
312
|
+
message: "stdio MCP servers require command",
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (value.transport === "http" && !value.url?.trim()) {
|
|
316
|
+
ctx.addIssue({
|
|
317
|
+
code: z.ZodIssueCode.custom,
|
|
318
|
+
path: ["url"],
|
|
319
|
+
message: "http MCP servers require url",
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
});
|
|
295
323
|
const runtimeToolSpecSchema = z
|
|
296
324
|
.object({
|
|
297
325
|
name: NON_EMPTY,
|
|
@@ -300,6 +328,7 @@ const runtimeToolSpecSchema = z
|
|
|
300
328
|
success_schema: runtimeToolSchemaNodeSchema.optional(),
|
|
301
329
|
failure_schema: runtimeToolSchemaNodeSchema.optional(),
|
|
302
330
|
executor: runtimeToolExecutorSchema,
|
|
331
|
+
mcp_server: runtimeToolMcpServerSchema.optional(),
|
|
303
332
|
})
|
|
304
333
|
.strict();
|
|
305
334
|
const runtimeToolSpecRegistrySchema = z
|
|
@@ -352,6 +381,15 @@ const unattendedTurnRecordSchema = z
|
|
|
352
381
|
stdout: z.string(),
|
|
353
382
|
stderr: z.string(),
|
|
354
383
|
tool_calls: z.array(unattendedToolCallRecordSchema),
|
|
384
|
+
turn_outcome: z.enum(["no_op_success", "meaningful_completion", "escalation_blocker"]).optional(),
|
|
385
|
+
outcome_reason: z.string().optional(),
|
|
386
|
+
})
|
|
387
|
+
.strict();
|
|
388
|
+
const runtimeOutputPolicySchema = z
|
|
389
|
+
.object({
|
|
390
|
+
emit_to: z.array(z.enum(["tui", "tracker", "handoff", "vericify"])),
|
|
391
|
+
silent_unless_blocked: z.boolean(),
|
|
392
|
+
require_approval_before_emit: z.boolean(),
|
|
355
393
|
})
|
|
356
394
|
.strict();
|
|
357
395
|
const runtimeExecutorSessionRecordSchema = z
|
|
@@ -377,6 +415,8 @@ const runtimeExecutorSessionRecordSchema = z
|
|
|
377
415
|
last_error: z.string().optional(),
|
|
378
416
|
cleanup_error: z.string().optional(),
|
|
379
417
|
workspace_cleanup_status: z.enum(["pending", "removed", "archived", "failed"]),
|
|
418
|
+
validated_plan_id: z.string().optional(),
|
|
419
|
+
output_policy: runtimeOutputPolicySchema.optional(),
|
|
380
420
|
turns: z.array(unattendedTurnRecordSchema),
|
|
381
421
|
})
|
|
382
422
|
.strict();
|
|
@@ -394,7 +434,7 @@ const vericifyProcessPostSchema = z
|
|
|
394
434
|
branch_id: z.string().optional(),
|
|
395
435
|
lane_id: z.string().optional(),
|
|
396
436
|
agent_id: NON_EMPTY,
|
|
397
|
-
kind: z.enum(["intent", "progress", "blocker", "handoff_note", "stale_ack", "completion"]),
|
|
437
|
+
kind: z.enum(["intent", "progress", "blocker", "handoff_note", "stale_ack", "completion", "plan_proposal", "plan_quality_assessment"]),
|
|
398
438
|
summary: NON_EMPTY,
|
|
399
439
|
tool_refs: z.array(z.string()),
|
|
400
440
|
evidence_refs: z.array(z.string()),
|
|
@@ -453,6 +493,13 @@ const vericifyBridgeSnapshotSchema = z
|
|
|
453
493
|
})
|
|
454
494
|
.strict(),
|
|
455
495
|
active_run_refs: z.array(vericifyBridgeActiveRunRefSchema),
|
|
496
|
+
ace_runtime_enrichment: z
|
|
497
|
+
.object({
|
|
498
|
+
live_session_id: z.string().optional(),
|
|
499
|
+
last_turn_outcome: z.string().optional(),
|
|
500
|
+
last_turn_outcome_reason: z.string().optional(),
|
|
501
|
+
})
|
|
502
|
+
.optional(),
|
|
456
503
|
})
|
|
457
504
|
.strict();
|
|
458
505
|
/**
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
3
5
|
import { buildServerInstructions, installAceToolGovernance, } from "./ace-server-instructions.js";
|
|
4
6
|
import { resolveWorkspaceRoot } from "./helpers.js";
|
|
7
|
+
import { openStore } from "./store/ace-packed-store.js";
|
|
8
|
+
import { getWorkspaceStorePath } from "./store/store-snapshot.js";
|
|
5
9
|
import { registerPrompts } from "./prompts.js";
|
|
6
10
|
import { registerResources } from "./resources.js";
|
|
7
11
|
import { registerTools } from "./tools.js";
|
|
@@ -24,7 +28,54 @@ export function createAceServer(options = {}) {
|
|
|
24
28
|
registerTools(server);
|
|
25
29
|
return server;
|
|
26
30
|
}
|
|
31
|
+
function appendEmergencyMcpStatusEvent(workspaceRoot, message) {
|
|
32
|
+
const event = {
|
|
33
|
+
schema_version: "1.0.0",
|
|
34
|
+
event_id: `evt-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
35
|
+
trace_id: `trace-${Date.now()}`,
|
|
36
|
+
timestamp: new Date().toISOString(),
|
|
37
|
+
source_module: "capability-ops",
|
|
38
|
+
event_type: "MCP_BINARY_PARSE_ERROR",
|
|
39
|
+
status: "blocked",
|
|
40
|
+
objective_id: "mcp-stdio-startup",
|
|
41
|
+
payload: {
|
|
42
|
+
reason_code: "mcp_binary_parse_error",
|
|
43
|
+
summary: message,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
const target = join(workspaceRoot, "agent-state", "STATUS_EVENTS.ndjson");
|
|
47
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
48
|
+
writeFileSync(target, `${JSON.stringify(event)}\n`, { flag: "a" });
|
|
49
|
+
}
|
|
50
|
+
async function verifyMcpStoreStartup(workspaceRoot, logStartup) {
|
|
51
|
+
const canonicalStore = getWorkspaceStorePath(workspaceRoot);
|
|
52
|
+
const legacyStore = join(workspaceRoot, ".agents", "ACE", "ace-state.ace");
|
|
53
|
+
const candidates = [canonicalStore, legacyStore].filter((storePath, index, all) => existsSync(storePath) && all.indexOf(storePath) === index);
|
|
54
|
+
if (candidates.length === 0)
|
|
55
|
+
return true;
|
|
56
|
+
const errors = [];
|
|
57
|
+
for (const storePath of candidates) {
|
|
58
|
+
try {
|
|
59
|
+
const store = await openStore(storePath, { readOnly: true });
|
|
60
|
+
await store.close();
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
errors.push(`${storePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const message = `ACE MCP startup could not open ace-state.ace as ACEPACK; refusing stdio startup without crashing. ` +
|
|
68
|
+
`reason_code=mcp_binary_parse_error. ${errors.join(" | ")}`;
|
|
69
|
+
appendEmergencyMcpStatusEvent(workspaceRoot, message);
|
|
70
|
+
if (logStartup)
|
|
71
|
+
console.error(message);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
27
74
|
export async function startStdioServer(logStartup = true) {
|
|
75
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
76
|
+
if (!(await verifyMcpStoreStartup(workspaceRoot, logStartup))) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
28
79
|
try {
|
|
29
80
|
const backfill = await backfillHandoffsIntoScheduler();
|
|
30
81
|
if (logStartup && backfill.scanned > 0) {
|
package/dist/shared.d.ts
CHANGED
|
@@ -19,10 +19,10 @@ export declare function looksLikeSwarmHandoffPath(path: string): boolean;
|
|
|
19
19
|
export declare const ROLE_TITLES: Record<string, string>;
|
|
20
20
|
export declare function getRoleTitle(role: string): string;
|
|
21
21
|
export declare const MCP_CLIENT_ENUM: z.ZodEnum<{
|
|
22
|
-
claude: "claude";
|
|
23
|
-
cursor: "cursor";
|
|
24
22
|
codex: "codex";
|
|
25
23
|
vscode: "vscode";
|
|
24
|
+
claude: "claude";
|
|
25
|
+
cursor: "cursor";
|
|
26
26
|
antigravity: "antigravity";
|
|
27
27
|
}>;
|
|
28
28
|
export declare const ROLE_ENUM: z.ZodEnum<{
|
|
@@ -43,6 +43,7 @@ export declare const ROLE_ENUM: z.ZodEnum<{
|
|
|
43
43
|
observability: "observability";
|
|
44
44
|
eval: "eval";
|
|
45
45
|
release: "release";
|
|
46
|
+
planner: "planner";
|
|
46
47
|
}>;
|
|
47
48
|
export declare const HANDOFF_VALIDATION_MODE: z.ZodOptional<z.ZodEnum<{
|
|
48
49
|
"agent-state": "agent-state";
|
package/dist/shared.js
CHANGED
|
@@ -57,6 +57,7 @@ export const ROLE_TITLES = {
|
|
|
57
57
|
observability: "Observability",
|
|
58
58
|
eval: "Eval",
|
|
59
59
|
release: "Release",
|
|
60
|
+
planner: "Planner",
|
|
60
61
|
};
|
|
61
62
|
export function getRoleTitle(role) {
|
|
62
63
|
return ROLE_TITLES[role] ?? role.toUpperCase();
|
|
@@ -89,6 +90,7 @@ export const ROLE_ENUM = z.enum([
|
|
|
89
90
|
"observability",
|
|
90
91
|
"eval",
|
|
91
92
|
"release",
|
|
93
|
+
"planner",
|
|
92
94
|
]);
|
|
93
95
|
export const HANDOFF_VALIDATION_MODE = z
|
|
94
96
|
.enum(["auto", "swarm", "agent-state"])
|
package/dist/status-events.js
CHANGED
|
@@ -8,7 +8,7 @@ import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
|
8
8
|
import { TrackerRepository } from "./store/repositories/tracker-repository.js";
|
|
9
9
|
import { getWorkspaceStorePath, listStoreKeysSync, readStoreJsonSync, storeExistsSync, } from "./store/store-snapshot.js";
|
|
10
10
|
import { operationalArtifactVirtualPath } from "./store/store-artifacts.js";
|
|
11
|
-
import {
|
|
11
|
+
import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
|
|
12
12
|
export const STATUS_EVENTS_REL_PATH = "agent-state/STATUS_EVENTS.ndjson";
|
|
13
13
|
const STATUS_EVENTS_ARCHIVE_REL = "agent-state/STATUS_EVENTS-archive.ndjson";
|
|
14
14
|
const MAX_EVENT_LINES = 2000;
|
|
@@ -94,7 +94,7 @@ async function mirrorStatusEventToStore(root, event) {
|
|
|
94
94
|
const storePath = getWorkspaceStorePath(root);
|
|
95
95
|
if (!existsSync(storePath))
|
|
96
96
|
return;
|
|
97
|
-
await
|
|
97
|
+
await withStoreWriteCoordinator(storePath, async () => {
|
|
98
98
|
const store = await openStore(storePath);
|
|
99
99
|
try {
|
|
100
100
|
const tracker = new TrackerRepository(store);
|
|
@@ -120,7 +120,7 @@ async function mirrorStatusEventToStore(root, event) {
|
|
|
120
120
|
finally {
|
|
121
121
|
await store.close();
|
|
122
122
|
}
|
|
123
|
-
});
|
|
123
|
+
}, { operation_label: "mirrorStatusEventToStore" });
|
|
124
124
|
}
|
|
125
125
|
function scheduleStatusEventMirror(event) {
|
|
126
126
|
const root = workspaceRoot();
|
|
@@ -135,7 +135,7 @@ export async function waitForPendingStatusEventMirrors() {
|
|
|
135
135
|
}
|
|
136
136
|
async function appendStatusEventStoreBacked(root, event) {
|
|
137
137
|
const storePath = getWorkspaceStorePath(root);
|
|
138
|
-
return
|
|
138
|
+
return withStoreWriteCoordinator(storePath, async () => {
|
|
139
139
|
const store = await openStore(storePath);
|
|
140
140
|
try {
|
|
141
141
|
const tracker = new TrackerRepository(store);
|
|
@@ -165,9 +165,10 @@ async function appendStatusEventStoreBacked(root, event) {
|
|
|
165
165
|
finally {
|
|
166
166
|
await store.close();
|
|
167
167
|
}
|
|
168
|
-
});
|
|
168
|
+
}, { operation_label: "appendStatusEventStoreBacked" });
|
|
169
169
|
}
|
|
170
170
|
export function appendStatusEvent(input) {
|
|
171
|
+
const root = workspaceRoot();
|
|
171
172
|
const event = buildStatusEvent(input);
|
|
172
173
|
validateStatusEvent(event);
|
|
173
174
|
// Atomic append under file lock to prevent lost writes under parallelism
|
|
@@ -176,7 +177,9 @@ export function appendStatusEvent(input) {
|
|
|
176
177
|
const combined = existing.length > 0 ? `${existing}\n${line}\n` : `${line}\n`;
|
|
177
178
|
const next = rotateIfNeeded(combined);
|
|
178
179
|
const path = safeWriteWorkspaceFile(STATUS_EVENTS_REL_PATH, next);
|
|
179
|
-
|
|
180
|
+
if (storeExistsSync(root)) {
|
|
181
|
+
scheduleStatusEventMirror(event);
|
|
182
|
+
}
|
|
180
183
|
return { path, event };
|
|
181
184
|
}
|
|
182
185
|
/**
|
|
@@ -59,6 +59,9 @@ export declare class AcePackedStore implements IAcePackedStore {
|
|
|
59
59
|
private fh;
|
|
60
60
|
private kvIndex;
|
|
61
61
|
private kvChunkEnd;
|
|
62
|
+
private pendingKv;
|
|
63
|
+
private pendingDeletes;
|
|
64
|
+
private dirty;
|
|
62
65
|
private committed;
|
|
63
66
|
private pending;
|
|
64
67
|
private evtBaseId;
|
|
@@ -67,8 +70,6 @@ export declare class AcePackedStore implements IAcePackedStore {
|
|
|
67
70
|
}): Promise<void>;
|
|
68
71
|
private _initNew;
|
|
69
72
|
private _loadExisting;
|
|
70
|
-
/** Read a KV blob directly from a loaded file buffer (used during migration). */
|
|
71
|
-
private _readKvBlobDirect;
|
|
72
73
|
commit(): Promise<void>;
|
|
73
74
|
/**
|
|
74
75
|
* Compacts the KV chunk region — removes dead space left by overwritten keys.
|