@voybio/ace-swarm 2.4.2 → 2.4.4
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 +9 -0
- 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/dist/astgrep-index.d.ts +7 -1
- package/dist/astgrep-index.js +66 -39
- package/dist/cli.js +1 -1
- package/dist/gate-contract.d.ts +63 -0
- package/dist/gate-contract.js +178 -0
- package/dist/helpers/bootstrap.js +4 -4
- 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/schemas.js +16 -15
- package/dist/store/bootstrap-store.js +30 -6
- package/dist/store/gate-contract-migration.d.ts +10 -0
- package/dist/store/gate-contract-migration.js +413 -0
- package/dist/store/materializers/host-file-materializer.js +9 -1
- package/dist/tools-files.js +68 -5
- package/dist/tools-framework.js +115 -37
- package/package.json +3 -1
- 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/runtime-executor.js
CHANGED
|
@@ -20,7 +20,7 @@ import { getWorkspaceStorePath, storeExistsSync, } from "./store/store-snapshot.
|
|
|
20
20
|
import { operationalArtifactVirtualPath } from "./store/store-artifacts.js";
|
|
21
21
|
import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
|
|
22
22
|
import { LocalModelRuntimeRepository, withLocalModelRuntimeRepository, } from "./store/repositories/local-model-runtime-repository.js";
|
|
23
|
-
import { proposePlanDeterministic, validatePlan } from "./plan-proposal.js";
|
|
23
|
+
import { loadAcceptanceTraceContract, proposePlanDeterministic, validatePlan } from "./plan-proposal.js";
|
|
24
24
|
export const RUNTIME_EXECUTOR_SESSION_REGISTRY_REL_PATH = "agent-state/runtime-executor-sessions.json";
|
|
25
25
|
export const RUNTIME_EXECUTOR_SESSION_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json";
|
|
26
26
|
export const RUNTIME_EXECUTOR_SESSION_SCHEMA_NAME = "runtime-executor-session-registry@1.0.0";
|
|
@@ -583,6 +583,7 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
|
|
|
583
583
|
}
|
|
584
584
|
return;
|
|
585
585
|
}
|
|
586
|
+
let mutationLanePolicy = { mutation_lane: "raw_write_allowed" };
|
|
586
587
|
// ── Plan validation preflight (Task 6) ───────────────────────────────
|
|
587
588
|
{
|
|
588
589
|
const currentValidatedPlanId = registryStart.validated_plan_id;
|
|
@@ -592,6 +593,13 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
|
|
|
592
593
|
const proposal = proposePlanDeterministic(task);
|
|
593
594
|
const verdict = await validatePlan({ proposal, sessionId });
|
|
594
595
|
if (verdict.ok) {
|
|
596
|
+
const structuralStep = proposal.steps.find((step) => step.structural_edit_plan_required === true);
|
|
597
|
+
if (structuralStep) {
|
|
598
|
+
mutationLanePolicy = {
|
|
599
|
+
mutation_lane: "structural_edit",
|
|
600
|
+
waiver: structuralStep.structural_edit_waiver ?? null,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
595
603
|
// Persist validated_plan_id opportunistically. This is a readiness hint,
|
|
596
604
|
// not a hard gate on unattended execution.
|
|
597
605
|
await mutateRegistry((registry) => {
|
|
@@ -606,6 +614,16 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
|
|
|
606
614
|
// even when the planner/model path or trace lookup is unavailable.
|
|
607
615
|
}
|
|
608
616
|
}
|
|
617
|
+
else {
|
|
618
|
+
const contract = loadAcceptanceTraceContract(currentValidatedPlanId);
|
|
619
|
+
const structuralStep = contract?.steps.find((step) => step.structural_edit_plan_required === true);
|
|
620
|
+
if (structuralStep) {
|
|
621
|
+
mutationLanePolicy = {
|
|
622
|
+
mutation_lane: "structural_edit",
|
|
623
|
+
waiver: structuralStep.structural_edit_waiver ?? null,
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
}
|
|
609
627
|
}
|
|
610
628
|
// ── End plan validation preflight ────────────────────────────────────
|
|
611
629
|
setSessionPhase(sessionId, "workspace_before_run");
|
|
@@ -706,7 +724,7 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
|
|
|
706
724
|
const turnStartedAt = new Date().toISOString();
|
|
707
725
|
const paths = buildTurnPaths(sessionId, sessionSnapshot.workspace_path, turnNumber);
|
|
708
726
|
// Build capability-filtered tool catalog in-memory from the resolved model class.
|
|
709
|
-
const filteredCatalog = buildFilteredToolCatalog(effectiveModelClass);
|
|
727
|
+
const filteredCatalog = buildFilteredToolCatalog(effectiveModelClass, undefined, mutationLanePolicy);
|
|
710
728
|
const capabilitySnapshotId = randomUUID();
|
|
711
729
|
const selectedBudget = resolveEffectiveSurgicalReadBudget(runtimeProfileSnapshot.profile, effectiveModelClass);
|
|
712
730
|
const toolCatalog = filteredCatalog.entries.map((e) => ({
|
|
@@ -1078,6 +1096,31 @@ async function runSessionLoop(sessionId, context, autoCleanup, runtimeProfileSna
|
|
|
1078
1096
|
for (const toolCall of response.tool_calls) {
|
|
1079
1097
|
const toolStartedAt = new Date().toISOString();
|
|
1080
1098
|
noteProgress(`Runtime tool ${toolCall.name} started during turn ${turnNumber}.`);
|
|
1099
|
+
const fencedReason = filteredCatalog.unavailable_tools.find((entry) => entry.name === toolCall.name && entry.reason_code === "structural_lane_fenced");
|
|
1100
|
+
if (fencedReason) {
|
|
1101
|
+
const toolResult = {
|
|
1102
|
+
ok: false,
|
|
1103
|
+
name: toolCall.name,
|
|
1104
|
+
summary: fencedReason.reason,
|
|
1105
|
+
request_path: "policy://structural-lane-fenced/request",
|
|
1106
|
+
response_path: "policy://structural-lane-fenced/response",
|
|
1107
|
+
error: { reason_code: "structural_lane_fenced", summary: fencedReason.reason },
|
|
1108
|
+
};
|
|
1109
|
+
await appendStatusEventSafe({
|
|
1110
|
+
source_module: "capability-framework",
|
|
1111
|
+
event_type: "STRUCTURAL_LANE_FENCED",
|
|
1112
|
+
status: "blocked",
|
|
1113
|
+
summary: fencedReason.reason,
|
|
1114
|
+
payload: {
|
|
1115
|
+
session_id: sessionId,
|
|
1116
|
+
turn_number: turnNumber,
|
|
1117
|
+
tool_name: toolCall.name,
|
|
1118
|
+
reason_code: "structural_lane_fenced",
|
|
1119
|
+
},
|
|
1120
|
+
}).catch(() => undefined);
|
|
1121
|
+
toolCalls.push(mapToolCallResult(toolResult, toolStartedAt));
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1081
1124
|
const toolResult = await executeRuntimeTool(toolCall.name, toolCall.input, {
|
|
1082
1125
|
session_id: sessionId,
|
|
1083
1126
|
workspace_path: sessionSnapshot.workspace_path,
|
|
@@ -102,6 +102,13 @@ export interface FilteredToolCatalog {
|
|
|
102
102
|
}[];
|
|
103
103
|
tool_cost_class: Record<string, ToolCostClass>;
|
|
104
104
|
}
|
|
105
|
-
export
|
|
105
|
+
export interface MutationLanePolicy {
|
|
106
|
+
mutation_lane: "structural_edit" | "raw_write_allowed";
|
|
107
|
+
waiver?: {
|
|
108
|
+
reason: string;
|
|
109
|
+
evidence_ref: string;
|
|
110
|
+
} | null;
|
|
111
|
+
}
|
|
112
|
+
export declare function buildFilteredToolCatalog(modelClass: ModelClass | undefined, allowedTools?: string[], lanePolicy?: MutationLanePolicy): FilteredToolCatalog;
|
|
106
113
|
export {};
|
|
107
114
|
//# sourceMappingURL=runtime-tool-specs.d.ts.map
|
|
@@ -594,7 +594,12 @@ function extractCostClass(tool) {
|
|
|
594
594
|
return "moderate";
|
|
595
595
|
return "cheap";
|
|
596
596
|
}
|
|
597
|
-
|
|
597
|
+
const STRUCTURAL_LANE_FENCED = new Set([
|
|
598
|
+
"write_workspace_file",
|
|
599
|
+
"safe_edit_file",
|
|
600
|
+
"apply_patch",
|
|
601
|
+
]);
|
|
602
|
+
export function buildFilteredToolCatalog(modelClass, allowedTools, lanePolicy) {
|
|
598
603
|
const allTools = listRuntimeToolSpecs();
|
|
599
604
|
const effectiveModelClass = modelClass ?? "frontier";
|
|
600
605
|
const allowed = allowedTools ? new Set(allowedTools) : null;
|
|
@@ -621,6 +626,19 @@ export function buildFilteredToolCatalog(modelClass, allowedTools) {
|
|
|
621
626
|
cost_class: costClass,
|
|
622
627
|
});
|
|
623
628
|
}
|
|
629
|
+
if (lanePolicy?.mutation_lane === "structural_edit"
|
|
630
|
+
&& !(lanePolicy.waiver?.reason?.trim() && lanePolicy.waiver?.evidence_ref?.trim())) {
|
|
631
|
+
entries = entries.filter((entry) => {
|
|
632
|
+
if (!STRUCTURAL_LANE_FENCED.has(entry.name))
|
|
633
|
+
return true;
|
|
634
|
+
unavailable.push({
|
|
635
|
+
name: entry.name,
|
|
636
|
+
reason: "Disabled by structural_edit lane. Use astgrep_locate -> compile_structural_edit -> preview_structural_edit -> astgrep_rewrite, or attach an evidence-backed structural_edit_waiver to the step.",
|
|
637
|
+
reason_code: "structural_lane_fenced",
|
|
638
|
+
});
|
|
639
|
+
return false;
|
|
640
|
+
});
|
|
641
|
+
}
|
|
624
642
|
if (effectiveModelClass === "small_local" || effectiveModelClass === "mid") {
|
|
625
643
|
const priority = new Map(SMALL_LOCAL_PRIORITY_TOOL_NAMES.map((name, idx) => [name, idx]));
|
|
626
644
|
entries.sort((a, b) => {
|
package/dist/schemas.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { extractFencedYamlBlock, parseTealConfigMarkdown } from "./gate-contract.js";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { isAbsolute, resolve } from "node:path";
|
|
4
5
|
import { isInside, mapAceWorkspaceRelativePath } from "./helpers.js";
|
|
@@ -616,26 +617,26 @@ export function validateStatusEventsNdjsonContent(content) {
|
|
|
616
617
|
}
|
|
617
618
|
export function validateTealConfigContent(content) {
|
|
618
619
|
const errors = [];
|
|
619
|
-
const
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
errors.push(`TEAL_CONFIG.md must contain exactly one fenced code block; found ${fencedBlocks.length}`);
|
|
620
|
+
const block = extractFencedYamlBlock(content);
|
|
621
|
+
if (!block) {
|
|
622
|
+
errors.push("TEAL_CONFIG.md must contain exactly one fenced code block; found 0 or multiple blocks");
|
|
623
623
|
}
|
|
624
|
-
const block = fencedBlocks[0];
|
|
625
624
|
if (block) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
if (language !== "yaml") {
|
|
629
|
-
errors.push(`TEAL_CONFIG.md active config block must be fenced as \`\`\`yaml, found "${block[1].trim() || "(none)"}"`);
|
|
625
|
+
if (block.language !== "yaml") {
|
|
626
|
+
errors.push(`TEAL_CONFIG.md active config block must be fenced as \`\`\`yaml, found "${block.language || "(none)"}"`);
|
|
630
627
|
}
|
|
631
|
-
if (!
|
|
628
|
+
if (!block.yaml) {
|
|
632
629
|
errors.push("TEAL_CONFIG.md YAML block is empty");
|
|
633
630
|
}
|
|
634
|
-
else {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
631
|
+
else if (!parseTealConfigMarkdown(content)) {
|
|
632
|
+
errors.push("TEAL_CONFIG.md YAML block could not be parsed");
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const parsed = parseTealConfigMarkdown(content);
|
|
636
|
+
if (parsed) {
|
|
637
|
+
for (const section of ["modules", "pipelines"]) {
|
|
638
|
+
if (!parsed[section] || typeof parsed[section] !== "object") {
|
|
639
|
+
errors.push(`TEAL_CONFIG.md YAML block is missing required top-level section "${section}:"`);
|
|
639
640
|
}
|
|
640
641
|
}
|
|
641
642
|
}
|
|
@@ -22,6 +22,7 @@ import { openStore } from "./ace-packed-store.js";
|
|
|
22
22
|
import { bakeAllCoreKnowledge } from "./knowledge-bake.js";
|
|
23
23
|
import { bakeTopology } from "./topology-bake.js";
|
|
24
24
|
import { buildCatalog } from "./catalog-builder.js";
|
|
25
|
+
import { migratePackageGateContract, recordPackageGateContractVersion, } from "./gate-contract-migration.js";
|
|
25
26
|
import { HostFileMaterializer } from "./materializers/host-file-materializer.js";
|
|
26
27
|
import { ProjectionManager } from "./materializers/projection-manager.js";
|
|
27
28
|
import { TodoSyncer } from "./materializers/todo-syncer.js";
|
|
@@ -104,6 +105,11 @@ async function materializeStoreSurfaces(store, workspaceRoot, opts) {
|
|
|
104
105
|
}
|
|
105
106
|
// Artifacts that receive live timestamp substitution on first seed.
|
|
106
107
|
const TIMESTAMPED_ARTIFACT_RELS = new Set(["agent-state/STATUS.md", "agent-state/EVIDENCE_LOG.md"]);
|
|
108
|
+
const GATE_CONTRACT_OPERATIONAL_ARTIFACT_RELS = [
|
|
109
|
+
"agent-state/TEAL_CONFIG.md",
|
|
110
|
+
"agent-state/QUALITY_GATES.md",
|
|
111
|
+
"agent-state/INTERFACE_REGISTRY.md",
|
|
112
|
+
];
|
|
107
113
|
async function seedOperationalArtifacts(store) {
|
|
108
114
|
const bootstrapTs = new Date().toISOString();
|
|
109
115
|
for (const relPath of OPERATIONAL_ARTIFACT_REL_PATHS) {
|
|
@@ -117,8 +123,23 @@ async function seedOperationalArtifacts(store) {
|
|
|
117
123
|
await store.setBlob(operationalArtifactKey(relPath), content);
|
|
118
124
|
}
|
|
119
125
|
}
|
|
126
|
+
async function syncOperationalArtifacts(store, relPaths) {
|
|
127
|
+
for (const relPath of relPaths) {
|
|
128
|
+
const knowledgeKey = `knowledge/agent-state/${relPath.replace(/^agent-state\//, "")}`;
|
|
129
|
+
const content = (await store.getBlob(knowledgeKey)) ?? "";
|
|
130
|
+
await store.setBlob(operationalArtifactKey(relPath), content);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async function migrateExistingStoreGateContract(store, workspaceRoot, assetsRoot, warnings) {
|
|
134
|
+
const migration = await migratePackageGateContract(store, workspaceRoot, assetsRoot);
|
|
135
|
+
warnings.push(...migration.messages);
|
|
136
|
+
if (migration.changed) {
|
|
137
|
+
await syncOperationalArtifacts(store, GATE_CONTRACT_OPERATIONAL_ARTIFACT_RELS);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
120
140
|
export async function bootstrapStoreWorkspace(opts) {
|
|
121
141
|
const { workspaceRoot, force = false } = opts;
|
|
142
|
+
const assetsRoot = getAssetsRoot();
|
|
122
143
|
const resolved = await ensureCanonicalWorkspaceStore(workspaceRoot, {
|
|
123
144
|
operationLabel: "bootstrapStoreWorkspace",
|
|
124
145
|
});
|
|
@@ -131,13 +152,14 @@ export async function bootstrapStoreWorkspace(opts) {
|
|
|
131
152
|
return { storePath, agents: 0, modules: { gates: 0, roles: 0, schemas: 0 }, materialized, warnings };
|
|
132
153
|
}
|
|
133
154
|
if (existsSync(storePath) && !force) {
|
|
134
|
-
if (!(opts.includeMcpConfig ?? false) && !(opts.includeClientConfigBundle ?? false) && !opts.llm) {
|
|
135
|
-
warnings.push(`Store already exists at ${storePath}. Use --force to reinitialize, or run 'ace migrate' to import existing state.`);
|
|
136
|
-
return { storePath, agents: 0, modules: { gates: 0, roles: 0, schemas: 0 }, materialized, warnings };
|
|
137
|
-
}
|
|
138
155
|
const existingStore = await openStore(storePath);
|
|
139
156
|
try {
|
|
140
|
-
await
|
|
157
|
+
await migrateExistingStoreGateContract(existingStore, workspaceRoot, assetsRoot, warnings);
|
|
158
|
+
if (!(opts.includeMcpConfig ?? false) && !(opts.includeClientConfigBundle ?? false) && !opts.llm) {
|
|
159
|
+
const agents = (await existingStore.listAgents()).length;
|
|
160
|
+
warnings.push(`Reused existing store at ${storePath}. Refreshed package gate contract state.`);
|
|
161
|
+
return { storePath, agents, modules: { gates: 0, roles: 0, schemas: 0 }, materialized, warnings };
|
|
162
|
+
}
|
|
141
163
|
materialized.push(...(await materializeStoreSurfaces(existingStore, workspaceRoot, opts)));
|
|
142
164
|
const agents = (await existingStore.listAgents()).length;
|
|
143
165
|
warnings.push(`Reused existing store at ${storePath} and refreshed host materializations.`);
|
|
@@ -163,8 +185,8 @@ export async function bootstrapStoreWorkspace(opts) {
|
|
|
163
185
|
include_mcp_config: opts.includeMcpConfig ?? false,
|
|
164
186
|
include_client_config_bundle: opts.includeClientConfigBundle ?? false,
|
|
165
187
|
});
|
|
166
|
-
const assetsRoot = getAssetsRoot();
|
|
167
188
|
const knowledge = await bakeAllCoreKnowledge(store, assetsRoot);
|
|
189
|
+
await recordPackageGateContractVersion(store);
|
|
168
190
|
await bakeTopology(store, assetsRoot);
|
|
169
191
|
await store.setJSON("state/handoffs/__index", []);
|
|
170
192
|
await store.setJSON("state/todo/index", []);
|
|
@@ -215,7 +237,9 @@ export async function repairWorkspace(workspaceRoot) {
|
|
|
215
237
|
const { storePath } = resolved;
|
|
216
238
|
const warnings = [...resolved.warnings];
|
|
217
239
|
const store = await openStore(storePath);
|
|
240
|
+
const assetsRoot = getAssetsRoot();
|
|
218
241
|
try {
|
|
242
|
+
await migrateExistingStoreGateContract(store, workspaceRoot, assetsRoot, warnings);
|
|
219
243
|
const hostPolicy = (await store.getJSON("meta/host_materialization")) ?? {};
|
|
220
244
|
// Re-materialize host files
|
|
221
245
|
const hostMat = new HostFileMaterializer(store, workspaceRoot);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IAcePackedStore } from "./types.js";
|
|
2
|
+
export declare const PACKAGE_GATE_CONTRACT_VERSION = 2;
|
|
3
|
+
export declare const PACKAGE_GATE_CONTRACT_VERSION_KEY = "meta/package_gate_contract_version";
|
|
4
|
+
export interface GateContractMigrationResult {
|
|
5
|
+
changed: boolean;
|
|
6
|
+
messages: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function recordPackageGateContractVersion(store: IAcePackedStore): Promise<void>;
|
|
9
|
+
export declare function migratePackageGateContract(store: IAcePackedStore, workspaceRoot: string, assetsRoot: string): Promise<GateContractMigrationResult>;
|
|
10
|
+
//# sourceMappingURL=gate-contract-migration.d.ts.map
|