toolroll 0.9.91 → 0.9.93
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 +41 -0
- package/dist/approval-authority.d.ts +9 -1
- package/dist/approval-authority.js +15 -2
- package/dist/assignment-status.d.ts +1 -1
- package/dist/assignment-ui.js +2 -2
- package/dist/assignment.js +11 -3
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +30 -30
- package/dist/browser-crew.js +2 -2
- package/dist/browser-workspace.d.ts +3 -5
- package/dist/build-review.js +2 -0
- package/dist/builder.js +21 -2
- package/dist/chat-actions.d.ts +11 -3
- package/dist/chat-actions.js +47 -2
- package/dist/chat-decide.js +2 -0
- package/dist/chat-review.d.ts +2 -1
- package/dist/chat-review.js +2 -2
- package/dist/check-facts.d.ts +124 -0
- package/dist/check-facts.js +272 -0
- package/dist/check-report.d.ts +5 -0
- package/dist/check-report.js +4 -3
- package/dist/cli.js +5 -0
- package/dist/contracts/chat-actions.d.ts +10 -0
- package/dist/contracts/chat-actions.js +4 -0
- package/dist/contracts/console-api.d.ts +3 -2
- package/dist/contracts/console-api.js +5 -2
- package/dist/contracts/release-check.d.ts +25 -1
- package/dist/contracts/release-check.js +19 -0
- package/dist/contracts/route-results.d.ts +121 -0
- package/dist/contracts/route-results.js +71 -0
- package/dist/contracts/route.d.ts +343 -5
- package/dist/contracts/route.js +36 -7
- package/dist/contracts/store-json.d.ts +2 -0
- package/dist/desktop-update.d.ts +2 -2
- package/dist/desktop-update.js +13 -9
- package/dist/dispose.js +6 -0
- package/dist/eval-hooks.d.ts +55 -0
- package/dist/eval-hooks.js +227 -4
- package/dist/flaky.d.ts +17 -0
- package/dist/flaky.js +9 -0
- package/dist/flow-engine.js +1 -1
- package/dist/flow-pull-request.js +1 -1
- package/dist/lead-doors.js +4 -2
- package/dist/lead-tools.js +10 -2
- package/dist/local-owner.d.ts +1 -1
- package/dist/local-owner.js +11 -0
- package/dist/memory-cli.d.ts +3 -0
- package/dist/memory-cli.js +3 -3
- package/dist/memory-pass.js +5 -1
- package/dist/model-choices.d.ts +7 -3
- package/dist/model-choices.js +26 -11
- package/dist/models-cli.d.ts +205 -0
- package/dist/models-cli.js +143 -4
- package/dist/models-ui.d.ts +57 -0
- package/dist/models-ui.js +44 -1
- package/dist/needs-you.d.ts +7 -7
- package/dist/needs-you.js +12 -10
- package/dist/operate-remote-arguments.js +4 -4
- package/dist/operate-remote.js +1 -1
- package/dist/operate.js +191 -23
- package/dist/phase-routing.d.ts +82 -2
- package/dist/phase-routing.js +132 -18
- package/dist/project-delete.d.ts +4 -0
- package/dist/project-delete.js +16 -4
- package/dist/project-memory.d.ts +8 -0
- package/dist/project-memory.js +20 -1
- package/dist/pull-request-flow.js +1 -1
- package/dist/push.d.ts +10 -3
- package/dist/push.js +22 -6
- package/dist/release-adapters.js +11 -1
- package/dist/release-approval.d.ts +191 -0
- package/dist/release-approval.js +382 -0
- package/dist/release-auto.js +22 -4
- package/dist/release-card.d.ts +73 -4
- package/dist/release-card.js +168 -23
- package/dist/release-cli.js +9 -1
- package/dist/release.d.ts +30 -0
- package/dist/release.js +42 -3
- package/dist/result-actions.d.ts +4 -0
- package/dist/result-actions.js +3 -0
- package/dist/result-acts.d.ts +3 -3
- package/dist/result-acts.js +1 -1
- package/dist/route-hooks.d.ts +54 -0
- package/dist/route-hooks.js +179 -0
- package/dist/route-results.d.ts +70 -0
- package/dist/route-results.js +340 -0
- package/dist/route-rules.d.ts +324 -0
- package/dist/route-rules.js +781 -0
- package/dist/route-savings.d.ts +136 -0
- package/dist/route-savings.js +193 -0
- package/dist/route-store.d.ts +300 -0
- package/dist/route-store.js +598 -0
- package/dist/scope.d.ts +3 -1
- package/dist/scope.js +4 -1
- package/dist/server/guards.js +1 -0
- package/dist/server/pages-assets.js +1 -1
- package/dist/server/render-chat.js +2 -2
- package/dist/server/render-release.d.ts +17 -0
- package/dist/server/render-release.js +84 -0
- package/dist/server/render-results.d.ts +0 -2
- package/dist/server/render-results.js +4 -6
- package/dist/server/render-tasks.d.ts +7 -0
- package/dist/server/render-tasks.js +16 -3
- package/dist/server/route-table.d.ts +1 -1
- package/dist/server/route-table.js +19 -1
- package/dist/server/settings.d.ts +2 -1
- package/dist/server/settings.js +87 -1
- package/dist/server/task-views.js +6 -0
- package/dist/server/tasks.d.ts +15 -0
- package/dist/server/tasks.js +105 -20
- package/dist/settings-proofs.js +2 -0
- package/dist/spend-ui.d.ts +11 -0
- package/dist/spend-ui.js +49 -2
- package/dist/stop-proof.d.ts +67 -0
- package/dist/stop-proof.js +293 -0
- package/dist/store.d.ts +106 -31
- package/dist/store.js +653 -128
- package/dist/surface.js +17 -5
- package/dist/task-outcome-cli.js +24 -2
- package/dist/toolroll-update-ui.js +2 -2
- package/dist/toolroll-update.js +4 -2
- package/dist/work-index.js +7 -9
- package/dist/work-summary.d.ts +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.93 — 2026-11-06
|
|
4
|
+
|
|
5
|
+
- **Approve a release with one tap.** When a release is ready for your yes,
|
|
6
|
+
your phone gets one notification ("A release is ready for your approval")
|
|
7
|
+
that opens a single screen: what changed since the last release, the
|
|
8
|
+
GitHub checks, the full terms, and one **Approve release** button. Approve
|
|
9
|
+
and walk away: it deploys only after everything passes, and you hear back
|
|
10
|
+
only if something fails. In the terminal, `toolroll release approve` finds
|
|
11
|
+
the release itself — no task names or codes to copy.
|
|
12
|
+
- **No more "Confirm it stopped".** When a finished build's process record
|
|
13
|
+
is incomplete, Toolroll now checks the computer itself, stops anything left
|
|
14
|
+
over that it can prove is that build's, and carries on. The task says
|
|
15
|
+
"Making sure build #N stopped. This clears on its own." Releases wait
|
|
16
|
+
through it instead of stopping.
|
|
17
|
+
- **Checks record what they saw.** Each finished check now keeps an advisory
|
|
18
|
+
record of the exact code, conditions and failures, groundwork for a list
|
|
19
|
+
of flaky tests and for lessons from fixed failures. It never changes a
|
|
20
|
+
check's result.
|
|
21
|
+
|
|
22
|
+
## 0.9.92 — 2026-11-05
|
|
23
|
+
|
|
24
|
+
- **Toolroll learns which cheaper models do the job.** Every task now gets a
|
|
25
|
+
result record (see it on the task page, or run `toolroll models results`).
|
|
26
|
+
Once a cheaper model has done well on a project's small or routine tasks,
|
|
27
|
+
Settings → Models suggests it, with the evidence. Accepting needs your
|
|
28
|
+
password and only affects that project's eligible tasks; if the cheaper
|
|
29
|
+
model then does badly, Toolroll switches back on its own and tells you.
|
|
30
|
+
Approvals show a line saying why a model was picked. Also in the terminal:
|
|
31
|
+
`toolroll models suggestions`, `accept`, `dismiss`, `turn-off`.
|
|
32
|
+
- **Try a cheaper model on the next 10 tasks.** When a cheaper model has no
|
|
33
|
+
track record yet, Settings → Models offers a trial. Each trial task says
|
|
34
|
+
"trial 3 of 10" and must be approved by a person; Stop ends it any time
|
|
35
|
+
(`toolroll models try` / `stop`).
|
|
36
|
+
- **See what it saved.** The Spend page and `toolroll spend` show cost per
|
|
37
|
+
task before and after each rule or trial — observed, not a promise, and
|
|
38
|
+
only once there are enough priced results.
|
|
39
|
+
- **Project memory is changed by a person only.** A personal API token used
|
|
40
|
+
from the remote CLI could record or retire project decisions without a
|
|
41
|
+
password; now it can't, and neither can the lead. Reading memory works as
|
|
42
|
+
before.
|
|
43
|
+
|
|
3
44
|
## 0.9.91 — 2026-11-04
|
|
4
45
|
|
|
5
46
|
- **A deploy checks your HTTPS console before it changes anything.** If the
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
/** SQL over a `task_scope` alias and a repo expression: whether that row's approval authority stands at `now` (an SQL
|
|
2
2
|
* expression, used exactly three times: the mode and grant clauses, and the release's expiry). It never asks whether the row is approved.
|
|
3
3
|
* COALESCE: a NULL basis would make the OR chain NULL, and NOT NULL reads as "not dead"; NULL stands on nothing. */
|
|
4
|
-
export declare
|
|
4
|
+
export declare function authorityStandsSql(scope: string, repo: string, now: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* SQL, conjunctive with every alternative above (results routing slice 3): a task whose sealed route tries a cheaper
|
|
7
|
+
* model (its build leg's selection is a trial) stands only on a PERSON's own yes to these exact bytes — the ceremony
|
|
8
|
+
* (`password`) or their paired-chat tap (`chat`), with that person's proof at the seal's instant, and no machine's.
|
|
9
|
+
* A mode, a grant, a schedule, a carry, an automatic seal or the lead never stand for one, whatever else allows them.
|
|
10
|
+
* The route is matched as text, so a route that can't be read as JSON is held to it too (fail closed).
|
|
11
|
+
*/
|
|
12
|
+
export declare function trialSealStandsSql(scope: string): string;
|
|
5
13
|
/**
|
|
6
14
|
* SQL, conjunctive with every alternative above (easy-start plan C5): a seal a local owner gave (its proof row at the
|
|
7
15
|
* seal's instant says `local-owner`) stands only while local mode is on, for that owner, at the local generation its
|
|
@@ -20,7 +20,8 @@ const PERSON_PROOFS = "('password-now', 'sso-now', 'fresh-password', 'fresh-sso'
|
|
|
20
20
|
/** SQL over a `task_scope` alias and a repo expression: whether that row's approval authority stands at `now` (an SQL
|
|
21
21
|
* expression, used exactly three times: the mode and grant clauses, and the release's expiry). It never asks whether the row is approved.
|
|
22
22
|
* COALESCE: a NULL basis would make the OR chain NULL, and NOT NULL reads as "not dead"; NULL stands on nothing. */
|
|
23
|
-
export
|
|
23
|
+
export function authorityStandsSql(scope, repo, now) {
|
|
24
|
+
return `COALESCE((((COALESCE(${scope}.approval_basis, '') IN ('password', 'chat') AND NOT ${machineSealSql(scope)}
|
|
24
25
|
AND (${personSealSql(scope)} OR NOT ${automationSql(scope)} OR ${approverStandsSql(scope, repo)}))
|
|
25
26
|
OR (${scope}.approval_basis = 'carried' AND ${carrySealSql(scope)})
|
|
26
27
|
OR (${scope}.approval_basis = 'mode' AND EXISTS (SELECT 1 FROM operating_mode om
|
|
@@ -30,7 +31,19 @@ export const authorityStandsSql = (scope, repo, now) => `COALESCE((((COALESCE(${
|
|
|
30
31
|
OR (${scope}.approval_basis = 'grant' AND ${grantStandsSql(`${scope}.mode_digest`, now, repo)})
|
|
31
32
|
OR (${scope}.approval_basis = 'schedule' AND ${approverStandsSql(scope, repo)})
|
|
32
33
|
OR (${scope}.approval_basis IN ('auto-check', 'auto-green') AND ${autoSealSql(scope, repo)} AND ${approverStandsSql(scope, repo)}))
|
|
33
|
-
AND ${releaseStandsSql(scope, now)} AND ${localSealStandsSql(scope, repo)}
|
|
34
|
+
AND ${releaseStandsSql(scope, now)} AND ${localSealStandsSql(scope, repo)}
|
|
35
|
+
AND (instr(COALESCE(${scope}.approved_route_json, ''), '"trial"') = 0 OR ${trialSealStandsSql(scope)})), 0)`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* SQL, conjunctive with every alternative above (results routing slice 3): a task whose sealed route tries a cheaper
|
|
39
|
+
* model (its build leg's selection is a trial) stands only on a PERSON's own yes to these exact bytes — the ceremony
|
|
40
|
+
* (`password`) or their paired-chat tap (`chat`), with that person's proof at the seal's instant, and no machine's.
|
|
41
|
+
* A mode, a grant, a schedule, a carry, an automatic seal or the lead never stand for one, whatever else allows them.
|
|
42
|
+
* The route is matched as text, so a route that can't be read as JSON is held to it too (fail closed).
|
|
43
|
+
*/
|
|
44
|
+
export function trialSealStandsSql(scope) {
|
|
45
|
+
return `(COALESCE(${scope}.approval_basis, '') IN ('password', 'chat') AND NOT ${machineSealSql(scope)} AND ${personSealSql(scope)})`;
|
|
46
|
+
}
|
|
34
47
|
/**
|
|
35
48
|
* SQL, conjunctive with every alternative above (easy-start plan C5): a seal a local owner gave (its proof row at the
|
|
36
49
|
* seal's instant says `local-owner`) stands only while local mode is on, for that owner, at the local generation its
|
|
@@ -23,7 +23,7 @@ export declare function assignmentStatusBrief(assignment: AssignmentSnapshot): {
|
|
|
23
23
|
} | null;
|
|
24
24
|
runId: number | null;
|
|
25
25
|
primaryAction: {
|
|
26
|
-
code: "answer-agent" | "
|
|
26
|
+
code: "answer-agent" | "inspect-decisions" | "inspect-run" | "inspect-stop" | "inspect-task" | "open-pr" | "recheck-stopped" | "reconcile-run" | "sign-in" | import("./dispatch.js").DispatchAction;
|
|
27
27
|
label: string;
|
|
28
28
|
target: import("./work-summary.js").WorkTarget;
|
|
29
29
|
} | null;
|
package/dist/assignment-ui.js
CHANGED
|
@@ -31,8 +31,8 @@ export function assignmentActionHref(assignment) {
|
|
|
31
31
|
if (action.code === 'sign-in')
|
|
32
32
|
return '/settings/agents#providers';
|
|
33
33
|
// The confirmation sits on the task page, behind the password.
|
|
34
|
-
if (action.code === '
|
|
35
|
-
return `/t/${encodeURIComponent(assignment.rootId)}#
|
|
34
|
+
if (action.code === 'recheck-stopped')
|
|
35
|
+
return `/t/${encodeURIComponent(assignment.rootId)}#recheck-stopped`;
|
|
36
36
|
if (action.code === 'open-pr' && /^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/pull\/\d+$/.test(assignment.publication?.prUrl ?? ''))
|
|
37
37
|
return assignment.publication.prUrl;
|
|
38
38
|
if (runId !== null && (action.code === 'open-result' || action.code === 'open-pr' || action.code === 'retry-review')) {
|
package/dist/assignment.js
CHANGED
|
@@ -252,13 +252,14 @@ export function assignmentOf(store, taskId, now, access, root, memo) {
|
|
|
252
252
|
: { code: "inspect-task", label: `Open ${successor}`, target: { taskId: successor, runId: null, decisionId: null }, access: "read", retry: "read-again" };
|
|
253
253
|
}
|
|
254
254
|
else if (!live && processFact !== null && result !== null) {
|
|
255
|
-
// Toolroll
|
|
256
|
-
//
|
|
255
|
+
// Toolroll hasn't yet proven the build stopped. It proves that itself
|
|
256
|
+
// (stop-proof.ts), so the work waits, needing no one; only when this
|
|
257
|
+
// computer can't look is it held, offering Check again — never a vouch.
|
|
257
258
|
const process = processNeedOf(processFact);
|
|
258
259
|
if ("need" in process) {
|
|
259
260
|
state = "needs-decision";
|
|
260
261
|
detail = NEEDS[process.need].sentence({ build: process.build });
|
|
261
|
-
primaryAction = { code: "
|
|
262
|
+
primaryAction = { code: "recheck-stopped", label: NEEDS[process.need].action.label, target: { taskId: current.id, runId: process.build, decisionId: null },
|
|
262
263
|
access: access.principal === "operator" ? "operator-control" : "operator-handoff", retry: "refresh-before-acting" };
|
|
263
264
|
need = { key: process.need, build: process.build };
|
|
264
265
|
}
|
|
@@ -480,6 +481,11 @@ function acknowledgeCurrent(store, current, receiptDigest, actor, now, root, acc
|
|
|
480
481
|
// The nightly check of main is a check only (release-once.md, part 3): nobody completes it.
|
|
481
482
|
if (store.handle.prepare("SELECT 1 AS hit FROM nightly_check WHERE task = ?").get(current.receipt.taskId) !== undefined)
|
|
482
483
|
return { ok: false, reason: "check-only", message: "The nightly check of main is only a check; it isn't completed." };
|
|
484
|
+
// Toolroll is still proving its build stopped (stop-proof.ts): a wait that clears on its own, said as one, so a
|
|
485
|
+
// release waits for it instead of stopping.
|
|
486
|
+
if (current.state !== "ready-to-check" && current.state !== "complete" && current.need != null && "wait" in current.need && STOP_WAITS.has(current.need.wait)) {
|
|
487
|
+
return { ok: false, reason: "stopping", message: `Toolroll is still making sure ${current.need.build === null ? "the build" : `build #${current.need.build}`} stopped. This clears on its own.` };
|
|
488
|
+
}
|
|
483
489
|
if (current.state !== "ready-to-check" && current.state !== "complete")
|
|
484
490
|
return { ok: false, reason: "not-ready", message: "This assignment still has unresolved execution, scope or decisions." };
|
|
485
491
|
// A release task completes only while its release approval stands for it (release-once.md, liveReleaseOf).
|
|
@@ -674,3 +680,5 @@ export function assignmentUpdates(store, now, access, query, root) {
|
|
|
674
680
|
});
|
|
675
681
|
return { events, nextCursor: page.length === 0 ? after : Number(page.at(-1)["id"]), hasMore: rows.length > limit };
|
|
676
682
|
}
|
|
683
|
+
/** The waits of a build Toolroll is still proving stopped by itself (needs-you.ts processNeedOf). */
|
|
684
|
+
const STOP_WAITS = new Set(["build-finishing", "build-checking", "build-stopping"]);
|