@voybio/ace-swarm 2.4.3 → 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/gate-contract.d.ts +63 -0
- package/dist/gate-contract.js +178 -0
- 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/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
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export const PACKAGE_GATE_CONTRACT_VERSION = 2;
|
|
4
|
+
export const PACKAGE_GATE_CONTRACT_VERSION_KEY = "meta/package_gate_contract_version";
|
|
5
|
+
const LEGACY_GATE_MIGRATIONS = [
|
|
6
|
+
{
|
|
7
|
+
fileName: "gate-autonomy.json",
|
|
8
|
+
legacyContent: JSON.stringify({
|
|
9
|
+
id: "gate-autonomy",
|
|
10
|
+
type: "artifact_scan",
|
|
11
|
+
invariant: "Every execution cycle records objective, strategy, evidence, and next state",
|
|
12
|
+
command: "",
|
|
13
|
+
evidence_requirement: "STATUS + EVIDENCE_LOG + HANDOFF pointers",
|
|
14
|
+
}, null, 2),
|
|
15
|
+
currentAssetRelativePath: "agent-state/MODULES/gates/gate-autonomy.json",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
fileName: "gate-completeness.json",
|
|
19
|
+
legacyContent: JSON.stringify({
|
|
20
|
+
id: "gate-completeness",
|
|
21
|
+
type: "artifact_scan",
|
|
22
|
+
invariant: "All required artifacts exist and are non-empty",
|
|
23
|
+
command: "",
|
|
24
|
+
evidence_requirement: "File presence and section completeness evidence",
|
|
25
|
+
}, null, 2),
|
|
26
|
+
currentAssetRelativePath: "agent-state/MODULES/gates/gate-completeness.json",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
fileName: "gate-operability.json",
|
|
30
|
+
legacyContent: JSON.stringify({
|
|
31
|
+
id: "gate-operability",
|
|
32
|
+
type: "artifact_scan",
|
|
33
|
+
invariant: "Runbooks, ownership, and incident routing are defined for release",
|
|
34
|
+
command: "",
|
|
35
|
+
evidence_requirement: "COMMUNICATION_CHANNELS.md + observability/release artifacts",
|
|
36
|
+
}, null, 2),
|
|
37
|
+
currentAssetRelativePath: "agent-state/MODULES/gates/gate-operability.json",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
fileName: "gate-security.json",
|
|
41
|
+
legacyContent: JSON.stringify({
|
|
42
|
+
id: "gate-security",
|
|
43
|
+
type: "manual_review",
|
|
44
|
+
invariant: "No unmitigated high-severity security or policy risks",
|
|
45
|
+
command: "",
|
|
46
|
+
evidence_requirement: "SECURITY_REPORT.md with explicit mitigations or accepted residual risk",
|
|
47
|
+
}, null, 2),
|
|
48
|
+
currentAssetRelativePath: "agent-state/MODULES/gates/gate-security.json",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
fileName: "gate-correctness.json",
|
|
52
|
+
legacyContent: JSON.stringify({
|
|
53
|
+
id: "gate-correctness",
|
|
54
|
+
type: "executable",
|
|
55
|
+
invariant: "All required tests pass",
|
|
56
|
+
command: "if [ -f ace-mcp-server/package.json ]; then cd ace-mcp-server && npm test --silent; elif [ -f package.json ]; then npm test --silent; else echo 'package.json not found for gate-correctness' && exit 1; fi",
|
|
57
|
+
evidence_requirement: "Command output snippet with exit code 0",
|
|
58
|
+
}, null, 2),
|
|
59
|
+
remove: true,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
fileName: "gate-evaluation.json",
|
|
63
|
+
legacyContent: JSON.stringify({
|
|
64
|
+
id: "gate-evaluation",
|
|
65
|
+
type: "executable",
|
|
66
|
+
invariant: "Core autonomy benchmark suites meet defined pass thresholds",
|
|
67
|
+
command: "bash scripts/ace/eval-harness.sh",
|
|
68
|
+
evidence_requirement: "EVAL_REPORT.md + command output",
|
|
69
|
+
}, null, 2),
|
|
70
|
+
remove: true,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
fileName: "gate-typescript-public-surface.json",
|
|
74
|
+
legacyContent: JSON.stringify({
|
|
75
|
+
id: "gate-typescript-public-surface",
|
|
76
|
+
type: "executable",
|
|
77
|
+
invariant: "Exported MCP tools, resources, prompts, and public status events stay registered, described, schema-backed where required, and covered by the audit gate",
|
|
78
|
+
command: "node --input-type=module -e \"import('./dist/public-surface.js').then(async (m) => { const result = await m.auditPublicSurface({ write_artifact: false }); if (!result.ok) { console.error(result.failures.join('\\n')); process.exit(1); } console.log(JSON.stringify(result.summary)); })\"",
|
|
79
|
+
evidence_requirement: "PUBLIC_SURFACE_REPORT.md + passing public-surface audit",
|
|
80
|
+
}, null, 2),
|
|
81
|
+
remove: true,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
const LEGACY_STATIC_ASSET_MIGRATIONS = [
|
|
85
|
+
{
|
|
86
|
+
key: "knowledge/agent-state/TEAL_CONFIG.md",
|
|
87
|
+
legacyContent: `# TEAL_CONFIG.md
|
|
88
|
+
|
|
89
|
+
\`\`\`yaml
|
|
90
|
+
teal_version: "1.1"
|
|
91
|
+
timestamp: "2026-03-03T20:45:00Z"
|
|
92
|
+
|
|
93
|
+
modules:
|
|
94
|
+
skeptic:
|
|
95
|
+
version: "1.0.0"
|
|
96
|
+
interface: "QUALITY_GATES.md"
|
|
97
|
+
sidecar: true
|
|
98
|
+
|
|
99
|
+
ops:
|
|
100
|
+
version: "1.0.0"
|
|
101
|
+
outputs: ["STATUS.md", "HANDOFF.json"]
|
|
102
|
+
|
|
103
|
+
astgrep:
|
|
104
|
+
version: "1.0.0"
|
|
105
|
+
inputs: ["TASK.md", "SCOPE.md", "AST_GREP_COMMANDS.md"]
|
|
106
|
+
outputs: ["AST_GREP_INDEX.md", "AST_GREP_INDEX.json"]
|
|
107
|
+
|
|
108
|
+
research:
|
|
109
|
+
version: "1.0.0"
|
|
110
|
+
inputs: ["TASK.md"]
|
|
111
|
+
outputs: ["EVIDENCE_LOG.md"]
|
|
112
|
+
|
|
113
|
+
spec:
|
|
114
|
+
version: "1.0.0"
|
|
115
|
+
depends_on: ["research", "skeptic"]
|
|
116
|
+
outputs: ["SPEC_CONTRACT.json", "INTERFACE_REGISTRY.md"]
|
|
117
|
+
|
|
118
|
+
build:
|
|
119
|
+
version: "1.0.0"
|
|
120
|
+
depends_on: ["spec"]
|
|
121
|
+
parallelizable: true
|
|
122
|
+
|
|
123
|
+
qa:
|
|
124
|
+
version: "1.0.0"
|
|
125
|
+
depends_on: ["build"]
|
|
126
|
+
gates: ["gate-completeness", "gate-correctness", "gate-autonomy"]
|
|
127
|
+
|
|
128
|
+
docs:
|
|
129
|
+
version: "1.0.0"
|
|
130
|
+
depends_on: ["build", "qa"]
|
|
131
|
+
|
|
132
|
+
memory:
|
|
133
|
+
version: "1.0.0"
|
|
134
|
+
sidecar: true
|
|
135
|
+
depends_on: ["ops"]
|
|
136
|
+
outputs: ["MEMORY_INDEX.md"]
|
|
137
|
+
|
|
138
|
+
security:
|
|
139
|
+
version: "1.0.0"
|
|
140
|
+
sidecar: true
|
|
141
|
+
depends_on: ["skeptic", "spec", "build"]
|
|
142
|
+
gates: ["gate-security"]
|
|
143
|
+
|
|
144
|
+
observability:
|
|
145
|
+
version: "1.0.0"
|
|
146
|
+
sidecar: true
|
|
147
|
+
depends_on: ["ops", "qa"]
|
|
148
|
+
outputs: ["OBSERVABILITY_REPORT.md"]
|
|
149
|
+
|
|
150
|
+
eval:
|
|
151
|
+
version: "1.0.0"
|
|
152
|
+
depends_on: ["qa"]
|
|
153
|
+
gates: ["gate-evaluation"]
|
|
154
|
+
|
|
155
|
+
release:
|
|
156
|
+
version: "1.0.0"
|
|
157
|
+
depends_on: ["security", "observability", "eval"]
|
|
158
|
+
gates: ["gate-operability"]
|
|
159
|
+
|
|
160
|
+
pipelines:
|
|
161
|
+
standard:
|
|
162
|
+
- astgrep -> research -> spec -> build -> qa -> docs
|
|
163
|
+
- skeptic (sidecar)
|
|
164
|
+
|
|
165
|
+
high_certainty:
|
|
166
|
+
- astgrep -> spec -> build -> qa
|
|
167
|
+
- security (sidecar)
|
|
168
|
+
- eval -> release
|
|
169
|
+
|
|
170
|
+
high_ambiguity:
|
|
171
|
+
- astgrep -> research -> spec -> build
|
|
172
|
+
- skeptic (continuous)
|
|
173
|
+
- memory (continuous)
|
|
174
|
+
- ops (orchestration)
|
|
175
|
+
|
|
176
|
+
production_promotion:
|
|
177
|
+
- qa -> security -> eval -> observability -> release
|
|
178
|
+
|
|
179
|
+
gates:
|
|
180
|
+
gate-completeness:
|
|
181
|
+
module: qa
|
|
182
|
+
inputs: ["SPEC_CONTRACT.json", "artifact_set"]
|
|
183
|
+
|
|
184
|
+
gate-correctness:
|
|
185
|
+
module: qa
|
|
186
|
+
inputs: ["test_results", "EVIDENCE_LOG.md"]
|
|
187
|
+
|
|
188
|
+
gate-autonomy:
|
|
189
|
+
module: skeptic
|
|
190
|
+
inputs: ["STATUS.md", "HANDOFF.json", "EVIDENCE_LOG.md"]
|
|
191
|
+
|
|
192
|
+
gate-security:
|
|
193
|
+
module: security
|
|
194
|
+
inputs: ["RISKS.md", "SECURITY_REPORT.md"]
|
|
195
|
+
|
|
196
|
+
gate-evaluation:
|
|
197
|
+
module: eval
|
|
198
|
+
inputs: ["EVAL_REPORT.md"]
|
|
199
|
+
|
|
200
|
+
gate-operability:
|
|
201
|
+
module: release
|
|
202
|
+
inputs: ["OBSERVABILITY_REPORT.md", "RELEASE_DECISION.md"]
|
|
203
|
+
\`\`\`
|
|
204
|
+
`,
|
|
205
|
+
currentAssetRelativePath: "agent-state/TEAL_CONFIG.md",
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
key: "knowledge/agent-state/MODULES/registry.json",
|
|
209
|
+
legacyContent: JSON.stringify({
|
|
210
|
+
roles: [
|
|
211
|
+
"capability-astgrep",
|
|
212
|
+
"capability-skeptic",
|
|
213
|
+
"capability-ops",
|
|
214
|
+
"capability-research",
|
|
215
|
+
"capability-spec",
|
|
216
|
+
"capability-build",
|
|
217
|
+
"capability-qa",
|
|
218
|
+
"capability-docs",
|
|
219
|
+
"capability-memory",
|
|
220
|
+
"capability-security",
|
|
221
|
+
"capability-observability",
|
|
222
|
+
"capability-eval",
|
|
223
|
+
"capability-release",
|
|
224
|
+
"capability-safety",
|
|
225
|
+
"capability-framework",
|
|
226
|
+
"capability-git",
|
|
227
|
+
],
|
|
228
|
+
gates: [
|
|
229
|
+
"gate-completeness",
|
|
230
|
+
"gate-correctness",
|
|
231
|
+
"gate-autonomy",
|
|
232
|
+
"gate-security",
|
|
233
|
+
"gate-operability",
|
|
234
|
+
"gate-evaluation",
|
|
235
|
+
"gate-typescript-public-surface",
|
|
236
|
+
],
|
|
237
|
+
schemas: [
|
|
238
|
+
"STATUS_EVENT.schema.json",
|
|
239
|
+
"HANDOFF.schema.json",
|
|
240
|
+
"ARTIFACT_MANIFEST.schema.json",
|
|
241
|
+
"ACE_RUNTIME_PROFILE.schema.json",
|
|
242
|
+
"WORKSPACE_SESSION_REGISTRY.schema.json",
|
|
243
|
+
"RUNTIME_TOOL_SPEC_REGISTRY.schema.json",
|
|
244
|
+
"RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json",
|
|
245
|
+
"TRACKER_SNAPSHOT.schema.json",
|
|
246
|
+
"VERICIFY_BRIDGE_SNAPSHOT.schema.json",
|
|
247
|
+
"VERICIFY_PROCESS_POST_LOG.schema.json",
|
|
248
|
+
],
|
|
249
|
+
}, null, 2),
|
|
250
|
+
currentAssetRelativePath: "agent-state/MODULES/registry.json",
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
key: "knowledge/agent-state/QUALITY_GATES.md",
|
|
254
|
+
legacyContent: `# QUALITY GATES
|
|
255
|
+
|
|
256
|
+
## gate-completeness
|
|
257
|
+
|
|
258
|
+
- Invariant: required artifacts exist and are non-empty.
|
|
259
|
+
- Pass condition: all required files are present with required sections.
|
|
260
|
+
- Fail condition: any missing required artifact or empty required section.
|
|
261
|
+
- Evidence: file list + section checks in \`EVIDENCE_LOG.md\`.
|
|
262
|
+
|
|
263
|
+
## gate-correctness
|
|
264
|
+
|
|
265
|
+
- Invariant: behavior satisfies spec acceptance criteria.
|
|
266
|
+
- Pass condition: mapped tests pass and no unresolved critical failures.
|
|
267
|
+
- Fail condition: failing mapped test, unresolved regression, or invalid provenance.
|
|
268
|
+
- Evidence: test output and verification report pointers.
|
|
269
|
+
`,
|
|
270
|
+
currentAssetRelativePath: "agent-state/QUALITY_GATES.md",
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
key: "knowledge/agent-state/INTERFACE_REGISTRY.md",
|
|
274
|
+
legacyContent: `# INTERFACE_REGISTRY
|
|
275
|
+
|
|
276
|
+
## Runtime Executor Session Contract
|
|
277
|
+
|
|
278
|
+
- \`runtime-executor-sessions.json\` is the authoritative session registry for unattended runtime executor workspaces.
|
|
279
|
+
- It must validate against \`MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json\`.
|
|
280
|
+
- Every session entry must expose \`session_id\`, \`workspace_path\`, \`root_path\`, \`status\`, \`source\`, timestamps, optional hook details, and any \`metadata\` required for routing/debugging.
|
|
281
|
+
|
|
282
|
+
## Runtime Tool Spec Contract
|
|
283
|
+
|
|
284
|
+
- \`runtime-tool-specs.json\` is the authoritative registry for external runtime tool declarations.
|
|
285
|
+
- It must validate against \`MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json\`.
|
|
286
|
+
- Each tool entry must include deterministic command metadata and explicitly declare environment inputs under \`env_keys\`.
|
|
287
|
+
|
|
288
|
+
## Workspace Session Registry Contract
|
|
289
|
+
|
|
290
|
+
- \`runtime-workspaces.json\` is the authoritative registry for active ACE workspace sessions.
|
|
291
|
+
- It must validate against \`MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json\`.
|
|
292
|
+
- Entries may optionally include hook health summaries and per-hook status under \`hooks\`.
|
|
293
|
+
|
|
294
|
+
## Tracker Snapshot Contract
|
|
295
|
+
|
|
296
|
+
- \`tracker-snapshot.json\` is the normalized cache of task/comment data mirrored from provider trackers.
|
|
297
|
+
- It must validate against \`MODULES/schemas/TRACKER_SNAPSHOT.schema.json\`.
|
|
298
|
+
- Scheduler and future executor consumers must use normalized item/comment fields only; provider-specific detail belongs under \`metadata\`.
|
|
299
|
+
|
|
300
|
+
## Vericify Sidecar Contract
|
|
301
|
+
|
|
302
|
+
- \`vericify/ace-bridge.json\` is an optional sidecar bridge snapshot for Vericify-style read-model consumers.
|
|
303
|
+
- It must validate against \`MODULES/schemas/VERICIFY_BRIDGE_SNAPSHOT.schema.json\`.
|
|
304
|
+
- \`vericify/process-posts.json\` is an optional structured process-post log and must validate against \`MODULES/schemas/VERICIFY_PROCESS_POST_LOG.schema.json\`.
|
|
305
|
+
- Vericify remains optional; ACE must not require the Vericify package to read or write these artifacts.
|
|
306
|
+
|
|
307
|
+
## Public Surface Gate
|
|
308
|
+
|
|
309
|
+
- \`audit_public_surface\` is the canonical TypeScript public-surface gate for MCP tools, resources, prompts, and registered event names.
|
|
310
|
+
- \`PUBLIC_SURFACE_REPORT.md\` is the durable audit artifact written by that gate.
|
|
311
|
+
- \`MODULES/gates/gate-typescript-public-surface.json\` is the executable gate manifest for CI or operator use.
|
|
312
|
+
|
|
313
|
+
## Provenance Contract
|
|
314
|
+
|
|
315
|
+
- \`ARTIFACT_MANIFEST.json\` is mandatory and must be schema-valid.
|
|
316
|
+
- Each artifact entry must include \`artifact_id\`, \`artifact_path\`, \`producer_module\`, \`spec_version\`, \`req_ids\`, \`checksum\`, \`confidence_level\`, \`evidence_ref\`, and \`timestamp\`.
|
|
317
|
+
- \`PROVENANCE_LOG.md\` must mirror manifest entries with append-only evidence-linked records.
|
|
318
|
+
|
|
319
|
+
## Versioning Rules
|
|
320
|
+
|
|
321
|
+
- additive changes: \`minor\`
|
|
322
|
+
- breaking changes: \`major\`
|
|
323
|
+
- bugfix/clarification: \`patch\`
|
|
324
|
+
|
|
325
|
+
## Compatibility Requirement
|
|
326
|
+
|
|
327
|
+
Producer and consumer compatibility must be documented before release.
|
|
328
|
+
`,
|
|
329
|
+
currentAssetRelativePath: "agent-state/INTERFACE_REGISTRY.md",
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
function normalizeForExactMatch(content) {
|
|
333
|
+
return content.replace(/\r\n/g, "\n").trimEnd();
|
|
334
|
+
}
|
|
335
|
+
function matchesLegacyPackageContent(content, legacyContent) {
|
|
336
|
+
return normalizeForExactMatch(content) === normalizeForExactMatch(legacyContent);
|
|
337
|
+
}
|
|
338
|
+
function matchesCurrentPackageContent(content, currentContent) {
|
|
339
|
+
return normalizeForExactMatch(content) === normalizeForExactMatch(currentContent);
|
|
340
|
+
}
|
|
341
|
+
function readAsset(assetsRoot, relativePath) {
|
|
342
|
+
return readFileSync(join(assetsRoot, relativePath), "utf-8");
|
|
343
|
+
}
|
|
344
|
+
export async function recordPackageGateContractVersion(store) {
|
|
345
|
+
await store.setJSON(PACKAGE_GATE_CONTRACT_VERSION_KEY, PACKAGE_GATE_CONTRACT_VERSION);
|
|
346
|
+
}
|
|
347
|
+
export async function migratePackageGateContract(store, workspaceRoot, assetsRoot) {
|
|
348
|
+
const messages = [];
|
|
349
|
+
let changed = false;
|
|
350
|
+
for (const entry of LEGACY_GATE_MIGRATIONS) {
|
|
351
|
+
const key = `knowledge/gates/${entry.fileName}`;
|
|
352
|
+
const currentContent = entry.currentAssetRelativePath
|
|
353
|
+
? readAsset(assetsRoot, entry.currentAssetRelativePath)
|
|
354
|
+
: undefined;
|
|
355
|
+
const existing = await store.getBlob(key);
|
|
356
|
+
if (typeof existing === "string") {
|
|
357
|
+
if (matchesLegacyPackageContent(existing, entry.legacyContent)) {
|
|
358
|
+
if (entry.remove) {
|
|
359
|
+
await store.delete(key);
|
|
360
|
+
messages.push(`Removed legacy package gate ${key}.`);
|
|
361
|
+
}
|
|
362
|
+
else if (currentContent) {
|
|
363
|
+
await store.setBlob(key, currentContent);
|
|
364
|
+
messages.push(`Updated legacy package gate ${key}.`);
|
|
365
|
+
}
|
|
366
|
+
changed = true;
|
|
367
|
+
}
|
|
368
|
+
else if (!currentContent || !matchesCurrentPackageContent(existing, currentContent)) {
|
|
369
|
+
messages.push(`Skipped customized gate ${key}; content differs from known package seed.`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const projectionPath = join(workspaceRoot, "agent-state", "MODULES", "gates", entry.fileName);
|
|
373
|
+
if (existsSync(projectionPath)) {
|
|
374
|
+
const projectionContent = readFileSync(projectionPath, "utf-8");
|
|
375
|
+
if (matchesLegacyPackageContent(projectionContent, entry.legacyContent)) {
|
|
376
|
+
if (entry.remove) {
|
|
377
|
+
rmSync(projectionPath, { force: true });
|
|
378
|
+
messages.push(`Removed legacy gate projection ${projectionPath}.`);
|
|
379
|
+
}
|
|
380
|
+
else if (currentContent) {
|
|
381
|
+
mkdirSync(join(workspaceRoot, "agent-state", "MODULES", "gates"), { recursive: true });
|
|
382
|
+
writeFileSync(projectionPath, currentContent, "utf-8");
|
|
383
|
+
messages.push(`Updated legacy gate projection ${projectionPath}.`);
|
|
384
|
+
}
|
|
385
|
+
changed = true;
|
|
386
|
+
}
|
|
387
|
+
else if (!currentContent || !matchesCurrentPackageContent(projectionContent, currentContent)) {
|
|
388
|
+
messages.push(`Skipped customized gate projection ${projectionPath}; content differs from known package seed.`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
for (const entry of LEGACY_STATIC_ASSET_MIGRATIONS) {
|
|
393
|
+
const currentContent = readAsset(assetsRoot, entry.currentAssetRelativePath);
|
|
394
|
+
const existing = await store.getBlob(entry.key);
|
|
395
|
+
if (typeof existing !== "string")
|
|
396
|
+
continue;
|
|
397
|
+
if (matchesLegacyPackageContent(existing, entry.legacyContent)) {
|
|
398
|
+
await store.setBlob(entry.key, currentContent);
|
|
399
|
+
messages.push(`Updated legacy package asset ${entry.key}.`);
|
|
400
|
+
changed = true;
|
|
401
|
+
}
|
|
402
|
+
else if (!matchesCurrentPackageContent(existing, currentContent)) {
|
|
403
|
+
messages.push(`Skipped customized asset ${entry.key}; content differs from known package seed.`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const recordedVersion = await store.getJSON(PACKAGE_GATE_CONTRACT_VERSION_KEY);
|
|
407
|
+
if (recordedVersion !== PACKAGE_GATE_CONTRACT_VERSION) {
|
|
408
|
+
await recordPackageGateContractVersion(store);
|
|
409
|
+
changed = true;
|
|
410
|
+
}
|
|
411
|
+
return { changed, messages };
|
|
412
|
+
}
|
|
413
|
+
//# sourceMappingURL=gate-contract-migration.js.map
|
package/dist/tools-files.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
5
|
import { spawnSync } from "node:child_process";
|
|
6
|
-
import { existsSync, mkdirSync, readdirSync,
|
|
6
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { ACE_TASKS_ROOT_REL, normalizePathForValidation, safeRead, safeWriteAsync, safeWrite, resolveWorkspaceWritePath, resolveStoreFallbackKeysForPath, wsPath, WORKSPACE_ROOT, } from "./helpers.js";
|
|
@@ -14,7 +14,7 @@ import { shouldAutoRefreshKanbanForPath, refreshKanbanArtifacts, } from "./kanba
|
|
|
14
14
|
import { appendRunLedgerEntrySafe } from "./run-ledger.js";
|
|
15
15
|
import { appendStatusEventSafe } from "./status-events.js";
|
|
16
16
|
import { safeEditFile, diffContents, applyPatch } from "./safe-edit.js";
|
|
17
|
-
import { detectAstgrepCommand, locateAstgrepMatches, runAstgrepQuery, } from "./astgrep-index.js";
|
|
17
|
+
import { detectAstgrepCommand, flattenMetaVariables, locateAstgrepMatches, runAstgrepQuery, } from "./astgrep-index.js";
|
|
18
18
|
import { readRuntimeProfile, resolveEffectiveSurgicalReadBudget, validateRuntimeProfileContent, } from "./runtime-profile.js";
|
|
19
19
|
import { withLocalModelRuntimeRepository } from "./store/repositories/local-model-runtime-repository.js";
|
|
20
20
|
function reasonCodeFromError(error) {
|
|
@@ -206,8 +206,12 @@ async function compileStructuralEditPlan(input) {
|
|
|
206
206
|
},
|
|
207
207
|
validation_command: input.validationCommand,
|
|
208
208
|
test_command: input.testCommand,
|
|
209
|
+
allow_multi_match: input.allowMultiMatch,
|
|
209
210
|
});
|
|
210
211
|
}
|
|
212
|
+
function renderStructuralRewrite(template, captures) {
|
|
213
|
+
return template.replace(/\$([A-Z][A-Z0-9_]*)/g, (full, name) => captures[name] ?? full);
|
|
214
|
+
}
|
|
211
215
|
function previewDiffText(diffText) {
|
|
212
216
|
const lines = diffText.trimEnd().split("\n");
|
|
213
217
|
const limited = lines.slice(0, 80).join("\n");
|
|
@@ -378,6 +382,24 @@ async function buildStructuralPreview(plan) {
|
|
|
378
382
|
}),
|
|
379
383
|
};
|
|
380
384
|
}
|
|
385
|
+
if (targetMatches.length > 1 && !plan.allow_multi_match) {
|
|
386
|
+
return {
|
|
387
|
+
artifact: await finalize({
|
|
388
|
+
ok: false,
|
|
389
|
+
reason_code: "ambiguous_multi_match_in_file",
|
|
390
|
+
error: `Plan ${plan.plan_id} resolves to ${targetMatches.length} matches in ${plan.target.file}. Pass allow_multi_match=true at compile time to opt in, or narrow the pattern.`,
|
|
391
|
+
target_file: plan.target.file,
|
|
392
|
+
expected_file_hash: plan.target.file_hash,
|
|
393
|
+
current_file_hash: currentFileHash,
|
|
394
|
+
matched_count: targetMatches.length,
|
|
395
|
+
affected_file_count: affectedFiles.length,
|
|
396
|
+
changed_ranges: [],
|
|
397
|
+
diff_summary: "",
|
|
398
|
+
diff_preview: "",
|
|
399
|
+
promotable: false,
|
|
400
|
+
}),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
381
403
|
const astgrepCmd = detectAstgrepCommand();
|
|
382
404
|
if (!astgrepCmd) {
|
|
383
405
|
return {
|
|
@@ -403,7 +425,7 @@ async function buildStructuralPreview(plan) {
|
|
|
403
425
|
mkdirSync(dirname(stagedOriginal), { recursive: true });
|
|
404
426
|
writeFileSync(stagedOriginal, currentContent, "utf-8");
|
|
405
427
|
writeFileSync(stagedRewrite, currentContent, "utf-8");
|
|
406
|
-
const rewriteResult = spawnSync(astgrepCmd, ["--pattern", plan.locator.pattern, "--rewrite", plan.rewrite.rewrite_template, "--lang", plan.locator.lang,
|
|
428
|
+
const rewriteResult = spawnSync(astgrepCmd, ["--pattern", plan.locator.pattern, "--rewrite", plan.rewrite.rewrite_template, "--lang", plan.locator.lang, "--json", stagedRewrite], { encoding: "utf8", cwd: WORKSPACE_ROOT });
|
|
407
429
|
if (rewriteResult.status !== 0) {
|
|
408
430
|
return {
|
|
409
431
|
artifact: await finalize({
|
|
@@ -423,7 +445,46 @@ async function buildStructuralPreview(plan) {
|
|
|
423
445
|
}),
|
|
424
446
|
};
|
|
425
447
|
}
|
|
426
|
-
|
|
448
|
+
let rawMatches = [];
|
|
449
|
+
try {
|
|
450
|
+
rawMatches = JSON.parse(rewriteResult.stdout || "[]");
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
rawMatches = [];
|
|
454
|
+
}
|
|
455
|
+
const selectedBytes = plan.target.selected_range?.byteOffset;
|
|
456
|
+
const targeted = rawMatches.find((match) => selectedBytes
|
|
457
|
+
&& match.range?.byteOffset?.start === selectedBytes.start
|
|
458
|
+
&& match.range?.byteOffset?.end === selectedBytes.end);
|
|
459
|
+
const replacement = targeted && (typeof targeted.replacement === "string"
|
|
460
|
+
? targeted.replacement
|
|
461
|
+
: renderStructuralRewrite(plan.rewrite.rewrite_template, flattenMetaVariables(targeted.metaVariables)));
|
|
462
|
+
if (!selectedBytes || !targeted || typeof replacement !== "string") {
|
|
463
|
+
return {
|
|
464
|
+
artifact: await finalize({
|
|
465
|
+
ok: false,
|
|
466
|
+
reason_code: "target_byte_range_drift",
|
|
467
|
+
error: `No match at selected byte range [${selectedBytes?.start},${selectedBytes?.end}] for plan ${plan.plan_id}`,
|
|
468
|
+
target_file: plan.target.file,
|
|
469
|
+
expected_file_hash: plan.target.file_hash,
|
|
470
|
+
current_file_hash: currentFileHash,
|
|
471
|
+
matched_count: rawMatches.length,
|
|
472
|
+
affected_file_count: affectedFiles.length,
|
|
473
|
+
changed_ranges: [],
|
|
474
|
+
diff_summary: "",
|
|
475
|
+
diff_preview: "",
|
|
476
|
+
promotable: false,
|
|
477
|
+
staging_path: stagingDir,
|
|
478
|
+
}),
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
const originalBuffer = Buffer.from(currentContent, "utf8");
|
|
482
|
+
const rewrittenContent = Buffer.concat([
|
|
483
|
+
originalBuffer.subarray(0, selectedBytes.start ?? 0),
|
|
484
|
+
Buffer.from(replacement, "utf8"),
|
|
485
|
+
originalBuffer.subarray(selectedBytes.end ?? 0),
|
|
486
|
+
]).toString("utf8");
|
|
487
|
+
writeFileSync(stagedRewrite, rewrittenContent, "utf8");
|
|
427
488
|
const rewriteHash = contentSha256(rewrittenContent);
|
|
428
489
|
const diff = diffContents(currentContent, rewrittenContent);
|
|
429
490
|
const diffProcess = spawnSync("diff", ["-u", "--label", `a/${plan.target.file}`, "--label", `b/${plan.target.file}`, stagedOriginal, stagedRewrite], { encoding: "utf8", cwd: WORKSPACE_ROOT });
|
|
@@ -1344,9 +1405,10 @@ export function registerFileTools(server) {
|
|
|
1344
1405
|
max_results: z.number().int().min(1).max(200).optional().describe("Max locator matches to keep (default: 50)"),
|
|
1345
1406
|
desired_change: z.string().describe("Plain-language edit intent; used as rewrite_template when no explicit template is provided"),
|
|
1346
1407
|
rewrite_template: z.string().optional().describe("Explicit ast-grep rewrite template; preferred when provided"),
|
|
1408
|
+
allow_multi_match: z.boolean().optional().describe("Opt into selecting and rewriting only the chosen match when multiple matches exist in the target file"),
|
|
1347
1409
|
validation_command: z.string().optional().describe("Shell command to validate before promotion"),
|
|
1348
1410
|
test_command: z.string().optional().describe("Shell command to run before promotion"),
|
|
1349
|
-
}, async ({ match_id, pattern, lang, scope, symbol_hint, max_results, desired_change, rewrite_template, validation_command, test_command, }) => {
|
|
1411
|
+
}, async ({ match_id, pattern, lang, scope, symbol_hint, max_results, desired_change, rewrite_template, allow_multi_match, validation_command, test_command, }) => {
|
|
1350
1412
|
if (!match_id && (!pattern || !lang)) {
|
|
1351
1413
|
return jsonResponse({
|
|
1352
1414
|
ok: false,
|
|
@@ -1455,6 +1517,7 @@ export function registerFileTools(server) {
|
|
|
1455
1517
|
selectedMatch,
|
|
1456
1518
|
desiredChange: desired_change,
|
|
1457
1519
|
rewriteTemplate: rewrite_template,
|
|
1520
|
+
allowMultiMatch: allow_multi_match,
|
|
1458
1521
|
validationCommand: validation_command,
|
|
1459
1522
|
testCommand: test_command,
|
|
1460
1523
|
}).catch((error) => {
|