toolroll 0.5.0
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/LICENSE +21 -0
- package/README.md +1113 -0
- package/THIRD_PARTY_NOTICES.md +449 -0
- package/dist/accent-colors.d.ts +48 -0
- package/dist/accent-colors.js +122 -0
- package/dist/action-ledger.d.ts +26 -0
- package/dist/action-ledger.js +75 -0
- package/dist/agent-fence.d.ts +42 -0
- package/dist/agent-fence.js +183 -0
- package/dist/agentconfig.d.ts +216 -0
- package/dist/agentconfig.js +454 -0
- package/dist/api-tokens.d.ts +16 -0
- package/dist/api-tokens.js +27 -0
- package/dist/approval-policy.d.ts +69 -0
- package/dist/approval-policy.js +137 -0
- package/dist/approval-rules-ui.d.ts +25 -0
- package/dist/approval-rules-ui.js +39 -0
- package/dist/assignment-adapters.d.ts +180 -0
- package/dist/assignment-adapters.js +239 -0
- package/dist/assignment-brief.d.ts +71 -0
- package/dist/assignment-brief.js +129 -0
- package/dist/assignment-delivery.d.ts +56 -0
- package/dist/assignment-delivery.js +160 -0
- package/dist/assignment-presentation.d.ts +26 -0
- package/dist/assignment-presentation.js +80 -0
- package/dist/assignment-status.d.ts +62 -0
- package/dist/assignment-status.js +152 -0
- package/dist/assignment-ui.d.ts +71 -0
- package/dist/assignment-ui.js +103 -0
- package/dist/assignment.d.ts +222 -0
- package/dist/assignment.js +399 -0
- package/dist/attest.d.ts +56 -0
- package/dist/attest.js +153 -0
- package/dist/backend.d.ts +97 -0
- package/dist/backend.js +166 -0
- package/dist/backup-ui.d.ts +22 -0
- package/dist/backup-ui.js +59 -0
- package/dist/backup.d.ts +84 -0
- package/dist/backup.js +421 -0
- package/dist/beads.d.ts +34 -0
- package/dist/beads.js +135 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +22 -0
- package/dist/board.d.ts +150 -0
- package/dist/board.js +210 -0
- package/dist/boot-identity.d.ts +63 -0
- package/dist/boot-identity.js +99 -0
- package/dist/browser/THIRD_PARTY_NOTICES.txt +2295 -0
- package/dist/browser/workspace.css +4 -0
- package/dist/browser/workspace.js +225 -0
- package/dist/browser-crew.d.ts +11 -0
- package/dist/browser-crew.js +50 -0
- package/dist/browser-shell.d.ts +8 -0
- package/dist/browser-shell.js +60 -0
- package/dist/browser-workspace.d.ts +780 -0
- package/dist/browser-workspace.js +47 -0
- package/dist/budget-alerts.d.ts +19 -0
- package/dist/budget-alerts.js +46 -0
- package/dist/builder.d.ts +370 -0
- package/dist/builder.js +3262 -0
- package/dist/capscan.d.ts +35 -0
- package/dist/capscan.js +113 -0
- package/dist/chat-acceptance.d.ts +71 -0
- package/dist/chat-acceptance.js +120 -0
- package/dist/chat-actions.d.ts +265 -0
- package/dist/chat-actions.js +1273 -0
- package/dist/chat-channel.d.ts +109 -0
- package/dist/chat-channel.js +652 -0
- package/dist/chat-continuity.d.ts +32 -0
- package/dist/chat-continuity.js +317 -0
- package/dist/chat-controls.d.ts +113 -0
- package/dist/chat-controls.js +53 -0
- package/dist/chat-delivery-state.d.ts +159 -0
- package/dist/chat-delivery-state.js +309 -0
- package/dist/chat-delivery.d.ts +38 -0
- package/dist/chat-delivery.js +693 -0
- package/dist/chat-display.d.ts +1 -0
- package/dist/chat-display.js +18 -0
- package/dist/chat-evidence.d.ts +123 -0
- package/dist/chat-evidence.js +207 -0
- package/dist/chat-flow.d.ts +44 -0
- package/dist/chat-flow.js +215 -0
- package/dist/chat-inbox.d.ts +57 -0
- package/dist/chat-inbox.js +123 -0
- package/dist/chat-polish.d.ts +16 -0
- package/dist/chat-polish.js +135 -0
- package/dist/chat-review.d.ts +46 -0
- package/dist/chat-review.js +98 -0
- package/dist/chat-rooms.d.ts +123 -0
- package/dist/chat-rooms.js +216 -0
- package/dist/chat-task-actions.d.ts +44 -0
- package/dist/chat-task-actions.js +79 -0
- package/dist/check-progress.d.ts +51 -0
- package/dist/check-progress.js +290 -0
- package/dist/child-database.d.ts +12 -0
- package/dist/child-database.js +30 -0
- package/dist/claim.d.ts +688 -0
- package/dist/claim.js +1740 -0
- package/dist/cli.d.ts +137 -0
- package/dist/cli.js +1461 -0
- package/dist/codex-limits.d.ts +15 -0
- package/dist/codex-limits.js +145 -0
- package/dist/coding-context.d.ts +55 -0
- package/dist/coding-context.js +92 -0
- package/dist/coding-handoff.d.ts +57 -0
- package/dist/coding-handoff.js +277 -0
- package/dist/coding-provider.d.ts +72 -0
- package/dist/coding-provider.js +424 -0
- package/dist/coding-shipping-ui.d.ts +4 -0
- package/dist/coding-shipping-ui.js +12 -0
- package/dist/coding-types.d.ts +62 -0
- package/dist/coding-types.js +1 -0
- package/dist/coding-ui.d.ts +17 -0
- package/dist/coding-ui.js +364 -0
- package/dist/coding-update.d.ts +15 -0
- package/dist/coding-update.js +149 -0
- package/dist/coding-workspace.d.ts +124 -0
- package/dist/coding-workspace.js +768 -0
- package/dist/container-state.d.ts +5 -0
- package/dist/container-state.js +62 -0
- package/dist/containment.d.ts +197 -0
- package/dist/containment.js +559 -0
- package/dist/contest.d.ts +318 -0
- package/dist/contest.js +753 -0
- package/dist/control-setup.d.ts +35 -0
- package/dist/control-setup.js +40 -0
- package/dist/control-ui.d.ts +29 -0
- package/dist/control-ui.js +53 -0
- package/dist/controller-service.d.ts +1 -0
- package/dist/controller-service.js +19 -0
- package/dist/controller-supervisor.d.ts +30 -0
- package/dist/controller-supervisor.js +104 -0
- package/dist/converse.d.ts +304 -0
- package/dist/converse.js +849 -0
- package/dist/coordinator-proposals.d.ts +29 -0
- package/dist/coordinator-proposals.js +137 -0
- package/dist/coordinator.d.ts +186 -0
- package/dist/coordinator.js +447 -0
- package/dist/credentials-ui.d.ts +25 -0
- package/dist/credentials-ui.js +48 -0
- package/dist/daemon.d.ts +209 -0
- package/dist/daemon.js +604 -0
- package/dist/decision.d.ts +120 -0
- package/dist/decision.js +388 -0
- package/dist/demo.d.ts +55 -0
- package/dist/demo.js +992 -0
- package/dist/desktop-access.d.ts +28 -0
- package/dist/desktop-access.js +88 -0
- package/dist/desktop-bundle.d.ts +16 -0
- package/dist/desktop-bundle.js +74 -0
- package/dist/desktop-host.d.ts +54 -0
- package/dist/desktop-host.js +508 -0
- package/dist/desktop-update-gate.d.ts +12 -0
- package/dist/desktop-update-gate.js +91 -0
- package/dist/desktop-update-recovery.d.ts +14 -0
- package/dist/desktop-update-recovery.js +243 -0
- package/dist/desktop-update.d.ts +110 -0
- package/dist/desktop-update.js +740 -0
- package/dist/discord-api.d.ts +20 -0
- package/dist/discord-api.js +138 -0
- package/dist/discord-chat.d.ts +16 -0
- package/dist/discord-chat.js +381 -0
- package/dist/discord-settings.d.ts +7 -0
- package/dist/discord-settings.js +75 -0
- package/dist/discord.d.ts +10 -0
- package/dist/discord.js +169 -0
- package/dist/discover.d.ts +75 -0
- package/dist/discover.js +150 -0
- package/dist/dispatch.d.ts +100 -0
- package/dist/dispatch.js +311 -0
- package/dist/dispose.d.ts +161 -0
- package/dist/dispose.js +803 -0
- package/dist/email-settings.d.ts +34 -0
- package/dist/email-settings.js +50 -0
- package/dist/envelope.d.ts +47 -0
- package/dist/envelope.js +105 -0
- package/dist/evidence-pack.d.ts +185 -0
- package/dist/evidence-pack.js +258 -0
- package/dist/evidence.d.ts +345 -0
- package/dist/evidence.js +782 -0
- package/dist/exec.d.ts +285 -0
- package/dist/exec.js +1841 -0
- package/dist/exhaustion.d.ts +85 -0
- package/dist/exhaustion.js +141 -0
- package/dist/export-ui.d.ts +4 -0
- package/dist/export-ui.js +17 -0
- package/dist/export.d.ts +48 -0
- package/dist/export.js +305 -0
- package/dist/flow-actions.d.ts +77 -0
- package/dist/flow-actions.js +203 -0
- package/dist/flow-code.d.ts +50 -0
- package/dist/flow-code.js +159 -0
- package/dist/flow-draft.d.ts +38 -0
- package/dist/flow-draft.js +80 -0
- package/dist/flow-engine.d.ts +66 -0
- package/dist/flow-engine.js +416 -0
- package/dist/flow-insights.d.ts +95 -0
- package/dist/flow-insights.js +149 -0
- package/dist/flow-live.d.ts +24 -0
- package/dist/flow-live.js +150 -0
- package/dist/flow-people.d.ts +24 -0
- package/dist/flow-people.js +91 -0
- package/dist/flow-replies.d.ts +48 -0
- package/dist/flow-replies.js +113 -0
- package/dist/flow-scripts.d.ts +38 -0
- package/dist/flow-scripts.js +73 -0
- package/dist/flow-secrets.d.ts +13 -0
- package/dist/flow-secrets.js +45 -0
- package/dist/flow-sort.d.ts +82 -0
- package/dist/flow-sort.js +153 -0
- package/dist/flow-steps.d.ts +40 -0
- package/dist/flow-steps.js +408 -0
- package/dist/flow-triggers.d.ts +244 -0
- package/dist/flow-triggers.js +959 -0
- package/dist/flows-ui.d.ts +20 -0
- package/dist/flows-ui.js +175 -0
- package/dist/flows.d.ts +229 -0
- package/dist/flows.js +968 -0
- package/dist/fonts.d.ts +19 -0
- package/dist/fonts.js +19 -0
- package/dist/gaps.d.ts +35 -0
- package/dist/gaps.js +101 -0
- package/dist/gate-failure.d.ts +40 -0
- package/dist/gate-failure.js +66 -0
- package/dist/git.d.ts +54 -0
- package/dist/git.js +94 -0
- package/dist/google-mail.d.ts +58 -0
- package/dist/google-mail.js +151 -0
- package/dist/grant.d.ts +136 -0
- package/dist/grant.js +238 -0
- package/dist/graph.d.ts +164 -0
- package/dist/graph.js +383 -0
- package/dist/guides.d.ts +22 -0
- package/dist/guides.js +363 -0
- package/dist/held.d.ts +150 -0
- package/dist/held.js +799 -0
- package/dist/invoke.d.ts +112 -0
- package/dist/invoke.js +780 -0
- package/dist/issues.d.ts +41 -0
- package/dist/issues.js +131 -0
- package/dist/job-object-helper.ps1 +252 -0
- package/dist/jsonl-discriminants.d.ts +11 -0
- package/dist/jsonl-discriminants.js +93 -0
- package/dist/keys.d.ts +104 -0
- package/dist/keys.js +229 -0
- package/dist/kits-ui.d.ts +17 -0
- package/dist/kits-ui.js +46 -0
- package/dist/kits.d.ts +86 -0
- package/dist/kits.js +215 -0
- package/dist/knowledge-cli.d.ts +101 -0
- package/dist/knowledge-cli.js +92 -0
- package/dist/knowledge-ui.d.ts +24 -0
- package/dist/knowledge-ui.js +40 -0
- package/dist/lead-context.d.ts +8 -0
- package/dist/lead-context.js +56 -0
- package/dist/lead-follow.d.ts +22 -0
- package/dist/lead-follow.js +167 -0
- package/dist/lead-status.d.ts +75 -0
- package/dist/lead-status.js +285 -0
- package/dist/ledger-chain.d.ts +46 -0
- package/dist/ledger-chain.js +187 -0
- package/dist/ledger-csv.d.ts +4 -0
- package/dist/ledger-csv.js +11 -0
- package/dist/ledger-view.d.ts +24 -0
- package/dist/ledger-view.js +58 -0
- package/dist/limits-ui.d.ts +26 -0
- package/dist/limits-ui.js +67 -0
- package/dist/link.d.ts +72 -0
- package/dist/link.js +217 -0
- package/dist/live.d.ts +96 -0
- package/dist/live.js +366 -0
- package/dist/liveness.d.ts +30 -0
- package/dist/liveness.js +42 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.js +25 -0
- package/dist/mailbox.d.ts +57 -0
- package/dist/mailbox.js +150 -0
- package/dist/maintenance.d.ts +11 -0
- package/dist/maintenance.js +35 -0
- package/dist/mate-cli.d.ts +52 -0
- package/dist/mate-cli.js +345 -0
- package/dist/mate-contract.d.ts +10 -0
- package/dist/mate-contract.js +30 -0
- package/dist/mate-doors.d.ts +69 -0
- package/dist/mate-doors.js +548 -0
- package/dist/mate-progress.d.ts +29 -0
- package/dist/mate-progress.js +121 -0
- package/dist/mate-tools.d.ts +94 -0
- package/dist/mate-tools.js +1878 -0
- package/dist/mate.d.ts +92 -0
- package/dist/mate.js +435 -0
- package/dist/mcp-connect.d.ts +103 -0
- package/dist/mcp-connect.js +252 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +651 -0
- package/dist/memory-cli.d.ts +466 -0
- package/dist/memory-cli.js +111 -0
- package/dist/memory-pass.d.ts +160 -0
- package/dist/memory-pass.js +409 -0
- package/dist/metrics.d.ts +3 -0
- package/dist/metrics.js +56 -0
- package/dist/mobile-viewport.d.ts +3 -0
- package/dist/mobile-viewport.js +37 -0
- package/dist/model-catalog.d.ts +118 -0
- package/dist/model-catalog.js +376 -0
- package/dist/models-cli.d.ts +102 -0
- package/dist/models-cli.js +66 -0
- package/dist/models-ui.d.ts +34 -0
- package/dist/models-ui.js +68 -0
- package/dist/modes.d.ts +80 -0
- package/dist/modes.js +182 -0
- package/dist/monitoring-settings.d.ts +44 -0
- package/dist/monitoring-settings.js +173 -0
- package/dist/monitoring-ui.d.ts +20 -0
- package/dist/monitoring-ui.js +51 -0
- package/dist/monitoring.d.ts +34 -0
- package/dist/monitoring.js +221 -0
- package/dist/names.d.ts +67 -0
- package/dist/names.js +102 -0
- package/dist/observations.d.ts +40 -0
- package/dist/observations.js +211 -0
- package/dist/oidc.d.ts +71 -0
- package/dist/oidc.js +182 -0
- package/dist/onboard.d.ts +108 -0
- package/dist/onboard.js +325 -0
- package/dist/openrouter-models.d.ts +30 -0
- package/dist/openrouter-models.js +120 -0
- package/dist/operate.d.ts +118 -0
- package/dist/operate.js +11321 -0
- package/dist/peek-cli.d.ts +97 -0
- package/dist/peek-cli.js +215 -0
- package/dist/peek.d.ts +134 -0
- package/dist/peek.js +578 -0
- package/dist/phase-routing.d.ts +307 -0
- package/dist/phase-routing.js +658 -0
- package/dist/plan-auto.d.ts +14 -0
- package/dist/plan-auto.js +92 -0
- package/dist/plan.d.ts +186 -0
- package/dist/plan.js +401 -0
- package/dist/planner-source.d.ts +251 -0
- package/dist/planner-source.js +460 -0
- package/dist/planner.d.ts +75 -0
- package/dist/planner.js +992 -0
- package/dist/policy-ui.d.ts +20 -0
- package/dist/policy-ui.js +41 -0
- package/dist/policy.d.ts +111 -0
- package/dist/policy.js +231 -0
- package/dist/prepared-evidence.d.ts +16 -0
- package/dist/prepared-evidence.js +96 -0
- package/dist/pricing.d.ts +45 -0
- package/dist/pricing.js +77 -0
- package/dist/principal.d.ts +42 -0
- package/dist/principal.js +82 -0
- package/dist/probe.d.ts +46 -0
- package/dist/probe.js +79 -0
- package/dist/process-custody.d.ts +17 -0
- package/dist/process-custody.js +91 -0
- package/dist/process-liveness.d.ts +10 -0
- package/dist/process-liveness.js +81 -0
- package/dist/process-recovery-anchor.d.ts +75 -0
- package/dist/process-recovery-anchor.js +188 -0
- package/dist/process-recovery-coalition.d.ts +25 -0
- package/dist/process-recovery-coalition.js +105 -0
- package/dist/process-recovery-eligibility.d.ts +178 -0
- package/dist/process-recovery-eligibility.js +170 -0
- package/dist/process-recovery-native.d.ts +172 -0
- package/dist/process-recovery-native.js +592 -0
- package/dist/process-recovery-provenance.d.ts +163 -0
- package/dist/process-recovery-provenance.js +292 -0
- package/dist/process-recovery-services.d.ts +56 -0
- package/dist/process-recovery-services.js +191 -0
- package/dist/process-recovery-settlement.d.ts +32 -0
- package/dist/process-recovery-settlement.js +132 -0
- package/dist/process-recovery.d.ts +41 -0
- package/dist/process-recovery.js +91 -0
- package/dist/process-tree.d.ts +41 -0
- package/dist/process-tree.js +264 -0
- package/dist/project-access.d.ts +14 -0
- package/dist/project-access.js +34 -0
- package/dist/project-cli.d.ts +18 -0
- package/dist/project-cli.js +104 -0
- package/dist/project-delete-ui.d.ts +22 -0
- package/dist/project-delete-ui.js +48 -0
- package/dist/project-delete.d.ts +55 -0
- package/dist/project-delete.js +413 -0
- package/dist/project-knowledge.d.ts +111 -0
- package/dist/project-knowledge.js +241 -0
- package/dist/project-learning.d.ts +100 -0
- package/dist/project-learning.js +438 -0
- package/dist/project-memory.d.ts +81 -0
- package/dist/project-memory.js +264 -0
- package/dist/project-skills.d.ts +152 -0
- package/dist/project-skills.js +660 -0
- package/dist/project-tools.d.ts +219 -0
- package/dist/project-tools.js +796 -0
- package/dist/project.d.ts +61 -0
- package/dist/project.js +125 -0
- package/dist/prompt.d.ts +22 -0
- package/dist/prompt.js +72 -0
- package/dist/proof.d.ts +513 -0
- package/dist/proof.js +1140 -0
- package/dist/proposal.d.ts +82 -0
- package/dist/proposal.js +210 -0
- package/dist/provider-connection.d.ts +22 -0
- package/dist/provider-connection.js +98 -0
- package/dist/provider-limits.d.ts +35 -0
- package/dist/provider-limits.js +95 -0
- package/dist/provider.d.ts +279 -0
- package/dist/provider.js +935 -0
- package/dist/publish.d.ts +107 -0
- package/dist/publish.js +650 -0
- package/dist/pulls.d.ts +118 -0
- package/dist/pulls.js +240 -0
- package/dist/push.d.ts +95 -0
- package/dist/push.js +353 -0
- package/dist/quality.d.ts +8 -0
- package/dist/quality.js +6 -0
- package/dist/recipe-ui.d.ts +18 -0
- package/dist/recipe-ui.js +152 -0
- package/dist/recipes.d.ts +81 -0
- package/dist/recipes.js +332 -0
- package/dist/remote.d.ts +67 -0
- package/dist/remote.js +122 -0
- package/dist/render.d.ts +64 -0
- package/dist/render.js +587 -0
- package/dist/report-summary.d.ts +13 -0
- package/dist/report-summary.js +15 -0
- package/dist/repos.d.ts +65 -0
- package/dist/repos.js +277 -0
- package/dist/repository-context-ui.d.ts +2 -0
- package/dist/repository-context-ui.js +10 -0
- package/dist/repository-context.d.ts +67 -0
- package/dist/repository-context.js +376 -0
- package/dist/restart-certification.d.ts +134 -0
- package/dist/restart-certification.js +226 -0
- package/dist/result-actions.d.ts +28 -0
- package/dist/result-actions.js +270 -0
- package/dist/result-completion.d.ts +18 -0
- package/dist/result-completion.js +66 -0
- package/dist/result-review.d.ts +213 -0
- package/dist/result-review.js +430 -0
- package/dist/retention-ui.d.ts +12 -0
- package/dist/retention-ui.js +39 -0
- package/dist/retention.d.ts +72 -0
- package/dist/retention.js +288 -0
- package/dist/review-context.d.ts +243 -0
- package/dist/review-context.js +1043 -0
- package/dist/review-evidence.d.ts +33 -0
- package/dist/review-evidence.js +56 -0
- package/dist/reviewer.d.ts +97 -0
- package/dist/reviewer.js +202 -0
- package/dist/routine.d.ts +228 -0
- package/dist/routine.js +764 -0
- package/dist/runner.d.ts +266 -0
- package/dist/runner.js +399 -0
- package/dist/scan.d.ts +32 -0
- package/dist/scan.js +92 -0
- package/dist/scope.d.ts +695 -0
- package/dist/scope.js +1176 -0
- package/dist/scout-report.d.ts +42 -0
- package/dist/scout-report.js +108 -0
- package/dist/scout.d.ts +69 -0
- package/dist/scout.js +351 -0
- package/dist/serve.d.ts +313 -0
- package/dist/serve.js +21776 -0
- package/dist/session-brief.d.ts +8 -0
- package/dist/session-brief.js +24 -0
- package/dist/session-cli.d.ts +13 -0
- package/dist/session-cli.js +284 -0
- package/dist/session-contract.d.ts +183 -0
- package/dist/session-contract.js +122 -0
- package/dist/session-http.d.ts +19 -0
- package/dist/session-http.js +154 -0
- package/dist/session-server.d.ts +12 -0
- package/dist/session-server.js +37 -0
- package/dist/session-service.d.ts +22 -0
- package/dist/session-service.js +157 -0
- package/dist/setup-guide.d.ts +39 -0
- package/dist/setup-guide.js +93 -0
- package/dist/sign-in-guard.d.ts +45 -0
- package/dist/sign-in-guard.js +74 -0
- package/dist/skills-ui.d.ts +11 -0
- package/dist/skills-ui.js +58 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.js +276 -0
- package/dist/slack-api.d.ts +55 -0
- package/dist/slack-api.js +229 -0
- package/dist/slack-chat.d.ts +28 -0
- package/dist/slack-chat.js +413 -0
- package/dist/slack-settings.d.ts +7 -0
- package/dist/slack-settings.js +75 -0
- package/dist/slack-state.d.ts +13 -0
- package/dist/slack-state.js +9 -0
- package/dist/slack.d.ts +13 -0
- package/dist/slack.js +167 -0
- package/dist/spend-ui.d.ts +28 -0
- package/dist/spend-ui.js +95 -0
- package/dist/spend.d.ts +142 -0
- package/dist/spend.js +313 -0
- package/dist/sqlite-runtime.d.ts +3 -0
- package/dist/sqlite-runtime.js +6 -0
- package/dist/sso-settings.d.ts +33 -0
- package/dist/sso-settings.js +103 -0
- package/dist/sso-ui.d.ts +22 -0
- package/dist/sso-ui.js +39 -0
- package/dist/storage.d.ts +15 -0
- package/dist/storage.js +84 -0
- package/dist/store.d.ts +7754 -0
- package/dist/store.js +23279 -0
- package/dist/structured-output.d.ts +58 -0
- package/dist/structured-output.js +103 -0
- package/dist/style-asset.d.ts +7 -0
- package/dist/style-asset.js +41 -0
- package/dist/subscription-chat.d.ts +46 -0
- package/dist/subscription-chat.js +275 -0
- package/dist/summary.d.ts +33 -0
- package/dist/summary.js +71 -0
- package/dist/supervisor.mjs +297 -0
- package/dist/surface.d.ts +63 -0
- package/dist/surface.js +352 -0
- package/dist/sync.d.ts +94 -0
- package/dist/sync.js +279 -0
- package/dist/task-composer.d.ts +13 -0
- package/dist/task-composer.js +52 -0
- package/dist/task-control.d.ts +165 -0
- package/dist/task-control.js +214 -0
- package/dist/task-outcome-cli.d.ts +10 -0
- package/dist/task-outcome-cli.js +92 -0
- package/dist/task-text.d.ts +30 -0
- package/dist/task-text.js +41 -0
- package/dist/team-cli.d.ts +15 -0
- package/dist/team-cli.js +618 -0
- package/dist/team-contract.d.ts +92 -0
- package/dist/team-contract.js +1 -0
- package/dist/team-http.d.ts +17 -0
- package/dist/team-http.js +144 -0
- package/dist/team-leads.d.ts +81 -0
- package/dist/team-leads.js +565 -0
- package/dist/team-runtime.d.ts +27 -0
- package/dist/team-runtime.js +264 -0
- package/dist/team-ui.d.ts +3 -0
- package/dist/team-ui.js +9 -0
- package/dist/team-updates.d.ts +8 -0
- package/dist/team-updates.js +57 -0
- package/dist/teammate-admin.d.ts +53 -0
- package/dist/teammate-admin.js +111 -0
- package/dist/teammate-desk.d.ts +60 -0
- package/dist/teammate-desk.js +154 -0
- package/dist/teammate-memory.d.ts +64 -0
- package/dist/teammate-memory.js +196 -0
- package/dist/teammate-question.d.ts +59 -0
- package/dist/teammate-question.js +175 -0
- package/dist/teammate-tools.d.ts +108 -0
- package/dist/teammate-tools.js +271 -0
- package/dist/teammate-week.d.ts +68 -0
- package/dist/teammate-week.js +132 -0
- package/dist/teammate-work.d.ts +76 -0
- package/dist/teammate-work.js +280 -0
- package/dist/teammates-ui.d.ts +20 -0
- package/dist/teammates-ui.js +166 -0
- package/dist/teammates.d.ts +180 -0
- package/dist/teammates.js +253 -0
- package/dist/teams-api.d.ts +33 -0
- package/dist/teams-api.js +204 -0
- package/dist/teams-chat.d.ts +26 -0
- package/dist/teams-chat.js +215 -0
- package/dist/teams-settings.d.ts +12 -0
- package/dist/teams-settings.js +57 -0
- package/dist/teams.d.ts +21 -0
- package/dist/teams.js +124 -0
- package/dist/telegram-flow.d.ts +49 -0
- package/dist/telegram-flow.js +110 -0
- package/dist/telegram-mate.d.ts +137 -0
- package/dist/telegram-mate.js +707 -0
- package/dist/telegram-progress.d.ts +14 -0
- package/dist/telegram-progress.js +129 -0
- package/dist/telegram-settings.d.ts +9 -0
- package/dist/telegram-settings.js +36 -0
- package/dist/telegram-status.d.ts +75 -0
- package/dist/telegram-status.js +265 -0
- package/dist/telegram-team.d.ts +85 -0
- package/dist/telegram-team.js +359 -0
- package/dist/telegram.d.ts +230 -0
- package/dist/telegram.js +1931 -0
- package/dist/templates.d.ts +65 -0
- package/dist/templates.js +118 -0
- package/dist/tool-launcher.d.ts +1 -0
- package/dist/tool-launcher.js +101 -0
- package/dist/tools-ui.d.ts +30 -0
- package/dist/tools-ui.js +40 -0
- package/dist/transitions-recipes.d.ts +1 -0
- package/dist/transitions-recipes.js +203 -0
- package/dist/tree-proof.d.ts +34 -0
- package/dist/tree-proof.js +58 -0
- package/dist/verification-evidence.d.ts +44 -0
- package/dist/verification-evidence.js +238 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +10 -0
- package/dist/webhooks.d.ts +91 -0
- package/dist/webhooks.js +283 -0
- package/dist/work-index.d.ts +68 -0
- package/dist/work-index.js +384 -0
- package/dist/work-summary.d.ts +57 -0
- package/dist/work-summary.js +116 -0
- package/dist/workspace-motion.d.ts +4 -0
- package/dist/workspace-motion.js +133 -0
- package/dist/workspace-revision.d.ts +19 -0
- package/dist/workspace-revision.js +92 -0
- package/dist/workspace-ui.d.ts +204 -0
- package/dist/workspace-ui.js +411 -0
- package/dist/worktree-notices.d.ts +13 -0
- package/dist/worktree-notices.js +17 -0
- package/dist/worktree.d.ts +249 -0
- package/dist/worktree.js +720 -0
- package/package.json +122 -0
- package/scripts/canary-assertions.mjs +42 -0
- package/scripts/crash-canary.mjs +253 -0
- package/scripts/fixtures/crash-process.mjs +74 -0
- package/scripts/fixtures/pilot-scenarios.mjs +85 -0
- package/scripts/fixtures/restart-service.mjs +27 -0
- package/scripts/launchd-certification.mjs +143 -0
- package/scripts/pilot.mjs +121 -0
- package/scripts/proof-preflight.mjs +140 -0
- package/scripts/provider-canary.mjs +379 -0
- package/scripts/recovery-canary.mjs +135 -0
- package/scripts/restart-certification.mjs +98 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function publicChatText(value: string, cap: number): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Shared privacy-preserving plain text for external conversations. */
|
|
2
|
+
import { scanForSecrets } from "./evidence.js";
|
|
3
|
+
export function publicChatText(value, cap) {
|
|
4
|
+
if (scanForSecrets(value).length > 0 ||
|
|
5
|
+
/\b\d{5,}:[A-Za-z0-9_-]{20,}\b/.test(value))
|
|
6
|
+
return "[sensitive text hidden]";
|
|
7
|
+
const text = value
|
|
8
|
+
.replace(/(?:[A-Za-z]:[\\/]|\\\\)[^\s"'<>]+/g, "[path]")
|
|
9
|
+
.replace(/(^|[\s"'`(<[=:,])\/(?:[A-Za-z0-9._~-]+\/)*[A-Za-z0-9._~-]+/g, "$1[path]")
|
|
10
|
+
.replace(/\b[0-9a-f]{32,}\b/gi, "[digest]")
|
|
11
|
+
.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u206f]/g, " ")
|
|
12
|
+
.replace(/\s+/g, " ")
|
|
13
|
+
.trim();
|
|
14
|
+
// Keep surrogate pairs intact while bounding Telegram's UTF-16 ceiling.
|
|
15
|
+
return text.length <= cap
|
|
16
|
+
? text
|
|
17
|
+
: text.slice(0, cap - 2).replace(/[\uD800-\uDBFF]$/, "") + "…";
|
|
18
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Artifact, Run, Store } from "./store.js";
|
|
2
|
+
import type { VerifiedApprover } from "./principal.js";
|
|
3
|
+
/** How many images one turn may select for delivery; more is a flood, not an answer. The rest are one more ask away, by offset or by id. */
|
|
4
|
+
export declare const RESULT_IMAGES_PER_TURN_CAP = 8;
|
|
5
|
+
/** Which of a result's images one ask selects: a page from an offset, or exact image ids from an earlier listing. Absent: the first page. */
|
|
6
|
+
export type ResultImagePick = {
|
|
7
|
+
offset?: number;
|
|
8
|
+
images?: readonly number[];
|
|
9
|
+
};
|
|
10
|
+
export type ResultImage = {
|
|
11
|
+
artifact: number;
|
|
12
|
+
sha256: string;
|
|
13
|
+
format: "png" | "jpeg";
|
|
14
|
+
bytes: number;
|
|
15
|
+
/** 1-based position among the result's deliverable images, and their count. */
|
|
16
|
+
ordinal: number;
|
|
17
|
+
total: number;
|
|
18
|
+
/** The short caption a channel sends with the file: task, result, position. No title, path or prose. */
|
|
19
|
+
caption: string;
|
|
20
|
+
/** The file name a channel uploads under: task and result identity, never a path. */
|
|
21
|
+
fileName: string;
|
|
22
|
+
};
|
|
23
|
+
export type ResultImageSelection = {
|
|
24
|
+
task: string;
|
|
25
|
+
/** The task as a caption or file name shows it: the id when it is plain, a stable opaque label when it is credential-shaped. */
|
|
26
|
+
label: string;
|
|
27
|
+
root: string;
|
|
28
|
+
currentExecution: string;
|
|
29
|
+
run: number;
|
|
30
|
+
role: Run["role"];
|
|
31
|
+
title: string;
|
|
32
|
+
/** Every deliverable image of the result, in evidence order, each with its position. */
|
|
33
|
+
images: ResultImage[];
|
|
34
|
+
/** The images THIS ask selects for delivery: at most the per-turn cap, in evidence order. */
|
|
35
|
+
selected: ResultImage[];
|
|
36
|
+
/** Where the next page starts when the selection stopped short of the last image; null when nothing remains after it. */
|
|
37
|
+
nextOffset: number | null;
|
|
38
|
+
/** Screenshot records that exist but cannot be delivered, each with its plain reason. */
|
|
39
|
+
unavailable: {
|
|
40
|
+
artifact: number;
|
|
41
|
+
problem: string;
|
|
42
|
+
}[];
|
|
43
|
+
/** The run delivered a report (a scout): images are not the point of this result. */
|
|
44
|
+
report: boolean;
|
|
45
|
+
};
|
|
46
|
+
type Problem = {
|
|
47
|
+
ok: false;
|
|
48
|
+
message: string;
|
|
49
|
+
};
|
|
50
|
+
/** The plain words for a screenshot record that must not travel. Null means the record itself is fine. */
|
|
51
|
+
export declare function screenshotRecordProblem(artifact: Artifact): string | null;
|
|
52
|
+
/** Telegram's own caption ceiling for a document (Bot API sendDocument, checked 2026-09-16). Ours are far shorter by construction. */
|
|
53
|
+
export declare const RESULT_IMAGE_CAPTION_MAX_CHARS = 1024;
|
|
54
|
+
/**
|
|
55
|
+
* Is this text safe to show a third-party transport as identity? Nothing
|
|
56
|
+
* credential-shaped, nothing that steers a terminal or a renderer, nothing
|
|
57
|
+
* longer than an id may be. A caption or file name shows only what passes.
|
|
58
|
+
*/
|
|
59
|
+
export declare function displayIdentityProblem(text: string): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* The task as a caption or file name may show it. A plain id is itself,
|
|
62
|
+
* with any character a file name cannot hold replaced. A credential-shaped
|
|
63
|
+
* id (a task filed under a token-like name, on purpose or by accident) is
|
|
64
|
+
* never repeated to Telegram: it shows as a stable opaque label instead,
|
|
65
|
+
* while the exact id stays in the typed part, the bindings and the link.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resultTaskLabel(taskId: string): string;
|
|
68
|
+
/** The caption: the task's display label, the exact result and the position — identity a person can read, nothing a person or a model typed. */
|
|
69
|
+
export declare function resultImageCaption(task: string, run: number, ordinal: number, total: number, current: boolean): string;
|
|
70
|
+
/** The upload name: task label, result and artifact identity — derivable from the typed part alone, never a path. */
|
|
71
|
+
export declare function resultImageFileName(task: string, run: number, artifact: number, format: "png" | "jpeg"): string;
|
|
72
|
+
/**
|
|
73
|
+
* A persisted caption, checked again at send and retry time: a row planned
|
|
74
|
+
* before this rule, or one edited by hand, may carry the raw task id. A
|
|
75
|
+
* caption that would show a credential, a control character or more than
|
|
76
|
+
* Telegram's ceiling is rebuilt from the part's typed identity instead —
|
|
77
|
+
* the exact task and run stay bound; only the words on the wire change.
|
|
78
|
+
*/
|
|
79
|
+
export declare function safeResultImageCaption(text: string, taskId: string, run: number): string;
|
|
80
|
+
/** The page or the exact ids one ask selects from a result's images; a refusal names what to choose instead. */
|
|
81
|
+
export declare function pickResultImages(images: readonly ResultImage[], pick: ResultImagePick | undefined, cap: number): {
|
|
82
|
+
ok: true;
|
|
83
|
+
selected: ResultImage[];
|
|
84
|
+
nextOffset: number | null;
|
|
85
|
+
} | Problem;
|
|
86
|
+
/**
|
|
87
|
+
* Select the images of precisely the result named. With no run, the latest
|
|
88
|
+
* finished result of THAT execution — and only while it is the current
|
|
89
|
+
* version: a newer revision is said, never switched to. A run must belong
|
|
90
|
+
* to the task and have finished; a role that delivers no images (a
|
|
91
|
+
* reviewer, a planner) is not a result at all. Every deliverable image is
|
|
92
|
+
* listed with its position; `selected` is the page (or the exact ids) this
|
|
93
|
+
* ask delivers, at most the per-turn cap, and `nextOffset` says where the
|
|
94
|
+
* rest begins so the remaining images are one more ask away, never lost.
|
|
95
|
+
*/
|
|
96
|
+
export declare function selectResultImages(store: Store, who: VerifiedApprover, evidenceRoot: string | undefined, task: string, run?: number, pick?: ResultImagePick): {
|
|
97
|
+
ok: true;
|
|
98
|
+
selection: ResultImageSelection;
|
|
99
|
+
} | Problem;
|
|
100
|
+
/**
|
|
101
|
+
* The send-time proof, run immediately before EVERY upload including
|
|
102
|
+
* retries: the task is still in the projects this channel may reach now,
|
|
103
|
+
* the run still belongs to it and finished, the artifact row is still the
|
|
104
|
+
* one the part was planned from (same run, same recorded hash — a changed
|
|
105
|
+
* record is a changed provenance, refused), and the bytes read now still
|
|
106
|
+
* verify and are still a bounded PNG or JPEG. Returns the original bytes;
|
|
107
|
+
* nothing is transformed.
|
|
108
|
+
*/
|
|
109
|
+
export declare function verifyResultImage(store: Store, evidenceRoot: string, repos: readonly string[], part: {
|
|
110
|
+
taskId: string;
|
|
111
|
+
run: number;
|
|
112
|
+
artifact: number;
|
|
113
|
+
sha256: string;
|
|
114
|
+
}): {
|
|
115
|
+
ok: true;
|
|
116
|
+
bytes: Buffer;
|
|
117
|
+
format: "png" | "jpeg";
|
|
118
|
+
artifact: Artifact;
|
|
119
|
+
} | {
|
|
120
|
+
ok: false;
|
|
121
|
+
problem: string;
|
|
122
|
+
};
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared, authorized evidence selection for the chat engine: the saved
|
|
3
|
+
* screenshots of one EXACT finished result, for every chat surface.
|
|
4
|
+
*
|
|
5
|
+
* The one road a chat tool takes to a result's images. It proves the same
|
|
6
|
+
* things `readChatResult` proves — the task is in the approver's projects,
|
|
7
|
+
* the run belongs to that task and finished — but it does not require the
|
|
8
|
+
* revision snapshot's terminal diff: a scout's report, a read-only or a
|
|
9
|
+
* no-image result is answered honestly as "no images here" rather than
|
|
10
|
+
* refused or silently swapped for a builder's result. Every image it names
|
|
11
|
+
* was read through `readVerifiedArtifact` and passed `validateScreenshotBytes`
|
|
12
|
+
* this instant; a failed, shortened or redacted capture is named as
|
|
13
|
+
* unavailable, in plain words that carry no path, digest or credential.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here is authority for a send: a channel that delivers files
|
|
16
|
+
* re-proves access and re-reads the bytes with `verifyResultImage`
|
|
17
|
+
* immediately before every upload, including retries.
|
|
18
|
+
*/
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
import { readVerifiedArtifact, scanForSecrets, SCREENSHOT_BYTE_CAP, validateScreenshotBytes } from "./evidence.js";
|
|
21
|
+
/** How many images one turn may select for delivery; more is a flood, not an answer. The rest are one more ask away, by offset or by id. */
|
|
22
|
+
export const RESULT_IMAGES_PER_TURN_CAP = 8;
|
|
23
|
+
/** The plain words for a screenshot record that must not travel. Null means the record itself is fine. */
|
|
24
|
+
export function screenshotRecordProblem(artifact) {
|
|
25
|
+
if (artifact.kind !== "screenshot")
|
|
26
|
+
return "the saved file is not a screenshot";
|
|
27
|
+
if (artifact.captureStatus === "failed")
|
|
28
|
+
return "the capture failed";
|
|
29
|
+
if (artifact.truncated)
|
|
30
|
+
return "the saved file was shortened";
|
|
31
|
+
if (artifact.redacted)
|
|
32
|
+
return "sensitive text was hidden from the saved file";
|
|
33
|
+
if (artifact.bytesStored === 0)
|
|
34
|
+
return "the saved file is empty";
|
|
35
|
+
if (artifact.bytesStored > SCREENSHOT_BYTE_CAP)
|
|
36
|
+
return "the saved file is too large to send";
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/** The record, then the bytes: verified against the row read now, and proved to be a bounded PNG or JPEG. */
|
|
40
|
+
function readScreenshot(evidenceRoot, artifact) {
|
|
41
|
+
const problem = screenshotRecordProblem(artifact);
|
|
42
|
+
if (problem !== null)
|
|
43
|
+
return { ok: false, problem };
|
|
44
|
+
const proven = readVerifiedArtifact(evidenceRoot, artifact);
|
|
45
|
+
if (!proven.ok)
|
|
46
|
+
return { ok: false, problem: "the saved file is missing or changed" };
|
|
47
|
+
const checked = validateScreenshotBytes(proven.content);
|
|
48
|
+
if (!checked.ok)
|
|
49
|
+
return { ok: false, problem: proven.content.length > SCREENSHOT_BYTE_CAP ? "the saved file is too large to send" : "the saved file is not a PNG or JPEG" };
|
|
50
|
+
return { ok: true, bytes: proven.content, format: checked.kind };
|
|
51
|
+
}
|
|
52
|
+
/** Telegram's own caption ceiling for a document (Bot API sendDocument, checked 2026-09-16). Ours are far shorter by construction. */
|
|
53
|
+
export const RESULT_IMAGE_CAPTION_MAX_CHARS = 1_024;
|
|
54
|
+
/** The longest task label a caption or file name carries. A task id is at most 64 characters at every tool boundary. */
|
|
55
|
+
const RESULT_TASK_LABEL_MAX_CHARS = 64;
|
|
56
|
+
/** A bot token's shape, the one credential the secret scanner does not know. */
|
|
57
|
+
const BOT_TOKEN_SHAPE = /\b\d{5,}:[A-Za-z0-9_-]{20,}\b/;
|
|
58
|
+
/**
|
|
59
|
+
* Is this text safe to show a third-party transport as identity? Nothing
|
|
60
|
+
* credential-shaped, nothing that steers a terminal or a renderer, nothing
|
|
61
|
+
* longer than an id may be. A caption or file name shows only what passes.
|
|
62
|
+
*/
|
|
63
|
+
export function displayIdentityProblem(text) {
|
|
64
|
+
if (text.length === 0 || text.length > RESULT_TASK_LABEL_MAX_CHARS)
|
|
65
|
+
return "too long";
|
|
66
|
+
if (scanForSecrets(text).length > 0 || BOT_TOKEN_SHAPE.test(text))
|
|
67
|
+
return "credential-shaped";
|
|
68
|
+
if (/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u206f\s]/.test(text))
|
|
69
|
+
return "control or space characters";
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The task as a caption or file name may show it. A plain id is itself,
|
|
74
|
+
* with any character a file name cannot hold replaced. A credential-shaped
|
|
75
|
+
* id (a task filed under a token-like name, on purpose or by accident) is
|
|
76
|
+
* never repeated to Telegram: it shows as a stable opaque label instead,
|
|
77
|
+
* while the exact id stays in the typed part, the bindings and the link.
|
|
78
|
+
*/
|
|
79
|
+
export function resultTaskLabel(taskId) {
|
|
80
|
+
if (displayIdentityProblem(taskId) !== null)
|
|
81
|
+
return `task-${createHash("sha256").update(taskId).digest("hex").slice(0, 12)}`;
|
|
82
|
+
return taskId.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
83
|
+
}
|
|
84
|
+
/** The caption: the task's display label, the exact result and the position — identity a person can read, nothing a person or a model typed. */
|
|
85
|
+
export function resultImageCaption(task, run, ordinal, total, current) {
|
|
86
|
+
return `${resultTaskLabel(task)} · result #${run} · screenshot ${ordinal} of ${total}${current ? "" : " · older version"}`.slice(0, RESULT_IMAGE_CAPTION_MAX_CHARS);
|
|
87
|
+
}
|
|
88
|
+
/** The upload name: task label, result and artifact identity — derivable from the typed part alone, never a path. */
|
|
89
|
+
export function resultImageFileName(task, run, artifact, format) {
|
|
90
|
+
return `${resultTaskLabel(task)}-result-${run}-${artifact}.${format === "png" ? "png" : "jpg"}`;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A persisted caption, checked again at send and retry time: a row planned
|
|
94
|
+
* before this rule, or one edited by hand, may carry the raw task id. A
|
|
95
|
+
* caption that would show a credential, a control character or more than
|
|
96
|
+
* Telegram's ceiling is rebuilt from the part's typed identity instead —
|
|
97
|
+
* the exact task and run stay bound; only the words on the wire change.
|
|
98
|
+
*/
|
|
99
|
+
export function safeResultImageCaption(text, taskId, run) {
|
|
100
|
+
const safe = text.length > 0 && text.length <= RESULT_IMAGE_CAPTION_MAX_CHARS
|
|
101
|
+
&& scanForSecrets(text).length === 0 && !BOT_TOKEN_SHAPE.test(text)
|
|
102
|
+
&& !/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u206f]/.test(text);
|
|
103
|
+
return safe ? text : `${resultTaskLabel(taskId)} · result #${run} · screenshot`;
|
|
104
|
+
}
|
|
105
|
+
/** The page or the exact ids one ask selects from a result's images; a refusal names what to choose instead. */
|
|
106
|
+
export function pickResultImages(images, pick, cap) {
|
|
107
|
+
if (pick?.images !== undefined) {
|
|
108
|
+
if (pick.offset !== undefined)
|
|
109
|
+
return { ok: false, message: "Choose image ids or an offset, not both." };
|
|
110
|
+
if (pick.images.length === 0)
|
|
111
|
+
return { ok: false, message: "Choose at least one image id from this result's list." };
|
|
112
|
+
if (pick.images.length > cap)
|
|
113
|
+
return { ok: false, message: `Choose at most ${cap} images per request.` };
|
|
114
|
+
const wanted = new Set(pick.images);
|
|
115
|
+
if (wanted.size !== pick.images.length)
|
|
116
|
+
return { ok: false, message: "Each image id once." };
|
|
117
|
+
const known = new Set(images.map(one => one.artifact));
|
|
118
|
+
if ([...wanted].some(id => !known.has(id)))
|
|
119
|
+
return { ok: false, message: "Choose image ids from this result's list of deliverable images." };
|
|
120
|
+
return { ok: true, selected: images.filter(one => wanted.has(one.artifact)), nextOffset: null };
|
|
121
|
+
}
|
|
122
|
+
const offset = pick?.offset ?? 0;
|
|
123
|
+
if (offset > 0 && offset >= images.length)
|
|
124
|
+
return { ok: false, message: images.length === 0 ? "This result has no deliverable images." : `Choose an image offset below ${images.length}.` };
|
|
125
|
+
const selected = images.slice(offset, offset + cap);
|
|
126
|
+
return { ok: true, selected, nextOffset: offset + selected.length < images.length ? offset + selected.length : null };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Select the images of precisely the result named. With no run, the latest
|
|
130
|
+
* finished result of THAT execution — and only while it is the current
|
|
131
|
+
* version: a newer revision is said, never switched to. A run must belong
|
|
132
|
+
* to the task and have finished; a role that delivers no images (a
|
|
133
|
+
* reviewer, a planner) is not a result at all. Every deliverable image is
|
|
134
|
+
* listed with its position; `selected` is the page (or the exact ids) this
|
|
135
|
+
* ask delivers, at most the per-turn cap, and `nextOffset` says where the
|
|
136
|
+
* rest begins so the remaining images are one more ask away, never lost.
|
|
137
|
+
*/
|
|
138
|
+
export function selectResultImages(store, who, evidenceRoot, task, run, pick) {
|
|
139
|
+
const ref = store.lookupRef(task);
|
|
140
|
+
if (ref?.repo == null || !who.repos.includes(ref.repo))
|
|
141
|
+
return { ok: false, message: "That task is not in your projects." };
|
|
142
|
+
const family = store.taskFamilyOf(task, who.repos, false);
|
|
143
|
+
if (family === null || family.problem !== null)
|
|
144
|
+
return { ok: false, message: "This task's revision history needs repair." };
|
|
145
|
+
const current = family.current.id === task;
|
|
146
|
+
if (run === undefined && !current)
|
|
147
|
+
return { ok: false, message: "A newer revision of this task is current. Say which result you mean before images are selected: this version or the newer one." };
|
|
148
|
+
const found = run === undefined
|
|
149
|
+
? store.runsFor(ref.id).find(one => one.outcome !== null && (one.role === "builder" || one.role === "repair" || one.role === "scout"))
|
|
150
|
+
: store.getRun(run);
|
|
151
|
+
if (found == null || found.taskRef !== ref.id || found.outcome === null || (found.role !== "builder" && found.role !== "repair" && found.role !== "scout")) {
|
|
152
|
+
return { ok: false, message: "There is no finished result for that version yet." };
|
|
153
|
+
}
|
|
154
|
+
if (evidenceRoot === undefined)
|
|
155
|
+
return { ok: false, message: "Saved images cannot be read from here." };
|
|
156
|
+
const artifacts = store.artifactsFor(found.id);
|
|
157
|
+
const shots = artifacts.filter(one => one.kind === "screenshot");
|
|
158
|
+
const images = [];
|
|
159
|
+
const unavailable = [];
|
|
160
|
+
const readable = [];
|
|
161
|
+
for (const artifact of shots) {
|
|
162
|
+
const read = readScreenshot(evidenceRoot, artifact);
|
|
163
|
+
if (read.ok)
|
|
164
|
+
readable.push({ artifact, bytes: read.bytes.length, format: read.format });
|
|
165
|
+
else
|
|
166
|
+
unavailable.push({ artifact: artifact.id, problem: read.problem });
|
|
167
|
+
}
|
|
168
|
+
readable.forEach((one, index) => {
|
|
169
|
+
const ordinal = index + 1;
|
|
170
|
+
images.push({
|
|
171
|
+
artifact: one.artifact.id, sha256: one.artifact.sha256, format: one.format, bytes: one.bytes, ordinal, total: readable.length,
|
|
172
|
+
caption: resultImageCaption(task, found.id, ordinal, readable.length, current),
|
|
173
|
+
fileName: resultImageFileName(task, found.id, one.artifact.id, one.format),
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
const picked = pickResultImages(images, pick, RESULT_IMAGES_PER_TURN_CAP);
|
|
177
|
+
if (!picked.ok)
|
|
178
|
+
return picked;
|
|
179
|
+
return { ok: true, selection: {
|
|
180
|
+
task, label: resultTaskLabel(task), root: family.root.id, currentExecution: family.current.id, run: found.id, role: found.role,
|
|
181
|
+
title: store.getTask(task)?.title ?? task, images, selected: picked.selected, nextOffset: picked.nextOffset, unavailable, report: artifacts.some(one => one.kind === "report"),
|
|
182
|
+
} };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The send-time proof, run immediately before EVERY upload including
|
|
186
|
+
* retries: the task is still in the projects this channel may reach now,
|
|
187
|
+
* the run still belongs to it and finished, the artifact row is still the
|
|
188
|
+
* one the part was planned from (same run, same recorded hash — a changed
|
|
189
|
+
* record is a changed provenance, refused), and the bytes read now still
|
|
190
|
+
* verify and are still a bounded PNG or JPEG. Returns the original bytes;
|
|
191
|
+
* nothing is transformed.
|
|
192
|
+
*/
|
|
193
|
+
export function verifyResultImage(store, evidenceRoot, repos, part) {
|
|
194
|
+
const ref = store.lookupRef(part.taskId);
|
|
195
|
+
if (ref?.repo == null || !repos.includes(ref.repo))
|
|
196
|
+
return { ok: false, problem: "the result is outside your connected projects now" };
|
|
197
|
+
const run = store.getRun(part.run);
|
|
198
|
+
if (run === null || run.taskRef !== ref.id || run.outcome === null)
|
|
199
|
+
return { ok: false, problem: "the result is no longer recorded" };
|
|
200
|
+
const artifact = store.artifactForRun(part.run, part.artifact);
|
|
201
|
+
if (artifact === null || artifact.kind !== "screenshot" || artifact.sha256 !== part.sha256)
|
|
202
|
+
return { ok: false, problem: "the saved file's record changed" };
|
|
203
|
+
const read = readScreenshot(evidenceRoot, artifact);
|
|
204
|
+
if (!read.ok)
|
|
205
|
+
return read;
|
|
206
|
+
return { ok: true, bytes: read.bytes, format: read.format, artifact };
|
|
207
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ChatBinding, ChatContent, ChatEvent, ChatState } from "./chat-delivery-state.js";
|
|
2
|
+
import type { Store } from "./store.js";
|
|
3
|
+
export type ChatFlowAction = "approve" | "edit" | "send-back";
|
|
4
|
+
export declare const CHAT_FLOW_LABELS: Record<ChatFlowAction, string>;
|
|
5
|
+
/** Words kept as written, line breaks and all; only control characters go. */
|
|
6
|
+
export declare function chatFlowText(text: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* A "flow-decision" notice as chat parts, or null when it isn't one (or the
|
|
9
|
+
* card has already moved on, and there is nothing left to decide).
|
|
10
|
+
*/
|
|
11
|
+
export declare function flowDecisionParts(store: Store, notification: {
|
|
12
|
+
dedupeKey: string;
|
|
13
|
+
subject: string;
|
|
14
|
+
body: string;
|
|
15
|
+
link: string | null;
|
|
16
|
+
}): ChatContent[] | null;
|
|
17
|
+
/** The live buttons on one part, in the order they were minted. */
|
|
18
|
+
export declare function chatFlowButtons(state: ChatState, part: number, now: Date): Array<{
|
|
19
|
+
token: string;
|
|
20
|
+
action: ChatFlowAction;
|
|
21
|
+
label: string;
|
|
22
|
+
}>;
|
|
23
|
+
/**
|
|
24
|
+
* A tapped flow button, inside the action's transaction. false when the
|
|
25
|
+
* token isn't a flow button (the caller looks for a proposal's); otherwise
|
|
26
|
+
* the event is finished and anything to say is planned on it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function applyChatFlowTap(options: {
|
|
29
|
+
store: Store;
|
|
30
|
+
state: ChatState;
|
|
31
|
+
label: string;
|
|
32
|
+
}, event: ChatEvent, binding: ChatBinding, token: string, repos: readonly string[], now: Date): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* A message from the person while a prompt is open: the new draft or the
|
|
35
|
+
* note. false when no prompt is open (the message goes on as usual).
|
|
36
|
+
*/
|
|
37
|
+
export declare function answerChatFlowPrompt(options: {
|
|
38
|
+
store: Store;
|
|
39
|
+
state: ChatState;
|
|
40
|
+
label: string;
|
|
41
|
+
}, event: ChatEvent, binding: ChatBinding, input: {
|
|
42
|
+
text: string;
|
|
43
|
+
originalLength?: number;
|
|
44
|
+
}, repos: readonly string[], now: Date): boolean;
|
|
@@ -0,0 +1,215 @@
|
|
|
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
|
+
import { keptDraft } from "./flow-draft.js";
|
|
21
|
+
import { decideFlowCard, flowCardHref, flowDefinitionOf } from "./flow-engine.js";
|
|
22
|
+
import { deciderOf } from "./flows.js";
|
|
23
|
+
import { MATE_MESSAGE_MAX_CHARS } from "./mate.js";
|
|
24
|
+
import { verifyApproverStanding } from "./principal.js";
|
|
25
|
+
import { FLOW_DECIDE_KEY, flowDecisionAt } from "./telegram-flow.js";
|
|
26
|
+
export const CHAT_FLOW_LABELS = { approve: "Approve", edit: "Edit", "send-back": "Send back" };
|
|
27
|
+
const PART_CHARS = 2800;
|
|
28
|
+
const PROMPT_MS = 30 * 60_000;
|
|
29
|
+
const DRAFT_LIMIT = 4000;
|
|
30
|
+
/** Words kept as written, line breaks and all; only control characters go. */
|
|
31
|
+
export function chatFlowText(text) {
|
|
32
|
+
return text.replace(/\r\n?/g, "\n").replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
33
|
+
}
|
|
34
|
+
/** Split on line breaks where it can, so a draft reads as written. */
|
|
35
|
+
function pieces(text) {
|
|
36
|
+
const out = [];
|
|
37
|
+
let rest = text;
|
|
38
|
+
while (rest.length > PART_CHARS) {
|
|
39
|
+
const cut = rest.lastIndexOf("\n", PART_CHARS);
|
|
40
|
+
let at = cut > PART_CHARS / 2 ? cut : PART_CHARS;
|
|
41
|
+
if (/[\uD800-\uDBFF]/.test(rest[at - 1]))
|
|
42
|
+
at -= 1;
|
|
43
|
+
out.push(rest.slice(0, at).trimEnd());
|
|
44
|
+
rest = rest.slice(at).trimStart();
|
|
45
|
+
}
|
|
46
|
+
out.push(rest);
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** The buttons a visit gets: Approve always, Edit when there is a draft, Send back when the zone sends back somewhere. */
|
|
50
|
+
function actionsFor(waiting) {
|
|
51
|
+
return ["approve",
|
|
52
|
+
...(waiting.draft !== null && waiting.card.outputs[waiting.draft.id] !== undefined ? ["edit"] : []),
|
|
53
|
+
...(waiting.stage.onFail !== null ? ["send-back"] : [])];
|
|
54
|
+
}
|
|
55
|
+
function decisionParts(text, visit, link) {
|
|
56
|
+
const all = pieces(chatFlowText(text));
|
|
57
|
+
return all.map((piece, index) => index < all.length - 1 ? { text: piece } : { text: piece, flow: visit, ...(link === null ? {} : { link }) });
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A "flow-decision" notice as chat parts, or null when it isn't one (or the
|
|
61
|
+
* card has already moved on, and there is nothing left to decide).
|
|
62
|
+
*/
|
|
63
|
+
export function flowDecisionParts(store, notification) {
|
|
64
|
+
const visit = FLOW_DECIDE_KEY.exec(notification.dedupeKey);
|
|
65
|
+
const waiting = visit === null ? null : flowDecisionAt(store, Number(visit[1]), Number(visit[2]));
|
|
66
|
+
if (waiting === null)
|
|
67
|
+
return null;
|
|
68
|
+
return decisionParts(`${notification.subject}\n\n${notification.body}`, { card: waiting.card.id, entry: waiting.card.entry, actions: actionsFor(waiting) }, notification.link === null ? null : { label: "Open", path: notification.link });
|
|
69
|
+
}
|
|
70
|
+
/** The live buttons on one part, in the order they were minted. */
|
|
71
|
+
export function chatFlowButtons(state, part, now) {
|
|
72
|
+
return state.prepare("SELECT token,action FROM chat_flow_action WHERE part=? AND consumed IS NULL AND expires>? ORDER BY rowid").all(part, now.toISOString())
|
|
73
|
+
.map(one => ({ token: String(one.token), action: one.action, label: CHAT_FLOW_LABELS[one.action] }));
|
|
74
|
+
}
|
|
75
|
+
const retire = (state, card, entry, now) => {
|
|
76
|
+
state.prepare("UPDATE chat_flow_action SET consumed=? WHERE card=? AND entry=? AND consumed IS NULL").run(now.toISOString(), card, entry);
|
|
77
|
+
state.prepare("UPDATE chat_flow_prompt SET consumed=? WHERE card=? AND entry=? AND consumed IS NULL").run(now.toISOString(), card, entry);
|
|
78
|
+
};
|
|
79
|
+
/** Show the tapped notice again with what happened, and without its buttons. */
|
|
80
|
+
function repaint(state, part, event, line) {
|
|
81
|
+
const row = state.prepare("SELECT payload FROM chat_part WHERE id=?").get(part);
|
|
82
|
+
const before = row === undefined ? { text: "" } : JSON.parse(String(row["payload"]));
|
|
83
|
+
const content = { text: `${before.text}\n\n${line}`.slice(0, PART_CHARS + 400), edit: event.ts, ...(before.link === undefined ? {} : { link: before.link }), ...(before.channel === undefined ? {} : { channel: before.channel }) };
|
|
84
|
+
state.prepare("UPDATE chat_part SET payload=?,state='pending',next_at=NULL WHERE id=?").run(JSON.stringify(content), part);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A tapped flow button, inside the action's transaction. false when the
|
|
88
|
+
* token isn't a flow button (the caller looks for a proposal's); otherwise
|
|
89
|
+
* the event is finished and anything to say is planned on it.
|
|
90
|
+
*/
|
|
91
|
+
export function applyChatFlowTap(options, event, binding, token, repos, now) {
|
|
92
|
+
const { store, state } = options;
|
|
93
|
+
const action = state.prepare("SELECT a.*,p.message,e.binding AS owner,e.channel FROM chat_flow_action a JOIN chat_part p ON p.id=a.part JOIN chat_event e ON e.id=p.event WHERE a.token=?").get(token);
|
|
94
|
+
if (action === undefined)
|
|
95
|
+
return false;
|
|
96
|
+
const say = (text) => state.plan(event.id, [{ text }], now);
|
|
97
|
+
// Bound to the person, the chat and the exact message the button rode.
|
|
98
|
+
if (Number(action["owner"]) !== binding.id || action["channel"] !== event.channel || action["message"] !== event.ts) {
|
|
99
|
+
say("That button expired or was already used. Ask for the current state before trying again.");
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
if (action["consumed"] !== null || String(action["expires"]) <= now.toISOString()) {
|
|
103
|
+
say("That was already decided, or these buttons are too old.");
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
const card = Number(action["card"]), entry = Number(action["entry"]), part = Number(action["part"]);
|
|
107
|
+
const waiting = flowDecisionAt(store, card, entry);
|
|
108
|
+
if (waiting === null) {
|
|
109
|
+
retire(state, card, entry, now);
|
|
110
|
+
repaint(state, part, event, "This card has moved on since; nothing was changed.");
|
|
111
|
+
state.finish(event.id);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
const decider = deciderOf(waiting.stage, waiting.flow);
|
|
115
|
+
if (decider !== null && decider !== binding.approver) {
|
|
116
|
+
say(`Only ${decider} decides here.`);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (!verifyApproverStanding(store, binding.approver, binding.generation, repos).ok) {
|
|
120
|
+
state.finish(event.id, true);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const kind = String(action["action"]);
|
|
124
|
+
if (kind === "approve") {
|
|
125
|
+
const decided = decideFlowCard(store, { card, decision: "approve", note: null, actor: binding.approver, repos, entry }, now);
|
|
126
|
+
if (!decided.ok) {
|
|
127
|
+
say(decided.message);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
retire(state, card, entry, now);
|
|
131
|
+
store.retireTelegramFlowVisit(card, entry, now);
|
|
132
|
+
repaint(state, part, event, `✅ You approved it. ${decided.said}`);
|
|
133
|
+
state.finish(event.id);
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
// One open prompt per person: the newest question is the one the next message answers.
|
|
137
|
+
state.prepare("UPDATE chat_flow_prompt SET consumed=? WHERE binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
|
|
138
|
+
state.prepare("UPDATE chat_question_prompt SET consumed=? WHERE binding=? AND consumed IS NULL").run(now.toISOString(), binding.id);
|
|
139
|
+
state.prepare("INSERT INTO chat_flow_prompt(binding,card,entry,mode,created,expires) VALUES(?,?,?,?,?,?)")
|
|
140
|
+
.run(binding.id, card, entry, kind, now.toISOString(), new Date(now.getTime() + PROMPT_MS).toISOString());
|
|
141
|
+
const back = waiting.stage.onFail === null ? "the zone before" : flowDefinitionOf(waiting.flow)?.stages.find(one => one.id === waiting.stage.onFail)?.title ?? waiting.stage.onFail;
|
|
142
|
+
say(kind === "edit"
|
|
143
|
+
? `Send your version of the draft for “${waiting.card.title}” as your next message here. It replaces the draft and comes back here for you to approve. Send “cancel” to leave it as it is.`
|
|
144
|
+
: `What should change on “${waiting.card.title}”? Your next message here is the note, and the card goes back to ${back} with it. Send “cancel” to leave it where it is.`);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A message from the person while a prompt is open: the new draft or the
|
|
149
|
+
* note. false when no prompt is open (the message goes on as usual).
|
|
150
|
+
*/
|
|
151
|
+
export function answerChatFlowPrompt(options, event, binding, input, repos, now) {
|
|
152
|
+
const { store, state } = options;
|
|
153
|
+
if (event.channel !== binding.channel)
|
|
154
|
+
return false;
|
|
155
|
+
return store.transact(() => {
|
|
156
|
+
const prompt = state.prepare("SELECT * FROM chat_flow_prompt WHERE binding=? AND consumed IS NULL AND expires>? ORDER BY id DESC LIMIT 1").get(binding.id, now.toISOString());
|
|
157
|
+
if (prompt === undefined)
|
|
158
|
+
return false;
|
|
159
|
+
const id = Number(prompt["id"]), card = Number(prompt["card"]), entry = Number(prompt["entry"]), mode = String(prompt["mode"]);
|
|
160
|
+
const say = (text) => state.plan(event.id, [{ text }], now);
|
|
161
|
+
const close = () => state.prepare("UPDATE chat_flow_prompt SET consumed=? WHERE id=?").run(now.toISOString(), id);
|
|
162
|
+
const said = input.text.trim();
|
|
163
|
+
if (/^cancel\.?$/i.test(said)) {
|
|
164
|
+
close();
|
|
165
|
+
say(mode === "edit" ? "Left the draft as it is. The buttons on the notice still work." : "Left it where it is. The buttons on the notice still work.");
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
const waiting = flowDecisionAt(store, card, entry);
|
|
169
|
+
if (waiting === null) {
|
|
170
|
+
retire(state, card, entry, now);
|
|
171
|
+
say("That card has moved on since; nothing was changed.");
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
if (said === "") {
|
|
175
|
+
say(mode === "edit" ? "Send the text itself, or “cancel”." : "Say what should change, or send “cancel”.");
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
// A long message arrives cut short (the chat keeps its first 2,000 characters): never take part of one as the draft.
|
|
179
|
+
const limit = (input.originalLength ?? 0) > input.text.length ? MATE_MESSAGE_MAX_CHARS : DRAFT_LIMIT;
|
|
180
|
+
if ((input.originalLength ?? 0) > input.text.length || said.length > DRAFT_LIMIT) {
|
|
181
|
+
say(`That's too long to take from here. Keep it under ${limit.toLocaleString("en-US")} characters, or change it in Toolroll.`);
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
if (!verifyApproverStanding(store, binding.approver, binding.generation, repos).ok) {
|
|
185
|
+
close();
|
|
186
|
+
state.finish(event.id, true);
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
if (mode === "edit") {
|
|
190
|
+
if (waiting.draft === null) {
|
|
191
|
+
close();
|
|
192
|
+
say("This decision has no draft to edit.");
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
const kept = keptDraft(said);
|
|
196
|
+
store.updateFlowCard(card, { outputs: { ...waiting.card.outputs, [waiting.draft.id]: kept } }, now);
|
|
197
|
+
store.addFlowComment({ card, author: binding.approver, body: `Edited the draft in ${options.label}.`, mentions: [] }, now);
|
|
198
|
+
retire(state, card, entry, now);
|
|
199
|
+
store.retireTelegramFlowVisit(card, entry, now);
|
|
200
|
+
const fresh = flowDecisionAt(store, card, entry);
|
|
201
|
+
state.plan(event.id, decisionParts(`${waiting.flow.name}: your version of the draft for “${waiting.card.title}”\n\n${kept}\n\nApprove to send it as written.`, { card, entry, actions: actionsFor(fresh) }, { label: "Open", path: flowCardHref(waiting.flow.id, card) }), now);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
const decided = decideFlowCard(store, { card, decision: "send-back", note: said.slice(0, 2000), actor: binding.approver, repos, entry }, now);
|
|
205
|
+
if (!decided.ok) {
|
|
206
|
+
close();
|
|
207
|
+
say(decided.message);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
retire(state, card, entry, now);
|
|
211
|
+
store.retireTelegramFlowVisit(card, entry, now);
|
|
212
|
+
say(`↩️ ${decided.said}`);
|
|
213
|
+
return true;
|
|
214
|
+
});
|
|
215
|
+
}
|