create-agdf 0.14.1 → 0.14.2
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/README.md +8 -2
- package/generated/plugins/agdf/.claude-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/plugins/agdf/meta/agdf-plugin.definition.json +6 -1
- package/generated/plugins/agdf/meta/copilot-payload-baseline.json +7 -0
- package/generated/plugins/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +6 -1
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/local-validator.js +29 -17
- package/generated/plugins/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +50 -1
- package/generated/plugins/agdf/runtime/create-agdf/package.json +1 -1
- package/generated/plugins/agdf/runtime/runtime-manifest.json +2 -2
- package/generated/plugins/agdf/scripts/check-runtime-integrity.mjs +2 -1
- package/generated/plugins/agdf/submission/openai/availability.md +1 -1
- package/generated/plugins/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/plugins/agdf/submission/openai/release-notes.md +1 -1
- package/generated/plugins/agdf/submission/openai/reviewer-cases.json +1 -1
- package/generated/plugins/copilot/agdf/.agdf-payload-inventory.json +771 -0
- package/generated/plugins/copilot/agdf/LICENSE +201 -0
- package/generated/plugins/copilot/agdf/meta/agdf-agent-router.md +129 -0
- package/generated/plugins/copilot/agdf/meta/agdf-constitution.md +329 -0
- package/generated/plugins/copilot/agdf/meta/agdf-plugin.definition.json +331 -0
- package/generated/plugins/copilot/agdf/meta/agdf-runtime-contract.md +16 -0
- package/generated/plugins/{agdf → copilot/agdf}/plugin.json +1 -1
- package/generated/plugins/copilot/agdf/runtime/agdf-local.js +18 -0
- package/generated/plugins/copilot/agdf/runtime/agdf-session-check.js +97 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/NOTICE +10 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/bin/agdf-validator.js +11 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-interaction-locales.json +512 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/generated/plugins/agdf/meta/agdf-plugin.definition.json +331 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/command-registry.js +144 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/delivery-path-search-command.js +139 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/parse-args.js +173 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/runtime-context.js +83 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/cli/validation-handlers.js +46 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/delivery-map.js +301 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/doctor.js +272 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-check.js +455 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/gate-policy.js +305 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/parent-reconciliation.js +284 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/required-files.js +9 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/run-state.js +217 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/shared.js +345 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-evaluation/verified-change.js +156 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/aggregate.js +18 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/gate-approval-validator.js +49 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/index.js +7 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/legacy-migration.js +95 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-parser.js +265 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-repository.js +68 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-resolver.js +45 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/control-state/run-state-writer.js +97 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/candidate-policy.js +99 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/contracts.js +237 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/claude.js +35 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/codex.js +44 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/opencode.js +234 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/prompt.js +13 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/evaluators/protocol.js +13 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/claude.js +40 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/codex.js +38 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/generators/protocol.js +12 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js +39 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/scoring.js +21 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js +155 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/state-adapter.js +161 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/surfaces/capabilities.js +27 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/transports/read-only-guard.js +43 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/interaction-presentation.js +842 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/local-validator.js +239 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/plugin-provenance.js +255 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime/validator-application.js +40 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/runtime-check-consent/contract.js +73 -0
- package/generated/plugins/copilot/agdf/runtime/create-agdf/package.json +6 -0
- package/generated/plugins/copilot/agdf/runtime/runtime-manifest.json +8 -0
- package/generated/submissions/openai/agdf/.codex-plugin/plugin.json +1 -1
- package/generated/submissions/openai/agdf/meta/agdf-plugin.definition.json +6 -1
- package/generated/submissions/openai/agdf/meta/copilot-payload-baseline.json +7 -0
- package/generated/submissions/openai/agdf/submission/openai/availability.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/capability-matrix.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/inventory.json +13 -9
- package/generated/submissions/openai/agdf/submission/openai/listing.json +1 -1
- package/generated/submissions/openai/agdf/submission/openai/readiness.json +2 -2
- package/generated/submissions/openai/agdf/submission/openai/readiness.md +2 -2
- package/generated/submissions/openai/agdf/submission/openai/release-notes.md +1 -1
- package/generated/submissions/openai/agdf/submission/openai/reviewer-cases.json +1 -1
- package/lib/installers/local-marketplace.js +112 -30
- package/lib/installers/plugin-installers.js +45 -9
- package/lib/public-plugin/copilot-profile.js +126 -0
- package/lib/release/version-coherence.js +4 -1
- package/lib/runtime/local-validator.js +29 -17
- package/lib/runtime/plugin-provenance.js +50 -1
- package/package.json +3 -2
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-brownfield-analysis/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-clean-implementation-review/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-code-review/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-delivery-closeout/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-delivery-path-search/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-gate-check/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-interaction-locales.json +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-qa-gate/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-release-or/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-runtime-contract.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-task-plan-review/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/agdf-ux-intent-definition/SKILL.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/closeout.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/context-graph.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/control-scaffold.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/gate-transition.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/interaction.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/modes.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/quality.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/copilot-skills/contracts/task-target-resolution.md +0 -0
- /package/generated/plugins/{agdf → copilot/agdf}/hooks/copilot-hooks.json +0 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { validateEvaluation } from "../contracts.js";
|
|
4
|
+
import { guardedExecFileSync } from "../transports/read-only-guard.js";
|
|
5
|
+
import { buildEvaluatorPrompt } from "./prompt.js";
|
|
6
|
+
|
|
7
|
+
export const OPENCODE_EVALUATOR_AGENT = "agdf-evaluator";
|
|
8
|
+
|
|
9
|
+
export const OPENCODE_DENY_PERMISSIONS = Object.freeze({
|
|
10
|
+
"*": "deny",
|
|
11
|
+
read: "deny",
|
|
12
|
+
edit: "deny",
|
|
13
|
+
bash: "deny",
|
|
14
|
+
task: "deny",
|
|
15
|
+
webfetch: "deny",
|
|
16
|
+
websearch: "deny",
|
|
17
|
+
skill: "deny",
|
|
18
|
+
lsp: "deny",
|
|
19
|
+
todowrite: "deny",
|
|
20
|
+
question: "deny",
|
|
21
|
+
external_directory: "deny",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const executablePermissions = Object.freeze([
|
|
25
|
+
"edit", "bash", "task", "webfetch", "websearch", "skill", "lsp", "todowrite", "question",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
export function openCodePermissionEnvironment(env = process.env) {
|
|
29
|
+
return {
|
|
30
|
+
...env,
|
|
31
|
+
OPENCODE_PERMISSION: JSON.stringify(OPENCODE_DENY_PERMISSIONS),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function preflightFailure(code, detail, evidence = []) {
|
|
36
|
+
return {
|
|
37
|
+
surface: "opencode",
|
|
38
|
+
status: "failed",
|
|
39
|
+
code,
|
|
40
|
+
detail,
|
|
41
|
+
evidence,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function extractAgentPermissions(output, agentName) {
|
|
46
|
+
const marker = `${agentName} (`;
|
|
47
|
+
const lines = output.split(/\r?\n/);
|
|
48
|
+
const start = lines.findIndex((line) => line.startsWith(marker));
|
|
49
|
+
if (start < 0) return null;
|
|
50
|
+
const mode = lines[start].slice(marker.length).split(")")[0];
|
|
51
|
+
let jsonStart = -1;
|
|
52
|
+
let depth = 0;
|
|
53
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
54
|
+
const line = lines[index].trim();
|
|
55
|
+
if (jsonStart < 0 && line === "[") {
|
|
56
|
+
jsonStart = index;
|
|
57
|
+
depth = 1;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (jsonStart >= 0) {
|
|
61
|
+
for (const character of line) {
|
|
62
|
+
if (character === "[") depth += 1;
|
|
63
|
+
if (character === "]") depth -= 1;
|
|
64
|
+
}
|
|
65
|
+
if (depth === 0) return { mode, permissions: JSON.parse(lines.slice(jsonStart, index + 1).join("\n")) };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function terminalPermissionDecision(permissions, permissionName) {
|
|
72
|
+
let decision = null;
|
|
73
|
+
for (const entry of permissions) {
|
|
74
|
+
if (entry?.pattern === "*" && (entry.permission === "*" || entry.permission === permissionName)) {
|
|
75
|
+
decision = entry.action;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return decision;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function preflightOpenCodeEvaluator(options = {}) {
|
|
82
|
+
const executable = options.openCodeBin ?? process.env.AGDF_OPENCODE_BIN ?? "opencode";
|
|
83
|
+
const run = options.execFileSync ?? execFileSync;
|
|
84
|
+
const cwd = options.cwd ?? process.cwd();
|
|
85
|
+
const agentName = options.agentName ?? OPENCODE_EVALUATOR_AGENT;
|
|
86
|
+
const env = openCodePermissionEnvironment(options.env ?? process.env);
|
|
87
|
+
let runtime;
|
|
88
|
+
let help;
|
|
89
|
+
let agents;
|
|
90
|
+
try {
|
|
91
|
+
runtime = run(executable, ["--version"], {
|
|
92
|
+
cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000,
|
|
93
|
+
}).trim();
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return preflightFailure("opencode_host_unavailable", (error.stderr || error.message).toString().trim());
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
if (options.execFileSync) {
|
|
99
|
+
help = run(executable, ["run", "--help"], {
|
|
100
|
+
cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000,
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
const helpProbe = (options.spawnSync ?? spawnSync)(executable, ["run", "--help"], {
|
|
104
|
+
cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 5000,
|
|
105
|
+
});
|
|
106
|
+
if (helpProbe.error || helpProbe.status !== 0) throw helpProbe.error ?? new Error(helpProbe.stderr || "OpenCode run help failed.");
|
|
107
|
+
help = `${helpProbe.stdout ?? ""}\n${helpProbe.stderr ?? ""}`;
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return preflightFailure("opencode_run_unavailable", (error.stderr || error.message).toString().trim(), [`opencode ${runtime}`]);
|
|
111
|
+
}
|
|
112
|
+
const requiredFlags = ["--pure", "--agent", "--format", "--dir"];
|
|
113
|
+
const missingFlags = requiredFlags.filter((flag) => !help.includes(flag));
|
|
114
|
+
if (missingFlags.length) {
|
|
115
|
+
return preflightFailure("opencode_run_capability_missing", `Missing required flags: ${missingFlags.join(", ")}`, [`opencode ${runtime}`]);
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
agents = run(executable, ["agent", "list", "--pure"], {
|
|
119
|
+
cwd, env, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 10000,
|
|
120
|
+
});
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return preflightFailure("opencode_agent_probe_failed", (error.stderr || error.message).toString().trim(), [`opencode ${runtime}`]);
|
|
123
|
+
}
|
|
124
|
+
let projection;
|
|
125
|
+
try {
|
|
126
|
+
projection = extractAgentPermissions(agents, agentName);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
return preflightFailure("opencode_agent_permissions_unparseable", error.message, [`opencode ${runtime}`]);
|
|
129
|
+
}
|
|
130
|
+
if (!projection) {
|
|
131
|
+
return preflightFailure("opencode_evaluator_agent_missing", `OpenCode agent ${agentName} is not available.`, [`opencode ${runtime}`]);
|
|
132
|
+
}
|
|
133
|
+
if (projection.mode !== "primary") {
|
|
134
|
+
return preflightFailure("opencode_evaluator_agent_incompatible", `OpenCode agent ${agentName} must be primary for opencode run --agent; observed ${projection.mode || "unknown"}.`, [
|
|
135
|
+
`opencode ${runtime}`,
|
|
136
|
+
`agent:${agentName}`,
|
|
137
|
+
]);
|
|
138
|
+
}
|
|
139
|
+
const unsafe = executablePermissions.filter((permission) => terminalPermissionDecision(projection.permissions, permission) !== "deny");
|
|
140
|
+
if (unsafe.length) {
|
|
141
|
+
return preflightFailure("opencode_evaluator_permissions_unsafe", `Terminal deny is not effective for: ${unsafe.join(", ")}`, [
|
|
142
|
+
`opencode ${runtime}`,
|
|
143
|
+
`agent:${agentName}`,
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
surface: "opencode",
|
|
148
|
+
status: "passed",
|
|
149
|
+
code: "opencode_evaluator_ready",
|
|
150
|
+
invocation_id: `${process.pid}-${Date.now()}`,
|
|
151
|
+
runtime,
|
|
152
|
+
agent: agentName,
|
|
153
|
+
evidence: [
|
|
154
|
+
`opencode ${runtime}`,
|
|
155
|
+
"opencode run --pure --agent agdf-evaluator --format json",
|
|
156
|
+
"effective executable permissions terminal-deny",
|
|
157
|
+
],
|
|
158
|
+
env,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function parseOpenCodeEvaluatorOutput(raw) {
|
|
163
|
+
const text = [];
|
|
164
|
+
for (const line of raw.split(/\r?\n/).filter(Boolean)) {
|
|
165
|
+
const event = JSON.parse(line);
|
|
166
|
+
if (event?.type === "error") {
|
|
167
|
+
const status = event.error?.data?.statusCode;
|
|
168
|
+
const detail = event.error?.data?.message || event.error?.message || "unknown OpenCode evaluator error";
|
|
169
|
+
const error = new Error(`OpenCode evaluator error${status ? ` (${status})` : ""}: ${detail}`);
|
|
170
|
+
error.code = status === 401 || /auth|credential|provider available/i.test(detail)
|
|
171
|
+
? "OPENCODE_EVALUATOR_AUTHENTICATION_FAILED"
|
|
172
|
+
: "OPENCODE_EVALUATOR_UNAVAILABLE";
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
if (event?.part?.type === "text" && typeof event.part.text === "string") text.push(event.part.text);
|
|
176
|
+
else if (event?.type === "text" && typeof event.text === "string") text.push(event.text);
|
|
177
|
+
}
|
|
178
|
+
if (!text.length) throw new Error("OpenCode evaluator returned no text event.");
|
|
179
|
+
if (text.length !== 1) throw new Error(`OpenCode evaluator returned ${text.length} text events; exactly one final payload is required.`);
|
|
180
|
+
return JSON.parse(text[0]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function fatalEvaluatorError(error) {
|
|
184
|
+
const wrapped = new Error(error.message);
|
|
185
|
+
wrapped.code = typeof error.code === "string" && error.code.startsWith("OPENCODE_EVALUATOR_")
|
|
186
|
+
? error.code
|
|
187
|
+
: error.code === "GENERATOR_MUTATION_DETECTED"
|
|
188
|
+
? "OPENCODE_EVALUATOR_MUTATION_DETECTED"
|
|
189
|
+
: error.code === "GENERATOR_TIMEOUT"
|
|
190
|
+
? "OPENCODE_EVALUATOR_TIMEOUT"
|
|
191
|
+
: error.code === "ENOENT"
|
|
192
|
+
? "OPENCODE_EVALUATOR_UNAVAILABLE"
|
|
193
|
+
: /auth|unauthorized|credential/i.test(`${error.message}\n${error.stderr ?? ""}`)
|
|
194
|
+
? "OPENCODE_EVALUATOR_AUTHENTICATION_FAILED"
|
|
195
|
+
: "OPENCODE_EVALUATOR_OUTPUT_INVALID";
|
|
196
|
+
wrapped.fatalEvaluator = true;
|
|
197
|
+
return wrapped;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function openCodeEvaluator(options = {}) {
|
|
201
|
+
if (options.preflight?.status !== "passed" || options.preflight.surface !== "opencode") {
|
|
202
|
+
throw new Error("OpenCode evaluator requires a successful current-invocation preflight.");
|
|
203
|
+
}
|
|
204
|
+
const executable = options.openCodeBin ?? process.env.AGDF_OPENCODE_BIN ?? "opencode";
|
|
205
|
+
const agentName = options.preflight.agent ?? OPENCODE_EVALUATOR_AGENT;
|
|
206
|
+
return {
|
|
207
|
+
name: "opencode",
|
|
208
|
+
metadata: {
|
|
209
|
+
name: "opencode",
|
|
210
|
+
runtime: options.preflight.runtime,
|
|
211
|
+
agent: agentName,
|
|
212
|
+
preflight: options.preflight.code,
|
|
213
|
+
},
|
|
214
|
+
async evaluate(input, candidate) {
|
|
215
|
+
const args = [
|
|
216
|
+
"run", "--pure", "--agent", agentName, "--format", "json", "--dir", options.cwd,
|
|
217
|
+
];
|
|
218
|
+
if (options.model) args.push("--model", options.model);
|
|
219
|
+
args.push(buildEvaluatorPrompt(input, candidate));
|
|
220
|
+
try {
|
|
221
|
+
const raw = guardedExecFileSync(executable, args, {
|
|
222
|
+
cwd: options.cwd,
|
|
223
|
+
env: options.preflight.env,
|
|
224
|
+
encoding: "utf8",
|
|
225
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
226
|
+
timeout: options.timeoutMs ?? 120000,
|
|
227
|
+
});
|
|
228
|
+
return validateEvaluation(parseOpenCodeEvaluatorOutput(raw), candidate.id);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
throw fatalEvaluatorError(error);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function buildEvaluatorPrompt(input, candidate) {
|
|
2
|
+
return [
|
|
3
|
+
"You are an evaluation-only component. Do not use tools, execute commands, inspect files, delegate work or modify state.",
|
|
4
|
+
`Return only one JSON object matching contract version ${input.contract_version}.`,
|
|
5
|
+
`Objective: ${input.objective}`,
|
|
6
|
+
`Current gate: ${input.current_gate}`,
|
|
7
|
+
`Candidate id: ${candidate.id}`,
|
|
8
|
+
`Candidate action: ${candidate.action}`,
|
|
9
|
+
"Score scope_fit, gate_readiness, risk_reduction, evidence_gain, testability, reversibility and cost from 0 to 5.",
|
|
10
|
+
"Cost 0 is low and 5 is high. uncertainty is 0 low to 5 high.",
|
|
11
|
+
"Keep rationale concise. child_actions must only contain actions permitted by the current gate.",
|
|
12
|
+
].join("\n");
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { validateEvaluation } from "../contracts.js";
|
|
2
|
+
|
|
3
|
+
export function fixtureEvaluator(evaluations) {
|
|
4
|
+
return {
|
|
5
|
+
name: "fixture",
|
|
6
|
+
metadata: { name: "fixture", runtime: "deterministic-test" },
|
|
7
|
+
async evaluate(_input, candidate) {
|
|
8
|
+
const value = evaluations[candidate.id];
|
|
9
|
+
if (!value) throw new Error(`missing fixture evaluation for ${candidate.id}`);
|
|
10
|
+
return validateEvaluation(value, candidate.id);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { generatorOutputSchema, validateGeneratorRequest, validateGeneratorResponse } from "../contracts.js";
|
|
3
|
+
import { guardedExecFileSync } from "../transports/read-only-guard.js";
|
|
4
|
+
|
|
5
|
+
export function claudeGenerator(options = {}) {
|
|
6
|
+
const claudeBin = options.claudeBin ?? "claude";
|
|
7
|
+
const execute = options.execute ?? guardedExecFileSync;
|
|
8
|
+
let runtime = options.runtime ?? "claude version unavailable";
|
|
9
|
+
if (!options.runtime) try { runtime = execFileSync(claudeBin, ["--version"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(); } catch {}
|
|
10
|
+
return {
|
|
11
|
+
name: "claude",
|
|
12
|
+
metadata: { name: "claude", runtime, model: options.model ?? "configured-default" },
|
|
13
|
+
async generate(requestValue) {
|
|
14
|
+
const request = validateGeneratorRequest(requestValue);
|
|
15
|
+
const prompt = [
|
|
16
|
+
"Generate bounded advisory delivery-path proposals. Do not use tools, execute commands, or modify files.",
|
|
17
|
+
"Return only JSON matching the supplied schema. Do not include hidden reasoning.",
|
|
18
|
+
`Normalized request: ${JSON.stringify(request)}`,
|
|
19
|
+
].join("\n");
|
|
20
|
+
const args = ["-p", "--disallowedTools", "Edit,Write,Bash", "--output-format", "json", "--json-schema", JSON.stringify(generatorOutputSchema())];
|
|
21
|
+
if (options.model) args.push("--model", options.model);
|
|
22
|
+
args.push(prompt);
|
|
23
|
+
let raw;
|
|
24
|
+
try {
|
|
25
|
+
raw = execute(claudeBin, args, { cwd: options.cwd, timeout: options.timeoutMs ?? request.budgets.max_duration_ms });
|
|
26
|
+
} catch (error) {
|
|
27
|
+
const providerOutput = String(error?.stdout ?? "");
|
|
28
|
+
if (/not logged in|unauthorized|authentication/i.test(providerOutput)) {
|
|
29
|
+
const authentication = new Error("claude generator authentication failed");
|
|
30
|
+
authentication.code = "GENERATOR_AUTHENTICATION_FAILED";
|
|
31
|
+
throw authentication;
|
|
32
|
+
}
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
const response = JSON.parse(raw);
|
|
36
|
+
if (response.is_error) throw new Error(`claude generator returned an error: ${response.result ?? "unknown error"}`);
|
|
37
|
+
return validateGeneratorResponse(JSON.parse(response.result));
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { generatorOutputSchema, validateGeneratorRequest, validateGeneratorResponse } from "../contracts.js";
|
|
6
|
+
import { guardedExecFileSync } from "../transports/read-only-guard.js";
|
|
7
|
+
|
|
8
|
+
export function codexGenerator(options = {}) {
|
|
9
|
+
const codexBin = options.codexBin ?? "codex";
|
|
10
|
+
const execute = options.execute ?? guardedExecFileSync;
|
|
11
|
+
let runtime = options.runtime ?? "codex version unavailable";
|
|
12
|
+
if (!options.runtime) try { runtime = execFileSync(codexBin, ["--version"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim(); } catch {}
|
|
13
|
+
return {
|
|
14
|
+
name: "codex",
|
|
15
|
+
metadata: { name: "codex", runtime, model: options.model ?? "configured-default" },
|
|
16
|
+
async generate(requestValue) {
|
|
17
|
+
const request = validateGeneratorRequest(requestValue);
|
|
18
|
+
const temp = mkdtempSync(join(tmpdir(), "agdf-dps-generator-"));
|
|
19
|
+
const schemaPath = join(temp, "schema.json");
|
|
20
|
+
const outputPath = join(temp, "output.json");
|
|
21
|
+
try {
|
|
22
|
+
writeFileSync(schemaPath, `${JSON.stringify(generatorOutputSchema(), null, 2)}\n`, "utf8");
|
|
23
|
+
const prompt = [
|
|
24
|
+
"Generate bounded advisory delivery-path proposals. Do not use tools, execute commands, or modify files.",
|
|
25
|
+
"Return only JSON matching the supplied schema. Do not include hidden reasoning.",
|
|
26
|
+
`Normalized request: ${JSON.stringify(request)}`,
|
|
27
|
+
].join("\n");
|
|
28
|
+
const args = ["exec", "--sandbox", "read-only", "--ephemeral", "--ignore-user-config", "--output-schema", schemaPath, "--output-last-message", outputPath, "--color", "never"];
|
|
29
|
+
if (options.model) args.push("--model", options.model);
|
|
30
|
+
args.push(prompt);
|
|
31
|
+
execute(codexBin, args, { cwd: options.cwd, timeout: options.timeoutMs ?? request.budgets.max_duration_ms });
|
|
32
|
+
return validateGeneratorResponse(JSON.parse(readFileSync(outputPath, "utf8")));
|
|
33
|
+
} finally {
|
|
34
|
+
rmSync(temp, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { validateGeneratorRequest, validateGeneratorResponse } from "../contracts.js";
|
|
2
|
+
|
|
3
|
+
export function fixtureGenerator(response, metadata = { name: "fixture-generator" }) {
|
|
4
|
+
return {
|
|
5
|
+
name: metadata.name,
|
|
6
|
+
metadata,
|
|
7
|
+
async generate(request) {
|
|
8
|
+
validateGeneratorRequest(request);
|
|
9
|
+
return validateGeneratorResponse(response);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/persistence.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
function safeResult(result) {
|
|
5
|
+
return JSON.parse(JSON.stringify(result, (key, value) => {
|
|
6
|
+
if (/(prompt|hidden_reasoning|secret|credential|source_snapshot)/i.test(key)) return undefined;
|
|
7
|
+
return value;
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function persistSearchResult(targetDir, result) {
|
|
12
|
+
const root = join(targetDir, ".agdf", "control", "artefacts", result.scope_key);
|
|
13
|
+
const jsonPath = join(root, "DELIVERY_PATH_SEARCH.json");
|
|
14
|
+
const markdownPath = join(root, "DELIVERY_PATH_SEARCH.md");
|
|
15
|
+
mkdirSync(dirname(jsonPath), { recursive: true });
|
|
16
|
+
const safe = safeResult(result);
|
|
17
|
+
writeFileSync(jsonPath, `${JSON.stringify(safe, null, 2)}\n`, "utf8");
|
|
18
|
+
const recommendation = safe.recommendation?.action ?? "No safe recommendation";
|
|
19
|
+
writeFileSync(markdownPath, [
|
|
20
|
+
"# Delivery Path Search Decision",
|
|
21
|
+
"",
|
|
22
|
+
`- status: ${safe.status}`,
|
|
23
|
+
`- current_gate: ${safe.current_gate}`,
|
|
24
|
+
`- enforcement: ${safe.enforcement.level}`,
|
|
25
|
+
`- recommendation: ${recommendation}`,
|
|
26
|
+
`- stopping_reason: ${safe.stopping_reason}`,
|
|
27
|
+
`- evaluations: ${safe.budgets.evaluations}`,
|
|
28
|
+
`- generation_status: ${safe.generation?.status ?? "legacy_deterministic"}`,
|
|
29
|
+
`- generated_candidates: ${safe.generation ? `${safe.generation.accepted}/${safe.generation.returned}` : "0/0"}`,
|
|
30
|
+
`- generation_cost_units: ${safe.generation?.cost_units ?? 0}`,
|
|
31
|
+
`- generation_duration_ms: ${safe.generation?.duration_ms ?? 0}`,
|
|
32
|
+
...(safe.generation?.failure_code ? [`- generation_failure: ${safe.generation.failure_code}`] : []),
|
|
33
|
+
`- next_gate_action: ${safe.next_gate_action}`,
|
|
34
|
+
"",
|
|
35
|
+
"This summary contains model judgements, not measured facts. Canonical AGDF gate-check remains authoritative.",
|
|
36
|
+
"",
|
|
37
|
+
].join("\n"), "utf8");
|
|
38
|
+
return { jsonPath, markdownPath };
|
|
39
|
+
}
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/scoring.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SCORING_POLICY_VERSION } from "./contracts.js";
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_WEIGHTS = Object.freeze({
|
|
4
|
+
scope_fit: 3,
|
|
5
|
+
gate_readiness: 3,
|
|
6
|
+
risk_reduction: 2,
|
|
7
|
+
evidence_gain: 2,
|
|
8
|
+
testability: 2,
|
|
9
|
+
reversibility: 1,
|
|
10
|
+
cost: -1,
|
|
11
|
+
uncertainty: -2,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const enforcementPenalty = { full: 0, tool_enforced: 1, instruction_only: 4 };
|
|
15
|
+
|
|
16
|
+
export function scoreEvaluation(evaluation, enforcementLevel, weights = DEFAULT_WEIGHTS) {
|
|
17
|
+
const contributions = Object.fromEntries(Object.entries(weights).map(([key, weight]) => [key, evaluation[key] * weight]));
|
|
18
|
+
const penalty = enforcementPenalty[enforcementLevel] ?? 4;
|
|
19
|
+
const score = Object.values(contributions).reduce((sum, value) => sum + value, 0) - penalty;
|
|
20
|
+
return { score, contributions, enforcement_penalty: penalty, scoring_policy_version: SCORING_POLICY_VERSION };
|
|
21
|
+
}
|
package/generated/plugins/copilot/agdf/runtime/create-agdf/lib/delivery-path-search/search-engine.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { CONTRACT_VERSION, SCORING_POLICY_VERSION, validateEvaluation, validateSearchInput } from "./contracts.js";
|
|
2
|
+
import { candidateLegality, candidatesFromInput, generatedCandidatesFromResponse } from "./candidate-policy.js";
|
|
3
|
+
import { scoreEvaluation } from "./scoring.js";
|
|
4
|
+
import { generatorRequestFromInput } from "./state-adapter.js";
|
|
5
|
+
|
|
6
|
+
function generatorFailureCode(error) {
|
|
7
|
+
if (error?.code === "GENERATOR_MUTATION_DETECTED") return "generator_mutation_detected";
|
|
8
|
+
if (error?.code === "GENERATOR_TIMEOUT") return "generator_timeout";
|
|
9
|
+
if (error?.code === "GENERATOR_AUTHENTICATION_FAILED") return "generator_authentication_failed";
|
|
10
|
+
if (error?.code === "ENOENT") return "generator_unavailable";
|
|
11
|
+
if (/auth/i.test(error?.message ?? "")) return "generator_authentication_failed";
|
|
12
|
+
if (/context|disallowed/i.test(error?.message ?? "")) return "generator_context_rejected";
|
|
13
|
+
if (/budget/i.test(error?.message ?? "")) return "generator_budget_exceeded";
|
|
14
|
+
return "generator_schema_invalid";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function runDeliveryPathSearch(inputValue, evaluator, options = {}) {
|
|
18
|
+
const input = validateSearchInput(inputValue);
|
|
19
|
+
const startedAt = Date.now();
|
|
20
|
+
const baseline = [...(options.candidates ?? candidatesFromInput(input))];
|
|
21
|
+
const queue = [...baseline];
|
|
22
|
+
const rejected = [];
|
|
23
|
+
const evaluated = [];
|
|
24
|
+
let evaluations = 0;
|
|
25
|
+
let costUnits = 0;
|
|
26
|
+
let lastLeaderId = null;
|
|
27
|
+
let stableComparisons = 0;
|
|
28
|
+
let stoppingReason = "candidate_queue_exhausted";
|
|
29
|
+
const generation = {
|
|
30
|
+
status: input.generation?.enabled ? "failed" : "disabled",
|
|
31
|
+
adapter: options.generator?.metadata ?? null,
|
|
32
|
+
budgets: input.generation ?? null,
|
|
33
|
+
returned: 0,
|
|
34
|
+
accepted: 0,
|
|
35
|
+
rejected: [],
|
|
36
|
+
cost_units: 0,
|
|
37
|
+
duration_ms: 0,
|
|
38
|
+
failure_code: null,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
if (input.generation?.enabled) {
|
|
42
|
+
if (!options.generator) {
|
|
43
|
+
generation.failure_code = "generator_unavailable";
|
|
44
|
+
} else {
|
|
45
|
+
const generationStartedAt = Date.now();
|
|
46
|
+
try {
|
|
47
|
+
const response = await options.generator.generate(generatorRequestFromInput(input));
|
|
48
|
+
const elapsed = Date.now() - generationStartedAt;
|
|
49
|
+
generation.returned = response.proposals.length;
|
|
50
|
+
generation.cost_units = response.cost_units;
|
|
51
|
+
costUnits += response.cost_units;
|
|
52
|
+
if (elapsed > input.generation.max_duration_ms) {
|
|
53
|
+
const error = new Error("generator duration budget exceeded");
|
|
54
|
+
error.code = "GENERATOR_TIMEOUT";
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
if (response.cost_units > input.generation.max_cost_units || response.proposals.length > input.generation.max_proposals) {
|
|
58
|
+
const error = new Error("generator response exceeds configured budget");
|
|
59
|
+
error.code = "GENERATOR_BUDGET_EXCEEDED";
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
const filtered = generatedCandidatesFromResponse(response, input, baseline);
|
|
63
|
+
generation.accepted = filtered.accepted.length;
|
|
64
|
+
generation.rejected = filtered.rejected;
|
|
65
|
+
queue.push(...filtered.accepted.slice(0, input.generation.max_proposals));
|
|
66
|
+
generation.status = filtered.accepted.length === response.proposals.length ? "success" : "partial";
|
|
67
|
+
if (!filtered.accepted.length) generation.failure_code = "generator_no_diverse_proposals";
|
|
68
|
+
} catch (error) {
|
|
69
|
+
generation.failure_code = generatorFailureCode(error);
|
|
70
|
+
if (generation.failure_code === "generator_mutation_detected") throw error;
|
|
71
|
+
} finally {
|
|
72
|
+
generation.duration_ms = Date.now() - generationStartedAt;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
while (queue.length > 0) {
|
|
78
|
+
if (evaluations >= input.budgets.max_evaluations) { stoppingReason = "evaluation_budget_exhausted"; break; }
|
|
79
|
+
if (costUnits >= input.budgets.max_cost_units) { stoppingReason = "cost_budget_exhausted"; break; }
|
|
80
|
+
if (Date.now() - startedAt >= input.budgets.max_duration_ms) { stoppingReason = "duration_budget_exhausted"; break; }
|
|
81
|
+
const candidate = queue.shift();
|
|
82
|
+
const legality = candidateLegality(candidate, input);
|
|
83
|
+
if (!legality.legal) {
|
|
84
|
+
rejected.push({ candidate_id: candidate.id, action: candidate.action, reason: legality.reason });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const evaluation = validateEvaluation(await evaluator.evaluate(input, candidate), candidate.id);
|
|
90
|
+
evaluations += 1;
|
|
91
|
+
costUnits += evaluation.cost;
|
|
92
|
+
const scored = scoreEvaluation(evaluation, input.enforcement.level);
|
|
93
|
+
evaluated.push({ candidate, evaluation, ...scored });
|
|
94
|
+
const currentLeader = [...evaluated].sort((a, b) => b.score - a.score || a.candidate.id.localeCompare(b.candidate.id))[0];
|
|
95
|
+
if (currentLeader?.candidate.id === lastLeaderId) stableComparisons += 1;
|
|
96
|
+
else stableComparisons = 1;
|
|
97
|
+
lastLeaderId = currentLeader?.candidate.id ?? null;
|
|
98
|
+
if (evaluated.length > 1 && stableComparisons >= input.budgets.stability_window) {
|
|
99
|
+
stoppingReason = "stable_leader";
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
if ((candidate.depth ?? 0) + 1 < input.budgets.max_depth) {
|
|
103
|
+
for (const [index, action] of evaluation.child_actions.slice(0, input.budgets.max_candidates).entries()) {
|
|
104
|
+
queue.push({
|
|
105
|
+
id: `${candidate.id}.${index + 1}`,
|
|
106
|
+
parent_id: candidate.id,
|
|
107
|
+
depth: (candidate.depth ?? 0) + 1,
|
|
108
|
+
action,
|
|
109
|
+
source: "expanded",
|
|
110
|
+
expected_evidence: [],
|
|
111
|
+
tests: [],
|
|
112
|
+
assumptions: evaluation.assumptions,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error?.fatalEvaluator === true) throw error;
|
|
118
|
+
rejected.push({ candidate_id: candidate.id, action: candidate.action, reason: `invalid_evaluation: ${error.message}` });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
evaluated.sort((a, b) => b.score - a.score || a.candidate.id.localeCompare(b.candidate.id));
|
|
123
|
+
const leader = evaluated[0];
|
|
124
|
+
if (!leader) stoppingReason = rejected.length ? "no_safe_candidate" : stoppingReason;
|
|
125
|
+
return {
|
|
126
|
+
contract_version: CONTRACT_VERSION,
|
|
127
|
+
scoring_policy_version: SCORING_POLICY_VERSION,
|
|
128
|
+
scope_key: input.scope_key,
|
|
129
|
+
current_gate: input.current_gate,
|
|
130
|
+
status: leader ? "recommendation" : "no_safe_recommendation",
|
|
131
|
+
recommendation: leader ? {
|
|
132
|
+
candidate_id: leader.candidate.id,
|
|
133
|
+
action: leader.candidate.action,
|
|
134
|
+
score: leader.score,
|
|
135
|
+
rationale: leader.evaluation.rationale,
|
|
136
|
+
expected_evidence: leader.candidate.expected_evidence,
|
|
137
|
+
tests: leader.candidate.tests,
|
|
138
|
+
risks: leader.evaluation.risks,
|
|
139
|
+
assumptions: leader.evaluation.assumptions,
|
|
140
|
+
} : null,
|
|
141
|
+
alternatives: evaluated.slice(1).map((item) => ({
|
|
142
|
+
candidate_id: item.candidate.id,
|
|
143
|
+
action: item.candidate.action,
|
|
144
|
+
score: item.score,
|
|
145
|
+
rejection_reason: `ranked_below_${leader?.candidate.id ?? "safety_threshold"}`,
|
|
146
|
+
})),
|
|
147
|
+
rejected,
|
|
148
|
+
enforcement: input.enforcement,
|
|
149
|
+
evaluator: evaluator.metadata ?? { name: evaluator.name ?? "unknown" },
|
|
150
|
+
generation,
|
|
151
|
+
budgets: { configured: input.budgets, evaluations, cost_units: costUnits, stable_comparisons: stableComparisons, duration_ms: Date.now() - startedAt },
|
|
152
|
+
stopping_reason: stoppingReason,
|
|
153
|
+
next_gate_action: "Run canonical AGDF gate-check; search does not grant permission.",
|
|
154
|
+
};
|
|
155
|
+
}
|