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/serve.js
CHANGED
|
@@ -20,10 +20,6 @@ import { html, htmlString, jsonScript, textHtml } from "./html.js";
|
|
|
20
20
|
import { browserAssetsAvailable, browserWorkspaceDocument } from './browser-shell.js';
|
|
21
21
|
import { accessKey, countsKey, indexKey, projectSummaryOf, RevisionCache, summaryKey } from './console-chrome.js';
|
|
22
22
|
import { usageChipOf } from './limits-ui.js';
|
|
23
|
-
/** Any header a proxy adds: a request carrying one is never "from this computer" (password freshness, one-time links). */
|
|
24
|
-
const PROXY_HEADER = /^(forwarded|via|x-forwarded-.+|x-real-ip|x-client-ip|x-original-.+|x-cluster-client-ip|true-client-ip|cf-connecting-ip|fastly-client-ip|x-proxyuser-ip)$/i;
|
|
25
|
-
/** How often the background checks look at what is due (docs/plans/get-side-effects.md), and how often this
|
|
26
|
-
* computer's agent sign-ins are asked again. */
|
|
27
23
|
const BACKGROUND_CHECKS_MS = 30_000;
|
|
28
24
|
const LOCAL_AGENTS_EVERY_MS = 2 * 60_000;
|
|
29
25
|
import { browserCrewFromIndex, browserCrewOf, browserNavigationOf, browserProjectsOf, needsYouLabelOf } from './browser-workspace.js';
|
|
@@ -63,48 +59,6 @@ import {} from "./tools-ui.js";
|
|
|
63
59
|
import { WORK_INDEX_MAX_LIMIT, workCountsByProject, workIndexPage } from "./work-index.js";
|
|
64
60
|
import { WORKSPACE_MOTION_SCRIPT } from "./workspace-motion.js";
|
|
65
61
|
import { prepareWorkspaceRevision, WorkspaceValidatorCache } from "./workspace-revision.js";
|
|
66
|
-
/**
|
|
67
|
-
* The web console (§7, grown per the console review): the whole built-in
|
|
68
|
-
* queue, visible and operable from a phone. `toolroll serve` — node:http,
|
|
69
|
-
* no dependencies, no JavaScript in the page. TLS is a proxy's job and the
|
|
70
|
-
* docs say so; what is not delegated is everything else:
|
|
71
|
-
*
|
|
72
|
-
* **Authentication is required on every bind, localhost included.** The
|
|
73
|
-
* credential is the approver's — the same name-and-token that approves a
|
|
74
|
-
* scope and answers in the CLI — because `answered_by` must be an identity
|
|
75
|
-
* somebody proved, not a string a request asserted. A browser logs in once
|
|
76
|
-
* (POST /login) and carries an HttpOnly SameSite=Strict session cookie; an
|
|
77
|
-
* API caller sends `Authorization: Bearer <name>:<token>` per request. The
|
|
78
|
-
* token never travels in a URL, where it would land in history and logs.
|
|
79
|
-
*
|
|
80
|
-
* **Every request proves its Host** against the names this server was told
|
|
81
|
-
* it answers as. Cookie-authenticated mutations additionally pass one
|
|
82
|
-
* centralized gate — `authorizeMutation` — that proves content type, an
|
|
83
|
-
* allowed Origin, and the per-session CSRF nonce, and refuses duplicated
|
|
84
|
-
* security fields; a mutation route cannot forget a check it never wrote.
|
|
85
|
-
* Bearer mutations carry no cookie for a hostile page to ride, so they skip CSRF.
|
|
86
|
-
* Step-up ceremonies still require a cookie session; repeating a bearer password cannot approve.
|
|
87
|
-
*
|
|
88
|
-
* **Approval is step-up.** A session alone never approves a scope: the
|
|
89
|
-
* approval form restates the goal, the exclusions, and the touches — the
|
|
90
|
-
* three fields the digest binds — and requires the approver token typed
|
|
91
|
-
* again, plus (for browsers) a single-use nonce minted when the form was
|
|
92
|
-
* rendered, bound server-side to who saw which digest of which task. A
|
|
93
|
-
* stolen cookie can read; it cannot agree to work.
|
|
94
|
-
*
|
|
95
|
-
* **GET never mutates.** Overdue-ness is derived at render time from the
|
|
96
|
-
* deadline on the row; the durable expiry sweep belongs to the CLI's
|
|
97
|
-
* surfaces and the loop, not to a crawler hitting a page.
|
|
98
|
-
*
|
|
99
|
-
* **Everything rendered is escaped at the sink**, and every identifier in an
|
|
100
|
-
* href is URL-encoded first — HTML escaping does not make `a/b?x=1` a valid
|
|
101
|
-
* path segment. The CSP is belt to those suspenders.
|
|
102
|
-
*
|
|
103
|
-
* **Evidence goes through one verified reader** (`readVerifiedArtifact`) and
|
|
104
|
-
* membership is enforced by the lookup — a decision serves only its linked
|
|
105
|
-
* artifacts, a run only its own rows, and when this server was scoped to a
|
|
106
|
-
* repo, only runs whose task belongs to that repo (or to no repo yet).
|
|
107
|
-
*/
|
|
108
62
|
import { createHash, randomBytes } from "node:crypto";
|
|
109
63
|
import { keepSlowRequest, Timeline } from "./latency-timeline.js";
|
|
110
64
|
import { readFileSync } from "node:fs";
|
|
@@ -137,7 +91,7 @@ import { canonicalProject, projectName, resolveCeiling, rowVisible } from "./pro
|
|
|
137
91
|
import { noteSignInProbe, signInNotices } from "./provider-auth.js";
|
|
138
92
|
import { createConnectionChecker } from "./provider-connection.js";
|
|
139
93
|
import { ALL_CREDENTIAL_ENV } from "./provider.js";
|
|
140
|
-
import { hstsFor, LOOPBACK_PEERS, plainHttpRefusal, transportOf } from "./public-access.js";
|
|
94
|
+
import { hstsFor, LOOPBACK_PEERS, plainHttpRefusal, requestFromThisComputer, transportOf } from "./public-access.js";
|
|
141
95
|
import { attachReadExecutor, defaultReadWorkers, ReadExecutor } from "./read-executor.js";
|
|
142
96
|
import { cachedRelease, isNewer, runnerVersions, updateChecksOff } from "./releases.js";
|
|
143
97
|
import { limitWords, FLUSH_MS as REQUEST_BUDGET_FLUSH_MS, RequestBudget, SOURCE_BUDGET_DEFAULTS, SourceAdmission } from "./request-budget.js";
|
|
@@ -158,8 +112,9 @@ import { createPeopleHandlers } from './server/people-tokens.js';
|
|
|
158
112
|
import { createSettingsHandlers } from './server/settings.js';
|
|
159
113
|
import { readPurpose, requestContext } from "./server/request-context.js";
|
|
160
114
|
import { inPlaceRoute, prefetchRoute } from "./server/in-place.js";
|
|
161
|
-
import {
|
|
162
|
-
import {
|
|
115
|
+
import { advancedShownFor } from "./advanced.js";
|
|
116
|
+
import { LOCAL_OWNER_ELSEWHERE, asLocalOwner, localModeOn, localOwnerRefusal, localOwnerStandsNow } from "./local-owner.js";
|
|
117
|
+
import { defaultAgentOf, fillDefaultRouting, fillRoutingOnSignIn } from "./model-choices.js";
|
|
163
118
|
import { chromeScript, DEMO_BANNER, DEMO_BANNER_SHORT, focusDocument, KBD_HELP, pinnedTheme, QUEUE_VIEW, refuse, screen, SENSITIVE_INPUT, shell, sidebarScript } from "./server/chrome.js";
|
|
164
119
|
import { BODY_CAP, form, loginHref, passwordSetNext, refusalNext, matchTaskPath, page, projectChatHref, redactedPath, redirect, respond, safeReturn, SHUTDOWN_WAIT_MS, taskChatHref } from "./server/http.js";
|
|
165
120
|
import { decisionsFor, LEAD_BY_DEFAULT_FACT, leadBrowserMessages, leadChatVersion, NOT_STARTED_SESSION, TASK_COMPOSER_MODES, teamProposalCardParts } from "./server/render-chat.js";
|
|
@@ -180,8 +135,6 @@ import { loadConsoleUrl } from "./webhooks.js";
|
|
|
180
135
|
import { reviewFactsOf } from "./workspace-ui.js";
|
|
181
136
|
import { WorktreePool } from "./worktree.js";
|
|
182
137
|
export function createDecisionServer(options) {
|
|
183
|
-
// Identities come from what the console's start resolved off its thread (startConsole); a path that didn't answer
|
|
184
|
-
// stays unresolved, kept restrictive. Without them (embedders, tests) the old synchronous reads apply.
|
|
185
138
|
const identities = options.identities ?? syncProjectIdentities();
|
|
186
139
|
const { projectFamilyPeek, familiesInView, familyTasksInView, revisionDestination, runIsLive, runIsTaskResult, revisionViewOf, taskViewData, routeViewOf, taskChatFocus, pullRequestTargetOf, failureOf, explainAttempt, taskScreen, armTaskResume, planViewOf, planContractViewOf, revisionDocOf, revisionLedgerOf, resultDetailOf } = createTaskViews({
|
|
187
140
|
get store() { return store; },
|
|
@@ -234,18 +187,14 @@ export function createDecisionServer(options) {
|
|
|
234
187
|
}
|
|
235
188
|
}
|
|
236
189
|
}
|
|
237
|
-
// Live terminals (docs/plans/live-terminal.md): on a machine that runs agents, when this install has a pty library.
|
|
238
190
|
const terminals = terminalHostFor(store, options);
|
|
239
191
|
const clock = options.clock ?? (() => new Date());
|
|
240
|
-
// Heavy reads (the work index behind `task list`) run on read-only worker connections, off the request loop.
|
|
241
192
|
const readFile = store.isDemo() ? null : store.databaseFile();
|
|
242
193
|
const readWorkers = options.readWorkers ?? (process.env["VITEST"] !== undefined ? 0 : defaultReadWorkers());
|
|
243
194
|
const reads = readFile === null || readWorkers < 1 ? null : new ReadExecutor(readFile, readWorkers);
|
|
244
195
|
const detachReads = reads === null ? () => { } : attachReadExecutor(store, reads);
|
|
245
|
-
/** Task checkouts, for Settings → Storage: the pool's own root, or the folder beside the database. */
|
|
246
196
|
const storagePool = (databaseFile) => new WorktreePool(store, { root: options.poolRoot ?? join(dirname(databaseFile), "worktrees") });
|
|
247
197
|
const install = installMethod(options.installBin);
|
|
248
|
-
/** What the last daily check found (the cache only, never the network), or nothing while checks are off. */
|
|
249
198
|
const updateFacts = () => {
|
|
250
199
|
if (options.configDir === undefined)
|
|
251
200
|
return null;
|
|
@@ -253,7 +202,6 @@ export function createDecisionServer(options) {
|
|
|
253
202
|
const release = switched.off ? null : cachedRelease(options.configDir);
|
|
254
203
|
return { release, newer: release !== null && isNewer(release.version, PACKAGE_VERSION), on: !switched.off, byEnv: switched.byEnv };
|
|
255
204
|
};
|
|
256
|
-
/** Settings → Updates, for everyone who can open Settings; only an operator may flip the switch. */
|
|
257
205
|
const settingsUpdates = (actor, csrf) => {
|
|
258
206
|
const facts = updateFacts();
|
|
259
207
|
if (facts === null)
|
|
@@ -271,29 +219,20 @@ export function createDecisionServer(options) {
|
|
|
271
219
|
};
|
|
272
220
|
};
|
|
273
221
|
const workspaceRevision = prepareWorkspaceRevision(store);
|
|
274
|
-
// The sidebar, the Tasks count and the crew: read at most once per workspace revision per reader's exact access
|
|
275
|
-
// (console-chrome.ts), and filled from a read worker before a page renders when one is attached.
|
|
276
222
|
const chromeReads = new RevisionCache(workspaceRevision, clock);
|
|
277
223
|
const workspaceIncarnation = randomBytes(16).toString('hex');
|
|
278
224
|
const workspaceValidators = new WorkspaceValidatorCache();
|
|
279
225
|
const providerHome = options.connectionHome ?? homedir();
|
|
280
226
|
const checkConnection = createConnectionChecker({ home: providerHome, clock, ...(options.connectionProbe === undefined ? {} : { probe: options.connectionProbe }) });
|
|
281
|
-
// Every sign-in check the console makes also answers a sign-in pause.
|
|
282
|
-
// Whether this computer can fence agents: asked once as the console starts (a sandbox running /usr/bin/true), so
|
|
283
|
-
// Settings → Project never starts a process (docs/plans/get-side-effects.md).
|
|
284
227
|
const fenceHere = options.fenceAvailable === undefined ? macosFenceAvailable() || linuxFenceAvailable() : false;
|
|
285
|
-
// Phone notifications' signing pair, made once as the console starts so /push/key only reads it.
|
|
286
228
|
if (options.telegramTokenFile !== undefined) {
|
|
287
229
|
try {
|
|
288
230
|
loadOrCreateVapidKeys(dirname(options.telegramTokenFile));
|
|
289
231
|
}
|
|
290
|
-
catch {
|
|
232
|
+
catch { }
|
|
291
233
|
}
|
|
292
|
-
// The last answer of every other check a page shows (docs/plans/get-side-effects.md).
|
|
293
234
|
const lastChecks = new LastChecks();
|
|
294
|
-
/** The newest release, asked of npm (Settings → Updates shows the last answer). */
|
|
295
235
|
const checkRelease = () => lastChecks.run("release", () => latestReleaseFor(options.updates?.latest));
|
|
296
|
-
// What each sign-in check said last: what a page shows (docs/plans/get-side-effects.md). A page never checks.
|
|
297
236
|
const knownConnections = new Map();
|
|
298
237
|
const connectionKnown = (provider) => knownConnections.get(provider) ?? null;
|
|
299
238
|
const connectionCheck = async (provider, fresh) => {
|
|
@@ -302,10 +241,9 @@ export function createDecisionServer(options) {
|
|
|
302
241
|
try {
|
|
303
242
|
noteSignInProbe(store, provider, value.state, clock());
|
|
304
243
|
}
|
|
305
|
-
catch {
|
|
244
|
+
catch { }
|
|
306
245
|
return value;
|
|
307
246
|
};
|
|
308
|
-
// Settings → Integrations: checked in the background with a short cache, as the first-run suggestions are.
|
|
309
247
|
const integrations = createIntegrationMonitor(() => ({
|
|
310
248
|
store,
|
|
311
249
|
dir: options.configDir ?? null,
|
|
@@ -326,24 +264,10 @@ export function createDecisionServer(options) {
|
|
|
326
264
|
...(options.modelPath === undefined ? {} : { path: options.modelPath }),
|
|
327
265
|
};
|
|
328
266
|
const sessions = new PersistentSessions(store);
|
|
329
|
-
// Sessions from before a restart that ran out, or whose account changed since, go now.
|
|
330
267
|
store.sweepWebSessions(Date.now(), SESSION_IDLE_MS, SESSION_ABSOLUTE_MS);
|
|
331
|
-
/** Where a new session signs in from: the browser (its user agent, short) and the address. */
|
|
332
268
|
const arrival = (request) => ({ agent: (request.headers["user-agent"] ?? "").slice(0, 300) || null, address: forwardedSourceOf(request).replace(/^fwd:/, "") });
|
|
333
|
-
/** Wrong setup codes left before the first-account road closes. */
|
|
334
269
|
let setupAttemptsLeft = 5;
|
|
335
|
-
// The /join road's limiter (D6; Codex people round 1, finding 4):
|
|
336
|
-
// PER-SOURCE buckets so one stranger cannot drain sign-up capacity for
|
|
337
|
-
// everyone, under a global ceiling that bounds total KDF work. Spent
|
|
338
|
-
// only by WELL-FORMED submissions — malformed requests are refused by
|
|
339
|
-
// the shape guards for free. The per-invite attempt counter remains the
|
|
340
|
-
// durable meter; these buckets only price the trying.
|
|
341
270
|
const joinBySource = new Map();
|
|
342
|
-
/** The per-source key (round 2, finding 4): the direct peer — except
|
|
343
|
-
* behind the documented same-host TLS proxy, where every client would
|
|
344
|
-
* share the proxy's one bucket. A LOOPBACK peer is that proxy, and only
|
|
345
|
-
* then is the forwarded chain believed, taking the LAST hop (the one
|
|
346
|
-
* the trusted proxy itself appended; earlier entries are client-typed). */
|
|
347
271
|
function forwardedSourceOf(request) {
|
|
348
272
|
const peer = request.socket.remoteAddress ?? "unknown";
|
|
349
273
|
const loopback = LOOPBACK_PEERS.has(peer);
|
|
@@ -354,52 +278,27 @@ export function createDecisionServer(options) {
|
|
|
354
278
|
const lastHop = chain.split(",").pop()?.trim() ?? "";
|
|
355
279
|
return lastHop === "" ? peer : `fwd:${lastHop.slice(0, 64)}`;
|
|
356
280
|
}
|
|
357
|
-
/** That source as every per-source budget counts it: a native IPv6 caller by its /64 (source-key.ts). */
|
|
358
281
|
function joinSourceOf(request) { return sourceKey(forwardedSourceOf(request)); }
|
|
359
282
|
const signInBudget = new SourceBudget();
|
|
360
|
-
// A re-check or a link is the session that started it: its kept hash and credential generation ride the visit and the
|
|
361
|
-
// hand-off, and finish refuses any other session (a sign-out and a new sign-in in between never inherit it).
|
|
362
283
|
const ssoVisits = new Map();
|
|
363
284
|
const ssoHandoffs = new Map();
|
|
364
285
|
let ssoProvider = null;
|
|
365
286
|
const ssoSettings = () => readSsoSettings(options.configDir);
|
|
366
287
|
const ssoOffer = () => { const settings = ssoSettings(); return settings === null ? null : { label: settings.label, operatorsOnly: settings.passwords === "operators" }; };
|
|
367
|
-
/** A step-up within this long of the provider checking someone needs no password. */
|
|
368
|
-
// The one-time sign-in link `up` opens in the browser: single use, ten minutes, from this computer only. Only a hash
|
|
369
|
-
// of the code is kept, in memory; the code itself is never written to a log or the ledger.
|
|
370
288
|
const signInLinks = new Map();
|
|
371
289
|
const linkKey = (code) => createHash("sha256").update(code).digest("hex");
|
|
372
290
|
function mintSignInLink(account) {
|
|
373
291
|
const known = store.accountOf(account);
|
|
374
|
-
// A full link is for an account with a password: a local owner without one gets only mintLocalLink's.
|
|
375
292
|
if (known === null || known.role !== "approver" || known.revokedAt !== null || !known.passwordSet)
|
|
376
293
|
return null;
|
|
377
294
|
return holdSignInLink({ kind: "full", account, expires: Date.now() + SIGN_IN_LINK_MS });
|
|
378
295
|
}
|
|
379
|
-
/** v147: this console start's key epoch (easy-start C2): the browser-key wave binds each session's mutation key to it. */
|
|
380
296
|
const keyEpoch = options.keyEpoch ?? randomBytes(16).toString("hex");
|
|
381
|
-
|
|
382
|
-
* any forwarding header at all (Forwarded, X-Forwarded-*, X-Real-IP, Via, …) disqualifies the request. */
|
|
383
|
-
const fromThisComputer = (request) => {
|
|
384
|
-
const peer = request.socket.remoteAddress ?? "";
|
|
385
|
-
if (!LOOPBACK_PEERS.has(peer))
|
|
386
|
-
return false;
|
|
387
|
-
for (const name of Object.keys(request.headers))
|
|
388
|
-
if (PROXY_HEADER.test(name))
|
|
389
|
-
return false;
|
|
390
|
-
return /^(localhost|127\.0\.0\.1|\[::1\]):[0-9]{1,5}$/.test(request.headers.host ?? "");
|
|
391
|
-
};
|
|
392
|
-
/**
|
|
393
|
-
* Cockpit B2: a request's password-freshness facts — the one place they are made (scope.ts honours them). Every
|
|
394
|
-
* reader looks at the session as it is at the moment of the act, never a snapshot taken before the body arrived:
|
|
395
|
-
* still signed in here, under the account's current credential generation, after any Forget.
|
|
396
|
-
*/
|
|
297
|
+
const fromThisComputer = (request) => requestFromThisComputer(request);
|
|
397
298
|
function freshFactsOf(request, who, allowlisted = false) {
|
|
398
299
|
if (who?.via !== 'cookie')
|
|
399
300
|
return null;
|
|
400
301
|
const session = who.session, local = fromThisComputer(request);
|
|
401
|
-
// The same validity lookupSession applies, read again at the act: held here, within its idle and absolute
|
|
402
|
-
// lifetimes, under the account's current credential generation, not revoked.
|
|
403
302
|
const live = () => {
|
|
404
303
|
const now = Date.now(), account = store.accountOf(who.name);
|
|
405
304
|
return sessions.holds(session) && sessions.savedNow(session) && sessionWithinLifetime(session, now)
|
|
@@ -411,14 +310,12 @@ export function createDecisionServer(options) {
|
|
|
411
310
|
consumeSsoNow: () => {
|
|
412
311
|
const was = session.ssoNowAt;
|
|
413
312
|
delete session.ssoNowAt;
|
|
414
|
-
// A refund puts back only what this spend took, once, and only if nothing newer arrived (Forget still floors it).
|
|
415
313
|
let refunded = false;
|
|
416
314
|
return () => { if (refunded)
|
|
417
315
|
return; refunded = true; if (was !== undefined && session.ssoNowAt === undefined)
|
|
418
316
|
session.ssoNowAt = was; };
|
|
419
317
|
},
|
|
420
318
|
floor: () => session.freshFloor ?? 0,
|
|
421
|
-
// Only a direct local request may start a password window (a typed password elsewhere proves now, nothing more).
|
|
422
319
|
stamp: at => { if (local && live())
|
|
423
320
|
session.passwordAt = at; } };
|
|
424
321
|
}
|
|
@@ -430,9 +327,6 @@ export function createDecisionServer(options) {
|
|
|
430
327
|
ssoProvider = { issuer, provider: found.provider, at: Date.now() };
|
|
431
328
|
return found;
|
|
432
329
|
}
|
|
433
|
-
/** The identity provider as last discovered: what starting a sign-in uses (a GET, so it fetches nothing,
|
|
434
|
-
* docs/plans/get-side-effects.md). Discovered as the console starts, by the background checks every ten minutes, and
|
|
435
|
-
* when sign-in settings are saved or tested. */
|
|
436
330
|
function providerKnown(issuer) {
|
|
437
331
|
return ssoProvider !== null && ssoProvider.issuer === issuer ? ssoProvider.provider : null;
|
|
438
332
|
}
|
|
@@ -441,10 +335,8 @@ export function createDecisionServer(options) {
|
|
|
441
335
|
if (settings !== null)
|
|
442
336
|
void providerFor(settings.issuer).catch(() => undefined);
|
|
443
337
|
}
|
|
444
|
-
/** Password sign-in is only for instance operators when the provider says so (a way in if it's down). */
|
|
445
338
|
const passwordAllowed = (name) => { const settings = ssoSettings(); return settings === null || settings.passwords === "everyone" || store.isInstanceOperator(name); };
|
|
446
339
|
const minutesWords = (ms) => { const minutes = Math.max(1, Math.ceil(ms / 60_000)); return minutes >= 120 ? `${Math.round(minutes / 60)} hours` : `${minutes} minute${minutes === 1 ? "" : "s"}`; };
|
|
447
|
-
/** A sign-in event names the account only when it exists: what someone typed into the name box stays out of history. */
|
|
448
340
|
const signInActor = (name) => name !== null && store.accountOf(name) !== null ? name : "unknown account";
|
|
449
341
|
const recordSignIn = (actor, action, outcome, detail = null) => {
|
|
450
342
|
try {
|
|
@@ -461,28 +353,14 @@ export function createDecisionServer(options) {
|
|
|
461
353
|
};
|
|
462
354
|
let joinGlobal = { tokens: 30, refilledAt: Date.now() };
|
|
463
355
|
const approvalNonces = new Map();
|
|
464
|
-
// Startup authority: the configured repos and roots as their identities say (startupCeiling). Missing or unresolved
|
|
465
|
-
// paths remain restrictive. Native additions come only from the controller after its admission checks.
|
|
466
356
|
const { ceiling, unresolved: unresolvedRepos, defaultProject, configured, configuredPaths } = startupCeiling([...(options.repo === undefined ? [] : [options.repo]), ...(options.repos ?? [])], options.projectRoots ?? [], options.identities === undefined ? undefined : identities, identities.strict);
|
|
467
|
-
/** No ceiling configured at all: the legacy trust-everything mode, named. */
|
|
468
|
-
// An `up` console (it is handed its admitted set) is never unscoped: with every saved project away it is empty.
|
|
469
|
-
// Judged by what was configured, never by what has resolved so far: a console whose folders haven't answered yet is
|
|
470
|
-
// scoped and empty, not unscoped.
|
|
471
357
|
const unscopedMode = !configured && options.admittedRepos === undefined;
|
|
472
|
-
/** Per-row visibility under the ceiling — the authorization question for reads. */
|
|
473
|
-
/** The exact repositories this console serves now: `up`'s live admitted set when it supplies one, else startup's. */
|
|
474
358
|
const exactRepos = () => options.admittedRepos?.() ?? ceiling.repos;
|
|
475
359
|
const liveCeiling = () => {
|
|
476
360
|
const repos = [...exactRepos(), ...(options.additionalProjectRepos?.() ?? [])];
|
|
477
|
-
// An admitted set that is empty for now is an empty ceiling, never the legacy unscoped mode.
|
|
478
361
|
return { repos: repos.length === 0 && ceiling.roots.length === 0 && !unscopedMode ? [NO_PROJECT] : repos, roots: ceiling.roots, canon: identities.strict };
|
|
479
362
|
};
|
|
480
|
-
/** The explicit, currently proved project list. The callback is supplied
|
|
481
|
-
* only by `up`, after it has independently checked git-ness and the root
|
|
482
|
-
* ceiling; this server still filters every row through its own ceiling. */
|
|
483
|
-
/** Projects deleted while this console runs: gone from its lists though a builder started with them still watches. */
|
|
484
363
|
const deletedRepos = new Set();
|
|
485
|
-
/** Those still gone: a project added again (from here, `repos add`, or the lead) has its row back and shows again. */
|
|
486
364
|
const goneRepos = () => {
|
|
487
365
|
if (deletedRepos.size === 0)
|
|
488
366
|
return deletedRepos;
|
|
@@ -503,7 +381,6 @@ export function createDecisionServer(options) {
|
|
|
503
381
|
}
|
|
504
382
|
return repos;
|
|
505
383
|
};
|
|
506
|
-
/** Settings → Project's view of a project: what's held for it, what's running, and whether this person may delete it. */
|
|
507
384
|
const projectViewOf = (repo, who) => ({
|
|
508
385
|
repo, name: projectName(repo), holdings: projectHoldings(store, repo), running: projectRunning(store, repo, clock()),
|
|
509
386
|
canDelete: who.via === "cookie" && store.isInstanceOperator(who.name),
|
|
@@ -523,18 +400,15 @@ export function createDecisionServer(options) {
|
|
|
523
400
|
liveView: (() => {
|
|
524
401
|
const commands = liveCommandsShown(store, repo);
|
|
525
402
|
const canChange = who.via === "cookie" && who.role === "approver" && !store.isDemo() && store.accountCanAccess(who.name, repo);
|
|
526
|
-
// Showing commands is consent to one exact change: a single-use nonce for this person, project and terms.
|
|
527
403
|
return { commands, canChange, ...(canChange && !commands ? { nonce: mintApprovalNonce(who.name, LIVE_VIEW_CONSENT, liveViewConsentDigest(repo)) } : {}) };
|
|
528
404
|
})(),
|
|
529
405
|
});
|
|
530
|
-
/** Settings → Project → Build access: the project's named tools and whether each loads for its builds. */
|
|
531
406
|
const projectAccessOf = (repo) => {
|
|
532
407
|
try {
|
|
533
408
|
const policy = store.orgPolicy();
|
|
534
409
|
return { tools: projectToolsOf(store, repo).map(tool => {
|
|
535
410
|
const refused = toolRefusal(policy, tool.name);
|
|
536
411
|
const set = secretsSetFor(repo, tool.spec, toolHome);
|
|
537
|
-
// As a launch decides (toolLaunchFor): a tool the organisation doesn't allow, or missing a secret, is left out.
|
|
538
412
|
return refused !== null ? { name: tool.name, loads: false, words: refused }
|
|
539
413
|
: { name: tool.name, loads: tool.spec.secrets.every(one => one.optional || set.includes(one.name)), words: toolStanding(tool, set).words };
|
|
540
414
|
}) };
|
|
@@ -543,7 +417,6 @@ export function createDecisionServer(options) {
|
|
|
543
417
|
return { problem: "Couldn't read this project's tools right now." };
|
|
544
418
|
}
|
|
545
419
|
};
|
|
546
|
-
/** Settings → Project → Builds at once: the saved number, its workers' capacity, and what builds now. */
|
|
547
420
|
const projectBuildsOf = (repo, who) => {
|
|
548
421
|
const capacities = store.listRunners().filter(one => one.retiredAt === null && one.repos.includes(repo)).map(one => one.capacity);
|
|
549
422
|
return {
|
|
@@ -553,7 +426,6 @@ export function createDecisionServer(options) {
|
|
|
553
426
|
canChange: who.via === "cookie" && who.role === "approver" && store.accountCanAccess(who.name, repo),
|
|
554
427
|
};
|
|
555
428
|
};
|
|
556
|
-
/** Codex's own MCP servers for a project (`codex mcp list --json`), for "Found on this computer"; null when Codex can't say. */
|
|
557
429
|
const codexServers = async (repo) => {
|
|
558
430
|
try {
|
|
559
431
|
const listed = await (options.codexToolList ?? (async (cwd) => {
|
|
@@ -567,38 +439,28 @@ export function createDecisionServer(options) {
|
|
|
567
439
|
return null;
|
|
568
440
|
}
|
|
569
441
|
};
|
|
570
|
-
/** One project's Tools page: its tools with which secrets are set (never values), the common tools not yet added, and what this computer already has. */
|
|
571
|
-
// A test's own folder for tool secrets and found tools; Toolroll's own (beside its database, and this home) otherwise.
|
|
572
442
|
const toolHome = options.toolHome;
|
|
573
443
|
const toolsViewOf = (repo, codex, kit = null, wanted = null) => {
|
|
574
444
|
const tools = projectToolsOf(store, repo);
|
|
575
445
|
const names = new Set(tools.map(one => one.name));
|
|
576
446
|
return {
|
|
577
447
|
repo, project: repo.split(/[\\/]/).filter(Boolean).at(-1) ?? repo,
|
|
578
|
-
// Secrets that can't be read (a refused folder, a locked keychain) read as none set: the page says why once (storage).
|
|
579
448
|
tools: tools.map(tool => { let secretsSet = []; try {
|
|
580
449
|
secretsSet = secretsSetFor(repo, tool.spec, toolHome);
|
|
581
450
|
}
|
|
582
451
|
catch {
|
|
583
452
|
secretsSet = [];
|
|
584
453
|
} return { tool, secretsSet }; }),
|
|
585
|
-
// A service that connects by signing in (or an app's own Connect) is offered only that way.
|
|
586
454
|
catalog: TOOL_CATALOG.filter(one => !names.has(one.name) && oneClickOf(one.name) === null && localConnectOf(one.name) === null),
|
|
587
455
|
connections: connectionsOf(store, repo, toolHome), kit, wanted,
|
|
588
|
-
// The project's own .mcp.json as the background pass last read it off the console's thread (never read here).
|
|
589
456
|
found: discoverTools(repo, codex, toolHome, process.env, options.identities === undefined ? undefined : savedProjectMcp.get(repo) ?? null).filter(one => !names.has(one.spec.name)),
|
|
590
457
|
storage: toolSecretStorage(),
|
|
591
458
|
};
|
|
592
459
|
};
|
|
593
460
|
const codingProjects = () => [...new Set([...managedRepos(), ...store.listProjects().map(project => project.path)])].filter(repo => rowVisible(liveCeiling(), repo));
|
|
594
|
-
/** Open chat streams (live replies), ended when the server closes. */
|
|
595
461
|
let liveTurnStarted = () => { };
|
|
596
|
-
/** Google sign-ins in progress (v89): each consent visit's state, for 10 minutes. */
|
|
597
462
|
const googleVisits = new Map();
|
|
598
|
-
/** One-click connections on their way: the service's sign-in page and back, 15 minutes at most. */
|
|
599
463
|
const connectVisits = new Map();
|
|
600
|
-
/** Flows open in a browser (v88): who's here, and a nudge when one changes. */
|
|
601
|
-
// Live views push on write (live-bus.ts): each commit moves the workspace revision, the rooms hear it.
|
|
602
464
|
const liveBus = createLiveBus();
|
|
603
465
|
const liveFollower = followWorkspace(store, () => workspaceRevision.current(), liveBus, { file: store.databaseFile() });
|
|
604
466
|
const flowRooms = createFlowRooms(flow => flowFingerprint(store, flow), { bus: liveBus });
|
|
@@ -609,9 +471,6 @@ export function createDecisionServer(options) {
|
|
|
609
471
|
provider: teamChatProvider,
|
|
610
472
|
...(options.subscriptionChatRunner ? { subscriptionRunner: options.subscriptionChatRunner } : {}),
|
|
611
473
|
});
|
|
612
|
-
// The personal lead queue (docs/plans/lead-message-queue.md). Projects and a message's task are re-proved AS the
|
|
613
|
-
// person (never from a live request's context), and its live reply display is the console's own (chat handlers below,
|
|
614
|
-
// reached only once the server is listening).
|
|
615
474
|
const asActor = (account, work) => requestContext.run({ csrf: "", returnTo: "/", actor: account }, work);
|
|
616
475
|
const admittedReposFor = (account) => asActor(account, () => managedRepos());
|
|
617
476
|
const personalQueue = createPersonalQueueRuntime({
|
|
@@ -631,12 +490,10 @@ export function createDecisionServer(options) {
|
|
|
631
490
|
return run !== null && family.versions.some(one => one.refId === run.taskRef);
|
|
632
491
|
}),
|
|
633
492
|
names: { project: projectName, modes: TASK_COMPOSER_MODES },
|
|
634
|
-
// A queued message's partials are shaped as its final reply will be: for its words, with this console's own addresses.
|
|
635
493
|
live: { begin: (thread, asked) => chatHandlers.beginLiveTurn(thread, { asked, appOrigin: [publicOrigin?.origin ?? null, options.configDir === undefined ? null : loadConsoleUrl(process.env, options.configDir)] }), end: (thread, ok, progress) => chatHandlers.endLiveTurn(thread, ok, progress) },
|
|
636
494
|
...(options.chatFetcher ? { fetcher: options.chatFetcher } : {}),
|
|
637
495
|
...(options.subscriptionChatRunner ? { subscriptionRunner: options.subscriptionChatRunner } : {}),
|
|
638
496
|
});
|
|
639
|
-
// Only browser sessions receive HTML cards. The CLI keeps its summary contract.
|
|
640
497
|
const teamBrowserReply = (reply, actor, csrf) => {
|
|
641
498
|
const snapshot = reply.snapshot;
|
|
642
499
|
if (!snapshot?.selected || !snapshot.proposals)
|
|
@@ -649,9 +506,7 @@ export function createDecisionServer(options) {
|
|
|
649
506
|
return { ...summary, card: teamProposalCardParts(store, actor, proposal, snapshot, csrf, decision, clock(), teamChatProvider).card };
|
|
650
507
|
}) } };
|
|
651
508
|
};
|
|
652
|
-
// v112: one request budget per server for person API tokens, shared by every route a token signs in at (request-budget.ts).
|
|
653
509
|
const requestBudget = new RequestBudget({ store, ...(options.requestBudgetClock === undefined ? {} : { clock: options.requestBudgetClock }) });
|
|
654
|
-
// Unproved password attempts share a source budget. Only successful proof spends an account budget.
|
|
655
510
|
const sourceClock = options.requestBudgetClock === undefined ? {} : { clock: options.requestBudgetClock };
|
|
656
511
|
const passwordSourceBudget = new SourceAdmission({ perMinute: SOURCE_BUDGET_DEFAULTS.password, ...sourceClock });
|
|
657
512
|
const passwordAccountBudget = new SourceAdmission({ perMinute: SOURCE_BUDGET_DEFAULTS.password, keyMode: "exact", ...sourceClock });
|
|
@@ -660,27 +515,19 @@ export function createDecisionServer(options) {
|
|
|
660
515
|
const oauthTokenBudget = new SourceAdmission({ perMinute: SOURCE_BUDGET_DEFAULTS.oauthToken, ...sourceClock });
|
|
661
516
|
const presentsPassword = (request) => /^Bearer (.+):(.+)$/.test(request.headers.authorization ?? "");
|
|
662
517
|
const admitPasswordSource = (request) => presentsPassword(request) ? passwordSourceBudget.admit(joinSourceOf(request)) : { ok: true };
|
|
663
|
-
/** Called only with a proved identity; cookies spend neither bearer budget. */
|
|
664
518
|
const admitBearer = (request, actor) => {
|
|
665
519
|
if (actor.principal !== undefined)
|
|
666
520
|
return requestBudget.admit(actor.principal.tokenId, "api");
|
|
667
521
|
return presentsPassword(request) ? passwordAccountBudget.admit(actor.name) : { ok: true };
|
|
668
522
|
};
|
|
669
|
-
/** Every project this person may pick in the console — the switcher, Projects, Settings, Tools and Flows read this
|
|
670
|
-
* one list: the live admitted set (taskless projects included) and every project with saved work, each through
|
|
671
|
-
* the ceiling and this account's project grants. A project removed or deleted here is gone from it. */
|
|
672
523
|
const consoleProjects = () => {
|
|
673
524
|
const gone = goneRepos();
|
|
674
525
|
return [...new Set([...(admissionList() ?? []), ...managedRepos(), ...store.knownRepos()])].filter(one => visible(one) && !gone.has(one));
|
|
675
526
|
};
|
|
676
527
|
const server = createServer((request, response) => {
|
|
677
528
|
instrumentRequest(store.telemetry, request, response);
|
|
678
|
-
// Every password checked while answering counts against where this request came from (sign-in-guard.ts).
|
|
679
|
-
// A GET or HEAD only reads (docs/plans/get-side-effects.md): store reads that would tidy up as they go don't.
|
|
680
529
|
const answer = () => consoleFolders(options, identities, () => handle(request, response));
|
|
681
530
|
void withPasswordSource(joinSourceOf(request), () => request.method === "GET" || request.method === "HEAD" ? asReadOnlyRequest(answer) : answer()).catch(error => {
|
|
682
|
-
// Every unhandled error is logged (v99): the path and the message, never the request's body or query. The reference
|
|
683
|
-
// is what a person's error page shows, so the log line can be found from it.
|
|
684
531
|
const reference = `err_${randomBytes(4).toString("hex")}`;
|
|
685
532
|
logEvent("error", "serve.error", { method: request.method, path: redactedPath(new URL(request.url ?? "/", "http://placeholder").pathname), error: error instanceof Error ? error.message : String(error), reference });
|
|
686
533
|
if (envValue(process.env, "SERVE_DEBUG") === "1")
|
|
@@ -689,15 +536,13 @@ export function createDecisionServer(options) {
|
|
|
689
536
|
const updating = error instanceof Error && error.message.includes(UPDATE_PAUSED);
|
|
690
537
|
if (updating)
|
|
691
538
|
response.setHeader("Retry-After", "5");
|
|
692
|
-
// A browser asking for a page gets a page (Wave E): what happened, whether anything changed, the reference. It
|
|
693
|
-
// holds no error text. Everything else keeps the plain answer.
|
|
694
539
|
if (/\btext\/html\b/.test(String(request.headers.accept ?? "")) && (request.method === "GET" || request.method === "POST")) {
|
|
695
540
|
const read = request.method === "GET";
|
|
696
541
|
const at = new URL(request.url ?? "/", "http://placeholder");
|
|
697
542
|
try {
|
|
698
543
|
return page(response, updating ? 503 : 500, serverErrorPage({ updating, read, reference, at: new Date(), retry: read ? at.pathname + at.search : null }));
|
|
699
544
|
}
|
|
700
|
-
catch {
|
|
545
|
+
catch { }
|
|
701
546
|
}
|
|
702
547
|
respond(response, updating ? 503 : 500, "text/plain; charset=utf-8", updating ? UPDATE_PAUSED : "something broke");
|
|
703
548
|
}
|
|
@@ -708,9 +553,6 @@ export function createDecisionServer(options) {
|
|
|
708
553
|
});
|
|
709
554
|
server.once('listening', () => store.telemetry.start());
|
|
710
555
|
server.once('close', () => store.telemetry.stop());
|
|
711
|
-
// --public-url (arc 3): validated to EXACTLY an https origin. Its host
|
|
712
|
-
// joins the allowed set, its origin authorizes POSTs, and cookies turn
|
|
713
|
-
// Secure. Anything malformed refuses at startup, loudly.
|
|
714
556
|
const publicOrigin = (() => {
|
|
715
557
|
if (options.publicUrl === undefined)
|
|
716
558
|
return null;
|
|
@@ -727,9 +569,7 @@ export function createDecisionServer(options) {
|
|
|
727
569
|
return parsed;
|
|
728
570
|
})();
|
|
729
571
|
const cookieSecure = publicOrigin === null ? "" : "; Secure";
|
|
730
|
-
/** Where Google may send someone back to (v89): the public https address, or this computer's own. */
|
|
731
572
|
const consoleOrigin = (host) => host === undefined ? null : publicOrigin !== null && host === publicOrigin.host ? publicOrigin.origin : /^(localhost|127\.0\.0\.1|\[::1\]):[0-9]{1,5}$/.test(host) ? `http://${host}` : null;
|
|
732
|
-
/** This computer's tailnet names, the last ones read (onboarding). */
|
|
733
573
|
let tailnet = [];
|
|
734
574
|
if (options.tailnetNames !== undefined) {
|
|
735
575
|
const readTailnet = options.tailnetNames;
|
|
@@ -743,7 +583,7 @@ export function createDecisionServer(options) {
|
|
|
743
583
|
const flushBudget = () => { try {
|
|
744
584
|
requestBudget.flush();
|
|
745
585
|
}
|
|
746
|
-
catch {
|
|
586
|
+
catch { } };
|
|
747
587
|
const timer = setInterval(flushBudget, REQUEST_BUDGET_FLUSH_MS);
|
|
748
588
|
timer.unref?.();
|
|
749
589
|
server.on("close", () => { clearInterval(timer); flushBudget(); });
|
|
@@ -752,7 +592,6 @@ export function createDecisionServer(options) {
|
|
|
752
592
|
const address = server.address();
|
|
753
593
|
return typeof address === "object" && address !== null ? address.port : null;
|
|
754
594
|
};
|
|
755
|
-
/** The names this server answers as. Anything else is a rebind, refused. */
|
|
756
595
|
const allowedHost = (host) => {
|
|
757
596
|
if (host === undefined)
|
|
758
597
|
return false;
|
|
@@ -760,8 +599,6 @@ export function createDecisionServer(options) {
|
|
|
760
599
|
const locals = port === null ? [] : [`localhost:${port}`, `127.0.0.1:${port}`, `[::1]:${port}`, ...tailnet.map(name => `${name}:${port}`)];
|
|
761
600
|
return [...locals, ...(options.allowedHosts ?? []), ...(publicOrigin === null ? [] : [publicOrigin.host])].includes(host);
|
|
762
601
|
};
|
|
763
|
-
/** Easy-start C2: a protected loopback install (bound to loopback only, no --allow-host, no public URL, no tailnet
|
|
764
|
-
* names) has one browser host, and there every browser mutation also presents the session's key (mutation-key.ts). */
|
|
765
602
|
const browserKeys = createBrowserKeys({
|
|
766
603
|
enabled: options.browserKey === true,
|
|
767
604
|
epoch: keyEpoch,
|
|
@@ -774,7 +611,6 @@ export function createDecisionServer(options) {
|
|
|
774
611
|
return address.address === "127.0.0.1" ? `127.0.0.1:${port}` : address.address === "::1" ? `[::1]:${port}` : null;
|
|
775
612
|
},
|
|
776
613
|
});
|
|
777
|
-
/** The exact command that starts this console again as it runs now, plus a listening address or one more allowed host. */
|
|
778
614
|
const consoleCommand = (change) => {
|
|
779
615
|
const port = servedPort() ?? 4180;
|
|
780
616
|
const address = server.address();
|
|
@@ -784,7 +620,6 @@ export function createDecisionServer(options) {
|
|
|
784
620
|
return [`toolroll ${options.upConsole === true ? "up" : "serve"}`, ...(host === null ? [] : [`--host ${host}`]), ...(port === 4180 ? [] : [`--port ${port}`]),
|
|
785
621
|
...(allowed.length === 0 ? [] : [`--allow-host ${allowed.join(",")}`])].join(" ");
|
|
786
622
|
};
|
|
787
|
-
/** The wrong-host page's facts: the address opened, where the console answers, and the exact command that admits it. */
|
|
788
623
|
const wrongHost = (host) => {
|
|
789
624
|
const opened = host !== undefined && /^[A-Za-z0-9.-]{1,253}(:[0-9]{1,5})?$|^\[[0-9A-Fa-f:.]{2,45}\](:[0-9]{1,5})?$/.test(host) ? host : null;
|
|
790
625
|
return { opened, served: `127.0.0.1:${servedPort() ?? 4180}`, command: opened === null ? null : consoleCommand({ allow: opened }) };
|
|
@@ -799,7 +634,6 @@ export function createDecisionServer(options) {
|
|
|
799
634
|
approvalNonces.set(nonce, { name, taskId, digest, expiresAt: Date.now() + NONCE_TTL_MS });
|
|
800
635
|
return nonce;
|
|
801
636
|
}
|
|
802
|
-
/** Single use, bound to who saw which digest of which task, and young. */
|
|
803
637
|
function consumeApprovalNonce(nonce, name, taskId, digest) {
|
|
804
638
|
const held = approvalNonces.get(nonce);
|
|
805
639
|
if (held === undefined)
|
|
@@ -812,22 +646,13 @@ export function createDecisionServer(options) {
|
|
|
812
646
|
}
|
|
813
647
|
const hookContext = { store, clock, options };
|
|
814
648
|
async function handle(request, response) {
|
|
815
|
-
// Webhooks come through a public relay (Tailscale Funnel, a reverse
|
|
816
|
-
// proxy) that forwards its own host name. The host check guards pages a
|
|
817
|
-
// signed-in browser reads; a delivery carries no session and its answer
|
|
818
|
-
// reveals nothing, so /hooks/ is answered before it, and only there.
|
|
819
649
|
const hook = new URL(request.url ?? "/", "http://placeholder");
|
|
820
|
-
// v99: a liveness probe for a load balancer or orchestrator. It answers before the host check (a probe
|
|
821
|
-
// uses the address it reached), reads one row, and says nothing else about the installation.
|
|
822
650
|
const method = request.method ?? "GET";
|
|
823
651
|
const matchedRoute = matchRoute(method, hook.pathname);
|
|
824
|
-
// Where a slow answer's time went (latency-timeline.ts): milestones by route id only, kept when it took a second.
|
|
825
652
|
const timeline = new Timeline();
|
|
826
653
|
timeline.mark("route matched");
|
|
827
654
|
const edgeRoute = matchedRoute?.stage === 'edge' ? matchedRoute : null;
|
|
828
655
|
const wrongMethodEdge = edgeRoute === null ? ROUTES.find(row => row.stage === 'edge' && new RegExp(row.pattern).test(hook.pathname)) ?? null : null;
|
|
829
|
-
// D7: a password on a request still signs in this release, announced on every answer and named in every refusal;
|
|
830
|
-
// from the release that removes it, it is refused here before anything checks it.
|
|
831
656
|
if (isPasswordBearer(request.headers.authorization)) {
|
|
832
657
|
if (!(options.passwordBearerAccepted ?? passwordBearerAccepted())) {
|
|
833
658
|
request.resume();
|
|
@@ -844,9 +669,6 @@ export function createDecisionServer(options) {
|
|
|
844
669
|
if (!allowedHost(request.headers.host)) {
|
|
845
670
|
return respond(response, 421, "text/html; charset=utf-8", wrongHostPage(wrongHost(request.headers.host)));
|
|
846
671
|
}
|
|
847
|
-
// Easy-start C2: a protected loopback install has one browser origin, whose storage holds the session's mutation key.
|
|
848
|
-
// A browser's page read at another loopback name (localhost, [::1]) moves there; machine callers and protocol
|
|
849
|
-
// adapters (an API token, MCP's own addresses) are left where they are.
|
|
850
672
|
const canonical = browserKeys.canonicalHost();
|
|
851
673
|
if (canonical !== null && request.headers.host !== canonical && (method === "GET" || method === "HEAD") && request.headers.authorization === undefined
|
|
852
674
|
&& (edgeRoute === null || edgeRoute.domain === "people")) {
|
|
@@ -855,8 +677,6 @@ export function createDecisionServer(options) {
|
|
|
855
677
|
response.end();
|
|
856
678
|
return;
|
|
857
679
|
}
|
|
858
|
-
// A real domain (public-access.ts): HSTS for the public https host reached over HTTPS, and no API token over
|
|
859
|
-
// plain HTTP from outside this computer and the tailnet — refused before anything reads or checks it.
|
|
860
680
|
const transport = transportOf({ peer: request.socket.remoteAddress, joinSource: forwardedSourceOf(request), forwardedProto: request.headers["x-forwarded-proto"], forwarded: request.headers["forwarded"] });
|
|
861
681
|
const hsts = hstsFor(publicOrigin?.host ?? null, request.headers.host, transport);
|
|
862
682
|
if (hsts !== null)
|
|
@@ -867,31 +687,19 @@ export function createDecisionServer(options) {
|
|
|
867
687
|
return respond(response, 403, "text/plain; charset=utf-8", insecure);
|
|
868
688
|
}
|
|
869
689
|
const url = new URL(request.url ?? "/", "http://placeholder");
|
|
870
|
-
// A token in a URL is a token in history, logs, and referers. Refused
|
|
871
|
-
// outright rather than ignored, so nobody learns the habit works.
|
|
872
690
|
if (url.searchParams.has("token")) {
|
|
873
691
|
return respond(response, 400, "text/plain; charset=utf-8", "credentials never travel in URLs");
|
|
874
692
|
}
|
|
875
693
|
if (edgeRoute === null && isEdgeAddress(url.pathname))
|
|
876
694
|
return refuseEdge(request, response, url.pathname, wrongMethodEdge);
|
|
877
|
-
// Live rooms share the browser identity/context below; their adapter carries token limits into each room.
|
|
878
695
|
if (edgeRoute !== null && edgeRoute.domain !== 'people' && edgeRoute.domain !== 'live')
|
|
879
696
|
return dispatchEdge(edgeRoute, { url, who: null, request, response, method });
|
|
880
|
-
// Passive polls authenticate without extending the browser session.
|
|
881
697
|
const fragmentName = url.searchParams.get("fragment");
|
|
882
698
|
const workspaceRead = method === "GET" && url.searchParams.get('format') === 'workspace';
|
|
883
|
-
// A workspace read's purpose (live-pages-and-home.md A1.4): bookkeeping only. Read on workspace GETs alone; on any other
|
|
884
|
-
// request the header means nothing. No authorisation decision below reads it.
|
|
885
699
|
const purpose = workspaceRead ? readPurpose(request.headers['x-toolroll-read']) : 'poll';
|
|
886
|
-
// The reads that never extend the session, whatever their header says.
|
|
887
700
|
const noTouch = edgeRoute?.domain === 'live' || (fragmentName !== null && NO_TOUCH_FRAGMENTS.has(fragmentName)) || /^\/code\/[a-f0-9]{32}\/state$/.test(url.pathname) || url.pathname === '/term/state'
|
|
888
|
-
// The Agents view's Live grid only watches: its polls never extend or write the session.
|
|
889
701
|
|| url.pathname === '/agents/live' || /^\/agents\/live\/[0-9]{1,15}$/.test(url.pathname) || (url.pathname === '/agents' && url.searchParams.get('view') === 'live');
|
|
890
|
-
// A followed link (`navigate`) extends the session exactly as the HTML GET of the same address would; every other
|
|
891
|
-
// workspace read (a re-read, a prefetch) does not.
|
|
892
702
|
const touch = !(method === "GET" && (noTouch || (workspaceRead && purpose !== 'navigate')));
|
|
893
|
-
// A read ahead is answered only for a browser session, only for the prefetch allowlist (in-place.ts): anything else
|
|
894
|
-
// hears 204 before any sign-in bookkeeping (a token's last use, a sign-in charge) is written.
|
|
895
703
|
if (purpose === 'prefetch' && (request.headers.authorization !== undefined || !prefetchRoute(matchedRoute?.id, url))) {
|
|
896
704
|
request.resume();
|
|
897
705
|
return respond(response, 204, "text/plain; charset=utf-8", "");
|
|
@@ -905,12 +713,16 @@ export function createDecisionServer(options) {
|
|
|
905
713
|
respond(response, admitted.status, "text/plain; charset=utf-8", admitted.status === 429 ? limitWords(admitted.limit, admitted.retryAfter) : "Request limits could not be checked; nothing ran. Try again shortly.");
|
|
906
714
|
return true;
|
|
907
715
|
};
|
|
908
|
-
// One source charge before password proof and one account/token charge after it, including GET /login.
|
|
909
716
|
if (refuseBudget(admitPasswordSource(request)))
|
|
910
717
|
return;
|
|
911
718
|
const refusedToken = {};
|
|
912
719
|
const who = identify(request, touch, edgeRoute?.domain === 'live', refusedToken);
|
|
913
720
|
timeline.mark("signed in");
|
|
721
|
+
if (refusedToken.elsewhere === true && edgeRoute?.domain !== 'people') {
|
|
722
|
+
request.resume();
|
|
723
|
+
response.setHeader("x-toolroll-refused", "local-owner-elsewhere");
|
|
724
|
+
return respond(response, 403, "text/plain; charset=utf-8", LOCAL_OWNER_ELSEWHERE);
|
|
725
|
+
}
|
|
914
726
|
if (refusedToken.principal !== undefined) {
|
|
915
727
|
if (refuseBudget(requestBudget.admit(refusedToken.principal.tokenId, "api")))
|
|
916
728
|
return;
|
|
@@ -921,7 +733,6 @@ export function createDecisionServer(options) {
|
|
|
921
733
|
}
|
|
922
734
|
if (who?.via === "bearer" && refuseBudget(admitBearer(request, who)))
|
|
923
735
|
return;
|
|
924
|
-
// A read ahead from anything but a signed-in browser session (signed out, a token): 204, never the sign-in page.
|
|
925
736
|
if (purpose === 'prefetch' && who?.via !== 'cookie') {
|
|
926
737
|
request.resume();
|
|
927
738
|
return respond(response, 204, "text/plain; charset=utf-8", "");
|
|
@@ -931,11 +742,8 @@ export function createDecisionServer(options) {
|
|
|
931
742
|
if (edgeRoute?.domain === 'people')
|
|
932
743
|
return freshFacts.run(fresh, () => provenPasswordAccount.run(passwordProof, () => dispatchEdge(edgeRoute, { url, who, request, response, method })));
|
|
933
744
|
if (who === null) {
|
|
934
|
-
// A password on a request that did not sign in is a machine's refusal, never a login page.
|
|
935
745
|
if (isPasswordBearer(request.headers.authorization))
|
|
936
746
|
return respond(response, 401, "text/plain; charset=utf-8", "authenticate first");
|
|
937
|
-
// A GET to an exact task or result (a phone's deep link) signs in and
|
|
938
|
-
// comes back to it — the destination is a same-site path only.
|
|
939
747
|
return method === "GET"
|
|
940
748
|
? redirect(response, loginHref(url.pathname + url.search))
|
|
941
749
|
: respond(response, 401, "text/plain; charset=utf-8", "authenticate first");
|
|
@@ -947,15 +755,11 @@ export function createDecisionServer(options) {
|
|
|
947
755
|
actor: who.name,
|
|
948
756
|
csrf: who.via === "cookie" ? who.session.csrf : "",
|
|
949
757
|
returnTo: safeReturn(url.pathname + url.search),
|
|
950
|
-
// This console's own addresses: a lead reply's links there read as "the task", "the result", "Settings → Lead".
|
|
951
758
|
appOrigins: [consoleOrigin(request.headers.host), publicOrigin?.origin ?? null, options.configDir === undefined ? null : loadConsoleUrl(process.env, options.configDir)],
|
|
952
|
-
// Every signed-in page shares the workspace shell; pages showing a one-time secret opt out (forceSensitive).
|
|
953
759
|
browser: who.via === 'cookie',
|
|
954
|
-
// Passwordless (read once for rendering; every act proves it again at the act): ceremony fields render as nothing.
|
|
955
760
|
passwordless: who.via === 'cookie' && passwordlessOn(store),
|
|
761
|
+
advanced: advancedShownFor(store, who.name),
|
|
956
762
|
openProject: who.via === 'cookie' ? who.session.project : null,
|
|
957
|
-
// v100: signed in with the identity provider: its label, and whether it checked them recently enough to stand in for a password.
|
|
958
|
-
// Cockpit B2: "fresh" is an explicit re-check made on request and not yet spent (read live; one step-up uses it).
|
|
959
763
|
sso: who.via === 'cookie' && who.session.sso !== undefined ? { label: ssoSettings()?.label ?? "your identity provider",
|
|
960
764
|
get fresh() { return freshFacts.getStore() != null && ssoNowAvailable(who.name); } } : undefined,
|
|
961
765
|
refusal: (answer, status, body) => sendScreen(answer, status, screen(status === 404 ? "Not found" : "Request refused", body, { chrome: chromeFor(who.via === "cookie" ? who.session.project : null, "work") })),
|
|
@@ -967,15 +771,11 @@ export function createDecisionServer(options) {
|
|
|
967
771
|
...(workspaceRead && typeof request.headers['if-none-match'] === 'string' ? { workspaceIfNoneMatch: request.headers['if-none-match'] } : {}),
|
|
968
772
|
workspaceRequest: url.searchParams.get('request'),
|
|
969
773
|
timeline,
|
|
970
|
-
// Easy-start C2: every page that posts carries the key fill while the key is required; a session whose key is from
|
|
971
|
-
// an earlier start (or has none) is told to reconnect.
|
|
972
774
|
keyFill: browserKeys.required(),
|
|
973
|
-
// Easy start: a local owner with no password sees "Set a password to do this." where a step-up field would be.
|
|
974
775
|
...(who.via === "cookie" && who.authority === "local-owner" && store.accountOf(who.name)?.passwordSet === false
|
|
975
776
|
? { localStepUp: localOwnerRefusal(false, method === "GET" ? passwordSetNext(pagePathOf(url)) : "/").href } : {}),
|
|
976
777
|
reconnect: browserKeys.stale(who, method === "GET" ? safeReturn(pagePathOf(url)) : "/"),
|
|
977
778
|
};
|
|
978
|
-
// A read ahead writes nothing, not even a slow request's record.
|
|
979
779
|
if (purpose !== 'prefetch')
|
|
980
780
|
response.once("finish", () => keepSlowRequest(store, timeline, matchedRoute?.id ?? "unmatched", response.statusCode, clock()));
|
|
981
781
|
if (method === "GET" || method === "POST")
|
|
@@ -985,16 +785,11 @@ export function createDecisionServer(options) {
|
|
|
985
785
|
const body = method === "POST" ? await form(request, (matchedRoute?.stage === "console" ? matchedRoute.bodyCap : undefined) ?? BODY_CAP) : null;
|
|
986
786
|
const target = actionTarget(url, who, request, body === null ? null : readForm(body, CONSOLE_FORMS.ledgerTarget));
|
|
987
787
|
const execute = async () => {
|
|
988
|
-
// THE ROUTE TABLE (server/route-table.ts): nothing reaches a handler unless a row declares this method and
|
|
989
|
-
// path and admits this kind of caller. Undeclared addresses retain the legacy instance-access refusal
|
|
990
|
-
// for project-limited accounts, before the unrestricted router's not-found response.
|
|
991
788
|
const route = matchedRoute?.stage === "console" ? matchedRoute : null;
|
|
992
789
|
if (route === null)
|
|
993
790
|
return restricted()
|
|
994
791
|
? refuse(response, who, 403, "This area requires instance access. Your account operates within its assigned projects.", "/projects")
|
|
995
792
|
: refuse(response, who, 404, "There's no page at this address.", "/chat");
|
|
996
|
-
// Easy-start C1: a local owner's session reaches only the rows marked for it (route-table.ts `local`), decided
|
|
997
|
-
// first, before the route's policy reads anything and before any handler runs, GET and POST alike.
|
|
998
793
|
if (who.via === "cookie" && who.authority !== "full" && (route.local !== "allow" || localPrincipalOf(who, route) === null)) {
|
|
999
794
|
request.resume();
|
|
1000
795
|
return refuseLocalOwner(response, who, method, url, request.headers.accept);
|
|
@@ -1006,8 +801,6 @@ export function createDecisionServer(options) {
|
|
|
1006
801
|
request.resume();
|
|
1007
802
|
if (method === "POST" && who.via === "bearer" && who.principal?.scope === "read")
|
|
1008
803
|
return refuse(response, who, 403, REMOTE_MESSAGES.read);
|
|
1009
|
-
// A viewer's act stays refused even when the table rejects their caller first (for example a
|
|
1010
|
-
// password bearer at a step-up route). Preserve the viewer wording and explicit protocol refusals.
|
|
1011
804
|
if (policy.reason === "scope" || (method === "POST" && who.role === "viewer" && !route.viewer && route.callerRefusal === undefined)) {
|
|
1012
805
|
return refuse(response, who, 403, route.scopeRefusal ?? "your login can watch, not act — ask an approver to upgrade you");
|
|
1013
806
|
}
|
|
@@ -1016,8 +809,6 @@ export function createDecisionServer(options) {
|
|
|
1016
809
|
: respond(response, route.callerRefusal.status, route.callerRefusal.type, route.callerRefusal.body);
|
|
1017
810
|
}
|
|
1018
811
|
if (method === "GET") {
|
|
1019
|
-
// Authorize every read before considering a conditional response.
|
|
1020
|
-
// Task/result opening and receipt reconciliation always re-read work.
|
|
1021
812
|
const facts = requestContext.getStore();
|
|
1022
813
|
if (facts?.workspaceRead && who.via === 'cookie' && route.id === 'chat.page' &&
|
|
1023
814
|
!url.searchParams.has('task') && !url.searchParams.has('result') && !url.searchParams.has('request')) {
|
|
@@ -1031,15 +822,11 @@ export function createDecisionServer(options) {
|
|
|
1031
822
|
return respond(response, 304, 'application/json; charset=utf-8', '');
|
|
1032
823
|
}
|
|
1033
824
|
const expiresAt = workspaceRevision.expiresAt(now);
|
|
1034
|
-
// A clock-bound refresh receives a different validator even when
|
|
1035
|
-
// no database row changed (for example an expired worker lease).
|
|
1036
825
|
facts.workspaceValidator = { key, revision, expiresAt,
|
|
1037
826
|
etag: '"' + createHash('sha256').update(`${key}:${revision}:${expiresAt}`).digest('hex') + '"' };
|
|
1038
827
|
}
|
|
1039
828
|
await prefetchChrome(route, url, who);
|
|
1040
829
|
timeline.mark("chrome and worker reads");
|
|
1041
|
-
// The worker wait lets a sign-out, credential/access change or console ceiling change land. Prove the
|
|
1042
|
-
// cookie and route again before rendering protected content or answering with a content validator.
|
|
1043
830
|
if (who.via === 'cookie') {
|
|
1044
831
|
const current = identify(request, false);
|
|
1045
832
|
if (current?.via !== 'cookie' || current.session !== who.session)
|
|
@@ -1051,16 +838,12 @@ export function createDecisionServer(options) {
|
|
|
1051
838
|
}
|
|
1052
839
|
return handlePost(route, url, who, request, response, body);
|
|
1053
840
|
};
|
|
1054
|
-
// Easy-start C1: an allowed row runs as the local owner, in the act the row is (execute refuses every other): the
|
|
1055
|
-
// prover answers "local-owner" only for that act, and every write it makes re-proves it still stands.
|
|
1056
841
|
const local = who.via === "cookie" && who.authority !== "full" && matchedRoute?.stage === "console" && matchedRoute.local === "allow" ? localPrincipalOf(who, matchedRoute) : null;
|
|
1057
842
|
if (local !== null)
|
|
1058
843
|
response.once("close", () => { local.request.ended = true; });
|
|
1059
844
|
const run = () => local === null ? execute() : asLocalOwner(local, execute);
|
|
1060
845
|
if (method === "GET")
|
|
1061
846
|
return projectAuthority.run({ actor: who.name, repo: target.repo }, run);
|
|
1062
|
-
// Request acceptance is separate from durable work completion. Never
|
|
1063
|
-
// persist a body, query string, token, password, or arbitrary URL.
|
|
1064
847
|
const entry = { at: clock().toISOString(), actor: who.name, ...target, source: "request", ...(who.via === "bearer" && who.token !== undefined ? { detail: `API token: ${who.token}` } : {}) };
|
|
1065
848
|
store.recordAction({ ...entry, outcome: "requested" });
|
|
1066
849
|
try {
|
|
@@ -1084,7 +867,7 @@ export function createDecisionServer(options) {
|
|
|
1084
867
|
try {
|
|
1085
868
|
repo = coding.get(match[1], { name: who.name, generation: who.session.generation }).repo;
|
|
1086
869
|
}
|
|
1087
|
-
catch {
|
|
870
|
+
catch { }
|
|
1088
871
|
}
|
|
1089
872
|
return { repo, taskId: null, runId: null, action: `coding ${body ? url.pathname.split('/').at(-1) : 'view'}` };
|
|
1090
873
|
}
|
|
@@ -1135,9 +918,6 @@ export function createDecisionServer(options) {
|
|
|
1135
918
|
if (project === undefined) {
|
|
1136
919
|
return refuse(response, who, 403, "that project is outside what this server was configured to show");
|
|
1137
920
|
}
|
|
1138
|
-
// A project link is a read context, not a session-changing operation.
|
|
1139
|
-
// Prove it against both admission and the known project catalog before
|
|
1140
|
-
// collection queries; unknown/foreign paths reveal no work.
|
|
1141
921
|
if ((route.id === 'tasks' || route.id === 'work' || route.id === 'system') && url.searchParams.has('project')) {
|
|
1142
922
|
const wanted = url.searchParams.get('project') ?? '';
|
|
1143
923
|
if (url.searchParams.getAll('project').length !== 1 || !visible(wanted) ||
|
|
@@ -1146,8 +926,6 @@ export function createDecisionServer(options) {
|
|
|
1146
926
|
}
|
|
1147
927
|
project = wanted;
|
|
1148
928
|
}
|
|
1149
|
-
// Home (UI revamp wave B) shows every project unless its address names one (proved the same way), and the rail's
|
|
1150
|
-
// and the dock's Needs-you count follow that same one scope.
|
|
1151
929
|
if (route.id === 'home') {
|
|
1152
930
|
const wanted = url.searchParams.get('project');
|
|
1153
931
|
if (wanted !== null && (url.searchParams.getAll('project').length !== 1 || !visible(wanted) ||
|
|
@@ -1156,19 +934,12 @@ export function createDecisionServer(options) {
|
|
|
1156
934
|
}
|
|
1157
935
|
project = wanted;
|
|
1158
936
|
}
|
|
1159
|
-
// All projects, asked for by name on Tasks (its project filter): the rolled-up list, admission-bound as ever.
|
|
1160
|
-
// Tasks (P1.4) says every project as scope=all; an old link's all=1 still reads so.
|
|
1161
937
|
if (route.id === 'tasks' && !url.searchParams.has('project') && ((url.searchParams.get('scope') === 'all' && url.searchParams.getAll('scope').length === 1)
|
|
1162
938
|
|| (url.searchParams.get('all') === '1' && url.searchParams.getAll('all').length === 1)))
|
|
1163
939
|
project = null;
|
|
1164
|
-
// Every page's Tasks count covers this one project view, whatever project the page itself shows.
|
|
1165
940
|
const facts = requestContext.getStore();
|
|
1166
941
|
if (facts !== undefined)
|
|
1167
942
|
facts.lens = project;
|
|
1168
|
-
// Exact result links carry their own read context. Check the stored
|
|
1169
|
-
// placement against both account and instance access before using it;
|
|
1170
|
-
// viewing a result never changes the session's selected project, and
|
|
1171
|
-
// the page's project switch keeps showing the person's own choice.
|
|
1172
943
|
const chosenProject = project;
|
|
1173
944
|
if (route.id === "review" && url.searchParams.has("result")) {
|
|
1174
945
|
const wanted = url.searchParams.get("result") ?? "";
|
|
@@ -1181,24 +952,6 @@ export function createDecisionServer(options) {
|
|
|
1181
952
|
if (who.via === "cookie" && ref !== null && visible(ref.repo))
|
|
1182
953
|
project = ref.repo;
|
|
1183
954
|
}
|
|
1184
|
-
// Nothing open and more than one thing to choose: land on the opener.
|
|
1185
|
-
// Decisions and their evidence stay reachable — answering must never be
|
|
1186
|
-
// blocked by project state — and the opener itself must not loop.
|
|
1187
|
-
// A run's page and its evidence (workspace package 1): a bookmarked
|
|
1188
|
-
// `/r/<id>` from All projects used to bounce to the opener until a
|
|
1189
|
-
// project was chosen. The handler below re-proves the run's repo
|
|
1190
|
-
// against the ceiling and the account (`runVisible`) before a byte
|
|
1191
|
-
// renders, so letting the path through widens nothing.
|
|
1192
|
-
// The chat's two read-only refresh routes (package 2 revision): the
|
|
1193
|
-
// page itself is allowed from All projects, so its status poll and
|
|
1194
|
-
// task fragment must be too — a 303 to the opener answered the poll
|
|
1195
|
-
// with HTML and the page wrongly said the sign-in was lost. Each
|
|
1196
|
-
// route keeps its own cookie, role, session, ceiling, and task
|
|
1197
|
-
// admission checks. Shared action links likewise recheck the saved
|
|
1198
|
-
// owner and project in their handler, so they stay reachable from All
|
|
1199
|
-
// projects without changing the selected project or granting access.
|
|
1200
|
-
// Which reads first need an open project is the route table's `needsProject` column; an exact result link
|
|
1201
|
-
// and the all-projects board carry their own read context.
|
|
1202
955
|
const needsProject = who.via === "cookie" && project === null && !unscopedMode &&
|
|
1203
956
|
route.needsProject &&
|
|
1204
957
|
!(route.id === "review" && url.searchParams.has("result")) &&
|
|
@@ -1207,32 +960,15 @@ export function createDecisionServer(options) {
|
|
|
1207
960
|
return redirect(response, `/projects?return=${encodeURIComponent(safeReturn(url.pathname + url.search))}`);
|
|
1208
961
|
return dispatchConsole(route, { url, who, request, response, route, now, project, chosenProject, posted: new URLSearchParams() });
|
|
1209
962
|
}
|
|
1210
|
-
/**
|
|
1211
|
-
* The first run (adoption track, step 3): three plain steps derived from
|
|
1212
|
-
* live state on every render, never a stored cursor, and retired
|
|
1213
|
-
* PERMANENTLY by the first-success installation fact (Codex adoption
|
|
1214
|
-
* review, finding 14). Each step is either something this console can do,
|
|
1215
|
-
* or the exact command where only the CLI can (finding e).
|
|
1216
|
-
*/
|
|
1217
963
|
function firstRunStepsNow(now) {
|
|
1218
964
|
if (restricted() || store.isDemo() || store.firstSuccessAt(now) !== null)
|
|
1219
965
|
return null;
|
|
1220
966
|
return firstRunSteps({ agentSignedIn: agentSignedIn(now), projects: managedRepos().length, hasTask: store.hasAnyWork(), firstResultAt: null, signInCommand: agentSignInCommand() });
|
|
1221
967
|
}
|
|
1222
|
-
/**
|
|
1223
|
-
* Whether a coding agent is signed in where the work runs. This machine's
|
|
1224
|
-
* own checks (the ones Settings makes) speak only for a worker on this
|
|
1225
|
-
* machine, or for the first one while none is registered yet (it is started
|
|
1226
|
-
* here); a worker elsewhere speaks through its own readiness report, and
|
|
1227
|
-
* the console never claims to have checked that machine (finding 15).
|
|
1228
|
-
* Nothing here waits on a probe: this machine's answer is the last one
|
|
1229
|
-
* seen, refreshed in the background, and null until the first one is in.
|
|
1230
|
-
*/
|
|
1231
968
|
function agentSignedIn(now) {
|
|
1232
969
|
const here = hostname();
|
|
1233
970
|
const registered = store.listRunners().filter(one => one.retiredAt === null);
|
|
1234
971
|
const workers = registered.filter(one => runnerAlive(one, now));
|
|
1235
|
-
// This machine under any of its own names (machine-identity.ts): a hostname that moved with the network is still here.
|
|
1236
972
|
if (workers.some(one => !sameMachineHost(one.host, here) && store.providerReadiness(one.name).some(seen => seen.state === "ready")))
|
|
1237
973
|
return true;
|
|
1238
974
|
return options.localRunner !== undefined || registered.length === 0 || workers.some(one => sameMachineHost(one.host, here)) ? localAgentSignedIn() : false;
|
|
@@ -1240,7 +976,6 @@ export function createDecisionServer(options) {
|
|
|
1240
976
|
let localSignIn = null;
|
|
1241
977
|
let localSignInChecking = null;
|
|
1242
978
|
let leadRecheckedAt = 0;
|
|
1243
|
-
/** Ask this computer's agent CLIs again (no model, nothing spent); `fresh` skips their 30-second cache. */
|
|
1244
979
|
function checkLocalAgents(fresh = false) {
|
|
1245
980
|
if (localSignInChecking !== null)
|
|
1246
981
|
return localSignInChecking;
|
|
@@ -1248,22 +983,16 @@ export function createDecisionServer(options) {
|
|
|
1248
983
|
.then(([claude, codex]) => {
|
|
1249
984
|
localSignIn = { at: Date.now(), signedIn: [claude, codex].some(state => state === "connected" || state === "key-works" || state === "key-present"), states: { claude: claude, codex: codex } };
|
|
1250
985
|
leadByDefault();
|
|
986
|
+
if (options.leadByDefault === true && !store.isDemo())
|
|
987
|
+
fillRoutingOnSignIn(store, localSignIn.states, clock());
|
|
1251
988
|
})
|
|
1252
989
|
.catch(() => { })
|
|
1253
990
|
.finally(() => { localSignInChecking = null; });
|
|
1254
991
|
return localSignInChecking;
|
|
1255
992
|
}
|
|
1256
|
-
/** The last answer only (docs/plans/get-side-effects.md): this computer is asked again by the background checks or a
|
|
1257
|
-
* person's Check now, never because a page was opened. */
|
|
1258
993
|
function localAgentSignedIn() {
|
|
1259
994
|
return localSignIn?.signedIn ?? null;
|
|
1260
995
|
}
|
|
1261
|
-
/**
|
|
1262
|
-
* The lead on by default (onboarding): with no lead set up yet, the agent CLI signed in on this computer runs it
|
|
1263
|
-
* (Claude Code first, else Codex) on its membership with today's safe defaults: the CLI's own default model, 50 turns
|
|
1264
|
-
* a day, no dollar spend, and every action it proposes still a card a person confirms. Once only, ever: a lead
|
|
1265
|
-
* someone turned off stays off, and Settings → Lead changes it.
|
|
1266
|
-
*/
|
|
1267
996
|
function leadByDefault() {
|
|
1268
997
|
if (options.leadByDefault !== true || store.isDemo() || store.getChatConfig() !== null || store.installationFact(LEAD_BY_DEFAULT_FACT) !== null)
|
|
1269
998
|
return;
|
|
@@ -1274,29 +1003,21 @@ export function createDecisionServer(options) {
|
|
|
1274
1003
|
store.transact(() => {
|
|
1275
1004
|
store.setChatConfig({ provider, model: "default", dailyTurns: 50, weeklyCeilingMicrousd: 0, priceInMicrousd: 0, priceOutMicrousd: 0 }, "toolroll", now);
|
|
1276
1005
|
store.recordInstallationFact(LEAD_BY_DEFAULT_FACT, provider, now);
|
|
1277
|
-
// A laptop install (local mode on): the agent chosen here also starts the models, on its default preset, while
|
|
1278
|
-
// nothing is chosen yet, so the owner's first task plans without a password (model-choices.ts fillDefaultRouting).
|
|
1279
1006
|
const agent = defaultAgentOf(provider);
|
|
1280
1007
|
if (agent !== null && localModeOn(store))
|
|
1281
1008
|
fillDefaultRouting(store, agent, "toolroll", { kind: "first-start" }, now);
|
|
1282
1009
|
});
|
|
1283
1010
|
}
|
|
1284
|
-
// Look once at start, so the lead is on by the time the browser opens.
|
|
1285
1011
|
if (options.leadByDefault === true && !store.isDemo() && store.getChatConfig() === null)
|
|
1286
1012
|
void checkLocalAgents();
|
|
1287
|
-
/** What the lead runs on, in words for one line ("your Claude Code sign-in"); null for a direct API or no lead. */
|
|
1288
1013
|
function leadWords() {
|
|
1289
1014
|
const config = store.getChatConfig();
|
|
1290
1015
|
return config?.provider === "claude-subscription" ? "your Claude Code sign-in" : config?.provider === "codex-subscription" ? "your Codex sign-in" : null;
|
|
1291
1016
|
}
|
|
1292
|
-
/** The one command that gets an agent signed in here, for this operating system: sign in when its CLI is installed, else install Claude Code and sign in. */
|
|
1293
1017
|
function agentSignInCommand() {
|
|
1294
1018
|
return signInCommandFor(localSignIn?.states ?? null, options.platform ?? process.platform);
|
|
1295
1019
|
}
|
|
1296
|
-
/** First tasks per project: the last ones found (safe generic ones until then), re-read in the background at most
|
|
1297
|
-
* every ten minutes. A render never waits on `gh` or `git grep`. */
|
|
1298
1020
|
const firstTasks = new Map();
|
|
1299
|
-
/** The phone setup itself (Settings → Chat apps): a chat app, or this console over Tailscale. */
|
|
1300
1021
|
function phoneSetup(who) {
|
|
1301
1022
|
if (who.role !== "approver" || store.isDemo())
|
|
1302
1023
|
return undefined;
|
|
@@ -1311,49 +1032,27 @@ export function createDecisionServer(options) {
|
|
|
1311
1032
|
dismissHref: "/onboarding/phone/dismiss",
|
|
1312
1033
|
};
|
|
1313
1034
|
}
|
|
1314
|
-
// ---- fleet chat (v13) ----------------------------------------------------
|
|
1315
1035
|
const chatFetcher = options.chatFetcher ?? fetch;
|
|
1316
1036
|
const chatEnv = options.chatEnv ?? process.env;
|
|
1317
|
-
/** The lead's last non-answer per BROWSER session (keyed by its csrf —
|
|
1318
|
-
* never by name, so two browsers on one account do not read each other's
|
|
1319
|
-
* notes), bound to the mate turn it came from so a note from a session
|
|
1320
|
-
* that has since ended never shows; bounded; read once. */
|
|
1321
1037
|
const leadSaid = new Map();
|
|
1322
|
-
/** The unified conversation's rows for one reader (package 2): the SAME
|
|
1323
|
-
* rows the page renders and the status poll's fragments re-render, so a
|
|
1324
|
-
* live update can never show a card the page would not. A task lens
|
|
1325
|
-
* keeps only that task's coordinator cards; the lead's own thread is one
|
|
1326
|
-
* thread regardless of lens. */
|
|
1327
1038
|
function leadConversationRows(who, principal, focusTask, now, chatProject = null) {
|
|
1328
1039
|
const allCoordinatorRows = store.listCoordinatorProposals({ repos: managedRepos(), states: ["pending", "confirmed", "refused"], limit: 30 });
|
|
1329
1040
|
const coordinatorProposals = focusTask !== null ? allCoordinatorRows.filter(one => focusTask.family.versions.some(version => version.id === one.payload["task"]))
|
|
1330
1041
|
: chatProject !== null ? allCoordinatorRows.filter(one => one.repo === chatProject) : allCoordinatorRows;
|
|
1331
|
-
// A visit only reads (quiet page visits): this opens no thread. The first message opens it (chatSend), and a
|
|
1332
|
-
// live thread kept under another ceiling is what that message replaces, so it reads here as the earlier one.
|
|
1333
1042
|
const found = store.liveLeadThreadFor(who.name, chatScopeOf(focusTask, chatProject));
|
|
1334
1043
|
const thread = found !== null && found.ceilingDigest === principal.ceilingDigest ? found : null;
|
|
1335
1044
|
const proposals = thread === null ? [] : store.listLeadProposals(thread.id);
|
|
1336
|
-
// One reply runs at a time per person; this thread shows it only when
|
|
1337
|
-
// the reply is its own.
|
|
1338
1045
|
const live = store.liveLeadTurnFor(who.name);
|
|
1339
1046
|
const messages = thread === null ? [] : store.listLeadMessages(thread.id, 40);
|
|
1340
|
-
// The lead's question with buttons, while its reply is the last word in the thread.
|
|
1341
1047
|
const last = messages.at(-1);
|
|
1342
1048
|
const asking = last?.role === "assistant" && last.turn !== null && store.getLeadTurn(last.turn)?.approver === who.name ? store.leadAskOpen(last.turn, now) : null;
|
|
1343
|
-
// A question about this task loses its buttons once the version shown moved after it was asked (its plan
|
|
1344
|
-
// rewritten or approved, a new attempt started): its options answered a state that is gone. Its words stay, marked so.
|
|
1345
1049
|
const staleAsk = asking !== null && focusTask !== null && taskMovedSince(store, focusTask.executionId, asking.createdAt) ? asking.turn : null;
|
|
1346
1050
|
const ask = staleAsk === null ? asking : null;
|
|
1347
|
-
// Answered questions stay readable above the answer; only their buttons go.
|
|
1348
1051
|
const asks = new Map(messages.flatMap(one => { const asked = one.role === "assistant" && one.turn !== null ? store.leadAsk(one.turn) : null; return asked === null ? [] : [[asked.turn, asked]]; }));
|
|
1349
|
-
// The person's queued messages in this thread: each shows waiting (with Remove) or why it was not sent.
|
|
1350
1052
|
const queue = new Map(thread === null ? [] : personalQueue.queue.forThread(thread.id).map(row => [row.message, row]));
|
|
1351
|
-
// The thread a ceiling change replaced (ruling 9), display only: closed already, or still live under the old ceiling.
|
|
1352
1053
|
const replaced = thread !== null ? store.replacedLeadThread(thread)
|
|
1353
1054
|
: found !== null && found.scope.kind === "lead" && store.listLeadMessages(found.id, 1).length > 0 ? found : null;
|
|
1354
1055
|
const previousProposals = replaced === null ? [] : store.listLeadProposals(replaced.id);
|
|
1355
|
-
// How this thread's last reply ended, read from its saved turn (never process memory, which a restart loses): a
|
|
1356
|
-
// failed turn that saved no message of its own says why in the thread, until the next reply starts.
|
|
1357
1056
|
const lastTurn = thread === null ? null : latestLeadTurnOfThread(store, thread.id);
|
|
1358
1057
|
const failure = lastTurn !== null && lastTurn.state === "failed" && !messages.some(one => one.role === "assistant" && one.turn === lastTurn.id)
|
|
1359
1058
|
? leadFailureText(lastTurn.failureReason, store.leadTurnCostUnconfirmed(lastTurn.id), lastTurn.failureDetail ?? null) : null;
|
|
@@ -1372,18 +1071,10 @@ export function createDecisionServer(options) {
|
|
|
1372
1071
|
recent: store.recentLeadTurns(who.name, 5),
|
|
1373
1072
|
};
|
|
1374
1073
|
}
|
|
1375
|
-
/** The thread a chat surface speaks in (v77): the task's own thread, the
|
|
1376
|
-
* project's, or the lead conversation across every project. */
|
|
1377
1074
|
function chatScopeOf(focusTask, chatProject) {
|
|
1378
1075
|
return focusTask !== null ? { kind: "task", key: focusTask.id } : chatProject !== null ? { kind: "project", key: chatProject } : LEAD_THREAD;
|
|
1379
1076
|
}
|
|
1380
1077
|
const liveTurns = new Map();
|
|
1381
|
-
/** The Ask panel (v77): the conversation a task, result or project page
|
|
1382
|
-
* docks beside itself — that page's own thread, the same session and
|
|
1383
|
-
* cards as /chat. Null when this person has no lead chat here. A page
|
|
1384
|
-
* view starts nothing (quiet page visits): with no membership
|
|
1385
|
-
* conversation yet, the panel binds its sends to NOT_STARTED_SESSION and
|
|
1386
|
-
* the first message starts one (chatSend). */
|
|
1387
1078
|
function dockedConversation(who, focusTask, chatProject, now, back, resultRunId = null) {
|
|
1388
1079
|
if (who.via !== "cookie" || who.role !== "approver" || (focusTask === null && chatProject === null))
|
|
1389
1080
|
return null;
|
|
@@ -1404,21 +1095,13 @@ export function createDecisionServer(options) {
|
|
|
1404
1095
|
taskId: focusTask?.id ?? null, resultRunId, project: focusTask === null ? chatProject : null, failure: rows.pending === null ? rows.failure : null,
|
|
1405
1096
|
};
|
|
1406
1097
|
}
|
|
1407
|
-
/** The session-layer principal (ruling 3): cookie, csrf, and role were
|
|
1408
|
-
* proved at the edge; the row and the generation are re-proved here. */
|
|
1409
|
-
/** Start the person's lead conversation: the session every later turn
|
|
1410
|
-
* debits (a membership spends nothing; direct API gets a per-conversation
|
|
1411
|
-
* ceiling) and its thread. Starting ends any older session. */
|
|
1412
1098
|
function startLeadConversation(who, principal, enabled, ceilingUsd, follow, now, scope = LEAD_THREAD, request) {
|
|
1413
|
-
// A page's first send (quiet page visits) starts it under that send's request: the start and its record are one
|
|
1414
|
-
// write, so a retry of the same request finds the conversation it started and never starts another.
|
|
1415
1099
|
if (request !== undefined) {
|
|
1416
1100
|
return store.transact(() => store.replay({ idempotencyKey: `mate-start:${who.name}:${request}`, actor: who.name, at: now }, "mate-start", () => ({ session: startLeadConversation(who, principal, enabled, ceilingUsd, follow, now, scope) })).session);
|
|
1417
1101
|
}
|
|
1418
1102
|
const ceilingMicrousd = enabled.billing === "subscription" ? 0 : Math.round(ceilingUsd * 1_000_000);
|
|
1419
1103
|
const termsDigest = createHash("sha256").update(`${ceilingMicrousd}\n${principal.ceilingDigest}`).digest("hex");
|
|
1420
1104
|
const sessionId = store.mintLeadSession({ approver: who.name, approverGeneration: principal.generation, credentialKey: enabled.credentialKey, ceilingMicrousd, ceilingDigest: principal.ceilingDigest, termsDigest }, now);
|
|
1421
|
-
// The thread the first message goes to (quiet page visits: a task's first message opens only the task's thread).
|
|
1422
1105
|
const thread = store.openLeadThread(who.name, principal.ceilingDigest, now, scope).thread;
|
|
1423
1106
|
if (follow)
|
|
1424
1107
|
configureLeadFollow(store, principal, store.getLeadSession(sessionId), thread, true, now);
|
|
@@ -1431,15 +1114,10 @@ export function createDecisionServer(options) {
|
|
|
1431
1114
|
}
|
|
1432
1115
|
const CHAT_CANDIDATES_PER_APPROVER = 9;
|
|
1433
1116
|
const CHAT_CANDIDATE_TTL_MS = 30 * 60_000;
|
|
1434
|
-
/** The frozen explicit repo list, digested canonically (sorted) — every
|
|
1435
|
-
* candidate binds to it and filing re-proves it (v2 new finding 5). */
|
|
1436
1117
|
const chatCeilingDigest = (repos = managedRepos()) => createHash("sha256").update([...repos].sort().join("\n")).digest("hex");
|
|
1437
|
-
/** The scripted lead answers only inside a demo database. */
|
|
1438
1118
|
function demoLeadHere() {
|
|
1439
1119
|
return options.demoLead !== undefined && store.isDemo() ? options.demoLead : null;
|
|
1440
1120
|
}
|
|
1441
|
-
/** Every condition re-proved per request — the render and the POST each
|
|
1442
|
-
* ask again; nothing is cached into authority. */
|
|
1443
1121
|
function chatEnablement() {
|
|
1444
1122
|
if (store.isDemo())
|
|
1445
1123
|
return { ok: false, code: "demo", why: "this is a demo database — chat cannot contact an external model" };
|
|
@@ -1472,13 +1150,6 @@ export function createDecisionServer(options) {
|
|
|
1472
1150
|
return { ok: false, code: "no-key", why: `no ${config.provider} key — paste one below (stored 0600 beside the database, never in it), or export ${CHAT_KEY_ENV[config.provider]} in the serve environment` };
|
|
1473
1151
|
return { ok: true, billing: "metered", config: config, key: key.key, keySource: key.source, price, credentialKey: credentialKeyOf(config.provider, key.key) };
|
|
1474
1152
|
}
|
|
1475
|
-
/**
|
|
1476
|
-
* Where a chat key comes from, in priority order: the serve process
|
|
1477
|
-
* environment, then the 0600 key file under the config directory (the
|
|
1478
|
-
* Telegram bot-token precedent — settable from the console, never the
|
|
1479
|
-
* database, never echoed whole). The file exists so onboarding lives
|
|
1480
|
-
* in the UI; the environment exists so operators who prefer it keep it.
|
|
1481
|
-
*/
|
|
1482
1153
|
function chatKeyFor(provider) {
|
|
1483
1154
|
const fromEnv = chatEnv[CHAT_KEY_ENV[provider]];
|
|
1484
1155
|
if (fromEnv !== undefined && fromEnv !== "")
|
|
@@ -1493,13 +1164,7 @@ export function createDecisionServer(options) {
|
|
|
1493
1164
|
return null;
|
|
1494
1165
|
}
|
|
1495
1166
|
}
|
|
1496
|
-
/** OpenRouter's live catalog, cached briefly: every selectable model
|
|
1497
|
-
* arrives WITH the price the config will pin (operator request — the
|
|
1498
|
-
* whole catalog, not a hand-pinned shortlist). null = no key or the
|
|
1499
|
-
* catalog is unreachable; callers fall back to the compiled table. */
|
|
1500
1167
|
let catalogCache = null;
|
|
1501
|
-
/** The catalog as last fetched, for a page to show (docs/plans/get-side-effects.md): a page never fetches it. The
|
|
1502
|
-
* background checks keep it current while an OpenRouter key is saved; saving a choice fetches it (chatCatalog). */
|
|
1503
1168
|
function chatCatalogKnown() {
|
|
1504
1169
|
return chatKeyFor("openrouter-api") === null ? null : catalogCache?.models ?? null;
|
|
1505
1170
|
}
|
|
@@ -1525,11 +1190,6 @@ export function createDecisionServer(options) {
|
|
|
1525
1190
|
clearTimeout(timer);
|
|
1526
1191
|
}
|
|
1527
1192
|
}
|
|
1528
|
-
/**
|
|
1529
|
-
* The palette's server-rendered index (attended review, finding 4): a
|
|
1530
|
-
* non-executable JSON block on an already-authorized page — the same
|
|
1531
|
-
* titles the page itself may render, bounded, saturation declared.
|
|
1532
|
-
*/
|
|
1533
1193
|
function paletteIndexTag(project) {
|
|
1534
1194
|
const admitted = project === null ? admissionList() : null;
|
|
1535
1195
|
const entries = [
|
|
@@ -1559,15 +1219,6 @@ export function createDecisionServer(options) {
|
|
|
1559
1219
|
const safeEntries = restricted() ? entries.filter(one => ["/tasks", "/tasks?view=needs-you", "/", "/tasks?view=board", "/flows", "/tasks?view=done", "/tasks?view=ready", "/runs", "/projects", "/ledger"].includes(one.href) || one.href.startsWith("/t/")) : entries;
|
|
1560
1220
|
return jsonScript(saturated ? [...safeEntries, { label: "… more in Tasks", href: "/tasks" }] : safeEntries, { id: "palette-index" });
|
|
1561
1221
|
}
|
|
1562
|
-
/**
|
|
1563
|
-
* The palette cache (arc 4, finding 6), and its honest contract: a
|
|
1564
|
-
* render may be up to five seconds stale after OUT-OF-PROCESS changes;
|
|
1565
|
-
* an accepted in-process mutation invalidates immediately (bustBadge),
|
|
1566
|
-
* so "one query per five seconds" holds only between invalidations;
|
|
1567
|
-
* and an OPEN page keeps its navigation-time snapshot until the next
|
|
1568
|
-
* navigation — no refresh mechanism exists or is promised. The key
|
|
1569
|
-
* carries the one configuration bit the entries vary by.
|
|
1570
|
-
*/
|
|
1571
1222
|
const paletteCache = new Map();
|
|
1572
1223
|
function paletteTagCached(project) {
|
|
1573
1224
|
const actor = requestContext.getStore()?.actor;
|
|
@@ -1579,44 +1230,26 @@ export function createDecisionServer(options) {
|
|
|
1579
1230
|
paletteCache.set(key, { at: Date.now(), tag });
|
|
1580
1231
|
return tag;
|
|
1581
1232
|
}
|
|
1582
|
-
/**
|
|
1583
|
-
* Every chromed HTML response leaves through here (arc 4, findings
|
|
1584
|
-
* 5/18): one place owns the sensitivity call, the nonce, the palette
|
|
1585
|
-
* index, the shortcuts overlay, script composition, and the CSP —
|
|
1586
|
-
* at most one nonce-bearing script per response, connect-src only when
|
|
1587
|
-
* that script fetches. The named exception: /fleet and /settings keep
|
|
1588
|
-
* their FUNCTIONAL scripts beside credential fields (a poller and the
|
|
1589
|
-
* push enrollment — neither reads the fields); the chrome additions
|
|
1590
|
-
* are what sensitivity strips.
|
|
1591
|
-
*/
|
|
1592
1233
|
function sendScreen(response, status, s) {
|
|
1593
1234
|
requestContext.getStore()?.timeline?.mark("handler done; rendering");
|
|
1594
|
-
// Sensitivity is the page as authored (cockpit §2, R14): its own explicit word, else a password field it was written
|
|
1595
|
-
// with — judged before an identity-provider sign-in replaces that field, so an SSO step-up page stays sensitive.
|
|
1596
1235
|
const authored = s.sensitive ?? (s.forceSensitive === true ||
|
|
1597
1236
|
SENSITIVE_INPUT.test(htmlString(s.body)) ||
|
|
1598
1237
|
(s.workspace?.pageHtml != null && SENSITIVE_INPUT.test(htmlString(s.workspace.pageHtml))) ||
|
|
1599
1238
|
(s.chrome?.listPane !== undefined && SENSITIVE_INPUT.test(htmlString(s.chrome.listPane))));
|
|
1600
|
-
// v100: signed in with the identity provider, a step-up's password field is that sign-in instead.
|
|
1601
1239
|
const ssoFacts = requestContext.getStore()?.sso;
|
|
1602
1240
|
if (ssoFacts !== undefined) {
|
|
1603
1241
|
const back = requestContext.getStore()?.returnTo ?? "/";
|
|
1604
|
-
// A typed-only proof (settings-kit.ts) stays a password field: its handler takes nothing else.
|
|
1605
1242
|
s = { ...s, body: keepTypedFields(s.body, ssoStepUps, ssoFacts, back), ...(s.workspace?.pageHtml == null ? {} : { workspace: { ...s.workspace, pageHtml: keepTypedFields(s.workspace.pageHtml, ssoStepUps, ssoFacts, back) } }) };
|
|
1606
1243
|
if (s.workspace?.view?.kind === "flow")
|
|
1607
1244
|
s = { ...s, workspace: { ...s.workspace, view: { ...s.workspace.view, stepUp: { label: ssoFacts.label, fresh: ssoFacts.fresh, confirmHref: `/login/sso?reauth=1&return=${encodeURIComponent(back)}` } } } };
|
|
1608
1245
|
}
|
|
1609
|
-
// Easy start: a local owner with no password has none to type: each step-up field says how to set one instead.
|
|
1610
1246
|
const localStepUp = requestContext.getStore()?.localStepUp;
|
|
1611
1247
|
if (localStepUp !== undefined) {
|
|
1612
1248
|
s = { ...s, body: localOwnerStepUps(s.body, localStepUp), ...(s.workspace?.pageHtml == null ? {} : { workspace: { ...s.workspace, pageHtml: localOwnerStepUps(s.workspace.pageHtml, localStepUp) } }) };
|
|
1613
1249
|
}
|
|
1614
|
-
// D5: team chat (the central team service) is deprecated; /chat?team=1 still opens this release, but nothing links to it.
|
|
1615
1250
|
const sensitive = authored || s.forceSensitive === true;
|
|
1616
1251
|
const requestFacts = requestContext.getStore();
|
|
1617
|
-
// Easy-start C2: while the browser mutation key is required, every page's one nonce'd script also fills the key.
|
|
1618
1252
|
const keyFill = requestFacts?.keyFill === true ? KEY_FILL_SCRIPT : "";
|
|
1619
|
-
// The settings frame (settings-kit.ts): its breadcrumb, for the shell and the workspace alike.
|
|
1620
1253
|
if (s.chrome !== undefined && s.chrome.crumbs === undefined) {
|
|
1621
1254
|
const crumbs = settingsCrumbs(new URL(requestFacts?.returnTo ?? '/', 'http://standing-orders.local').pathname, s.title, htmlString(s.body));
|
|
1622
1255
|
if (crumbs !== undefined)
|
|
@@ -1628,8 +1261,6 @@ export function createDecisionServer(options) {
|
|
|
1628
1261
|
path.searchParams.delete('request');
|
|
1629
1262
|
const currentPath = path.pathname + path.search;
|
|
1630
1263
|
path.searchParams.set('format', 'workspace');
|
|
1631
|
-
// The legacy list pane (every task or build as links) duplicates the
|
|
1632
|
-
// Crew panel and Tasks page; inside the workspace the page stands alone.
|
|
1633
1264
|
const pageHtml = htmlString(s.body);
|
|
1634
1265
|
const extras = s.workspace ?? {};
|
|
1635
1266
|
const notices = [...(extras.notices ?? [])];
|
|
@@ -1643,7 +1274,6 @@ export function createDecisionServer(options) {
|
|
|
1643
1274
|
try {
|
|
1644
1275
|
const project = s.chrome.active === 'chat' ? null : s.chrome.project;
|
|
1645
1276
|
crew = extras.team?.tasks ? browserCrewFromIndex(extras.team.tasks, extras.team.selected?.id) : requestFacts.workCrew?.project === project ? browserCrewFromIndex(requestFacts.workCrew.page)
|
|
1646
|
-
// The reader's own lead's claims read "<name> is on it" here too.
|
|
1647
1277
|
: (() => {
|
|
1648
1278
|
const access = { principal: 'operator', repos: managedRepos(), includeUnplaced: false, viewer: requestFacts.actor ?? null };
|
|
1649
1279
|
return chromeRead(`crew\u0000${accessKey(access)}\u0000${project ?? ''}`, () => browserCrewOf(store, clock(), access, { evidenceRoot, project }));
|
|
@@ -1652,7 +1282,6 @@ export function createDecisionServer(options) {
|
|
|
1652
1282
|
catch {
|
|
1653
1283
|
notices.push('Crew updates are unavailable. Open Tasks to inspect saved work.');
|
|
1654
1284
|
}
|
|
1655
|
-
// A running row says what its agent did last, and when (the task page's own line).
|
|
1656
1285
|
try {
|
|
1657
1286
|
const now = clock(), live = new Map();
|
|
1658
1287
|
if (crew.crew.length > 0)
|
|
@@ -1664,14 +1293,9 @@ export function createDecisionServer(options) {
|
|
|
1664
1293
|
if (live.size > 0)
|
|
1665
1294
|
crew = { ...crew, crew: crew.crew.map(item => { const activity = live.get(item.id); return activity === undefined ? item : { ...item, activity }; }) };
|
|
1666
1295
|
}
|
|
1667
|
-
catch {
|
|
1668
|
-
// "Wake me only for these": the navigation carries the sidebar's own
|
|
1669
|
-
// Tasks count (chromeFor) and links to the project view it covers, so
|
|
1670
|
-
// it matches that page's Needs you tab on every screen.
|
|
1296
|
+
catch { }
|
|
1671
1297
|
const needsYou = s.chrome.inboxCount;
|
|
1672
1298
|
const tasksProject = s.chrome.inboxProject === undefined ? s.chrome.project : s.chrome.inboxProject;
|
|
1673
|
-
// The person's own project and task conversations (v77), for the
|
|
1674
|
-
// sidebar's chat list; a thread whose project is out of view is left out.
|
|
1675
1299
|
let chats = [];
|
|
1676
1300
|
try {
|
|
1677
1301
|
const actor = requestFacts.actor ?? '';
|
|
@@ -1694,31 +1318,18 @@ export function createDecisionServer(options) {
|
|
|
1694
1318
|
}
|
|
1695
1319
|
const conversation = extras.conversation ?? null;
|
|
1696
1320
|
const request = requestFacts.workspaceRequest;
|
|
1697
|
-
// React owns the conversation and its refresh/draft lifecycle. Native
|
|
1698
|
-
// guarded forms keep their current behavior after React inserts them.
|
|
1699
|
-
// A page with its own view keeps its script; the chat page runs the result script.
|
|
1700
|
-
// The exact page script the document would run: the JSON arm and the document arm read this one value.
|
|
1701
1321
|
const ownScript = conversation === null || extras.view;
|
|
1702
1322
|
const pageScript = ownScript ? (s.functional?.script ?? '') : RESULT_REVIEW_SCRIPT;
|
|
1703
1323
|
const routeInPlace = inPlaceRoute(requestFacts.routeId, path);
|
|
1704
|
-
// The app runs the page's behaviour itself (docs/plans/task-page-in-app.md §2) only when all of these hold: the
|
|
1705
|
-
// app covers the WHOLE script, no password field (a password page keeps its string path), an in-place route, and a
|
|
1706
|
-
// reader that can run it: a document load (its bundle is this server's, served no-store) or a workspace read that
|
|
1707
|
-
// says so. Anything else is the page exactly as before: the string, no work, a document.
|
|
1708
1324
|
const covered = ownScript ? s.functional?.app : undefined;
|
|
1709
1325
|
const work = pageScript !== '' && covered !== undefined && covered.length > 0 && !sensitive && routeInPlace
|
|
1710
1326
|
&& (!requestFacts.workspaceRead || requestFacts.appWork === true) ? covered : null;
|
|
1711
1327
|
const functional = work === null ? pageScript : '';
|
|
1712
|
-
// May replace another page without a document load (live-pages-and-home.md A1.1): an allowlisted route, no password
|
|
1713
|
-
// field, no page script (a page script runs once per document, so its page always loads as one).
|
|
1714
1328
|
const inPlace = routeInPlace && !sensitive && functional === '';
|
|
1715
1329
|
const workspace = {
|
|
1716
1330
|
version: 1, path: currentPath, title: s.title, user: requestFacts.actor ?? '', csrf: requestFacts.csrf, sensitive, inPlace,
|
|
1717
1331
|
...(work === null ? {} : { work }),
|
|
1718
|
-
// Said on the document that ships it: this document's page script listens for as long as the document lives.
|
|
1719
1332
|
...(functional !== '' && !requestFacts.workspaceRead ? { scripted: true } : {}),
|
|
1720
|
-
// The project picker names the open project; the settings frame carries its breadcrumb.
|
|
1721
|
-
// Only a project this person may see now: never the raw saved path (settings-kit.ts openProjectFor).
|
|
1722
1333
|
openProject: openProjectFor(requestFacts.openProject ?? null, s.chrome.project, s.chrome.projects ?? []),
|
|
1723
1334
|
...(s.chrome.crumbs === undefined ? {} : { crumbs: s.chrome.crumbs }),
|
|
1724
1335
|
...(requestFacts.passwordless === true ? { passwordless: true } : {}),
|
|
@@ -1727,7 +1338,6 @@ export function createDecisionServer(options) {
|
|
|
1727
1338
|
receipt: conversation !== null && request && REQUEST_TOKEN.test(request)
|
|
1728
1339
|
? { request, received: store.leadRequestReceived(conversation.sessionId, request) } : null,
|
|
1729
1340
|
projects: browserProjectsOf(s.chrome.projects ?? []), ...crew,
|
|
1730
|
-
// The sidebar's project states: never on a sensitive page (no rail, no summary stream there).
|
|
1731
1341
|
...(() => { if (sensitive)
|
|
1732
1342
|
return {}; try {
|
|
1733
1343
|
return { projectStates: projectSummary() };
|
|
@@ -1735,7 +1345,6 @@ export function createDecisionServer(options) {
|
|
|
1735
1345
|
catch {
|
|
1736
1346
|
return {};
|
|
1737
1347
|
} })(),
|
|
1738
|
-
// Cockpit B2: the remembered password's chip, only where it could be used (a direct local request).
|
|
1739
1348
|
...(() => {
|
|
1740
1349
|
const facts = freshFacts.getStore();
|
|
1741
1350
|
if (sensitive || facts == null || !facts.local || !facts.live())
|
|
@@ -1746,10 +1355,9 @@ export function createDecisionServer(options) {
|
|
|
1746
1355
|
const left = FRESH_WINDOW_MS - (Date.now() - at);
|
|
1747
1356
|
return left <= 0 ? {} : { fresh: { minutes: Math.max(1, Math.ceil(left / 60_000)), forget: '/session/forget-password' } };
|
|
1748
1357
|
})(),
|
|
1749
|
-
// The usage chip: the Limits view's own audience (an operator on a browser session), never a sensitive page.
|
|
1750
1358
|
...(() => {
|
|
1751
1359
|
const actor = requestFacts.actor;
|
|
1752
|
-
if (sensitive || !actor || requestFacts.csrf === '' || !store.isInstanceOperator(actor))
|
|
1360
|
+
if (sensitive || !actor || requestFacts.csrf === '' || requestFacts.advanced === false || !store.isInstanceOperator(actor))
|
|
1753
1361
|
return {};
|
|
1754
1362
|
try {
|
|
1755
1363
|
const usage = usageChipOf(store.providerLimits(), clock());
|
|
@@ -1763,15 +1371,12 @@ export function createDecisionServer(options) {
|
|
|
1763
1371
|
catchUpHtml: extras.catchUpHtml === undefined ? '' : htmlString(extras.catchUpHtml), controlsHtml: extras.controlsHtml === undefined ? '' : htmlString(extras.controlsHtml), notices, view: extras.view ?? null,
|
|
1764
1372
|
...(s.chrome.demo ? { demo: { text: DEMO_BANNER, short: DEMO_BANNER_SHORT } } : {}),
|
|
1765
1373
|
pageHtml: extras.pageHtml === undefined ? (conversation === null ? pageHtml : null) : extras.pageHtml === null ? null : htmlString(extras.pageHtml),
|
|
1766
|
-
navigation: browserNavigationOf(currentPath, s.chrome.project, needsYou, tasksProject, s.chrome.inboxLabel),
|
|
1767
|
-
|
|
1768
|
-
// view (or every project), so the rail, the phone header and Home all read the same number.
|
|
1374
|
+
navigation: browserNavigationOf(currentPath, s.chrome.project, needsYou, tasksProject, s.chrome.inboxLabel, requestFacts.advanced !== false),
|
|
1375
|
+
...(requestFacts.advanced === false ? { advanced: false } : {}),
|
|
1769
1376
|
needsYou: { count: needsYou, saturated: s.chrome.inboxSaturated === true, scope: tasksProject ?? null,
|
|
1770
1377
|
label: s.chrome.inboxLabel ?? `${needsYou} ${needsYou === 1 ? 'needs' : 'need'} you`,
|
|
1771
|
-
// Every project, said by name (all=1), so a session's own open project never narrows the list the count opens.
|
|
1772
1378
|
href: tasksProject === null ? '/tasks?scope=all&view=needs-you' : `/tasks?project=${encodeURIComponent(tasksProject)}&view=needs-you` },
|
|
1773
1379
|
chats,
|
|
1774
|
-
// The same count per project (the rail's project rows), read from the same work index totals.
|
|
1775
1380
|
...(() => { if (sensitive)
|
|
1776
1381
|
return {}; try {
|
|
1777
1382
|
return { needsYouByProject: projectCounts(clock()).map(one => ({ path: one.repo, count: one.totals['needs-you'] })) };
|
|
@@ -1785,7 +1390,6 @@ export function createDecisionServer(options) {
|
|
|
1785
1390
|
...(extras.firstRun === undefined ? {} : { firstRun: extras.firstRun }),
|
|
1786
1391
|
...(extras.phone === undefined ? {} : { phone: extras.phone }),
|
|
1787
1392
|
...(extras.home == null ? {} : { home: extras.home }),
|
|
1788
|
-
// Easy-start C2: this tab's key is from an earlier start (or it has none): one way to reconnect.
|
|
1789
1393
|
...(requestFacts.reconnect == null ? {} : { reconnect: requestFacts.reconnect }),
|
|
1790
1394
|
};
|
|
1791
1395
|
if (requestFacts.workspaceRead) {
|
|
@@ -1795,9 +1399,6 @@ export function createDecisionServer(options) {
|
|
|
1795
1399
|
response.setHeader('ETag', validator.etag);
|
|
1796
1400
|
}
|
|
1797
1401
|
const body = JSON.stringify(workspace);
|
|
1798
|
-
// Every other page's re-read after a live `change` (chat keeps its own validator, and its receipt, task and
|
|
1799
|
-
// result reads none): when what it renders now is exactly what the page holds, it hears 304 and keeps its view
|
|
1800
|
-
// (no transfer, no re-render).
|
|
1801
1402
|
if (status === 200 && validator === undefined && path.pathname !== '/chat') {
|
|
1802
1403
|
const etag = '"w' + createHash('sha256').update(body).digest('base64url') + '"';
|
|
1803
1404
|
response.setHeader('ETag', etag);
|
|
@@ -1808,14 +1409,8 @@ export function createDecisionServer(options) {
|
|
|
1808
1409
|
}
|
|
1809
1410
|
const nonce = randomBytes(16).toString('base64');
|
|
1810
1411
|
const document = shell(s.title, s.body, { chrome: s.chrome, sensitive });
|
|
1811
|
-
// The keys and their list (keymap.ts) work here as on every chromed page; the list sits beside the app React owns.
|
|
1812
|
-
// A sensitive page keeps to its own script.
|
|
1813
1412
|
return page(response, status, browserWorkspaceDocument(document, workspace, nonce, functional + MOBILE_VIEWPORT_SCRIPT + keyFill + (sensitive ? '' : chromeScript()), sensitive ? html `` : KBD_HELP), nonce, true);
|
|
1814
1413
|
}
|
|
1815
|
-
// A one-time secret on screen (a token, an invite link, a pairing code): the
|
|
1816
|
-
// page carries no script of any kind, so it leaves the workspace for a
|
|
1817
|
-
// focused page in the same look: the brand, the one card, and the way back.
|
|
1818
|
-
// Easy-start C2: but for the key fill when it has a form that posts (the fill reads nothing on the page).
|
|
1819
1414
|
if (s.forceSensitive === true) {
|
|
1820
1415
|
if (keyFill === "" || !/<form\b[^>]*method="post"/i.test(htmlString(s.body)))
|
|
1821
1416
|
return page(response, status, focusDocument(s.title, s.body));
|
|
@@ -1824,10 +1419,7 @@ export function createDecisionServer(options) {
|
|
|
1824
1419
|
}
|
|
1825
1420
|
const chromeLayer = !sensitive && s.chrome !== undefined;
|
|
1826
1421
|
const functional = s.functional?.script ?? "";
|
|
1827
|
-
// Sensitive pages strip the palette and keys but keep the sidebar toggle.
|
|
1828
1422
|
const sensitiveChrome = sensitive && s.chrome !== undefined ? sidebarScript() : "";
|
|
1829
|
-
// Kit markup the workspace bundle would have wired (a composer, an overlay, a switch, an idea) gets the kit's own small
|
|
1830
|
-
// behaviour here (kit-standalone.ts); never on a sensitive page, which carries no chrome additions.
|
|
1831
1423
|
const kitBehavior = chromeLayer && KIT_BEHAVIOR_MARKUP.test(htmlString(s.body)) ? KIT_STANDALONE_SCRIPT : "";
|
|
1832
1424
|
const script = functional + (chromeLayer ? chromeScript() + WORKSPACE_MOTION_SCRIPT + kitBehavior : sensitiveChrome) + keyFill;
|
|
1833
1425
|
const nonce = script === "" ? undefined : randomBytes(16).toString("base64");
|
|
@@ -1841,23 +1433,14 @@ export function createDecisionServer(options) {
|
|
|
1841
1433
|
...(s.refreshSeconds === undefined ? {} : { refreshSeconds: s.refreshSeconds }),
|
|
1842
1434
|
...(nonce === undefined ? {} : { live: { nonce, script, fallbackRefresh: s.functional?.fetches === true } }),
|
|
1843
1435
|
});
|
|
1844
|
-
// Pages that ship the chrome layer keep connect-src (v28 granted it for
|
|
1845
|
-
// the since-removed watched-session beat), beside pages whose own
|
|
1846
|
-
// functional script polls.
|
|
1847
1436
|
return page(response, status, document, nonce, s.functional?.fetches === true || chromeLayer || sensitiveChrome !== "");
|
|
1848
1437
|
}
|
|
1849
|
-
/** The needs-you count every surface wears, read from this request's own
|
|
1850
|
-
* Work counts (one read per request, kept only while the workspace revision
|
|
1851
|
-
* it was read at stands), so a live page's badge and its Needs you tab
|
|
1852
|
-
* always agree. */
|
|
1853
1438
|
function needsYouBadge(project) {
|
|
1854
1439
|
return needsYouCount(project);
|
|
1855
1440
|
}
|
|
1856
1441
|
const bustBadge = () => {
|
|
1857
1442
|
paletteCache.clear();
|
|
1858
1443
|
};
|
|
1859
|
-
/** Enrolled projects first (most recently opened), then the repos this server was told to serve that nobody has
|
|
1860
|
-
* opened yet — the switcher lists every project it is allowed to show, each exactly once. */
|
|
1861
1444
|
function sidebarProjects() {
|
|
1862
1445
|
const seen = new Set();
|
|
1863
1446
|
const rows = [];
|
|
@@ -1872,33 +1455,21 @@ export function createDecisionServer(options) {
|
|
|
1872
1455
|
}
|
|
1873
1456
|
return rows;
|
|
1874
1457
|
}
|
|
1875
|
-
/** The sidebar's projects with their states (agent-state.ts), for the request's own reader: the page's first read
|
|
1876
|
-
* and the summary room's snapshots are this one function, so the two never disagree. Never marks anything seen.
|
|
1877
|
-
* Work with no project yet is its own entry when this reader may see it and something counts there. */
|
|
1878
1458
|
function projectSummary() {
|
|
1879
1459
|
const projects = sidebarProjects();
|
|
1880
1460
|
const actor = requestContext.getStore()?.actor ?? '';
|
|
1881
1461
|
const access = workAccess();
|
|
1882
1462
|
return chromeRead(summaryKey(actor, access, projects), () => projectSummaryOf(store, clock(), access, actor, projects, evidenceRoot));
|
|
1883
1463
|
}
|
|
1884
|
-
/** What one account may see now, as the summary room keys it: a narrowed ceiling or grant is a different key, so a
|
|
1885
|
-
* snapshot read for the wider scope never reaches it. Null: no such account. */
|
|
1886
1464
|
const projectScopeFor = (account) => store.accountOf(account) === null ? null : requestContext.run({ csrf: '', returnTo: '/', actor: account }, () => {
|
|
1887
|
-
// Canonical: what may be seen, never the order it was last opened in.
|
|
1888
1465
|
const access = workAccess();
|
|
1889
1466
|
return JSON.stringify({ projects: sidebarProjects().map(one => one.path).sort(), access: { ...access, repos: access.repos === null ? null : [...access.repos].sort() } });
|
|
1890
1467
|
});
|
|
1891
|
-
/** One account's summary snapshot, read as that account for this exact scope (the summary room's fingerprint): null
|
|
1892
|
-
* when the account is gone or its scope moved. */
|
|
1893
1468
|
const projectSummaryFor = (account, scope) => projectScopeFor(account) !== scope ? null : requestContext.run({ csrf: '', returnTo: '/', actor: account }, () => JSON.stringify(projectSummary()));
|
|
1894
|
-
/** The sidebar's facts for this request. */
|
|
1895
1469
|
function chromeFor(project, active, listPane, scope) {
|
|
1896
1470
|
if (restricted() && !visible(project))
|
|
1897
1471
|
project = null;
|
|
1898
1472
|
const actor = requestContext.getStore()?.actor;
|
|
1899
|
-
// One Tasks count per request: Needs you in the request's project view
|
|
1900
|
-
// (the open project, else every admitted project), never the page's own
|
|
1901
|
-
// project — a flow or task in another project shows the same number.
|
|
1902
1473
|
const lens = requestContext.getStore()?.lens;
|
|
1903
1474
|
const inboxProject = lens !== undefined && (!restricted() || visible(lens)) ? lens : project;
|
|
1904
1475
|
const badge = needsYouBadge(inboxProject);
|
|
@@ -1910,7 +1481,6 @@ export function createDecisionServer(options) {
|
|
|
1910
1481
|
projectPeek = projectFamilyPeek(project, clock());
|
|
1911
1482
|
}
|
|
1912
1483
|
catch {
|
|
1913
|
-
// Chrome is orientation, never a reason to fail the actual screen.
|
|
1914
1484
|
projectPeek = undefined;
|
|
1915
1485
|
}
|
|
1916
1486
|
}
|
|
@@ -1921,9 +1491,6 @@ export function createDecisionServer(options) {
|
|
|
1921
1491
|
project,
|
|
1922
1492
|
projectScoped: restricted(),
|
|
1923
1493
|
...(projectPeek === undefined ? {} : { projectPeek }),
|
|
1924
|
-
// Enrolled projects first (most recently opened), then the repos this
|
|
1925
|
-
// server was told to serve that nobody has opened yet — the switcher
|
|
1926
|
-
// lists every project it is allowed to show, each exactly once.
|
|
1927
1494
|
projects: sidebarProjects(),
|
|
1928
1495
|
...(facts === undefined ? {} : { csrf: facts.csrf, returnTo: facts.returnTo }),
|
|
1929
1496
|
inboxCount: badge.count,
|
|
@@ -1935,7 +1502,6 @@ export function createDecisionServer(options) {
|
|
|
1935
1502
|
...(store.isDemo() ? { demo: true } : {}),
|
|
1936
1503
|
...(() => { const signIn = signInNotices(store); return signIn.length === 0 ? {} : { signIn }; })(),
|
|
1937
1504
|
...(() => {
|
|
1938
|
-
// Operators only (only someone at this computer can update it), and gone once this version is dismissed.
|
|
1939
1505
|
if (!facts?.csrf || !actor || !store.isInstanceOperator(actor))
|
|
1940
1506
|
return {};
|
|
1941
1507
|
const update = updateFacts();
|
|
@@ -1945,7 +1511,6 @@ export function createDecisionServer(options) {
|
|
|
1945
1511
|
})(),
|
|
1946
1512
|
...(() => {
|
|
1947
1513
|
const databaseFile = store.databaseFile();
|
|
1948
|
-
// Settings → Updates says a `toolroll update` in full; every other page (and the app's update everywhere) names it once.
|
|
1949
1514
|
if (!actor || databaseFile === null || !store.isInstanceOperator(actor))
|
|
1950
1515
|
return {};
|
|
1951
1516
|
const waiting = waitingUpdate(databaseFile, run => store.stopQuiescenceProblem(run) !== null, clock());
|
|
@@ -1960,15 +1525,11 @@ export function createDecisionServer(options) {
|
|
|
1960
1525
|
},
|
|
1961
1526
|
}),
|
|
1962
1527
|
...(!restricted() && !unscopedMode && managedRepos().length > 0 ? { chat: true } : {}),
|
|
1963
|
-
// Live terminals: offered to an installation-wide operator (the routes prove it again).
|
|
1964
1528
|
...(actor !== undefined && !store.isDemo() && store.isInstanceOperator(actor) ? { code: true } : {}),
|
|
1965
1529
|
...(listPane === undefined ? {} : { listPane }),
|
|
1966
1530
|
...(scope === undefined ? {} : { scope }),
|
|
1967
1531
|
};
|
|
1968
1532
|
}
|
|
1969
|
-
// All project counts are read together, at most once per workspace revision
|
|
1970
|
-
// for this reader's access. No task artifacts or processes are inspected to
|
|
1971
|
-
// paint a badge or a project switcher.
|
|
1972
1533
|
function projectCounts(now) {
|
|
1973
1534
|
const facts = requestContext.getStore();
|
|
1974
1535
|
if (facts?.workCounts !== undefined)
|
|
@@ -1984,16 +1545,11 @@ export function createDecisionServer(options) {
|
|
|
1984
1545
|
.reduce((sum, one) => sum + one.totals['needs-you'], 0);
|
|
1985
1546
|
return { count, saturated: false };
|
|
1986
1547
|
}
|
|
1987
|
-
/** One work index page for an already-admitted reader, through the chrome cache: a whole view the chat landing reads
|
|
1988
|
-
* on every refresh. */
|
|
1989
1548
|
function sharedIndexPage(access, options) {
|
|
1990
|
-
// Receipt reconciliation, task and result reads always read work afresh (disk state included).
|
|
1991
1549
|
if (requestContext.getStore()?.freshWork === true)
|
|
1992
1550
|
return workIndexPage(store, clock(), access, options);
|
|
1993
1551
|
return chromeRead(indexKey(access, options), () => workIndexPage(store, clock(), access, options));
|
|
1994
1552
|
}
|
|
1995
|
-
/** A chrome read for this request: what it already read (its prefetch included) stands for the whole request, else
|
|
1996
|
-
* the revision cache's value, else `compute()` now. */
|
|
1997
1553
|
function chromeRead(key, compute) {
|
|
1998
1554
|
const facts = requestContext.getStore();
|
|
1999
1555
|
const held = facts?.chromeReads?.get(key);
|
|
@@ -2004,23 +1560,16 @@ export function createDecisionServer(options) {
|
|
|
2004
1560
|
(facts.chromeReads ??= new Map()).set(key, value);
|
|
2005
1561
|
return value;
|
|
2006
1562
|
}
|
|
2007
|
-
/** The chat landing's three whole views (chat.ts chatHomeOf), as it asks for them. */
|
|
2008
1563
|
const chatHomeViews = () => ["all", "needs-you", "completed"]
|
|
2009
1564
|
.map(view => ({ view, limit: WORK_INDEX_MAX_LIMIT, ...(evidenceRoot === undefined ? {} : { root: evidenceRoot }) }));
|
|
2010
|
-
/** Pages that wear the chrome (the sidebar's states and the Tasks count). */
|
|
2011
1565
|
const CHROME_PAGES = new Set(["home", "work", "board", "agents", "agents.live", "task.page", "run.page", "chat.page", "review", "tasks", "inbox",
|
|
2012
1566
|
"next", "done", "projects.page", "flows.page", "flow.page", "settings.page", "decision.page", "menu"]);
|
|
2013
|
-
/** Before a chromed page renders: its chrome reads (and the chat landing's views) run on a read worker, so the
|
|
2014
|
-
* request thread finds them cached instead of computing them. The reader is the one this request admitted; the keys
|
|
2015
|
-
* carry its exact access (console-chrome.ts). Without a worker, or when one fails, the page computes them as before. */
|
|
2016
1567
|
async function prefetchChrome(route, url, who) {
|
|
2017
1568
|
if (reads === null || who.via !== "cookie" || !CHROME_PAGES.has(route.id))
|
|
2018
1569
|
return;
|
|
2019
1570
|
try {
|
|
2020
1571
|
const actor = requestContext.getStore()?.actor ?? "";
|
|
2021
1572
|
const access = workAccess(), projects = sidebarProjects(), now = clock();
|
|
2022
|
-
// Populate only the revision cache. Each answer must still pass its revision/expiry check when rendering:
|
|
2023
|
-
// another job can remain pending while an earlier answer's approval or Needs-you state changes.
|
|
2024
1573
|
const fill = async (key, read) => { await chromeReads.fill(key, read); };
|
|
2025
1574
|
const jobs = [
|
|
2026
1575
|
fill(countsKey(access), () => reads.workCounts(now, access)),
|
|
@@ -2030,7 +1579,6 @@ export function createDecisionServer(options) {
|
|
|
2030
1579
|
if (!["project", "room", "team"].some(name => url.searchParams.has(name)) && requestContext.getStore()?.freshWork !== true)
|
|
2031
1580
|
for (const options of chatHomeViews())
|
|
2032
1581
|
jobs.push(fill(indexKey(access, options), () => reads.workIndexPage(now, access, options)));
|
|
2033
|
-
// The conversation's briefing (chat.ts: the fleet snapshot's states), read as chat reads it.
|
|
2034
1582
|
const repos = managedRepos();
|
|
2035
1583
|
if (repos.length > 0 && requestContext.getStore()?.freshWork !== true) {
|
|
2036
1584
|
const fleet = { principal: "operator", repos, includeUnplaced: false, viewer: who.name };
|
|
@@ -2040,26 +1588,15 @@ export function createDecisionServer(options) {
|
|
|
2040
1588
|
}
|
|
2041
1589
|
await Promise.all(jobs);
|
|
2042
1590
|
}
|
|
2043
|
-
catch {
|
|
1591
|
+
catch { }
|
|
2044
1592
|
}
|
|
2045
|
-
/** A live page's own beat without a docked chat: 10 s while any admitted task is building, else 30 s. */
|
|
2046
1593
|
function liveRefreshSeconds() {
|
|
2047
1594
|
return projectCounts(clock()).some(one => one.totals.running > 0) ? 10 : 30;
|
|
2048
1595
|
}
|
|
2049
|
-
/** This exact run's review facts for the shared projection (review
|
|
2050
|
-
* fixes, finding 4): the store's bounded retry projection plus the live
|
|
2051
|
-
* reviewer's own liveness — read per run, so an older selected result
|
|
2052
|
-
* never wears a newer run's review. */
|
|
2053
1596
|
const reviewFactsFor = (runId) => reviewFactsOf(store.reviewRetryStateOf(runId), runner => {
|
|
2054
1597
|
const one = store.getRunner(runner)?.runner;
|
|
2055
1598
|
return one !== undefined && runnerAlive(one, clock());
|
|
2056
1599
|
}, buildReviewOf(store, runId)?.state === "pending");
|
|
2057
|
-
// ---- the live peek (A2; three review rounds' findings are the spec) ----
|
|
2058
|
-
//
|
|
2059
|
-
// Names and counts only, never content; nothing durable, ever. The cache
|
|
2060
|
-
// holds finished ESCAPED fragments keyed by run:base:epoch — the epoch
|
|
2061
|
-
// rotates with every lease AND release, so a stale entry's key can never
|
|
2062
|
-
// be asked for again; hits still re-prove the whole guard list.
|
|
2063
1600
|
const peekCache = new Map();
|
|
2064
1601
|
let peekCacheBytes = 0;
|
|
2065
1602
|
const peekInFlight = new Map();
|
|
@@ -2078,39 +1615,24 @@ export function createDecisionServer(options) {
|
|
|
2078
1615
|
peekCacheBytes -= entry.bytes;
|
|
2079
1616
|
}
|
|
2080
1617
|
};
|
|
2081
|
-
/** One typed sentence inside the region — plain words, no raw errors.
|
|
2082
|
-
* `final` marks conditions that cannot heal for this run (finished,
|
|
2083
|
-
* superseded, wrong machine): the region poller reads the marker and
|
|
2084
|
-
* stops, instead of refetching a dead build every beat forever. */
|
|
2085
1618
|
const peekSay = (message, final = false) => html `<p class="meta"${final ? html ` data-region-stop` : ""}>${message}</p>`;
|
|
2086
|
-
/** The sanitize pipeline (finding 30/35): normalize → mask → escape. */
|
|
2087
1619
|
const peekName = (path) => {
|
|
2088
1620
|
const normalized = path.replace(/[\u0000-\u001f\u007f]/g, "");
|
|
2089
1621
|
const masked = scanForSecrets(normalized).length > 0 ? "[redacted: a credential-shaped name]" : normalized;
|
|
2090
1622
|
return textHtml(masked);
|
|
2091
1623
|
};
|
|
2092
1624
|
async function handlePost(route, url, who, request, response, posted) {
|
|
2093
|
-
// Every POST passes the shared mutation guard. Caller, scope, project (including a limited account's posted repo)
|
|
2094
|
-
// and role were admitted by the route table.
|
|
2095
1625
|
const denied = authorizeMutation(request, who, readForm(posted, CONSOLE_FORMS.mutationGuard));
|
|
2096
1626
|
if (denied !== null)
|
|
2097
1627
|
return refuse(response, who, denied.status, denied.message, "/", denied.reconnect?.kind === "password" ? { label: "Reconnect", href: denied.reconnect.href } : undefined);
|
|
2098
|
-
// Standing can change after a token was minted: a viewer's act stays refused, cookie and bearer alike, except
|
|
2099
|
-
// the row's session-local acts.
|
|
2100
1628
|
if (who.role === "viewer" && !route.viewer)
|
|
2101
1629
|
return refuse(response, who, 403, "your login can watch, not act — ask an approver to upgrade you");
|
|
2102
1630
|
const now = clock();
|
|
2103
|
-
// Any accepted mutation may change what the inbox owes; the badge
|
|
2104
|
-
// re-counts within five seconds either way, this just makes it exact.
|
|
2105
1631
|
bustBadge();
|
|
2106
1632
|
return dispatchConsole(route, { url, who, request, response, route, now, project: projectOf(who, request) ?? null, chosenProject: projectOf(who, request) ?? null, posted });
|
|
2107
1633
|
}
|
|
2108
|
-
// Only explicitly authorized conversations receive automatic turns. The
|
|
2109
|
-
// deterministic scan is idle without new meaningful work; shutdown drains it.
|
|
2110
1634
|
let leadMaintenance = null;
|
|
2111
1635
|
let leadClosing = false;
|
|
2112
|
-
// Started in the console's folder scope (folder-gate.ts), so their own timers and the personal queue's kick (which
|
|
2113
|
-
// restores the context it was started in, never a request's) read folders as the console's work does.
|
|
2114
1636
|
server.once('listening', () => consoleFolders(options, identities, () => {
|
|
2115
1637
|
team.start();
|
|
2116
1638
|
personalQueue.start();
|
|
@@ -2118,23 +1640,17 @@ export function createDecisionServer(options) {
|
|
|
2118
1640
|
run: () => runLeadFollowPass({ store, repos: managedRepos, evidenceRoot, clock,
|
|
2119
1641
|
provider: () => { const enabled = chatEnablement(); return enabled.ok ? { config: enabled.config, key: enabled.key } : null; },
|
|
2120
1642
|
fetcher: chatFetcher, ...(options.subscriptionChatRunner ? { subscriptionRunner: options.subscriptionChatRunner } : {}) }),
|
|
2121
|
-
// Pending delivery is durable and is read again on the next pass; a failure is said where follow status is read.
|
|
2122
1643
|
onError: error => recordLeadFollowFailure(store, error, clock()),
|
|
2123
1644
|
});
|
|
2124
1645
|
}));
|
|
2125
|
-
// Background checks (docs/plans/get-side-effects.md): what pages show about this computer's agent sign-ins and the
|
|
2126
|
-
// services Toolroll uses is asked here, on a timer, and a page reads the last answer. A page visit never starts a
|
|
2127
|
-
// process or a network call, and never acts on what a check finds: lifting a sign-in pause and the lead-by-default
|
|
2128
|
-
// rule run from this pass (or a person's Check now), not from a GET.
|
|
2129
1646
|
let backgroundChecks = null;
|
|
2130
1647
|
let agentsCheckedAt = 0;
|
|
2131
1648
|
if (options.backgroundChecks === true)
|
|
2132
1649
|
server.once('listening', () => {
|
|
2133
|
-
// This Toolroll's folder for secrets, checked once now, so a page can read which are set without preparing it.
|
|
2134
1650
|
try {
|
|
2135
1651
|
secretFolder();
|
|
2136
1652
|
}
|
|
2137
|
-
catch {
|
|
1653
|
+
catch { }
|
|
2138
1654
|
backgroundChecks = startMaintenance({ intervalMs: options.backgroundChecksMs ?? BACKGROUND_CHECKS_MS, shouldStop: () => leadClosing,
|
|
2139
1655
|
run: () => consoleFolders(options, identities, async () => {
|
|
2140
1656
|
const now = Date.now();
|
|
@@ -2142,11 +1658,9 @@ export function createDecisionServer(options) {
|
|
|
2142
1658
|
if (now - agentsCheckedAt >= (options.backgroundChecksMs ?? LOCAL_AGENTS_EVERY_MS)) {
|
|
2143
1659
|
agentsCheckedAt = now;
|
|
2144
1660
|
waits.push(checkLocalAgents());
|
|
2145
|
-
// The other agents Settings lists: their last answer is what the page shows.
|
|
2146
1661
|
for (const provider of PROVIDER_IDS.filter(isKeyedProvider).filter(one => one !== "claude" && one !== "codex"))
|
|
2147
1662
|
waits.push(connectionCheck(provider).catch(() => undefined));
|
|
2148
1663
|
}
|
|
2149
|
-
// Project identities, resolved off the thread: what pages asked about, and answers gone stale.
|
|
2150
1664
|
waits.push(identities.refresh([...configuredPaths, ...exactRepos(), ...(options.currentRepos?.() ?? []), ...store.listProjects().map(one => one.path), ...store.knownRepos()]));
|
|
2151
1665
|
integrations.refreshIfDue();
|
|
2152
1666
|
const sso = ssoSettings();
|
|
@@ -2155,28 +1669,22 @@ export function createDecisionServer(options) {
|
|
|
2155
1669
|
if (chatKeyFor("openrouter-api") !== null)
|
|
2156
1670
|
waits.push(chatCatalog());
|
|
2157
1671
|
const openrouter = readProviderKey("openrouter", providerHome);
|
|
2158
|
-
// The cache answers until its list is due again (ten minutes, or 30 s after a failure), then fetches.
|
|
2159
1672
|
if (openrouter !== null)
|
|
2160
1673
|
waits.push(modelCatalog(openrouter));
|
|
2161
|
-
// What used to be closed by whichever page read it first: an operating mode or a flow's "runs on its own"
|
|
2162
|
-
// grant past its expiry, a stalled update, old coordinator proposals. (Lead turns past their deadline are the
|
|
2163
|
-
// follow pass's, which settles them even while an update drains.)
|
|
2164
1674
|
const at = clock();
|
|
2165
1675
|
store.closeExpiredModes(at);
|
|
2166
1676
|
closeExpiredUnattendedGrants(store, at);
|
|
2167
1677
|
store.sweepCoordinatorProposals(at);
|
|
2168
|
-
// The demo's scripted story, if a beat timer missed (its pages only read it).
|
|
2169
1678
|
try {
|
|
2170
1679
|
options.demoLead?.advance(at);
|
|
2171
1680
|
}
|
|
2172
|
-
catch {
|
|
1681
|
+
catch { }
|
|
2173
1682
|
const databaseFile = store.databaseFile();
|
|
2174
1683
|
if (databaseFile !== null && !updateChecksOff(process.env, options.configDir ?? dirname(databaseFile)).off) {
|
|
2175
1684
|
const last = lastChecks.known("release");
|
|
2176
1685
|
if (lastChecks.due("release", last !== null && "problem" in last.value ? 60_000 : 15 * 60_000))
|
|
2177
1686
|
waits.push(checkRelease());
|
|
2178
1687
|
}
|
|
2179
|
-
// Chat's first tasks (issues and TODOs) while the first run is open and nothing is filed yet.
|
|
2180
1688
|
if (firstRunStepsNow(clock()) !== null && !store.hasAnyWork()) {
|
|
2181
1689
|
for (const repo of managedRepos()) {
|
|
2182
1690
|
const entry = firstTasks.get(repo);
|
|
@@ -2184,18 +1692,11 @@ export function createDecisionServer(options) {
|
|
|
2184
1692
|
continue;
|
|
2185
1693
|
const reading = { until: Date.now() + 10 * 60_000, found: entry?.found ?? null, reading: true };
|
|
2186
1694
|
firstTasks.set(repo, reading);
|
|
2187
|
-
// A test's runner reads in-process; otherwise in a process of its own (learning-look.ts), so the hardened
|
|
2188
|
-
// git's synchronous reads of the project's folder never hold the console's thread.
|
|
2189
1695
|
waits.push((options.firstTaskRunner !== undefined ? findFirstTasks(repo, options.firstTaskRunner) : firstTasksOffThread(repo)).then(found => { reading.found = found; }, () => { }).finally(() => { reading.reading = false; }));
|
|
2190
1696
|
}
|
|
2191
1697
|
}
|
|
2192
|
-
// Learning recovered from reviewed runs (and a project's saved rows carried to its current identity), which
|
|
2193
|
-
// Settings → Learning and Knowledge used to do as they opened.
|
|
2194
1698
|
if (lastChecks.due("learning", 10 * 60_000)) {
|
|
2195
1699
|
waits.push(lastChecks.run("learning", async () => {
|
|
2196
|
-
// Its reads of each project's Git folder are synchronous, so with a database file they run in a process of
|
|
2197
|
-
// their own (learning-look.ts): a folder that doesn't answer costs its own recovery, never the console. An
|
|
2198
|
-
// in-memory database (a test) can't be shared, so it is read here.
|
|
2199
1700
|
if (databaseFile !== null) {
|
|
2200
1701
|
await recoverLearningOffThread({ databaseFile, evidenceRoot, repos: managedRepos() }, options.learningRunner === undefined ? {} : { runner: options.learningRunner });
|
|
2201
1702
|
return;
|
|
@@ -2204,17 +1705,15 @@ export function createDecisionServer(options) {
|
|
|
2204
1705
|
try {
|
|
2205
1706
|
acceptedIdentities(store, repo);
|
|
2206
1707
|
}
|
|
2207
|
-
catch {
|
|
1708
|
+
catch { }
|
|
2208
1709
|
try {
|
|
2209
1710
|
recoverLearning(store, evidenceRoot, repo);
|
|
2210
1711
|
}
|
|
2211
|
-
catch {
|
|
1712
|
+
catch { }
|
|
2212
1713
|
}
|
|
2213
1714
|
}));
|
|
2214
1715
|
}
|
|
2215
|
-
// What Codex has configured, per project, for Settings → Tools to offer.
|
|
2216
1716
|
for (const repo of managedRepos()) {
|
|
2217
|
-
// The project's own .mcp.json, read off the console's thread (folder-look.ts) for the Tools page and the lead.
|
|
2218
1717
|
if (options.identities !== undefined && lastChecks.due(`project-mcp:${repo}`, 10 * 60_000)) {
|
|
2219
1718
|
waits.push(lastChecks.run(`project-mcp:${repo}`, async () => { const looked = await lookProjectMcp(repo); if (looked.ok)
|
|
2220
1719
|
savedProjectMcp.set(repo, looked.value); return looked.ok; }));
|
|
@@ -2225,25 +1724,17 @@ export function createDecisionServer(options) {
|
|
|
2225
1724
|
if (databaseFile !== null && lastChecks.due("storage", 30 * 60_000)) {
|
|
2226
1725
|
waits.push(lastChecks.run("storage", () => checkoutPlan(store, storagePool(databaseFile), clock(), { manual: true })));
|
|
2227
1726
|
}
|
|
2228
|
-
// The same reading the chrome does, outside a page visit, so it releases a stalled `toolroll update` and a stalled
|
|
2229
|
-
// desktop update in every folder the chrome looks in.
|
|
2230
1727
|
if (databaseFile !== null) {
|
|
2231
1728
|
try {
|
|
2232
1729
|
waitingUpdate(databaseFile, run => store.stopQuiescenceProblem(run) !== null, at);
|
|
2233
1730
|
}
|
|
2234
|
-
catch {
|
|
1731
|
+
catch { }
|
|
2235
1732
|
}
|
|
2236
1733
|
await Promise.all(waits);
|
|
2237
1734
|
}),
|
|
2238
1735
|
onError: () => { }, });
|
|
2239
1736
|
void backgroundChecks.runNow();
|
|
2240
1737
|
});
|
|
2241
|
-
// Keep native process custody until shutdown has verified agent/tool exit.
|
|
2242
|
-
// Coding custody does not depend on subagents or the lead's follow pass, so
|
|
2243
|
-
// it closes at once and never waits behind a model turn: a stop must release
|
|
2244
|
-
// the owner record well inside the service's exit window (Oct 2: deploys
|
|
2245
|
-
// over 0.9.11 found it still held by a killed process).
|
|
2246
|
-
/** Set once the terminal handlers exist (below): drops every socket and ends every program. */
|
|
2247
1738
|
const terminalClosing = {};
|
|
2248
1739
|
let codingClosing = null;
|
|
2249
1740
|
const closeCoding = () => {
|
|
@@ -2260,14 +1751,13 @@ export function createDecisionServer(options) {
|
|
|
2260
1751
|
try {
|
|
2261
1752
|
requestBudget.flush();
|
|
2262
1753
|
}
|
|
2263
|
-
catch {
|
|
1754
|
+
catch { }
|
|
2264
1755
|
flowRooms.close();
|
|
2265
1756
|
taskRooms.close();
|
|
2266
1757
|
liveHandlers.close();
|
|
2267
1758
|
detachReads();
|
|
2268
1759
|
liveFollower.close();
|
|
2269
1760
|
void Promise.all([closeCoding(), terminalClosing.close?.().then(problem => { terminalClosing.problem = problem; }), bounded(team.close()), bounded(personalQueue.close()), bounded(leadMaintenance?.stop()), bounded(backgroundChecks?.stop()), bounded(reads?.close())])
|
|
2270
|
-
// The server always closes; a terminal whose processes couldn't be proved stopped is the close's error.
|
|
2271
1761
|
.then(() => closeServer(error => callback?.(error ?? terminalClosing.problem ?? undefined))).catch(error => {
|
|
2272
1762
|
if (callback)
|
|
2273
1763
|
callback(error instanceof Error ? error : Error('Coding session shutdown failed.'));
|
|
@@ -2465,7 +1955,6 @@ export function createDecisionServer(options) {
|
|
|
2465
1955
|
};
|
|
2466
1956
|
const liveHandlers = createLiveHandlers(handlerRuntime, { bus: liveBus, workspaceRevision, chatProjectOf: chatHandlers.chatProjectOf, projectSummaryFor, projectScopeFor });
|
|
2467
1957
|
liveTurnStarted = liveHandlers.turnStarted;
|
|
2468
|
-
// The one upgrade this server takes is a terminal's socket (terminals.ts listens); anything else is dropped.
|
|
2469
1958
|
const terminalHandlers = createTerminalHandlers(handlerRuntime, { ...terminals, bus: liveBus, allowedHost, publicOrigin, forwardedSourceOf, server });
|
|
2470
1959
|
terminalClosing.close = terminalHandlers.close;
|
|
2471
1960
|
const remoteHandlers = createRemoteHandlers(remoteRuntime);
|
|
@@ -2475,7 +1964,6 @@ export function createDecisionServer(options) {
|
|
|
2475
1964
|
...remoteHandlers.registrations, ...liveHandlers.registrations, ...terminalHandlers.registrations, ...createSweepHandlers(handlerRuntime).registrations,
|
|
2476
1965
|
...createWelcomeHandlers(handlerRuntime).registrations,
|
|
2477
1966
|
]);
|
|
2478
|
-
/** The row's role, after admission and before its one handler. */
|
|
2479
1967
|
function dispatchConsole(route, ctx) {
|
|
2480
1968
|
const { who, response } = ctx;
|
|
2481
1969
|
const held = route.role === "any" || ((!route.roleBrowser || who.via === "cookie") &&
|
|
@@ -2498,8 +1986,6 @@ export function createDecisionServer(options) {
|
|
|
2498
1986
|
}
|
|
2499
1987
|
return withEdgePolicy(route, () => registry.edge(route, { ...ctx, route }));
|
|
2500
1988
|
}
|
|
2501
|
-
/** Easy-start C1: the local owner a browser session acts as on an allowed row, bound to its session's saved row and
|
|
2502
|
-
* the generations it was made under; null when it no longer stands (refused like any other row). */
|
|
2503
1989
|
function localPrincipalOf(who, route) {
|
|
2504
1990
|
const sessionHash = sessions.hashOf(who.session);
|
|
2505
1991
|
if (route.localAct === null || who.session.localGeneration === null || sessionHash === null)
|
|
@@ -2508,17 +1994,13 @@ export function createDecisionServer(options) {
|
|
|
2508
1994
|
transport: "browser", act: route.localAct, sessionHash, request: { ended: false } };
|
|
2509
1995
|
return localOwnerStandsNow(store, principal) ? principal : null;
|
|
2510
1996
|
}
|
|
2511
|
-
/** What a local owner hears on a row not marked for it: set a first password, or sign in with the one set. */
|
|
2512
1997
|
function refuseLocalOwner(response, who, method, url, accept) {
|
|
2513
|
-
// The page it was refused on, built here from the request (never from a header): a form's nearest page above it.
|
|
2514
1998
|
const words = localOwnerRefusal(store.accountOf(who.name)?.passwordSet === true, refusalNext(method, url));
|
|
2515
|
-
// Which refusal this was, for the shell (it offers the same way on) and the route inventory's rule table.
|
|
2516
1999
|
response.setHeader("x-toolroll-refused", "local-owner");
|
|
2517
2000
|
if (String(accept ?? "").includes("application/json"))
|
|
2518
2001
|
return respond(response, 403, "application/json", JSON.stringify({ ok: false, said: words.message, href: words.href }));
|
|
2519
2002
|
return refuse(response, who, 403, words.message, words.href, { label: words.action, href: words.href });
|
|
2520
2003
|
}
|
|
2521
|
-
// Defined last, so the reviewed functions above keep their places (password-freshness.test.ts).
|
|
2522
2004
|
function holdSignInLink(link) {
|
|
2523
2005
|
for (const [key, one] of signInLinks)
|
|
2524
2006
|
if (one.expires < Date.now())
|
|
@@ -2529,8 +2011,6 @@ export function createDecisionServer(options) {
|
|
|
2529
2011
|
signInLinks.set(linkKey(code), link);
|
|
2530
2012
|
return `${SIGN_IN_LINK_PATH}${code}`;
|
|
2531
2013
|
}
|
|
2532
|
-
/** Easy-start C4: a local owner's one-time link. It names no account: local mode's own owner, read now, while local
|
|
2533
|
-
* mode is on and the owner is active. Redemption reads local mode again and issues only a local-owner session. */
|
|
2534
2014
|
function mintLocalLink() {
|
|
2535
2015
|
const binding = localOwnerBinding(store);
|
|
2536
2016
|
if (binding === null)
|
|
@@ -2547,12 +2027,6 @@ export { ssoStepUps, wrongHostPage } from "./server/render-people.js";
|
|
|
2547
2027
|
export { diffFileAnchor, editorFileHref, needActionOf, orderChangedFiles, plainConclusionOf, rankReviewQueue, reviewFilePriority, reviewPriorityOf, runFactsFragment, withinSignedTouches } from "./server/render-results.js";
|
|
2548
2028
|
export { decisionAnswerScript, earlierVersionsWords, revisionLineageWords } from "./server/render-tasks.js";
|
|
2549
2029
|
export { SIGN_IN_LINK_MS, SIGN_IN_LINK_PATH } from "./server/session.js";
|
|
2550
|
-
/**
|
|
2551
|
-
* The console's configured repos and roots. Without identities (embedders and tests) they resolve once, synchronously,
|
|
2552
|
-
* as before. With them, the console resolves them in the background after it listens (operate.ts startConsole), so
|
|
2553
|
-
* each is read from the last answer: a configured path counts by its real path from the moment its folder answers,
|
|
2554
|
-
* and until then only by its spelling, which the strict `canon` never matches for an unresolved row.
|
|
2555
|
-
*/
|
|
2556
2030
|
function startupCeiling(repos, roots, identities, canon) {
|
|
2557
2031
|
const configured = repos.length > 0 || roots.length > 0;
|
|
2558
2032
|
const configuredPaths = [...repos, ...roots];
|
|
@@ -2566,8 +2040,6 @@ function startupCeiling(repos, roots, identities, canon) {
|
|
|
2566
2040
|
configuredPaths,
|
|
2567
2041
|
};
|
|
2568
2042
|
}
|
|
2569
|
-
// Only resolved entries authorize: an unresolved configured path is in no list, so it matches nothing (not even its
|
|
2570
|
-
// own spelling) and is no session's default until its folder answers.
|
|
2571
2043
|
const resolved = (paths) => [...new Set(paths.flatMap(path => { const real = identities.known(path); return typeof real === "string" ? [real] : []; }))];
|
|
2572
2044
|
const ceiling = {
|
|
2573
2045
|
get repos() { return resolved(repos); },
|
|
@@ -2582,14 +2054,7 @@ function startupCeiling(repos, roots, identities, canon) {
|
|
|
2582
2054
|
configuredPaths,
|
|
2583
2055
|
};
|
|
2584
2056
|
}
|
|
2585
|
-
/** Each console's folder gate (folder-gate.ts), made once for its options. */
|
|
2586
2057
|
const consoleGates = new WeakMap();
|
|
2587
|
-
/**
|
|
2588
|
-
* Runs the console's own work (a request, a background pass) in its folder scope: with identities resolved off its
|
|
2589
|
-
* thread (up and serve), it reads a person's folder synchronously only while that folder answered a look lately; its
|
|
2590
|
-
* own data (the database's folder, evidence, build checkouts) is read freely. Without identities (embedders, tests):
|
|
2591
|
-
* as before. Work that isn't the console's (a worker in the same process) never runs in it.
|
|
2592
|
-
*/
|
|
2593
2058
|
function consoleFolders(options, identities, work) {
|
|
2594
2059
|
if (options.identities === undefined)
|
|
2595
2060
|
return work();
|