@voybio/ace-swarm 2.4.8 → 2.4.10
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 +52 -14
- package/README.md +14 -10
- package/assets/.agents/ACE/ACE_coders/AGENTS.md +23 -30
- package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +21 -30
- package/assets/.agents/skills/landing-review-watcher/SKILL.md +1 -1
- package/assets/.agents/skills/learn-from-work/SKILL.md +41 -0
- package/assets/agent-state/ACE_WORKFLOW.md +25 -2
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +143 -2
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +2 -2
- package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +10 -0
- package/assets/eval-battery/battery.json +1 -1
- package/assets/instructions/ACE_Coder.instructions.md +9 -13
- package/assets/scripts/ace-hook-dispatch.mjs +16 -411
- package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +1 -2
- package/assets/vendor/README.md +9 -0
- package/assets/vendor/vericify-reader.mjs +272 -0
- package/assets/vendor/vericify-reader.provenance.json +10 -0
- package/dist/ace-autonomy.d.ts +58 -0
- package/dist/ace-autonomy.js +211 -6
- package/dist/ace-internal-tools.d.ts +1 -0
- package/dist/ace-internal-tools.js +3 -3
- package/dist/ace-server-instructions.d.ts +3 -3
- package/dist/ace-server-instructions.js +46 -192
- package/dist/cli.js +44 -6
- package/dist/experience-reflector.d.ts +7 -0
- package/dist/experience-reflector.js +39 -0
- package/dist/goal-engine.d.ts +64 -7
- package/dist/goal-engine.js +197 -41
- package/dist/handoff-registry.d.ts +10 -0
- package/dist/handoff-registry.js +59 -0
- package/dist/helpers/artifacts.d.ts +6 -2
- package/dist/helpers/artifacts.js +8 -6
- package/dist/helpers/bootstrap.js +13 -20
- package/dist/helpers/path-utils.js +2 -0
- package/dist/helpers/store-resolution.js +18 -3
- package/dist/helpers/workspace-root.d.ts +2 -0
- package/dist/helpers/workspace-root.js +9 -0
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +1 -0
- package/dist/hermes/bridge-protocol.d.ts +1 -1
- package/dist/hermes/session-manager.d.ts +17 -0
- package/dist/hermes/session-manager.js +67 -14
- package/dist/host-goal-engine.d.ts +138 -0
- package/dist/host-goal-engine.js +793 -0
- package/dist/host-goal-status.d.ts +113 -0
- package/dist/host-goal-status.js +175 -0
- package/dist/host-learning.d.ts +68 -0
- package/dist/host-learning.js +115 -0
- package/dist/host-observation.d.ts +12 -0
- package/dist/host-observation.js +91 -0
- package/dist/host-plan-revision.d.ts +14 -0
- package/dist/host-plan-revision.js +101 -0
- package/dist/host-verification-job.d.ts +12 -0
- package/dist/host-verification-job.js +183 -0
- package/dist/host-work-context.d.ts +12 -0
- package/dist/host-work-context.js +8 -0
- package/dist/job-scheduler.d.ts +58 -13
- package/dist/job-scheduler.js +298 -48
- package/dist/job-worker.d.ts +3 -1
- package/dist/job-worker.js +10 -3
- package/dist/kanban.d.ts +4 -1
- package/dist/kanban.js +44 -16
- package/dist/learned-artifacts.d.ts +64 -0
- package/dist/learned-artifacts.js +321 -0
- package/dist/learning-controls.d.ts +142 -0
- package/dist/learning-controls.js +135 -0
- package/dist/learning-input-context.d.ts +9 -0
- package/dist/learning-input-context.js +62 -0
- package/dist/learning-lifecycle.d.ts +10 -0
- package/dist/learning-lifecycle.js +11 -0
- package/dist/learning-overlays.d.ts +3 -0
- package/dist/learning-overlays.js +17 -0
- package/dist/learning-policy.d.ts +40 -0
- package/dist/learning-policy.js +38 -0
- package/dist/learning-records.d.ts +35 -0
- package/dist/learning-records.js +17 -0
- package/dist/learning-scorecard.d.ts +82 -0
- package/dist/learning-scorecard.js +86 -0
- package/dist/learning-serving.d.ts +29 -0
- package/dist/learning-serving.js +134 -0
- package/dist/linked-work.d.ts +9 -0
- package/dist/linked-work.js +28 -0
- package/dist/local-model-runtime.js +1 -1
- package/dist/mcp-command-progress.d.ts +16 -0
- package/dist/mcp-command-progress.js +70 -0
- package/dist/model-bridge.d.ts +14 -13
- package/dist/model-bridge.js +73 -41
- package/dist/orchestrator-supervisor.d.ts +111 -6
- package/dist/orchestrator-supervisor.js +67 -43
- package/dist/plan-proposal.js +3 -3
- package/dist/playbook-curator.d.ts +1 -0
- package/dist/playbook-curator.js +11 -9
- package/dist/process-identity.d.ts +8 -0
- package/dist/process-identity.js +38 -0
- package/dist/progress-command.d.ts +34 -0
- package/dist/progress-command.js +153 -0
- package/dist/prompts.js +15 -15
- package/dist/resources.js +2 -2
- package/dist/runtime-command.d.ts +8 -1
- package/dist/runtime-command.js +48 -7
- package/dist/runtime-executor.js +33 -11
- package/dist/runtime-profile-syntax.d.ts +6 -0
- package/dist/runtime-profile-syntax.js +121 -0
- package/dist/runtime-profile.d.ts +37 -5
- package/dist/runtime-profile.js +36 -129
- package/dist/runtime-tool-specs.js +1 -1
- package/dist/safe-edit.d.ts +7 -3
- package/dist/safe-edit.js +40 -47
- package/dist/schemas.js +4 -2
- package/dist/server.d.ts +2 -16
- package/dist/server.js +14 -20
- package/dist/shared.d.ts +2 -0
- package/dist/shared.js +2 -0
- package/dist/skill-auditor.d.ts +19 -0
- package/dist/skill-auditor.js +32 -2
- package/dist/skill-catalog.d.ts +4 -1
- package/dist/skill-catalog.js +6 -5
- package/dist/store/ace-packed-store.d.ts +5 -1
- package/dist/store/ace-packed-store.js +33 -1
- package/dist/store/bootstrap-store.js +19 -12
- package/dist/store/cache-workspace.d.ts +8 -0
- package/dist/store/cache-workspace.js +94 -27
- package/dist/store/importer.js +11 -20
- package/dist/store/knowledge-bake.d.ts +3 -1
- package/dist/store/knowledge-bake.js +10 -1
- package/dist/store/legacy-vericify-posts.d.ts +8 -0
- package/dist/store/legacy-vericify-posts.js +76 -0
- package/dist/store/materializers/hook-context-materializer.d.ts +2 -1
- package/dist/store/materializers/hook-context-materializer.js +6 -2
- package/dist/store/materializers/host-file-materializer.js +2 -2
- package/dist/store/materializers/projection-manager.d.ts +4 -1
- package/dist/store/materializers/projection-manager.js +10 -3
- package/dist/store/materializers/todo-syncer.d.ts +39 -20
- package/dist/store/materializers/todo-syncer.js +208 -110
- package/dist/store/materializers/vericify-projector.js +15 -26
- package/dist/store/repositories/goal-repository.d.ts +51 -2
- package/dist/store/repositories/goal-repository.js +114 -48
- package/dist/store/repositories/playbook-repository.d.ts +41 -3
- package/dist/store/repositories/playbook-repository.js +61 -5
- package/dist/store/repositories/todo-repository.d.ts +8 -2
- package/dist/store/repositories/todo-repository.js +1 -1
- package/dist/store/repositories/vericify-repository.d.ts +4 -0
- package/dist/store/repositories/vericify-repository.js +46 -0
- package/dist/store/skills-install.d.ts +1 -0
- package/dist/store/skills-install.js +20 -16
- package/dist/store/source-preservation.d.ts +24 -0
- package/dist/store/source-preservation.js +79 -0
- package/dist/store/store-artifacts.js +8 -1
- package/dist/store/test-write-boundary.d.ts +16 -0
- package/dist/store/test-write-boundary.js +175 -0
- package/dist/store/types.d.ts +5 -1
- package/dist/store/write-coordinator.js +16 -0
- package/dist/task-plan-context.d.ts +11 -0
- package/dist/task-plan-context.js +59 -0
- package/dist/todo-state.d.ts +19 -9
- package/dist/todo-state.js +204 -321
- package/dist/tools-agent.d.ts +1 -1
- package/dist/tools-agent.js +38 -62
- package/dist/tools-files.d.ts +0 -3
- package/dist/tools-files.js +21 -11
- package/dist/tools-framework.js +16 -25
- package/dist/tools-goal.d.ts +2 -4
- package/dist/tools-goal.js +119 -16
- package/dist/tools-handoff.d.ts +3 -2
- package/dist/tools-handoff.js +53 -23
- package/dist/tools-lifecycle.js +1 -1
- package/dist/tools-memory.d.ts +0 -3
- package/dist/tools-memory.js +51 -6
- package/dist/tools-scheduler.js +20 -17
- package/dist/tools-todo.js +160 -148
- package/dist/vericify-bridge.d.ts +8 -0
- package/dist/vericify-bridge.js +18 -0
- package/dist/vericify-context.d.ts +4 -0
- package/dist/vericify-context.js +26 -5
- package/dist/vericify-native.d.ts +101 -0
- package/dist/vericify-native.js +221 -0
- package/dist/verification-worker.d.ts +2 -0
- package/dist/verification-worker.js +31 -0
- package/docs/mcp-tool-reference.md +50 -10
- package/docs/troubleshooting.md +29 -0
- package/package.json +19 -6
- package/scripts/dev/run-tests-isolated.mjs +165 -0
- package/scripts/dev/run_hermes_crispr_live.mjs +1 -0
- package/scripts/dev/test-isolation-guard.mjs +20 -0
- package/scripts/dev/test-preload.mjs +153 -0
- package/scripts/dev/test-source-inventory.mjs +31 -0
- package/docs/development.md +0 -46
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Run the whole test process outside the developer's workspace, including fallbacks.
|
|
3
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, statSync, symlinkSync, writeFileSync, realpathSync, renameSync, unlinkSync } from "node:fs";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
+
import { spawn, spawnSync, execFileSync } from "node:child_process";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
+
import { testSourceFiles, findTestDependencies } from './test-source-inventory.mjs';
|
|
10
|
+
|
|
11
|
+
const source = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
12
|
+
// Darwin sockaddr_un paths are short. Keep every allocated fixture beneath a
|
|
13
|
+
// canonical short base so native control sockets fit without moving outside it.
|
|
14
|
+
const sandbox = mkdtempSync(join(process.platform === 'darwin' ? '/private/tmp' : tmpdir(), 'at-'));
|
|
15
|
+
const checkout = join(sandbox, "checkout");
|
|
16
|
+
mkdirSync(checkout);
|
|
17
|
+
const fixtures = join(sandbox, 'fixtures'); const registry = join(sandbox, 'owned-node-processes');
|
|
18
|
+
mkdirSync(fixtures); mkdirSync(registry);
|
|
19
|
+
|
|
20
|
+
function stateHashes(root) {
|
|
21
|
+
const values = {};
|
|
22
|
+
function visit(path) {
|
|
23
|
+
if (!existsSync(path)) return;
|
|
24
|
+
if (statSync(path).isDirectory()) {
|
|
25
|
+
for (const child of readdirSync(path)) visit(join(path, child));
|
|
26
|
+
} else values[relative(root, path)] = createHash("sha256").update(readFileSync(path)).digest("hex");
|
|
27
|
+
}
|
|
28
|
+
for (const name of ["agent-state", ".agents/ACE"]) visit(join(root, name));
|
|
29
|
+
return values;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const protectedBefore = stateHashes(source);
|
|
33
|
+
// Include current edits and new tests, but never copy a live store or host state.
|
|
34
|
+
const files = testSourceFiles(source);
|
|
35
|
+
for (const name of new Set(files)) {
|
|
36
|
+
if (/^(?:agent-state|\.agents|\.git|node_modules|dist)(?:\/|$)/.test(name)) continue;
|
|
37
|
+
const from = join(source, name);
|
|
38
|
+
if (!existsSync(from) || !statSync(from).isFile()) continue;
|
|
39
|
+
mkdirSync(dirname(join(checkout, name)), { recursive: true });
|
|
40
|
+
cpSync(from, join(checkout, name));
|
|
41
|
+
}
|
|
42
|
+
cpSync(join(source, "dist"), join(checkout, "dist"), { recursive: true });
|
|
43
|
+
symlinkSync(findTestDependencies(source), join(checkout, "node_modules"), "dir");
|
|
44
|
+
writeFileSync(join(checkout, '.ace-test-source-files.json'), JSON.stringify(files));
|
|
45
|
+
|
|
46
|
+
const runId = randomUUID(); const leaseFile = join(sandbox, 'lease.json'); const sessionFile = join(sandbox, 'session.json');
|
|
47
|
+
const sentinel = join(checkout, '.ace-test-protected-sentinel'); writeFileSync(sentinel, 'Protected disposable source sentinel\n');
|
|
48
|
+
const sentinelBefore = readFileSync(sentinel);
|
|
49
|
+
const nativeSentinel = join(sandbox, 'outside-fixture-sentinel');
|
|
50
|
+
writeFileSync(nativeSentinel, 'Protected disposable non-source sentinel\n');
|
|
51
|
+
const nativeSentinelBefore = readFileSync(nativeSentinel);
|
|
52
|
+
const lease = status => writeFileSync(leaseFile, JSON.stringify({ version: 1, run_id: runId, status }));
|
|
53
|
+
lease('active');
|
|
54
|
+
writeFileSync(sessionFile, JSON.stringify({ run_id: runId, lease_file: leaseFile, fixtures_root: realpathSync(fixtures), registry_root: registry, sentinel }));
|
|
55
|
+
const preload = join(checkout, 'scripts', 'dev', 'test-preload.mjs');
|
|
56
|
+
// On macOS, a verified Seatbelt profile also covers shell/native descendants.
|
|
57
|
+
// Availability varies under an outer sandbox, so never infer enforcement from
|
|
58
|
+
// the executable merely existing. The self-test touches a disposable sentinel.
|
|
59
|
+
let osBoundary = { mode: process.platform === 'darwin' ? 'unavailable' : 'not_supported' };
|
|
60
|
+
let command = process.execPath; let prefix = [];
|
|
61
|
+
const processRequests = join(sandbox, 'process-info-requests'); const processResponses = join(sandbox, 'process-info-responses');
|
|
62
|
+
mkdirSync(processRequests); mkdirSync(processResponses);
|
|
63
|
+
if (process.platform === 'darwin' && existsSync('/usr/bin/sandbox-exec')) {
|
|
64
|
+
const writable = [fixtures, registry, processRequests, processResponses].map(path => `(subpath ${JSON.stringify(realpathSync.native(path))})`).join(' ');
|
|
65
|
+
const seatbelt = `(version 1) (allow default) (deny file-write*) (allow file-write* ${writable} (literal "/dev/null") (literal "/dev/tty") (literal "/dev/stdout") (literal "/dev/stderr") (subpath "/dev/fd"))`;
|
|
66
|
+
const probePath = join(checkout, '.ace-test-os-sentinel'); const original = 'Protected native sentinel\n'; writeFileSync(probePath, original);
|
|
67
|
+
const result = spawnSync('/usr/bin/sandbox-exec', ['-p', seatbelt, '/bin/sh', '-c', 'printf mutation > "$1"', 'isolation-probe', probePath], { encoding: 'utf8' });
|
|
68
|
+
const preserved = readFileSync(probePath, 'utf8') === original;
|
|
69
|
+
if (!preserved) { lease('quarantined'); throw new Error('OS isolation self-test failed: a disposable protected sentinel was writable'); }
|
|
70
|
+
if (result.status === 1 && /Operation not permitted|Permission denied/.test(result.stderr)) {
|
|
71
|
+
osBoundary = { mode: 'seatbelt', self_test: 'native write refused' }; command = '/usr/bin/sandbox-exec'; prefix = ['-p', seatbelt, process.execPath];
|
|
72
|
+
} else osBoundary = { mode: 'unavailable', reason: (result.stderr || result.error?.message || `probe exit ${result.status}`).trim() };
|
|
73
|
+
}
|
|
74
|
+
if (process.env.ACE_TEST_REQUIRE_OS_BOUNDARY === '1' && osBoundary.mode !== 'seatbelt') { lease('quarantined'); throw new Error(`Required OS test boundary unavailable: ${JSON.stringify(osBoundary)}`); }
|
|
75
|
+
// Seatbelt can restrict Darwin process inspection even though file reads work.
|
|
76
|
+
// Broker only fixed, read-only ps fields through the unsandboxed owning runner.
|
|
77
|
+
// Responses are fresh OS output, never inferred from a heartbeat or PID alone.
|
|
78
|
+
const session = JSON.parse(readFileSync(sessionFile, 'utf8'));
|
|
79
|
+
session.process_info_broker = osBoundary.mode === 'seatbelt';
|
|
80
|
+
session.process_info_requests = processRequests; session.process_info_responses = processResponses;
|
|
81
|
+
writeFileSync(sessionFile, JSON.stringify(session));
|
|
82
|
+
const brokerStats = { requests: 0, failed: 0 };
|
|
83
|
+
function serveProcessInfo() {
|
|
84
|
+
for (const name of readdirSync(processRequests).slice(0, 64)) {
|
|
85
|
+
if (!/^[a-f0-9-]+\.json$/.test(name)) continue;
|
|
86
|
+
let request;
|
|
87
|
+
try { request = JSON.parse(readFileSync(join(processRequests, name), 'utf8')); } catch { continue; }
|
|
88
|
+
if (request.run_id !== runId || !Number.isSafeInteger(request.pid) || request.pid <= 1 || !['start', 'stat', 'both'].includes(request.fields)) { unlinkSync(join(processRequests, name)); continue; }
|
|
89
|
+
const psArgs = ['-p', String(request.pid), ...(request.fields === 'stat' ? [] : ['-o', 'lstart=']), ...(request.fields === 'start' ? [] : ['-o', 'stat='])];
|
|
90
|
+
let response;
|
|
91
|
+
try { response = { ok: true, stdout: execFileSync('ps', psArgs, { encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'pipe'] }) }; }
|
|
92
|
+
catch (error) { brokerStats.failed++; response = { ok: false, stdout: String(error.stdout ?? ''), stderr: String(error.stderr ?? ''), status: error.status ?? null, code: error.code ?? 'PROCESS_INFO_UNAVAILABLE' }; }
|
|
93
|
+
brokerStats.requests++;
|
|
94
|
+
const path = join(processResponses, name); writeFileSync(`${path}.tmp`, JSON.stringify({ ...response, pid: request.pid, run_id: runId, checked_at: Date.now() })); renameSync(`${path}.tmp`, path); unlinkSync(join(processRequests, name));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const broker = session.process_info_broker ? setInterval(serveProcessInfo, 5) : undefined;
|
|
98
|
+
console.error(`[test isolation] OS source boundary: ${osBoundary.mode}`);
|
|
99
|
+
const requested = process.argv.slice(2);
|
|
100
|
+
const probe = requested[0] === '--probe'; if (probe) requested.shift();
|
|
101
|
+
if (probe && !requested.length) throw new Error('Usage: run-tests-isolated.mjs --probe <probe.mjs> [arguments]');
|
|
102
|
+
const args = requested.length ? requested : ["test/*.test.mjs", "test/**/*.test.mjs"];
|
|
103
|
+
console.error(`[test isolation] Disposable checkout: ${checkout}`);
|
|
104
|
+
const childEnv = { ...process.env, ACE_WORKSPACE_ROOT: fixtures, INIT_CWD: checkout, PWD: checkout, ACE_TEST_SESSION_FILE: sessionFile,
|
|
105
|
+
ACE_TEST_ISOLATION: '1', ACE_TEST_RUN_ID: runId, ACE_TEST_LEASE_FILE: leaseFile,
|
|
106
|
+
ACE_TEST_NATIVE_SENTINEL_PATH: nativeSentinel,
|
|
107
|
+
ACE_TEST_PROBE: probe ? '1' : '0',
|
|
108
|
+
NODE_OPTIONS: [process.env.NODE_OPTIONS ?? '', `--import=${pathToFileURL(preload).href}`].join(' ').trim() };
|
|
109
|
+
delete childEnv.ACE_TUI_WORKSPACE_ROOT;
|
|
110
|
+
delete childEnv.ACE_TEST_WORKER_ROOT;
|
|
111
|
+
const child = spawn(command, [...prefix, ...(probe ? args : ["--test", ...args])], {
|
|
112
|
+
cwd: checkout,
|
|
113
|
+
env: childEnv,
|
|
114
|
+
stdio: "inherit",
|
|
115
|
+
});
|
|
116
|
+
const outcome = await new Promise((resolveExit, reject) => {
|
|
117
|
+
child.on("error", reject);
|
|
118
|
+
child.on("close", (code, signal) => resolveExit({ code, signal }));
|
|
119
|
+
});
|
|
120
|
+
function ownedProcesses() {
|
|
121
|
+
return readdirSync(registry).flatMap(name => {
|
|
122
|
+
try {
|
|
123
|
+
const record = JSON.parse(readFileSync(join(registry, name), 'utf8'));
|
|
124
|
+
try { process.kill(record.pid, 0); } catch (error) { if (error.code === 'ESRCH') return []; return [{ ...record, state: 'uncertain' }]; }
|
|
125
|
+
try {
|
|
126
|
+
const actual = execFileSync('ps', ['-p', String(record.pid), '-o', 'lstart=', '-o', 'stat='], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
127
|
+
const match = actual.match(/^(.*?)\s+([A-Za-z+<>NsLslWEIXZ-]+)$/);
|
|
128
|
+
if (match?.[2].includes('Z')) return [];
|
|
129
|
+
if (record.identity && match && match[1].replace(/\s+/g, ' ') !== record.identity) return [];
|
|
130
|
+
return [{ ...record, state: record.identity && match ? 'alive' : 'uncertain' }];
|
|
131
|
+
} catch { return [{ ...record, state: 'uncertain' }]; }
|
|
132
|
+
} catch { return []; }
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const configuredWait = Number(process.env.ACE_TEST_CHILD_WAIT_MS ?? 15000);
|
|
136
|
+
const waitUntil = Date.now() + (Number.isFinite(configuredWait) && configuredWait >= 0 ? configuredWait : 15000);
|
|
137
|
+
let retained = ownedProcesses();
|
|
138
|
+
while (retained.length && Date.now() < waitUntil) {
|
|
139
|
+
await new Promise(resolveWait => setTimeout(resolveWait, 100)); retained = ownedProcesses();
|
|
140
|
+
}
|
|
141
|
+
// Closing the allocation is an instruction to test writers, not a total-runtime
|
|
142
|
+
// kill of healthy work. Unfinished/uncertain descendants quarantine this run.
|
|
143
|
+
lease(retained.length ? 'quarantined' : 'closed');
|
|
144
|
+
if (broker) clearInterval(broker);
|
|
145
|
+
// close fires after the test runner and its workers have finished, not when an
|
|
146
|
+
// alphabetically-last test happens to run. The copy's source root is a sentinel:
|
|
147
|
+
// fixtures must use their disposable workspace, including in spawned children.
|
|
148
|
+
const unexpected = Object.keys(stateHashes(checkout));
|
|
149
|
+
const protectedAfter = stateHashes(source);
|
|
150
|
+
const changed = [...new Set([...Object.keys(protectedBefore), ...Object.keys(protectedAfter)])]
|
|
151
|
+
.filter((name) => protectedBefore[name] !== protectedAfter[name]);
|
|
152
|
+
const sentinelChanged = !readFileSync(sentinel).equals(sentinelBefore);
|
|
153
|
+
const nativeSentinelChanged = !existsSync(nativeSentinel) || !readFileSync(nativeSentinel).equals(nativeSentinelBefore);
|
|
154
|
+
const quarantined = Boolean(retained.length || unexpected.length || changed.length || sentinelChanged || nativeSentinelChanged);
|
|
155
|
+
if (quarantined) lease('quarantined');
|
|
156
|
+
writeFileSync(join(sandbox, "isolation.json"), JSON.stringify({ checkout, outcome, unexpected, protected_source_changes: changed, sentinel_changed: sentinelChanged, native_sentinel_changed: nativeSentinelChanged, retained_owned_node_processes: retained, allocation: quarantined ? 'quarantined' : 'closed', os_boundary: osBoundary, process_info_broker: { enabled: session.process_info_broker, ...brokerStats } }, null, 2));
|
|
157
|
+
if (quarantined) {
|
|
158
|
+
console.error(`[test isolation] Unauthorized fixture writes: ${unexpected.join(", ") || "none"}; source changes: ${changed.join(", ") || "none"}`);
|
|
159
|
+
if (nativeSentinelChanged) console.error('[test isolation] Native write outside allocated fixtures changed a disposable sentinel.');
|
|
160
|
+
if (retained.length) console.error(`[test isolation] Allocation quarantined; ${retained.length} owned Node descendants remain alive or uncertain. Lease closed; no claim of finished child work.`);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
} else {
|
|
163
|
+
console.error("[test isolation] No source-state changes or fallback writes into the disposable source root.");
|
|
164
|
+
process.exitCode = outcome.code ?? 1;
|
|
165
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFileSync, realpathSync, statSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, relative, sep } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const denied = detail => { throw new Error(`ACE test isolation required: ${detail}. Use node scripts/dev/run-tests-isolated.mjs.`); };
|
|
5
|
+
export function assertTestIsolation() {
|
|
6
|
+
if (process.env.ACE_TEST_ISOLATION !== '1' || !globalThis[Symbol.for('ace.test.preload')]) denied('the isolation preloader must run before test imports');
|
|
7
|
+
let roots, lease;
|
|
8
|
+
try {
|
|
9
|
+
roots = JSON.parse(process.env.ACE_TEST_ALLOWED_ROOTS ?? 'null');
|
|
10
|
+
lease = JSON.parse(readFileSync(process.env.ACE_TEST_LEASE_FILE, 'utf8'));
|
|
11
|
+
} catch { denied('missing fixture allocation or lease'); }
|
|
12
|
+
if (!Array.isArray(roots) || !roots.length || roots.some(root => typeof root !== 'string' || !isAbsolute(root))) denied('invalid fixture roots');
|
|
13
|
+
if (lease.status !== 'active' || lease.run_id !== process.env.ACE_TEST_RUN_ID) denied('the fixture lease is no longer active');
|
|
14
|
+
let workspace;
|
|
15
|
+
try { workspace = realpathSync.native(process.env.ACE_TEST_WORKER_ROOT); } catch { denied('missing worker allocation'); }
|
|
16
|
+
if (!roots.some(root => {
|
|
17
|
+
try { const base = realpathSync.native(root); const path = relative(base, workspace); return statSync(base).isDirectory() && path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path); } catch { return false; }
|
|
18
|
+
})) denied('worker root is outside the fixture allocation');
|
|
19
|
+
}
|
|
20
|
+
assertTestIsolation();
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Test-only Node boundary. It is not an OS sandbox for arbitrary native code.
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import fsp from 'node:fs/promises';
|
|
4
|
+
import cp from 'node:child_process';
|
|
5
|
+
import { syncBuiltinESMExports } from 'node:module';
|
|
6
|
+
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { promisify } from 'node:util';
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
|
|
11
|
+
const raw = Object.fromEntries(Object.entries(fs).filter(([, value]) => typeof value === 'function').map(([key, value]) => [key, value.bind(fs)]));
|
|
12
|
+
const rawExecFileSync = cp.execFileSync.bind(cp);
|
|
13
|
+
const rawRealpathNative = fs.realpathSync.native.bind(fs);
|
|
14
|
+
const sessionFile = process.env.ACE_TEST_SESSION_FILE;
|
|
15
|
+
if (!sessionFile) throw new Error('ACE test isolation required: missing session file');
|
|
16
|
+
const session = JSON.parse(raw.readFileSync(sessionFile, 'utf8'));
|
|
17
|
+
const lease = () => JSON.parse(raw.readFileSync(session.lease_file, 'utf8'));
|
|
18
|
+
if (lease().status !== 'active' || lease().run_id !== session.run_id) throw new Error('ACE test isolation required: inactive session');
|
|
19
|
+
if ((process.env.NODE_TEST_CONTEXT || process.env.ACE_TEST_PROBE === '1') && !process.env.ACE_TEST_WORKER_ROOT) {
|
|
20
|
+
const allocation = raw.mkdtempSync(join(session.fixtures_root, 'worker-'));
|
|
21
|
+
const workspace = join(allocation, 'workspace'); const temporary = join(allocation, 'tmp');
|
|
22
|
+
raw.mkdirSync(workspace); raw.mkdirSync(temporary);
|
|
23
|
+
process.env.ACE_TEST_WORKER_ROOT = workspace;
|
|
24
|
+
process.env.ACE_WORKSPACE_ROOT = workspace;
|
|
25
|
+
process.env.ACE_TEST_ALLOWED_ROOTS = JSON.stringify([allocation]);
|
|
26
|
+
process.env.TMPDIR = temporary; process.env.TMP = temporary; process.env.TEMP = temporary;
|
|
27
|
+
}
|
|
28
|
+
process.env.ACE_TEST_ISOLATION = '1';
|
|
29
|
+
process.env.ACE_TEST_RUN_ID = session.run_id;
|
|
30
|
+
process.env.ACE_TEST_LEASE_FILE = session.lease_file;
|
|
31
|
+
process.env.ACE_TEST_SENTINEL_PATH = session.sentinel;
|
|
32
|
+
|
|
33
|
+
function processFields(args) {
|
|
34
|
+
if (!Array.isArray(args) || args[0] !== '-p' || !/^\d+$/.test(args[1] ?? '')) return undefined;
|
|
35
|
+
const rest = args.slice(2).join(' ');
|
|
36
|
+
return rest === '-o lstart=' ? 'start' : rest === '-o stat=' ? 'stat' : rest === '-o lstart= -o stat=' ? 'both' : undefined;
|
|
37
|
+
}
|
|
38
|
+
function inspectProcess(args, opts = {}) {
|
|
39
|
+
const fields = processFields(args);
|
|
40
|
+
if (!session.process_info_broker || !fields) return rawExecFileSync('ps', args, opts);
|
|
41
|
+
const id = randomUUID(); const name = `${id}.json`; const request = join(session.process_info_requests, name); const response = join(session.process_info_responses, name);
|
|
42
|
+
raw.writeFileSync(`${request}.tmp`, JSON.stringify({ run_id: session.run_id, pid: Number(args[1]), fields })); raw.renameSync(`${request}.tmp`, request);
|
|
43
|
+
const end = Date.now() + (Number.isFinite(opts.timeout) && opts.timeout > 0 ? opts.timeout : 10000);
|
|
44
|
+
const wait = new Int32Array(new SharedArrayBuffer(4));
|
|
45
|
+
while (Date.now() < end) {
|
|
46
|
+
if (raw.existsSync(response)) {
|
|
47
|
+
const result = JSON.parse(raw.readFileSync(response, 'utf8')); raw.unlinkSync(response);
|
|
48
|
+
if (result.run_id !== session.run_id || result.pid !== Number(args[1])) throw new Error('Invalid owned process-info response');
|
|
49
|
+
if (!result.ok) throw Object.assign(new Error(result.stderr || 'OS process information unavailable'), { code: result.code, status: result.status, stdout: result.stdout, stderr: result.stderr });
|
|
50
|
+
return opts.encoding && opts.encoding !== 'buffer' ? result.stdout : Buffer.from(result.stdout);
|
|
51
|
+
}
|
|
52
|
+
Atomics.wait(wait, 0, 0, 5);
|
|
53
|
+
}
|
|
54
|
+
throw Object.assign(new Error('Owned process-info request timed out'), { code: 'ETIMEDOUT' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Every Node descendant inherits this import, including detached verification.
|
|
58
|
+
const registration = join(session.registry_root, `${process.pid}.json`);
|
|
59
|
+
let identity;
|
|
60
|
+
try { identity = inspectProcess(['-p', String(process.pid), '-o', 'lstart='], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim().replace(/\s+/g, ' '); } catch { identity = null; }
|
|
61
|
+
raw.writeFileSync(registration, JSON.stringify({ pid: process.pid, parent: process.ppid, identity, registered_at: Date.now() }));
|
|
62
|
+
process.once('exit', () => { try { raw.unlinkSync(registration); } catch {} });
|
|
63
|
+
|
|
64
|
+
if (process.env.ACE_TEST_WORKER_ROOT) {
|
|
65
|
+
const { captureTestWriteBoundary, currentTestWriteAnchors } = await import(new URL('../../dist/store/test-write-boundary.js', import.meta.url));
|
|
66
|
+
const check = path => {
|
|
67
|
+
if (typeof path === 'number') {
|
|
68
|
+
if (path === 1 || path === 2) return () => {};
|
|
69
|
+
const pinned = descriptors.get(path); if (!pinned) throw new Error('ace_test_write_boundary: untracked file descriptor'); pinned(); return pinned;
|
|
70
|
+
}
|
|
71
|
+
const pin = captureTestWriteBoundary(path instanceof URL ? fileURLToPath(path) : Buffer.isBuffer(path) ? path.toString() : path); pin(); return pin;
|
|
72
|
+
};
|
|
73
|
+
const descriptors = new Map();
|
|
74
|
+
const paths = { appendFile: [0], writeFile: [0], mkdir: [0], mkdtemp: [0], rm: [0], rmdir: [0], unlink: [0], rename: [0, 1], copyFile: [1], cp: [1], link: [0, 1], symlink: [1], chmod: [0], chown: [0], lchown: [0], lchmod: [0], truncate: [0], utimes: [0], lutimes: [0], createWriteStream: [0] };
|
|
75
|
+
for (const [name, indices] of Object.entries(paths)) {
|
|
76
|
+
for (const method of [name, `${name}Sync`]) if (typeof fs[method] === 'function') {
|
|
77
|
+
const original = fs[method].bind(fs);
|
|
78
|
+
fs[method] = (...args) => { indices.forEach(index => check(args[index])); return original(...args); };
|
|
79
|
+
}
|
|
80
|
+
if (typeof fsp[name] === 'function') {
|
|
81
|
+
const original = fsp[name].bind(fsp);
|
|
82
|
+
fsp[name] = async (...args) => { indices.forEach(index => check(args[index])); return original(...args); };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const writable = flags => typeof flags === 'number' ? Boolean(flags & (fs.constants.O_WRONLY | fs.constants.O_RDWR | fs.constants.O_CREAT | fs.constants.O_TRUNC | fs.constants.O_APPEND)) : /[wa+]/.test(flags ?? 'r');
|
|
86
|
+
const openSync = fs.openSync.bind(fs);
|
|
87
|
+
fs.openSync = (path, flags, ...rest) => { const pin = writable(flags) ? check(path) : undefined; const fd = openSync(path, flags, ...rest); if (pin) descriptors.set(fd, pin); return fd; };
|
|
88
|
+
const open = fs.open.bind(fs);
|
|
89
|
+
fs.open = (path, flags, ...rest) => {
|
|
90
|
+
const pin = writable(flags) ? check(path) : undefined; const callback = rest.pop();
|
|
91
|
+
return open(path, flags, ...rest, (error, fd) => { if (!error && pin) descriptors.set(fd, pin); callback(error, fd); });
|
|
92
|
+
};
|
|
93
|
+
const promiseOpen = fsp.open.bind(fsp);
|
|
94
|
+
fsp.open = async (path, flags, ...rest) => {
|
|
95
|
+
const pin = writable(flags) ? check(path) : undefined; const handle = await promiseOpen(path, flags, ...rest);
|
|
96
|
+
if (pin) {
|
|
97
|
+
descriptors.set(handle.fd, pin);
|
|
98
|
+
for (const key of ['write', 'writev', 'writeFile', 'appendFile', 'truncate', 'chmod', 'chown', 'utimes', 'createWriteStream']) if (typeof handle[key] === 'function') {
|
|
99
|
+
const operation = handle[key].bind(handle); handle[key] = (...args) => { pin(); return operation(...args); };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return handle;
|
|
103
|
+
};
|
|
104
|
+
for (const method of ['write', 'writeSync', 'writev', 'writevSync', 'ftruncate', 'ftruncateSync', 'fchmod', 'fchmodSync', 'fchown', 'fchownSync', 'futimes', 'futimesSync']) if (typeof fs[method] === 'function') {
|
|
105
|
+
const operation = fs[method].bind(fs); fs[method] = (fd, ...args) => { check(fd); return operation(fd, ...args); };
|
|
106
|
+
}
|
|
107
|
+
const inside = (path, base) => { const tail = relative(base, path); return tail !== '..' && !tail.startsWith(`..${sep}`) && !isAbsolute(tail); };
|
|
108
|
+
const inherited = Object.fromEntries(['ACE_TEST_ISOLATION', 'ACE_TEST_SESSION_FILE', 'ACE_TEST_RUN_ID', 'ACE_TEST_LEASE_FILE', 'ACE_TEST_ALLOWED_ROOTS', 'ACE_TEST_WORKER_ROOT', 'ACE_TEST_SENTINEL_PATH', 'NODE_OPTIONS'].map(key => [key, process.env[key]]));
|
|
109
|
+
function options(given = {}) {
|
|
110
|
+
// An explicit environment may intentionally omit credentials. Preserve that
|
|
111
|
+
// boundary and reattach only the isolation identity below.
|
|
112
|
+
const env = { ...(given.env ?? process.env) };
|
|
113
|
+
let requested;
|
|
114
|
+
try { requested = JSON.parse(env.ACE_TEST_ALLOWED_ROOTS); } catch {}
|
|
115
|
+
const parentRoots = JSON.parse(inherited.ACE_TEST_ALLOWED_ROOTS);
|
|
116
|
+
const narrow = Array.isArray(requested) && requested.length && requested.every(path => typeof path === 'string' && isAbsolute(path) && parentRoots.some(base => inside(rawRealpathNative(path), rawRealpathNative(base))));
|
|
117
|
+
const anchors = [...currentTestWriteAnchors()];
|
|
118
|
+
const workspace = env.ACE_WORKSPACE_ROOT;
|
|
119
|
+
if (workspace && raw.existsSync(workspace)) { const path = rawRealpathNative(workspace); const st = raw.statSync(path, { bigint: true }); anchors.push({ path, dev: String(st.dev), ino: String(st.ino) }); }
|
|
120
|
+
Object.assign(env, inherited);
|
|
121
|
+
if (narrow) env.ACE_TEST_ALLOWED_ROOTS = JSON.stringify(requested);
|
|
122
|
+
env.ACE_TEST_WRITE_ANCHORS = JSON.stringify(anchors);
|
|
123
|
+
return { ...given, env };
|
|
124
|
+
}
|
|
125
|
+
function registerOwned(child) {
|
|
126
|
+
if (!child?.pid) return child;
|
|
127
|
+
const file = join(session.registry_root, `${child.pid}.json`); let childIdentity;
|
|
128
|
+
try { childIdentity = inspectProcess(['-p', String(child.pid), '-o', 'lstart='], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim().replace(/\s+/g, ' '); } catch { childIdentity = null; }
|
|
129
|
+
raw.writeFileSync(file, JSON.stringify({ pid: child.pid, parent: process.pid, identity: childIdentity, registered_at: Date.now() }));
|
|
130
|
+
child.once('exit', () => { try { raw.unlinkSync(file); } catch {} }); return child;
|
|
131
|
+
}
|
|
132
|
+
for (const name of ['spawn', 'spawnSync', 'execFile', 'execFileSync', 'fork']) {
|
|
133
|
+
const original = cp[name].bind(cp);
|
|
134
|
+
cp[name] = (command, args, opts, callback) => {
|
|
135
|
+
if (!Array.isArray(args)) { callback = opts; opts = args; args = []; }
|
|
136
|
+
if (typeof opts === 'function') { callback = opts; opts = {}; }
|
|
137
|
+
if (name === 'execFileSync' && (command === 'ps' || command === '/bin/ps') && session.process_info_broker && processFields(args)) return inspectProcess(args, opts);
|
|
138
|
+
const child = callback ? original(command, args, options(opts), callback) : original(command, args, options(opts));
|
|
139
|
+
return name.endsWith('Sync') ? child : registerOwned(child);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
for (const name of ['exec', 'execSync']) {
|
|
143
|
+
const original = cp[name].bind(cp);
|
|
144
|
+
cp[name] = (command, opts, callback) => { if (typeof opts === 'function') { callback = opts; opts = {}; } const child = callback ? original(command, options(opts), callback) : original(command, options(opts)); return name.endsWith('Sync') ? child : registerOwned(child); };
|
|
145
|
+
}
|
|
146
|
+
for (const name of ['exec', 'execFile']) cp[name][promisify.custom] = (...args) => {
|
|
147
|
+
let child;
|
|
148
|
+
const result = new Promise((resolvePromise, reject) => { child = cp[name](...args, (error, stdout, stderr) => error ? reject(Object.assign(error, { stdout, stderr })) : resolvePromise({ stdout, stderr })); });
|
|
149
|
+
result.child = child; return result;
|
|
150
|
+
};
|
|
151
|
+
syncBuiltinESMExports();
|
|
152
|
+
globalThis[Symbol.for('ace.test.preload')] = true;
|
|
153
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const safe = name => typeof name === 'string' && name && !isAbsolute(name) && !name.split(/[\\/]/).includes('..') && !/^(?:agent-state|\.agents|\.git|node_modules)(?:[\\/]|$)/.test(name);
|
|
6
|
+
|
|
7
|
+
/** An installed package has no Git checkout. Inventory only its declared files. */
|
|
8
|
+
export function testSourceFiles(source) {
|
|
9
|
+
const manifest = join(source, '.ace-test-source-files.json');
|
|
10
|
+
if (existsSync(manifest)) return JSON.parse(readFileSync(manifest, 'utf8')).filter(safe);
|
|
11
|
+
if (existsSync(join(source, '.git'))) return execFileSync('git', ['ls-files', '-z', '--cached', '--others', '--exclude-standard'], { cwd: source, encoding: 'utf8' }).split('\0').filter(safe);
|
|
12
|
+
const pkg = JSON.parse(readFileSync(join(source, 'package.json'), 'utf8'));
|
|
13
|
+
const roots = ['package.json', 'package-lock.json', 'README.md', 'CHANGELOG.md', 'LICENSE', ...(pkg.files ?? [])].map(name => typeof name === 'string' ? name.split(/[*?[]/, 1)[0].replace(/[\\/]$/, '') : '').filter(safe);
|
|
14
|
+
const files = new Set();
|
|
15
|
+
function visit(name) {
|
|
16
|
+
if (!safe(name) || !existsSync(join(source, name))) return;
|
|
17
|
+
const stat = lstatSync(join(source, name));
|
|
18
|
+
if (stat.isSymbolicLink()) return;
|
|
19
|
+
if (stat.isDirectory()) for (const child of readdirSync(join(source, name))) visit(join(name, child));
|
|
20
|
+
else if (stat.isFile()) files.add(name);
|
|
21
|
+
}
|
|
22
|
+
roots.forEach(visit); return [...files].sort();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function findTestDependencies(source) {
|
|
26
|
+
let cursor = resolve(source);
|
|
27
|
+
for (;;) {
|
|
28
|
+
const candidate = join(cursor, 'node_modules'); if (existsSync(candidate)) return candidate;
|
|
29
|
+
const parent = dirname(cursor); if (parent === cursor) throw new Error('Installed ACE dependencies are unavailable; use the package installation containing node_modules'); cursor = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
package/docs/development.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Development
|
|
2
|
-
|
|
3
|
-
> Companion to the [README](../README.md).
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
cd ace-mcp-server
|
|
7
|
-
npm install
|
|
8
|
-
npm run build
|
|
9
|
-
npm test
|
|
10
|
-
npm run start
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Run the Hermes live driver from a source checkout. The script is included in the npm package as of 2.4.6, but it imports the checkout/package `dist/` tree and is a diagnostic, not a supported production command. It exits nonzero if setup, protocol, or artifact checks fail.
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm run build
|
|
17
|
-
# with a local llama.cpp or Ollama runtime running:
|
|
18
|
-
ACE_WORKSPACE_ROOT=$PWD \
|
|
19
|
-
ACE_HERMES_ROOT=/path/to/hermes-agent \
|
|
20
|
-
HERMES_PYTHON=/path/to/hermes-python \
|
|
21
|
-
ACE_LIVE_PROVIDERS=llama.cpp \
|
|
22
|
-
ACE_LIVE_LLAMA_CPP_BASE_URL=http://127.0.0.1:8080/v1 \
|
|
23
|
-
ACE_LIVE_LLAMA_CPP_MODEL=gemma-3-4b-it \
|
|
24
|
-
ACE_LIVE_HERMES_MAX_TURNS=3 \
|
|
25
|
-
node scripts/dev/run_hermes_crispr_live.mjs
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`ACE_LIVE_OUTPUT_ONLY=1` deliberately bypasses tool writing and is only for inspecting raw model output; it cannot satisfy the driver’s evidence-gated job check.
|
|
29
|
-
|
|
30
|
-
## Key source files
|
|
31
|
-
|
|
32
|
-
| File | Role |
|
|
33
|
-
|---|---|
|
|
34
|
-
| `src/index.ts` | Package entry point |
|
|
35
|
-
| `src/cli.ts` | CLI command dispatch |
|
|
36
|
-
| `src/server.ts` | MCP server setup and stdio transport |
|
|
37
|
-
| `src/tools.ts` | Tool registration hub (delegates to domain modules) |
|
|
38
|
-
| `src/hermes/session-manager.ts` | `HermesSubprocessExecutor` — Hermes subprocess lifecycle |
|
|
39
|
-
| `src/hermes/bridge-protocol.ts` | Frame decoder for the Hermes bridge protocol |
|
|
40
|
-
| `src/model-bridge.ts` | Model bridge interface and process orchestration |
|
|
41
|
-
| `src/goal-engine.ts` | Goal engine — self-paced, evidence-gated objective loop |
|
|
42
|
-
| `src/job-worker.ts` | In-process job worker for the scheduler execution loop |
|
|
43
|
-
| `src/public-surface.ts` | Public surface audit and schema-bound entry map |
|
|
44
|
-
| `src/store/ace-packed-store.ts` | ACEPACK binary store read/write |
|
|
45
|
-
| `src/local-model-policy.ts` | Operator allow/block policy for local models |
|
|
46
|
-
| `scripts/hermes_bridge_worker.py` | Packaged Python worker for the Hermes bridge |
|