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/task-status.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { DEFAULT_LEAD_NAME } from "./lead-identity.js";
|
|
14
14
|
import { html } from "./html.js";
|
|
15
15
|
import { FAILED_CHECK } from "./result-acts.js";
|
|
16
|
-
import {
|
|
16
|
+
import { CODE_READING, NEEDS, needSentence, waitSentence } from "./needs-you.js";
|
|
17
17
|
/** A cancelled task with a successor reads "Replaced by <id>" everywhere, never "Cancelled". */
|
|
18
18
|
export const replacedWords = (successor) => `Replaced by ${successor}`;
|
|
19
19
|
const REPLACED = /^Replaced by \S+/;
|
|
@@ -28,6 +28,8 @@ export const CHECKS_LABEL = "Project checks";
|
|
|
28
28
|
export const PR_CI_LABEL = "PR CI";
|
|
29
29
|
/** The one verb for opening a saved result, on every surface. */
|
|
30
30
|
export const OPEN_RESULT = "Open result";
|
|
31
|
+
/** A machine-run check under way (a prepared candidate: no agent runs), on every surface that has no richer words. */
|
|
32
|
+
export const MACHINE_CHECK_RUNNING = "Running the check on the prepared version.";
|
|
31
33
|
export const CHECKED_AT_RELEASE = "Checked at release";
|
|
32
34
|
export function releaseStateOf(checks) {
|
|
33
35
|
if (checks?.level !== "off" || checks.status === "passed" || checks.status === "failed")
|
|
@@ -162,46 +164,47 @@ function sentenceOf(headline, facts) {
|
|
|
162
164
|
const reason = facts.reason?.trim() || null;
|
|
163
165
|
const sha = short(facts.checks?.head ?? null);
|
|
164
166
|
switch (headline) {
|
|
165
|
-
case "Queued": return reason ?? "Waiting for a
|
|
167
|
+
case "Queued": return reason ?? "Waiting for a builder.";
|
|
166
168
|
case "Planning": return reason ?? "The lead is writing the plan.";
|
|
167
169
|
// One plain sentence per need (needs-you.ts); a recorded reason only where the need keeps one.
|
|
168
170
|
case "Needs you": return facts.stage === "finished" ? resultAsk(facts) ?? needSentence("review-result", reason, facts.needContext) : needSentence(facts.need ?? "other", reason, facts.needContext);
|
|
169
171
|
case "Waiting": return waitSentence(facts.wait, reason, facts.needContext);
|
|
170
172
|
case "Building": return facts.stage === "checking" ? "Checks are running on the change."
|
|
171
|
-
: facts.stage === "reviewing" ? "Reviewing the change before it reaches you."
|
|
173
|
+
: facts.stage === "reviewing" ? "Reviewing the change before it reaches you."
|
|
174
|
+
: facts.machine === true ? MACHINE_CHECK_RUNNING : reason ?? "An agent is working on it.";
|
|
172
175
|
case "Ready for review":
|
|
173
176
|
if (facts.report)
|
|
174
|
-
return "The report is ready to read. Read it, then
|
|
177
|
+
return "The report is ready to read. Read it, then accept it.";
|
|
175
178
|
if (releaseStateOf(facts.checks) === "covered")
|
|
176
|
-
return `Checked at release on ${short(facts.checks.release.head) ?? "its release"}. Review the change, then
|
|
179
|
+
return `Checked at release on ${short(facts.checks.release.head) ?? "its release"}. Review the change, then accept it.`;
|
|
177
180
|
if (facts.checks?.level === "off" && facts.checks.status !== "passed")
|
|
178
|
-
return "Checked at release. Review the change, then
|
|
181
|
+
return "Checked at release. Review the change, then accept it.";
|
|
179
182
|
if (facts.checks?.status === "passed") {
|
|
180
183
|
// The Checks row names the others and the batch commit; the sentence doesn't repeat them.
|
|
181
184
|
const peers = facts.checks.batch?.state === "together" ? facts.checks.batch.peers.length : 0;
|
|
182
185
|
if (peers > 0)
|
|
183
|
-
return `Checks passed together with ${peers === 1 ? "1 other result" : `${peers} other results`}. Review the change, then
|
|
184
|
-
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} passed${sha === null ? "" : ` on ${sha}`}. Review the change, then
|
|
186
|
+
return `Checks passed together with ${peers === 1 ? "1 other result" : `${peers} other results`}. Review the change, then accept it.`;
|
|
187
|
+
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} passed${sha === null ? "" : ` on ${sha}`}. Review the change, then accept it.`;
|
|
185
188
|
}
|
|
186
189
|
if (facts.checks?.batch?.state === "waiting")
|
|
187
|
-
return "Its full check runs with other results within 10 minutes. Review the change, then
|
|
190
|
+
return "Its full check runs with other results within 10 minutes. Review the change, then accept it.";
|
|
188
191
|
if (facts.checks?.status === "unavailable")
|
|
189
|
-
return `${facts.checks.cause === "command-changed" ? CHECK_COMMAND_CHANGED : CHECK_UNREADABLE} Review the change, then
|
|
192
|
+
return `${facts.checks.cause === "command-changed" ? CHECK_COMMAND_CHANGED : CHECK_UNREADABLE} Review the change, then accept it.`;
|
|
190
193
|
// Never claim a check that isn't known to have passed.
|
|
191
194
|
if (facts.checks == null)
|
|
192
|
-
return "Review the change, then
|
|
193
|
-
return "Built without a passing project check. Review the change, then
|
|
195
|
+
return "Review the change, then accept it.";
|
|
196
|
+
return "Built without a passing project check. Review the change, then accept it.";
|
|
194
197
|
case "Complete": {
|
|
195
198
|
// Landed work says why once; its merged pull request is that reason, not a second sentence.
|
|
196
199
|
if (facts.landed)
|
|
197
|
-
return `
|
|
198
|
-
const by = facts.completedBy ? `
|
|
200
|
+
return `Accepted automatically: ${facts.landed}.`;
|
|
201
|
+
const by = facts.completedBy ? `Accepted by ${facts.completedBy}.` : "Accepted.";
|
|
199
202
|
const pr = facts.pullRequest;
|
|
200
203
|
return pr?.state === "merged" ? `${by} ${pr.number === null ? "Its pull request" : `Pull request #${pr.number}`} merged.` : by;
|
|
201
204
|
}
|
|
202
205
|
case "Failed":
|
|
203
206
|
if (facts.checks?.status === "failed")
|
|
204
|
-
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} failed${sha === null ? "" : ` on ${sha}`}. See what broke, then retry or
|
|
207
|
+
return `${facts.checks.level === "quick" ? "Quick checks" : "Checks"} failed${sha === null ? "" : ` on ${sha}`}. See what broke, then retry or send it back.`;
|
|
205
208
|
return reason ?? "The last attempt stopped before it finished. Review it, then retry.";
|
|
206
209
|
case "Stopped": return reason ?? "Stopped by a person. The work so far is kept.";
|
|
207
210
|
}
|
|
@@ -291,13 +294,13 @@ function detailsOf(headline, facts) {
|
|
|
291
294
|
if (evidence != null) {
|
|
292
295
|
const action = { label: "See what was kept", href: facts.links?.result ?? null };
|
|
293
296
|
if (evidence.damaged > 0)
|
|
294
|
-
row("evidence", "Saved
|
|
297
|
+
row("evidence", "Saved files", "Some saved files changed", problem(headline), headline === "Failed" ? {} : { action });
|
|
295
298
|
else if (evidence.missing > 0)
|
|
296
|
-
row("evidence", "Saved
|
|
299
|
+
row("evidence", "Saved files", "Some saved files are missing", problem(headline), headline === "Failed" ? {} : { action });
|
|
297
300
|
else if (evidence.shortened > 0)
|
|
298
|
-
row("evidence", "Saved
|
|
301
|
+
row("evidence", "Saved files", "Some output shortened", "note", { action });
|
|
299
302
|
else
|
|
300
|
-
row("evidence", "Saved
|
|
303
|
+
row("evidence", "Saved files", "All kept", "ok");
|
|
301
304
|
}
|
|
302
305
|
return rows;
|
|
303
306
|
}
|
|
@@ -331,24 +334,9 @@ export function statusOf(facts) {
|
|
|
331
334
|
/** The name most surfaces already import. */
|
|
332
335
|
export const taskStatusOf = statusOf;
|
|
333
336
|
// ---- reading the existing projections ----------------------------------------
|
|
334
|
-
/**
|
|
335
|
-
*
|
|
336
|
-
const
|
|
337
|
-
queued: ["queued"], ready: ["queued"], "worker-at-capacity": ["queued"], "retry-scheduled": ["queued"], updating: ["queued"],
|
|
338
|
-
"waiting-dependency": ["queued"], "scouting-ready": ["queued"], "planning-ready": ["queued"], "provider-quota": ["queued"],
|
|
339
|
-
// A request the planner cannot be given (too long to hand back, say) waits on a person to shorten it.
|
|
340
|
-
"planner-source": ["needs-you", "fix-request"],
|
|
341
|
-
running: ["building"], reviewing: ["reviewing"], "review-pending": ["building"], "checks-running": ["checking"],
|
|
342
|
-
"signed-out": ["needs-you", "sign-in"],
|
|
343
|
-
"needs-approval": ["needs-you", "approval"],
|
|
344
|
-
"waiting-decision": ["needs-you", "answer"], "decision-queue": ["needs-you", "answer"],
|
|
345
|
-
"build-stopping": ["waiting"], "other-computer": ["waiting"],
|
|
346
|
-
stopping: ["stopped"], stopped: ["stopped"], paused: ["stopped"], cancelled: ["stopped"],
|
|
347
|
-
failed: ["failed"], "waiting-incident": ["failed"], "checks-failed": ["failed"],
|
|
348
|
-
"ready-to-check": ["finished"], "ready-to-review": ["finished"], "report-ready": ["finished"], "no-change": ["finished"],
|
|
349
|
-
"pr-opened": ["finished"], "merge-observed": ["finished"], "pr-closed": ["finished"], "agent-attested": ["finished"], "accepted-exception": ["finished"],
|
|
350
|
-
complete: ["complete"],
|
|
351
|
-
};
|
|
337
|
+
/** Codes that read Needs you whoever asks (their reason is a person's by nature). Any other need reads Needs you
|
|
338
|
+
* only when the caller says a person is needed; otherwise it waits in the queue. */
|
|
339
|
+
const ALWAYS_NEEDS = new Set(["planner-source", "signed-out", "needs-approval", "waiting-decision", "decision-queue"]);
|
|
352
340
|
/** Queue reasons worth a sentence of their own; any other queued or
|
|
353
341
|
* building reason is scheduler wording, and the default sentence is plainer. */
|
|
354
342
|
const QUEUED_REASONS = new Set(["worker-at-capacity", "waiting-dependency", "retry-scheduled", "updating", "planning-ready", "scouting-ready", "provider-quota"]);
|
|
@@ -359,15 +347,18 @@ export function plainReasonOf(stage, code, detail) {
|
|
|
359
347
|
return QUEUED_REASONS.has(code) ? detail ?? null : null;
|
|
360
348
|
return detail ?? null;
|
|
361
349
|
}
|
|
350
|
+
/** Every dispatch or work-index code, as the stage it means (needs-you.ts CODE_READING, the one table). Unknown
|
|
351
|
+
* codes that wait on a person stay Needs you; everything else waits in the queue. */
|
|
362
352
|
export function stageOfCode(code, options = {}) {
|
|
363
353
|
if (code === "running" && options.planning)
|
|
364
354
|
return { stage: "planning" };
|
|
365
355
|
if (code === "held")
|
|
366
356
|
return options.operatorHold === false ? { stage: "needs-you", need: "hold" } : { stage: "stopped" };
|
|
367
|
-
const known =
|
|
368
|
-
if (known !== undefined)
|
|
369
|
-
return known
|
|
370
|
-
|
|
357
|
+
const known = Object.prototype.hasOwnProperty.call(CODE_READING, code) ? CODE_READING[code] : undefined;
|
|
358
|
+
if (known !== undefined && (known.stage !== "needs-you" || ALWAYS_NEEDS.has(code))) {
|
|
359
|
+
return { stage: known.stage, ...(known.need === undefined ? {} : { need: known.need }), ...(known.wait === undefined ? {} : { wait: known.wait }) };
|
|
360
|
+
}
|
|
361
|
+
return options.needsPerson === false ? { stage: "queued" } : { stage: "needs-you", need: known?.need ?? "other" };
|
|
371
362
|
}
|
|
372
363
|
/** The pull request as the task page's follower reports it, or the bare publication row. */
|
|
373
364
|
export function pullRequestFactOf(publication) {
|
|
@@ -394,6 +385,9 @@ export function pullRequestFactOf(publication) {
|
|
|
394
385
|
/** The assignment's stage. The work status, when the caller read one, names
|
|
395
386
|
* the exact reason (a running planner, a pause, a failed attempt, a sign-in). */
|
|
396
387
|
export function assignmentStageOf(assignment, work, planning = false) {
|
|
388
|
+
// A finding held for a person outlives the stage the work is in, a saved result included: only a person closes it.
|
|
389
|
+
if (work?.token === "finding-held" && assignment.state !== "cancelled")
|
|
390
|
+
return { stage: "needs-you", need: "finding" };
|
|
397
391
|
// The assignment's own reading (a build Toolroll can't confirm stopped) comes first.
|
|
398
392
|
const own = assignment.need ?? null;
|
|
399
393
|
if (own !== null && (assignment.state === "working" || assignment.state === "needs-decision"))
|
|
@@ -416,6 +410,8 @@ export function assignmentStageOf(assignment, work, planning = false) {
|
|
|
416
410
|
return { stage: "needs-you", need: "answer" };
|
|
417
411
|
if (action === "inspect-decisions")
|
|
418
412
|
return { stage: "needs-you", need: "questions" };
|
|
413
|
+
if (action === "inspect-findings")
|
|
414
|
+
return { stage: "needs-you", need: "finding" };
|
|
419
415
|
const token = work?.token ?? "";
|
|
420
416
|
// A failed attempt is Failed. A saved result that still needs a decision
|
|
421
417
|
// (its scope, a hold, a process exit) is Needs you, even if its checks
|
|
@@ -473,7 +469,7 @@ const quoted = (statement) => {
|
|
|
473
469
|
function unresolvedAsk(row, word) {
|
|
474
470
|
const what = word === "Not shown yet" && screenshotMissing(row) ? "A screenshot is missing for"
|
|
475
471
|
: word === "Not shown yet" ? "Nothing shows this requirement is met" : word === "Unverified" ? "This requirement couldn't be verified" : "This requirement isn't met";
|
|
476
|
-
return `${what}: ${quoted(row.statement) || row.id || "a requirement"}.
|
|
472
|
+
return `${what}: ${quoted(row.statement) || row.id || "a requirement"}. Send it back, or accept the result with a reason.`;
|
|
477
473
|
}
|
|
478
474
|
/** Requirements from the stored matrix: met, the ones only a person confirms, and the total. */
|
|
479
475
|
export function requirementsOf(matrix, release) {
|
|
@@ -482,7 +478,7 @@ export function requirementsOf(matrix, release) {
|
|
|
482
478
|
const words = matrix.map(row => requirementWordOf(row, null, release));
|
|
483
479
|
const yours = matrix.find((_, index) => words[index] === "You check");
|
|
484
480
|
const unshown = matrix.filter((row, index) => words[index] === "Not shown yet" && screenshotMissing(row));
|
|
485
|
-
const ask = yours !== undefined ? `Check this requirement yourself, then
|
|
481
|
+
const ask = yours !== undefined ? `Check this requirement yourself, then accept it: ${quoted(yours.statement)}`
|
|
486
482
|
: unshown.length > 0 ? unresolvedAsk(unshown[0], "Not shown yet") : null;
|
|
487
483
|
return { met: words.filter(word => word === "Met").length, total: matrix.length, yours: words.filter(word => word === "You check").length,
|
|
488
484
|
missed: words.filter(word => word === "Not met" || word === "Not shown yet").length,
|
|
@@ -499,12 +495,12 @@ export function completionBlockersOf(f) {
|
|
|
499
495
|
const release = releaseStateOf(f.checks);
|
|
500
496
|
const backing = checkBackingOf(f.checks);
|
|
501
497
|
if (!f.report && f.checks?.status === "failed") {
|
|
502
|
-
blockers.push({ key: "check-failed", message: `Checks failed${f.checks.exitCode === null ? "" : ` (exit ${f.checks.exitCode})`}. Fix them and run checks again, or
|
|
498
|
+
blockers.push({ key: "check-failed", message: `Checks failed${f.checks.exitCode === null ? "" : ` (exit ${f.checks.exitCode})`}. Fix them and run checks again, or send it back.` });
|
|
503
499
|
}
|
|
504
500
|
else if (!f.report && f.checkRequired && f.checks?.status !== "passed" && release === undefined) {
|
|
505
|
-
blockers.push({ key: "check-missing", message: checksUnderway(f.checks) ? "Its project check hasn't finished yet.
|
|
506
|
-
: f.checks?.status === "unavailable" ? f.checks.cause === "command-changed" ? `${CHECK_COMMAND_CHANGED.replace(/; run checks again\.$/, ".")} Run checks again, then
|
|
507
|
-
: "No passing project check is recorded for this result. Run checks on it, then
|
|
501
|
+
blockers.push({ key: "check-missing", message: checksUnderway(f.checks) ? "Its project check hasn't finished yet. Accept it once it passes."
|
|
502
|
+
: f.checks?.status === "unavailable" ? f.checks.cause === "command-changed" ? `${CHECK_COMMAND_CHANGED.replace(/; run checks again\.$/, ".")} Run checks again, then accept it.` : `${CHECK_UNREADABLE} Run checks on it, then accept it.`
|
|
503
|
+
: "No passing project check is recorded for this result. Run checks on it, then accept it." });
|
|
508
504
|
}
|
|
509
505
|
if (!f.accepted && f.checks?.status !== "failed") {
|
|
510
506
|
const refuted = f.verdict === "refuted";
|
|
@@ -514,11 +510,11 @@ export function completionBlockersOf(f) {
|
|
|
514
510
|
blockers.push({ key: "criteria", message: unresolvedAsk(unresolved[0].row, unresolved[0].word) });
|
|
515
511
|
else if (unresolved.length > 1) {
|
|
516
512
|
const ids = unresolved.map(one => one.row.id ?? "?");
|
|
517
|
-
blockers.push({ key: "criteria", message: `${unresolved.length} requirements ${unresolved.every(one => one.word === "Unverified") ? "couldn't be verified" : "aren't met yet"} (${ids.slice(0, 6).join(", ")}${ids.length > 6 ? ", …" : ""}).
|
|
513
|
+
blockers.push({ key: "criteria", message: `${unresolved.length} requirements ${unresolved.every(one => one.word === "Unverified") ? "couldn't be verified" : "aren't met yet"} (${ids.slice(0, 6).join(", ")}${ids.length > 6 ? ", …" : ""}). Send it back, or accept the result with a reason.` });
|
|
518
514
|
}
|
|
519
515
|
}
|
|
520
516
|
if (f.high > 0)
|
|
521
|
-
blockers.push({ key: "high", message: `The automatic review found ${f.high === 1 ? "a high-severity problem" : `${f.high} high-severity problems`}.
|
|
517
|
+
blockers.push({ key: "high", message: `The automatic review found ${f.high === 1 ? "a high-severity problem" : `${f.high} high-severity problems`}. Send it back before accepting it.` });
|
|
522
518
|
return blockers;
|
|
523
519
|
}
|
|
524
520
|
/** A refuted report verifies none of its requirements, whatever it marked met (a person's acceptance doesn't change that). */
|
package/dist/teardown-git.d.ts
CHANGED
|
@@ -46,6 +46,12 @@ export declare const INCLUDE_PARSE_TIMED_OUT = "Git didn't finish reading it in
|
|
|
46
46
|
export type GitBinding = {
|
|
47
47
|
repo?: string | null;
|
|
48
48
|
};
|
|
49
|
+
/** The shared git folder of a recorded project, read from disk and proven as boundDirs proves it: the project's own
|
|
50
|
+
* real `.git` folder, holding no `commondir`. No pointer file or `commondir` an agent can write decides it, and no git
|
|
51
|
+
* runs (a page visit reads this). Throws with the reason otherwise. */
|
|
52
|
+
export declare function recordedCommonDir(repo: string): string;
|
|
53
|
+
/** Where git incidents are recorded: beside the fenced views folder (git-incident.ts). */
|
|
54
|
+
export declare function gitIncidentRecord(): string;
|
|
49
55
|
/** Settings fixed on every write through a view: `worktree add` records absolute paths (relative ones would need a
|
|
50
56
|
* config write, which a view keeps); no transport at all (a caller that fetches allows its one protocol by name, as the
|
|
51
57
|
* nightly check's local fetch does with protocol.file.allow); and no split index (its shared index files would be
|
|
@@ -55,6 +61,12 @@ export declare const WRITE_VIEW_FLAGS: readonly string[];
|
|
|
55
61
|
* program (anything but INERT_LFS). A file that
|
|
56
62
|
* isn't a plain one, or doesn't parse, counts as naming one. */
|
|
57
63
|
export declare function lfsExtensionIn(checkout: string, gitFile?: string, limit?: Limit): boolean;
|
|
64
|
+
/** The hardened runners' own reads before the caller's command (the checkout's config listing): their own bounds, never
|
|
65
|
+
* the caller's. A caller's small `maxBuffer` is for its own command's output (builder.ts reads one ls-tree line with
|
|
66
|
+
* 2 KiB): inherited, it failed the config listing once a project's .git/config grew past 2 KiB (one [branch] section per
|
|
67
|
+
* work branch), and every prepared release check then failed as "its config couldn't be read" (runs 3316–3324). */
|
|
68
|
+
export declare const CONFIG_READ_BYTES: number;
|
|
69
|
+
export declare const CONFIG_READ_MS = 30000;
|
|
58
70
|
/** Every command runs against a view of the checkout's repository (gitView) whose config and info are copies the
|
|
59
71
|
* plane made, inspected there, so neither can change between the inspection and the run. `mode` "read" is for reading
|
|
60
72
|
* a checkout an agent may still be writing (the code page): every command also takes readFlags(), and the view holds
|
|
@@ -143,4 +155,10 @@ export declare function lazyFetchSetting(listing: string): string | null;
|
|
|
143
155
|
* command and protocol settings refused, a partial clone refused) is the config git then runs with.
|
|
144
156
|
*/
|
|
145
157
|
export declare function lazyFetchSafeGit(inner?: Runner): Runner;
|
|
158
|
+
/**
|
|
159
|
+
* The checkout a folder sits in, for choosing a folder (easy-start): the nearest parent holding a `.git`, then read
|
|
160
|
+
* through checkoutGitSync, so a config that includes another file (a FIFO), or sets a command, is refused and the read
|
|
161
|
+
* is bounded. Null when there is no checkout or it can't be read that way.
|
|
162
|
+
*/
|
|
163
|
+
export declare function checkoutTop(dir: string, timeoutMs?: number): string | null;
|
|
146
164
|
export {};
|
package/dist/teardown-git.js
CHANGED
|
@@ -191,7 +191,7 @@ function nestedRepository(path) {
|
|
|
191
191
|
* repository (boundDirs; `binding.repo` when the caller has the project's), is refused; so is a command whose
|
|
192
192
|
* repository changed under it while git ran.
|
|
193
193
|
*/
|
|
194
|
-
async function inView(cwd, binding, work, after = true, lockTimeoutMs, gitFile = "git") {
|
|
194
|
+
async function inView(cwd, binding, work, after = true, lockTimeoutMs, doing, gitFile = "git") {
|
|
195
195
|
const root = checkoutRootOf(cwd);
|
|
196
196
|
if (root === null)
|
|
197
197
|
return NOT_A_REPOSITORY;
|
|
@@ -207,7 +207,7 @@ async function inView(cwd, binding, work, after = true, lockTimeoutMs, gitFile =
|
|
|
207
207
|
return await withCheckoutLock(realpathSync(homedir()), key, async () => {
|
|
208
208
|
let view;
|
|
209
209
|
try {
|
|
210
|
-
view = gitView(root, "write", binding, gitFile);
|
|
210
|
+
view = gitView(root, "write", binding, gitFile, undefined, doing);
|
|
211
211
|
}
|
|
212
212
|
catch (error) {
|
|
213
213
|
return refused(error instanceof Error ? error.message : "the checkout couldn't be read");
|
|
@@ -255,6 +255,7 @@ export function teardownGit(runner = run, binding = {}, lockTimeoutMs, gitFile =
|
|
|
255
255
|
const forced = removal && args.includes("--force");
|
|
256
256
|
const inspection = args.includes("status") || args.includes("diff");
|
|
257
257
|
const target = removal ? resolve(cwd, args.at(-1)) : cwd;
|
|
258
|
+
const doing = teardownDoing(args);
|
|
258
259
|
const safeArgs = [...args];
|
|
259
260
|
if (inspection && !safeArgs.includes("--ignore-submodules=all")) {
|
|
260
261
|
const command = safeArgs.findIndex(arg => arg === "status" || arg === "diff");
|
|
@@ -288,8 +289,22 @@ export function teardownGit(runner = run, binding = {}, lockTimeoutMs, gitFile =
|
|
|
288
289
|
// the target's view, inspected above, so it judges the target by the target's own settings.
|
|
289
290
|
return ran(removal && !forced ? { ...view.env, GIT_COMMON_DIR: targetView.env["GIT_COMMON_DIR"] } : view.env);
|
|
290
291
|
// A removed target has no git directory left to prove: the folder it was removed from is proven instead.
|
|
291
|
-
}, !removal, lockTimeoutMs, gitFile);
|
|
292
|
-
}, true, lockTimeoutMs, gitFile);
|
|
292
|
+
}, !removal, lockTimeoutMs, doing, gitFile);
|
|
293
|
+
}, true, lockTimeoutMs, teardownDoing(args), gitFile);
|
|
294
|
+
}
|
|
295
|
+
/** What teardown git is doing, for an incident's notice: removing a checkout, or looking one over to tidy it. */
|
|
296
|
+
function teardownDoing(args) {
|
|
297
|
+
return args.includes("worktree") && args.includes("remove") ? "removing an old checkout" : "tidying an old checkout";
|
|
298
|
+
}
|
|
299
|
+
/** What a build's git is doing, for an incident's notice. */
|
|
300
|
+
function checkoutDoing(args) {
|
|
301
|
+
if (args.includes("worktree"))
|
|
302
|
+
return args.includes("remove") ? "removing a checkout" : args.includes("add") ? "making a checkout" : "listing checkouts";
|
|
303
|
+
if (args.some(arg => arg === "commit" || arg === "add" || arg === "merge" || arg === "rebase" || arg === "reset" || arg === "checkout" || arg === "switch"))
|
|
304
|
+
return "saving a build's work";
|
|
305
|
+
if (args.some(arg => arg === "fetch" || arg === "push"))
|
|
306
|
+
return "updating a repository";
|
|
307
|
+
return "reading a build's checkout";
|
|
293
308
|
}
|
|
294
309
|
/** A setup call's time limit: its own five seconds, or less when a caller's deadline leaves less (never below 1 ms;
|
|
295
310
|
* a caller checks the deadline itself before calling). */
|
|
@@ -330,7 +345,7 @@ function regularFile(path) {
|
|
|
330
345
|
}
|
|
331
346
|
/** A checkout's git directory and common directory, as git finds them from its `.git` (a folder, or a one-line
|
|
332
347
|
* `gitdir: ` file); null when the pointer isn't one git would read the same way. */
|
|
333
|
-
function gitDirsOf(checkout) {
|
|
348
|
+
function gitDirsOf(checkout, vouched = NO_VOUCHED) {
|
|
334
349
|
let gitdir = join(checkout, ".git");
|
|
335
350
|
try {
|
|
336
351
|
if (!lstatSync(gitdir).isDirectory()) {
|
|
@@ -350,11 +365,14 @@ function gitDirsOf(checkout) {
|
|
|
350
365
|
// Where the checkout's own files point (a build can write both): read, in the console, only while that folder
|
|
351
366
|
// answered a look lately, and never vouched for by the checkout pool's spelling (folder-gate.ts). Held back: not one
|
|
352
367
|
// git would read the same way, so every caller refuses.
|
|
353
|
-
|
|
368
|
+
// `vouched`: the very folders this view was made from, read again after git ran (settle). They answered moments ago
|
|
369
|
+
// and git just ran in them; the gate's staleness is no evidence about them. Anything they now point elsewhere is
|
|
370
|
+
// gated as ever, so a re-pointed `.git` or `commondir` is never read on the console's thread on its say.
|
|
371
|
+
if (gitdir !== join(checkout, ".git") && !vouched.has(gitdir) && !folderAnswers(gitdir, true))
|
|
354
372
|
return null;
|
|
355
373
|
const named = regularFile(join(gitdir, "commondir"))?.trim();
|
|
356
374
|
const common = named === undefined || named === "" ? gitdir : resolve(gitdir, named);
|
|
357
|
-
if (common !== gitdir && !folderAnswers(common, true))
|
|
375
|
+
if (common !== gitdir && !vouched.has(common) && !folderAnswers(common, true))
|
|
358
376
|
return null;
|
|
359
377
|
return { gitdir, common };
|
|
360
378
|
}
|
|
@@ -382,7 +400,8 @@ function includeIn(path, gitFile = "git", limit) {
|
|
|
382
400
|
return named.toLowerCase();
|
|
383
401
|
return /^\s*\[\s*include(if)?\b/im.test(text) ? "include" : null;
|
|
384
402
|
}
|
|
385
|
-
|
|
403
|
+
const NO_VOUCHED = new Set();
|
|
404
|
+
function boundDirs(checkout, binding = {}, vouched = NO_VOUCHED) {
|
|
386
405
|
const fail = (why) => { throw new Error(`the checkout's .git ${why}`); };
|
|
387
406
|
let top;
|
|
388
407
|
try {
|
|
@@ -403,12 +422,12 @@ function boundDirs(checkout, binding = {}) {
|
|
|
403
422
|
if (held.isDirectory()) {
|
|
404
423
|
if (existsSync(join(dotgit, "commondir")))
|
|
405
424
|
fail("names another repository (commondir)");
|
|
406
|
-
dirs = { checkout: top, gitdir: dotgit, common: dotgit, linked: false };
|
|
425
|
+
dirs = { checkout: top, gitdir: dotgit, common: dotgit, linked: false, spelled: [dotgit] };
|
|
407
426
|
}
|
|
408
427
|
else {
|
|
409
428
|
if (!held.isFile())
|
|
410
429
|
fail("is a link");
|
|
411
|
-
const named = gitDirsOf(top);
|
|
430
|
+
const named = gitDirsOf(top, vouched);
|
|
412
431
|
if (named === null)
|
|
413
432
|
return fail("couldn't be read as git reads it");
|
|
414
433
|
let gitdir;
|
|
@@ -444,7 +463,7 @@ function boundDirs(checkout, binding = {}) {
|
|
|
444
463
|
}
|
|
445
464
|
if (backReal !== dotgit)
|
|
446
465
|
fail("belongs to a git directory that names another checkout");
|
|
447
|
-
dirs = { checkout: top, gitdir, common, linked: true };
|
|
466
|
+
dirs = { checkout: top, gitdir, common, linked: true, spelled: [named.gitdir, named.common, gitdir, common] };
|
|
448
467
|
}
|
|
449
468
|
// The common folder is a main checkout's own `.git` folder, a real one.
|
|
450
469
|
if (basename(dirs.common) !== ".git")
|
|
@@ -477,6 +496,12 @@ function boundDirs(checkout, binding = {}) {
|
|
|
477
496
|
}
|
|
478
497
|
return dirs;
|
|
479
498
|
}
|
|
499
|
+
/** The shared git folder of a recorded project, read from disk and proven as boundDirs proves it: the project's own
|
|
500
|
+
* real `.git` folder, holding no `commondir`. No pointer file or `commondir` an agent can write decides it, and no git
|
|
501
|
+
* runs (a page visit reads this). Throws with the reason otherwise. */
|
|
502
|
+
export function recordedCommonDir(repo) {
|
|
503
|
+
return boundDirs(repo, { repo }).common;
|
|
504
|
+
}
|
|
480
505
|
/** What git keeps only per worktree and the plane never links: HEAD (git won't take it as a link) and the pointers. */
|
|
481
506
|
/** What the plane links of a worktree's own git folder (git writes them in place through the link: the index and its
|
|
482
507
|
* logs by lockfile, per-worktree refs and logs inside the folders), whether or not they exist yet; a rebase's,
|
|
@@ -484,7 +509,12 @@ function boundDirs(checkout, binding = {}) {
|
|
|
484
509
|
const ADMIN_LINKED = ["index", "logs", "refs", "reftable"];
|
|
485
510
|
/** What git keeps in a common directory (its path.c common_list, and reftable), linked whether or not it exists yet. */
|
|
486
511
|
const COMMON_NAMES = ["branches", "gc.pid", "logs", "lost-found", "modules", "objects", "packed-refs", "reftable", "refs", "remotes", "rr-cache", "shallow", "svn", "worktrees"];
|
|
487
|
-
|
|
512
|
+
/** Where git incidents are recorded: beside the fenced views folder (git-incident.ts). */
|
|
513
|
+
export function gitIncidentRecord() {
|
|
514
|
+
return join(dirname(ensureGitViewsRoot(realpathSync(homedir()))), "git-incidents.jsonl");
|
|
515
|
+
}
|
|
516
|
+
/** `doing`: what the plane was doing, in a person's words ("tidying an old checkout"), for an incident's notice. */
|
|
517
|
+
function gitView(checkout, kind, binding = {}, gitFile = "git", limit, doing) {
|
|
488
518
|
const dirs = boundDirs(checkout, binding);
|
|
489
519
|
const root = ensureGitViewsRoot(realpathSync(homedir()));
|
|
490
520
|
const view = realpathSync(mkdtempSync(join(root, `${kind}-`)));
|
|
@@ -652,8 +682,10 @@ function gitView(checkout, kind, binding = {}, gitFile = "git", limit) {
|
|
|
652
682
|
const settle = () => {
|
|
653
683
|
const now = pinned(dirs);
|
|
654
684
|
let bound = false;
|
|
685
|
+
// The same proof as before git ran, of the same folders: re-read past the console's folder gate only where they are
|
|
686
|
+
// the very ones this view was made from (vouched), so a gate whose answer went stale mid-run is never read as a swap.
|
|
655
687
|
try {
|
|
656
|
-
const again = boundDirs(checkout, binding);
|
|
688
|
+
const again = boundDirs(checkout, binding, new Set(dirs.spelled));
|
|
657
689
|
bound = again.gitdir === dirs.gitdir && again.common === dirs.common;
|
|
658
690
|
}
|
|
659
691
|
catch {
|
|
@@ -663,7 +695,7 @@ function gitView(checkout, kind, binding = {}, gitFile = "git", limit) {
|
|
|
663
695
|
// Too late to prevent: recorded and told (git-incident.ts), never rolled back where the swap leads.
|
|
664
696
|
if (kind === "write") {
|
|
665
697
|
gitIncident({ checkout: dirs.checkout, repository: dirs.common, swapped: now.swapped.length > 0 ? now.swapped.map(({ path, leadsTo }) => ({ path, leadsTo })) : [{ path: join(dirs.checkout, ".git"), leadsTo: null }],
|
|
666
|
-
|
|
698
|
+
cause: now.swapped.length > 0 ? "swapped" : "rebound", ...(doing === undefined ? {} : { doing }), record: gitIncidentRecord() });
|
|
667
699
|
}
|
|
668
700
|
return "the checkout's repository changed while git ran";
|
|
669
701
|
}
|
|
@@ -677,7 +709,7 @@ function gitView(checkout, kind, binding = {}, gitFile = "git", limit) {
|
|
|
677
709
|
catch (error) {
|
|
678
710
|
if (!(error instanceof AnchorMoved))
|
|
679
711
|
return "the checkout's git state couldn't be carried back after git ran";
|
|
680
|
-
gitIncident({ checkout: dirs.checkout, repository: dirs.common, swapped: [{ path: error.message, leadsTo: null }],
|
|
712
|
+
gitIncident({ checkout: dirs.checkout, repository: dirs.common, swapped: [{ path: error.message, leadsTo: null }], cause: "moved", ...(doing === undefined ? {} : { doing }), record: gitIncidentRecord() });
|
|
681
713
|
return "the checkout's repository changed while git ran";
|
|
682
714
|
}
|
|
683
715
|
if (left.length > 0)
|
|
@@ -1061,6 +1093,28 @@ export function lfsExtensionIn(checkout, gitFile = "git", limit) {
|
|
|
1061
1093
|
const parsed = spawnSync(gitFile, ["config", "--file", path, "--no-includes", "--name-only", "--list", "-z"], { cwd: "/", encoding: "utf8", timeout: setupTimeout(limit), killSignal: "SIGKILL", env: { PATH: process.env["PATH"] ?? "", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", HOME: "/nonexistent" } });
|
|
1062
1094
|
return parsed.status !== 0 || parsed.stdout.split("\0").filter(Boolean).some(name => lfsCommand(name) || ["include", "includeif"].includes(keyParts(name).section));
|
|
1063
1095
|
}
|
|
1096
|
+
/** The hardened runners' own reads before the caller's command (the checkout's config listing): their own bounds, never
|
|
1097
|
+
* the caller's. A caller's small `maxBuffer` is for its own command's output (builder.ts reads one ls-tree line with
|
|
1098
|
+
* 2 KiB): inherited, it failed the config listing once a project's .git/config grew past 2 KiB (one [branch] section per
|
|
1099
|
+
* work branch), and every prepared release check then failed as "its config couldn't be read" (runs 3316–3324). */
|
|
1100
|
+
export const CONFIG_READ_BYTES = 16 * 1024 * 1024;
|
|
1101
|
+
export const CONFIG_READ_MS = 30_000;
|
|
1102
|
+
/** What of a caller's options an internal read keeps: where it runs and with what environment and containment —
|
|
1103
|
+
* never its time limit, output limit, stdin or output callbacks. */
|
|
1104
|
+
function internalOptions(options, env) {
|
|
1105
|
+
const { cwd, envAllowlist, omitEnv, fence, fenceGrants, confinement, processGroup, owner, signal } = options ?? {};
|
|
1106
|
+
return { ...(cwd === undefined ? {} : { cwd }), ...(envAllowlist === undefined ? {} : { envAllowlist }), ...(omitEnv === undefined ? {} : { omitEnv }),
|
|
1107
|
+
...(fence === undefined ? {} : { fence }), ...(fenceGrants === undefined ? {} : { fenceGrants }), ...(confinement === undefined ? {} : { confinement }),
|
|
1108
|
+
...(processGroup === undefined ? {} : { processGroup }), ...(owner === undefined ? {} : { owner }), ...(signal === undefined ? {} : { signal }),
|
|
1109
|
+
env, timeoutMs: CONFIG_READ_MS, maxBuffer: CONFIG_READ_BYTES };
|
|
1110
|
+
}
|
|
1111
|
+
/** Why the checkout's config listing failed, in words, for the refusal: git's own first line, or what stopped it. */
|
|
1112
|
+
function listingFailure(result) {
|
|
1113
|
+
if (result.timedOut)
|
|
1114
|
+
return `its config couldn't be read (the read ran past ${CONFIG_READ_MS / 1000} s)`;
|
|
1115
|
+
const said = `${result.stderr}`.split("\n").map(line => line.trim()).find(line => line !== "");
|
|
1116
|
+
return `its config couldn't be read${said === undefined ? ` (git exited ${result.code})` : ` (${said.slice(0, 300)})`}`;
|
|
1117
|
+
}
|
|
1064
1118
|
/** Every command runs against a view of the checkout's repository (gitView) whose config and info are copies the
|
|
1065
1119
|
* plane made, inspected there, so neither can change between the inspection and the run. `mode` "read" is for reading
|
|
1066
1120
|
* a checkout an agent may still be writing (the code page): every command also takes readFlags(), and the view holds
|
|
@@ -1082,17 +1136,17 @@ export function checkoutGit(inner = run, mode = "build", binding = {}) {
|
|
|
1082
1136
|
const once = async () => {
|
|
1083
1137
|
let view;
|
|
1084
1138
|
try {
|
|
1085
|
-
view = gitView(cwd, mode === "read" ? "read" : "write", binding);
|
|
1139
|
+
view = gitView(cwd, mode === "read" ? "read" : "write", binding, "git", undefined, checkoutDoing(args));
|
|
1086
1140
|
}
|
|
1087
1141
|
catch (error) {
|
|
1088
1142
|
return refused(error instanceof Error ? error.message : "the checkout couldn't be read");
|
|
1089
1143
|
}
|
|
1090
1144
|
try {
|
|
1091
1145
|
const viewed = { ...options, env: { ...(options?.env ?? {}), ...view.env } };
|
|
1092
|
-
// The checkout's own config may name no command.
|
|
1093
|
-
const own = await inner("git", ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], viewed);
|
|
1146
|
+
// The checkout's own config may name no command. Read with this runner's own bounds (internalOptions).
|
|
1147
|
+
const own = await inner("git", ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], internalOptions(options, viewed.env));
|
|
1094
1148
|
if (own.code !== 0)
|
|
1095
|
-
return refused(
|
|
1149
|
+
return refused(listingFailure(own));
|
|
1096
1150
|
const key = commandSetting(own.stdout, { lfsChecked: true });
|
|
1097
1151
|
if (key !== null)
|
|
1098
1152
|
return refused(`the checkout's git config sets ${key}, which can run a command`);
|
|
@@ -1185,9 +1239,11 @@ export function viewedGitSync(cwd, args, options = {}) {
|
|
|
1185
1239
|
const env = { PATH: process.env["PATH"] ?? "", HOME: process.env["HOME"] ?? "", LANG: "C", GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: "/dev/null", GIT_TERMINAL_PROMPT: "0", ...view.env };
|
|
1186
1240
|
const timeoutNow = () => { expire(); return limit === undefined ? options.timeoutMs ?? 60_000 : Math.max(1, Math.min(options.timeoutMs ?? 60_000, limit())); };
|
|
1187
1241
|
const fixed = { cwd, env, encoding: "utf8", killSignal: "SIGKILL", maxBuffer: options.maxBuffer ?? 1_048_576, stdio: ["ignore", "pipe", "pipe"] };
|
|
1188
|
-
|
|
1242
|
+
// Its own bounds, never the caller's output limit (CONFIG_READ_BYTES); a caller's deadline still holds.
|
|
1243
|
+
const own = spawnSync(gitFile, ["--no-optional-locks", "config", "--list", "--show-scope", "-z"], { ...fixed, maxBuffer: CONFIG_READ_BYTES,
|
|
1244
|
+
timeout: limit === undefined ? CONFIG_READ_MS : Math.max(1, Math.min(CONFIG_READ_MS, limit())) });
|
|
1189
1245
|
if (own.status !== 0)
|
|
1190
|
-
throw new Error("its config couldn't be read");
|
|
1246
|
+
throw new Error(own.error !== undefined ? `its config couldn't be read (${own.error.message.slice(0, 300)})` : "its config couldn't be read");
|
|
1191
1247
|
const key = commandSetting(own.stdout, { lfsChecked: true });
|
|
1192
1248
|
if (key !== null)
|
|
1193
1249
|
throw new Error(`the checkout's git config sets ${key}, which can run a command`);
|
|
@@ -1333,3 +1389,31 @@ export function lazyFetchSafeGit(inner = run) {
|
|
|
1333
1389
|
// repository's config and its includes as git found them, and an include naming a FIFO could hang the plane.
|
|
1334
1390
|
return checkoutGit(inner, "read");
|
|
1335
1391
|
}
|
|
1392
|
+
/**
|
|
1393
|
+
* The checkout a folder sits in, for choosing a folder (easy-start): the nearest parent holding a `.git`, then read
|
|
1394
|
+
* through checkoutGitSync, so a config that includes another file (a FIFO), or sets a command, is refused and the read
|
|
1395
|
+
* is bounded. Null when there is no checkout or it can't be read that way.
|
|
1396
|
+
*/
|
|
1397
|
+
export function checkoutTop(dir, timeoutMs = 10_000) {
|
|
1398
|
+
let at;
|
|
1399
|
+
try {
|
|
1400
|
+
at = realpathSync(dir);
|
|
1401
|
+
}
|
|
1402
|
+
catch {
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
for (;;) {
|
|
1406
|
+
if (existsSync(join(at, ".git"))) {
|
|
1407
|
+
try {
|
|
1408
|
+
return checkoutGitSync(at, ["rev-parse", "--show-toplevel"], { timeoutMs }).trim() || null;
|
|
1409
|
+
}
|
|
1410
|
+
catch {
|
|
1411
|
+
return null;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
const up = dirname(at);
|
|
1415
|
+
if (up === at)
|
|
1416
|
+
return null;
|
|
1417
|
+
at = up;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
package/dist/telegram-lead.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { telegramCardMessage } from "./card-render.js";
|
|
2
|
+
import { cardOfProposal, cardPlainOf, cardsFirst, cardWithin, isCardProposal } from "./chat-card.js";
|
|
1
3
|
import { CHAT_ACTIONS, sharedActionAllowsChallenge, sharedActionPayload } from "./chat-actions.js";
|
|
2
|
-
import { taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, NO_PHONE_LINK, NO_TASK_LINK, phoneProposalLink, confirmedLink, phoneProposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText } from "./chat-channel.js";
|
|
4
|
+
import { armedQuestion, taskInCeiling, mirrorToTaskChat, couldNotAnswerText, channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, NO_PHONE_LINK, NO_TASK_LINK, phoneProposalLink, confirmedLink, phoneProposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText } from "./chat-channel.js";
|
|
3
5
|
export { channelRepos as telegramConversationRepos, resolveChannelLead as resolveTelegramLead, tooLongText, whichTaskText, replyContextFor, NO_PHONE_LINK, NO_TASK_LINK, proposalLink, confirmedLink, proposalPreview, proposalOutcomeText, handoffCardText, confirmedCardText, CHAT_ACTION_PARITY as TELEGRAM_ACTION_PARITY, parityGaps } from "./chat-channel.js";
|
|
4
6
|
/**
|
|
5
7
|
* The paired phone as one more way to use the same assistant.
|
|
@@ -260,7 +262,7 @@ export function applyProposalTap(store, binding, token, message, repos, options,
|
|
|
260
262
|
const body = preview.text.split("\n\nConfirm or Dismiss below")[0];
|
|
261
263
|
if (challenged) {
|
|
262
264
|
ack("confirm it");
|
|
263
|
-
edit(`${body}\n\
|
|
265
|
+
edit(`${body}\n\n${armedQuestion(proposal)}`, [
|
|
264
266
|
[telegramButton(`✓ Yes, ${CHAT_ACTIONS[sharedAction.operation].label.toLowerCase()}`, yes)],
|
|
265
267
|
[telegramButton("Cancel", cancel)],
|
|
266
268
|
]);
|
|
@@ -288,6 +290,7 @@ export function applyProposalTap(store, binding, token, message, repos, options,
|
|
|
288
290
|
// The paired phone speaks for its account's full authority (pairing is a full session's act).
|
|
289
291
|
authority: "full",
|
|
290
292
|
via: "telegram",
|
|
293
|
+
binding: binding.id,
|
|
291
294
|
evidenceRoot: options.evidenceRoot,
|
|
292
295
|
confirm: action.phase === "yes",
|
|
293
296
|
deferSignal: signal => effects.push({ kind: "signal", run: signal }),
|
|
@@ -549,17 +552,26 @@ async function runTelegramConversation(row, args) {
|
|
|
549
552
|
const origin = proposal === null ? null : options.phoneOrigin?.() ?? null;
|
|
550
553
|
const wanted = proposal === null ? null : phoneProposalLink(store, proposal, repos);
|
|
551
554
|
const keyboard = keyboardWith(part.keyboard, phoneLinkButton(origin, wanted));
|
|
555
|
+
// A Toolroll card: read again now, shown only where every project behind it is still reachable. An unreadable
|
|
556
|
+
// one goes out as its saved plain words.
|
|
557
|
+
const isCard = proposal !== null && isCardProposal(proposal);
|
|
558
|
+
const rich = isCard ? cardOfProposal(proposal) : null;
|
|
559
|
+
const shown = rich !== null && cardWithin(rich, repos) ? telegramCardMessage(rich) : null;
|
|
560
|
+
const links = rich === null ? [] : rich.buttons.flatMap(one => phoneLinkButton(origin, one) ?? []).map(one => [one]);
|
|
552
561
|
// The lead's own words carry their bold anchors and labelled links as entities, never as Markdown characters.
|
|
553
562
|
const voiced = part.kind === "reply" ? telegramReply(part.text) : null;
|
|
554
|
-
const text =
|
|
563
|
+
const text = isCard ? shown?.text ?? (rich === null ? part.text : "This card's projects changed, so it wasn't sent.")
|
|
564
|
+
: proposal !== null && part.keyboard === null ? handoffCardText(part.text, linkNote(origin, wanted)) : voiced?.text ?? part.text;
|
|
565
|
+
const entities = isCard ? shown?.entities ?? [] : voiced?.entities ?? [];
|
|
566
|
+
const buttons = isCard ? (shown === null || links.length === 0 ? undefined : links) : keyboard;
|
|
555
567
|
method = "sendMessage";
|
|
556
568
|
send = () => transport("sendMessage", {
|
|
557
569
|
chat_id: chatId,
|
|
558
570
|
text,
|
|
559
|
-
...(
|
|
571
|
+
...(entities.length === 0 ? {} : { entities }),
|
|
560
572
|
link_preview_options: { is_disabled: true },
|
|
561
573
|
...(part.replyTo === null ? {} : { reply_parameters: { message_id: Number(part.replyTo) } }),
|
|
562
|
-
...(
|
|
574
|
+
...(buttons === undefined ? {} : { reply_markup: { inline_keyboard: buttons } }),
|
|
563
575
|
});
|
|
564
576
|
}
|
|
565
577
|
// The first time the answer itself fails to go out, the person hears so once,
|
|
@@ -626,7 +638,12 @@ async function runTelegramConversation(row, args) {
|
|
|
626
638
|
const selected = store.getLeadTurn(turn)?.state === "answered" ? store.listLeadTurnEvidence(turn) : [];
|
|
627
639
|
for (const image of selected)
|
|
628
640
|
parts.push({ kind: "image", text: image.caption, taskId: image.taskId, run: image.run, artifact: image.artifact, sha256: image.sha256 });
|
|
629
|
-
|
|
641
|
+
// Cards first, so what a card leads to sits right under it. A card mints no tokens: it confirms nothing.
|
|
642
|
+
for (const proposal of cardsFirst(proposals)) {
|
|
643
|
+
if (isCardProposal(proposal)) {
|
|
644
|
+
parts.push({ kind: "card", text: cardPlainOf(proposal), proposal: proposal.id, keyboard: null });
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
630
647
|
const preview = phoneProposalPreview(store, proposal, repos);
|
|
631
648
|
const keyboard = preview.buttons ? mintCardTokens(store, binding, proposal.id, now).keyboard : null;
|
|
632
649
|
parts.push({ kind: "card", text: preview.text, proposal: proposal.id, keyboard });
|