@voybio/ace-swarm 2.4.3 → 2.4.6
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 +78 -0
- package/README.md +72 -532
- package/assets/agent-state/INTERFACE_REGISTRY.md +2 -2
- package/assets/agent-state/MODULES/gates/gate-autonomy.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-completeness.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-operability.json +4 -2
- package/assets/agent-state/MODULES/gates/gate-security.json +3 -1
- package/assets/agent-state/MODULES/registry.json +1 -4
- package/assets/agent-state/MODULES/roles/capability-build.json +1 -2
- package/assets/agent-state/MODULES/roles/capability-eval.json +1 -1
- package/assets/agent-state/MODULES/roles/capability-qa.json +1 -2
- package/assets/agent-state/QUALITY_GATES.md +25 -9
- package/assets/agent-state/TEAL_CONFIG.md +2 -11
- package/assets/eval-battery/battery.json +85 -0
- package/assets/eval-battery/fixtures/edit-target.js +3 -0
- package/assets/scripts/render-mcp-configs.sh +12 -0
- package/dist/ace-context.d.ts +18 -0
- package/dist/ace-context.js +161 -16
- package/dist/ace-server-instructions.js +36 -9
- package/dist/astgrep-index.d.ts +7 -1
- package/dist/astgrep-index.js +66 -39
- package/dist/bridge-trace.d.ts +17 -0
- package/dist/bridge-trace.js +23 -0
- package/dist/cli.d.ts +9 -1
- package/dist/cli.js +619 -10
- package/dist/discovery-runtime-wrappers.d.ts +16 -1
- package/dist/discovery-runtime-wrappers.js +65 -9
- package/dist/eval-harness.d.ts +110 -0
- package/dist/eval-harness.js +254 -0
- package/dist/experience-reflector.d.ts +13 -0
- package/dist/experience-reflector.js +62 -0
- package/dist/gate-contract.d.ts +63 -0
- package/dist/gate-contract.js +178 -0
- package/dist/goal-engine.d.ts +155 -0
- package/dist/goal-engine.js +795 -0
- package/dist/helpers/store-resolution.js +40 -39
- package/dist/hermes/session-manager.d.ts +10 -0
- package/dist/hermes/session-manager.js +130 -27
- package/dist/job-scheduler.d.ts +58 -0
- package/dist/job-scheduler.js +202 -2
- package/dist/job-worker.d.ts +87 -0
- package/dist/job-worker.js +182 -0
- package/dist/local-model-policy.d.ts +8 -2
- package/dist/local-model-policy.js +31 -17
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +130 -2
- package/dist/measured-classing.d.ts +66 -0
- package/dist/measured-classing.js +165 -0
- package/dist/model-bridge.d.ts +45 -0
- package/dist/model-bridge.js +502 -44
- package/dist/orchestrator-supervisor.js +1 -1
- package/dist/plan-proposal.d.ts +7 -0
- package/dist/plan-proposal.js +74 -4
- package/dist/playbook-adaptation.d.ts +37 -0
- package/dist/playbook-adaptation.js +159 -0
- package/dist/playbook-curator.d.ts +16 -0
- package/dist/playbook-curator.js +106 -0
- package/dist/playbook-llm-reflector.d.ts +26 -0
- package/dist/playbook-llm-reflector.js +157 -0
- package/dist/public-surface.d.ts +2 -0
- package/dist/public-surface.js +26 -14
- package/dist/runtime-executor.js +45 -2
- package/dist/runtime-tool-specs.d.ts +8 -1
- package/dist/runtime-tool-specs.js +19 -1
- package/dist/safe-edit.d.ts +20 -0
- package/dist/safe-edit.js +355 -78
- package/dist/schemas.js +16 -15
- package/dist/server.d.ts +11 -2
- package/dist/server.js +55 -5
- package/dist/shared.d.ts +12 -0
- package/dist/shared.js +13 -1
- package/dist/source-mutation-coordinator.d.ts +54 -0
- package/dist/source-mutation-coordinator.js +259 -0
- package/dist/status-events.d.ts +4 -1
- package/dist/status-events.js +41 -6
- package/dist/store/ace-packed-store.d.ts +28 -9
- package/dist/store/ace-packed-store.js +324 -57
- package/dist/store/bgzf.d.ts +4 -0
- package/dist/store/bgzf.js +94 -0
- package/dist/store/bootstrap-store.d.ts +4 -1
- package/dist/store/bootstrap-store.js +87 -9
- package/dist/store/cache-workspace.d.ts +8 -0
- package/dist/store/cache-workspace.js +67 -1
- package/dist/store/crc32.d.ts +2 -0
- package/dist/store/crc32.js +16 -0
- package/dist/store/fsck.d.ts +16 -0
- package/dist/store/fsck.js +240 -0
- package/dist/store/gate-contract-migration.d.ts +10 -0
- package/dist/store/gate-contract-migration.js +413 -0
- package/dist/store/materializers/context-snapshot-materializer.d.ts +1 -0
- package/dist/store/materializers/context-snapshot-materializer.js +4 -1
- package/dist/store/materializers/projection-manager.d.ts +7 -0
- package/dist/store/materializers/projection-manager.js +37 -0
- package/dist/store/materializers/scheduler-projection-materializer.js +10 -3
- package/dist/store/repositories/goal-repository.d.ts +112 -0
- package/dist/store/repositories/goal-repository.js +157 -0
- package/dist/store/repositories/playbook-repository.d.ts +107 -0
- package/dist/store/repositories/playbook-repository.js +314 -0
- package/dist/store/store-artifacts.js +44 -2
- package/dist/store/store-authority-audit.js +1 -0
- package/dist/store/store-cli.d.ts +30 -0
- package/dist/store/store-cli.js +242 -0
- package/dist/store/store-snapshot.d.ts +12 -0
- package/dist/store/store-snapshot.js +74 -11
- package/dist/store/types.d.ts +1 -0
- package/dist/store/write-coordinator.d.ts +7 -0
- package/dist/store/write-coordinator.js +11 -0
- package/dist/todo-state.d.ts +21 -0
- package/dist/todo-state.js +150 -53
- package/dist/tools-agent.js +86 -22
- package/dist/tools-discovery.js +5 -3
- package/dist/tools-files.js +107 -23
- package/dist/tools-framework.js +146 -40
- package/dist/tools-goal.d.ts +11 -0
- package/dist/tools-goal.js +108 -0
- package/dist/tools-handoff.js +7 -11
- package/dist/tools-lifecycle.js +2 -0
- package/dist/tools-scheduler.js +44 -1
- package/dist/tools-skills.js +3 -2
- package/dist/tools-todo.js +64 -3
- package/dist/tools.js +2 -0
- package/dist/tui/chat.js +4 -0
- package/dist/tui/index.js +4 -1
- package/dist/tui/ollama.d.ts +2 -0
- package/dist/tui/openai-compatible.d.ts +14 -0
- package/dist/tui/openai-compatible.js +15 -2
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +2 -2
- package/docs/cli-reference.md +125 -0
- package/docs/development.md +46 -0
- package/docs/evaluation-safety.md +27 -0
- package/docs/experience-playbook.md +35 -0
- package/docs/hermes.md +129 -0
- package/docs/library-api.md +31 -0
- package/docs/mcp-tool-reference.md +290 -0
- package/docs/provider-security.md +85 -0
- package/docs/state-model.md +113 -0
- package/docs/troubleshooting.md +95 -0
- package/docs/tui-operator-guide.md +75 -0
- package/package.json +5 -1
- package/scripts/dev/run_hermes_crispr_live.mjs +212 -0
- package/assets/agent-state/MODULES/gates/gate-correctness.json +0 -7
- package/assets/agent-state/MODULES/gates/gate-evaluation.json +0 -7
- package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +0 -7
package/dist/tools-files.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { existsSync, mkdirSync, readdirSync,
|
|
6
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
import { ACE_TASKS_ROOT_REL, normalizePathForValidation, safeRead, safeWriteAsync, safeWrite, resolveWorkspaceWritePath, resolveStoreFallbackKeysForPath, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
|
|
9
|
+
import { ACE_TASKS_ROOT_REL, normalizePathForValidation, isReadError, safeRead, safeWriteAsync, safeWrite, resolveWorkspaceWritePath, resolveStoreFallbackKeysForPath, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
|
|
10
10
|
import { isInside, looksLikeSwarmHandoffPath, normalizeRelPath } from "./shared.js";
|
|
11
11
|
import { validateAgentStateHandoffPayload, validateArtifactManifestPayload, validateProvenanceLogContent, validateStatusEventsNdjsonContent, validateSwarmHandoffPayload, validateTealConfigContent, validateRuntimeExecutorSessionRegistryPayload, validateRuntimeToolSpecRegistryPayload, validateTrackerSnapshotPayload, validateVericifyBridgeSnapshotPayload, validateVericifyProcessPostLogPayload, validateWorkspaceSessionRegistryPayload, lintHandoffPayload, } from "./schemas.js";
|
|
12
12
|
import { syncTodoStateSafe } from "./todo-state.js";
|
|
@@ -14,9 +14,10 @@ import { shouldAutoRefreshKanbanForPath, refreshKanbanArtifacts, } from "./kanba
|
|
|
14
14
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
15
15
|
import { appendStatusEventSafe } from "./status-events.js";
|
|
16
16
|
import { safeEditFile, diffContents, applyPatch } from "./safe-edit.js";
|
|
17
|
-
import { detectAstgrepCommand, locateAstgrepMatches, runAstgrepQuery, } from "./astgrep-index.js";
|
|
17
|
+
import { detectAstgrepCommand, flattenMetaVariables, locateAstgrepMatches, runAstgrepQuery, } from "./astgrep-index.js";
|
|
18
18
|
import { readRuntimeProfile, resolveEffectiveSurgicalReadBudget, validateRuntimeProfileContent, } from "./runtime-profile.js";
|
|
19
19
|
import { withLocalModelRuntimeRepository } from "./store/repositories/local-model-runtime-repository.js";
|
|
20
|
+
import { listStoreKeysSync } from "./store/store-snapshot.js";
|
|
20
21
|
function reasonCodeFromError(error) {
|
|
21
22
|
return typeof error === "object" && error !== null && "reason_code" in error
|
|
22
23
|
? String(error.reason_code)
|
|
@@ -129,13 +130,22 @@ async function storeLocateArtifact(input, result) {
|
|
|
129
130
|
});
|
|
130
131
|
}
|
|
131
132
|
function listStructuralEditArtifacts(prefix) {
|
|
133
|
+
const relPaths = new Set();
|
|
134
|
+
const storePrefix = `state/artifacts/${STRUCTURAL_EDIT_ARTIFACTS_REL}/${prefix}-`;
|
|
135
|
+
for (const key of listStoreKeysSync(WORKSPACE_ROOT, storePrefix)) {
|
|
136
|
+
if (key.endsWith(".json")) {
|
|
137
|
+
relPaths.add(key.slice("state/artifacts/".length));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
132
140
|
const dir = wsPath(STRUCTURAL_EDIT_ARTIFACTS_REL);
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
if (existsSync(dir)) {
|
|
142
|
+
for (const name of readdirSync(dir)) {
|
|
143
|
+
if (name.startsWith(`${prefix}-`) && name.endsWith(".json")) {
|
|
144
|
+
relPaths.add(normalizeRelPath(relative(WORKSPACE_ROOT, resolve(dir, name))));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return [...relPaths].sort((a, b) => b.localeCompare(a));
|
|
139
149
|
}
|
|
140
150
|
function findStoredMatch(matchId) {
|
|
141
151
|
for (const relPath of listStructuralEditArtifacts("locate")) {
|
|
@@ -206,8 +216,12 @@ async function compileStructuralEditPlan(input) {
|
|
|
206
216
|
},
|
|
207
217
|
validation_command: input.validationCommand,
|
|
208
218
|
test_command: input.testCommand,
|
|
219
|
+
allow_multi_match: input.allowMultiMatch,
|
|
209
220
|
});
|
|
210
221
|
}
|
|
222
|
+
function renderStructuralRewrite(template, captures) {
|
|
223
|
+
return template.replace(/\$([A-Z][A-Z0-9_]*)/g, (full, name) => captures[name] ?? full);
|
|
224
|
+
}
|
|
211
225
|
function previewDiffText(diffText) {
|
|
212
226
|
const lines = diffText.trimEnd().split("\n");
|
|
213
227
|
const limited = lines.slice(0, 80).join("\n");
|
|
@@ -378,6 +392,24 @@ async function buildStructuralPreview(plan) {
|
|
|
378
392
|
}),
|
|
379
393
|
};
|
|
380
394
|
}
|
|
395
|
+
if (targetMatches.length > 1 && !plan.allow_multi_match) {
|
|
396
|
+
return {
|
|
397
|
+
artifact: await finalize({
|
|
398
|
+
ok: false,
|
|
399
|
+
reason_code: "ambiguous_multi_match_in_file",
|
|
400
|
+
error: `Plan ${plan.plan_id} resolves to ${targetMatches.length} matches in ${plan.target.file}. Pass allow_multi_match=true at compile time to opt in, or narrow the pattern.`,
|
|
401
|
+
target_file: plan.target.file,
|
|
402
|
+
expected_file_hash: plan.target.file_hash,
|
|
403
|
+
current_file_hash: currentFileHash,
|
|
404
|
+
matched_count: targetMatches.length,
|
|
405
|
+
affected_file_count: affectedFiles.length,
|
|
406
|
+
changed_ranges: [],
|
|
407
|
+
diff_summary: "",
|
|
408
|
+
diff_preview: "",
|
|
409
|
+
promotable: false,
|
|
410
|
+
}),
|
|
411
|
+
};
|
|
412
|
+
}
|
|
381
413
|
const astgrepCmd = detectAstgrepCommand();
|
|
382
414
|
if (!astgrepCmd) {
|
|
383
415
|
return {
|
|
@@ -403,7 +435,7 @@ async function buildStructuralPreview(plan) {
|
|
|
403
435
|
mkdirSync(dirname(stagedOriginal), { recursive: true });
|
|
404
436
|
writeFileSync(stagedOriginal, currentContent, "utf-8");
|
|
405
437
|
writeFileSync(stagedRewrite, currentContent, "utf-8");
|
|
406
|
-
const rewriteResult = spawnSync(astgrepCmd, ["--pattern", plan.locator.pattern, "--rewrite", plan.rewrite.rewrite_template, "--lang", plan.locator.lang,
|
|
438
|
+
const rewriteResult = spawnSync(astgrepCmd, ["--pattern", plan.locator.pattern, "--rewrite", plan.rewrite.rewrite_template, "--lang", plan.locator.lang, "--json", stagedRewrite], { encoding: "utf8", cwd: WORKSPACE_ROOT });
|
|
407
439
|
if (rewriteResult.status !== 0) {
|
|
408
440
|
return {
|
|
409
441
|
artifact: await finalize({
|
|
@@ -423,7 +455,46 @@ async function buildStructuralPreview(plan) {
|
|
|
423
455
|
}),
|
|
424
456
|
};
|
|
425
457
|
}
|
|
426
|
-
|
|
458
|
+
let rawMatches = [];
|
|
459
|
+
try {
|
|
460
|
+
rawMatches = JSON.parse(rewriteResult.stdout || "[]");
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
rawMatches = [];
|
|
464
|
+
}
|
|
465
|
+
const selectedBytes = plan.target.selected_range?.byteOffset;
|
|
466
|
+
const targeted = rawMatches.find((match) => selectedBytes
|
|
467
|
+
&& match.range?.byteOffset?.start === selectedBytes.start
|
|
468
|
+
&& match.range?.byteOffset?.end === selectedBytes.end);
|
|
469
|
+
const replacement = targeted && (typeof targeted.replacement === "string"
|
|
470
|
+
? targeted.replacement
|
|
471
|
+
: renderStructuralRewrite(plan.rewrite.rewrite_template, flattenMetaVariables(targeted.metaVariables)));
|
|
472
|
+
if (!selectedBytes || !targeted || typeof replacement !== "string") {
|
|
473
|
+
return {
|
|
474
|
+
artifact: await finalize({
|
|
475
|
+
ok: false,
|
|
476
|
+
reason_code: "target_byte_range_drift",
|
|
477
|
+
error: `No match at selected byte range [${selectedBytes?.start},${selectedBytes?.end}] for plan ${plan.plan_id}`,
|
|
478
|
+
target_file: plan.target.file,
|
|
479
|
+
expected_file_hash: plan.target.file_hash,
|
|
480
|
+
current_file_hash: currentFileHash,
|
|
481
|
+
matched_count: rawMatches.length,
|
|
482
|
+
affected_file_count: affectedFiles.length,
|
|
483
|
+
changed_ranges: [],
|
|
484
|
+
diff_summary: "",
|
|
485
|
+
diff_preview: "",
|
|
486
|
+
promotable: false,
|
|
487
|
+
staging_path: stagingDir,
|
|
488
|
+
}),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
const originalBuffer = Buffer.from(currentContent, "utf8");
|
|
492
|
+
const rewrittenContent = Buffer.concat([
|
|
493
|
+
originalBuffer.subarray(0, selectedBytes.start ?? 0),
|
|
494
|
+
Buffer.from(replacement, "utf8"),
|
|
495
|
+
originalBuffer.subarray(selectedBytes.end ?? 0),
|
|
496
|
+
]).toString("utf8");
|
|
497
|
+
writeFileSync(stagedRewrite, rewrittenContent, "utf8");
|
|
427
498
|
const rewriteHash = contentSha256(rewrittenContent);
|
|
428
499
|
const diff = diffContents(currentContent, rewrittenContent);
|
|
429
500
|
const diffProcess = spawnSync("diff", ["-u", "--label", `a/${plan.target.file}`, "--label", `b/${plan.target.file}`, stagedOriginal, stagedRewrite], { encoding: "utf8", cwd: WORKSPACE_ROOT });
|
|
@@ -432,6 +503,7 @@ async function buildStructuralPreview(plan) {
|
|
|
432
503
|
: diffProcess.status === 0
|
|
433
504
|
? ""
|
|
434
505
|
: diff.diff_summary;
|
|
506
|
+
const hasUnifiedDiff = diffProcess.status === 1;
|
|
435
507
|
return {
|
|
436
508
|
artifact: await finalize({
|
|
437
509
|
ok: true,
|
|
@@ -444,7 +516,7 @@ async function buildStructuralPreview(plan) {
|
|
|
444
516
|
changed_ranges: parseChangedRanges(diffText),
|
|
445
517
|
diff_summary: diff.diff_summary,
|
|
446
518
|
diff_preview: diffText ? previewDiffText(diffText) : diff.diff_summary,
|
|
447
|
-
promotable:
|
|
519
|
+
promotable: hasUnifiedDiff,
|
|
448
520
|
staging_path: stagingDir,
|
|
449
521
|
}),
|
|
450
522
|
rewritten_content: rewrittenContent,
|
|
@@ -1073,10 +1145,15 @@ export function registerFileTools(server) {
|
|
|
1073
1145
|
.boolean()
|
|
1074
1146
|
.optional()
|
|
1075
1147
|
.describe("Keep .ace-backup copy (default: true)"),
|
|
1076
|
-
|
|
1148
|
+
expected_base_hash: z
|
|
1149
|
+
.string()
|
|
1150
|
+
.optional()
|
|
1151
|
+
.describe("Optional source precondition checked immediately before promotion; prefer full sha256:<hex>"),
|
|
1152
|
+
}, async ({ path, content, validation_command, test_command, backup, expected_base_hash }) => {
|
|
1077
1153
|
const result = safeEditFile({
|
|
1078
1154
|
path,
|
|
1079
1155
|
content,
|
|
1156
|
+
expected_base_hash,
|
|
1080
1157
|
validation_command,
|
|
1081
1158
|
test_command,
|
|
1082
1159
|
backup,
|
|
@@ -1093,6 +1170,9 @@ export function registerFileTools(server) {
|
|
|
1093
1170
|
original_hash: result.original_hash,
|
|
1094
1171
|
new_hash: result.new_hash,
|
|
1095
1172
|
restored: result.restored,
|
|
1173
|
+
reason_code: result.reason_code,
|
|
1174
|
+
expected_hash: result.expected_hash,
|
|
1175
|
+
observed_hash: result.observed_hash,
|
|
1096
1176
|
validation_passed: result.validation_passed,
|
|
1097
1177
|
test_passed: result.test_passed,
|
|
1098
1178
|
},
|
|
@@ -1154,19 +1234,19 @@ export function registerFileTools(server) {
|
|
|
1154
1234
|
};
|
|
1155
1235
|
});
|
|
1156
1236
|
// ── Diff (new P2 tool) ────────────────────────────────────────────
|
|
1157
|
-
server.tool("diff_files", "
|
|
1237
|
+
server.tool("diff_files", "Produce a sequence-aware unified diff between two contents or two workspace paths", {
|
|
1158
1238
|
original: z.string().describe("Original content or workspace-relative file path"),
|
|
1159
1239
|
modified: z.string().describe("Modified content or workspace-relative file path"),
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
.optional()
|
|
1163
|
-
.describe("If true, treat original/modified as file paths and read their contents"),
|
|
1164
|
-
}, async ({ original, modified, is_path }) => {
|
|
1240
|
+
mode: z.enum(["content", "path"]).default("content").describe("Explicitly interpret both inputs as literal content or workspace-relative paths"),
|
|
1241
|
+
}, async ({ original, modified, mode }) => {
|
|
1165
1242
|
let origContent = original;
|
|
1166
1243
|
let modContent = modified;
|
|
1167
|
-
if (
|
|
1244
|
+
if (mode === "path") {
|
|
1168
1245
|
origContent = safeRead(original);
|
|
1169
1246
|
modContent = safeRead(modified);
|
|
1247
|
+
if (isReadError(origContent) || isReadError(modContent)) {
|
|
1248
|
+
throw new Error(`Unable to read diff path: ${isReadError(origContent) ? origContent : modContent}`);
|
|
1249
|
+
}
|
|
1170
1250
|
}
|
|
1171
1251
|
const diff = diffContents(origContent, modContent);
|
|
1172
1252
|
return {
|
|
@@ -1344,9 +1424,10 @@ export function registerFileTools(server) {
|
|
|
1344
1424
|
max_results: z.number().int().min(1).max(200).optional().describe("Max locator matches to keep (default: 50)"),
|
|
1345
1425
|
desired_change: z.string().describe("Plain-language edit intent; used as rewrite_template when no explicit template is provided"),
|
|
1346
1426
|
rewrite_template: z.string().optional().describe("Explicit ast-grep rewrite template; preferred when provided"),
|
|
1427
|
+
allow_multi_match: z.boolean().optional().describe("Opt into selecting and rewriting only the chosen match when multiple matches exist in the target file"),
|
|
1347
1428
|
validation_command: z.string().optional().describe("Shell command to validate before promotion"),
|
|
1348
1429
|
test_command: z.string().optional().describe("Shell command to run before promotion"),
|
|
1349
|
-
}, async ({ match_id, pattern, lang, scope, symbol_hint, max_results, desired_change, rewrite_template, validation_command, test_command, }) => {
|
|
1430
|
+
}, async ({ match_id, pattern, lang, scope, symbol_hint, max_results, desired_change, rewrite_template, allow_multi_match, validation_command, test_command, }) => {
|
|
1350
1431
|
if (!match_id && (!pattern || !lang)) {
|
|
1351
1432
|
return jsonResponse({
|
|
1352
1433
|
ok: false,
|
|
@@ -1455,6 +1536,7 @@ export function registerFileTools(server) {
|
|
|
1455
1536
|
selectedMatch,
|
|
1456
1537
|
desiredChange: desired_change,
|
|
1457
1538
|
rewriteTemplate: rewrite_template,
|
|
1539
|
+
allowMultiMatch: allow_multi_match,
|
|
1458
1540
|
validationCommand: validation_command,
|
|
1459
1541
|
testCommand: test_command,
|
|
1460
1542
|
}).catch((error) => {
|
|
@@ -1597,6 +1679,7 @@ export function registerFileTools(server) {
|
|
|
1597
1679
|
const safeResult = safeEditFile({
|
|
1598
1680
|
path: compiledPlan.target.file,
|
|
1599
1681
|
content: preview.rewritten_content,
|
|
1682
|
+
expected_base_hash: compiledPlan.target.file_hash,
|
|
1600
1683
|
validation_command: validation_command ?? compiledPlan.validation_command,
|
|
1601
1684
|
test_command: test_command ?? compiledPlan.test_command,
|
|
1602
1685
|
});
|
|
@@ -1644,10 +1727,11 @@ export function registerFileTools(server) {
|
|
|
1644
1727
|
server.tool("apply_patch", "Apply a unified diff patch to a workspace file. Applies the patch to staged content, runs optional validation/tests, and promotes to the target only on success. Cost: moderate.", {
|
|
1645
1728
|
path: z.string().describe("Workspace-relative file path to patch"),
|
|
1646
1729
|
patch: z.string().describe("Unified diff patch (output of diff -u or similar)"),
|
|
1730
|
+
expected_base_hash: z.string().optional().describe("Optional source precondition checked immediately before promotion; prefer full sha256:<hex>"),
|
|
1647
1731
|
validation_command: z.string().optional().describe("Shell command to validate after applying (e.g. 'npx tsc --noEmit')"),
|
|
1648
1732
|
test_command: z.string().optional().describe("Shell command to run tests after applying"),
|
|
1649
|
-
}, async ({ path: relPath, patch, validation_command, test_command }) => {
|
|
1650
|
-
const result = applyPatch({ path: relPath, patch, validation_command, test_command });
|
|
1733
|
+
}, async ({ path: relPath, patch, expected_base_hash, validation_command, test_command }) => {
|
|
1734
|
+
const result = applyPatch({ path: relPath, patch, expected_base_hash, validation_command, test_command });
|
|
1651
1735
|
if (!result.ok) {
|
|
1652
1736
|
return {
|
|
1653
1737
|
content: [{ type: "text", text: `apply_patch failed: ${result.error ?? "unknown error"}\n${result.validation_output ?? ""}` }],
|
package/dist/tools-framework.js
CHANGED
|
@@ -18,6 +18,9 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
18
18
|
import { resolve } from "node:path";
|
|
19
19
|
import { auditPublicSurface } from "./public-surface.js";
|
|
20
20
|
import { auditStoreAuthority, writeStoreAuthorityAuditReport, } from "./store/store-authority-audit.js";
|
|
21
|
+
import { openStore } from "./store/ace-packed-store.js";
|
|
22
|
+
import { getWorkspaceStorePath } from "./store/store-snapshot.js";
|
|
23
|
+
import { parseGateManifest, resolveGateSelection, } from "./gate-contract.js";
|
|
21
24
|
import { PROVENANCE_CRITICAL_EVENT_TYPES, validateArtifactManifestPayload, validateProvenanceLogContent, validateTealConfigContent, } from "./schemas.js";
|
|
22
25
|
import { readAceTaskContractAssessment } from "./ace-autonomy.js";
|
|
23
26
|
import { listStoreKeysSync, readStoreBlobSync } from "./store/store-snapshot.js";
|
|
@@ -54,28 +57,6 @@ function getArtifactManifestEntries(payload) {
|
|
|
54
57
|
}
|
|
55
58
|
return [];
|
|
56
59
|
}
|
|
57
|
-
function parseGateManifest(raw, sourceRef) {
|
|
58
|
-
try {
|
|
59
|
-
const gate = JSON.parse(raw);
|
|
60
|
-
const id = typeof gate.id === "string" ? gate.id.trim() : "";
|
|
61
|
-
if (!id)
|
|
62
|
-
return undefined;
|
|
63
|
-
const type = gate.type === "executable" || gate.type === "artifact_scan" || gate.type === "manual_review"
|
|
64
|
-
? gate.type
|
|
65
|
-
: "manual_review";
|
|
66
|
-
return {
|
|
67
|
-
id,
|
|
68
|
-
type,
|
|
69
|
-
invariant: typeof gate.invariant === "string" ? gate.invariant : "",
|
|
70
|
-
command: typeof gate.command === "string" ? gate.command : "",
|
|
71
|
-
evidence_requirement: typeof gate.evidence_requirement === "string" ? gate.evidence_requirement : "",
|
|
72
|
-
source_ref: sourceRef,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
60
|
function parseJsonForValidation(raw) {
|
|
80
61
|
return parseJsonLikeText(raw);
|
|
81
62
|
}
|
|
@@ -130,10 +111,14 @@ function hasArtifactEvidence(reference) {
|
|
|
130
111
|
const normalized = reference.trim();
|
|
131
112
|
if (!normalized)
|
|
132
113
|
return false;
|
|
133
|
-
const
|
|
134
|
-
|
|
114
|
+
const relativeCandidates = [
|
|
115
|
+
normalized,
|
|
116
|
+
normalized.startsWith("agent-state/") ? normalized : `agent-state/${normalized}`,
|
|
117
|
+
];
|
|
118
|
+
const absoluteCandidates = relativeCandidates.map((candidate) => wsPath(candidate));
|
|
119
|
+
if (absoluteCandidates.some((candidate) => existsSync(candidate)))
|
|
135
120
|
return true;
|
|
136
|
-
return
|
|
121
|
+
return relativeCandidates.some((candidate) => {
|
|
137
122
|
const content = safeRead(candidate);
|
|
138
123
|
return !content.startsWith("[FILE NOT FOUND]") && !content.startsWith("[ACCESS DENIED]");
|
|
139
124
|
});
|
|
@@ -1216,6 +1201,27 @@ export function registerFrameworkTools(server) {
|
|
|
1216
1201
|
ok: isGitRepo(),
|
|
1217
1202
|
detail: isGitRepo() ? "initialized" : "NOT a git repo",
|
|
1218
1203
|
});
|
|
1204
|
+
// Check: malformed store events were not silently dropped.
|
|
1205
|
+
const storePath = getWorkspaceStorePath(resolveWorkspaceRoot());
|
|
1206
|
+
if (existsSync(storePath)) {
|
|
1207
|
+
try {
|
|
1208
|
+
const store = await openStore(storePath, { readOnly: true });
|
|
1209
|
+
const skipped = store.skippedMalformedEventCount;
|
|
1210
|
+
await store.close();
|
|
1211
|
+
checks.push({
|
|
1212
|
+
name: "acepack:skipped-malformed-events",
|
|
1213
|
+
ok: skipped === 0,
|
|
1214
|
+
detail: skipped === 0 ? "0 skipped malformed events" : `${skipped} skipped malformed events`,
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
catch (error) {
|
|
1218
|
+
checks.push({
|
|
1219
|
+
name: "acepack:skipped-malformed-events",
|
|
1220
|
+
ok: false,
|
|
1221
|
+
detail: `store validation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1222
|
+
});
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1219
1225
|
// ── Deep checks (P2 hardening) ─────────────────────────────────
|
|
1220
1226
|
// Check: module registry role coverage vs event emitters
|
|
1221
1227
|
const registryRaw = safeRead("agent-state/MODULES/registry.json");
|
|
@@ -1385,7 +1391,9 @@ export function registerFrameworkTools(server) {
|
|
|
1385
1391
|
result.ok
|
|
1386
1392
|
? `✅ Public surface audit passed (${result.summary.tools} tools, ${result.summary.resources} resources, ${result.summary.prompts} prompts)`
|
|
1387
1393
|
: `❌ Public surface audit failed (${result.failures.length} failure${result.failures.length === 1 ? "" : "s"})`,
|
|
1388
|
-
`
|
|
1394
|
+
`Artifact: ${result.artifact_path}`,
|
|
1395
|
+
`Read: ace read ${result.artifact_path}`,
|
|
1396
|
+
...(result.written_path ? [`Store path: ${result.written_path}`] : []),
|
|
1389
1397
|
`Events: ${result.summary.emitted_events}`,
|
|
1390
1398
|
...(result.failures.slice(0, 12).map((entry) => `- ${entry}`)),
|
|
1391
1399
|
...(result.warnings.slice(0, 12).map((entry) => `- warning: ${entry}`)),
|
|
@@ -1406,8 +1414,9 @@ export function registerFrameworkTools(server) {
|
|
|
1406
1414
|
}, async ({ write_artifact, artifact_path }) => {
|
|
1407
1415
|
const result = auditStoreAuthority();
|
|
1408
1416
|
const reportPath = artifact_path ?? result.report_path;
|
|
1417
|
+
let writtenPath;
|
|
1409
1418
|
if (write_artifact !== false) {
|
|
1410
|
-
writeStoreAuthorityAuditReport({
|
|
1419
|
+
writtenPath = writeStoreAuthorityAuditReport({
|
|
1411
1420
|
...result,
|
|
1412
1421
|
report_path: reportPath,
|
|
1413
1422
|
}, reportPath);
|
|
@@ -1418,7 +1427,9 @@ export function registerFrameworkTools(server) {
|
|
|
1418
1427
|
type: "text",
|
|
1419
1428
|
text: [
|
|
1420
1429
|
`✅ Store authority audit completed (${result.total_safe_write_sites} safeWrite users)`,
|
|
1421
|
-
`
|
|
1430
|
+
`Artifact: ${reportPath}`,
|
|
1431
|
+
`Read: ace read ${reportPath}`,
|
|
1432
|
+
...(writtenPath ? [`Store path: ${writtenPath}`] : []),
|
|
1422
1433
|
`Bypassing ACEPACK: ${result.bypassing_sites.length}`,
|
|
1423
1434
|
`Runtime files without fallback keys: ${result.runtime_files_without_store_fallback_keys.length}`,
|
|
1424
1435
|
`Non-regenerable projections: ${result.non_regenerable_projections.length}`,
|
|
@@ -1436,7 +1447,7 @@ export function registerFrameworkTools(server) {
|
|
|
1436
1447
|
gate_ids: z
|
|
1437
1448
|
.array(z.string())
|
|
1438
1449
|
.optional()
|
|
1439
|
-
.describe("Specific gate IDs to run (e.g. ['gate-correctness']). Omit to run
|
|
1450
|
+
.describe("Specific gate IDs to run (e.g. ['gate-correctness']). Omit to run the active stage's stage-activated workspace gates."),
|
|
1440
1451
|
review_mode: z
|
|
1441
1452
|
.enum(["skeptic_adversarial"])
|
|
1442
1453
|
.optional()
|
|
@@ -1449,25 +1460,102 @@ export function registerFrameworkTools(server) {
|
|
|
1449
1460
|
const gatesDir = wsPath("agent-state", "MODULES", "gates");
|
|
1450
1461
|
const resolved = resolveGateManifests(gatesDir);
|
|
1451
1462
|
const allGates = resolved.gates;
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
:
|
|
1463
|
+
const manifestEvidenceRef = resolved.source === "store" ? "knowledge/gates/*" : "agent-state/MODULES/gates/";
|
|
1464
|
+
const selection = resolveGateSelection({
|
|
1465
|
+
gates: allGates,
|
|
1466
|
+
manifestSource: resolved.source,
|
|
1467
|
+
statusRaw: safeRead("agent-state/STATUS.md"),
|
|
1468
|
+
tealRaw: safeRead("agent-state/TEAL_CONFIG.md"),
|
|
1469
|
+
gateIds: gate_ids,
|
|
1470
|
+
});
|
|
1471
|
+
const targets = selection.selected;
|
|
1457
1472
|
if (targets.length === 0) {
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1473
|
+
if (allGates.length === 0) {
|
|
1474
|
+
return {
|
|
1475
|
+
content: [
|
|
1476
|
+
{
|
|
1477
|
+
type: "text",
|
|
1478
|
+
text: "❌ No gate manifests found in agent-state/MODULES/gates/ or ace-state.ace knowledge/gates/*.",
|
|
1479
|
+
},
|
|
1480
|
+
],
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
if (selection.selection_mode === "explicit") {
|
|
1484
|
+
const noGateMessage = selection.missing_explicit_gate_ids.length > 0
|
|
1485
|
+
? `❌ No matching gates found for: ${selection.missing_explicit_gate_ids.join(", ")}. Available: ${allGates.map((g) => g.id).join(", ")}`
|
|
1486
|
+
: `❌ No matching gates found. Available: ${allGates.map((g) => g.id).join(", ")}`;
|
|
1487
|
+
return {
|
|
1488
|
+
content: [
|
|
1489
|
+
{
|
|
1490
|
+
type: "text",
|
|
1491
|
+
text: noGateMessage,
|
|
1492
|
+
},
|
|
1493
|
+
],
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
const message = selection.note ??
|
|
1497
|
+
"No stage-activated workspace gates were selected for the active ACE stage. Use explicit gate_ids when needed.";
|
|
1498
|
+
await appendStatusEventSafe({
|
|
1499
|
+
source_module: "capability-framework",
|
|
1500
|
+
event_type: "GATES_EXECUTED",
|
|
1501
|
+
status: "pass",
|
|
1502
|
+
summary: message,
|
|
1503
|
+
payload: {
|
|
1504
|
+
gates_run: [],
|
|
1505
|
+
gate_results: [],
|
|
1506
|
+
passed: 0,
|
|
1507
|
+
failed: 0,
|
|
1508
|
+
selection_mode: selection.selection_mode,
|
|
1509
|
+
selected_gate_ids: [],
|
|
1510
|
+
skipped_gate_ids: selection.skipped_gate_ids,
|
|
1511
|
+
gate_manifest_source: resolved.source,
|
|
1512
|
+
active_role: selection.active_role ?? null,
|
|
1513
|
+
active_module: selection.active_module ?? null,
|
|
1514
|
+
active_pipeline: selection.active_pipeline ?? null,
|
|
1515
|
+
selection_note: message,
|
|
1516
|
+
evidence_ref: manifestEvidenceRef,
|
|
1517
|
+
},
|
|
1518
|
+
objective_id: "gate-execution",
|
|
1519
|
+
});
|
|
1520
|
+
const ledger = await appendRunLedgerEntrySafe({
|
|
1521
|
+
tool: "execute_gates",
|
|
1522
|
+
category: "info",
|
|
1523
|
+
message,
|
|
1524
|
+
artifacts: [],
|
|
1525
|
+
metadata: {
|
|
1526
|
+
passed: 0,
|
|
1527
|
+
failed: 0,
|
|
1528
|
+
selection_mode: selection.selection_mode,
|
|
1529
|
+
selected_gate_ids: [],
|
|
1530
|
+
skipped_gate_ids: selection.skipped_gate_ids,
|
|
1531
|
+
gate_manifest_source: resolved.source,
|
|
1532
|
+
active_role: selection.active_role ?? null,
|
|
1533
|
+
active_module: selection.active_module ?? null,
|
|
1534
|
+
active_pipeline: selection.active_pipeline ?? null,
|
|
1535
|
+
selection_note: message,
|
|
1536
|
+
},
|
|
1537
|
+
});
|
|
1461
1538
|
return {
|
|
1462
1539
|
content: [
|
|
1463
1540
|
{
|
|
1464
1541
|
type: "text",
|
|
1465
|
-
text:
|
|
1542
|
+
text: [
|
|
1543
|
+
`ℹ️ ${message}`,
|
|
1544
|
+
`Selection mode: ${selection.selection_mode}`,
|
|
1545
|
+
`Active role/module: ${selection.active_role ?? "(unknown)"} / ${selection.active_module ?? "(unknown)"}`,
|
|
1546
|
+
`Active pipeline: ${selection.active_pipeline ?? "(unknown)"}`,
|
|
1547
|
+
`Selected gates: none`,
|
|
1548
|
+
`Skipped gates: ${selection.skipped_gate_ids.length > 0 ? selection.skipped_gate_ids.join(", ") : "none"}`,
|
|
1549
|
+
`Gate manifest source: ${resolved.source === "store"
|
|
1550
|
+
? "ace-state.ace (knowledge/gates/*)"
|
|
1551
|
+
: "agent-state/MODULES/gates/"}`,
|
|
1552
|
+
`Run ledger: ${ledger.path} (${ledger.entry.id})`,
|
|
1553
|
+
].join("\n"),
|
|
1466
1554
|
},
|
|
1467
1555
|
],
|
|
1468
1556
|
};
|
|
1469
1557
|
}
|
|
1470
|
-
const gateArtifactRefs = targets.map((gate) => gate.source_ref ??
|
|
1558
|
+
const gateArtifactRefs = targets.map((gate) => gate.source_ref ?? manifestEvidenceRef);
|
|
1471
1559
|
const results = evaluateGateTargets(targets);
|
|
1472
1560
|
const passed = results.filter((r) => r.ok).length;
|
|
1473
1561
|
const failed = results.filter((r) => !r.ok).length;
|
|
@@ -1495,7 +1583,13 @@ export function registerFrameworkTools(server) {
|
|
|
1495
1583
|
})),
|
|
1496
1584
|
passed,
|
|
1497
1585
|
failed,
|
|
1498
|
-
evidence_ref: evidence?.evidenceRef ??
|
|
1586
|
+
evidence_ref: evidence?.evidenceRef ?? manifestEvidenceRef,
|
|
1587
|
+
selection_mode: selection.selection_mode,
|
|
1588
|
+
active_role: selection.active_role ?? null,
|
|
1589
|
+
active_module: selection.active_module ?? null,
|
|
1590
|
+
active_pipeline: selection.active_pipeline ?? null,
|
|
1591
|
+
selected_gate_ids: results.map((r) => r.id),
|
|
1592
|
+
skipped_gate_ids: selection.skipped_gate_ids,
|
|
1499
1593
|
gate_manifest_source: resolved.source,
|
|
1500
1594
|
...(review
|
|
1501
1595
|
? {
|
|
@@ -1530,6 +1624,13 @@ export function registerFrameworkTools(server) {
|
|
|
1530
1624
|
passed,
|
|
1531
1625
|
failed,
|
|
1532
1626
|
gate_ids: results.map((r) => r.id),
|
|
1627
|
+
selection_mode: selection.selection_mode,
|
|
1628
|
+
active_role: selection.active_role ?? null,
|
|
1629
|
+
active_module: selection.active_module ?? null,
|
|
1630
|
+
active_pipeline: selection.active_pipeline ?? null,
|
|
1631
|
+
selected_gate_ids: results.map((r) => r.id),
|
|
1632
|
+
skipped_gate_ids: selection.skipped_gate_ids,
|
|
1633
|
+
missing_explicit_gate_ids: selection.missing_explicit_gate_ids,
|
|
1533
1634
|
gate_manifest_source: resolved.source,
|
|
1534
1635
|
...(review
|
|
1535
1636
|
? {
|
|
@@ -1559,6 +1660,11 @@ export function registerFrameworkTools(server) {
|
|
|
1559
1660
|
`Gate manifest source: ${resolved.source === "store"
|
|
1560
1661
|
? "ace-state.ace (knowledge/gates/*)"
|
|
1561
1662
|
: "agent-state/MODULES/gates/"}`,
|
|
1663
|
+
`Selection mode: ${selection.selection_mode}`,
|
|
1664
|
+
`Active role/module: ${selection.active_role ?? "(unknown)"} / ${selection.active_module ?? "(unknown)"}`,
|
|
1665
|
+
`Active pipeline: ${selection.active_pipeline ?? "(unknown)"}`,
|
|
1666
|
+
`Selected gates: ${results.map((r) => r.id).join(", ")}`,
|
|
1667
|
+
`Skipped gates: ${selection.skipped_gate_ids.length > 0 ? selection.skipped_gate_ids.join(", ") : "none"}`,
|
|
1562
1668
|
`Run ledger: ${ledger.path} (${ledger.entry.id})`,
|
|
1563
1669
|
...(evidence ? [`Evidence: ${evidence.path} (${evidence.evidenceRef})`] : []),
|
|
1564
1670
|
"",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACE Goal Engine tool registrations (WS-F).
|
|
3
|
+
*
|
|
4
|
+
* These let a premium host delegate a long-running objective to ACE and poll
|
|
5
|
+
* it. `start_goal`/`resume_goal` never block the MCP server for the life of the
|
|
6
|
+
* goal: they compile/prepare, then hand execution to a detached `ace goal run`
|
|
7
|
+
* child and return immediately. The host polls `get_goal_status`.
|
|
8
|
+
*/
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
export declare function registerGoalTools(server: McpServer): void;
|
|
11
|
+
//# sourceMappingURL=tools-goal.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACE Goal Engine tool registrations (WS-F).
|
|
3
|
+
*
|
|
4
|
+
* These let a premium host delegate a long-running objective to ACE and poll
|
|
5
|
+
* it. `start_goal`/`resume_goal` never block the MCP server for the life of the
|
|
6
|
+
* goal: they compile/prepare, then hand execution to a detached `ace goal run`
|
|
7
|
+
* child and return immediately. The host polls `get_goal_status`.
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { getGoalStatus, listGoals, startGoal, stopGoal, } from "./goal-engine.js";
|
|
13
|
+
function asJsonBlock(value) {
|
|
14
|
+
return `\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``;
|
|
15
|
+
}
|
|
16
|
+
function spawnDetachedRunner(goalId, verb) {
|
|
17
|
+
try {
|
|
18
|
+
// dist/cli.js is a sibling of this compiled module.
|
|
19
|
+
const cliPath = fileURLToPath(new URL("./cli.js", import.meta.url));
|
|
20
|
+
const child = spawn(process.execPath, [cliPath, "goal", verb, goalId], {
|
|
21
|
+
detached: true,
|
|
22
|
+
stdio: "ignore",
|
|
23
|
+
});
|
|
24
|
+
child.unref();
|
|
25
|
+
return child.pid;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function registerGoalTools(server) {
|
|
32
|
+
server.tool("start_goal", "Compile and launch a long-running autonomous goal (ACE plans it, executes step-by-step under evidence-gated completion, and can never declare success by prose). Returns immediately; poll with get_goal_status.", {
|
|
33
|
+
objective: z.string().min(1).describe("What the goal should accomplish"),
|
|
34
|
+
budget_hours: z.number().positive().max(24).optional().describe("Wall-clock budget (default 4h)"),
|
|
35
|
+
max_replans: z.number().int().min(0).max(20).optional().describe("Max plan-level replans (default 3)"),
|
|
36
|
+
plan_only: z.boolean().optional().describe("Compile + validate the plan but do not execute"),
|
|
37
|
+
detach: z.boolean().optional().describe("Run in a detached background child (default true when not plan_only)"),
|
|
38
|
+
}, async ({ objective, budget_hours, max_replans, plan_only, detach }) => {
|
|
39
|
+
const started = await startGoal(objective, { budget_hours, max_replans, plan_only });
|
|
40
|
+
let runnerPid;
|
|
41
|
+
if (started.ok && !plan_only && detach !== false) {
|
|
42
|
+
runnerPid = spawnDetachedRunner(started.goal.goal_id, "run");
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: [
|
|
49
|
+
started.ok ? "✅ Goal started" : "❌ Goal not started",
|
|
50
|
+
`goal_id: ${started.goal.goal_id}`,
|
|
51
|
+
`status: ${started.goal.status}`,
|
|
52
|
+
started.reason_code ? `reason: ${started.reason_code}` : "",
|
|
53
|
+
runnerPid ? `detached runner pid: ${runnerPid}` : "",
|
|
54
|
+
asJsonBlock({ ...started, runner_pid: runnerPid }),
|
|
55
|
+
]
|
|
56
|
+
.filter(Boolean)
|
|
57
|
+
.join("\n"),
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
server.tool("get_goal_status", "Get a goal's status (tick count, current step, last heartbeat, escalations, plan status), or list all goals when no goal_id is given.", {
|
|
63
|
+
goal_id: z.string().optional().describe("Goal id; omit to list all goals"),
|
|
64
|
+
}, async ({ goal_id }) => {
|
|
65
|
+
if (!goal_id) {
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: "text", text: asJsonBlock({ goals: listGoals() }) }],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const view = getGoalStatus(goal_id);
|
|
71
|
+
if (!view) {
|
|
72
|
+
return { content: [{ type: "text", text: `❌ Unknown goal: ${goal_id}` }] };
|
|
73
|
+
}
|
|
74
|
+
return { content: [{ type: "text", text: asJsonBlock(view) }] };
|
|
75
|
+
});
|
|
76
|
+
server.tool("stop_goal", "Request that a running goal stop at its next tick boundary.", { goal_id: z.string().describe("Goal id to stop") }, async ({ goal_id }) => {
|
|
77
|
+
try {
|
|
78
|
+
const goal = stopGoal(goal_id);
|
|
79
|
+
return {
|
|
80
|
+
content: [{ type: "text", text: `🛑 Stop requested for ${goal.goal_id} (status ${goal.status}).` }],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: "text", text: `❌ ${err instanceof Error ? err.message : String(err)}` }],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
server.tool("resume_goal", "Resume a blocked/stopped goal: reset any step left running mid-crash and continue execution in a detached runner.", { goal_id: z.string().describe("Goal id to resume") }, async ({ goal_id }) => {
|
|
90
|
+
const view = getGoalStatus(goal_id);
|
|
91
|
+
if (!view) {
|
|
92
|
+
return { content: [{ type: "text", text: `❌ Unknown goal: ${goal_id}` }] };
|
|
93
|
+
}
|
|
94
|
+
const runnerPid = spawnDetachedRunner(goal_id, "resume");
|
|
95
|
+
return {
|
|
96
|
+
content: [
|
|
97
|
+
{
|
|
98
|
+
type: "text",
|
|
99
|
+
text: [
|
|
100
|
+
`▶️ Resuming ${goal_id}`,
|
|
101
|
+
runnerPid ? `detached runner pid: ${runnerPid}` : "runner spawn failed; run `ace goal resume` manually",
|
|
102
|
+
].join("\n"),
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=tools-goal.js.map
|