toolroll 0.9.103 → 0.9.105
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 +65 -0
- package/README.md +27 -27
- package/dist/accent-colors.js +0 -29
- package/dist/acp-agents.js +0 -3
- package/dist/acp-attest.js +0 -30
- package/dist/acp-check.js +3 -70
- package/dist/acp-client.js +3 -94
- package/dist/acp-inspect.js +1 -20
- package/dist/action-ledger.js +0 -8
- package/dist/activity-line.js +0 -14
- package/dist/actor.js +0 -15
- package/dist/advanced-schema.js +12 -0
- package/dist/advanced-view.js +26 -0
- package/dist/advanced.js +61 -0
- package/dist/agent-board.js +0 -117
- package/dist/agent-fence.js +11 -367
- package/dist/agent-onboard.js +12 -50
- package/dist/agent-pulse.js +5 -38
- package/dist/agent-state-words.js +0 -8
- package/dist/agent-state.js +1 -45
- package/dist/agent-turns.js +3 -17
- package/dist/agentconfig.js +1 -99
- package/dist/agents-board.js +0 -60
- package/dist/agents-live.js +1 -8
- package/dist/api-tokens.js +0 -27
- package/dist/approval-authority.js +0 -59
- package/dist/approval-password.js +0 -10
- package/dist/approval-policy.js +0 -29
- package/dist/approval-rules-ui.js +0 -9
- package/dist/assignment-adapters.js +0 -14
- package/dist/assignment-brief.js +1 -17
- package/dist/assignment-delivery.js +0 -22
- package/dist/assignment-presentation.js +0 -14
- package/dist/assignment-status.js +1 -30
- package/dist/assignment-ui.js +0 -12
- package/dist/assignment.js +117 -103
- package/dist/attest.js +0 -38
- package/dist/audit-cli.js +0 -9
- package/dist/backend.js +0 -59
- package/dist/backup-ui.js +0 -12
- package/dist/backup.js +8 -61
- package/dist/batch-checks.js +0 -20
- package/dist/batch-policy.js +0 -4
- package/dist/beads.js +0 -48
- package/dist/bin.js +0 -8
- package/dist/board.js +1 -36
- package/dist/boot-identity.js +0 -42
- package/dist/bounded-spawn.js +4 -28
- package/dist/brand-mark.js +0 -21
- package/dist/brief-fence.js +0 -16
- package/dist/browser/terminal.css +1 -1
- package/dist/browser/terminal.js +16 -16
- package/dist/browser/workspace.css +2 -2
- package/dist/browser/workspace.js +26 -26
- package/dist/browser-crew.js +0 -8
- package/dist/browser-shell.js +2 -15
- package/dist/browser-workspace.js +6 -30
- package/dist/budget-alerts.js +1 -4
- package/dist/build-review.js +2 -50
- package/dist/builder-restart.js +0 -13
- package/dist/builder.js +21 -1035
- package/dist/canonical-json.js +0 -12
- package/dist/capscan.js +1 -32
- package/dist/card-catalog.js +0 -12
- package/dist/card-cli.js +0 -1
- package/dist/card-render.js +0 -25
- package/dist/card-sources.js +0 -10
- package/dist/chat-acceptance.js +0 -7
- package/dist/chat-actions.js +5 -190
- package/dist/chat-admission.js +0 -7
- package/dist/chat-approval.js +0 -25
- package/dist/chat-ask.js +0 -17
- package/dist/chat-card-part.js +0 -3
- package/dist/chat-card.js +0 -6
- package/dist/chat-channel.js +2 -83
- package/dist/chat-continuity.js +0 -31
- package/dist/chat-controls.js +0 -7
- package/dist/chat-core.js +0 -55
- package/dist/chat-decide.js +2 -153
- package/dist/chat-delivery-state.js +0 -49
- package/dist/chat-delivery.js +2 -143
- package/dist/chat-display.js +0 -2
- package/dist/chat-evidence.js +0 -65
- package/dist/chat-flow.js +0 -72
- package/dist/chat-follow.js +0 -13
- package/dist/chat-inbox.js +0 -34
- package/dist/chat-live-schema.js +0 -14
- package/dist/chat-live.js +2 -87
- package/dist/chat-message-states.js +13 -40
- package/dist/chat-messages.js +0 -153
- package/dist/chat-migration.js +0 -57
- package/dist/chat-polish.js +0 -7
- package/dist/chat-quiet.js +0 -37
- package/dist/chat-review.js +0 -4
- package/dist/chat-rooms.js +0 -22
- package/dist/chat-settings-page.js +0 -4
- package/dist/chat-task-actions.js +0 -7
- package/dist/chat-terms.js +0 -21
- package/dist/chat-voice.js +0 -38
- package/dist/chat-warmth.js +1 -3
- package/dist/check-access.js +0 -30
- package/dist/check-facts.js +2 -62
- package/dist/check-failure.js +0 -98
- package/dist/check-levels-ui.js +4 -10
- package/dist/check-levels.js +3 -35
- package/dist/check-load.js +1 -49
- package/dist/check-only-standing.js +0 -6
- package/dist/check-only.js +0 -22
- package/dist/check-progress.js +0 -32
- package/dist/check-repair.js +2 -35
- package/dist/check-report.js +1 -108
- package/dist/check-stamp.js +0 -20
- package/dist/checkout-cleanup.js +0 -84
- package/dist/checkout-proof.js +1 -13
- package/dist/child-database.js +0 -3
- package/dist/claim.js +4 -508
- package/dist/claude-print.js +0 -10
- package/dist/cli-fast.js +0 -8
- package/dist/cli-help.js +90 -78
- package/dist/cli-http.js +0 -10
- package/dist/cli.js +26 -241
- package/dist/code-extract.js +0 -6
- package/dist/code-map-acts.js +0 -7
- package/dist/code-map-admission.js +2 -26
- package/dist/code-map-build.js +3 -24
- package/dist/code-map-cli.js +0 -4
- package/dist/code-map-main.js +0 -6
- package/dist/code-map-read.js +0 -12
- package/dist/code-map-report.js +0 -9
- package/dist/code-map-shape.js +0 -19
- package/dist/code-map-store.js +0 -26
- package/dist/codex-limits.js +1 -17
- package/dist/coding-context.js +0 -10
- package/dist/coding-handoff.js +0 -14
- package/dist/coding-provider.js +0 -39
- package/dist/coding-shipping-ui.js +0 -1
- package/dist/coding-ui.js +0 -1
- package/dist/coding-update.js +1 -18
- package/dist/coding-workspace.js +0 -27
- package/dist/commit-extract.js +0 -83
- package/dist/connect-cli.js +0 -19
- package/dist/console-chrome.js +1 -34
- package/dist/console-instance.js +2 -91
- package/dist/container-state.js +0 -4
- package/dist/containment.js +2 -105
- package/dist/contract-changes.js +0 -6
- package/dist/contracts/acceptance-terms.js +0 -7
- package/dist/contracts/chat-actions.js +0 -46
- package/dist/contracts/chat-callback-rows.js +0 -8
- package/dist/contracts/chat-card.js +0 -14
- package/dist/contracts/chat-content.js +0 -55
- package/dist/contracts/chat-tables.js +0 -7
- package/dist/contracts/cli.js +0 -59
- package/dist/contracts/coding-activity.js +0 -15
- package/dist/contracts/coding-context.js +0 -15
- package/dist/contracts/coding-handoff.js +0 -11
- package/dist/contracts/coding-workspace.js +0 -14
- package/dist/contracts/console-api.js +3 -54
- package/dist/contracts/contract-test.js +0 -14
- package/dist/contracts/contract.js +0 -48
- package/dist/contracts/decision.js +0 -20
- package/dist/contracts/diff-stat.js +0 -16
- package/dist/contracts/discord-callback.js +0 -9
- package/dist/contracts/flow-card.js +0 -18
- package/dist/contracts/flow-propose.js +0 -8
- package/dist/contracts/flow-send.js +0 -31
- package/dist/contracts/flow.js +0 -112
- package/dist/contracts/gateway-tools.js +0 -22
- package/dist/contracts/handoff.js +0 -22
- package/dist/contracts/integration-metadata.js +0 -39
- package/dist/contracts/lead-context.js +0 -14
- package/dist/contracts/lead-tools.js +0 -49
- package/dist/contracts/mcp-oauth.js +0 -12
- package/dist/contracts/memory-pass.js +0 -21
- package/dist/contracts/model-catalog.js +0 -9
- package/dist/contracts/observation-request.js +0 -11
- package/dist/contracts/plan.js +0 -16
- package/dist/contracts/project-knowledge.js +0 -26
- package/dist/contracts/project-memory.js +0 -16
- package/dist/contracts/project-skills.js +0 -31
- package/dist/contracts/proof.js +0 -23
- package/dist/contracts/provider.js +0 -9
- package/dist/contracts/recipes.js +0 -16
- package/dist/contracts/release-check.js +0 -61
- package/dist/contracts/release-terms.js +0 -27
- package/dist/contracts/retention.js +0 -12
- package/dist/contracts/review-findings.js +0 -13
- package/dist/contracts/route-results.js +0 -15
- package/dist/contracts/route.js +0 -33
- package/dist/contracts/scope.js +0 -29
- package/dist/contracts/scout-report.js +0 -34
- package/dist/contracts/slack-callback.js +0 -11
- package/dist/contracts/sort-answer.js +0 -24
- package/dist/contracts/stage-output.js +0 -46
- package/dist/contracts/storage.js +0 -8
- package/dist/contracts/store-json.js +0 -41
- package/dist/contracts/subagent-turn.js +0 -23
- package/dist/contracts/task-sizing.js +0 -10
- package/dist/contracts/teams-callback.js +0 -7
- package/dist/contracts/telegram-callback.js +0 -27
- package/dist/contracts/trigger-payloads.js +0 -41
- package/dist/contracts/update-journal.js +0 -47
- package/dist/contracts/verification-receipt.js +0 -36
- package/dist/contracts/verification-view.js +0 -15
- package/dist/contracts/youiyoux-request.js +0 -26
- package/dist/control-setup.js +0 -7
- package/dist/control-ui.js +0 -3
- package/dist/controller-service.js +0 -4
- package/dist/controller-supervisor.js +0 -15
- package/dist/converse.js +3 -129
- package/dist/coordinator-proposals.js +1 -6
- package/dist/coordinator.js +0 -65
- package/dist/credentials-ui.js +0 -6
- package/dist/daemon.js +0 -137
- package/dist/database-reclaim.js +0 -3
- package/dist/decision.js +0 -78
- package/dist/demo-chat.js +4 -15
- package/dist/demo-sign-in.js +77 -0
- package/dist/demo.js +5 -194
- package/dist/deploy-gate.js +0 -20
- package/dist/deploy-lock.js +0 -4
- package/dist/deploy-phase.js +0 -22
- package/dist/deploy-rescue.js +0 -21
- package/dist/deprecations.js +0 -12
- package/dist/design/kit.css +9 -6
- package/dist/design/kit.js +2 -33
- package/dist/desktop-access.js +0 -9
- package/dist/desktop-bundle.js +1 -3
- package/dist/desktop-host.js +0 -34
- package/dist/desktop-update-gate.js +0 -8
- package/dist/desktop-update-recovery.js +1 -18
- package/dist/desktop-update.js +2 -48
- package/dist/digest-times.js +0 -2
- package/dist/disclosure.js +0 -5
- package/dist/discord-api.js +0 -6
- package/dist/discord-chat.js +0 -31
- package/dist/discord.js +0 -7
- package/dist/discover.js +0 -50
- package/dist/dispatch.js +61 -60
- package/dist/dispose.js +0 -208
- package/dist/doctor.js +0 -48
- package/dist/easy-start-args.js +7 -7
- package/dist/easy-start.js +32 -45
- package/dist/egress.js +1 -63
- package/dist/email-settings.js +0 -8
- package/dist/envelope.js +50 -86
- package/dist/eval-gate.js +1 -37
- package/dist/eval-hooks.js +0 -120
- package/dist/eval-oracle-main.js +0 -7
- package/dist/eval-oracle.js +3 -42
- package/dist/eval-world.js +1 -23
- package/dist/evals.js +6 -134
- package/dist/evidence-pack.js +0 -29
- package/dist/evidence-pair.js +0 -17
- package/dist/evidence.js +4 -280
- package/dist/exec.js +19 -404
- package/dist/exhaustion.js +0 -30
- package/dist/export-ui.js +0 -7
- package/dist/export.js +1 -38
- package/dist/failing-first.js +0 -6
- package/dist/fallback-schema.js +0 -5
- package/dist/fallback.js +0 -81
- package/dist/fast-start.js +0 -97
- package/dist/fast-terms.js +0 -3
- package/dist/fenced-check-coverage.js +0 -11
- package/dist/findings.js +1 -63
- package/dist/first-run.js +4 -57
- package/dist/first-tasks-child.js +0 -5
- package/dist/flaky-ci.js +1 -24
- package/dist/flaky-cli.js +0 -4
- package/dist/flaky.js +0 -43
- package/dist/fleet-schema.js +0 -11
- package/dist/flow-actions.js +0 -31
- package/dist/flow-browser.js +0 -42
- package/dist/flow-cadence.js +1 -24
- package/dist/flow-card-task.js +0 -11
- package/dist/flow-changed.js +0 -16
- package/dist/flow-code.js +1 -45
- package/dist/flow-draft.js +0 -17
- package/dist/flow-engine.js +2 -76
- package/dist/flow-gallery-ui.js +0 -28
- package/dist/flow-gallery.js +0 -54
- package/dist/flow-insights.js +0 -12
- package/dist/flow-items.js +0 -27
- package/dist/flow-live.js +0 -31
- package/dist/flow-people.js +0 -14
- package/dist/flow-pull-request.js +0 -32
- package/dist/flow-replies.js +0 -9
- package/dist/flow-result-card.js +0 -8
- package/dist/flow-schedule.js +0 -74
- package/dist/flow-scripts.js +0 -13
- package/dist/flow-secrets.js +0 -10
- package/dist/flow-send.js +0 -45
- package/dist/flow-share.js +0 -60
- package/dist/flow-sort.js +1 -24
- package/dist/flow-starters-ui.js +0 -2
- package/dist/flow-starters.js +0 -13
- package/dist/flow-steps.js +0 -80
- package/dist/flow-triggers.js +2 -256
- package/dist/flow-unattended-words.js +0 -13
- package/dist/flow-unattended.js +2 -115
- package/dist/flows-cli.js +2 -36
- package/dist/flows-ui.js +0 -51
- package/dist/flows.js +0 -158
- package/dist/folder-gate.js +0 -21
- package/dist/folder-look.js +0 -42
- package/dist/follow-through-brief.js +0 -6
- package/dist/follow-through-cli.js +0 -5
- package/dist/fonts.js +0 -13
- package/dist/gaps.js +0 -20
- package/dist/gate-failure.js +0 -7
- package/dist/git-floor.js +0 -12
- package/dist/git-incident.js +2 -38
- package/dist/git-views.js +0 -24
- package/dist/git.js +0 -31
- package/dist/github-import.js +0 -22
- package/dist/github-jobs.js +0 -12
- package/dist/google-mail.js +2 -27
- package/dist/grant.js +0 -110
- package/dist/graph.js +0 -108
- package/dist/guides.js +4 -15
- package/dist/handover-note.js +0 -12
- package/dist/health-cli.js +0 -2
- package/dist/home-groups.js +0 -15
- package/dist/html.js +0 -54
- package/dist/install-method.js +0 -12
- package/dist/integrations-ui.js +0 -15
- package/dist/integrations.js +7 -50
- package/dist/invoke.js +1 -381
- package/dist/is-record.js +0 -1
- package/dist/issues.js +0 -48
- package/dist/jsonl-discriminants.js +0 -6
- package/dist/keymap.js +2 -16
- package/dist/keys.js +0 -56
- package/dist/kits-ui.js +0 -3
- package/dist/kits.js +1 -24
- package/dist/knowledge-cli.js +0 -4
- package/dist/knowledge-ui.js +0 -8
- package/dist/landed-cleanup.js +0 -119
- package/dist/laptop-background.js +0 -85
- package/dist/laptop-service.js +0 -67
- package/dist/latency-timeline.js +2 -23
- package/dist/lead-about.js +0 -7
- package/dist/lead-answer-eval.js +0 -39
- package/dist/lead-capabilities.js +0 -19
- package/dist/lead-cli-live.js +0 -22
- package/dist/lead-cli.js +0 -27
- package/dist/lead-commitments.js +1 -27
- package/dist/lead-context.js +2 -42
- package/dist/lead-contract.js +0 -8
- package/dist/lead-doors.js +0 -105
- package/dist/lead-follow.js +0 -41
- package/dist/lead-identity.js +1 -4
- package/dist/lead-latency-schema.js +0 -13
- package/dist/lead-people.js +3 -18
- package/dist/lead-progress.js +0 -12
- package/dist/lead-queue-runtime.js +0 -26
- package/dist/lead-queue.js +0 -35
- package/dist/lead-route.js +0 -33
- package/dist/lead-status.js +0 -37
- package/dist/lead-sweep.js +0 -18
- package/dist/lead-tools.js +0 -160
- package/dist/lead-voice.js +0 -54
- package/dist/lead.js +2 -105
- package/dist/learning-child.js +0 -6
- package/dist/learning-look.js +0 -14
- package/dist/ledger-chain.js +0 -34
- package/dist/ledger-csv.js +0 -2
- package/dist/ledger-view.js +0 -3
- package/dist/lesson-notes.js +0 -56
- package/dist/limits-ui.js +0 -14
- package/dist/link.js +0 -79
- package/dist/live-backup.js +3 -33
- package/dist/live-bus.js +7 -81
- package/dist/live-children-read.js +4 -47
- package/dist/live-children.js +4 -44
- package/dist/live-detail.js +0 -17
- package/dist/live-input.js +5 -68
- package/dist/live-main-read.js +1 -14
- package/dist/live-notes.js +0 -12
- package/dist/live-open.js +1 -23
- package/dist/live-records.js +4 -151
- package/dist/live-reply.js +1 -51
- package/dist/live-terminal.js +12 -111
- package/dist/live.js +4 -130
- package/dist/local-owner-acts.js +168 -0
- package/dist/local-owner.js +15 -80
- package/dist/log.js +0 -8
- package/dist/login-secrets.js +2 -38
- package/dist/machine-check.js +0 -2
- package/dist/machine-identity.js +1 -46
- package/dist/mailbox.js +0 -30
- package/dist/maintenance.js +1 -3
- package/dist/mcp-connect.js +0 -88
- package/dist/mcp-core.js +0 -45
- package/dist/mcp-http.js +0 -13
- package/dist/mcp-oauth.js +0 -37
- package/dist/mcp-person.js +0 -11
- package/dist/mcp.js +3 -73
- package/dist/memory-cli.js +0 -3
- package/dist/memory-pass.js +0 -53
- package/dist/memory-tidy.js +0 -55
- package/dist/memory-use.js +2 -24
- package/dist/merge-checklist.js +21 -29
- package/dist/metrics.js +0 -10
- package/dist/mobile-viewport.js +0 -6
- package/dist/model-catalog.js +1 -33
- package/dist/model-choices.js +9 -84
- package/dist/model-names.js +0 -9
- package/dist/models-cli.js +0 -13
- package/dist/models-ui.js +0 -17
- package/dist/modes.js +0 -35
- package/dist/monitoring-settings.js +2 -27
- package/dist/monitoring-ui.js +2 -14
- package/dist/monitoring.js +2 -38
- package/dist/names.js +0 -39
- package/dist/needs-you.js +7 -70
- package/dist/never-index.js +0 -9
- package/dist/nightly-check.js +0 -74
- package/dist/nightly-github.js +0 -168
- package/dist/notification-delivery.js +0 -3
- package/dist/notification-rows.js +0 -17
- package/dist/oauth-run-token.js +1 -37
- package/dist/observations.js +0 -16
- package/dist/oidc.js +1 -20
- package/dist/onboard.js +4 -57
- package/dist/one-step-chat-schema.js +0 -15
- package/dist/openrouter-models.js +0 -6
- package/dist/operate-remote-arguments.js +2 -17
- package/dist/operate-remote.js +0 -33
- package/dist/operate.js +280 -2304
- package/dist/orphan-sweep.js +3 -37
- package/dist/page-work-contract.js +0 -3
- package/dist/password-bearer.js +1 -11
- package/dist/passwordless.js +0 -3
- package/dist/peek-cli.js +0 -46
- package/dist/peek.js +2 -61
- package/dist/people-audit-ui.js +0 -6
- package/dist/person-proof.js +0 -6
- package/dist/phase-routing.js +8 -183
- package/dist/plain-schema.js +0 -14
- package/dist/plain-words.js +18 -12
- package/dist/plan-auto.js +3 -18
- package/dist/plan-login.js +0 -16
- package/dist/plan.js +0 -68
- package/dist/plane-review.js +0 -56
- package/dist/planner-source.js +0 -96
- package/dist/planner.js +0 -117
- package/dist/policy-ui.js +0 -8
- package/dist/policy.js +0 -47
- package/dist/prepared-evidence.js +0 -5
- package/dist/principal.js +0 -29
- package/dist/probe.js +0 -30
- package/dist/process-custody.js +0 -15
- package/dist/process-liveness.js +0 -32
- package/dist/process-tree.js +3 -29
- package/dist/project-access.js +0 -10
- package/dist/project-admission.js +1 -41
- package/dist/project-cli.js +0 -6
- package/dist/project-concurrency.js +0 -35
- package/dist/project-delete-ui.js +4 -29
- package/dist/project-delete.js +1 -73
- package/dist/project-demo.js +0 -13
- package/dist/project-file.js +0 -2
- package/dist/project-identity.js +0 -41
- package/dist/project-knowledge.js +0 -39
- package/dist/project-learning.js +3 -55
- package/dist/project-memory.js +2 -45
- package/dist/project-outcomes.js +0 -6
- package/dist/project-sandbox.js +0 -23
- package/dist/project-skills.js +0 -12
- package/dist/project-tools.js +6 -184
- package/dist/project.js +0 -48
- package/dist/prompt.js +0 -31
- package/dist/proof.js +0 -220
- package/dist/proposal.js +0 -50
- package/dist/protected-storage-look.js +0 -6
- package/dist/protected-storage.js +1 -84
- package/dist/provider-auth.js +3 -69
- package/dist/provider-capture.js +1 -15
- package/dist/provider-connection.js +0 -8
- package/dist/provider-home.js +0 -25
- package/dist/provider-limits.js +0 -14
- package/dist/provider.js +0 -324
- package/dist/public-access.js +11 -30
- package/dist/public-check.js +0 -16
- package/dist/publish.js +1 -150
- package/dist/pull-request-flow.js +6 -74
- package/dist/pull-request-schema.js +0 -4
- package/dist/pull-request-ui.js +0 -5
- package/dist/pulls.js +0 -80
- package/dist/push.js +5 -89
- package/dist/quick-start.js +1 -23
- package/dist/read-executor-worker.js +0 -1
- package/dist/read-executor.js +0 -10
- package/dist/read-only-request.js +0 -7
- package/dist/recipe-ui.js +0 -6
- package/dist/recipes.js +0 -13
- package/dist/release-adapters.js +6 -214
- package/dist/release-approval.js +0 -74
- package/dist/release-authority.js +0 -42
- package/dist/release-auto.js +0 -142
- package/dist/release-card.js +26 -103
- package/dist/release-carry.js +0 -132
- package/dist/release-ci-hold.js +0 -20
- package/dist/release-cli.js +0 -46
- package/dist/release-coverage.js +0 -16
- package/dist/release-gate.js +0 -30
- package/dist/release-git.js +0 -24
- package/dist/release-links.js +0 -17
- package/dist/release-net.js +0 -42
- package/dist/release-next.js +0 -25
- package/dist/release-recheck.js +0 -89
- package/dist/release-refusal.js +0 -18
- package/dist/release-view.js +0 -5
- package/dist/release.js +7 -341
- package/dist/releases.js +0 -49
- package/dist/remote-audit.js +0 -25
- package/dist/remote-command.js +0 -16
- package/dist/remote-destination.js +5 -26
- package/dist/remote-exec.js +1 -27
- package/dist/remote-main.js +1 -53
- package/dist/remote-run.js +0 -5
- package/dist/remote.js +0 -49
- package/dist/render.js +0 -133
- package/dist/reply-shape.js +0 -85
- package/dist/reply-voice.js +0 -7
- package/dist/report-summary.js +0 -5
- package/dist/reported-usage.js +0 -30
- package/dist/repos.js +9 -62
- package/dist/repository-context.js +1 -24
- package/dist/request-budget-ui.js +0 -4
- package/dist/request-budget.js +0 -23
- package/dist/restart-certification.js +0 -37
- package/dist/result-actions.js +0 -62
- package/dist/result-acts.js +0 -34
- package/dist/result-completion.js +9 -28
- package/dist/result-evidence-readers.js +0 -3
- package/dist/result-follow-ups.js +1 -39
- package/dist/result-review.js +0 -86
- package/dist/result-shots.js +0 -39
- package/dist/retention-ui.js +0 -7
- package/dist/retention.js +2 -46
- package/dist/review-context.js +0 -129
- package/dist/review-evidence.js +0 -4
- package/dist/review-switch.js +1 -7
- package/dist/reviewer.js +0 -35
- package/dist/route-change-check.js +0 -2
- package/dist/route-change.js +0 -16
- package/dist/route-hooks.js +0 -27
- package/dist/route-results.js +2 -58
- package/dist/route-rules.js +1 -106
- package/dist/route-savings.js +0 -36
- package/dist/route-store.js +1 -133
- package/dist/run-closeout.js +2 -50
- package/dist/run-efficiency.js +2 -44
- package/dist/run-ending.js +0 -13
- package/dist/runner-process.js +0 -33
- package/dist/runner.js +17 -189
- package/dist/sample-project.js +2 -27
- package/dist/sandbox-launch.js +4 -25
- package/dist/sandbox-runtime.js +1 -88
- package/dist/scan.js +0 -27
- package/dist/scope.js +88 -566
- package/dist/scout-browser-lock.js +0 -6
- package/dist/scout-browser.js +2 -68
- package/dist/scout-net.js +1 -32
- package/dist/scout-report.js +0 -22
- package/dist/scout.js +1 -116
- package/dist/secret-mint.js +0 -6
- package/dist/serve.js +29 -564
- package/dist/server/agents-computers.js +0 -10
- package/dist/server/agents-live-filed.js +0 -13
- package/dist/server/agents-live-lead.js +0 -19
- package/dist/server/agents-live-room.js +0 -19
- package/dist/server/chat.js +2 -194
- package/dist/server/chrome.js +17 -263
- package/dist/server/console.css +4 -4
- package/dist/server/edge-refusal.js +0 -3
- package/dist/server/flows.js +5 -74
- package/dist/server/guards.js +9 -86
- package/dist/server/handler-registry.js +0 -2
- package/dist/server/home-asks.js +1 -22
- package/dist/server/home.js +1 -30
- package/dist/server/http.js +0 -82
- package/dist/server/in-place.js +0 -15
- package/dist/server/kit-standalone.js +0 -20
- package/dist/server/kit.js +2 -114
- package/dist/server/last-checks.js +0 -2
- package/dist/server/live.js +1 -36
- package/dist/server/merge-ready-view.js +0 -6
- package/dist/server/mutation-key.js +3 -40
- package/dist/server/pages-assets.js +0 -16
- package/dist/server/pages.js +7 -195
- package/dist/server/people-tokens.js +3 -101
- package/dist/server/remote-hooks.js +0 -13
- package/dist/server/remote.js +0 -28
- package/dist/server/render-chat.js +8 -144
- package/dist/server/render-code-map.js +0 -5
- package/dist/server/render-flaky.js +0 -8
- package/dist/server/render-follow-through.js +2 -5
- package/dist/server/render-history.js +2 -34
- package/dist/server/render-new-task.js +2 -29
- package/dist/server/render-pages.js +1 -111
- package/dist/server/render-people.js +2 -46
- package/dist/server/render-projects.js +15 -32
- package/dist/server/render-release.js +1 -14
- package/dist/server/render-results.js +4 -355
- package/dist/server/render-settings.js +0 -15
- package/dist/server/render-tasks.js +17 -497
- package/dist/server/request-context.js +2 -9
- package/dist/server/retired-routes.js +0 -11
- package/dist/server/route-policy.js +0 -3
- package/dist/server/route-table.js +8 -133
- package/dist/server/runtime.js +0 -1
- package/dist/server/session.js +0 -28
- package/dist/server/settings.js +40 -246
- package/dist/server/signup-gate.js +0 -11
- package/dist/server/sweeps.js +0 -6
- package/dist/server/task-views.js +12 -227
- package/dist/server/tasks.js +37 -531
- package/dist/server/terminals.js +2 -46
- package/dist/server/welcome.js +2 -22
- package/dist/server-telemetry.js +0 -9
- package/dist/settings-groups-ui.js +50 -63
- package/dist/settings-kit-script.js +0 -19
- package/dist/settings-kit.js +0 -63
- package/dist/settings-proofs.js +0 -23
- package/dist/setup-guide.js +1 -9
- package/dist/shared-deps.js +9 -84
- package/dist/sign-in-guard.js +0 -27
- package/dist/skills-ui.js +0 -2
- package/dist/skills.js +2 -54
- package/dist/slack-api.js +0 -3
- package/dist/slack-chat.js +0 -25
- package/dist/slack-settings.js +0 -1
- package/dist/slack-socket.js +0 -5
- package/dist/slack.js +0 -4
- package/dist/sleep.js +0 -1
- package/dist/source-key.js +0 -10
- package/dist/spend-report.js +0 -29
- package/dist/spend-ui.js +0 -26
- package/dist/spend.js +0 -40
- package/dist/sqlite-runtime.js +0 -2
- package/dist/sqlite-telemetry.js +0 -9
- package/dist/sso-settings.js +0 -13
- package/dist/sso-ui.js +0 -2
- package/dist/staged-runtimes.js +5 -39
- package/dist/steer-capabilities.js +1 -7
- package/dist/steer-controls.js +0 -10
- package/dist/steer-live.js +0 -31
- package/dist/steer-mailbox.js +4 -65
- package/dist/steer-status.js +0 -7
- package/dist/stop-proof.js +5 -62
- package/dist/storage-sweep-child.js +1 -5
- package/dist/storage-sweep-process.js +2 -20
- package/dist/storage-sweep-schedule.js +0 -32
- package/dist/storage-sweep.js +2 -51
- package/dist/storage-ui.js +0 -18
- package/dist/storage.js +0 -31
- package/dist/store-baseline.js +0 -14
- package/dist/store.js +109 -4763
- package/dist/stream-scope.js +0 -16
- package/dist/structured-output.js +0 -39
- package/dist/style-asset.js +0 -2
- package/dist/subagent-admin.js +0 -9
- package/dist/subagent-desk.js +0 -29
- package/dist/subagent-memory.js +0 -38
- package/dist/subagent-question.js +0 -29
- package/dist/subagent-tools.js +1 -60
- package/dist/subagent-week.js +0 -28
- package/dist/subagent-work.js +1 -39
- package/dist/subagents-ui.js +1 -13
- package/dist/subagents.js +0 -40
- package/dist/subscription-chat.js +1 -42
- package/dist/summary.js +0 -19
- package/dist/surface.js +8 -73
- package/dist/sweep-gate.js +0 -2
- package/dist/sweep-quarantine.js +2 -13
- package/dist/sync.js +0 -47
- package/dist/tailnet.js +0 -9
- package/dist/tamper-scan.js +0 -15
- package/dist/task-activity.js +1 -16
- package/dist/task-aliases.js +0 -3
- package/dist/task-archive.js +0 -7
- package/dist/task-base.js +0 -18
- package/dist/task-composer.js +0 -2
- package/dist/task-control.js +0 -74
- package/dist/task-live.js +0 -23
- package/dist/task-moves.js +0 -28
- package/dist/task-outcome-cli.js +66 -19
- package/dist/task-review-brief.js +0 -7
- package/dist/task-sizing.js +0 -47
- package/dist/task-source.js +0 -23
- package/dist/task-status.js +0 -106
- package/dist/task-text.js +0 -12
- package/dist/team-chat-authorization.js +0 -3
- package/dist/team-contract.js +0 -2
- package/dist/team-http.js +0 -4
- package/dist/team-leads.js +1 -16
- package/dist/team-runtime.js +0 -13
- package/dist/team-ui.js +0 -1
- package/dist/team-updates.js +0 -4
- package/dist/teams-api.js +0 -12
- package/dist/teams-chat.js +0 -28
- package/dist/teams.js +0 -12
- package/dist/teardown-git.js +6 -294
- package/dist/telegram-flow.js +0 -51
- package/dist/telegram-lead.js +2 -203
- package/dist/telegram-live.js +1 -42
- package/dist/telegram-progress.js +0 -18
- package/dist/telegram-settings.js +0 -7
- package/dist/telegram-status.js +0 -46
- package/dist/telegram-team.js +1 -33
- package/dist/telegram.js +6 -454
- package/dist/templates.js +0 -24
- package/dist/terminal-catalog.js +2 -20
- package/dist/terminal-text.js +0 -12
- package/dist/test-infra.js +0 -50
- package/dist/test-temp.js +3 -26
- package/dist/text-limits.js +0 -92
- package/dist/tidy.js +0 -76
- package/dist/tier-report.js +0 -2
- package/dist/token-notices.js +1 -9
- package/dist/tokens-cli.js +0 -13
- package/dist/tool-broker.js +2 -53
- package/dist/tool-launcher.js +0 -14
- package/dist/tool-secret-store.js +3 -125
- package/dist/toolroll-update-cli.js +0 -9
- package/dist/toolroll-update-ui.js +0 -14
- package/dist/toolroll-update.js +13 -252
- package/dist/tools-ui.js +0 -17
- package/dist/transitions-recipes.js +0 -2
- package/dist/tree-proof.js +0 -23
- package/dist/tui-contract.js +0 -8
- package/dist/tui-source.js +91 -40
- package/dist/tui.js +54 -54
- package/dist/typed-start.js +0 -27
- package/dist/unfenced-test-support.js +0 -5
- package/dist/up-processes.js +1 -40
- package/dist/up-role-child.js +1 -13
- package/dist/update-notice.js +0 -1
- package/dist/vanished-retry.js +0 -40
- package/dist/verification-evidence.js +0 -39
- package/dist/version.js +0 -1
- package/dist/wake-hub.js +0 -28
- package/dist/webhook-limits.js +0 -7
- package/dist/webhook-readers.js +0 -30
- package/dist/webhook-signing.js +0 -14
- package/dist/webhooks.js +0 -54
- package/dist/welcome-facts.js +2 -14
- package/dist/when-html.js +0 -27
- package/dist/work-index.js +26 -98
- package/dist/work-summary.js +2 -17
- package/dist/workspace-motion.js +0 -2
- package/dist/workspace-revision.js +1 -21
- package/dist/workspace-ui.js +15 -135
- package/dist/worktree-notices.js +0 -3
- package/dist/worktree.js +0 -308
- package/dist/writes-mode.js +0 -1
- package/dist/youiyoux-intake.js +4 -159
- package/package.json +5 -4
- package/dist/accent-colors.d.ts +0 -51
- package/dist/acp-agents.d.ts +0 -43
- package/dist/acp-attest.d.ts +0 -44
- package/dist/acp-check.d.ts +0 -37
- package/dist/acp-client.d.ts +0 -163
- package/dist/acp-inspect.d.ts +0 -23
- package/dist/action-ledger.d.ts +0 -27
- package/dist/activity-line.d.ts +0 -29
- package/dist/actor.d.ts +0 -11
- package/dist/agent-board.d.ts +0 -229
- package/dist/agent-fence.d.ts +0 -479
- package/dist/agent-onboard.d.ts +0 -158
- package/dist/agent-pulse.d.ts +0 -43
- package/dist/agent-state-words.d.ts +0 -19
- package/dist/agent-state.d.ts +0 -65
- package/dist/agent-turns.d.ts +0 -52
- package/dist/agentconfig.d.ts +0 -191
- package/dist/agents-board.d.ts +0 -145
- package/dist/agents-live.d.ts +0 -31
- package/dist/api-tokens.d.ts +0 -80
- package/dist/approval-authority.d.ts +0 -51
- package/dist/approval-password.d.ts +0 -31
- package/dist/approval-policy.d.ts +0 -69
- package/dist/approval-rules-ui.d.ts +0 -40
- package/dist/assignment-adapters.d.ts +0 -152
- package/dist/assignment-brief.d.ts +0 -83
- package/dist/assignment-delivery.d.ts +0 -86
- package/dist/assignment-presentation.d.ts +0 -37
- package/dist/assignment-status.d.ts +0 -71
- package/dist/assignment-ui.d.ts +0 -68
- package/dist/assignment.d.ts +0 -318
- package/dist/attest.d.ts +0 -59
- package/dist/audit-cli.d.ts +0 -17
- package/dist/backend.d.ts +0 -97
- package/dist/backup-ui.d.ts +0 -32
- package/dist/backup.d.ts +0 -92
- package/dist/batch-checks.d.ts +0 -15
- package/dist/batch-policy.d.ts +0 -23
- package/dist/beads.d.ts +0 -34
- package/dist/bin.d.ts +0 -2
- package/dist/board.d.ts +0 -134
- package/dist/boot-identity.d.ts +0 -64
- package/dist/bounded-spawn.d.ts +0 -57
- package/dist/brand-icons.d.ts +0 -168
- package/dist/brand-mark.d.ts +0 -28
- package/dist/brief-fence.d.ts +0 -9
- package/dist/browser-crew.d.ts +0 -12
- package/dist/browser-shell.d.ts +0 -9
- package/dist/browser-workspace.d.ts +0 -1972
- package/dist/budget-alerts.d.ts +0 -19
- package/dist/build-review.d.ts +0 -38
- package/dist/builder-restart.d.ts +0 -21
- package/dist/builder.d.ts +0 -409
- package/dist/canonical-json.d.ts +0 -14
- package/dist/capscan.d.ts +0 -35
- package/dist/card-catalog.d.ts +0 -209
- package/dist/card-cli.d.ts +0 -43
- package/dist/card-render.d.ts +0 -43
- package/dist/card-sources.d.ts +0 -68
- package/dist/chat-acceptance.d.ts +0 -74
- package/dist/chat-actions.d.ts +0 -460
- package/dist/chat-admission.d.ts +0 -19
- package/dist/chat-approval.d.ts +0 -58
- package/dist/chat-ask.d.ts +0 -32
- package/dist/chat-card-part.d.ts +0 -11
- package/dist/chat-card.d.ts +0 -46
- package/dist/chat-channel.d.ts +0 -126
- package/dist/chat-continuity.d.ts +0 -32
- package/dist/chat-controls.d.ts +0 -117
- package/dist/chat-core.d.ts +0 -177
- package/dist/chat-decide.d.ts +0 -269
- package/dist/chat-delivery-state.d.ts +0 -132
- package/dist/chat-delivery.d.ts +0 -71
- package/dist/chat-display.d.ts +0 -1
- package/dist/chat-evidence.d.ts +0 -123
- package/dist/chat-flow.d.ts +0 -70
- package/dist/chat-follow.d.ts +0 -20
- package/dist/chat-inbox.d.ts +0 -57
- package/dist/chat-live-schema.d.ts +0 -17
- package/dist/chat-live.d.ts +0 -161
- package/dist/chat-message-states.d.ts +0 -75
- package/dist/chat-messages.d.ts +0 -338
- package/dist/chat-migration.d.ts +0 -30
- package/dist/chat-polish.d.ts +0 -17
- package/dist/chat-quiet.d.ts +0 -40
- package/dist/chat-review.d.ts +0 -51
- package/dist/chat-rooms.d.ts +0 -123
- package/dist/chat-settings-page.d.ts +0 -43
- package/dist/chat-task-actions.d.ts +0 -59
- package/dist/chat-terms.d.ts +0 -29
- package/dist/chat-voice.d.ts +0 -69
- package/dist/chat-warmth.d.ts +0 -21
- package/dist/check-access.d.ts +0 -54
- package/dist/check-facts.d.ts +0 -126
- package/dist/check-failure.d.ts +0 -113
- package/dist/check-levels-ui.d.ts +0 -36
- package/dist/check-levels.d.ts +0 -70
- package/dist/check-load.d.ts +0 -85
- package/dist/check-only-standing.d.ts +0 -36
- package/dist/check-only.d.ts +0 -15
- package/dist/check-progress.d.ts +0 -78
- package/dist/check-repair.d.ts +0 -47
- package/dist/check-report.d.ts +0 -177
- package/dist/check-stamp.d.ts +0 -46
- package/dist/checkout-cleanup.d.ts +0 -157
- package/dist/checkout-proof.d.ts +0 -28
- package/dist/child-database.d.ts +0 -12
- package/dist/claim.d.ts +0 -627
- package/dist/claude-print.d.ts +0 -12
- package/dist/cli-fast.d.ts +0 -8
- package/dist/cli-help.d.ts +0 -8
- package/dist/cli-http.d.ts +0 -58
- package/dist/cli.d.ts +0 -149
- package/dist/code-extract.d.ts +0 -44
- package/dist/code-map-acts.d.ts +0 -24
- package/dist/code-map-admission.d.ts +0 -52
- package/dist/code-map-build.d.ts +0 -63
- package/dist/code-map-cli.d.ts +0 -153
- package/dist/code-map-main.d.ts +0 -1
- package/dist/code-map-read.d.ts +0 -106
- package/dist/code-map-report.d.ts +0 -37
- package/dist/code-map-shape.d.ts +0 -172
- package/dist/code-map-store.d.ts +0 -136
- package/dist/codex-limits.d.ts +0 -15
- package/dist/coding-context.d.ts +0 -18
- package/dist/coding-handoff.d.ts +0 -39
- package/dist/coding-provider.d.ts +0 -88
- package/dist/coding-shipping-ui.d.ts +0 -5
- package/dist/coding-types.d.ts +0 -16
- package/dist/coding-ui.d.ts +0 -19
- package/dist/coding-update.d.ts +0 -28
- package/dist/coding-workspace.d.ts +0 -141
- package/dist/commit-extract.d.ts +0 -135
- package/dist/connect-cli.d.ts +0 -34
- package/dist/console-chrome.d.ts +0 -41
- package/dist/console-instance.d.ts +0 -157
- package/dist/container-state.d.ts +0 -5
- package/dist/containment.d.ts +0 -205
- package/dist/contract-changes.d.ts +0 -52
- package/dist/contracts/acceptance-terms.d.ts +0 -14
- package/dist/contracts/chat-actions.d.ts +0 -465
- package/dist/contracts/chat-callback-rows.d.ts +0 -59
- package/dist/contracts/chat-card.d.ts +0 -91
- package/dist/contracts/chat-content.d.ts +0 -551
- package/dist/contracts/chat-tables.d.ts +0 -12
- package/dist/contracts/cli.d.ts +0 -987
- package/dist/contracts/coding-activity.d.ts +0 -64
- package/dist/contracts/coding-context.d.ts +0 -97
- package/dist/contracts/coding-handoff.d.ts +0 -44
- package/dist/contracts/coding-workspace.d.ts +0 -165
- package/dist/contracts/console-api.d.ts +0 -539
- package/dist/contracts/contract-test.d.ts +0 -45
- package/dist/contracts/contract.d.ts +0 -92
- package/dist/contracts/decision.d.ts +0 -45
- package/dist/contracts/diff-stat.d.ts +0 -58
- package/dist/contracts/discord-callback.d.ts +0 -45
- package/dist/contracts/flow-card.d.ts +0 -53
- package/dist/contracts/flow-propose.d.ts +0 -334
- package/dist/contracts/flow-send.d.ts +0 -245
- package/dist/contracts/flow.d.ts +0 -19565
- package/dist/contracts/gateway-tools.d.ts +0 -472
- package/dist/contracts/handoff.d.ts +0 -91
- package/dist/contracts/health.d.ts +0 -82
- package/dist/contracts/integration-metadata.d.ts +0 -100
- package/dist/contracts/lead-context.d.ts +0 -165
- package/dist/contracts/lead-tools.d.ts +0 -2312
- package/dist/contracts/mcp-oauth.d.ts +0 -53
- package/dist/contracts/memory-pass.d.ts +0 -157
- package/dist/contracts/model-catalog.d.ts +0 -21
- package/dist/contracts/observation-request.d.ts +0 -37
- package/dist/contracts/plan.d.ts +0 -58
- package/dist/contracts/project-knowledge.d.ts +0 -159
- package/dist/contracts/project-memory.d.ts +0 -119
- package/dist/contracts/project-skills.d.ts +0 -120
- package/dist/contracts/proof.d.ts +0 -136
- package/dist/contracts/provider.d.ts +0 -40
- package/dist/contracts/recipes.d.ts +0 -166
- package/dist/contracts/release-check.d.ts +0 -371
- package/dist/contracts/release-terms.d.ts +0 -107
- package/dist/contracts/retention.d.ts +0 -42
- package/dist/contracts/review-findings.d.ts +0 -46
- package/dist/contracts/route-results.d.ts +0 -121
- package/dist/contracts/route.d.ts +0 -897
- package/dist/contracts/scope.d.ts +0 -105
- package/dist/contracts/scout-report.d.ts +0 -234
- package/dist/contracts/slack-callback.d.ts +0 -43
- package/dist/contracts/sort-answer.d.ts +0 -83
- package/dist/contracts/stage-output.d.ts +0 -127
- package/dist/contracts/storage.d.ts +0 -37
- package/dist/contracts/store-json.d.ts +0 -313
- package/dist/contracts/subagent-turn.d.ts +0 -43
- package/dist/contracts/task-sizing.d.ts +0 -24
- package/dist/contracts/teams-callback.d.ts +0 -13
- package/dist/contracts/telegram-callback.d.ts +0 -120
- package/dist/contracts/trigger-payloads.d.ts +0 -389
- package/dist/contracts/update-journal.d.ts +0 -254
- package/dist/contracts/verification-receipt.d.ts +0 -200
- package/dist/contracts/verification-view.d.ts +0 -102
- package/dist/contracts/youiyoux-request.d.ts +0 -248
- package/dist/control-setup.d.ts +0 -44
- package/dist/control-ui.d.ts +0 -37
- package/dist/controller-service.d.ts +0 -1
- package/dist/controller-supervisor.d.ts +0 -38
- package/dist/converse.d.ts +0 -316
- package/dist/coordinator-proposals.d.ts +0 -29
- package/dist/coordinator.d.ts +0 -186
- package/dist/credentials-ui.d.ts +0 -33
- package/dist/daemon.d.ts +0 -226
- package/dist/database-reclaim.d.ts +0 -4
- package/dist/decision.d.ts +0 -119
- package/dist/demo-chat.d.ts +0 -32
- package/dist/demo.d.ts +0 -153
- package/dist/deploy-gate.d.ts +0 -20
- package/dist/deploy-lock.d.ts +0 -8
- package/dist/deploy-phase.d.ts +0 -37
- package/dist/deploy-rescue.d.ts +0 -42
- package/dist/deprecations.d.ts +0 -20
- package/dist/design/kit.d.ts +0 -216
- package/dist/desktop-access.d.ts +0 -28
- package/dist/desktop-bundle.d.ts +0 -16
- package/dist/desktop-host.d.ts +0 -54
- package/dist/desktop-update-gate.d.ts +0 -12
- package/dist/desktop-update-recovery.d.ts +0 -14
- package/dist/desktop-update.d.ts +0 -147
- package/dist/digest-times.d.ts +0 -3
- package/dist/disclosure.d.ts +0 -6
- package/dist/discord-api.d.ts +0 -24
- package/dist/discord-chat.d.ts +0 -27
- package/dist/discord-settings.d.ts +0 -4
- package/dist/discord.d.ts +0 -10
- package/dist/discover.d.ts +0 -75
- package/dist/dispatch.d.ts +0 -123
- package/dist/dispose.d.ts +0 -194
- package/dist/doctor.d.ts +0 -67
- package/dist/easy-start-args.d.ts +0 -13
- package/dist/easy-start.d.ts +0 -77
- package/dist/egress.d.ts +0 -121
- package/dist/email-settings.d.ts +0 -34
- package/dist/envelope.d.ts +0 -47
- package/dist/eval-gate.d.ts +0 -51
- package/dist/eval-hooks.d.ts +0 -94
- package/dist/eval-oracle-main.d.ts +0 -1
- package/dist/eval-oracle.d.ts +0 -60
- package/dist/eval-world.d.ts +0 -17
- package/dist/evals.d.ts +0 -299
- package/dist/evidence-pack.d.ts +0 -186
- package/dist/evidence-pair.d.ts +0 -53
- package/dist/evidence.d.ts +0 -371
- package/dist/exec.d.ts +0 -355
- package/dist/exhaustion.d.ts +0 -58
- package/dist/export-ui.d.ts +0 -11
- package/dist/export.d.ts +0 -48
- package/dist/failing-first.d.ts +0 -31
- package/dist/fallback-schema.d.ts +0 -8
- package/dist/fallback.d.ts +0 -138
- package/dist/fast-start.d.ts +0 -213
- package/dist/fast-terms.d.ts +0 -9
- package/dist/fenced-check-coverage.d.ts +0 -62
- package/dist/findings.d.ts +0 -224
- package/dist/first-run.d.ts +0 -197
- package/dist/first-tasks-child.d.ts +0 -1
- package/dist/flaky-ci.d.ts +0 -31
- package/dist/flaky-cli.d.ts +0 -60
- package/dist/flaky.d.ts +0 -83
- package/dist/fleet-schema.d.ts +0 -17
- package/dist/flow-actions.d.ts +0 -79
- package/dist/flow-browser.d.ts +0 -103
- package/dist/flow-cadence.d.ts +0 -48
- package/dist/flow-card-task.d.ts +0 -11
- package/dist/flow-changed.d.ts +0 -14
- package/dist/flow-code.d.ts +0 -84
- package/dist/flow-draft.d.ts +0 -42
- package/dist/flow-engine.d.ts +0 -82
- package/dist/flow-gallery-ui.d.ts +0 -73
- package/dist/flow-gallery.d.ts +0 -147
- package/dist/flow-insights.d.ts +0 -104
- package/dist/flow-items.d.ts +0 -37
- package/dist/flow-live.d.ts +0 -16
- package/dist/flow-people.d.ts +0 -23
- package/dist/flow-pull-request.d.ts +0 -14
- package/dist/flow-replies.d.ts +0 -48
- package/dist/flow-result-card.d.ts +0 -22
- package/dist/flow-schedule.d.ts +0 -154
- package/dist/flow-scripts.d.ts +0 -31
- package/dist/flow-secrets.d.ts +0 -13
- package/dist/flow-send.d.ts +0 -54
- package/dist/flow-share.d.ts +0 -78
- package/dist/flow-sort.d.ts +0 -63
- package/dist/flow-starters-ui.d.ts +0 -15
- package/dist/flow-starters.d.ts +0 -63
- package/dist/flow-steps.d.ts +0 -52
- package/dist/flow-triggers.d.ts +0 -303
- package/dist/flow-unattended-words.d.ts +0 -48
- package/dist/flow-unattended.d.ts +0 -259
- package/dist/flows-cli.d.ts +0 -358
- package/dist/flows-ui.d.ts +0 -70
- package/dist/flows.d.ts +0 -136
- package/dist/folder-gate.d.ts +0 -32
- package/dist/folder-look.d.ts +0 -110
- package/dist/follow-through-brief.d.ts +0 -12
- package/dist/follow-through-cli.d.ts +0 -23
- package/dist/fonts.d.ts +0 -19
- package/dist/gaps.d.ts +0 -35
- package/dist/gate-failure.d.ts +0 -40
- package/dist/git-floor.d.ts +0 -16
- package/dist/git-incident.d.ts +0 -65
- package/dist/git-views.d.ts +0 -20
- package/dist/git.d.ts +0 -54
- package/dist/github-import.d.ts +0 -62
- package/dist/github-jobs.d.ts +0 -25
- package/dist/google-mail.d.ts +0 -59
- package/dist/grant.d.ts +0 -136
- package/dist/graph.d.ts +0 -164
- package/dist/guides.d.ts +0 -22
- package/dist/handover-note.d.ts +0 -33
- package/dist/health-cli.d.ts +0 -3
- package/dist/home-groups.d.ts +0 -39
- package/dist/html.d.ts +0 -85
- package/dist/install-method.d.ts +0 -17
- package/dist/integrations-ui.d.ts +0 -17
- package/dist/integrations.d.ts +0 -138
- package/dist/invoke.d.ts +0 -228
- package/dist/is-record.d.ts +0 -2
- package/dist/issues.d.ts +0 -41
- package/dist/jsonl-discriminants.d.ts +0 -11
- package/dist/keymap.d.ts +0 -41
- package/dist/keys.d.ts +0 -108
- package/dist/kits-ui.d.ts +0 -16
- package/dist/kits.d.ts +0 -88
- package/dist/knowledge-cli.d.ts +0 -132
- package/dist/knowledge-ui.d.ts +0 -45
- package/dist/landed-cleanup.d.ts +0 -175
- package/dist/laptop-background.d.ts +0 -187
- package/dist/laptop-service.d.ts +0 -111
- package/dist/latency-timeline.d.ts +0 -46
- package/dist/lead-about.d.ts +0 -35
- package/dist/lead-answer-eval.d.ts +0 -93
- package/dist/lead-capabilities.d.ts +0 -57
- package/dist/lead-cli-live.d.ts +0 -56
- package/dist/lead-cli.d.ts +0 -58
- package/dist/lead-commitments.d.ts +0 -84
- package/dist/lead-context.d.ts +0 -45
- package/dist/lead-contract.d.ts +0 -10
- package/dist/lead-doors.d.ts +0 -122
- package/dist/lead-follow.d.ts +0 -26
- package/dist/lead-identity.d.ts +0 -23
- package/dist/lead-latency-schema.d.ts +0 -24
- package/dist/lead-people.d.ts +0 -55
- package/dist/lead-progress.d.ts +0 -67
- package/dist/lead-queue-runtime.d.ts +0 -46
- package/dist/lead-queue.d.ts +0 -104
- package/dist/lead-route.d.ts +0 -56
- package/dist/lead-status.d.ts +0 -116
- package/dist/lead-sweep.d.ts +0 -67
- package/dist/lead-tools.d.ts +0 -167
- package/dist/lead-voice.d.ts +0 -85
- package/dist/lead.d.ts +0 -142
- package/dist/learning-child.d.ts +0 -1
- package/dist/learning-look.d.ts +0 -25
- package/dist/ledger-chain.d.ts +0 -46
- package/dist/ledger-csv.d.ts +0 -4
- package/dist/ledger-view.d.ts +0 -27
- package/dist/lesson-notes.d.ts +0 -81
- package/dist/limits-ui.d.ts +0 -34
- package/dist/link.d.ts +0 -72
- package/dist/live-backup.d.ts +0 -39
- package/dist/live-bus.d.ts +0 -141
- package/dist/live-children-read.d.ts +0 -52
- package/dist/live-children.d.ts +0 -73
- package/dist/live-detail.d.ts +0 -27
- package/dist/live-input.d.ts +0 -68
- package/dist/live-main-read.d.ts +0 -19
- package/dist/live-notes.d.ts +0 -20
- package/dist/live-open.d.ts +0 -28
- package/dist/live-records.d.ts +0 -149
- package/dist/live-reply.d.ts +0 -83
- package/dist/live-terminal.d.ts +0 -273
- package/dist/live.d.ts +0 -152
- package/dist/local-owner.d.ts +0 -124
- package/dist/log.d.ts +0 -7
- package/dist/login-secrets.d.ts +0 -60
- package/dist/machine-check.d.ts +0 -16
- package/dist/machine-identity.d.ts +0 -75
- package/dist/mailbox.d.ts +0 -57
- package/dist/maintenance.d.ts +0 -13
- package/dist/mcp-connect.d.ts +0 -181
- package/dist/mcp-core.d.ts +0 -110
- package/dist/mcp-http.d.ts +0 -32
- package/dist/mcp-oauth.d.ts +0 -92
- package/dist/mcp-person.d.ts +0 -22
- package/dist/mcp.d.ts +0 -20
- package/dist/memory-cli.d.ts +0 -768
- package/dist/memory-pass.d.ts +0 -151
- package/dist/memory-tidy.d.ts +0 -181
- package/dist/memory-use.d.ts +0 -35
- package/dist/merge-checklist.d.ts +0 -55
- package/dist/metrics.d.ts +0 -6
- package/dist/mobile-viewport.d.ts +0 -7
- package/dist/model-catalog.d.ts +0 -123
- package/dist/model-choices.d.ts +0 -235
- package/dist/model-names.d.ts +0 -6
- package/dist/models-cli.d.ts +0 -307
- package/dist/models-ui.d.ts +0 -161
- package/dist/modes.d.ts +0 -99
- package/dist/monitoring-settings.d.ts +0 -44
- package/dist/monitoring-ui.d.ts +0 -23
- package/dist/monitoring.d.ts +0 -34
- package/dist/names.d.ts +0 -67
- package/dist/needs-you.d.ts +0 -164
- package/dist/never-index.d.ts +0 -3
- package/dist/nightly-check.d.ts +0 -107
- package/dist/nightly-github.d.ts +0 -318
- package/dist/notification-delivery.d.ts +0 -19
- package/dist/notification-rows.d.ts +0 -30
- package/dist/oauth-run-token.d.ts +0 -51
- package/dist/observations.d.ts +0 -36
- package/dist/oidc.d.ts +0 -71
- package/dist/onboard.d.ts +0 -108
- package/dist/one-step-chat-schema.d.ts +0 -22
- package/dist/openrouter-models.d.ts +0 -34
- package/dist/operate-remote-arguments.d.ts +0 -36
- package/dist/operate-remote.d.ts +0 -98
- package/dist/operate.d.ts +0 -401
- package/dist/orphan-sweep.d.ts +0 -65
- package/dist/page-work-contract.d.ts +0 -21
- package/dist/password-bearer.d.ts +0 -25
- package/dist/passwordless.d.ts +0 -23
- package/dist/peek-cli.d.ts +0 -112
- package/dist/peek.d.ts +0 -110
- package/dist/people-audit-ui.d.ts +0 -16
- package/dist/person-proof.d.ts +0 -2
- package/dist/phase-routing.d.ts +0 -401
- package/dist/plain-schema.d.ts +0 -23
- package/dist/plain-words.d.ts +0 -20
- package/dist/plan-auto.d.ts +0 -25
- package/dist/plan-login.d.ts +0 -14
- package/dist/plan.d.ts +0 -188
- package/dist/plane-review.d.ts +0 -54
- package/dist/planner-source.d.ts +0 -225
- package/dist/planner.d.ts +0 -76
- package/dist/policy-ui.d.ts +0 -22
- package/dist/policy.d.ts +0 -92
- package/dist/prepared-evidence.d.ts +0 -17
- package/dist/principal.d.ts +0 -42
- package/dist/probe.d.ts +0 -46
- package/dist/process-custody.d.ts +0 -17
- package/dist/process-liveness.d.ts +0 -13
- package/dist/process-tree.d.ts +0 -39
- package/dist/project-access.d.ts +0 -17
- package/dist/project-activity.d.ts +0 -20
- package/dist/project-admission.d.ts +0 -105
- package/dist/project-cli.d.ts +0 -19
- package/dist/project-concurrency.d.ts +0 -62
- package/dist/project-delete-ui.d.ts +0 -96
- package/dist/project-delete.d.ts +0 -59
- package/dist/project-demo.d.ts +0 -12
- package/dist/project-file.d.ts +0 -3
- package/dist/project-identity.d.ts +0 -52
- package/dist/project-knowledge.d.ts +0 -124
- package/dist/project-learning.d.ts +0 -120
- package/dist/project-memory.d.ts +0 -70
- package/dist/project-outcomes.d.ts +0 -119
- package/dist/project-sandbox.d.ts +0 -59
- package/dist/project-skills.d.ts +0 -139
- package/dist/project-tools.d.ts +0 -318
- package/dist/project.d.ts +0 -65
- package/dist/prompt.d.ts +0 -33
- package/dist/proof.d.ts +0 -457
- package/dist/proposal.d.ts +0 -70
- package/dist/protected-storage-look.d.ts +0 -1
- package/dist/protected-storage.d.ts +0 -98
- package/dist/provider-auth.d.ts +0 -143
- package/dist/provider-capture.d.ts +0 -34
- package/dist/provider-connection.d.ts +0 -23
- package/dist/provider-home.d.ts +0 -22
- package/dist/provider-limits.d.ts +0 -37
- package/dist/provider.d.ts +0 -369
- package/dist/public-access.d.ts +0 -52
- package/dist/public-check.d.ts +0 -37
- package/dist/publish.d.ts +0 -146
- package/dist/pull-request-flow.d.ts +0 -189
- package/dist/pull-request-schema.d.ts +0 -5
- package/dist/pull-request-ui.d.ts +0 -25
- package/dist/pulls.d.ts +0 -121
- package/dist/push.d.ts +0 -120
- package/dist/quality.d.ts +0 -8
- package/dist/quick-start.d.ts +0 -26
- package/dist/read-executor-worker.d.ts +0 -1
- package/dist/read-executor.d.ts +0 -66
- package/dist/read-only-request.d.ts +0 -4
- package/dist/recipe-ui.d.ts +0 -21
- package/dist/recipes.d.ts +0 -56
- package/dist/release-adapters.d.ts +0 -205
- package/dist/release-approval.d.ts +0 -180
- package/dist/release-authority.d.ts +0 -173
- package/dist/release-auto.d.ts +0 -184
- package/dist/release-card.d.ts +0 -156
- package/dist/release-carry.d.ts +0 -261
- package/dist/release-ci-hold.d.ts +0 -41
- package/dist/release-cli.d.ts +0 -34
- package/dist/release-coverage.d.ts +0 -16
- package/dist/release-gate.d.ts +0 -84
- package/dist/release-git.d.ts +0 -43
- package/dist/release-links.d.ts +0 -29
- package/dist/release-net.d.ts +0 -90
- package/dist/release-next.d.ts +0 -48
- package/dist/release-recheck.d.ts +0 -146
- package/dist/release-refusal.d.ts +0 -32
- package/dist/release-view.d.ts +0 -37
- package/dist/release.d.ts +0 -806
- package/dist/releases.d.ts +0 -94
- package/dist/remote-audit.d.ts +0 -79
- package/dist/remote-command.d.ts +0 -45
- package/dist/remote-destination.d.ts +0 -36
- package/dist/remote-exec.d.ts +0 -38
- package/dist/remote-main.d.ts +0 -111
- package/dist/remote-run.d.ts +0 -20
- package/dist/remote.d.ts +0 -67
- package/dist/render.d.ts +0 -64
- package/dist/reply-shape.d.ts +0 -106
- package/dist/reply-voice.d.ts +0 -23
- package/dist/report-summary.d.ts +0 -22
- package/dist/reported-usage.d.ts +0 -69
- package/dist/repos.d.ts +0 -67
- package/dist/repository-context-ui.d.ts +0 -3
- package/dist/repository-context.d.ts +0 -66
- package/dist/request-budget-ui.d.ts +0 -14
- package/dist/request-budget.d.ts +0 -127
- package/dist/restart-certification.d.ts +0 -133
- package/dist/result-actions.d.ts +0 -36
- package/dist/result-acts.d.ts +0 -112
- package/dist/result-completion.d.ts +0 -35
- package/dist/result-evidence-readers.d.ts +0 -42
- package/dist/result-follow-ups.d.ts +0 -118
- package/dist/result-review.d.ts +0 -220
- package/dist/result-shots.d.ts +0 -59
- package/dist/retention-ui.d.ts +0 -21
- package/dist/retention.d.ts +0 -77
- package/dist/review-context.d.ts +0 -245
- package/dist/review-evidence.d.ts +0 -33
- package/dist/review-switch.d.ts +0 -36
- package/dist/reviewer.d.ts +0 -99
- package/dist/route-change-check.d.ts +0 -4
- package/dist/route-change.d.ts +0 -22
- package/dist/route-hooks.d.ts +0 -54
- package/dist/route-results.d.ts +0 -75
- package/dist/route-rules.d.ts +0 -324
- package/dist/route-savings.d.ts +0 -136
- package/dist/route-store.d.ts +0 -300
- package/dist/run-closeout.d.ts +0 -89
- package/dist/run-efficiency.d.ts +0 -74
- package/dist/run-ending.d.ts +0 -21
- package/dist/runner-process.d.ts +0 -76
- package/dist/runner.d.ts +0 -324
- package/dist/sample-project.d.ts +0 -31
- package/dist/sandbox-launch.d.ts +0 -1
- package/dist/sandbox-runtime.d.ts +0 -128
- package/dist/scan.d.ts +0 -32
- package/dist/scope.d.ts +0 -1067
- package/dist/scout-browser-lock.d.ts +0 -69
- package/dist/scout-browser.d.ts +0 -107
- package/dist/scout-net.d.ts +0 -25
- package/dist/scout-report.d.ts +0 -23
- package/dist/scout.d.ts +0 -142
- package/dist/secret-mint.d.ts +0 -1
- package/dist/serve.d.ts +0 -11
- package/dist/server/agents-computers.d.ts +0 -9
- package/dist/server/agents-live-filed.d.ts +0 -17
- package/dist/server/agents-live-lead.d.ts +0 -47
- package/dist/server/agents-live-room.d.ts +0 -23
- package/dist/server/chat.d.ts +0 -39
- package/dist/server/chrome.d.ts +0 -328
- package/dist/server/edge-refusal.d.ts +0 -7
- package/dist/server/flows.d.ts +0 -8
- package/dist/server/guards.d.ts +0 -80
- package/dist/server/handler-context.d.ts +0 -22
- package/dist/server/handler-registry.d.ts +0 -23
- package/dist/server/home-asks.d.ts +0 -45
- package/dist/server/home.d.ts +0 -65
- package/dist/server/http.d.ts +0 -337
- package/dist/server/in-place.d.ts +0 -15
- package/dist/server/kit-standalone.d.ts +0 -22
- package/dist/server/kit.d.ts +0 -452
- package/dist/server/last-checks.d.ts +0 -17
- package/dist/server/live.d.ts +0 -74
- package/dist/server/merge-ready-view.d.ts +0 -10
- package/dist/server/mutation-key.d.ts +0 -70
- package/dist/server/pages-assets.d.ts +0 -11
- package/dist/server/pages.d.ts +0 -8
- package/dist/server/people-tokens.d.ts +0 -18
- package/dist/server/remote-hooks.d.ts +0 -29
- package/dist/server/remote.d.ts +0 -59
- package/dist/server/render-chat.d.ts +0 -439
- package/dist/server/render-code-map.d.ts +0 -30
- package/dist/server/render-flaky.d.ts +0 -20
- package/dist/server/render-follow-through.d.ts +0 -11
- package/dist/server/render-history.d.ts +0 -51
- package/dist/server/render-new-task.d.ts +0 -54
- package/dist/server/render-pages.d.ts +0 -236
- package/dist/server/render-people.d.ts +0 -102
- package/dist/server/render-projects.d.ts +0 -104
- package/dist/server/render-release.d.ts +0 -19
- package/dist/server/render-results.d.ts +0 -754
- package/dist/server/render-settings.d.ts +0 -93
- package/dist/server/render-tasks.d.ts +0 -774
- package/dist/server/request-context.d.ts +0 -78
- package/dist/server/retired-routes.d.ts +0 -17
- package/dist/server/route-policy.d.ts +0 -18
- package/dist/server/route-table.d.ts +0 -114
- package/dist/server/runtime.d.ts +0 -342
- package/dist/server/session.d.ts +0 -192
- package/dist/server/settings.d.ts +0 -76
- package/dist/server/signup-gate.d.ts +0 -9
- package/dist/server/sweeps.d.ts +0 -4
- package/dist/server/task-views.d.ts +0 -98
- package/dist/server/tasks.d.ts +0 -83
- package/dist/server/terminals.d.ts +0 -31
- package/dist/server/welcome.d.ts +0 -28
- package/dist/server-telemetry.d.ts +0 -87
- package/dist/settings-groups-ui.d.ts +0 -286
- package/dist/settings-kit-script.d.ts +0 -21
- package/dist/settings-kit.d.ts +0 -173
- package/dist/settings-proofs.d.ts +0 -64
- package/dist/setup-guide.d.ts +0 -39
- package/dist/shared-deps.d.ts +0 -115
- package/dist/sign-in-guard.d.ts +0 -70
- package/dist/skills-ui.d.ts +0 -12
- package/dist/skills.d.ts +0 -99
- package/dist/slack-api.d.ts +0 -59
- package/dist/slack-chat.d.ts +0 -37
- package/dist/slack-settings.d.ts +0 -4
- package/dist/slack-socket.d.ts +0 -12
- package/dist/slack.d.ts +0 -6
- package/dist/sleep.d.ts +0 -2
- package/dist/source-key.d.ts +0 -2
- package/dist/spend-report.d.ts +0 -87
- package/dist/spend-ui.d.ts +0 -80
- package/dist/spend.d.ts +0 -142
- package/dist/sqlite-runtime.d.ts +0 -3
- package/dist/sqlite-telemetry.d.ts +0 -9
- package/dist/sso-settings.d.ts +0 -33
- package/dist/sso-ui.d.ts +0 -26
- package/dist/staged-runtimes.d.ts +0 -54
- package/dist/steer-capabilities.d.ts +0 -32
- package/dist/steer-controls.d.ts +0 -14
- package/dist/steer-live.d.ts +0 -19
- package/dist/steer-mailbox.d.ts +0 -46
- package/dist/steer-status.d.ts +0 -25
- package/dist/stop-proof.d.ts +0 -67
- package/dist/storage-sweep-child.d.ts +0 -1
- package/dist/storage-sweep-process.d.ts +0 -55
- package/dist/storage-sweep-schedule.d.ts +0 -30
- package/dist/storage-sweep.d.ts +0 -77
- package/dist/storage-ui.d.ts +0 -23
- package/dist/storage.d.ts +0 -68
- package/dist/store-baseline.d.ts +0 -19
- package/dist/store.d.ts +0 -8613
- package/dist/stream-scope.d.ts +0 -18
- package/dist/structured-output.d.ts +0 -57
- package/dist/style-asset.d.ts +0 -7
- package/dist/subagent-admin.d.ts +0 -53
- package/dist/subagent-desk.d.ts +0 -59
- package/dist/subagent-memory.d.ts +0 -65
- package/dist/subagent-question.d.ts +0 -59
- package/dist/subagent-tools.d.ts +0 -125
- package/dist/subagent-week.d.ts +0 -68
- package/dist/subagent-work.d.ts +0 -76
- package/dist/subagents-ui.d.ts +0 -22
- package/dist/subagents.d.ts +0 -146
- package/dist/subscription-chat.d.ts +0 -54
- package/dist/summary.d.ts +0 -33
- package/dist/surface.d.ts +0 -49
- package/dist/sweep-gate.d.ts +0 -10
- package/dist/sweep-quarantine.d.ts +0 -29
- package/dist/sync.d.ts +0 -94
- package/dist/tailnet.d.ts +0 -11
- package/dist/tamper-scan.d.ts +0 -4
- package/dist/task-activity.d.ts +0 -27
- package/dist/task-aliases.d.ts +0 -5
- package/dist/task-archive.d.ts +0 -13
- package/dist/task-base.d.ts +0 -39
- package/dist/task-composer.d.ts +0 -14
- package/dist/task-control.d.ts +0 -159
- package/dist/task-live.d.ts +0 -8
- package/dist/task-moves.d.ts +0 -90
- package/dist/task-outcome-cli.d.ts +0 -30
- package/dist/task-review-brief.d.ts +0 -69
- package/dist/task-sizing.d.ts +0 -58
- package/dist/task-source.d.ts +0 -77
- package/dist/task-status.d.ts +0 -324
- package/dist/task-text.d.ts +0 -31
- package/dist/team-chat-authorization.d.ts +0 -11
- package/dist/team-contract.d.ts +0 -103
- package/dist/team-http.d.ts +0 -25
- package/dist/team-leads.d.ts +0 -85
- package/dist/team-runtime.d.ts +0 -25
- package/dist/team-ui.d.ts +0 -4
- package/dist/team-updates.d.ts +0 -8
- package/dist/teams-api.d.ts +0 -33
- package/dist/teams-chat.d.ts +0 -36
- package/dist/teams-settings.d.ts +0 -14
- package/dist/teams.d.ts +0 -24
- package/dist/teardown-git.d.ts +0 -164
- package/dist/telegram-flow.d.ts +0 -112
- package/dist/telegram-lead.d.ts +0 -151
- package/dist/telegram-live.d.ts +0 -116
- package/dist/telegram-progress.d.ts +0 -20
- package/dist/telegram-settings.d.ts +0 -9
- package/dist/telegram-status.d.ts +0 -83
- package/dist/telegram-team.d.ts +0 -68
- package/dist/telegram.d.ts +0 -271
- package/dist/templates.d.ts +0 -69
- package/dist/terminal-catalog.d.ts +0 -11
- package/dist/terminal-text.d.ts +0 -11
- package/dist/test-infra.d.ts +0 -34
- package/dist/test-temp.d.ts +0 -40
- package/dist/text-limits.d.ts +0 -297
- package/dist/tidy.d.ts +0 -130
- package/dist/tier-report.d.ts +0 -44
- package/dist/token-notices.d.ts +0 -11
- package/dist/tokens-cli.d.ts +0 -21
- package/dist/tool-broker.d.ts +0 -52
- package/dist/tool-launcher.d.ts +0 -1
- package/dist/tool-secret-store.d.ts +0 -167
- package/dist/toolroll-update-cli.d.ts +0 -15
- package/dist/toolroll-update-ui.d.ts +0 -47
- package/dist/toolroll-update.d.ts +0 -265
- package/dist/tools-ui.d.ts +0 -51
- package/dist/transitions-recipes.d.ts +0 -1
- package/dist/tree-proof.d.ts +0 -34
- package/dist/tui-contract.d.ts +0 -144
- package/dist/tui-source.d.ts +0 -14
- package/dist/typed-start.d.ts +0 -51
- package/dist/unfenced-test-support.d.ts +0 -2
- package/dist/up-processes.d.ts +0 -162
- package/dist/up-role-child.d.ts +0 -1
- package/dist/update-notice.d.ts +0 -5
- package/dist/vanished-retry.d.ts +0 -30
- package/dist/verification-evidence.d.ts +0 -68
- package/dist/version.d.ts +0 -2
- package/dist/wake-hub.d.ts +0 -72
- package/dist/webhook-limits.d.ts +0 -23
- package/dist/webhook-readers.d.ts +0 -56
- package/dist/webhook-signing.d.ts +0 -27
- package/dist/webhooks.d.ts +0 -84
- package/dist/welcome-facts.d.ts +0 -18
- package/dist/when-html.d.ts +0 -40
- package/dist/work-index.d.ts +0 -175
- package/dist/work-summary.d.ts +0 -61
- package/dist/workspace-motion.d.ts +0 -4
- package/dist/workspace-revision.d.ts +0 -48
- package/dist/workspace-ui.d.ts +0 -385
- package/dist/worktree-notices.d.ts +0 -13
- package/dist/worktree.d.ts +0 -365
- package/dist/writes-mode.d.ts +0 -9
- package/dist/youiyoux-intake.d.ts +0 -234
package/dist/telegram.js
CHANGED
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The Telegram bridge: decisions out, answers back, zero tokens spent.
|
|
3
|
-
*
|
|
4
|
-
* This is channel plumbing — no LLM is anywhere in this path, and everything
|
|
5
|
-
* it renders is deterministic. The security model, in one breath: a chat is
|
|
6
|
-
* not a person, so pairing is a local authenticated act that binds one
|
|
7
|
-
* private chat AND one immutable user id to one approver generation; a
|
|
8
|
-
* button is not a command, so callback_data carries only an opaque one-time
|
|
9
|
-
* token whose meaning lives in this database where a stolen bot token
|
|
10
|
-
* cannot read it; and an answer lands in the same transaction that proves
|
|
11
|
-
* the binding is still live and consumes the token — or it does not land.
|
|
12
|
-
*
|
|
13
|
-
* What a stolen bot token CAN do is stated rather than wished away: read
|
|
14
|
-
* the decision text this installation chose to send through Telegram,
|
|
15
|
-
* repaint the bot's keyboards with deceptive labels, and race our poll for
|
|
16
|
-
* updates. It cannot mint an action token, answer as the operator, or make
|
|
17
|
-
* `answered_by` say anything the pairing did not authorize. Rotating the
|
|
18
|
-
* BotFather token plus `bridge telegram unpair` is the recovery, and both
|
|
19
|
-
* are one command.
|
|
20
|
-
*/
|
|
21
1
|
import { recordTypedStart, typedStartCard, typedYes } from "./typed-start.js";
|
|
22
2
|
import { telegramCardMessage } from "./card-render.js";
|
|
23
3
|
import { flowDoneCard } from "./flow-result-card.js";
|
|
@@ -50,28 +30,17 @@ import { envValue } from "./names.js";
|
|
|
50
30
|
import { applyProposalTap, processTelegramConversations, phoneLinkButton, replyContextFor, telegramConversationRepos, telegramRequestId, tooLongText, whichTaskText, } from "./telegram-lead.js";
|
|
51
31
|
import { telegramAck } from "./telegram-live.js";
|
|
52
32
|
import { askOf, parseTelegramUpdate, pickOf, readTelegramButtonData, readTelegramUpdate, telegramButton } from "./contracts/telegram-callback.js";
|
|
53
|
-
/** Read the enrolled project list on demand. No callback means no task data,
|
|
54
|
-
* never an implicit all-database ceiling. Shared by pass and embedded follower. */
|
|
55
|
-
/** A delivery its person began watching after it was claimed: held or settled at the final fence (cockpit §3). */
|
|
56
33
|
const WATCHED_SINCE_CLAIM = "Held: its person is watching this agent now";
|
|
57
|
-
/** The environment name — and therefore the name the builder strips from agents (both names; see names.ts). */
|
|
58
34
|
export { TELEGRAM_TOKEN_ENV as TOKEN_ENV, TELEGRAM_TOKEN_ENVS as TOKEN_ENVS } from "./names.js";
|
|
59
|
-
/** BotFather's shape: numeric bot id, colon, secret. */
|
|
60
35
|
const TOKEN_SHAPE = /^(\d+):[A-Za-z0-9_-]{20,}$/;
|
|
61
36
|
export const PAIRING_TTL_MS = 10 * 60_000;
|
|
62
37
|
export const BRIDGE_LEASE_MS = 2 * 60_000;
|
|
63
38
|
export const DELIVERY_CLAIM_MS = 2 * 60_000;
|
|
64
|
-
/** Telegram's own message ceiling, with room for our part headers. */
|
|
65
39
|
const PART_CAP = 3_900;
|
|
66
|
-
/** Telegram's own limit on one message's text (Bot API sendMessage), less room for the line a tap adds under it. */
|
|
67
40
|
const TELEGRAM_TEXT_MAX = 4_096 - 300;
|
|
68
|
-
/** Pages of getUpdates one pass will read before reporting a backlog. */
|
|
69
41
|
const PAGE_BUDGET = 10;
|
|
70
|
-
// ---- pushed updates (v98) ----------------------------------------------------
|
|
71
|
-
/** Where Telegram pushes this bot's updates: under the public hooks address (Tailscale Funnel covers /hooks). */
|
|
72
42
|
export const TELEGRAM_HOOK_PATH = "/hooks/telegram";
|
|
73
43
|
const HOOK_SECRET_FILE = "telegram-hook-secret";
|
|
74
|
-
/** The secret Telegram sends with every push (its header), kept beside the database; made once when asked to. */
|
|
75
44
|
export function telegramHookSecret(dir, make = false) {
|
|
76
45
|
const file = join(dir, HOOK_SECRET_FILE);
|
|
77
46
|
try {
|
|
@@ -79,7 +48,7 @@ export function telegramHookSecret(dir, make = false) {
|
|
|
79
48
|
if (/^[A-Za-z0-9_-]{32,256}$/.test(saved))
|
|
80
49
|
return saved;
|
|
81
50
|
}
|
|
82
|
-
catch {
|
|
51
|
+
catch { }
|
|
83
52
|
if (!make)
|
|
84
53
|
return null;
|
|
85
54
|
const made = randomBytes(32).toString("base64url");
|
|
@@ -87,7 +56,6 @@ export function telegramHookSecret(dir, make = false) {
|
|
|
87
56
|
chmodSync(file, 0o600);
|
|
88
57
|
return made;
|
|
89
58
|
}
|
|
90
|
-
/** The address Telegram pushes to: the public hooks address (https only) and /hooks/telegram; null without one. */
|
|
91
59
|
export function telegramPushUrl(hooksBase) {
|
|
92
60
|
if (hooksBase === null)
|
|
93
61
|
return null;
|
|
@@ -99,14 +67,12 @@ export function telegramPushUrl(hooksBase) {
|
|
|
99
67
|
return null;
|
|
100
68
|
}
|
|
101
69
|
}
|
|
102
|
-
/** A push is Telegram's when its secret header matches ours (compared in constant time). */
|
|
103
70
|
export function pushedByTelegram(header, secret) {
|
|
104
71
|
if (secret === null || typeof header !== "string")
|
|
105
72
|
return false;
|
|
106
73
|
const given = Buffer.from(header), wanted = Buffer.from(secret);
|
|
107
74
|
return given.length === wanted.length && timingSafeEqual(given, wanted);
|
|
108
75
|
}
|
|
109
|
-
/** Keep one pushed update for the bridge. A schema-invalid JSON update is logged and acknowledged, never retried. */
|
|
110
76
|
export function keepPushedUpdate(store, botId, body, now) {
|
|
111
77
|
let raw;
|
|
112
78
|
try {
|
|
@@ -121,17 +87,10 @@ export function keepPushedUpdate(store, botId, body, now) {
|
|
|
121
87
|
return { ok: true, kept: false };
|
|
122
88
|
}
|
|
123
89
|
const kept = store.queueTelegramUpdate(botId, update.value.update_id, JSON.stringify(raw), now);
|
|
124
|
-
// The bridge in this process (if any) stops idling and drains it now; elsewhere it waits at most one cycle.
|
|
125
90
|
if (kept)
|
|
126
91
|
wakeTelegramBridge(botId);
|
|
127
92
|
return { ok: true, kept };
|
|
128
93
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Environment wins, the credential file beside the database otherwise.
|
|
131
|
-
* The file is how the CLI and the web settings card set it (0600, owner
|
|
132
|
-
* only); the env var is how people who already run keychain tooling keep
|
|
133
|
-
* it out of files entirely.
|
|
134
|
-
*/
|
|
135
94
|
export function loadBotToken(env, file) {
|
|
136
95
|
const fromEnv = envValue(env, "TELEGRAM_TOKEN");
|
|
137
96
|
if (fromEnv !== undefined && fromEnv.trim() !== "") {
|
|
@@ -148,7 +107,6 @@ export function loadBotToken(env, file) {
|
|
|
148
107
|
const parsed = TOKEN_SHAPE.exec(raw);
|
|
149
108
|
return parsed === null ? null : { token: raw, botId: parsed[1], source: "file" };
|
|
150
109
|
}
|
|
151
|
-
/** Write the credential file, owner-only. Refuses a string that is not a bot token. */
|
|
152
110
|
export function saveBotToken(file, token) {
|
|
153
111
|
if (!TOKEN_SHAPE.test(token.trim())) {
|
|
154
112
|
return {
|
|
@@ -157,8 +115,6 @@ export function saveBotToken(file, token) {
|
|
|
157
115
|
};
|
|
158
116
|
}
|
|
159
117
|
writeFileSync(file, `${token.trim()}\n`, { mode: 0o600 });
|
|
160
|
-
// writeFileSync applies the mode only on creation; an existing file keeps
|
|
161
|
-
// whatever it had, so the permission is asserted rather than assumed.
|
|
162
118
|
chmodSync(file, 0o600);
|
|
163
119
|
return { ok: true };
|
|
164
120
|
}
|
|
@@ -171,15 +127,12 @@ export function clearBotToken(file) {
|
|
|
171
127
|
return false;
|
|
172
128
|
}
|
|
173
129
|
}
|
|
174
|
-
/** The last four characters are enough to recognize a token without holding it. */
|
|
175
130
|
export function redactToken(token) {
|
|
176
131
|
return `…${token.slice(-4)}`;
|
|
177
132
|
}
|
|
178
|
-
/** Scrub a token out of any text on its way to a log, an error, or a row. */
|
|
179
133
|
export function scrub(text, token) {
|
|
180
134
|
return token === "" ? text : text.split(token).join(redactToken(token));
|
|
181
135
|
}
|
|
182
|
-
/** A release card's full terms (release-card.ts) as the document Telegram sends with it: text this process wrote. */
|
|
183
136
|
export const termsUpload = (file) => ({ field: "document", fileName: file.name, contentType: "text/plain", bytes: Buffer.from(file.text, "utf8") });
|
|
184
137
|
export function createTransport(token, timeoutMs = 30_000) {
|
|
185
138
|
return async (method, params, signal, upload, more) => {
|
|
@@ -193,17 +146,11 @@ export function createTransport(token, timeoutMs = 30_000) {
|
|
|
193
146
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
194
147
|
}
|
|
195
148
|
try {
|
|
196
|
-
// A JSON call stays byte for byte what it was. A multipart call lets
|
|
197
|
-
// fetch mint the boundary and the content-type: scalars ride as
|
|
198
|
-
// fields, objects (reply_parameters, reply_markup) as their JSON, and
|
|
199
|
-
// the verified bytes as one Blob under the file name given.
|
|
200
149
|
const request = upload === undefined
|
|
201
150
|
? { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(params), signal: controller.signal }
|
|
202
151
|
: { method: "POST", body: multipartOf(params, upload, ...(more ?? [])), signal: controller.signal };
|
|
203
152
|
const response = await fetch(`https://api.telegram.org/bot${token}/${method}`, request);
|
|
204
153
|
const body = (await response.json());
|
|
205
|
-
// A missing or malformed acknowledgement does not prove a send failed.
|
|
206
|
-
// Only Telegram's explicit rejection is a definite failed delivery.
|
|
207
154
|
if (body === null || typeof body !== "object" || Array.isArray(body) || typeof body.ok !== "boolean" || (body.ok && !response.ok)) {
|
|
208
155
|
return { ok: false, description: "Telegram returned an invalid acknowledgement", uncertain: true };
|
|
209
156
|
}
|
|
@@ -211,7 +158,6 @@ export function createTransport(token, timeoutMs = 30_000) {
|
|
|
211
158
|
ok: body.ok === true,
|
|
212
159
|
result: body.result,
|
|
213
160
|
...(body.parameters === undefined ? {} : { parameters: body.parameters }),
|
|
214
|
-
// Whatever Telegram said, the token must not be in what we keep.
|
|
215
161
|
...(typeof body.description !== "string" ? {} : { description: scrub(body.description, token) }),
|
|
216
162
|
};
|
|
217
163
|
}
|
|
@@ -228,15 +174,6 @@ export function createTransport(token, timeoutMs = 30_000) {
|
|
|
228
174
|
}
|
|
229
175
|
};
|
|
230
176
|
}
|
|
231
|
-
/**
|
|
232
|
-
* Why this process can't reach Telegram the way the service does, or null.
|
|
233
|
-
* A coding agent's sandbox fences the network: Codex says so in its own
|
|
234
|
-
* variables, and Claude Code's sandbox routes traffic through a proxy on
|
|
235
|
-
* this computer, which curl uses and Node's fetch does not — so sends fail
|
|
236
|
-
* "fetch failed" while curl works (Oct 2). Builds, checks and agents also
|
|
237
|
-
* get a database of their own, so none of them can hold the live bridge;
|
|
238
|
-
* this keeps an agent-launched `watch` or `bridge` from holding it either.
|
|
239
|
-
*/
|
|
240
177
|
export function networkFence(env) {
|
|
241
178
|
const set = (name) => (env[name] ?? "") !== "";
|
|
242
179
|
if (set("CODEX_SANDBOX_NETWORK_DISABLED") || set("CODEX_SANDBOX"))
|
|
@@ -255,11 +192,9 @@ export function networkFence(env) {
|
|
|
255
192
|
});
|
|
256
193
|
return agent && loopbackProxy ? "a Claude Code sandbox" : null;
|
|
257
194
|
}
|
|
258
|
-
/** The plain line a fenced process says instead of polling or sending. */
|
|
259
195
|
export function networkFenceLine(reason) {
|
|
260
196
|
return `Telegram: not connecting from here — this process runs inside ${reason}, which blocks its network. Replies and notifications go out from the Toolroll service.`;
|
|
261
197
|
}
|
|
262
|
-
/** fetch's own message ("fetch failed") with the reason under it, e.g. "fetch failed (EMFILE: too many open files)". */
|
|
263
198
|
export function transportError(error) {
|
|
264
199
|
const message = error instanceof Error ? error.message : String(error);
|
|
265
200
|
const cause = error instanceof Error ? error.cause : undefined;
|
|
@@ -270,7 +205,6 @@ export function transportError(error) {
|
|
|
270
205
|
const why = code !== null && !detail.includes(code) ? `${code}: ${detail}` : detail;
|
|
271
206
|
return why === "" || why === message ? message : `${message} (${why.slice(0, 160)})`;
|
|
272
207
|
}
|
|
273
|
-
/** The multipart body: every param a field (objects as JSON), the file last. Exported for the adapter's own test only. */
|
|
274
208
|
export function multipartOf(params, upload, ...more) {
|
|
275
209
|
const form = new FormData();
|
|
276
210
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -282,20 +216,12 @@ export function multipartOf(params, upload, ...more) {
|
|
|
282
216
|
form.append(one.field, new Blob([new Uint8Array(one.bytes)], { type: one.contentType }), one.fileName);
|
|
283
217
|
return form;
|
|
284
218
|
}
|
|
285
|
-
// ---- pairing ---------------------------------------------------------------
|
|
286
219
|
export function hashPairingCode(code) {
|
|
287
220
|
return createHash("sha256").update(code, "utf8").digest("hex");
|
|
288
221
|
}
|
|
289
|
-
/** 128 bits, hex — pasteable, and not guessable inside any code's lifetime. */
|
|
290
222
|
export function mintPairingCode() {
|
|
291
223
|
return randomBytes(16).toString("hex");
|
|
292
224
|
}
|
|
293
|
-
/**
|
|
294
|
-
* One bridge pass: claim and send what the outbox holds, then read one
|
|
295
|
-
* budgeted window of updates and apply each in its own transaction. Cron
|
|
296
|
-
* calls this; running it twice concurrently loses the lease race and does
|
|
297
|
-
* nothing, which is the design working.
|
|
298
|
-
*/
|
|
299
225
|
export async function bridgePass(store, options) {
|
|
300
226
|
const clock = options.clock ?? (() => new Date());
|
|
301
227
|
const owner = options.owner ?? randomBytes(8).toString("hex");
|
|
@@ -314,15 +240,11 @@ export async function bridgePass(store, options) {
|
|
|
314
240
|
await deliverOutbox(store, botId, transport, owner, clock, report, options.readProjects, options.canDeliver, options.conversation?.phoneOrigin, options.conversation?.evidenceRoot);
|
|
315
241
|
await deliverTeam(store, botId, transport, clock, report, options.readProjects, options.canDeliver, options.conversation?.phoneOrigin);
|
|
316
242
|
}
|
|
317
|
-
// v98: updates Telegram pushed to the console apply first; asking for more only works while it isn't pushing.
|
|
318
243
|
const pushed = await drainInbox({ store, botId, transport, clock, report, readProjects: options.readProjects, conversation: options.conversation, projects: null }, owner, lease.generation);
|
|
319
244
|
if (pushed > 0)
|
|
320
245
|
report.pushed = pushed;
|
|
321
246
|
await drainUpdates(store, botId, transport, owner, lease.generation, lease.cursor, clock, report, 0, undefined, options.readProjects, options.conversation);
|
|
322
247
|
if (options.conversation !== undefined && options.readProjects !== undefined) {
|
|
323
|
-
// The queued turns, outside any transaction. A model turn can outlive
|
|
324
|
-
// the poll lease, so the lease is renewed under the same owner while
|
|
325
|
-
// they run — the follower's own fenced renewal, reused.
|
|
326
248
|
const renew = setInterval(() => { store.acquireBridgeLease(botId, owner, BRIDGE_LEASE_MS, clock()); }, 30_000);
|
|
327
249
|
renew.unref?.();
|
|
328
250
|
try {
|
|
@@ -334,45 +256,15 @@ export async function bridgePass(store, options) {
|
|
|
334
256
|
}
|
|
335
257
|
}
|
|
336
258
|
finally {
|
|
337
|
-
// Handed back so the next cron firing is not told busy for the rest of
|
|
338
|
-
// this pass's TTL. A crash skips this and the lease expires instead —
|
|
339
|
-
// which is exactly what the TTL is for.
|
|
340
259
|
store.releaseBridgeLease(botId, owner, clock());
|
|
341
260
|
}
|
|
342
261
|
return { ok: true, report };
|
|
343
262
|
}
|
|
344
|
-
// ---- the follower ----------------------------------------------------------
|
|
345
|
-
/**
|
|
346
|
-
* The longest long poll the follower may ask for. `createTransport`'s HTTP
|
|
347
|
-
* timeout is 30s and must outlive the poll window, or the client would abort
|
|
348
|
-
* a poll Telegram is still honestly holding open.
|
|
349
|
-
*/
|
|
350
263
|
export const MAX_POLL_SECONDS = 25;
|
|
351
|
-
/** Reconnect backoff: starts here, doubles per consecutive failure, capped. */
|
|
352
264
|
const FOLLOW_BACKOFF_MS = [1_000, 2_000, 4_000, 8_000, 16_000, 30_000];
|
|
353
|
-
/** A cycle that returned instantly with nothing is padded to this — a scripted or broken server must not spin the loop hot. */
|
|
354
265
|
const FOLLOW_IDLE_FLOOR_MS = 1_000;
|
|
355
|
-
/** While a long poll is open, how often the follower looks for a message due to go out (live-wakeups.md). */
|
|
356
266
|
const DELIVERY_CHECK_MS = 1_000;
|
|
357
|
-
/** v98: how often the bridge checks Telegram still pushes to us; how long pushes may keep failing before it asks
|
|
358
|
-
* for updates itself; and how long it asks before trying pushes again. */
|
|
359
267
|
const PUSH_CHECK_MS = 60_000, PUSH_GIVE_UP_MS = 5 * 60_000, PUSH_RETRY_MS = 15 * 60_000, PUSH_IDLE_MS = 2_000;
|
|
360
|
-
/**
|
|
361
|
-
* The follower (§M4): one actor that holds the poll lease and stays on the
|
|
362
|
-
* wire, so an answer tapped on a phone reaches the store in seconds, not at
|
|
363
|
-
* the next cron firing. `bridge telegram --follow` runs it standalone;
|
|
364
|
-
* watch embeds the same actor — the poll lease guarantees only one is
|
|
365
|
-
* live, and a cron pass overlapping it simply loses the lease race.
|
|
366
|
-
*
|
|
367
|
-
* Each cycle re-acquires the lease under the same owner — that is the
|
|
368
|
-
* fenced renewal: same generation while held, and if the lease lapsed
|
|
369
|
-
* mid-poll (a stall longer than the TTL), the re-acquire takes the next
|
|
370
|
-
* generation and the cursor rides it, so nothing this follower stamped
|
|
371
|
-
* with the old generation can move state afterwards. Transport failures
|
|
372
|
-
* back off exponentially and are counted, not hidden; cancellation aborts
|
|
373
|
-
* the in-flight long poll instead of waiting it out.
|
|
374
|
-
*/
|
|
375
|
-
/** What Telegram lists when the person taps "/". */
|
|
376
268
|
export const TELEGRAM_COMMANDS = [
|
|
377
269
|
{ command: "tasks", description: "Pick a task to talk about" },
|
|
378
270
|
{ command: "status", description: "Recent work across your projects" },
|
|
@@ -394,7 +286,6 @@ export async function followBridge(store, options) {
|
|
|
394
286
|
}
|
|
395
287
|
signal.addEventListener("abort", finish, { once: true });
|
|
396
288
|
}));
|
|
397
|
-
/** The push idle: the ordinary wait, ended early when this process keeps a push for this bot. */
|
|
398
289
|
const idle = (ms) => {
|
|
399
290
|
if (bridgeWokenEarly.delete(botId))
|
|
400
291
|
return Promise.resolve();
|
|
@@ -412,14 +303,6 @@ export async function followBridge(store, options) {
|
|
|
412
303
|
const total = { cycles: 0, sent: 0, answered: 0, paired: 0, ignored: 0, problems: [] };
|
|
413
304
|
let commandsListed = false;
|
|
414
305
|
let failures = 0;
|
|
415
|
-
// Queued turns run BESIDE the poll, never inside a cycle: a long model
|
|
416
|
-
// turn must not stall the long poll, and each cycle's lease re-acquire is
|
|
417
|
-
// the renewal that keeps this the only live poller meanwhile. One
|
|
418
|
-
// processor at a time; its counts land on the totals when it finishes.
|
|
419
|
-
// It only persists the reply: the cycle sends it, in the same slot as
|
|
420
|
-
// notifications, so a reply never needs a connection of its own beside
|
|
421
|
-
// the long poll (Oct 2: replies failed "fetch failed" for minutes while
|
|
422
|
-
// notifications from the same process went out).
|
|
423
306
|
let inFlight = null;
|
|
424
307
|
const kick = () => {
|
|
425
308
|
if (inFlight !== null || options.conversation === undefined || options.readProjects === undefined)
|
|
@@ -438,12 +321,6 @@ export async function followBridge(store, options) {
|
|
|
438
321
|
options.onCycle?.(report);
|
|
439
322
|
});
|
|
440
323
|
};
|
|
441
|
-
// v98: pushed updates. With a public address, Telegram pushes each update to
|
|
442
|
-
// /hooks/telegram and the console keeps it for us; while it does, nobody can
|
|
443
|
-
// ask Telegram for this bot's updates — no other program can take them. Every
|
|
444
|
-
// minute we check the address is still ours (a framework starting up elsewhere
|
|
445
|
-
// may delete it); if pushes keep failing we ask for updates ourselves for a
|
|
446
|
-
// while, then try pushes again.
|
|
447
324
|
const push = options.push ?? null;
|
|
448
325
|
const pushCheckMs = options.pushCheckMs ?? PUSH_CHECK_MS;
|
|
449
326
|
let pushMode = push === null ? "poll" : "push";
|
|
@@ -495,7 +372,6 @@ export async function followBridge(store, options) {
|
|
|
495
372
|
return;
|
|
496
373
|
}
|
|
497
374
|
pushRegistered = true;
|
|
498
|
-
// Telegram couldn't deliver lately and updates are waiting: the address isn't reachable.
|
|
499
375
|
const failing = (hook.pending_update_count ?? 0) > 0 && typeof hook.last_error_date === "number" && now / 1000 - hook.last_error_date < 180;
|
|
500
376
|
if (!failing) {
|
|
501
377
|
pushFailingSince = null;
|
|
@@ -513,48 +389,34 @@ export async function followBridge(store, options) {
|
|
|
513
389
|
while (!signal.aborted) {
|
|
514
390
|
const lease = store.acquireBridgeLease(botId, owner, BRIDGE_LEASE_MS, clock());
|
|
515
391
|
if (!lease.ok) {
|
|
516
|
-
// A cron pass (or a rival follower) holds the poll. Not an error —
|
|
517
|
-
// wait our turn and try again.
|
|
518
392
|
await wait(FOLLOW_BACKOFF_MS[Math.min(failures, FOLLOW_BACKOFF_MS.length - 1)]);
|
|
519
393
|
failures = Math.min(failures + 1, FOLLOW_BACKOFF_MS.length - 1);
|
|
520
394
|
continue;
|
|
521
395
|
}
|
|
522
|
-
// The "/" menu in Telegram, once, by the poller that holds the lease:
|
|
523
|
-
// the picker first. Best effort; typed commands work either way.
|
|
524
396
|
if (!commandsListed && options.conversation !== undefined) {
|
|
525
397
|
commandsListed = true;
|
|
526
|
-
// Both carry the stop signal: a stop never waits on a slow Bot API for them.
|
|
527
398
|
try {
|
|
528
399
|
await transport("setMyCommands", { commands: TELEGRAM_COMMANDS }, signal);
|
|
529
400
|
}
|
|
530
|
-
catch {
|
|
531
|
-
// After an upgrade, a bot still called StandingOrders becomes Toolroll, once per start; any other name is kept.
|
|
401
|
+
catch { }
|
|
532
402
|
await nameTelegramBot(transport, "upgrade", signal);
|
|
533
403
|
}
|
|
534
404
|
const startedAt = Date.now();
|
|
535
405
|
const report = { sent: 0, answered: 0, paired: 0, ignored: 0, backlog: false, problems: [] };
|
|
536
406
|
await managePush(report);
|
|
537
|
-
// What this cycle's delivery below covers: anything newer may end the long poll early.
|
|
538
407
|
const deliveredThrough = newestNotificationIdOf(store);
|
|
539
408
|
if (options.deliver !== false) {
|
|
540
409
|
await deliverOutbox(store, botId, transport, owner, clock, report, options.readProjects, options.canDeliver, options.conversation?.phoneOrigin, options.conversation?.evidenceRoot);
|
|
541
410
|
await deliverTeam(store, botId, transport, clock, report, options.readProjects, options.canDeliver, options.conversation?.phoneOrigin);
|
|
542
411
|
}
|
|
543
|
-
// Replies the turn worker has planned go out here, like notifications.
|
|
544
412
|
if (options.conversation !== undefined && options.readProjects !== undefined) {
|
|
545
413
|
await processConversations(store, botId, transport, owner, clock, report, options.readProjects, options.conversation, signal, "replies");
|
|
546
414
|
}
|
|
547
|
-
// Pushed updates first (some may wait from before a fall back), then — only while
|
|
548
|
-
// Telegram isn't pushing — ask for more.
|
|
549
415
|
const pushed = await drainInbox({ store, botId, transport, clock, report, readProjects: options.readProjects, conversation: options.conversation, projects: null }, owner, lease.generation);
|
|
550
416
|
if (pushed > 0)
|
|
551
417
|
report.pushed = pushed;
|
|
552
418
|
if (!pushing()) {
|
|
553
|
-
// While a turn is running, poll briefly so its reply is sent within a second or two of being written.
|
|
554
419
|
const seconds = inFlight === null ? pollSeconds : 1;
|
|
555
|
-
// A DELIVERY WAKE (live-wakeups.md): while the long poll is open, a message written for this bot since this
|
|
556
|
-
// cycle began delivering ends the poll, once per cycle, so it goes out now instead of after the poll. Same
|
|
557
|
-
// owner, same generation, one connection; the cursor moves only past updates already processed.
|
|
558
420
|
const poll = new AbortController();
|
|
559
421
|
const endPoll = () => poll.abort();
|
|
560
422
|
if (signal.aborted)
|
|
@@ -588,7 +450,6 @@ export async function followBridge(store, options) {
|
|
|
588
450
|
clearInterval(checker);
|
|
589
451
|
signal.removeEventListener("abort", endPoll);
|
|
590
452
|
}
|
|
591
|
-
// A push address nobody here wants any more (the public address was removed): take it down, so asking works again.
|
|
592
453
|
if (report.webhookActive === true && push === null)
|
|
593
454
|
await transport("deleteWebhook", { drop_pending_updates: false }, signal);
|
|
594
455
|
}
|
|
@@ -613,7 +474,6 @@ export async function followBridge(store, options) {
|
|
|
613
474
|
options.onCycle?.(report);
|
|
614
475
|
}
|
|
615
476
|
if (report.problems.length > 0 && report.sent === 0 && report.answered === 0 && (report.chatAnswered ?? 0) === 0) {
|
|
616
|
-
// The wire is down. Back off; the counter resets on the first clean cycle.
|
|
617
477
|
if (!signal.aborted) {
|
|
618
478
|
await wait(FOLLOW_BACKOFF_MS[Math.min(failures, FOLLOW_BACKOFF_MS.length - 1)]);
|
|
619
479
|
}
|
|
@@ -621,25 +481,16 @@ export async function followBridge(store, options) {
|
|
|
621
481
|
continue;
|
|
622
482
|
}
|
|
623
483
|
failures = 0;
|
|
624
|
-
// A healthy cycle's wait IS the long poll. A cycle that came back
|
|
625
|
-
// instantly and empty (scripted transport, misbehaving server) gets
|
|
626
|
-
// padded so the loop cannot spin hot.
|
|
627
484
|
const took = Date.now() - startedAt;
|
|
628
|
-
// While Telegram pushes (v98) there is no long poll to wait on: a pushed update waits at most this long.
|
|
629
485
|
const floor = pushing() ? PUSH_IDLE_MS : FOLLOW_IDLE_FLOOR_MS;
|
|
630
|
-
// A delivery wake goes straight back round to send: no padding.
|
|
631
486
|
if (!signal.aborted && report.deliveryWake !== true && report.sent === 0 && report.answered === 0 && (report.chatAnswered ?? 0) === 0 && took < floor) {
|
|
632
|
-
// While Telegram pushes, a push kept by this process's hook ends the idle at once.
|
|
633
487
|
await (pushing() ? idle(floor - took) : wait(floor - took));
|
|
634
488
|
}
|
|
635
489
|
}
|
|
636
490
|
}
|
|
637
491
|
finally {
|
|
638
|
-
// A turn in flight finishes on its own bounds (the engine's wall clock);
|
|
639
|
-
// its reply is fenced on the claim and the channel like every other part.
|
|
640
492
|
if (inFlight !== null) {
|
|
641
493
|
await inFlight;
|
|
642
|
-
// Its reply was written for the cycle that will not come: send it now, while the lease is still ours.
|
|
643
494
|
if (options.conversation !== undefined && options.readProjects !== undefined) {
|
|
644
495
|
const report = { sent: 0, answered: 0, paired: 0, ignored: 0, backlog: false, problems: [] };
|
|
645
496
|
await processConversations(store, botId, transport, owner, clock, report, options.readProjects, options.conversation, undefined, "replies").catch(() => { });
|
|
@@ -652,7 +503,6 @@ export async function followBridge(store, options) {
|
|
|
652
503
|
}
|
|
653
504
|
return total;
|
|
654
505
|
}
|
|
655
|
-
/** The queued turns for one bot, counted onto the pass's report. */
|
|
656
506
|
async function processConversations(store, botId, transport, owner, clock, report, readProjects, conversation, signal, only) {
|
|
657
507
|
const chat = { answered: 0, refused: 0, problems: [] };
|
|
658
508
|
await processTelegramConversations({ store, botId, transport, owner, clock, readProjects, options: conversation, report: chat, ...(signal === undefined ? {} : { signal }), ...(only === undefined ? {} : { only }) });
|
|
@@ -662,7 +512,6 @@ async function processConversations(store, botId, transport, owner, clock, repor
|
|
|
662
512
|
report.chatRefused = (report.chatRefused ?? 0) + chat.refused;
|
|
663
513
|
report.problems.push(...chat.problems);
|
|
664
514
|
}
|
|
665
|
-
/** A fact's own next-action button row under the trusted origin read now; none without one. */
|
|
666
515
|
function factButton(phoneOrigin, link) {
|
|
667
516
|
try {
|
|
668
517
|
return phoneLinkButton(phoneOrigin?.() ?? null, { label: factLinkLabel(link), path: link });
|
|
@@ -671,7 +520,6 @@ function factButton(phoneOrigin, link) {
|
|
|
671
520
|
return null;
|
|
672
521
|
}
|
|
673
522
|
}
|
|
674
|
-
/** A view's buttons on one row: its link, then any others ([Merge] [Look first]); none without a trusted origin. */
|
|
675
523
|
function viewKeyboard(phoneOrigin, view) {
|
|
676
524
|
try {
|
|
677
525
|
const origin = phoneOrigin?.() ?? null;
|
|
@@ -682,11 +530,9 @@ function viewKeyboard(phoneOrigin, view) {
|
|
|
682
530
|
return [];
|
|
683
531
|
}
|
|
684
532
|
}
|
|
685
|
-
/** Where a paired person taps: their binding and private chat. */
|
|
686
533
|
function decideSeat(binding) {
|
|
687
534
|
return { channel: "telegram", binding: binding.id, chat: binding.chatId, approver: binding.approver, generation: binding.approverGeneration };
|
|
688
535
|
}
|
|
689
|
-
/** Decide buttons as Telegram draws them: an act's opaque token, or a link under the trusted origin read now (none without one). */
|
|
690
536
|
function decideKeyboard(phoneOrigin, rows) {
|
|
691
537
|
let origin = null;
|
|
692
538
|
try {
|
|
@@ -698,14 +544,12 @@ function decideKeyboard(phoneOrigin, rows) {
|
|
|
698
544
|
return rows.map(row => row.flatMap((one) => "token" in one ? [telegramButton(one.label, one.token)]
|
|
699
545
|
: phoneLinkButton(origin, one.link) ?? [])).filter(row => row.length > 0);
|
|
700
546
|
}
|
|
701
|
-
/** What this card may act on in the chat now, for this person under their current ceiling; null keeps its link. */
|
|
702
547
|
function decideOfferFor(store, binding, target, projects, now, root) {
|
|
703
548
|
if (target === null || target === undefined)
|
|
704
549
|
return null;
|
|
705
550
|
const principal = verifyApproverStanding(store, binding.approver, binding.approverGeneration, telegramConversationRepos(store, binding.approver, projects));
|
|
706
551
|
return principal.ok ? decideOffer(store, target, principal.who, now, "telegram", root) : null;
|
|
707
552
|
}
|
|
708
|
-
/** Team-conversation traffic to the chats that follow one — after the outbox, under the same delivery switch, never a problem to raise when nothing follows anything. */
|
|
709
553
|
async function deliverTeam(store, botId, transport, clock, report, readProjects, canDeliver, phoneOrigin) {
|
|
710
554
|
if (store.listTelegramTeamChats(botId).length === 0)
|
|
711
555
|
return;
|
|
@@ -732,38 +576,27 @@ async function deliverTeam(store, botId, transport, clock, report, readProjects,
|
|
|
732
576
|
async function deliverOutbox(store, botId, transport, owner, clock, report, readProjects, canDeliver, phoneOrigin, evidenceRoot) {
|
|
733
577
|
const bindings = store.liveTelegramBindings(botId);
|
|
734
578
|
if (bindings.length === 0) {
|
|
735
|
-
// Routine progress facts are not a problem to fix: a first pairing
|
|
736
|
-
// starts from now and settles them as history. Anything else pending
|
|
737
|
-
// is named, once per pass, as before.
|
|
738
579
|
if (store.listNotifications("pending").some(row => !isLifecycleNotification(row) && !isShotsKind(row.kind)))
|
|
739
580
|
report.problems.push("outbox rows are pending but no chat is paired — `toolroll bridge telegram pair`");
|
|
740
581
|
return;
|
|
741
582
|
}
|
|
742
583
|
const digest = store.telegramDigest();
|
|
743
584
|
const now = clock();
|
|
744
|
-
// An evening digest someone asked for is due: record it as their own notification, delivered below.
|
|
745
585
|
try {
|
|
746
586
|
enqueueEveningDigests(store, now, evidenceRoot);
|
|
747
587
|
}
|
|
748
588
|
catch {
|
|
749
589
|
report.problems.push("evening digest could not be prepared");
|
|
750
590
|
}
|
|
751
|
-
// A lead claim that went two hours quiet repaints its owner's card back to the task's real state.
|
|
752
591
|
enqueueLeadLapses(store, now);
|
|
753
592
|
const digestDue = digest.everyMs === null || digest.lastSentAt === null || now.getTime() >= new Date(digest.lastSentAt).getTime() + digest.everyMs;
|
|
754
|
-
// Every paired person is a destination of their own: each binding claims
|
|
755
|
-
// and settles its own rows under its own ceiling, in turn.
|
|
756
593
|
for (const binding of bindings)
|
|
757
594
|
await deliverOutboxTo(store, botId, binding, transport, owner, clock, report, digest, digestDue, readProjects, canDeliver, phoneOrigin, evidenceRoot);
|
|
758
595
|
}
|
|
759
596
|
async function deliverOutboxTo(store, botId, binding, transport, owner, clock, report, digest, digestDue, readProjects, canDeliver, phoneOrigin, evidenceRoot) {
|
|
760
597
|
const now = clock();
|
|
761
|
-
// Only when I'm needed (the default): a task's facts edit its one card and only what needs this person makes a
|
|
762
|
-
// new message, so the installation's routine-fact cadence does not apply. Every step keeps today's behaviour.
|
|
763
598
|
const quiet = store.notificationPreference(binding.approver).mode === "quiet";
|
|
764
599
|
const claimed = store.claimTelegramDeliveries(binding, owner, DELIVERY_CLAIM_MS, now, quiet || digestDue ? "all" : "urgent");
|
|
765
|
-
// Preserve ID order, flushing earlier routine facts before an urgent task
|
|
766
|
-
// update. An earlier failed/retrying row also fences later rows for that task.
|
|
767
600
|
const groups = [];
|
|
768
601
|
for (const row of claimed) {
|
|
769
602
|
const last = groups[groups.length - 1];
|
|
@@ -794,15 +627,10 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
794
627
|
return null;
|
|
795
628
|
};
|
|
796
629
|
const retryAt = () => [store.telegramRetryAt(botId), new Date(clock().getTime() + 1_000).toISOString()].sort().at(-1);
|
|
797
|
-
// Partition before anything is sent: a row without live authority is
|
|
798
|
-
// retained with its reason, and the rows that ARE eligible go out now
|
|
799
|
-
// rather than waiting on it. Walking in ID order with the eligible set
|
|
800
|
-
// as the batch keeps a task's later facts behind its blocked earlier one.
|
|
801
630
|
const rows = [];
|
|
802
631
|
const blocked = [];
|
|
803
632
|
const preflight = (await readAccess()) ?? channelProblem();
|
|
804
633
|
for (const row of group) {
|
|
805
|
-
// Watched since the claim (cockpit §3): held for later or settled quietly, never sent now, never a failure.
|
|
806
634
|
if (preflight === null && store.telegramWatchFence(row, binding, owner, clock()) !== "send")
|
|
807
635
|
continue;
|
|
808
636
|
const problem = preflight ?? store.telegramDeliveryProblem(row, binding, owner, projects, clock(), [...rows.map(one => one.id), row.id]);
|
|
@@ -820,8 +648,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
820
648
|
if (rows.length === 0)
|
|
821
649
|
continue;
|
|
822
650
|
const ids = rows.map(row => row.id);
|
|
823
|
-
// Once any part of this delivery has reached Telegram it is committed: a watch opening between its parts never
|
|
824
|
-
// releases it (that would send the parts already accepted again). Every transport call below goes through `wire`.
|
|
825
651
|
let sentAny = false;
|
|
826
652
|
const wire = async (...args) => {
|
|
827
653
|
const answer = await transport(...args);
|
|
@@ -829,7 +655,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
829
655
|
sentAny = true;
|
|
830
656
|
return answer;
|
|
831
657
|
};
|
|
832
|
-
/** A release card's terms file (release-card.ts), sent before the card itself. */
|
|
833
658
|
const fileSender = async (file) => {
|
|
834
659
|
try {
|
|
835
660
|
return (await wire("sendDocument", { chat_id: binding.chatId, caption: "Every term of this release" }, undefined, termsUpload(file))).ok === true;
|
|
@@ -838,8 +663,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
838
663
|
return false;
|
|
839
664
|
}
|
|
840
665
|
};
|
|
841
|
-
// `beforeSend`: the fence before a transport call also asks the watch — until something of this delivery went out;
|
|
842
|
-
// the one after a confirmed send never does, so a message Telegram accepted is recorded as delivered.
|
|
843
666
|
const fence = (beforeSend = !sentAny) => {
|
|
844
667
|
const channel = channelProblem();
|
|
845
668
|
if (channel !== null)
|
|
@@ -848,15 +671,12 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
848
671
|
const problem = store.telegramDeliveryProblem(row, binding, owner, projects, clock(), ids);
|
|
849
672
|
if (problem !== null)
|
|
850
673
|
return problem;
|
|
851
|
-
// The watch, at the last moment before the network: a held row's claim is released (its finalize is then a
|
|
852
|
-
// no-op), so nothing is spent on it.
|
|
853
674
|
if (beforeSend && store.telegramWatchFence(row, binding, owner, clock()) !== "send")
|
|
854
675
|
return WATCHED_SINCE_CLAIM;
|
|
855
676
|
}
|
|
856
677
|
return null;
|
|
857
678
|
};
|
|
858
679
|
const sender = async (text, keyboard, messageRows = rows, entities, silent) => {
|
|
859
|
-
// Finish every await before the synchronous fence and transport call.
|
|
860
680
|
const problem = (await readAccess()) ?? fence();
|
|
861
681
|
if (problem !== null)
|
|
862
682
|
return { ok: false, error: problem };
|
|
@@ -870,7 +690,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
870
690
|
return { ok: false, error: "Telegram returned no confirmed message identity" };
|
|
871
691
|
for (const row of messageRows)
|
|
872
692
|
store.recordTelegramMessage(row, binding, sent.messageId, clock());
|
|
873
|
-
// Keep the old message's history but never acknowledge a replaced pairing.
|
|
874
693
|
const after = (await readAccess()) ?? fence(false);
|
|
875
694
|
return after === null ? sent : { ok: false, error: after };
|
|
876
695
|
};
|
|
@@ -891,13 +710,10 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
891
710
|
const sent = await sender(`A screenshot for result #${row.run} could not be sent: ${verified.problem}. Inspect the result before accepting.`);
|
|
892
711
|
return sent.ok ? { ok: true, receipt: receiptFor(botId, binding.chatId, sent.messageId) } : sent;
|
|
893
712
|
}
|
|
894
|
-
// Already sent with its result (result-shots.ts): the same screenshot is not sent again.
|
|
895
713
|
const artifact = Number(match[2]);
|
|
896
714
|
if (store.resultShotsSent(row.destination, row.run).has(artifact))
|
|
897
715
|
return { ok: true, receipt: "skipped:screenshot-sent" };
|
|
898
|
-
// Marked while it goes, so the screenshots sent with the result skip it; any failure unmarks it for the retry.
|
|
899
716
|
store.markResultShotsSent(row.destination, row.run, [artifact], clock());
|
|
900
|
-
// No await between the access fence, file validation and upload.
|
|
901
717
|
let answer;
|
|
902
718
|
try {
|
|
903
719
|
answer = await wire("sendDocument", { chat_id: binding.chatId, caption: `${resultTaskLabel(row.taskId)} · result #${row.run} · screenshot for acceptance` }, undefined, {
|
|
@@ -919,8 +735,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
919
735
|
const after = (await readAccess()) ?? fence(false);
|
|
920
736
|
return after === null ? { ok: true, receipt: receiptFor(botId, binding.chatId, String(messageId)) } : { ok: false, error: after };
|
|
921
737
|
};
|
|
922
|
-
/** Screenshots with a result (result-shots.ts): photos, several as one album, re-proved before every call. Each is
|
|
923
|
-
* marked sent before it goes, so an unconfirmed send is never repeated; only Telegram's outright refusal unmarks it. */
|
|
924
738
|
const shotsSender = async (row) => {
|
|
925
739
|
let receipt = null;
|
|
926
740
|
for (let call = 0; call <= 4; call++) {
|
|
@@ -934,7 +748,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
934
748
|
const sent = await sender(plan.text, undefined, [row]);
|
|
935
749
|
return sent.ok ? { ok: true, receipt: receiptFor(botId, binding.chatId, sent.messageId) } : sent;
|
|
936
750
|
}
|
|
937
|
-
// No await between the access fence, the files' verification and the upload.
|
|
938
751
|
const photos = plan.shots.filter(one => one.photo);
|
|
939
752
|
const batch = photos.length >= 2 ? photos : [plan.shots[0]];
|
|
940
753
|
const upload = (one, field) => ({ field, fileName: one.fileName, contentType: one.format === "png" ? "image/png" : "image/jpeg", bytes: one.bytes });
|
|
@@ -959,7 +772,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
959
772
|
}
|
|
960
773
|
const messages = (Array.isArray(answer.result) ? answer.result : [answer.result])
|
|
961
774
|
.map(one => one?.message_id).filter((one) => Number.isSafeInteger(one));
|
|
962
|
-
// An unconfirmed send stays marked: it may have arrived, so it is never sent again.
|
|
963
775
|
if (!answer.ok || messages.length === 0)
|
|
964
776
|
report.problems.push(`notification ${row.id}: Telegram did not confirm the screenshots; they are not sent again`);
|
|
965
777
|
for (const one of messages)
|
|
@@ -986,7 +798,7 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
986
798
|
try {
|
|
987
799
|
button = phoneLinkButton(phoneOrigin?.() ?? null, card.link);
|
|
988
800
|
}
|
|
989
|
-
catch {
|
|
801
|
+
catch { }
|
|
990
802
|
const keyboard = button === null ? [] : [button];
|
|
991
803
|
if (messageId === null)
|
|
992
804
|
return sender(card.text, keyboard, rows, card.entities);
|
|
@@ -994,8 +806,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
994
806
|
if (!edited.ok) {
|
|
995
807
|
if (edited.retryAfter !== undefined)
|
|
996
808
|
store.deferTelegram(botId, new Date(clock().getTime() + edited.retryAfter * 1000).toISOString());
|
|
997
|
-
// Only Telegram's definitive missing/uneditable response permits a
|
|
998
|
-
// replacement. Timeouts, rate limits and uncertain edits retry in place.
|
|
999
809
|
if (!onlyExisting && edited.replace)
|
|
1000
810
|
return sender(card.text, keyboard, rows, card.entities);
|
|
1001
811
|
return edited;
|
|
@@ -1005,7 +815,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1005
815
|
const after = (await readAccess()) ?? fence(false);
|
|
1006
816
|
return after === null ? edited : { ok: false, error: after };
|
|
1007
817
|
};
|
|
1008
|
-
/** Quiet mode: the task's one card (or its group's), created silently and then only ever edited. */
|
|
1009
818
|
const updateCard = async (fact) => {
|
|
1010
819
|
const problem = (await readAccess()) ?? fence();
|
|
1011
820
|
if (problem !== null)
|
|
@@ -1018,7 +827,7 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1018
827
|
try {
|
|
1019
828
|
button = phoneLinkButton(phoneOrigin?.() ?? null, view.link);
|
|
1020
829
|
}
|
|
1021
|
-
catch {
|
|
830
|
+
catch { }
|
|
1022
831
|
const keyboard = button === null ? [] : [button];
|
|
1023
832
|
const shown = createHash("sha256").update(JSON.stringify([view.text, view.entities, keyboard])).digest("hex");
|
|
1024
833
|
if (card.message !== null && card.digest === shown) {
|
|
@@ -1035,12 +844,10 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1035
844
|
}
|
|
1036
845
|
if (edited.retryAfter !== undefined)
|
|
1037
846
|
store.deferTelegram(botId, new Date(clock().getTime() + edited.retryAfter * 1000).toISOString());
|
|
1038
|
-
// Only Telegram's definitive missing/uneditable answer permits a replacement card.
|
|
1039
847
|
if (!edited.replace)
|
|
1040
848
|
return edited;
|
|
1041
849
|
}
|
|
1042
850
|
const sent = await sender(view.text, keyboard, [fact], view.entities, true);
|
|
1043
|
-
// A card that reached the chat is kept even if a later fence refused the acknowledgement: never a second card.
|
|
1044
851
|
const placed = sent.ok ? sent.messageId : store.telegramMessageOf(fact.id, fact.destination);
|
|
1045
852
|
if (placed !== null)
|
|
1046
853
|
store.setChatCardMessage(card.id, placed, shown);
|
|
@@ -1054,9 +861,7 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1054
861
|
return { ok: true, receipt: receiptFor(botId, binding.chatId, card.messageId) };
|
|
1055
862
|
const readyRun = fact.kind === "run-finished" ? store.telegramProgressRun(fact) : null;
|
|
1056
863
|
if (readyRun !== null || (fact.kind !== "run-finished" && joinsBatch(fact))) {
|
|
1057
|
-
// Any update for this person joins their open batch: one message, edited in place while it grows.
|
|
1058
864
|
const batch = store.chatBatchFor(fact.destination, fact.taskRef, readyRun?.id ?? store.telegramProgressRun(fact)?.id ?? null, clock(), BATCH_MS, readyRun !== null ? null : fact.id);
|
|
1059
|
-
// A lone update that is not finished work goes out exactly as it always has; a later one edits it in place.
|
|
1060
865
|
const lone = batch.items.length === 1 && batch.items[0].notification !== null;
|
|
1061
866
|
if (batch.message === null && lone) {
|
|
1062
867
|
const sent = await deliverOne(store, botId, binding, sender, fact, clock, phoneOrigin, evidenceRoot, projects, fileSender);
|
|
@@ -1066,26 +871,18 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1066
871
|
return sent;
|
|
1067
872
|
}
|
|
1068
873
|
const view = finishedView(store, batch, clock(), evidenceRoot, binding.approver);
|
|
1069
|
-
// Nothing in the batch reads as a line. Finished work left out on purpose (a release check, a replaced task,
|
|
1070
|
-
// the reader's own completion) stays quiet; any other update that needs this person still reaches them alone.
|
|
1071
874
|
if (view === null) {
|
|
1072
875
|
if (readyRun !== null)
|
|
1073
876
|
return { ok: true, receipt: receiptFor(botId, binding.chatId, card.messageId) };
|
|
1074
877
|
return deliverOne(store, botId, binding, sender, fact, clock, phoneOrigin, evidenceRoot, projects, fileSender);
|
|
1075
878
|
}
|
|
1076
|
-
// A message about one result, plan, failure or pull request acts in place (chat-decide.ts); its buttons are
|
|
1077
|
-
// minted only when what it offers changed, or its own were spent (Not now), so an unchanged repaint keeps the
|
|
1078
|
-
// ones already in the chat.
|
|
1079
879
|
const offer = decideOfferFor(store, binding, view.target, projects, clock(), evidenceRoot);
|
|
1080
880
|
const text = offer?.text ?? view.text;
|
|
1081
|
-
// While a lone update is still the only item, its edit keeps that update's own action button.
|
|
1082
881
|
const own = offer === null && lone && fact.link !== null ? factButton(phoneOrigin, fact.link) : null;
|
|
1083
882
|
let keyboard = own !== null ? [own] : viewKeyboard(phoneOrigin, view);
|
|
1084
883
|
const shown = createHash("sha256").update(JSON.stringify(offer === null ? [view.text, keyboard] : [text, offerFingerprint(offer)])).digest("hex");
|
|
1085
884
|
let tokens = [];
|
|
1086
|
-
// A release card's terms go as a file first, on this road as on a lone delivery: no file accepted, no buttons.
|
|
1087
885
|
const termsFirst = async () => offer?.file === undefined || (fileSender !== undefined && await fileSender(offer.file)) ? null : { ok: false, error: "The release's terms file wasn't accepted" };
|
|
1088
|
-
// Minted unplaced: they ride a message only once it shows them, and until then the old card's buttons work.
|
|
1089
886
|
const mintFor = () => {
|
|
1090
887
|
if (offer === null)
|
|
1091
888
|
return;
|
|
@@ -1107,7 +904,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1107
904
|
mintFor();
|
|
1108
905
|
const edited = await editProgress(wire, binding.chatId, batch.message, text, keyboard);
|
|
1109
906
|
if (edited.ok) {
|
|
1110
|
-
// The repaint landed: the old words' buttons retire and the new ones ride the message.
|
|
1111
907
|
retireDecideTokens(store, "telegram", binding.chatId, batch.message, clock());
|
|
1112
908
|
placeDecideTokens(store, tokens, batch.message);
|
|
1113
909
|
store.setChatBatchMessage(batch.id, batch.message, shown);
|
|
@@ -1115,15 +911,12 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1115
911
|
const after = (await readAccess()) ?? fence(false);
|
|
1116
912
|
return after === null ? { ok: true, receipt: receiptFor(botId, binding.chatId, batch.message) } : { ok: false, error: after };
|
|
1117
913
|
}
|
|
1118
|
-
// A failed edit: the buttons it carried never act, and the card's old ones keep working.
|
|
1119
914
|
dropDecideTokens(store, tokens, clock());
|
|
1120
915
|
if (edited.retryAfter !== undefined)
|
|
1121
916
|
store.deferTelegram(botId, new Date(clock().getTime() + edited.retryAfter * 1000).toISOString());
|
|
1122
|
-
// Only Telegram's definitive missing/uneditable answer permits a new message.
|
|
1123
917
|
if (!edited.replace)
|
|
1124
918
|
return edited;
|
|
1125
919
|
}
|
|
1126
|
-
// A new message: its buttons are minted for it, never the ones a failed edit was given.
|
|
1127
920
|
if (batch.message === null) {
|
|
1128
921
|
const terms = await termsFirst();
|
|
1129
922
|
if (terms !== null)
|
|
@@ -1135,7 +928,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1135
928
|
if (placed !== null)
|
|
1136
929
|
store.setChatBatchMessage(batch.id, placed, shown);
|
|
1137
930
|
if (sent.ok && sent.messageId !== null) {
|
|
1138
|
-
// The new card replaces the old one: only its buttons act.
|
|
1139
931
|
if (batch.message !== null)
|
|
1140
932
|
retireDecideTokens(store, "telegram", binding.chatId, batch.message, clock());
|
|
1141
933
|
placeDecideTokens(store, tokens, sent.messageId);
|
|
@@ -1146,7 +938,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1146
938
|
}
|
|
1147
939
|
return fact.kind === "acceptance-evidence" ? imageSender(fact) : deliverOne(store, botId, binding, sender, fact, clock, phoneOrigin, evidenceRoot, projects, fileSender);
|
|
1148
940
|
};
|
|
1149
|
-
/** The lead's words (lead-voice.ts): one message; a later say within two minutes edits it in place. */
|
|
1150
941
|
const leadSayOutcome = async (fact) => {
|
|
1151
942
|
const words = leadSayText(store, fact, binding.approver);
|
|
1152
943
|
const button = fact.link === null ? null : factButton(phoneOrigin, fact.link);
|
|
@@ -1164,7 +955,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1164
955
|
}
|
|
1165
956
|
if (edited.retryAfter !== undefined)
|
|
1166
957
|
store.deferTelegram(botId, new Date(clock().getTime() + edited.retryAfter * 1000).toISOString());
|
|
1167
|
-
// Only Telegram's definitive missing/uneditable answer permits a new message.
|
|
1168
958
|
if (!edited.replace)
|
|
1169
959
|
return edited;
|
|
1170
960
|
}
|
|
@@ -1179,8 +969,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1179
969
|
else if (quiet && rows.length === 1 && isTaskFact(row))
|
|
1180
970
|
outcome = await quietOutcome(row);
|
|
1181
971
|
else {
|
|
1182
|
-
// A failure or decision still gets its own alert. Refresh an existing
|
|
1183
|
-
// card first so it does not keep saying the build is running.
|
|
1184
972
|
if (!progress && progressRun !== null)
|
|
1185
973
|
await updateProgress(true);
|
|
1186
974
|
const updated = progress ? await updateProgress(false) : null;
|
|
@@ -1191,7 +979,6 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1191
979
|
}
|
|
1192
980
|
const finalProblem = outcome.ok ? (await readAccess()) ?? fence(false) : null;
|
|
1193
981
|
const settled = finalProblem === null ? outcome : { ok: false, error: finalProblem };
|
|
1194
|
-
// A skipped screenshot settles its row but nothing reached the phone.
|
|
1195
982
|
const skipped = settled.ok && settled.receipt !== null && settled.receipt.startsWith("skipped:");
|
|
1196
983
|
const finalized = store.transact(() => {
|
|
1197
984
|
let count = 0;
|
|
@@ -1214,18 +1001,12 @@ async function deliverOutboxTo(store, botId, binding, transport, owner, clock, r
|
|
|
1214
1001
|
report.problems.push("Telegram delivery claim expired before acknowledgement; retry may duplicate a message");
|
|
1215
1002
|
}
|
|
1216
1003
|
}
|
|
1217
|
-
/** What pages singly whatever the cadence: a decision, or an attention-class fact. */
|
|
1218
1004
|
function isUrgent(notification) {
|
|
1219
1005
|
return /^decision:\d+$/.test(notification.dedupeKey) || notification.pushClass === "attention" || notification.kind === "acceptance-evidence" || notification.kind === "acceptance-ready";
|
|
1220
1006
|
}
|
|
1221
|
-
/** A routine fact the digest may carry. Screenshots are never urgent (they follow their result message, which the
|
|
1222
|
-
* task's order fence holds them behind) and never a digest line: they go as their own photos once it has gone. */
|
|
1223
1007
|
function joinsDigest(notification) {
|
|
1224
1008
|
return !isUrgent(notification) && !isShotsKind(notification.kind);
|
|
1225
1009
|
}
|
|
1226
|
-
/** The digest text: a header with the count and the window, then one
|
|
1227
|
-
* fact per entry — its subject, then its body's first line, indented.
|
|
1228
|
-
* Plain text, no buttons: nothing in a digest is tappable. */
|
|
1229
1010
|
export function digestText(rows, since, now, titleOf) {
|
|
1230
1011
|
const window = since === null ? "" : ` since ${since.slice(0, 16).replace("T", " ")}`;
|
|
1231
1012
|
const lines = [`digest — ${rows.length} routine fact(s)${window} (as of ${now.toISOString().slice(0, 16).replace("T", " ")})`, ""];
|
|
@@ -1244,8 +1025,6 @@ function digestEntry(row, titleOf) {
|
|
|
1244
1025
|
}
|
|
1245
1026
|
async function deliverDigest(botId, binding, sender, rows, since, clock, titleOf) {
|
|
1246
1027
|
const text = digestText(rows, since, clock(), titleOf);
|
|
1247
|
-
// Track the exact rows represented by each text part. A split row may bind
|
|
1248
|
-
// several messages; a digest message may bind several rows.
|
|
1249
1028
|
let offset = text.indexOf("\n\n") + 2;
|
|
1250
1029
|
const spans = rows.map(row => {
|
|
1251
1030
|
const length = digestEntry(row, titleOf).length;
|
|
@@ -1272,11 +1051,6 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1272
1051
|
return { ok: false, error: "Decision does not match notification provenance" };
|
|
1273
1052
|
}
|
|
1274
1053
|
if (decision === null || decision.state === "answered") {
|
|
1275
|
-
// A plain fact, or a decision settled before the bridge got to it: the
|
|
1276
|
-
// text is the message. Its one next-action button — the fact's
|
|
1277
|
-
// machine-minted link under the trusted origin read now, never a token
|
|
1278
|
-
// and never persisted — rides the LAST part only, exactly as `/task`'s
|
|
1279
|
-
// does; with no trusted origin the words stand alone.
|
|
1280
1054
|
const alreadyAccepted = notification.kind === "acceptance-ready" && notification.run !== null && store.proofAcceptance(notification.run) !== null;
|
|
1281
1055
|
let body = alreadyAccepted ? "This result already has recorded human acceptance. No further acceptance is needed." : notification.body;
|
|
1282
1056
|
let current = true;
|
|
@@ -1296,12 +1070,9 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1296
1070
|
else
|
|
1297
1071
|
body = `${packet.text}\n\n${notification.body}`;
|
|
1298
1072
|
}
|
|
1299
|
-
// A short title, never the task's id or a "— revision" suffix (chat-voice.ts).
|
|
1300
1073
|
const title = notification.taskId === null ? undefined : chatTitle(store, notification.taskId);
|
|
1301
1074
|
const task = notification.taskId === null ? [] : [{ id: notification.taskId, ...(title === undefined ? {} : { title }) }];
|
|
1302
1075
|
const words = chatText(`${alreadyAccepted ? "Acceptance recorded" : leadSubjectOf(store, notification, binding.approver)}${body === "" ? "" : `\n\n${body}`}`, task);
|
|
1303
|
-
// The task's title once: in front, unless the words already name it.
|
|
1304
|
-
// A result, plan, failure or ready pull request acts in place (chat-decide.ts); otherwise its one link.
|
|
1305
1076
|
const target = alreadyAccepted || !current ? null : decideTargetOf(notification);
|
|
1306
1077
|
const offer = decideOfferFor(store, binding, target, projects, clock(), evidenceRoot);
|
|
1307
1078
|
const decided = offer === null ? null : mintDecideButtons(store, decideSeat(binding), target, offer, clock());
|
|
@@ -1314,11 +1085,9 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1314
1085
|
return null;
|
|
1315
1086
|
} })()
|
|
1316
1087
|
: offer === null && notification.link !== null && !alreadyAccepted && current ? factButton(phoneOrigin, notification.link) : null;
|
|
1317
|
-
// A flow card waiting on a decision (v86): Approve, Edit, Send back on the last part, for this visit only.
|
|
1318
1088
|
const visit = FLOW_DECIDE_KEY.exec(notification.dedupeKey);
|
|
1319
1089
|
const waiting = visit === null ? null : flowDecisionAt(store, Number(visit[1]), Number(visit[2]));
|
|
1320
1090
|
const flowKeys = waiting === null ? null : flowButtons(store, binding, waiting, clock());
|
|
1321
|
-
// A flow's "Send to me" or "Person chooses" (flow-send.ts): its links as buttons, and a choice's options above them.
|
|
1322
1091
|
const sentVisit = FLOW_SEND_KEY.exec(notification.dedupeKey) ?? FLOW_CHOOSE_KEY.exec(notification.dedupeKey);
|
|
1323
1092
|
const sent = sentVisit === null ? null : flowSentContent(store, Number(sentVisit[1]), Number(sentVisit[2]));
|
|
1324
1093
|
const choosing = sent === null || !FLOW_CHOOSE_KEY.test(notification.dedupeKey) || notification.recipient !== binding.approver ? null : flowChoiceAt(store, Number(sentVisit[1]), Number(sentVisit[2]));
|
|
@@ -1331,8 +1100,6 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1331
1100
|
} })();
|
|
1332
1101
|
if (sentRow !== null)
|
|
1333
1102
|
button = sentRow.length === 0 ? null : sentRow;
|
|
1334
|
-
// A flow decision or choice for this person leads with what to do and where each button takes the card
|
|
1335
|
-
// (telegram-flow.ts); the flow and zone close it. The saved notice keeps its own words for every other place.
|
|
1336
1103
|
const pull = sent === null ? [] : sent.links.flatMap(one => "url" in one ? [`${one.label}: ${one.url}`] : []).slice(0, 1);
|
|
1337
1104
|
const choiceHead = choosing === null || sent === null ? null : flowChoiceHead(choosing, sent);
|
|
1338
1105
|
const choiceTail = choosing === null ? [] : [...pull, `${choosing.flow.name}${sent?.from === undefined ? "" : ` · after ${sent.from}`}`];
|
|
@@ -1340,8 +1107,6 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1340
1107
|
parts = split(`${notificationIdentity(notification)}${chatText(flowDecisionText(waiting, notification.body), task)}`);
|
|
1341
1108
|
else if (offer === null && choiceHead !== null && sent !== null)
|
|
1342
1109
|
parts = split(`${notificationIdentity(notification)}${chatText([...choiceHead, "", sent.summary, "", ...choiceTail.flatMap((line, index) => index === 0 ? [line] : ["", line])].join("\n"), task)}`);
|
|
1343
|
-
// After research (flow-items.ts): each item numbered under the summary, its words cleaned as the rest of the message's
|
|
1344
|
-
// are, within Telegram's limit with every link kept — one message, unless head, links and tail alone overflow it.
|
|
1345
1110
|
let entities = [];
|
|
1346
1111
|
if (sent?.items !== undefined && sent.items.length > 0 && offer === null) {
|
|
1347
1112
|
const subject = chatText(leadSubjectOf(store, notification, binding.approver), task);
|
|
@@ -1352,21 +1117,17 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1352
1117
|
parts = voiced.map(one => one.text);
|
|
1353
1118
|
entities = voiced.map(one => one.entities);
|
|
1354
1119
|
}
|
|
1355
|
-
// A flow's result as a card (flow-result-card.ts): one more message, its tables as monospaced text.
|
|
1356
1120
|
const resultCard = offer === null ? sent?.card ?? flowDoneCard(store, notification, clock()) : null;
|
|
1357
1121
|
if (resultCard != null) {
|
|
1358
1122
|
const message = telegramCardMessage(resultCard);
|
|
1359
1123
|
entities = [...parts.map((_, at) => entities[at] ?? []), message.entities];
|
|
1360
1124
|
parts = [...parts, message.text];
|
|
1361
1125
|
}
|
|
1362
|
-
// Its links stay on the words: the card follows them.
|
|
1363
1126
|
const keyedPart = parts.length - (resultCard != null ? 2 : 1);
|
|
1364
|
-
// A subagent's question (v93): its options and "Answer in words", for the person it asks.
|
|
1365
1127
|
const asked = flowKeys === null && choiceKeys === null ? openQuestionOf(store, notification.dedupeKey) : null;
|
|
1366
1128
|
const questionKeys = asked === null ? null : telegramQuestionButtons(store, binding, asked, clock());
|
|
1367
1129
|
const keys = flowKeys ?? choiceKeys ?? questionKeys;
|
|
1368
1130
|
let last = null;
|
|
1369
|
-
// A release card: every term goes as a file first, so Continue is offered only once it has arrived.
|
|
1370
1131
|
if (offer?.file !== undefined && decided !== null) {
|
|
1371
1132
|
if (fileSender === undefined || !await fileSender(offer.file)) {
|
|
1372
1133
|
dropDecideTokens(store, decided.tokens, clock());
|
|
@@ -1389,7 +1150,6 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1389
1150
|
store.placeTelegramFlowActions(flowKeys.tokens, last);
|
|
1390
1151
|
if (choiceKeys !== null && last !== null) {
|
|
1391
1152
|
store.placeTelegramFlowChoices(choiceKeys.tokens, last);
|
|
1392
|
-
// A reply to this message, instead of a tap, is the person's note (applyFlowReply).
|
|
1393
1153
|
if (sent?.reply === true)
|
|
1394
1154
|
store.recordTelegramFlowPrompt({ chatId: binding.chatId, messageId: last, binding: binding.id, card: choosing.card.id, entry: choosing.card.entry, mode: "send-back" }, clock(), 7 * 24);
|
|
1395
1155
|
}
|
|
@@ -1399,25 +1159,15 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1399
1159
|
placeDecideTokens(store, decided.tokens, last);
|
|
1400
1160
|
return { ok: true, receipt: receiptFor(botId, binding.chatId, last) };
|
|
1401
1161
|
}
|
|
1402
|
-
// A decision. Every safety-bearing word goes out before anything tappable
|
|
1403
|
-
// exists: the question and every option's consequence first, then the
|
|
1404
|
-
// recap, split across as many plain messages as they need — a button whose
|
|
1405
|
-
// warning was truncated away is a trap, so the keyboard rides the LAST part
|
|
1406
|
-
// only, and only if every earlier part arrived.
|
|
1407
1162
|
const parts = split([`${notificationIdentity(notification)}${decisionAsk(decision)}`, "", `Background: ${decision.recap}`].join("\n"));
|
|
1408
1163
|
for (const part of parts.slice(0, -1)) {
|
|
1409
1164
|
const sent = await sender(part);
|
|
1410
1165
|
if (!sent.ok)
|
|
1411
1166
|
return { ok: false, error: sent.error };
|
|
1412
|
-
// Every part is a message somebody may REPLY to with a note: each id
|
|
1413
|
-
// routes to this decision, exactly (Codex free-text review, finding 1).
|
|
1414
1167
|
if (sent.messageId !== null) {
|
|
1415
1168
|
store.recordTelegramDecisionMessage(binding.id, binding.chatId, sent.messageId, decision.id, clock());
|
|
1416
1169
|
}
|
|
1417
1170
|
}
|
|
1418
|
-
// The buttons: one opaque token per option, minted before the send so a
|
|
1419
|
-
// tap can never arrive for a token that does not exist, placed onto the
|
|
1420
|
-
// message afterwards so a tap on any OTHER message proves itself stale.
|
|
1421
1171
|
const tokens = decision.options.map(option => ({
|
|
1422
1172
|
option,
|
|
1423
1173
|
token: randomBytes(16).toString("hex"),
|
|
@@ -1446,8 +1196,6 @@ async function deliverOne(store, botId, binding, sender, notification, clock, ph
|
|
|
1446
1196
|
return { ok: true, receipt: receiptFor(botId, binding.chatId, sent.messageId) };
|
|
1447
1197
|
}
|
|
1448
1198
|
async function send(transport, chatId, text, keyboard, entities, silent) {
|
|
1449
|
-
// No markup parsing. Only machine-selected heading ranges may be bold;
|
|
1450
|
-
// agent text remains literal and URLs never trigger link previews.
|
|
1451
1199
|
let answer;
|
|
1452
1200
|
try {
|
|
1453
1201
|
answer = await transport("sendMessage", {
|
|
@@ -1455,7 +1203,6 @@ async function send(transport, chatId, text, keyboard, entities, silent) {
|
|
|
1455
1203
|
text,
|
|
1456
1204
|
...(entities === undefined ? {} : { entities }),
|
|
1457
1205
|
link_preview_options: { is_disabled: true },
|
|
1458
|
-
// A new quiet card arrives without a sound; only a ping should buzz.
|
|
1459
1206
|
...(silent === true ? { disable_notification: true } : {}),
|
|
1460
1207
|
...(keyboard === undefined ? {} : { reply_markup: { inline_keyboard: keyboard } }),
|
|
1461
1208
|
});
|
|
@@ -1479,8 +1226,6 @@ async function editProgress(transport, chatId, messageId, text, keyboard, entiti
|
|
|
1479
1226
|
catch {
|
|
1480
1227
|
return { ok: false, error: "Telegram progress update is unconfirmed; it will retry in place" };
|
|
1481
1228
|
}
|
|
1482
|
-
// The retry may be repainting the same bytes after an acknowledgement was
|
|
1483
|
-
// lost. Telegram's explicit unchanged response confirms this target state.
|
|
1484
1229
|
if (!answer.ok && !answer.uncertain && /^Bad Request: message is not modified\b/i.test(answer.description ?? ""))
|
|
1485
1230
|
return { ok: true, messageId };
|
|
1486
1231
|
if (!answer.ok) {
|
|
@@ -1492,7 +1237,6 @@ async function editProgress(transport, chatId, messageId, text, keyboard, entiti
|
|
|
1492
1237
|
const confirmed = answer.result?.message_id;
|
|
1493
1238
|
return String(confirmed) === messageId ? { ok: true, messageId } : { ok: false, error: "Telegram did not confirm the progress message identity" };
|
|
1494
1239
|
}
|
|
1495
|
-
/** A tapped message's own formatting, read back for its edit: only bold and links that still fall inside the text. */
|
|
1496
1240
|
function keptEntities(raw, length) {
|
|
1497
1241
|
return (raw ?? []).flatMap((one) => {
|
|
1498
1242
|
if (typeof one !== "object" || one === null)
|
|
@@ -1525,34 +1269,25 @@ function taskOf(store, decision) {
|
|
|
1525
1269
|
const run = store.getRun(decision.run);
|
|
1526
1270
|
return run === null ? "?" : store.externalIdFor(run.taskRef) ?? "?";
|
|
1527
1271
|
}
|
|
1528
|
-
async function drainUpdates(store, botId, transport, owner, generation, cursor, clock, report, pollSeconds = 0, signal, readProjects, conversation,
|
|
1529
|
-
/** True when the poll was ended on purpose for a delivery: an answer that failed then is that abort, not the wire. */
|
|
1530
|
-
deliveryWake) {
|
|
1272
|
+
async function drainUpdates(store, botId, transport, owner, generation, cursor, clock, report, pollSeconds = 0, signal, readProjects, conversation, deliveryWake) {
|
|
1531
1273
|
const context = { store, botId, transport, clock, report, readProjects, conversation, projects: null };
|
|
1532
1274
|
let offset = cursor + 1;
|
|
1533
1275
|
for (let page = 0; page < PAGE_BUDGET; page++) {
|
|
1534
1276
|
const answer = await transport("getUpdates", {
|
|
1535
1277
|
offset,
|
|
1536
|
-
// Only the first page long-polls; a backlog drains at full speed.
|
|
1537
1278
|
timeout: page === 0 ? pollSeconds : 0,
|
|
1538
1279
|
allowed_updates: ["message", "callback_query"],
|
|
1539
1280
|
}, signal);
|
|
1540
1281
|
if (!answer.ok) {
|
|
1541
|
-
// Ended on purpose so a due message goes out now: no problem, no backoff; the next cycle asks again from the
|
|
1542
|
-
// same cursor, so nothing is lost.
|
|
1543
1282
|
if (deliveryWake?.() === true) {
|
|
1544
1283
|
report.deliveryWake = true;
|
|
1545
1284
|
return;
|
|
1546
1285
|
}
|
|
1547
1286
|
const said = answer.description ?? "failed";
|
|
1548
|
-
// v98: another program asked Telegram for this bot's updates, and Telegram ended our
|
|
1549
|
-
// request in its favour. Nothing is lost: an update is only gone once someone
|
|
1550
|
-
// confirms it, and the next request asks again. Not a problem to report.
|
|
1551
1287
|
if (/terminated by other getUpdates/i.test(said)) {
|
|
1552
1288
|
report.contention = (report.contention ?? 0) + 1;
|
|
1553
1289
|
return;
|
|
1554
1290
|
}
|
|
1555
|
-
// Telegram is pushing this bot's updates to a webhook: polling isn't how they arrive now.
|
|
1556
1291
|
if (/webhook is active/i.test(said)) {
|
|
1557
1292
|
report.webhookActive = true;
|
|
1558
1293
|
return;
|
|
@@ -1566,7 +1301,6 @@ deliveryWake) {
|
|
|
1566
1301
|
for (const raw of updates) {
|
|
1567
1302
|
const update = readTelegramUpdate(raw);
|
|
1568
1303
|
if (!update.ok) {
|
|
1569
|
-
// Telegram's own update in a shape this bridge doesn't read: said, and passed over so the next one is read.
|
|
1570
1304
|
const id = raw?.update_id;
|
|
1571
1305
|
report.problems.push(`an update Telegram sent couldn't be read: ${update.issues.map(issue => issue.line).join("; ")}`);
|
|
1572
1306
|
if (typeof id !== "number" || !Number.isSafeInteger(id))
|
|
@@ -1578,24 +1312,11 @@ deliveryWake) {
|
|
|
1578
1312
|
offset = update.value.update_id + 1;
|
|
1579
1313
|
}
|
|
1580
1314
|
}
|
|
1581
|
-
// The budget ran out with Telegram still holding pages: said, not hidden.
|
|
1582
1315
|
report.backlog = true;
|
|
1583
1316
|
}
|
|
1584
|
-
/**
|
|
1585
|
-
* One update, polled or pushed (v98): the projects it may read, applied in
|
|
1586
|
-
* its own transaction (once: applyUpdate refuses an update id it has seen),
|
|
1587
|
-
* its Telegram-side effects, and the cursor moved past it.
|
|
1588
|
-
*/
|
|
1589
1317
|
async function processUpdate(context, update, owner, generation) {
|
|
1590
|
-
// A proposal tap confirms as a principal minted against the CURRENT
|
|
1591
|
-
// enrolled ceiling, and the registry is a file read: it happens
|
|
1592
|
-
// before the update's transaction, and only once the envelope has
|
|
1593
|
-
// proved the exact paired sender and chat — a stranger reads nothing.
|
|
1594
1318
|
context.projects = update.message !== undefined ? await enrolledForMessage(context, update) : await projectsForTap(context, update);
|
|
1595
1319
|
const effects = applyUpdate(context, update);
|
|
1596
|
-
// Effects are Telegram-side conveniences — acks, edits, replies. They
|
|
1597
|
-
// retry-or-drop; they never decide whether the cursor moves, because
|
|
1598
|
-
// an unreachable edit must not make the bridge re-apply an answer.
|
|
1599
1320
|
for (const effect of effects) {
|
|
1600
1321
|
try {
|
|
1601
1322
|
await effect();
|
|
@@ -1606,7 +1327,6 @@ async function processUpdate(context, update, owner, generation) {
|
|
|
1606
1327
|
}
|
|
1607
1328
|
context.store.advanceBridgeCursor(context.botId, owner, generation, update.update_id, context.clock());
|
|
1608
1329
|
}
|
|
1609
|
-
/** v98: updates Telegram pushed to /hooks/telegram, applied in order through the same door as polled ones. */
|
|
1610
1330
|
async function drainInbox(context, owner, generation) {
|
|
1611
1331
|
let applied = 0;
|
|
1612
1332
|
for (const queued of context.store.telegramInbox(context.botId, PAGE_BUDGET * 100)) {
|
|
@@ -1620,17 +1340,10 @@ async function drainInbox(context, owner, generation) {
|
|
|
1620
1340
|
}
|
|
1621
1341
|
return applied;
|
|
1622
1342
|
}
|
|
1623
|
-
/** The enrolled registry, read before a message is applied: the team layer
|
|
1624
|
-
* scopes leads and conversations to it (each person's own access is checked
|
|
1625
|
-
* inside the domain). Unreadable reads as null, and the personal paths stay
|
|
1626
|
-
* exactly as they were. */
|
|
1627
1343
|
async function enrolledForMessage(context, update) {
|
|
1628
1344
|
const message = update.message;
|
|
1629
1345
|
if (message?.chat === undefined || message.from === undefined || message.text === undefined || context.readProjects === undefined)
|
|
1630
1346
|
return null;
|
|
1631
|
-
// An untrusted envelope reads nothing: the sender must be paired, the
|
|
1632
|
-
// text plain and direct, and the chat either that person's own private
|
|
1633
|
-
// chat or a group that follows (or is being pointed at) a conversation.
|
|
1634
1347
|
if (message.forward_origin !== undefined || message.forward_date !== undefined || message.via_bot !== undefined || message.sender_chat !== undefined || message.caption !== undefined)
|
|
1635
1348
|
return null;
|
|
1636
1349
|
const binding = context.store.liveTelegramBindingFor(context.botId, String(message.from.id));
|
|
@@ -1652,17 +1365,14 @@ async function enrolledForMessage(context, update) {
|
|
|
1652
1365
|
}
|
|
1653
1366
|
async function projectsForTap(context, update) {
|
|
1654
1367
|
const callback = update.callback_query;
|
|
1655
|
-
// Flow decision buttons (v86) work with or without the lead's conversation on this phone.
|
|
1656
1368
|
const flowTap = callback !== undefined && (context.store.getTelegramFlowAction(callback.data ?? "") !== null || context.store.getTelegramFlowChoice(callback.data ?? "") !== null
|
|
1657
1369
|
|| flowConfirmOf(context.store, callback.data ?? "") !== null);
|
|
1658
|
-
// So do a result's, plan's or pull request's own buttons (chat-decide.ts).
|
|
1659
1370
|
const decideTap = callback !== undefined && isDecideToken(context.store, callback.data ?? "");
|
|
1660
1371
|
if (callback === undefined || context.readProjects === undefined || (context.conversation === undefined && !flowTap && !decideTap))
|
|
1661
1372
|
return null;
|
|
1662
1373
|
const binding = callback.from === undefined ? null : context.store.liveTelegramBindingFor(context.botId, String(callback.from.id));
|
|
1663
1374
|
if (binding === null || callback.from === undefined ||
|
|
1664
1375
|
callback.message?.chat === undefined ||
|
|
1665
|
-
// Proposal cards and task picks read the chat's project ceiling.
|
|
1666
1376
|
(context.store.getTelegramProposalAction(callback.data ?? "") === null && !flowTap && !decideTap && !(callback.data ?? "").startsWith("pick:")))
|
|
1667
1377
|
return null;
|
|
1668
1378
|
const chat = callback.message.chat;
|
|
@@ -1677,18 +1387,10 @@ async function projectsForTap(context, update) {
|
|
|
1677
1387
|
return null;
|
|
1678
1388
|
}
|
|
1679
1389
|
}
|
|
1680
|
-
/**
|
|
1681
|
-
* Apply one update in one transaction; return the Telegram-side effects to
|
|
1682
|
-
* attempt afterwards. Everything suspicious lands in the same place:
|
|
1683
|
-
* `ignored`, silently — an unbound stranger learns nothing, including
|
|
1684
|
-
* whether there was anything to learn.
|
|
1685
|
-
*/
|
|
1686
1390
|
function applyUpdate(context, update) {
|
|
1687
1391
|
const effects = [];
|
|
1688
1392
|
context.store.transact(() => {
|
|
1689
1393
|
if (!context.store.markTelegramUpdateApplied(update.update_id, "seen", context.clock())) {
|
|
1690
|
-
// Already applied by an earlier pass. The local mutation happened;
|
|
1691
|
-
// the edits were attempted then; nothing repeats.
|
|
1692
1394
|
return;
|
|
1693
1395
|
}
|
|
1694
1396
|
if (update.message !== undefined) {
|
|
@@ -1703,7 +1405,6 @@ function applyUpdate(context, update) {
|
|
|
1703
1405
|
});
|
|
1704
1406
|
return effects;
|
|
1705
1407
|
}
|
|
1706
|
-
/** A group message that connects the group to a flow, or that the group's flow takes as a card. False: not for an inbox. */
|
|
1707
1408
|
function applyGroupInbox(context, message, effects) {
|
|
1708
1409
|
const { store, botId, transport, clock } = context;
|
|
1709
1410
|
const chatId = String(message.chat.id), text = message.text ?? "";
|
|
@@ -1733,7 +1434,6 @@ function applyGroupInbox(context, message, effects) {
|
|
|
1733
1434
|
const trigger = watchedChannel(store, "telegram", botId, chatId);
|
|
1734
1435
|
if (trigger === null)
|
|
1735
1436
|
return false;
|
|
1736
|
-
// Forwards, other bots and channel posts don't become cards: only people writing in the group.
|
|
1737
1437
|
if (message.forward_origin !== undefined || message.forward_date !== undefined || message.via_bot !== undefined || message.sender_chat !== undefined) {
|
|
1738
1438
|
context.report.ignored++;
|
|
1739
1439
|
return true;
|
|
@@ -1752,13 +1452,9 @@ function applyMessage(context, update, effects) {
|
|
|
1752
1452
|
const chat = message.chat;
|
|
1753
1453
|
const from = message.from;
|
|
1754
1454
|
const pair = /^\/pair\s+([0-9a-f]{32})\s*$/.exec(message.text ?? "");
|
|
1755
|
-
// A group as a flow's inbox (v89): "/flow 12" from a paired approver connects it; after that its messages are cards.
|
|
1756
1455
|
if (pair === null && chat !== undefined && from !== undefined && (chat.type === "group" || chat.type === "supergroup") && typeof message.text === "string" && applyGroupInbox(context, message, effects))
|
|
1757
1456
|
return;
|
|
1758
1457
|
if (pair === null && chat !== undefined && from !== undefined) {
|
|
1759
|
-
// The team layer first: `/team` anywhere, everything in a followed
|
|
1760
|
-
// group, and a private chat that chose a conversation. Its replies ride
|
|
1761
|
-
// the same post-commit effects as every other answer.
|
|
1762
1458
|
const consumed = applyTeamInbound({
|
|
1763
1459
|
store, botId, now: clock(), report, projects: context.projects, phoneOrigin: context.conversation?.phoneOrigin, updateId: update.update_id, message,
|
|
1764
1460
|
say: (chatId, text, keyboard) => {
|
|
@@ -1770,23 +1466,13 @@ function applyMessage(context, update, effects) {
|
|
|
1770
1466
|
});
|
|
1771
1467
|
if (consumed)
|
|
1772
1468
|
return;
|
|
1773
|
-
// Replies to decisions retain their existing note meaning, even if the
|
|
1774
|
-
// note starts with a slash. New read commands are direct messages only.
|
|
1775
|
-
// A bare "start" as the very next message after a Start card is that card's yes (docs/plans/one-step-chat.md C5),
|
|
1776
|
-
// not the help command. Only the routing is decided here, by the same ids the message path reads again when it
|
|
1777
|
-
// records the yes; "/start", and "start" anywhere else, stay help.
|
|
1778
1469
|
const yesSeat = message.reply_to_message === undefined && chat.type === "private" && typeof message.text === "string" && typedYes(message.text)
|
|
1779
1470
|
&& context.conversation !== undefined && context.readProjects !== undefined ? store.liveTelegramBindingFor(botId, String(from.id)) : null;
|
|
1780
1471
|
const answersStartCard = yesSeat !== null && String(chat.id) === yesSeat.chatId
|
|
1781
1472
|
&& typedStartCard(store, { binding: yesSeat.id, chatId: yesSeat.chatId, approver: yesSeat.approver }, { id: String(message.message_id), replyTo: null }) !== null;
|
|
1782
1473
|
if (message.reply_to_message === undefined && !answersStartCard && applyPhoneRead(context, update, effects))
|
|
1783
1474
|
return;
|
|
1784
|
-
// A reply to a decision message this bot sent is a note, exactly as
|
|
1785
|
-
// before. Everything else that is ordinary text talks to the shared
|
|
1786
|
-
// assistant when a conversation is configured; otherwise silence.
|
|
1787
1475
|
const binding = store.liveTelegramBindingFor(botId, String(from.id));
|
|
1788
|
-
// A reply to an Edit or Send back prompt (v86): the new draft, or the note it goes back with.
|
|
1789
|
-
// A reply to a subagent's "Answer in words" prompt (v93): the answer.
|
|
1790
1476
|
const questionPrompt = binding !== null && message.reply_to_message !== undefined && String(chat.id) === binding.chatId
|
|
1791
1477
|
? store.telegramQuestionPrompt(binding.chatId, String(message.reply_to_message.message_id), clock()) : null;
|
|
1792
1478
|
if (binding !== null && questionPrompt !== null) {
|
|
@@ -1811,8 +1497,6 @@ function applyMessage(context, update, effects) {
|
|
|
1811
1497
|
}
|
|
1812
1498
|
return;
|
|
1813
1499
|
}
|
|
1814
|
-
// After Request changes asked "What should change?": the person's next message in their own chat is the feedback.
|
|
1815
|
-
// A reply to some other message (a decision, a result) keeps its own meaning.
|
|
1816
1500
|
const decidePrompt = binding !== null && chat.type === "private" && String(chat.id) === binding.chatId && typeof message.text === "string" && message.text.trim() !== "" && !message.text.startsWith("/")
|
|
1817
1501
|
? openPromptFor(store, { channel: "telegram", binding: binding.id, chat: binding.chatId }, clock()) : null;
|
|
1818
1502
|
if (binding !== null && decidePrompt !== null && answersPrompt(decidePrompt, message.reply_to_message === undefined ? null : String(message.reply_to_message.message_id))) {
|
|
@@ -1831,7 +1515,6 @@ function applyMessage(context, update, effects) {
|
|
|
1831
1515
|
});
|
|
1832
1516
|
return;
|
|
1833
1517
|
}
|
|
1834
|
-
// D5: a message naming a subagent ("@rosa …") goes to the lead like any other, which asks the subagent with a card.
|
|
1835
1518
|
const repliedDecision = binding !== null && message.reply_to_message !== undefined
|
|
1836
1519
|
? store.decisionForTelegramMessage(binding.id, binding.chatId, String(message.reply_to_message.message_id))
|
|
1837
1520
|
: null;
|
|
@@ -1842,9 +1525,6 @@ function applyMessage(context, update, effects) {
|
|
|
1842
1525
|
applyNote(context, update, effects);
|
|
1843
1526
|
return;
|
|
1844
1527
|
}
|
|
1845
|
-
// Only /pair, only in a private chat, only with the sender on the record.
|
|
1846
|
-
// A group is exactly where "the chat" and "the person" diverge, which is
|
|
1847
|
-
// why a group cannot pair at all.
|
|
1848
1528
|
if (pair === null || chat === undefined || chat.type !== "private" || from === undefined) {
|
|
1849
1529
|
report.ignored++;
|
|
1850
1530
|
return;
|
|
@@ -1857,8 +1537,6 @@ function applyMessage(context, update, effects) {
|
|
|
1857
1537
|
updateId: update.update_id,
|
|
1858
1538
|
}, clock());
|
|
1859
1539
|
if (!consumed.ok) {
|
|
1860
|
-
// A wrong code gets the same silence as everything else wrong: replying
|
|
1861
|
-
// "no such code" to a guesser is an oracle.
|
|
1862
1540
|
report.ignored++;
|
|
1863
1541
|
return;
|
|
1864
1542
|
}
|
|
@@ -1866,27 +1544,14 @@ function applyMessage(context, update, effects) {
|
|
|
1866
1544
|
const chatId = String(chat.id);
|
|
1867
1545
|
const approver = consumed.binding.approver;
|
|
1868
1546
|
effects.push(async () => {
|
|
1869
|
-
// At-least-once across a crash-after-send window, by design: a repeated
|
|
1870
|
-
// "paired" line is annoying; a paired chat that never heard so is worse.
|
|
1871
1547
|
await transport("sendMessage", {
|
|
1872
1548
|
chat_id: chatId,
|
|
1873
1549
|
text: `paired: this chat now answers as ${approver}\n\nSend /status to check recent work, /task <id> for one task, or /help for your options.`,
|
|
1874
1550
|
link_preview_options: { is_disabled: true },
|
|
1875
1551
|
});
|
|
1876
|
-
// The bot is Toolroll in every chat list from here on (best effort; pairing never waits on it).
|
|
1877
1552
|
await nameTelegramBot(transport, "pairing");
|
|
1878
1553
|
});
|
|
1879
1554
|
}
|
|
1880
|
-
/**
|
|
1881
|
-
* An ordinary message from the paired person: persisted for the shared
|
|
1882
|
-
* assistant in THIS transaction — before the cursor moves — with the exact
|
|
1883
|
-
* binding, sender, update, message and the request identity the engine
|
|
1884
|
-
* receipts its turn under. Two things are answered here and now without a
|
|
1885
|
-
* model: text over the engine's bound (never truncated), and a reply to a
|
|
1886
|
-
* message that carried several tasks (never guessed). Everything hostile is
|
|
1887
|
-
* silence, as for every other inbound shape.
|
|
1888
|
-
*/
|
|
1889
|
-
/** At most this many of a chat's messages wait for the lead at once (the personal lead queue's bound). */
|
|
1890
1555
|
export const TELEGRAM_MAX_WAITING = 10;
|
|
1891
1556
|
function applyConversation(context, update, effects) {
|
|
1892
1557
|
const { store, botId, transport, clock, report } = context;
|
|
@@ -1923,8 +1588,6 @@ function applyConversation(context, update, effects) {
|
|
|
1923
1588
|
report.chatRefused = (report.chatRefused ?? 0) + 1;
|
|
1924
1589
|
return;
|
|
1925
1590
|
}
|
|
1926
|
-
// A reply binds to what the replied-to message carried: exactly one task
|
|
1927
|
-
// (and its run) pins the turn; several ask which; none is a plain turn.
|
|
1928
1591
|
let context_ = null;
|
|
1929
1592
|
let taskId = null;
|
|
1930
1593
|
let sourceRun = null;
|
|
@@ -1943,8 +1606,6 @@ function applyConversation(context, update, effects) {
|
|
|
1943
1606
|
context_ = replyContextFor(taskId, sourceRun, bindings.find(one => one.image !== undefined)?.image);
|
|
1944
1607
|
}
|
|
1945
1608
|
}
|
|
1946
|
-
// No reply target: the task this chat chose to talk about, if any. The
|
|
1947
|
-
// turn re-proves it; a task no longer in reach reads as a plain message.
|
|
1948
1609
|
if (taskId === null) {
|
|
1949
1610
|
const focused = store.chatFocus("telegram", binding.id);
|
|
1950
1611
|
if (focused !== null) {
|
|
@@ -1952,16 +1613,11 @@ function applyConversation(context, update, effects) {
|
|
|
1952
1613
|
context_ = focusContextFor(focused);
|
|
1953
1614
|
}
|
|
1954
1615
|
}
|
|
1955
|
-
// The lead queue's bound, per chat: ten messages wait at most; the next is answered at once instead of queued.
|
|
1956
1616
|
if (store.telegramMessagesWaiting(binding.id) >= TELEGRAM_MAX_WAITING) {
|
|
1957
1617
|
say(`${TELEGRAM_MAX_WAITING} messages are already waiting for the lead. Wait for a reply, then send this again.`);
|
|
1958
1618
|
report.ignored++;
|
|
1959
1619
|
return;
|
|
1960
1620
|
}
|
|
1961
|
-
// A typed yes to a Start card (docs/plans/one-step-chat.md C5) is read here, before any model runs, and recorded with
|
|
1962
|
-
// the message in the same transaction: the fixed words only, tied to one exact card by message ids and Toolroll's own
|
|
1963
|
-
// delivery record. The worker acts on it (confirmTypedStart) and no lead turn runs for it. Anything else is queued for
|
|
1964
|
-
// the lead exactly as before.
|
|
1965
1621
|
store.transact(() => {
|
|
1966
1622
|
const typed = typedYes(text) ? typedStartCard(store, { binding: binding.id, chatId: binding.chatId, approver: binding.approver }, { id: String(message.message_id), replyTo }) : null;
|
|
1967
1623
|
store.enqueueTelegramConversation({
|
|
@@ -1972,9 +1628,6 @@ function applyConversation(context, update, effects) {
|
|
|
1972
1628
|
recordTypedStart(store, `m${update.update_id}`, typed, clock());
|
|
1973
1629
|
});
|
|
1974
1630
|
report.chatQueued = (report.chatQueued ?? 0) + 1;
|
|
1975
|
-
// Saved: 👀 on the owner's message right away (docs/plans/responsive-chat.md §3.1), once this update's transaction
|
|
1976
|
-
// has committed. It comes off when the answer or the failure notice is delivered. Never awaited: a burst of messages
|
|
1977
|
-
// waits its turn on the chat's own lane, not the drain.
|
|
1978
1631
|
const gapMs = context.conversation?.liveTiming?.ackGapMs;
|
|
1979
1632
|
effects.push(async () => {
|
|
1980
1633
|
void telegramAck({
|
|
@@ -1986,9 +1639,6 @@ function applyConversation(context, update, effects) {
|
|
|
1986
1639
|
});
|
|
1987
1640
|
});
|
|
1988
1641
|
}
|
|
1989
|
-
/** The picker's buttons: one task per row (its title and where it stands),
|
|
1990
|
-
* carried by the task's numeric reference so the data fits Telegram's 64
|
|
1991
|
-
* bytes; a tap re-proves the task against the ceiling. */
|
|
1992
1642
|
function pickKeyboard(store, choices, focused) {
|
|
1993
1643
|
const rows = [];
|
|
1994
1644
|
for (const one of choices) {
|
|
@@ -2000,7 +1650,6 @@ function pickKeyboard(store, choices, focused) {
|
|
|
2000
1650
|
rows.push([telegramButton("Back to the lead", "pick:lead")]);
|
|
2001
1651
|
return rows;
|
|
2002
1652
|
}
|
|
2003
|
-
/** The title of the task this Telegram chat chose to talk about, if any. */
|
|
2004
1653
|
function focusedTitle(store, binding) {
|
|
2005
1654
|
const id = store.chatFocus("telegram", binding.id);
|
|
2006
1655
|
return id === null ? null : { id, title: phoneText(store.getTask(id)?.title ?? id, 64) };
|
|
@@ -2020,8 +1669,6 @@ function applyPhoneRead(context, update, effects) {
|
|
|
2020
1669
|
report.ignored++;
|
|
2021
1670
|
return true;
|
|
2022
1671
|
}
|
|
2023
|
-
// Like decision-message edits, a read reply is best-effort after the update
|
|
2024
|
-
// was consumed. A crash or send failure cannot turn replay into a new action.
|
|
2025
1672
|
effects.push(async () => {
|
|
2026
1673
|
const stillPaired = () => {
|
|
2027
1674
|
const live = store.liveTelegramBindingById(binding.id);
|
|
@@ -2030,12 +1677,8 @@ function applyPhoneRead(context, update, effects) {
|
|
|
2030
1677
|
if (!stillPaired())
|
|
2031
1678
|
return;
|
|
2032
1679
|
let response = PHONE_HELP;
|
|
2033
|
-
// `/task` may carry ONE url button to the exact recorded task or result:
|
|
2034
|
-
// minted from the trusted origin read now, never persisted, never a token.
|
|
2035
1680
|
let button = null;
|
|
2036
|
-
// `/tasks` and an ambiguous `/task <name>` offer tasks as buttons.
|
|
2037
1681
|
let keyboard = null;
|
|
2038
|
-
// The one task (and saved result) a `/task` reply shows: a reply to it is about that task.
|
|
2039
1682
|
let shown = null;
|
|
2040
1683
|
if (command.kind === "lead") {
|
|
2041
1684
|
store.setChatFocus("telegram", binding.id, null, clock());
|
|
@@ -2043,9 +1686,6 @@ function applyPhoneRead(context, update, effects) {
|
|
|
2043
1686
|
}
|
|
2044
1687
|
else if (command.kind !== "help") {
|
|
2045
1688
|
try {
|
|
2046
|
-
// The registry, then — after the await — the pairing again and the
|
|
2047
|
-
// account's OWN ceiling over it: a project this approver was never
|
|
2048
|
-
// given, or lost since, is not read, named or linked from here.
|
|
2049
1689
|
const registry = await context.readProjects?.() ?? [];
|
|
2050
1690
|
if (!stillPaired())
|
|
2051
1691
|
return;
|
|
@@ -2072,22 +1712,17 @@ function applyPhoneRead(context, update, effects) {
|
|
|
2072
1712
|
const view = phoneTaskView(store, repos, pick.view, clock(), context.conversation?.evidenceRoot);
|
|
2073
1713
|
shown = { task: pick.view, run: view.run };
|
|
2074
1714
|
button = phoneLinkButton(context.conversation?.phoneOrigin?.() ?? null, view.link);
|
|
2075
|
-
// A destination with no trusted origin to carry it: the words say where instead.
|
|
2076
1715
|
response = phoneFocusText(button === null && view.link !== null ? `${view.text}\n\n${PHONE_CONSOLE_FOOTER}` : view.text);
|
|
2077
1716
|
}
|
|
2078
1717
|
}
|
|
2079
1718
|
}
|
|
2080
1719
|
catch {
|
|
2081
|
-
// No registry paths, SQLite errors, credentials, or stale snapshots
|
|
2082
|
-
// leave on the failure road. A new request can try again.
|
|
2083
1720
|
response = "I couldn't read the current project status. No tasks were changed. Try /status again; if it persists, check Toolroll on the computer.";
|
|
2084
1721
|
report.problems.push("phone status could not read the current project records");
|
|
2085
1722
|
}
|
|
2086
1723
|
}
|
|
2087
1724
|
if (!stillPaired())
|
|
2088
1725
|
return;
|
|
2089
|
-
// Each view fits one message. Fail visibly if a future change violates
|
|
2090
|
-
// that contract, rather than cutting off the important next action.
|
|
2091
1726
|
if (response.length > PART_CAP) {
|
|
2092
1727
|
response = "This status is too large for one phone message. Open the console for the full view, or send /task <id> for one task.";
|
|
2093
1728
|
report.problems.push("phone status exceeded its message bound");
|
|
@@ -2105,24 +1740,14 @@ function applyPhoneRead(context, update, effects) {
|
|
|
2105
1740
|
report.problems.push(`phone status reply failed for update ${update.update_id}; send a new command to retry`);
|
|
2106
1741
|
const sentId = sent.ok ? sent.result?.message_id : undefined;
|
|
2107
1742
|
if (shown !== null && typeof sentId === "number" && Number.isSafeInteger(sentId) && sentId > 0) {
|
|
2108
|
-
// Best effort: without it a reply still reaches the chosen task through the focus.
|
|
2109
1743
|
try {
|
|
2110
1744
|
store.recordTelegramTaskMessage(binding, String(sentId), shown.task, shown.run, clock());
|
|
2111
1745
|
}
|
|
2112
|
-
catch {
|
|
1746
|
+
catch { }
|
|
2113
1747
|
}
|
|
2114
1748
|
});
|
|
2115
1749
|
return true;
|
|
2116
1750
|
}
|
|
2117
|
-
/**
|
|
2118
|
-
* A free-text note, accepted only as an AUTHENTICATED REPLY to a recorded
|
|
2119
|
-
* decision message (Codex free-text review, prescribed design): live
|
|
2120
|
-
* binding, private chat, exact chat AND user, a reply_to that maps to
|
|
2121
|
-
* exactly one decision this bot sent, the decision still unanswered, and
|
|
2122
|
-
* direct initial plain text — no forwards, media, captions, bots, or
|
|
2123
|
-
* channel identities. Everything else is silence: a reply naming what was
|
|
2124
|
-
* wrong is an oracle. Choice stays TAP-ONLY; prose never selects an option.
|
|
2125
|
-
*/
|
|
2126
1751
|
function applyNote(context, update, effects) {
|
|
2127
1752
|
const { store, botId, transport, clock, report } = context;
|
|
2128
1753
|
const message = update.message;
|
|
@@ -2155,8 +1780,6 @@ function applyNote(context, update, effects) {
|
|
|
2155
1780
|
}
|
|
2156
1781
|
const decisionId = store.decisionForTelegramMessage(binding.id, binding.chatId, String(message.reply_to_message.message_id));
|
|
2157
1782
|
if (decisionId === null) {
|
|
2158
|
-
// A reply to something that never carried a decision — including a
|
|
2159
|
-
// send whose record was lost: fail closed, never guess by recency.
|
|
2160
1783
|
report.ignored++;
|
|
2161
1784
|
return;
|
|
2162
1785
|
}
|
|
@@ -2185,16 +1808,11 @@ function applyNote(context, update, effects) {
|
|
|
2185
1808
|
note: valid.note,
|
|
2186
1809
|
}, clock());
|
|
2187
1810
|
if (!saved) {
|
|
2188
|
-
// An older or equal update raced in late: the newer note stands.
|
|
2189
1811
|
report.ignored++;
|
|
2190
1812
|
return;
|
|
2191
1813
|
}
|
|
2192
|
-
// A new note voids any ARMED irreversible confirmation: what it showed
|
|
2193
|
-
// is no longer what would travel (Codex free-text review, finding 3).
|
|
2194
1814
|
store.consumeTelegramChallenges(decision.id, clock());
|
|
2195
1815
|
report.noted = (report.noted ?? 0) + 1;
|
|
2196
|
-
// The echo IS the ceremony: the exact captured text, line-prefixed, so a
|
|
2197
|
-
// later edit of the operator's own message cannot rewrite the audit.
|
|
2198
1816
|
say([
|
|
2199
1817
|
`noted for ${taskOf(store, decision)}:`,
|
|
2200
1818
|
...valid.note.split("\n").map((line) => `| ${line}`),
|
|
@@ -2234,15 +1852,9 @@ function applyCallback(context, update, effects) {
|
|
|
2234
1852
|
});
|
|
2235
1853
|
});
|
|
2236
1854
|
};
|
|
2237
|
-
// The person, the chat, and the message must all be the paired ones. A
|
|
2238
|
-
// callback with no accessible message (inline mode, too-old messages) is
|
|
2239
|
-
// out; so is a tap from anyone but the exact paired user id — usernames
|
|
2240
|
-
// change hands, immutable ids do not.
|
|
2241
1855
|
const binding = from === undefined ? null : store.liveTelegramBindingFor(botId, String(from.id));
|
|
2242
1856
|
const tapChat = message?.chat === undefined ? null : String(message.chat.id);
|
|
2243
1857
|
const followedGroup = tapChat !== null && binding !== null && tapChat !== binding.chatId && store.telegramTeamChat(botId, tapChat)?.kind === "group";
|
|
2244
|
-
// A result's, plan's, failure's or pull request's own buttons act only in the paired person's own chat; a tap
|
|
2245
|
-
// anywhere else is still answered, so the button never just spins.
|
|
2246
1858
|
if (isDecideToken(store, token) && (binding === null || tapChat === null || tapChat !== binding.chatId)) {
|
|
2247
1859
|
report.ignored++;
|
|
2248
1860
|
ack(binding === null ? "These buttons work only for the person they were sent to. Nothing was done."
|
|
@@ -2257,16 +1869,12 @@ function applyCallback(context, update, effects) {
|
|
|
2257
1869
|
report.ignored++;
|
|
2258
1870
|
return;
|
|
2259
1871
|
}
|
|
2260
|
-
// The paired person, in their chat: a button whose data isn't one Toolroll makes is answered with why, and does nothing.
|
|
2261
1872
|
const data = readTelegramButtonData(callback.data);
|
|
2262
1873
|
if (!data.ok) {
|
|
2263
1874
|
report.ignored++;
|
|
2264
1875
|
ack(`That button couldn't be read (${data.issues.map(issue => issue.line).join("; ")}). Nothing was done.`.slice(0, 190));
|
|
2265
1876
|
return;
|
|
2266
1877
|
}
|
|
2267
|
-
// A task picked from /tasks: this private chat now talks about it (or,
|
|
2268
|
-
// "Back to the lead", about everything again). The task is re-proved
|
|
2269
|
-
// against the chat's ceiling at the tap.
|
|
2270
1878
|
const pick = pickOf(token);
|
|
2271
1879
|
if (pick !== null) {
|
|
2272
1880
|
if (tapChat !== binding.chatId) {
|
|
@@ -2297,7 +1905,6 @@ function applyCallback(context, update, effects) {
|
|
|
2297
1905
|
editText(phoneFocusText(view.text), [[telegramButton("Back to the lead", "pick:lead")]]);
|
|
2298
1906
|
return;
|
|
2299
1907
|
}
|
|
2300
|
-
// A subagent's question (v93): an option answers it; "Answer in words" asks for a reply.
|
|
2301
1908
|
const questionAction = store.getTelegramQuestionAction(token);
|
|
2302
1909
|
if (questionAction !== null) {
|
|
2303
1910
|
if (questionAction.binding !== binding.id || questionAction.chatId !== tapChat || (questionAction.messageId !== null && questionAction.messageId !== String(message.message_id))) {
|
|
@@ -2320,13 +1927,11 @@ function applyCallback(context, update, effects) {
|
|
|
2320
1927
|
}
|
|
2321
1928
|
return;
|
|
2322
1929
|
}
|
|
2323
|
-
// A flow decision's repaint: its buttons (fresh ones stamped on this message) or links under the console.
|
|
2324
1930
|
const flowEdit = (effect) => {
|
|
2325
1931
|
if (effect.place !== undefined && effect.place.length > 0)
|
|
2326
1932
|
store.placeTelegramFlowActions(effect.place, String(message.message_id));
|
|
2327
1933
|
editText(effect.text, decideKeyboard(context.conversation?.phoneOrigin, effect.keyboard ?? []));
|
|
2328
1934
|
};
|
|
2329
|
-
// The Yes or Cancel a flow Approve armed (telegram-flow.ts): on the message it was armed on, by the person it was armed for.
|
|
2330
1935
|
const flowConfirm = flowConfirmOf(store, token);
|
|
2331
1936
|
if (flowConfirm !== null) {
|
|
2332
1937
|
if (flowConfirm.binding !== binding.id || flowConfirm.chatId !== tapChat || flowConfirm.messageId !== String(message.message_id)) {
|
|
@@ -2358,7 +1963,6 @@ function applyCallback(context, update, effects) {
|
|
|
2358
1963
|
flowEdit(effect);
|
|
2359
1964
|
else
|
|
2360
1965
|
effects.push(async () => {
|
|
2361
|
-
// A reply box: whatever they send back as a reply to this prompt is the new draft, or the note.
|
|
2362
1966
|
const answer = await transport("sendMessage", { chat_id: binding.chatId, text: effect.text, link_preview_options: { is_disabled: true },
|
|
2363
1967
|
reply_parameters: { message_id: message.message_id }, reply_markup: { force_reply: true, input_field_placeholder: effect.placeholder } });
|
|
2364
1968
|
const id = answer.result?.message_id;
|
|
@@ -2368,7 +1972,6 @@ function applyCallback(context, update, effects) {
|
|
|
2368
1972
|
}
|
|
2369
1973
|
return;
|
|
2370
1974
|
}
|
|
2371
|
-
// A "Person chooses" option (flow-send.ts): the card goes where it leads, or is ignored.
|
|
2372
1975
|
const choiceAction = store.getTelegramFlowChoice(token);
|
|
2373
1976
|
if (choiceAction !== null) {
|
|
2374
1977
|
if (choiceAction.binding !== binding.id || choiceAction.chatId !== tapChat || (choiceAction.messageId !== null && choiceAction.messageId !== String(message.message_id))) {
|
|
@@ -2380,13 +1983,11 @@ function applyCallback(context, update, effects) {
|
|
|
2380
1983
|
for (const effect of applyFlowChoiceTap(store, binding, choiceAction, { text: message.text ?? "" }, repos, clock())) {
|
|
2381
1984
|
if (effect.kind === "ack")
|
|
2382
1985
|
ack(effect.text);
|
|
2383
|
-
// An item list's message (flow-items.ts) keeps its bold titles and labelled links under what was chosen; any other stays plain.
|
|
2384
1986
|
else if (effect.kind === "edit")
|
|
2385
1987
|
editText(effect.text, undefined, listed ? keptEntities(message.entities, effect.text.length) : undefined);
|
|
2386
1988
|
}
|
|
2387
1989
|
return;
|
|
2388
1990
|
}
|
|
2389
|
-
// The lead's question to its owner: an option is sent as their next message; "Something else" asks them to type it.
|
|
2390
1991
|
const askTap = askOf(token);
|
|
2391
1992
|
if (askTap !== null) {
|
|
2392
1993
|
if (tapChat !== binding.chatId || context.conversation === undefined) {
|
|
@@ -2430,7 +2031,6 @@ function applyCallback(context, update, effects) {
|
|
|
2430
2031
|
editText(`${message.text ?? ask.question}\n\nYou chose: ${option}`.slice(0, 4000));
|
|
2431
2032
|
return;
|
|
2432
2033
|
}
|
|
2433
|
-
// A result's, plan's, failure's or pull request's own buttons (chat-decide.ts): in the person's own chat only.
|
|
2434
2034
|
if (isDecideToken(store, token)) {
|
|
2435
2035
|
const repos = context.projects === null ? null : telegramConversationRepos(store, binding.approver, context.projects);
|
|
2436
2036
|
if (repos === null) {
|
|
@@ -2447,8 +2047,6 @@ function applyCallback(context, update, effects) {
|
|
|
2447
2047
|
if (tapped.ignored === true)
|
|
2448
2048
|
report.ignored++;
|
|
2449
2049
|
ack(tapped.ack.slice(0, 190));
|
|
2450
|
-
// A release card's terms, as a file, before its Approve is shown (chat-management.md §0a.5): Approve appears only
|
|
2451
|
-
// once Telegram has accepted this file; a refused or failed upload shows the card without it, Continue to try again.
|
|
2452
2050
|
if (tapped.file !== undefined && tapped.edit !== undefined && tapped.unsent !== undefined) {
|
|
2453
2051
|
const file = tapped.file, edit = tapped.edit, unsent = tapped.unsent;
|
|
2454
2052
|
effects.push(async () => {
|
|
@@ -2466,7 +2064,6 @@ function applyCallback(context, update, effects) {
|
|
|
2466
2064
|
});
|
|
2467
2065
|
}
|
|
2468
2066
|
else if (tapped.file !== undefined) {
|
|
2469
|
-
// A file without its gate never reaches here (chat-decide pairs them); fail closed: nothing that approves is shown.
|
|
2470
2067
|
if (tapped.edit !== undefined)
|
|
2471
2068
|
dropDecideTokens(store, tapped.edit.tokens, clock());
|
|
2472
2069
|
editText(`${message.text ?? ""}\n\nThis release can't be approved here. Open it in Toolroll.`.trim().slice(0, 4000), []);
|
|
@@ -2485,7 +2082,6 @@ function applyCallback(context, update, effects) {
|
|
|
2485
2082
|
}
|
|
2486
2083
|
if (tapped.merge !== undefined) {
|
|
2487
2084
|
const merge = tapped.merge, shown = tapped.edit?.text ?? message.text ?? "";
|
|
2488
|
-
// GitHub is a network call: after the tap's transaction, then the card says how it went.
|
|
2489
2085
|
effects.push(async () => {
|
|
2490
2086
|
let merged;
|
|
2491
2087
|
try {
|
|
@@ -2495,9 +2091,7 @@ function applyCallback(context, update, effects) {
|
|
|
2495
2091
|
catch {
|
|
2496
2092
|
merged = { ok: false, message: "GitHub couldn't be reached just now. Try again from the task." };
|
|
2497
2093
|
}
|
|
2498
|
-
// The ledger says what happened, failure included, once GitHub has answered.
|
|
2499
2094
|
recordChatMerge(store, merge, merged.ok ? { ok: true } : { ok: false, message: merged.message }, clock());
|
|
2500
|
-
// A merge that didn't land keeps a way to the task.
|
|
2501
2095
|
await transport("editMessageText", { chat_id: binding.chatId, message_id: message.message_id, text: mergedText(shown, merged.ok ? { ok: true } : { ok: false, message: merged.message }).slice(0, 4000),
|
|
2502
2096
|
link_preview_options: { is_disabled: true }, reply_markup: { inline_keyboard: merged.ok ? [] : decideKeyboard(context.conversation?.phoneOrigin, linksFor({ kind: "merge", taskId: merge.taskId, run: merge.runId })) } });
|
|
2503
2097
|
});
|
|
@@ -2506,9 +2100,6 @@ function applyCallback(context, update, effects) {
|
|
|
2506
2100
|
}
|
|
2507
2101
|
const action = store.getTelegramAction(token);
|
|
2508
2102
|
if (action === null && context.conversation !== undefined && store.getTelegramProposalAction(token) !== null) {
|
|
2509
|
-
// A proposal card's button: the shared confirm door, inside this
|
|
2510
|
-
// update's transaction, with the stop's process signal deferred to
|
|
2511
|
-
// after its commit (the door's own ordering, preserved from here).
|
|
2512
2103
|
const tapped = applyProposalTap(store, binding, token, message, context.projects, context.conversation, clock());
|
|
2513
2104
|
for (const effect of tapped.effects) {
|
|
2514
2105
|
if (effect.kind === "ack")
|
|
@@ -2528,7 +2119,6 @@ function applyCallback(context, update, effects) {
|
|
|
2528
2119
|
action.binding !== binding.id ||
|
|
2529
2120
|
action.chatId !== binding.chatId ||
|
|
2530
2121
|
(action.messageId !== null && action.messageId !== String(message.message_id))) {
|
|
2531
|
-
// Bound person, dead or foreign button: acknowledged, not acted on.
|
|
2532
2122
|
ack("that button is stale — toolroll decide shows what still waits");
|
|
2533
2123
|
report.ignored++;
|
|
2534
2124
|
return;
|
|
@@ -2551,8 +2141,6 @@ function applyCallback(context, update, effects) {
|
|
|
2551
2141
|
return;
|
|
2552
2142
|
}
|
|
2553
2143
|
if (expiredDraftGuard(store, binding, decision.id, clock())) {
|
|
2554
|
-
// The token is NOT consumed: the same button answers on the next tap,
|
|
2555
|
-
// now that the operator knows the note is gone.
|
|
2556
2144
|
ack("your note expired — tap again to answer without it, or reply with a fresh note first");
|
|
2557
2145
|
return;
|
|
2558
2146
|
}
|
|
@@ -2561,16 +2149,9 @@ function applyCallback(context, update, effects) {
|
|
|
2561
2149
|
return;
|
|
2562
2150
|
}
|
|
2563
2151
|
if (!option.reversible) {
|
|
2564
|
-
// The arm. Nothing is answered here: two fresh one-time tokens make a
|
|
2565
|
-
// real challenge — a stolen bot token can repaint a keyboard, but it
|
|
2566
|
-
// cannot mint a row in this table, so a tap on a forged "confirm"
|
|
2567
|
-
// lands in the stale-button branch above.
|
|
2568
2152
|
const confirm = randomBytes(16).toString("hex");
|
|
2569
2153
|
const cancel = randomBytes(16).toString("hex");
|
|
2570
2154
|
const placedOn = String(message.message_id);
|
|
2571
|
-
// The confirmation binds the EXACT answer tuple: option AND the note
|
|
2572
|
-
// it displays (its digest; null when none). A note that changes,
|
|
2573
|
-
// expires, or is cancelled strands this challenge (finding 3).
|
|
2574
2155
|
const draft = store.liveNoteDraft(binding.id, decision.id, clock());
|
|
2575
2156
|
const digest = draft === null ? undefined : noteDigestOf(draft.note);
|
|
2576
2157
|
if (draft !== null)
|
|
@@ -2592,15 +2173,11 @@ function applyCallback(context, update, effects) {
|
|
|
2592
2173
|
ack("that confirmation expired — start again from the option");
|
|
2593
2174
|
return;
|
|
2594
2175
|
}
|
|
2595
|
-
// Re-proved at the moment of commitment, not remembered from the arm.
|
|
2596
2176
|
const option = decision.options.find(one => one.id === action.optionId);
|
|
2597
2177
|
if (option === undefined) {
|
|
2598
2178
|
ack("that option no longer exists");
|
|
2599
2179
|
return;
|
|
2600
2180
|
}
|
|
2601
|
-
// The tuple the challenge displayed must still be the tuple that
|
|
2602
|
-
// travels: the CURRENT live draft's digest (or none) must equal what
|
|
2603
|
-
// was armed. Anything else strands the yes (finding 3).
|
|
2604
2181
|
const current = store.liveNoteDraft(binding.id, decision.id, clock());
|
|
2605
2182
|
const currentDigest = current === null ? null : noteDigestOf(current.note);
|
|
2606
2183
|
if ((action.noteDigest ?? null) !== currentDigest) {
|
|
@@ -2611,9 +2188,6 @@ function applyCallback(context, update, effects) {
|
|
|
2611
2188
|
answerNow(context, decision, option.id, binding, ack, editText);
|
|
2612
2189
|
return;
|
|
2613
2190
|
}
|
|
2614
|
-
// cancel: consume it, kill its sibling confirm, discard the note draft
|
|
2615
|
-
// (cancel means cancelled — the note it displayed dies with it), and
|
|
2616
|
-
// restore the choices.
|
|
2617
2191
|
store.consumeTelegramAction(token, clock());
|
|
2618
2192
|
store.consumeTelegramChallenges(decision.id, clock());
|
|
2619
2193
|
{
|
|
@@ -2626,7 +2200,6 @@ function applyCallback(context, update, effects) {
|
|
|
2626
2200
|
store.createTelegramAction({ token: choose, binding: binding.id, decision: decision.id, optionId: option.id, phase: "choose", chatId: binding.chatId, messageId: String(message.message_id) }, clock());
|
|
2627
2201
|
}
|
|
2628
2202
|
ack("cancelled");
|
|
2629
|
-
// The card again, within one message; a decision too long for one keeps the consequences its earlier parts show.
|
|
2630
2203
|
const ask = decisionAsk(decision);
|
|
2631
2204
|
editText(ask.length <= PART_CAP ? ask : `Decide: ${decision.question}`, fresh.map(({ option, token: choose }) => [
|
|
2632
2205
|
telegramButton(`${option.label}${option.id === decision.recommendation ? " ✓" : ""}${option.reversible ? "" : " ⚠"}`, choose),
|
|
@@ -2634,9 +2207,6 @@ function applyCallback(context, update, effects) {
|
|
|
2634
2207
|
}
|
|
2635
2208
|
function answerNow(context, decision, choice, binding, ack, editText) {
|
|
2636
2209
|
const { store, clock, report } = context;
|
|
2637
|
-
// The live draft is the note that travels — consumed WITH the answer in
|
|
2638
|
-
// the same transaction the whole update already holds; a CAS loss
|
|
2639
|
-
// discards it (Codex free-text review, finding 4: never choose-then-note).
|
|
2640
2210
|
const draft = store.liveNoteDraft(binding.id, decision.id, clock());
|
|
2641
2211
|
const answered = store.answerDecisionLocked({ id: decision.id, choice, by: binding.approver, via: "telegram", ...(draft === null ? {} : { note: draft.note }) }, clock());
|
|
2642
2212
|
if (answered.ok) {
|
|
@@ -2658,7 +2228,6 @@ function answerNow(context, decision, choice, binding, ack, editText) {
|
|
|
2658
2228
|
}
|
|
2659
2229
|
ack(`could not answer: ${answered.reason}`);
|
|
2660
2230
|
}
|
|
2661
|
-
/** A decision's card, action first: the question, then each option with what it does, then its deadline. */
|
|
2662
2231
|
function decisionAsk(decision) {
|
|
2663
2232
|
return [
|
|
2664
2233
|
`Decide: ${decision.question}`,
|
|
@@ -2666,7 +2235,6 @@ function decisionAsk(decision) {
|
|
|
2666
2235
|
...(decision.deadline === null ? [] : [`Decide by ${decision.deadline.slice(0, 16).replace("T", " ")} UTC`]),
|
|
2667
2236
|
].join("\n");
|
|
2668
2237
|
}
|
|
2669
|
-
/** The armed confirm of an option that can't be undone: what it is and what it does, then the note that travels. */
|
|
2670
2238
|
function armedDecisionText(option, note) {
|
|
2671
2239
|
return [
|
|
2672
2240
|
`${option.label}? ⚠ This can't be undone.`,
|
|
@@ -2681,19 +2249,12 @@ function answeredText(store, decision) {
|
|
|
2681
2249
|
return [
|
|
2682
2250
|
`✓ Answered: ${chosen}`,
|
|
2683
2251
|
`${taskOf(store, decision)} · by ${decision.answeredBy ?? "?"} via ${decision.answeredVia ?? "?"}`,
|
|
2684
|
-
// Line-prefixed, never inline: a multiline note must not be able to
|
|
2685
|
-
// draw fake status lines (Codex free-text review, finding 7).
|
|
2686
2252
|
...(decision.note === null ? [] : ["with note:", ...decision.note.split("\n").map(line => `| ${line}`)]),
|
|
2687
2253
|
].join("\n");
|
|
2688
2254
|
}
|
|
2689
2255
|
function noteDigestOf(note) {
|
|
2690
2256
|
return createHash("sha256").update(note, "utf8").digest("hex").slice(0, 32);
|
|
2691
2257
|
}
|
|
2692
|
-
/**
|
|
2693
|
-
* A pending draft that ALREADY EXPIRED must never silently drop: the tap
|
|
2694
|
-
* proceeds only after the operator is told (Codex free-text review, state
|
|
2695
|
-
* machine — "never silently answer without the expected note").
|
|
2696
|
-
*/
|
|
2697
2258
|
function expiredDraftGuard(store, binding, decisionId, now) {
|
|
2698
2259
|
const expired = store.telegramChat()
|
|
2699
2260
|
.prepare(`SELECT id FROM chat_note_draft
|
|
@@ -2704,11 +2265,8 @@ function expiredDraftGuard(store, binding, decisionId, now) {
|
|
|
2704
2265
|
store.setNoteDraftState(Number(expired["id"]), ["pending", "armed"], "discarded");
|
|
2705
2266
|
return true;
|
|
2706
2267
|
}
|
|
2707
|
-
/** Bridges in this process waiting out their push idle, by bot: a kept push ends the wait early. */
|
|
2708
2268
|
const bridgeWakers = new Map();
|
|
2709
|
-
/** Bots with a push kept while no bridge idled (it was mid-cycle): the next idle doesn't wait. */
|
|
2710
2269
|
const bridgeWokenEarly = new Set();
|
|
2711
|
-
/** A pushed update was kept for this bot: wake its bridge if it runs in this process (docs/plans/responsive-chat.md §3.1). */
|
|
2712
2270
|
export function wakeTelegramBridge(botId) {
|
|
2713
2271
|
const wakers = bridgeWakers.get(botId);
|
|
2714
2272
|
if (wakers === undefined || wakers.size === 0) {
|
|
@@ -2718,15 +2276,9 @@ export function wakeTelegramBridge(botId) {
|
|
|
2718
2276
|
for (const wake of wakers)
|
|
2719
2277
|
wake();
|
|
2720
2278
|
}
|
|
2721
|
-
/** The newest notification id now: a follower cycle's mark for what its delivery covers. */
|
|
2722
2279
|
function newestNotificationIdOf(store) {
|
|
2723
2280
|
return Number(store.handle.prepare("SELECT COALESCE(MAX(id), 0) AS id FROM notification").get()?.["id"] ?? 0);
|
|
2724
2281
|
}
|
|
2725
|
-
/**
|
|
2726
|
-
* Whether a message for this bot was written after `mark` and is still due: unresolved, addressed to everyone or to a
|
|
2727
|
-
* person paired with this bot, and the bot not held back by Telegram's rate limit. Read-only; delivery itself still
|
|
2728
|
-
* decides each row under its own fences.
|
|
2729
|
-
*/
|
|
2730
2282
|
export function telegramOutboxDue(store, botId, mark, now) {
|
|
2731
2283
|
const approvers = [...new Set(store.liveTelegramBindings(botId).map(binding => binding.approver))];
|
|
2732
2284
|
if (approvers.length === 0)
|