@voybio/ace-swarm 2.4.0 → 2.4.1
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 +8 -0
- package/README.md +1 -0
- package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
- package/assets/agent-state/runtime-tool-specs.json +70 -2
- package/assets/instructions/ACE_Coder.instructions.md +13 -0
- package/assets/instructions/ACE_UI.instructions.md +11 -0
- package/dist/ace-context.js +70 -11
- package/dist/ace-internal-tools.d.ts +3 -1
- package/dist/ace-internal-tools.js +10 -2
- package/dist/agent-runtime/role-adapters.d.ts +18 -1
- package/dist/agent-runtime/role-adapters.js +49 -5
- package/dist/astgrep-index.d.ts +48 -0
- package/dist/astgrep-index.js +126 -1
- package/dist/cli.js +205 -15
- package/dist/discovery-runtime-wrappers.d.ts +108 -0
- package/dist/discovery-runtime-wrappers.js +615 -0
- package/dist/helpers/bootstrap.js +1 -1
- package/dist/helpers/constants.d.ts +2 -2
- package/dist/helpers/constants.js +7 -0
- package/dist/helpers/path-utils.d.ts +8 -1
- package/dist/helpers/path-utils.js +27 -8
- package/dist/helpers/store-resolution.js +7 -3
- package/dist/job-scheduler.js +30 -4
- package/dist/json-sanitizer.d.ts +16 -0
- package/dist/json-sanitizer.js +26 -0
- package/dist/local-model-policy.d.ts +27 -0
- package/dist/local-model-policy.js +84 -0
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +21 -20
- package/dist/model-bridge.d.ts +6 -1
- package/dist/model-bridge.js +338 -21
- package/dist/orchestrator-supervisor.d.ts +42 -0
- package/dist/orchestrator-supervisor.js +110 -3
- package/dist/plan-proposal.d.ts +115 -0
- package/dist/plan-proposal.js +1073 -0
- package/dist/runtime-executor.d.ts +6 -1
- package/dist/runtime-executor.js +72 -5
- package/dist/runtime-tool-specs.d.ts +19 -1
- package/dist/runtime-tool-specs.js +67 -26
- package/dist/schemas.js +29 -1
- package/dist/server.js +51 -0
- package/dist/shared.d.ts +1 -0
- package/dist/shared.js +2 -0
- package/dist/store/bootstrap-store.d.ts +1 -0
- package/dist/store/bootstrap-store.js +8 -2
- package/dist/store/repositories/local-model-runtime-repository.d.ts +1 -1
- package/dist/store/repositories/local-model-runtime-repository.js +1 -1
- package/dist/store/repositories/vericify-repository.d.ts +1 -1
- package/dist/tools-agent.d.ts +20 -0
- package/dist/tools-agent.js +538 -28
- package/dist/tools-discovery.js +135 -0
- package/dist/tools-files.js +768 -66
- package/dist/tools-framework.js +80 -61
- package/dist/tui/index.js +10 -1
- package/dist/tui/ollama.d.ts +8 -1
- package/dist/tui/ollama.js +53 -12
- package/dist/tui/openai-compatible.d.ts +13 -0
- package/dist/tui/openai-compatible.js +305 -5
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +35 -11
- package/dist/vericify-bridge.d.ts +1 -1
- package/package.json +1 -1
package/dist/tools-agent.js
CHANGED
|
@@ -11,12 +11,13 @@ import { getTrackerAdapter, listTrackerAdapterKinds, loadTrackerSnapshot, valida
|
|
|
11
11
|
import { refreshTrackerSnapshot } from "./tracker-sync.js";
|
|
12
12
|
import { appendVericifyProcessPost, loadVericifyBridgeSnapshot, loadVericifyProcessPostLog, refreshVericifyBridgeSnapshot, validateVericifyBridgeSnapshotContent, validateVericifyProcessPostLogContent, } from "./vericify-bridge.js";
|
|
13
13
|
import { getRoleTitle, ROLE_ENUM, KERNEL_KEY_ENUM, ROLE_TITLES } from "./shared.js";
|
|
14
|
-
import { createDefaultModelBridgeClients, resolveLocalModelRuntime, runLocalModelTask, } from "./local-model-runtime.js";
|
|
14
|
+
import { createDefaultModelBridgeClients, resolveLocalModelRuntime, resolveTier, runLocalModelTask, } from "./local-model-runtime.js";
|
|
15
15
|
import { withLocalModelRuntimeRepository } from "./store/repositories/local-model-runtime-repository.js";
|
|
16
16
|
import { executeAceInternalTool } from "./ace-internal-tools.js";
|
|
17
17
|
import { ModelBridge } from "./model-bridge.js";
|
|
18
18
|
import { getVericifyContextPacket, getVericifyDelta } from "./vericify-context.js";
|
|
19
|
-
import {
|
|
19
|
+
import { proposePlan as proposePlanImpl, proposePlanDeterministic, validatePlan as validatePlanImpl, loadAcceptanceTraceContract as loadAcceptanceTraceContractImpl, persistAcceptanceTraceMapWithContract, } from "./plan-proposal.js";
|
|
20
|
+
import { amendTaskPlan, createTaskPlan, superviseTaskPlan, } from "./orchestrator-supervisor.js";
|
|
20
21
|
function parseOptionalJsonObject(raw) {
|
|
21
22
|
if (!raw)
|
|
22
23
|
return {};
|
|
@@ -77,6 +78,67 @@ function extractHandoffId(text) {
|
|
|
77
78
|
const lineMatch = text.match(/handoff_id:\s*([A-Z0-9-]+)/i);
|
|
78
79
|
return lineMatch?.[1];
|
|
79
80
|
}
|
|
81
|
+
export function formatStepTaskForBridge(step) {
|
|
82
|
+
const upstreamOutputs = step.upstream_outputs ?? [];
|
|
83
|
+
if (upstreamOutputs.length === 0) {
|
|
84
|
+
return step.task;
|
|
85
|
+
}
|
|
86
|
+
const renderedOutputs = upstreamOutputs
|
|
87
|
+
.map((output) => `- ${output.step_id}: ${output.result_summary || "[no summary]"}${output.evidence_refs.length > 0 ? ` (evidence: ${output.evidence_refs.join(", ")})` : ""}`)
|
|
88
|
+
.join("\n");
|
|
89
|
+
return [
|
|
90
|
+
step.task,
|
|
91
|
+
"",
|
|
92
|
+
"Upstream outputs:",
|
|
93
|
+
renderedOutputs,
|
|
94
|
+
].join("\n");
|
|
95
|
+
}
|
|
96
|
+
// ── Re-export plan-proposal API ────────────────────────────────────────────
|
|
97
|
+
export { loadAcceptanceTraceContractImpl as loadAcceptanceTraceContract };
|
|
98
|
+
export { proposePlanImpl as proposePlan, validatePlanImpl as validatePlan };
|
|
99
|
+
// ── proposalToSteps: maps PlanProposal steps to local PlannedStepInput ────
|
|
100
|
+
function proposalToSteps(proposal) {
|
|
101
|
+
return proposal.steps.map((s) => ({
|
|
102
|
+
role: s.role,
|
|
103
|
+
task: s.task,
|
|
104
|
+
depends_on: s.depends_on && s.depends_on.length > 0 ? s.depends_on : undefined,
|
|
105
|
+
tool_scope: s.tool_scope && s.tool_scope.length > 0 ? s.tool_scope : undefined,
|
|
106
|
+
expected_output_class: s.expected_output_class,
|
|
107
|
+
expected_artifacts: s.expected_artifacts,
|
|
108
|
+
allowed_tools: s.allowed_tools,
|
|
109
|
+
forbidden_patterns: s.forbidden_patterns,
|
|
110
|
+
required_evidence_refs: s.required_evidence_refs,
|
|
111
|
+
structural_edit_plan_required: s.structural_edit_plan_required,
|
|
112
|
+
structural_edit_waiver: s.structural_edit_waiver,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
function stripPlanIdFromVerdict(verdict) {
|
|
116
|
+
return {
|
|
117
|
+
ok: verdict.ok,
|
|
118
|
+
score: verdict.score,
|
|
119
|
+
blocking_findings: [...verdict.blocking_findings],
|
|
120
|
+
soft_findings: [...verdict.soft_findings],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function proposalToNormalization(proposal, verdict) {
|
|
124
|
+
const acMap = new Map();
|
|
125
|
+
const stopConditionsMap = new Map();
|
|
126
|
+
proposal.steps.forEach((ps, idx) => {
|
|
127
|
+
acMap.set(stepLabel(idx), ps.acceptance_criteria ?? []);
|
|
128
|
+
stopConditionsMap.set(stepLabel(idx), ps.stop_condition ?? []);
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
planSource: proposal.plan_source,
|
|
132
|
+
steps: proposalToSteps(proposal),
|
|
133
|
+
insertedResearch: false,
|
|
134
|
+
shipFanoutEnabled: false,
|
|
135
|
+
intentSummary: proposal.intent_summary,
|
|
136
|
+
successCriteria: proposal.success_criteria,
|
|
137
|
+
validationVerdict: stripPlanIdFromVerdict(verdict),
|
|
138
|
+
acceptanceCriteriaByStep: acMap,
|
|
139
|
+
stopConditionsByStep: stopConditionsMap,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
80
142
|
function stepLabel(index) {
|
|
81
143
|
return `step-${index + 1}`;
|
|
82
144
|
}
|
|
@@ -87,8 +149,18 @@ function isImplementationRole(role) {
|
|
|
87
149
|
return role === "coders" || role === "builder";
|
|
88
150
|
}
|
|
89
151
|
async function buildOrchestratorSteps(task, sessionId) {
|
|
90
|
-
|
|
91
|
-
|
|
152
|
+
// (a) propose a plan via the planner model
|
|
153
|
+
const proposal = await proposePlanImpl(task, sessionId);
|
|
154
|
+
// (b) validate the proposal
|
|
155
|
+
const verdict = await validatePlanImpl({ proposal, sessionId });
|
|
156
|
+
// (c) if ok, return validated steps with planner enrichment
|
|
157
|
+
if (verdict.ok && proposal.steps.length > 0) {
|
|
158
|
+
return proposalToNormalization(proposal, verdict);
|
|
159
|
+
}
|
|
160
|
+
// Deterministic fallback: still validate the floor before returning it.
|
|
161
|
+
const fallbackProposal = proposePlanDeterministic(task);
|
|
162
|
+
const fallbackVerdict = await validatePlanImpl({ proposal: fallbackProposal, sessionId });
|
|
163
|
+
return proposalToNormalization(fallbackProposal, fallbackVerdict);
|
|
92
164
|
}
|
|
93
165
|
function normalizeExplicitPlanSteps(steps, task) {
|
|
94
166
|
const originalIdByLabel = new Map();
|
|
@@ -181,6 +253,7 @@ function normalizeExplicitPlanSteps(steps, task) {
|
|
|
181
253
|
finalIdByInternal.set(step.id, stepLabel(index));
|
|
182
254
|
});
|
|
183
255
|
return {
|
|
256
|
+
planSource: "explicit_steps",
|
|
184
257
|
steps: normalized.map((step) => ({
|
|
185
258
|
role: step.role,
|
|
186
259
|
task: step.task,
|
|
@@ -189,6 +262,13 @@ function normalizeExplicitPlanSteps(steps, task) {
|
|
|
189
262
|
: undefined,
|
|
190
263
|
parallel_group: step.parallel_group,
|
|
191
264
|
tool_scope: step.tool_scope,
|
|
265
|
+
expected_output_class: step.expected_output_class,
|
|
266
|
+
expected_artifacts: step.expected_artifacts,
|
|
267
|
+
allowed_tools: step.allowed_tools,
|
|
268
|
+
forbidden_patterns: step.forbidden_patterns,
|
|
269
|
+
required_evidence_refs: step.required_evidence_refs,
|
|
270
|
+
structural_edit_plan_required: step.structural_edit_plan_required,
|
|
271
|
+
structural_edit_waiver: step.structural_edit_waiver,
|
|
192
272
|
})),
|
|
193
273
|
insertedResearch,
|
|
194
274
|
shipFanoutEnabled,
|
|
@@ -196,32 +276,49 @@ function normalizeExplicitPlanSteps(steps, task) {
|
|
|
196
276
|
}
|
|
197
277
|
async function normalizeOrchestratorPlanSteps(task, steps, sessionId) {
|
|
198
278
|
if (!Array.isArray(steps) || steps.length === 0) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
normalization: {
|
|
202
|
-
steps: await buildOrchestratorSteps(task, sessionId),
|
|
203
|
-
insertedResearch: false,
|
|
204
|
-
shipFanoutEnabled: false,
|
|
205
|
-
},
|
|
206
|
-
};
|
|
279
|
+
const normalization = await buildOrchestratorSteps(task, sessionId);
|
|
280
|
+
return { planSource: normalization.planSource, normalization };
|
|
207
281
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
282
|
+
const normalization = normalizeExplicitPlanSteps(steps, task);
|
|
283
|
+
// Run the same artifact/output-class gate that planner-derived steps go through.
|
|
284
|
+
const syntheticProposal = {
|
|
285
|
+
plan_id: `explicit-${Date.now()}`,
|
|
286
|
+
status: "planning",
|
|
287
|
+
intent_summary: task,
|
|
288
|
+
success_criteria: [],
|
|
289
|
+
steps: normalization.steps.map((s) => ({
|
|
290
|
+
role: s.role,
|
|
291
|
+
task: s.task,
|
|
292
|
+
depends_on: s.depends_on,
|
|
293
|
+
tool_scope: s.tool_scope,
|
|
294
|
+
acceptance_criteria: [],
|
|
295
|
+
expected_output_class: s.expected_output_class,
|
|
296
|
+
expected_artifacts: s.expected_artifacts,
|
|
297
|
+
allowed_tools: s.allowed_tools,
|
|
298
|
+
forbidden_patterns: s.forbidden_patterns,
|
|
299
|
+
required_evidence_refs: s.required_evidence_refs,
|
|
300
|
+
structural_edit_plan_required: s.structural_edit_plan_required,
|
|
301
|
+
structural_edit_waiver: s.structural_edit_waiver,
|
|
302
|
+
})),
|
|
303
|
+
plan_source: "explicit_steps",
|
|
211
304
|
};
|
|
305
|
+
const verdict = await validatePlanImpl({ proposal: syntheticProposal, sessionId });
|
|
306
|
+
normalization.validationVerdict = stripPlanIdFromVerdict(verdict);
|
|
307
|
+
return { planSource: "explicit_steps", normalization };
|
|
212
308
|
}
|
|
213
309
|
async function persistAcceptanceTraceMap(input) {
|
|
214
|
-
return
|
|
215
|
-
version: 1,
|
|
216
|
-
generated_at: new Date().toISOString(),
|
|
310
|
+
return persistAcceptanceTraceMapWithContract({
|
|
217
311
|
plan_id: input.plan.plan_id,
|
|
218
312
|
task: input.task,
|
|
219
313
|
plan_source: input.planSource,
|
|
314
|
+
intent_summary: input.normalization?.intentSummary,
|
|
315
|
+
success_criteria: input.normalization?.successCriteria,
|
|
316
|
+
validation_verdict: input.normalization?.validationVerdict,
|
|
220
317
|
policies: {
|
|
221
318
|
inserted_research_before_spec: input.insertedResearch,
|
|
222
319
|
ship_fanout_enabled: input.shipFanoutEnabled,
|
|
223
320
|
},
|
|
224
|
-
steps: input.plan.steps.map((step) => ({
|
|
321
|
+
steps: input.plan.steps.map((step, idx) => ({
|
|
225
322
|
step_id: step.step_id,
|
|
226
323
|
role: step.role,
|
|
227
324
|
task: step.task,
|
|
@@ -232,8 +329,17 @@ async function persistAcceptanceTraceMap(input) {
|
|
|
232
329
|
: step.role === "spec"
|
|
233
330
|
? "research"
|
|
234
331
|
: null,
|
|
332
|
+
acceptance_criteria: input.normalization?.acceptanceCriteriaByStep?.get(`step-${idx + 1}`),
|
|
333
|
+
stop_condition: input.normalization?.stopConditionsByStep?.get(`step-${idx + 1}`),
|
|
334
|
+
expected_output_class: step.expected_output_class,
|
|
335
|
+
expected_artifacts: step.expected_artifacts,
|
|
336
|
+
allowed_tools: step.allowed_tools,
|
|
337
|
+
forbidden_patterns: step.forbidden_patterns,
|
|
338
|
+
required_evidence_refs: step.required_evidence_refs,
|
|
339
|
+
structural_edit_plan_required: step.structural_edit_plan_required,
|
|
340
|
+
structural_edit_waiver: step.structural_edit_waiver,
|
|
235
341
|
})),
|
|
236
|
-
}
|
|
342
|
+
});
|
|
237
343
|
}
|
|
238
344
|
function appendUniqueNote(target, note) {
|
|
239
345
|
if (!target.includes(note)) {
|
|
@@ -276,6 +382,265 @@ function buildDefaultOrchestratorAmendment(input) {
|
|
|
276
382
|
add_after_step_id: input.step.step_id,
|
|
277
383
|
};
|
|
278
384
|
}
|
|
385
|
+
function isAcceptanceTraceContract(value) {
|
|
386
|
+
if (!value || typeof value !== "object")
|
|
387
|
+
return false;
|
|
388
|
+
const candidate = value;
|
|
389
|
+
return Array.isArray(candidate.steps);
|
|
390
|
+
}
|
|
391
|
+
const INTENT_STOPWORDS = new Set([
|
|
392
|
+
"the",
|
|
393
|
+
"and",
|
|
394
|
+
"for",
|
|
395
|
+
"with",
|
|
396
|
+
"this",
|
|
397
|
+
"that",
|
|
398
|
+
"step",
|
|
399
|
+
"step1",
|
|
400
|
+
"step2",
|
|
401
|
+
"step3",
|
|
402
|
+
"step4",
|
|
403
|
+
"step5",
|
|
404
|
+
"step6",
|
|
405
|
+
"step7",
|
|
406
|
+
"step8",
|
|
407
|
+
"step9",
|
|
408
|
+
"step10",
|
|
409
|
+
"complete",
|
|
410
|
+
"completed",
|
|
411
|
+
"validate",
|
|
412
|
+
"validation",
|
|
413
|
+
"implement",
|
|
414
|
+
"implementation",
|
|
415
|
+
"done",
|
|
416
|
+
"ready",
|
|
417
|
+
]);
|
|
418
|
+
function normalizeIntentText(value) {
|
|
419
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
420
|
+
}
|
|
421
|
+
function intentTokens(value) {
|
|
422
|
+
return normalizeIntentText(value)
|
|
423
|
+
.split(/\s+/)
|
|
424
|
+
.filter((token) => token.length > 2 && !INTENT_STOPWORDS.has(token));
|
|
425
|
+
}
|
|
426
|
+
function intentCriterionSatisfied(haystack, criterion) {
|
|
427
|
+
const normalizedHaystack = normalizeIntentText(haystack);
|
|
428
|
+
const tokens = intentTokens(criterion);
|
|
429
|
+
if (tokens.length === 0)
|
|
430
|
+
return false;
|
|
431
|
+
const hits = tokens.filter((token) => normalizedHaystack.includes(token)).length;
|
|
432
|
+
return hits >= Math.min(2, tokens.length) || hits / tokens.length >= 0.5;
|
|
433
|
+
}
|
|
434
|
+
function resultContractText(result) {
|
|
435
|
+
return [
|
|
436
|
+
result.summary,
|
|
437
|
+
JSON.stringify(result.tool_calls ?? []),
|
|
438
|
+
JSON.stringify(result.child_results ?? []),
|
|
439
|
+
].join("\n");
|
|
440
|
+
}
|
|
441
|
+
const ARTIFACT_STUB_PATTERN = /\b(?:todo-only|placeholder|stub(?:bed|by)?|tbd|boilerplate(?:-only)?|scaffold(?:ing)?\s+(?:stub|only))\b/i;
|
|
442
|
+
function isUiPlainTextContract(step, contractStep) {
|
|
443
|
+
const expected = contractStep.expected_output_class ?? inferContractClassFromStep(step);
|
|
444
|
+
return step.role === "ui" && expected === "plain_text_plan";
|
|
445
|
+
}
|
|
446
|
+
function validateUiTopicAnchor(input) {
|
|
447
|
+
if (!isUiPlainTextContract(input.step, input.contract_step)) {
|
|
448
|
+
return { ok: true };
|
|
449
|
+
}
|
|
450
|
+
const acceptanceCriteria = (input.contract_step.acceptance_criteria ?? []).filter((criterion) => typeof criterion === "string" && criterion.trim().length > 0);
|
|
451
|
+
if (acceptanceCriteria.length === 0) {
|
|
452
|
+
return { ok: true };
|
|
453
|
+
}
|
|
454
|
+
const outputText = resultContractText(input.result);
|
|
455
|
+
if (intentTokens(outputText).length < 4) {
|
|
456
|
+
return { ok: true };
|
|
457
|
+
}
|
|
458
|
+
const taskAnchored = intentCriterionSatisfied(outputText, input.step.task);
|
|
459
|
+
const matchedCriteria = acceptanceCriteria.filter((criterion) => intentCriterionSatisfied(outputText, criterion));
|
|
460
|
+
if (!taskAnchored && matchedCriteria.length === 0) {
|
|
461
|
+
return {
|
|
462
|
+
ok: false,
|
|
463
|
+
reason_code: "role_drift_ui_off_topic",
|
|
464
|
+
reason: `UI step ${input.step.step_id} drifted off topic instead of staying anchored to the requested plan.`,
|
|
465
|
+
uncovered_clauses: acceptanceCriteria,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
return { ok: true };
|
|
469
|
+
}
|
|
470
|
+
function inferContractClassFromStep(step) {
|
|
471
|
+
if (step.role === "qa" || step.role === "release")
|
|
472
|
+
return "qa_verdict";
|
|
473
|
+
if ((step.tool_scope ?? []).some((tool) => tool.includes("astgrep") || tool.includes("structural_edit"))) {
|
|
474
|
+
return "structural_edit_plan";
|
|
475
|
+
}
|
|
476
|
+
if ((step.tool_scope ?? []).some((tool) => tool.includes("write") || tool.includes("safe_edit"))) {
|
|
477
|
+
return "code_artifact";
|
|
478
|
+
}
|
|
479
|
+
if ((step.tool_scope ?? []).length > 0)
|
|
480
|
+
return "tool_envelope";
|
|
481
|
+
return "plain_text_plan";
|
|
482
|
+
}
|
|
483
|
+
function requiresContract(step) {
|
|
484
|
+
if ((step.tool_scope ?? []).length > 0)
|
|
485
|
+
return true;
|
|
486
|
+
if (step.role === "coders" || step.role === "builder" || step.role === "qa")
|
|
487
|
+
return true;
|
|
488
|
+
return (step.tool_scope ?? []).some((tool) => /write|edit|safe_edit|astgrep|structural/i.test(tool));
|
|
489
|
+
}
|
|
490
|
+
function validateContractClass(input) {
|
|
491
|
+
const expected = input.contract_step.expected_output_class ?? inferContractClassFromStep(input.step);
|
|
492
|
+
const toolCalls = input.result.tool_calls ?? [];
|
|
493
|
+
const evidenceRefs = input.result.evidence_refs ?? [];
|
|
494
|
+
if (input.contract_step.allowed_tools?.length) {
|
|
495
|
+
const allowed = new Set(input.contract_step.allowed_tools);
|
|
496
|
+
const disallowed = toolCalls.find((call) => !allowed.has(call.tool));
|
|
497
|
+
if (disallowed) {
|
|
498
|
+
return {
|
|
499
|
+
ok: false,
|
|
500
|
+
reason_code: "bridge_output_malformed_json",
|
|
501
|
+
reason: `Tool ${disallowed.tool} is not allowed for ${input.step.step_id}.`,
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (expected === "qa_verdict" && toolCalls.some((call) => /write|edit|rewrite|safe_edit/i.test(call.tool))) {
|
|
506
|
+
return {
|
|
507
|
+
ok: false,
|
|
508
|
+
reason_code: "qa_rewrote_artifact",
|
|
509
|
+
reason: `QA step ${input.step.step_id} attempted a mutation tool.`,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
if ((expected === "tool_envelope" || expected === "structural_edit_plan") && toolCalls.length === 0) {
|
|
513
|
+
return {
|
|
514
|
+
ok: false,
|
|
515
|
+
reason_code: "bridge_output_malformed_json",
|
|
516
|
+
reason: `Step ${input.step.step_id} expected a tool envelope but produced none.`,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
if (expected === "code_artifact" || expected === "structural_edit_plan") {
|
|
520
|
+
const expectedArtifacts = (input.contract_step.expected_artifacts ?? []).filter((artifact) => artifact.required !== false);
|
|
521
|
+
const missingArtifacts = expectedArtifacts
|
|
522
|
+
.map((artifact) => artifact.path)
|
|
523
|
+
.filter((artifactPath) => !evidenceRefs.some((ref) => ref.includes(artifactPath)));
|
|
524
|
+
if (missingArtifacts.length > 0) {
|
|
525
|
+
return {
|
|
526
|
+
ok: false,
|
|
527
|
+
reason_code: "artifact_mismatch",
|
|
528
|
+
reason: `Step ${input.step.step_id} did not prove expected artifact evidence.`,
|
|
529
|
+
uncovered_clauses: missingArtifacts,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (expected === "code_artifact") {
|
|
534
|
+
const mutationSignal = toolCalls.some((call) => /write|edit|patch|create|apply|safe_edit/i.test(call.tool)) || evidenceRefs.length > 0;
|
|
535
|
+
if (mutationSignal && ARTIFACT_STUB_PATTERN.test(resultContractText(input.result))) {
|
|
536
|
+
return {
|
|
537
|
+
ok: false,
|
|
538
|
+
reason_code: "coder_artifact_stub",
|
|
539
|
+
reason: `Step ${input.step.step_id} reported a stub or placeholder artifact instead of a real implementation.`,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
const forbiddenPatterns = input.contract_step.forbidden_patterns ?? [];
|
|
544
|
+
const outputText = resultContractText(input.result);
|
|
545
|
+
const forbiddenHit = forbiddenPatterns.find((pattern) => outputText.includes(pattern));
|
|
546
|
+
if (forbiddenHit) {
|
|
547
|
+
return {
|
|
548
|
+
ok: false,
|
|
549
|
+
reason_code: "forbidden_pattern",
|
|
550
|
+
reason: `Step ${input.step.step_id} output matched a forbidden pattern.`,
|
|
551
|
+
uncovered_clauses: [forbiddenHit],
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
const missingEvidence = (input.contract_step.required_evidence_refs ?? [])
|
|
555
|
+
.filter((required) => !evidenceRefs.some((ref) => ref.includes(required)));
|
|
556
|
+
if (missingEvidence.length > 0) {
|
|
557
|
+
return {
|
|
558
|
+
ok: false,
|
|
559
|
+
reason_code: "required_evidence_missing",
|
|
560
|
+
reason: `Step ${input.step.step_id} is missing required evidence refs.`,
|
|
561
|
+
uncovered_clauses: missingEvidence,
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return { ok: true };
|
|
565
|
+
}
|
|
566
|
+
export function verifyIntentAgainstContract(input) {
|
|
567
|
+
const contract = isAcceptanceTraceContract(input.intent_contract)
|
|
568
|
+
? input.intent_contract
|
|
569
|
+
: loadAcceptanceTraceContractImpl(input.plan.plan_id);
|
|
570
|
+
if (!contract) {
|
|
571
|
+
if (requiresContract(input.step)) {
|
|
572
|
+
return {
|
|
573
|
+
outcome: "revisit_step",
|
|
574
|
+
reason: `No acceptance trace contract available for ${input.plan.plan_id}.`,
|
|
575
|
+
reason_code: "contract_missing",
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
outcome: "ok",
|
|
580
|
+
reason: `No acceptance trace contract available for ${input.plan.plan_id}.`,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
const contractStep = contract.steps.find((candidate) => candidate.step_id === input.step.step_id);
|
|
584
|
+
if (!contractStep) {
|
|
585
|
+
return {
|
|
586
|
+
outcome: requiresContract(input.step) ? "revisit_step" : "ok",
|
|
587
|
+
reason: `No acceptance trace step recorded for ${input.step.step_id}.`,
|
|
588
|
+
reason_code: requiresContract(input.step) ? "contract_missing" : undefined,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
const classValidation = validateContractClass({
|
|
592
|
+
step: input.step,
|
|
593
|
+
result: input.result,
|
|
594
|
+
contract_step: contractStep,
|
|
595
|
+
});
|
|
596
|
+
if (!classValidation.ok) {
|
|
597
|
+
return {
|
|
598
|
+
outcome: "revisit_step",
|
|
599
|
+
reason: classValidation.reason ?? `Step ${input.step.step_id} failed contract-class validation.`,
|
|
600
|
+
reason_code: classValidation.reason_code,
|
|
601
|
+
uncovered_clauses: classValidation.uncovered_clauses,
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
const acceptanceCriteria = (contractStep?.acceptance_criteria ?? []).filter((criterion) => typeof criterion === "string" && criterion.trim().length > 0);
|
|
605
|
+
if (acceptanceCriteria.length === 0) {
|
|
606
|
+
return {
|
|
607
|
+
outcome: "ok",
|
|
608
|
+
reason: `No acceptance criteria recorded for ${input.step.step_id}.`,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
const uiTopicValidation = validateUiTopicAnchor({
|
|
612
|
+
step: input.step,
|
|
613
|
+
result: input.result,
|
|
614
|
+
contract_step: contractStep,
|
|
615
|
+
});
|
|
616
|
+
if (!uiTopicValidation.ok) {
|
|
617
|
+
return {
|
|
618
|
+
outcome: "revisit_step",
|
|
619
|
+
reason: uiTopicValidation.reason ?? `Step ${input.step.step_id} drifted off topic.`,
|
|
620
|
+
reason_code: uiTopicValidation.reason_code,
|
|
621
|
+
uncovered_clauses: uiTopicValidation.uncovered_clauses,
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
const haystack = [
|
|
625
|
+
input.step.task,
|
|
626
|
+
input.result.summary,
|
|
627
|
+
JSON.stringify(input.result.tool_calls ?? []),
|
|
628
|
+
JSON.stringify(input.result.child_results ?? []),
|
|
629
|
+
].join("\n");
|
|
630
|
+
const uncovered = acceptanceCriteria.filter((criterion) => !intentCriterionSatisfied(haystack, criterion));
|
|
631
|
+
if (uncovered.length === 0) {
|
|
632
|
+
return {
|
|
633
|
+
outcome: "ok",
|
|
634
|
+
reason: `Step ${input.step.step_id} satisfied persisted acceptance criteria.`,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
outcome: "revisit_step",
|
|
639
|
+
reason: `Step ${input.step.step_id} did not satisfy acceptance criteria yet.`,
|
|
640
|
+
reason_code: "contract_invalid",
|
|
641
|
+
uncovered_clauses: uncovered,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
279
644
|
async function tryVericifyPacket(factory, onWarning) {
|
|
280
645
|
try {
|
|
281
646
|
return await factory();
|
|
@@ -771,6 +1136,56 @@ export function registerAgentTools(server) {
|
|
|
771
1136
|
],
|
|
772
1137
|
};
|
|
773
1138
|
});
|
|
1139
|
+
server.tool("propose_plan", "Use the ACE Planner role to decompose a task into a multi-step PlanProposal with intent_summary, success_criteria, per-step acceptance_criteria, and explicit stop conditions. On model failure, falls back to the deterministic goal compiler scaffold.", {
|
|
1140
|
+
task: z.string().describe("The task to decompose into a plan"),
|
|
1141
|
+
session_id: z.string().optional().describe("Optional session ID for transition record linkage"),
|
|
1142
|
+
}, async ({ task, session_id }) => {
|
|
1143
|
+
const proposal = await proposePlanImpl(task, session_id);
|
|
1144
|
+
return {
|
|
1145
|
+
content: [
|
|
1146
|
+
{
|
|
1147
|
+
type: "text",
|
|
1148
|
+
text: JSON.stringify(proposal, null, 2),
|
|
1149
|
+
},
|
|
1150
|
+
],
|
|
1151
|
+
};
|
|
1152
|
+
});
|
|
1153
|
+
server.tool("validate_plan", "Run shape checks against a PlanProposal (coverage, verification chain, tool-scope realism, acceptance criteria presence, stop conditions visibility). Returns { ok, score, blocking_findings, soft_findings }. Persists a transition record and Vericify process post.", {
|
|
1154
|
+
plan_id: z.string().optional().describe("ID of a previously proposed plan to validate"),
|
|
1155
|
+
proposal: z
|
|
1156
|
+
.object({
|
|
1157
|
+
plan_id: z.string(),
|
|
1158
|
+
status: z.literal("planning"),
|
|
1159
|
+
intent_summary: z.string(),
|
|
1160
|
+
success_criteria: z.array(z.string()),
|
|
1161
|
+
steps: z.array(z.object({
|
|
1162
|
+
role: z.string(),
|
|
1163
|
+
task: z.string(),
|
|
1164
|
+
depends_on: z.array(z.string()).optional(),
|
|
1165
|
+
tool_scope: z.array(z.string()).optional(),
|
|
1166
|
+
acceptance_criteria: z.array(z.string()),
|
|
1167
|
+
stop_condition: z.array(z.string()).optional(),
|
|
1168
|
+
})),
|
|
1169
|
+
plan_source: z.string(),
|
|
1170
|
+
})
|
|
1171
|
+
.optional()
|
|
1172
|
+
.describe("Inline proposal to validate; mutually exclusive with plan_id"),
|
|
1173
|
+
session_id: z.string().optional().describe("Optional session ID for transition record linkage"),
|
|
1174
|
+
}, async ({ plan_id, proposal, session_id }) => {
|
|
1175
|
+
const result = await validatePlanImpl({
|
|
1176
|
+
plan_id,
|
|
1177
|
+
proposal: proposal,
|
|
1178
|
+
sessionId: session_id,
|
|
1179
|
+
});
|
|
1180
|
+
return {
|
|
1181
|
+
content: [
|
|
1182
|
+
{
|
|
1183
|
+
type: "text",
|
|
1184
|
+
text: JSON.stringify(result, null, 2),
|
|
1185
|
+
},
|
|
1186
|
+
],
|
|
1187
|
+
};
|
|
1188
|
+
});
|
|
774
1189
|
server.tool("run_local_model", "Offload a governed ACE subtask to the provider-backed ACE bridge and return the result", {
|
|
775
1190
|
task: z.string().describe("Task to execute with the ACE model bridge"),
|
|
776
1191
|
role: ROLE_ENUM.optional().describe("Optional ACE role; defaults to orchestrator"),
|
|
@@ -792,6 +1207,10 @@ export function registerAgentTools(server) {
|
|
|
792
1207
|
.string()
|
|
793
1208
|
.optional()
|
|
794
1209
|
.describe("Optional model override; otherwise discovered from workspace/runtime context"),
|
|
1210
|
+
model_class: z
|
|
1211
|
+
.enum(["frontier", "mid", "small_local"])
|
|
1212
|
+
.optional()
|
|
1213
|
+
.describe("Optional capability class override; provider name alone is not used as capability authority"),
|
|
795
1214
|
base_url: z
|
|
796
1215
|
.string()
|
|
797
1216
|
.optional()
|
|
@@ -808,13 +1227,14 @@ export function registerAgentTools(server) {
|
|
|
808
1227
|
.string()
|
|
809
1228
|
.optional()
|
|
810
1229
|
.describe("Optional workspace root override; defaults to the active workspace"),
|
|
811
|
-
}, async ({ task, role, max_turns, tier, provider, model, base_url, ollama_url, tool_scope, workspace_root, }) => {
|
|
1230
|
+
}, async ({ task, role, max_turns, tier, provider, model, model_class, base_url, ollama_url, tool_scope, workspace_root, }) => {
|
|
812
1231
|
const delegated = await runLocalModelTask({
|
|
813
1232
|
task,
|
|
814
1233
|
role,
|
|
815
1234
|
workspaceRoot: workspace_root,
|
|
816
1235
|
provider,
|
|
817
1236
|
model,
|
|
1237
|
+
modelClass: model_class,
|
|
818
1238
|
baseUrl: base_url,
|
|
819
1239
|
ollamaUrl: ollama_url,
|
|
820
1240
|
maxTurns: max_turns,
|
|
@@ -830,6 +1250,9 @@ export function registerAgentTools(server) {
|
|
|
830
1250
|
`- role: ${delegated.role}`,
|
|
831
1251
|
`- provider: ${delegated.runtime.provider}`,
|
|
832
1252
|
`- model: ${delegated.runtime.model}`,
|
|
1253
|
+
`- model_class: ${delegated.policy.model_class}`,
|
|
1254
|
+
`- tier: ${delegated.policy.tier}`,
|
|
1255
|
+
`- mutation_lane: ${delegated.policy.mutation_lane}`,
|
|
833
1256
|
`- workspace: ${delegated.runtime.workspaceRoot}`,
|
|
834
1257
|
`- status: ${delegated.result.status}`,
|
|
835
1258
|
`- turns: ${delegated.result.turns}`,
|
|
@@ -860,7 +1283,7 @@ export function registerAgentTools(server) {
|
|
|
860
1283
|
],
|
|
861
1284
|
};
|
|
862
1285
|
});
|
|
863
|
-
server.tool("run_orchestrator", "Execute a supervised plan via model bridge child runs; when steps are omitted, the plan
|
|
1286
|
+
server.tool("run_orchestrator", "Execute a supervised plan via model bridge child runs; when steps are omitted, the plan is compiled through the deterministic goal scaffold first", {
|
|
864
1287
|
task: z.string().describe("The task to decompose and execute"),
|
|
865
1288
|
steps: z
|
|
866
1289
|
.array(z.object({
|
|
@@ -878,9 +1301,44 @@ export function registerAgentTools(server) {
|
|
|
878
1301
|
.array(z.string())
|
|
879
1302
|
.optional()
|
|
880
1303
|
.describe("Optional ACE tool allowlist for the step"),
|
|
1304
|
+
expected_output_class: z
|
|
1305
|
+
.enum(["plain_text_plan", "tool_envelope", "code_artifact", "structural_edit_plan", "qa_verdict"])
|
|
1306
|
+
.optional()
|
|
1307
|
+
.describe("Optional expected output contract class for intent verification"),
|
|
1308
|
+
expected_artifacts: z
|
|
1309
|
+
.array(z.object({
|
|
1310
|
+
path: z.string(),
|
|
1311
|
+
required: z.boolean().optional(),
|
|
1312
|
+
evidence_ref_kind: z.enum(["artifact", "diff", "hash", "test", "gate"]).optional(),
|
|
1313
|
+
}))
|
|
1314
|
+
.optional()
|
|
1315
|
+
.describe("Optional artifact evidence expected from this step"),
|
|
1316
|
+
allowed_tools: z
|
|
1317
|
+
.array(z.string())
|
|
1318
|
+
.optional()
|
|
1319
|
+
.describe("Optional stricter tool allowlist for contract verification"),
|
|
1320
|
+
forbidden_patterns: z
|
|
1321
|
+
.array(z.string())
|
|
1322
|
+
.optional()
|
|
1323
|
+
.describe("Optional forbidden output substrings for contract verification"),
|
|
1324
|
+
required_evidence_refs: z
|
|
1325
|
+
.array(z.string())
|
|
1326
|
+
.optional()
|
|
1327
|
+
.describe("Optional evidence ref substrings required from this step"),
|
|
1328
|
+
structural_edit_plan_required: z
|
|
1329
|
+
.boolean()
|
|
1330
|
+
.optional()
|
|
1331
|
+
.describe("Require this code-mutating step to route through a structural edit plan"),
|
|
1332
|
+
structural_edit_waiver: z
|
|
1333
|
+
.object({
|
|
1334
|
+
reason: z.string(),
|
|
1335
|
+
evidence_ref: z.string(),
|
|
1336
|
+
})
|
|
1337
|
+
.optional()
|
|
1338
|
+
.describe("Evidence-backed waiver when a code-mutating step cannot use structural edits"),
|
|
881
1339
|
}))
|
|
882
1340
|
.optional()
|
|
883
|
-
.describe("Pre-defined steps; if omitted, the orchestrator
|
|
1341
|
+
.describe("Pre-defined steps; if omitted, the orchestrator compiles a deterministic goal scaffold first"),
|
|
884
1342
|
execution_mode: z
|
|
885
1343
|
.enum(["sequential", "scheduled"])
|
|
886
1344
|
.optional()
|
|
@@ -936,13 +1394,15 @@ export function registerAgentTools(server) {
|
|
|
936
1394
|
steps: normalization.steps,
|
|
937
1395
|
execution_mode: execution_mode ?? "sequential",
|
|
938
1396
|
});
|
|
939
|
-
|
|
1397
|
+
let traceArtifactPath = await persistAcceptanceTraceMap({
|
|
940
1398
|
plan,
|
|
941
1399
|
task,
|
|
942
1400
|
planSource,
|
|
943
1401
|
insertedResearch: normalization.insertedResearch,
|
|
944
1402
|
shipFanoutEnabled: normalization.shipFanoutEnabled,
|
|
1403
|
+
normalization,
|
|
945
1404
|
});
|
|
1405
|
+
const intentContract = loadAcceptanceTraceContractImpl(plan.plan_id);
|
|
946
1406
|
const bridge = runtime
|
|
947
1407
|
? new ModelBridge(createDefaultModelBridgeClients(runtime))
|
|
948
1408
|
: undefined;
|
|
@@ -971,9 +1431,10 @@ export function registerAgentTools(server) {
|
|
|
971
1431
|
async spawnStep(step) {
|
|
972
1432
|
if (bridge && runtime) {
|
|
973
1433
|
return bridge.spawn({
|
|
974
|
-
task: step
|
|
1434
|
+
task: formatStepTaskForBridge(step),
|
|
975
1435
|
role: step.role,
|
|
976
1436
|
workspace: runtime.workspaceRoot,
|
|
1437
|
+
tier: resolveTier(undefined, runtime.provider, runtime.model, step.role),
|
|
977
1438
|
maxTurns: max_turns_per_step ?? 6,
|
|
978
1439
|
provider: runtime.provider,
|
|
979
1440
|
model: runtime.model,
|
|
@@ -1018,11 +1479,23 @@ export function registerAgentTools(server) {
|
|
|
1018
1479
|
: step.status === "blocked"
|
|
1019
1480
|
? "step_blocked"
|
|
1020
1481
|
: "step_failed");
|
|
1021
|
-
|
|
1482
|
+
const amendment = buildDefaultOrchestratorAmendment({
|
|
1022
1483
|
plan: activePlan,
|
|
1023
1484
|
step,
|
|
1024
1485
|
result,
|
|
1025
1486
|
});
|
|
1487
|
+
if (!amendment)
|
|
1488
|
+
return undefined;
|
|
1489
|
+
const amendedPlan = amendTaskPlan(activePlan, amendment);
|
|
1490
|
+
traceArtifactPath = await persistAcceptanceTraceMap({
|
|
1491
|
+
plan: amendedPlan,
|
|
1492
|
+
task,
|
|
1493
|
+
planSource,
|
|
1494
|
+
insertedResearch: normalization.insertedResearch,
|
|
1495
|
+
shipFanoutEnabled: normalization.shipFanoutEnabled,
|
|
1496
|
+
normalization,
|
|
1497
|
+
});
|
|
1498
|
+
return amendedPlan;
|
|
1026
1499
|
},
|
|
1027
1500
|
async getVericifyContext() {
|
|
1028
1501
|
return tryVericifyPacket(() => getVericifyContextPacket({
|
|
@@ -1035,6 +1508,34 @@ export function registerAgentTools(server) {
|
|
|
1035
1508
|
workspaceRoot: effectiveWorkspaceRoot,
|
|
1036
1509
|
}), (message) => appendUniqueNote(vericifyWarnings, `Vericify delta unavailable for ${plan.plan_id}: ${message}`));
|
|
1037
1510
|
},
|
|
1511
|
+
async verifyIntent({ plan: activePlan, step, result, intent_contract }) {
|
|
1512
|
+
const verification = verifyIntentAgainstContract({
|
|
1513
|
+
plan: activePlan,
|
|
1514
|
+
step,
|
|
1515
|
+
result,
|
|
1516
|
+
intent_contract: intent_contract ?? loadAcceptanceTraceContractImpl(activePlan.plan_id) ?? intentContract,
|
|
1517
|
+
});
|
|
1518
|
+
// Transition recording is deferred to recordIntentVerificationFailure so
|
|
1519
|
+
// the supervisor can supply the correct from/to based on retry state.
|
|
1520
|
+
return verification;
|
|
1521
|
+
},
|
|
1522
|
+
async replanForClauses({ uncovered_clauses }) {
|
|
1523
|
+
if (!uncovered_clauses.length) {
|
|
1524
|
+
return undefined;
|
|
1525
|
+
}
|
|
1526
|
+
return {
|
|
1527
|
+
append_steps: [
|
|
1528
|
+
{
|
|
1529
|
+
role: "research",
|
|
1530
|
+
task: `Resolve uncovered acceptance clauses: ${uncovered_clauses.join("; ")}`,
|
|
1531
|
+
tool_scope: ["recall_context", "read_workspace_file", "build_continuity_packet"],
|
|
1532
|
+
},
|
|
1533
|
+
],
|
|
1534
|
+
};
|
|
1535
|
+
},
|
|
1536
|
+
async recordIntentVerificationFailure({ step, verification, from, to }) {
|
|
1537
|
+
await appendSessionPlanTransition(step.step_id, from, to, verification.reason, verification.reason_code);
|
|
1538
|
+
},
|
|
1038
1539
|
async openCircuitBreaker(reason) {
|
|
1039
1540
|
await executeAceInternalTool("open_circuit_breaker", {
|
|
1040
1541
|
reason,
|
|
@@ -1076,6 +1577,14 @@ export function registerAgentTools(server) {
|
|
|
1076
1577
|
}, sessionId);
|
|
1077
1578
|
},
|
|
1078
1579
|
});
|
|
1580
|
+
traceArtifactPath = await persistAcceptanceTraceMap({
|
|
1581
|
+
plan: supervised.plan,
|
|
1582
|
+
task,
|
|
1583
|
+
planSource,
|
|
1584
|
+
insertedResearch: normalization.insertedResearch,
|
|
1585
|
+
shipFanoutEnabled: normalization.shipFanoutEnabled,
|
|
1586
|
+
normalization,
|
|
1587
|
+
});
|
|
1079
1588
|
const step_summaries = supervised.plan.steps.map((step) => ({
|
|
1080
1589
|
step_id: step.step_id,
|
|
1081
1590
|
role: step.role,
|
|
@@ -1096,8 +1605,8 @@ export function registerAgentTools(server) {
|
|
|
1096
1605
|
runtime_warnings: runtimeWarnings,
|
|
1097
1606
|
workspace_root: effectiveWorkspaceRoot,
|
|
1098
1607
|
plan_source: planSource,
|
|
1099
|
-
planning_note: planSource === "orchestrator_default_step"
|
|
1100
|
-
? "Auto-planning
|
|
1608
|
+
planning_note: planSource === "orchestrator_default_step" || planSource === "deterministic_fallback" || planSource === "planner_model"
|
|
1609
|
+
? "Auto-planning now starts with a deterministic goal compiler scaffold; planner_model means the scaffold was refined."
|
|
1101
1610
|
: normalization.insertedResearch
|
|
1102
1611
|
? "Research was inserted ahead of spec work to require source-backed evidence before specification."
|
|
1103
1612
|
: normalization.shipFanoutEnabled
|
|
@@ -1114,6 +1623,7 @@ export function registerAgentTools(server) {
|
|
|
1114
1623
|
job_ids: supervised.job_ids,
|
|
1115
1624
|
circuit_opened: supervised.circuit_opened,
|
|
1116
1625
|
final_gate: supervised.final_gate ?? null,
|
|
1626
|
+
plan_validation_verdict: normalization.validationVerdict ?? null,
|
|
1117
1627
|
vericify_warnings: vericifyWarnings,
|
|
1118
1628
|
}, null, 2),
|
|
1119
1629
|
},
|