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
|
@@ -7,6 +7,7 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
import { adjudicate } from "./proof.js";
|
|
8
8
|
import { parseReviewContext, reviewContextCustodyProblem } from "./review-context.js";
|
|
9
9
|
import { readVerifiedArtifact, storeEvidence, scanForSecrets, redactSecretLines } from "./evidence.js";
|
|
10
|
+
import { readVerificationReceipt } from "./contracts/verification-receipt.js";
|
|
10
11
|
import { liveQuickCommand, quickVerifyKey, runCheckLevel } from "./check-levels.js";
|
|
11
12
|
export const VERIFICATION_RECEIPT_CAPTURE = "machine verification receipt v1";
|
|
12
13
|
export const REVIEW_GATE_NAME = "REVIEW-VERIFICATION.json";
|
|
@@ -18,11 +19,16 @@ export function sealVerificationReceipt(store, root, runId, head, command, resul
|
|
|
18
19
|
const log = store.artifactsFor(runId).find(a => a.kind === "check-log");
|
|
19
20
|
if (!log)
|
|
20
21
|
throw new Error("Verification receipt requires its retained log");
|
|
21
|
-
const
|
|
22
|
+
const receipt = {
|
|
22
23
|
version: reusedFrom === undefined ? 1 : 2, run: runId, head, base: source.baseRevision, scopeDigest: source.scopeDigest,
|
|
23
24
|
...(reusedFrom === undefined ? {} : { reusedFrom, executedHere: false }),
|
|
24
25
|
command, result, log: binding(log),
|
|
25
|
-
}
|
|
26
|
+
};
|
|
27
|
+
const checked = readVerificationReceipt(receipt);
|
|
28
|
+
if (!checked.ok)
|
|
29
|
+
throw new Error(`The verification receipt does not match its contract: ${checked.issues.map(issue => issue.line).join("; ")}`);
|
|
30
|
+
// Sealed in the writer's own key order, as every receipt before the schema: its bytes and digests stay comparable.
|
|
31
|
+
const raw = JSON.stringify(receipt, null, 1);
|
|
26
32
|
const hits = scanForSecrets(raw);
|
|
27
33
|
storeEvidence(store, root, runId, "structured-output", "verification-receipt.json", Buffer.from(hits.length ? redactSecretLines(raw, hits) : raw), reusedFrom === undefined ? VERIFICATION_RECEIPT_CAPTURE : "machine verification reuse v1", now, { captureStatus: "ok", redacted: hits.length > 0 });
|
|
28
34
|
}
|
|
@@ -73,21 +79,27 @@ export function verificationEvidence(store, root, runId) {
|
|
|
73
79
|
catch {
|
|
74
80
|
return fail("The verification receipt cannot be read.");
|
|
75
81
|
}
|
|
76
|
-
|
|
82
|
+
// The schema checks the shape; the bindings below check it against the store. The receipt itself stays exactly
|
|
83
|
+
// as sealed: its bytes are this view's bytes and part of its digest.
|
|
84
|
+
const read = readVerificationReceipt(receipt);
|
|
85
|
+
if (!read.ok)
|
|
86
|
+
return fail(`The verification receipt is malformed: ${read.issues.map(issue => issue.line).join("; ")}`);
|
|
87
|
+
const checked = read.value;
|
|
88
|
+
if (checked.run !== runId || checked.head !== source.headRevision || checked.base !== source.baseRevision || checked.scopeDigest !== source.scopeDigest || JSON.stringify(receipt.command) !== JSON.stringify(command) || JSON.stringify(receipt.log) !== JSON.stringify(binding(log)))
|
|
77
89
|
return fail("The candidate, approved command or retained log changed since verification.");
|
|
78
90
|
if (receipt.version === 2) {
|
|
79
91
|
try {
|
|
80
92
|
const brief = observationBrief(store, root, source.taskRef);
|
|
81
|
-
const reused =
|
|
93
|
+
const reused = checked.reusedFrom;
|
|
82
94
|
if (!brief || !readObservationEvidence(store, root, runId) || !reused || reused.run !== brief.sourceRun || reused.run >= runId || reused.digest !== brief.gateDigest ||
|
|
83
|
-
source.baseRevision !== brief.head || source.headRevision !== brief.head ||
|
|
95
|
+
source.baseRevision !== brief.head || source.headRevision !== brief.head || checked.executedHere !== false)
|
|
84
96
|
return fail("The reused gate is not bound to an unchanged observation follow-up.");
|
|
85
97
|
const original = verificationEvidence(store, root, reused.run);
|
|
86
98
|
if (!original.ok || !original.bytes || original.digest !== reused.digest)
|
|
87
99
|
return fail("The original passing gate no longer verifies.");
|
|
88
100
|
const previous = JSON.parse(original.bytes);
|
|
89
101
|
const originalLog = store.getArtifact(previous.log.artifactId);
|
|
90
|
-
if (previous.head !== source.headRevision || previous.result.exitCode !== 0 || JSON.stringify(previous.command) !== JSON.stringify(command) ||
|
|
102
|
+
if (previous.head !== source.headRevision || !("exitCode" in previous.result) || previous.result.exitCode !== 0 || JSON.stringify(previous.command) !== JSON.stringify(command) ||
|
|
91
103
|
JSON.stringify(previous.result) !== JSON.stringify(receipt.result) || originalLog?.sha256 !== log.sha256 ||
|
|
92
104
|
originalLog.bytesOriginal !== log.bytesOriginal || originalLog.redacted !== log.redacted || originalLog.truncated !== log.truncated)
|
|
93
105
|
return fail("The reused gate's candidate, command, result or copied log changed.");
|
|
@@ -96,10 +108,10 @@ export function verificationEvidence(store, root, runId) {
|
|
|
96
108
|
return fail("The reused gate or its observations no longer verify.");
|
|
97
109
|
}
|
|
98
110
|
}
|
|
99
|
-
else if (
|
|
111
|
+
else if (checked.reusedFrom !== undefined || checked.executedHere !== undefined)
|
|
100
112
|
return fail("The gate reuse receipt has an unsupported version.");
|
|
101
|
-
const result =
|
|
102
|
-
if (
|
|
113
|
+
const result = checked.result;
|
|
114
|
+
if (!result.configured)
|
|
103
115
|
return fail("The machine verification result is malformed.");
|
|
104
116
|
if (verified && (!result.ran || result.exitCode !== 0))
|
|
105
117
|
return fail("The passing machine verdict disagrees with its verification receipt.");
|
|
@@ -142,8 +154,8 @@ function sealedCommandKey(root, receipts) {
|
|
|
142
154
|
if (!sealed.ok)
|
|
143
155
|
return null;
|
|
144
156
|
try {
|
|
145
|
-
const
|
|
146
|
-
return
|
|
157
|
+
const read = readVerificationReceipt(JSON.parse(sealed.content.toString("utf8")));
|
|
158
|
+
return read.ok ? read.value.command.repo : null;
|
|
147
159
|
}
|
|
148
160
|
catch {
|
|
149
161
|
return null;
|
|
@@ -244,7 +256,7 @@ export function reuseObservationVerification(store, root, runId, now) {
|
|
|
244
256
|
if (!original.ok || !original.bytes || original.digest !== brief.gateDigest)
|
|
245
257
|
throw Error("The original passing gate is unavailable or changed.");
|
|
246
258
|
const receipt = JSON.parse(original.bytes), log = store.getArtifact(receipt.log.artifactId);
|
|
247
|
-
if (!log || receipt.head !== brief.head || receipt.result
|
|
259
|
+
if (!log || receipt.head !== brief.head || !("ran" in receipt.result) || receipt.result.ran !== true || receipt.result.exitCode !== 0)
|
|
248
260
|
throw Error("The source gate did not pass this candidate.");
|
|
249
261
|
const read = readVerifiedArtifact(root, log);
|
|
250
262
|
if (!read.ok)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolroll",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.36",
|
|
4
4
|
"description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
|
|
5
5
|
"directories": {
|
|
6
6
|
"doc": "docs"
|