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/server/settings.js
CHANGED
|
@@ -2,11 +2,15 @@ import { PASSWORDLESS_TRADE, passwordlessSetting, setPasswordless } from "../pas
|
|
|
2
2
|
import { ACP_AGENTS, isKeyedProvider } from "../acp-agents.js";
|
|
3
3
|
import { LIVE_VIEW_CONSENT, liveViewConsentDigest, setLiveCommands } from "../live-detail.js";
|
|
4
4
|
import { CONSOLE_FORMS, readForm } from "../contracts/console-api.js";
|
|
5
|
+
import { clearFallbackChain, parseEntry, setFallbackChain, setPaidGrant } from "../fallback.js";
|
|
5
6
|
import { galleryTemplateOf } from "../flow-gallery.js";
|
|
6
7
|
import { finishGoogleConsent, GOOGLE_CALLBACK } from "../google-mail.js";
|
|
7
8
|
import { kitInstalled, kitOf } from "../kits.js";
|
|
8
9
|
import { CONNECT_CALLBACK, connectionsOf, finishConnect, localConnectOf, oneClickOf } from "../mcp-connect.js";
|
|
9
10
|
import { learningView } from "../project-learning.js";
|
|
11
|
+
import { memoryUseOf } from "../memory-use.js";
|
|
12
|
+
import { afterwardsWords, lessonNoteOf, noteAfterwards } from "../lesson-notes.js";
|
|
13
|
+
import { decideTidy, disabledLessons, listTidy, proposeTidy, restoreDecision, restoreLesson, restoreReference, undoableMerges, unmergeDecisions } from "../memory-tidy.js";
|
|
10
14
|
import { skillTestResult } from "../project-skills.js";
|
|
11
15
|
import { discoverTools, projectToolsOf } from "../project-tools.js";
|
|
12
16
|
import { lookProjectMcp } from "../folder-look.js";
|
|
@@ -48,6 +52,7 @@ import { releaseApprovalSetting, setReleaseApproval } from "../release-auto.js";
|
|
|
48
52
|
import { backupHtml } from "../backup-ui.js";
|
|
49
53
|
import { BACKUP_EVERY_HOURS, backupFolderOf, backupNow, checkBackupFolder, defaultBackupFolder, MAX_KEEP } from "../backup.js";
|
|
50
54
|
import { projectBatchChecks, setProjectBatchChecks } from '../batch-policy.js';
|
|
55
|
+
import { fastStartOn, setFastStart } from '../fast-start.js';
|
|
51
56
|
import { ALL_PROJECTS as CHAT_APPROVAL_ALL, chatApprovalWords, setChatApproval } from "../chat-approval.js";
|
|
52
57
|
import { ChatState } from "../chat-delivery-state.js";
|
|
53
58
|
import { checkSettingsHtml } from '../check-levels-ui.js';
|
|
@@ -67,20 +72,20 @@ import { disconnectGoogle, googleConnected, googleConsent, readGoogleMail, saveG
|
|
|
67
72
|
import { integrationsHtml } from "../integrations-ui.js";
|
|
68
73
|
import { setYouiyouxIntake } from "../youiyoux-intake.js";
|
|
69
74
|
import { clearProviderKey, keyStatus, plausibleKey, PROVIDER_KEY_ENV, readAuthMode, readProviderKey, saveProviderKey, setAuthMode, SUBSCRIPTION_CAPABLE, verdictWords, verifyProviderKey } from "../keys.js";
|
|
70
|
-
import { decisionsHtml, framedSection, knowledgeHtml, MEMORY_INTRO, memorySearchHtml, proposalsHtml } from "../knowledge-ui.js";
|
|
75
|
+
import { cleanUpHtml, decisionsHtml, framedSection, knowledgeHtml, MEMORY_INTRO, memorySearchHtml, proposalsHtml, referenceRestoreHtml, restoreHtml } from "../knowledge-ui.js";
|
|
71
76
|
import { aboutYouOf, checkAboutYou, saveAboutYou } from "../lead-about.js";
|
|
72
77
|
import { cancelCommitment, conditionWords, openCommitments } from '../lead-commitments.js';
|
|
73
78
|
import { checkLeadIdentity, leadIdentityOf } from "../lead-identity.js";
|
|
74
79
|
import { mailboxAccess, readThroughImap } from "../mailbox.js";
|
|
75
80
|
import { startConnect } from "../mcp-connect.js";
|
|
76
|
-
import { decideProposal, listProposals, memoryStatus } from "../memory-pass.js";
|
|
81
|
+
import { decideProposal, defaultTidyAnalyzer, listProposals, memoryStatus } from "../memory-pass.js";
|
|
77
82
|
import { checkModels, isNewModel, modelOptions, modelWords, RUNTIME_TOOLS, runtimeStates, seenModels, setWatch, updateRuntime, watchState } from "../model-catalog.js";
|
|
78
83
|
import { modelsHtml, modelsScript } from "../models-ui.js";
|
|
79
84
|
import { acceptSuggestion, dismissSuggestion, grantWords, keepRule, modelName, ruleWords, rulesOf, setSuggestModels, startTrial, stopTrial, suggestionsFor, suggestionWords, suggestModelsOn, trialOffersFor, trialOfferWords, trialsOf, trialWords, turnOffRule } from "../route-rules.js";
|
|
80
85
|
import { ALL_SLOTS, applyModelChoices, browserWords, checkScope, DEFAULT_PRESET, defaultAgentOf, fillDefaultRouting, isPresetName, newerModels, PRESETS, presetChange, presetInUse, previewModelChange, recentRuns, rowAt, slotKey, SLOTS, slotWords, stateDigest, switchChange } from "../model-choices.js";
|
|
81
86
|
import { HEADROOM_FIVE_HOUR_LIMIT, HEADROOM_WEEKLY_LIMIT, exactModelId } from "../phase-routing.js";
|
|
82
87
|
import { RISKY_MEANS, SIZE_MEANS, SIZING_CLAUDE_MODEL } from "../task-sizing.js";
|
|
83
|
-
import { CHAT_APPROVE_ALL, MODE_MAX_DAYS, modeDigestOf, modeTermsJson, modeWords, presetTerms } from "../modes.js";
|
|
88
|
+
import { CHAT_APPROVE_ALL, CHECK_REPAIR_LIMIT, MODE_MAX_DAYS, modeDigestOf, modeTermsJson, modeWords, presetTerms } from "../modes.js";
|
|
84
89
|
import { monitoringChange, readMonitoring, saveMonitoring } from "../monitoring-settings.js";
|
|
85
90
|
import { monitoringHtml, signingSecretHtml } from "../monitoring-ui.js";
|
|
86
91
|
import { openRouterPickerScript } from "../openrouter-models.js";
|
|
@@ -93,6 +98,7 @@ import { isCheckAccess, signInsUnavailableWords, widerThan } from "../check-acce
|
|
|
93
98
|
import { LOCAL_MODE_CONFINES, localModeOn } from "../local-owner.js";
|
|
94
99
|
import { parseMachLookup, parseReadDeny } from "../project-sandbox.js";
|
|
95
100
|
import { projectDeleteConfirmHtml, projectSettingsHtml } from "../project-delete-ui.js";
|
|
101
|
+
import { endHoldout, setCodeMapSwitch } from "../code-map-acts.js";
|
|
96
102
|
import { deleteProject, holdingsWords } from "../project-delete.js";
|
|
97
103
|
import { applySavedKnowledge, changeKnowledge, knowledgeVersion, knowledgeView } from "../project-knowledge.js";
|
|
98
104
|
import { changeLearning } from "../project-learning.js";
|
|
@@ -128,8 +134,8 @@ import { lastSweep, saveSweep, storageSweepOff } from "../storage-sweep.js";
|
|
|
128
134
|
import { sweepSchedule } from "../storage-sweep-schedule.js";
|
|
129
135
|
import { storageHtml } from "../storage-ui.js";
|
|
130
136
|
import { landedSummary } from "../landed-cleanup.js";
|
|
131
|
-
import { bytesWords, parseCleanup } from "../storage.js";
|
|
132
|
-
import { isDigestTime, liveBackupState, localModeOf, RESULT_SCREENSHOTS, setLiveBackupSettings, setLocalModeOff } from "../store.js";
|
|
137
|
+
import { bytesWords, parseCleanup, parseIdle } from "../storage.js";
|
|
138
|
+
import { checkoutIdle, isDigestTime, liveBackupState, setCheckoutIdle, localModeOf, RESULT_SCREENSHOTS, setLiveBackupSettings, setLocalModeOff } from "../store.js";
|
|
133
139
|
import { defaultLiveFolder, liveFolderOf, liveFolderProblem } from "../live-backup.js";
|
|
134
140
|
import { LIVE_EVERY } from "../backup-ui.js";
|
|
135
141
|
import { checkTeamsCredentials, clearTeamsCredentials, loadTeamsCredentials, saveTeamsCredentials, TeamsError } from "../teams-api.js";
|
|
@@ -353,9 +359,34 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
353
359
|
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
354
360
|
const selector = projectPicker("/settings/project", projects.map(repo => ({ repo, name: projectName(repo) })), chosen);
|
|
355
361
|
const notice = { said: url.searchParams.get("said"), problem: url.searchParams.get("problem") };
|
|
356
|
-
const content = chosen === "" ? html `${notice.said ? infoLine(notice.said) : ""}${empty({ title: "No projects yet.", action: btn("Add a project", { sm: true, href: "/projects" }) })}` : (projectSettingsHtml({ ...projectViewOf(chosen, who), remoteUrl: store.projectRemote(chosen) }, notice));
|
|
362
|
+
const content = chosen === "" ? html `${notice.said ? infoLine(notice.said) : ""}${empty({ title: "No projects yet.", action: btn("Add a project", { sm: true, href: "/projects" }) })}` : (projectSettingsHtml({ ...projectViewOf(chosen, who), remoteUrl: store.projectRemote(chosen), codeMap: codeMapView(chosen, who, ctx.now) }, notice));
|
|
357
363
|
return sendScreen(response, 200, screen("Project", settingsSubPage("projects", "Project", html `${selector}${content}`), { chrome: chromeFor(chosen || null, "settings") }));
|
|
358
364
|
}
|
|
365
|
+
/** The project's code map section: the switch, and the holdout while new builds are still being enrolled. */
|
|
366
|
+
function codeMapView(repo, who, now) {
|
|
367
|
+
const row = store.handle.prepare("SELECT code_map, code_map_enroll_until FROM project WHERE path = ?").get(repo);
|
|
368
|
+
const until = typeof row?.["code_map_enroll_until"] === "string" ? row["code_map_enroll_until"] : null;
|
|
369
|
+
return { on: row === undefined || Number(row["code_map"]) === 1, enrollUntil: until !== null && Date.parse(until) > now.getTime() ? until : null,
|
|
370
|
+
canEndHoldout: who.via === "cookie" && who.authority !== "local-owner", href: `/projects/${encodeURIComponent(projectName(repo))}/map` };
|
|
371
|
+
}
|
|
372
|
+
function codeMapAct(kind) {
|
|
373
|
+
return async (ctx) => {
|
|
374
|
+
const { response, now, posted } = ctx;
|
|
375
|
+
const who = browserCaller(ctx.who);
|
|
376
|
+
const repo = posted.get("repo") ?? "";
|
|
377
|
+
const back = (key, words) => redirect(response, `/settings/project?repo=${encodeURIComponent(repo)}&${key}=${encodeURIComponent(words)}#code-map`);
|
|
378
|
+
if (posted.getAll("repo").length !== 1 || posted.getAll("on").length > 1)
|
|
379
|
+
return back("problem", "That form was sent twice over. Nothing changed.");
|
|
380
|
+
if (!consoleProjects().includes(repo) || !store.accountCanAccess(who.name, repo))
|
|
381
|
+
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
382
|
+
const door = who.authority === "local-owner" ? { kind: "local-owner", actor: who.name } : { kind: "person", person: modelsAuthority(who, "session") };
|
|
383
|
+
const on = posted.get("on") === "1";
|
|
384
|
+
const done = kind === "switch" ? setCodeMapSwitch(store, door, repo, on, now) : endHoldout(store, door, repo, now);
|
|
385
|
+
if (!done.ok)
|
|
386
|
+
return back("problem", done.problem);
|
|
387
|
+
return back("said", kind === "holdout" ? "The holdout has ended. Builds already enrolled keep their arm." : on ? "The code map is on." : "The code map is off.");
|
|
388
|
+
};
|
|
389
|
+
}
|
|
359
390
|
// v102: Settings → Approvals and safety: the defaults for new tasks, a project's separation of duties and release
|
|
360
391
|
// approval, and the way to its operating mode. Anyone who sees the project reads them; each form's own route decides
|
|
361
392
|
// who may change it (an approver for the defaults and releases, an instance operator for the rules).
|
|
@@ -467,9 +498,9 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
467
498
|
const measured = lastChecks.known("storage");
|
|
468
499
|
const check = postForm("/settings/storage/check", btn("Check now", { sm: true, type: "submit" }));
|
|
469
500
|
if (measured === null) {
|
|
470
|
-
return sendScreen(response, 200, screen("Storage", settingsSubPage("system", "Storage", settingsSection({ id: "storage", title: "
|
|
501
|
+
return sendScreen(response, 200, screen("Storage", settingsSubPage("system", "Storage", settingsSection({ id: "storage", title: "Task folders not measured yet", lead: "Toolroll measures the task folders in the background. Check now measures them at once.", body: check })), { chrome: chromeFor(project, "settings") }));
|
|
471
502
|
}
|
|
472
|
-
const plan = { ...measured.value, cleanup: store.checkoutCleanup() };
|
|
503
|
+
const plan = { ...measured.value, cleanup: store.checkoutCleanup(), idle: checkoutIdle(store) };
|
|
473
504
|
const sweep = { last: lastSweep(store), off: storageSweepOff(), schedule: sweepSchedule(store, now) };
|
|
474
505
|
return sendScreen(response, 200, screen("Storage", settingsSubPage("system", "Storage", html `${(storageHtml({ plan, csrf: who.session.csrf, sweep, landed: landedSummary(store), measured: html `<div class="k-acts"><span class="k-note">Measured ${new Date(measured.at).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })}</span>${check}</div>` }, { said: url.searchParams.get("said"), problem: url.searchParams.get("problem") }))}`), { chrome: chromeFor(project, "settings") }));
|
|
475
506
|
}
|
|
@@ -499,7 +530,7 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
499
530
|
const quick = liveQuickCommand(store, repo);
|
|
500
531
|
const body = (checkSettingsHtml({ repo, name: projectName(repo), csrf: who.session.csrf, canChange: who.role === "approver" && !store.isDemo(),
|
|
501
532
|
level: projectCheckLevel(store, repo).level, full: store.liveVerifyCommand(repo), quick, suggestion: quick === null ? suggestQuickCommand(repo) : null,
|
|
502
|
-
review: (({ on, source }) => ({ on, source }))(store.reviewSwitch(repo, clock())), batch: { on: projectBatchChecks(store, repo).on },
|
|
533
|
+
review: (({ on, source }) => ({ on, source }))(store.reviewSwitch(repo, clock())), batch: { on: projectBatchChecks(store, repo).on }, fast: { on: fastStartOn(store, repo) },
|
|
503
534
|
said: url.searchParams.get("said"), problem: url.searchParams.get("problem") }));
|
|
504
535
|
return sendScreen(response, 200, screen("Checks", settingsSubPage("projects", "Checks", html `${body}`), { chrome: chromeFor(project, "projects") }));
|
|
505
536
|
}
|
|
@@ -577,21 +608,25 @@ ${connectionHtml(asked, connection, task)}${command === null ? "" : html `<p>On
|
|
|
577
608
|
const revision = Number(url.searchParams.get('version'));
|
|
578
609
|
const previous = knowledgeVersion(store, chosen, who.name, revision);
|
|
579
610
|
const restore = csrf && who.role === 'approver' && revision !== view.revision ? postForm('/settings/knowledge/change', html `<p>Replaces knowledge for future tasks. Existing runs keep their saved context.</p><button>Restore version ${revision}</button>`, { attrs: { class: 'knowledge' }, hidden: { repo: chosen, identity: view.identity, revision: String(view.revision), action: 'restore', restore: String(revision) } }) : '';
|
|
580
|
-
return sendScreen(response, 200, screen('Knowledge history', settingsSubPage('agents', `Knowledge · version ${revision}`, html `<p class="k-note"><a class="k-link" href="/settings/knowledge?repo=${encodeURIComponent(chosen)}">Current knowledge</a></p>${knowledgeHtml({ ...view, knowledge: previous, history: [], stale: null }, '', false)}${restore}`), { chrome: chromeFor(chosen, 'settings') }));
|
|
611
|
+
return sendScreen(response, 200, screen('Knowledge history', settingsSubPage('agents', `Knowledge · version ${revision}`, html `<p class="k-note"><a class="k-link" href="/settings/knowledge?repo=${encodeURIComponent(chosen)}">Current knowledge</a></p>${knowledgeHtml({ ...view, knowledge: previous, history: [], stale: null }, '', false)}${restore}${referenceRestoreHtml(chosen, revision, previous.references.filter(ref => !view.knowledge.references.some(now => now.id === ref.id)), !!csrf && who.role === 'approver')}`), { chrome: chromeFor(chosen, 'settings') }));
|
|
581
612
|
}
|
|
582
613
|
const query = (url.searchParams.get('q') ?? '').slice(0, 1000);
|
|
583
|
-
const result = query.trim() ? repositoryContextRead({ repo: chosen, query, mode: url.searchParams.get('mode') === 'impact' ? 'impact' : 'search', cacheRoot: join(dirname(evidenceRoot), 'repository-context') }) : null;
|
|
614
|
+
const result = query.trim() ? repositoryContextRead({ repo: chosen, project: chosen, query, mode: url.searchParams.get('mode') === 'impact' ? 'impact' : 'search', cacheRoot: join(dirname(evidenceRoot), 'repository-context') }) : null;
|
|
584
615
|
const memoryQuery = (url.searchParams.get('memory') ?? '').slice(0, 300);
|
|
585
616
|
const hits = memoryQuery.trim() ? searchMemory(store, { actor: who.name, repos: [chosen], query: memoryQuery, limit: 20 }) : null;
|
|
586
617
|
let proposals = html ``;
|
|
587
618
|
try {
|
|
588
|
-
proposals = (proposalsHtml(chosen, listProposals(store, chosen), memoryStatus(store, chosen, who.name), csrf, who.role === 'approver'));
|
|
619
|
+
proposals = (proposalsHtml(chosen, listProposals(store, chosen), memoryStatus(store, chosen, who.name), csrf, who.role === 'approver', who.name));
|
|
589
620
|
}
|
|
590
621
|
catch {
|
|
591
622
|
proposals = html ``;
|
|
592
623
|
}
|
|
593
|
-
|
|
594
|
-
|
|
624
|
+
const use = memoryUseOf(store, chosen);
|
|
625
|
+
const manageMemory = who.role === 'approver' && csrf !== '';
|
|
626
|
+
const retired = listDecisions(store, chosen, who.name, { status: 'all', limit: 100 }).filter(d => d.status !== 'active');
|
|
627
|
+
content = html `<p class="meta">${MEMORY_INTRO}</p>${memorySearchHtml(chosen, memoryQuery, hits)}${proposals}${cleanUpHtml(chosen, listTidy(store, chosen), manageMemory)}${(decisionsHtml(chosen, listDecisions(store, chosen, who.name, { limit: 50 }), csrf, who.role === 'approver', undefined, undefined, use, d => { const after = noteAfterwards(store, d); return after === null ? null : afterwardsWords(after); }))}\
|
|
628
|
+
${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf : '')}${knowledgeHtml(view, csrf, who.role === 'approver', undefined, undefined, use)}\
|
|
629
|
+
${restoreHtml(chosen, retired, undoableMerges(store, chosen), disabledLessons(store, chosen), manageMemory)}\
|
|
595
630
|
<details class="knowledge frame"><summary class="frame-head">Learned lessons</summary><div class="frame-panel">${(learningHtml(learningView(store, evidenceRoot, chosen, who.name), csrf, who.role === 'approver'))}</div></details>`;
|
|
596
631
|
}
|
|
597
632
|
catch {
|
|
@@ -803,11 +838,11 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
803
838
|
const { url, who, response } = ctx;
|
|
804
839
|
const provider = url.pathname.split("/")[2];
|
|
805
840
|
if (!isProviderId(provider))
|
|
806
|
-
return refuse(response, who, 400, "unknown provider", "/
|
|
841
|
+
return refuse(response, who, 400, "unknown provider", "/tasks");
|
|
807
842
|
if (who.role !== "approver")
|
|
808
|
-
return refuse(response, who, 403, "Only an operator can resume an agent's work.", "/
|
|
843
|
+
return refuse(response, who, 403, "Only an operator can resume an agent's work.", "/tasks");
|
|
809
844
|
liftAuthPause(store, provider, "person", who.name, clock());
|
|
810
|
-
return redirect(response, "/
|
|
845
|
+
return redirect(response, "/tasks");
|
|
811
846
|
}
|
|
812
847
|
// The update notice is dismissed per browser and per version: a newer release shows it again.
|
|
813
848
|
// Cockpit B2: Forget — nothing verified on this session before now counts as fresh (a CSRF-checked post, like every act).
|
|
@@ -1235,16 +1270,20 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1235
1270
|
// What the page shows next is measured again after the change.
|
|
1236
1271
|
const said = sayTo(response, "/settings/storage");
|
|
1237
1272
|
const back = async (kind, words) => { await measureStorage(databaseFile).catch(() => undefined); return said(kind, words); };
|
|
1238
|
-
const what = op === "clean" ? "clean up" : op === "discard" ? "discard a
|
|
1273
|
+
const what = op === "clean" ? "clean up" : op === "discard" ? "discard a task folder" : "change automatic cleanup";
|
|
1239
1274
|
if (!authenticateApprover(who, body.get("password") ?? "").ok)
|
|
1240
1275
|
return back("problem", `Enter your Toolroll password to ${what}.`);
|
|
1241
1276
|
if (op === "cleanup") {
|
|
1242
1277
|
const cleanup = parseCleanup(body.get("cleanup") ?? "");
|
|
1243
1278
|
if (cleanup === undefined)
|
|
1244
|
-
return back("problem", "Choose when a finished task's
|
|
1245
|
-
const
|
|
1279
|
+
return back("problem", "Choose when a finished task's folder is removed.");
|
|
1280
|
+
const idle = body.get("idle") === null ? checkoutIdle(store) : parseIdle(body.get("idle") ?? "");
|
|
1281
|
+
if (idle === undefined)
|
|
1282
|
+
return back("problem", "Choose when an idle task's folder is removed.");
|
|
1283
|
+
const same = cleanup === store.checkoutCleanup() && idle === checkoutIdle(store);
|
|
1246
1284
|
store.setCheckoutCleanup(cleanup, who.name, now);
|
|
1247
|
-
|
|
1285
|
+
setCheckoutIdle(store, idle, who.name, now);
|
|
1286
|
+
return back("said", same ? "Nothing changed." : "Saved.");
|
|
1248
1287
|
}
|
|
1249
1288
|
const pool = storagePool(databaseFile);
|
|
1250
1289
|
if (op === "clean") {
|
|
@@ -1256,7 +1295,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1256
1295
|
return back("problem", "What a clean-up would remove changed since you looked. Check the list again.");
|
|
1257
1296
|
const done = await cleanCheckouts(store, pool, clock, { manual: true, actor: who.name, only: new Set(plan.go.map(one => one.path)) });
|
|
1258
1297
|
saveSweep(store, { at: clock().toISOString(), source: "manual", actor: who.name, parts: [{ kind: "checkouts", count: done.removed.length, bytes: done.freed, failed: done.kept.filter(one => one.why === "git refused").length, items: done.removed.slice(0, 20).map(one => one.path) }] });
|
|
1259
|
-
return back("said", done.removed.length === 0 ? "Nothing was removed: those
|
|
1298
|
+
return back("said", done.removed.length === 0 ? "Nothing was removed: those task folders are no longer ready to go." : `Removed ${done.removed.length} task folder${done.removed.length === 1 ? "" : "s"}, about ${bytesWords(done.freed)}. Their branches stay.`);
|
|
1260
1299
|
}
|
|
1261
1300
|
const done = await discardCheckout(store, pool, body.get("path") ?? "", now, who.name);
|
|
1262
1301
|
return done.ok ? back("said", `Discarded, about ${bytesWords(done.bytes)}. Its branch stays.`) : back("problem", done.message);
|
|
@@ -1331,6 +1370,13 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1331
1370
|
const changed = setProjectBatchChecks(store, repo, on, who.name, now);
|
|
1332
1371
|
return back("said", !changed.changed ? "Saved." : on ? "Batch checks are on." : "Batch checks are off.");
|
|
1333
1372
|
}
|
|
1373
|
+
// Small tasks start right away: a person's own logged settings act, behind this form's password.
|
|
1374
|
+
if (act === "small-tasks") {
|
|
1375
|
+
const on = body.get("on") === "1";
|
|
1376
|
+
store.upsertProject(repo, projectName(repo), now);
|
|
1377
|
+
setFastStart(store, repo, on, who.name, now);
|
|
1378
|
+
return back("said", on ? "Small tasks start right away." : "Small tasks wait for a plan.");
|
|
1379
|
+
}
|
|
1334
1380
|
if (act === "level") {
|
|
1335
1381
|
const level = body.get("level");
|
|
1336
1382
|
if (!isCheckLevel(level))
|
|
@@ -1736,7 +1782,7 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1736
1782
|
const repo = body.get('repo') ?? '';
|
|
1737
1783
|
if (!visible(repo) || !store.accountCanAccess(who.name, repo) || !consoleProjects().includes(repo))
|
|
1738
1784
|
return refuse(response, who, 403, 'That project is outside your access.', '/settings/knowledge');
|
|
1739
|
-
const refreshed = await repositoryContext({ repo, query: '', refresh: true, cacheRoot: join(dirname(evidenceRoot), 'repository-context') });
|
|
1785
|
+
const refreshed = await repositoryContext({ repo, project: repo, query: '', refresh: true, cacheRoot: join(dirname(evidenceRoot), 'repository-context') });
|
|
1740
1786
|
return sendScreen(response, refreshed.index.status === 'ready' ? 200 : 503, screen('Project context', settingsSubPage("agents", "Project context", html `<p>${refreshed.index.status === 'ready' ? 'Code index refreshed.' : 'The index is unavailable. Source search still works.'}</p><a class="button-link" href="/settings/knowledge?repo=${encodeURIComponent(repo)}">Open knowledge</a>`), { chrome: chromeFor(repo, 'settings') }));
|
|
1741
1787
|
}
|
|
1742
1788
|
async function knowledgeProposal(ctx) {
|
|
@@ -1755,6 +1801,64 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1755
1801
|
}
|
|
1756
1802
|
return redirect(response, `/settings/knowledge?repo=${encodeURIComponent(repo)}&saved=1`);
|
|
1757
1803
|
}
|
|
1804
|
+
/** A lesson note: a person's own words, accepted as a decision through the one proposal writer, or rejected. */
|
|
1805
|
+
async function knowledgeNote(ctx) {
|
|
1806
|
+
const { who, response, now, posted } = ctx;
|
|
1807
|
+
const body = readForm(posted, CONSOLE_FORMS.knowledgeNote);
|
|
1808
|
+
const repo = body.get('repo') ?? '', decision = body.get('decision') ?? '';
|
|
1809
|
+
if (!visible(repo) || !consoleProjects().includes(repo))
|
|
1810
|
+
return refuse(response, who, 403, 'That project is outside your access.', '/settings/knowledge');
|
|
1811
|
+
if (!['accept', 'reject'].includes(decision) || !/^[0-9]{1,12}$/.test(body.get('proposal') ?? ''))
|
|
1812
|
+
return refuse(response, who, 400, 'Choose accept or reject for one note.', '/settings/knowledge');
|
|
1813
|
+
const id = Number(body.get('proposal'));
|
|
1814
|
+
try {
|
|
1815
|
+
if (lessonNoteOf(store, id) === null)
|
|
1816
|
+
throw Error('That note is unavailable.');
|
|
1817
|
+
decideProposal(store, { repo, actor: who.name, id, decision: decision, claim: body.get('claim') ?? '' }, now);
|
|
1818
|
+
}
|
|
1819
|
+
catch (error) {
|
|
1820
|
+
return sendScreen(response, 409, screen('Knowledge', settingsSubPage("agents", "Knowledge", html `<p role="alert">${error instanceof Error ? error.message : 'That note could not be decided.'}</p><p><a class="k-link" href="/settings/knowledge?repo=${encodeURIComponent(repo)}">Back to Knowledge</a></p>`), { chrome: chromeFor(repo, 'settings') }));
|
|
1821
|
+
}
|
|
1822
|
+
return redirect(response, `/settings/knowledge?repo=${encodeURIComponent(repo)}&saved=1`);
|
|
1823
|
+
}
|
|
1824
|
+
/** Project memory's clean-up: a person's request, accepting or rejecting one proposal, and restores. Every writer
|
|
1825
|
+
* proves the person inside its own transaction. */
|
|
1826
|
+
async function knowledgeTidy(ctx) {
|
|
1827
|
+
const { who, response, now, posted } = ctx;
|
|
1828
|
+
const body = readForm(posted, CONSOLE_FORMS.knowledgeTidy);
|
|
1829
|
+
const repo = body.get('repo') ?? '', action = body.get('action') ?? '', target = body.get('target') ?? '';
|
|
1830
|
+
if (!visible(repo) || !consoleProjects().includes(repo))
|
|
1831
|
+
return refuse(response, who, 403, 'That project is outside your access.', '/settings/knowledge');
|
|
1832
|
+
const back = `/settings/knowledge?repo=${encodeURIComponent(repo)}`;
|
|
1833
|
+
const said = (id) => /^[0-9]{1,12}$/.test(id) ? Number(id) : null;
|
|
1834
|
+
// Only a browser session is a person's proof here; the writers refuse anything else.
|
|
1835
|
+
const proof = who.via === 'cookie' ? 'session' : 'remote-token';
|
|
1836
|
+
try {
|
|
1837
|
+
if (action === 'propose')
|
|
1838
|
+
await proposeTidy(store, { repo, actor: who.name, analyzer: defaultTidyAnalyzer(store, store.databaseFile() === null ? {} : { configDir: dirname(store.databaseFile()) }) }, now);
|
|
1839
|
+
else if (action === 'accept' || action === 'reject') {
|
|
1840
|
+
const id = said(body.get('proposal') ?? '');
|
|
1841
|
+
if (id === null)
|
|
1842
|
+
throw Error('Choose one proposal.');
|
|
1843
|
+
const choice = body.get('choice'), claim = body.get('claim'), why = body.get('why');
|
|
1844
|
+
decideTidy(store, evidenceRoot, { repo, actor: who.name, id, decision: action, proof, ...(choice === null ? {} : { choice }), ...(claim === null ? {} : { claim }), ...(why === null ? {} : { why }) }, now);
|
|
1845
|
+
}
|
|
1846
|
+
else if (action === 'restore' && said(target) !== null)
|
|
1847
|
+
restoreDecision(store, { repo, actor: who.name, id: said(target), proof }, now);
|
|
1848
|
+
else if (action === 'unmerge' && said(target) !== null)
|
|
1849
|
+
unmergeDecisions(store, { repo, actor: who.name, merge: said(target), proof }, now);
|
|
1850
|
+
else if (action === 'readopt' && said(target) !== null)
|
|
1851
|
+
restoreLesson(store, evidenceRoot, { repo, actor: who.name, id: said(target), proof }, now);
|
|
1852
|
+
else if (action === 'reference' && /^[a-f0-9]{20}$/.test(target) && said(body.get('revision') ?? '') !== null)
|
|
1853
|
+
restoreReference(store, { repo, actor: who.name, id: target, fromRevision: said(body.get('revision') ?? ''), proof }, now);
|
|
1854
|
+
else
|
|
1855
|
+
return refuse(response, who, 400, 'Choose one clean-up action.', back);
|
|
1856
|
+
}
|
|
1857
|
+
catch (error) {
|
|
1858
|
+
return sendScreen(response, 409, screen('Knowledge', settingsSubPage("agents", "Knowledge", html `<p role="alert">${error instanceof Error ? error.message : 'That could not be done.'}</p><p><a class="k-link" href="${back}">Back to Knowledge</a></p>`), { chrome: chromeFor(repo, 'settings') }));
|
|
1859
|
+
}
|
|
1860
|
+
return redirect(response, `${back}&saved=1`);
|
|
1861
|
+
}
|
|
1758
1862
|
async function knowledgeDecision(ctx) {
|
|
1759
1863
|
const { response, now, posted } = ctx;
|
|
1760
1864
|
const who = browserCaller(ctx.who);
|
|
@@ -2358,19 +2462,17 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2358
2462
|
planAuto: body.get("plan-auto") === "1",
|
|
2359
2463
|
reviewAuto: false,
|
|
2360
2464
|
reviewRetryAuto: false,
|
|
2361
|
-
//
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
repairAuto: false,
|
|
2365
|
-
repairMaxAttempts: 0,
|
|
2465
|
+
// Repairing failed checks: only the explicit choice grants it, with the limit it signs (check-repair.ts).
|
|
2466
|
+
repairAuto: body.get("repair-auto") === "1",
|
|
2467
|
+
repairMaxAttempts: body.get("repair-auto") === "1" ? CHECK_REPAIR_LIMIT : 0,
|
|
2366
2468
|
// Approving from the paired chat: only the explicit choice grants it, naming every chat app it covers (a grant
|
|
2367
2469
|
// signed before it named them stays Telegram only).
|
|
2368
2470
|
chatApprove: body.get("chat-approve") === "1",
|
|
2369
2471
|
...(body.get("chat-approve") === "1" ? { chatApproveChats: CHAT_APPROVE_ALL } : {}),
|
|
2370
2472
|
publication: body.get("publication") === "automerge" ? "automerge" : "notify",
|
|
2371
2473
|
};
|
|
2372
|
-
if (["review-auto", "review-retry-auto"
|
|
2373
|
-
return refuse(response, who, 400, "Review scheduling
|
|
2474
|
+
if (["review-auto", "review-retry-auto"].some(field => body.get(field) === "1")) {
|
|
2475
|
+
return refuse(response, who, 400, "Review scheduling is no longer available. Reload the mode form.", "/mode");
|
|
2374
2476
|
}
|
|
2375
2477
|
if (terms.planAuto && !terms.autoApproveFiling) {
|
|
2376
2478
|
return refuse(response, who, 400, "Automatic planner approval requires automatic filing approval.", "/mode");
|
|
@@ -2383,9 +2485,9 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2383
2485
|
// THE CEREMONY (M1): every resolved term in words, and the password
|
|
2384
2486
|
// signs exactly this digest — a drifted form refuses at /mode/sign.
|
|
2385
2487
|
const nonce = mintApprovalNonce(who.name, "mode-sign", `${project}:${digest}`);
|
|
2386
|
-
const signed = Object.fromEntries(["name", "days", "publication", "auto-approve", "plan-auto", "chat-approve"]
|
|
2488
|
+
const signed = Object.fromEntries(["name", "days", "publication", "auto-approve", "plan-auto", "chat-approve", "repair-auto"]
|
|
2387
2489
|
.map(field => [field, body.get(field) ?? ""]));
|
|
2388
|
-
const bodyHtml = modeCeremonyHtml({ name, project: projectName(project), words: modeWords(terms), chatApprove: signed["chat-approve"] === "1", hidden: { nonce, digest, ...signed, expiry } });
|
|
2490
|
+
const bodyHtml = modeCeremonyHtml({ name, project: projectName(project), words: modeWords(terms), chatApprove: signed["chat-approve"] === "1", repairAuto: signed["repair-auto"] === "1", hidden: { nonce, digest, ...signed, expiry } });
|
|
2389
2491
|
return sendScreen(response, 200, screen("Sign the mode", bodyHtml, { chrome: chromeFor(project, "mode") }));
|
|
2390
2492
|
}
|
|
2391
2493
|
// /mode/sign: the fixed expiry rides the form; the digest is
|
|
@@ -2401,8 +2503,8 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2401
2503
|
return refuse(response, who, 409, "the terms moved while you were reading — read them again", "/mode");
|
|
2402
2504
|
}
|
|
2403
2505
|
const token = body.get("token") ?? "";
|
|
2404
|
-
// A mode that lets a chat app approve opens a way to act: a credential act. Any other mode is an ordinary one.
|
|
2405
|
-
if (!(body.get("chat-approve") === "1" ? authenticateCredential(who, token) : authenticateApprover(who, token)).ok) {
|
|
2506
|
+
// A mode that lets a chat app approve, or repairs start by themselves, opens a way to act: a credential act. Any other mode is an ordinary one.
|
|
2507
|
+
if (!(body.get("chat-approve") === "1" || body.get("repair-auto") === "1" ? authenticateCredential(who, token) : authenticateApprover(who, token)).ok) {
|
|
2406
2508
|
return refuse(response, who, 403, "signing a mode takes your password, typed again", "/mode");
|
|
2407
2509
|
}
|
|
2408
2510
|
store.signMode({
|
|
@@ -2711,6 +2813,7 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2711
2813
|
"settings.project-concurrency": projectConcurrency,
|
|
2712
2814
|
"settings.project-live-view": projectLiveView,
|
|
2713
2815
|
"settings.project-network": projectNetwork,
|
|
2816
|
+
"settings.project-fallback": projectFallback,
|
|
2714
2817
|
"settings.project-writes": projectWrites,
|
|
2715
2818
|
"settings.project-check-access": projectCheckAccess,
|
|
2716
2819
|
"settings.project-sandbox": projectSandbox,
|
|
@@ -2754,6 +2857,8 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2754
2857
|
"settings.models-stop": modelsRuleAct("stop"),
|
|
2755
2858
|
"settings.knowledge-refresh": knowledgeRefresh,
|
|
2756
2859
|
"settings.knowledge-proposal": knowledgeProposal,
|
|
2860
|
+
"settings.knowledge-note": knowledgeNote,
|
|
2861
|
+
"settings.knowledge-tidy": knowledgeTidy,
|
|
2757
2862
|
"settings.knowledge-decision": knowledgeDecision,
|
|
2758
2863
|
"settings.knowledge-change": knowledgeChange,
|
|
2759
2864
|
"settings.learning-change": learningChange,
|
|
@@ -2803,6 +2908,8 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
2803
2908
|
"settings.lead-about": leadAbout,
|
|
2804
2909
|
"settings.lead-promise-cancel": leadPromiseCancel,
|
|
2805
2910
|
"settings.lead-on": leadOn,
|
|
2911
|
+
"settings.code-map": codeMapAct("switch"),
|
|
2912
|
+
"settings.code-map-holdout": codeMapAct("holdout"),
|
|
2806
2913
|
}, {
|
|
2807
2914
|
"edge.google-callback": googleCallback,
|
|
2808
2915
|
"edge.connect-callback": connectCallback,
|
|
@@ -3091,5 +3198,30 @@ ${postForm("/settings/chat-approval/save", html `\
|
|
|
3091
3198
|
: kind === "suggest-on" ? "Suggestions are on for this project." : "Suggestions are off for this project.");
|
|
3092
3199
|
};
|
|
3093
3200
|
}
|
|
3201
|
+
// Settings → Project → Fallback agents (fallback.ts): who takes over when an agent runs out of its plan. An approver for
|
|
3202
|
+
// the project, with their password typed now; the ledger keeps every change. New tasks seal it; approved ones keep theirs.
|
|
3203
|
+
async function projectFallback(ctx) {
|
|
3204
|
+
const { who, response, now, posted } = ctx;
|
|
3205
|
+
const body = readForm(posted, CONSOLE_FORMS.projectFallback);
|
|
3206
|
+
const repo = body.get("repo") ?? "";
|
|
3207
|
+
if (!consoleProjects().includes(repo) || !visible(repo))
|
|
3208
|
+
return refuse(response, who, 403, "That project is outside your access.", "/settings/project");
|
|
3209
|
+
if (who.via !== "cookie" || who.role !== "approver" || store.isDemo() || !store.accountCanAccess(who.name, repo))
|
|
3210
|
+
return refuse(response, who, 403, "An approver for this project changes its fallback agents.", "/settings/project");
|
|
3211
|
+
const back = sayTo(response, `/settings/project?repo=${encodeURIComponent(repo)}`, "#fallback");
|
|
3212
|
+
const parsed = (body.get("agents") ?? "").split(/[\n,]+/).map(one => one.trim()).filter(Boolean).map(parseEntry);
|
|
3213
|
+
const bad = parsed.find((one) => typeof one === "string");
|
|
3214
|
+
if (bad !== undefined)
|
|
3215
|
+
return back("problem", `Nothing changed: ${bad}.`);
|
|
3216
|
+
if (!authenticateCredential(who, body.get("password") ?? "").ok)
|
|
3217
|
+
return back("problem", "Enter your Toolroll password to change fallback agents.");
|
|
3218
|
+
const done = store.transact(() => {
|
|
3219
|
+
const set = parsed.length === 0 ? clearFallbackChain(store, repo, who.name, now) : setFallbackChain(store, repo, parsed, who.name, now);
|
|
3220
|
+
if (!set.ok || parsed.length === 0)
|
|
3221
|
+
return set;
|
|
3222
|
+
return setPaidGrant(store, repo, body.get("paid") === "on", who.name, now);
|
|
3223
|
+
});
|
|
3224
|
+
return done.ok ? back("said", "Saved. Tasks scoped from now on use it; tasks already approved keep the chain they were approved with.") : back("problem", `Nothing changed: ${done.problem}`);
|
|
3225
|
+
}
|
|
3094
3226
|
return { registrations, modelsView };
|
|
3095
3227
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { heldFindingCount } from "../findings.js";
|
|
2
|
+
import { familyInWebReach } from "../findings.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
1
4
|
import { resultRecordWordsOf } from "../route-results.js";
|
|
2
5
|
import { selectionEvidenceWords } from "../route-rules.js";
|
|
3
6
|
import { feedbackImageProblem } from "../result-actions.js";
|
|
@@ -7,8 +10,10 @@ import { RELEASE_STEPS } from "../release.js";
|
|
|
7
10
|
import { releaseProgressFor, shipChecksForTask } from "../store.js";
|
|
8
11
|
import { releaseCiRunningOf } from "../release-ci-hold.js";
|
|
9
12
|
import { releaseObligationsOf } from "../release-recheck.js";
|
|
10
|
-
import { approvalProofOf,
|
|
11
|
-
import {
|
|
13
|
+
import { approvalProofOf, freshnessFor, withFreshness } from "../scope.js";
|
|
14
|
+
import { approvalPasswordField } from "../approval-password.js";
|
|
15
|
+
import { fastChangeProblem, fastStarted } from "../fast-start.js";
|
|
16
|
+
import { routeChangeOf } from "../route-change.js";
|
|
12
17
|
import { liveAttemptOf, steerControlsFor } from "../steer-controls.js";
|
|
13
18
|
import { originOf } from "../task-source.js";
|
|
14
19
|
import { recentGitReads, taskBaseOf } from "../task-base.js";
|
|
@@ -25,6 +30,7 @@ import { readSkillsSnapshot, skillTestResult } from "../project-skills.js";
|
|
|
25
30
|
import { newestPullRequestOf, publishingOf, pullRequestBlocker } from '../pull-request-flow.js';
|
|
26
31
|
import { structuredHandoffView, terminalDiffView } from "../result-evidence-readers.js";
|
|
27
32
|
import { resultCheckLevel } from '../result-follow-ups.js';
|
|
33
|
+
import { followThroughView } from "./render-follow-through.js";
|
|
28
34
|
import { skillsSnapshotHtml, skillTestFeedbackHtml } from "../skills-ui.js";
|
|
29
35
|
import { archiveOf } from "../store.js";
|
|
30
36
|
import { runActivityOf } from "../task-activity.js";
|
|
@@ -55,6 +61,8 @@ import {} from "./render-pages.js";
|
|
|
55
61
|
import { completionReceiptView, diffFileAnchor, evidenceLinksFor, followUpsFor, orderChangedFiles, parseReviewDiff, proofBundleView, receiptStatusOf, withinSignedTouches } from "./render-results.js";
|
|
56
62
|
import { approvalFormDigest, consentDoorOf, nonceHashOf, publicationFactsOf, resumeCeremonyPage, resumeDigestOf, taskBody, taskPage } from "./render-tasks.js";
|
|
57
63
|
import {} from "./session.js";
|
|
64
|
+
import { mergeReadyViewOf } from "./merge-ready-view.js";
|
|
65
|
+
import { machineChecked, machineCheckWords } from "../machine-check.js";
|
|
58
66
|
import { RESULT_REVIEW_SCRIPT } from "../result-review.js";
|
|
59
67
|
import { reviewEvidenceScript } from "./render-results.js";
|
|
60
68
|
import { budgetHoldWords, budgetLabel, monthOf } from "../spend.js";
|
|
@@ -138,6 +146,7 @@ export function createTaskViews(runtime) {
|
|
|
138
146
|
const live = runs.find(one => runIsLive(one)) ?? null;
|
|
139
147
|
const facts = {
|
|
140
148
|
id: task.id,
|
|
149
|
+
heldFindings: heldFindingCount(runtime.store, task.id),
|
|
141
150
|
title: task.title,
|
|
142
151
|
repo: task.repo,
|
|
143
152
|
state: task.state,
|
|
@@ -162,6 +171,7 @@ export function createTaskViews(runtime) {
|
|
|
162
171
|
},
|
|
163
172
|
publication: latest === null ? null : publicationFactsOf(runtime.store.publicationForRun(latest.id)),
|
|
164
173
|
liveRunId: live === null ? null : live.id,
|
|
174
|
+
...(machineChecked(runtime.store, task.id) ? { machine: true } : {}),
|
|
165
175
|
openDecision: ref === null ? null : openWorkDecisionOf(runtime.store, ref.id, now),
|
|
166
176
|
unfinishedRunId: runs.find(one => one.outcome === null && one.role !== "reviewer")?.id ?? null,
|
|
167
177
|
control: ref === null ? { kind: "none" } : taskControlOf(runtime.store, ref.id, now),
|
|
@@ -521,23 +531,26 @@ export function createTaskViews(runtime) {
|
|
|
521
531
|
})(),
|
|
522
532
|
// Cockpit B2: whether this person's remembered password may approve these exact terms (the same rule approve()
|
|
523
533
|
// applies), and since when.
|
|
524
|
-
|
|
534
|
+
// The one password rule every approve form reads (approval-password.ts): remembered since when, or a local
|
|
535
|
+
// owner's no-password approval (eligible, or why not and where to go).
|
|
536
|
+
...(() => {
|
|
525
537
|
const scope = ref === null ? null : runtime.store.getScope(ref.externalId);
|
|
526
|
-
if (scope === null || who?.via !== "cookie")
|
|
527
|
-
return null;
|
|
538
|
+
if (scope === null || ref === null || who?.via !== "cookie")
|
|
539
|
+
return { approvalFresh: null, approvalLocal: null };
|
|
528
540
|
const fresh = withFreshness("approve-standard", () => freshnessFor(who.name, Date.now(), true));
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
541
|
+
const field = approvalPasswordField(runtime.store, { name: who.name, taskId: ref.externalId, scope, passwordless: false,
|
|
542
|
+
localOwner: who.authority === "local-owner", freshAt: fresh?.at ?? null, returnTo: taskHref(ref.externalId) });
|
|
543
|
+
return { approvalFresh: field.field === "remembered" ? field.since : null,
|
|
544
|
+
approvalLocal: field.field === "none" && field.why === "local-owner" ? { eligible: true }
|
|
545
|
+
: field.field === "local-refused" ? { eligible: false, message: field.message, href: field.href, action: field.action } : null };
|
|
533
546
|
})(),
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
547
|
+
routeChange: ref === null ? null : routeChangeOf(runtime.store, ref.externalId),
|
|
548
|
+
followThrough: who === undefined || familyInWebReach(runtime.store, who, taskId, runtime.visible) === null ? null : followThroughView(runtime.store, runtime.evidenceRoot, taskId, taskHref(taskId), who.role === "approver" && who.via === "cookie" && !runtime.store.isDemo(), runtime.clock()),
|
|
549
|
+
fastGrown: (() => {
|
|
550
|
+
if (ref === null || runtime.store.getTask(ref.externalId)?.state !== "done" || !fastStarted(runtime.store, ref.externalId))
|
|
537
551
|
return null;
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
return { eligible: false, ...localOwnerRefusal(runtime.store.accountOf(who.name)?.passwordSet === true, taskHref(ref.externalId)) };
|
|
552
|
+
const last = runtime.store.lastBuilderRun(ref.id);
|
|
553
|
+
return last === null ? null : fastChangeProblem(runtime.store, ref.externalId, last.id);
|
|
541
554
|
})(),
|
|
542
555
|
steerRecent: ref === null ? [] : runtime.store.runsFor(ref.id).filter(one => one.outcome !== null).map(one => one.id).sort((a, b) => b - a).slice(0, 5),
|
|
543
556
|
// The running attempt's composer, for an approver on a browser session (steer-controls.ts).
|
|
@@ -805,7 +818,7 @@ export function createTaskViews(runtime) {
|
|
|
805
818
|
return runtime.sendScreen(response, status, screen(admittedFamily.root.title, html `<h1>${admittedFamily.root.title}</h1><p>${admittedFamily.current.state}</p><p class="problem" data-history-problem>${admittedFamily.problem}</p>`, { chrome: runtime.chromeFor(admittedFamily.root.repo, "tasks") }));
|
|
806
819
|
const data = taskViewData(taskId, who, problem);
|
|
807
820
|
if (data === null)
|
|
808
|
-
return refuse(response, who, 404, "no such task", "/
|
|
821
|
+
return refuse(response, who, 404, "no such task", "/tasks");
|
|
809
822
|
const family = runtime.familyOf(taskId);
|
|
810
823
|
const presentedData = { ...data, rootId: family?.root.id ?? taskId, rootTitle: family?.root.title ?? data.task.title,
|
|
811
824
|
...(family === null || data.assignment != null ? {} : { history: familyHistory(family) }),
|
|
@@ -858,7 +871,21 @@ export function createTaskViews(runtime) {
|
|
|
858
871
|
// Ship (its pull request's checks, each on one time axis) and a release's own steps, read from the store now.
|
|
859
872
|
const rootId = family?.root.id ?? taskId;
|
|
860
873
|
const oneTaskView = onePage?.kind === "task" ? { ...onePage, ship: shipViewOf(shipChecksForTask(runtime.store, rootId), runtime.clock()),
|
|
861
|
-
release: releaseStepsViewOf(releaseProgressFor(runtime.store, rootId), runtime.clock())
|
|
874
|
+
release: releaseStepsViewOf(releaseProgressFor(runtime.store, rootId), runtime.clock()),
|
|
875
|
+
// "Can I merge?" (P1.6): the checklist from the same assignment this page read, each blocker with its own form.
|
|
876
|
+
mergeReady: mergeReadyViewOf(runtime.store, taskId, data.assignment, who, presentedData.runChecks ?? null, taskHref(taskId)) } : onePage;
|
|
877
|
+
// A check the machine runs (a prepared candidate: a release's check) is said as the check it is, with no agent's
|
|
878
|
+
// activity, no notes to read, and for a release its own steps instead of the generic Plan → Build stepper.
|
|
879
|
+
if (oneTaskView?.kind === "task" && machineChecked(runtime.store, taskId)) {
|
|
880
|
+
const words = machineCheckWords(runtime.store, taskId, data.liveRunId ?? null, oneTaskView.release ?? null, runtime.clock());
|
|
881
|
+
const card = oneTaskView.status;
|
|
882
|
+
if (words !== null && card !== null && card.status.headline === "Building")
|
|
883
|
+
oneTaskView.status = { ...card, status: { ...card.status, sentence: words } };
|
|
884
|
+
oneTaskView.activity = null;
|
|
885
|
+
oneTaskView.steering = null;
|
|
886
|
+
if (oneTaskView.release != null)
|
|
887
|
+
oneTaskView.journey = null;
|
|
888
|
+
}
|
|
862
889
|
// The Result tab's no-script page, under the task's own (the same HTML its result page serves), and its script.
|
|
863
890
|
if (extras.result != null) {
|
|
864
891
|
page.body = html `${page.body}<section class="task-result-tab" id="task-result" aria-label="Result">${extras.result.html}</section>`;
|
|
@@ -895,14 +922,14 @@ export function createTaskViews(runtime) {
|
|
|
895
922
|
const stop = runtime.store.stopOf(runId);
|
|
896
923
|
const run = runtime.store.getRun(runId);
|
|
897
924
|
if (stop === null || run === null || stop.taskRef !== ref.id)
|
|
898
|
-
return taskScreen(response, who, taskId, `
|
|
925
|
+
return taskScreen(response, who, taskId, `build #${runId} is not a stopped attempt of this task`, 409);
|
|
899
926
|
if (run.role === "reviewer")
|
|
900
|
-
return taskScreen(response, who, taskId, `
|
|
927
|
+
return taskScreen(response, who, taskId, `build #${runId} was an old review. Nothing reruns it; open the saved result instead`, 409);
|
|
901
928
|
if (stop.resumedAt !== null)
|
|
902
|
-
return taskScreen(response, who, taskId, `
|
|
929
|
+
return taskScreen(response, who, taskId, `build #${runId} was already resumed by ${stop.resumedBy ?? "?"}`, 409);
|
|
903
930
|
const control = taskControlOf(runtime.store, ref.id, now);
|
|
904
931
|
if (control.kind !== "paused" || control.run !== runId) {
|
|
905
|
-
return taskScreen(response, who, taskId, control.kind === "stopping" ? `
|
|
932
|
+
return taskScreen(response, who, taskId, control.kind === "stopping" ? `build #${runId} is still stopping. Toolroll is making sure it has ended` : `build #${runId} is no longer the one to resume. Reload and decide again`, 409);
|
|
906
933
|
}
|
|
907
934
|
const digest = resumeDigestOf(taskId, runId, stop.settledAt ?? "", runtime.store.getScope(taskId));
|
|
908
935
|
const nonceValue = randomBytes(18).toString("base64url");
|
|
@@ -1168,7 +1195,7 @@ export function createTaskViews(runtime) {
|
|
|
1168
1195
|
// Editor links (arc 6): the deployment capability, THIS machine's
|
|
1169
1196
|
// runner owning the run, the session's own device-side yes, and a
|
|
1170
1197
|
// checkout to open — or nothing renders.
|
|
1171
|
-
editor: runtime.options.editorLinks !== undefined && runtime.options.localRunner !== undefined && run.runner === runtime.options.localRunner && who.via === "cookie" && who.session.editorLinks === true && run.worktree !== null
|
|
1198
|
+
editor: runtime.options.editorLinks !== undefined && runtime.options.localRunner !== undefined && run.runner === runtime.options.localRunner && who.via === "cookie" && who.session.editorLinks === true && run.worktree !== null && existsSync(run.worktree)
|
|
1172
1199
|
? { worktree: run.worktree }
|
|
1173
1200
|
: null,
|
|
1174
1201
|
signedCriteria: scope?.acceptance.length ?? 0,
|
|
@@ -1231,18 +1258,28 @@ export function releaseStepsViewOf(progress, now) {
|
|
|
1231
1258
|
const all = RELEASE_STEPS.filter(step => step !== "done");
|
|
1232
1259
|
// A release that started part way (one carried under an earlier approval, or resumed from an older journal) shows
|
|
1233
1260
|
// the steps from where its own record starts; a step missing between two recorded ones is a write that was lost.
|
|
1234
|
-
|
|
1261
|
+
// A build running since the stop is the release's check, wherever the release last stopped; what follows it is to come.
|
|
1262
|
+
const building = progress.building, checkAt = all.indexOf("check");
|
|
1263
|
+
const firstAt = building === null ? all.findIndex(step => recorded.has(step)) : Math.min(all.findIndex(step => recorded.has(step)), checkAt);
|
|
1235
1264
|
const lastAt = all.reduce((at, step, index) => recorded.has(step) ? index : at, -1);
|
|
1236
1265
|
const steps = firstAt <= 0 ? all : all.slice(firstAt);
|
|
1237
1266
|
const rows = steps.map(step => {
|
|
1238
1267
|
const one = recorded.get(step);
|
|
1268
|
+
if (building !== null && step === "check") {
|
|
1269
|
+
const start = Date.parse(building.startedAt);
|
|
1270
|
+
return { name: RELEASE_STEP_WORDS[step] ?? step, st: "run", start: Number.isFinite(start) && Number.isFinite(origin) ? Math.max(0, (start - origin) / 1000) : 0, dur: Number.isFinite(start) ? Math.max(0, (now.getTime() - start) / 1000) : 0 };
|
|
1271
|
+
}
|
|
1272
|
+
if (building !== null && all.indexOf(step) > checkAt)
|
|
1273
|
+
return { name: RELEASE_STEP_WORDS[step] ?? step, st: "q", start: 0, dur: 0 };
|
|
1239
1274
|
// A step with no record is never painted as done: between recorded ones it says so, after them it is queued.
|
|
1240
1275
|
if (one === undefined)
|
|
1241
1276
|
return all.indexOf(step) < lastAt ? { name: RELEASE_STEP_WORDS[step] ?? step, sub: "not recorded", st: "q", start: 0, dur: 0 }
|
|
1242
1277
|
: { name: RELEASE_STEP_WORDS[step] ?? step, st: "q", start: 0, dur: 0 };
|
|
1243
1278
|
const start = Date.parse(one.startedAt), end = one.endedAt === null ? now.getTime() : Date.parse(one.endedAt);
|
|
1244
|
-
|
|
1245
|
-
|
|
1279
|
+
// A failure the release has gone on from (resumed past it, or a build since) is history, not where it stands.
|
|
1280
|
+
const passed = one.state === "failed" && progress.stopped === null;
|
|
1281
|
+
const st = one.state === "done" || passed ? "ok" : one.state === "failed" ? "bad" : one.state === "waiting" ? "wait" : "run";
|
|
1282
|
+
return { name: RELEASE_STEP_WORDS[step] ?? step, ...(passed ? { sub: "Stopped here, then went on" } : one.detail === null || one.state === "done" ? {} : { sub: one.detail.slice(0, 160) }), st,
|
|
1246
1283
|
start: Number.isFinite(start) && Number.isFinite(origin) ? Math.max(0, (start - origin) / 1000) : 0, dur: Number.isFinite(start) && Number.isFinite(end) ? Math.max(0, (end - start) / 1000) : 0 };
|
|
1247
1284
|
});
|
|
1248
1285
|
const current = rows.findIndex(row => row.st === "run" || row.st === "wait" || row.st === "bad");
|
|
@@ -1254,7 +1291,10 @@ export function releaseStepsViewOf(progress, now) {
|
|
|
1254
1291
|
return {
|
|
1255
1292
|
tag: progress.tag, version: progress.version, pr: progress.pr,
|
|
1256
1293
|
rows,
|
|
1257
|
-
|
|
1294
|
+
// Waiting is a person's act only at Approval; a step waiting on a machine (its check, GitHub's checks, quiet) is the
|
|
1295
|
+
// release running, never "Needs you".
|
|
1296
|
+
state: progress.stopped !== null ? "failed" : done ? "done" : at?.st === "wait" && steps[current] === "approval" ? "waiting" : "running",
|
|
1297
|
+
at: current === -1 ? null : { step: steps[current], name: at.name, index: current + 1, of: rows.length },
|
|
1258
1298
|
headline: done ? `Released ${progress.version}` : progress.stopped !== null ? `Stopped at ${RELEASE_STEP_WORDS[progress.stopped.step] ?? progress.stopped.step}`
|
|
1259
1299
|
: at !== null ? `${at.name} · Step ${current + 1} of ${rows.length}` : last === null ? "Starting" : `${last} done · the next step starts on its own`,
|
|
1260
1300
|
stopped: progress.stopped === null ? null : { step: RELEASE_STEP_WORDS[progress.stopped.step] ?? progress.stopped.step, message: progress.stopped.message, at: progress.stopped.at },
|