mixdog 1.3.4 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/oh-my-pi-MIT.txt +23 -0
- package/NOTICE.md +17 -2
- package/README.md +11 -0
- package/package.json +3 -11
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/embed-google-oauth-client.mjs +49 -0
- package/scripts/gate-local.mjs +4 -2
- package/scripts/lib/test-spawn-ledger.mjs +46 -1
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +10 -1
- package/scripts/test.mjs +12 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +25 -1
- package/src/defaults/skills/browser-use/SKILL.md +77 -17
- package/src/defaults/skills/code-tidy/SKILL.md +142 -270
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
- package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
- package/src/defaults/skills/computer-use/SKILL.md +33 -9
- package/src/defaults/skills/setup/SKILL.md +29 -2
- package/src/defaults/skills/setup/references/actions.md +117 -28
- package/src/defaults/skills/setup/references/surfaces.md +77 -35
- package/src/defaults/skills/setup-guide/SKILL.md +3 -3
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/keychain-cjs.cjs +359 -29
- package/src/lib/rules-builder.cjs +10 -8
- package/src/rules/agent/AGENT.md +6 -6
- package/src/rules/agent-mode/AGENT_MODE.md +10 -2
- package/src/rules/agent-mode/MIXY.md +12 -0
- package/src/rules/lead/LEAD.md +18 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +193 -0
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
- package/src/runtime/agent/orchestrator/config.mjs +34 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +72 -67
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +54 -1
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +177 -73
- package/src/runtime/agent/orchestrator/mcp/client.mjs +25 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +31 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +16 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +42 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +21 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +23 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +11 -1
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +230 -137
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +64 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +18 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +160 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +64 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +52 -8
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +17 -2
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +142 -104
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +10 -4
- package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +10 -217
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -6
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +409 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-families.mjs +187 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-identity.mjs +342 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-models.mjs +541 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-protobuf.mjs +305 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-request.mjs +558 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-schema.mjs +880 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-session.mjs +190 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-tool-args.mjs +351 -0
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +560 -0
- package/src/runtime/agent/orchestrator/providers/devin.mjs +491 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/gemini-output-limit.mjs +46 -0
- package/src/runtime/agent/orchestrator/providers/gemini-reasoning-relay.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +7 -11
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +61 -22
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -4
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +96 -5
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +23 -5
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +39 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/provider-availability.mjs +19 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
- package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/registry.mjs +43 -3
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/wire-protobuf-codec.mjs +368 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +12 -7
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/reaction-tool-defs.mjs +133 -0
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +23 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-profile.mjs +35 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +57 -10
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +9 -6
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +0 -3
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +792 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +148 -25
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +47 -9
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +18 -22
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +51 -3
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +23 -5
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
- package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +23 -13
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +129 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +18 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/message-reactions.mjs +89 -0
- package/src/runtime/agent/orchestrator/session/message-reactions.test-support.mjs +75 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +19 -8
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +0 -2
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +47 -11
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +20 -2
- package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
- package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
- package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header-cache.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +133 -18
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -14
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +84 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +25 -2
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +25 -5
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +65 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
- package/src/runtime/browser-bridge/action-schema.mjs +13 -2
- package/src/runtime/browser-bridge/client.mjs +73 -8
- package/src/runtime/browser-bridge/input-fields.mjs +8 -9
- package/src/runtime/browser-bridge/mail-links.mjs +107 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
- package/src/runtime/channels/lib/config.mjs +6 -61
- package/src/runtime/channels/lib/crash-log.mjs +1 -1
- package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
- package/src/runtime/channels/lib/event-queue.mjs +3 -18
- package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
- package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
- package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
- package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
- package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
- package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
- package/src/runtime/channels/lib/schedule-errors.mjs +51 -0
- package/src/runtime/channels/lib/scheduler.mjs +135 -154
- package/src/runtime/channels/lib/schedules-db.mjs +83 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +14 -2
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
- package/src/runtime/channels/lib/worker-main.mjs +19 -34
- package/src/runtime/computer-bridge/action-schema.mjs +42 -1
- package/src/runtime/computer-bridge/actions.mjs +9 -1
- package/src/runtime/computer-bridge/client.mjs +180 -8
- package/src/runtime/computer-bridge/core-actions.mjs +2 -2
- package/src/runtime/computer-bridge/error-recovery.mjs +27 -2
- package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
- package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
- package/src/runtime/connectors/gmail-app-password.mjs +172 -39
- package/src/runtime/connectors/google-mcp.mjs +10 -12
- package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
- package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
- package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
- package/src/runtime/office/com/com-adapter.mjs +17 -1
- package/src/runtime/office/com/office-com-host.ps1 +51 -1
- package/src/runtime/office/pdf/cfb-fixture.mjs +328 -0
- package/src/runtime/office/pdf/cfb-protection.mjs +311 -0
- package/src/runtime/office/pdf/document-preview.mjs +326 -8
- package/src/runtime/office/pdf/ooxml-crypto.mjs +469 -0
- package/src/runtime/office/pdf/ooxml-key-worker.mjs +14 -0
- package/src/runtime/office/portable/portable-soffice.mjs +45 -5
- package/src/runtime/session-search/session-search-db.mjs +73 -9
- package/src/runtime/session-search/session-search-ingest.mjs +110 -20
- package/src/runtime/shared/abort-race.mjs +89 -0
- package/src/runtime/shared/agent-avatar.d.mts +0 -1
- package/src/runtime/shared/agent-kind.mjs +4 -3
- package/src/runtime/shared/bot-hops.mjs +62 -2
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +51 -35
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +33 -2
- package/src/runtime/shared/connector-catalog.mjs +166 -8
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -14
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/developer-options.mjs +32 -1
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/file-permissions.d.mts +0 -8
- package/src/runtime/shared/file-permissions.mjs +0 -11
- package/src/runtime/shared/git-direct.test-support.mjs +19 -0
- package/src/runtime/shared/google-oauth-client.generated.json +4 -0
- package/src/runtime/shared/google-oauth-client.mjs +34 -9
- package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
- package/src/runtime/shared/heartbeat-interval.mjs +19 -0
- package/src/runtime/shared/idle-auto-clear-gate.mjs +104 -0
- package/src/runtime/shared/llm/anthropic-betas.mjs +7 -0
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +12 -0
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +1 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/llm/well-formed.mjs +66 -0
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/orchestration.d.mts +2 -0
- package/src/runtime/shared/pg-schema-db.mjs +27 -16
- package/src/runtime/shared/profile-config.cjs +1 -1
- package/src/runtime/shared/prompt-origin.d.mts +4 -0
- package/src/runtime/shared/prompt-origin.mjs +21 -0
- package/src/runtime/shared/provider-accounts.mjs +231 -59
- package/src/runtime/shared/provider-api-key.mjs +19 -8
- package/src/runtime/shared/provider-display-name.mjs +29 -0
- package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
- package/src/runtime/shared/routine-trigger.d.mts +2 -0
- package/src/runtime/shared/routine-trigger.mjs +52 -0
- package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
- package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
- package/src/runtime/shared/session-activity-signal.mjs +34 -1
- package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
- package/src/runtime/shared/session-sidebar-host.mjs +7 -3
- package/src/runtime/shared/session-turn-prelude.mjs +18 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -14
- package/src/runtime/shared/transcript-metadata.mjs +38 -0
- package/src/runtime/shared/user-data-guard.mjs +30 -2
- package/src/runtime/shared/worktrees/activity-gate.mjs +12 -5
- package/src/runtime/shared/worktrees/test-support.mjs +9 -4
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +27 -8
- package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
- package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
- package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
- package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
- package/src/session-runtime/agent-home-delivery.mjs +486 -179
- package/src/session-runtime/agent-room-delivery.mjs +28 -1
- package/src/session-runtime/agent-task-sessions.mjs +561 -199
- package/src/session-runtime/boot/apis.mjs +9 -0
- package/src/session-runtime/boot/begin.mjs +3 -2
- package/src/session-runtime/boot/infrastructure.mjs +4 -0
- package/src/session-runtime/boot/providers.mjs +3 -0
- package/src/session-runtime/boot/tools.mjs +8 -1
- package/src/session-runtime/bot-chain.mjs +32 -0
- package/src/session-runtime/bot-status.mjs +7 -4
- package/src/session-runtime/channel-config-api.mjs +63 -17
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +132 -28
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +45 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +72 -5
- package/src/session-runtime/internal-tool-executor.mjs +4 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +3 -1
- package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
- package/src/session-runtime/lifecycle/teardown.mjs +23 -5
- package/src/session-runtime/mcp-glue/server-catalog.mjs +61 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +927 -0
- package/src/session-runtime/model-route/route-tuning.mjs +10 -6
- package/src/session-runtime/notification-bus.mjs +31 -117
- package/src/session-runtime/notification-completion-queue.mjs +54 -0
- package/src/session-runtime/notification-listeners.mjs +76 -0
- package/src/session-runtime/owned-automation-run.mjs +35 -13
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-auth/login.mjs +7 -0
- package/src/session-runtime/provider-models/row-hydration.mjs +13 -2
- package/src/session-runtime/resource-mcp-api.mjs +143 -224
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
- package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
- package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
- package/src/session-runtime/resource-mcp-servers.mjs +78 -0
- package/src/session-runtime/resource-mcp-store.mjs +49 -0
- package/src/session-runtime/routine-home-turn.mjs +33 -6
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/schedule-session-run.mjs +7 -3
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +8 -12
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +7 -2
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
- package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
- package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
- package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
- package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +73 -31
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/provider-admin.mjs +128 -25
- package/src/session-runtime/services/schedule-access.mjs +14 -0
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/services/usage-dashboard-model.mjs +1 -0
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +8 -4
- package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
- package/src/session-runtime/settings-compaction-api.mjs +2 -40
- package/src/session-runtime/settings-system-api.mjs +28 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +187 -29
- package/src/session-runtime/setup-tool/extended-actions.mjs +33 -2
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +68 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +92 -6
- package/src/session-runtime/task-session-route.mjs +99 -0
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +21 -4
- package/src/session-runtime/tool-policy/surface-helpers.mjs +2 -3
- package/src/session-runtime/tool-surface.mjs +4 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +98 -21
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -1
- package/src/session-runtime/workspace/transitions.mjs +10 -2
- package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
- package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
- package/src/standalone/channel-transport/client-registry.mjs +24 -163
- package/src/standalone/channel-transport.mjs +1 -1
- package/src/standalone/daemon-channels-loader.mjs +10 -8
- package/src/standalone/daemon-session-control/list-page.mjs +72 -0
- package/src/standalone/daemon-session-control/request.mjs +51 -0
- package/src/standalone/daemon-session-control/room-host.mjs +57 -0
- package/src/standalone/daemon-session-control/route-control.mjs +48 -0
- package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
- package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
- package/src/standalone/daemon-session-control.mjs +161 -157
- package/src/standalone/daemon.mjs +33 -2
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +10 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +6 -2
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/quota-resume.mjs +3 -0
- package/src/standalone/session-service/retention/busy.mjs +65 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +23 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +6 -0
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-service.mjs +13 -0
- package/src/standalone/session-tool-output.mjs +21 -10
- package/src/tui/app/model-options.mjs +4 -27
- package/src/tui/dist/index.mjs +81 -65
- package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/notice-surface.mjs +23 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- package/src/tui/session/session-api/integrations.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +8 -0
- package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
- package/src/tui/session/session-api/routes-write.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +30 -2
- package/src/tui/session/session-flow/auto-clear.mjs +4 -33
- package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
- package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
- package/src/tui/session/session-flow/drain-retry.mjs +35 -0
- package/src/tui/session/session-flow/drain.mjs +118 -149
- package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
- package/src/tui/session/session-flow/queue/take-entries.mjs +5 -2
- package/src/tui/session/tool-approval-delegation.mjs +64 -0
- package/src/tui/session/tool-approval-request.mjs +39 -0
- package/src/tui/session/tool-approval-settle.mjs +62 -0
- package/src/tui/session/tool-approval.mjs +69 -115
- package/src/tui/session/tui-steering-persist.mjs +8 -1
- package/src/tui/session/turn-stream.mjs +36 -3
- package/src/tui/session/turn.mjs +23 -2
- package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
- package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
- package/scripts/code-graph-description-contract.mjs +0 -188
- package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
|
@@ -6,24 +6,18 @@
|
|
|
6
6
|
import {
|
|
7
7
|
isGoalQueuedEntry,
|
|
8
8
|
isQueuedEntryEditable,
|
|
9
|
-
isQueuedEntryVisible,
|
|
10
9
|
isSlashQueuedEntry,
|
|
11
10
|
mergePromptContents,
|
|
12
11
|
mergePastedImages,
|
|
13
12
|
mergePastedTexts,
|
|
14
13
|
} from '../queue-helpers.mjs';
|
|
15
|
-
import { appendTuiSteeringPersist, dropTuiSteeringPersist } from '../tui-steering-persist.mjs';
|
|
16
14
|
import { parseModelVisibleCompletionWrapper } from '../agent-envelope.mjs';
|
|
17
15
|
import { rewoundFailedTurnItems } from './failed-turn-rewind.mjs';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const BLOCKED_DRAIN_RETRY_MS = 50;
|
|
24
|
-
// Session commands (setModel/newSession/resume/auto-clear) release in seconds;
|
|
25
|
-
// an active turn can run for many minutes, so its gate is re-read coarsely.
|
|
26
|
-
const ACTIVE_TURN_RECHECK_MS = 2_000;
|
|
16
|
+
import { PROMPT_ORIGIN_AGENT_REPORT, PROMPT_ORIGIN_CHANNEL } from '../../../runtime/shared/prompt-origin.mjs';
|
|
17
|
+
import { ACTIVE_TURN_RECHECK_MS, createBlockedDrainRetry } from './drain-retry.mjs';
|
|
18
|
+
import { createClosedQueueSettler } from './drain-closed-queue.mjs';
|
|
19
|
+
import { createEnqueue } from './drain-enqueue.mjs';
|
|
20
|
+
import { sessionTurnPrelude } from '../../../runtime/shared/session-turn-prelude.mjs';
|
|
27
21
|
|
|
28
22
|
function earliestSubmittedAt(batch) {
|
|
29
23
|
const earliest = batch.reduce((min, entry) => {
|
|
@@ -89,6 +83,18 @@ function batchPromptSource(batch) {
|
|
|
89
83
|
return undefined;
|
|
90
84
|
}
|
|
91
85
|
|
|
86
|
+
// The batch's provenance for the prompt-cache warmer (cache-warm.mjs
|
|
87
|
+
// cacheWarmTurnKind): one inbound channel message makes it real external
|
|
88
|
+
// activity whatever its presentation; it is an agent report only when every
|
|
89
|
+
// entry is one (a user prompt merged into it is the user speaking).
|
|
90
|
+
function batchPromptOrigin(batch) {
|
|
91
|
+
if (batch.some((entry) => entry?.origin === PROMPT_ORIGIN_CHANNEL)) return { promptOrigin: PROMPT_ORIGIN_CHANNEL };
|
|
92
|
+
if (batch.length > 0 && batch.every((entry) => entry?.origin === PROMPT_ORIGIN_AGENT_REPORT)) {
|
|
93
|
+
return { promptOrigin: PROMPT_ORIGIN_AGENT_REPORT };
|
|
94
|
+
}
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
|
|
92
98
|
/** The batch's transcript metadata; a batch of completion wakes also records
|
|
93
99
|
* the execution ids it delivers (`consumedExecutionIds`, from each entry's
|
|
94
100
|
* execution or resume keys; null for one with neither), the structured
|
|
@@ -117,6 +123,7 @@ export function turnOptionsForBatch(batch, steering) {
|
|
|
117
123
|
);
|
|
118
124
|
return {
|
|
119
125
|
promptSource: batchPromptSource(batch),
|
|
126
|
+
...batchPromptOrigin(batch),
|
|
120
127
|
retryFailedTurn: batch[0]?.retryFailedTurn === true,
|
|
121
128
|
retryTurnId: batch[0]?.retryTurnId || null,
|
|
122
129
|
retryAttemptId: batch[0]?.retryAttemptId || null,
|
|
@@ -151,19 +158,36 @@ export function turnOptionsForBatch(batch, steering) {
|
|
|
151
158
|
for (const entry of batch) {
|
|
152
159
|
try {
|
|
153
160
|
entry.onToolResult?.(message);
|
|
154
|
-
} catch {
|
|
161
|
+
} catch {
|
|
162
|
+
// One submitter's callback must not stop the batch's other entries.
|
|
163
|
+
}
|
|
155
164
|
}
|
|
156
165
|
},
|
|
157
166
|
onSettled: (detail) => {
|
|
158
167
|
for (const entry of batch) {
|
|
159
168
|
try {
|
|
160
169
|
entry.onSettled?.(detail);
|
|
161
|
-
} catch {
|
|
170
|
+
} catch {
|
|
171
|
+
// One submitter's callback must not stop the batch's other entries.
|
|
172
|
+
}
|
|
162
173
|
}
|
|
163
174
|
},
|
|
164
175
|
};
|
|
165
176
|
}
|
|
166
177
|
|
|
178
|
+
// A failed-turn retry: drop the failed turn's rows when it produced no
|
|
179
|
+
// output, so the resubmitted prompt renders once (the session rewinds the
|
|
180
|
+
// matching message under its own lock).
|
|
181
|
+
function rewindFailedTurnRows(batch, { getState, set, replaceItems }) {
|
|
182
|
+
const entry = batch[0];
|
|
183
|
+
if (entry?.retryFailedTurn !== true || entry.retryTurnId) return;
|
|
184
|
+
const rewound = rewoundFailedTurnItems(getState().items, entry.text);
|
|
185
|
+
if (!rewound) return;
|
|
186
|
+
set({
|
|
187
|
+
items: replaceItems(rewound, { preserveStreamingTail: true, preserveSpill: true, preserveTranscriptView: true }),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
167
191
|
export function createDrainLoop(
|
|
168
192
|
bag,
|
|
169
193
|
{ queue, steering, submissions, flushDeferredClearedSessionUi, autoClearBeforeWake }
|
|
@@ -181,35 +205,7 @@ export function createDrainLoop(
|
|
|
181
205
|
flushDeferredExecutionPendingResumeKick,
|
|
182
206
|
} = bag;
|
|
183
207
|
|
|
184
|
-
|
|
185
|
-
// output, so the resubmitted prompt renders once (the session rewinds the
|
|
186
|
-
// matching message under its own lock).
|
|
187
|
-
function rewindFailedTurnRows(batch) {
|
|
188
|
-
const entry = batch[0];
|
|
189
|
-
if (entry?.retryFailedTurn !== true || entry.retryTurnId) return;
|
|
190
|
-
const rewound = rewoundFailedTurnItems(getState().items, entry.text);
|
|
191
|
-
if (!rewound) return;
|
|
192
|
-
set({
|
|
193
|
-
items: replaceItems(rewound, { preserveStreamingTail: true, preserveSpill: true, preserveTranscriptView: true }),
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function scheduleBlockedDrainRetry(delayMs = BLOCKED_DRAIN_RETRY_MS) {
|
|
198
|
-
if (pending.length === 0 || flags.disposed) return;
|
|
199
|
-
if (flags.blockedDrainRetryTimer) return;
|
|
200
|
-
const timer = setTimeout(() => {
|
|
201
|
-
flags.blockedDrainRetryTimer = null;
|
|
202
|
-
if (pending.length > 0) void drain();
|
|
203
|
-
}, delayMs);
|
|
204
|
-
if (typeof timer.unref === 'function') timer.unref();
|
|
205
|
-
flags.blockedDrainRetryTimer = timer;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function clearBlockedDrainRetry() {
|
|
209
|
-
if (!flags.blockedDrainRetryTimer) return;
|
|
210
|
-
clearTimeout(flags.blockedDrainRetryTimer);
|
|
211
|
-
flags.blockedDrainRetryTimer = null;
|
|
212
|
-
}
|
|
208
|
+
const { scheduleBlockedDrainRetry, clearBlockedDrainRetry } = createBlockedDrainRetry({ flags, pending, drain });
|
|
213
209
|
|
|
214
210
|
function hasModelDrainablePending() {
|
|
215
211
|
return pending.some((entry) => !isSlashQueuedEntry(entry));
|
|
@@ -217,45 +213,34 @@ export function createDrainLoop(
|
|
|
217
213
|
|
|
218
214
|
const admission = () => flags.admission || 'open';
|
|
219
215
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (entries.length === 0) return;
|
|
229
|
-
queue.removeQueuedEntries(entries);
|
|
230
|
-
const mirrored = entries.filter((entry) => steering.shouldMirrorSteeringEntry(entry) && entry.steeringPersistId);
|
|
231
|
-
if (mirrored.length > 0) {
|
|
232
|
-
void Promise.resolve(dropTuiSteeringPersist(steering.leadSessionId(), mirrored)).catch(() => {});
|
|
233
|
-
}
|
|
234
|
-
for (const entry of entries) {
|
|
235
|
-
if (entry.id) submissions.forget(entry.id);
|
|
236
|
-
try {
|
|
237
|
-
entry.onSettled?.({ status: 'rejected', reason: 'session-closed', result: null, session: null });
|
|
238
|
-
} catch {}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
216
|
+
const settleClosedQueue = createClosedQueueSettler({
|
|
217
|
+
pending,
|
|
218
|
+
pendingNotificationKeys,
|
|
219
|
+
clearBlockedDrainRetry,
|
|
220
|
+
queue,
|
|
221
|
+
steering,
|
|
222
|
+
submissions,
|
|
223
|
+
});
|
|
241
224
|
|
|
242
|
-
|
|
225
|
+
// Whether a drain may start now. A closed gate either settles (terminal),
|
|
226
|
+
// waits for its own release edge, or schedules a re-check of itself.
|
|
227
|
+
function mayStartDrain() {
|
|
243
228
|
// Execution admission fence: a closing session starts nothing (the close
|
|
244
229
|
// path re-kicks the queue if the durable close fails); a closed one
|
|
245
230
|
// settles what was queued.
|
|
246
|
-
if (admission() === 'closing') return;
|
|
231
|
+
if (admission() === 'closing') return false;
|
|
247
232
|
if (admission() === 'terminal') {
|
|
248
233
|
settleClosedQueue();
|
|
249
|
-
return;
|
|
234
|
+
return false;
|
|
250
235
|
}
|
|
251
|
-
if (flags.draining) return;
|
|
236
|
+
if (flags.draining) return false;
|
|
252
237
|
// Bail while any session command holds commandBusy (auto-clear implies it,
|
|
253
238
|
// but so do setModel/newSession/resume/etc). Running a turn concurrently
|
|
254
239
|
// with a command that swaps or reroutes the live session is a race; the
|
|
255
240
|
// commandBusy-release hook re-kicks drain once the command finishes.
|
|
256
241
|
if (flags.autoClearRunning || flags.quotaResumePending || getState().commandBusy) {
|
|
257
242
|
scheduleBlockedDrainRetry();
|
|
258
|
-
return;
|
|
243
|
+
return false;
|
|
259
244
|
}
|
|
260
245
|
// A queued prompt/notification can arrive while a
|
|
261
246
|
// provider turn is already in flight (scheduled message, webhook, agent
|
|
@@ -273,8 +258,44 @@ export function createDrainLoop(
|
|
|
273
258
|
// a lead idled 24 minutes on ten queued agent completions and only moved
|
|
274
259
|
// when the user typed. Queued work re-reads this gate on its own.
|
|
275
260
|
scheduleBlockedDrainRetry(ACTIVE_TURN_RECHECK_MS);
|
|
276
|
-
return;
|
|
261
|
+
return false;
|
|
277
262
|
}
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Slash commands must run through the TUI command dispatcher, not be
|
|
267
|
+
// delivered to the model as plain text, so slash entries are handled
|
|
268
|
+
// outside the queued-command drain.
|
|
269
|
+
function dequeueModelBatch(firstBatch) {
|
|
270
|
+
return queue.dequeueQueueBatch('later', {
|
|
271
|
+
limit: firstBatch ? 1 : Infinity,
|
|
272
|
+
predicate: (entry) => !isSlashQueuedEntry(entry),
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// The batch's rows go on the transcript before its turn starts; returns the
|
|
277
|
+
// merged prompt the turn sends.
|
|
278
|
+
function presentBatch(batch) {
|
|
279
|
+
tuiDebug(`busy-queue drain batch=${batch.length} remaining=${pending.length}`);
|
|
280
|
+
const merged = mergePromptContents(batch);
|
|
281
|
+
rewindFailedTurnRows(batch, { getState, set, replaceItems });
|
|
282
|
+
renderBatchItems(batch, pushUserOrSyntheticItem);
|
|
283
|
+
return merged;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// The drain that still owns the loop hands off: settle a closed queue, run
|
|
287
|
+
// again for model work that arrived meanwhile, or release the deferred
|
|
288
|
+
// pending-resume kick.
|
|
289
|
+
function finishDrain() {
|
|
290
|
+
flags.draining = false;
|
|
291
|
+
flushDeferredClearedSessionUi();
|
|
292
|
+
if (admission() === 'terminal') settleClosedQueue();
|
|
293
|
+
else if (hasModelDrainablePending()) void drain();
|
|
294
|
+
else flushDeferredExecutionPendingResumeKick();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function drain() {
|
|
298
|
+
if (!mayStartDrain()) return;
|
|
278
299
|
clearBlockedDrainRetry();
|
|
279
300
|
const drainEpoch = (Number(flags.drainEpoch) || 0) + 1;
|
|
280
301
|
flags.drainEpoch = drainEpoch;
|
|
@@ -293,13 +314,7 @@ export function createDrainLoop(
|
|
|
293
314
|
// Drain one priority/mode bucket at a time (unified command queue):
|
|
294
315
|
// prompt steering stays editable and task notifications stay
|
|
295
316
|
// non-editable but model-visible.
|
|
296
|
-
const batch =
|
|
297
|
-
limit: firstBatch ? 1 : Infinity,
|
|
298
|
-
// Slash commands must run through the TUI command dispatcher, not be
|
|
299
|
-
// delivered to the model as plain text, so slash entries are handled
|
|
300
|
-
// outside the queued-command drain.
|
|
301
|
-
predicate: (entry) => !isSlashQueuedEntry(entry),
|
|
302
|
-
});
|
|
317
|
+
const batch = dequeueModelBatch(firstBatch);
|
|
303
318
|
firstBatch = false;
|
|
304
319
|
if (batch.length === 0) break;
|
|
305
320
|
if (isGoalQueuedEntry(batch[0]) && bag.shouldRunGoalContinuation?.(batch[0]) !== true) {
|
|
@@ -314,10 +329,17 @@ export function createDrainLoop(
|
|
|
314
329
|
return;
|
|
315
330
|
}
|
|
316
331
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
332
|
+
// Rows owed to this thread before its next prompt (a bot's progress
|
|
333
|
+
// updates from the sessions it handed work to) land first.
|
|
334
|
+
const prelude = sessionTurnPrelude(bag.runtime?.id);
|
|
335
|
+
if (prelude) {
|
|
336
|
+
await prelude.catch((error) => tuiDebug(`turn prelude failed: ${error?.message || error}`));
|
|
337
|
+
if (flags.drainEpoch !== drainEpoch || admission() !== 'open' || getState().commandBusy) {
|
|
338
|
+
queue.requeueEntriesFront(batch);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const merged = presentBatch(batch);
|
|
321
343
|
const turnStatus = await bag.runTurn(merged, turnOptionsForBatch(batch, steering));
|
|
322
344
|
if (flags.drainEpoch !== drainEpoch) return;
|
|
323
345
|
// A deferred cleared-session UI sync (from a late-settling abandoned
|
|
@@ -330,78 +352,25 @@ export function createDrainLoop(
|
|
|
330
352
|
if (turnStatus === 'cancelled' && pending.length === 0) break;
|
|
331
353
|
}
|
|
332
354
|
} finally {
|
|
333
|
-
if (flags.drainEpoch === drainEpoch)
|
|
334
|
-
flags.draining = false;
|
|
335
|
-
flushDeferredClearedSessionUi();
|
|
336
|
-
if (admission() === 'terminal') settleClosedQueue();
|
|
337
|
-
else if (hasModelDrainablePending()) void drain();
|
|
338
|
-
else flushDeferredExecutionPendingResumeKick();
|
|
339
|
-
}
|
|
355
|
+
if (flags.drainEpoch === drainEpoch) finishDrain();
|
|
340
356
|
}
|
|
341
357
|
}
|
|
342
358
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
// This is an idempotent success, not a rejected submit. Returning false
|
|
359
|
-
// makes Desktop restore and retry a message that is already queued.
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
362
|
-
const entry = queue.makeQueueEntry(text, options);
|
|
363
|
-
if (entry.mode === 'task-notification' && entry.key) {
|
|
364
|
-
if (pendingNotificationKeys.has(entry.key)) return false;
|
|
365
|
-
pendingNotificationKeys.add(entry.key);
|
|
366
|
-
}
|
|
367
|
-
submissions.remember(submissionId);
|
|
368
|
-
pending.push(entry);
|
|
369
|
-
// Typed input or a deliverable notification ends a passive task wait or
|
|
370
|
-
// shell hold so the next boundary hands it to the model now.
|
|
371
|
-
const wake = steering.taskWaitWakeForEntry(entry);
|
|
372
|
-
if (wake) {
|
|
373
|
-
try {
|
|
374
|
-
bag.runtime?.interruptTaskWait?.(wake, wake === 'notification' ? entry.execution?.id || null : null);
|
|
375
|
-
} catch {}
|
|
376
|
-
}
|
|
377
|
-
const needsDurableSteering =
|
|
378
|
-
(getState().busy || getState().commandBusy || flags.autoClearRunning || flags.quotaResumePending) &&
|
|
379
|
-
steering.shouldMirrorSteeringEntry(entry);
|
|
380
|
-
const persistence = needsDurableSteering ? appendTuiSteeringPersist(steering.leadSessionId(), entry) : null;
|
|
381
|
-
if (isQueuedEntryVisible(entry)) {
|
|
382
|
-
set({ queued: [...getState().queued, entry] });
|
|
383
|
-
if (isQueuedEntryEditable(entry)) flushEmitImmediate?.();
|
|
384
|
-
}
|
|
385
|
-
const queueAgeMs = Math.max(0, Date.now() - Number(entry.submittedAt || Date.now()));
|
|
386
|
-
tuiDebug(`prompt-queued id=${String(entry.id)} busy=${getState().busy ? 1 : 0} ageMs=${queueAgeMs}`);
|
|
387
|
-
if (getState().busy) tuiDebug(`busy-queue enqueue mode=${entry.mode} pending=${pending.length}`);
|
|
388
|
-
void drain();
|
|
389
|
-
if (options.awaitPersistence === true && persistence) {
|
|
390
|
-
return Promise.resolve(persistence).then((persisted) => {
|
|
391
|
-
if (persisted !== false) return true;
|
|
392
|
-
const index = pending.indexOf(entry);
|
|
393
|
-
// A turn already took the prompt while its durable mirror was being
|
|
394
|
-
// written: it was delivered, so rejecting it would hand the sender a
|
|
395
|
-
// copy of a message the model already has.
|
|
396
|
-
if (index < 0) return true;
|
|
397
|
-
pending.splice(index, 1);
|
|
398
|
-
queue.removeQueuedEntries([entry]);
|
|
399
|
-
if (submissionId) submissions.forget(submissionId);
|
|
400
|
-
return false;
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
return true;
|
|
404
|
-
}
|
|
359
|
+
const enqueue = createEnqueue({
|
|
360
|
+
admission,
|
|
361
|
+
tuiDebug,
|
|
362
|
+
flags,
|
|
363
|
+
pending,
|
|
364
|
+
pendingNotificationKeys,
|
|
365
|
+
getState,
|
|
366
|
+
set,
|
|
367
|
+
flushEmitImmediate,
|
|
368
|
+
getRuntime: () => bag.runtime,
|
|
369
|
+
queue,
|
|
370
|
+
steering,
|
|
371
|
+
submissions,
|
|
372
|
+
drain,
|
|
373
|
+
});
|
|
405
374
|
|
|
406
375
|
return { drain, enqueue, settleClosedQueue };
|
|
407
376
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
promptDisplayText,
|
|
10
10
|
promptContentImageMeta,
|
|
11
11
|
} from '../../queue-helpers.mjs';
|
|
12
|
+
import { promptOriginOf } from '../../../../runtime/shared/prompt-origin.mjs';
|
|
12
13
|
|
|
13
14
|
export function makeQueueEntry(text, options = {}, nextId) {
|
|
14
15
|
const mode = options.mode || 'prompt';
|
|
@@ -33,6 +34,10 @@ export function makeQueueEntry(text, options = {}, nextId) {
|
|
|
33
34
|
...(options.execution && typeof options.execution === 'object' ? { execution: { ...options.execution } } : {}),
|
|
34
35
|
priority,
|
|
35
36
|
key: options.key || null,
|
|
37
|
+
// Provenance apart from the mode (shared/prompt-origin.mjs): an inbound
|
|
38
|
+
// channel message queued as a task notification is real external
|
|
39
|
+
// activity; an agent report queued as a prompt is the runtime's.
|
|
40
|
+
origin: promptOriginOf(options.origin),
|
|
36
41
|
skipSlashCommands: options.skipSlashCommands === true,
|
|
37
42
|
displayText: mode === 'task-notification' ? notificationDisplayText(displayText) : String(displayText || ''),
|
|
38
43
|
suppressDisplay: options.suppressDisplay === true,
|
|
@@ -19,6 +19,9 @@ const attributionOf = (entry) =>
|
|
|
19
19
|
entry?.transcriptMeta?.hop || 0
|
|
20
20
|
}`;
|
|
21
21
|
|
|
22
|
+
/** A retry of a failed turn runs alone: its options belong to its own turn. */
|
|
23
|
+
const isRetryEntry = (entry) => entry.retryFailedTurn === true || Boolean(entry.retryTurnId);
|
|
24
|
+
|
|
22
25
|
export function createTakeEntriesOps({ pending, pendingNotificationKeys, removeQueuedEntries, leadSessionId }) {
|
|
23
26
|
function dequeueQueueBatch(maxPriority = 'later', options = {}) {
|
|
24
27
|
if (pending.length === 0) return [];
|
|
@@ -45,14 +48,14 @@ export function createTakeEntriesOps({ pending, pendingNotificationKeys, removeQ
|
|
|
45
48
|
(entry.mode || 'prompt') === targetMode &&
|
|
46
49
|
queuePriorityValue(entry.priority) === bestPriority
|
|
47
50
|
) {
|
|
48
|
-
if (batch.length > 0 && entry
|
|
51
|
+
if (batch.length > 0 && isRetryEntry(entry)) break;
|
|
49
52
|
// One turn records one sender: a bot's message never merges with the user's.
|
|
50
53
|
if (batch.length > 0 && attributionOf(entry) !== attributionOf(batch[0])) break;
|
|
51
54
|
batch.push(entry);
|
|
52
55
|
pending.splice(i, 1);
|
|
53
56
|
if (entry.mode === 'task-notification' && entry.key) pendingNotificationKeys.delete(entry.key);
|
|
54
57
|
// A routine fire is a turn of its own, never merged with another.
|
|
55
|
-
if (entry
|
|
58
|
+
if (isRetryEntry(entry) || entry.mode === 'routine' || batch.length >= limit) break;
|
|
56
59
|
} else {
|
|
57
60
|
i += 1;
|
|
58
61
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/tui/session/tool-approval-delegation.mjs — where else a tool-approval
|
|
3
|
+
* request shows: the top-level chat above a sub-agent (`delegationSurface`) or
|
|
4
|
+
* the chats handing this session work (`handsOff`, `offerToHandoffs`;
|
|
5
|
+
* runtime/shared/delegated-approval-host.mjs).
|
|
6
|
+
*/
|
|
7
|
+
import { agentIdOfHomeSession } from '../../runtime/agent/orchestrator/session/agent-home.mjs';
|
|
8
|
+
import { roomMemberOfSession } from '../../runtime/agent/orchestrator/session/agent-room.mjs';
|
|
9
|
+
import { delegateToHandoffs } from '../../runtime/shared/delegated-approval-host.mjs';
|
|
10
|
+
|
|
11
|
+
/** Requests a sub-agent (or a session a chat handed work to) shows in the
|
|
12
|
+
* conversation the user watches too. A secret and a takeover go there as
|
|
13
|
+
* well: the value goes to this PC by its request id and the sign-in window
|
|
14
|
+
* is bound to the asking session, wherever the card is answered. A
|
|
15
|
+
* sub-agent's question stays in its own session (its answer belongs to that
|
|
16
|
+
* conversation); a handed-off session's question shows in the chats handing
|
|
17
|
+
* it work too, beside their hand-off line (tool-approval.mjs
|
|
18
|
+
* `handoffQuestions`), never in their queue. */
|
|
19
|
+
const DELEGATED_KINDS = new Set(['', 'approval', 'outbound', 'permission', 'secret', 'takeover']);
|
|
20
|
+
const HANDOFF_KINDS = new Set([...DELEGATED_KINDS, 'question']);
|
|
21
|
+
|
|
22
|
+
export function createApprovalDelegation({ surfaceOf, ownSessionId }) {
|
|
23
|
+
/** `{ sessionId, chain }` of the top-level chat a sub-agent's request also
|
|
24
|
+
* shows in, or null (no delegation, another kind, or this is the top). */
|
|
25
|
+
function delegationSurface(request) {
|
|
26
|
+
if (typeof surfaceOf !== 'function' || !DELEGATED_KINDS.has(request.kind)) return null;
|
|
27
|
+
const own = String(request.sessionId || ownSessionId());
|
|
28
|
+
let surface = null;
|
|
29
|
+
try {
|
|
30
|
+
surface = surfaceOf(own);
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const sessionId = String(surface?.sessionId || '');
|
|
35
|
+
if (!sessionId || sessionId === own || sessionId === ownSessionId()) return null;
|
|
36
|
+
return { sessionId, chain: Array.isArray(surface.chain) ? surface.chain.map(String) : [] };
|
|
37
|
+
}
|
|
38
|
+
/** Whether a request this (top-level) session itself makes also shows in
|
|
39
|
+
* the chats handing it work (delegated-approval-host.mjs), while it waits. */
|
|
40
|
+
function handsOff(request) {
|
|
41
|
+
if (typeof surfaceOf !== 'function' || !HANDOFF_KINDS.has(request.kind)) return false;
|
|
42
|
+
const own = ownSessionId();
|
|
43
|
+
return Boolean(own) && String(request.sessionId || own) === own;
|
|
44
|
+
}
|
|
45
|
+
function offerToHandoffs(id, request, answer) {
|
|
46
|
+
const own = ownSessionId();
|
|
47
|
+
const agentId = agentIdOfHomeSession(own) || roomMemberOfSession(own)?.agentId || '';
|
|
48
|
+
// Each chat handing this session work names it its own way.
|
|
49
|
+
delegateToHandoffs({
|
|
50
|
+
id,
|
|
51
|
+
sessionId: own,
|
|
52
|
+
request,
|
|
53
|
+
answer,
|
|
54
|
+
copyFor: (chat, { title }) => ({
|
|
55
|
+
...request,
|
|
56
|
+
sessionId: request.sessionId || own,
|
|
57
|
+
chain: [],
|
|
58
|
+
surfaceSessionId: chat,
|
|
59
|
+
handoff: { title, ...(agentId ? { agentId } : {}) },
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return { delegationSurface, handsOff, offerToHandoffs };
|
|
64
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/tui/session/tool-approval-request.mjs — the request record a tool-approval
|
|
3
|
+
* queue (tool-approval.mjs) holds and presents: name, args, reason, and its
|
|
4
|
+
* own wait limit.
|
|
5
|
+
*/
|
|
6
|
+
import { humanRequestFields } from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
|
|
7
|
+
|
|
8
|
+
/** The queue's default wait limit from its `timeoutMs` option: a positive
|
|
9
|
+
* number, else null (no limit). */
|
|
10
|
+
export function defaultApprovalTimeoutMs(timeoutMs) {
|
|
11
|
+
return Number.isFinite(Number(timeoutMs)) && Number(timeoutMs) > 0 ? Number(timeoutMs) : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function normalizeToolApprovalRequest(input, id, defaultTimeoutMs) {
|
|
15
|
+
const now = Date.now();
|
|
16
|
+
// An explicit `timeoutMs: null` means no wait limit (no timer, no expiry);
|
|
17
|
+
// any other missing/invalid value falls back to the session default,
|
|
18
|
+
// which is itself no limit unless the runtime configures one.
|
|
19
|
+
let requestTimeoutMs = defaultTimeoutMs;
|
|
20
|
+
if (input?.timeoutMs === null) requestTimeoutMs = null;
|
|
21
|
+
else if (Number.isFinite(Number(input?.timeoutMs)) && Number(input.timeoutMs) > 0) {
|
|
22
|
+
requestTimeoutMs = Number(input.timeoutMs);
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
id,
|
|
26
|
+
name: String(input?.name || input?.tool_name || 'tool'),
|
|
27
|
+
kind: typeof input?.kind === 'string' ? input.kind : '',
|
|
28
|
+
args: input?.args ?? input?.tool_input ?? null,
|
|
29
|
+
cwd: input?.cwd || null,
|
|
30
|
+
sessionId: input?.sessionId || input?.session_id || null,
|
|
31
|
+
toolCallId: input?.toolCallId || input?.tool_use_id || null,
|
|
32
|
+
reason: String(input?.reason || input?.message || 'approval requested by hook').trim(),
|
|
33
|
+
requestedAt: now,
|
|
34
|
+
timeoutMs: requestTimeoutMs,
|
|
35
|
+
expiresAt: requestTimeoutMs === null ? null : now + requestTimeoutMs,
|
|
36
|
+
// outbound / secret / takeover / permission fields (human-gate/requests.mjs).
|
|
37
|
+
...humanRequestFields(input),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/tui/session/tool-approval-settle.mjs — how one tool-approval entry ends:
|
|
3
|
+
* the answer its waiter gets, the forwarded answer of a copy, and the outcome
|
|
4
|
+
* record published on the state (`toolApprovalResult`).
|
|
5
|
+
*/
|
|
6
|
+
import { normalizeHumanResponse } from '../../runtime/agent/orchestrator/session/human-gate/requests.mjs';
|
|
7
|
+
import { withdrawDelegatedApproval } from '../../runtime/shared/delegated-approval-host.mjs';
|
|
8
|
+
|
|
9
|
+
// Settle one entry: drop its timeout, then hand the waiter its answer. The
|
|
10
|
+
// resolve stays guarded — a waiter whose caller already went away must never
|
|
11
|
+
// take the rest of the queue down with it.
|
|
12
|
+
export function settleToolApproval(entry, approved, reason, answers, reply, extra, device = '') {
|
|
13
|
+
if (entry?.timer) clearTimeout(entry.timer);
|
|
14
|
+
const result = { approved, reason };
|
|
15
|
+
if (answers !== undefined) result.answers = answers;
|
|
16
|
+
if (reply !== undefined) result.reply = reply;
|
|
17
|
+
// A human-kind request answers with its canonical response (never a value).
|
|
18
|
+
if (entry?.request?.remember) {
|
|
19
|
+
Object.assign(result, normalizeHumanResponse(entry.request, approved, extra));
|
|
20
|
+
if (device) result.device = device;
|
|
21
|
+
}
|
|
22
|
+
if (extra?.timedOut === true) result.timedOut = true;
|
|
23
|
+
// Settled here: its copy in the top-level chat goes away.
|
|
24
|
+
if (entry?.delegated) {
|
|
25
|
+
withdrawDelegatedApproval(entry.id, { approved, device, ...(extra?.timedOut === true ? { expired: true } : {}) });
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
entry.resolve(result);
|
|
29
|
+
} catch {
|
|
30
|
+
// The requester's resolver must not break settling the queue.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Hand a copy's answer to the queue that owns the request; false when the
|
|
35
|
+
* owner had already settled it. */
|
|
36
|
+
export function forwardToOwner(entry, approved, reason, device, answers, reply, extra) {
|
|
37
|
+
try {
|
|
38
|
+
return entry.mirror.answer(approved, reason, device, answers, reply, extra) === true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** How the shown entry ended, from its own finish: only an answer from a known
|
|
45
|
+
* device is attributed; a real timeout says `expired`; any other unattributed
|
|
46
|
+
* end names nobody (null). */
|
|
47
|
+
export function finishedOutcome(id, approved, device, extra) {
|
|
48
|
+
if (device) return { id, approved: approved === true, device: String(device), at: Date.now() };
|
|
49
|
+
if (extra?.timedOut === true) return { id, approved: false, device: '', expired: true, at: Date.now() };
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** How a copy ended, from the owner's settle: answered from a known device
|
|
54
|
+
* (who), a real timeout (`expired`), or taken away with no answer
|
|
55
|
+
* (`withdrawn`: the owner's turn ended, the hand-off copy was removed). */
|
|
56
|
+
export function copyOutcome(id, result) {
|
|
57
|
+
const device = String(result?.device || '');
|
|
58
|
+
const at = Date.now();
|
|
59
|
+
if (device) return { id, approved: result.approved === true, device, at };
|
|
60
|
+
if (result?.expired === true) return { id, approved: false, device: '', expired: true, at };
|
|
61
|
+
return { id, approved: false, device: '', withdrawn: true, at };
|
|
62
|
+
}
|