imprint-mcp 0.6.1 → 0.6.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 +2 -0
- package/package.json +3 -2
- package/prompts/audit-agent.md +4 -2
- package/prompts/compile-agent.md +46 -23
- package/prompts/live-verifier-agent.md +101 -0
- package/prompts/playbook-compilation.md +8 -1
- package/src/cli.ts +83 -1
- package/src/imprint/audit.ts +219 -20
- package/src/imprint/backend-cache.ts +18 -11
- package/src/imprint/backend-ladder.ts +48 -34
- package/src/imprint/cdp-browser-fetch.ts +135 -42
- package/src/imprint/claude-cli-compile.ts +38 -16
- package/src/imprint/codex-cli-compile.ts +120 -21
- package/src/imprint/compile-agent.ts +71 -26
- package/src/imprint/compile-tools.ts +169 -38
- package/src/imprint/compile-verification.ts +187 -0
- package/src/imprint/compile.ts +71 -2
- package/src/imprint/freeform-redact.ts +27 -6
- package/src/imprint/live-verifier.ts +1665 -0
- package/src/imprint/llm.ts +16 -0
- package/src/imprint/mcp-compile-server.ts +380 -81
- package/src/imprint/mcp-live-verifier-server.ts +359 -0
- package/src/imprint/mcp-server.ts +12 -2
- package/src/imprint/probe-backends.ts +88 -7
- package/src/imprint/runtime.ts +61 -11
- package/src/imprint/teach-state.ts +18 -1
- package/src/imprint/teach.ts +77 -19
- package/src/imprint/types.ts +39 -2
package/src/imprint/llm.ts
CHANGED
|
@@ -1238,6 +1238,22 @@ export function preferredAgentModel(provider: ProviderName): string {
|
|
|
1238
1238
|
}
|
|
1239
1239
|
}
|
|
1240
1240
|
|
|
1241
|
+
/** Model policy for the independent live semantic verifier. Keep this separate
|
|
1242
|
+
* from the compile-agent preference so the reviewer is intentionally a fresh,
|
|
1243
|
+
* differently configured agent. No fallback is allowed: an unavailable model
|
|
1244
|
+
* makes verification inconclusive and the compile fails closed. */
|
|
1245
|
+
export function preferredVerificationModel(provider: ProviderName): string {
|
|
1246
|
+
if (provider === 'codex-cli') return 'gpt-5.6-terra';
|
|
1247
|
+
if (provider === 'claude-cli' || provider === 'anthropic-api') {
|
|
1248
|
+
const latestSonnet = availableModelsForProvider(provider).find((option) =>
|
|
1249
|
+
option.model.startsWith('claude-sonnet-'),
|
|
1250
|
+
);
|
|
1251
|
+
if (latestSonnet) return latestSonnet.model;
|
|
1252
|
+
throw new Error(`No Sonnet model is available for verification on ${provider}`);
|
|
1253
|
+
}
|
|
1254
|
+
throw new Error(`Provider ${provider} does not support live semantic verification`);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1241
1257
|
interface ModelOption {
|
|
1242
1258
|
model: string;
|
|
1243
1259
|
isDefault: boolean;
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* loss.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
18
|
+
import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
18
19
|
import { join as pathJoin } from 'node:path';
|
|
19
20
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
20
21
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
@@ -40,8 +41,15 @@ import {
|
|
|
40
41
|
buildCompileTools,
|
|
41
42
|
externalVerification,
|
|
42
43
|
} from './compile-tools.ts';
|
|
44
|
+
import {
|
|
45
|
+
mergeSemanticParamVerification,
|
|
46
|
+
runLiveSemanticVerification,
|
|
47
|
+
semanticVerificationFailures,
|
|
48
|
+
} from './live-verifier.ts';
|
|
49
|
+
import type { ProviderName } from './llm.ts';
|
|
43
50
|
import { loadJsonFile } from './load-json.ts';
|
|
44
51
|
import { createLog } from './log.ts';
|
|
52
|
+
import { rebindExistingBackendsCacheToWorkflow } from './probe-backends.ts';
|
|
45
53
|
import { redactSession } from './redact.ts';
|
|
46
54
|
import { loadCredentialStore } from './runtime.ts';
|
|
47
55
|
import type { SharedCompileContext, ToolCandidate } from './tool-candidates.ts';
|
|
@@ -69,19 +77,155 @@ interface RunCompileMcpServerOptions {
|
|
|
69
77
|
/** Site slug (required in auth mode) — used to load credentials for the live
|
|
70
78
|
* auth-test tools. */
|
|
71
79
|
site?: string;
|
|
80
|
+
/** Compile provider. The independent verifier uses the same provider family
|
|
81
|
+
* with its dedicated review model (Terra for Codex, latest Sonnet for Claude). */
|
|
82
|
+
provider?: ProviderName;
|
|
83
|
+
/** Bounded resume of an already-generated data tool. */
|
|
84
|
+
revisionMode?: boolean;
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
const DONE_SENTINEL = '.compile-done.json';
|
|
75
88
|
const GIVE_UP_SENTINEL = '.compile-give-up.json';
|
|
89
|
+
const VERIFICATION_STATE_SENTINEL = '.compile-verification-state.json';
|
|
90
|
+
// Keep the compile watchdog from charging live verification against the model's
|
|
91
|
+
// reasoning budget, while retaining a hard bound if the MCP server dies during
|
|
92
|
+
// done(). This matches the longest supported MCP tool call.
|
|
93
|
+
const MAX_VERIFICATION_WALL_TIME_MS = 30 * 60_000;
|
|
76
94
|
/** Auth mode only: a mid-loop checkpoint the agent reaches. The tool records
|
|
77
95
|
* the request here and the
|
|
78
96
|
* agent STOPS; the orchestrator (teach) performs the action and resumes the
|
|
79
97
|
* agent (`claude --resume`) with the result. One pending checkpoint per segment. */
|
|
80
98
|
const CHECKPOINT_SENTINEL = '.compile-checkpoint.json';
|
|
81
99
|
|
|
100
|
+
const COMPILE_ARTIFACT_FILES = [
|
|
101
|
+
'workflow.json',
|
|
102
|
+
'parser.ts',
|
|
103
|
+
'parser.test.ts',
|
|
104
|
+
'integration.test.ts',
|
|
105
|
+
'request-transform.ts',
|
|
106
|
+
'playbook.yaml',
|
|
107
|
+
] as const;
|
|
108
|
+
|
|
109
|
+
interface PendingInconclusiveDecision {
|
|
110
|
+
artifactFingerprint: string;
|
|
111
|
+
report: Awaited<ReturnType<typeof runLiveSemanticVerification>>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface CompileVerificationState {
|
|
115
|
+
excludedMs: number;
|
|
116
|
+
activeSinceMs?: number;
|
|
117
|
+
activeUntilMs?: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function readCompileVerificationState(toolDir: string): CompileVerificationState {
|
|
121
|
+
try {
|
|
122
|
+
const parsed = JSON.parse(
|
|
123
|
+
readFileSync(pathJoin(toolDir, VERIFICATION_STATE_SENTINEL), 'utf8'),
|
|
124
|
+
) as Partial<CompileVerificationState>;
|
|
125
|
+
return {
|
|
126
|
+
excludedMs:
|
|
127
|
+
typeof parsed.excludedMs === 'number' && Number.isFinite(parsed.excludedMs)
|
|
128
|
+
? Math.max(0, parsed.excludedMs)
|
|
129
|
+
: 0,
|
|
130
|
+
activeSinceMs:
|
|
131
|
+
typeof parsed.activeSinceMs === 'number' && Number.isFinite(parsed.activeSinceMs)
|
|
132
|
+
? parsed.activeSinceMs
|
|
133
|
+
: undefined,
|
|
134
|
+
activeUntilMs:
|
|
135
|
+
typeof parsed.activeUntilMs === 'number' && Number.isFinite(parsed.activeUntilMs)
|
|
136
|
+
? parsed.activeUntilMs
|
|
137
|
+
: undefined,
|
|
138
|
+
};
|
|
139
|
+
} catch {
|
|
140
|
+
return { excludedMs: 0 };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function writeCompileVerificationState(toolDir: string, state: CompileVerificationState): void {
|
|
145
|
+
const destination = pathJoin(toolDir, VERIFICATION_STATE_SENTINEL);
|
|
146
|
+
const temporary = pathJoin(
|
|
147
|
+
toolDir,
|
|
148
|
+
`${VERIFICATION_STATE_SENTINEL}.${process.pid}.${randomUUID()}.tmp`,
|
|
149
|
+
);
|
|
150
|
+
try {
|
|
151
|
+
writeFileSync(temporary, `${JSON.stringify(state)}\n`, {
|
|
152
|
+
encoding: 'utf8',
|
|
153
|
+
flag: 'wx',
|
|
154
|
+
mode: 0o600,
|
|
155
|
+
});
|
|
156
|
+
renameSync(temporary, destination);
|
|
157
|
+
} finally {
|
|
158
|
+
// rename removes the source on success; clean up a partial temp file if a
|
|
159
|
+
// write or rename fails without disturbing the last complete state.
|
|
160
|
+
if (existsSync(temporary)) unlinkSync(temporary);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function beginCompileVerification(toolDir: string): () => void {
|
|
165
|
+
const previous = readCompileVerificationState(toolDir);
|
|
166
|
+
const startedAt = Date.now();
|
|
167
|
+
writeCompileVerificationState(toolDir, {
|
|
168
|
+
excludedMs: previous.excludedMs,
|
|
169
|
+
activeSinceMs: startedAt,
|
|
170
|
+
activeUntilMs: startedAt + MAX_VERIFICATION_WALL_TIME_MS,
|
|
171
|
+
});
|
|
172
|
+
return () => {
|
|
173
|
+
const finishedAt = Date.now();
|
|
174
|
+
writeCompileVerificationState(toolDir, {
|
|
175
|
+
excludedMs:
|
|
176
|
+
previous.excludedMs +
|
|
177
|
+
Math.min(MAX_VERIFICATION_WALL_TIME_MS, Math.max(0, finishedAt - startedAt)),
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Effective compiler deadline after excluding bounded time spent inside done()
|
|
183
|
+
* verification. Exported for the CLI watchdogs and focused lifecycle tests. */
|
|
184
|
+
export function compileDeadlineAfterVerification(
|
|
185
|
+
toolDir: string,
|
|
186
|
+
compilerDeadlineMs: number,
|
|
187
|
+
now = Date.now(),
|
|
188
|
+
): number {
|
|
189
|
+
const state = readCompileVerificationState(toolDir);
|
|
190
|
+
const activeElapsedMs =
|
|
191
|
+
state.activeSinceMs === undefined
|
|
192
|
+
? 0
|
|
193
|
+
: Math.max(
|
|
194
|
+
0,
|
|
195
|
+
Math.min(now, state.activeUntilMs ?? state.activeSinceMs) - state.activeSinceMs,
|
|
196
|
+
);
|
|
197
|
+
return compilerDeadlineMs + state.excludedMs + activeElapsedMs;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function compileArtifactFingerprint(toolDir: string): string {
|
|
201
|
+
const hash = createHash('sha256');
|
|
202
|
+
for (const relativePath of COMPILE_ARTIFACT_FILES) {
|
|
203
|
+
const absolutePath = pathJoin(toolDir, relativePath);
|
|
204
|
+
hash.update(relativePath);
|
|
205
|
+
hash.update('\0');
|
|
206
|
+
if (existsSync(absolutePath)) hash.update(readFileSync(absolutePath));
|
|
207
|
+
hash.update('\0');
|
|
208
|
+
}
|
|
209
|
+
return hash.digest('hex');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function canAcceptInconclusiveDecision(opts: {
|
|
213
|
+
pendingFingerprint?: string;
|
|
214
|
+
currentFingerprint: string;
|
|
215
|
+
acceptInconclusive?: boolean;
|
|
216
|
+
inconclusiveReason?: string;
|
|
217
|
+
}): boolean {
|
|
218
|
+
return (
|
|
219
|
+
opts.acceptInconclusive === true &&
|
|
220
|
+
Boolean(opts.inconclusiveReason?.trim()) &&
|
|
221
|
+
opts.pendingFingerprint === opts.currentFingerprint
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
82
225
|
export async function runCompileMcpServer(opts: RunCompileMcpServerOptions): Promise<void> {
|
|
83
226
|
const isAuthMode = !!opts.authToolPlan;
|
|
84
227
|
const maxVerificationCycles = opts.maxVerificationCycles ?? 5;
|
|
228
|
+
let pendingInconclusive: PendingInconclusiveDecision | undefined;
|
|
85
229
|
|
|
86
230
|
// Load + auto-redact the session, exactly as compile-agent.ts does.
|
|
87
231
|
let session: Session = loadJsonFile(
|
|
@@ -125,6 +269,7 @@ export async function runCompileMcpServer(opts: RunCompileMcpServerOptions): Pro
|
|
|
125
269
|
sharedContext: opts.sharedContext,
|
|
126
270
|
buildPlanPath: opts.buildPlanPath,
|
|
127
271
|
sharedModules: opts.sharedModules,
|
|
272
|
+
revisionMode: opts.revisionMode,
|
|
128
273
|
});
|
|
129
274
|
|
|
130
275
|
// Resolve the shared modules + producer→consumer token contracts + the general
|
|
@@ -148,6 +293,16 @@ export async function runCompileMcpServer(opts: RunCompileMcpServerOptions): Pro
|
|
|
148
293
|
type: 'object',
|
|
149
294
|
properties: {
|
|
150
295
|
summary: { type: 'string', description: 'Brief summary of what was accomplished' },
|
|
296
|
+
accept_inconclusive: {
|
|
297
|
+
type: 'boolean',
|
|
298
|
+
description:
|
|
299
|
+
'After an infrastructure-only inconclusive verifier result, explicitly ship the unchanged deterministic artifact without a liveVerified stamp.',
|
|
300
|
+
},
|
|
301
|
+
inconclusive_reason: {
|
|
302
|
+
type: 'string',
|
|
303
|
+
description:
|
|
304
|
+
'Compiler reasoning for accepting the unchanged artifact after reviewing the inconclusive verifier evidence.',
|
|
305
|
+
},
|
|
151
306
|
},
|
|
152
307
|
required: ['summary'],
|
|
153
308
|
},
|
|
@@ -294,7 +449,12 @@ export async function runCompileMcpServer(opts: RunCompileMcpServerOptions): Pro
|
|
|
294
449
|
|
|
295
450
|
// Custom done — runs verification inline.
|
|
296
451
|
if (name === 'done') {
|
|
297
|
-
const
|
|
452
|
+
const doneArgs = args as {
|
|
453
|
+
summary?: string;
|
|
454
|
+
accept_inconclusive?: boolean;
|
|
455
|
+
inconclusive_reason?: string;
|
|
456
|
+
};
|
|
457
|
+
const summary = doneArgs.summary ?? 'Task completed';
|
|
298
458
|
log(`done() called: ${summary}`);
|
|
299
459
|
|
|
300
460
|
// Auth mode: lightweight structural verification (the agent already proved
|
|
@@ -345,96 +505,234 @@ Fix the issues in workflow.json, re-test with run_verification, and call done ag
|
|
|
345
505
|
};
|
|
346
506
|
}
|
|
347
507
|
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
508
|
+
const finishVerification = beginCompileVerification(opts.toolDir);
|
|
509
|
+
try {
|
|
510
|
+
const { failures, warnings, paramVerification, integrationEvidence } =
|
|
511
|
+
await externalVerification(opts.toolDir, session, opts.sessionPath, {
|
|
512
|
+
expectedToolName: opts.candidate?.toolName,
|
|
513
|
+
likelyParams: opts.candidate?.likelyParams,
|
|
514
|
+
candidateRequestSeqs: opts.candidate?.requestSeqs,
|
|
515
|
+
// Widen Fix B's variation pool to dependency requests so a token that
|
|
516
|
+
// varies only across them and is frozen as a literal in the tool's
|
|
517
|
+
// request is caught (the cross-request session-token leak case).
|
|
518
|
+
dependencyRequestSeqs: opts.candidate?.dependencySeqs,
|
|
519
|
+
assignedSharedModules,
|
|
520
|
+
tokenParams,
|
|
521
|
+
tokenParamShapes,
|
|
522
|
+
emittedTokens,
|
|
523
|
+
requiredInputs,
|
|
524
|
+
credentialValues,
|
|
525
|
+
credentialNames: opts.sharedContext?.credentialNames,
|
|
526
|
+
deferLiveIntegrationToSemanticAgent: true,
|
|
527
|
+
});
|
|
528
|
+
if (warnings.length > 0) {
|
|
529
|
+
log(`verification warnings (non-blocking):\n${warnings.join('\n')}`);
|
|
530
|
+
}
|
|
531
|
+
if (failures.length === 0) {
|
|
532
|
+
if (!opts.provider) {
|
|
533
|
+
failures.push('compile provider was not supplied to the independent semantic verifier');
|
|
534
|
+
} else {
|
|
535
|
+
const currentFingerprint = compileArtifactFingerprint(opts.toolDir);
|
|
536
|
+
if (
|
|
537
|
+
pendingInconclusive &&
|
|
538
|
+
canAcceptInconclusiveDecision({
|
|
539
|
+
pendingFingerprint: pendingInconclusive.artifactFingerprint,
|
|
540
|
+
currentFingerprint,
|
|
541
|
+
acceptInconclusive: doneArgs.accept_inconclusive,
|
|
542
|
+
inconclusiveReason: doneArgs.inconclusive_reason,
|
|
543
|
+
})
|
|
544
|
+
) {
|
|
545
|
+
const semantic = pendingInconclusive.report;
|
|
546
|
+
const semanticParams = mergeSemanticParamVerification(
|
|
547
|
+
paramVerification,
|
|
548
|
+
semantic.report,
|
|
549
|
+
);
|
|
550
|
+
const inconclusiveReason = doneArgs.inconclusive_reason?.trim() ?? '';
|
|
551
|
+
applyLiveVerification(opts.toolDir, {
|
|
552
|
+
kind: 'waived-infra',
|
|
553
|
+
firstError: inconclusiveReason,
|
|
554
|
+
exhaustedBackends: [],
|
|
555
|
+
});
|
|
556
|
+
const paramWarnings = applyParamVerification(opts.toolDir, semanticParams);
|
|
557
|
+
// The unverified stamp and parameter annotations are metadata-only,
|
|
558
|
+
// so carry the already-proven preference to the final workflow hash.
|
|
559
|
+
rebindExistingBackendsCacheToWorkflow(opts.toolDir);
|
|
560
|
+
const allWarnings = [
|
|
561
|
+
...warnings,
|
|
562
|
+
`independent semantic verification was inconclusive: ${semantic.report.summary}`,
|
|
563
|
+
...paramWarnings,
|
|
564
|
+
];
|
|
565
|
+
const sentinel = pathJoin(opts.toolDir, DONE_SENTINEL);
|
|
566
|
+
writeFileSync(
|
|
567
|
+
sentinel,
|
|
568
|
+
JSON.stringify(
|
|
569
|
+
{
|
|
570
|
+
summary,
|
|
571
|
+
verification: 'passed',
|
|
572
|
+
liveVerified: false,
|
|
573
|
+
semanticVerification: {
|
|
574
|
+
status: semantic.report.status,
|
|
575
|
+
provider: semantic.provider,
|
|
576
|
+
model: semantic.model,
|
|
577
|
+
attempts: semantic.attempts,
|
|
578
|
+
evidenceArtifact: semantic.report.evidenceArtifact,
|
|
579
|
+
logArtifact: semantic.report.logArtifact,
|
|
580
|
+
},
|
|
581
|
+
compilerDecision: {
|
|
582
|
+
decision: 'ship_unverified',
|
|
583
|
+
reason: inconclusiveReason,
|
|
584
|
+
},
|
|
585
|
+
warnings: allWarnings,
|
|
586
|
+
timestamp: Date.now(),
|
|
587
|
+
},
|
|
588
|
+
null,
|
|
589
|
+
2,
|
|
590
|
+
),
|
|
591
|
+
'utf8',
|
|
592
|
+
);
|
|
593
|
+
log(`compiler accepted unchanged inconclusive artifact; wrote ${sentinel}`);
|
|
594
|
+
return {
|
|
595
|
+
content: [
|
|
596
|
+
{
|
|
597
|
+
type: 'text',
|
|
598
|
+
text: `DONE_UNVERIFIED — deterministic verification passed and the compiler explicitly accepted the unchanged artifact after an infrastructure-only inconclusive live review. Evidence: ${semantic.report.evidenceArtifact ?? '.live-verification-evidence.json'}. Verifier log: ${semantic.report.logArtifact ?? '.live-verifier-log.jsonl'}. The orchestrator will exit shortly. Do not call any more tools.`,
|
|
599
|
+
},
|
|
600
|
+
],
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (
|
|
605
|
+
pendingInconclusive &&
|
|
606
|
+
pendingInconclusive.artifactFingerprint !== currentFingerprint
|
|
607
|
+
) {
|
|
608
|
+
log(
|
|
609
|
+
'compile artifacts changed after inconclusive review; running verification again',
|
|
610
|
+
);
|
|
611
|
+
pendingInconclusive = undefined;
|
|
612
|
+
}
|
|
613
|
+
const semantic = await runLiveSemanticVerification({
|
|
614
|
+
provider: opts.provider,
|
|
615
|
+
toolDir: opts.toolDir,
|
|
616
|
+
evidence: integrationEvidence,
|
|
617
|
+
});
|
|
618
|
+
if (semantic.report.status === 'inconclusive') {
|
|
619
|
+
pendingInconclusive = {
|
|
620
|
+
artifactFingerprint: compileArtifactFingerprint(opts.toolDir),
|
|
621
|
+
report: semantic,
|
|
622
|
+
};
|
|
623
|
+
} else {
|
|
624
|
+
pendingInconclusive = undefined;
|
|
625
|
+
}
|
|
626
|
+
const semanticFailures = semanticVerificationFailures(semantic.report);
|
|
627
|
+
failures.push(...semanticFailures);
|
|
628
|
+
if (semantic.report.status === 'approved_with_gaps') {
|
|
629
|
+
warnings.push(
|
|
630
|
+
`independent semantic verification approved with gaps: ${semantic.report.gaps.join('; ')}`,
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
if (semanticFailures.length === 0) {
|
|
634
|
+
const semanticParams = mergeSemanticParamVerification(
|
|
635
|
+
paramVerification,
|
|
636
|
+
semantic.report,
|
|
637
|
+
);
|
|
638
|
+
// Only the independent semantic agent can mint liveVerified=true.
|
|
639
|
+
applyLiveVerification(opts.toolDir, undefined);
|
|
640
|
+
const paramWarnings = applyParamVerification(opts.toolDir, semanticParams);
|
|
641
|
+
// liveVerified/parameter annotations are metadata-only, but they
|
|
642
|
+
// change the hash-strict workflow cache key. Keep the verifier's
|
|
643
|
+
// already-proven backend preference current without probing again.
|
|
644
|
+
rebindExistingBackendsCacheToWorkflow(opts.toolDir);
|
|
645
|
+
if (paramWarnings.length > 0) {
|
|
646
|
+
log(`parameter verification:\n${paramWarnings.join('\n')}`);
|
|
647
|
+
}
|
|
648
|
+
const allWarnings = [...warnings, ...paramWarnings];
|
|
649
|
+
const sentinel = pathJoin(opts.toolDir, DONE_SENTINEL);
|
|
650
|
+
writeFileSync(
|
|
651
|
+
sentinel,
|
|
652
|
+
JSON.stringify(
|
|
653
|
+
{
|
|
654
|
+
summary,
|
|
655
|
+
verification: 'passed',
|
|
656
|
+
semanticVerification: {
|
|
657
|
+
status: semantic.report.status,
|
|
658
|
+
provider: semantic.provider,
|
|
659
|
+
model: semantic.model,
|
|
660
|
+
attempts: semantic.attempts,
|
|
661
|
+
evidenceArtifact: semantic.report.evidenceArtifact,
|
|
662
|
+
logArtifact: semantic.report.logArtifact,
|
|
663
|
+
},
|
|
664
|
+
warnings: allWarnings,
|
|
665
|
+
timestamp: Date.now(),
|
|
666
|
+
},
|
|
667
|
+
null,
|
|
668
|
+
2,
|
|
669
|
+
),
|
|
670
|
+
'utf8',
|
|
671
|
+
);
|
|
672
|
+
log(`verification passed; wrote ${sentinel}`);
|
|
673
|
+
return {
|
|
674
|
+
content: [
|
|
675
|
+
{
|
|
676
|
+
type: 'text',
|
|
677
|
+
text: `DONE_VERIFIED — deterministic checks and independent live semantic verification passed (${semantic.report.status}). Evidence: ${semantic.report.evidenceArtifact ?? '.live-verification-evidence.json'}. Verifier log: ${semantic.report.logArtifact ?? '.live-verifier-log.jsonl'}. The orchestrator will exit shortly. Do not call any more tools.`,
|
|
678
|
+
},
|
|
679
|
+
],
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
verificationFailures++;
|
|
685
|
+
log(`verification failed (cycle ${verificationFailures}/${maxVerificationCycles})`);
|
|
686
|
+
if (verificationFailures >= maxVerificationCycles) {
|
|
687
|
+
const sentinel = pathJoin(opts.toolDir, DONE_SENTINEL);
|
|
688
|
+
writeFileSync(
|
|
689
|
+
sentinel,
|
|
690
|
+
JSON.stringify(
|
|
691
|
+
{
|
|
692
|
+
summary,
|
|
693
|
+
verification: 'failed',
|
|
694
|
+
cycles: verificationFailures,
|
|
695
|
+
failures,
|
|
696
|
+
warnings,
|
|
697
|
+
timestamp: Date.now(),
|
|
698
|
+
},
|
|
699
|
+
null,
|
|
700
|
+
2,
|
|
701
|
+
),
|
|
702
|
+
'utf8',
|
|
703
|
+
);
|
|
704
|
+
return {
|
|
705
|
+
isError: true,
|
|
706
|
+
content: [
|
|
707
|
+
{
|
|
708
|
+
type: 'text',
|
|
709
|
+
text: `Verification failed after ${maxVerificationCycles} cycles. Giving up. Final failures:\n${failures.map((f) => `- ${f}`).join('\n')}`,
|
|
710
|
+
},
|
|
711
|
+
],
|
|
712
|
+
};
|
|
375
713
|
}
|
|
376
|
-
const allWarnings = [...warnings, ...paramWarnings];
|
|
377
|
-
const sentinel = pathJoin(opts.toolDir, DONE_SENTINEL);
|
|
378
|
-
writeFileSync(
|
|
379
|
-
sentinel,
|
|
380
|
-
JSON.stringify(
|
|
381
|
-
{ summary, verification: 'passed', warnings: allWarnings, timestamp: Date.now() },
|
|
382
|
-
null,
|
|
383
|
-
2,
|
|
384
|
-
),
|
|
385
|
-
'utf8',
|
|
386
|
-
);
|
|
387
|
-
log(`verification passed; wrote ${sentinel}`);
|
|
388
|
-
return {
|
|
389
|
-
content: [
|
|
390
|
-
{
|
|
391
|
-
type: 'text',
|
|
392
|
-
text: 'DONE_VERIFIED — verification passed. The orchestrator will exit shortly. Do not call any more tools.',
|
|
393
|
-
},
|
|
394
|
-
],
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
714
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
verification: 'failed',
|
|
408
|
-
cycles: verificationFailures,
|
|
409
|
-
failures,
|
|
410
|
-
warnings,
|
|
411
|
-
timestamp: Date.now(),
|
|
412
|
-
},
|
|
413
|
-
null,
|
|
414
|
-
2,
|
|
415
|
-
),
|
|
416
|
-
'utf8',
|
|
417
|
-
);
|
|
715
|
+
const continuationMessage = `You called done but verification failed (cycle ${verificationFailures}/${maxVerificationCycles}):
|
|
716
|
+
|
|
717
|
+
${failures.map((f) => `- ${f}`).join('\n')}
|
|
718
|
+
|
|
719
|
+
Resume your work. Read the files you wrote (workflow.json, parser.ts, parser.test.ts), fix the issues, re-run tests, and call done again when fixed.`;
|
|
720
|
+
const compilerDecisionGuidance = `\n\nUse the verifier's semantic evidence, not a requirement for 100% of the original candidate inputs. If the core intent works but a secondary parameter cannot be supported from the recording and live evidence without guessing, remove that parameter and add workflow.limitations with omittedParameters and a specific reason. Close that omission over its dependencies: also remove dependent public inputs and narrow parameter/intent descriptions so callers cannot still enter the unsupported branch indirectly. Do not use a limitation to hide a broken core tool, and do not keep a known broken or ignored public input.`;
|
|
721
|
+
const inconclusiveDecisionGuidance = pendingInconclusive
|
|
722
|
+
? '\n\nThe live verifier was infrastructure-only inconclusive. Review its evidence and log. If they expose a tool defect, revise the artifacts and call done normally so verification reruns. If they expose no tool-level defect and the deterministic artifact should ship explicitly unverified, call done again without changing compile artifacts, set accept_inconclusive=true, and provide your concrete reasoning in inconclusive_reason. This compiler decision is persisted and does not mint liveVerified.'
|
|
723
|
+
: '';
|
|
418
724
|
return {
|
|
419
725
|
isError: true,
|
|
420
726
|
content: [
|
|
421
727
|
{
|
|
422
728
|
type: 'text',
|
|
423
|
-
text:
|
|
729
|
+
text: continuationMessage + compilerDecisionGuidance + inconclusiveDecisionGuidance,
|
|
424
730
|
},
|
|
425
731
|
],
|
|
426
732
|
};
|
|
733
|
+
} finally {
|
|
734
|
+
finishVerification();
|
|
427
735
|
}
|
|
428
|
-
|
|
429
|
-
const continuationMessage = `You called done but verification failed (cycle ${verificationFailures}/${maxVerificationCycles}):
|
|
430
|
-
|
|
431
|
-
${failures.map((f) => `- ${f}`).join('\n')}
|
|
432
|
-
|
|
433
|
-
Resume your work. Read the files you wrote (workflow.json, parser.ts, parser.test.ts), fix the issues, re-run tests, and call done again when fixed.`;
|
|
434
|
-
return {
|
|
435
|
-
isError: true,
|
|
436
|
-
content: [{ type: 'text', text: continuationMessage }],
|
|
437
|
-
};
|
|
438
736
|
}
|
|
439
737
|
|
|
440
738
|
// Auth-mode checkpoint tools — record the request and end the segment. The
|
|
@@ -558,4 +856,5 @@ export const COMPILE_SENTINELS = {
|
|
|
558
856
|
done: DONE_SENTINEL,
|
|
559
857
|
giveUp: GIVE_UP_SENTINEL,
|
|
560
858
|
checkpoint: CHECKPOINT_SENTINEL,
|
|
859
|
+
verificationState: VERIFICATION_STATE_SENTINEL,
|
|
561
860
|
} as const;
|