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
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { type CheckoutCleanup } from "./storage.js";
|
|
2
|
-
import type
|
|
1
|
+
import { type CheckoutCleanup, type CheckoutIdle } from "./storage.js";
|
|
2
|
+
import { type Store, type WorktreeRow } from "./store.js";
|
|
3
3
|
import { type WorktreePool } from "./worktree.js";
|
|
4
4
|
/** A release candidate's checkout holds the build a deploy installs: the newest complete one waits at most a week for it. */
|
|
5
5
|
export declare const CANDIDATE_KEEP_MS: number;
|
|
6
6
|
/** The worker's pass removes at most this many; a clean-up by hand has no cap. */
|
|
7
7
|
export declare const AUTO_MAX = 20;
|
|
8
|
-
export type KeepWhy = "in use" | "task not finished" | "waiting for review" | "release candidate" | "has changes" | "has commits" | "unreadable" | "git refused" | "not due yet" | "cleanup is off" | "no task"
|
|
8
|
+
export type KeepWhy = "in use" | "task not finished" | "waiting for review" | "release candidate" | "has changes" | "has commits" | "not on its branch" | "unreadable" | "git refused" | "not due yet" | "cleanup is off" | "no task"
|
|
9
|
+
/** A clean-up by hand leaves it; the next sweep takes it. */
|
|
10
|
+
| "idle";
|
|
9
11
|
export type CheckoutItem = {
|
|
10
12
|
path: string;
|
|
11
13
|
repo: string;
|
|
@@ -17,10 +19,13 @@ export type CheckoutItem = {
|
|
|
17
19
|
why: KeepWhy | null;
|
|
18
20
|
/** With "not due yet" or "release candidate": when it may go. */
|
|
19
21
|
dueAt?: string;
|
|
22
|
+
/** Past the idle limit: when its task last saw any activity. */
|
|
23
|
+
idleSince?: string;
|
|
20
24
|
};
|
|
21
25
|
export type CheckoutPlan = {
|
|
22
26
|
at: string;
|
|
23
27
|
cleanup: CheckoutCleanup;
|
|
28
|
+
idle: CheckoutIdle;
|
|
24
29
|
manual: boolean;
|
|
25
30
|
go: CheckoutItem[];
|
|
26
31
|
stay: CheckoutItem[];
|
|
@@ -30,6 +35,9 @@ export type CheckoutPlan = {
|
|
|
30
35
|
freeBytes: number;
|
|
31
36
|
waitingReview: number;
|
|
32
37
|
withChanges: number;
|
|
38
|
+
/** What the next sweep's idle cleanup takes. */
|
|
39
|
+
idleCount: number;
|
|
40
|
+
idleBytes: number;
|
|
33
41
|
};
|
|
34
42
|
type Status = {
|
|
35
43
|
keep: KeepWhy | null;
|
|
@@ -64,6 +72,21 @@ export declare function finishedCheckouts(store: Store, now: Date, repo?: string
|
|
|
64
72
|
taskId: string | null;
|
|
65
73
|
finishedAt: string;
|
|
66
74
|
}[];
|
|
75
|
+
/** The newest activity of a task's whole family, and whether something rules idle cleanup out for it. */
|
|
76
|
+
type IdleFact = {
|
|
77
|
+
activeAt: string;
|
|
78
|
+
blocked: boolean;
|
|
79
|
+
};
|
|
80
|
+
export type IdleFacts = {
|
|
81
|
+
byRef: Map<number, IdleFact>;
|
|
82
|
+
byBranch: Map<string, IdleFact>;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* When each task family last saw a run, a person's act or a lease, read in one pass. A revision builds on what it
|
|
86
|
+
* revises, so a family is idle together. `blocked`: an open run, an unreleased claim, a release gate, or a task only an
|
|
87
|
+
* external tracker knows, in the family.
|
|
88
|
+
*/
|
|
89
|
+
export declare function idleFacts(store: Store): IdleFacts;
|
|
67
90
|
/** Why a checkout stays as a clean-up by hand reads it (in use, or its task not done with it), before its files; null: it may go. */
|
|
68
91
|
export declare function checkoutKeepWhy(pool: WorktreePool, row: WorktreeRow, statuses: ReturnType<typeof taskStatuses>, now: Date): KeepWhy | null;
|
|
69
92
|
/**
|
|
@@ -83,11 +106,15 @@ export type CleanResult = {
|
|
|
83
106
|
why: KeepWhy;
|
|
84
107
|
}[];
|
|
85
108
|
freed: number;
|
|
109
|
+
/** The worker's pass stopped at AUTO_MAX: more may be due, and the sweep comes back an hour later. */
|
|
110
|
+
more?: boolean;
|
|
86
111
|
};
|
|
87
112
|
/**
|
|
88
113
|
* Remove what may go: the worker's pass (`manual` false: under the setting, at most AUTO_MAX, one repository) or a
|
|
89
114
|
* clean-up by hand (no cap, every repository). Each checkout is asked again right before it goes; each removal is in
|
|
90
|
-
* the ledger.
|
|
115
|
+
* the ledger. Idle checkouts go only with `idle`, the storage sweep's child (never the service's own pass, whose git
|
|
116
|
+
* and du must not hold up its heartbeats): each is judged again from a fresh read of its task and its family's
|
|
117
|
+
* activity, `spare` (live work), the OS's list of what runs in the folder, and `proveRecreatable`, inside its reservation.
|
|
91
118
|
*/
|
|
92
119
|
export declare function cleanCheckouts(store: Store, pool: WorktreePool, now: () => Date, options: {
|
|
93
120
|
manual: boolean;
|
|
@@ -95,6 +122,10 @@ export declare function cleanCheckouts(store: Store, pool: WorktreePool, now: ()
|
|
|
95
122
|
repo?: string;
|
|
96
123
|
only?: ReadonlySet<string>;
|
|
97
124
|
spare?: (path: string) => boolean;
|
|
125
|
+
/** The storage sweep's child only: idle checkouts go too. `occupied` asks the OS whether any process works in a folder. */
|
|
126
|
+
idle?: {
|
|
127
|
+
occupied: (path: string) => boolean;
|
|
128
|
+
};
|
|
98
129
|
}): Promise<CleanResult>;
|
|
99
130
|
export type SlimResult = {
|
|
100
131
|
slimmed: {
|
|
@@ -122,5 +153,5 @@ export declare function discardCheckout(store: Store, pool: WorktreePool, path:
|
|
|
122
153
|
message: string;
|
|
123
154
|
}>;
|
|
124
155
|
/** "Waiting for review", "Has changes" … as a sentence fragment for a person. */
|
|
125
|
-
export declare function whyWords(item: Pick<CheckoutItem, "why" | "dueAt">): string;
|
|
156
|
+
export declare function whyWords(item: Pick<CheckoutItem, "why" | "dueAt" | "idleSince">): string;
|
|
126
157
|
export {};
|
package/dist/checkout-cleanup.js
CHANGED
|
@@ -5,14 +5,22 @@
|
|
|
5
5
|
* 2 days or a week after that, or never (only a clean-up by hand). A clean-up by hand doesn't wait for the setting.
|
|
6
6
|
* Only the working copy goes: its branch, and so every commit, stays, and a later lease of the branch makes a new one.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* branch. A release check's checkout (a deploy installs from it) goes once its task is complete, with its dependency
|
|
8
|
+
* Kept until idle (below): one whose task isn't finished (on hold, failed, a revision under way) and a result still
|
|
9
|
+
* waiting for review. Always kept: a checkout in use, one with changes (files Toolroll wrote itself aside), and one
|
|
10
|
+
* with commits on no branch. A release check's checkout (a deploy installs from it) goes once its task is complete, with its dependency
|
|
11
11
|
* install and journey output, except the one deployed now and the project's newest complete one still waiting for its
|
|
12
12
|
* deploy (no deploy since it was complete; at most a week). A superseded or cancelled gate goes at once, whatever its
|
|
13
13
|
* task's state (staged releases keep the rollback copy). A checkout no task names (adopted after a crash) goes only
|
|
14
14
|
* in a clean-up by hand. Every removal is in the ledger.
|
|
15
15
|
*
|
|
16
|
+
* Idle checkouts go too (the idle limit, 14 days by default): one whose whole task family has been idle that long (no
|
|
17
|
+
* run, no person's act, no open lease) goes even with its result waiting for review or its task unfinished, in the
|
|
18
|
+
* storage sweep's child only, never in a clean-up by hand, and only when `WorktreePool.proveRecreatable` says nothing is lost.
|
|
19
|
+
* Nothing needs that working copy: a result page, Accept, Send back and Run checks read saved evidence and the branch,
|
|
20
|
+
* and a retry, revision or resume leases the branch, which makes a new one. Excluded, because that can't be proven: a
|
|
21
|
+
* release gate (a deploy installs from its checkout), work from an external tracker, and a checkout no task names. A
|
|
22
|
+
* checkout with uncommitted changes is never removed by this; it is listed for a person.
|
|
23
|
+
*
|
|
16
24
|
* A kept checkout waiting for review or with changes drops its dependencies and build output once its run ends
|
|
17
25
|
* (`slimKeptCheckouts`); the next run's setup restores them.
|
|
18
26
|
*/
|
|
@@ -21,7 +29,8 @@ import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
|
21
29
|
import { basename, dirname, join } from "node:path";
|
|
22
30
|
import { taskBranches } from "./names.js";
|
|
23
31
|
import { COMPLETION_ACTION } from "./result-completion.js";
|
|
24
|
-
import { CLEANUP_CHOICES, bytesWords, diskBytes, diskBytesAsync } from "./storage.js";
|
|
32
|
+
import { CLEANUP_CHOICES, IDLE_CHOICES, bytesWords, diskBytes, diskBytesAsync } from "./storage.js";
|
|
33
|
+
import { checkoutIdle } from "./store.js";
|
|
25
34
|
import { worktreePath } from "./worktree.js";
|
|
26
35
|
const DAY_MS = 86_400_000;
|
|
27
36
|
/** A release candidate's checkout holds the build a deploy installs: the newest complete one waits at most a week for it. */
|
|
@@ -218,6 +227,114 @@ export function finishedCheckouts(store, now, repo) {
|
|
|
218
227
|
return [{ row, taskId: status.taskId, finishedAt: status.finishedAt }];
|
|
219
228
|
});
|
|
220
229
|
}
|
|
230
|
+
/** Toolroll's own bookkeeping about a task: none of it is anyone using the task. Every other ledger entry counts as activity. */
|
|
231
|
+
const NOT_ACTIVITY = ["assignment status baseline", "assignment status observed", "task archived", "checkout removed", "checkout slimmed", "checkout processes stopped",
|
|
232
|
+
"run processes stopped", "orphan process stopped", "process observation failed", "process witness settled", "branch deleted", "landed work cleaned up"];
|
|
233
|
+
/**
|
|
234
|
+
* When each task family last saw a run, a person's act or a lease, read in one pass. A revision builds on what it
|
|
235
|
+
* revises, so a family is idle together. `blocked`: an open run, an unreleased claim, a release gate, or a task only an
|
|
236
|
+
* external tracker knows, in the family.
|
|
237
|
+
*/
|
|
238
|
+
export function idleFacts(store) {
|
|
239
|
+
const db = store.handle;
|
|
240
|
+
const refs = db.prepare(`SELECT r.id AS ref, r.external_id AS id, r.revision_of AS revision_of, t.state AS state, t.created_at AS created_at, t.updated_at AS updated_at,
|
|
241
|
+
(SELECT MAX(s.candidate) FROM task_scope s WHERE s.task_id = r.external_id) AS candidate
|
|
242
|
+
FROM task_ref r LEFT JOIN task t ON t.id = r.external_id AND r.backend = 'built-in'`).all();
|
|
243
|
+
const parentOf = new Map(refs.filter(row => row["revision_of"] !== null).map(row => [String(row["id"]), String(row["revision_of"])]));
|
|
244
|
+
const rootOf = (id) => {
|
|
245
|
+
const seen = new Set([id]);
|
|
246
|
+
let root = id;
|
|
247
|
+
for (let parent = parentOf.get(root); parent !== undefined && !seen.has(parent); parent = parentOf.get(root)) {
|
|
248
|
+
seen.add(parent);
|
|
249
|
+
root = parent;
|
|
250
|
+
}
|
|
251
|
+
return root;
|
|
252
|
+
};
|
|
253
|
+
const families = new Map();
|
|
254
|
+
const rootByRef = new Map();
|
|
255
|
+
const rootsById = new Map();
|
|
256
|
+
const note = (root, at, blocked = false) => {
|
|
257
|
+
if (root === undefined)
|
|
258
|
+
return;
|
|
259
|
+
const fact = families.get(root) ?? { activeAt: "", blocked: false };
|
|
260
|
+
if (typeof at === "string" && at > fact.activeAt)
|
|
261
|
+
fact.activeAt = at;
|
|
262
|
+
if (blocked)
|
|
263
|
+
fact.blocked = true;
|
|
264
|
+
families.set(root, fact);
|
|
265
|
+
};
|
|
266
|
+
for (const row of refs) {
|
|
267
|
+
const id = String(row["id"]), root = rootOf(id);
|
|
268
|
+
rootByRef.set(Number(row["ref"]), root);
|
|
269
|
+
rootsById.set(id, root);
|
|
270
|
+
note(root, row["created_at"]);
|
|
271
|
+
note(root, row["updated_at"], row["state"] === null || row["candidate"] !== null);
|
|
272
|
+
}
|
|
273
|
+
const byTaskRef = (at, ref, blocked = false) => note(rootByRef.get(Number(ref)), at, blocked);
|
|
274
|
+
const runRoot = new Map();
|
|
275
|
+
const branches = [];
|
|
276
|
+
for (const row of db.prepare("SELECT id, task_ref, branch, started_at, finished_at FROM run").all()) {
|
|
277
|
+
const root = rootByRef.get(Number(row["task_ref"]));
|
|
278
|
+
runRoot.set(Number(row["id"]), root);
|
|
279
|
+
note(root, row["started_at"], row["finished_at"] === null);
|
|
280
|
+
note(root, row["finished_at"]);
|
|
281
|
+
if (root !== undefined && row["branch"] !== null)
|
|
282
|
+
branches.push([String(row["branch"]), root]);
|
|
283
|
+
}
|
|
284
|
+
for (const row of db.prepare("SELECT task_ref, acquired_at, heartbeat_at, released_at FROM claim").all()) {
|
|
285
|
+
for (const at of [row["acquired_at"], row["heartbeat_at"], row["released_at"]])
|
|
286
|
+
byTaskRef(at, row["task_ref"]);
|
|
287
|
+
byTaskRef(null, row["task_ref"], row["released_at"] === null);
|
|
288
|
+
}
|
|
289
|
+
for (const row of db.prepare("SELECT task_ref, held_at AS at FROM hold UNION ALL SELECT task_ref, created_at FROM task_steer UNION ALL SELECT task_ref, at FROM task_act").all())
|
|
290
|
+
byTaskRef(row["at"], row["task_ref"]);
|
|
291
|
+
for (const row of db.prepare("SELECT run, created_at AS at FROM run_note UNION ALL SELECT run, created_at FROM diff_comment UNION ALL SELECT run, created_at FROM decision UNION ALL SELECT run, answered_at FROM decision").all()) {
|
|
292
|
+
note(runRoot.get(Number(row["run"])), row["at"]);
|
|
293
|
+
}
|
|
294
|
+
for (const row of db.prepare(`SELECT task_id, run_id, MAX(at) AS at FROM action_ledger WHERE (task_id IS NOT NULL OR run_id IS NOT NULL)
|
|
295
|
+
AND action NOT IN (SELECT value FROM json_each(?)) GROUP BY task_id, run_id`).all(JSON.stringify(NOT_ACTIVITY))) {
|
|
296
|
+
if (row["task_id"] !== null)
|
|
297
|
+
note(rootsById.get(String(row["task_id"])), row["at"]);
|
|
298
|
+
if (row["run_id"] !== null)
|
|
299
|
+
note(runRoot.get(Number(row["run_id"])), row["at"]);
|
|
300
|
+
}
|
|
301
|
+
const byRef = new Map();
|
|
302
|
+
for (const [ref, root] of rootByRef)
|
|
303
|
+
byRef.set(ref, families.get(root));
|
|
304
|
+
// A branch answers for every family that used it: the newest activity and any block among them.
|
|
305
|
+
const byBranch = new Map();
|
|
306
|
+
const share = (branch, root) => {
|
|
307
|
+
const one = families.get(root), seen = byBranch.get(branch);
|
|
308
|
+
byBranch.set(branch, seen === undefined ? { ...one } : { activeAt: seen.activeAt > one.activeAt ? seen.activeAt : one.activeAt, blocked: seen.blocked || one.blocked });
|
|
309
|
+
};
|
|
310
|
+
for (const row of refs)
|
|
311
|
+
for (const branch of taskBranches(String(row["id"])))
|
|
312
|
+
share(branch, rootOf(String(row["id"])));
|
|
313
|
+
for (const [branch, root] of branches)
|
|
314
|
+
share(branch, root);
|
|
315
|
+
return { byRef, byBranch };
|
|
316
|
+
}
|
|
317
|
+
/** The idle limit in days as it stands: null when idle cleanup is off (the limit, or automatic cleanup altogether). */
|
|
318
|
+
function idleDays(store) {
|
|
319
|
+
return store.checkoutCleanup() === "never" ? null : IDLE_CHOICES.find(one => one.value === checkoutIdle(store)).days;
|
|
320
|
+
}
|
|
321
|
+
/** When this checkout's task last saw activity, when that is past the limit and only its task's standing keeps it; else null. */
|
|
322
|
+
function idleSince(pool, row, statuses, facts, days, now) {
|
|
323
|
+
if (days === null || row.taskRef === null || row.branch === "unknown" || row.releasedAt === null || row.runner !== null || pool.inUse(row.path).held)
|
|
324
|
+
return null;
|
|
325
|
+
const keep = statusOf(row, statuses)?.keep;
|
|
326
|
+
if (keep !== "waiting for review" && keep !== "task not finished")
|
|
327
|
+
return null;
|
|
328
|
+
const own = facts.byRef.get(row.taskRef), shared = facts.byBranch.get(row.branch);
|
|
329
|
+
if (own === undefined || own.blocked || shared?.blocked === true)
|
|
330
|
+
return null;
|
|
331
|
+
const since = [own.activeAt, shared?.activeAt, row.createdAt, row.leasedAt, row.releasedAt].filter((one) => typeof one === "string").sort().at(-1);
|
|
332
|
+
return Date.parse(since) + days * DAY_MS <= now.getTime() ? since : null;
|
|
333
|
+
}
|
|
334
|
+
/** Every commit a task's runs recorded on this checkout or its branch: what `proveRecreatable` must find on a branch. */
|
|
335
|
+
function recordedHeads(store, row) {
|
|
336
|
+
return store.handle.prepare("SELECT DISTINCT head_revision AS head FROM run WHERE (branch = ? OR worktree = ?) AND head_revision IS NOT NULL").all(row.branch, row.path).map(one => String(one["head"]));
|
|
337
|
+
}
|
|
221
338
|
/** Why a checkout stays before anyone looks at its files, or null: it may go (as far as its task and the setting say). */
|
|
222
339
|
function keptFor(pool, row, statuses, now, cleanup) {
|
|
223
340
|
const status = statusOf(row, statuses);
|
|
@@ -266,6 +383,8 @@ export async function checkoutPlan(store, pool, now, options) {
|
|
|
266
383
|
const rows = store.listWorktrees().filter(row => existsSync(row.path) && (options.repo === undefined || row.repo === options.repo));
|
|
267
384
|
const sizes = await diskBytesAsync(rows.map(row => row.path));
|
|
268
385
|
const statuses = taskStatuses(store, now);
|
|
386
|
+
const days = idleDays(store);
|
|
387
|
+
const facts = idleFacts(store);
|
|
269
388
|
const items = await eachAtMost(rows, INSPECT_AT_ONCE, async (row) => {
|
|
270
389
|
const kept = keptFor(pool, row, statuses, now, options.manual ? "manual" : cleanup);
|
|
271
390
|
let why = kept.why;
|
|
@@ -273,14 +392,23 @@ export async function checkoutPlan(store, pool, now, options) {
|
|
|
273
392
|
const found = await pool.inspect(row.path);
|
|
274
393
|
why = found === "clean" ? null : found;
|
|
275
394
|
}
|
|
276
|
-
|
|
395
|
+
const since = kept.why === null ? null : idleSince(pool, row, statuses, facts, days, now);
|
|
396
|
+
if (since !== null) {
|
|
397
|
+
const found = await pool.inspect(row.path);
|
|
398
|
+
const proven = found === "clean" ? await pool.proveRecreatable(row, recordedHeads(store, row)) : found;
|
|
399
|
+
why = proven !== "clean" ? proven : options.manual ? "idle" : null;
|
|
400
|
+
}
|
|
401
|
+
return { path: row.path, repo: row.repo, branch: row.branch, taskId: kept.taskId, bytes: sizes.get(row.path) ?? 0, releasedAt: row.releasedAt, why,
|
|
402
|
+
...(kept.dueAt === undefined ? {} : { dueAt: kept.dueAt }), ...(since === null ? {} : { idleSince: since }) };
|
|
277
403
|
});
|
|
278
404
|
const go = items.filter(one => one.why === null).sort((a, b) => b.bytes - a.bytes);
|
|
279
405
|
const stay = items.filter(one => one.why !== null).sort((a, b) => (a.why ?? "").localeCompare(b.why ?? "") || b.bytes - a.bytes);
|
|
406
|
+
const idle = items.filter(one => one.idleSince !== undefined && (one.why === null || one.why === "idle"));
|
|
280
407
|
return {
|
|
281
|
-
at: now.toISOString(), cleanup, manual: options.manual, go, stay,
|
|
408
|
+
at: now.toISOString(), cleanup, idle: checkoutIdle(store), manual: options.manual, go, stay,
|
|
282
409
|
count: items.length, totalBytes: items.reduce((sum, one) => sum + one.bytes, 0), freeBytes: go.reduce((sum, one) => sum + one.bytes, 0),
|
|
283
410
|
waitingReview: stay.filter(one => one.why === "waiting for review").length, withChanges: stay.filter(one => one.why === "has changes").length,
|
|
411
|
+
idleCount: idle.length, idleBytes: idle.reduce((sum, one) => sum + one.bytes, 0),
|
|
284
412
|
};
|
|
285
413
|
}
|
|
286
414
|
/** Names exactly what a preview would remove, so a confirmation removes that and nothing else. */
|
|
@@ -290,7 +418,9 @@ export function previewDigest(plan) {
|
|
|
290
418
|
/**
|
|
291
419
|
* Remove what may go: the worker's pass (`manual` false: under the setting, at most AUTO_MAX, one repository) or a
|
|
292
420
|
* clean-up by hand (no cap, every repository). Each checkout is asked again right before it goes; each removal is in
|
|
293
|
-
* the ledger.
|
|
421
|
+
* the ledger. Idle checkouts go only with `idle`, the storage sweep's child (never the service's own pass, whose git
|
|
422
|
+
* and du must not hold up its heartbeats): each is judged again from a fresh read of its task and its family's
|
|
423
|
+
* activity, `spare` (live work), the OS's list of what runs in the folder, and `proveRecreatable`, inside its reservation.
|
|
294
424
|
*/
|
|
295
425
|
export async function cleanCheckouts(store, pool, now, options) {
|
|
296
426
|
const cleanup = store.checkoutCleanup();
|
|
@@ -299,31 +429,51 @@ export async function cleanCheckouts(store, pool, now, options) {
|
|
|
299
429
|
// `only`: the checkouts a person saw in the preview; nothing they didn't see goes.
|
|
300
430
|
const rows = store.listWorktrees().filter(row => existsSync(row.path) && (options.repo === undefined || row.repo === options.repo) && (options.only === undefined || options.only.has(row.path)));
|
|
301
431
|
let statuses = taskStatuses(store, now());
|
|
432
|
+
const days = options.idle === undefined ? null : idleDays(store);
|
|
433
|
+
let facts = days === null ? { byRef: new Map(), byBranch: new Map() } : idleFacts(store);
|
|
434
|
+
// The checkouts going for being idle, and since when: judged at every look, so a task coming back drops out.
|
|
435
|
+
const idle = new Map();
|
|
436
|
+
const mayGo = (row) => {
|
|
437
|
+
idle.delete(row.path);
|
|
438
|
+
if (keptFor(pool, row, statuses, now(), options.manual ? "manual" : cleanup).why === null)
|
|
439
|
+
return options.spare?.(row.path) !== true;
|
|
440
|
+
const since = idleSince(pool, row, statuses, facts, days, now());
|
|
441
|
+
if (since === null)
|
|
442
|
+
return false;
|
|
443
|
+
idle.set(row.path, since);
|
|
444
|
+
return options.spare?.(row.path) !== true;
|
|
445
|
+
};
|
|
446
|
+
const refresh = () => { statuses = taskStatuses(store, now()); if (days !== null)
|
|
447
|
+
facts = idleFacts(store); };
|
|
302
448
|
const sizes = new Map();
|
|
303
449
|
const wanted = (row, fresh) => {
|
|
304
450
|
// Right before a checkout goes, where its task stands is read afresh, and its size taken for the ledger.
|
|
305
451
|
// `spare`: live work uses it (storage-sweep.ts), asked at both looks.
|
|
306
452
|
if (fresh)
|
|
307
|
-
|
|
308
|
-
const go =
|
|
453
|
+
refresh();
|
|
454
|
+
const go = mayGo(row);
|
|
309
455
|
if (go && fresh) {
|
|
310
456
|
sizes.set(row.path, diskBytes([row.path]).get(row.path) ?? 0);
|
|
311
457
|
// du can be slow. The reservation excludes a concurrent checkout lease, and this last read also catches
|
|
312
458
|
// an open run/claim or task change that arrived while size was measured in the sweep child.
|
|
313
|
-
|
|
459
|
+
refresh();
|
|
460
|
+
return mayGo(row) && !(idle.has(row.path) && options.idle?.occupied(row.path) === true);
|
|
314
461
|
}
|
|
315
462
|
return go;
|
|
316
463
|
};
|
|
317
|
-
const
|
|
464
|
+
const proof = async (row) => idle.has(row.path) ? pool.proveRecreatable(row, recordedHeads(store, row)) : "clean";
|
|
465
|
+
const max = options.manual ? Infinity : AUTO_MAX;
|
|
466
|
+
const pruned = await pool.prune(rows, wanted, max, undefined, proof);
|
|
318
467
|
const removed = [];
|
|
319
468
|
for (const row of pruned.removed) {
|
|
320
469
|
const taskId = row.taskRef === null ? null : store.externalIdFor(row.taskRef);
|
|
321
470
|
const bytes = sizes.get(row.path) ?? 0;
|
|
322
|
-
|
|
471
|
+
const since = idle.get(row.path);
|
|
472
|
+
removed.push({ path: row.path, repo: row.repo, branch: row.branch, taskId, bytes, releasedAt: row.releasedAt, why: null, ...(since === undefined ? {} : { idleSince: since }) });
|
|
323
473
|
store.recordAction({ at: now().toISOString(), actor: options.actor, repo: row.repo, taskId, runId: null, action: "checkout removed", outcome: "removed",
|
|
324
|
-
source: options.manual ? "request" : "work", detail: `${basename(row.path)} (released ${row.releasedAt?.slice(0, 10) ?? "?"}, about ${bytesWords(bytes)}); branch ${row.branch} kept` });
|
|
474
|
+
source: options.manual ? "request" : "work", detail: `${basename(row.path)} (${since === undefined ? "" : `idle since ${since.slice(0, 10)}, `}released ${row.releasedAt?.slice(0, 10) ?? "?"}, about ${bytesWords(bytes)}); branch ${row.branch} kept` });
|
|
325
475
|
}
|
|
326
|
-
return { removed, kept: pruned.kept, freed: removed.reduce((sum, one) => sum + one.bytes, 0) };
|
|
476
|
+
return { removed, kept: pruned.kept, freed: removed.reduce((sum, one) => sum + one.bytes, 0), ...(pruned.removed.length >= max ? { more: true } : {}) };
|
|
327
477
|
}
|
|
328
478
|
/** Checkouts this process already looked at since they were let go (path and release time): looked at once. */
|
|
329
479
|
const looked = new Set();
|
|
@@ -381,6 +531,8 @@ export function whyWords(item) {
|
|
|
381
531
|
case "release candidate": return item.dueAt === undefined ? "the deployed release candidate" : `newest release candidate, kept for its deploy until ${item.dueAt.slice(0, 10)}`;
|
|
382
532
|
case "has changes": return "has changes";
|
|
383
533
|
case "has commits": return "has commits on no branch";
|
|
534
|
+
case "not on its branch": return "isn't on its task's branch";
|
|
535
|
+
case "idle": return `idle since ${item.idleSince?.slice(0, 10) ?? "a while"}; the next sweep removes it`;
|
|
384
536
|
case "unreadable": return "git couldn't read it";
|
|
385
537
|
case "git refused": return "git wouldn't remove it";
|
|
386
538
|
case "not due yet": return `due ${item.dueAt?.slice(0, 10) ?? "later"}`;
|
package/dist/claim.d.ts
CHANGED
|
@@ -214,6 +214,8 @@ export declare function acquireIfReady(store: Store, taskRef: number, runner: st
|
|
|
214
214
|
provider?: string;
|
|
215
215
|
/** The model this dispatch would run — the quota scope. */
|
|
216
216
|
model?: string;
|
|
217
|
+
/** A fallback entry's pinned auth mode: its quota is that credential's, never the base's. */
|
|
218
|
+
authMode?: "subscription" | "api-key";
|
|
217
219
|
/** What this dispatch is FOR. Re-proved inside the transaction — the
|
|
218
220
|
* caller's survey is not trusted (Codex planning review, finding 2). */
|
|
219
221
|
dispatchRole?: "builder" | "planner" | "scout";
|
|
@@ -348,6 +350,10 @@ export type FailureDisposition = {
|
|
|
348
350
|
ok: true;
|
|
349
351
|
disposition: "auth-expired";
|
|
350
352
|
provider: string;
|
|
353
|
+
} | {
|
|
354
|
+
ok: true;
|
|
355
|
+
disposition: "fallback";
|
|
356
|
+
provider: string;
|
|
351
357
|
} | {
|
|
352
358
|
ok: false;
|
|
353
359
|
reason: "fenced" | "unknown" | "stopped";
|
package/dist/claim.js
CHANGED
|
@@ -38,6 +38,7 @@ import { sealUnchangedPlanUnderMode } from "./plan-auto.js";
|
|
|
38
38
|
import { routeFromJson } from "./phase-routing.js";
|
|
39
39
|
import { contractChangesOf, decodePlannerSource, encodePlannerSource, canonicalContractJson, PLANNER_SOURCE_LIMITS, describeContractChanges, encodePlanContractRecord, plannerContractOf, plannerSourceDigest, } from "./planner-source.js";
|
|
40
40
|
import { storeEvidence, readVerifiedArtifact, EVIDENCE_CAPS } from "./evidence.js";
|
|
41
|
+
import { moveOnIfExhausted } from "./fallback.js";
|
|
41
42
|
/** How stale a mirror's last complete sync may be before admission refuses. */
|
|
42
43
|
export const SYNC_MAX_AGE_MS = 15 * 60_000;
|
|
43
44
|
/**
|
|
@@ -298,7 +299,7 @@ export function acquireIfReady(store, taskRef, runner, options) {
|
|
|
298
299
|
// Quota is keyed to the credential that actually exhausts: the RESOLVED
|
|
299
300
|
// provider, never a fixed binary name — codex quota must not collide
|
|
300
301
|
// with claude's, nor bypass it (Codex provider review, high finding 5).
|
|
301
|
-
const quota = store.quotaState(runner, options.provider ?? "claude", scope, options.now);
|
|
302
|
+
const quota = store.quotaState(runner, options.provider ?? "claude", scope, options.now, options.authMode ?? "subscription");
|
|
302
303
|
if (quota !== null && quota.state === "exhausted") {
|
|
303
304
|
return {
|
|
304
305
|
ok: false,
|
|
@@ -349,7 +350,7 @@ export function acquireIfReady(store, taskRef, runner, options) {
|
|
|
349
350
|
// exhausted with no probe in flight (finding 3). Same transaction, so
|
|
350
351
|
// consume-with-claim is all-or-nothing.
|
|
351
352
|
if (taken.ok && quota !== null) {
|
|
352
|
-
store.consumeHalfOpen(runner, options.provider ?? "claude", scope, options.now);
|
|
353
|
+
store.consumeHalfOpen(runner, options.provider ?? "claude", scope, options.now, options.authMode ?? "subscription");
|
|
353
354
|
}
|
|
354
355
|
return taken;
|
|
355
356
|
});
|
|
@@ -880,6 +881,16 @@ function sealFailure(store, leaseId, args) {
|
|
|
880
881
|
if (failureClass !== "commit-failure" && run.terminalClass === "auth-expired") {
|
|
881
882
|
return { ok: true, ...sealAuthExpired(store, run, taskId, now) };
|
|
882
883
|
}
|
|
884
|
+
// Out of plan on an approved chain: the walk moves on, no strike, and the next agent runs through the ordinary claim.
|
|
885
|
+
// Anything else about this ending (fallback.ts says what closed the walk) stays the ordinary road below.
|
|
886
|
+
// Only the agent's own exit moves: a lease, branch or HEAD law that failed, or a commit that failed, is that ending.
|
|
887
|
+
if (failureClass === "unknown") {
|
|
888
|
+
const moved = moveOnIfExhausted(store, runId, taskId, now);
|
|
889
|
+
if (moved !== null) {
|
|
890
|
+
store.finishRun(runId, { outcome: "failed", reason: "usage-exhausted", now });
|
|
891
|
+
return { ok: true, disposition: "fallback", provider: moved.to.provider };
|
|
892
|
+
}
|
|
893
|
+
}
|
|
883
894
|
store.finishRun(runId, { outcome: "failed", reason: failureClass, now });
|
|
884
895
|
if (failureClass === "commit-failure") {
|
|
885
896
|
// The work exists and is uncommitted; nothing times its way out of
|
package/dist/cli-help.d.ts
CHANGED
|
@@ -2,4 +2,7 @@
|
|
|
2
2
|
* The top-level usage text, on its own so `toolroll --help` can print it
|
|
3
3
|
* without loading every command family (see cli-fast.ts).
|
|
4
4
|
*/
|
|
5
|
-
export declare const HELP = "toolroll \u2014
|
|
5
|
+
export declare const HELP = "toolroll \u2014 hand coding work to agents, walk away, come back to pull requests\n\nUsage\n toolroll at your terminal: start Toolroll here (or attach) and open it in the terminal\n (--web: the browser; --project-root <dir>: where projects live)\n in a repository without a terminal, under an agent or with --json: only says what it would do\n toolroll open [--web] open this computer's Toolroll again, signed in\n toolroll welcome the three steps to a first result (--json: the model)\n toolroll password set add a password (typed twice, at your terminal)\n toolroll scan [path...] report what is in flight (also bare `toolroll` outside a repository)\n toolroll [path...] the same report for the paths given\n toolroll pulls report what is waiting on a person\n toolroll graph report which work graph is already here\n toolroll repos list connected repositories, and how to adjust\n toolroll repos add <path> add a project; it is in the console at once (no path: the repo you are in)\n toolroll repos remove <path>\n take it out of the console; its tasks and results stay\n toolroll repos add-from-github <owner/name> --root <dir>\n preview, then clone and connect (--yes)\n toolroll link put `toolroll` on your PATH\n toolroll unlink take it off again\n toolroll update update to the latest release: verified, drained, undoable\n (preview first; --yes, --now, --at HH:MM, --rollback)\n toolroll release <branch> release Toolroll itself: checks, your approval, deploy, merge, tag, publish\n (rerun the same command to continue; --help)\n toolroll skills install --claude-code [--dir <path>]\n install this binary's guides for Claude Code (preview first)\n toolroll skills install teach a repo's agents this queue exists (preview first)\n toolroll demo a seeded throwaway sandbox \u2014 see it working in 90 seconds\n toolroll up app + builder for every saved project \u2014 the normal start\n toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)\n toolroll status running, queued, ready results, release check and plan windows\n toolroll integrations which integrations work, and what to do about the ones that don't (--json)\n toolroll lead token mint your lead agent's sign-in: its own work pings nobody\n toolroll lead say \"<text>\" (the lead) one short message in its person's chat\n\nTasks \u2014 `toolroll task` prints the whole list,\nand any task command + --help prints it too\n toolroll approver add <name>\n add a person who can approve work\n toolroll task add <title> queue work\n toolroll task show <id> one task: its plan, builds and what it waits for\n toolroll task approve <id>\n the yes \u2014 nothing builds without one\n toolroll task accept <id> --finish\n accept the result you read (--despite \"<why>\": accept it with a reason, without finishing)\n toolroll task send-back <id> --feedback \"<what to change>\"\n send the result back for changes\n toolroll task retry <id> build it again\n toolroll task ready <id> can I merge it? each check, and what clears a blocker\n toolroll chat --as <you> --token <t>\n talk to your lead: one conversation across every\n project; it proposes, you confirm (--say \"\u2026\" for one turn)\n toolroll card <source> the data chat shows as a card: tasks, results, flows, spend,\n projects, builders, mode, integrations, providers, findings, handover\n toolroll flows list | show <id> | create | edit | trigger | script | card | archive\n set up and inspect flows (writes preview until --yes)\n\nMachine and agent commands (the builder, agent tools, recovery):\n toolroll internals lists them; each keeps working as before\n\nWith nothing connected it reports everything it can find below the working\ndirectory. Once you connect repositories it reports those instead.\n\nOptions\n --all report everything discoverable, ignoring connected repos\n --depth <n> directory levels to descend (default: 6)\n --hidden include dot-directories, which are skipped by default\n --dirty also read each working tree for uncommitted files\n --local branches only: no network, no pull requests, no issues\n --json emit a machine-readable envelope instead of a report\n -o <file> also write that envelope to a file (requires --json)\n -h, --help show this\n\nNothing is written, installed, or configured. Every repository is read\nthrough git with --no-optional-locks, so a scan never contends for the\nindex lock with an editor you have open.\n\n--dirty is off by default because reading a working tree is unbounded\nwork: on a large or cloud-synced repo `git status` can take minutes,\nwhere listing branches takes milliseconds.\n\nlink shows what it would do and changes nothing; add --yes to apply it,\nor --to <dir> to choose where. It never uses sudo, never leaves your\nhome directory, and never touches a file it did not create.";
|
|
6
|
+
/** The commands people rarely type: the builder's own loop, the machine contract and recovery. They all keep working;
|
|
7
|
+
* `toolroll internals` (or `toolroll internals --help`) lists them so top-level help stays about the work. */
|
|
8
|
+
export declare const INTERNALS_HELP = "toolroll internals \u2014 machine and agent commands\n\n toolroll contract the machine contract: envelope version + capabilities\n (--commands dumps the declared command guide)\n toolroll skills list the guides this exact binary serves\n toolroll skills get <name> print one guide (version-matched, never stale)\n toolroll health recent server latency, write waits and live streams\n toolroll ready what could be started right now\n toolroll check-progress <run> current or final approved check progress\n toolroll task wait <id> [--timeout <seconds>]\n wait for this attempt to finish or need a person\n toolroll task scope <id> --goal <text>\n write a task's plan from the command line\n toolroll claim <id> --runner <name>\n toolroll heartbeat <lease> / worker release <lease> / reap\n toolroll tick --runner <name> --token <t> --repo <path>\n one unattended pass over the ready set\n toolroll serve --repo <path> run only the console\n (--editor vscode with --runner: file links\n open in VS Code on the device you browse from)\n toolroll watch / daemon install run only the builder\n toolroll doctor check that Toolroll's service holds no key a build could read\n toolroll reconcile recover what the last stretch left behind\n toolroll release --recover-deploy [--stage <dir>] [--yes]\n toolroll release --release-gate <id> [--yes]\n the release tool's recovery steps (toolroll release <branch> runs them for you)";
|
package/dist/cli-help.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* without loading every command family (see cli-fast.ts).
|
|
4
4
|
*/
|
|
5
5
|
import { DEFAULT_MAX_DEPTH } from "./scan.js";
|
|
6
|
-
export const HELP = `toolroll —
|
|
6
|
+
export const HELP = `toolroll — hand coding work to agents, walk away, come back to pull requests
|
|
7
7
|
|
|
8
8
|
Usage
|
|
9
9
|
toolroll at your terminal: start Toolroll here (or attach) and open it in the terminal
|
|
@@ -26,52 +26,43 @@ Usage
|
|
|
26
26
|
toolroll unlink take it off again
|
|
27
27
|
toolroll update update to the latest release: verified, drained, undoable
|
|
28
28
|
(preview first; --yes, --now, --at HH:MM, --rollback)
|
|
29
|
-
toolroll release <branch> release Toolroll itself:
|
|
30
|
-
(
|
|
31
|
-
toolroll contract the machine contract: envelope version + capabilities
|
|
32
|
-
(--commands dumps the declared command guide)
|
|
29
|
+
toolroll release <branch> release Toolroll itself: checks, your approval, deploy, merge, tag, publish
|
|
30
|
+
(rerun the same command to continue; --help)
|
|
33
31
|
toolroll skills install --claude-code [--dir <path>]
|
|
34
32
|
install this binary's guides for Claude Code (preview first)
|
|
35
33
|
toolroll skills install teach a repo's agents this queue exists (preview first)
|
|
36
|
-
toolroll skills list the guides this exact binary serves
|
|
37
|
-
toolroll skills get <name> print one guide (version-matched, never stale)
|
|
38
34
|
toolroll demo a seeded throwaway sandbox — see it working in 90 seconds
|
|
39
35
|
toolroll up app + builder for every saved project — the normal start
|
|
40
36
|
toolroll onboard make your agent Toolroll's lead: add this repo, install its skill (--yes), offer starter flows (--starter)
|
|
41
37
|
toolroll status running, queued, ready results, release check and plan windows
|
|
42
|
-
toolroll health recent server latency, write waits and live streams
|
|
43
38
|
toolroll integrations which integrations work, and what to do about the ones that don't (--json)
|
|
44
|
-
toolroll lead token mint your lead agent's
|
|
39
|
+
toolroll lead token mint your lead agent's sign-in: its own work pings nobody
|
|
45
40
|
toolroll lead say "<text>" (the lead) one short message in its person's chat
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
and any
|
|
42
|
+
Tasks — \`toolroll task\` prints the whole list,
|
|
43
|
+
and any task command + --help prints it too
|
|
49
44
|
toolroll approver add <name>
|
|
50
|
-
|
|
51
|
-
toolroll ready what could be dispatched right now
|
|
52
|
-
toolroll check-progress <run> current or final approved check progress
|
|
53
|
-
toolroll task wait <id> [--timeout <seconds>]
|
|
54
|
-
wait for this attempt to finish or need a person
|
|
45
|
+
add a person who can approve work
|
|
55
46
|
toolroll task add <title> queue work
|
|
56
|
-
toolroll task
|
|
57
|
-
state what success is; approval binds to it
|
|
47
|
+
toolroll task show <id> one task: its plan, builds and what it waits for
|
|
58
48
|
toolroll task approve <id>
|
|
59
49
|
the yes — nothing builds without one
|
|
60
|
-
toolroll
|
|
61
|
-
|
|
62
|
-
toolroll
|
|
63
|
-
|
|
64
|
-
toolroll
|
|
65
|
-
|
|
50
|
+
toolroll task accept <id> --finish
|
|
51
|
+
accept the result you read (--despite "<why>": accept it with a reason, without finishing)
|
|
52
|
+
toolroll task send-back <id> --feedback "<what to change>"
|
|
53
|
+
send the result back for changes
|
|
54
|
+
toolroll task retry <id> build it again
|
|
55
|
+
toolroll task ready <id> can I merge it? each check, and what clears a blocker
|
|
66
56
|
toolroll chat --as <you> --token <t>
|
|
67
57
|
talk to your lead: one conversation across every
|
|
68
58
|
project; it proposes, you confirm (--say "…" for one turn)
|
|
69
|
-
toolroll
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
59
|
+
toolroll card <source> the data chat shows as a card: tasks, results, flows, spend,
|
|
60
|
+
projects, builders, mode, integrations, providers, findings, handover
|
|
61
|
+
toolroll flows list | show <id> | create | edit | trigger | script | card | archive
|
|
62
|
+
set up and inspect flows (writes preview until --yes)
|
|
63
|
+
|
|
64
|
+
Machine and agent commands (the builder, agent tools, recovery):
|
|
65
|
+
toolroll internals lists them; each keeps working as before
|
|
75
66
|
|
|
76
67
|
With nothing connected it reports everything it can find below the working
|
|
77
68
|
directory. Once you connect repositories it reports those instead.
|
|
@@ -97,3 +88,31 @@ where listing branches takes milliseconds.
|
|
|
97
88
|
link shows what it would do and changes nothing; add --yes to apply it,
|
|
98
89
|
or --to <dir> to choose where. It never uses sudo, never leaves your
|
|
99
90
|
home directory, and never touches a file it did not create.`;
|
|
91
|
+
/** The commands people rarely type: the builder's own loop, the machine contract and recovery. They all keep working;
|
|
92
|
+
* `toolroll internals` (or `toolroll internals --help`) lists them so top-level help stays about the work. */
|
|
93
|
+
export const INTERNALS_HELP = `toolroll internals — machine and agent commands
|
|
94
|
+
|
|
95
|
+
toolroll contract the machine contract: envelope version + capabilities
|
|
96
|
+
(--commands dumps the declared command guide)
|
|
97
|
+
toolroll skills list the guides this exact binary serves
|
|
98
|
+
toolroll skills get <name> print one guide (version-matched, never stale)
|
|
99
|
+
toolroll health recent server latency, write waits and live streams
|
|
100
|
+
toolroll ready what could be started right now
|
|
101
|
+
toolroll check-progress <run> current or final approved check progress
|
|
102
|
+
toolroll task wait <id> [--timeout <seconds>]
|
|
103
|
+
wait for this attempt to finish or need a person
|
|
104
|
+
toolroll task scope <id> --goal <text>
|
|
105
|
+
write a task's plan from the command line
|
|
106
|
+
toolroll claim <id> --runner <name>
|
|
107
|
+
toolroll heartbeat <lease> / worker release <lease> / reap
|
|
108
|
+
toolroll tick --runner <name> --token <t> --repo <path>
|
|
109
|
+
one unattended pass over the ready set
|
|
110
|
+
toolroll serve --repo <path> run only the console
|
|
111
|
+
(--editor vscode with --runner: file links
|
|
112
|
+
open in VS Code on the device you browse from)
|
|
113
|
+
toolroll watch / daemon install run only the builder
|
|
114
|
+
toolroll doctor check that Toolroll's service holds no key a build could read
|
|
115
|
+
toolroll reconcile recover what the last stretch left behind
|
|
116
|
+
toolroll release --recover-deploy [--stage <dir>] [--yes]
|
|
117
|
+
toolroll release --release-gate <id> [--yes]
|
|
118
|
+
the release tool's recovery steps (toolroll release <branch> runs them for you)`;
|
package/dist/cli.js
CHANGED
|
@@ -37,7 +37,8 @@ import { detectGraphs, chooseBackend, setupOptions, LABELS as GRAPH_LABELS, } fr
|
|
|
37
37
|
import { openStore, openStoreReadOnly, databasePath } from "./store.js";
|
|
38
38
|
import { renderReport, renderPulls, renderGraph } from "./render.js";
|
|
39
39
|
import { DEFAULT_MAX_DEPTH } from "./scan.js";
|
|
40
|
-
import { HELP } from "./cli-help.js";
|
|
40
|
+
import { HELP, INTERNALS_HELP } from "./cli-help.js";
|
|
41
|
+
import { withTaskAlias } from "./task-aliases.js";
|
|
41
42
|
import { quickAnswer } from "./cli-fast.js";
|
|
42
43
|
import { BIN_NAME, chooseTarget, planLink, applyLink, planUnlink, applyUnlink, } from "./link.js";
|
|
43
44
|
const USAGE_EXIT = 2;
|
|
@@ -137,7 +138,13 @@ export const OPERATE_COMMANDS = new Set([
|
|
|
137
138
|
"review",
|
|
138
139
|
"assignment",
|
|
139
140
|
"knowledge",
|
|
141
|
+
"map",
|
|
140
142
|
"memory",
|
|
143
|
+
"flaky",
|
|
144
|
+
"card",
|
|
145
|
+
// Follow-through (follow-through-cli.ts): findings and saved provider failures.
|
|
146
|
+
"finding",
|
|
147
|
+
"captures",
|
|
141
148
|
"models",
|
|
142
149
|
"claim",
|
|
143
150
|
"heartbeat",
|
|
@@ -305,6 +312,8 @@ function routinesMoved(argv, write) {
|
|
|
305
312
|
return 0;
|
|
306
313
|
}
|
|
307
314
|
async function dispatch(argv, write, mainOptions) {
|
|
315
|
+
// People's names for task commands (task send-back, retry, recheck) are the commands they name, before anything else.
|
|
316
|
+
argv = withTaskAlias(argv);
|
|
308
317
|
// v115: routines became scheduled flows. Every old `routine …` invocation answers with one line pointing there —
|
|
309
318
|
// hidden from help and the contract, before anything remote or local opens, changing nothing.
|
|
310
319
|
if (argv[0] === "routine")
|
|
@@ -321,6 +330,11 @@ async function dispatch(argv, write, mainOptions) {
|
|
|
321
330
|
// Command families below load when their command runs, so one command never pays for the others.
|
|
322
331
|
if (argv[0] === 'health')
|
|
323
332
|
return (await import("./health-cli.js")).runHealthCommand(argv.slice(1), write, mainOptions.connect ?? mainOptions.remote);
|
|
333
|
+
// The machine and agent commands, listed apart so top-level help stays about the work (each still runs by name).
|
|
334
|
+
if (argv[0] === 'internals' && argv.slice(1).every(arg => arg === '--help' || arg === '-h')) {
|
|
335
|
+
write(INTERNALS_HELP);
|
|
336
|
+
return 0;
|
|
337
|
+
}
|
|
324
338
|
// This computer's own service, read here and never on a central server.
|
|
325
339
|
if (argv[0] === 'doctor')
|
|
326
340
|
return (await import("./doctor.js")).runDoctorCommand(argv.slice(1), write);
|