@voybio/ace-swarm 0.2.5 → 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/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
- package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/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 +27 -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 -125
- package/dist/helpers.js +8 -1768
- 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.js +11 -5
- package/dist/store/materializers/context-snapshot-materializer.js +6 -2
- package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
- package/dist/store/materializers/hook-context-materializer.js +11 -21
- package/dist/store/materializers/host-file-materializer.js +6 -0
- package/dist/store/materializers/projection-manager.d.ts +0 -1
- package/dist/store/materializers/projection-manager.js +5 -13
- package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
- package/dist/store/materializers/vericify-projector.d.ts +7 -7
- package/dist/store/materializers/vericify-projector.js +11 -11
- package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
- package/dist/store/repositories/local-model-runtime-repository.js +242 -6
- package/dist/store/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 +268 -14
- 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 +25 -5
- 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, resolveWorkspaceRoot, 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";
|
|
@@ -21,6 +21,20 @@ import { auditStoreAuthority, writeStoreAuthorityAuditReport, } from "./store/st
|
|
|
21
21
|
import { PROVENANCE_CRITICAL_EVENT_TYPES, validateArtifactManifestPayload, validateProvenanceLogContent, validateTealConfigContent, } from "./schemas.js";
|
|
22
22
|
import { readAceTaskContractAssessment } from "./ace-autonomy.js";
|
|
23
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
|
+
}
|
|
24
38
|
function getArtifactManifestEntries(payload) {
|
|
25
39
|
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
26
40
|
return [];
|
|
@@ -287,7 +301,7 @@ async function appendEvidenceLogEntrySafe(review) {
|
|
|
287
301
|
const timestamp = new Date().toISOString();
|
|
288
302
|
const anchor = `#ts:${timestamp}`;
|
|
289
303
|
const evidenceRef = `agent-state/EVIDENCE_LOG.md${anchor}`;
|
|
290
|
-
return withFileLock("agent-state/EVIDENCE_LOG.md", () => {
|
|
304
|
+
return withFileLock("agent-state/EVIDENCE_LOG.md", async () => {
|
|
291
305
|
const existing = safeRead("agent-state/EVIDENCE_LOG.md");
|
|
292
306
|
const seed = existing.startsWith("[FILE NOT FOUND]") || existing.startsWith("[ACCESS DENIED]")
|
|
293
307
|
? "# EVIDENCE LOG\n\nAppend-only validation evidence.\n\n## Entries\n"
|
|
@@ -319,7 +333,7 @@ async function appendEvidenceLogEntrySafe(review) {
|
|
|
319
333
|
})
|
|
320
334
|
: [" - none"]),
|
|
321
335
|
];
|
|
322
|
-
const path =
|
|
336
|
+
const path = await safeWriteAsync("agent-state/EVIDENCE_LOG.md", `${seed}${lines.join("\n")}\n`);
|
|
323
337
|
return { path, evidenceRef };
|
|
324
338
|
});
|
|
325
339
|
}
|
|
@@ -534,6 +548,12 @@ export function registerFrameworkTools(server) {
|
|
|
534
548
|
recommendedSubagents.add("memory");
|
|
535
549
|
}
|
|
536
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
|
+
},
|
|
537
557
|
content: [
|
|
538
558
|
{
|
|
539
559
|
type: "text",
|
|
@@ -635,7 +655,7 @@ export function registerFrameworkTools(server) {
|
|
|
635
655
|
model: resolvedLlmModel ?? undefined,
|
|
636
656
|
baseUrl: resolvedLlmBaseUrl ?? undefined,
|
|
637
657
|
});
|
|
638
|
-
const astIndex = refreshAstgrepIndex({
|
|
658
|
+
const astIndex = await refreshAstgrepIndex({
|
|
639
659
|
scope: ".",
|
|
640
660
|
append_evidence: true,
|
|
641
661
|
emit_event: true,
|
|
@@ -662,7 +682,7 @@ export function registerFrameworkTools(server) {
|
|
|
662
682
|
category: "major_update",
|
|
663
683
|
message: "Bootstrapped ACE workspace state",
|
|
664
684
|
artifacts: [
|
|
665
|
-
|
|
685
|
+
storeResult.storePath,
|
|
666
686
|
`${ACE_TASKS_ROOT_REL}/todo.md`,
|
|
667
687
|
normalizePathForValidation(delta.index_path),
|
|
668
688
|
"agent-state/AST_GREP_INDEX.json",
|
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",
|
package/dist/tools-memory.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { resolveWorkspaceRoot, safeRead,
|
|
6
|
+
import { resolveWorkspaceRoot, safeRead, safeWriteAsync, wsPath } from "./helpers.js";
|
|
7
7
|
import { isReadError } from "./shared.js";
|
|
8
8
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
9
9
|
import { appendStatusEventSafe } from "./status-events.js";
|
|
@@ -13,7 +13,7 @@ import { openStore } from "./store/ace-packed-store.js";
|
|
|
13
13
|
import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
14
14
|
import { ContextSnapshotRepository } from "./store/repositories/context-snapshot-repository.js";
|
|
15
15
|
import { getWorkspaceStorePath, storeExistsSync, toVirtualStorePath, } from "./store/store-snapshot.js";
|
|
16
|
-
import {
|
|
16
|
+
import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
|
|
17
17
|
const CONTEXT_STORE_REL = "agent-state/context-snapshots";
|
|
18
18
|
export function registerMemoryTools(server) {
|
|
19
19
|
server.tool("context_snapshot", "Capture a named context snapshot for later retrieval (decisions, progress, state)", {
|
|
@@ -41,7 +41,7 @@ export function registerMemoryTools(server) {
|
|
|
41
41
|
let path = wsPath(CONTEXT_STORE_REL, `${name}.json`);
|
|
42
42
|
if (storeExistsSync(root) && existsSync(getWorkspaceStorePath(root))) {
|
|
43
43
|
const storePath = getWorkspaceStorePath(root);
|
|
44
|
-
const saved = await
|
|
44
|
+
const saved = await withStoreWriteCoordinator(storePath, async () => {
|
|
45
45
|
const store = await openStore(storePath);
|
|
46
46
|
try {
|
|
47
47
|
const repo = new ContextSnapshotRepository(store);
|
|
@@ -62,7 +62,7 @@ export function registerMemoryTools(server) {
|
|
|
62
62
|
finally {
|
|
63
63
|
await store.close();
|
|
64
64
|
}
|
|
65
|
-
});
|
|
65
|
+
}, { operation_label: "contextSnapshotSave" });
|
|
66
66
|
path = toVirtualStorePath(storePath, `state/memory/context_snapshots/${saved.file}`);
|
|
67
67
|
}
|
|
68
68
|
else {
|
|
@@ -82,7 +82,7 @@ export function registerMemoryTools(server) {
|
|
|
82
82
|
.slice(0, 48);
|
|
83
83
|
const filename = `${slug}.json`;
|
|
84
84
|
path = wsPath(CONTEXT_STORE_REL, filename);
|
|
85
|
-
|
|
85
|
+
await safeWriteAsync(path, JSON.stringify(snapshot, null, 2));
|
|
86
86
|
const indexPath = wsPath(CONTEXT_STORE_REL, "index.json");
|
|
87
87
|
const indexRaw = safeRead(indexPath);
|
|
88
88
|
let index = { snapshots: [] };
|
|
@@ -97,7 +97,7 @@ export function registerMemoryTools(server) {
|
|
|
97
97
|
index.snapshots = index.snapshots.filter((s) => s.name !== name);
|
|
98
98
|
index.snapshots.push({ name, file: filename, timestamp, summary });
|
|
99
99
|
index.snapshots.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
100
|
-
|
|
100
|
+
await safeWriteAsync(indexPath, JSON.stringify(index, null, 2));
|
|
101
101
|
}
|
|
102
102
|
const ledger = await appendRunLedgerEntrySafe({
|
|
103
103
|
tool: "context_snapshot",
|
package/dist/tools-todo.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* TODO, task management, and run-ledger tool registrations.
|
|
3
3
|
*/
|
|
4
4
|
import { z } from "zod";
|
|
5
|
-
import { ACE_TASKS_ROOT_REL, readTaskArtifact, resolveWritableTaskPath,
|
|
5
|
+
import { ACE_TASKS_ROOT_REL, readTaskArtifact, resolveWritableTaskPath, safeWriteAsync, } from "./helpers.js";
|
|
6
6
|
import { TODO_STATUS_ENUM } from "./shared.js";
|
|
7
7
|
import { getTodoStatePath, readTodoState, syncTodoStateSafe, transitionTodoNodeSafe, } from "./todo-state.js";
|
|
8
8
|
import { appendRunLedgerEntrySafe, getRunLedgerPath, readRunLedger, } from "./run-ledger.js";
|
|
@@ -33,7 +33,7 @@ export function registerTodoTools(server) {
|
|
|
33
33
|
}, async ({ new_content, node_id, status, note }) => {
|
|
34
34
|
if (typeof new_content === "string") {
|
|
35
35
|
const synced = await syncTodoStateSafe(new_content);
|
|
36
|
-
const path =
|
|
36
|
+
const path = await safeWriteAsync(resolveWritableTaskPath("todo"), new_content);
|
|
37
37
|
const runLedger = await appendRunLedgerEntrySafe({
|
|
38
38
|
tool: "update_todo",
|
|
39
39
|
category: "major_update",
|
|
@@ -67,7 +67,7 @@ export declare function validateTrackerSnapshotContent(raw: string): ValidationR
|
|
|
67
67
|
export declare function loadTrackerSnapshot(explicitPath?: string): TrackerSnapshotResult;
|
|
68
68
|
export declare function readTrackerSnapshot(): TrackerSnapshot;
|
|
69
69
|
export declare function getTrackerSnapshotPath(): string;
|
|
70
|
-
export declare function writeTrackerSnapshot(snapshot: TrackerSnapshot): string
|
|
70
|
+
export declare function writeTrackerSnapshot(snapshot: TrackerSnapshot): Promise<string>;
|
|
71
71
|
export declare function listTrackerAdapterKinds(): string[];
|
|
72
72
|
export declare function getTrackerAdapter(kind?: string): IssueTrackerAdapter;
|
|
73
73
|
export declare function buildDefaultTrackerSnapshot(kind?: TrackerAdapterKind): TrackerSnapshot;
|