@tea-agent/loop-agent 0.42.0-next.1 → 0.42.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +260 -1
- package/dist/application/dag/generate-task-dag.js +75 -19
- package/dist/application/task-lifecycle/advance.js +24 -5
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +7 -0
- package/dist/cli/program.js +5 -0
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/init.js +27 -1
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +3784 -231
- package/dist/executors/pi-executor.js +15 -4
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/shell-executor.js +942 -187
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/infrastructure/console/operation-store.js +226 -6
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +7 -0
- package/dist/task/config-types.js +105 -5
- package/dist/task/contract/adopt.js +4 -0
- package/dist/task/contract/import-revision.js +4 -0
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +203 -20
- package/dist/task/runtime.js +5 -2
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +15 -9
- package/dist/task/source-prepare/prepare.js +89 -1
- package/dist/task/source-prepare/semantic-intake.js +6 -2
- package/dist/task/source-references.js +22 -1
- package/dist/worker/console/chat/chat-ui-policy.js +4 -2
- package/dist/worker/console/chat/pi-runtime.js +173 -7
- package/dist/worker/console/chat/repo-browser.js +10 -3
- package/dist/worker/console/chat/routes.js +30 -19
- package/dist/worker/console/chat/session-store.js +117 -70
- package/dist/worker/console/chat/shortcuts.js +1 -1
- package/dist/worker/console/chat/turn-process.js +111 -56
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/operator-actions.js +169 -7
- package/dist/worker/console/server.js +3 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DZZ8m3PO.js → abnfDiagram-N423BO3Z-CeznJ4Iw.js} +1 -1
- package/dist/worker/console/static/assets/{arc-D6PvaVd-.js → arc-B4u9u7SX.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-B_OTOiI8.js → architectureDiagram-T3A2C74G-oghpT81p.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-Bv6rqHBg.js → blockDiagram-VBNYF7ZC-BPJ5Kvcj.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B8eHr0oz.js → c4Diagram-5PPSVZJV-CoIu69pd.js} +1 -1
- package/dist/worker/console/static/assets/channel-DmIXqJ6B.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DYwR0im2.js → chunk-2GRJ4B5K-DtgiAsSc.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-D2WPGqXt.js → chunk-2Q5K7J3B-YSu_EVSD.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CQISJ_I7.js → chunk-5RXB4S5H-DaRO-NB6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-C0o2Du1e.js → chunk-5VM5RSS4-Dh0ZKiGA.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-4f8kr-U7.js → chunk-6Q2QTUOP-DOrvbR-H.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D_OWvXzX.js → chunk-GF5L2VYU-C3b22LN2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-CasdPz5X.js → chunk-JWPE2WC7-CXlqO8l6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Cj8lRrla.js → chunk-KBJHAD2P-BblTBrDu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-CUvD1FSd.js → chunk-RYQCIY6F-DswqPALe.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CqLw_eqB.js → chunk-XXDRQBXY-CfTwVOlc.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-MFV5Qxb5.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-MFV5Qxb5.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-ku-WqJPx.js → cose-bilkent-JH36ORCC-bLD8jVFo.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-Bq_PYDhl.js → cynefin-VYW2F7L2-BvoG1fh5.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BiGZV641.js → cynefinDiagram-MW4NZA55-DEPAdJu3.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-C4JRlglF.js → dagre-VZM6K2ZE-DmrNvBLy.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B0dNeWYG.js → diagram-7IWD3JNH-CdwzIwvs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Cj35YvYM.js → diagram-B4RE2ZJO-CJ3hj-md.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-uzQoJ2-8.js → diagram-LBJQPF4R-C6YJT2a4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D1a-Buoz.js → diagram-Q27KOJAE-C_LPfjhX.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-XjRrLRSs.js → diagram-UB23O5K3-xvsSb5Ie.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-Da_O24cW.js → ebnfDiagram-BXEA7PRR-Rxp2tKcD.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-BLJ8jrYU.js → erDiagram-JOGREHBK-DRyLYmZh.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-B9GrLjM0.js → flowDiagram-UKHOOZJN-TLSU_D2B.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CeJ0TqiK.js → ganttDiagram-PKOTCBZU-DGqeTvHC.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js → gitGraphDiagram-DS77QQ5N-BZxyH2wh.js} +1 -1
- package/dist/worker/console/static/assets/index-C8V8K9b4.js +451 -0
- package/dist/worker/console/static/assets/index-Cnf_KxTQ.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DB26i3d8.js → infoDiagram-6WML65LV-BsYtzx01.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-BOGRyeZT.js → ishikawaDiagram-WSZJBQD7-Qr-ezGv-.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CZRoBO_6.js → journeyDiagram-NVQOT4AX-DYbB3ibo.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-qlWhJyoB.js → kanban-definition-27J2QSJJ-BHJHewZO.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CfUiDDB3.js → linear-CmPIPmCG.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BQe6fpqj.js → mermaid.core-F--C1uV0.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-zFzWHw64.js → mindmap-definition-FAOFIHXS-YTXfL1st.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-DFGUqjZO.js → pegDiagram-VL7TDLO6-DdDMYXP9.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BB5i0l8Z.js → pieDiagram-7S7Q4E2Y-C3nH4KVd.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-obbVZ_X5.js → quadrantDiagram-CIZ2JOQS-kXyxjG7Y.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BwdaNzc1.js → railroadDiagram-AXF67PYL-KNzbl-7w.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-CkTJaugh.js → requirementDiagram-LRYGKXZP-CSwI6dG0.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-BwJ-hIgq.js → sankeyDiagram-W5VNT64P-Bl1AyuXy.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BzSgmm7w.js → sequenceDiagram-SI44F4Z6-2y4EmXQa.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Bn3oj5Q1.js → sizeCapture-X5ZJPWSS-E8v8YrMg.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-dxhGtL8K.js → stateDiagram-OKZ733FA-eGGlR856.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8e4J3at.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BLpEInya.js → swimlanes-SLNWSIFB-B7g7xc07.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Do88I1Nb.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-ChJGYcXy.js → timeline-definition-Z64GVDOM-DR3VCHBG.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DK-qjRer.js → vennDiagram-T6HMQDX7-LUvapZIy.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-3qeaWAg-.js → wardleyDiagram-T6FBY63Y-C7GDcaXq.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CootlyP9.js → xychartDiagram-ELKLHX3M-CbNAqoGK.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +19 -8
- package/dist/worker/console/static-src/operator-chat/open-preview-in-browser.js +15 -7
- package/dist/worker/console/workspace-context.js +111 -1
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/static/dag-node-purpose.js +5 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +99 -4
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +45 -3
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +907 -162
- package/dist/workflows/dag/frontend-plan-render.js +2 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +255 -349
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +202 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +1 -432
- package/dist/workflows/dag/frontend-review-context.js +261 -15
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +914 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +419 -0
- package/dist/workflows/dag/frontend-shape.js +435 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +249 -24
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +285 -0
- package/dist/workflows/dag/frontend-writer-status.js +256 -0
- package/dist/workflows/dag/init-hybrid.js +777 -525
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +752 -4
- package/dist/workflows/dag/prompt.js +34 -1
- package/dist/workflows/dag/recovery-lease.js +80 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +256 -1
- package/dist/workflows/dag/rerun-plan.js +17 -6
- package/dist/workflows/dag/rerun-run.js +23 -0
- package/dist/workflows/dag/rerun-task.js +29 -0
- package/dist/workflows/dag/retry-policy.js +219 -104
- package/dist/workflows/dag/runner.js +433 -126
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/types.js +239 -12
- package/dist/workflows/dag/validate.js +28 -12
- package/docs/examples/README.md +5 -0
- package/docs/init-surface.manifest.json +30 -12
- package/docs/skills/vetted-skill-registry.md +4 -2
- package/docs/templates/README.md +2 -0
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +1 -1
- package/docs/templates/backend-test-dag.json +18 -18
- package/docs/templates/frontend-implementation-contract.schema.json +4 -1
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +1 -1
- package/skills/frontend-bounded-implement/SKILL.md +15 -14
- package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
- package/skills/frontend-contract/SKILL.md +23 -0
- package/skills/frontend-contract/references/contract-protocol.md +34 -0
- package/skills/frontend-design-review/SKILL.md +22 -41
- package/skills/frontend-plan/SKILL.md +26 -0
- package/skills/frontend-plan/references/decision-contract.md +37 -0
- package/skills/frontend-plan/references/design-decisions.md +17 -0
- package/skills/frontend-review/SKILL.md +20 -15
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/frontend-scout/SKILL.md +25 -0
- package/skills/frontend-scout/references/design-evidence.md +16 -0
- package/skills/frontend-scout/references/scout-evidence.md +23 -0
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +1 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/worker/console/static/assets/channel-BU5gOilw.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +0 -1
- package/dist/worker/console/static/assets/index-H9rFJiGL.css +0 -1
- package/dist/worker/console/static/assets/index-xwu9GxEc.js +0 -451
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +0 -8
- package/skills/frontend-implementation/SKILL.md +0 -52
- package/skills/frontend-implementation/references/code-standards.md +0 -33
- package/skills/frontend-implementation/references/design-spec.md +0 -56
- package/skills/frontend-implementation/references/node-contracts.md +0 -31
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const id = z.string().trim().min(1);
|
|
4
|
+
const digest = z.string().regex(/^[a-f0-9]{64}$/);
|
|
5
|
+
const revision = z.number().int().positive();
|
|
6
|
+
const instant = z.string().datetime();
|
|
7
|
+
const factRef = z.object({ ref: id, digest }).strict();
|
|
8
|
+
function canonicalValue(value) {
|
|
9
|
+
if (Array.isArray(value))
|
|
10
|
+
return value.map(canonicalValue);
|
|
11
|
+
if (value !== null && typeof value === "object")
|
|
12
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => [key, canonicalValue(item)]));
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
export function canonicalFrontendHumanDecisionJson(value) {
|
|
16
|
+
return JSON.stringify(canonicalValue(value));
|
|
17
|
+
}
|
|
18
|
+
export function computeFrontendHumanDecisionDigest(value) {
|
|
19
|
+
return createHash("sha256").update(canonicalFrontendHumanDecisionJson(value), "utf8").digest("hex");
|
|
20
|
+
}
|
|
21
|
+
const baseEnvelope = {
|
|
22
|
+
operationId: id,
|
|
23
|
+
runId: id,
|
|
24
|
+
operationRevision: revision,
|
|
25
|
+
targetDigest: digest,
|
|
26
|
+
staleOnTargetDigestChange: z.literal(true),
|
|
27
|
+
expiresAt: instant.optional(),
|
|
28
|
+
};
|
|
29
|
+
const contractEnvelopeInput = z.object({ ...baseEnvelope, kind: z.literal("contract-decision"), resumeAction: z.literal("resume-contract"), payload: z.object({ questionFactIds: z.array(id).min(1) }).strict() }).strict();
|
|
30
|
+
const highEnvelopeInput = z.object({ ...baseEnvelope, kind: z.literal("high-admission"), resumeAction: z.literal("admit-high"), payload: z.object({ admissionCandidateRef: factRef }).strict() }).strict();
|
|
31
|
+
const splitEnvelopeInput = z.object({ ...baseEnvelope, kind: z.literal("split-materialization-approval"), resumeAction: z.literal("materialize-split"), payload: z.object({ splitManifestRef: factRef }).strict() }).strict();
|
|
32
|
+
const baselineEnvelopeInput = z.object({ ...baseEnvelope, kind: z.literal("baseline-debt-acceptance"), resumeAction: z.literal("accept-baseline-debt"), payload: z.object({ baselineEvidenceRefs: z.array(factRef).min(1), candidateDigest: digest, commandDigest: digest, resultingPolicy: z.literal("allow-non-regression") }).strict() }).strict();
|
|
33
|
+
const verificationEnvelopeInput = z.object({ ...baseEnvelope, kind: z.literal("verification-config"), resumeAction: z.literal("select-verification-config"), payload: z.object({ unresolvedEntrypointRefs: z.array(factRef).min(1), allowlistedResolvers: z.array(id).min(1) }).strict() }).strict();
|
|
34
|
+
export const frontendHumanDecisionEnvelopeInputSchema = z.discriminatedUnion("kind", [contractEnvelopeInput, highEnvelopeInput, splitEnvelopeInput, baselineEnvelopeInput, verificationEnvelopeInput]);
|
|
35
|
+
const envelopeSchemas = [contractEnvelopeInput.extend({ digest }).strict(), highEnvelopeInput.extend({ digest }).strict(), splitEnvelopeInput.extend({ digest }).strict(), baselineEnvelopeInput.extend({ digest }).strict(), verificationEnvelopeInput.extend({ digest }).strict()];
|
|
36
|
+
const envelopeStructuralSchema = z.discriminatedUnion("kind", envelopeSchemas);
|
|
37
|
+
export const frontendHumanDecisionEnvelopeSchema = envelopeStructuralSchema.superRefine((value, ctx) => {
|
|
38
|
+
const { digest: actual, ...payload } = value;
|
|
39
|
+
if (computeFrontendHumanDecisionDigest(payload) !== actual)
|
|
40
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["digest"], message: "envelope digest mismatch" });
|
|
41
|
+
});
|
|
42
|
+
export function buildFrontendHumanDecisionEnvelope(raw) {
|
|
43
|
+
const payload = frontendHumanDecisionEnvelopeInputSchema.parse(raw);
|
|
44
|
+
return envelopeStructuralSchema.parse({ ...payload, digest: computeFrontendHumanDecisionDigest(payload) });
|
|
45
|
+
}
|
|
46
|
+
export function parseFrontendHumanDecisionEnvelope(value) { return frontendHumanDecisionEnvelopeSchema.parse(value); }
|
|
47
|
+
const receiptBase = { operationId: id, runId: id, operationRevision: revision, targetDigest: digest, receiptId: id, envelopeDigest: digest, commitState: z.literal("committed"), consumeState: z.enum(["unconsumed", "consumed"]) };
|
|
48
|
+
const resolutionFact = { resolutionFactRef: factRef };
|
|
49
|
+
const contractReceiptInput = z.object({ ...receiptBase, kind: z.literal("contract-decision"), resumeAction: z.literal("resume-contract"), resolution: z.object({ questionFactIds: z.array(id).min(1), outcome: z.literal("resume-contract"), ...resolutionFact }).strict() }).strict();
|
|
50
|
+
const highReceiptInput = z.object({ ...receiptBase, kind: z.literal("high-admission"), resumeAction: z.literal("admit-high"), resolution: z.object({ admissionCandidateRef: factRef, outcome: z.literal("admitted-high"), ...resolutionFact }).strict() }).strict();
|
|
51
|
+
const splitReceiptInput = z.object({ ...receiptBase, kind: z.literal("split-materialization-approval"), resumeAction: z.literal("materialize-split"), resolution: z.object({ splitManifestRef: factRef, ...resolutionFact }).strict() }).strict();
|
|
52
|
+
const baselineReceiptInput = z.object({ ...receiptBase, kind: z.literal("baseline-debt-acceptance"), resumeAction: z.literal("accept-baseline-debt"), resolution: z.object({ baselineEvidenceRefs: z.array(factRef).min(1), candidateDigest: digest, commandDigest: digest, resultingPolicy: z.literal("allow-non-regression"), outcome: z.literal("accepted-baseline-debt"), remediated: z.literal(false), ...resolutionFact }).strict() }).strict();
|
|
53
|
+
const verificationReceiptInput = z.object({ ...receiptBase, kind: z.literal("verification-config"), resumeAction: z.literal("select-verification-config"), resolution: z.object({ entrypointRef: factRef, resolver: id, sourceDigest: digest, commandDigest: digest, ...resolutionFact }).strict() }).strict();
|
|
54
|
+
export const frontendHumanDecisionReceiptInputSchema = z.discriminatedUnion("kind", [contractReceiptInput, highReceiptInput, splitReceiptInput, baselineReceiptInput, verificationReceiptInput]);
|
|
55
|
+
const receiptStructuralSchema = z.discriminatedUnion("kind", [contractReceiptInput.extend({ digest }).strict(), highReceiptInput.extend({ digest }).strict(), splitReceiptInput.extend({ digest }).strict(), baselineReceiptInput.extend({ digest }).strict(), verificationReceiptInput.extend({ digest }).strict()]);
|
|
56
|
+
export const frontendHumanDecisionReceiptSchema = receiptStructuralSchema.superRefine((value, ctx) => {
|
|
57
|
+
const { digest: actual, ...payload } = value;
|
|
58
|
+
if (computeFrontendHumanDecisionDigest(payload) !== actual)
|
|
59
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["digest"], message: "receipt digest mismatch" });
|
|
60
|
+
});
|
|
61
|
+
export function buildFrontendHumanDecisionReceipt(raw) {
|
|
62
|
+
const payload = frontendHumanDecisionReceiptInputSchema.parse(raw);
|
|
63
|
+
return receiptStructuralSchema.parse({ ...payload, digest: computeFrontendHumanDecisionDigest(payload) });
|
|
64
|
+
}
|
|
65
|
+
export function parseFrontendHumanDecisionReceipt(value) { return frontendHumanDecisionReceiptSchema.parse(value); }
|
|
66
|
+
const activeInput = z.object({ status: z.literal("active"), operationId: id, runId: id, revision }).strict();
|
|
67
|
+
const blockedInput = z.object({ status: z.literal("blocked-human"), operationId: id, runId: id, revision, envelope: frontendHumanDecisionEnvelopeSchema }).strict();
|
|
68
|
+
const resolvedInput = z.object({ status: z.literal("resolved"), operationId: id, runId: id, revision, receipt: frontendHumanDecisionReceiptSchema }).strict();
|
|
69
|
+
export const frontendHumanDecisionProjectionInputSchema = z.discriminatedUnion("status", [activeInput, blockedInput, resolvedInput]);
|
|
70
|
+
const projectionStructuralSchema = z.discriminatedUnion("status", [activeInput.extend({ digest }).strict(), blockedInput.extend({ digest }).strict(), resolvedInput.extend({ digest }).strict()]);
|
|
71
|
+
export const frontendHumanDecisionProjectionSchema = projectionStructuralSchema.superRefine((value, ctx) => { const { digest: actual, ...payload } = value; if (computeFrontendHumanDecisionDigest(payload) !== actual)
|
|
72
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["digest"], message: "projection digest mismatch" }); });
|
|
73
|
+
export function buildFrontendHumanDecisionProjection(raw) { const payload = frontendHumanDecisionProjectionInputSchema.parse(raw); return projectionStructuralSchema.parse({ ...payload, digest: computeFrontendHumanDecisionDigest(payload) }); }
|
|
74
|
+
export function parseFrontendHumanDecisionProjection(value) { return frontendHumanDecisionProjectionSchema.parse(value); }
|
|
75
|
+
const reject = (projection, reason) => ({ outcome: "rejected", reason, projection });
|
|
76
|
+
export function materializeFrontendHumanDecision(input) {
|
|
77
|
+
const projection = parseFrontendHumanDecisionProjection(input.projection);
|
|
78
|
+
const envelope = parseFrontendHumanDecisionEnvelope(input.envelope);
|
|
79
|
+
if (projection.status !== "active")
|
|
80
|
+
return reject(projection, "projection is not active");
|
|
81
|
+
if (projection.revision !== input.expectedRecordRevision || projection.digest !== input.expectedProjectionDigest)
|
|
82
|
+
return reject(projection, "revision or projection digest conflict");
|
|
83
|
+
if (envelope.operationId !== projection.operationId || envelope.runId !== projection.runId || envelope.operationRevision !== projection.revision)
|
|
84
|
+
return reject(projection, "envelope binding mismatch");
|
|
85
|
+
return { outcome: "applied", projection: buildFrontendHumanDecisionProjection({ status: "blocked-human", operationId: projection.operationId, runId: projection.runId, revision: projection.revision + 1, envelope }) };
|
|
86
|
+
}
|
|
87
|
+
function receiptMatchesEnvelope(receipt, envelope) {
|
|
88
|
+
if (receipt.kind !== envelope.kind || receipt.resumeAction !== envelope.resumeAction || receipt.operationId !== envelope.operationId || receipt.runId !== envelope.runId || receipt.operationRevision !== envelope.operationRevision || receipt.targetDigest !== envelope.targetDigest || receipt.envelopeDigest !== envelope.digest)
|
|
89
|
+
return false;
|
|
90
|
+
switch (envelope.kind) {
|
|
91
|
+
case "contract-decision": return receipt.kind === envelope.kind && canonicalFrontendHumanDecisionJson(receipt.resolution.questionFactIds) === canonicalFrontendHumanDecisionJson(envelope.payload.questionFactIds);
|
|
92
|
+
case "high-admission": return receipt.kind === envelope.kind && canonicalFrontendHumanDecisionJson(receipt.resolution.admissionCandidateRef) === canonicalFrontendHumanDecisionJson(envelope.payload.admissionCandidateRef);
|
|
93
|
+
case "split-materialization-approval": return receipt.kind === envelope.kind && canonicalFrontendHumanDecisionJson(receipt.resolution.splitManifestRef) === canonicalFrontendHumanDecisionJson(envelope.payload.splitManifestRef);
|
|
94
|
+
case "baseline-debt-acceptance": return receipt.kind === envelope.kind && receipt.resolution.candidateDigest === envelope.payload.candidateDigest && receipt.resolution.commandDigest === envelope.payload.commandDigest && canonicalFrontendHumanDecisionJson(receipt.resolution.baselineEvidenceRefs) === canonicalFrontendHumanDecisionJson(envelope.payload.baselineEvidenceRefs);
|
|
95
|
+
case "verification-config": return receipt.kind === envelope.kind && envelope.payload.allowlistedResolvers.includes(receipt.resolution.resolver) && envelope.payload.unresolvedEntrypointRefs.some((item) => canonicalFrontendHumanDecisionJson(item) === canonicalFrontendHumanDecisionJson(receipt.resolution.entrypointRef));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export function consumeFrontendHumanDecision(input) {
|
|
99
|
+
const projection = parseFrontendHumanDecisionProjection(input.projection);
|
|
100
|
+
if (projection.status !== "blocked-human")
|
|
101
|
+
return reject(projection, "projection has no pending envelope");
|
|
102
|
+
const consumeInput = z.object({ expectedRecordRevision: revision, expectedProjectionDigest: digest, currentTargetDigest: digest, now: instant }).strict().safeParse({
|
|
103
|
+
expectedRecordRevision: input.expectedRecordRevision,
|
|
104
|
+
expectedProjectionDigest: input.expectedProjectionDigest,
|
|
105
|
+
currentTargetDigest: input.currentTargetDigest,
|
|
106
|
+
now: input.now,
|
|
107
|
+
});
|
|
108
|
+
if (!consumeInput.success)
|
|
109
|
+
return reject(projection, "invalid consume input");
|
|
110
|
+
const receipt = parseFrontendHumanDecisionReceipt(input.receipt);
|
|
111
|
+
if (projection.revision !== input.expectedRecordRevision || projection.digest !== input.expectedProjectionDigest)
|
|
112
|
+
return reject(projection, "revision or projection digest conflict");
|
|
113
|
+
if (input.currentTargetDigest !== projection.envelope.targetDigest) {
|
|
114
|
+
const payload = { kind: "frontend-human-decision-stale", operationId: projection.operationId, envelopeDigest: projection.envelope.digest, previousTargetDigest: projection.envelope.targetDigest, currentTargetDigest: input.currentTargetDigest };
|
|
115
|
+
return { outcome: "stale", reason: "target digest changed", projection, auditFact: { ...payload, digest: computeFrontendHumanDecisionDigest(payload) } };
|
|
116
|
+
}
|
|
117
|
+
if (projection.envelope.expiresAt && Date.parse(input.now) >= Date.parse(projection.envelope.expiresAt))
|
|
118
|
+
return reject(projection, "envelope expired");
|
|
119
|
+
if (receipt.commitState !== "committed" || receipt.consumeState !== "unconsumed" || !receiptMatchesEnvelope(receipt, projection.envelope))
|
|
120
|
+
return reject(projection, "receipt freshness or payload mismatch");
|
|
121
|
+
const { digest: _digest, ...receiptPayload } = receipt;
|
|
122
|
+
const consumed = buildFrontendHumanDecisionReceipt({ ...receiptPayload, consumeState: "consumed" });
|
|
123
|
+
return { outcome: "applied", projection: buildFrontendHumanDecisionProjection({ status: "resolved", operationId: projection.operationId, runId: projection.runId, revision: projection.revision + 1, receipt: consumed }) };
|
|
124
|
+
}
|
|
125
|
+
/* ------------------------------------------------------------------ *
|
|
126
|
+
* A+B producer/consumer wiring (no new envelope schema in the frozen *
|
|
127
|
+
* five-kind union). *
|
|
128
|
+
* ------------------------------------------------------------------ */
|
|
129
|
+
/**
|
|
130
|
+
* A+B (AC-002): contract `blocked` uniquely maps to `blocked-human`
|
|
131
|
+
* (contract-decision envelope) or `blocked-external` (wait-external), reusing
|
|
132
|
+
* the existing five-envelope schema without widening it. Any other disposition
|
|
133
|
+
* is `not-blocked`.
|
|
134
|
+
*/
|
|
135
|
+
export function mapContractBlockedOwner(input) {
|
|
136
|
+
if (input.disposition !== "blocked")
|
|
137
|
+
return "not-blocked";
|
|
138
|
+
return input.blockingOwner === "blocked-external"
|
|
139
|
+
? "blocked-external"
|
|
140
|
+
: "blocked-human";
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* A+B (AC-007): High-topology `high-admission-receipt`. The receipt is a
|
|
144
|
+
* runtime-materialized catalog (not a sixth human-decision envelope): a receipt
|
|
145
|
+
* whose candidate digest matches the admission digest resolves to `accepted`;
|
|
146
|
+
* any digest drift resolves to `stale`.
|
|
147
|
+
*/
|
|
148
|
+
export const highAdmissionReceiptSchema = z
|
|
149
|
+
.object({
|
|
150
|
+
schemaVersion: z.literal(1),
|
|
151
|
+
schemaId: z.literal("high-admission-receipt-v1"),
|
|
152
|
+
candidateDigest: digest,
|
|
153
|
+
admissionDigest: digest,
|
|
154
|
+
status: z.enum(["accepted", "stale"]),
|
|
155
|
+
})
|
|
156
|
+
.strict();
|
|
157
|
+
export function evaluateHighAdmissionReceipt(input) {
|
|
158
|
+
return highAdmissionReceiptSchema.parse({
|
|
159
|
+
schemaVersion: 1,
|
|
160
|
+
schemaId: "high-admission-receipt-v1",
|
|
161
|
+
candidateDigest: input.candidateDigest,
|
|
162
|
+
admissionDigest: input.admissionDigest,
|
|
163
|
+
status: input.candidateDigest === input.admissionDigest
|
|
164
|
+
? "accepted"
|
|
165
|
+
: "stale",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* A+B (AC-008): verification-config routing. A verification-config signal
|
|
170
|
+
* either restarts the contract or produces a verification-config-decision
|
|
171
|
+
* (independent of the frozen `verification-config` envelope). Fail-closed to
|
|
172
|
+
* contract restart when the signal is untyped.
|
|
173
|
+
*/
|
|
174
|
+
export function routeVerificationConfig(input) {
|
|
175
|
+
const entrypointRefs = [...new Set(input.unresolvedEntrypointRefs)].filter((item) => item.length > 0);
|
|
176
|
+
return {
|
|
177
|
+
decision: entrypointRefs.length > 0
|
|
178
|
+
? "verification-config-decision"
|
|
179
|
+
: "contract-restart",
|
|
180
|
+
entrypointRefs,
|
|
181
|
+
};
|
|
182
|
+
}
|