toolroll 0.9.34 → 0.9.36
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 +44 -0
- package/README.md +7 -0
- package/dist/assignment.d.ts +1 -0
- package/dist/assignment.js +2 -2
- package/dist/browser-workspace.d.ts +3 -3
- package/dist/builder.js +4 -7
- package/dist/chat-acceptance.d.ts +1 -1
- package/dist/contracts/contract.d.ts +33 -5
- package/dist/contracts/contract.js +143 -16
- package/dist/contracts/flow-propose.d.ts +325 -0
- package/dist/contracts/flow-propose.js +41 -0
- package/dist/contracts/flow.d.ts +19400 -0
- package/dist/contracts/flow.js +359 -0
- package/dist/contracts/handoff.d.ts +91 -0
- package/dist/contracts/handoff.js +82 -0
- package/dist/contracts/proof.d.ts +136 -0
- package/dist/contracts/proof.js +110 -0
- package/dist/contracts/verification-receipt.d.ts +148 -0
- package/dist/contracts/verification-receipt.js +87 -0
- package/dist/demo.js +28 -11
- package/dist/desktop-update.d.ts +2 -2
- package/dist/evidence.d.ts +3 -40
- package/dist/evidence.js +5 -1
- package/dist/flow-gallery.d.ts +3 -2
- package/dist/flow-share.d.ts +10 -32
- package/dist/flow-share.js +65 -83
- package/dist/flow-starters.d.ts +2 -1
- package/dist/flow-triggers.d.ts +15 -72
- package/dist/flow-triggers.js +94 -83
- package/dist/flows.d.ts +37 -218
- package/dist/flows.js +350 -385
- package/dist/mate-tools.js +14 -44
- package/dist/mcp-connect.d.ts +3 -0
- package/dist/mcp-connect.js +16 -4
- package/dist/operate.js +31 -2
- package/dist/plan.d.ts +5 -5
- package/dist/plan.js +5 -25
- package/dist/process-recovery-eligibility.d.ts +2 -2
- package/dist/process-recovery-eligibility.js +13 -8
- package/dist/proof.d.ts +15 -91
- package/dist/proof.js +90 -249
- package/dist/result-evidence-readers.d.ts +42 -0
- package/dist/result-evidence-readers.js +91 -0
- package/dist/serve.js +8 -99
- package/dist/setup-guide.d.ts +2 -2
- package/dist/surface.js +8 -1
- package/dist/task-review-brief.d.ts +75 -0
- package/dist/task-review-brief.js +179 -0
- package/dist/text-limits.d.ts +70 -0
- package/dist/text-limits.js +70 -0
- package/dist/verification-evidence.js +24 -12
- package/package.json +1 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The builder's proof: one schema for the file a builder writes (the proof manifest), the `proof.json` the machine
|
|
3
|
+
* stores, and the reader that reads both back (`parseProof` in proof.ts). The rules JSON Schema cannot state — UTF-8
|
|
4
|
+
* byte limits, blank text, control characters, relative screenshot paths and duplicate ids or paths — run in plain
|
|
5
|
+
* code after parsing, each with a path-named error.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { type ContractResult } from "./contract.js";
|
|
9
|
+
/** How many entries each proof list may hold. Text limits live in TEXT_LIMITS. */
|
|
10
|
+
export declare const PROOF_COUNTS: {
|
|
11
|
+
readonly criteria: 12;
|
|
12
|
+
readonly evidencePerCriterion: 4;
|
|
13
|
+
readonly checks: 12;
|
|
14
|
+
readonly changed: 64;
|
|
15
|
+
readonly caveats: 8;
|
|
16
|
+
readonly screenshots: 8;
|
|
17
|
+
};
|
|
18
|
+
/** Every proof limit by its long-standing name: the counts above and the byte limits from TEXT_LIMITS. */
|
|
19
|
+
export declare const PROOF_LIMITS: {
|
|
20
|
+
readonly criteria: 12;
|
|
21
|
+
readonly evidencePerCriterion: 4;
|
|
22
|
+
readonly checks: 12;
|
|
23
|
+
readonly changed: 64;
|
|
24
|
+
readonly caveats: 8;
|
|
25
|
+
readonly screenshots: 8;
|
|
26
|
+
readonly payload: number;
|
|
27
|
+
readonly criterionId: 40;
|
|
28
|
+
readonly criterionStatement: 300;
|
|
29
|
+
readonly criterionHow: 500;
|
|
30
|
+
readonly evidenceRef: 300;
|
|
31
|
+
readonly checkCommand: 300;
|
|
32
|
+
readonly checkSummary: 300;
|
|
33
|
+
readonly changedPath: 300;
|
|
34
|
+
readonly caveat: 300;
|
|
35
|
+
readonly screenshotPath: 300;
|
|
36
|
+
readonly screenshotCaption: 300;
|
|
37
|
+
};
|
|
38
|
+
export declare const CRITERION_VERDICTS: readonly ["met", "not-met", "not-checked", "pending-verification"];
|
|
39
|
+
/** One typed reference a criterion cites: an existing check's command, screenshot's path, a path in `changed`, or
|
|
40
|
+
* (kind `manual-review`) free text pointing at nothing machine-checkable. */
|
|
41
|
+
export declare const criterionEvidenceSchema: z.ZodObject<{
|
|
42
|
+
kind: z.ZodEnum<{
|
|
43
|
+
"changed-path": "changed-path";
|
|
44
|
+
check: "check";
|
|
45
|
+
"manual-review": "manual-review";
|
|
46
|
+
screenshot: "screenshot";
|
|
47
|
+
}>;
|
|
48
|
+
ref: z.ZodString;
|
|
49
|
+
}, z.core.$strict>;
|
|
50
|
+
/** One criterion the proof answers. `pending-verification` asserts all agent-owned work is met and only the
|
|
51
|
+
* machine-owned final check remains. */
|
|
52
|
+
export declare const proofCriterionSchema: z.ZodObject<{
|
|
53
|
+
id: z.ZodString;
|
|
54
|
+
statement: z.ZodString;
|
|
55
|
+
how: z.ZodString;
|
|
56
|
+
verdict: z.ZodEnum<{
|
|
57
|
+
met: "met";
|
|
58
|
+
"not-checked": "not-checked";
|
|
59
|
+
"not-met": "not-met";
|
|
60
|
+
"pending-verification": "pending-verification";
|
|
61
|
+
}>;
|
|
62
|
+
evidence: z.ZodArray<z.ZodObject<{
|
|
63
|
+
kind: z.ZodEnum<{
|
|
64
|
+
"changed-path": "changed-path";
|
|
65
|
+
check: "check";
|
|
66
|
+
"manual-review": "manual-review";
|
|
67
|
+
screenshot: "screenshot";
|
|
68
|
+
}>;
|
|
69
|
+
ref: z.ZodString;
|
|
70
|
+
}, z.core.$strict>>;
|
|
71
|
+
}, z.core.$strict>;
|
|
72
|
+
export declare const proofCheckSchema: z.ZodObject<{
|
|
73
|
+
command: z.ZodString;
|
|
74
|
+
summary: z.ZodString;
|
|
75
|
+
exitCode: z.ZodInt;
|
|
76
|
+
}, z.core.$strict>;
|
|
77
|
+
/** A screenshot the proof claims; its path is where the file lived in the worktree, its bytes are checked by the
|
|
78
|
+
* caller. */
|
|
79
|
+
export declare const proofScreenshotSchema: z.ZodObject<{
|
|
80
|
+
path: z.ZodString;
|
|
81
|
+
caption: z.ZodString;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
export declare const PROOF_VERSION = 2;
|
|
84
|
+
/** The proof as stored and as every reader returns it: every list present, nothing unknown. */
|
|
85
|
+
export declare const proofSchema: z.ZodObject<{
|
|
86
|
+
version: z.ZodLiteral<2>;
|
|
87
|
+
criteria: z.ZodArray<z.ZodObject<{
|
|
88
|
+
id: z.ZodString;
|
|
89
|
+
statement: z.ZodString;
|
|
90
|
+
how: z.ZodString;
|
|
91
|
+
verdict: z.ZodEnum<{
|
|
92
|
+
met: "met";
|
|
93
|
+
"not-checked": "not-checked";
|
|
94
|
+
"not-met": "not-met";
|
|
95
|
+
"pending-verification": "pending-verification";
|
|
96
|
+
}>;
|
|
97
|
+
evidence: z.ZodArray<z.ZodObject<{
|
|
98
|
+
kind: z.ZodEnum<{
|
|
99
|
+
"changed-path": "changed-path";
|
|
100
|
+
check: "check";
|
|
101
|
+
"manual-review": "manual-review";
|
|
102
|
+
screenshot: "screenshot";
|
|
103
|
+
}>;
|
|
104
|
+
ref: z.ZodString;
|
|
105
|
+
}, z.core.$strict>>;
|
|
106
|
+
}, z.core.$strict>>;
|
|
107
|
+
checks: z.ZodArray<z.ZodObject<{
|
|
108
|
+
command: z.ZodString;
|
|
109
|
+
summary: z.ZodString;
|
|
110
|
+
exitCode: z.ZodInt;
|
|
111
|
+
}, z.core.$strict>>;
|
|
112
|
+
changed: z.ZodArray<z.ZodString>;
|
|
113
|
+
caveats: z.ZodArray<z.ZodString>;
|
|
114
|
+
screenshots: z.ZodArray<z.ZodObject<{
|
|
115
|
+
path: z.ZodString;
|
|
116
|
+
caption: z.ZodString;
|
|
117
|
+
}, z.core.$strict>>;
|
|
118
|
+
}, z.core.$strict>;
|
|
119
|
+
export type ParsedProof = z.infer<typeof proofSchema>;
|
|
120
|
+
export type ParsedCriterion = z.infer<typeof proofCriterionSchema>;
|
|
121
|
+
export type CriterionEvidenceRef = z.infer<typeof criterionEvidenceSchema>;
|
|
122
|
+
export type ParsedCheck = z.infer<typeof proofCheckSchema>;
|
|
123
|
+
export type ParsedScreenshot = z.infer<typeof proofScreenshotSchema>;
|
|
124
|
+
/**
|
|
125
|
+
* A version 1 proof — what a builder writes (`{ "version": 1, "screenshots": [...] }`) and every proof stored through
|
|
126
|
+
* 0.9.34 — as version 2. Version 1 read only the fields it knew and ignored any others, and read a missing or null
|
|
127
|
+
* list (a criterion's `evidence` too) as empty; this keeps exactly that. Values themselves are never rewritten.
|
|
128
|
+
*/
|
|
129
|
+
export declare function upgradeProofV1(body: Record<string, unknown>): Record<string, unknown>;
|
|
130
|
+
export declare const PROOF_UPGRADES: {
|
|
131
|
+
readonly 1: typeof upgradeProofV1;
|
|
132
|
+
};
|
|
133
|
+
/** The payload as version 2 (upgraded from version 1 when needed), for the plain-code rules; null for a non-object. */
|
|
134
|
+
export declare function proofPayloadBody(input: unknown): Record<string, unknown> | null;
|
|
135
|
+
/** Read a proof payload: version 2 as itself, version 1 upgraded, a newer one refused plainly. */
|
|
136
|
+
export declare function readProofPayload(input: unknown): ContractResult<ParsedProof>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The builder's proof: one schema for the file a builder writes (the proof manifest), the `proof.json` the machine
|
|
3
|
+
* stores, and the reader that reads both back (`parseProof` in proof.ts). The rules JSON Schema cannot state — UTF-8
|
|
4
|
+
* byte limits, blank text, control characters, relative screenshot paths and duplicate ids or paths — run in plain
|
|
5
|
+
* code after parsing, each with a path-named error.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { TEXT_LIMITS } from "../text-limits.js";
|
|
9
|
+
import { EVIDENCE_KINDS } from "../scope.js";
|
|
10
|
+
import { limited, readVersioned, versioned } from "./contract.js";
|
|
11
|
+
/** How many entries each proof list may hold. Text limits live in TEXT_LIMITS. */
|
|
12
|
+
export const PROOF_COUNTS = { criteria: 12, evidencePerCriterion: 4, checks: 12, changed: 64, caveats: 8, screenshots: 8 };
|
|
13
|
+
/** Every proof limit by its long-standing name: the counts above and the byte limits from TEXT_LIMITS. */
|
|
14
|
+
export const PROOF_LIMITS = {
|
|
15
|
+
payload: TEXT_LIMITS.proofPayloadBytes,
|
|
16
|
+
...PROOF_COUNTS,
|
|
17
|
+
criterionId: TEXT_LIMITS.proofCriterionIdBytes,
|
|
18
|
+
criterionStatement: TEXT_LIMITS.proofLineBytes,
|
|
19
|
+
criterionHow: TEXT_LIMITS.proofHowBytes,
|
|
20
|
+
evidenceRef: TEXT_LIMITS.proofLineBytes,
|
|
21
|
+
checkCommand: TEXT_LIMITS.proofLineBytes,
|
|
22
|
+
checkSummary: TEXT_LIMITS.proofLineBytes,
|
|
23
|
+
changedPath: TEXT_LIMITS.proofLineBytes,
|
|
24
|
+
caveat: TEXT_LIMITS.proofLineBytes,
|
|
25
|
+
screenshotPath: TEXT_LIMITS.proofLineBytes,
|
|
26
|
+
screenshotCaption: TEXT_LIMITS.proofLineBytes,
|
|
27
|
+
};
|
|
28
|
+
export const CRITERION_VERDICTS = ["met", "not-met", "not-checked", "pending-verification"];
|
|
29
|
+
const line = (field) => limited(field, "proofLineBytes").min(1);
|
|
30
|
+
/** One typed reference a criterion cites: an existing check's command, screenshot's path, a path in `changed`, or
|
|
31
|
+
* (kind `manual-review`) free text pointing at nothing machine-checkable. */
|
|
32
|
+
export const criterionEvidenceSchema = z.strictObject({
|
|
33
|
+
kind: z.enum(EVIDENCE_KINDS),
|
|
34
|
+
ref: line("evidence ref"),
|
|
35
|
+
});
|
|
36
|
+
/** One criterion the proof answers. `pending-verification` asserts all agent-owned work is met and only the
|
|
37
|
+
* machine-owned final check remains. */
|
|
38
|
+
export const proofCriterionSchema = z.strictObject({
|
|
39
|
+
id: limited("criterion id", "proofCriterionIdBytes").min(1),
|
|
40
|
+
statement: line("criterion statement"),
|
|
41
|
+
how: limited("criterion how", "proofHowBytes").min(1),
|
|
42
|
+
verdict: z.enum(CRITERION_VERDICTS),
|
|
43
|
+
evidence: z.array(criterionEvidenceSchema).max(PROOF_COUNTS.evidencePerCriterion),
|
|
44
|
+
});
|
|
45
|
+
export const proofCheckSchema = z.strictObject({
|
|
46
|
+
command: line("check command"),
|
|
47
|
+
summary: line("check summary"),
|
|
48
|
+
exitCode: z.int().min(0).max(255),
|
|
49
|
+
});
|
|
50
|
+
/** A screenshot the proof claims; its path is where the file lived in the worktree, its bytes are checked by the
|
|
51
|
+
* caller. */
|
|
52
|
+
export const proofScreenshotSchema = z.strictObject({
|
|
53
|
+
path: line("screenshot path"),
|
|
54
|
+
caption: line("screenshot caption"),
|
|
55
|
+
});
|
|
56
|
+
export const PROOF_VERSION = 2;
|
|
57
|
+
/** The proof as stored and as every reader returns it: every list present, nothing unknown. */
|
|
58
|
+
export const proofSchema = versioned(PROOF_VERSION, {
|
|
59
|
+
criteria: z.array(proofCriterionSchema).max(PROOF_COUNTS.criteria),
|
|
60
|
+
checks: z.array(proofCheckSchema).max(PROOF_COUNTS.checks),
|
|
61
|
+
changed: z.array(line("changed path")).max(PROOF_COUNTS.changed),
|
|
62
|
+
caveats: z.array(line("caveat")).max(PROOF_COUNTS.caveats),
|
|
63
|
+
screenshots: z.array(proofScreenshotSchema).max(PROOF_COUNTS.screenshots),
|
|
64
|
+
});
|
|
65
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66
|
+
/** The listed fields of `body`, a null or missing one left out (the old parser read null text as missing). */
|
|
67
|
+
function pick(body, fields) {
|
|
68
|
+
const out = {};
|
|
69
|
+
for (const field of fields) {
|
|
70
|
+
if (body[field] !== undefined && body[field] !== null)
|
|
71
|
+
out[field] = body[field];
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
const each = (value, fields) => (Array.isArray(value) ? value.map(one => (isRecord(one) ? pick(one, fields) : one)) : value);
|
|
76
|
+
/**
|
|
77
|
+
* A version 1 proof — what a builder writes (`{ "version": 1, "screenshots": [...] }`) and every proof stored through
|
|
78
|
+
* 0.9.34 — as version 2. Version 1 read only the fields it knew and ignored any others, and read a missing or null
|
|
79
|
+
* list (a criterion's `evidence` too) as empty; this keeps exactly that. Values themselves are never rewritten.
|
|
80
|
+
*/
|
|
81
|
+
export function upgradeProofV1(body) {
|
|
82
|
+
const list = (field) => (body[field] === undefined || body[field] === null ? [] : body[field]);
|
|
83
|
+
const criteria = list("criteria");
|
|
84
|
+
return {
|
|
85
|
+
version: PROOF_VERSION,
|
|
86
|
+
criteria: Array.isArray(criteria)
|
|
87
|
+
? criteria.map(one => {
|
|
88
|
+
if (!isRecord(one))
|
|
89
|
+
return one;
|
|
90
|
+
const evidence = one["evidence"] === undefined || one["evidence"] === null ? [] : one["evidence"];
|
|
91
|
+
return { ...pick(one, ["id", "statement", "how", "verdict"]), evidence: each(evidence, ["kind", "ref"]) };
|
|
92
|
+
})
|
|
93
|
+
: criteria,
|
|
94
|
+
checks: each(list("checks"), ["command", "summary", "exitCode"]),
|
|
95
|
+
changed: list("changed"),
|
|
96
|
+
caveats: list("caveats"),
|
|
97
|
+
screenshots: each(list("screenshots"), ["path", "caption"]),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export const PROOF_UPGRADES = { 1: upgradeProofV1 };
|
|
101
|
+
/** The payload as version 2 (upgraded from version 1 when needed), for the plain-code rules; null for a non-object. */
|
|
102
|
+
export function proofPayloadBody(input) {
|
|
103
|
+
if (!isRecord(input))
|
|
104
|
+
return null;
|
|
105
|
+
return input["version"] === 1 ? upgradeProofV1(input) : input;
|
|
106
|
+
}
|
|
107
|
+
/** Read a proof payload: version 2 as itself, version 1 upgraded, a newer one refused plainly. */
|
|
108
|
+
export function readProofPayload(input) {
|
|
109
|
+
return readVersioned(proofSchema, input, PROOF_UPGRADES);
|
|
110
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The machine verification receipt (`verification-receipt.json`): one schema for what `sealVerificationReceipt`
|
|
3
|
+
* writes and what `verificationEvidence` reads back. Only the machine writes it, from typed values, so every receipt
|
|
4
|
+
* ever sealed already has exactly this shape; the bindings that need the store — the run, its candidate, the approved
|
|
5
|
+
* command, the retained log and a reused gate's source — are checked in plain code after parsing.
|
|
6
|
+
*
|
|
7
|
+
* Version 1 is a gate executed in its own run. Version 2 added `reusedFrom` and `executedHere: false` for a gate
|
|
8
|
+
* reused by an unchanged observation follow-up. A direct receipt is still sealed as version 1, so its bytes (and the
|
|
9
|
+
* digests bound to them) read the same in every Toolroll that reads receipts.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { type ContractResult } from "./contract.js";
|
|
13
|
+
export declare const VERIFICATION_FAILURES: readonly ["spawn-failed", "timed-out", "dependency-missing", "setup-stale", "setup-failed", "own-install-failed", "tracked-files-changed", "setup-changed-files", "checkout-moved", "cleanliness-unavailable", "dependency-still-missing", "retry-spawn-failed", "retry-timed-out", "custody-lost"];
|
|
14
|
+
/**
|
|
15
|
+
* What the repository's approved verification command did: not configured; configured but the run could not attempt
|
|
16
|
+
* it (`attemptFailed`, which downgrades to `short` rather than `refuted`: "we could not check" is not "the claim is
|
|
17
|
+
* false"); or ran, with its exit code.
|
|
18
|
+
*/
|
|
19
|
+
export declare const verifyCommandFactsSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
20
|
+
configured: z.ZodLiteral<false>;
|
|
21
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
22
|
+
configured: z.ZodLiteral<true>;
|
|
23
|
+
ran: z.ZodLiteral<false>;
|
|
24
|
+
attemptFailed: z.ZodLiteral<true>;
|
|
25
|
+
failure: z.ZodOptional<z.ZodEnum<{
|
|
26
|
+
"checkout-moved": "checkout-moved";
|
|
27
|
+
"cleanliness-unavailable": "cleanliness-unavailable";
|
|
28
|
+
"custody-lost": "custody-lost";
|
|
29
|
+
"dependency-missing": "dependency-missing";
|
|
30
|
+
"dependency-still-missing": "dependency-still-missing";
|
|
31
|
+
"own-install-failed": "own-install-failed";
|
|
32
|
+
"retry-spawn-failed": "retry-spawn-failed";
|
|
33
|
+
"retry-timed-out": "retry-timed-out";
|
|
34
|
+
"setup-changed-files": "setup-changed-files";
|
|
35
|
+
"setup-failed": "setup-failed";
|
|
36
|
+
"setup-stale": "setup-stale";
|
|
37
|
+
"spawn-failed": "spawn-failed";
|
|
38
|
+
"timed-out": "timed-out";
|
|
39
|
+
"tracked-files-changed": "tracked-files-changed";
|
|
40
|
+
}>>;
|
|
41
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
42
|
+
configured: z.ZodLiteral<true>;
|
|
43
|
+
ran: z.ZodLiteral<true>;
|
|
44
|
+
exitCode: z.ZodInt;
|
|
45
|
+
setupReplayed: z.ZodOptional<z.ZodLiteral<true>>;
|
|
46
|
+
}, z.core.$strict>]>;
|
|
47
|
+
export type VerifyCommandFacts = z.infer<typeof verifyCommandFactsSchema>;
|
|
48
|
+
/** The approved verification grant the gate ran, exactly as the store held it. */
|
|
49
|
+
export declare const receiptCommandSchema: z.ZodObject<{
|
|
50
|
+
id: z.ZodInt;
|
|
51
|
+
repo: z.ZodString;
|
|
52
|
+
command: z.ZodString;
|
|
53
|
+
timeoutMs: z.ZodInt;
|
|
54
|
+
digest: z.ZodString;
|
|
55
|
+
recoverySetupDigest: z.ZodNullable<z.ZodString>;
|
|
56
|
+
approvedBy: z.ZodString;
|
|
57
|
+
approvedAt: z.ZodString;
|
|
58
|
+
revokedAt: z.ZodNullable<z.ZodString>;
|
|
59
|
+
revokedBy: z.ZodNullable<z.ZodString>;
|
|
60
|
+
}, z.core.$strict>;
|
|
61
|
+
/** The retained check log the receipt binds, by artifact id and content. */
|
|
62
|
+
export declare const receiptLogSchema: z.ZodObject<{
|
|
63
|
+
artifactId: z.ZodInt;
|
|
64
|
+
sha256: z.ZodString;
|
|
65
|
+
bytesStored: z.ZodInt;
|
|
66
|
+
bytesOriginal: z.ZodInt;
|
|
67
|
+
truncated: z.ZodBoolean;
|
|
68
|
+
redacted: z.ZodBoolean;
|
|
69
|
+
captureStatus: z.ZodNullable<z.ZodEnum<{
|
|
70
|
+
failed: "failed";
|
|
71
|
+
ok: "ok";
|
|
72
|
+
}>>;
|
|
73
|
+
}, z.core.$strict>;
|
|
74
|
+
export declare const RECEIPT_VERSION = 2;
|
|
75
|
+
/** The receipt as read: a reused gate names its source run and digest; a direct one names neither. */
|
|
76
|
+
export declare const verificationReceiptSchema: z.ZodObject<{
|
|
77
|
+
version: z.ZodLiteral<2>;
|
|
78
|
+
run: z.ZodInt;
|
|
79
|
+
head: z.ZodString;
|
|
80
|
+
base: z.ZodNullable<z.ZodString>;
|
|
81
|
+
scopeDigest: z.ZodNullable<z.ZodString>;
|
|
82
|
+
reusedFrom: z.ZodOptional<z.ZodObject<{
|
|
83
|
+
run: z.ZodInt;
|
|
84
|
+
digest: z.ZodString;
|
|
85
|
+
}, z.core.$strict>>;
|
|
86
|
+
executedHere: z.ZodOptional<z.ZodLiteral<false>>;
|
|
87
|
+
command: z.ZodObject<{
|
|
88
|
+
id: z.ZodInt;
|
|
89
|
+
repo: z.ZodString;
|
|
90
|
+
command: z.ZodString;
|
|
91
|
+
timeoutMs: z.ZodInt;
|
|
92
|
+
digest: z.ZodString;
|
|
93
|
+
recoverySetupDigest: z.ZodNullable<z.ZodString>;
|
|
94
|
+
approvedBy: z.ZodString;
|
|
95
|
+
approvedAt: z.ZodString;
|
|
96
|
+
revokedAt: z.ZodNullable<z.ZodString>;
|
|
97
|
+
revokedBy: z.ZodNullable<z.ZodString>;
|
|
98
|
+
}, z.core.$strict>;
|
|
99
|
+
result: z.ZodUnion<readonly [z.ZodObject<{
|
|
100
|
+
configured: z.ZodLiteral<false>;
|
|
101
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
102
|
+
configured: z.ZodLiteral<true>;
|
|
103
|
+
ran: z.ZodLiteral<false>;
|
|
104
|
+
attemptFailed: z.ZodLiteral<true>;
|
|
105
|
+
failure: z.ZodOptional<z.ZodEnum<{
|
|
106
|
+
"checkout-moved": "checkout-moved";
|
|
107
|
+
"cleanliness-unavailable": "cleanliness-unavailable";
|
|
108
|
+
"custody-lost": "custody-lost";
|
|
109
|
+
"dependency-missing": "dependency-missing";
|
|
110
|
+
"dependency-still-missing": "dependency-still-missing";
|
|
111
|
+
"own-install-failed": "own-install-failed";
|
|
112
|
+
"retry-spawn-failed": "retry-spawn-failed";
|
|
113
|
+
"retry-timed-out": "retry-timed-out";
|
|
114
|
+
"setup-changed-files": "setup-changed-files";
|
|
115
|
+
"setup-failed": "setup-failed";
|
|
116
|
+
"setup-stale": "setup-stale";
|
|
117
|
+
"spawn-failed": "spawn-failed";
|
|
118
|
+
"timed-out": "timed-out";
|
|
119
|
+
"tracked-files-changed": "tracked-files-changed";
|
|
120
|
+
}>>;
|
|
121
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
122
|
+
configured: z.ZodLiteral<true>;
|
|
123
|
+
ran: z.ZodLiteral<true>;
|
|
124
|
+
exitCode: z.ZodInt;
|
|
125
|
+
setupReplayed: z.ZodOptional<z.ZodLiteral<true>>;
|
|
126
|
+
}, z.core.$strict>]>;
|
|
127
|
+
log: z.ZodObject<{
|
|
128
|
+
artifactId: z.ZodInt;
|
|
129
|
+
sha256: z.ZodString;
|
|
130
|
+
bytesStored: z.ZodInt;
|
|
131
|
+
bytesOriginal: z.ZodInt;
|
|
132
|
+
truncated: z.ZodBoolean;
|
|
133
|
+
redacted: z.ZodBoolean;
|
|
134
|
+
captureStatus: z.ZodNullable<z.ZodEnum<{
|
|
135
|
+
failed: "failed";
|
|
136
|
+
ok: "ok";
|
|
137
|
+
}>>;
|
|
138
|
+
}, z.core.$strict>;
|
|
139
|
+
}, z.core.$strict>;
|
|
140
|
+
export type VerificationReceipt = z.infer<typeof verificationReceiptSchema>;
|
|
141
|
+
/** A version 1 receipt read as version 2: the same fields. Reuse is a version 2 field; a version 1 receipt naming it
|
|
142
|
+
* is refused by its reader, which still knows the version it was sealed as. */
|
|
143
|
+
export declare function upgradeDirectReceipt(body: Record<string, unknown>): Record<string, unknown>;
|
|
144
|
+
export declare const RECEIPT_UPGRADES: {
|
|
145
|
+
readonly 1: typeof upgradeDirectReceipt;
|
|
146
|
+
};
|
|
147
|
+
/** Read a receipt: version 2 as itself, version 1 as a direct gate, a newer one refused plainly. */
|
|
148
|
+
export declare function readVerificationReceipt(input: unknown): ContractResult<VerificationReceipt>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The machine verification receipt (`verification-receipt.json`): one schema for what `sealVerificationReceipt`
|
|
3
|
+
* writes and what `verificationEvidence` reads back. Only the machine writes it, from typed values, so every receipt
|
|
4
|
+
* ever sealed already has exactly this shape; the bindings that need the store — the run, its candidate, the approved
|
|
5
|
+
* command, the retained log and a reused gate's source — are checked in plain code after parsing.
|
|
6
|
+
*
|
|
7
|
+
* Version 1 is a gate executed in its own run. Version 2 added `reusedFrom` and `executedHere: false` for a gate
|
|
8
|
+
* reused by an unchanged observation follow-up. A direct receipt is still sealed as version 1, so its bytes (and the
|
|
9
|
+
* digests bound to them) read the same in every Toolroll that reads receipts.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { readVersioned, versioned } from "./contract.js";
|
|
13
|
+
export const VERIFICATION_FAILURES = [
|
|
14
|
+
"spawn-failed",
|
|
15
|
+
"timed-out",
|
|
16
|
+
"dependency-missing",
|
|
17
|
+
"setup-stale",
|
|
18
|
+
"setup-failed",
|
|
19
|
+
"own-install-failed",
|
|
20
|
+
"tracked-files-changed",
|
|
21
|
+
"setup-changed-files",
|
|
22
|
+
"checkout-moved",
|
|
23
|
+
"cleanliness-unavailable",
|
|
24
|
+
"dependency-still-missing",
|
|
25
|
+
"retry-spawn-failed",
|
|
26
|
+
"retry-timed-out",
|
|
27
|
+
"custody-lost",
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* What the repository's approved verification command did: not configured; configured but the run could not attempt
|
|
31
|
+
* it (`attemptFailed`, which downgrades to `short` rather than `refuted`: "we could not check" is not "the claim is
|
|
32
|
+
* false"); or ran, with its exit code.
|
|
33
|
+
*/
|
|
34
|
+
export const verifyCommandFactsSchema = z.union([
|
|
35
|
+
z.strictObject({ configured: z.literal(false) }),
|
|
36
|
+
z.strictObject({ configured: z.literal(true), ran: z.literal(false), attemptFailed: z.literal(true), failure: z.enum(VERIFICATION_FAILURES).optional() }),
|
|
37
|
+
z.strictObject({ configured: z.literal(true), ran: z.literal(true), exitCode: z.int().min(0).max(255), setupReplayed: z.literal(true).optional() }),
|
|
38
|
+
]);
|
|
39
|
+
/** The approved verification grant the gate ran, exactly as the store held it. */
|
|
40
|
+
export const receiptCommandSchema = z.strictObject({
|
|
41
|
+
id: z.int(),
|
|
42
|
+
repo: z.string(),
|
|
43
|
+
command: z.string(),
|
|
44
|
+
timeoutMs: z.int(),
|
|
45
|
+
digest: z.string(),
|
|
46
|
+
recoverySetupDigest: z.string().nullable(),
|
|
47
|
+
approvedBy: z.string(),
|
|
48
|
+
approvedAt: z.string(),
|
|
49
|
+
revokedAt: z.string().nullable(),
|
|
50
|
+
revokedBy: z.string().nullable(),
|
|
51
|
+
});
|
|
52
|
+
/** The retained check log the receipt binds, by artifact id and content. */
|
|
53
|
+
export const receiptLogSchema = z.strictObject({
|
|
54
|
+
artifactId: z.int(),
|
|
55
|
+
sha256: z.string(),
|
|
56
|
+
bytesStored: z.int(),
|
|
57
|
+
bytesOriginal: z.int(),
|
|
58
|
+
truncated: z.boolean(),
|
|
59
|
+
redacted: z.boolean(),
|
|
60
|
+
captureStatus: z.enum(["ok", "failed"]).nullable(),
|
|
61
|
+
});
|
|
62
|
+
export const RECEIPT_VERSION = 2;
|
|
63
|
+
const receiptFields = {
|
|
64
|
+
run: z.int(),
|
|
65
|
+
head: z.string(),
|
|
66
|
+
base: z.string().nullable(),
|
|
67
|
+
scopeDigest: z.string().nullable(),
|
|
68
|
+
};
|
|
69
|
+
/** The receipt as read: a reused gate names its source run and digest; a direct one names neither. */
|
|
70
|
+
export const verificationReceiptSchema = versioned(RECEIPT_VERSION, {
|
|
71
|
+
...receiptFields,
|
|
72
|
+
reusedFrom: z.strictObject({ run: z.int(), digest: z.string() }).optional(),
|
|
73
|
+
executedHere: z.literal(false).optional(),
|
|
74
|
+
command: receiptCommandSchema,
|
|
75
|
+
result: verifyCommandFactsSchema,
|
|
76
|
+
log: receiptLogSchema,
|
|
77
|
+
});
|
|
78
|
+
/** A version 1 receipt read as version 2: the same fields. Reuse is a version 2 field; a version 1 receipt naming it
|
|
79
|
+
* is refused by its reader, which still knows the version it was sealed as. */
|
|
80
|
+
export function upgradeDirectReceipt(body) {
|
|
81
|
+
return { ...body, version: RECEIPT_VERSION };
|
|
82
|
+
}
|
|
83
|
+
export const RECEIPT_UPGRADES = { 1: upgradeDirectReceipt };
|
|
84
|
+
/** Read a receipt: version 2 as itself, version 1 as a direct gate, a newer one refused plainly. */
|
|
85
|
+
export function readVerificationReceipt(input) {
|
|
86
|
+
return readVersioned(verificationReceiptSchema, input, RECEIPT_UPGRADES);
|
|
87
|
+
}
|
package/dist/demo.js
CHANGED
|
@@ -38,6 +38,7 @@ import { fileTaskProposal, fileRoutineProposal } from "./proposal.js";
|
|
|
38
38
|
import { storeEvidence, budgetedStatJson, imageDimensions } from "./evidence.js";
|
|
39
39
|
import { parseProof, adjudicate } from "./proof.js";
|
|
40
40
|
import { sealVerificationReceipt } from "./verification-evidence.js";
|
|
41
|
+
import { HANDOFF_VERSION, readHandoffArtifact } from "./contracts/handoff.js";
|
|
41
42
|
import { parseExecutionPlanDocument, milestonesOf } from "./plan.js";
|
|
42
43
|
import { maybeTriggerRepair } from "./dispose.js";
|
|
43
44
|
import { deflateSync } from "node:zlib";
|
|
@@ -84,8 +85,21 @@ index 11aa0b2..c44d1f7 100644
|
|
|
84
85
|
+ expect(settle(1005, 0.031)).toBe(31.16);
|
|
85
86
|
+ });
|
|
86
87
|
`;
|
|
88
|
+
/** A demo run's handoff artifact: the run's own identity around the story's conclusion and lists, written through
|
|
89
|
+
* the handoff schema like a real one. */
|
|
90
|
+
function demoHandoffBytes(store, runId, told) {
|
|
91
|
+
const run = store.getRun(runId);
|
|
92
|
+
const head = run.headRevision ?? run.baseRevision ?? "0".repeat(40);
|
|
93
|
+
const read = readHandoffArtifact({
|
|
94
|
+
version: HANDOFF_VERSION, taskId: store.externalIdFor(run.taskRef) ?? String(run.taskRef), runId, provider: run.provider,
|
|
95
|
+
sessionId: null, branch: run.branch ?? "", worktree: run.worktree ?? "", base: run.baseRevision ?? head, head, ...told,
|
|
96
|
+
freshness: { stampedAt: run.startedAt, currentAsOf: head },
|
|
97
|
+
});
|
|
98
|
+
if (!read.ok)
|
|
99
|
+
throw new Error(`demo handoff: ${read.issues.map(issue => issue.line).join("; ")}`);
|
|
100
|
+
return Buffer.from(JSON.stringify(read.value, null, 2), "utf8");
|
|
101
|
+
}
|
|
87
102
|
const DEMO_HANDOFF = {
|
|
88
|
-
schema: 1,
|
|
89
103
|
outcome: "built",
|
|
90
104
|
committed: true,
|
|
91
105
|
conclusion: "Fixed the payout rounding drift: settle() now rounds at cent precision instead of accumulating half-cent errors. Added boundary tests against the ledger fixtures. All 214 tests pass.",
|
|
@@ -123,7 +137,6 @@ index 2b3c4d5..8e9f0a1 100644
|
|
|
123
137
|
};
|
|
124
138
|
`;
|
|
125
139
|
const DEMO_FAILED_HANDOFF = {
|
|
126
|
-
schema: 1,
|
|
127
140
|
outcome: "built",
|
|
128
141
|
committed: true,
|
|
129
142
|
conclusion: "Removed LEGACY_PAYOUT from settlement and config. The admin override still reads it, so one reference remains.",
|
|
@@ -193,7 +206,6 @@ index a1b2c3d..d4e5f6a 100644
|
|
|
193
206
|
+export const EMPTY_STATE_ILLUSTRATION = "quiet-inbox";
|
|
194
207
|
`;
|
|
195
208
|
const DEMO_COPY_HANDOFF = {
|
|
196
|
-
schema: 1,
|
|
197
209
|
outcome: "built",
|
|
198
210
|
committed: true,
|
|
199
211
|
conclusion: "Rewrote the inbox's empty-state copy so it explains why the list is empty instead of just saying so.",
|
|
@@ -605,7 +617,7 @@ export function seedDemo(store, repos, evidenceRoot, now) {
|
|
|
605
617
|
filesTruncated: false,
|
|
606
618
|
};
|
|
607
619
|
storeEvidence(store, evidenceRoot, doneRun, "diff-stat", "diff-stat.json", budgetedStatJson(stat), "parsed from git diff --numstat -z [demo: synthetic]", hoursAgo(8.5));
|
|
608
|
-
storeEvidence(store, evidenceRoot, doneRun, "handoff", "handoff.json",
|
|
620
|
+
storeEvidence(store, evidenceRoot, doneRun, "handoff", "handoff.json", demoHandoffBytes(store, doneRun, DEMO_HANDOFF), "composed at completion [demo: synthetic]", hoursAgo(8.4));
|
|
609
621
|
// The evidence bundle (Priority 2): a validated proof, its claimed
|
|
610
622
|
// screenshot stored as immutable image evidence, the plane's own re-run
|
|
611
623
|
// check, and the closed verdict — computed once, exactly as the real
|
|
@@ -669,7 +681,7 @@ export function seedDemo(store, repos, evidenceRoot, now) {
|
|
|
669
681
|
filesTruncated: false,
|
|
670
682
|
};
|
|
671
683
|
storeEvidence(store, evidenceRoot, failedRun, "diff-stat", "diff-stat.json", budgetedStatJson(failedStat), "parsed from git diff --numstat -z [demo: synthetic]", hoursAgo(5.6));
|
|
672
|
-
storeEvidence(store, evidenceRoot, failedRun, "handoff", "handoff.json",
|
|
684
|
+
storeEvidence(store, evidenceRoot, failedRun, "handoff", "handoff.json", demoHandoffBytes(store, failedRun, DEMO_FAILED_HANDOFF), "composed at completion [demo: synthetic]", hoursAgo(5.6));
|
|
673
685
|
storeEvidence(store, evidenceRoot, failedRun, "proof", "proof.json", Buffer.from(JSON.stringify(DEMO_FAILED_PROOF, null, 2), "utf8"), "agent-authored proof (validated, re-serialized) [demo: synthetic]", hoursAgo(5.6));
|
|
674
686
|
const failedAdjudicated = adjudicate({
|
|
675
687
|
proofArtifactPresent: true,
|
|
@@ -734,7 +746,7 @@ export function seedDemo(store, repos, evidenceRoot, now) {
|
|
|
734
746
|
filesTruncated: false,
|
|
735
747
|
};
|
|
736
748
|
storeEvidence(store, evidenceRoot, copyReviewRun, "diff-stat", "diff-stat.json", budgetedStatJson(copyStat), "parsed from git diff --numstat -z [demo: synthetic]", hoursAgo(3.6));
|
|
737
|
-
storeEvidence(store, evidenceRoot, copyReviewRun, "handoff", "handoff.json",
|
|
749
|
+
storeEvidence(store, evidenceRoot, copyReviewRun, "handoff", "handoff.json", demoHandoffBytes(store, copyReviewRun, DEMO_COPY_HANDOFF), "composed at completion [demo: synthetic]", hoursAgo(3.5));
|
|
738
750
|
storeEvidence(store, evidenceRoot, copyReviewRun, "proof", "proof.json", Buffer.from(JSON.stringify(DEMO_COPY_PROOF, null, 2), "utf8"), "agent-authored proof (validated, re-serialized) [demo: synthetic]", hoursAgo(3.5));
|
|
739
751
|
const copyReviewProofParse = parseProof(JSON.stringify(DEMO_COPY_PROOF));
|
|
740
752
|
const copyReviewAdjudicated = adjudicate({
|
|
@@ -815,10 +827,15 @@ export function seedDemo(store, repos, evidenceRoot, now) {
|
|
|
815
827
|
};
|
|
816
828
|
storeEvidence(store, evidenceRoot, reviewedRun, "diff-stat", "diff-stat.json", budgetedStatJson(reviewedStat), "parsed from git diff --numstat -z [demo: synthetic]", hoursAgo(4.6));
|
|
817
829
|
const DEMO_REPAIR_HANDOFF = {
|
|
818
|
-
|
|
819
|
-
|
|
830
|
+
outcome: "built",
|
|
831
|
+
committed: true,
|
|
832
|
+
conclusion: "Added a TODO comment marking the per-account lock — ran out of turns before wiring the actual guard.",
|
|
833
|
+
changes: ["Marked the per-account lock in src/payout-limiter.ts."],
|
|
834
|
+
verification: [],
|
|
835
|
+
followUps: [],
|
|
836
|
+
decisionsIncorporated: [],
|
|
820
837
|
};
|
|
821
|
-
storeEvidence(store, evidenceRoot, reviewedRun, "handoff", "handoff.json",
|
|
838
|
+
storeEvidence(store, evidenceRoot, reviewedRun, "handoff", "handoff.json", demoHandoffBytes(store, reviewedRun, DEMO_REPAIR_HANDOFF), "composed at completion [demo: synthetic]", hoursAgo(4.5));
|
|
822
839
|
const DEMO_REPAIR_PROOF = {
|
|
823
840
|
version: 1,
|
|
824
841
|
criteria: [
|
|
@@ -1389,7 +1406,7 @@ export function createDemoLead(input) {
|
|
|
1389
1406
|
};
|
|
1390
1407
|
const shotPath = `evidence/${plan.kind}.png`;
|
|
1391
1408
|
const png = demoScreenshot(plan.screenshot.accent);
|
|
1392
|
-
const handoff = {
|
|
1409
|
+
const handoff = { outcome: "built", committed: true, conclusion: plan.conclusion, changes: diffs.map(one => `Changed ${one.path}.`), verification: [plan.checks[0]], followUps: [], decisionsIncorporated: [] };
|
|
1393
1410
|
const proof = {
|
|
1394
1411
|
version: 1,
|
|
1395
1412
|
criteria: [
|
|
@@ -1404,7 +1421,7 @@ export function createDemoLead(input) {
|
|
|
1404
1421
|
const synthetic = "[demo: scripted]";
|
|
1405
1422
|
storeEvidence(store, evidenceRoot, run, "terminal-diff", "terminal-diff.patch", Buffer.from(diffs.map(one => one.patch).join(""), "utf8"), `git diff --no-ext-diff --no-textconv --no-color ${base.slice(0, 8)}..HEAD (exit 0) ${synthetic}`, at);
|
|
1406
1423
|
storeEvidence(store, evidenceRoot, run, "diff-stat", "diff-stat.json", budgetedStatJson(stat), `parsed from git diff --numstat -z ${synthetic}`, at);
|
|
1407
|
-
storeEvidence(store, evidenceRoot, run, "handoff", "handoff.json",
|
|
1424
|
+
storeEvidence(store, evidenceRoot, run, "handoff", "handoff.json", demoHandoffBytes(store, run, handoff), `composed at completion ${synthetic}`, at);
|
|
1408
1425
|
storeEvidence(store, evidenceRoot, run, "proof", "proof.json", Buffer.from(JSON.stringify(proof, null, 2), "utf8"), `agent-authored proof (validated, re-serialized) ${synthetic}`, at);
|
|
1409
1426
|
storeEvidence(store, evidenceRoot, run, "screenshot", `screenshot-${plan.kind}.png`, png, `agent-claimed screenshot at ${shotPath} (validated png) ${synthetic}`, at);
|
|
1410
1427
|
const log = `=== Attempt summary ===\n- Project check · attempt 1: (exit 0)\n\n=== Project check · attempt 1 ===\n$ ${DEMO_CHECK_COMMAND}\n(exit 0)\n\n--- stdout ---\n${plan.checkOutput}\n--- stderr ---\n`;
|
package/dist/desktop-update.d.ts
CHANGED
|
@@ -125,20 +125,20 @@ export declare function prepareDesktopUpdate(stateDir: string, installed: string
|
|
|
125
125
|
export declare function sqliteLock(file: string, busyTimeoutMs?: number): DatabaseSync | null;
|
|
126
126
|
export declare const installationLock: (j: UpdateJournal) => DatabaseSync | null;
|
|
127
127
|
export declare function desktopUpdateStatus(stateDir: string): {
|
|
128
|
-
|
|
128
|
+
version?: never;
|
|
129
129
|
active: boolean;
|
|
130
130
|
phase: string;
|
|
131
131
|
detail: string;
|
|
132
132
|
running: boolean;
|
|
133
133
|
canResume: boolean;
|
|
134
134
|
canCancel: boolean;
|
|
135
|
+
error?: never;
|
|
135
136
|
automaticRecovery?: never;
|
|
136
137
|
recoveryAttempts?: never;
|
|
137
138
|
recoveryRepairs?: never;
|
|
138
139
|
wasRunning?: never;
|
|
139
140
|
backupPath?: never;
|
|
140
141
|
updateId?: never;
|
|
141
|
-
version?: never;
|
|
142
142
|
buildId?: never;
|
|
143
143
|
workDir?: never;
|
|
144
144
|
} | {
|