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/chat-decide.js
CHANGED
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
* and each button is one opaque token bound to the exact result, receipt, plan or commit the card showed: a card a
|
|
5
5
|
* newer result, a finished task or a changed plan has overtaken says so and acts on nothing.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* card keeps its link. The change requests one youiyoux pass filed come as one card that shows every word of each
|
|
7
|
+
* A person with the authority approves a plan or merges from their own linked chat by default (owner's decision,
|
|
8
|
+
* docs/plans/toolroll-cards.md); their own off or limits (chat-approval.ts) and a signed mode's chatApprove term still
|
|
9
|
+
* hold. A tap is theirs alone, and the card binds it to the exact terms. The change requests one youiyoux pass filed come as one card that shows every word of each
|
|
11
10
|
* task's terms, with Approve all and each one's own Approve (the requests card, below).
|
|
12
11
|
*
|
|
13
12
|
* The same in every chat app: Telegram's bridge and the shared engine Slack, Discord and Teams run on (chat-delivery.ts)
|
|
14
13
|
* offer, mint and answer these through the same tokens (the `channel` column). Every act runs through the door the
|
|
15
14
|
* console uses. */
|
|
15
|
+
import { routeChangeOf } from "./route-change.js";
|
|
16
16
|
import { NIGHTLY_CHECK_ONLY, RELEASE_MERGES_ONLY, nightlyTaskRef, releaseTaskRef } from "./store.js";
|
|
17
17
|
import { isAcpProfile } from "./acp-agents.js";
|
|
18
18
|
import { DECIDE_ACTS, readDecideActionRow } from "./contracts/chat-callback-rows.js";
|
|
@@ -22,18 +22,19 @@ import { chatControlHref, chatResultHref } from "./chat-controls.js";
|
|
|
22
22
|
import { applyChatTaskAction, chatTaskStamp } from "./chat-task-actions.js";
|
|
23
23
|
import { chatTitle } from "./chat-voice.js";
|
|
24
24
|
import { readVerifiedArtifact, scanForSecrets } from "./evidence.js";
|
|
25
|
-
import {
|
|
26
|
-
import { chatApproveChatsOf, chatNames, modeTermsFromJson } from "./modes.js";
|
|
25
|
+
import { chatApproveMode } from "./chat-approval.js";
|
|
27
26
|
import { parseExecutionPlanDocument } from "./plan.js";
|
|
28
27
|
import { verifyApproverStanding } from "./principal.js";
|
|
29
28
|
import { pullRequestFollowOf } from "./pull-request-flow.js";
|
|
30
29
|
import { requestResultChanges } from "./result-actions.js";
|
|
31
30
|
import { revisionSourceOf } from "./result-review.js";
|
|
32
|
-
import { APPROVAL_PROOF, approvalOf, approvalProofDetail, deployGateScope, quarantinedAuthor } from "./scope.js";
|
|
31
|
+
import { APPROVAL_PROOF, approvalOf, approvalProofDetail, approve, deployGateScope, quarantinedAuthor } from "./scope.js";
|
|
32
|
+
import { gateWords } from "./approval-policy.js";
|
|
33
33
|
import { releaseCardOf } from "./release-card.js";
|
|
34
34
|
import { releaseCiHoldOf, releaseCiHoldWords } from "./release-ci-hold.js";
|
|
35
35
|
import { phoneText, projectLabel } from "./telegram-status.js";
|
|
36
|
-
import { permissionPlainWords, scopeTermsText, shownTermsDigest } from "./chat-terms.js";
|
|
36
|
+
import { cardTermsDigest, chatLimitProblem, permissionPlainWords, scopeTermsText, shownTermsDigest } from "./chat-terms.js";
|
|
37
|
+
import { chainTermsLines } from "./scope.js";
|
|
37
38
|
import { YOUIYOUX_CARD_KIND, YOUIYOUX_FILED_VIA, YOUIYOUX_HOME_PATH } from "./contracts/youiyoux-request.js";
|
|
38
39
|
import { FILED_PER_PASS, requestKindWord, requestPassOf } from "./youiyoux-intake.js";
|
|
39
40
|
import { discordPlain } from "./reply-shape.js";
|
|
@@ -41,13 +42,18 @@ import { PLATFORM_LIMITS } from "./text-limits.js";
|
|
|
41
42
|
export { permissionPlainWords };
|
|
42
43
|
/** The words, said once. */
|
|
43
44
|
export const DECIDE_LABELS = {
|
|
44
|
-
accept: "Accept
|
|
45
|
-
changes: "
|
|
45
|
+
accept: "Accept",
|
|
46
|
+
changes: "Send back",
|
|
46
47
|
retry: "Retry",
|
|
47
48
|
approve: "Approve & start",
|
|
48
49
|
"not-now": "Not now",
|
|
49
50
|
merge: "Merge",
|
|
50
51
|
look: "Look first",
|
|
52
|
+
/** A result's one link (docs/plans/plain-toolroll.md P1.5). */
|
|
53
|
+
open: "Open result",
|
|
54
|
+
/** A plan card's one button: it shows the full terms, and the Approve sits under them. Never an approval itself. */
|
|
55
|
+
review: "Review plan",
|
|
56
|
+
details: "Details",
|
|
51
57
|
edit: "Edit",
|
|
52
58
|
yes: "Yes",
|
|
53
59
|
cancel: "Cancel",
|
|
@@ -61,33 +67,7 @@ const sha = (text) => createHash("sha256").update(text).digest("hex");
|
|
|
61
67
|
const isAct = (one) => "act" in one;
|
|
62
68
|
const title = (store, taskId) => phoneText(chatTitle(store, taskId), 60);
|
|
63
69
|
// ---- the chatApprove term ----------------------------------------------------------
|
|
64
|
-
|
|
65
|
-
* (chat-approval.ts), or else a live signed mode carrying the chatApprove term, signed by them, that names this chat
|
|
66
|
-
* app (a mode signed before the term named its apps covers Telegram alone). `digest` names that authority for the
|
|
67
|
-
* ledger; the yes itself is the person's tap and is sealed as theirs, so it doesn't lapse when the authority does. */
|
|
68
|
-
export function chatApproveMode(store, repo, approver, now, channel) {
|
|
69
|
-
if (repo === null)
|
|
70
|
-
return { ok: false, why: "Approving from chat isn't turned on for this project." };
|
|
71
|
-
const setting = effectiveChatApproval(store, repo, approver);
|
|
72
|
-
if (setting.ok)
|
|
73
|
-
return { ok: true, digest: setting.setting.digest, limits: setting.limits, source: "setting" };
|
|
74
|
-
const mode = store.activeMode(repo, now);
|
|
75
|
-
const terms = mode === null ? null : modeTermsFromJson(mode.termsJson);
|
|
76
|
-
if (mode === null || terms === null || !terms.chatApprove)
|
|
77
|
-
return setting;
|
|
78
|
-
if (mode.signedBy !== approver || !store.accountCanAccess(approver, repo))
|
|
79
|
-
return { ok: false, why: "Only the person who signed this project's mode can approve from chat." };
|
|
80
|
-
const chats = chatApproveChatsOf(terms);
|
|
81
|
-
if (!chats.includes(channel))
|
|
82
|
-
return { ok: false, why: `Your mode approves from ${chatNames(chats)} only. Sign it again to approve from ${chatNames([channel])}.` };
|
|
83
|
-
return { ok: true, digest: mode.digest, limits: { fullAccess: terms.permissionDefault === "escalated", capMicrousd: terms.perAttemptBudgetMicrousd }, source: "mode" };
|
|
84
|
-
}
|
|
85
|
-
function fullAccess(profile) {
|
|
86
|
-
if (profile === null || profile === undefined)
|
|
87
|
-
return false;
|
|
88
|
-
return profile.provider === "claude" ? profile.permissionArgv === "bypassPermissions"
|
|
89
|
-
: profile.provider === "gemini" ? profile.approvalArgv === "yolo" : isAcpProfile(profile) ? true : profile.sandboxMode === "danger-full-access";
|
|
90
|
-
}
|
|
70
|
+
export { chatApproveMode };
|
|
91
71
|
const money = (micro) => `$${(micro / 1_000_000).toFixed(2)}`;
|
|
92
72
|
/** A plan this person's chat may approve now: the term, the plan's exact bytes, and none of the reasons it must open
|
|
93
73
|
* Toolroll instead. `digest` binds the scope and the plan document the card shows. */
|
|
@@ -115,19 +95,16 @@ from) {
|
|
|
115
95
|
const family = store.taskFamilyOf(taskId, [ref.repo], false);
|
|
116
96
|
if (family === null || family.problem !== null || family.current.id !== taskId)
|
|
117
97
|
return { ok: false, why: "A newer version of this task is current." };
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
return { ok: false, why: "This plan was written for you, so you approve it in Toolroll." };
|
|
98
|
+
// Terms a machine wrote, or checks that reach past the sandbox: a person reads every word before a yes, so the card shows them whole.
|
|
99
|
+
const whole = ref.coordinatorCid !== null || quarantinedAuthor(scope.proposedVia ?? null);
|
|
121
100
|
if (scope.profileState === "unresolved" || scope.routeEra == null || (scope.termsProblem ?? null) !== null)
|
|
122
101
|
return { ok: false, why: "This plan can't say exactly which agents run, so you approve it in Toolroll." };
|
|
123
|
-
const
|
|
124
|
-
if (
|
|
125
|
-
return { ok: false, why:
|
|
126
|
-
const
|
|
127
|
-
if (
|
|
128
|
-
return { ok: false, why:
|
|
129
|
-
if (store.approvalGate(taskId, approver, "mode").verdict !== "seal")
|
|
130
|
-
return { ok: false, why: "This plan touches protected work, so two people approve it in Toolroll." };
|
|
102
|
+
const limited = chatLimitProblem(scope, mode.limits, mode.source === "setting" ? "your chat approval setting" : "your mode");
|
|
103
|
+
if (limited !== null)
|
|
104
|
+
return { ok: false, why: limited };
|
|
105
|
+
const gate = store.approvalGate(taskId, approver, "person");
|
|
106
|
+
if (gate.verdict === "refuse")
|
|
107
|
+
return { ok: false, why: gateWords(gate) };
|
|
131
108
|
if (store.scopePolicyRefusal(taskId) !== null)
|
|
132
109
|
return { ok: false, why: "Your organisation's policy needs this plan approved in Toolroll." };
|
|
133
110
|
// A revision approves only against notes that still verify, as on the console.
|
|
@@ -156,7 +133,8 @@ from) {
|
|
|
156
133
|
if (!verified)
|
|
157
134
|
return { ok: false, why: "The saved plan can't be verified here, so you approve it in Toolroll." };
|
|
158
135
|
}
|
|
159
|
-
|
|
136
|
+
// Terms shown whole are part of what the card is bound to, so the digest kept on its buttons covers those words.
|
|
137
|
+
return { ok: true, modeDigest: mode.digest, source: mode.source, scope, whole, digest: sha(`${scope.digest}:${plan?.sha256 ?? ""}${whole ? `:${cardTermsDigest(scopeTermsText(scope))}` : ""}`) };
|
|
160
138
|
}
|
|
161
139
|
/** A ready pull request this person's chat may merge now, bound to the exact commit. */
|
|
162
140
|
export function mergeInChat(store, taskId, run, approver, now, channel) {
|
|
@@ -390,9 +368,14 @@ export function planCardText(store, taskId, scope, root, channel) {
|
|
|
390
368
|
const goal = scope.goal.split("\n").filter(one => one.trim() !== "").slice(0, 2).map(one => phoneText(one, 160));
|
|
391
369
|
const allowing = [permissionPlainWords(scope.profile), scope.budgetMicrousd === null ? "no attempt limit" : `up to ${money(scope.budgetMicrousd)} per attempt`]
|
|
392
370
|
.filter((one) => one !== null);
|
|
371
|
+
const changed = routeChangeOf(store, taskId);
|
|
372
|
+
// Fallback agents are terms: in full on every card, near the top, and never cut.
|
|
373
|
+
const chain = chainTermsLines(scope);
|
|
393
374
|
if (channel === "telegram") {
|
|
394
375
|
return [
|
|
395
376
|
`Plan ready: ${title(store, taskId)}`,
|
|
377
|
+
...(changed === null ? [] : [changed]),
|
|
378
|
+
...chain,
|
|
396
379
|
`Starting allows: ${allowing.join(" · ")}`,
|
|
397
380
|
"",
|
|
398
381
|
...goal,
|
|
@@ -405,6 +388,8 @@ export function planCardText(store, taskId, scope, root, channel) {
|
|
|
405
388
|
}
|
|
406
389
|
return [
|
|
407
390
|
`Plan ready: ${title(store, taskId)}`,
|
|
391
|
+
...(changed === null ? [] : [changed]),
|
|
392
|
+
...chain,
|
|
408
393
|
"",
|
|
409
394
|
...goal,
|
|
410
395
|
"",
|
|
@@ -433,7 +418,7 @@ export function decideOffer(store, target, who, now, channel, root) {
|
|
|
433
418
|
const fresh = resultFresh(store, target.taskId, target.run, who, now, root);
|
|
434
419
|
if (!fresh.ok)
|
|
435
420
|
return null;
|
|
436
|
-
const look = { label: DECIDE_LABELS.
|
|
421
|
+
const look = { label: DECIDE_LABELS.open, path: chatResultHref(target.taskId, target.run, "changes") };
|
|
437
422
|
// A result accepted only with a reason ("Accept anyway") keeps its link: the reason is typed on the result.
|
|
438
423
|
if (fresh.exception)
|
|
439
424
|
return null;
|
|
@@ -461,7 +446,7 @@ export function decideOffer(store, target, who, now, channel, root) {
|
|
|
461
446
|
catch {
|
|
462
447
|
head = [title(store, target.taskId)];
|
|
463
448
|
}
|
|
464
|
-
return { text: [...head, "",
|
|
449
|
+
return { text: [...head, "", needsFullCard(plan.scope)].join("\n"), rows: [[{ label: "Approve in Toolroll", path: chatControlHref("approval", target.taskId) }]] };
|
|
465
450
|
}
|
|
466
451
|
// A deploy gate is approved from chat only through its release card (chat-management.md §0a.5).
|
|
467
452
|
if (release) {
|
|
@@ -473,15 +458,17 @@ export function decideOffer(store, target, who, now, channel, root) {
|
|
|
473
458
|
rows: [[{ act: "approve", run: null, digest: releaseDigest(card), label: "Continue" }], [{ label: DECIDE_LABELS.edit, path: chatControlHref("approval", target.taskId) }, { act: "not-now", run: null, digest: releaseDigest(card) }]],
|
|
474
459
|
...(channel === "telegram" ? { file: { name: card.fileName, text: card.full } } : {}) };
|
|
475
460
|
}
|
|
476
|
-
|
|
477
|
-
|
|
461
|
+
// One line, one button and Details (docs/plans/plain-toolroll.md P1.5): Review plan shows the full terms in the
|
|
462
|
+
// card, and only there does Approve appear, bound to the same digest.
|
|
463
|
+
return { text: planLineOf(store, target.taskId, ref.id),
|
|
464
|
+
rows: [[{ act: "approve", run: null, digest: plan.digest, label: DECIDE_LABELS.review }], [{ label: DECIDE_LABELS.details, path: chatControlHref("approval", target.taskId) }]] };
|
|
478
465
|
}
|
|
479
466
|
case "requests": return requestsOffer(store, target.taskId, who, now, channel, root);
|
|
480
467
|
case "merge": {
|
|
481
468
|
const merge = mergeInChat(store, target.taskId, target.run, who.name, now, channel);
|
|
482
469
|
if (!merge.ok)
|
|
483
470
|
return null;
|
|
484
|
-
return { text: null, rows: [[{ act: "merge", run: target.run, digest: merge.head }], [{ label: DECIDE_LABELS.
|
|
471
|
+
return { text: null, rows: [[{ act: "merge", run: target.run, digest: merge.head }], [{ label: DECIDE_LABELS.open, path: chatResultHref(target.taskId, target.run, "changes") }]] };
|
|
485
472
|
}
|
|
486
473
|
}
|
|
487
474
|
}
|
|
@@ -565,7 +552,7 @@ export function isDecideToken(store, token) {
|
|
|
565
552
|
/** A release card's acts carry its card digest (release-card.ts), never a plan's. */
|
|
566
553
|
const RELEASE_DIGEST = "release:";
|
|
567
554
|
/** Why a password-only scope (passwordOnlySeal) isn't approved from chat, for a release or a plan. */
|
|
568
|
-
const
|
|
555
|
+
const needsFullCard = (scope) => `This ${deployGateScope(scope) ? "release" : "plan"}'s checks run outside the sandbox, so this card can't approve it. Approve it in Toolroll, or ask your lead for its approval card, which shows every term.`;
|
|
569
556
|
/** Whether only a person's password ceremony seals this scope (store.ts sealScopeApproval): checks that reach beyond the
|
|
570
557
|
* sandbox (sign-ins, or none at all) or extra macOS services, sealed into its profile. */
|
|
571
558
|
const passwordOnlySeal = (scope) => {
|
|
@@ -604,7 +591,7 @@ seat, root) {
|
|
|
604
591
|
if (!deployGateScope(plan.scope))
|
|
605
592
|
return { ok: false, why: "This plan isn't a release." };
|
|
606
593
|
if (passwordOnlySeal(plan.scope))
|
|
607
|
-
return { ok: false, why:
|
|
594
|
+
return { ok: false, why: needsFullCard(plan.scope) };
|
|
608
595
|
const ref = store.lookupRef(taskId);
|
|
609
596
|
if (ref === null || nightlyTaskRef(store, ref.id))
|
|
610
597
|
return { ok: false, why: NIGHTLY_CHECK_ONLY };
|
|
@@ -623,7 +610,7 @@ seat, root) {
|
|
|
623
610
|
return { ok: true, card, modeDigest: plan.modeDigest, source: plan.source, scope: plan.scope };
|
|
624
611
|
}
|
|
625
612
|
const QUESTIONS = {
|
|
626
|
-
accept: name => `Accept
|
|
613
|
+
accept: name => `Accept "${name}"?`,
|
|
627
614
|
retry: name => `Retry "${name}"? It queues another attempt.`,
|
|
628
615
|
approve: name => `Approve and start "${name}"?`,
|
|
629
616
|
merge: name => `Merge "${name}"?`,
|
|
@@ -698,7 +685,7 @@ function staleWhy(store, row, who, now, channel, root) {
|
|
|
698
685
|
if (deployGateScope(plan.scope))
|
|
699
686
|
return "This is a release: approve it from its release card.";
|
|
700
687
|
if (passwordOnlySeal(plan.scope))
|
|
701
|
-
return
|
|
688
|
+
return needsFullCard(plan.scope);
|
|
702
689
|
return plan.digest === row.digest ? null : "The plan changed since this card was sent.";
|
|
703
690
|
}
|
|
704
691
|
case "merge": {
|
|
@@ -712,7 +699,7 @@ function staleWhy(store, row, who, now, channel, root) {
|
|
|
712
699
|
/** The links a card keeps once its acts are spent or stale: where to look, never a token. */
|
|
713
700
|
export function linksFor(target) {
|
|
714
701
|
switch (target.kind) {
|
|
715
|
-
case "result": return [[{ label: DECIDE_LABELS.
|
|
702
|
+
case "result": return [[{ label: DECIDE_LABELS.open, link: { label: DECIDE_LABELS.open, path: chatResultHref(target.taskId, target.run, "changes") } }]];
|
|
716
703
|
case "merge": return [[{ label: "Open task", link: { label: "Open task", path: chatControlHref("task", target.taskId) } }]];
|
|
717
704
|
case "plan": return [[{ label: "Review & start", link: { label: "Review & start", path: chatControlHref("approval", target.taskId) } }]];
|
|
718
705
|
case "failed": return [[{ label: "Open task", link: { label: "Open task", path: chatControlHref("task", target.taskId) } }]];
|
|
@@ -740,7 +727,7 @@ export function planWhereNow(store, taskId, who, now, root) {
|
|
|
740
727
|
}
|
|
741
728
|
return { said: `"${name}" is under way.`, rows: [] };
|
|
742
729
|
}
|
|
743
|
-
const QUESTION_STARTS = ["Accept and finish \"", "Retry \"", "Approve and start \"", "Merge \"", "Approve release "];
|
|
730
|
+
const QUESTION_STARTS = ["Accept \"", "Accept and finish \"", "Retry \"", "Approve and start \"", "Merge \"", "Approve release "];
|
|
744
731
|
/** The card's words without a question this module appended. */
|
|
745
732
|
function cardBody(shown) {
|
|
746
733
|
const at = shown.lastIndexOf("\n\n");
|
|
@@ -847,6 +834,22 @@ export function applyDecideTap(store, seat, input) {
|
|
|
847
834
|
return { ack: "Confirm below.", edit: { text: `${body}\n\n${asked}`,
|
|
848
835
|
rows: [[{ label: DECIDE_LABELS.yes, token: yes }, { label: DECIDE_LABELS.cancel, token: cancel }]], tokens: [yes, cancel] } };
|
|
849
836
|
}
|
|
837
|
+
// A plan card's Review plan: the full terms in the card, and under them the Approve (the yes, bound to the same
|
|
838
|
+
// digest the card was minted with, checked again at the tap). Approve is reachable only from here.
|
|
839
|
+
if (row.act === "approve") {
|
|
840
|
+
const plan = planInChat(store, row.taskId, who.name, now, seat.channel, input.root);
|
|
841
|
+
if (!plan.ok)
|
|
842
|
+
return stale(plan.why);
|
|
843
|
+
spend();
|
|
844
|
+
const spec = { act: "approve", run: null, digest: row.digest };
|
|
845
|
+
const yes = mint(store, seat, input.message, spec, row.taskId, "yes", now);
|
|
846
|
+
const later = mint(store, seat, input.message, { act: "not-now", run: null, digest: row.digest }, row.taskId, "offer", now);
|
|
847
|
+
// Terms a machine wrote go on the card word for word, or the yes isn't to what they say.
|
|
848
|
+
if (plan.whole && scopeTermsText(plan.scope).length > PLATFORM_LIMITS[seat.channel] - ROOM)
|
|
849
|
+
return stale(`This plan's ${TOO_LONG}, so you approve it in Toolroll.`);
|
|
850
|
+
return { ack: "Read the plan, then approve.", edit: { text: `${plan.whole ? `${name}\n\n${scopeTermsText(plan.scope)}` : planCardText(store, row.taskId, plan.scope, input.root, seat.channel)}\n\n${QUESTIONS.approve(name)} This approves exactly this plan.`,
|
|
851
|
+
rows: [[{ label: DECIDE_LABELS.approve, token: yes }], [{ label: DECIDE_LABELS.edit, link: { label: DECIDE_LABELS.edit, path: chatControlHref("approval", row.taskId) } }, { label: DECIDE_LABELS["not-now"], token: later }]], tokens: [yes, later] } };
|
|
852
|
+
}
|
|
850
853
|
// The first tap asks; only the yes acts, bound to exactly what this button was.
|
|
851
854
|
spend();
|
|
852
855
|
const spec = { act: row.act, run: row.run, digest: row.digest };
|
|
@@ -905,9 +908,19 @@ export function applyDecideTap(store, seat, input) {
|
|
|
905
908
|
if (deployGateScope(plan.scope))
|
|
906
909
|
return notDone("This is a release: approve it from its release card.");
|
|
907
910
|
if (passwordOnlySeal(plan.scope))
|
|
908
|
-
return notDone(
|
|
909
|
-
|
|
911
|
+
return notDone(needsFullCard(plan.scope));
|
|
912
|
+
// The one approval every road shares (scope.ts approve): the rules, two-person votes and the proof, as this person.
|
|
913
|
+
// plan.digest matched this button's kept digest above (staleWhy), so these are the words its card showed.
|
|
914
|
+
const words = scopeTermsText(plan.scope);
|
|
915
|
+
if (plan.whole && (plan.digest !== row.digest || words.length > PLATFORM_LIMITS[seat.channel] - ROOM))
|
|
916
|
+
return notDone("this plan changed. Open it in Toolroll.");
|
|
917
|
+
const approved = approve(store, row.taskId, who.name, now, plan.scope.digest, "", {}, undefined, { modeDigest: plan.modeDigest, shown: plan.whole ? { expect: cardTermsDigest(words), terms: words } : null });
|
|
918
|
+
if (!approved.ok && approved.reason === "second-approver")
|
|
919
|
+
return done(gateWords({ verdict: "vote", have: approved.have, need: 2, already: approved.already }));
|
|
920
|
+
if (!approved.ok)
|
|
910
921
|
return notDone("the plan couldn't be approved from chat. Open it in Toolroll.");
|
|
922
|
+
store.recordAction({ at: now.toISOString(), actor: who.name, repo: ref.repo, taskId: row.taskId, runId: null, action: "plan approved in chat", outcome: "approved",
|
|
923
|
+
source: "request", detail: `via ${seat.channel} · chat binding #${seat.binding} · ${plan.source} ${plan.modeDigest}` });
|
|
911
924
|
return done("✓ Approved. Work starts when a worker is free.");
|
|
912
925
|
}
|
|
913
926
|
case "merge": {
|
|
@@ -1007,3 +1020,7 @@ export function decideTargetOf(row) {
|
|
|
1007
1020
|
return { kind: "failed", taskId: row.taskId };
|
|
1008
1021
|
return null;
|
|
1009
1022
|
}
|
|
1023
|
+
/** A plan card's one line: the task's number, "plan ready", and its title (docs/plans/plain-toolroll.md P1.5). */
|
|
1024
|
+
function planLineOf(store, taskId, number) {
|
|
1025
|
+
return `#${number}: plan ready · ${title(store, taskId)}`;
|
|
1026
|
+
}
|
package/dist/chat-delivery.js
CHANGED
|
@@ -10,6 +10,9 @@ import { ceilingDigestOf, verifyApproverStanding } from "./principal.js";
|
|
|
10
10
|
import { ChatState, ChatDeliveryError, chatHash, savedChatPart, } from "./chat-delivery-state.js";
|
|
11
11
|
import { readChatActionBody, readChatMessageBody, readChatPairBody, readChatPart } from "./contracts/chat-content.js";
|
|
12
12
|
import { readProposalActionRow } from "./contracts/chat-callback-rows.js";
|
|
13
|
+
import { cardContent, cardPart } from "./chat-card-part.js";
|
|
14
|
+
import { flowDoneCard } from "./flow-result-card.js";
|
|
15
|
+
import { cardsFirst, isCardProposal } from "./chat-card.js";
|
|
13
16
|
import { answerChatFlowPrompt, answerChoiceMessage, applyChatFlowTap, flowDecisionParts, flowSendParts } from "./chat-flow.js";
|
|
14
17
|
import { connectChannel, FLOW_WORDS, takeChannelMessage, watchedChannel } from "./chat-inbox.js";
|
|
15
18
|
import { triggerConfigOf } from "./flow-triggers.js";
|
|
@@ -484,10 +487,9 @@ export async function processChatEvent(options) {
|
|
|
484
487
|
task: image.taskId,
|
|
485
488
|
run: image.run,
|
|
486
489
|
});
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
.
|
|
490
|
-
parts.push({ text: "", proposal: proposal.id });
|
|
490
|
+
// Cards first, so what a card leads to sits right under it. A card is an ordinary part: it confirms nothing.
|
|
491
|
+
for (const proposal of cardsFirst(store.listLeadProposals(turn.thread, ["pending"]).filter((p) => p.turn === turn.id)))
|
|
492
|
+
parts.push(isCardProposal(proposal) ? cardPart(proposal) : { text: "", proposal: proposal.id });
|
|
491
493
|
state.plan(event.id, parts, nowOf(options));
|
|
492
494
|
return true;
|
|
493
495
|
}
|
|
@@ -655,6 +657,7 @@ export function applyChatAction(options, event, binding, repos) {
|
|
|
655
657
|
// A paired chat seat speaks for its account's full authority (pairing is a full session's act).
|
|
656
658
|
authority: "full",
|
|
657
659
|
via: options.state.provider,
|
|
660
|
+
binding: binding.id,
|
|
658
661
|
evidenceRoot: options.evidenceRoot,
|
|
659
662
|
confirm: phase === "yes",
|
|
660
663
|
deferSignal: (signal) => signals.push(signal),
|
|
@@ -1053,6 +1056,12 @@ function offerNotice(options, notification, binding, registry) {
|
|
|
1053
1056
|
ts: "", thread: "", payload: {}, created: now.toISOString() });
|
|
1054
1057
|
state.plan(id, flowSendParts(store, notification, state.provider), now);
|
|
1055
1058
|
}
|
|
1059
|
+
else if (personal && flowDoneCard(store, notification, now) !== null) {
|
|
1060
|
+
// A card that reached Done: what it went through, as a card with its links.
|
|
1061
|
+
state.enqueue({ id, installation: identity.installation, binding: binding.id, kind: "notice", channel: binding.channel, member: binding.member,
|
|
1062
|
+
ts: "", thread: "", payload: {}, created: now.toISOString() });
|
|
1063
|
+
state.plan(id, [cardContent(flowDoneCard(store, notification, now))], now);
|
|
1064
|
+
}
|
|
1056
1065
|
else if (notification.kind === "flow-card" && questionParts(store, notification, binding) !== null) {
|
|
1057
1066
|
// A subagent's question (v93): its options and "Answer in words" on the notice, for the person it asks.
|
|
1058
1067
|
state.enqueue({ id, installation: identity.installation, binding: binding.id, kind: "notice", channel: binding.channel, member: binding.member,
|
package/dist/chat-flow.d.ts
CHANGED
|
@@ -1,30 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Flow decisions in Slack, Discord and Teams (v88): the Approve / Edit /
|
|
3
|
-
* Send back that Telegram has (telegram-flow.ts), on the notice itself.
|
|
4
|
-
*
|
|
5
|
-
* - The notice carries the draft (split over as many parts as it needs, so
|
|
6
|
-
* what is approved is what was read) and the buttons on its last part.
|
|
7
|
-
* - Approve decides through the same door as the console (decideFlowCard),
|
|
8
|
-
* as the paired person, for exactly the visit the notice was about, then
|
|
9
|
-
* repaints the notice with what happened and no buttons.
|
|
10
|
-
* - Edit and Send back open a prompt: the person's next message in their
|
|
11
|
-
* chat with Toolroll is the new draft (which comes back with fresh
|
|
12
|
-
* buttons) or the note. "cancel" leaves it, and a prompt lapses after
|
|
13
|
-
* 30 minutes. Chat apps can't all name the message a reply answers, so
|
|
14
|
-
* the prompt says plainly what the next message does.
|
|
15
|
-
*
|
|
16
|
-
* Every button is one opaque token for one visit of one card, on the part it
|
|
17
|
-
* rides: a tap on any other message, from anyone but the paired person, or
|
|
18
|
-
* for a card that moved on, changes nothing.
|
|
19
|
-
*
|
|
20
|
-
* A "Send to me" visit (flow-send.ts) is one notice with its links; a "Person
|
|
21
|
-
* chooses" visit is the same with the zone's own options as buttons. Instead
|
|
22
|
-
* of a tap, a reply in the notice's thread is the note for where replies go;
|
|
23
|
-
* the first other message in their chat is asked about once ("Use this as
|
|
24
|
-
* your note?"), and everything else goes to the lead as usual. Choices are
|
|
25
|
-
* made through chooseFlowCard, by that person alone, for exactly that visit,
|
|
26
|
-
* and spend that visit's buttons in every chat app.
|
|
27
|
-
*/
|
|
28
1
|
import type { ChatProvider } from "./contracts/chat-tables.js";
|
|
29
2
|
import { type ChatBinding, type ChatContent, type ChatEvent, type ChatState } from "./chat-delivery-state.js";
|
|
30
3
|
import type { Store } from "./store.js";
|
package/dist/chat-flow.js
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow decisions in Slack, Discord and Teams (v88): the Approve / Edit /
|
|
3
|
+
* Send back that Telegram has (telegram-flow.ts), on the notice itself.
|
|
4
|
+
*
|
|
5
|
+
* - The notice carries the draft (split over as many parts as it needs, so
|
|
6
|
+
* what is approved is what was read) and the buttons on its last part.
|
|
7
|
+
* - Approve decides through the same door as the console (decideFlowCard),
|
|
8
|
+
* as the paired person, for exactly the visit the notice was about, then
|
|
9
|
+
* repaints the notice with what happened and no buttons.
|
|
10
|
+
* - Edit and Send back open a prompt: the person's next message in their
|
|
11
|
+
* chat with Toolroll is the new draft (which comes back with fresh
|
|
12
|
+
* buttons) or the note. "cancel" leaves it, and a prompt lapses after
|
|
13
|
+
* 30 minutes. Chat apps can't all name the message a reply answers, so
|
|
14
|
+
* the prompt says plainly what the next message does.
|
|
15
|
+
*
|
|
16
|
+
* Every button is one opaque token for one visit of one card, on the part it
|
|
17
|
+
* rides: a tap on any other message, from anyone but the paired person, or
|
|
18
|
+
* for a card that moved on, changes nothing.
|
|
19
|
+
*
|
|
20
|
+
* A "Send to me" visit (flow-send.ts) is one notice with its links; a "Person
|
|
21
|
+
* chooses" visit is the same with the zone's own options as buttons. Instead
|
|
22
|
+
* of a tap, a reply in the notice's thread is the note for where replies go;
|
|
23
|
+
* the first other message in their chat is asked about once ("Use this as
|
|
24
|
+
* your note?"), and everything else goes to the lead as usual. Choices are
|
|
25
|
+
* made through chooseFlowCard, by that person alone, for exactly that visit,
|
|
26
|
+
* and spend that visit's buttons in every chat app.
|
|
27
|
+
*/
|
|
28
|
+
import { cardContent } from "./chat-card-part.js";
|
|
1
29
|
import { LIMITS } from "./decision.js";
|
|
2
30
|
import { keptDraft } from "./flow-draft.js";
|
|
3
31
|
import { decideFlowCard, flowCardHref, flowDefinitionOf } from "./flow-engine.js";
|
|
@@ -66,11 +94,12 @@ export function flowSendParts(store, notification, app) {
|
|
|
66
94
|
const content = kept === null ? null : readFlowSend(kept.contentJson);
|
|
67
95
|
if (content === null)
|
|
68
96
|
return null;
|
|
97
|
+
const card = content.card === undefined ? [] : [cardContent(content.card)];
|
|
69
98
|
const voiced = itemsText(content, notification.subject, app);
|
|
70
99
|
if (voiced !== null)
|
|
71
|
-
return voiced.map((text, index) => index < voiced.length - 1 ? { text, voice: true } : { text, voice: true, ...sendLinks(flowSendPaths(content)) });
|
|
100
|
+
return [...voiced.map((text, index) => index < voiced.length - 1 ? { text, voice: true } : { text, voice: true, ...sendLinks(flowSendPaths(content)) }), ...card];
|
|
72
101
|
const all = pieces(chatFlowText(`${notification.subject}\n\n${notification.body}`));
|
|
73
|
-
return all.map((piece, index) => index < all.length - 1 ? { text: piece } : { text: piece, ...sendLinks(flowSendPaths(content)) });
|
|
102
|
+
return [...all.map((piece, index) => index < all.length - 1 ? { text: piece } : { text: piece, ...sendLinks(flowSendPaths(content)) }), ...card];
|
|
74
103
|
}
|
|
75
104
|
/**
|
|
76
105
|
* A "flow-decision" notice as chat parts, or null when it isn't one (or the
|
package/dist/chat-quiet.js
CHANGED
|
@@ -19,7 +19,7 @@ import { YOUIYOUX_CARD_KIND } from "./contracts/youiyoux-request.js";
|
|
|
19
19
|
* (a sign-in or plan-limit pause, an evening digest). */
|
|
20
20
|
const NEEDED_KINDS = new Set([
|
|
21
21
|
"acceptance-ready", "acceptance-evidence", "flow-decision", "flow-card", "plan-ready", "report-ready",
|
|
22
|
-
"stale-approval", "security-release", "secret-detected",
|
|
22
|
+
"stale-approval", "security-release", "secret-detected", "protected-storage",
|
|
23
23
|
]);
|
|
24
24
|
/** What still wants a person who pairs after it landed, in every chat app: an open decision or an unresolved attention
|
|
25
25
|
* notice. Routine history is never replayed to a chat that just arrived (store.ts WANTED_BEFORE_PAIRING, the same rule). */
|
|
@@ -102,7 +102,7 @@ export function quietCardView(store, taskRefs, now, root, viewer) {
|
|
|
102
102
|
const heading = `${lines.length} tasks`;
|
|
103
103
|
const projects = [...new Set(lines.map(one => projectLabel(one.project)))];
|
|
104
104
|
const text = [heading, ...lines.map(one => `${one.icon} ${phoneText(one.title, 60)} · ${one.status}`), "", projects.join(", ")].join("\n");
|
|
105
|
-
return { text, entities: [{ type: "bold", offset: 0, length: heading.length }], link: { label: "Open tasks", path: "/
|
|
105
|
+
return { text, entities: [{ type: "bold", offset: 0, length: heading.length }], link: { label: "Open tasks", path: "/tasks" } };
|
|
106
106
|
}
|
|
107
107
|
/** Facts that carry their own controls or must stand alone (a security alert, a decision, a flow card, a
|
|
108
108
|
* screenshot, an acceptance packet, a youiyoux pass's card) are never folded into a batch. */
|
|
@@ -177,7 +177,7 @@ export function finishedView(store, batch, now, root, viewer) {
|
|
|
177
177
|
return null;
|
|
178
178
|
const text = batchLine(lines.map(one => one.fact));
|
|
179
179
|
if (lines.length > 1)
|
|
180
|
-
return { text, entities: [], link: { label: "Open", path: "/
|
|
180
|
+
return { text, entities: [], link: { label: "Open", path: "/tasks" } };
|
|
181
181
|
const target = lines[0].target ?? null;
|
|
182
182
|
return { text, entities: [], link: lines[0].link, ...(lines[0].also.length === 0 ? {} : { also: lines[0].also }), ...(target === null ? {} : { target }) };
|
|
183
183
|
}
|
|
@@ -244,7 +244,7 @@ export function enqueueEveningDigests(store, now, root) {
|
|
|
244
244
|
if (body === null)
|
|
245
245
|
return;
|
|
246
246
|
if (store.enqueueNotification({ dedupeKey: `digest:evening:${person.account}:${day}`, kind: "evening-digest", subject: "Evening digest", body,
|
|
247
|
-
link: "/
|
|
247
|
+
link: "/tasks", source: { installation: true }, recipient: person.account }, now))
|
|
248
248
|
queued++;
|
|
249
249
|
});
|
|
250
250
|
}
|
|
@@ -7,8 +7,8 @@ export declare const CHAT_TASK_ACTIONS: {
|
|
|
7
7
|
readonly detail: "Pauses this task once the run acknowledges the stop. Saved work remains; other tasks continue.";
|
|
8
8
|
};
|
|
9
9
|
readonly resume: {
|
|
10
|
-
readonly label: "
|
|
11
|
-
readonly detail: "
|
|
10
|
+
readonly label: "Resume task";
|
|
11
|
+
readonly detail: "Lifts this stop’s hold and continues from saved work. Other holds and approval requirements still apply.";
|
|
12
12
|
};
|
|
13
13
|
readonly retry: {
|
|
14
14
|
readonly label: "Try again";
|
|
@@ -48,6 +48,7 @@ export declare function chatTaskRun(store: Store, task: string, operation: "stop
|
|
|
48
48
|
export declare function applyChatTaskAction(store: Store, who: VerifiedApprover, payload: Record<string, unknown>, now: Date, web: boolean, controls?: Pick<StopRequest, "deferSignal"> & {
|
|
49
49
|
via?: StopRequest["via"];
|
|
50
50
|
evidenceRoot?: string;
|
|
51
|
+
person?: boolean;
|
|
51
52
|
}): {
|
|
52
53
|
ok: true;
|
|
53
54
|
taskId: string;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { verifiedAuthor } from "./store.js";
|
|
2
|
-
import {
|
|
2
|
+
import { chatApproveMode, effectiveChatApproval } from "./chat-approval.js";
|
|
3
|
+
import { requestTaskStop, resumeTaskStop, taskControlOf } from "./task-control.js";
|
|
3
4
|
import { authorizePlanUnderMode } from "./plan-auto.js";
|
|
4
5
|
import { fileAddTestsTask, requestFollowUpChecks } from "./result-follow-ups.js";
|
|
5
6
|
export const CHAT_TASK_ACTIONS = {
|
|
6
7
|
// Build #1604 feedback: one short consequence that still says saved work
|
|
7
8
|
// remains, other tasks continue, and the pause waits for acknowledgement.
|
|
8
9
|
stop: { label: "Stop task", detail: "Pauses this task once the run acknowledges the stop. Saved work remains; other tasks continue." },
|
|
9
|
-
resume: { label: "
|
|
10
|
+
resume: { label: "Resume task", detail: "Lifts this stop’s hold and continues from saved work. Other holds and approval requirements still apply." },
|
|
10
11
|
retry: { label: "Try again", detail: "Queues another attempt. Existing approvals and holds still apply." },
|
|
11
12
|
plan: { label: "Plan first", detail: "Requests a plan. Building still requires approval." },
|
|
12
13
|
wait_for: { label: "Add dependency", detail: "Waits for the selected task to finish." },
|
|
@@ -52,9 +53,22 @@ export function applyChatTaskAction(store, who, payload, now, web, controls = {}
|
|
|
52
53
|
const run = chatTaskRun(store, task, operation, now);
|
|
53
54
|
if (run === null || run !== payload["run"])
|
|
54
55
|
return { ok: false, message: "This attempt changed. Review the current task before confirming." };
|
|
55
|
-
//
|
|
56
|
-
if (operation === "resume")
|
|
57
|
-
|
|
56
|
+
// Resuming is an approval: a person's own tap or signed-in click lifts the stop; any other road only asks for it.
|
|
57
|
+
if (operation === "resume") {
|
|
58
|
+
if (controls.person !== true)
|
|
59
|
+
return { ok: true, taskId: task, said: `Resume requested for ${task}. Finish it in your chat or on the task.` };
|
|
60
|
+
// Resuming is an approval: a person's own off holds here as on every other one.
|
|
61
|
+
const allowed = effectiveChatApproval(store, ref.repo, who.name);
|
|
62
|
+
if (!allowed.ok && controls.via !== "web") {
|
|
63
|
+
const mode = controls.via === undefined || controls.via === "cli" ? allowed : chatApproveMode(store, ref.repo, who.name, now, controls.via);
|
|
64
|
+
if (!mode.ok)
|
|
65
|
+
return { ok: false, message: mode.why };
|
|
66
|
+
}
|
|
67
|
+
if (!allowed.ok && controls.via === "web")
|
|
68
|
+
return { ok: false, message: allowed.why };
|
|
69
|
+
const resumed = resumeTaskStop(store, { taskId: task, runId: run, by: verifiedAuthor(who.name), via: controls.via ?? "web" }, now);
|
|
70
|
+
return resumed.ok ? { ok: true, taskId: task, said: `Stop released. ${resumed.gate ? "Other requirements still prevent the next attempt." : "The next attempt may start."}` } : { ok: false, message: resumed.detail };
|
|
71
|
+
}
|
|
58
72
|
const { via, evidenceRoot: _root, ...rest } = controls;
|
|
59
73
|
const result = requestTaskStop(store, { taskId: task, runId: run, by: verifiedAuthor(who.name), via: via ?? (web ? "web" : "cli"), ...rest }, now);
|
|
60
74
|
return result.ok ? { ok: true, taskId: task, said: `Stop requested for ${task}, run #${run}.` }
|
package/dist/chat-terms.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Scope } from "./scope.js";
|
|
2
2
|
/** What a plan's yes allows an agent to do, in a few plain words (the approval sheet's "You're allowing"). */
|
|
3
3
|
export declare function permissionPlainWords(profile: Scope["profile"] | null | undefined): string | null;
|
|
4
|
-
export type ShownScope = Pick<Scope, "goal" | "outOfScope" | "touches" | "acceptance" | "profile" | "budgetMicrousd"
|
|
4
|
+
export type ShownScope = Pick<Scope, "goal" | "outOfScope" | "touches" | "acceptance" | "profile" | "budgetMicrousd"> & Partial<Pick<Scope, "proposedChainJson">>;
|
|
5
5
|
/** Every word of the terms a yes agrees to, as the card shows them: the goal, what's excluded, the files (or plainly any
|
|
6
6
|
* file), each requirement with the evidence it needs, and what the agent may do and spend. Nothing is shortened. */
|
|
7
7
|
export declare function scopeTermsText(scope: ShownScope): string;
|
|
8
8
|
/** The digest of the terms shown, bound to the scope's own digest: a changed word, limit or route is another digest. */
|
|
9
9
|
export declare function shownTermsDigest(scope: ShownScope & Pick<Scope, "digest">): string;
|
|
10
|
+
/** The digest of the exact words a card showed, taken when the card was made and kept with it. */
|
|
11
|
+
export declare function cardTermsDigest(terms: string): string;
|
|
12
|
+
/** Whether these words show this scope as it is now: its whole terms text, word for word, and what only a person's own
|
|
13
|
+
* yes may agree to (checks past the sandbox, extra services). Part of the terms, or an earlier version's, is not. */
|
|
14
|
+
export declare function cardShowsScope(scope: ShownScope, terms: string): boolean;
|
|
15
|
+
/** Whether a scope lets its agent do anything, nothing asking first. */
|
|
16
|
+
export declare function scopeFullAccess(profile: Scope["profile"] | null | undefined): boolean;
|
|
17
|
+
/** Why a person's own limits refuse this scope, or null. */
|
|
18
|
+
export declare function chatLimitProblem(scope: Pick<Scope, "profile" | "budgetMicrousd">, limits: {
|
|
19
|
+
fullAccess: boolean;
|
|
20
|
+
capMicrousd: number | null;
|
|
21
|
+
}, yours: string): string | null;
|
package/dist/chat-terms.js
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { isAcpProfile } from "./acp-agents.js";
|
|
9
|
+
import { checkAccessWords } from "./check-access.js";
|
|
10
|
+
import { sandboxTermsWords } from "./project-sandbox.js";
|
|
11
|
+
import { chainTermsLines } from "./scope.js";
|
|
9
12
|
/** What a plan's yes allows an agent to do, in a few plain words (the approval sheet's "You're allowing"). */
|
|
10
13
|
export function permissionPlainWords(profile) {
|
|
11
14
|
if (profile === null || profile === undefined)
|
|
@@ -38,9 +41,41 @@ export function scopeTermsText(scope) {
|
|
|
38
41
|
...(scope.acceptance.length === 0 ? ["You decide when you review the result."] : scope.acceptance.map(one => `• ${one.statement} (${one.evidence.join(", ")})`)),
|
|
39
42
|
"",
|
|
40
43
|
`Starting allows: ${allows.join(" · ")}`,
|
|
44
|
+
...((chain) => chain.length === 0 ? [] : ["", "Fallback agents:", ...chain])(chainTermsLines(scope)),
|
|
41
45
|
].join("\n");
|
|
42
46
|
}
|
|
43
47
|
/** The digest of the terms shown, bound to the scope's own digest: a changed word, limit or route is another digest. */
|
|
44
48
|
export function shownTermsDigest(scope) {
|
|
45
49
|
return createHash("sha256").update(JSON.stringify({ v: 1, scope: scope.digest, shown: scopeTermsText(scope) })).digest("hex");
|
|
46
50
|
}
|
|
51
|
+
/** The digest of the exact words a card showed, taken when the card was made and kept with it. */
|
|
52
|
+
export function cardTermsDigest(terms) {
|
|
53
|
+
return createHash("sha256").update(JSON.stringify({ v: 2, shown: terms })).digest("hex");
|
|
54
|
+
}
|
|
55
|
+
/** Whether these words show this scope as it is now: its whole terms text, word for word, and what only a person's own
|
|
56
|
+
* yes may agree to (checks past the sandbox, extra services). Part of the terms, or an earlier version's, is not. */
|
|
57
|
+
export function cardShowsScope(scope, terms) {
|
|
58
|
+
if (!terms.includes(scopeTermsText(scope)))
|
|
59
|
+
return false;
|
|
60
|
+
const profile = scope.profile;
|
|
61
|
+
if (profile === null || profile === undefined)
|
|
62
|
+
return true;
|
|
63
|
+
const access = profile.checkAccess, mach = sandboxTermsWords(profile).mach;
|
|
64
|
+
return (access === undefined || terms.includes(checkAccessWords(access))) && (mach === null || terms.includes(mach));
|
|
65
|
+
}
|
|
66
|
+
/** Whether a scope lets its agent do anything, nothing asking first. */
|
|
67
|
+
export function scopeFullAccess(profile) {
|
|
68
|
+
if (profile === null || profile === undefined)
|
|
69
|
+
return false;
|
|
70
|
+
return profile.provider === "claude" ? profile.permissionArgv === "bypassPermissions"
|
|
71
|
+
: profile.provider === "gemini" ? profile.approvalArgv === "yolo" : isAcpProfile(profile) ? true : profile.sandboxMode === "danger-full-access";
|
|
72
|
+
}
|
|
73
|
+
/** Why a person's own limits refuse this scope, or null. */
|
|
74
|
+
export function chatLimitProblem(scope, limits, yours) {
|
|
75
|
+
if (scopeFullAccess(scope.profile) && !limits.fullAccess)
|
|
76
|
+
return `This plan asks for more access than ${yours} allows.`;
|
|
77
|
+
const cap = limits.capMicrousd;
|
|
78
|
+
if (cap !== null && (scope.budgetMicrousd === null || scope.budgetMicrousd > cap))
|
|
79
|
+
return `This plan has ${scope.budgetMicrousd === null ? "no attempt limit" : `a ${money(scope.budgetMicrousd)} attempt limit`}, more than ${yours === "your mode" ? "your mode's" : "your chat approval limit of"} ${money(cap)}.`;
|
|
80
|
+
return null;
|
|
81
|
+
}
|
package/dist/chat-voice.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export type FinishedFact = {
|
|
|
39
39
|
/** The two buttons under a result ready for a person: the real next step, then a look first. */
|
|
40
40
|
export declare const READY_ACTIONS: {
|
|
41
41
|
readonly merge: "Merge";
|
|
42
|
-
readonly complete: "Accept
|
|
42
|
+
readonly complete: "Accept";
|
|
43
43
|
readonly look: "Look first";
|
|
44
44
|
};
|
|
45
45
|
/** Whether this line asks for the result's next step, so it carries [Merge] or [Accept and finish] and [Look first]. */
|