@jmanuelcorral/openteam 0.1.41 → 0.2.0
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/.opencode/openteam.example.json +6 -1
- package/README.es.md +1 -1
- package/README.md +1 -1
- package/dist/capabilities/types.d.ts +4 -4
- package/dist/classifier/types.d.ts +3 -3
- package/dist/cli.js +430 -36
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/graph.d.ts +36 -2
- package/dist/commands/graph.d.ts.map +1 -1
- package/dist/config/graphFeatureGate.d.ts +96 -0
- package/dist/config/graphFeatureGate.d.ts.map +1 -0
- package/dist/config/migrations.d.ts.map +1 -1
- package/dist/config/resolve.d.ts +20 -1
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/schema.d.ts +40 -19
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/console/assets.d.ts +9 -1
- package/dist/console/assets.d.ts.map +1 -1
- package/dist/console/opencodeClient.d.ts +6 -0
- package/dist/console/opencodeClient.d.ts.map +1 -1
- package/dist/console/protocol.d.ts +8 -0
- package/dist/console/protocol.d.ts.map +1 -1
- package/dist/console/render.d.ts +25 -0
- package/dist/console/render.d.ts.map +1 -1
- package/dist/context/redaction.d.ts +23 -0
- package/dist/context/redaction.d.ts.map +1 -1
- package/dist/context/schema.d.ts +3 -3
- package/dist/contract/opencode.d.ts +2 -2
- package/dist/contract/opencode.d.ts.map +1 -1
- package/dist/graph/certificate.d.ts +505 -0
- package/dist/graph/certificate.d.ts.map +1 -0
- package/dist/graph/ids.d.ts +9 -0
- package/dist/graph/ids.d.ts.map +1 -1
- package/dist/graph/privacyPolicy.d.ts.map +1 -1
- package/dist/graph/reducer.d.ts.map +1 -1
- package/dist/graph/replay.d.ts.map +1 -1
- package/dist/graph/reviewerPolicy.d.ts.map +1 -1
- package/dist/graph/schema.d.ts +33 -33
- package/dist/graph/schema.d.ts.map +1 -1
- package/dist/graph/soakLedger.d.ts +144 -0
- package/dist/graph/soakLedger.d.ts.map +1 -0
- package/dist/graph/types.d.ts +2 -0
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/graph/validate.d.ts.map +1 -1
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2351 -180
- package/dist/local/registry.d.ts.map +1 -1
- package/dist/memory/types.d.ts +2 -2
- package/dist/orchestrator/graphFacade.d.ts +39 -0
- package/dist/orchestrator/graphFacade.d.ts.map +1 -0
- package/dist/orchestrator/graphIngress.d.ts +136 -0
- package/dist/orchestrator/graphIngress.d.ts.map +1 -0
- package/dist/orchestrator/graphShadow.d.ts +121 -0
- package/dist/orchestrator/graphShadow.d.ts.map +1 -0
- package/dist/orchestrator/ledger.d.ts +14 -2
- package/dist/orchestrator/ledger.d.ts.map +1 -1
- package/dist/orchestrator/opencodeSessionAdapter.d.ts.map +1 -1
- package/dist/orchestrator/roles.d.ts +3 -3
- package/dist/orchestrator/sdd.d.ts +55 -1
- package/dist/orchestrator/sdd.d.ts.map +1 -1
- package/dist/orchestrator/sddCompiler.d.ts +6 -2
- package/dist/orchestrator/sddCompiler.d.ts.map +1 -1
- package/dist/orchestrator/worktreeAdapter.d.ts +39 -0
- package/dist/orchestrator/worktreeAdapter.d.ts.map +1 -0
- package/dist/orchestrator/worktreeDispatch.d.ts +84 -0
- package/dist/orchestrator/worktreeDispatch.d.ts.map +1 -0
- package/dist/orchestrator/worktreeReconciler.d.ts +74 -0
- package/dist/orchestrator/worktreeReconciler.d.ts.map +1 -0
- package/dist/plugin/graphShadowIngress.d.ts +165 -0
- package/dist/plugin/graphShadowIngress.d.ts.map +1 -0
- package/dist/plugin/graphTool.d.ts +63 -0
- package/dist/plugin/graphTool.d.ts.map +1 -0
- package/dist/plugin/hooks.d.ts +6 -0
- package/dist/plugin/hooks.d.ts.map +1 -1
- package/dist/plugin/legacyRunProducer.d.ts +60 -0
- package/dist/plugin/legacyRunProducer.d.ts.map +1 -0
- package/dist/plugin/reportRunSchema.d.ts +60 -0
- package/dist/plugin/reportRunSchema.d.ts.map +1 -0
- package/dist/plugin/sessionTracker.d.ts +42 -0
- package/dist/plugin/sessionTracker.d.ts.map +1 -0
- package/dist/plugin/shadowPipeline.d.ts +49 -0
- package/dist/plugin/shadowPipeline.d.ts.map +1 -0
- package/dist/plugin/soakObserver.d.ts +73 -0
- package/dist/plugin/soakObserver.d.ts.map +1 -0
- package/dist/storage/graph/codec.d.ts.map +1 -1
- package/dist/storage/graph/migrateLegacyBriefs.d.ts.map +1 -1
- package/dist/storage/graph/migration.d.ts +91 -0
- package/dist/storage/graph/migration.d.ts.map +1 -0
- package/dist/storage/graph/provider.d.ts +1 -0
- package/dist/storage/graph/provider.d.ts.map +1 -1
- package/dist/storage/graph/recovery.d.ts.map +1 -1
- package/dist/storage/graph/runRegistry.d.ts.map +1 -1
- package/dist/storage/graph/soakRecorder.d.ts +59 -0
- package/dist/storage/graph/soakRecorder.d.ts.map +1 -0
- package/dist/storage/graph/workspaceLease.d.ts.map +1 -1
- package/dist/storage/graph/worktreeRegistry.d.ts +29 -0
- package/dist/storage/graph/worktreeRegistry.d.ts.map +1 -0
- package/dist/storage/graph/writer.d.ts +15 -0
- package/dist/storage/graph/writer.d.ts.map +1 -1
- package/dist/storage/path.d.ts.map +1 -1
- package/dist/telemetry/events.d.ts +12 -12
- package/dist/telemetry/graphRuntimeHealth.d.ts +53 -0
- package/dist/telemetry/graphRuntimeHealth.d.ts.map +1 -0
- package/dist/telemetry/graphView.d.ts +76 -0
- package/dist/telemetry/graphView.d.ts.map +1 -0
- package/dist/telemetry/types.d.ts +4 -4
- package/dist/web/console.d.ts +6 -0
- package/dist/web/console.d.ts.map +1 -1
- package/dist/web/graphRoute.d.ts +36 -0
- package/dist/web/graphRoute.d.ts.map +1 -0
- package/dist/web/server.d.ts +21 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/start.d.ts +11 -0
- package/dist/web/start.d.ts.map +1 -1
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -339,7 +339,11 @@ var BaselineModeSchema = z.enum(["auto", "pinned"]);
|
|
|
339
339
|
var GraphModeSchema = z.enum(["off", "shadow", "active"]);
|
|
340
340
|
var GraphConfigSchema = z.object({
|
|
341
341
|
mode: GraphModeSchema.default("off"),
|
|
342
|
-
killSwitch: z.boolean().default(false)
|
|
342
|
+
killSwitch: z.boolean().default(false),
|
|
343
|
+
operatorApproval: z.boolean().default(false),
|
|
344
|
+
worktrees: z.object({
|
|
345
|
+
enabled: z.boolean().default(false)
|
|
346
|
+
}).default({ enabled: false })
|
|
343
347
|
});
|
|
344
348
|
var ConsoleHostSchema = z.enum(["127.0.0.1", "localhost"]);
|
|
345
349
|
var ConsoleConfigSchema = z.object({
|
|
@@ -353,7 +357,10 @@ var ConsoleConfigSchema = z.object({
|
|
|
353
357
|
terminal: z.object({
|
|
354
358
|
enabled: z.boolean().default(true),
|
|
355
359
|
pty: z.boolean().optional()
|
|
356
|
-
}).default({ enabled: true })
|
|
360
|
+
}).default({ enabled: true }),
|
|
361
|
+
graphView: z.object({
|
|
362
|
+
enabled: z.boolean().default(false)
|
|
363
|
+
}).optional()
|
|
357
364
|
}).default({
|
|
358
365
|
host: "127.0.0.1",
|
|
359
366
|
port: 4599,
|
|
@@ -504,6 +511,59 @@ async function writeOpenTeamConfigFile(config, path = DEFAULT_CONFIG_PATH, stora
|
|
|
504
511
|
// src/commands/setup.ts
|
|
505
512
|
var OPENCODE_CONFIG_PATH = "opencode.json";
|
|
506
513
|
|
|
514
|
+
// src/config/graphFeatureGate.ts
|
|
515
|
+
function checkCertificate(status, name) {
|
|
516
|
+
if (status.status === "absent") {
|
|
517
|
+
return {
|
|
518
|
+
code: `${name}-certificate-absent`,
|
|
519
|
+
detail: `${name} certificate not found — run the ${name} certification suite to produce it`
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
if (status.status === "invalid") {
|
|
523
|
+
return {
|
|
524
|
+
code: `${name}-certificate-invalid`,
|
|
525
|
+
detail: `${name} certificate rejected by recomputing parser: ${status.code} — ${status.detail}`
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
function evaluateGraphGate(input) {
|
|
531
|
+
const violations = [];
|
|
532
|
+
const shadowV = checkCertificate(input.shadow, "shadow");
|
|
533
|
+
if (shadowV !== undefined)
|
|
534
|
+
violations.push(shadowV);
|
|
535
|
+
const releaseV = checkCertificate(input.release, "release");
|
|
536
|
+
if (releaseV !== undefined)
|
|
537
|
+
violations.push(releaseV);
|
|
538
|
+
const soakV = checkCertificate(input.soak, "soak");
|
|
539
|
+
if (soakV !== undefined)
|
|
540
|
+
violations.push(soakV);
|
|
541
|
+
if (input.migration === "pending") {
|
|
542
|
+
violations.push({
|
|
543
|
+
code: "migration-pending",
|
|
544
|
+
detail: "legacy migration has pending work — run the migration tool to completion before enabling active mode"
|
|
545
|
+
});
|
|
546
|
+
} else if (input.migration === "unknown") {
|
|
547
|
+
violations.push({
|
|
548
|
+
code: "migration-unknown",
|
|
549
|
+
detail: "could not determine migration status — check ledger readability and storage access"
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (!input.operatorApproval) {
|
|
553
|
+
violations.push({
|
|
554
|
+
code: "operator-approval-missing",
|
|
555
|
+
detail: "set graph.operatorApproval to true in .opencode/openteam.json after reviewing all certificates and completing migration — this is a declaration of intent, not a proof"
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
allowed: violations.length === 0,
|
|
560
|
+
violations
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function isGateApplicable(mode) {
|
|
564
|
+
return mode === "active";
|
|
565
|
+
}
|
|
566
|
+
|
|
507
567
|
// src/config/migrations.ts
|
|
508
568
|
var CURRENT_CONFIG_VERSION = 1;
|
|
509
569
|
|
|
@@ -540,6 +600,725 @@ function loadOpenTeamConfig(raw) {
|
|
|
540
600
|
return OpenTeamConfigSchema.parse(migrateConfig(raw ?? {}));
|
|
541
601
|
}
|
|
542
602
|
|
|
603
|
+
// src/config/resolve.ts
|
|
604
|
+
function resolveGraphConfig(config, source) {
|
|
605
|
+
const configuredMode = config.graph?.mode ?? "off";
|
|
606
|
+
const killSwitch = config.graph?.killSwitch ?? false;
|
|
607
|
+
const effectiveMode = killSwitch ? "off" : configuredMode;
|
|
608
|
+
return {
|
|
609
|
+
configuredMode,
|
|
610
|
+
effectiveMode,
|
|
611
|
+
executorEnabled: effectiveMode === "active",
|
|
612
|
+
killSwitch,
|
|
613
|
+
source
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function applyGraphGate(resolved, gateResult) {
|
|
617
|
+
if (gateResult.allowed) {
|
|
618
|
+
return { ...resolved, gateDenied: false, gateViolations: [] };
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
...resolved,
|
|
622
|
+
effectiveMode: "off",
|
|
623
|
+
executorEnabled: false,
|
|
624
|
+
gateDenied: true,
|
|
625
|
+
gateViolations: gateResult.violations
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/graph/certificate.ts
|
|
630
|
+
import { z as z3 } from "zod";
|
|
631
|
+
|
|
632
|
+
// src/context/redaction.ts
|
|
633
|
+
import { createHash } from "node:crypto";
|
|
634
|
+
|
|
635
|
+
class RedactionError extends Error {
|
|
636
|
+
code;
|
|
637
|
+
key;
|
|
638
|
+
constructor(key) {
|
|
639
|
+
super(`raw content field is not allowed: "${key}"`);
|
|
640
|
+
this.name = "RedactionError";
|
|
641
|
+
this.code = "raw-content";
|
|
642
|
+
this.key = key;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function sha256Hex(input) {
|
|
646
|
+
return createHash("sha256").update(input, "utf8").digest("hex");
|
|
647
|
+
}
|
|
648
|
+
var FORBIDDEN_RAW_KEYS = new Set([
|
|
649
|
+
"content",
|
|
650
|
+
"text",
|
|
651
|
+
"brief",
|
|
652
|
+
"prompt",
|
|
653
|
+
"raw",
|
|
654
|
+
"body",
|
|
655
|
+
"output",
|
|
656
|
+
"findings",
|
|
657
|
+
"message",
|
|
658
|
+
"source"
|
|
659
|
+
]);
|
|
660
|
+
function walkForbiddenKeys(value, onForbidden) {
|
|
661
|
+
if (Array.isArray(value)) {
|
|
662
|
+
for (const item of value) {
|
|
663
|
+
walkForbiddenKeys(item, onForbidden);
|
|
664
|
+
}
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (value === null || typeof value !== "object") {
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
for (const [key, child] of Object.entries(value)) {
|
|
671
|
+
if (FORBIDDEN_RAW_KEYS.has(key)) {
|
|
672
|
+
const shouldStop = onForbidden(key);
|
|
673
|
+
if (shouldStop)
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
walkForbiddenKeys(child, onForbidden);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function assertReferenceOnly(value) {
|
|
680
|
+
walkForbiddenKeys(value, (key) => {
|
|
681
|
+
throw new RedactionError(key);
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
function scanReferenceOnly(surfaces) {
|
|
685
|
+
let rawFindings = 0;
|
|
686
|
+
for (const surface of surfaces) {
|
|
687
|
+
walkForbiddenKeys(surface, () => {
|
|
688
|
+
rawFindings++;
|
|
689
|
+
return false;
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
return { surfacesScanned: surfaces.length, rawFindings };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// src/graph/soakLedger.ts
|
|
696
|
+
import { z as z2 } from "zod";
|
|
697
|
+
var Sha256Schema = z2.string().regex(/^[0-9a-f]{64}$/);
|
|
698
|
+
var SoakPlatformSchema = z2.enum(["linux", "win32", "darwin"]);
|
|
699
|
+
var PrivacyPairSchema = z2.object({
|
|
700
|
+
surfacesScanned: z2.number().int().nonnegative(),
|
|
701
|
+
rawFindings: z2.number().int().nonnegative()
|
|
702
|
+
}).strict();
|
|
703
|
+
var DuplicateEffectPairSchema = z2.object({
|
|
704
|
+
effectsExamined: z2.number().int().nonnegative(),
|
|
705
|
+
duplicatesFound: z2.number().int().nonnegative()
|
|
706
|
+
}).strict();
|
|
707
|
+
var ModelVerificationPairSchema = z2.object({
|
|
708
|
+
nodesChecked: z2.number().int().nonnegative(),
|
|
709
|
+
unverified: z2.number().int().nonnegative(),
|
|
710
|
+
mismatched: z2.number().int().nonnegative()
|
|
711
|
+
}).strict();
|
|
712
|
+
var SoakObservationSchema = z2.object({
|
|
713
|
+
version: z2.literal(1),
|
|
714
|
+
seq: z2.number().int().nonnegative(),
|
|
715
|
+
timestamp: z2.string().datetime(),
|
|
716
|
+
platform: SoakPlatformSchema,
|
|
717
|
+
opencodeVersion: z2.string().min(1),
|
|
718
|
+
provenance: z2.enum(["genuine-usage", "ci-synthetic"]),
|
|
719
|
+
traceDigest: Sha256Schema,
|
|
720
|
+
criticalDivergences: z2.number().int().nonnegative().nullable(),
|
|
721
|
+
privacy: PrivacyPairSchema.nullable(),
|
|
722
|
+
duplicateEffects: DuplicateEffectPairSchema.nullable(),
|
|
723
|
+
modelVerification: ModelVerificationPairSchema.nullable(),
|
|
724
|
+
chainDigest: Sha256Schema
|
|
725
|
+
}).strict();
|
|
726
|
+
function canonicalObservationPreimage(obs) {
|
|
727
|
+
return JSON.stringify({
|
|
728
|
+
version: obs.version,
|
|
729
|
+
seq: obs.seq,
|
|
730
|
+
timestamp: obs.timestamp,
|
|
731
|
+
platform: obs.platform,
|
|
732
|
+
opencodeVersion: obs.opencodeVersion,
|
|
733
|
+
provenance: obs.provenance,
|
|
734
|
+
traceDigest: obs.traceDigest,
|
|
735
|
+
criticalDivergences: obs.criticalDivergences,
|
|
736
|
+
privacy: obs.privacy,
|
|
737
|
+
duplicateEffects: obs.duplicateEffects,
|
|
738
|
+
modelVerification: obs.modelVerification
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
function validateChain(observations) {
|
|
742
|
+
if (observations.length === 0) {
|
|
743
|
+
return { valid: true, observations: [] };
|
|
744
|
+
}
|
|
745
|
+
let prevChainDigest = "0".repeat(64);
|
|
746
|
+
for (let i = 0;i < observations.length; i++) {
|
|
747
|
+
const obs = observations[i];
|
|
748
|
+
if (obs.seq !== i) {
|
|
749
|
+
return { valid: false, observations };
|
|
750
|
+
}
|
|
751
|
+
if (i > 0) {
|
|
752
|
+
const prev = observations[i - 1];
|
|
753
|
+
if (obs.timestamp <= prev.timestamp) {
|
|
754
|
+
return { valid: false, observations };
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
const preimage = canonicalObservationPreimage(obs);
|
|
758
|
+
const expectedDigest = sha256Hex(prevChainDigest + preimage);
|
|
759
|
+
if (obs.chainDigest !== expectedDigest) {
|
|
760
|
+
return { valid: false, observations };
|
|
761
|
+
}
|
|
762
|
+
prevChainDigest = obs.chainDigest;
|
|
763
|
+
}
|
|
764
|
+
return { valid: true, observations };
|
|
765
|
+
}
|
|
766
|
+
function canonicalLedger(chains) {
|
|
767
|
+
const validChains = [];
|
|
768
|
+
for (const chain of chains) {
|
|
769
|
+
const result = validateChain(chain);
|
|
770
|
+
if (result.valid && chain.length > 0) {
|
|
771
|
+
validChains.push(chain);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
validChains.sort((a, b) => {
|
|
775
|
+
const aDigest = a[0].chainDigest;
|
|
776
|
+
const bDigest = b[0].chainDigest;
|
|
777
|
+
return aDigest < bDigest ? -1 : aDigest > bDigest ? 1 : 0;
|
|
778
|
+
});
|
|
779
|
+
const lines = [];
|
|
780
|
+
for (const chain of validChains) {
|
|
781
|
+
for (const obs of chain) {
|
|
782
|
+
lines.push(JSON.stringify(obs));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
return sha256Hex(lines.join(`
|
|
786
|
+
`));
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
// src/graph/certificate.ts
|
|
790
|
+
var SHADOW_CERTIFICATE_MIN_RUNS = 1000;
|
|
791
|
+
var DivergenceCodeSchema = z3.enum([
|
|
792
|
+
"status",
|
|
793
|
+
"order",
|
|
794
|
+
"roles",
|
|
795
|
+
"models",
|
|
796
|
+
"fixes",
|
|
797
|
+
"outcome"
|
|
798
|
+
]);
|
|
799
|
+
var ProvenanceSchema = z3.enum([
|
|
800
|
+
"recomputed",
|
|
801
|
+
"compiled",
|
|
802
|
+
"validated",
|
|
803
|
+
"carried"
|
|
804
|
+
]);
|
|
805
|
+
var EvidenceSchema = z3.object({
|
|
806
|
+
opencodeVersion: z3.string().min(1),
|
|
807
|
+
parity: z3.object({
|
|
808
|
+
runs: z3.number().int().nonnegative(),
|
|
809
|
+
match: z3.number().int().nonnegative(),
|
|
810
|
+
divergent: z3.number().int().nonnegative(),
|
|
811
|
+
inconclusive: z3.number().int().nonnegative(),
|
|
812
|
+
divergences: z3.array(DivergenceCodeSchema)
|
|
813
|
+
}).strict(),
|
|
814
|
+
legacy: z3.object({
|
|
815
|
+
observed: z3.number().int().nonnegative(),
|
|
816
|
+
duplicated: z3.number().int().nonnegative()
|
|
817
|
+
}).strict(),
|
|
818
|
+
privacy: z3.object({
|
|
819
|
+
surfacesScanned: z3.number().int().nonnegative(),
|
|
820
|
+
rawFindings: z3.number().int().nonnegative()
|
|
821
|
+
}).strict(),
|
|
822
|
+
overhead: z3.object({
|
|
823
|
+
p95Millis: z3.number().nonnegative(),
|
|
824
|
+
budgetMillis: z3.number().positive(),
|
|
825
|
+
samples: z3.number().int().nonnegative()
|
|
826
|
+
}).strict(),
|
|
827
|
+
provenance: z3.record(DivergenceCodeSchema, ProvenanceSchema)
|
|
828
|
+
}).strict();
|
|
829
|
+
var CertificateSchema = z3.object({
|
|
830
|
+
version: z3.literal(1),
|
|
831
|
+
certificate: z3.literal("graph-shadow"),
|
|
832
|
+
opencodeVersion: z3.string().min(1),
|
|
833
|
+
evidence: EvidenceSchema,
|
|
834
|
+
verdict: z3.enum(["pass", "fail"]),
|
|
835
|
+
failedGates: z3.array(z3.enum([
|
|
836
|
+
"sample",
|
|
837
|
+
"parity",
|
|
838
|
+
"one-legacy-execution",
|
|
839
|
+
"privacy",
|
|
840
|
+
"overhead"
|
|
841
|
+
])),
|
|
842
|
+
digest: z3.string().regex(/^[0-9a-f]{64}$/)
|
|
843
|
+
}).strict();
|
|
844
|
+
|
|
845
|
+
class ShadowCertificateError extends Error {
|
|
846
|
+
code;
|
|
847
|
+
detail;
|
|
848
|
+
constructor(code, detail) {
|
|
849
|
+
super(`${code}: ${detail}`);
|
|
850
|
+
this.name = "ShadowCertificateError";
|
|
851
|
+
this.code = code;
|
|
852
|
+
this.detail = detail;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
function evaluateGates(evidence) {
|
|
856
|
+
const failed = [];
|
|
857
|
+
if (evidence.parity.runs < SHADOW_CERTIFICATE_MIN_RUNS) {
|
|
858
|
+
failed.push("sample");
|
|
859
|
+
}
|
|
860
|
+
if (evidence.parity.divergent > 0 || evidence.parity.inconclusive > 0 || evidence.parity.divergences.length > 0 || evidence.parity.match !== evidence.parity.runs) {
|
|
861
|
+
failed.push("parity");
|
|
862
|
+
}
|
|
863
|
+
if (evidence.legacy.duplicated > 0 || evidence.legacy.observed < 1) {
|
|
864
|
+
failed.push("one-legacy-execution");
|
|
865
|
+
}
|
|
866
|
+
if (evidence.privacy.rawFindings > 0 || evidence.privacy.surfacesScanned < 1) {
|
|
867
|
+
failed.push("privacy");
|
|
868
|
+
}
|
|
869
|
+
if (evidence.overhead.samples < 1 || evidence.overhead.p95Millis > evidence.overhead.budgetMillis) {
|
|
870
|
+
failed.push("overhead");
|
|
871
|
+
}
|
|
872
|
+
return failed;
|
|
873
|
+
}
|
|
874
|
+
function canonicalCertificate(certificate) {
|
|
875
|
+
const evidence = certificate.evidence;
|
|
876
|
+
return JSON.stringify({
|
|
877
|
+
version: certificate.version,
|
|
878
|
+
certificate: certificate.certificate,
|
|
879
|
+
opencodeVersion: certificate.opencodeVersion,
|
|
880
|
+
verdict: certificate.verdict,
|
|
881
|
+
failedGates: [...certificate.failedGates],
|
|
882
|
+
evidence: {
|
|
883
|
+
opencodeVersion: evidence.opencodeVersion,
|
|
884
|
+
parity: {
|
|
885
|
+
runs: evidence.parity.runs,
|
|
886
|
+
match: evidence.parity.match,
|
|
887
|
+
divergent: evidence.parity.divergent,
|
|
888
|
+
inconclusive: evidence.parity.inconclusive,
|
|
889
|
+
divergences: [...evidence.parity.divergences].sort()
|
|
890
|
+
},
|
|
891
|
+
legacy: {
|
|
892
|
+
observed: evidence.legacy.observed,
|
|
893
|
+
duplicated: evidence.legacy.duplicated
|
|
894
|
+
},
|
|
895
|
+
privacy: {
|
|
896
|
+
surfacesScanned: evidence.privacy.surfacesScanned,
|
|
897
|
+
rawFindings: evidence.privacy.rawFindings
|
|
898
|
+
},
|
|
899
|
+
overhead: {
|
|
900
|
+
p95Millis: evidence.overhead.p95Millis,
|
|
901
|
+
budgetMillis: evidence.overhead.budgetMillis,
|
|
902
|
+
samples: evidence.overhead.samples
|
|
903
|
+
},
|
|
904
|
+
provenance: Object.fromEntries(Object.entries(evidence.provenance).sort(([a], [b]) => a < b ? -1 : 1))
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
function sameGates(a, b) {
|
|
909
|
+
return a.length === b.length && a.every((gate, index) => gate === b[index]);
|
|
910
|
+
}
|
|
911
|
+
function parseShadowCertificate(value, opencodeVersion) {
|
|
912
|
+
const parsed = CertificateSchema.safeParse(value);
|
|
913
|
+
if (!parsed.success) {
|
|
914
|
+
const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
915
|
+
throw new ShadowCertificateError("invalid-certificate", detail);
|
|
916
|
+
}
|
|
917
|
+
const certificate = parsed.data;
|
|
918
|
+
const failedGates = evaluateGates(certificate.evidence);
|
|
919
|
+
const verdict = failedGates.length === 0 ? "pass" : "fail";
|
|
920
|
+
if (certificate.verdict !== verdict || !sameGates(certificate.failedGates, failedGates)) {
|
|
921
|
+
throw new ShadowCertificateError("verdict-mismatch", `evidence yields "${verdict}" (${failedGates.join(",") || "no failed gate"})`);
|
|
922
|
+
}
|
|
923
|
+
const { digest, ...unsigned } = certificate;
|
|
924
|
+
if (sha256Hex(canonicalCertificate(unsigned)) !== digest) {
|
|
925
|
+
throw new ShadowCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
|
|
926
|
+
}
|
|
927
|
+
if (certificate.opencodeVersion !== opencodeVersion) {
|
|
928
|
+
throw new ShadowCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", expected "${opencodeVersion}"`);
|
|
929
|
+
}
|
|
930
|
+
return certificate;
|
|
931
|
+
}
|
|
932
|
+
var ReleaseDeterminismSchema = z3.object({ runs: z3.number().int().nonnegative(), allMatch: z3.boolean() }).strict();
|
|
933
|
+
var ReleaseCrashRecoverySchema = z3.object({
|
|
934
|
+
scenarios: z3.number().int().nonnegative(),
|
|
935
|
+
allConverge: z3.boolean()
|
|
936
|
+
}).strict();
|
|
937
|
+
var ReleaseReplayEquivalenceSchema = z3.object({
|
|
938
|
+
checks: z3.number().int().nonnegative(),
|
|
939
|
+
allEquivalent: z3.boolean()
|
|
940
|
+
}).strict();
|
|
941
|
+
var ReleasePrivacySchema = z3.object({
|
|
942
|
+
surfacesScanned: z3.number().int().nonnegative(),
|
|
943
|
+
rawFindings: z3.number().int().nonnegative()
|
|
944
|
+
}).strict();
|
|
945
|
+
var ReleaseContractSchema = z3.object({
|
|
946
|
+
roundTrips: z3.number().int().nonnegative(),
|
|
947
|
+
allSettled: z3.boolean()
|
|
948
|
+
}).strict();
|
|
949
|
+
var ReleaseMigrationSchema = z3.object({
|
|
950
|
+
cases: z3.number().int().nonnegative(),
|
|
951
|
+
allSucceeded: z3.boolean()
|
|
952
|
+
}).strict();
|
|
953
|
+
var ReleaseHealthSchema = z3.object({
|
|
954
|
+
samples: z3.number().int().nonnegative(),
|
|
955
|
+
bounded: z3.boolean(),
|
|
956
|
+
idempotent: z3.boolean()
|
|
957
|
+
}).strict();
|
|
958
|
+
var ReleasePerformanceSchema = z3.object({
|
|
959
|
+
p95Millis: z3.number().nonnegative(),
|
|
960
|
+
budgetMillis: z3.number().positive(),
|
|
961
|
+
samples: z3.number().int().nonnegative()
|
|
962
|
+
}).strict();
|
|
963
|
+
var ReleaseEvidenceSchema = z3.object({
|
|
964
|
+
opencodeVersion: z3.string().min(1),
|
|
965
|
+
platform: z3.enum(["linux", "win32"]),
|
|
966
|
+
shadowCertificateDigest: z3.string().regex(/^[0-9a-f]{64}$/),
|
|
967
|
+
determinism: ReleaseDeterminismSchema,
|
|
968
|
+
crashRecovery: ReleaseCrashRecoverySchema,
|
|
969
|
+
replayEquivalence: ReleaseReplayEquivalenceSchema,
|
|
970
|
+
privacy: ReleasePrivacySchema,
|
|
971
|
+
contract: ReleaseContractSchema,
|
|
972
|
+
migration: ReleaseMigrationSchema,
|
|
973
|
+
health: ReleaseHealthSchema,
|
|
974
|
+
performance: ReleasePerformanceSchema
|
|
975
|
+
}).strict();
|
|
976
|
+
var ReleaseCertificateSchema = z3.object({
|
|
977
|
+
version: z3.literal(1),
|
|
978
|
+
certificate: z3.literal("graph-release"),
|
|
979
|
+
platform: z3.enum(["linux", "win32"]),
|
|
980
|
+
opencodeVersion: z3.string().min(1),
|
|
981
|
+
shadowCertificateDigest: z3.string().regex(/^[0-9a-f]{64}$/),
|
|
982
|
+
evidence: ReleaseEvidenceSchema,
|
|
983
|
+
verdict: z3.enum(["pass", "fail"]),
|
|
984
|
+
failedGates: z3.array(z3.enum([
|
|
985
|
+
"shadow-valid",
|
|
986
|
+
"determinism",
|
|
987
|
+
"crash-recovery",
|
|
988
|
+
"replay-equivalence",
|
|
989
|
+
"privacy",
|
|
990
|
+
"contract",
|
|
991
|
+
"migration",
|
|
992
|
+
"health",
|
|
993
|
+
"performance"
|
|
994
|
+
])),
|
|
995
|
+
digest: z3.string().regex(/^[0-9a-f]{64}$/)
|
|
996
|
+
}).strict();
|
|
997
|
+
|
|
998
|
+
class ReleaseCertificateError extends Error {
|
|
999
|
+
code;
|
|
1000
|
+
detail;
|
|
1001
|
+
constructor(code, detail) {
|
|
1002
|
+
super(`${code}: ${detail}`);
|
|
1003
|
+
this.name = "ReleaseCertificateError";
|
|
1004
|
+
this.code = code;
|
|
1005
|
+
this.detail = detail;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function evaluateReleaseGates(evidence, expectedShadowDigest) {
|
|
1009
|
+
const failed = [];
|
|
1010
|
+
if (evidence.shadowCertificateDigest !== expectedShadowDigest) {
|
|
1011
|
+
failed.push("shadow-valid");
|
|
1012
|
+
}
|
|
1013
|
+
if (evidence.determinism.runs < 1 || !evidence.determinism.allMatch) {
|
|
1014
|
+
failed.push("determinism");
|
|
1015
|
+
}
|
|
1016
|
+
if (evidence.crashRecovery.scenarios < 1 || !evidence.crashRecovery.allConverge) {
|
|
1017
|
+
failed.push("crash-recovery");
|
|
1018
|
+
}
|
|
1019
|
+
if (evidence.replayEquivalence.checks < 1 || !evidence.replayEquivalence.allEquivalent) {
|
|
1020
|
+
failed.push("replay-equivalence");
|
|
1021
|
+
}
|
|
1022
|
+
if (evidence.privacy.surfacesScanned < 1 || evidence.privacy.rawFindings > 0) {
|
|
1023
|
+
failed.push("privacy");
|
|
1024
|
+
}
|
|
1025
|
+
if (evidence.contract.roundTrips < 1 || !evidence.contract.allSettled) {
|
|
1026
|
+
failed.push("contract");
|
|
1027
|
+
}
|
|
1028
|
+
if (evidence.migration.cases < 1 || !evidence.migration.allSucceeded) {
|
|
1029
|
+
failed.push("migration");
|
|
1030
|
+
}
|
|
1031
|
+
if (evidence.health.samples < 1 || !evidence.health.bounded || !evidence.health.idempotent) {
|
|
1032
|
+
failed.push("health");
|
|
1033
|
+
}
|
|
1034
|
+
if (evidence.performance.samples < 1 || evidence.performance.p95Millis > evidence.performance.budgetMillis) {
|
|
1035
|
+
failed.push("performance");
|
|
1036
|
+
}
|
|
1037
|
+
return failed;
|
|
1038
|
+
}
|
|
1039
|
+
function canonicalReleaseCertificate(certificate) {
|
|
1040
|
+
const ev = certificate.evidence;
|
|
1041
|
+
return JSON.stringify({
|
|
1042
|
+
version: certificate.version,
|
|
1043
|
+
certificate: certificate.certificate,
|
|
1044
|
+
platform: certificate.platform,
|
|
1045
|
+
opencodeVersion: certificate.opencodeVersion,
|
|
1046
|
+
shadowCertificateDigest: certificate.shadowCertificateDigest,
|
|
1047
|
+
verdict: certificate.verdict,
|
|
1048
|
+
failedGates: [...certificate.failedGates],
|
|
1049
|
+
evidence: {
|
|
1050
|
+
opencodeVersion: ev.opencodeVersion,
|
|
1051
|
+
platform: ev.platform,
|
|
1052
|
+
shadowCertificateDigest: ev.shadowCertificateDigest,
|
|
1053
|
+
determinism: {
|
|
1054
|
+
runs: ev.determinism.runs,
|
|
1055
|
+
allMatch: ev.determinism.allMatch
|
|
1056
|
+
},
|
|
1057
|
+
crashRecovery: {
|
|
1058
|
+
scenarios: ev.crashRecovery.scenarios,
|
|
1059
|
+
allConverge: ev.crashRecovery.allConverge
|
|
1060
|
+
},
|
|
1061
|
+
replayEquivalence: {
|
|
1062
|
+
checks: ev.replayEquivalence.checks,
|
|
1063
|
+
allEquivalent: ev.replayEquivalence.allEquivalent
|
|
1064
|
+
},
|
|
1065
|
+
privacy: {
|
|
1066
|
+
surfacesScanned: ev.privacy.surfacesScanned,
|
|
1067
|
+
rawFindings: ev.privacy.rawFindings
|
|
1068
|
+
},
|
|
1069
|
+
contract: {
|
|
1070
|
+
roundTrips: ev.contract.roundTrips,
|
|
1071
|
+
allSettled: ev.contract.allSettled
|
|
1072
|
+
},
|
|
1073
|
+
migration: {
|
|
1074
|
+
cases: ev.migration.cases,
|
|
1075
|
+
allSucceeded: ev.migration.allSucceeded
|
|
1076
|
+
},
|
|
1077
|
+
health: {
|
|
1078
|
+
samples: ev.health.samples,
|
|
1079
|
+
bounded: ev.health.bounded,
|
|
1080
|
+
idempotent: ev.health.idempotent
|
|
1081
|
+
},
|
|
1082
|
+
performance: {
|
|
1083
|
+
p95Millis: ev.performance.p95Millis,
|
|
1084
|
+
budgetMillis: ev.performance.budgetMillis,
|
|
1085
|
+
samples: ev.performance.samples
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
function sameReleaseGates(a, b) {
|
|
1091
|
+
return a.length === b.length && a.every((gate, index) => gate === b[index]);
|
|
1092
|
+
}
|
|
1093
|
+
function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
|
|
1094
|
+
const parsed = ReleaseCertificateSchema.safeParse(value);
|
|
1095
|
+
if (!parsed.success) {
|
|
1096
|
+
const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
1097
|
+
throw new ReleaseCertificateError("invalid-certificate", detail);
|
|
1098
|
+
}
|
|
1099
|
+
const certificate = parsed.data;
|
|
1100
|
+
const inconsistencies = [];
|
|
1101
|
+
if (certificate.platform !== certificate.evidence.platform) {
|
|
1102
|
+
inconsistencies.push("platform");
|
|
1103
|
+
}
|
|
1104
|
+
if (certificate.opencodeVersion !== certificate.evidence.opencodeVersion) {
|
|
1105
|
+
inconsistencies.push("opencodeVersion");
|
|
1106
|
+
}
|
|
1107
|
+
if (certificate.shadowCertificateDigest !== certificate.evidence.shadowCertificateDigest) {
|
|
1108
|
+
inconsistencies.push("shadowCertificateDigest");
|
|
1109
|
+
}
|
|
1110
|
+
if (inconsistencies.length > 0) {
|
|
1111
|
+
throw new ReleaseCertificateError("field-inconsistency", `top-level/evidence divergence: ${inconsistencies.join(", ")}`);
|
|
1112
|
+
}
|
|
1113
|
+
const failedGates = evaluateReleaseGates(certificate.evidence, expectedShadowDigest);
|
|
1114
|
+
const verdict = failedGates.length === 0 ? "pass" : "fail";
|
|
1115
|
+
if (certificate.verdict !== verdict || !sameReleaseGates(certificate.failedGates, failedGates)) {
|
|
1116
|
+
throw new ReleaseCertificateError("verdict-mismatch", `evidence yields "${verdict}" (${failedGates.join(",") || "no failed gate"})`);
|
|
1117
|
+
}
|
|
1118
|
+
const { digest, ...unsigned } = certificate;
|
|
1119
|
+
if (sha256Hex(canonicalReleaseCertificate(unsigned)) !== digest) {
|
|
1120
|
+
throw new ReleaseCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
|
|
1121
|
+
}
|
|
1122
|
+
if (certificate.opencodeVersion !== opencodeVersion) {
|
|
1123
|
+
throw new ReleaseCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", expected "${opencodeVersion}"`);
|
|
1124
|
+
}
|
|
1125
|
+
return certificate;
|
|
1126
|
+
}
|
|
1127
|
+
var ShadowDivergenceCodeSchema = z3.enum([
|
|
1128
|
+
"status",
|
|
1129
|
+
"order",
|
|
1130
|
+
"roles",
|
|
1131
|
+
"models",
|
|
1132
|
+
"fixes",
|
|
1133
|
+
"outcome"
|
|
1134
|
+
]);
|
|
1135
|
+
var RECOMPUTED_DIVERGENCE_CODES = ["fixes"];
|
|
1136
|
+
var RecomputedDivergenceCodeSchema = z3.enum(RECOMPUTED_DIVERGENCE_CODES);
|
|
1137
|
+
var SoakPolicySchema = z3.object({
|
|
1138
|
+
minimumGenuineObservations: z3.number().int().positive(),
|
|
1139
|
+
minimumDistinctDays: z3.number().int().positive(),
|
|
1140
|
+
requireGenuineUsageOnAllPlatforms: z3.boolean(),
|
|
1141
|
+
supportedOpencodeVersion: z3.string().min(1),
|
|
1142
|
+
criticalDivergenceCodes: z3.array(RecomputedDivergenceCodeSchema).min(1)
|
|
1143
|
+
}).strict();
|
|
1144
|
+
var DEFAULT_SOAK_POLICY = {
|
|
1145
|
+
minimumGenuineObservations: 100,
|
|
1146
|
+
minimumDistinctDays: 14,
|
|
1147
|
+
requireGenuineUsageOnAllPlatforms: true,
|
|
1148
|
+
supportedOpencodeVersion: "1.17.13",
|
|
1149
|
+
criticalDivergenceCodes: ["fixes"]
|
|
1150
|
+
};
|
|
1151
|
+
var SoakPlatformEnumSchema = z3.enum(["linux", "win32", "darwin"]);
|
|
1152
|
+
var SoakPrivacyPairEvidenceSchema = z3.object({
|
|
1153
|
+
surfacesScanned: z3.number().int().nonnegative(),
|
|
1154
|
+
rawFindings: z3.number().int().nonnegative()
|
|
1155
|
+
}).strict();
|
|
1156
|
+
var SoakDuplicateEffectPairEvidenceSchema = z3.object({
|
|
1157
|
+
effectsExamined: z3.number().int().nonnegative(),
|
|
1158
|
+
duplicatesFound: z3.number().int().nonnegative()
|
|
1159
|
+
}).strict();
|
|
1160
|
+
var SoakModelVerificationPairEvidenceSchema = z3.object({
|
|
1161
|
+
nodesChecked: z3.number().int().nonnegative(),
|
|
1162
|
+
unverified: z3.number().int().nonnegative(),
|
|
1163
|
+
mismatched: z3.number().int().nonnegative()
|
|
1164
|
+
}).strict();
|
|
1165
|
+
var SoakEvidenceSchema = z3.object({
|
|
1166
|
+
totalObservations: z3.number().int().nonnegative(),
|
|
1167
|
+
genuineUsageObservations: z3.number().int().nonnegative(),
|
|
1168
|
+
ciSyntheticObservations: z3.number().int().nonnegative(),
|
|
1169
|
+
recorderCount: z3.number().int().nonnegative(),
|
|
1170
|
+
firstTimestamp: z3.string().datetime().nullable(),
|
|
1171
|
+
lastTimestamp: z3.string().datetime().nullable(),
|
|
1172
|
+
distinctDays: z3.number().int().nonnegative(),
|
|
1173
|
+
criticalDivergences: z3.number().int().nonnegative().nullable(),
|
|
1174
|
+
privacy: SoakPrivacyPairEvidenceSchema.nullable(),
|
|
1175
|
+
duplicateEffects: SoakDuplicateEffectPairEvidenceSchema.nullable(),
|
|
1176
|
+
modelVerification: SoakModelVerificationPairEvidenceSchema.nullable(),
|
|
1177
|
+
platformsGenuine: z3.array(SoakPlatformEnumSchema),
|
|
1178
|
+
platformsSynthetic: z3.array(SoakPlatformEnumSchema),
|
|
1179
|
+
opencodeVersionsObserved: z3.array(z3.string().min(1)),
|
|
1180
|
+
invalidChains: z3.number().int().nonnegative()
|
|
1181
|
+
}).strict();
|
|
1182
|
+
var SoakCertificateSchema = z3.object({
|
|
1183
|
+
version: z3.literal(1),
|
|
1184
|
+
certificate: z3.literal("graph-soak"),
|
|
1185
|
+
trustBoundary: z3.literal("contributors"),
|
|
1186
|
+
policy: SoakPolicySchema,
|
|
1187
|
+
evidence: SoakEvidenceSchema,
|
|
1188
|
+
verdict: z3.enum(["pass", "fail"]),
|
|
1189
|
+
failedGates: z3.array(z3.enum([
|
|
1190
|
+
"minimum-observations",
|
|
1191
|
+
"minimum-duration",
|
|
1192
|
+
"chain-integrity",
|
|
1193
|
+
"zero-critical-divergence",
|
|
1194
|
+
"zero-privacy-breach",
|
|
1195
|
+
"zero-duplicate-effect",
|
|
1196
|
+
"zero-model-mismatch",
|
|
1197
|
+
"os-diversity",
|
|
1198
|
+
"opencode-pin"
|
|
1199
|
+
])),
|
|
1200
|
+
ledgerDigest: z3.string().regex(/^[0-9a-f]{64}$/),
|
|
1201
|
+
digest: z3.string().regex(/^[0-9a-f]{64}$/)
|
|
1202
|
+
}).strict();
|
|
1203
|
+
|
|
1204
|
+
class SoakCertificateError extends Error {
|
|
1205
|
+
code;
|
|
1206
|
+
detail;
|
|
1207
|
+
constructor(code, detail) {
|
|
1208
|
+
super(`${code}: ${detail}`);
|
|
1209
|
+
this.name = "SoakCertificateError";
|
|
1210
|
+
this.code = code;
|
|
1211
|
+
this.detail = detail;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
function evaluateSoakGates(evidence, policy) {
|
|
1215
|
+
const failed = [];
|
|
1216
|
+
if (evidence.genuineUsageObservations < policy.minimumGenuineObservations) {
|
|
1217
|
+
failed.push("minimum-observations");
|
|
1218
|
+
}
|
|
1219
|
+
if (evidence.distinctDays < policy.minimumDistinctDays) {
|
|
1220
|
+
failed.push("minimum-duration");
|
|
1221
|
+
}
|
|
1222
|
+
if (evidence.invalidChains > 0) {
|
|
1223
|
+
failed.push("chain-integrity");
|
|
1224
|
+
}
|
|
1225
|
+
if (evidence.criticalDivergences === null || evidence.criticalDivergences > 0) {
|
|
1226
|
+
failed.push("zero-critical-divergence");
|
|
1227
|
+
}
|
|
1228
|
+
if (evidence.privacy === null || evidence.privacy.surfacesScanned < 1 || evidence.privacy.rawFindings > 0) {
|
|
1229
|
+
failed.push("zero-privacy-breach");
|
|
1230
|
+
}
|
|
1231
|
+
if (evidence.duplicateEffects === null || evidence.duplicateEffects.effectsExamined < 1 || evidence.duplicateEffects.duplicatesFound > 0) {
|
|
1232
|
+
failed.push("zero-duplicate-effect");
|
|
1233
|
+
}
|
|
1234
|
+
if (evidence.modelVerification === null || evidence.modelVerification.nodesChecked < 1 || evidence.modelVerification.mismatched > 0) {
|
|
1235
|
+
failed.push("zero-model-mismatch");
|
|
1236
|
+
}
|
|
1237
|
+
if (policy.requireGenuineUsageOnAllPlatforms) {
|
|
1238
|
+
if (!evidence.platformsGenuine.includes("linux") || !evidence.platformsGenuine.includes("win32")) {
|
|
1239
|
+
failed.push("os-diversity");
|
|
1240
|
+
}
|
|
1241
|
+
} else {
|
|
1242
|
+
const allPlatforms = new Set([
|
|
1243
|
+
...evidence.platformsGenuine,
|
|
1244
|
+
...evidence.platformsSynthetic
|
|
1245
|
+
]);
|
|
1246
|
+
if (!allPlatforms.has("linux") || !allPlatforms.has("win32") || evidence.platformsGenuine.length < 1) {
|
|
1247
|
+
failed.push("os-diversity");
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
if (!evidence.opencodeVersionsObserved.includes(policy.supportedOpencodeVersion)) {
|
|
1251
|
+
failed.push("opencode-pin");
|
|
1252
|
+
}
|
|
1253
|
+
return failed;
|
|
1254
|
+
}
|
|
1255
|
+
function canonicalSoakCertificate(certificate) {
|
|
1256
|
+
const ev = certificate.evidence;
|
|
1257
|
+
const pol = certificate.policy;
|
|
1258
|
+
return JSON.stringify({
|
|
1259
|
+
version: certificate.version,
|
|
1260
|
+
certificate: certificate.certificate,
|
|
1261
|
+
trustBoundary: certificate.trustBoundary,
|
|
1262
|
+
policy: {
|
|
1263
|
+
minimumGenuineObservations: pol.minimumGenuineObservations,
|
|
1264
|
+
minimumDistinctDays: pol.minimumDistinctDays,
|
|
1265
|
+
requireGenuineUsageOnAllPlatforms: pol.requireGenuineUsageOnAllPlatforms,
|
|
1266
|
+
supportedOpencodeVersion: pol.supportedOpencodeVersion,
|
|
1267
|
+
criticalDivergenceCodes: [...pol.criticalDivergenceCodes].sort()
|
|
1268
|
+
},
|
|
1269
|
+
verdict: certificate.verdict,
|
|
1270
|
+
failedGates: [...certificate.failedGates],
|
|
1271
|
+
ledgerDigest: certificate.ledgerDigest,
|
|
1272
|
+
evidence: {
|
|
1273
|
+
totalObservations: ev.totalObservations,
|
|
1274
|
+
genuineUsageObservations: ev.genuineUsageObservations,
|
|
1275
|
+
ciSyntheticObservations: ev.ciSyntheticObservations,
|
|
1276
|
+
recorderCount: ev.recorderCount,
|
|
1277
|
+
firstTimestamp: ev.firstTimestamp,
|
|
1278
|
+
lastTimestamp: ev.lastTimestamp,
|
|
1279
|
+
distinctDays: ev.distinctDays,
|
|
1280
|
+
criticalDivergences: ev.criticalDivergences,
|
|
1281
|
+
privacy: ev.privacy,
|
|
1282
|
+
duplicateEffects: ev.duplicateEffects,
|
|
1283
|
+
modelVerification: ev.modelVerification,
|
|
1284
|
+
platformsGenuine: [...ev.platformsGenuine].sort(),
|
|
1285
|
+
platformsSynthetic: [...ev.platformsSynthetic].sort(),
|
|
1286
|
+
opencodeVersionsObserved: [...ev.opencodeVersionsObserved].sort(),
|
|
1287
|
+
invalidChains: ev.invalidChains
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
function sameSoakGates(a, b) {
|
|
1292
|
+
return a.length === b.length && a.every((gate, index) => gate === b[index]);
|
|
1293
|
+
}
|
|
1294
|
+
function sameSoakPolicy(a, b) {
|
|
1295
|
+
return a.minimumGenuineObservations === b.minimumGenuineObservations && a.minimumDistinctDays === b.minimumDistinctDays && a.requireGenuineUsageOnAllPlatforms === b.requireGenuineUsageOnAllPlatforms && a.supportedOpencodeVersion === b.supportedOpencodeVersion && a.criticalDivergenceCodes.length === b.criticalDivergenceCodes.length && [...a.criticalDivergenceCodes].sort().every((code, i) => code === [...b.criticalDivergenceCodes].sort()[i]);
|
|
1296
|
+
}
|
|
1297
|
+
function parseSoakCertificate(value, expectedPolicy, expectedLedgerDigest) {
|
|
1298
|
+
const parsed = SoakCertificateSchema.safeParse(value);
|
|
1299
|
+
if (!parsed.success) {
|
|
1300
|
+
const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
1301
|
+
throw new SoakCertificateError("invalid-certificate", detail);
|
|
1302
|
+
}
|
|
1303
|
+
const certificate = parsed.data;
|
|
1304
|
+
if (!sameSoakPolicy(certificate.policy, expectedPolicy)) {
|
|
1305
|
+
throw new SoakCertificateError("policy-mismatch", "embedded policy diverges from the expected policy");
|
|
1306
|
+
}
|
|
1307
|
+
const failedGates = evaluateSoakGates(certificate.evidence, certificate.policy);
|
|
1308
|
+
const verdict = failedGates.length === 0 ? "pass" : "fail";
|
|
1309
|
+
if (certificate.verdict !== verdict || !sameSoakGates(certificate.failedGates, failedGates)) {
|
|
1310
|
+
throw new SoakCertificateError("verdict-mismatch", `evidence yields "${verdict}" (${failedGates.join(",") || "no failed gate"})`);
|
|
1311
|
+
}
|
|
1312
|
+
const { digest, ...unsigned } = certificate;
|
|
1313
|
+
if (sha256Hex(canonicalSoakCertificate(unsigned)) !== digest) {
|
|
1314
|
+
throw new SoakCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
|
|
1315
|
+
}
|
|
1316
|
+
if (certificate.ledgerDigest !== expectedLedgerDigest) {
|
|
1317
|
+
throw new SoakCertificateError("ledger-mismatch", "ledger digest does not match the evidence the caller holds");
|
|
1318
|
+
}
|
|
1319
|
+
return certificate;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
543
1322
|
// src/local/openai-compatible.ts
|
|
544
1323
|
function normalizeBaseURL(baseURL) {
|
|
545
1324
|
return baseURL.replace(/\/+$/, "");
|
|
@@ -556,8 +1335,8 @@ function errorMessage(error) {
|
|
|
556
1335
|
}
|
|
557
1336
|
return String(error);
|
|
558
1337
|
}
|
|
559
|
-
async function listOpenAICompatibleModels(baseURL,
|
|
560
|
-
const response = await
|
|
1338
|
+
async function listOpenAICompatibleModels(baseURL, fetch2) {
|
|
1339
|
+
const response = await fetch2(modelsEndpoint(baseURL), {
|
|
561
1340
|
method: "GET",
|
|
562
1341
|
headers: { accept: "application/json" }
|
|
563
1342
|
});
|
|
@@ -980,123 +1759,123 @@ function createAvailabilityCache(options) {
|
|
|
980
1759
|
}
|
|
981
1760
|
|
|
982
1761
|
// src/telemetry/events.ts
|
|
983
|
-
import { z as
|
|
1762
|
+
import { z as z5 } from "zod";
|
|
984
1763
|
|
|
985
1764
|
// src/capabilities/types.ts
|
|
986
|
-
import { z as
|
|
987
|
-
var CapabilityTierSchema =
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1765
|
+
import { z as z4 } from "zod";
|
|
1766
|
+
var CapabilityTierSchema = z4.union([
|
|
1767
|
+
z4.literal(0),
|
|
1768
|
+
z4.literal(1),
|
|
1769
|
+
z4.literal(2),
|
|
1770
|
+
z4.literal(3),
|
|
1771
|
+
z4.literal(4),
|
|
1772
|
+
z4.literal(5)
|
|
994
1773
|
]);
|
|
995
|
-
var ComplexityTierSchema =
|
|
1774
|
+
var ComplexityTierSchema = z4.enum([
|
|
996
1775
|
"trivial",
|
|
997
1776
|
"simple",
|
|
998
1777
|
"moderate",
|
|
999
1778
|
"hard"
|
|
1000
1779
|
]);
|
|
1001
|
-
var ModelCapabilityProfileSchema =
|
|
1002
|
-
ref:
|
|
1003
|
-
providerID:
|
|
1004
|
-
modelID:
|
|
1780
|
+
var ModelCapabilityProfileSchema = z4.object({
|
|
1781
|
+
ref: z4.object({
|
|
1782
|
+
providerID: z4.string().min(1),
|
|
1783
|
+
modelID: z4.string().min(1)
|
|
1005
1784
|
}),
|
|
1006
|
-
kind:
|
|
1007
|
-
contextWindow:
|
|
1008
|
-
maxOutputTokens:
|
|
1009
|
-
supportsToolCalling:
|
|
1010
|
-
supportsVision:
|
|
1785
|
+
kind: z4.enum(["local", "frontier", "router"]),
|
|
1786
|
+
contextWindow: z4.number().int().positive(),
|
|
1787
|
+
maxOutputTokens: z4.number().int().positive(),
|
|
1788
|
+
supportsToolCalling: z4.boolean(),
|
|
1789
|
+
supportsVision: z4.boolean(),
|
|
1011
1790
|
reasoningTier: CapabilityTierSchema,
|
|
1012
1791
|
codeQualityTier: CapabilityTierSchema,
|
|
1013
|
-
costPer1M:
|
|
1014
|
-
inputUSD:
|
|
1015
|
-
outputUSD:
|
|
1792
|
+
costPer1M: z4.object({
|
|
1793
|
+
inputUSD: z4.number().min(0),
|
|
1794
|
+
outputUSD: z4.number().min(0)
|
|
1016
1795
|
}),
|
|
1017
|
-
availability:
|
|
1796
|
+
availability: z4.enum(["available", "degraded", "unavailable"])
|
|
1018
1797
|
});
|
|
1019
1798
|
|
|
1020
1799
|
// src/telemetry/events.ts
|
|
1021
1800
|
var EVENT_SCHEMA_VERSION = 1;
|
|
1022
|
-
var EventBaseSchema =
|
|
1023
|
-
v:
|
|
1024
|
-
ts:
|
|
1025
|
-
sessionID:
|
|
1801
|
+
var EventBaseSchema = z5.object({
|
|
1802
|
+
v: z5.literal(EVENT_SCHEMA_VERSION),
|
|
1803
|
+
ts: z5.number().finite(),
|
|
1804
|
+
sessionID: z5.string().min(1)
|
|
1026
1805
|
});
|
|
1027
1806
|
var RouteEventSchema = EventBaseSchema.extend({
|
|
1028
|
-
type:
|
|
1029
|
-
promptHash:
|
|
1030
|
-
promptChars:
|
|
1807
|
+
type: z5.literal("route"),
|
|
1808
|
+
promptHash: z5.string().min(1),
|
|
1809
|
+
promptChars: z5.number().int().min(0),
|
|
1031
1810
|
tier: ComplexityTierSchema,
|
|
1032
|
-
routeKind:
|
|
1811
|
+
routeKind: z5.enum(["local", "frontier"]),
|
|
1033
1812
|
selected: ModelRefSchema,
|
|
1034
|
-
rationale:
|
|
1035
|
-
estimatedCostUSD:
|
|
1036
|
-
baselineCostUSD:
|
|
1037
|
-
estimatedSavingsUSD:
|
|
1038
|
-
budgetAction:
|
|
1039
|
-
tokensIn:
|
|
1040
|
-
tokensOut:
|
|
1041
|
-
decisionID:
|
|
1042
|
-
agent:
|
|
1043
|
-
success:
|
|
1044
|
-
batchID:
|
|
1045
|
-
taskID:
|
|
1046
|
-
failureReason:
|
|
1047
|
-
failureStage:
|
|
1813
|
+
rationale: z5.string(),
|
|
1814
|
+
estimatedCostUSD: z5.number().finite().min(0),
|
|
1815
|
+
baselineCostUSD: z5.number().finite().min(0),
|
|
1816
|
+
estimatedSavingsUSD: z5.number().finite(),
|
|
1817
|
+
budgetAction: z5.string().min(1),
|
|
1818
|
+
tokensIn: z5.number().int().min(0).optional(),
|
|
1819
|
+
tokensOut: z5.number().int().min(0).optional(),
|
|
1820
|
+
decisionID: z5.string().min(1).optional(),
|
|
1821
|
+
agent: z5.string().min(1).optional(),
|
|
1822
|
+
success: z5.boolean().optional(),
|
|
1823
|
+
batchID: z5.string().min(1).optional(),
|
|
1824
|
+
taskID: z5.string().min(1).optional(),
|
|
1825
|
+
failureReason: z5.string().min(1).optional(),
|
|
1826
|
+
failureStage: z5.string().min(1).optional()
|
|
1048
1827
|
});
|
|
1049
1828
|
var MessageEventSchema = EventBaseSchema.extend({
|
|
1050
|
-
type:
|
|
1051
|
-
providerID:
|
|
1052
|
-
modelID:
|
|
1053
|
-
agent:
|
|
1054
|
-
mode:
|
|
1055
|
-
messageID:
|
|
1056
|
-
costUSD:
|
|
1057
|
-
tokensIn:
|
|
1058
|
-
tokensOut:
|
|
1059
|
-
tokensReasoning:
|
|
1060
|
-
tokensCacheRead:
|
|
1061
|
-
tokensCacheWrite:
|
|
1062
|
-
durationMs:
|
|
1829
|
+
type: z5.literal("message"),
|
|
1830
|
+
providerID: z5.string().min(1),
|
|
1831
|
+
modelID: z5.string().min(1),
|
|
1832
|
+
agent: z5.string().min(1).optional(),
|
|
1833
|
+
mode: z5.string().min(1).optional(),
|
|
1834
|
+
messageID: z5.string().min(1).optional(),
|
|
1835
|
+
costUSD: z5.number().finite().min(0),
|
|
1836
|
+
tokensIn: z5.number().int().min(0),
|
|
1837
|
+
tokensOut: z5.number().int().min(0),
|
|
1838
|
+
tokensReasoning: z5.number().int().min(0),
|
|
1839
|
+
tokensCacheRead: z5.number().int().min(0),
|
|
1840
|
+
tokensCacheWrite: z5.number().int().min(0),
|
|
1841
|
+
durationMs: z5.number().int().min(0)
|
|
1063
1842
|
});
|
|
1064
1843
|
var ToolcallEventSchema = EventBaseSchema.extend({
|
|
1065
|
-
type:
|
|
1066
|
-
tool:
|
|
1067
|
-
callID:
|
|
1068
|
-
agent:
|
|
1069
|
-
durationMs:
|
|
1070
|
-
ok:
|
|
1071
|
-
title:
|
|
1844
|
+
type: z5.literal("toolcall"),
|
|
1845
|
+
tool: z5.string().min(1),
|
|
1846
|
+
callID: z5.string().min(1),
|
|
1847
|
+
agent: z5.string().min(1).optional(),
|
|
1848
|
+
durationMs: z5.number().int().min(0),
|
|
1849
|
+
ok: z5.boolean(),
|
|
1850
|
+
title: z5.string().optional()
|
|
1072
1851
|
});
|
|
1073
1852
|
var MeetingEventSchema = EventBaseSchema.extend({
|
|
1074
|
-
type:
|
|
1075
|
-
batchID:
|
|
1076
|
-
purpose:
|
|
1077
|
-
roles:
|
|
1078
|
-
decisionIDs:
|
|
1853
|
+
type: z5.literal("meeting"),
|
|
1854
|
+
batchID: z5.string().min(1),
|
|
1855
|
+
purpose: z5.string().optional(),
|
|
1856
|
+
roles: z5.array(z5.string().min(1)),
|
|
1857
|
+
decisionIDs: z5.array(z5.string().min(1)).optional()
|
|
1079
1858
|
});
|
|
1080
1859
|
var DecisionEventSchema = EventBaseSchema.extend({
|
|
1081
|
-
type:
|
|
1082
|
-
agent:
|
|
1083
|
-
summary:
|
|
1084
|
-
tags:
|
|
1860
|
+
type: z5.literal("decision"),
|
|
1861
|
+
agent: z5.string().min(1).optional(),
|
|
1862
|
+
summary: z5.string().min(1),
|
|
1863
|
+
tags: z5.array(z5.string().min(1)).optional()
|
|
1085
1864
|
});
|
|
1086
1865
|
var ActivityEventSchema = EventBaseSchema.extend({
|
|
1087
|
-
type:
|
|
1088
|
-
kind:
|
|
1089
|
-
agent:
|
|
1090
|
-
summary:
|
|
1866
|
+
type: z5.literal("activity"),
|
|
1867
|
+
kind: z5.enum(["route", "agent", "decision", "commit"]),
|
|
1868
|
+
agent: z5.string().min(1).optional(),
|
|
1869
|
+
summary: z5.string().min(1)
|
|
1091
1870
|
});
|
|
1092
1871
|
var SessionEndpointEventSchema = EventBaseSchema.extend({
|
|
1093
|
-
type:
|
|
1094
|
-
url:
|
|
1095
|
-
directory:
|
|
1096
|
-
worktree:
|
|
1097
|
-
title:
|
|
1872
|
+
type: z5.literal("session-endpoint"),
|
|
1873
|
+
url: z5.string().url(),
|
|
1874
|
+
directory: z5.string().min(1).optional(),
|
|
1875
|
+
worktree: z5.string().min(1).optional(),
|
|
1876
|
+
title: z5.string().min(1).optional()
|
|
1098
1877
|
});
|
|
1099
|
-
var OpenTeamEventSchema =
|
|
1878
|
+
var OpenTeamEventSchema = z5.discriminatedUnion("type", [
|
|
1100
1879
|
RouteEventSchema,
|
|
1101
1880
|
MessageEventSchema,
|
|
1102
1881
|
ToolcallEventSchema,
|
|
@@ -1473,6 +2252,15 @@ function renderDoctor(input) {
|
|
|
1473
2252
|
if (reachable === 0) {
|
|
1474
2253
|
lines.push(" ⚠ Ningún runtime local alcanzable: todo el tráfico irá a frontier.");
|
|
1475
2254
|
}
|
|
2255
|
+
if (input.graphHealth !== undefined) {
|
|
2256
|
+
const h = input.graphHealth;
|
|
2257
|
+
lines.push(" graph health:");
|
|
2258
|
+
lines.push(` recuperación requerida: ${h.recoveryRequired ? "sí" : "no"}`);
|
|
2259
|
+
lines.push(` efectos desconocidos: ${h.unknownEffects}`);
|
|
2260
|
+
lines.push(` completaciones tardías: ${h.staleCompletions}`);
|
|
2261
|
+
lines.push(` conflictos: ${h.conflicts}`);
|
|
2262
|
+
lines.push(` kill switch: ${h.killSwitch ? "activo" : "inactivo"}`);
|
|
2263
|
+
}
|
|
1476
2264
|
return lines.join(`
|
|
1477
2265
|
`);
|
|
1478
2266
|
}
|
|
@@ -1921,42 +2709,254 @@ function createCommandTool(deps) {
|
|
|
1921
2709
|
});
|
|
1922
2710
|
}
|
|
1923
2711
|
|
|
1924
|
-
// src/
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2712
|
+
// src/plugin/graphTool.ts
|
|
2713
|
+
import { tool as tool2 } from "@opencode-ai/plugin";
|
|
2714
|
+
|
|
2715
|
+
// src/graph/schema.ts
|
|
2716
|
+
import { z as z7 } from "zod";
|
|
2717
|
+
|
|
2718
|
+
// src/context/schema.ts
|
|
2719
|
+
import { z as z6 } from "zod";
|
|
2720
|
+
var PrivacyClassSchema = z6.enum(["public", "internal", "sensitive"]);
|
|
2721
|
+
var Sha256Schema2 = z6.string().regex(/^[0-9a-f]{64}$/, "expected lowercase hex sha-256");
|
|
2722
|
+
var IdentifierSchema = z6.string().min(1).max(200).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/, "invalid identifier");
|
|
2723
|
+
var TaskAnchorSchema = z6.object({
|
|
2724
|
+
taskID: IdentifierSchema,
|
|
2725
|
+
specDigest: Sha256Schema2
|
|
2726
|
+
}).strict();
|
|
2727
|
+
var ContextRefSchema = z6.object({
|
|
2728
|
+
uri: z6.string().min(1).max(1024),
|
|
2729
|
+
sha256: Sha256Schema2,
|
|
2730
|
+
bytes: z6.number().int().nonnegative(),
|
|
2731
|
+
mediaType: z6.string().min(1).max(128).optional()
|
|
2732
|
+
}).strict();
|
|
2733
|
+
var ManifestEntrySchema = z6.object({
|
|
2734
|
+
label: IdentifierSchema,
|
|
2735
|
+
sha256: Sha256Schema2,
|
|
2736
|
+
bytes: z6.number().int().nonnegative(),
|
|
2737
|
+
ref: ContextRefSchema.optional()
|
|
2738
|
+
}).strict();
|
|
2739
|
+
var ArtifactManifestSchema = z6.object({
|
|
2740
|
+
version: z6.literal(1),
|
|
2741
|
+
taskID: IdentifierSchema,
|
|
2742
|
+
privacyClass: PrivacyClassSchema,
|
|
2743
|
+
entries: z6.array(ManifestEntrySchema)
|
|
2744
|
+
}).strict();
|
|
2745
|
+
var NodeContextContractSchema = z6.object({
|
|
2746
|
+
version: z6.literal(1),
|
|
2747
|
+
refs: z6.array(ContextRefSchema),
|
|
2748
|
+
privacyClass: PrivacyClassSchema,
|
|
2749
|
+
maxInputTokens: z6.number().int().positive().optional()
|
|
2750
|
+
}).strict();
|
|
2751
|
+
|
|
2752
|
+
// src/graph/schema.ts
|
|
2753
|
+
var GRAPH_SPEC_VERSION = 1;
|
|
2754
|
+
var GRAPH_EVENT_VERSION = 1;
|
|
2755
|
+
var NodeIDSchema = z7.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "invalid node id");
|
|
2756
|
+
var NodeRoleSchema = z7.enum(["implementer", "reviewer"]);
|
|
2757
|
+
var OperationIDSchema = z7.string().regex(/^[0-9a-f]{8,64}$/, "invalid operation id");
|
|
2758
|
+
var AttemptIDSchema = z7.string().regex(/^[0-9a-f]{8,64}$/, "invalid attempt id");
|
|
2759
|
+
var ErrorClassSchema = z7.enum([
|
|
2760
|
+
"transient",
|
|
2761
|
+
"permanent",
|
|
2762
|
+
"timeout",
|
|
2763
|
+
"cancelled",
|
|
2764
|
+
"unknown"
|
|
2765
|
+
]);
|
|
2766
|
+
var GraphNodeSchema = z7.object({
|
|
2767
|
+
id: NodeIDSchema,
|
|
2768
|
+
role: NodeRoleSchema,
|
|
2769
|
+
dependsOn: z7.array(NodeIDSchema).default([]),
|
|
2770
|
+
anchor: TaskAnchorSchema,
|
|
2771
|
+
reviews: NodeIDSchema.optional()
|
|
2772
|
+
}).strict();
|
|
2773
|
+
var GraphSpecV1Schema = z7.object({
|
|
2774
|
+
version: z7.literal(GRAPH_SPEC_VERSION),
|
|
2775
|
+
runID: NodeIDSchema,
|
|
2776
|
+
nodes: z7.array(GraphNodeSchema).min(1)
|
|
2777
|
+
}).strict();
|
|
2778
|
+
var eventEnvelope = {
|
|
2779
|
+
v: z7.literal(GRAPH_EVENT_VERSION),
|
|
2780
|
+
seq: z7.number().int().nonnegative(),
|
|
2781
|
+
runID: NodeIDSchema
|
|
2782
|
+
};
|
|
2783
|
+
var RunStartedEventSchema = z7.object({
|
|
2784
|
+
...eventEnvelope,
|
|
2785
|
+
type: z7.literal("run.started"),
|
|
2786
|
+
specDigest: Sha256Schema2
|
|
2787
|
+
}).strict();
|
|
2788
|
+
var NodeDispatchedEventSchema = z7.object({
|
|
2789
|
+
...eventEnvelope,
|
|
2790
|
+
type: z7.literal("node.dispatched"),
|
|
2791
|
+
nodeID: NodeIDSchema,
|
|
2792
|
+
operationID: OperationIDSchema,
|
|
2793
|
+
attemptID: AttemptIDSchema
|
|
2794
|
+
}).strict();
|
|
2795
|
+
var NodeSucceededEventSchema = z7.object({
|
|
2796
|
+
...eventEnvelope,
|
|
2797
|
+
type: z7.literal("node.succeeded"),
|
|
2798
|
+
nodeID: NodeIDSchema,
|
|
2799
|
+
operationID: OperationIDSchema,
|
|
2800
|
+
attemptID: AttemptIDSchema,
|
|
2801
|
+
artifact: ContextRefSchema
|
|
2802
|
+
}).strict();
|
|
2803
|
+
var NodeFailedEventSchema = z7.object({
|
|
2804
|
+
...eventEnvelope,
|
|
2805
|
+
type: z7.literal("node.failed"),
|
|
2806
|
+
nodeID: NodeIDSchema,
|
|
2807
|
+
operationID: OperationIDSchema,
|
|
2808
|
+
attemptID: AttemptIDSchema,
|
|
2809
|
+
errorClass: ErrorClassSchema
|
|
2810
|
+
}).strict();
|
|
2811
|
+
var NodeCancelledEventSchema = z7.object({
|
|
2812
|
+
...eventEnvelope,
|
|
2813
|
+
type: z7.literal("node.cancelled"),
|
|
2814
|
+
nodeID: NodeIDSchema
|
|
2815
|
+
}).strict();
|
|
2816
|
+
var RunCompletedEventSchema = z7.object({ ...eventEnvelope, type: z7.literal("run.completed") }).strict();
|
|
2817
|
+
var RunFailedEventSchema = z7.object({ ...eventEnvelope, type: z7.literal("run.failed") }).strict();
|
|
2818
|
+
var RunCancelledEventSchema = z7.object({ ...eventEnvelope, type: z7.literal("run.cancelled") }).strict();
|
|
2819
|
+
var GraphEventV1Schema = z7.discriminatedUnion("type", [
|
|
2820
|
+
RunStartedEventSchema,
|
|
2821
|
+
NodeDispatchedEventSchema,
|
|
2822
|
+
NodeSucceededEventSchema,
|
|
2823
|
+
NodeFailedEventSchema,
|
|
2824
|
+
NodeCancelledEventSchema,
|
|
2825
|
+
RunCompletedEventSchema,
|
|
2826
|
+
RunFailedEventSchema,
|
|
2827
|
+
RunCancelledEventSchema
|
|
2828
|
+
]);
|
|
2829
|
+
|
|
2830
|
+
// src/graph/ids.ts
|
|
2831
|
+
var NUL = "\x00";
|
|
2832
|
+
function operationID(runID, nodeID, revision = 0) {
|
|
2833
|
+
return sha256Hex(`op${NUL}${runID}${NUL}${nodeID}${NUL}${revision}`).slice(0, 32);
|
|
2834
|
+
}
|
|
2835
|
+
function attemptID(operation, attempt) {
|
|
2836
|
+
return sha256Hex(`att${NUL}${operation}${NUL}${attempt}`).slice(0, 32);
|
|
2837
|
+
}
|
|
2838
|
+
function canonicalSpec(spec) {
|
|
2839
|
+
const nodes = spec.nodes.map((node) => ({
|
|
2840
|
+
id: node.id,
|
|
2841
|
+
role: node.role,
|
|
2842
|
+
dependsOn: [...node.dependsOn],
|
|
2843
|
+
anchor: { taskID: node.anchor.taskID, specDigest: node.anchor.specDigest },
|
|
2844
|
+
...node.reviews === undefined ? {} : { reviews: node.reviews }
|
|
2845
|
+
}));
|
|
2846
|
+
return JSON.stringify({
|
|
2847
|
+
version: spec.version,
|
|
2848
|
+
runID: spec.runID,
|
|
2849
|
+
nodes
|
|
2850
|
+
});
|
|
1928
2851
|
}
|
|
1929
|
-
function
|
|
1930
|
-
return
|
|
2852
|
+
function graphSpecDigest(spec) {
|
|
2853
|
+
return sha256Hex(canonicalSpec(spec));
|
|
1931
2854
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
2855
|
+
|
|
2856
|
+
// src/graph/validate.ts
|
|
2857
|
+
class GraphValidationError extends Error {
|
|
2858
|
+
code;
|
|
2859
|
+
detail;
|
|
2860
|
+
constructor(code, detail) {
|
|
2861
|
+
super(`${code}: ${detail}`);
|
|
2862
|
+
this.name = "GraphValidationError";
|
|
2863
|
+
this.code = code;
|
|
2864
|
+
this.detail = detail;
|
|
2865
|
+
}
|
|
1934
2866
|
}
|
|
1935
|
-
function
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
2867
|
+
function indexNodes(nodes) {
|
|
2868
|
+
const byID = new Map;
|
|
2869
|
+
for (const node of nodes) {
|
|
2870
|
+
if (byID.has(node.id)) {
|
|
2871
|
+
throw new GraphValidationError("duplicate-node", node.id);
|
|
2872
|
+
}
|
|
2873
|
+
byID.set(node.id, node);
|
|
2874
|
+
}
|
|
2875
|
+
return byID;
|
|
1940
2876
|
}
|
|
1941
|
-
function
|
|
1942
|
-
const
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
2877
|
+
function assertDependenciesExist(nodes, byID) {
|
|
2878
|
+
for (const node of nodes) {
|
|
2879
|
+
for (const dep of node.dependsOn) {
|
|
2880
|
+
if (!byID.has(dep)) {
|
|
2881
|
+
throw new GraphValidationError("missing-dependency", `${node.id} -> ${dep}`);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
1946
2884
|
}
|
|
1947
|
-
|
|
1948
|
-
|
|
2885
|
+
}
|
|
2886
|
+
function assertAcyclic(nodes, byID) {
|
|
2887
|
+
const state = new Map;
|
|
2888
|
+
const visit = (id) => {
|
|
2889
|
+
const current = state.get(id);
|
|
2890
|
+
if (current === "done") {
|
|
2891
|
+
return;
|
|
2892
|
+
}
|
|
2893
|
+
if (current === "visiting") {
|
|
2894
|
+
throw new GraphValidationError("cycle", id);
|
|
2895
|
+
}
|
|
2896
|
+
state.set(id, "visiting");
|
|
2897
|
+
const node = byID.get(id);
|
|
2898
|
+
for (const dep of node.dependsOn) {
|
|
2899
|
+
visit(dep);
|
|
2900
|
+
}
|
|
2901
|
+
state.set(id, "done");
|
|
2902
|
+
};
|
|
2903
|
+
for (const node of nodes) {
|
|
2904
|
+
visit(node.id);
|
|
1949
2905
|
}
|
|
1950
|
-
|
|
1951
|
-
|
|
2906
|
+
}
|
|
2907
|
+
function assertReviewRules(nodes, byID) {
|
|
2908
|
+
for (const node of nodes) {
|
|
2909
|
+
if (node.role === "implementer") {
|
|
2910
|
+
if (node.reviews !== undefined) {
|
|
2911
|
+
throw new GraphValidationError("implementer-has-review", node.id);
|
|
2912
|
+
}
|
|
2913
|
+
continue;
|
|
2914
|
+
}
|
|
2915
|
+
if (node.reviews === undefined) {
|
|
2916
|
+
throw new GraphValidationError("reviewer-missing-review", node.id);
|
|
2917
|
+
}
|
|
2918
|
+
if (node.reviews === node.id) {
|
|
2919
|
+
throw new GraphValidationError("self-review", node.id);
|
|
2920
|
+
}
|
|
2921
|
+
const target = byID.get(node.reviews);
|
|
2922
|
+
if (target === undefined || target.role !== "implementer") {
|
|
2923
|
+
throw new GraphValidationError("invalid-review-target", `${node.id} -> ${node.reviews}`);
|
|
2924
|
+
}
|
|
1952
2925
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
2926
|
+
}
|
|
2927
|
+
function assertConnected(nodes) {
|
|
2928
|
+
const adjacency = new Map;
|
|
2929
|
+
for (const node of nodes) {
|
|
2930
|
+
adjacency.set(node.id, adjacency.get(node.id) ?? new Set);
|
|
2931
|
+
for (const dep of node.dependsOn) {
|
|
2932
|
+
adjacency.get(node.id)?.add(dep);
|
|
2933
|
+
const back = adjacency.get(dep) ?? new Set;
|
|
2934
|
+
back.add(node.id);
|
|
2935
|
+
adjacency.set(dep, back);
|
|
2936
|
+
}
|
|
1955
2937
|
}
|
|
1956
|
-
|
|
1957
|
-
|
|
2938
|
+
const root = nodes[0];
|
|
2939
|
+
const seen = new Set([root.id]);
|
|
2940
|
+
const queue = [root.id];
|
|
2941
|
+
while (queue.length > 0) {
|
|
2942
|
+
const id = queue.shift();
|
|
2943
|
+
for (const neighbor of adjacency.get(id) ?? new Set) {
|
|
2944
|
+
if (!seen.has(neighbor)) {
|
|
2945
|
+
seen.add(neighbor);
|
|
2946
|
+
queue.push(neighbor);
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
if (seen.size !== nodes.length) {
|
|
2951
|
+
throw new GraphValidationError("disconnected", `${seen.size}/${nodes.length} reachable`);
|
|
1958
2952
|
}
|
|
1959
|
-
|
|
2953
|
+
}
|
|
2954
|
+
function validateGraphSpec(spec) {
|
|
2955
|
+
const byID = indexNodes(spec.nodes);
|
|
2956
|
+
assertDependenciesExist(spec.nodes, byID);
|
|
2957
|
+
assertAcyclic(spec.nodes, byID);
|
|
2958
|
+
assertReviewRules(spec.nodes, byID);
|
|
2959
|
+
assertConnected(spec.nodes);
|
|
1960
2960
|
}
|
|
1961
2961
|
|
|
1962
2962
|
// src/capabilities/classify.ts
|
|
@@ -2033,24 +3033,62 @@ function classifyHeuristic(task) {
|
|
|
2033
3033
|
};
|
|
2034
3034
|
}
|
|
2035
3035
|
|
|
2036
|
-
// src/
|
|
2037
|
-
|
|
2038
|
-
|
|
3036
|
+
// src/capabilities/budget.ts
|
|
3037
|
+
var WARNING_UTILIZATION = 0.8;
|
|
3038
|
+
function projectedSpend(current, estimatedCostUSD) {
|
|
3039
|
+
return Math.max(0, current) + Math.max(0, estimatedCostUSD);
|
|
2039
3040
|
}
|
|
2040
|
-
function
|
|
2041
|
-
return
|
|
3041
|
+
function budgetExceeded(projected, limit) {
|
|
3042
|
+
return limit !== undefined && projected >= limit;
|
|
2042
3043
|
}
|
|
2043
|
-
function
|
|
2044
|
-
return
|
|
3044
|
+
function budgetNearLimit(projected, limit) {
|
|
3045
|
+
return limit !== undefined && projected >= limit * WARNING_UTILIZATION && projected < limit;
|
|
2045
3046
|
}
|
|
2046
|
-
function
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
}
|
|
2051
|
-
return [...byKey.values()].sort(compareModels);
|
|
3047
|
+
function exhaustedDecision(budgets, reason) {
|
|
3048
|
+
return {
|
|
3049
|
+
action: budgets.hardStopOnBudgetExhaustion ? "blockFrontier" : "forceLocal",
|
|
3050
|
+
reason
|
|
3051
|
+
};
|
|
2052
3052
|
}
|
|
2053
|
-
function
|
|
3053
|
+
function evaluateBudget(state, estimatedCostUSD, budgets) {
|
|
3054
|
+
const projectedSessionUSD = projectedSpend(state.sessionUSD, estimatedCostUSD);
|
|
3055
|
+
const projectedMonthlyUSD = budgets.monthlyUSD === undefined ? undefined : projectedSpend(state.monthlyUSD ?? 0, estimatedCostUSD);
|
|
3056
|
+
if (budgetExceeded(projectedSessionUSD, budgets.sessionUSD)) {
|
|
3057
|
+
return exhaustedDecision(budgets, "session-budget-exhausted");
|
|
3058
|
+
}
|
|
3059
|
+
if (budgetExceeded(projectedMonthlyUSD ?? 0, budgets.monthlyUSD)) {
|
|
3060
|
+
return exhaustedDecision(budgets, "monthly-budget-exhausted");
|
|
3061
|
+
}
|
|
3062
|
+
if (state.frontierTokens !== undefined && budgets.frontierTokensPerSession !== undefined && state.frontierTokens >= budgets.frontierTokensPerSession) {
|
|
3063
|
+
return exhaustedDecision(budgets, "frontier-token-budget-exhausted");
|
|
3064
|
+
}
|
|
3065
|
+
if (budgetNearLimit(projectedSessionUSD, budgets.sessionUSD)) {
|
|
3066
|
+
return { action: "warn", reason: "session-budget-near-limit" };
|
|
3067
|
+
}
|
|
3068
|
+
if (budgetNearLimit(projectedMonthlyUSD ?? 0, budgets.monthlyUSD)) {
|
|
3069
|
+
return { action: "warn", reason: "monthly-budget-near-limit" };
|
|
3070
|
+
}
|
|
3071
|
+
return { action: "ok" };
|
|
3072
|
+
}
|
|
3073
|
+
|
|
3074
|
+
// src/router/modelSelection.ts
|
|
3075
|
+
function modelKey2(model) {
|
|
3076
|
+
return `${model.providerID}/${model.modelID}`;
|
|
3077
|
+
}
|
|
3078
|
+
function sameModel(left, right) {
|
|
3079
|
+
return left.providerID === right.providerID && left.modelID === right.modelID;
|
|
3080
|
+
}
|
|
3081
|
+
function compareModels(left, right) {
|
|
3082
|
+
return modelKey2(left).localeCompare(modelKey2(right));
|
|
3083
|
+
}
|
|
3084
|
+
function uniqueModelSelections(models) {
|
|
3085
|
+
const byKey = new Map;
|
|
3086
|
+
for (const model of models) {
|
|
3087
|
+
byKey.set(modelKey2(model), model);
|
|
3088
|
+
}
|
|
3089
|
+
return [...byKey.values()].sort(compareModels);
|
|
3090
|
+
}
|
|
3091
|
+
function localDefault(config) {
|
|
2054
3092
|
return config.local.runtimes.find((runtime) => runtime.enabled)?.defaultModel ?? config.router.localDefault;
|
|
2055
3093
|
}
|
|
2056
3094
|
function resolveConfiguredFrontierBaseline(config) {
|
|
@@ -2353,22 +3391,22 @@ function toCostRecord(decision2, context) {
|
|
|
2353
3391
|
}
|
|
2354
3392
|
|
|
2355
3393
|
// src/telemetry/types.ts
|
|
2356
|
-
import { z as
|
|
2357
|
-
var CostRecordSchema =
|
|
2358
|
-
ts:
|
|
2359
|
-
sessionID:
|
|
2360
|
-
promptHash:
|
|
2361
|
-
promptChars:
|
|
3394
|
+
import { z as z8 } from "zod";
|
|
3395
|
+
var CostRecordSchema = z8.object({
|
|
3396
|
+
ts: z8.number().finite(),
|
|
3397
|
+
sessionID: z8.string().min(1).optional(),
|
|
3398
|
+
promptHash: z8.string().min(1),
|
|
3399
|
+
promptChars: z8.number().int().min(0),
|
|
2362
3400
|
tier: ComplexityTierSchema,
|
|
2363
|
-
routeKind:
|
|
3401
|
+
routeKind: z8.enum(["local", "frontier"]),
|
|
2364
3402
|
selected: ModelRefSchema,
|
|
2365
|
-
rationale:
|
|
2366
|
-
estimatedCostUSD:
|
|
2367
|
-
baselineCostUSD:
|
|
2368
|
-
estimatedSavingsUSD:
|
|
2369
|
-
budgetAction:
|
|
2370
|
-
tokensIn:
|
|
2371
|
-
tokensOut:
|
|
3403
|
+
rationale: z8.string(),
|
|
3404
|
+
estimatedCostUSD: z8.number().finite().min(0),
|
|
3405
|
+
baselineCostUSD: z8.number().finite().min(0),
|
|
3406
|
+
estimatedSavingsUSD: z8.number().finite(),
|
|
3407
|
+
budgetAction: z8.string().min(1),
|
|
3408
|
+
tokensIn: z8.number().int().min(0).optional(),
|
|
3409
|
+
tokensOut: z8.number().int().min(0).optional()
|
|
2372
3410
|
});
|
|
2373
3411
|
|
|
2374
3412
|
// src/telemetry/read.ts
|
|
@@ -2528,6 +3566,973 @@ function hashPrompt(prompt) {
|
|
|
2528
3566
|
return `fnv1a32:${(hash >>> 0).toString(16).padStart(8, "0")}`;
|
|
2529
3567
|
}
|
|
2530
3568
|
|
|
3569
|
+
// src/orchestrator/graphIngress.ts
|
|
3570
|
+
var ACTIVE_INGRESS_BRAND = Symbol("openteam.activeIngress");
|
|
3571
|
+
function gateReason(policy) {
|
|
3572
|
+
if (policy.capability === undefined) {
|
|
3573
|
+
return "gate-closed";
|
|
3574
|
+
}
|
|
3575
|
+
if (policy.graph.killSwitch) {
|
|
3576
|
+
return "kill-switch";
|
|
3577
|
+
}
|
|
3578
|
+
return policy.graph.configuredMode === "active" ? undefined : "graph-disabled";
|
|
3579
|
+
}
|
|
3580
|
+
function decideIngress(policy, taskID) {
|
|
3581
|
+
const blocked = gateReason(policy);
|
|
3582
|
+
if (blocked !== undefined) {
|
|
3583
|
+
return { route: "legacy", reason: blocked };
|
|
3584
|
+
}
|
|
3585
|
+
if (!policy.allowList.includes(taskID)) {
|
|
3586
|
+
return { route: "legacy", reason: "not-allow-listed" };
|
|
3587
|
+
}
|
|
3588
|
+
return { route: "graph", taskID };
|
|
3589
|
+
}
|
|
3590
|
+
|
|
3591
|
+
// src/plugin/legacyRunProducer.ts
|
|
3592
|
+
function toObservedNode(node) {
|
|
3593
|
+
return {
|
|
3594
|
+
id: node.id,
|
|
3595
|
+
role: node.role,
|
|
3596
|
+
model: node.model,
|
|
3597
|
+
ok: node.ok,
|
|
3598
|
+
...node.sessionRef !== undefined ? { sessionRef: node.sessionRef } : {},
|
|
3599
|
+
...node.errorClass !== undefined ? { errorClass: node.errorClass } : {}
|
|
3600
|
+
};
|
|
3601
|
+
}
|
|
3602
|
+
function buildObservedRun(payload) {
|
|
3603
|
+
return {
|
|
3604
|
+
runID: payload.runID,
|
|
3605
|
+
executed: true,
|
|
3606
|
+
status: payload.status,
|
|
3607
|
+
fixes: payload.fixes,
|
|
3608
|
+
nodes: payload.nodes.map(toObservedNode),
|
|
3609
|
+
...payload.parentSessionRef !== undefined ? { parentSessionRef: payload.parentSessionRef } : {}
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
function crossVerify(payload, tracker) {
|
|
3613
|
+
const unverifiedModels = [];
|
|
3614
|
+
const modelMismatches = [];
|
|
3615
|
+
const unknownSessions = [];
|
|
3616
|
+
for (const node of payload.nodes) {
|
|
3617
|
+
if (node.sessionRef !== undefined) {
|
|
3618
|
+
const mv = tracker.verifyModel(node.sessionRef, node.model);
|
|
3619
|
+
if (!mv.verified) {
|
|
3620
|
+
unverifiedModels.push(node.id);
|
|
3621
|
+
} else if (!mv.match) {
|
|
3622
|
+
modelMismatches.push(node.id);
|
|
3623
|
+
}
|
|
3624
|
+
const sv = tracker.verifySessionRef(node.sessionRef);
|
|
3625
|
+
if (sv.known === false) {
|
|
3626
|
+
unknownSessions.push(node.id);
|
|
3627
|
+
}
|
|
3628
|
+
} else {
|
|
3629
|
+
unverifiedModels.push(node.id);
|
|
3630
|
+
}
|
|
3631
|
+
}
|
|
3632
|
+
return { unverifiedModels, modelMismatches, unknownSessions };
|
|
3633
|
+
}
|
|
3634
|
+
function formatWarnings(verification) {
|
|
3635
|
+
const warnings = [];
|
|
3636
|
+
if (verification.unverifiedModels.length > 0) {
|
|
3637
|
+
warnings.push(`model not verifiable for nodes: ${verification.unverifiedModels.join(", ")}`);
|
|
3638
|
+
}
|
|
3639
|
+
if (verification.modelMismatches.length > 0) {
|
|
3640
|
+
warnings.push(`model mismatch for nodes: ${verification.modelMismatches.join(", ")}`);
|
|
3641
|
+
}
|
|
3642
|
+
if (verification.unknownSessions.length > 0) {
|
|
3643
|
+
warnings.push(`unknown sessionRef for nodes: ${verification.unknownSessions.join(", ")}`);
|
|
3644
|
+
}
|
|
3645
|
+
return warnings;
|
|
3646
|
+
}
|
|
3647
|
+
function formatResponse(response) {
|
|
3648
|
+
const parts = [`report-run: ${response.result}`];
|
|
3649
|
+
if (response.detail !== undefined) {
|
|
3650
|
+
parts.push(response.detail);
|
|
3651
|
+
}
|
|
3652
|
+
if (response.warnings !== undefined && response.warnings.length > 0) {
|
|
3653
|
+
parts.push(`warnings: ${response.warnings.join("; ")}`);
|
|
3654
|
+
}
|
|
3655
|
+
return parts.join(" — ");
|
|
3656
|
+
}
|
|
3657
|
+
|
|
3658
|
+
// src/plugin/reportRunSchema.ts
|
|
3659
|
+
import { z as z10 } from "zod";
|
|
3660
|
+
|
|
3661
|
+
// src/plugin/graphShadowIngress.ts
|
|
3662
|
+
import { z as z9 } from "zod";
|
|
3663
|
+
var LEGACY_EXECUTION_TRACE_VERSION = 1;
|
|
3664
|
+
var LegacyExecutionStatusSchema = z9.enum([
|
|
3665
|
+
"completed",
|
|
3666
|
+
"failed",
|
|
3667
|
+
"cancelled"
|
|
3668
|
+
]);
|
|
3669
|
+
var LegacyReviewOutcomeSchema = z9.enum([
|
|
3670
|
+
"approved",
|
|
3671
|
+
"rejected",
|
|
3672
|
+
"inconclusive"
|
|
3673
|
+
]);
|
|
3674
|
+
var LegacyTraceNodeV1Schema = z9.object({
|
|
3675
|
+
id: NodeIDSchema,
|
|
3676
|
+
role: NodeRoleSchema,
|
|
3677
|
+
model: z9.string().min(1),
|
|
3678
|
+
sessionRef: z9.string().min(1).optional(),
|
|
3679
|
+
errorClass: ErrorClassSchema.optional()
|
|
3680
|
+
}).strict();
|
|
3681
|
+
var LegacyExecutionTraceV1Schema = z9.object({
|
|
3682
|
+
version: z9.literal(LEGACY_EXECUTION_TRACE_VERSION),
|
|
3683
|
+
runID: NodeIDSchema,
|
|
3684
|
+
status: LegacyExecutionStatusSchema,
|
|
3685
|
+
outcome: LegacyReviewOutcomeSchema,
|
|
3686
|
+
fixes: z9.number().int().nonnegative(),
|
|
3687
|
+
nodes: z9.array(LegacyTraceNodeV1Schema).min(1),
|
|
3688
|
+
parentSessionRef: z9.string().min(1).optional()
|
|
3689
|
+
}).strict();
|
|
3690
|
+
function gateReason2(gate) {
|
|
3691
|
+
if (gate === undefined || gate.mode === "off") {
|
|
3692
|
+
return "graph-disabled";
|
|
3693
|
+
}
|
|
3694
|
+
if (gate.killSwitch) {
|
|
3695
|
+
return "kill-switch";
|
|
3696
|
+
}
|
|
3697
|
+
return;
|
|
3698
|
+
}
|
|
3699
|
+
function deriveOutcome(nodes) {
|
|
3700
|
+
for (let index = nodes.length - 1;index >= 0; index -= 1) {
|
|
3701
|
+
const node = nodes[index];
|
|
3702
|
+
if (node !== undefined && node.role === "reviewer") {
|
|
3703
|
+
return node.ok ? "approved" : "rejected";
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
return "inconclusive";
|
|
3707
|
+
}
|
|
3708
|
+
function toTraceNode(node) {
|
|
3709
|
+
return {
|
|
3710
|
+
id: node.id,
|
|
3711
|
+
role: node.role,
|
|
3712
|
+
model: node.model,
|
|
3713
|
+
...node.sessionRef !== undefined ? { sessionRef: node.sessionRef } : {},
|
|
3714
|
+
...node.errorClass !== undefined ? { errorClass: node.errorClass } : {}
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3717
|
+
function ingestLegacyExecution(observation, gate) {
|
|
3718
|
+
const gated = gateReason2(gate);
|
|
3719
|
+
if (gated !== undefined) {
|
|
3720
|
+
return { eligible: false, reason: gated };
|
|
3721
|
+
}
|
|
3722
|
+
if (!observation.executed) {
|
|
3723
|
+
return { eligible: false, reason: "not-executed" };
|
|
3724
|
+
}
|
|
3725
|
+
if (observation.nodes.length === 0) {
|
|
3726
|
+
return { eligible: false, reason: "no-observation" };
|
|
3727
|
+
}
|
|
3728
|
+
const trace = {
|
|
3729
|
+
version: LEGACY_EXECUTION_TRACE_VERSION,
|
|
3730
|
+
runID: observation.runID,
|
|
3731
|
+
status: observation.status,
|
|
3732
|
+
outcome: deriveOutcome(observation.nodes),
|
|
3733
|
+
fixes: Math.max(0, Math.trunc(observation.fixes)),
|
|
3734
|
+
nodes: observation.nodes.map(toTraceNode),
|
|
3735
|
+
...observation.parentSessionRef !== undefined ? { parentSessionRef: observation.parentSessionRef } : {}
|
|
3736
|
+
};
|
|
3737
|
+
assertReferenceOnly(trace);
|
|
3738
|
+
return { eligible: true, trace };
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
class LegacyTraceError extends Error {
|
|
3742
|
+
code = "invalid-legacy-trace";
|
|
3743
|
+
issues;
|
|
3744
|
+
constructor(error) {
|
|
3745
|
+
super(`invalid-legacy-trace: ${error.issues.map((issue) => issue.message).join("; ")}`);
|
|
3746
|
+
this.name = "LegacyTraceError";
|
|
3747
|
+
this.issues = error.issues.map((issue) => issue.message);
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
function parseLegacyExecutionTrace(input) {
|
|
3751
|
+
const result = LegacyExecutionTraceV1Schema.safeParse(input);
|
|
3752
|
+
if (!result.success) {
|
|
3753
|
+
throw new LegacyTraceError(result.error);
|
|
3754
|
+
}
|
|
3755
|
+
return result.data;
|
|
3756
|
+
}
|
|
3757
|
+
|
|
3758
|
+
// src/plugin/reportRunSchema.ts
|
|
3759
|
+
var ReportRunNodeSchema = z10.object({
|
|
3760
|
+
id: NodeIDSchema,
|
|
3761
|
+
role: NodeRoleSchema,
|
|
3762
|
+
model: z10.string().min(1),
|
|
3763
|
+
ok: z10.boolean(),
|
|
3764
|
+
sessionRef: z10.string().min(1).optional(),
|
|
3765
|
+
errorClass: ErrorClassSchema.optional()
|
|
3766
|
+
}).strict();
|
|
3767
|
+
var ReportRunPayloadSchema = z10.object({
|
|
3768
|
+
runID: NodeIDSchema,
|
|
3769
|
+
status: LegacyExecutionStatusSchema,
|
|
3770
|
+
fixes: z10.number().int().nonnegative(),
|
|
3771
|
+
nodes: z10.array(ReportRunNodeSchema).min(1),
|
|
3772
|
+
parentSessionRef: z10.string().min(1).optional()
|
|
3773
|
+
}).strict();
|
|
3774
|
+
|
|
3775
|
+
// src/graph/types.ts
|
|
3776
|
+
var TERMINAL_NODE_STATUSES = new Set([
|
|
3777
|
+
"succeeded",
|
|
3778
|
+
"cancelled"
|
|
3779
|
+
]);
|
|
3780
|
+
var TERMINAL_RUN_STATUSES = new Set([
|
|
3781
|
+
"completed",
|
|
3782
|
+
"failed",
|
|
3783
|
+
"cancelled"
|
|
3784
|
+
]);
|
|
3785
|
+
|
|
3786
|
+
// src/graph/reducer.ts
|
|
3787
|
+
class ReducerError extends Error {
|
|
3788
|
+
code;
|
|
3789
|
+
detail;
|
|
3790
|
+
constructor(code, detail) {
|
|
3791
|
+
super(`${code}: ${detail}`);
|
|
3792
|
+
this.name = "ReducerError";
|
|
3793
|
+
this.code = code;
|
|
3794
|
+
this.detail = detail;
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
function initialState(spec) {
|
|
3798
|
+
const nodes = {};
|
|
3799
|
+
for (const node of spec.nodes) {
|
|
3800
|
+
nodes[node.id] = { status: "pending", attempts: 0 };
|
|
3801
|
+
}
|
|
3802
|
+
return { runID: spec.runID, status: "running", seq: -1, nodes };
|
|
3803
|
+
}
|
|
3804
|
+
function withNode(state, nodeID, next) {
|
|
3805
|
+
return {
|
|
3806
|
+
...state,
|
|
3807
|
+
nodes: { ...state.nodes, [nodeID]: next },
|
|
3808
|
+
seq: state.seq
|
|
3809
|
+
};
|
|
3810
|
+
}
|
|
3811
|
+
function specNode(spec, nodeID) {
|
|
3812
|
+
return spec.nodes.find((node) => node.id === nodeID);
|
|
3813
|
+
}
|
|
3814
|
+
function applyRunStarted(state, seq, specDigest) {
|
|
3815
|
+
if (state.specDigest !== undefined) {
|
|
3816
|
+
if (state.specDigest === specDigest) {
|
|
3817
|
+
return state;
|
|
3818
|
+
}
|
|
3819
|
+
throw new ReducerError("run-started-conflict", specDigest);
|
|
3820
|
+
}
|
|
3821
|
+
return { ...state, specDigest, seq };
|
|
3822
|
+
}
|
|
3823
|
+
function applyDispatched(spec, state, event) {
|
|
3824
|
+
const node = specNode(spec, event.nodeID);
|
|
3825
|
+
const current = state.nodes[event.nodeID];
|
|
3826
|
+
if (node === undefined || current === undefined) {
|
|
3827
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3828
|
+
}
|
|
3829
|
+
for (const dep of node.dependsOn) {
|
|
3830
|
+
if (state.nodes[dep]?.status !== "succeeded") {
|
|
3831
|
+
throw new ReducerError("dependency-not-satisfied", `${event.nodeID} <- ${dep}`);
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
const running = {
|
|
3835
|
+
status: "running",
|
|
3836
|
+
attempts: current.attempts + 1,
|
|
3837
|
+
operationID: event.operationID,
|
|
3838
|
+
attemptID: event.attemptID
|
|
3839
|
+
};
|
|
3840
|
+
if (current.status === "pending") {
|
|
3841
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, running);
|
|
3842
|
+
}
|
|
3843
|
+
if (current.status === "failed") {
|
|
3844
|
+
if (current.operationID !== event.operationID) {
|
|
3845
|
+
throw new ReducerError("dispatch-conflict", event.nodeID);
|
|
3846
|
+
}
|
|
3847
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, running);
|
|
3848
|
+
}
|
|
3849
|
+
if (current.status === "running") {
|
|
3850
|
+
if (current.operationID === event.operationID && current.attemptID === event.attemptID) {
|
|
3851
|
+
return state;
|
|
3852
|
+
}
|
|
3853
|
+
throw new ReducerError("dispatch-conflict", event.nodeID);
|
|
3854
|
+
}
|
|
3855
|
+
throw new ReducerError("illegal-transition", `dispatch ${current.status}`);
|
|
3856
|
+
}
|
|
3857
|
+
function applySucceeded(state, event) {
|
|
3858
|
+
const current = state.nodes[event.nodeID];
|
|
3859
|
+
if (current === undefined) {
|
|
3860
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3861
|
+
}
|
|
3862
|
+
if (current.status === "succeeded") {
|
|
3863
|
+
if (current.operationID === event.operationID && current.artifactSha256 === event.artifact.sha256) {
|
|
3864
|
+
return state;
|
|
3865
|
+
}
|
|
3866
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3867
|
+
}
|
|
3868
|
+
if (current.status !== "running") {
|
|
3869
|
+
throw new ReducerError("illegal-transition", `succeed ${current.status}`);
|
|
3870
|
+
}
|
|
3871
|
+
if (current.operationID !== event.operationID || current.attemptID !== event.attemptID) {
|
|
3872
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3873
|
+
}
|
|
3874
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, {
|
|
3875
|
+
status: "succeeded",
|
|
3876
|
+
attempts: current.attempts,
|
|
3877
|
+
operationID: event.operationID,
|
|
3878
|
+
attemptID: event.attemptID,
|
|
3879
|
+
artifactSha256: event.artifact.sha256
|
|
3880
|
+
});
|
|
3881
|
+
}
|
|
3882
|
+
function applyFailed(state, event) {
|
|
3883
|
+
const current = state.nodes[event.nodeID];
|
|
3884
|
+
if (current === undefined) {
|
|
3885
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3886
|
+
}
|
|
3887
|
+
if (current.status === "failed") {
|
|
3888
|
+
if (current.operationID === event.operationID && current.attemptID === event.attemptID) {
|
|
3889
|
+
return state;
|
|
3890
|
+
}
|
|
3891
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3892
|
+
}
|
|
3893
|
+
if (current.status !== "running") {
|
|
3894
|
+
throw new ReducerError("illegal-transition", `fail ${current.status}`);
|
|
3895
|
+
}
|
|
3896
|
+
if (current.operationID !== event.operationID || current.attemptID !== event.attemptID) {
|
|
3897
|
+
throw new ReducerError("receipt-conflict", event.nodeID);
|
|
3898
|
+
}
|
|
3899
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, {
|
|
3900
|
+
status: "failed",
|
|
3901
|
+
attempts: current.attempts,
|
|
3902
|
+
operationID: event.operationID,
|
|
3903
|
+
attemptID: event.attemptID
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
3906
|
+
function applyCancelled(state, event) {
|
|
3907
|
+
const current = state.nodes[event.nodeID];
|
|
3908
|
+
if (current === undefined) {
|
|
3909
|
+
throw new ReducerError("unknown-node", event.nodeID);
|
|
3910
|
+
}
|
|
3911
|
+
if (current.status === "cancelled") {
|
|
3912
|
+
return state;
|
|
3913
|
+
}
|
|
3914
|
+
if (current.status === "succeeded") {
|
|
3915
|
+
throw new ReducerError("illegal-transition", "cancel succeeded");
|
|
3916
|
+
}
|
|
3917
|
+
return withNode({ ...state, seq: event.seq }, event.nodeID, {
|
|
3918
|
+
...current,
|
|
3919
|
+
status: "cancelled"
|
|
3920
|
+
});
|
|
3921
|
+
}
|
|
3922
|
+
function applyRunCompleted(state, seq) {
|
|
3923
|
+
for (const node of Object.values(state.nodes)) {
|
|
3924
|
+
if (node.status !== "succeeded" && node.status !== "cancelled") {
|
|
3925
|
+
throw new ReducerError("incomplete-run", node.status);
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
return { ...state, status: "completed", seq };
|
|
3929
|
+
}
|
|
3930
|
+
function applyEvent(spec, state, event) {
|
|
3931
|
+
if (event.runID !== state.runID) {
|
|
3932
|
+
throw new ReducerError("run-mismatch", event.runID);
|
|
3933
|
+
}
|
|
3934
|
+
if (TERMINAL_RUN_STATUSES.has(state.status)) {
|
|
3935
|
+
if (event.type === "run.completed" && state.status === "completed" || event.type === "run.failed" && state.status === "failed" || event.type === "run.cancelled" && state.status === "cancelled") {
|
|
3936
|
+
return state;
|
|
3937
|
+
}
|
|
3938
|
+
throw new ReducerError("post-terminal", event.type);
|
|
3939
|
+
}
|
|
3940
|
+
switch (event.type) {
|
|
3941
|
+
case "run.started":
|
|
3942
|
+
return applyRunStarted(state, event.seq, event.specDigest);
|
|
3943
|
+
case "node.dispatched":
|
|
3944
|
+
return applyDispatched(spec, state, event);
|
|
3945
|
+
case "node.succeeded":
|
|
3946
|
+
return applySucceeded(state, event);
|
|
3947
|
+
case "node.failed":
|
|
3948
|
+
return applyFailed(state, event);
|
|
3949
|
+
case "node.cancelled":
|
|
3950
|
+
return applyCancelled(state, event);
|
|
3951
|
+
case "run.completed":
|
|
3952
|
+
return applyRunCompleted(state, event.seq);
|
|
3953
|
+
case "run.failed":
|
|
3954
|
+
return { ...state, status: "failed", seq: event.seq };
|
|
3955
|
+
case "run.cancelled":
|
|
3956
|
+
return { ...state, status: "cancelled", seq: event.seq };
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
|
|
3960
|
+
// src/orchestrator/graphShadow.ts
|
|
3961
|
+
class ShadowMirrorError extends Error {
|
|
3962
|
+
code;
|
|
3963
|
+
detail;
|
|
3964
|
+
constructor(code, detail) {
|
|
3965
|
+
super(`${code}: ${detail}`);
|
|
3966
|
+
this.name = "ShadowMirrorError";
|
|
3967
|
+
this.code = code;
|
|
3968
|
+
this.detail = detail;
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
function traceAnchorDigest(trace) {
|
|
3972
|
+
return sha256Hex(JSON.stringify({
|
|
3973
|
+
version: trace.version,
|
|
3974
|
+
runID: trace.runID,
|
|
3975
|
+
nodes: trace.nodes.map((node) => [node.id, node.role])
|
|
3976
|
+
}));
|
|
3977
|
+
}
|
|
3978
|
+
function compileShadowSpec(trace) {
|
|
3979
|
+
const anchor = { taskID: trace.runID, specDigest: traceAnchorDigest(trace) };
|
|
3980
|
+
const nodes = [];
|
|
3981
|
+
let previousID;
|
|
3982
|
+
let lastImplementerID;
|
|
3983
|
+
for (const node of trace.nodes) {
|
|
3984
|
+
const dependsOn = previousID === undefined ? [] : [previousID];
|
|
3985
|
+
if (node.role === "implementer") {
|
|
3986
|
+
nodes.push({ id: node.id, role: "implementer", dependsOn, anchor });
|
|
3987
|
+
lastImplementerID = node.id;
|
|
3988
|
+
} else {
|
|
3989
|
+
nodes.push({
|
|
3990
|
+
id: node.id,
|
|
3991
|
+
role: "reviewer",
|
|
3992
|
+
dependsOn,
|
|
3993
|
+
anchor,
|
|
3994
|
+
...lastImplementerID === undefined ? {} : { reviews: lastImplementerID }
|
|
3995
|
+
});
|
|
3996
|
+
}
|
|
3997
|
+
previousID = node.id;
|
|
3998
|
+
}
|
|
3999
|
+
let spec;
|
|
4000
|
+
try {
|
|
4001
|
+
spec = GraphSpecV1Schema.parse({ version: 1, runID: trace.runID, nodes });
|
|
4002
|
+
validateGraphSpec(spec);
|
|
4003
|
+
} catch (error) {
|
|
4004
|
+
throw new ShadowMirrorError("invalid-spec", error instanceof Error ? error.message : String(error));
|
|
4005
|
+
}
|
|
4006
|
+
assertReferenceOnly(spec);
|
|
4007
|
+
return spec;
|
|
4008
|
+
}
|
|
4009
|
+
var TERMINAL_EVENT = {
|
|
4010
|
+
completed: "run.completed",
|
|
4011
|
+
failed: "run.failed",
|
|
4012
|
+
cancelled: "run.cancelled"
|
|
4013
|
+
};
|
|
4014
|
+
function shadowArtifact(runID, nodeID) {
|
|
4015
|
+
return {
|
|
4016
|
+
uri: `shadow://${runID}/${nodeID}`,
|
|
4017
|
+
sha256: sha256Hex(`shadow-artifact\x00${runID}\x00${nodeID}`),
|
|
4018
|
+
bytes: 0
|
|
4019
|
+
};
|
|
4020
|
+
}
|
|
4021
|
+
function mirrorJournal(spec, trace) {
|
|
4022
|
+
const runID = trace.runID;
|
|
4023
|
+
const events = [
|
|
4024
|
+
{
|
|
4025
|
+
v: 1,
|
|
4026
|
+
seq: 0,
|
|
4027
|
+
runID,
|
|
4028
|
+
type: "run.started",
|
|
4029
|
+
specDigest: graphSpecDigest(spec)
|
|
4030
|
+
}
|
|
4031
|
+
];
|
|
4032
|
+
for (const node of trace.nodes) {
|
|
4033
|
+
const operation = operationID(runID, node.id);
|
|
4034
|
+
const attempt = attemptID(operation, 0);
|
|
4035
|
+
events.push({
|
|
4036
|
+
v: 1,
|
|
4037
|
+
seq: events.length,
|
|
4038
|
+
runID,
|
|
4039
|
+
type: "node.dispatched",
|
|
4040
|
+
nodeID: node.id,
|
|
4041
|
+
operationID: operation,
|
|
4042
|
+
attemptID: attempt
|
|
4043
|
+
});
|
|
4044
|
+
if (node.errorClass === undefined) {
|
|
4045
|
+
events.push({
|
|
4046
|
+
v: 1,
|
|
4047
|
+
seq: events.length,
|
|
4048
|
+
runID,
|
|
4049
|
+
type: "node.succeeded",
|
|
4050
|
+
nodeID: node.id,
|
|
4051
|
+
operationID: operation,
|
|
4052
|
+
attemptID: attempt,
|
|
4053
|
+
artifact: shadowArtifact(runID, node.id)
|
|
4054
|
+
});
|
|
4055
|
+
continue;
|
|
4056
|
+
}
|
|
4057
|
+
if (node.errorClass === "cancelled") {
|
|
4058
|
+
events.push({
|
|
4059
|
+
v: 1,
|
|
4060
|
+
seq: events.length,
|
|
4061
|
+
runID,
|
|
4062
|
+
type: "node.cancelled",
|
|
4063
|
+
nodeID: node.id
|
|
4064
|
+
});
|
|
4065
|
+
continue;
|
|
4066
|
+
}
|
|
4067
|
+
events.push({
|
|
4068
|
+
v: 1,
|
|
4069
|
+
seq: events.length,
|
|
4070
|
+
runID,
|
|
4071
|
+
type: "node.failed",
|
|
4072
|
+
nodeID: node.id,
|
|
4073
|
+
operationID: operation,
|
|
4074
|
+
attemptID: attempt,
|
|
4075
|
+
errorClass: node.errorClass
|
|
4076
|
+
});
|
|
4077
|
+
}
|
|
4078
|
+
events.push({
|
|
4079
|
+
v: 1,
|
|
4080
|
+
seq: events.length,
|
|
4081
|
+
runID,
|
|
4082
|
+
type: TERMINAL_EVENT[trace.status]
|
|
4083
|
+
});
|
|
4084
|
+
return events;
|
|
4085
|
+
}
|
|
4086
|
+
function reduceMirror(spec, journal) {
|
|
4087
|
+
let state = initialState(spec);
|
|
4088
|
+
try {
|
|
4089
|
+
for (const event of journal) {
|
|
4090
|
+
state = applyEvent(spec, state, event);
|
|
4091
|
+
}
|
|
4092
|
+
} catch (error) {
|
|
4093
|
+
throw new ShadowMirrorError("illegal-sequence", error instanceof ReducerError ? `${error.code}: ${error.detail}` : String(error));
|
|
4094
|
+
}
|
|
4095
|
+
return state;
|
|
4096
|
+
}
|
|
4097
|
+
function projectLegacySummary(trace) {
|
|
4098
|
+
return {
|
|
4099
|
+
runID: trace.runID,
|
|
4100
|
+
status: trace.status,
|
|
4101
|
+
outcome: trace.outcome,
|
|
4102
|
+
fixes: trace.fixes,
|
|
4103
|
+
nodes: trace.nodes.map((node) => ({
|
|
4104
|
+
id: node.id,
|
|
4105
|
+
role: node.role,
|
|
4106
|
+
model: node.model
|
|
4107
|
+
}))
|
|
4108
|
+
};
|
|
4109
|
+
}
|
|
4110
|
+
function projectShadowSummary(spec, trace) {
|
|
4111
|
+
const implementers = spec.nodes.filter((node) => node.role === "implementer").length;
|
|
4112
|
+
return {
|
|
4113
|
+
runID: spec.runID,
|
|
4114
|
+
status: trace.status,
|
|
4115
|
+
outcome: trace.outcome,
|
|
4116
|
+
fixes: implementers - 1,
|
|
4117
|
+
nodes: spec.nodes.map((node, index) => ({
|
|
4118
|
+
id: node.id,
|
|
4119
|
+
role: node.role,
|
|
4120
|
+
model: trace.nodes[index].model
|
|
4121
|
+
}))
|
|
4122
|
+
};
|
|
4123
|
+
}
|
|
4124
|
+
function mirrorLegacyExecution(trace) {
|
|
4125
|
+
const spec = compileShadowSpec(trace);
|
|
4126
|
+
const journal = mirrorJournal(spec, trace);
|
|
4127
|
+
const state = reduceMirror(spec, journal);
|
|
4128
|
+
const summary = projectShadowSummary(spec, trace);
|
|
4129
|
+
assertReferenceOnly(summary);
|
|
4130
|
+
return {
|
|
4131
|
+
spec,
|
|
4132
|
+
specDigest: graphSpecDigest(spec),
|
|
4133
|
+
journal,
|
|
4134
|
+
state,
|
|
4135
|
+
summary
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
|
|
4139
|
+
// src/orchestrator/graphFacade.ts
|
|
4140
|
+
function mirrorLegacyTrace(trace) {
|
|
4141
|
+
try {
|
|
4142
|
+
return {
|
|
4143
|
+
ok: true,
|
|
4144
|
+
mirror: mirrorLegacyExecution(parseLegacyExecutionTrace(trace))
|
|
4145
|
+
};
|
|
4146
|
+
} catch (error) {
|
|
4147
|
+
if (error instanceof ShadowMirrorError) {
|
|
4148
|
+
return { ok: false, code: error.code, detail: error.detail };
|
|
4149
|
+
}
|
|
4150
|
+
if (error instanceof LegacyTraceError) {
|
|
4151
|
+
return {
|
|
4152
|
+
ok: false,
|
|
4153
|
+
code: "invalid-trace",
|
|
4154
|
+
detail: error.issues.join("; ")
|
|
4155
|
+
};
|
|
4156
|
+
}
|
|
4157
|
+
return {
|
|
4158
|
+
ok: false,
|
|
4159
|
+
code: "mirror-error",
|
|
4160
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
4161
|
+
};
|
|
4162
|
+
}
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
// src/orchestrator/shadowComparator.ts
|
|
4166
|
+
function sequenceEqual(a, b) {
|
|
4167
|
+
if (a.length !== b.length) {
|
|
4168
|
+
return false;
|
|
4169
|
+
}
|
|
4170
|
+
return a.every((value, index) => value === b[index]);
|
|
4171
|
+
}
|
|
4172
|
+
function compareShadowRun(legacy, shadow) {
|
|
4173
|
+
if (legacy === undefined || shadow === undefined) {
|
|
4174
|
+
return { verdict: "inconclusive", divergences: [] };
|
|
4175
|
+
}
|
|
4176
|
+
const divergences = [];
|
|
4177
|
+
if (legacy.status !== shadow.status) {
|
|
4178
|
+
divergences.push("status");
|
|
4179
|
+
}
|
|
4180
|
+
if (!sequenceEqual(legacy.nodes.map((node) => node.id), shadow.nodes.map((node) => node.id))) {
|
|
4181
|
+
divergences.push("order");
|
|
4182
|
+
}
|
|
4183
|
+
if (!sequenceEqual(legacy.nodes.map((node) => node.role), shadow.nodes.map((node) => node.role))) {
|
|
4184
|
+
divergences.push("roles");
|
|
4185
|
+
}
|
|
4186
|
+
if (!sequenceEqual(legacy.nodes.map((node) => node.model), shadow.nodes.map((node) => node.model))) {
|
|
4187
|
+
divergences.push("models");
|
|
4188
|
+
}
|
|
4189
|
+
if (legacy.fixes !== shadow.fixes) {
|
|
4190
|
+
divergences.push("fixes");
|
|
4191
|
+
}
|
|
4192
|
+
if (legacy.outcome !== shadow.outcome) {
|
|
4193
|
+
divergences.push("outcome");
|
|
4194
|
+
}
|
|
4195
|
+
return {
|
|
4196
|
+
verdict: divergences.length === 0 ? "match" : "divergent",
|
|
4197
|
+
divergences
|
|
4198
|
+
};
|
|
4199
|
+
}
|
|
4200
|
+
|
|
4201
|
+
// src/storage/graph/soakRecorder.ts
|
|
4202
|
+
async function ensureRecorderId(ports, basePath) {
|
|
4203
|
+
const idPath = `${basePath}/soak-recorder-id`;
|
|
4204
|
+
if (await ports.fs.exists(idPath)) {
|
|
4205
|
+
const id = (await ports.fs.readFile(idPath)).trim();
|
|
4206
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id)) {
|
|
4207
|
+
return id;
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
const newId = ports.id.randomUUID();
|
|
4211
|
+
await ports.fs.writeFile(idPath, newId);
|
|
4212
|
+
return newId;
|
|
4213
|
+
}
|
|
4214
|
+
async function readAllChains(ports, evidenceDir) {
|
|
4215
|
+
if (!await ports.fs.exists(evidenceDir)) {
|
|
4216
|
+
return [];
|
|
4217
|
+
}
|
|
4218
|
+
const files = await ports.fs.readDir(evidenceDir);
|
|
4219
|
+
const chains = [];
|
|
4220
|
+
for (const file of files) {
|
|
4221
|
+
if (!file.endsWith(".jsonl")) {
|
|
4222
|
+
continue;
|
|
4223
|
+
}
|
|
4224
|
+
const content = await ports.fs.readFile(`${evidenceDir}/${file}`);
|
|
4225
|
+
const lines = content.split(`
|
|
4226
|
+
`).filter((l) => l.trim().length > 0);
|
|
4227
|
+
const observations = [];
|
|
4228
|
+
for (const line of lines) {
|
|
4229
|
+
const parsed = SoakObservationSchema.safeParse(JSON.parse(line));
|
|
4230
|
+
if (parsed.success) {
|
|
4231
|
+
observations.push(parsed.data);
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
if (observations.length > 0) {
|
|
4235
|
+
chains.push(observations);
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
return chains;
|
|
4239
|
+
}
|
|
4240
|
+
function canonicalObservationPreimage2(obs) {
|
|
4241
|
+
return JSON.stringify({
|
|
4242
|
+
version: obs.version,
|
|
4243
|
+
seq: obs.seq,
|
|
4244
|
+
timestamp: obs.timestamp,
|
|
4245
|
+
platform: obs.platform,
|
|
4246
|
+
opencodeVersion: obs.opencodeVersion,
|
|
4247
|
+
provenance: obs.provenance,
|
|
4248
|
+
traceDigest: obs.traceDigest,
|
|
4249
|
+
criticalDivergences: obs.criticalDivergences,
|
|
4250
|
+
privacy: obs.privacy,
|
|
4251
|
+
duplicateEffects: obs.duplicateEffects,
|
|
4252
|
+
modelVerification: obs.modelVerification
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4255
|
+
async function appendObservation(ports, evidenceDir, recorderId, input) {
|
|
4256
|
+
await ports.fs.mkdir(evidenceDir);
|
|
4257
|
+
const chainPath = `${evidenceDir}/${recorderId}.jsonl`;
|
|
4258
|
+
let seq = 0;
|
|
4259
|
+
let prevChainDigest = "0".repeat(64);
|
|
4260
|
+
if (await ports.fs.exists(chainPath)) {
|
|
4261
|
+
const content = await ports.fs.readFile(chainPath);
|
|
4262
|
+
const lines = content.split(`
|
|
4263
|
+
`).filter((l) => l.trim().length > 0);
|
|
4264
|
+
if (lines.length > 0) {
|
|
4265
|
+
const lastLine = lines[lines.length - 1];
|
|
4266
|
+
let rawParsed;
|
|
4267
|
+
try {
|
|
4268
|
+
rawParsed = JSON.parse(lastLine);
|
|
4269
|
+
} catch {
|
|
4270
|
+
throw new Error("corrupt chain tail: invalid JSON");
|
|
4271
|
+
}
|
|
4272
|
+
const parsed = SoakObservationSchema.safeParse(rawParsed);
|
|
4273
|
+
if (!parsed.success) {
|
|
4274
|
+
const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
4275
|
+
throw new Error(`corrupt chain tail: ${detail}`);
|
|
4276
|
+
}
|
|
4277
|
+
seq = parsed.data.seq + 1;
|
|
4278
|
+
prevChainDigest = parsed.data.chainDigest;
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
const timestamp = ports.clock.now();
|
|
4282
|
+
const base = {
|
|
4283
|
+
version: 1,
|
|
4284
|
+
seq,
|
|
4285
|
+
timestamp,
|
|
4286
|
+
platform: input.platform,
|
|
4287
|
+
opencodeVersion: input.opencodeVersion,
|
|
4288
|
+
provenance: input.provenance,
|
|
4289
|
+
traceDigest: input.traceDigest,
|
|
4290
|
+
criticalDivergences: input.criticalDivergences,
|
|
4291
|
+
privacy: input.privacy,
|
|
4292
|
+
duplicateEffects: input.duplicateEffects,
|
|
4293
|
+
modelVerification: input.modelVerification
|
|
4294
|
+
};
|
|
4295
|
+
const preimage = canonicalObservationPreimage2(base);
|
|
4296
|
+
const chainDigest = sha256Hex(prevChainDigest + preimage);
|
|
4297
|
+
const observation = { ...base, chainDigest };
|
|
4298
|
+
assertReferenceOnly(observation);
|
|
4299
|
+
const line = JSON.stringify(observation);
|
|
4300
|
+
if (await ports.fs.exists(chainPath)) {
|
|
4301
|
+
const existing = await ports.fs.readFile(chainPath);
|
|
4302
|
+
await ports.fs.writeFile(chainPath, `${existing}
|
|
4303
|
+
${line}`);
|
|
4304
|
+
} else {
|
|
4305
|
+
await ports.fs.writeFile(chainPath, line);
|
|
4306
|
+
}
|
|
4307
|
+
return observation;
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
// src/plugin/soakObserver.ts
|
|
4311
|
+
function resolvePlatform(platform) {
|
|
4312
|
+
if (platform === "win32")
|
|
4313
|
+
return "win32";
|
|
4314
|
+
if (platform === "darwin")
|
|
4315
|
+
return "darwin";
|
|
4316
|
+
if (platform === "linux")
|
|
4317
|
+
return "linux";
|
|
4318
|
+
return null;
|
|
4319
|
+
}
|
|
4320
|
+
function countCriticalDivergences(parity, criticalCodes) {
|
|
4321
|
+
if (parity === undefined)
|
|
4322
|
+
return null;
|
|
4323
|
+
if (criticalCodes.length === 0)
|
|
4324
|
+
return null;
|
|
4325
|
+
const criticalSet = new Set(criticalCodes);
|
|
4326
|
+
return parity.divergences.filter((code) => criticalSet.has(code)).length;
|
|
4327
|
+
}
|
|
4328
|
+
function scanTracePrivacy(trace) {
|
|
4329
|
+
const result = scanReferenceOnly([trace]);
|
|
4330
|
+
return {
|
|
4331
|
+
surfacesScanned: result.surfacesScanned,
|
|
4332
|
+
rawFindings: result.rawFindings
|
|
4333
|
+
};
|
|
4334
|
+
}
|
|
4335
|
+
function detectDuplicateEffects(trace) {
|
|
4336
|
+
const nodes = trace.nodes;
|
|
4337
|
+
if (nodes.length === 0)
|
|
4338
|
+
return null;
|
|
4339
|
+
const seen = new Set;
|
|
4340
|
+
let duplicates = 0;
|
|
4341
|
+
for (const node of nodes) {
|
|
4342
|
+
if (seen.has(node.id)) {
|
|
4343
|
+
duplicates++;
|
|
4344
|
+
} else {
|
|
4345
|
+
seen.add(node.id);
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
return { effectsExamined: nodes.length, duplicatesFound: duplicates };
|
|
4349
|
+
}
|
|
4350
|
+
function traceToObservationInput(trace, opencodeVersion, parity, criticalCodes, modelVerification, platformOverride) {
|
|
4351
|
+
const platform = resolvePlatform(platformOverride ?? process.platform);
|
|
4352
|
+
if (platform === null)
|
|
4353
|
+
return null;
|
|
4354
|
+
const traceDigest = sha256Hex(JSON.stringify(trace));
|
|
4355
|
+
return {
|
|
4356
|
+
platform,
|
|
4357
|
+
opencodeVersion,
|
|
4358
|
+
provenance: "genuine-usage",
|
|
4359
|
+
traceDigest,
|
|
4360
|
+
criticalDivergences: countCriticalDivergences(parity, criticalCodes),
|
|
4361
|
+
privacy: scanTracePrivacy(trace),
|
|
4362
|
+
duplicateEffects: detectDuplicateEffects(trace),
|
|
4363
|
+
modelVerification
|
|
4364
|
+
};
|
|
4365
|
+
}
|
|
4366
|
+
async function recordSoakObservation(ports, evidenceDir, recorderId, trace, opencodeVersion, parity, criticalCodes, modelVerification, platformOverride) {
|
|
4367
|
+
const input = traceToObservationInput(trace, opencodeVersion, parity, criticalCodes, modelVerification, platformOverride);
|
|
4368
|
+
if (input === null)
|
|
4369
|
+
return false;
|
|
4370
|
+
await appendObservation(ports, evidenceDir, recorderId, input);
|
|
4371
|
+
return true;
|
|
4372
|
+
}
|
|
4373
|
+
|
|
4374
|
+
// src/plugin/shadowPipeline.ts
|
|
4375
|
+
function createShadowPipeline(deps) {
|
|
4376
|
+
return async (observation, modelVerification) => {
|
|
4377
|
+
try {
|
|
4378
|
+
const ingested = ingestLegacyExecution(observation, deps.gate);
|
|
4379
|
+
if (!ingested.eligible) {
|
|
4380
|
+
return { kind: "ineligible", reason: ingested.reason };
|
|
4381
|
+
}
|
|
4382
|
+
const mirror = mirrorLegacyTrace(ingested.trace);
|
|
4383
|
+
let parity;
|
|
4384
|
+
if (mirror.ok) {
|
|
4385
|
+
const legacySummary = projectLegacySummary(ingested.trace);
|
|
4386
|
+
const report = compareShadowRun(legacySummary, mirror.mirror.summary);
|
|
4387
|
+
parity = { divergences: [...report.divergences] };
|
|
4388
|
+
}
|
|
4389
|
+
const recorded = await recordSoakObservation(deps.ports, deps.evidenceDir, deps.recorderId, ingested.trace, deps.opencodeVersion, parity, deps.criticalCodes, modelVerification ?? null, deps.platformOverride);
|
|
4390
|
+
if (!recorded) {
|
|
4391
|
+
return {
|
|
4392
|
+
kind: "observation-rejected",
|
|
4393
|
+
reason: "unrecognized-platform"
|
|
4394
|
+
};
|
|
4395
|
+
}
|
|
4396
|
+
return { kind: "observed" };
|
|
4397
|
+
} catch (error) {
|
|
4398
|
+
return {
|
|
4399
|
+
kind: "contained-failure",
|
|
4400
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
4401
|
+
};
|
|
4402
|
+
}
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4405
|
+
|
|
4406
|
+
// src/plugin/graphTool.ts
|
|
4407
|
+
function describeDecision(decision2) {
|
|
4408
|
+
if (decision2.route === "graph") {
|
|
4409
|
+
return `graph: la tarea ${decision2.taskID} se enrutaría al runtime activo.`;
|
|
4410
|
+
}
|
|
4411
|
+
switch (decision2.reason) {
|
|
4412
|
+
case "gate-closed":
|
|
4413
|
+
return "legacy: gate-closed — el ingress activo no es alcanzable por configuración en esta versión.";
|
|
4414
|
+
case "kill-switch":
|
|
4415
|
+
return "legacy: kill-switch — apagado de emergencia activo.";
|
|
4416
|
+
case "graph-disabled":
|
|
4417
|
+
return "legacy: graph-disabled — graph.mode no es 'active'.";
|
|
4418
|
+
case "not-allow-listed":
|
|
4419
|
+
return "legacy: not-allow-listed — la tarea no está en la allow-list del ingress.";
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
function describeOutcome(outcome) {
|
|
4423
|
+
switch (outcome.kind) {
|
|
4424
|
+
case "legacy":
|
|
4425
|
+
return describeDecision({ route: "legacy", reason: outcome.reason });
|
|
4426
|
+
case "accepted":
|
|
4427
|
+
return `accepted: run ${outcome.runID}, paso ${outcome.step.kind}.`;
|
|
4428
|
+
case "revision":
|
|
4429
|
+
return `revision: run ${outcome.runID}, plan ${outcome.plan.kind}.`;
|
|
4430
|
+
case "reassigned":
|
|
4431
|
+
return `reassigned: run ${outcome.runID}, reviewer ${outcome.reviewerID}.`;
|
|
4432
|
+
case "cancelled":
|
|
4433
|
+
return `cancelled: run ${outcome.runID}, ${outcome.cancelledNodeIDs.length} nodo(s) cancelado(s).`;
|
|
4434
|
+
case "error":
|
|
4435
|
+
return `error: ${outcome.code}.`;
|
|
4436
|
+
}
|
|
4437
|
+
}
|
|
4438
|
+
function unavailableReason(deps, taskID) {
|
|
4439
|
+
const decision2 = decideIngress(deps.policy, taskID);
|
|
4440
|
+
return decision2.route === "legacy" ? describeDecision(decision2) : "unavailable: el ejecutor de efectos del runtime activo no está enchufado.";
|
|
4441
|
+
}
|
|
4442
|
+
function createGraphTool(deps) {
|
|
4443
|
+
const observe = deps.shadow !== undefined ? createShadowPipeline(deps.shadow) : async () => ({
|
|
4444
|
+
kind: "ineligible",
|
|
4445
|
+
reason: "shadow-not-configured"
|
|
4446
|
+
});
|
|
4447
|
+
const definition = tool2({
|
|
4448
|
+
description: "Ingress activo de SDD de openteam (GE-050). En esta versión la puerta interna está cerrada: informa del estado de enrutado y no ejecuta runs. La acción report-run registra una observación shadow de la ejecución legacy.",
|
|
4449
|
+
args: {
|
|
4450
|
+
action: tool2.schema.enum(["status", "start", "cancel", "report-run"]).describe("Acción a ejecutar"),
|
|
4451
|
+
taskID: tool2.schema.string().optional().describe("Identificador de la tarea SDD"),
|
|
4452
|
+
runID: tool2.schema.string().optional().describe("Identificador del run (para action=cancel o report-run)"),
|
|
4453
|
+
status: tool2.schema.enum(["completed", "failed", "cancelled"]).optional().describe("Estado terminal del run (solo action=report-run)"),
|
|
4454
|
+
fixes: tool2.schema.number().optional().describe("Número de iteraciones de fix (solo action=report-run)"),
|
|
4455
|
+
nodes: tool2.schema.array(tool2.schema.object({
|
|
4456
|
+
id: tool2.schema.string().describe("ID del nodo"),
|
|
4457
|
+
role: tool2.schema.enum(["implementer", "reviewer"]).describe("Rol del nodo"),
|
|
4458
|
+
model: tool2.schema.string().describe("Modelo utilizado por el nodo"),
|
|
4459
|
+
ok: tool2.schema.boolean().describe("Éxito terminal del nodo"),
|
|
4460
|
+
sessionRef: tool2.schema.string().optional().describe("ID de sesión de opencode del nodo"),
|
|
4461
|
+
errorClass: tool2.schema.enum(["transient", "permanent", "exhausted"]).optional().describe("Clase de error si el nodo falló")
|
|
4462
|
+
})).optional().describe("Nodos de la ejecución (solo action=report-run)"),
|
|
4463
|
+
parentSessionRef: tool2.schema.string().optional().describe("Sesión padre del run (solo action=report-run)")
|
|
4464
|
+
},
|
|
4465
|
+
async execute(args) {
|
|
4466
|
+
if (args.action === "report-run") {
|
|
4467
|
+
return handleReportRun(args, observe, deps.tracker);
|
|
4468
|
+
}
|
|
4469
|
+
const taskID = args.taskID ?? "";
|
|
4470
|
+
if (args.action === "status") {
|
|
4471
|
+
return describeDecision(decideIngress(deps.policy, taskID));
|
|
4472
|
+
}
|
|
4473
|
+
const ingress = deps.ingress;
|
|
4474
|
+
if (ingress === undefined) {
|
|
4475
|
+
return unavailableReason(deps, taskID);
|
|
4476
|
+
}
|
|
4477
|
+
if (args.action === "cancel") {
|
|
4478
|
+
return describeOutcome(await ingress.cancel(args.runID ?? ""));
|
|
4479
|
+
}
|
|
4480
|
+
return describeOutcome(await ingress.start({
|
|
4481
|
+
id: taskID,
|
|
4482
|
+
implementerRoleID: "linus",
|
|
4483
|
+
title: taskID,
|
|
4484
|
+
brief: taskID,
|
|
4485
|
+
task: {
|
|
4486
|
+
promptChars: 0,
|
|
4487
|
+
hasCode: false,
|
|
4488
|
+
estimatedInputTokens: 0,
|
|
4489
|
+
estimatedOutputTokens: 0
|
|
4490
|
+
}
|
|
4491
|
+
}));
|
|
4492
|
+
}
|
|
4493
|
+
});
|
|
4494
|
+
return { definition, observeLegacyExecution: observe };
|
|
4495
|
+
}
|
|
4496
|
+
async function handleReportRun(args, observe, tracker) {
|
|
4497
|
+
const parsed = ReportRunPayloadSchema.safeParse({
|
|
4498
|
+
runID: args.runID,
|
|
4499
|
+
status: args.status,
|
|
4500
|
+
fixes: args.fixes,
|
|
4501
|
+
nodes: args.nodes,
|
|
4502
|
+
...args.parentSessionRef !== undefined ? { parentSessionRef: args.parentSessionRef } : {}
|
|
4503
|
+
});
|
|
4504
|
+
if (!parsed.success) {
|
|
4505
|
+
const issues = parsed.error.issues.map((issue) => issue.message).join("; ");
|
|
4506
|
+
return formatResponse({
|
|
4507
|
+
result: "ineligible",
|
|
4508
|
+
detail: `validation: ${issues}`
|
|
4509
|
+
});
|
|
4510
|
+
}
|
|
4511
|
+
const verification = crossVerify(parsed.data, tracker);
|
|
4512
|
+
const warnings = formatWarnings(verification);
|
|
4513
|
+
const verificationPair = {
|
|
4514
|
+
nodesChecked: parsed.data.nodes.length,
|
|
4515
|
+
unverified: verification.unverifiedModels.length,
|
|
4516
|
+
mismatched: verification.modelMismatches.length
|
|
4517
|
+
};
|
|
4518
|
+
const run = buildObservedRun(parsed.data);
|
|
4519
|
+
try {
|
|
4520
|
+
const result = await observe(run, verificationPair);
|
|
4521
|
+
const base = { result: result.kind };
|
|
4522
|
+
const detail = result.kind === "ineligible" ? result.reason : result.kind === "observation-rejected" ? result.reason : result.kind === "contained-failure" ? result.detail : null;
|
|
4523
|
+
return formatResponse({
|
|
4524
|
+
...base,
|
|
4525
|
+
...detail !== null ? { detail } : {},
|
|
4526
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
4527
|
+
});
|
|
4528
|
+
} catch (error) {
|
|
4529
|
+
return formatResponse({
|
|
4530
|
+
result: "contained-failure",
|
|
4531
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
4532
|
+
});
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
|
|
2531
4536
|
// src/plugin/hooks.ts
|
|
2532
4537
|
var codeSignals = [
|
|
2533
4538
|
"```",
|
|
@@ -2615,6 +4620,12 @@ function createChatMessageHook(config, getAvailable, options = {}) {
|
|
|
2615
4620
|
telemetryContext.tokensOut = signals.estimatedOutputTokens;
|
|
2616
4621
|
}
|
|
2617
4622
|
emitTelemetry(sink, costRecordToRouteEvent(toCostRecord(decision2, telemetryContext)));
|
|
4623
|
+
if (options.onRoute !== undefined) {
|
|
4624
|
+
const routedSessionID = input.sessionID ?? output.message.sessionID;
|
|
4625
|
+
if (typeof routedSessionID === "string" && routedSessionID.length > 0) {
|
|
4626
|
+
options.onRoute(routedSessionID, `${decision2.selected.providerID}/${decision2.selected.modelID}`);
|
|
4627
|
+
}
|
|
4628
|
+
}
|
|
2618
4629
|
if (options.injectMemory !== undefined) {
|
|
2619
4630
|
try {
|
|
2620
4631
|
const block = await options.injectMemory(prompt);
|
|
@@ -2676,54 +4687,54 @@ ${lines.join(`
|
|
|
2676
4687
|
}
|
|
2677
4688
|
|
|
2678
4689
|
// src/memory/types.ts
|
|
2679
|
-
import { z as
|
|
4690
|
+
import { z as z11 } from "zod";
|
|
2680
4691
|
var SHARED_OWNER_KEY = "*";
|
|
2681
|
-
var OwnerKeySchema =
|
|
2682
|
-
var MemoryKindSchema =
|
|
2683
|
-
var MemoryBaseSchema =
|
|
2684
|
-
id:
|
|
4692
|
+
var OwnerKeySchema = z11.string().min(1);
|
|
4693
|
+
var MemoryKindSchema = z11.enum(["fact", "preference", "entity"]);
|
|
4694
|
+
var MemoryBaseSchema = z11.object({
|
|
4695
|
+
id: z11.string().min(1),
|
|
2685
4696
|
ownerKey: OwnerKeySchema,
|
|
2686
|
-
confidence:
|
|
2687
|
-
validFrom:
|
|
2688
|
-
validUntil:
|
|
2689
|
-
createdAt:
|
|
2690
|
-
invalidatedAt:
|
|
2691
|
-
sourceHash:
|
|
2692
|
-
supersededBy:
|
|
4697
|
+
confidence: z11.number().min(0).max(1),
|
|
4698
|
+
validFrom: z11.number().finite(),
|
|
4699
|
+
validUntil: z11.number().finite().nullable().default(null),
|
|
4700
|
+
createdAt: z11.number().finite(),
|
|
4701
|
+
invalidatedAt: z11.number().finite().nullable().default(null),
|
|
4702
|
+
sourceHash: z11.string().min(1),
|
|
4703
|
+
supersededBy: z11.string().min(1).nullable().default(null)
|
|
2693
4704
|
});
|
|
2694
4705
|
var FactSchema = MemoryBaseSchema.extend({
|
|
2695
|
-
kind:
|
|
2696
|
-
subject:
|
|
2697
|
-
predicate:
|
|
2698
|
-
object:
|
|
2699
|
-
category:
|
|
4706
|
+
kind: z11.literal("fact"),
|
|
4707
|
+
subject: z11.string().min(1),
|
|
4708
|
+
predicate: z11.string().min(1),
|
|
4709
|
+
object: z11.string().min(1),
|
|
4710
|
+
category: z11.string().min(1).nullable().default(null)
|
|
2700
4711
|
});
|
|
2701
4712
|
var PreferenceSchema = MemoryBaseSchema.extend({
|
|
2702
|
-
kind:
|
|
2703
|
-
category:
|
|
2704
|
-
preference:
|
|
2705
|
-
context:
|
|
2706
|
-
lastAccessedAt:
|
|
2707
|
-
accessCount:
|
|
4713
|
+
kind: z11.literal("preference"),
|
|
4714
|
+
category: z11.string().min(1),
|
|
4715
|
+
preference: z11.string().min(1),
|
|
4716
|
+
context: z11.string().min(1).nullable().default(null),
|
|
4717
|
+
lastAccessedAt: z11.number().finite().nullable().default(null),
|
|
4718
|
+
accessCount: z11.number().int().min(0).default(0)
|
|
2708
4719
|
});
|
|
2709
4720
|
var EntitySchema = MemoryBaseSchema.extend({
|
|
2710
|
-
kind:
|
|
2711
|
-
canonicalName:
|
|
2712
|
-
type:
|
|
2713
|
-
aliases:
|
|
4721
|
+
kind: z11.literal("entity"),
|
|
4722
|
+
canonicalName: z11.string().min(1),
|
|
4723
|
+
type: z11.string().min(1),
|
|
4724
|
+
aliases: z11.array(z11.string().min(1)).default([])
|
|
2714
4725
|
});
|
|
2715
|
-
var MemoryRecordSchema =
|
|
4726
|
+
var MemoryRecordSchema = z11.discriminatedUnion("kind", [
|
|
2716
4727
|
FactSchema,
|
|
2717
4728
|
PreferenceSchema,
|
|
2718
4729
|
EntitySchema
|
|
2719
4730
|
]);
|
|
2720
|
-
var RecallQuerySchema =
|
|
4731
|
+
var RecallQuerySchema = z11.object({
|
|
2721
4732
|
ownerKey: OwnerKeySchema,
|
|
2722
|
-
kinds:
|
|
2723
|
-
asOf:
|
|
2724
|
-
limit:
|
|
2725
|
-
minSimilarity:
|
|
2726
|
-
includeShared:
|
|
4733
|
+
kinds: z11.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
|
|
4734
|
+
asOf: z11.number().finite().nullable().default(null),
|
|
4735
|
+
limit: z11.number().int().positive().default(8),
|
|
4736
|
+
minSimilarity: z11.number().min(0).max(1).default(0.2),
|
|
4737
|
+
includeShared: z11.boolean().default(true)
|
|
2727
4738
|
});
|
|
2728
4739
|
|
|
2729
4740
|
// src/memory/rank.ts
|
|
@@ -2872,6 +4883,33 @@ function buildMemoryInjector(deps, policy) {
|
|
|
2872
4883
|
};
|
|
2873
4884
|
}
|
|
2874
4885
|
|
|
4886
|
+
// src/plugin/sessionTracker.ts
|
|
4887
|
+
function createSessionTracker() {
|
|
4888
|
+
const knownSessions = new Set;
|
|
4889
|
+
const routedModels = new Map;
|
|
4890
|
+
return {
|
|
4891
|
+
trackSession(sessionID) {
|
|
4892
|
+
knownSessions.add(sessionID);
|
|
4893
|
+
},
|
|
4894
|
+
trackRoutedModel(sessionID, model) {
|
|
4895
|
+
routedModels.set(sessionID, model);
|
|
4896
|
+
},
|
|
4897
|
+
verifyModel(sessionRef, reportedModel) {
|
|
4898
|
+
const routedModel = routedModels.get(sessionRef);
|
|
4899
|
+
if (routedModel === undefined) {
|
|
4900
|
+
return { verified: false };
|
|
4901
|
+
}
|
|
4902
|
+
return { verified: true, match: routedModel === reportedModel };
|
|
4903
|
+
},
|
|
4904
|
+
verifySessionRef(sessionRef) {
|
|
4905
|
+
if (sessionRef === undefined) {
|
|
4906
|
+
return { known: null };
|
|
4907
|
+
}
|
|
4908
|
+
return { known: knownSessions.has(sessionRef) };
|
|
4909
|
+
}
|
|
4910
|
+
};
|
|
4911
|
+
}
|
|
4912
|
+
|
|
2875
4913
|
// src/plugin/toolcalls.ts
|
|
2876
4914
|
function key(sessionID, callID) {
|
|
2877
4915
|
return `${sessionID}\x00${callID}`;
|
|
@@ -3331,6 +5369,82 @@ function logTelemetryError(error) {
|
|
|
3331
5369
|
const message = error instanceof Error ? error.message : String(error);
|
|
3332
5370
|
console.warn(`[openteam] telemetry write failed: ${message}`);
|
|
3333
5371
|
}
|
|
5372
|
+
var SHADOW_CERT_PATH = "artifacts/graph-shadow-certificate.json";
|
|
5373
|
+
var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
|
|
5374
|
+
var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
|
|
5375
|
+
var SOAK_EVIDENCE_DIR = ".opencode/soak";
|
|
5376
|
+
async function readAndParseCertificate(storage, path, parse, digestExtractor) {
|
|
5377
|
+
try {
|
|
5378
|
+
const content = await storage.read(path);
|
|
5379
|
+
if (content === undefined) {
|
|
5380
|
+
return { status: "absent" };
|
|
5381
|
+
}
|
|
5382
|
+
const raw = JSON.parse(content);
|
|
5383
|
+
const cert = parse(raw);
|
|
5384
|
+
return { status: "valid", digest: digestExtractor(cert) };
|
|
5385
|
+
} catch (error) {
|
|
5386
|
+
if (isMissingFile(error)) {
|
|
5387
|
+
return { status: "absent" };
|
|
5388
|
+
}
|
|
5389
|
+
if (error instanceof ShadowCertificateError || error instanceof ReleaseCertificateError || error instanceof SoakCertificateError) {
|
|
5390
|
+
return { status: "invalid", code: error.code, detail: error.detail };
|
|
5391
|
+
}
|
|
5392
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
5393
|
+
return { status: "invalid", code: "read-error", detail: msg };
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
async function readOpencodeVersion(serverUrl) {
|
|
5397
|
+
try {
|
|
5398
|
+
const url = new URL("/global/health", serverUrl);
|
|
5399
|
+
const res = await fetch(url);
|
|
5400
|
+
if (!res.ok) {
|
|
5401
|
+
return "unknown";
|
|
5402
|
+
}
|
|
5403
|
+
const body = await res.json();
|
|
5404
|
+
if (typeof body === "object" && body !== null && "version" in body && typeof body.version === "string" && body.version.length > 0) {
|
|
5405
|
+
return body.version;
|
|
5406
|
+
}
|
|
5407
|
+
return "unknown";
|
|
5408
|
+
} catch {
|
|
5409
|
+
return "unknown";
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
async function buildGateInput(config, storage, opencodeVersion) {
|
|
5413
|
+
const shadow = await readAndParseCertificate(storage, SHADOW_CERT_PATH, (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => cert.digest);
|
|
5414
|
+
const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
|
|
5415
|
+
const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest);
|
|
5416
|
+
let soakLedgerDigest = "0".repeat(64);
|
|
5417
|
+
try {
|
|
5418
|
+
const recorderPorts = recorderPortsFromStorage(storage);
|
|
5419
|
+
const chains = await readAllChains(recorderPorts, SOAK_EVIDENCE_DIR);
|
|
5420
|
+
if (chains.length > 0) {
|
|
5421
|
+
soakLedgerDigest = canonicalLedger(chains);
|
|
5422
|
+
}
|
|
5423
|
+
} catch {}
|
|
5424
|
+
const soak = await readAndParseCertificate(storage, SOAK_CERT_PATH, (raw) => parseSoakCertificate(raw, DEFAULT_SOAK_POLICY, soakLedgerDigest), (cert) => cert.digest);
|
|
5425
|
+
const migration = "complete";
|
|
5426
|
+
const operatorApproval = config.graph?.operatorApproval ?? false;
|
|
5427
|
+
return { shadow, release, soak, migration, operatorApproval };
|
|
5428
|
+
}
|
|
5429
|
+
function recorderPortsFromStorage(storage) {
|
|
5430
|
+
return {
|
|
5431
|
+
fs: {
|
|
5432
|
+
readFile: async (path) => {
|
|
5433
|
+
const content = await storage.read(path);
|
|
5434
|
+
if (content === undefined) {
|
|
5435
|
+
throw Object.assign(new Error(`ENOENT: ${path}`), { code: "ENOENT" });
|
|
5436
|
+
}
|
|
5437
|
+
return content;
|
|
5438
|
+
},
|
|
5439
|
+
writeFile: (path, content) => storage.write(path, content),
|
|
5440
|
+
exists: (path) => storage.exists(path),
|
|
5441
|
+
readDir: (path) => storage.list(path),
|
|
5442
|
+
mkdir: (path) => storage.mkdir(path)
|
|
5443
|
+
},
|
|
5444
|
+
clock: { now: () => new Date().toISOString() },
|
|
5445
|
+
id: { randomUUID: () => crypto.randomUUID() }
|
|
5446
|
+
};
|
|
5447
|
+
}
|
|
3334
5448
|
async function buildMemoryInjectorFromConfig(config, storage) {
|
|
3335
5449
|
const runtime = await buildMemoryRuntimeFromConfig(config, { storage }, { requireInjectionEnabled: true });
|
|
3336
5450
|
if (runtime === undefined) {
|
|
@@ -3430,7 +5544,15 @@ var server = async (ctx, rawOptions) => {
|
|
|
3430
5544
|
const storage = createFsStorageProvider(process.cwd());
|
|
3431
5545
|
const sink = createEventSink(rawOptions, storage);
|
|
3432
5546
|
const injectMemory = await buildMemoryInjectorFromConfig(config, storage);
|
|
3433
|
-
const
|
|
5547
|
+
const sessionTracker = createSessionTracker();
|
|
5548
|
+
const hooks = createHooks(config, cache.get, injectMemory === undefined ? {
|
|
5549
|
+
sink,
|
|
5550
|
+
onRoute: (sid, model) => sessionTracker.trackRoutedModel(sid, model)
|
|
5551
|
+
} : {
|
|
5552
|
+
sink,
|
|
5553
|
+
injectMemory,
|
|
5554
|
+
onRoute: (sid, model) => sessionTracker.trackRoutedModel(sid, model)
|
|
5555
|
+
});
|
|
3434
5556
|
const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage);
|
|
3435
5557
|
const toolcalls = createToolcallTracker({ sink });
|
|
3436
5558
|
const now = () => Date.now();
|
|
@@ -3448,10 +5570,54 @@ var server = async (ctx, rawOptions) => {
|
|
|
3448
5570
|
sink.emit(endpoint);
|
|
3449
5571
|
}
|
|
3450
5572
|
};
|
|
5573
|
+
let graphConfig = resolveGraphConfig(config, "file");
|
|
5574
|
+
const opencodeVersion = graphConfig.effectiveMode !== "off" ? await readOpencodeVersion(ctx.serverUrl) : undefined;
|
|
5575
|
+
if (isGateApplicable(graphConfig.effectiveMode)) {
|
|
5576
|
+
if (opencodeVersion === undefined) {
|
|
5577
|
+
throw new Error("[openteam] BUG: gate applicable but opencodeVersion was not read");
|
|
5578
|
+
}
|
|
5579
|
+
const gateInput = await buildGateInput(config, storage, opencodeVersion);
|
|
5580
|
+
const gateResult = evaluateGraphGate(gateInput);
|
|
5581
|
+
graphConfig = applyGraphGate(graphConfig, gateResult);
|
|
5582
|
+
if (graphConfig.gateDenied === true && graphConfig.gateViolations !== undefined) {
|
|
5583
|
+
console.warn("[openteam] ⛔ cutover gate denied active mode — degrading to off");
|
|
5584
|
+
for (const v of graphConfig.gateViolations) {
|
|
5585
|
+
console.warn(`[openteam] • ${v.code}: ${v.detail}`);
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
}
|
|
5589
|
+
let shadowDeps;
|
|
5590
|
+
if (graphConfig.effectiveMode !== "off") {
|
|
5591
|
+
if (opencodeVersion === undefined) {
|
|
5592
|
+
throw new Error("[openteam] BUG: shadow pipeline active but opencodeVersion was not read");
|
|
5593
|
+
}
|
|
5594
|
+
const recorderPorts = recorderPortsFromStorage(storage);
|
|
5595
|
+
const recorderId = await ensureRecorderId(recorderPorts, ".opencode");
|
|
5596
|
+
shadowDeps = {
|
|
5597
|
+
gate: {
|
|
5598
|
+
mode: graphConfig.configuredMode,
|
|
5599
|
+
killSwitch: graphConfig.killSwitch
|
|
5600
|
+
},
|
|
5601
|
+
ports: recorderPorts,
|
|
5602
|
+
evidenceDir: ".opencode/soak",
|
|
5603
|
+
recorderId,
|
|
5604
|
+
opencodeVersion,
|
|
5605
|
+
criticalCodes: DEFAULT_SOAK_POLICY.criticalDivergenceCodes
|
|
5606
|
+
};
|
|
5607
|
+
}
|
|
5608
|
+
const graphSurface = createGraphTool({
|
|
5609
|
+
policy: {
|
|
5610
|
+
graph: graphConfig,
|
|
5611
|
+
allowList: []
|
|
5612
|
+
},
|
|
5613
|
+
shadow: shadowDeps,
|
|
5614
|
+
tracker: sessionTracker
|
|
5615
|
+
});
|
|
3451
5616
|
return {
|
|
3452
5617
|
...hooks,
|
|
3453
5618
|
tool: {
|
|
3454
|
-
openteam: createCommandTool(cliDeps)
|
|
5619
|
+
openteam: createCommandTool(cliDeps),
|
|
5620
|
+
"openteam-graph": graphSurface.definition
|
|
3455
5621
|
},
|
|
3456
5622
|
"tool.execute.before": async (input) => {
|
|
3457
5623
|
toolcalls.before(input);
|
|
@@ -3466,6 +5632,7 @@ var server = async (ctx, rawOptions) => {
|
|
|
3466
5632
|
const activity = activityEventFrom(event, now);
|
|
3467
5633
|
if (activity !== undefined) {
|
|
3468
5634
|
announceEndpoint(activity.sessionID);
|
|
5635
|
+
sessionTracker.trackSession(activity.sessionID);
|
|
3469
5636
|
sink.emit(activity);
|
|
3470
5637
|
}
|
|
3471
5638
|
if (event.type === "message.updated") {
|
|
@@ -3473,6 +5640,7 @@ var server = async (ctx, rawOptions) => {
|
|
|
3473
5640
|
const message = info === undefined ? undefined : messageEventFrom(info, now);
|
|
3474
5641
|
if (message !== undefined) {
|
|
3475
5642
|
announceEndpoint(message.sessionID);
|
|
5643
|
+
sessionTracker.trackSession(message.sessionID);
|
|
3476
5644
|
sink.emit(message);
|
|
3477
5645
|
}
|
|
3478
5646
|
}
|
|
@@ -3486,10 +5654,13 @@ var plugin = {
|
|
|
3486
5654
|
var src_default = plugin;
|
|
3487
5655
|
export {
|
|
3488
5656
|
server,
|
|
5657
|
+
recorderPortsFromStorage,
|
|
5658
|
+
readOpencodeVersion,
|
|
3489
5659
|
logTelemetryError,
|
|
3490
5660
|
logAvailabilityRefreshError,
|
|
3491
5661
|
isMissingFile,
|
|
3492
5662
|
src_default as default,
|
|
3493
5663
|
createEventSink,
|
|
3494
|
-
createCliDeps
|
|
5664
|
+
createCliDeps,
|
|
5665
|
+
buildGateInput
|
|
3495
5666
|
};
|