@open-agent-toolkit/cli 0.2.76 → 0.2.79
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/assets/agents/recon-worker.md +12 -12
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/workflows/skills/recon.md +53 -27
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/explainer-kit/SKILL.md +1 -1
- package/assets/skills/explainer-kit/scripts/bundle.mjs +26 -2
- package/assets/skills/explainer-kit/scripts/record.mjs +26 -3
- package/assets/skills/explainer-kit/scripts/verify.mjs +26 -3
- package/assets/skills/oat-explainer-kit/SKILL.md +1 -1
- package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +25 -5
- package/assets/skills/oat-project-complete/SKILL.md +1 -1
- package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +7 -0
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +26 -5
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +26 -5
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/resolve-synced-archive-entry.mjs +26 -5
- package/assets/skills/oat-project-complete/scripts/validate-durable-archive-receipt.mjs +7 -0
- package/assets/skills/oat-project-complete/scripts/validate-nonarchive-lifecycle-receipt.mjs +126 -95
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/scripts/capture-dirty-tree.mjs +3 -0
- package/assets/skills/recon/SKILL.md +37 -19
- package/assets/skills/recon/references/packet-contract.md +33 -30
- package/assets/skills/recon/references/profiles.md +18 -15
- package/assets/skills/recon/references/worker-contract.md +87 -7
- package/assets/skills/recon/scripts/create-review-brief.mjs +2 -5
- package/assets/skills/recon/scripts/lib/cli-entry.mjs +21 -0
- package/assets/skills/recon/scripts/lib/contracts.mjs +144 -38
- package/assets/skills/recon/scripts/lib/routing.mjs +2 -5
- package/assets/skills/recon/scripts/lib/validated-run.mjs +1 -1
- package/assets/skills/recon/scripts/prepare-routing.mjs +2 -2
- package/assets/skills/recon/scripts/reconcile-ledger.mjs +269 -2
- package/assets/skills/recon/scripts/render-packet.mjs +2 -5
- package/assets/skills/recon/scripts/validate-artifact.mjs +2 -5
- package/assets/skills/recon/scripts/validate-packet.mjs +73 -23
- package/package.json +2 -2
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import {
|
|
5
|
+
link as linkFile,
|
|
6
|
+
open,
|
|
7
|
+
readFile,
|
|
8
|
+
realpath,
|
|
9
|
+
unlink,
|
|
10
|
+
} from 'node:fs/promises';
|
|
11
|
+
import { dirname, resolve } from 'node:path';
|
|
12
|
+
|
|
13
|
+
import { canonicalJson, hashFile } from './lib/canonical-json.mjs';
|
|
14
|
+
import { isDirectExecution } from './lib/cli-entry.mjs';
|
|
2
15
|
import { validateArtifactShape } from './lib/contracts.mjs';
|
|
16
|
+
import {
|
|
17
|
+
assertCanonicalRoot,
|
|
18
|
+
assertSafeExistingPath,
|
|
19
|
+
assertSafeOutputPath,
|
|
20
|
+
assertUnchangedRoot,
|
|
21
|
+
} from './lib/safe-path.mjs';
|
|
3
22
|
|
|
4
23
|
const requiredDispositions = new Map([
|
|
5
24
|
['semantic', 'affirmed'],
|
|
@@ -42,7 +61,7 @@ export function reconcileLedger({
|
|
|
42
61
|
reviewResults,
|
|
43
62
|
priorReference,
|
|
44
63
|
runId = priorLedger?.runId,
|
|
45
|
-
reviewerLane = '
|
|
64
|
+
reviewerLane = 'controller:reconcile-ledger-v1',
|
|
46
65
|
}) {
|
|
47
66
|
if (!priorLedger || priorLedger.runId !== runId || priorLedger.revision < 1) {
|
|
48
67
|
throw new Error(
|
|
@@ -358,3 +377,251 @@ export function reconcileLedger({
|
|
|
358
377
|
}
|
|
359
378
|
return { ledger, reconciliation };
|
|
360
379
|
}
|
|
380
|
+
|
|
381
|
+
function parseArgs(argv) {
|
|
382
|
+
const values = { review: [] };
|
|
383
|
+
for (let index = 0; index < argv.length; index += 2) {
|
|
384
|
+
const key = argv[index]?.replace(/^--/, '');
|
|
385
|
+
const value = argv[index + 1];
|
|
386
|
+
if (!key || value === undefined)
|
|
387
|
+
throw new Error('Invalid reconciliation arguments');
|
|
388
|
+
if (key === 'review') values.review.push(value);
|
|
389
|
+
else values[key] = value;
|
|
390
|
+
}
|
|
391
|
+
for (const key of [
|
|
392
|
+
'manifest',
|
|
393
|
+
'input-ledger',
|
|
394
|
+
'output-ledger',
|
|
395
|
+
'output-review',
|
|
396
|
+
]) {
|
|
397
|
+
if (!values[key]) throw new Error(`Missing --${key}`);
|
|
398
|
+
}
|
|
399
|
+
return values;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function removeCreatedPath(path) {
|
|
403
|
+
try {
|
|
404
|
+
await unlink(path);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
if (error?.code !== 'ENOENT') throw error;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function stageOutput(path, value) {
|
|
411
|
+
const temporary = `${path}.tmp-${process.pid}-${randomUUID()}`;
|
|
412
|
+
const handle = await open(temporary, 'wx', 0o600);
|
|
413
|
+
let closed = false;
|
|
414
|
+
try {
|
|
415
|
+
await handle.writeFile(`${canonicalJson(value)}\n`, 'utf8');
|
|
416
|
+
await handle.sync();
|
|
417
|
+
await handle.close();
|
|
418
|
+
closed = true;
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if (!closed) await handle.close().catch(() => {});
|
|
421
|
+
await removeCreatedPath(temporary).catch(() => {});
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
424
|
+
return temporary;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
async function writeAtomicPair(outputs, packetIdentity) {
|
|
428
|
+
const staged = [];
|
|
429
|
+
const published = [];
|
|
430
|
+
try {
|
|
431
|
+
for (const output of outputs) {
|
|
432
|
+
staged.push({
|
|
433
|
+
...output,
|
|
434
|
+
temporary: await stageOutput(output.path, output.value),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
await assertUnchangedRoot(packetIdentity);
|
|
438
|
+
for (const output of staged) {
|
|
439
|
+
await linkFile(output.temporary, output.path);
|
|
440
|
+
published.push(output.path);
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
443
|
+
const cleanupErrors = [];
|
|
444
|
+
for (const path of published.reverse()) {
|
|
445
|
+
try {
|
|
446
|
+
await removeCreatedPath(path);
|
|
447
|
+
} catch (cleanupError) {
|
|
448
|
+
cleanupErrors.push(cleanupError);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
for (const { temporary } of staged) {
|
|
452
|
+
try {
|
|
453
|
+
await removeCreatedPath(temporary);
|
|
454
|
+
} catch (cleanupError) {
|
|
455
|
+
cleanupErrors.push(cleanupError);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (cleanupErrors.length > 0) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
`Reconciliation output publication failed and ${cleanupErrors.length} cleanup operation(s) also failed`,
|
|
461
|
+
{ cause: error },
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
throw error;
|
|
465
|
+
}
|
|
466
|
+
for (const { temporary } of staged) {
|
|
467
|
+
await removeCreatedPath(temporary);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
async function main(argv = process.argv.slice(2)) {
|
|
472
|
+
const options = parseArgs(argv);
|
|
473
|
+
const manifestPath = await realpath(options.manifest);
|
|
474
|
+
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
|
|
475
|
+
const manifestValidation = validateArtifactShape(manifest);
|
|
476
|
+
if (!manifestValidation.valid) {
|
|
477
|
+
throw new Error(
|
|
478
|
+
`Reconciliation rejects invalid manifest: ${manifestValidation.errors
|
|
479
|
+
.map((error) => error.code)
|
|
480
|
+
.join(', ')}`,
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
const declaration = manifest.execution?.reconciliation;
|
|
484
|
+
if (
|
|
485
|
+
!declaration ||
|
|
486
|
+
declaration.producer !== 'controller:reconcile-ledger-v1'
|
|
487
|
+
) {
|
|
488
|
+
throw new Error(
|
|
489
|
+
'Manifest does not authorize controller:reconcile-ledger-v1',
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
const artifacts = manifest.artifacts ?? [];
|
|
493
|
+
const packetRoot = dirname(manifestPath);
|
|
494
|
+
const packetIdentity = await assertCanonicalRoot(packetRoot);
|
|
495
|
+
await assertSafeExistingPath(packetRoot, resolve(options.manifest));
|
|
496
|
+
const expected = (path) => resolve(packetRoot, path);
|
|
497
|
+
const resolvedInputs = new Map();
|
|
498
|
+
const resolvedOutputs = new Map();
|
|
499
|
+
for (const [option, field] of [
|
|
500
|
+
['input-ledger', 'inputLedger'],
|
|
501
|
+
['output-ledger', 'outputLedger'],
|
|
502
|
+
['output-review', 'outputReview'],
|
|
503
|
+
]) {
|
|
504
|
+
if (resolve(options[option]) !== expected(declaration[field])) {
|
|
505
|
+
throw new Error(
|
|
506
|
+
`Reconciliation ${option} drifts from the manifest declaration`,
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
if (option === 'input-ledger') {
|
|
510
|
+
await assertSafeExistingPath(packetRoot, resolve(options[option]));
|
|
511
|
+
resolvedInputs.set(option, await realpath(options[option]));
|
|
512
|
+
} else {
|
|
513
|
+
const outputPath = resolve(options[option]);
|
|
514
|
+
await assertSafeExistingPath(packetRoot, dirname(outputPath));
|
|
515
|
+
await assertSafeOutputPath(packetRoot, outputPath);
|
|
516
|
+
resolvedOutputs.set(option, outputPath);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
const declaredReviews = [
|
|
520
|
+
...(declaration.requiredReviews ?? []),
|
|
521
|
+
...(declaration.conditionalReviews ?? []).filter((path) =>
|
|
522
|
+
artifacts.some((reference) => reference.path === path),
|
|
523
|
+
),
|
|
524
|
+
];
|
|
525
|
+
const declaredReviewInputs = await Promise.all(
|
|
526
|
+
declaredReviews.map(async (path) => {
|
|
527
|
+
const reference = artifacts.find((artifact) => artifact.path === path);
|
|
528
|
+
if (!reference) {
|
|
529
|
+
throw new Error(
|
|
530
|
+
`Reconciliation review ${path} has no manifest artifact reference`,
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
return { path: await realpath(expected(path)), reference };
|
|
534
|
+
}),
|
|
535
|
+
);
|
|
536
|
+
const declaredByPath = new Map(
|
|
537
|
+
declaredReviewInputs.map((input) => [input.path, input.reference]),
|
|
538
|
+
);
|
|
539
|
+
const suppliedReviews = await Promise.all(
|
|
540
|
+
options.review.map(async (path) => {
|
|
541
|
+
await assertSafeExistingPath(packetRoot, resolve(path));
|
|
542
|
+
return realpath(path);
|
|
543
|
+
}),
|
|
544
|
+
);
|
|
545
|
+
if (
|
|
546
|
+
new Set(suppliedReviews).size !== suppliedReviews.length ||
|
|
547
|
+
suppliedReviews.length !== declaredByPath.size ||
|
|
548
|
+
suppliedReviews.some((path) => !declaredByPath.has(path))
|
|
549
|
+
) {
|
|
550
|
+
throw new Error(
|
|
551
|
+
'Reconciliation review set drifts from the manifest declaration',
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
const priorLedger = JSON.parse(
|
|
555
|
+
await readFile(resolvedInputs.get('input-ledger'), 'utf8'),
|
|
556
|
+
);
|
|
557
|
+
const priorReference = artifacts.find(
|
|
558
|
+
(reference) => reference.path === declaration.inputLedger,
|
|
559
|
+
);
|
|
560
|
+
if (
|
|
561
|
+
!priorReference ||
|
|
562
|
+
(await hashFile(resolvedInputs.get('input-ledger'))) !==
|
|
563
|
+
priorReference.digest
|
|
564
|
+
) {
|
|
565
|
+
throw new Error(
|
|
566
|
+
'Reconciliation input ledger does not match its manifest digest',
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
const reviewResults = await Promise.all(
|
|
570
|
+
declaredReviewInputs.map(async ({ path, reference: artifactReference }) => {
|
|
571
|
+
if ((await hashFile(path)) !== artifactReference.digest) {
|
|
572
|
+
throw new Error(
|
|
573
|
+
`Reconciliation review ${artifactReference.path} does not match its manifest digest`,
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
const value = JSON.parse(await readFile(path, 'utf8'));
|
|
577
|
+
return {
|
|
578
|
+
...value,
|
|
579
|
+
artifactReference,
|
|
580
|
+
};
|
|
581
|
+
}),
|
|
582
|
+
);
|
|
583
|
+
const result = reconcileLedger({
|
|
584
|
+
priorLedger,
|
|
585
|
+
reviewResults,
|
|
586
|
+
priorReference,
|
|
587
|
+
runId: manifest.run.id,
|
|
588
|
+
reviewerLane: declaration.producer,
|
|
589
|
+
});
|
|
590
|
+
await assertUnchangedRoot(packetIdentity);
|
|
591
|
+
await writeAtomicPair(
|
|
592
|
+
[
|
|
593
|
+
{
|
|
594
|
+
path: resolvedOutputs.get('output-ledger'),
|
|
595
|
+
value: result.ledger,
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
path: resolvedOutputs.get('output-review'),
|
|
599
|
+
value: result.reconciliation,
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
packetIdentity,
|
|
603
|
+
);
|
|
604
|
+
process.stdout.write(
|
|
605
|
+
`${JSON.stringify(
|
|
606
|
+
{
|
|
607
|
+
ledger: {
|
|
608
|
+
path: declaration.outputLedger,
|
|
609
|
+
digest: await hashFile(resolvedOutputs.get('output-ledger')),
|
|
610
|
+
},
|
|
611
|
+
reconciliation: {
|
|
612
|
+
path: declaration.outputReview,
|
|
613
|
+
digest: await hashFile(resolvedOutputs.get('output-review')),
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
null,
|
|
617
|
+
2,
|
|
618
|
+
)}\n`,
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (isDirectExecution(import.meta.url)) {
|
|
623
|
+
main().catch((error) => {
|
|
624
|
+
process.stderr.write(`${error instanceof Error ? error.message : error}\n`);
|
|
625
|
+
process.exitCode = 1;
|
|
626
|
+
});
|
|
627
|
+
}
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
import { lstat, open, rename, rm } from 'node:fs/promises';
|
|
5
5
|
import { join, resolve } from 'node:path';
|
|
6
|
-
import { pathToFileURL } from 'node:url';
|
|
7
6
|
|
|
8
7
|
import { hashFile } from './lib/canonical-json.mjs';
|
|
8
|
+
import { isDirectExecution } from './lib/cli-entry.mjs';
|
|
9
9
|
import {
|
|
10
10
|
assertSafeExistingPath,
|
|
11
11
|
assertSafeOutputPath,
|
|
@@ -436,10 +436,7 @@ async function main(argv) {
|
|
|
436
436
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
if (
|
|
440
|
-
process.argv[1] &&
|
|
441
|
-
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
442
|
-
) {
|
|
439
|
+
if (isDirectExecution(import.meta.url)) {
|
|
443
440
|
main(process.argv.slice(2)).catch((error) => {
|
|
444
441
|
process.stderr.write(`${error instanceof Error ? error.message : error}\n`);
|
|
445
442
|
process.exitCode = 1;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { basename, join, resolve } from 'node:path';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
5
|
|
|
6
|
+
import { isDirectExecution } from './lib/cli-entry.mjs';
|
|
7
7
|
import { validateArtifactShape } from './lib/contracts.mjs';
|
|
8
8
|
import {
|
|
9
9
|
assertSafeExistingPath,
|
|
@@ -85,10 +85,7 @@ async function main(argv) {
|
|
|
85
85
|
process.exitCode = result.valid ? 0 : 1;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
if (
|
|
89
|
-
process.argv[1] &&
|
|
90
|
-
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
91
|
-
) {
|
|
88
|
+
if (isDirectExecution(import.meta.url)) {
|
|
92
89
|
main(process.argv.slice(2)).catch((error) => {
|
|
93
90
|
process.stderr.write(`${error instanceof Error ? error.message : error}\n`);
|
|
94
91
|
process.exitCode = 2;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { readFile, rm } from 'node:fs/promises';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
6
5
|
|
|
7
6
|
import { hashCanonicalJson, hashFile, sha256 } from './lib/canonical-json.mjs';
|
|
7
|
+
import { isDirectExecution } from './lib/cli-entry.mjs';
|
|
8
8
|
import {
|
|
9
9
|
isDigest,
|
|
10
10
|
isObject,
|
|
@@ -29,7 +29,6 @@ const requiredPasses = {
|
|
|
29
29
|
'semantic-verification',
|
|
30
30
|
'adversarial',
|
|
31
31
|
'coverage',
|
|
32
|
-
'reconciliation',
|
|
33
32
|
],
|
|
34
33
|
thorough: [
|
|
35
34
|
'map',
|
|
@@ -37,7 +36,6 @@ const requiredPasses = {
|
|
|
37
36
|
'semantic-verification',
|
|
38
37
|
'adversarial',
|
|
39
38
|
'coverage',
|
|
40
|
-
'reconciliation',
|
|
41
39
|
'redundant-gather',
|
|
42
40
|
'redundant-verification',
|
|
43
41
|
],
|
|
@@ -674,10 +672,6 @@ const passContracts = {
|
|
|
674
672
|
},
|
|
675
673
|
adversarial: { kind: 'recon.review-result', reviewKind: 'adversarial' },
|
|
676
674
|
coverage: { kind: 'recon.review-result', reviewKind: 'coverage' },
|
|
677
|
-
reconciliation: {
|
|
678
|
-
kind: 'recon.review-result',
|
|
679
|
-
reviewKind: 'reconciliation',
|
|
680
|
-
},
|
|
681
675
|
'redundant-verification': {
|
|
682
676
|
kind: 'recon.review-result',
|
|
683
677
|
reviewKind: 'redundant-verification',
|
|
@@ -746,7 +740,6 @@ const reviewWaveMode = {
|
|
|
746
740
|
semantic: 'semantic-verification',
|
|
747
741
|
adversarial: 'adversarial',
|
|
748
742
|
coverage: 'coverage',
|
|
749
|
-
reconciliation: 'reconciliation',
|
|
750
743
|
'redundant-verification': 'redundant-verification',
|
|
751
744
|
'contradiction-resolution': 'contradiction-resolution',
|
|
752
745
|
};
|
|
@@ -967,6 +960,13 @@ function validateApprovedLanes(
|
|
|
967
960
|
const written = new Set();
|
|
968
961
|
for (const [id, { reference, value }] of artifactsById) {
|
|
969
962
|
if (value.runId !== manifest.run.id) continue;
|
|
963
|
+
if (
|
|
964
|
+
value.kind === 'recon.review-result' &&
|
|
965
|
+
value.reviewKind === 'reconciliation' &&
|
|
966
|
+
value.reviewerLane === 'controller:reconcile-ledger-v1'
|
|
967
|
+
) {
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
970
|
const laneId = artifactLaneId(value);
|
|
971
971
|
if (laneId === null) continue;
|
|
972
972
|
const approved = lanes.get(laneId);
|
|
@@ -1242,33 +1242,68 @@ function sameReference(left, right) {
|
|
|
1242
1242
|
}
|
|
1243
1243
|
|
|
1244
1244
|
function resolveTerminalReconciliation(
|
|
1245
|
+
manifest,
|
|
1245
1246
|
artifactsById,
|
|
1246
1247
|
artifactsByPath,
|
|
1247
|
-
passes,
|
|
1248
1248
|
reconciliationRequired,
|
|
1249
1249
|
errors,
|
|
1250
1250
|
) {
|
|
1251
|
-
const
|
|
1251
|
+
const declaration = manifest.execution?.reconciliation;
|
|
1252
1252
|
const reconciliationResults = [...artifactsById.values()].filter(
|
|
1253
1253
|
({ value }) =>
|
|
1254
1254
|
value.kind === 'recon.review-result' &&
|
|
1255
1255
|
value.reviewKind === 'reconciliation',
|
|
1256
1256
|
);
|
|
1257
1257
|
const expectedCount = reconciliationRequired ? 1 : 0;
|
|
1258
|
-
if (
|
|
1259
|
-
reconciliationResults.length !== expectedCount ||
|
|
1260
|
-
(reconciliationRequired &&
|
|
1261
|
-
reconciliationResults[0]?.value.id !== terminalId)
|
|
1262
|
-
) {
|
|
1258
|
+
if (reconciliationResults.length !== expectedCount) {
|
|
1263
1259
|
errors.push(
|
|
1264
1260
|
issue(
|
|
1265
1261
|
'SHADOW_RECONCILIATION',
|
|
1266
1262
|
'The packet must contain exactly its one complete terminal reconciliation',
|
|
1267
|
-
|
|
1263
|
+
declaration?.outputReview ?? '$.artifacts',
|
|
1264
|
+
),
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
1267
|
+
const entry = declaration
|
|
1268
|
+
? artifactsByPath.get(declaration.outputReview)
|
|
1269
|
+
: null;
|
|
1270
|
+
const outputLedgerEntry = declaration
|
|
1271
|
+
? artifactsByPath.get(declaration.outputLedger)
|
|
1272
|
+
: null;
|
|
1273
|
+
const reconciliation = entry?.value ?? null;
|
|
1274
|
+
if (
|
|
1275
|
+
reconciliationRequired &&
|
|
1276
|
+
(!entry ||
|
|
1277
|
+
reconciliation.reviewKind !== 'reconciliation' ||
|
|
1278
|
+
reconciliation.reviewerLane !== declaration.producer ||
|
|
1279
|
+
!sameReference(
|
|
1280
|
+
entry.reference,
|
|
1281
|
+
manifest.artifacts.find(
|
|
1282
|
+
(item) => item.path === declaration.outputReview,
|
|
1283
|
+
),
|
|
1284
|
+
))
|
|
1285
|
+
) {
|
|
1286
|
+
errors.push(
|
|
1287
|
+
issue(
|
|
1288
|
+
'INVALID_RECONCILIATION_BINDING',
|
|
1289
|
+
'Reconciliation result must match the manifest-bound controller producer and output path',
|
|
1290
|
+
declaration?.outputReview ?? '$.execution.reconciliation',
|
|
1291
|
+
),
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
if (
|
|
1295
|
+
reconciliationRequired &&
|
|
1296
|
+
(!outputLedgerEntry ||
|
|
1297
|
+
outputLedgerEntry.value.kind !== 'recon.claim-ledger')
|
|
1298
|
+
) {
|
|
1299
|
+
errors.push(
|
|
1300
|
+
issue(
|
|
1301
|
+
'INVALID_RECONCILIATION_OUTPUT',
|
|
1302
|
+
'Controller reconciliation must produce its manifest-declared ledger candidate',
|
|
1303
|
+
declaration?.outputLedger ?? '$.execution.reconciliation',
|
|
1268
1304
|
),
|
|
1269
1305
|
);
|
|
1270
1306
|
}
|
|
1271
|
-
const reconciliation = artifactsById.get(terminalId)?.value ?? null;
|
|
1272
1307
|
const priorLedger = reconciliation
|
|
1273
1308
|
? ([...artifactsByPath.values()].find(
|
|
1274
1309
|
({ reference, value }) =>
|
|
@@ -1276,7 +1311,11 @@ function resolveTerminalReconciliation(
|
|
|
1276
1311
|
value.kind === 'recon.claim-ledger',
|
|
1277
1312
|
)?.value ?? null)
|
|
1278
1313
|
: null;
|
|
1279
|
-
return {
|
|
1314
|
+
return {
|
|
1315
|
+
reconciliation,
|
|
1316
|
+
priorLedger,
|
|
1317
|
+
outputLedger: outputLedgerEntry?.value ?? null,
|
|
1318
|
+
};
|
|
1280
1319
|
}
|
|
1281
1320
|
|
|
1282
1321
|
function reviewBriefBindsClaim(brief, reviewKind, claim, ledger, manifest) {
|
|
@@ -2461,12 +2500,26 @@ export async function compileValidatedRun(packetDirectory) {
|
|
|
2461
2500
|
const reconciliationRequired =
|
|
2462
2501
|
achievedProfile === 'standard' || achievedProfile === 'thorough';
|
|
2463
2502
|
const reconciliationContext = resolveTerminalReconciliation(
|
|
2503
|
+
manifest,
|
|
2464
2504
|
artifactsById,
|
|
2465
2505
|
artifactsByPath,
|
|
2466
|
-
passes,
|
|
2467
2506
|
reconciliationRequired,
|
|
2468
2507
|
errors,
|
|
2469
2508
|
);
|
|
2509
|
+
if (
|
|
2510
|
+
reconciliationRequired &&
|
|
2511
|
+
reconciliationContext.outputLedger &&
|
|
2512
|
+
hashCanonicalJson(reconciliationContext.outputLedger) !==
|
|
2513
|
+
hashCanonicalJson(ledger)
|
|
2514
|
+
) {
|
|
2515
|
+
errors.push(
|
|
2516
|
+
issue(
|
|
2517
|
+
'RECONCILIATION_PROMOTION_MISMATCH',
|
|
2518
|
+
'Canonical claims.json must exactly match the validated controller ledger candidate',
|
|
2519
|
+
'$.claims',
|
|
2520
|
+
),
|
|
2521
|
+
);
|
|
2522
|
+
}
|
|
2470
2523
|
if (
|
|
2471
2524
|
routing &&
|
|
2472
2525
|
(!reconciliationRequired || reconciliationContext.priorLedger)
|
|
@@ -2598,10 +2651,7 @@ async function main(argv) {
|
|
|
2598
2651
|
process.exitCode = result.publishable ? 0 : 1;
|
|
2599
2652
|
}
|
|
2600
2653
|
|
|
2601
|
-
if (
|
|
2602
|
-
process.argv[1] &&
|
|
2603
|
-
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
2604
|
-
) {
|
|
2654
|
+
if (isDirectExecution(import.meta.url)) {
|
|
2605
2655
|
main(process.argv.slice(2)).catch((error) => {
|
|
2606
2656
|
process.stderr.write(`${error instanceof Error ? error.message : error}\n`);
|
|
2607
2657
|
process.exitCode = 2;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.79",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Open Agent Toolkit CLI",
|
|
6
6
|
"homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"ora": "^9.0.0",
|
|
35
35
|
"yaml": "2.8.2",
|
|
36
36
|
"zod": "^3.25.76",
|
|
37
|
-
"@open-agent-toolkit/control-plane": "0.2.
|
|
37
|
+
"@open-agent-toolkit/control-plane": "0.2.79"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|