@voybio/ace-swarm 0.2.4 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/README.md +20 -13
- package/assets/.agents/skills/eval-harness/SKILL.md +14 -0
- package/assets/.agents/skills/handoff-lint/SKILL.md +14 -0
- package/assets/.agents/skills/incident-commander/SKILL.md +14 -0
- package/assets/.agents/skills/memory-curator/SKILL.md +14 -0
- package/assets/.agents/skills/release-sentry/SKILL.md +14 -0
- package/assets/.agents/skills/risk-quant/SKILL.md +14 -0
- package/assets/.agents/skills/schema-forge/SKILL.md +14 -0
- package/assets/.agents/skills/state-auditor/SKILL.md +14 -0
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/MODULES/gates/gate-correctness.json +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/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
- package/assets/agent-state/STATUS.md +2 -2
- package/assets/scripts/ace-hook-dispatch.mjs +70 -6
- package/assets/scripts/render-mcp-configs.sh +19 -5
- package/dist/ace-context.js +22 -1
- package/dist/ace-server-instructions.js +3 -3
- package/dist/ace-state-resolver.js +5 -3
- package/dist/astgrep-index.d.ts +9 -1
- package/dist/astgrep-index.js +14 -3
- package/dist/cli.js +52 -20
- 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 +288 -0
- package/dist/helpers/drift.d.ts +13 -0
- package/dist/helpers/drift.js +45 -0
- package/dist/helpers/path-utils.d.ts +17 -0
- package/dist/helpers/path-utils.js +104 -0
- package/dist/helpers/store-resolution.d.ts +19 -0
- package/dist/helpers/store-resolution.js +301 -0
- package/dist/helpers/workspace-root.d.ts +3 -0
- package/dist/helpers/workspace-root.js +80 -0
- package/dist/helpers.d.ts +8 -123
- package/dist/helpers.js +8 -1747
- package/dist/job-scheduler.js +3 -3
- package/dist/local-model-runtime.js +12 -1
- package/dist/model-bridge.d.ts +7 -0
- package/dist/model-bridge.js +75 -5
- package/dist/orchestrator-supervisor.d.ts +14 -0
- package/dist/orchestrator-supervisor.js +72 -1
- 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 +14 -0
- package/dist/runtime-executor.js +669 -171
- package/dist/runtime-profile.d.ts +32 -0
- package/dist/runtime-profile.js +89 -13
- package/dist/runtime-tool-specs.d.ts +21 -0
- package/dist/runtime-tool-specs.js +78 -3
- package/dist/safe-edit.d.ts +7 -0
- package/dist/safe-edit.js +163 -37
- package/dist/schemas.js +19 -0
- package/dist/shared.d.ts +2 -2
- 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 +1 -1
- package/dist/store/bootstrap-store.js +94 -81
- package/dist/store/cache-workspace.d.ts +22 -0
- package/dist/store/cache-workspace.js +149 -0
- package/dist/store/materializers/context-snapshot-materializer.js +6 -7
- 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/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.js +319 -34
- package/dist/tools-discovery.js +1 -1
- package/dist/tools-files.d.ts +7 -0
- package/dist/tools-files.js +299 -10
- package/dist/tools-framework.js +107 -27
- 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/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/vericify-bridge.d.ts +5 -0
- 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/tools-files.js
CHANGED
|
@@ -1,18 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* File operation tool registrations + new safe-edit and diff tools.
|
|
3
3
|
*/
|
|
4
|
-
import { readdirSync } from "node:fs";
|
|
5
|
-
import { isAbsolute, relative } from "node:path";
|
|
4
|
+
import { mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import { ACE_TASKS_ROOT_REL, normalizePathForValidation, safeRead, safeWrite, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
|
|
8
|
-
import { looksLikeSwarmHandoffPath } from "./shared.js";
|
|
7
|
+
import { ACE_TASKS_ROOT_REL, normalizePathForValidation, safeRead, safeWriteAsync, safeWrite, resolveStoreFallbackKeysForPath, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
|
|
8
|
+
import { isInside, looksLikeSwarmHandoffPath, normalizeRelPath } from "./shared.js";
|
|
9
9
|
import { validateAgentStateHandoffPayload, validateArtifactManifestPayload, validateProvenanceLogContent, validateStatusEventsNdjsonContent, validateSwarmHandoffPayload, validateTealConfigContent, validateRuntimeExecutorSessionRegistryPayload, validateRuntimeToolSpecRegistryPayload, validateTrackerSnapshotPayload, validateVericifyBridgeSnapshotPayload, validateVericifyProcessPostLogPayload, validateWorkspaceSessionRegistryPayload, lintHandoffPayload, } from "./schemas.js";
|
|
10
10
|
import { syncTodoStateSafe } from "./todo-state.js";
|
|
11
|
-
import { validateRuntimeProfileContent } from "./runtime-profile.js";
|
|
12
11
|
import { shouldAutoRefreshKanbanForPath, refreshKanbanArtifacts, } from "./kanban.js";
|
|
13
12
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
14
13
|
import { appendStatusEventSafe } from "./status-events.js";
|
|
15
|
-
import { safeEditFile, diffContents } from "./safe-edit.js";
|
|
14
|
+
import { safeEditFile, diffContents, applyPatch } from "./safe-edit.js";
|
|
15
|
+
import { readRuntimeProfile, resolveEffectiveSurgicalReadBudget, validateRuntimeProfileContent, } from "./runtime-profile.js";
|
|
16
|
+
import { withLocalModelRuntimeRepository } from "./store/repositories/local-model-runtime-repository.js";
|
|
17
|
+
export function planAstgrepRewriteTargets(files) {
|
|
18
|
+
const affected = [...new Set(files.filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
19
|
+
if (affected.length > 1) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
affected_files: affected,
|
|
23
|
+
error: `astgrep_rewrite refuses multi-file rewrites (${affected.length} files): ${affected.slice(0, 5).join(", ")}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
ok: true,
|
|
28
|
+
affected_files: affected,
|
|
29
|
+
target_file: affected[0],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function appendAstgrepRewriteTransition(sessionId, to, reason, evidenceRefs) {
|
|
33
|
+
if (!sessionId)
|
|
34
|
+
return;
|
|
35
|
+
await withLocalModelRuntimeRepository(WORKSPACE_ROOT, async (repo) => {
|
|
36
|
+
await repo.appendTransitionRecord({
|
|
37
|
+
subject_kind: "plan_step",
|
|
38
|
+
subject_id: `${sessionId}/astgrep_rewrite`,
|
|
39
|
+
from: "staged",
|
|
40
|
+
to,
|
|
41
|
+
reason,
|
|
42
|
+
reason_code: to === "promoted" ? "surgical_rewrite" : "surgical_rewrite_failed",
|
|
43
|
+
evidence_refs: evidenceRefs,
|
|
44
|
+
});
|
|
45
|
+
}).catch(() => undefined);
|
|
46
|
+
}
|
|
47
|
+
function astgrepFileToWorkspaceRel(file) {
|
|
48
|
+
const abs = isAbsolute(file) ? file : resolve(WORKSPACE_ROOT, file);
|
|
49
|
+
if (!isInside(WORKSPACE_ROOT, abs))
|
|
50
|
+
return undefined;
|
|
51
|
+
return normalizeRelPath(relative(WORKSPACE_ROOT, abs));
|
|
52
|
+
}
|
|
53
|
+
async function resolveReadFileLinesBudget() {
|
|
54
|
+
let modelClass;
|
|
55
|
+
const statuses = await withLocalModelRuntimeRepository(WORKSPACE_ROOT, async (repo) => repo.listRuntimeStatuses()).catch(() => undefined);
|
|
56
|
+
modelClass = statuses?.[0]?.model_class;
|
|
57
|
+
const profile = readRuntimeProfile();
|
|
58
|
+
const resolved = resolveEffectiveSurgicalReadBudget(profile, modelClass);
|
|
59
|
+
return {
|
|
60
|
+
...resolved,
|
|
61
|
+
source: modelClass ? "runtime-status" : "runtime-profile",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
16
64
|
export function registerFileTools(server) {
|
|
17
65
|
// ── Append-only artifact protection ───────────────────────────────
|
|
18
66
|
// These files must only grow; overwrites from the generic write tool
|
|
@@ -26,12 +74,12 @@ export function registerFileTools(server) {
|
|
|
26
74
|
"agent-state/PROVENANCE_LOG.md",
|
|
27
75
|
]);
|
|
28
76
|
// ── Core file operations ──────────────────────────────────────────
|
|
29
|
-
server.tool("read_workspace_file", "Read any workspace file by relative path", {
|
|
77
|
+
server.tool("read_workspace_file", "Read any workspace file by relative path. Prefer outline_file for discovery, read_file_lines for known regions, astgrep_query for symbol lookup. For small_local runs, this is the fallback after surgical reads are insufficient. Cost: heavy for large files.", {
|
|
30
78
|
path: z.string().describe("Relative path from workspace root"),
|
|
31
79
|
}, async ({ path }) => ({
|
|
32
80
|
content: [{ type: "text", text: safeRead(path) }],
|
|
33
81
|
}));
|
|
34
|
-
server.tool("write_workspace_file", "Write or update a workspace file", {
|
|
82
|
+
server.tool("write_workspace_file", "Write or update a workspace file. Prefer apply_patch for targeted edits, astgrep_rewrite for structural rewrites. Cost: heavy.", {
|
|
35
83
|
path: z.string().describe("Relative path from workspace root"),
|
|
36
84
|
content: z.string().describe("File content"),
|
|
37
85
|
}, async ({ path, content }) => {
|
|
@@ -475,8 +523,10 @@ export function registerFileTools(server) {
|
|
|
475
523
|
const synced = await syncTodoStateSafe(content);
|
|
476
524
|
todoStateSuffix = `\nTODO state synced: ${synced.path}`;
|
|
477
525
|
}
|
|
526
|
+
const storeFallbackKeys = resolveStoreFallbackKeysForPath(normalizedPath);
|
|
527
|
+
const useAsyncStoreAdmission = storeFallbackKeys.length > 0;
|
|
478
528
|
// Keep the file as a materialized projection after the canonical store update.
|
|
479
|
-
const abs = safeWrite(path, content);
|
|
529
|
+
const abs = useAsyncStoreAdmission ? await safeWriteAsync(path, content) : safeWrite(path, content);
|
|
480
530
|
let kanbanSuffix = "";
|
|
481
531
|
const shouldRefreshKanban = shouldAutoRefreshKanbanForPath(normalizedPath);
|
|
482
532
|
if (shouldRefreshKanban) {
|
|
@@ -544,7 +594,7 @@ export function registerFileTools(server) {
|
|
|
544
594
|
}
|
|
545
595
|
});
|
|
546
596
|
// ── Safe-edit (new P0 tool) ───────────────────────────────────────
|
|
547
|
-
server.tool("safe_edit_file", "Edit a file using copy→validate→swap pattern. Automatically rolls back if validation or tests fail.", {
|
|
597
|
+
server.tool("safe_edit_file", "Edit a file using copy→validate→swap pattern. Automatically rolls back if validation or tests fail. Prefer apply_patch for small targeted edits. Cost: heavy for large files.", {
|
|
548
598
|
path: z.string().describe("Workspace-relative file path to edit"),
|
|
549
599
|
content: z.string().describe("New file content"),
|
|
550
600
|
validation_command: z
|
|
@@ -676,5 +726,244 @@ export function registerFileTools(server) {
|
|
|
676
726
|
],
|
|
677
727
|
};
|
|
678
728
|
});
|
|
729
|
+
// ── Surgical read / query / patch tools ──────────────────────────
|
|
730
|
+
server.tool("read_file_lines", "Read a specific line range from a workspace file. Prefer this over read_workspace_file for large files. Cost: moderate.", {
|
|
731
|
+
path: z.string().describe("Workspace-relative file path"),
|
|
732
|
+
start_line: z.number().int().min(1).describe("First line to read (1-indexed)"),
|
|
733
|
+
end_line: z.number().int().min(1).describe("Last line to read (inclusive)"),
|
|
734
|
+
symbol_anchor: z.string().optional().describe("Optional symbol name hint for context (informational only)"),
|
|
735
|
+
}, async ({ path: relPath, start_line, end_line, symbol_anchor }) => {
|
|
736
|
+
const budget = await resolveReadFileLinesBudget();
|
|
737
|
+
const requestedLines = end_line - start_line + 1;
|
|
738
|
+
const budgetText = budget.read_file_lines_max_lines === null
|
|
739
|
+
? "unbounded"
|
|
740
|
+
: String(budget.read_file_lines_max_lines);
|
|
741
|
+
const headerLines = [
|
|
742
|
+
`# Budget: model_class=${budget.model_class}; read_file_lines_max_lines=${budgetText}`,
|
|
743
|
+
`# Budget source: ${budget.source}`,
|
|
744
|
+
];
|
|
745
|
+
if (budget.read_file_lines_max_lines !== null &&
|
|
746
|
+
requestedLines > budget.read_file_lines_max_lines) {
|
|
747
|
+
return {
|
|
748
|
+
content: [
|
|
749
|
+
{
|
|
750
|
+
type: "text",
|
|
751
|
+
text: [
|
|
752
|
+
...headerLines,
|
|
753
|
+
`Error: requested range (${requestedLines} lines) exceeds ${budget.model_class} budget of ${budgetText} lines.`,
|
|
754
|
+
"Use outline_file for discovery or astgrep_query for symbol lookup.",
|
|
755
|
+
].join("\n"),
|
|
756
|
+
},
|
|
757
|
+
],
|
|
758
|
+
isError: true,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
const content = safeRead(relPath);
|
|
762
|
+
const lines = content.split("\n");
|
|
763
|
+
const slice = lines.slice(start_line - 1, end_line);
|
|
764
|
+
const result = slice.map((l, i) => `${start_line + i}: ${l}`).join("\n");
|
|
765
|
+
return {
|
|
766
|
+
content: [
|
|
767
|
+
{
|
|
768
|
+
type: "text",
|
|
769
|
+
text: [
|
|
770
|
+
...headerLines,
|
|
771
|
+
`# ${relPath} (lines ${start_line}–${end_line})`,
|
|
772
|
+
symbol_anchor ? `# Symbol context: ${symbol_anchor}` : "",
|
|
773
|
+
"",
|
|
774
|
+
result,
|
|
775
|
+
]
|
|
776
|
+
.filter(Boolean)
|
|
777
|
+
.join("\n"),
|
|
778
|
+
},
|
|
779
|
+
],
|
|
780
|
+
};
|
|
781
|
+
});
|
|
782
|
+
server.tool("outline_file", "Return symbol signatures and top-level structure of a file without bodies. Use for discovery before reading specific regions. Cost: cheap.", {
|
|
783
|
+
path: z.string().describe("Workspace-relative file path"),
|
|
784
|
+
}, async ({ path: relPath }) => {
|
|
785
|
+
const content = safeRead(relPath);
|
|
786
|
+
const lines = content.split("\n");
|
|
787
|
+
const outlinePatterns = [
|
|
788
|
+
/^export\s+(async\s+)?function\s+\w+/,
|
|
789
|
+
/^export\s+(class|interface|type|enum|const|abstract)\s+\w+/,
|
|
790
|
+
/^(export\s+)?default\s+/,
|
|
791
|
+
/^\s+(async\s+)?(\w+\s+)?\w+\s*\([^)]*\)\s*[:{]/,
|
|
792
|
+
/^(function|class|interface|type|enum|const|let|var)\s+\w+/,
|
|
793
|
+
];
|
|
794
|
+
const outline = [];
|
|
795
|
+
lines.forEach((line, idx) => {
|
|
796
|
+
if (outlinePatterns.some(p => p.test(line))) {
|
|
797
|
+
outline.push(`${idx + 1}: ${line.trimEnd()}`);
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
return {
|
|
801
|
+
content: [{
|
|
802
|
+
type: "text",
|
|
803
|
+
text: [
|
|
804
|
+
`# Outline: ${relPath} (${lines.length} lines total)`,
|
|
805
|
+
"",
|
|
806
|
+
outline.length > 0 ? outline.join("\n") : "(no symbols found)",
|
|
807
|
+
].join("\n"),
|
|
808
|
+
}],
|
|
809
|
+
};
|
|
810
|
+
});
|
|
811
|
+
server.tool("astgrep_query", "Search for a structural pattern in workspace files using ast-grep. Returns matching ranges without reading whole files. Cost: cheap.", {
|
|
812
|
+
pattern: z.string().describe("ast-grep pattern, e.g. 'export function $NAME($$$)'"),
|
|
813
|
+
lang: z.string().describe("Language: ts, py, rust, go, js"),
|
|
814
|
+
scope: z.string().optional().describe("Workspace-relative directory to search (default: src)"),
|
|
815
|
+
max_results: z.number().int().min(1).max(200).optional().describe("Max results to return (default: 50)"),
|
|
816
|
+
}, async ({ pattern, lang, scope, max_results }) => {
|
|
817
|
+
const { runAstgrepQuery } = await import("./astgrep-index.js");
|
|
818
|
+
const root = wsPath(scope ?? "src");
|
|
819
|
+
const matches = runAstgrepQuery(pattern, lang, [root]);
|
|
820
|
+
const limited = matches.slice(0, max_results ?? 50);
|
|
821
|
+
const lines = limited.map((m) => `${m.file}:${m.line}: ${m.text}`);
|
|
822
|
+
return {
|
|
823
|
+
content: [{
|
|
824
|
+
type: "text",
|
|
825
|
+
text: [
|
|
826
|
+
`# astgrep_query: ${pattern} (${lang})`,
|
|
827
|
+
`# Found ${matches.length} matches, showing ${limited.length}`,
|
|
828
|
+
"",
|
|
829
|
+
...lines,
|
|
830
|
+
].join("\n"),
|
|
831
|
+
}],
|
|
832
|
+
};
|
|
833
|
+
});
|
|
834
|
+
server.tool("astgrep_rewrite", "Apply a structural rewrite to one workspace file using ast-grep pattern + replacement. Multi-file rewrites are refused; staged through safe_edit_file. Emits transition-compatible result details. Cost: heavy.", {
|
|
835
|
+
pattern: z.string().describe("ast-grep pattern to match"),
|
|
836
|
+
rewrite: z.string().describe("Replacement template (use $NAME etc. from pattern)"),
|
|
837
|
+
lang: z.string().describe("Language: ts, py, rust, go, js"),
|
|
838
|
+
scope: z.string().optional().describe("Workspace-relative directory (default: src)"),
|
|
839
|
+
confirm_multi_file: z.boolean().optional().describe("Deprecated; multi-file rewrites are always refused"),
|
|
840
|
+
validation_command: z.string().optional().describe("Shell command to validate the staged rewrite before promotion"),
|
|
841
|
+
test_command: z.string().optional().describe("Shell command to test the staged rewrite before promotion"),
|
|
842
|
+
session_id: z.string().optional().describe("Optional runtime session id for transition-record emission"),
|
|
843
|
+
}, async ({ pattern, rewrite, lang, scope, validation_command, test_command, session_id }) => {
|
|
844
|
+
const { runAstgrepQuery } = await import("./astgrep-index.js");
|
|
845
|
+
const root = wsPath(scope ?? "src");
|
|
846
|
+
if (!isInside(WORKSPACE_ROOT, root)) {
|
|
847
|
+
return {
|
|
848
|
+
content: [{ type: "text", text: `astgrep_rewrite failed: scope escapes workspace root (${scope})` }],
|
|
849
|
+
isError: true,
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
const matches = runAstgrepQuery(pattern, lang, [root]);
|
|
853
|
+
const affectedFiles = [...new Set(matches.map(m => m.file))];
|
|
854
|
+
const plan = planAstgrepRewriteTargets(affectedFiles);
|
|
855
|
+
if (!plan.ok) {
|
|
856
|
+
await appendAstgrepRewriteTransition(session_id, "refused", plan.error ?? "astgrep_rewrite refused the requested rewrite", plan.affected_files);
|
|
857
|
+
return {
|
|
858
|
+
content: [{
|
|
859
|
+
type: "text",
|
|
860
|
+
text: plan.error ?? "astgrep_rewrite refused the requested rewrite",
|
|
861
|
+
}],
|
|
862
|
+
isError: true,
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
if (matches.length === 0) {
|
|
866
|
+
return {
|
|
867
|
+
content: [{ type: "text", text: "No matches found for pattern. No files modified." }],
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
const targetRel = astgrepFileToWorkspaceRel(plan.target_file ?? "");
|
|
871
|
+
if (!targetRel) {
|
|
872
|
+
return {
|
|
873
|
+
content: [{ type: "text", text: `astgrep_rewrite failed: matched path escapes workspace root (${plan.target_file ?? ""})` }],
|
|
874
|
+
isError: true,
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
const originalContent = safeRead(targetRel);
|
|
878
|
+
if (originalContent.startsWith("[FILE NOT FOUND]") || originalContent.startsWith("[ACCESS DENIED]")) {
|
|
879
|
+
return {
|
|
880
|
+
content: [{ type: "text", text: `astgrep_rewrite failed: cannot read ${targetRel}\n${originalContent}` }],
|
|
881
|
+
isError: true,
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
const stagingDir = wsPath(".ace-staging", `astgrep-rewrite-${Date.now()}`);
|
|
885
|
+
const stagedFile = resolve(stagingDir, targetRel);
|
|
886
|
+
mkdirSync(dirname(stagedFile), { recursive: true });
|
|
887
|
+
writeFileSync(stagedFile, originalContent, "utf-8");
|
|
888
|
+
const { spawnSync } = await import("node:child_process");
|
|
889
|
+
const result = spawnSync("ast-grep", ["--pattern", pattern, "--rewrite", rewrite, "--lang", lang, stagedFile, "--update-all"], { encoding: "utf8", cwd: WORKSPACE_ROOT });
|
|
890
|
+
if (result.status !== 0) {
|
|
891
|
+
await appendAstgrepRewriteTransition(session_id, "failed", `astgrep_rewrite failed in staging: ${result.stderr || result.stdout || "unknown error"}`, [targetRel]);
|
|
892
|
+
return {
|
|
893
|
+
content: [{ type: "text", text: `astgrep rewrite failed:\n${result.stderr}` }],
|
|
894
|
+
isError: true,
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
const rewrittenContent = readFileSync(stagedFile, "utf-8");
|
|
898
|
+
const safeResult = safeEditFile({
|
|
899
|
+
path: targetRel,
|
|
900
|
+
content: rewrittenContent,
|
|
901
|
+
validation_command,
|
|
902
|
+
test_command,
|
|
903
|
+
});
|
|
904
|
+
if (!safeResult.ok) {
|
|
905
|
+
await appendAstgrepRewriteTransition(session_id, "failed", `astgrep_rewrite failed before promotion for ${targetRel}: ${safeResult.error ?? "unknown error"}`, [targetRel]);
|
|
906
|
+
return {
|
|
907
|
+
content: [{
|
|
908
|
+
type: "text",
|
|
909
|
+
text: [
|
|
910
|
+
`astgrep_rewrite failed before promotion`,
|
|
911
|
+
`Path: ${targetRel}`,
|
|
912
|
+
`Error: ${safeResult.error ?? "unknown error"}`,
|
|
913
|
+
safeResult.validation_passed !== undefined ? `Validation: ${safeResult.validation_passed ? "passed" : "FAILED"}` : "Validation: not requested",
|
|
914
|
+
safeResult.validation_output ? `Validation output:\n${safeResult.validation_output}` : "",
|
|
915
|
+
safeResult.test_passed !== undefined ? `Tests: ${safeResult.test_passed ? "passed" : "FAILED"}` : "Tests: not requested",
|
|
916
|
+
safeResult.test_output ? `Test output:\n${safeResult.test_output}` : "",
|
|
917
|
+
`Staging: ${safeResult.staging_path || stagingDir}`,
|
|
918
|
+
].filter(Boolean).join("\n"),
|
|
919
|
+
}],
|
|
920
|
+
isError: true,
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
await appendAstgrepRewriteTransition(session_id, "promoted", `astgrep_rewrite promoted staged rewrite for ${targetRel}`, [targetRel]);
|
|
924
|
+
return {
|
|
925
|
+
content: [{
|
|
926
|
+
type: "text",
|
|
927
|
+
text: [
|
|
928
|
+
`# astgrep_rewrite completed`,
|
|
929
|
+
`Pattern: ${pattern}`,
|
|
930
|
+
`Rewrite: ${rewrite}`,
|
|
931
|
+
`Files affected: ${affectedFiles.length}`,
|
|
932
|
+
`Path: ${targetRel}`,
|
|
933
|
+
`Hash: ${safeResult.original_hash} → ${safeResult.new_hash}`,
|
|
934
|
+
safeResult.validation_passed !== undefined ? `Validation: ${safeResult.validation_passed ? "passed" : "failed"}` : "Validation: not requested",
|
|
935
|
+
safeResult.test_passed !== undefined ? `Tests: ${safeResult.test_passed ? "passed" : "failed"}` : "Tests: not requested",
|
|
936
|
+
`Staging: ${safeResult.staging_path}`,
|
|
937
|
+
result.stdout ? `\nOutput:\n${result.stdout}` : "",
|
|
938
|
+
].filter(Boolean).join("\n"),
|
|
939
|
+
}],
|
|
940
|
+
};
|
|
941
|
+
});
|
|
942
|
+
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.", {
|
|
943
|
+
path: z.string().describe("Workspace-relative file path to patch"),
|
|
944
|
+
patch: z.string().describe("Unified diff patch (output of diff -u or similar)"),
|
|
945
|
+
validation_command: z.string().optional().describe("Shell command to validate after applying (e.g. 'npx tsc --noEmit')"),
|
|
946
|
+
test_command: z.string().optional().describe("Shell command to run tests after applying"),
|
|
947
|
+
}, async ({ path: relPath, patch, validation_command, test_command }) => {
|
|
948
|
+
const result = applyPatch({ path: relPath, patch, validation_command, test_command });
|
|
949
|
+
if (!result.ok) {
|
|
950
|
+
return {
|
|
951
|
+
content: [{ type: "text", text: `apply_patch failed: ${result.error ?? "unknown error"}\n${result.validation_output ?? ""}` }],
|
|
952
|
+
isError: true,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
return {
|
|
956
|
+
content: [{
|
|
957
|
+
type: "text",
|
|
958
|
+
text: [
|
|
959
|
+
`# apply_patch: ${relPath}`,
|
|
960
|
+
`Status: success`,
|
|
961
|
+
result.validation_passed !== undefined ? `Validation: ${result.validation_passed ? "passed" : "failed"}` : "",
|
|
962
|
+
result.test_passed !== undefined ? `Tests: ${result.test_passed ? "passed" : "failed"}` : "",
|
|
963
|
+
`Staging: ${result.staging_path}`,
|
|
964
|
+
].filter(Boolean).join("\n"),
|
|
965
|
+
}],
|
|
966
|
+
};
|
|
967
|
+
});
|
|
679
968
|
}
|
|
680
969
|
//# sourceMappingURL=tools-files.js.map
|
package/dist/tools-framework.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { bootstrapStoreWorkspace } from "./store/bootstrap-store.js";
|
|
7
|
-
import { ACE_ROOT_REL, ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_LLM_PROVIDERS, ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, WORKSPACE_ROOT, classifyPathSource, detectAssetDrift, getAllMcpServerConfigSnippets, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, getMcpClientInstallHint, getMcpServerConfigSnippet, getTaskArtifactPath, isSwarmRole, listAvailableSkills, normalizePathForValidation, safeRead,
|
|
7
|
+
import { ACE_ROOT_REL, ACE_TASKS_ROOT_REL, ALL_MCP_CLIENTS, ALL_LLM_PROVIDERS, ALL_AGENTS, COMPOSABLE_AGENTS, SWARM_AGENTS, SWARM_SUBAGENT_MAP, WORKSPACE_ROOT, classifyPathSource, detectAssetDrift, getAllMcpServerConfigSnippets, getAgentInstructionPath, getAgentManifestPath, getKernelArtifactPath, getMcpClientInstallHint, getMcpServerConfigSnippet, getTaskArtifactPath, isSwarmRole, listAvailableSkills, normalizePathForValidation, resolveWorkspaceRoot, safeRead, safeWriteAsync, withFileLock, wsPath, } from "./helpers.js";
|
|
8
8
|
import { getRoleTitle, MCP_CLIENT_ENUM, scoreDomains, } from "./shared.js";
|
|
9
9
|
import { defaultModelForProvider, } from "./tui/provider-discovery.js";
|
|
10
10
|
import { refreshAstgrepIndex } from "./astgrep-index.js";
|
|
@@ -20,6 +20,21 @@ import { auditPublicSurface } from "./public-surface.js";
|
|
|
20
20
|
import { auditStoreAuthority, writeStoreAuthorityAuditReport, } from "./store/store-authority-audit.js";
|
|
21
21
|
import { PROVENANCE_CRITICAL_EVENT_TYPES, validateArtifactManifestPayload, validateProvenanceLogContent, validateTealConfigContent, } from "./schemas.js";
|
|
22
22
|
import { readAceTaskContractAssessment } from "./ace-autonomy.js";
|
|
23
|
+
import { listStoreKeysSync, readStoreBlobSync } from "./store/store-snapshot.js";
|
|
24
|
+
function executionRoleForDomain(domain) {
|
|
25
|
+
switch (domain) {
|
|
26
|
+
case "venture":
|
|
27
|
+
return "vos";
|
|
28
|
+
case "ux":
|
|
29
|
+
return "ui";
|
|
30
|
+
case "engineering":
|
|
31
|
+
return "coders";
|
|
32
|
+
case "research":
|
|
33
|
+
return "research";
|
|
34
|
+
default:
|
|
35
|
+
return "orchestrator";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
23
38
|
function getArtifactManifestEntries(payload) {
|
|
24
39
|
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
25
40
|
return [];
|
|
@@ -38,15 +53,37 @@ function getArtifactManifestEntries(payload) {
|
|
|
38
53
|
}
|
|
39
54
|
return [];
|
|
40
55
|
}
|
|
56
|
+
function parseGateManifest(raw, sourceRef) {
|
|
57
|
+
try {
|
|
58
|
+
const gate = JSON.parse(raw);
|
|
59
|
+
const id = typeof gate.id === "string" ? gate.id.trim() : "";
|
|
60
|
+
if (!id)
|
|
61
|
+
return undefined;
|
|
62
|
+
const type = gate.type === "executable" || gate.type === "artifact_scan" || gate.type === "manual_review"
|
|
63
|
+
? gate.type
|
|
64
|
+
: "manual_review";
|
|
65
|
+
return {
|
|
66
|
+
id,
|
|
67
|
+
type,
|
|
68
|
+
invariant: typeof gate.invariant === "string" ? gate.invariant : "",
|
|
69
|
+
command: typeof gate.command === "string" ? gate.command : "",
|
|
70
|
+
evidence_requirement: typeof gate.evidence_requirement === "string" ? gate.evidence_requirement : "",
|
|
71
|
+
source_ref: sourceRef,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
41
78
|
function readGateManifests(gatesDir) {
|
|
42
79
|
const files = readdirSync(gatesDir).filter((f) => f.endsWith(".json"));
|
|
43
80
|
const allGates = [];
|
|
44
81
|
for (const file of files) {
|
|
45
82
|
try {
|
|
46
83
|
const raw = readFileSync(resolve(gatesDir, file), "utf-8");
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
allGates.push(
|
|
84
|
+
const parsed = parseGateManifest(raw, `agent-state/MODULES/gates/${file}`);
|
|
85
|
+
if (parsed)
|
|
86
|
+
allGates.push(parsed);
|
|
50
87
|
}
|
|
51
88
|
catch {
|
|
52
89
|
/* skip corrupt manifests */
|
|
@@ -54,6 +91,44 @@ function readGateManifests(gatesDir) {
|
|
|
54
91
|
}
|
|
55
92
|
return allGates;
|
|
56
93
|
}
|
|
94
|
+
function readStoreGateManifests(workspaceRoot) {
|
|
95
|
+
const gateKeys = listStoreKeysSync(workspaceRoot, "knowledge/gates/").filter((key) => key.endsWith(".json"));
|
|
96
|
+
const allGates = [];
|
|
97
|
+
for (const key of gateKeys) {
|
|
98
|
+
const raw = readStoreBlobSync(workspaceRoot, key);
|
|
99
|
+
if (typeof raw !== "string")
|
|
100
|
+
continue;
|
|
101
|
+
const parsed = parseGateManifest(raw, key);
|
|
102
|
+
if (parsed)
|
|
103
|
+
allGates.push(parsed);
|
|
104
|
+
}
|
|
105
|
+
return allGates;
|
|
106
|
+
}
|
|
107
|
+
function resolveGateManifests(gatesDir) {
|
|
108
|
+
if (existsSync(gatesDir)) {
|
|
109
|
+
const workspaceGates = readGateManifests(gatesDir);
|
|
110
|
+
if (workspaceGates.length > 0) {
|
|
111
|
+
return { gates: workspaceGates, source: "workspace" };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const storeGates = readStoreGateManifests(resolveWorkspaceRoot());
|
|
115
|
+
if (storeGates.length > 0) {
|
|
116
|
+
return { gates: storeGates, source: "store" };
|
|
117
|
+
}
|
|
118
|
+
return { gates: [], source: "none" };
|
|
119
|
+
}
|
|
120
|
+
function hasArtifactEvidence(reference) {
|
|
121
|
+
const normalized = reference.trim();
|
|
122
|
+
if (!normalized)
|
|
123
|
+
return false;
|
|
124
|
+
const candidates = [wsPath("agent-state", normalized), wsPath(normalized)];
|
|
125
|
+
if (candidates.some((candidate) => existsSync(candidate)))
|
|
126
|
+
return true;
|
|
127
|
+
return candidates.some((candidate) => {
|
|
128
|
+
const content = safeRead(candidate);
|
|
129
|
+
return !content.startsWith("[FILE NOT FOUND]") && !content.startsWith("[ACCESS DENIED]");
|
|
130
|
+
});
|
|
131
|
+
}
|
|
57
132
|
function evaluateGateTargets(targets) {
|
|
58
133
|
const results = [];
|
|
59
134
|
for (const gate of targets) {
|
|
@@ -97,7 +172,7 @@ function evaluateGateTargets(targets) {
|
|
|
97
172
|
}
|
|
98
173
|
else {
|
|
99
174
|
for (const artifact of relevantArtifacts) {
|
|
100
|
-
if (!
|
|
175
|
+
if (!hasArtifactEvidence(artifact)) {
|
|
101
176
|
missing.push(artifact);
|
|
102
177
|
}
|
|
103
178
|
}
|
|
@@ -109,11 +184,9 @@ function evaluateGateTargets(targets) {
|
|
|
109
184
|
.map((s) => s.trim())
|
|
110
185
|
.filter(Boolean);
|
|
111
186
|
for (const ref of evidenceFiles) {
|
|
112
|
-
|
|
113
|
-
const found = candidates.some((candidate) => existsSync(candidate));
|
|
114
|
-
if (!found) {
|
|
187
|
+
if (!hasArtifactEvidence(ref)) {
|
|
115
188
|
const asFile = ref.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_./-]/g, "");
|
|
116
|
-
const fileFound =
|
|
189
|
+
const fileFound = hasArtifactEvidence(asFile);
|
|
117
190
|
if (!fileFound)
|
|
118
191
|
missing.push(ref);
|
|
119
192
|
}
|
|
@@ -228,7 +301,7 @@ async function appendEvidenceLogEntrySafe(review) {
|
|
|
228
301
|
const timestamp = new Date().toISOString();
|
|
229
302
|
const anchor = `#ts:${timestamp}`;
|
|
230
303
|
const evidenceRef = `agent-state/EVIDENCE_LOG.md${anchor}`;
|
|
231
|
-
return withFileLock("agent-state/EVIDENCE_LOG.md", () => {
|
|
304
|
+
return withFileLock("agent-state/EVIDENCE_LOG.md", async () => {
|
|
232
305
|
const existing = safeRead("agent-state/EVIDENCE_LOG.md");
|
|
233
306
|
const seed = existing.startsWith("[FILE NOT FOUND]") || existing.startsWith("[ACCESS DENIED]")
|
|
234
307
|
? "# EVIDENCE LOG\n\nAppend-only validation evidence.\n\n## Entries\n"
|
|
@@ -260,7 +333,7 @@ async function appendEvidenceLogEntrySafe(review) {
|
|
|
260
333
|
})
|
|
261
334
|
: [" - none"]),
|
|
262
335
|
];
|
|
263
|
-
const path =
|
|
336
|
+
const path = await safeWriteAsync("agent-state/EVIDENCE_LOG.md", `${seed}${lines.join("\n")}\n`);
|
|
264
337
|
return { path, evidenceRef };
|
|
265
338
|
});
|
|
266
339
|
}
|
|
@@ -475,6 +548,12 @@ export function registerFrameworkTools(server) {
|
|
|
475
548
|
recommendedSubagents.add("memory");
|
|
476
549
|
}
|
|
477
550
|
return {
|
|
551
|
+
structuredContent: {
|
|
552
|
+
detected_domain: detected,
|
|
553
|
+
suggested_execution_role: executionRoleForDomain(detected),
|
|
554
|
+
default_entry_agent: "orchestrator",
|
|
555
|
+
task_contract_ok: taskContract.ok,
|
|
556
|
+
},
|
|
478
557
|
content: [
|
|
479
558
|
{
|
|
480
559
|
type: "text",
|
|
@@ -576,7 +655,7 @@ export function registerFrameworkTools(server) {
|
|
|
576
655
|
model: resolvedLlmModel ?? undefined,
|
|
577
656
|
baseUrl: resolvedLlmBaseUrl ?? undefined,
|
|
578
657
|
});
|
|
579
|
-
const astIndex = refreshAstgrepIndex({
|
|
658
|
+
const astIndex = await refreshAstgrepIndex({
|
|
580
659
|
scope: ".",
|
|
581
660
|
append_evidence: true,
|
|
582
661
|
emit_event: true,
|
|
@@ -603,7 +682,7 @@ export function registerFrameworkTools(server) {
|
|
|
603
682
|
category: "major_update",
|
|
604
683
|
message: "Bootstrapped ACE workspace state",
|
|
605
684
|
artifacts: [
|
|
606
|
-
|
|
685
|
+
storeResult.storePath,
|
|
607
686
|
`${ACE_TASKS_ROOT_REL}/todo.md`,
|
|
608
687
|
normalizePathForValidation(delta.index_path),
|
|
609
688
|
"agent-state/AST_GREP_INDEX.json",
|
|
@@ -1349,31 +1428,27 @@ export function registerFrameworkTools(server) {
|
|
|
1349
1428
|
.describe("Optional short focus string persisted with skeptic adversarial review evidence."),
|
|
1350
1429
|
}, async ({ gate_ids, review_mode, review_focus }) => {
|
|
1351
1430
|
const gatesDir = wsPath("agent-state", "MODULES", "gates");
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
{
|
|
1356
|
-
type: "text",
|
|
1357
|
-
text: "❌ Gates directory not found: agent-state/MODULES/gates/",
|
|
1358
|
-
},
|
|
1359
|
-
],
|
|
1360
|
-
};
|
|
1361
|
-
}
|
|
1362
|
-
const allGates = readGateManifests(gatesDir);
|
|
1431
|
+
const resolved = resolveGateManifests(gatesDir);
|
|
1432
|
+
const allGates = resolved.gates;
|
|
1433
|
+
const gateEvidenceRef = resolved.source === "store" ? "knowledge/gates/*" : "agent-state/MODULES/gates/";
|
|
1363
1434
|
// Filter to requested gates (or run all)
|
|
1364
1435
|
const targets = gate_ids
|
|
1365
1436
|
? allGates.filter((g) => gate_ids.includes(g.id))
|
|
1366
1437
|
: allGates;
|
|
1367
1438
|
if (targets.length === 0) {
|
|
1439
|
+
const noGateMessage = allGates.length === 0
|
|
1440
|
+
? "❌ No gate manifests found in agent-state/MODULES/gates/ or ace-state.ace knowledge/gates/*."
|
|
1441
|
+
: `❌ No matching gates found. Available: ${allGates.map((g) => g.id).join(", ")}`;
|
|
1368
1442
|
return {
|
|
1369
1443
|
content: [
|
|
1370
1444
|
{
|
|
1371
1445
|
type: "text",
|
|
1372
|
-
text:
|
|
1446
|
+
text: noGateMessage,
|
|
1373
1447
|
},
|
|
1374
1448
|
],
|
|
1375
1449
|
};
|
|
1376
1450
|
}
|
|
1451
|
+
const gateArtifactRefs = targets.map((gate) => gate.source_ref ?? gateEvidenceRef);
|
|
1377
1452
|
const results = evaluateGateTargets(targets);
|
|
1378
1453
|
const passed = results.filter((r) => r.ok).length;
|
|
1379
1454
|
const failed = results.filter((r) => !r.ok).length;
|
|
@@ -1401,7 +1476,8 @@ export function registerFrameworkTools(server) {
|
|
|
1401
1476
|
})),
|
|
1402
1477
|
passed,
|
|
1403
1478
|
failed,
|
|
1404
|
-
evidence_ref: evidence?.evidenceRef ??
|
|
1479
|
+
evidence_ref: evidence?.evidenceRef ?? gateEvidenceRef,
|
|
1480
|
+
gate_manifest_source: resolved.source,
|
|
1405
1481
|
...(review
|
|
1406
1482
|
? {
|
|
1407
1483
|
review: {
|
|
@@ -1428,13 +1504,14 @@ export function registerFrameworkTools(server) {
|
|
|
1428
1504
|
? `Gate execution with skeptic review: ${review.summary.confirmed_count} confirmed findings`
|
|
1429
1505
|
: `Gate execution: ${passed}/${results.length} passed`,
|
|
1430
1506
|
artifacts: [
|
|
1431
|
-
...
|
|
1507
|
+
...gateArtifactRefs,
|
|
1432
1508
|
...(evidence ? ["agent-state/EVIDENCE_LOG.md"] : []),
|
|
1433
1509
|
],
|
|
1434
1510
|
metadata: {
|
|
1435
1511
|
passed,
|
|
1436
1512
|
failed,
|
|
1437
1513
|
gate_ids: results.map((r) => r.id),
|
|
1514
|
+
gate_manifest_source: resolved.source,
|
|
1438
1515
|
...(review
|
|
1439
1516
|
? {
|
|
1440
1517
|
review_mode: review.mode,
|
|
@@ -1460,6 +1537,9 @@ export function registerFrameworkTools(server) {
|
|
|
1460
1537
|
: allOk
|
|
1461
1538
|
? `✅ All gates passed: ${passed}/${results.length}`
|
|
1462
1539
|
: `❌ Gate failures: ${failed}/${results.length} failed`,
|
|
1540
|
+
`Gate manifest source: ${resolved.source === "store"
|
|
1541
|
+
? "ace-state.ace (knowledge/gates/*)"
|
|
1542
|
+
: "agent-state/MODULES/gates/"}`,
|
|
1463
1543
|
`Run ledger: ${ledger.path} (${ledger.entry.id})`,
|
|
1464
1544
|
...(evidence ? [`Evidence: ${evidence.path} (${evidence.evidenceRef})`] : []),
|
|
1465
1545
|
"",
|
package/dist/tools-handoff.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { ACE_TASKS_ROOT_REL, normalizePathForValidation, readTaskArtifact,
|
|
6
|
+
import { ACE_TASKS_ROOT_REL, normalizePathForValidation, readTaskArtifact, safeWriteAsync, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
|
|
7
7
|
import { ROLE_ENUM, HANDOFF_VALIDATION_MODE, HANDOFF_LIFECYCLE_STATUS_ENUM, } from "./shared.js";
|
|
8
8
|
import { validateHandoffPayload, validateSwarmHandoffPayload, lintHandoffPayload, } from "./schemas.js";
|
|
9
9
|
import { acknowledgeHandoffSafe, getHandoffRegistryPath, readHandoffRegistry, registerHandoffSafe, } from "./handoff-registry.js";
|
|
@@ -247,7 +247,7 @@ export function registerHandoffTools(server) {
|
|
|
247
247
|
.replace("T", "-")
|
|
248
248
|
.slice(0, 15);
|
|
249
249
|
const filePath = wsPath("tasks", `SWARM_HANDOFF.${from}_to_${to}_${timeStr}_${shortId}.json`);
|
|
250
|
-
|
|
250
|
+
await safeWriteAsync(filePath, JSON.stringify(handoff, null, 2));
|
|
251
251
|
const registry = await registerHandoffSafe({
|
|
252
252
|
handoff_id: handoff.handoff_id,
|
|
253
253
|
from: handoff.router.from,
|
package/dist/tools-lifecycle.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Lifecycle tool registrations: status events, circuit breaker, and kanban.
|
|
3
3
|
*/
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { safeRead,
|
|
6
|
-
import { appendStatusEventSafe, getStatusEventsPath, readStatusEvents
|
|
5
|
+
import { safeRead, safeWriteAsync } from "./helpers.js";
|
|
6
|
+
import { appendStatusEventSafe, getStatusEventsPath, readStatusEvents } from "./status-events.js";
|
|
7
7
|
import { refreshKanbanArtifacts } from "./kanban.js";
|
|
8
8
|
export function registerLifecycleTools(server) {
|
|
9
9
|
server.tool("emit_status_event", "Append a schema-valid lifecycle event into agent-state/STATUS_EVENTS.ndjson", {
|
|
@@ -119,7 +119,7 @@ export function registerLifecycleTools(server) {
|
|
|
119
119
|
? "# STATUS\n\n"
|
|
120
120
|
: current.trimEnd();
|
|
121
121
|
const next = `${seed}\n- [${timestamp}] CIRCUIT_BREAKER_OPENED: ${reason}${owner ? ` (owner: ${owner})` : ""}\n`;
|
|
122
|
-
const statusPath =
|
|
122
|
+
const statusPath = await safeWriteAsync("agent-state/STATUS.md", next);
|
|
123
123
|
const event = await appendStatusEventSafe({
|
|
124
124
|
source_module: "capability-ops",
|
|
125
125
|
event_type: "CIRCUIT_BREAKER_OPENED",
|
|
@@ -158,7 +158,7 @@ export function registerLifecycleTools(server) {
|
|
|
158
158
|
? "# STATUS\n\n"
|
|
159
159
|
: current.trimEnd();
|
|
160
160
|
const next = `${seed}\n- [${timestamp}] CIRCUIT_BREAKER_CLOSED: ${reason}\n`;
|
|
161
|
-
const statusPath =
|
|
161
|
+
const statusPath = await safeWriteAsync("agent-state/STATUS.md", next);
|
|
162
162
|
const event = await appendStatusEventSafe({
|
|
163
163
|
source_module: "capability-ops",
|
|
164
164
|
event_type: "CIRCUIT_BREAKER_CLOSED",
|