toolroll 0.9.93 → 0.9.95
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 +79 -0
- package/dist/activity-line.d.ts +7 -4
- package/dist/activity-line.js +9 -7
- package/dist/agent-fence.d.ts +10 -0
- package/dist/agent-fence.js +6 -3
- package/dist/approval-authority.d.ts +7 -1
- package/dist/approval-authority.js +11 -2
- package/dist/approval-password.d.ts +31 -0
- package/dist/approval-password.js +26 -0
- package/dist/assignment-status.d.ts +1 -1
- package/dist/assignment-ui.js +3 -1
- package/dist/assignment.js +5 -0
- package/dist/attest.d.ts +3 -0
- package/dist/attest.js +10 -0
- package/dist/boot-identity.d.ts +2 -1
- package/dist/boot-identity.js +6 -4
- package/dist/bounded-spawn.d.ts +57 -0
- package/dist/bounded-spawn.js +220 -0
- package/dist/browser/terminal.js +42 -42
- package/dist/browser/workspace.css +1 -1
- package/dist/browser/workspace.js +32 -32
- package/dist/browser-crew.js +2 -0
- package/dist/browser-shell.d.ts +1 -1
- package/dist/browser-shell.js +2 -2
- package/dist/browser-workspace.d.ts +50 -2
- package/dist/browser-workspace.js +3 -3
- package/dist/build-review.js +2 -0
- package/dist/builder-restart.d.ts +21 -0
- package/dist/builder-restart.js +50 -0
- package/dist/builder.d.ts +11 -2
- package/dist/builder.js +234 -101
- package/dist/card-catalog.d.ts +209 -0
- package/dist/card-catalog.js +133 -0
- package/dist/card-cli.d.ts +43 -0
- package/dist/card-cli.js +44 -0
- package/dist/card-render.d.ts +26 -0
- package/dist/card-render.js +223 -0
- package/dist/card-sources.d.ts +68 -0
- package/dist/card-sources.js +234 -0
- package/dist/chat-acceptance.js +1 -1
- package/dist/chat-actions.d.ts +106 -9
- package/dist/chat-actions.js +473 -46
- package/dist/chat-approval.d.ts +13 -0
- package/dist/chat-approval.js +39 -9
- package/dist/chat-card-part.d.ts +11 -0
- package/dist/chat-card-part.js +18 -0
- package/dist/chat-card.d.ts +46 -0
- package/dist/chat-card.js +99 -0
- package/dist/chat-channel.d.ts +2 -0
- package/dist/chat-channel.js +37 -14
- package/dist/chat-controls.d.ts +4 -0
- package/dist/chat-controls.js +4 -0
- package/dist/chat-decide.d.ts +10 -16
- package/dist/chat-decide.js +79 -62
- package/dist/chat-delivery.js +13 -4
- package/dist/chat-flow.d.ts +0 -27
- package/dist/chat-flow.js +31 -2
- package/dist/chat-quiet.js +4 -4
- package/dist/chat-task-actions.d.ts +3 -2
- package/dist/chat-task-actions.js +19 -5
- package/dist/chat-terms.d.ts +14 -2
- package/dist/chat-terms.js +35 -0
- package/dist/chat-voice.d.ts +1 -1
- package/dist/chat-voice.js +2 -2
- package/dist/check-facts.d.ts +3 -1
- package/dist/check-facts.js +57 -11
- package/dist/check-failure.d.ts +24 -3
- package/dist/check-failure.js +89 -13
- package/dist/check-levels-ui.d.ts +4 -0
- package/dist/check-levels-ui.js +10 -1
- package/dist/check-load.d.ts +85 -0
- package/dist/check-load.js +147 -0
- package/dist/check-only-standing.d.ts +36 -0
- package/dist/check-only-standing.js +39 -0
- package/dist/check-only.d.ts +15 -0
- package/dist/check-only.js +99 -0
- package/dist/check-repair.d.ts +47 -0
- package/dist/check-repair.js +240 -0
- package/dist/check-report.d.ts +2 -0
- package/dist/check-report.js +1 -1
- package/dist/check-stamp.d.ts +46 -0
- package/dist/check-stamp.js +93 -0
- package/dist/checkout-cleanup.d.ts +36 -5
- package/dist/checkout-cleanup.js +166 -14
- package/dist/claim.d.ts +6 -0
- package/dist/claim.js +13 -2
- package/dist/cli-help.d.ts +4 -1
- package/dist/cli-help.js +49 -30
- package/dist/cli.js +15 -1
- package/dist/code-extract.d.ts +44 -0
- package/dist/code-extract.js +88 -0
- package/dist/code-map-acts.d.ts +24 -0
- package/dist/code-map-acts.js +53 -0
- package/dist/code-map-admission.d.ts +52 -0
- package/dist/code-map-admission.js +295 -0
- package/dist/code-map-build.d.ts +63 -0
- package/dist/code-map-build.js +415 -0
- package/dist/code-map-cli.d.ts +153 -0
- package/dist/code-map-cli.js +103 -0
- package/dist/code-map-main.d.ts +1 -0
- package/dist/code-map-main.js +48 -0
- package/dist/code-map-read.d.ts +106 -0
- package/dist/code-map-read.js +171 -0
- package/dist/code-map-report.d.ts +37 -0
- package/dist/code-map-report.js +95 -0
- package/dist/code-map-shape.d.ts +172 -0
- package/dist/code-map-shape.js +327 -0
- package/dist/code-map-store.d.ts +136 -0
- package/dist/code-map-store.js +233 -0
- package/dist/coding-provider.d.ts +3 -1
- package/dist/coding-provider.js +12 -7
- package/dist/coding-update.js +3 -2
- package/dist/coding-workspace.d.ts +1 -1
- package/dist/coding-workspace.js +8 -8
- package/dist/console-chrome.js +2 -2
- package/dist/containment.d.ts +5 -0
- package/dist/containment.js +14 -1
- package/dist/contracts/chat-actions.d.ts +92 -0
- package/dist/contracts/chat-actions.js +20 -0
- package/dist/contracts/chat-card.d.ts +91 -0
- package/dist/contracts/chat-card.js +54 -0
- package/dist/contracts/chat-content.d.ts +191 -0
- package/dist/contracts/chat-content.js +4 -0
- package/dist/contracts/cli.d.ts +52 -0
- package/dist/contracts/cli.js +4 -0
- package/dist/contracts/console-api.d.ts +12 -3
- package/dist/contracts/console-api.js +12 -3
- package/dist/contracts/flow-propose.d.ts +8 -1
- package/dist/contracts/flow-propose.js +3 -1
- package/dist/contracts/flow-send.d.ts +126 -0
- package/dist/contracts/flow-send.js +3 -0
- package/dist/contracts/gateway-tools.d.ts +17 -0
- package/dist/contracts/gateway-tools.js +3 -1
- package/dist/contracts/lead-tools.d.ts +234 -0
- package/dist/contracts/lead-tools.js +25 -1
- package/dist/contracts/project-knowledge.d.ts +38 -5
- package/dist/contracts/project-knowledge.js +11 -5
- package/dist/contracts/project-memory.d.ts +3 -1
- package/dist/contracts/project-memory.js +3 -2
- package/dist/contracts/storage.d.ts +14 -0
- package/dist/contracts/storage.js +3 -1
- package/dist/contracts/store-json.d.ts +18 -0
- package/dist/contracts/youiyoux-request.d.ts +1 -1
- package/dist/contracts/youiyoux-request.js +1 -1
- package/dist/desktop-access.js +6 -2
- package/dist/discord-chat.js +14 -0
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +25 -16
- package/dist/dispose.d.ts +19 -0
- package/dist/dispose.js +55 -9
- package/dist/easy-start.js +2 -3
- package/dist/eval-oracle.js +4 -1
- package/dist/evals.js +2 -1
- package/dist/evidence-pair.d.ts +53 -0
- package/dist/evidence-pair.js +128 -0
- package/dist/exhaustion.d.ts +17 -4
- package/dist/exhaustion.js +44 -4
- package/dist/export.js +1 -1
- package/dist/failing-first.d.ts +31 -0
- package/dist/failing-first.js +44 -0
- package/dist/fallback-schema.d.ts +8 -0
- package/dist/fallback-schema.js +40 -0
- package/dist/fallback.d.ts +138 -0
- package/dist/fallback.js +605 -0
- package/dist/fast-start.d.ts +136 -0
- package/dist/fast-start.js +431 -0
- package/dist/fast-terms.d.ts +9 -0
- package/dist/fast-terms.js +23 -0
- package/dist/fenced-check-coverage.d.ts +62 -0
- package/dist/fenced-check-coverage.js +54 -0
- package/dist/findings.d.ts +224 -0
- package/dist/findings.js +449 -0
- package/dist/first-run.js +1 -1
- package/dist/flaky-ci.d.ts +31 -0
- package/dist/flaky-ci.js +149 -0
- package/dist/flaky-cli.d.ts +60 -0
- package/dist/flaky-cli.js +60 -0
- package/dist/flaky.d.ts +75 -9
- package/dist/flaky.js +255 -0
- package/dist/fleet-schema.d.ts +17 -0
- package/dist/fleet-schema.js +135 -0
- package/dist/flow-browser.js +2 -2
- package/dist/flow-card-task.js +3 -0
- package/dist/flow-code.d.ts +2 -2
- package/dist/flow-code.js +9 -5
- package/dist/flow-result-card.d.ts +22 -0
- package/dist/flow-result-card.js +55 -0
- package/dist/flow-send.js +12 -9
- package/dist/flow-steps.js +1 -1
- package/dist/flow-triggers.d.ts +1 -1
- package/dist/flow-triggers.js +2 -2
- package/dist/flow-unattended.d.ts +26 -0
- package/dist/flow-unattended.js +12 -3
- package/dist/flows-ui.js +10 -2
- package/dist/follow-through-brief.d.ts +12 -0
- package/dist/follow-through-brief.js +31 -0
- package/dist/follow-through-cli.d.ts +23 -0
- package/dist/follow-through-cli.js +153 -0
- package/dist/git-incident.d.ts +16 -0
- package/dist/git-incident.js +69 -6
- package/dist/github-import.d.ts +62 -0
- package/dist/github-import.js +146 -0
- package/dist/guides.js +15 -10
- package/dist/handover-note.d.ts +33 -0
- package/dist/handover-note.js +106 -0
- package/dist/invoke.d.ts +25 -2
- package/dist/invoke.js +157 -58
- package/dist/job-object-helper.ps1 +10 -3
- package/dist/keymap.d.ts +41 -0
- package/dist/keymap.js +51 -0
- package/dist/knowledge-cli.js +1 -1
- package/dist/knowledge-ui.d.ts +21 -4
- package/dist/knowledge-ui.js +75 -7
- package/dist/laptop-background.d.ts +16 -0
- package/dist/laptop-background.js +108 -2
- package/dist/lead-cli.js +26 -24
- package/dist/lead-context.js +1 -1
- package/dist/lead-contract.d.ts +1 -1
- package/dist/lead-contract.js +3 -2
- package/dist/lead-doors.d.ts +4 -0
- package/dist/lead-doors.js +2 -2
- package/dist/lead-progress.js +5 -0
- package/dist/lead-status.d.ts +2 -0
- package/dist/lead-status.js +6 -2
- package/dist/lead-tools.js +162 -34
- package/dist/lead.js +2 -2
- package/dist/lesson-notes.d.ts +81 -0
- package/dist/lesson-notes.js +336 -0
- package/dist/limits-ui.js +1 -1
- package/dist/local-owner.d.ts +1 -1
- package/dist/local-owner.js +16 -0
- package/dist/machine-check.d.ts +16 -0
- package/dist/machine-check.js +26 -0
- package/dist/machine-identity.d.ts +75 -0
- package/dist/machine-identity.js +170 -0
- package/dist/mcp-core.js +11 -1
- package/dist/memory-cli.d.ts +300 -1
- package/dist/memory-cli.js +62 -9
- package/dist/memory-pass.d.ts +16 -0
- package/dist/memory-pass.js +67 -3
- package/dist/memory-tidy.d.ts +181 -0
- package/dist/memory-tidy.js +523 -0
- package/dist/memory-use.d.ts +35 -0
- package/dist/memory-use.js +109 -0
- package/dist/merge-checklist.d.ts +55 -0
- package/dist/merge-checklist.js +182 -0
- package/dist/modes.d.ts +6 -6
- package/dist/modes.js +3 -1
- package/dist/needs-you.d.ts +31 -9
- package/dist/needs-you.js +110 -41
- package/dist/nightly-github.d.ts +2 -0
- package/dist/nightly-github.js +4 -4
- package/dist/operate-remote-arguments.js +7 -1
- package/dist/operate-remote.js +3 -1
- package/dist/operate.d.ts +6 -1
- package/dist/operate.js +565 -64
- package/dist/phase-routing.js +2 -2
- package/dist/plain-schema.d.ts +23 -0
- package/dist/plain-schema.js +94 -0
- package/dist/plain-words.d.ts +20 -0
- package/dist/plain-words.js +66 -0
- package/dist/plan-login.d.ts +14 -0
- package/dist/plan-login.js +58 -0
- package/dist/planner.d.ts +1 -1
- package/dist/planner.js +4 -2
- package/dist/process-liveness.d.ts +2 -0
- package/dist/process-liveness.js +61 -17
- package/dist/project-delete-ui.d.ts +19 -2
- package/dist/project-delete-ui.js +29 -1
- package/dist/project-delete.js +14 -1
- package/dist/project-knowledge.d.ts +18 -11
- package/dist/project-knowledge.js +39 -9
- package/dist/project-learning.d.ts +2 -1
- package/dist/project-learning.js +15 -45
- package/dist/project-memory.d.ts +5 -0
- package/dist/project-memory.js +22 -2
- package/dist/project-tools.d.ts +4 -4
- package/dist/project-tools.js +23 -7
- package/dist/proof.js +30 -5
- package/dist/protected-storage-look.d.ts +1 -0
- package/dist/protected-storage-look.js +13 -0
- package/dist/protected-storage.d.ts +70 -39
- package/dist/protected-storage.js +196 -147
- package/dist/provider-auth.js +1 -1
- package/dist/provider-capture.d.ts +34 -0
- package/dist/provider-capture.js +53 -0
- package/dist/publish.js +9 -1
- package/dist/pull-request-flow.js +5 -0
- package/dist/push.d.ts +18 -2
- package/dist/push.js +40 -16
- package/dist/release-adapters.d.ts +9 -0
- package/dist/release-adapters.js +76 -0
- package/dist/release-auto.js +13 -13
- package/dist/release-card.d.ts +1 -2
- package/dist/release-card.js +11 -0
- package/dist/release-carry.d.ts +7 -2
- package/dist/release-carry.js +49 -10
- package/dist/release-cli.d.ts +4 -0
- package/dist/release-cli.js +29 -22
- package/dist/release-coverage.js +1 -1
- package/dist/release-next.d.ts +48 -0
- package/dist/release-next.js +161 -0
- package/dist/release.d.ts +20 -1
- package/dist/release.js +85 -9
- package/dist/remote-destination.d.ts +36 -0
- package/dist/remote-destination.js +217 -0
- package/dist/remote-main.d.ts +48 -0
- package/dist/remote-main.js +84 -5
- package/dist/repository-context.d.ts +3 -2
- package/dist/repository-context.js +112 -70
- package/dist/restart-certification.d.ts +1 -0
- package/dist/restart-certification.js +8 -6
- package/dist/result-acts.d.ts +27 -19
- package/dist/result-acts.js +36 -25
- package/dist/result-follow-ups.d.ts +2 -2
- package/dist/result-follow-ups.js +18 -4
- package/dist/result-review.d.ts +1 -1
- package/dist/route-change-check.d.ts +4 -0
- package/dist/route-change-check.js +13 -0
- package/dist/route-change.d.ts +22 -0
- package/dist/route-change.js +89 -0
- package/dist/route-hooks.js +3 -2
- package/dist/route-results.d.ts +6 -1
- package/dist/route-results.js +14 -5
- package/dist/run-closeout.d.ts +1 -1
- package/dist/run-closeout.js +7 -5
- package/dist/runner-process.js +3 -2
- package/dist/runner.js +2 -1
- package/dist/scope.d.ts +28 -4
- package/dist/scope.js +92 -28
- package/dist/scout.d.ts +1 -1
- package/dist/scout.js +2 -1
- package/dist/serve.d.ts +1 -1
- package/dist/serve.js +26 -18
- package/dist/server/chat.js +16 -14
- package/dist/server/chrome.d.ts +1 -1
- package/dist/server/chrome.js +26 -35
- package/dist/server/console.css +73 -0
- package/dist/server/guards.js +3 -0
- package/dist/server/home.d.ts +6 -0
- package/dist/server/home.js +21 -13
- package/dist/server/http.js +1 -1
- package/dist/server/kit.js +1 -1
- package/dist/server/merge-ready-view.d.ts +10 -0
- package/dist/server/merge-ready-view.js +30 -0
- package/dist/server/pages-assets.d.ts +1 -0
- package/dist/server/pages-assets.js +8 -5
- package/dist/server/pages.js +103 -82
- package/dist/server/render-chat.js +16 -3
- package/dist/server/render-code-map.d.ts +30 -0
- package/dist/server/render-code-map.js +137 -0
- package/dist/server/render-flaky.d.ts +20 -0
- package/dist/server/render-flaky.js +59 -0
- package/dist/server/render-follow-through.d.ts +11 -0
- package/dist/server/render-follow-through.js +68 -0
- package/dist/server/render-history.js +1 -1
- package/dist/server/render-new-task.d.ts +26 -1
- package/dist/server/render-new-task.js +30 -5
- package/dist/server/render-pages.d.ts +1 -175
- package/dist/server/render-pages.js +38 -313
- package/dist/server/render-projects.d.ts +2 -1
- package/dist/server/render-projects.js +9 -7
- package/dist/server/render-release.d.ts +2 -0
- package/dist/server/render-release.js +3 -3
- package/dist/server/render-results.d.ts +2 -0
- package/dist/server/render-results.js +68 -68
- package/dist/server/render-tasks.d.ts +16 -2
- package/dist/server/render-tasks.js +177 -141
- package/dist/server/retired-routes.d.ts +17 -0
- package/dist/server/retired-routes.js +53 -0
- package/dist/server/route-table.d.ts +3 -0
- package/dist/server/route-table.js +48 -12
- package/dist/server/settings.js +166 -34
- package/dist/server/task-views.js +66 -26
- package/dist/server/tasks.d.ts +2 -6
- package/dist/server/tasks.js +250 -321
- package/dist/server/welcome.js +1 -1
- package/dist/settings-groups-ui.d.ts +1 -0
- package/dist/settings-groups-ui.js +4 -2
- package/dist/settings-kit.js +3 -2
- package/dist/settings-proofs.js +3 -1
- package/dist/slack-chat.js +14 -1
- package/dist/steer-mailbox.d.ts +8 -1
- package/dist/steer-mailbox.js +164 -6
- package/dist/stop-proof.js +6 -4
- package/dist/storage-sweep-process.js +1 -1
- package/dist/storage-sweep.d.ts +5 -3
- package/dist/storage-sweep.js +30 -9
- package/dist/storage-ui.js +28 -17
- package/dist/storage.d.ts +27 -2
- package/dist/storage.js +39 -2
- package/dist/store.d.ts +89 -7
- package/dist/store.js +386 -114
- package/dist/surface.js +67 -6
- package/dist/sweep-quarantine.d.ts +1 -0
- package/dist/sweep-quarantine.js +6 -4
- package/dist/tamper-scan.d.ts +4 -0
- package/dist/tamper-scan.js +90 -0
- package/dist/task-aliases.d.ts +5 -0
- package/dist/task-aliases.js +9 -0
- package/dist/task-control.d.ts +2 -1
- package/dist/task-control.js +4 -1
- package/dist/task-outcome-cli.d.ts +2 -0
- package/dist/task-outcome-cli.js +17 -17
- package/dist/task-sizing.d.ts +5 -0
- package/dist/task-sizing.js +2 -0
- package/dist/task-source.d.ts +7 -0
- package/dist/task-source.js +14 -0
- package/dist/task-status.d.ts +9 -0
- package/dist/task-status.js +45 -49
- package/dist/teardown-git.d.ts +18 -0
- package/dist/teardown-git.js +105 -21
- package/dist/telegram-lead.js +23 -6
- package/dist/telegram-progress.js +5 -3
- package/dist/telegram-status.js +6 -4
- package/dist/telegram.js +12 -1
- package/dist/test-infra.d.ts +8 -0
- package/dist/test-infra.js +18 -0
- package/dist/tidy.js +1 -1
- package/dist/toolroll-update.js +19 -5
- package/dist/tui-contract.d.ts +24 -0
- package/dist/tui-source.js +67 -8
- package/dist/tui.js +51 -51
- package/dist/vanished-retry.d.ts +30 -0
- package/dist/vanished-retry.js +174 -0
- package/dist/work-index.d.ts +2 -0
- package/dist/work-index.js +95 -66
- package/dist/work-summary.d.ts +1 -1
- package/dist/work-summary.js +7 -2
- package/dist/workspace-ui.d.ts +7 -3
- package/dist/workspace-ui.js +38 -23
- package/dist/worktree.d.ts +12 -3
- package/dist/worktree.js +91 -14
- package/package.json +2 -1
- package/dist/server/render-next.d.ts +0 -63
- package/dist/server/render-next.js +0 -109
package/dist/dispose.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
// Assignment tracking grants no repair authority or attempts.
|
|
2
|
+
import { STORAGE_HOLD_PREFIX } from "./needs-you.js";
|
|
3
|
+
import { blockAfterSignedOut } from "./fallback.js";
|
|
4
|
+
import { authorizeCheckOnly } from "./check-only.js";
|
|
5
|
+
import { proposeNewRoute } from "./route-change.js";
|
|
2
6
|
import { originalTaskBase, focusedTestCommandSupported } from "./observations.js";
|
|
3
7
|
/**
|
|
4
8
|
* The build disposition service (Parity II Phase 2, v4 Q2 / v6 W1): the
|
|
@@ -18,6 +22,7 @@ import { bodyHashOf, publicationBody } from "./publish.js";
|
|
|
18
22
|
import { modeTermsFromJson } from "./modes.js";
|
|
19
23
|
import { evidenceRoot, readVerifiedArtifact, writeEvidenceFile } from "./evidence.js";
|
|
20
24
|
import { queueBuildReview } from "./build-review.js";
|
|
25
|
+
import { followBuiltRun } from "./check-repair.js";
|
|
21
26
|
import { homedir } from "node:os";
|
|
22
27
|
import { nightlyTaskRef, releaseTaskRef } from "./store.js";
|
|
23
28
|
import {} from "./proof.js";
|
|
@@ -158,6 +163,8 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
158
163
|
});
|
|
159
164
|
// A stated no-change publishes nothing.
|
|
160
165
|
if (result.noChange !== true && result.committed) {
|
|
166
|
+
// The approval that built this commit allows one automatic recheck of it, should its verdict go missing.
|
|
167
|
+
authorizeCheckOnly(store, runId, clock());
|
|
161
168
|
const grant = store.publicationGrantFor(repo);
|
|
162
169
|
const headSha = store.getRun(runId)?.headRevision ?? null;
|
|
163
170
|
if (grant !== null &&
|
|
@@ -198,6 +205,7 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
198
205
|
store.resetStrikes(taskRef);
|
|
199
206
|
store.clearQuota(runner, provider, model ?? "");
|
|
200
207
|
maybeRequestAutoReview(store, repo, runId, result.committed, result.noChange === true, clock(), context.evidenceRoot);
|
|
208
|
+
followBuiltRun(store, repo, context.evidenceRoot ?? evidenceRoot(homedir()), runId, worktreePath, clock());
|
|
201
209
|
return { kind: "built", committed: result.committed, noChange: result.noChange === true };
|
|
202
210
|
}
|
|
203
211
|
store.transact(() => {
|
|
@@ -327,11 +335,26 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
327
335
|
sealed: sealed.ok,
|
|
328
336
|
};
|
|
329
337
|
}
|
|
338
|
+
if (result.reason === "protected-storage") {
|
|
339
|
+
// The storage check couldn't finish (protected-storage.ts): nothing spent,
|
|
340
|
+
// no strike. The words are kept on the run, the task holds and tries
|
|
341
|
+
// again on its own, and a person reads why and which program to allow.
|
|
342
|
+
if (leaseId !== undefined)
|
|
343
|
+
release(store, leaseId, clock());
|
|
344
|
+
if (result.message.trim() !== "")
|
|
345
|
+
store.recordOutcomeFacts(runId, { handoff: result.message });
|
|
346
|
+
store.finishRun(runId, { outcome: "refused", reason: "protected-storage", now: clock() });
|
|
347
|
+
holdForStorage(store, { taskRef, taskId, message: result.message }, clock());
|
|
348
|
+
return { kind: "skipped", reason: "protected-storage" };
|
|
349
|
+
}
|
|
330
350
|
// no-claim, not-yours, not-leased, protected-branch, wrong-branch:
|
|
331
351
|
// invariants the dispatcher was supposed to uphold.
|
|
332
352
|
if (leaseId !== undefined)
|
|
333
353
|
release(store, leaseId, clock());
|
|
334
354
|
store.finishRun(runId, { outcome: "refused", reason: result.reason, now: clock() });
|
|
355
|
+
// A fallback agent not signed in to its plan: the task waits for a person's retry, never loops (fallback.ts).
|
|
356
|
+
if (result.reason === "auth-mode" && blockAfterSignedOut(store, runId, clock()))
|
|
357
|
+
return { kind: "skipped", reason: "fallback-signed-out" };
|
|
335
358
|
if (result.reason === "stale-approval") {
|
|
336
359
|
// A deterministic refusal (setup review): the approval no longer
|
|
337
360
|
// matches the routing, and nothing about the next pass changes that.
|
|
@@ -347,6 +370,27 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
347
370
|
}
|
|
348
371
|
return { kind: "invariant", reason: result.reason };
|
|
349
372
|
}
|
|
373
|
+
/** How long a task waits after the storage check couldn't finish before it tries again on its own. */
|
|
374
|
+
export const STORAGE_RETRY_MS = 10 * 60_000;
|
|
375
|
+
export { STORAGE_HOLD_PREFIX };
|
|
376
|
+
/**
|
|
377
|
+
* The storage HOLD: the check couldn't finish, so no launch can be fenced
|
|
378
|
+
* now. The task waits STORAGE_RETRY_MS and tries again on its own (a person
|
|
379
|
+
* who allowed access in Privacy & Security needn't do more), and "Try again"
|
|
380
|
+
* (unhold) lifts it at once. One message per task an hour, with the words.
|
|
381
|
+
*/
|
|
382
|
+
export function holdForStorage(store, args, now) {
|
|
383
|
+
store.holdOwned({ taskRef: args.taskRef, ownerKind: "backoff", ownerId: `storage:${args.taskRef}`, reason: `${STORAGE_HOLD_PREFIX}${args.message}`, until: new Date(now.getTime() + STORAGE_RETRY_MS) }, now);
|
|
384
|
+
store.enqueueNotification({
|
|
385
|
+
source: { taskRef: args.taskRef },
|
|
386
|
+
dedupeKey: `protected-storage:${args.taskRef}:${now.toISOString().slice(0, 13)}`,
|
|
387
|
+
kind: "protected-storage",
|
|
388
|
+
pushClass: "attention",
|
|
389
|
+
link: `/t/${encodeURIComponent(args.taskId)}`,
|
|
390
|
+
subject: `${args.taskId}: waiting for macOS permission`,
|
|
391
|
+
body: `${args.message}\nIt tries again on its own in ${STORAGE_RETRY_MS / 60_000} minutes.`,
|
|
392
|
+
}, now);
|
|
393
|
+
}
|
|
350
394
|
/**
|
|
351
395
|
* The stale-approval HOLD, shared by the post-build disposition above and
|
|
352
396
|
* the tick's pre-admission refusal (v48 integrity): a pre-routing row
|
|
@@ -357,6 +401,8 @@ function disposeBuildOutcomeLocked(context, result) {
|
|
|
357
401
|
*/
|
|
358
402
|
export function holdStaleApproval(store, args, now) {
|
|
359
403
|
const scope = store.getScope(args.taskId);
|
|
404
|
+
// Toolroll proposes the new exact route itself (route-change.ts): the person's one act is Approve, on one line.
|
|
405
|
+
const proposed = proposeNewRoute(store, args.taskRef, args.taskId, now);
|
|
360
406
|
store.holdOwned({
|
|
361
407
|
taskRef: args.taskRef,
|
|
362
408
|
ownerKind: "backoff",
|
|
@@ -370,8 +416,8 @@ export function holdStaleApproval(store, args, now) {
|
|
|
370
416
|
kind: "stale-approval",
|
|
371
417
|
pushClass: "attention",
|
|
372
418
|
link: `/t/${encodeURIComponent(args.taskId)}`,
|
|
373
|
-
subject: `${args.taskId}: its
|
|
374
|
-
body: `${args.message}\nNothing runs on it until
|
|
419
|
+
subject: `${args.taskId}: its agents changed — approve it again`,
|
|
420
|
+
body: `${proposed?.words ?? args.message}\nNothing runs on it until it's approved again.`,
|
|
375
421
|
}, now);
|
|
376
422
|
}
|
|
377
423
|
// ---- the bounded repair loop (v40, evidence-review-v1) --------------------
|
|
@@ -538,9 +584,9 @@ export function regateTask(store, taskId, now, approval) {
|
|
|
538
584
|
export function maybeTriggerRepair(store, repo, evidenceRoot, sourceRunId, verdict, now, cause = "review", automatic = false) {
|
|
539
585
|
if (verdict !== "short" && verdict !== "refuted")
|
|
540
586
|
return { kind: "none" };
|
|
541
|
-
//
|
|
542
|
-
//
|
|
543
|
-
if (automatic)
|
|
587
|
+
// A review's findings go to the lead, and a person asks for their repair. A failed check is the one automatic road
|
|
588
|
+
// (check-repair.ts), and only under a mode a person signed for it, checked below.
|
|
589
|
+
if (automatic && cause !== "verification")
|
|
544
590
|
return { kind: "none" };
|
|
545
591
|
// One attempt per source run, ever (source_run UNIQUE) — checked first so
|
|
546
592
|
// a re-fired trigger is a silent no-op, never a duplicate.
|
|
@@ -598,9 +644,9 @@ export function maybeTriggerRepair(store, repo, evidenceRoot, sourceRunId, verdi
|
|
|
598
644
|
const terms = mode === null ? null : modeTermsFromJson(mode.termsJson);
|
|
599
645
|
let verification;
|
|
600
646
|
if (cause === "verification") {
|
|
601
|
-
// A signed repair mode is required for this automatic producer.
|
|
602
|
-
//
|
|
603
|
-
if (terms?.repairAuto !== true || run.finishedAt === null || !run.headRevision ||
|
|
647
|
+
// A signed repair mode is required for this automatic producer. A person
|
|
648
|
+
// asking (check-repair.ts startCheckRepair) gets an unapproved draft instead.
|
|
649
|
+
if ((automatic && terms?.repairAuto !== true) || run.finishedAt === null || !run.headRevision ||
|
|
604
650
|
(run.outcome !== "built" && run.outcome !== "no-change"))
|
|
605
651
|
return { kind: "none" };
|
|
606
652
|
const owner = store.refForId(run.taskRef);
|
|
@@ -633,7 +679,7 @@ export function maybeTriggerRepair(store, repo, evidenceRoot, sourceRunId, verdi
|
|
|
633
679
|
subject: unrelated.kind === "gate-timed-out" ? "The project check ran out of time; running it once more" : "A slow test outside this change failed the check; running it once more",
|
|
634
680
|
body: `${gateFailureSummary(unrelated)} The check runs again once on the same commit ${run.headRevision.slice(0, 7)}, with no agent. If it fails again, a person decides.`,
|
|
635
681
|
link: `/t/${encodeURIComponent(taskId)}` }, now);
|
|
636
|
-
return { kind: "
|
|
682
|
+
return { kind: "rerun" };
|
|
637
683
|
}
|
|
638
684
|
}
|
|
639
685
|
store.enqueueNotification({ source: { run: sourceRunId }, dedupeKey: `repair-unrelated:${sourceRunId}`, kind: "repair-evidence", pushClass: "attention",
|
package/dist/easy-start.js
CHANGED
|
@@ -284,9 +284,8 @@ export function defaultEasyStartSeams(options) {
|
|
|
284
284
|
}
|
|
285
285
|
},
|
|
286
286
|
findRepo: async (dir) => {
|
|
287
|
-
const {
|
|
288
|
-
|
|
289
|
-
return top.code === 0 ? top.stdout.trim() : null;
|
|
287
|
+
const { checkoutTop } = await import("./teardown-git.js");
|
|
288
|
+
return checkoutTop(dir);
|
|
290
289
|
},
|
|
291
290
|
enrolled: async (repo) => {
|
|
292
291
|
const { loadRepos } = await import("./repos.js");
|
package/dist/eval-oracle.js
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* The build's code never runs in the oracle's own process, and nothing the build wrote (a result file, a verdict) is
|
|
20
20
|
* ever read as one: the verdict travels from the oracle to the runner over the oracle's own pipe.
|
|
21
21
|
*/
|
|
22
|
+
import { prepareProtectedStorage } from "./protected-storage.js";
|
|
22
23
|
import { spawn } from "node:child_process";
|
|
23
24
|
import { createHash, randomBytes } from "node:crypto";
|
|
24
25
|
import { closeSync, constants, lstatSync, mkdirSync, mkdtempSync, openSync, realpathSync, rmSync, writeSync } from "node:fs";
|
|
@@ -55,10 +56,12 @@ export function canonical(value) {
|
|
|
55
56
|
export const fencedGradeRunner = async ({ cwd, stdin, timeoutMs, deny }) => {
|
|
56
57
|
if (!macosFenceAvailable() && !linuxFenceAvailable())
|
|
57
58
|
throw new Error("this machine can't fence the graded program");
|
|
59
|
+
// The storage check, awaited off this thread (protected-storage.ts); a refusal keeps its words.
|
|
60
|
+
const storage = await prepareProtectedStorage();
|
|
58
61
|
const egress = await startEgressProxy({ hosts: [] });
|
|
59
62
|
try {
|
|
60
63
|
const network = { mode: "allowlist", proxyPort: egress.port, brokerPorts: [], loopback: "proxy-only", socketRoots: [cwd], token: egress.secret };
|
|
61
|
-
const launch = fenceFor("check", { databaseFile: null, worktree: cwd, boundary: null, binding: null, runFolders: runFolders(null), poolRoots: [], extra: [...deny],
|
|
64
|
+
const launch = fenceFor("check", { databaseFile: null, worktree: cwd, boundary: null, binding: null, runFolders: runFolders(null), poolRoots: [], storage, extra: [...deny],
|
|
62
65
|
writes: { writes: { subpaths: [cwd], prefixes: [] }, readOnly: [] }, network });
|
|
63
66
|
const result = await run(process.execPath, ["probe.mjs"], { cwd, stdin, timeoutMs, maxBuffer: ORACLE_OUTPUT_BYTES, processGroup: true, envAllowlist: ["PATH"],
|
|
64
67
|
env: { ...proxyEnv(egress, "proxy-only"), HOME: cwd, TMPDIR: cwd }, fence: launch.fence, fenceGrants: launch.fenceGrants, confinement: "required" });
|
package/dist/evals.js
CHANGED
|
@@ -27,6 +27,7 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
27
27
|
import { closeSync, constants, lstatSync, mkdirSync, mkdtempSync, openSync, readdirSync, realpathSync, rmSync, writeSync } from "node:fs";
|
|
28
28
|
import { dirname, join, relative, sep } from "node:path";
|
|
29
29
|
import { homedir, hostname } from "node:os";
|
|
30
|
+
import { sameMachineHost } from "./machine-identity.js";
|
|
30
31
|
import { fileURLToPath } from "node:url";
|
|
31
32
|
import { createRequire } from "node:module";
|
|
32
33
|
import { readVerifiedCommit, verifiedBlobs } from "./commit-extract.js";
|
|
@@ -840,7 +841,7 @@ export function recoverEvalCampaigns(store, now, options = {}) {
|
|
|
840
841
|
const out = [];
|
|
841
842
|
for (const campaign of store.handle.prepare("SELECT id, runner_host, runner_pid, runner_started FROM eval_campaign WHERE state = 'running' ORDER BY admitted_at").all()) {
|
|
842
843
|
const id = String(campaign["id"]);
|
|
843
|
-
if (String(campaign["runner_host"])
|
|
844
|
+
if (!sameMachineHost(String(campaign["runner_host"]), host)) {
|
|
844
845
|
out.push({ id, recovered: false, why: `its runner is on ${String(campaign["runner_host"])}` });
|
|
845
846
|
continue;
|
|
846
847
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Store } from "./store.js";
|
|
2
|
+
export type EvidenceReading = "failing" | "fixed" | "still-failing" | "check-changed" | "out-of-date";
|
|
3
|
+
export type EvidenceSide = {
|
|
4
|
+
run: number;
|
|
5
|
+
head: string;
|
|
6
|
+
status: "passed" | "failed";
|
|
7
|
+
via: "build" | "follow-up";
|
|
8
|
+
screenshots: number[];
|
|
9
|
+
};
|
|
10
|
+
export type EvidencePair = {
|
|
11
|
+
id: number;
|
|
12
|
+
familyRoot: string;
|
|
13
|
+
before: EvidenceSide;
|
|
14
|
+
receiptDigest: string;
|
|
15
|
+
commandDigest: string | null;
|
|
16
|
+
savedBy: string;
|
|
17
|
+
savedAt: string;
|
|
18
|
+
after: EvidenceSide | null;
|
|
19
|
+
latestHead: string | null;
|
|
20
|
+
latestRun: number | null;
|
|
21
|
+
reading: EvidenceReading;
|
|
22
|
+
};
|
|
23
|
+
export declare function evidencePairsOf(store: Store, root: string, taskId: string, now: Date): EvidencePair[];
|
|
24
|
+
export type SavePair = {
|
|
25
|
+
ok: true;
|
|
26
|
+
id: number;
|
|
27
|
+
existing: boolean;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
reason: "unknown" | "not-on-task" | "no-failed-check";
|
|
31
|
+
message: string;
|
|
32
|
+
};
|
|
33
|
+
/** Save a run's failed check as the before side. Only a sealed failure can be saved: there's nothing else to compare. */
|
|
34
|
+
export declare function saveEvidencePair(store: Store, root: string, input: {
|
|
35
|
+
taskId: string;
|
|
36
|
+
runId?: number;
|
|
37
|
+
by: string;
|
|
38
|
+
}, now: Date): SavePair;
|
|
39
|
+
export type RerunPair = {
|
|
40
|
+
ok: true;
|
|
41
|
+
run: number;
|
|
42
|
+
head: string;
|
|
43
|
+
request: number;
|
|
44
|
+
existing: boolean;
|
|
45
|
+
} | {
|
|
46
|
+
ok: false;
|
|
47
|
+
message: string;
|
|
48
|
+
};
|
|
49
|
+
/** Ask for the same check on the latest commit. Its level is the one the saved failure ran at. The caller proves the person. */
|
|
50
|
+
export declare function rerunEvidence(store: Store, taskId: string, actor: string, now: Date): RerunPair;
|
|
51
|
+
/** How a pair reads, in one sentence. */
|
|
52
|
+
export declare function evidenceWords(pair: EvidencePair): string;
|
|
53
|
+
export { askFailingFirst, dropFailingFirst, FAILING_FIRST_FILE, failingFirstOf, failingFirstReportSchema, failingFirstWords, saveFailingFirstReport, type FailingFirst } from "./failing-first.js";
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Before-and-after evidence (docs/plans/fleet-follow-through.md, part 4), and a bug fix's optional failing test first.
|
|
3
|
+
*
|
|
4
|
+
* The before side is saved from a sealed failed check: its run, commit, receipt digest and command digest. The after
|
|
5
|
+
* side is never stored. It is read each time from the family's newest result, so a newer commit reads as out of date
|
|
6
|
+
* without anything having to notice. Rerunning uses the follow-up check road (result-follow-ups.ts): the same approved
|
|
7
|
+
* command, in a fresh checkout, on the latest commit, with no agent.
|
|
8
|
+
*
|
|
9
|
+
* A failing-test-first report is the agent's own words, shown as that. It is not a check Toolroll ran.
|
|
10
|
+
*/
|
|
11
|
+
import { readVerificationView } from "./contracts/verification-view.js";
|
|
12
|
+
import { followUpChecksOf, requestFollowUpChecks } from "./result-follow-ups.js";
|
|
13
|
+
import { readingFleet } from "./fleet-schema.js";
|
|
14
|
+
import { cleanFindingText, familyRootOf, NOT_ON_TASK, runOnTask } from "./findings.js";
|
|
15
|
+
import { runCheckLevel } from "./check-levels.js";
|
|
16
|
+
import { failedVerificationEvidence, verificationEvidence } from "./verification-evidence.js";
|
|
17
|
+
/** The family's builder runs that finished with a commit, newest first. */
|
|
18
|
+
function familyRuns(store, taskId) {
|
|
19
|
+
const family = store.taskFamilyOf(taskId, null, true);
|
|
20
|
+
const ids = family === null || family.problem !== null ? [taskId] : family.versions.map(one => one.id);
|
|
21
|
+
return ids.flatMap(id => {
|
|
22
|
+
const ref = store.lookupRef(id);
|
|
23
|
+
return ref === null ? [] : store.runsFor(ref.id).filter(one => one.role === "builder" && one.finishedAt !== null && one.headRevision !== null);
|
|
24
|
+
}).sort((a, b) => b.id - a.id);
|
|
25
|
+
}
|
|
26
|
+
const screenshotsOf = (store, runId) => store.artifactsFor(runId).filter(one => one.kind === "screenshot").map(one => one.id);
|
|
27
|
+
/** The command a run's sealed receipt ran, and whether it passed. Null when it has no readable receipt. */
|
|
28
|
+
function sealedCheck(store, root, runId) {
|
|
29
|
+
const gate = verificationEvidence(store, root, runId);
|
|
30
|
+
if (!gate.ok || gate.bytes === null)
|
|
31
|
+
return null;
|
|
32
|
+
const view = readVerificationView(gate.bytes);
|
|
33
|
+
if (!view.ok)
|
|
34
|
+
return null;
|
|
35
|
+
const result = view.value.result;
|
|
36
|
+
return { digest: view.value.command.digest, passed: result.configured && result.ran === true && result.exitCode === 0 };
|
|
37
|
+
}
|
|
38
|
+
/** The newest result of the family that ran `digest`, by the build's own check or a follow-up check on its commit. */
|
|
39
|
+
function afterSide(store, root, runs, beforeRun, digest, now) {
|
|
40
|
+
let otherCommand = false;
|
|
41
|
+
for (const run of runs) {
|
|
42
|
+
if (run.id < beforeRun)
|
|
43
|
+
break;
|
|
44
|
+
const follow = followUpChecksOf(store, run.id, now).filter(one => (one.state === "passed" || one.state === "failed") && one.head === run.headRevision).at(-1);
|
|
45
|
+
if (follow !== undefined && (digest === null || follow.digest === digest))
|
|
46
|
+
return { run: run.id, head: follow.head, status: follow.state, via: "follow-up", screenshots: screenshotsOf(store, run.id) };
|
|
47
|
+
if (follow !== undefined)
|
|
48
|
+
otherCommand = true;
|
|
49
|
+
if (run.id === beforeRun)
|
|
50
|
+
continue;
|
|
51
|
+
const own = sealedCheck(store, root, run.id);
|
|
52
|
+
if (own === null)
|
|
53
|
+
continue;
|
|
54
|
+
if (digest !== null && own.digest !== digest) {
|
|
55
|
+
otherCommand = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
return { run: run.id, head: run.headRevision, status: own.passed ? "passed" : "failed", via: "build", screenshots: screenshotsOf(store, run.id) };
|
|
59
|
+
}
|
|
60
|
+
return otherCommand ? "check-changed" : null;
|
|
61
|
+
}
|
|
62
|
+
function pairOf(store, root, row, taskId, now) {
|
|
63
|
+
const runs = familyRuns(store, taskId);
|
|
64
|
+
const beforeRun = Number(row["before_run"]);
|
|
65
|
+
const commandDigest = row["command_digest"] === null ? null : String(row["command_digest"]);
|
|
66
|
+
const found = afterSide(store, root, runs, beforeRun, commandDigest, now);
|
|
67
|
+
const latest = runs[0] ?? null;
|
|
68
|
+
const after = found === "check-changed" ? null : found;
|
|
69
|
+
const reading = found === "check-changed" ? "check-changed"
|
|
70
|
+
: after === null ? latest !== null && latest.headRevision !== String(row["before_head"]) ? "out-of-date" : "failing"
|
|
71
|
+
: after.head !== latest?.headRevision ? "out-of-date"
|
|
72
|
+
: after.status === "passed" ? "fixed" : "still-failing";
|
|
73
|
+
return {
|
|
74
|
+
id: Number(row["id"]), familyRoot: String(row["family_root"]),
|
|
75
|
+
before: { run: beforeRun, head: String(row["before_head"]), status: "failed", via: "build", screenshots: screenshotsOf(store, beforeRun) },
|
|
76
|
+
receiptDigest: String(row["receipt_digest"]), commandDigest, savedBy: String(row["saved_by"]), savedAt: String(row["saved_at"]),
|
|
77
|
+
after, latestHead: latest?.headRevision ?? null, latestRun: latest?.id ?? null, reading,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function evidencePairsOf(store, root, taskId, now) {
|
|
81
|
+
return readingFleet(() => store.handle.prepare("SELECT * FROM evidence_pair WHERE family_root = ? ORDER BY id DESC").all(familyRootOf(store, taskId)).map(row => pairOf(store, root, row, taskId, now)), []);
|
|
82
|
+
}
|
|
83
|
+
/** Save a run's failed check as the before side. Only a sealed failure can be saved: there's nothing else to compare. */
|
|
84
|
+
export function saveEvidencePair(store, root, input, now) {
|
|
85
|
+
const ref = store.lookupRef(input.taskId);
|
|
86
|
+
if (ref === null)
|
|
87
|
+
return { ok: false, reason: "unknown", message: `no task \`${input.taskId}\`` };
|
|
88
|
+
// A run named by a caller is checked first, and said plainly: every road (the CLI's --run, the console's form) comes here.
|
|
89
|
+
if (input.runId !== undefined && !runOnTask(store, input.runId, input.taskId))
|
|
90
|
+
return { ok: false, reason: "not-on-task", message: NOT_ON_TASK };
|
|
91
|
+
const runId = input.runId ?? familyRuns(store, input.taskId)[0]?.id;
|
|
92
|
+
const run = runId === undefined ? null : store.getRun(runId);
|
|
93
|
+
const root1 = familyRootOf(store, input.taskId);
|
|
94
|
+
if (run === null || run.headRevision === null)
|
|
95
|
+
return { ok: false, reason: "no-failed-check", message: `${input.taskId} has no finished attempt to save.` };
|
|
96
|
+
const failure = failedVerificationEvidence(store, root, run.id);
|
|
97
|
+
if (failure.kind !== "failed")
|
|
98
|
+
return { ok: false, reason: "no-failed-check", message: `Attempt #${run.id} has no failed check to save.` };
|
|
99
|
+
const view = readVerificationView(failure.receipt);
|
|
100
|
+
const made = store.handle.prepare(`INSERT INTO evidence_pair (family_root, repo, before_run, before_head, receipt_digest, command_digest, saved_by, saved_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
101
|
+
ON CONFLICT (family_root, before_run) DO NOTHING`).run(root1, ref.repo, run.id, run.headRevision, failure.digest, view.ok ? view.value.command.digest : null, input.by, now.toISOString());
|
|
102
|
+
if (Number(made.changes) === 1)
|
|
103
|
+
return { ok: true, id: Number(made.lastInsertRowid), existing: false };
|
|
104
|
+
const row = store.handle.prepare("SELECT id FROM evidence_pair WHERE family_root = ? AND before_run = ?").get(root1, run.id);
|
|
105
|
+
return { ok: true, id: Number(row?.["id"]), existing: true };
|
|
106
|
+
}
|
|
107
|
+
/** Ask for the same check on the latest commit. Its level is the one the saved failure ran at. The caller proves the person. */
|
|
108
|
+
export function rerunEvidence(store, taskId, actor, now) {
|
|
109
|
+
const latest = familyRuns(store, taskId)[0];
|
|
110
|
+
if (latest === undefined)
|
|
111
|
+
return { ok: false, message: `${taskId} has no finished attempt to check.` };
|
|
112
|
+
const level = runCheckLevel(store, latest.id);
|
|
113
|
+
const asked = requestFollowUpChecks(store, { runId: latest.id, level: (level === "off" || level === null ? "full" : level), actor }, now);
|
|
114
|
+
return asked.ok ? { ok: true, run: latest.id, head: latest.headRevision, request: asked.request, existing: asked.existing } : { ok: false, message: asked.message };
|
|
115
|
+
}
|
|
116
|
+
const short = (head) => head === null ? "no commit" : head.slice(0, 7);
|
|
117
|
+
/** How a pair reads, in one sentence. */
|
|
118
|
+
export function evidenceWords(pair) {
|
|
119
|
+
const before = `Failed on ${short(pair.before.head)} (build #${pair.before.run})`;
|
|
120
|
+
switch (pair.reading) {
|
|
121
|
+
case "failing": return `${before}. Not checked since.`;
|
|
122
|
+
case "fixed": return `${before}, passes on ${short(pair.after.head)} (build #${pair.after.run}).`;
|
|
123
|
+
case "still-failing": return `${before}, still fails on ${short(pair.after.head)} (build #${pair.after.run}).`;
|
|
124
|
+
case "check-changed": return `${before}. The project's check changed since, so the two can't be compared.`;
|
|
125
|
+
case "out-of-date": return `${before}. Out of date: the code is now at ${short(pair.latestHead)}${pair.after === null ? "" : `, and the last check ran on ${short(pair.after.head)}`}.`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export { askFailingFirst, dropFailingFirst, FAILING_FIRST_FILE, failingFirstOf, failingFirstReportSchema, failingFirstWords, saveFailingFirstReport } from "./failing-first.js";
|
package/dist/exhaustion.d.ts
CHANGED
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* someone signs in, instead of burning attempts. Everything else is the
|
|
7
7
|
* ordinary failure road.
|
|
8
8
|
*
|
|
9
|
-
* `usage-exhausted
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* `usage-exhausted` moves the work to the next agent a person approved
|
|
10
|
+
* (fallback.ts), so it comes only from a recogniser below, and a recogniser
|
|
11
|
+
* comes only from a real run (docs/plans/fallback-chains.md). A provider with
|
|
12
|
+
* none never reads as exhausted. `credits-depleted` and `transient-throttle`
|
|
13
|
+
* are only ever read from older runs' records.
|
|
12
14
|
*/
|
|
13
15
|
export type TerminalClass = "usage-exhausted" | "credits-depleted" | "transient-throttle"
|
|
14
16
|
/** The provider's sign-in or API key no longer works — no retry:
|
|
@@ -31,15 +33,26 @@ export type StructuralTerminal = {
|
|
|
31
33
|
code: string | null;
|
|
32
34
|
};
|
|
33
35
|
/** Classify a finished attempt: a sign-in that no longer works first, for
|
|
34
|
-
* any provider or version; then a
|
|
36
|
+
* any provider or version; then a recognised plan limit, on the exact provider,
|
|
37
|
+
* auth mode and CLI line it was seen on; then a structural failure terminal is
|
|
35
38
|
* `not-exhausted`, and anything else `unknown`. */
|
|
36
39
|
export declare function classifyTerminal(args: {
|
|
37
40
|
terminal: StructuralTerminal | null;
|
|
41
|
+
/** The run's own provider, its auth mode, and the version the installed CLI reports to the gateway: never the checkout's word. */
|
|
42
|
+
run?: {
|
|
43
|
+
provider: string;
|
|
44
|
+
authMode: string;
|
|
45
|
+
version: string | null;
|
|
46
|
+
};
|
|
38
47
|
/** What a run that failed within seconds, having produced nothing, said
|
|
39
48
|
* on its way out (stderr and unstructured stdout) — read ONLY for the
|
|
40
49
|
* sign-in signal: a CLI that is not logged in often exits before its
|
|
41
50
|
* structured stream begins. */
|
|
42
51
|
earlyExit?: string | null;
|
|
43
52
|
}): TerminalClass;
|
|
53
|
+
/** Whether a terminal reads as a limit for this provider and auth mode, before its CLI version is asked. */
|
|
54
|
+
export declare function terminalMatchesLimit(provider: string, authMode: string, terminal: StructuralTerminal | null): boolean;
|
|
55
|
+
/** Whether Toolroll can recognise this agent running out at all: the words every surface shows beside an entry. */
|
|
56
|
+
export declare function limitRecognised(provider: string, authMode: string): boolean;
|
|
44
57
|
/** Whether a provider's own words say its sign-in or key no longer works. */
|
|
45
58
|
export declare function isAuthFailure(text: string): boolean;
|
package/dist/exhaustion.js
CHANGED
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
* someone signs in, instead of burning attempts. Everything else is the
|
|
7
7
|
* ordinary failure road.
|
|
8
8
|
*
|
|
9
|
-
* `usage-exhausted
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* `usage-exhausted` moves the work to the next agent a person approved
|
|
10
|
+
* (fallback.ts), so it comes only from a recogniser below, and a recogniser
|
|
11
|
+
* comes only from a real run (docs/plans/fallback-chains.md). A provider with
|
|
12
|
+
* none never reads as exhausted. `credits-depleted` and `transient-throttle`
|
|
13
|
+
* are only ever read from older runs' records.
|
|
12
14
|
*/
|
|
13
15
|
/** Classify a finished attempt: a sign-in that no longer works first, for
|
|
14
|
-
* any provider or version; then a
|
|
16
|
+
* any provider or version; then a recognised plan limit, on the exact provider,
|
|
17
|
+
* auth mode and CLI line it was seen on; then a structural failure terminal is
|
|
15
18
|
* `not-exhausted`, and anything else `unknown`. */
|
|
16
19
|
export function classifyTerminal(args) {
|
|
17
20
|
const { terminal } = args;
|
|
@@ -21,8 +24,45 @@ export function classifyTerminal(args) {
|
|
|
21
24
|
return "auth-expired";
|
|
22
25
|
if (terminal === null || terminal.failed !== true)
|
|
23
26
|
return "unknown";
|
|
27
|
+
if (args.run !== undefined && terminal.text !== null && planLimitReached(args.run, terminal.text))
|
|
28
|
+
return "usage-exhausted";
|
|
24
29
|
return "not-exhausted";
|
|
25
30
|
}
|
|
31
|
+
const RECOGNISERS = Object.freeze([
|
|
32
|
+
{
|
|
33
|
+
provider: "claude", authMode: "subscription", from: [2, 1, 290], below: [2, 2, 0],
|
|
34
|
+
terminal: /^You've hit your (?:session|weekly|Opus|Sonnet) limit · resets [^\n]{1,64}$/,
|
|
35
|
+
},
|
|
36
|
+
]);
|
|
37
|
+
function versionOf(version) {
|
|
38
|
+
const parts = version === null ? null : /^(\d{1,6})\.(\d{1,6})\.(\d{1,6})(?:\s|$)/.exec(version);
|
|
39
|
+
return parts === null ? null : [Number(parts[1]), Number(parts[2]), Number(parts[3])];
|
|
40
|
+
}
|
|
41
|
+
function before(a, b) {
|
|
42
|
+
for (let at = 0; at < 3; at++)
|
|
43
|
+
if (a[at] !== b[at])
|
|
44
|
+
return a[at] < b[at];
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
function planLimitReached(run, text) {
|
|
48
|
+
const version = versionOf(run.version);
|
|
49
|
+
if (version === null)
|
|
50
|
+
return false;
|
|
51
|
+
const words = text.trim();
|
|
52
|
+
return RECOGNISERS.some(one => one.provider === run.provider && one.authMode === run.authMode
|
|
53
|
+
&& !before(version, one.from) && before(version, one.below) && one.terminal.test(words));
|
|
54
|
+
}
|
|
55
|
+
/** Whether a terminal reads as a limit for this provider and auth mode, before its CLI version is asked. */
|
|
56
|
+
export function terminalMatchesLimit(provider, authMode, terminal) {
|
|
57
|
+
if (terminal === null || terminal.failed !== true || terminal.text === null)
|
|
58
|
+
return false;
|
|
59
|
+
const words = terminal.text.trim();
|
|
60
|
+
return RECOGNISERS.some(one => one.provider === provider && one.authMode === authMode && one.terminal.test(words));
|
|
61
|
+
}
|
|
62
|
+
/** Whether Toolroll can recognise this agent running out at all: the words every surface shows beside an entry. */
|
|
63
|
+
export function limitRecognised(provider, authMode) {
|
|
64
|
+
return RECOGNISERS.some(one => one.provider === provider && one.authMode === authMode);
|
|
65
|
+
}
|
|
26
66
|
/**
|
|
27
67
|
* The sign-in signals, provider-neutral: Claude's expired or unrefreshable
|
|
28
68
|
* OAuth session and its "Please run /login", an API key the provider calls
|
package/dist/export.js
CHANGED
|
@@ -57,7 +57,7 @@ export function categoryOf(table) {
|
|
|
57
57
|
[/^(flow|workflow_)/, "flows"],
|
|
58
58
|
[/^(chat_|lead_|team_|notification|push_)/, "chats"],
|
|
59
59
|
[/^(approver|credential$|invite|sso_identity|coordinator_|oauth_)/, "people"],
|
|
60
|
-
[/^(run|artifact|claim|worktree$|execution_slot|criterion_review|diff_comment|proof_|review_request|repair_chain|incident|publication$|merge_|side_spend|knowledge_snapshot|learning_snapshot|skill_snapshot)/, "runs"],
|
|
60
|
+
[/^(run|artifact|claim|worktree$|execution_slot|criterion_review|diff_comment|proof_|review_request|repair_chain|check_repair|evidence_pair|failing_first|finding|provider_capture|incident|publication$|merge_|side_spend|knowledge_snapshot|learning_snapshot|skill_snapshot)/, "runs"],
|
|
61
61
|
[/^(task|hold$|plan_|scope_|tool_seal|decision|external_|skill_test)/, "tasks"],
|
|
62
62
|
[/^(project|approval_policy|capability|verify_command|worktree_setup|backend_grant|intake_grant|publication_grant|operating_mode|knowledge_|learning_|memory_|skill_)/, "projects"],
|
|
63
63
|
[/(_config|_default|_defaults)$|^(budget|model_|provider_|installation_fact|runtime_check|monitoring_status|integration_check|quota|mode_rail|schema_version)/, "settings"],
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Failing test first (docs/plans/fleet-follow-through.md), on its own so the steering mailbox can read it. Evidence pairs
|
|
2
|
+
* reach the builder through follow-up checks, and the mailbox loads with the provider table: importing them from there
|
|
3
|
+
* is a cycle that fails at load in every built command. */
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import type { Store } from "./store.js";
|
|
6
|
+
export declare const FAILING_FIRST_OUTPUT_LIMIT = 2000;
|
|
7
|
+
/** Where the agent writes its report, in the attempt's own mailbox folder. */
|
|
8
|
+
export declare const FAILING_FIRST_FILE = "failing-first.json";
|
|
9
|
+
export declare const failingFirstReportSchema: z.ZodObject<{
|
|
10
|
+
key: z.ZodString;
|
|
11
|
+
test: z.ZodString;
|
|
12
|
+
output: z.ZodString;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
export type FailingFirst = {
|
|
15
|
+
askedBy: string;
|
|
16
|
+
askedAt: string;
|
|
17
|
+
reportRun: number | null;
|
|
18
|
+
test: string | null;
|
|
19
|
+
output: string | null;
|
|
20
|
+
reportedAt: string | null;
|
|
21
|
+
};
|
|
22
|
+
export declare function failingFirstOf(store: Store, taskId: string): FailingFirst | null;
|
|
23
|
+
export declare function askFailingFirst(store: Store, taskId: string, by: string, now: Date): boolean;
|
|
24
|
+
export declare function dropFailingFirst(store: Store, taskId: string): void;
|
|
25
|
+
/** The agent's report, as data. Only a task that asked takes one. */
|
|
26
|
+
export declare function saveFailingFirstReport(store: Store, taskRef: number, runId: number, report: {
|
|
27
|
+
test: string;
|
|
28
|
+
output: string;
|
|
29
|
+
}, now: Date): boolean;
|
|
30
|
+
/** How the step stands, or null when this task didn't ask for it. */
|
|
31
|
+
export declare function failingFirstWords(step: FailingFirst | null): string | null;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** Failing test first (docs/plans/fleet-follow-through.md), on its own so the steering mailbox can read it. Evidence pairs
|
|
2
|
+
* reach the builder through follow-up checks, and the mailbox loads with the provider table: importing them from there
|
|
3
|
+
* is a cycle that fails at load in every built command. */
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { readingFleet } from "./fleet-schema.js";
|
|
6
|
+
import { cleanFindingText, familyRootOf } from "./findings.js";
|
|
7
|
+
export const FAILING_FIRST_OUTPUT_LIMIT = 2_000;
|
|
8
|
+
/** Where the agent writes its report, in the attempt's own mailbox folder. */
|
|
9
|
+
export const FAILING_FIRST_FILE = "failing-first.json";
|
|
10
|
+
export const failingFirstReportSchema = z.strictObject({ key: z.string().min(1).max(64), test: z.string().min(1).max(300), output: z.string().min(1).max(FAILING_FIRST_OUTPUT_LIMIT) });
|
|
11
|
+
export function failingFirstOf(store, taskId) {
|
|
12
|
+
return readingFleet(() => {
|
|
13
|
+
const row = store.handle.prepare("SELECT * FROM failing_first WHERE family_root = ?").get(familyRootOf(store, taskId));
|
|
14
|
+
if (row === undefined)
|
|
15
|
+
return null;
|
|
16
|
+
const text = (key) => row[key] === null ? null : String(row[key]);
|
|
17
|
+
return { askedBy: String(row["asked_by"]), askedAt: String(row["asked_at"]), reportRun: row["report_run"] === null ? null : Number(row["report_run"]), test: text("test"), output: text("output"), reportedAt: text("reported_at") };
|
|
18
|
+
}, null);
|
|
19
|
+
}
|
|
20
|
+
export function askFailingFirst(store, taskId, by, now) {
|
|
21
|
+
const ref = store.lookupRef(taskId);
|
|
22
|
+
if (ref === null)
|
|
23
|
+
return false;
|
|
24
|
+
store.handle.prepare("INSERT INTO failing_first (family_root, repo, asked_by, asked_at) VALUES (?, ?, ?, ?) ON CONFLICT (family_root) DO NOTHING").run(familyRootOf(store, taskId), ref.repo, by, now.toISOString());
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
export function dropFailingFirst(store, taskId) {
|
|
28
|
+
store.handle.prepare("DELETE FROM failing_first WHERE family_root = ?").run(familyRootOf(store, taskId));
|
|
29
|
+
}
|
|
30
|
+
/** The agent's report, as data. Only a task that asked takes one. */
|
|
31
|
+
export function saveFailingFirstReport(store, taskRef, runId, report, now) {
|
|
32
|
+
const taskId = store.refById(taskRef)?.externalId;
|
|
33
|
+
if (taskId === undefined)
|
|
34
|
+
return false;
|
|
35
|
+
const saved = store.handle.prepare("UPDATE failing_first SET report_run = ?, test = ?, output = ?, reported_at = ? WHERE family_root = ?")
|
|
36
|
+
.run(runId, cleanFindingText(report.test, 300), cleanFindingText(report.output, FAILING_FIRST_OUTPUT_LIMIT), now.toISOString(), familyRootOf(store, taskId));
|
|
37
|
+
return Number(saved.changes) === 1;
|
|
38
|
+
}
|
|
39
|
+
/** How the step stands, or null when this task didn't ask for it. */
|
|
40
|
+
export function failingFirstWords(step) {
|
|
41
|
+
if (step === null)
|
|
42
|
+
return null;
|
|
43
|
+
return step.reportedAt === null ? "Failing test first was asked for. No report yet." : `The agent reports it wrote ${step.test ?? "a test"} first and saw it fail. Toolroll didn't run this itself.`;
|
|
44
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v156: fallback chains (docs/plans/fallback-chains.md). New tables only, so a saved row reads as it did. Their names
|
|
3
|
+
* differ from the tables v115 dropped: nothing old comes back under an old name. Kept beside nothing else so the update
|
|
4
|
+
* road can read the list without loading the store.
|
|
5
|
+
*/
|
|
6
|
+
export declare const V156_SCHEMA = "\nCREATE TABLE IF NOT EXISTS fallback_setting (\n repo TEXT PRIMARY KEY,\n entries_json TEXT NOT NULL,\n paid_granted_by TEXT,\n paid_granted_at TEXT,\n updated_by TEXT NOT NULL,\n updated_at TEXT NOT NULL\n);\nCREATE TABLE IF NOT EXISTS fallback_walk (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n task_ref INTEGER NOT NULL,\n scope_digest TEXT NOT NULL,\n chain_digest TEXT NOT NULL,\n cursor INTEGER NOT NULL DEFAULT 0,\n tail_run INTEGER NOT NULL,\n state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open','closed')),\n closed_reason TEXT,\n opened_at TEXT NOT NULL,\n closed_at TEXT\n);\nCREATE UNIQUE INDEX IF NOT EXISTS fallback_walk_one_open ON fallback_walk (task_ref) WHERE state = 'open';\nCREATE TABLE IF NOT EXISTS fallback_move (\n walk INTEGER NOT NULL REFERENCES fallback_walk(id),\n to_index INTEGER NOT NULL,\n from_run INTEGER NOT NULL UNIQUE,\n to_run INTEGER UNIQUE,\n terminal_class TEXT NOT NULL,\n paid INTEGER NOT NULL DEFAULT 0,\n moved_at TEXT NOT NULL,\n PRIMARY KEY (walk, to_index)\n);\n";
|
|
7
|
+
export declare const V156_TABLES: readonly ["fallback_setting", "fallback_walk", "fallback_move"];
|
|
8
|
+
export declare const V156_STEPS: readonly ["fallback-tables"];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v156: fallback chains (docs/plans/fallback-chains.md). New tables only, so a saved row reads as it did. Their names
|
|
3
|
+
* differ from the tables v115 dropped: nothing old comes back under an old name. Kept beside nothing else so the update
|
|
4
|
+
* road can read the list without loading the store.
|
|
5
|
+
*/
|
|
6
|
+
export const V156_SCHEMA = `
|
|
7
|
+
CREATE TABLE IF NOT EXISTS fallback_setting (
|
|
8
|
+
repo TEXT PRIMARY KEY,
|
|
9
|
+
entries_json TEXT NOT NULL,
|
|
10
|
+
paid_granted_by TEXT,
|
|
11
|
+
paid_granted_at TEXT,
|
|
12
|
+
updated_by TEXT NOT NULL,
|
|
13
|
+
updated_at TEXT NOT NULL
|
|
14
|
+
);
|
|
15
|
+
CREATE TABLE IF NOT EXISTS fallback_walk (
|
|
16
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
17
|
+
task_ref INTEGER NOT NULL,
|
|
18
|
+
scope_digest TEXT NOT NULL,
|
|
19
|
+
chain_digest TEXT NOT NULL,
|
|
20
|
+
cursor INTEGER NOT NULL DEFAULT 0,
|
|
21
|
+
tail_run INTEGER NOT NULL,
|
|
22
|
+
state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open','closed')),
|
|
23
|
+
closed_reason TEXT,
|
|
24
|
+
opened_at TEXT NOT NULL,
|
|
25
|
+
closed_at TEXT
|
|
26
|
+
);
|
|
27
|
+
CREATE UNIQUE INDEX IF NOT EXISTS fallback_walk_one_open ON fallback_walk (task_ref) WHERE state = 'open';
|
|
28
|
+
CREATE TABLE IF NOT EXISTS fallback_move (
|
|
29
|
+
walk INTEGER NOT NULL REFERENCES fallback_walk(id),
|
|
30
|
+
to_index INTEGER NOT NULL,
|
|
31
|
+
from_run INTEGER NOT NULL UNIQUE,
|
|
32
|
+
to_run INTEGER UNIQUE,
|
|
33
|
+
terminal_class TEXT NOT NULL,
|
|
34
|
+
paid INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
moved_at TEXT NOT NULL,
|
|
36
|
+
PRIMARY KEY (walk, to_index)
|
|
37
|
+
);
|
|
38
|
+
`;
|
|
39
|
+
export const V156_TABLES = Object.freeze(["fallback_setting", "fallback_walk", "fallback_move"]);
|
|
40
|
+
export const V156_STEPS = Object.freeze(["fallback-tables"]);
|