mixdog 1.3.5 → 1.4.1
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/NOTICE.md +2 -2
- package/README.md +2 -2
- package/package.json +2 -11
- package/scripts/check-voice-runtime-inputs.mjs +25 -0
- package/scripts/daemon-stub.mjs +1 -1
- package/scripts/gate-local.mjs +16 -5
- package/scripts/latest-guard.mjs +21 -9
- package/scripts/lib/isolated-test-env.mjs +10 -5
- package/scripts/lib/run-node-tests.mjs +37 -37
- package/scripts/lib/scoped-temp.d.mts +16 -0
- package/scripts/lib/scoped-temp.mjs +123 -0
- package/scripts/lib/stale-temp.mjs +44 -0
- package/scripts/native-release-status.mjs +2 -2
- package/scripts/post-release-check.mjs +1 -6
- package/scripts/pptx-kit-index.mjs +17 -4
- package/scripts/recall-history-cases.json +1 -1
- package/scripts/suite-health.mjs +20 -2
- package/scripts/test-data-dir.mjs +10 -7
- package/scripts/test.mjs +7 -11
- package/src/agents/reviewer/AGENT.md +7 -11
- package/src/agents/reviewer/agent.json +1 -1
- package/src/app.mjs +4 -0
- package/src/defaults/shipped-history.json +22 -1
- package/src/defaults/skills/browser-use/SKILL.md +62 -12
- 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 +35 -9
- package/src/defaults/skills/setup/SKILL.md +22 -0
- package/src/defaults/skills/setup/references/actions.md +39 -5
- package/src/defaults/skills/setup/references/surfaces.md +11 -2
- package/src/defaults/skills/visualize/SKILL.md +2 -1
- package/src/lib/rules-builder.cjs +39 -20
- package/src/output-styles/common.md +2 -2
- package/src/output-styles/simple.md +1 -1
- package/src/rules/agent/AGENT.md +11 -10
- package/src/rules/lead/LEAD.md +23 -20
- package/src/rules/shared/05-tool-calls.md +5 -6
- package/src/rules/shared/10-tool-workflow.md +4 -0
- package/src/rules/shared/45-destructive.md +0 -2
- package/src/rules/shared/50-execution.md +2 -0
- package/src/rules/shared/60-verification.md +7 -5
- package/src/rules/shared/70-delivery.md +7 -9
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
- 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/cache-break-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/config.mjs +17 -1
- package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
- package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- 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-request/initial-status.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
- package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
- package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -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 +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
- 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/openai-ws-stream/event-switch.mjs +20 -12
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
- 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 +38 -0
- 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/runtime-core/ask-user-tool-defs.mjs +49 -16
- 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 +7 -6
- package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
- 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/context-utils.mjs +13 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
- 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 +5 -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 +49 -21
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
- 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-execute.mjs +133 -90
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -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 +24 -14
- 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 +130 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
- package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
- package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
- 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-delete.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
- package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
- 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/transcript-restore/restore.mjs +79 -3
- 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/stall-policy.mjs +23 -0
- 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/invocation-plan.mjs +2 -2
- 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/spawn-env.mjs +4 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
- package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
- 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/scheduler.mjs +33 -147
- package/src/runtime/channels/lib/schedules-db.mjs +34 -11
- package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
- package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
- package/src/runtime/channels/lib/whisper-port.mjs +10 -1
- 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 +10 -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 +28 -2
- package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
- package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -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/local-provider/server-process.mjs +2 -1
- package/src/runtime/media/adapters/gemini-video.mjs +4 -1
- package/src/runtime/media/adapters/xai-media.mjs +2 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
- package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
- package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
- 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/authoring/pdf-author-action.mjs +1 -6
- package/src/runtime/office/design/design-art-direction.mjs +29 -27
- package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
- package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
- package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
- package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
- package/src/runtime/office/quality/design-review.mjs +1 -5
- package/src/runtime/office/quality/document-brief.mjs +1 -5
- package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
- package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
- 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-owned-work.mjs +56 -0
- package/src/runtime/shared/bounded-download.mjs +8 -3
- package/src/runtime/shared/config.mjs +2 -13
- package/src/runtime/shared/connector-accounts.mjs +20 -10
- package/src/runtime/shared/connector-catalog.d.mts +29 -0
- package/src/runtime/shared/connector-catalog.mjs +150 -0
- package/src/runtime/shared/connector-triggers.mjs +22 -19
- package/src/runtime/shared/defaults-separation.mjs +2 -17
- package/src/runtime/shared/delegated-approval-host.mjs +103 -11
- package/src/runtime/shared/err-text.mjs +24 -15
- package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
- package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
- 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/handoff-artifacts.d.mts +2 -1
- package/src/runtime/shared/handoff-artifacts.mjs +18 -8
- 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 +126 -0
- package/src/runtime/shared/llm/cost.mjs +5 -1
- package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
- package/src/runtime/shared/loopback-listener.mjs +25 -2
- package/src/runtime/shared/mixy-presence.mjs +22 -0
- package/src/runtime/shared/model-route-chain.d.mts +7 -2
- package/src/runtime/shared/model-route-chain.mjs +22 -4
- package/src/runtime/shared/open-url.mjs +10 -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 +7 -1
- 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 +10 -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 +48 -1
- package/src/runtime/shared/session-scratch.mjs +37 -0
- 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-card-model/agent-surface.mjs +1 -3
- package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
- package/src/runtime/shared/tool-result-summary.mjs +2 -15
- package/src/runtime/shared/tool-surface.mjs +3 -0
- package/src/runtime/shared/tool-work-units.mjs +2 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
- package/src/runtime/shared/worktrees/contract.mjs +4 -1
- package/src/runtime/shared/worktrees/git.mjs +15 -0
- package/src/runtime/shared/worktrees/manager.mjs +87 -38
- package/src/runtime/shared/worktrees/test-support.mjs +14 -13
- package/src/runtime/terminal-bridge/client.mjs +10 -1
- package/src/runtime/visualize-bridge/client.mjs +29 -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 +489 -185
- package/src/session-runtime/agent-task-sessions.mjs +467 -229
- package/src/session-runtime/agent-tool-routing.mjs +8 -41
- package/src/session-runtime/boot/apis.mjs +9 -0
- 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 +11 -2
- package/src/session-runtime/bot-status.mjs +12 -6
- package/src/session-runtime/bot-target.mjs +38 -0
- package/src/session-runtime/channel-config-api.mjs +6 -1
- package/src/session-runtime/config-lifecycle.mjs +6 -0
- package/src/session-runtime/connector-merge-migration.mjs +305 -0
- package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
- package/src/session-runtime/conversation-import/discovery.mjs +271 -25
- package/src/session-runtime/conversation-import/service.mjs +26 -5
- package/src/session-runtime/handoff-attachments.mjs +82 -31
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
- package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
- 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 +55 -3
- package/src/session-runtime/mcp-oauth-login.mjs +5 -3
- package/src/session-runtime/mixy-heartbeat.mjs +153 -33
- package/src/session-runtime/model-route/set-route.mjs +11 -2
- 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 +2 -1
- package/src/session-runtime/prewarm/channel-start.mjs +3 -7
- package/src/session-runtime/provider-catalog-cache.mjs +82 -36
- package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
- package/src/session-runtime/provider-models.mjs +4 -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/resource-recall-api.mjs +6 -1
- package/src/session-runtime/routine-owner.mjs +40 -9
- package/src/session-runtime/runtime-feature-gates.mjs +5 -1
- package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
- package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
- package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
- package/src/session-runtime/services/agent-tool/render.mjs +71 -17
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
- package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
- 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 +7 -13
- package/src/session-runtime/services/channel-session-router.mjs +4 -2
- package/src/session-runtime/services/provider-admin.mjs +2 -1
- package/src/session-runtime/services/seeds.mjs +0 -28
- package/src/session-runtime/session-compaction-sync.mjs +27 -0
- package/src/session-runtime/session-create/session-options.mjs +1 -0
- 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 +13 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
- package/src/session-runtime/setup-tool/executor.mjs +145 -22
- package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
- package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
- package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
- package/src/session-runtime/workspace/transitions.mjs +25 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
- 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 +142 -190
- package/src/standalone/daemon.mjs +26 -3
- package/src/standalone/session-client.mjs +7 -3
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
- 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 +30 -2
- package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
- package/src/standalone/session-tool-output.mjs +15 -7
- package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
- package/src/tui/app/app-view/floating-panel.jsx +7 -4
- package/src/tui/app/model-options.mjs +3 -28
- package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
- package/src/tui/app/model-picker/route-selection.mjs +5 -6
- package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
- package/src/tui/app/provider-setup/provider-items.mjs +8 -2
- package/src/tui/app/provider-setup-picker.mjs +9 -5
- package/src/tui/components/QuestionPanel.jsx +13 -4
- package/src/tui/dist/index.mjs +133 -92
- 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 +7 -1
- package/src/tui/session/queue-helpers.mjs +3 -42
- 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 +17 -1
- 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 +16 -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 +19 -2
- package/src/tui/session/turn.mjs +1 -0
- package/src/ui/statusline-agents.mjs +1 -3
- 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
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// The live client records of the channel transport: pid-liveness reads and
|
|
2
|
+
// pruning, the one removal primitive every path uses, and the UI control
|
|
3
|
+
// pointer lookup. `replays` and `grace` are the registry's replay records and
|
|
4
|
+
// self-shutdown grace timer.
|
|
5
|
+
import { isPidAlive } from '../../../runtime/shared/pid-liveness.mjs';
|
|
6
|
+
|
|
7
|
+
export function createClientRecords({ state, log, publishRemoteState, replays, grace }) {
|
|
8
|
+
const { clients } = state;
|
|
9
|
+
|
|
10
|
+
// pid death is the authoritative signal for client liveness: a client whose
|
|
11
|
+
// SSE stream closed may still reconnect, one whose lead pid is gone cannot.
|
|
12
|
+
function pruneDeadClients() {
|
|
13
|
+
for (const [token, c] of clients) {
|
|
14
|
+
if (!isPidAlive(c.leadPid)) dropClient(token, 'pid dead');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function liveClients() {
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const [token, c] of clients) {
|
|
21
|
+
if (isPidAlive(c.leadPid)) out.push([token, c]);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Every removal path uses this primitive, including replacement's deliberate
|
|
27
|
+
// no-failover retirement, so replay records/timers never target absent tokens.
|
|
28
|
+
function removeClientRecord(token) {
|
|
29
|
+
const c = clients.get(token);
|
|
30
|
+
if (!c) return null;
|
|
31
|
+
clients.delete(token);
|
|
32
|
+
replays.forgetFor(token);
|
|
33
|
+
try {
|
|
34
|
+
c.sse?.end?.();
|
|
35
|
+
} catch {
|
|
36
|
+
// The stream may already be closed by the peer.
|
|
37
|
+
}
|
|
38
|
+
return c;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function dropClient(token, reason) {
|
|
42
|
+
const c = removeClientRecord(token);
|
|
43
|
+
if (!c) return;
|
|
44
|
+
if (state.pointerToken === token) state.pointerToken = null;
|
|
45
|
+
log(`client ${token} (lead=${c.leadPid}) removed: ${reason}`);
|
|
46
|
+
// Client presence is not channel authority. The durable session pin stays
|
|
47
|
+
// active until that same session explicitly turns Remote OFF.
|
|
48
|
+
publishRemoteState();
|
|
49
|
+
grace.maybeArm('client removed');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Resolve the active UI pointer for state delivery only. Session routing
|
|
53
|
+
// never depends on this client.
|
|
54
|
+
function resolveTarget() {
|
|
55
|
+
if (state.pointerToken) {
|
|
56
|
+
const c = clients.get(state.pointerToken);
|
|
57
|
+
if (c && isPidAlive(c.leadPid)) return c;
|
|
58
|
+
if (c) dropClient(state.pointerToken, 'pid dead (notify-time)');
|
|
59
|
+
else state.pointerToken = null;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { pruneDeadClients, liveClients, removeClientRecord, dropClient, resolveTarget };
|
|
65
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Client registration for the channel transport: a fresh record per attach,
|
|
2
|
+
// a passive reconnect's response-loss replay, the replaced token's retirement
|
|
3
|
+
// with its token-scoped state carried over, and the replacement's
|
|
4
|
+
// cancellation. `records` is the registry's client records (client-records).
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { parsePid } from '../../../runtime/shared/pid-liveness.mjs';
|
|
7
|
+
import { replayMatchesCancellation, replayMatchesRegistration } from './registration-replays.mjs';
|
|
8
|
+
|
|
9
|
+
export function createClientRegistration({ state, log, onClientRegistered, replays, grace, records }) {
|
|
10
|
+
const { clients } = state;
|
|
11
|
+
|
|
12
|
+
function cancelReplacementRegistration(request) {
|
|
13
|
+
const replay = replays.lookup(request.registrationId);
|
|
14
|
+
if (!replay) return 'missing';
|
|
15
|
+
if (!replayMatchesCancellation(replay, request)) return 'forbidden';
|
|
16
|
+
records.dropClient(replay.token, 'replacement deregister');
|
|
17
|
+
return 'cancelled';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A passive reconnect retrying under a known registration id gets the fresh
|
|
21
|
+
* token it already created; a different identity under that id is a 409. */
|
|
22
|
+
function replayedToken(replayId, identity) {
|
|
23
|
+
const existing = replays.lookup(replayId);
|
|
24
|
+
if (!existing) return null;
|
|
25
|
+
if (replayMatchesRegistration(existing, identity) && clients.has(existing.token)) {
|
|
26
|
+
replays.arm(replayId, existing);
|
|
27
|
+
log(`client reconnect replay token=${existing.token} lead=${identity.pid}`);
|
|
28
|
+
return existing.token;
|
|
29
|
+
}
|
|
30
|
+
if (clients.has(existing.token)) {
|
|
31
|
+
const err = new Error('registration replay identity mismatch');
|
|
32
|
+
err.statusCode = 409;
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
replays.remove(replayId, existing);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A reconnect names the exact token it replaces. Token-scoped state belongs
|
|
40
|
+
* to the logical client, so the fresh record inherits the buffered frame,
|
|
41
|
+
* remote session and pointer; the old token is retired even when it is not
|
|
42
|
+
* the pointer — retaining it would let it later call or accumulate a
|
|
43
|
+
* buffered frame after the fresh client has gone away. */
|
|
44
|
+
function adoptReplacedClient(fresh, replaced) {
|
|
45
|
+
const replacedWasPointer = state.pointerToken === replaced.token;
|
|
46
|
+
if (replaced.pendingRemoteStateFrame) {
|
|
47
|
+
fresh.pendingRemoteStateFrame = replaced.pendingRemoteStateFrame;
|
|
48
|
+
replaced.pendingRemoteStateFrame = null;
|
|
49
|
+
}
|
|
50
|
+
if (replaced.remoteSessionId) fresh.remoteSessionId = replaced.remoteSessionId;
|
|
51
|
+
if (replaced.restoreSessionId) fresh.restoreSessionId = replaced.restoreSessionId;
|
|
52
|
+
if (replacedWasPointer) state.pointerToken = fresh.token;
|
|
53
|
+
records.removeClientRecord(replaced.token);
|
|
54
|
+
log(`client reconnect replaced token=${replaced.token} -> ${fresh.token} lead=${fresh.leadPid}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function registerClient({
|
|
58
|
+
leadPid,
|
|
59
|
+
cwd,
|
|
60
|
+
passive = false,
|
|
61
|
+
replaceToken = null,
|
|
62
|
+
registrationId = null,
|
|
63
|
+
restoreSessionId = null,
|
|
64
|
+
}) {
|
|
65
|
+
const pid = parsePid(leadPid) ?? 0;
|
|
66
|
+
const restoreId = /^[A-Za-z0-9_-]+$/.test(String(restoreSessionId || '')) ? String(restoreSessionId) : null;
|
|
67
|
+
const replacementToken = replaceToken ? String(replaceToken) : null;
|
|
68
|
+
const replayId = passive && registrationId ? replays.replayIdOf(registrationId) : null;
|
|
69
|
+
const replayed = replayedToken(replayId, { pid, cwd, replacementToken, restoreId });
|
|
70
|
+
if (replayed) return replayed;
|
|
71
|
+
const token = randomUUID();
|
|
72
|
+
const fresh = {
|
|
73
|
+
token,
|
|
74
|
+
leadPid: pid,
|
|
75
|
+
cwd: cwd || null,
|
|
76
|
+
sse: null,
|
|
77
|
+
pendingRemoteStateFrame: null,
|
|
78
|
+
lastSeen: Date.now(),
|
|
79
|
+
registeredAt: Date.now(),
|
|
80
|
+
restoreSessionId: restoreId,
|
|
81
|
+
};
|
|
82
|
+
clients.set(token, fresh);
|
|
83
|
+
if (state.remoteAcquired && restoreId && restoreId === state.pinnedSessionId) fresh.remoteSessionId = restoreId;
|
|
84
|
+
state.everHadClient = true;
|
|
85
|
+
grace.cancel();
|
|
86
|
+
grace.resetBackoff();
|
|
87
|
+
grace.startSweep();
|
|
88
|
+
log(`client registered token=${token} lead=${pid} cwd=${cwd || '-'}`);
|
|
89
|
+
// The unified daemon starts the channels runtime (automation, webhooks)
|
|
90
|
+
// only once a CHANNELS client is actually present — a
|
|
91
|
+
// session runtime-only daemon must not run tunnels nobody asked for.
|
|
92
|
+
if (typeof onClientRegistered === 'function') {
|
|
93
|
+
try {
|
|
94
|
+
onClientRegistered({ token, leadPid: pid, cwd: cwd || null });
|
|
95
|
+
} catch {
|
|
96
|
+
// The registered-client hook must not fail the registration itself.
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Token replacement never crosses leadPid boundaries. Registration is
|
|
100
|
+
// otherwise transport-only, including same-pid fresh attaches.
|
|
101
|
+
const replaced = passive && replacementToken ? clients.get(replacementToken) : null;
|
|
102
|
+
if (replaced && replaced.leadPid === pid) adoptReplacedClient(fresh, replaced);
|
|
103
|
+
if (replayId) {
|
|
104
|
+
replays.remember(replayId, {
|
|
105
|
+
token,
|
|
106
|
+
leadPid: pid,
|
|
107
|
+
cwd: cwd || null,
|
|
108
|
+
replaceToken: replacementToken,
|
|
109
|
+
restoreSessionId: restoreId,
|
|
110
|
+
responseFinished: false,
|
|
111
|
+
timer: null,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return token;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { registerClient, cancelReplacementRegistration };
|
|
118
|
+
}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* transport: the live client records, registration with reconnect
|
|
4
4
|
* replacement, pid-liveness pruning, and the UI control pointer lookup. The
|
|
5
5
|
* supporting state machines live under ./client-registry/:
|
|
6
|
+
* client-records — live records, removal, pruning, pointer lookup
|
|
7
|
+
* client-registration — registration, replay and reconnect replacement
|
|
6
8
|
* registration-replays — response-loss replay records and their TTL
|
|
7
9
|
* client-grace — self-shutdown grace timer + dead-pid sweep
|
|
8
10
|
* sse-attach — binding a client's notify stream
|
|
@@ -11,14 +13,10 @@
|
|
|
11
13
|
* pointerToken, everHadClient, closed, remoteAcquired, pinnedSessionId,
|
|
12
14
|
* stickyRemoteFrame.
|
|
13
15
|
*/
|
|
14
|
-
import { randomUUID } from 'node:crypto';
|
|
15
|
-
import { isPidAlive, parsePid } from '../../runtime/shared/pid-liveness.mjs';
|
|
16
16
|
import { createClientGrace } from './client-registry/client-grace.mjs';
|
|
17
|
-
import {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
replayMatchesRegistration,
|
|
21
|
-
} from './client-registry/registration-replays.mjs';
|
|
17
|
+
import { createClientRecords } from './client-registry/client-records.mjs';
|
|
18
|
+
import { createClientRegistration } from './client-registry/client-registration.mjs';
|
|
19
|
+
import { createRegistrationReplays } from './client-registry/registration-replays.mjs';
|
|
22
20
|
import { attachClientStream } from './client-registry/sse-attach.mjs';
|
|
23
21
|
|
|
24
22
|
export function createClientRegistry({
|
|
@@ -37,162 +35,25 @@ export function createClientRegistry({
|
|
|
37
35
|
ttlMs: registrationReplayTtlMs,
|
|
38
36
|
onExpiredUnflushed: (token) => dropClient(token, 'unflushed registration replay expired'),
|
|
39
37
|
});
|
|
40
|
-
const grace = createClientGrace({
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// no-failover retirement, so replay records/timers never target absent tokens.
|
|
60
|
-
function removeClientRecord(token) {
|
|
61
|
-
const c = clients.get(token);
|
|
62
|
-
if (!c) return null;
|
|
63
|
-
clients.delete(token);
|
|
64
|
-
replays.forgetFor(token);
|
|
65
|
-
try {
|
|
66
|
-
c.sse?.end?.();
|
|
67
|
-
} catch {}
|
|
68
|
-
return c;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function dropClient(token, reason) {
|
|
72
|
-
const c = removeClientRecord(token);
|
|
73
|
-
if (!c) return;
|
|
74
|
-
if (state.pointerToken === token) state.pointerToken = null;
|
|
75
|
-
log(`client ${token} (lead=${c.leadPid}) removed: ${reason}`);
|
|
76
|
-
// Client presence is not channel authority. The durable session pin stays
|
|
77
|
-
// active until that same session explicitly turns Remote OFF.
|
|
78
|
-
publishRemoteState();
|
|
79
|
-
grace.maybeArm('client removed');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function cancelReplacementRegistration(request) {
|
|
83
|
-
const replay = replays.lookup(request.registrationId);
|
|
84
|
-
if (!replay) return 'missing';
|
|
85
|
-
if (!replayMatchesCancellation(replay, request)) return 'forbidden';
|
|
86
|
-
dropClient(replay.token, 'replacement deregister');
|
|
87
|
-
return 'cancelled';
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Resolve the active UI pointer for state delivery only. Session routing
|
|
91
|
-
// never depends on this client.
|
|
92
|
-
function resolveTarget() {
|
|
93
|
-
if (state.pointerToken) {
|
|
94
|
-
const c = clients.get(state.pointerToken);
|
|
95
|
-
if (c && isPidAlive(c.leadPid)) return c;
|
|
96
|
-
if (c) dropClient(state.pointerToken, 'pid dead (notify-time)');
|
|
97
|
-
else state.pointerToken = null;
|
|
98
|
-
}
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/** A passive reconnect retrying under a known registration id gets the fresh
|
|
103
|
-
* token it already created; a different identity under that id is a 409. */
|
|
104
|
-
function replayedToken(replayId, identity) {
|
|
105
|
-
const existing = replays.lookup(replayId);
|
|
106
|
-
if (!existing) return null;
|
|
107
|
-
if (replayMatchesRegistration(existing, identity) && clients.has(existing.token)) {
|
|
108
|
-
replays.arm(replayId, existing);
|
|
109
|
-
log(`client reconnect replay token=${existing.token} lead=${identity.pid}`);
|
|
110
|
-
return existing.token;
|
|
111
|
-
}
|
|
112
|
-
if (clients.has(existing.token)) {
|
|
113
|
-
const err = new Error('registration replay identity mismatch');
|
|
114
|
-
err.statusCode = 409;
|
|
115
|
-
throw err;
|
|
116
|
-
}
|
|
117
|
-
replays.remove(replayId, existing);
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/** A reconnect names the exact token it replaces. Token-scoped state belongs
|
|
122
|
-
* to the logical client, so the fresh record inherits the buffered frame,
|
|
123
|
-
* remote session and pointer; the old token is retired even when it is not
|
|
124
|
-
* the pointer — retaining it would let it later call or accumulate a
|
|
125
|
-
* buffered frame after the fresh client has gone away. */
|
|
126
|
-
function adoptReplacedClient(fresh, replaced) {
|
|
127
|
-
const replacedWasPointer = state.pointerToken === replaced.token;
|
|
128
|
-
if (replaced.pendingRemoteStateFrame) {
|
|
129
|
-
fresh.pendingRemoteStateFrame = replaced.pendingRemoteStateFrame;
|
|
130
|
-
replaced.pendingRemoteStateFrame = null;
|
|
131
|
-
}
|
|
132
|
-
if (replaced.remoteSessionId) fresh.remoteSessionId = replaced.remoteSessionId;
|
|
133
|
-
if (replaced.restoreSessionId) fresh.restoreSessionId = replaced.restoreSessionId;
|
|
134
|
-
if (replacedWasPointer) state.pointerToken = fresh.token;
|
|
135
|
-
removeClientRecord(replaced.token);
|
|
136
|
-
log(`client reconnect replaced token=${replaced.token} -> ${fresh.token} lead=${fresh.leadPid}`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function registerClient({
|
|
140
|
-
leadPid,
|
|
141
|
-
cwd,
|
|
142
|
-
passive = false,
|
|
143
|
-
replaceToken = null,
|
|
144
|
-
registrationId = null,
|
|
145
|
-
restoreSessionId = null,
|
|
146
|
-
}) {
|
|
147
|
-
const pid = parsePid(leadPid) ?? 0;
|
|
148
|
-
const restoreId = /^[A-Za-z0-9_-]+$/.test(String(restoreSessionId || '')) ? String(restoreSessionId) : null;
|
|
149
|
-
const replacementToken = replaceToken ? String(replaceToken) : null;
|
|
150
|
-
const replayId = passive && registrationId ? replays.replayIdOf(registrationId) : null;
|
|
151
|
-
const replayed = replayedToken(replayId, { pid, cwd, replacementToken, restoreId });
|
|
152
|
-
if (replayed) return replayed;
|
|
153
|
-
const token = randomUUID();
|
|
154
|
-
const fresh = {
|
|
155
|
-
token,
|
|
156
|
-
leadPid: pid,
|
|
157
|
-
cwd: cwd || null,
|
|
158
|
-
sse: null,
|
|
159
|
-
pendingRemoteStateFrame: null,
|
|
160
|
-
lastSeen: Date.now(),
|
|
161
|
-
registeredAt: Date.now(),
|
|
162
|
-
restoreSessionId: restoreId,
|
|
163
|
-
};
|
|
164
|
-
clients.set(token, fresh);
|
|
165
|
-
if (state.remoteAcquired && restoreId && restoreId === state.pinnedSessionId) fresh.remoteSessionId = restoreId;
|
|
166
|
-
state.everHadClient = true;
|
|
167
|
-
grace.cancel();
|
|
168
|
-
grace.resetBackoff();
|
|
169
|
-
grace.startSweep();
|
|
170
|
-
log(`client registered token=${token} lead=${pid} cwd=${cwd || '-'}`);
|
|
171
|
-
// The unified daemon starts the channels runtime (automation, webhooks,
|
|
172
|
-
// messaging provider) only once a CHANNELS client is actually present — a
|
|
173
|
-
// session runtime-only daemon must not run tunnels nobody asked for.
|
|
174
|
-
if (typeof onClientRegistered === 'function') {
|
|
175
|
-
try {
|
|
176
|
-
onClientRegistered({ token, leadPid: pid, cwd: cwd || null });
|
|
177
|
-
} catch {}
|
|
178
|
-
}
|
|
179
|
-
// Token replacement never crosses leadPid boundaries. Registration is
|
|
180
|
-
// otherwise transport-only, including same-pid fresh attaches.
|
|
181
|
-
const replaced = passive && replacementToken ? clients.get(replacementToken) : null;
|
|
182
|
-
if (replaced && replaced.leadPid === pid) adoptReplacedClient(fresh, replaced);
|
|
183
|
-
if (replayId) {
|
|
184
|
-
replays.remember(replayId, {
|
|
185
|
-
token,
|
|
186
|
-
leadPid: pid,
|
|
187
|
-
cwd: cwd || null,
|
|
188
|
-
replaceToken: replacementToken,
|
|
189
|
-
restoreSessionId: restoreId,
|
|
190
|
-
responseFinished: false,
|
|
191
|
-
timer: null,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
return token;
|
|
195
|
-
}
|
|
38
|
+
const grace = createClientGrace({
|
|
39
|
+
state,
|
|
40
|
+
clients,
|
|
41
|
+
log,
|
|
42
|
+
clientGraceMs,
|
|
43
|
+
sweepMs,
|
|
44
|
+
onClientsEmpty,
|
|
45
|
+
pruneDeadClients: () => records.pruneDeadClients(),
|
|
46
|
+
});
|
|
47
|
+
const records = createClientRecords({ state, log, publishRemoteState, replays, grace });
|
|
48
|
+
const { liveClients, dropClient, resolveTarget } = records;
|
|
49
|
+
const { registerClient, cancelReplacementRegistration } = createClientRegistration({
|
|
50
|
+
state,
|
|
51
|
+
log,
|
|
52
|
+
onClientRegistered,
|
|
53
|
+
replays,
|
|
54
|
+
grace,
|
|
55
|
+
records,
|
|
56
|
+
});
|
|
196
57
|
|
|
197
58
|
function attachSse(token, res) {
|
|
198
59
|
const c = clients.get(token);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// This module owns ONLY the transport (sockets, client registry, notify
|
|
11
11
|
// routing and lifecycle). The channels runtime is injected via `handleCall`
|
|
12
12
|
// so the same transport is exercised by the real daemon entry AND the smoke
|
|
13
|
-
// harness (stub runtime
|
|
13
|
+
// harness (stub runtime).
|
|
14
14
|
//
|
|
15
15
|
// Composition root: the shared transport state below is read and written by
|
|
16
16
|
// the sub-services in channel-transport/ (remote binding, client registry,
|
|
@@ -26,16 +26,16 @@ export function createChannelsRuntimeLoader({ log, onLoaded = () => {}, setOwner
|
|
|
26
26
|
return channelsReady;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// Channels bring automation with them (schedules, webhook listener + tunnel
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
29
|
+
// Channels bring automation with them (schedules, webhook listener + tunnel).
|
|
30
|
+
// A daemon spawned by a TUI starts them exactly as before; a daemon spawned
|
|
31
|
+
// for session views stays dormant until a channels client actually registers,
|
|
32
|
+
// so an app-only service runs no tunnels. The channel bridge is activated
|
|
33
|
+
// separately, by the activate_channel_bridge call.
|
|
33
34
|
let channelsStartPromise = null;
|
|
34
|
-
function start(
|
|
35
|
+
function start() {
|
|
35
36
|
if (channelsStartPromise) return channelsStartPromise;
|
|
36
|
-
const messaging = options.messaging === true;
|
|
37
37
|
channelsStartPromise = ensure()
|
|
38
|
-
.then((module) => module.start({
|
|
38
|
+
.then((module) => module.start({ bridge: false }))
|
|
39
39
|
.catch((e) => {
|
|
40
40
|
channelsStartPromise = null;
|
|
41
41
|
log(`channels.start failed (non-fatal): ${e?.message || e}`);
|
|
@@ -50,7 +50,9 @@ export function createChannelsRuntimeLoader({ log, onLoaded = () => {}, setOwner
|
|
|
50
50
|
if (ctx && BINDING_TOOLS.has(name)) {
|
|
51
51
|
try {
|
|
52
52
|
setOwnerContext({ leadPid: ctx.leadPid, cwd: ctx.cwd });
|
|
53
|
-
} catch {
|
|
53
|
+
} catch {
|
|
54
|
+
// Binding context is advisory; the tool call still runs without it.
|
|
55
|
+
}
|
|
54
56
|
}
|
|
55
57
|
return module.handleToolCall(name, args || {});
|
|
56
58
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { sessionBotOf } from '../../session-runtime/bot-status.mjs';
|
|
2
|
+
import { isAgentOnlySession } from '../../runtime/agent/orchestrator/session/store-summary-visibility.mjs';
|
|
3
|
+
import { activityOf, encodeCatalogCursor } from '../../runtime/agent/orchestrator/session/catalog-db/schema.mjs';
|
|
4
|
+
|
|
5
|
+
// The keyset position before every catalog row: a page continuing from a
|
|
6
|
+
// pinned live row starts over at the top of the catalog order.
|
|
7
|
+
export const TOP_CURSOR = encodeCatalogCursor({ id: '', lastUsedAt: Number.MAX_SAFE_INTEGER });
|
|
8
|
+
|
|
9
|
+
/** Throws unless the list's opt-in flags are booleans when given. */
|
|
10
|
+
export function assertListFlags(args) {
|
|
11
|
+
for (const flag of ['include_agents', 'include_archived']) {
|
|
12
|
+
if (args[flag] !== undefined && typeof args[flag] !== 'boolean') {
|
|
13
|
+
throw new Error(`session: ${flag} must be a boolean`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Whether the listing keeps a catalog row: sub-agent sessions and archived
|
|
19
|
+
* ones (`archived`, the desktop's flags) only on request. */
|
|
20
|
+
export function listRowFilter(args, archived) {
|
|
21
|
+
return (row) =>
|
|
22
|
+
(args.include_agents === true || !isAgentOnlySession(row)) &&
|
|
23
|
+
(args.include_archived === true || !Object.hasOwn(archived, row.id));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A row with the live work of `live` (storedLiveWork) overlaid. */
|
|
27
|
+
export function liveRowOverlay(live) {
|
|
28
|
+
return (row) => {
|
|
29
|
+
const working = row.working === true || live.own.has(row.id);
|
|
30
|
+
const agentWorking = row.agentWorking === true || live.agents.has(row.id);
|
|
31
|
+
if (!working && !agentWorking) return row;
|
|
32
|
+
return {
|
|
33
|
+
...row,
|
|
34
|
+
...(working ? { working: true, liveAt: live.own.get(row.id) || 0 } : {}),
|
|
35
|
+
...(agentWorking ? { agentWorking: true } : {}),
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const liveActivity = (row) => Math.max(row.liveAt || 0, activityOf(row), Number(row.updatedAt) || 0);
|
|
41
|
+
|
|
42
|
+
/** Every session with live work in `live`, each once. */
|
|
43
|
+
export function liveCandidates(live) {
|
|
44
|
+
return [...new Set([...live.own.keys(), ...live.agents])];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** The live rows a first page leads with: kept like the listing, overlaid,
|
|
48
|
+
* newest sign of work first, at most `limit`. */
|
|
49
|
+
export function pinnedLiveRows(rows, keep, withLive, limit) {
|
|
50
|
+
return rows
|
|
51
|
+
.filter((row) => row && keep(row))
|
|
52
|
+
.map(withLive)
|
|
53
|
+
.sort((left, right) => liveActivity(right) - liveActivity(left) || (left.id < right.id ? -1 : 1))
|
|
54
|
+
.slice(0, limit);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The page's sessions: the pinned live rows, then the scanned ones overlaid,
|
|
58
|
+
* archived ones marked. */
|
|
59
|
+
export function listedSessions(pinned, scanned, withLive, archived) {
|
|
60
|
+
return [...pinned, ...scanned.map(withLive)].map((row) =>
|
|
61
|
+
Object.hasOwn(archived, row.id) ? { ...row, archived: true } : row
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** sessionListOutput options: rich rows for the human, or only the caller
|
|
66
|
+
* bot's own (`own`, scopedBot); a pinned row continues from the top. */
|
|
67
|
+
export function listOutputOptions(own, pinnedIds) {
|
|
68
|
+
return {
|
|
69
|
+
rich: own ? (row) => sessionBotOf(row) === own : own !== null,
|
|
70
|
+
cursorOf: (row) => (pinnedIds.has(row.id) ? TOP_CURSOR : encodeCatalogCursor(row)),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SESSION_ID_PATTERN } from '../../runtime/agent/orchestrator/runtime-core/session-id.mjs';
|
|
2
|
+
|
|
3
|
+
const TARGET_ACTIONS = new Set(['status', 'read', 'send', 'cancel', 'delete']);
|
|
4
|
+
const MUTATING_ACTIONS = new Set(['send', 'cancel', 'delete']);
|
|
5
|
+
|
|
6
|
+
/** The calling session of a `session` request; throws on an action the tool
|
|
7
|
+
* does not run or a request without a valid caller session. */
|
|
8
|
+
export function requestCaller(action, context) {
|
|
9
|
+
if (
|
|
10
|
+
action !== 'list' &&
|
|
11
|
+
action !== 'create' &&
|
|
12
|
+
action !== 'bots' &&
|
|
13
|
+
action !== 'worktree' &&
|
|
14
|
+
action !== 'route' &&
|
|
15
|
+
!TARGET_ACTIONS.has(action)
|
|
16
|
+
) {
|
|
17
|
+
throw new Error(`session: unsupported remote action "${action}"`);
|
|
18
|
+
}
|
|
19
|
+
const caller = context.callerSessionId;
|
|
20
|
+
if (typeof caller !== 'string' || !SESSION_ID_PATTERN.test(caller)) {
|
|
21
|
+
throw new Error('session: caller session is required');
|
|
22
|
+
}
|
|
23
|
+
return caller;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The addressed session and transcript paging of a `list` or target action:
|
|
27
|
+
* `{ sessionId, limit, offset, textOffset }`; throws on a missing target, a
|
|
28
|
+
* self mutation or paging out of range. */
|
|
29
|
+
export function targetRequest(args, action, caller) {
|
|
30
|
+
const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
|
|
31
|
+
if (TARGET_ACTIONS.has(action) && !SESSION_ID_PATTERN.test(sessionId)) {
|
|
32
|
+
throw new Error(`session: session_id is required for action=${action}`);
|
|
33
|
+
}
|
|
34
|
+
if (MUTATING_ACTIONS.has(action) && sessionId === caller) {
|
|
35
|
+
throw new Error(`session: ${action} must target another session`);
|
|
36
|
+
}
|
|
37
|
+
const limit = args.limit ?? 25;
|
|
38
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
39
|
+
throw new Error('session: limit must be an integer from 1 to 100');
|
|
40
|
+
}
|
|
41
|
+
const offset = args.offset ?? 0;
|
|
42
|
+
const textOffset = args.text_offset ?? 0;
|
|
43
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || !Number.isSafeInteger(offset + limit)) {
|
|
44
|
+
throw new Error('session: offset must be a nonnegative safe integer');
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isSafeInteger(textOffset) || textOffset < 0) throw new Error('session: invalid text_offset');
|
|
47
|
+
if (args.text_offset !== undefined && (args.offset === undefined || limit !== 1 || !args.item_id)) {
|
|
48
|
+
throw new Error('session: text_offset requires offset, limit:1 and item_id from read');
|
|
49
|
+
}
|
|
50
|
+
return { sessionId, limit, offset, textOffset };
|
|
51
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { rememberSubmissionBotChain } from '../../runtime/shared/bot-hops.mjs';
|
|
2
|
+
import { agentRoomHost } from '../../runtime/shared/agent-room-host.mjs';
|
|
3
|
+
|
|
4
|
+
/** The member a send to room log `sessionId` addresses, through the desktop
|
|
5
|
+
* room host: `{ host, agentId, sessionId, text }`; throws when no desktop is
|
|
6
|
+
* attached or no member resolves. */
|
|
7
|
+
export async function roomAddressee(sessionId, args) {
|
|
8
|
+
let host;
|
|
9
|
+
try {
|
|
10
|
+
host = agentRoomHost();
|
|
11
|
+
} catch (error) {
|
|
12
|
+
throw new Error(`session: cannot send to ${sessionId}: ${error.message}`);
|
|
13
|
+
}
|
|
14
|
+
const member = await host.member(sessionId, { to: args.to, prompt: args.prompt }).catch((error) => {
|
|
15
|
+
throw new Error(`session: ${error?.message || error}`);
|
|
16
|
+
});
|
|
17
|
+
return { host, ...member };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The submit of a send to `room` (roomAddressee) at room log `sessionId`:
|
|
21
|
+
* the member's turn carries the send's hop (`transcriptMeta.hop`), so a
|
|
22
|
+
* chain through the room counts on toward MAX_BOT_HOPS; its bot chain is
|
|
23
|
+
* recorded under every submission id that turn may take. */
|
|
24
|
+
export function roomSubmitter(room, sessionId, prompt, from) {
|
|
25
|
+
return async ({ id, onSettled, follow, transcriptMeta } = {}) => {
|
|
26
|
+
rememberSubmissionBotChain(id, transcriptMeta?.botChain);
|
|
27
|
+
const followed = follow
|
|
28
|
+
? (submissionId) => {
|
|
29
|
+
rememberSubmissionBotChain(submissionId, transcriptMeta?.botChain);
|
|
30
|
+
return follow(submissionId);
|
|
31
|
+
}
|
|
32
|
+
: null;
|
|
33
|
+
return {
|
|
34
|
+
accepted:
|
|
35
|
+
(await room.host.ask(sessionId, {
|
|
36
|
+
agentId: room.agentId,
|
|
37
|
+
prompt,
|
|
38
|
+
from,
|
|
39
|
+
...(id ? { id } : {}),
|
|
40
|
+
...(transcriptMeta?.hop > 0 ? { hop: transcriptMeta.hop } : {}),
|
|
41
|
+
...(onSettled ? { onSettled } : {}),
|
|
42
|
+
...(followed ? { follow: followed } : {}),
|
|
43
|
+
})) === true,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Stops the exchange of room log `sessionId` through the desktop room host. */
|
|
49
|
+
export function stopRoomExchange(sessionId) {
|
|
50
|
+
let host;
|
|
51
|
+
try {
|
|
52
|
+
host = agentRoomHost();
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw new Error(`session: cannot cancel ${sessionId}: ${error.message}`);
|
|
55
|
+
}
|
|
56
|
+
return { sessionId, aborted: host.stop(sessionId) === true };
|
|
57
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SESSION_ID_PATTERN } from '../../runtime/agent/orchestrator/runtime-core/session-id.mjs';
|
|
2
|
+
|
|
3
|
+
// The route methods the setup tool reaches on another session (all on the
|
|
4
|
+
// session protocol's configure allowlist), through the same configure lane
|
|
5
|
+
// the desktop's model picker and quota card use.
|
|
6
|
+
const ROUTE_METHODS = new Set(['setRoute', 'continueSessionInModel', 'scheduleQuotaResume', 'cancelQuotaResume']);
|
|
7
|
+
const ROUTE_FIELDS = [
|
|
8
|
+
'provider',
|
|
9
|
+
'model',
|
|
10
|
+
'effort',
|
|
11
|
+
'fast',
|
|
12
|
+
'autoEffort',
|
|
13
|
+
'modelParameters',
|
|
14
|
+
'contextPercent',
|
|
15
|
+
'contextTokens',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// Only route fields cross: no option of the picker's write (keepMainModel,
|
|
19
|
+
// applyToCurrentSession) can ride along and change the Lead route.
|
|
20
|
+
function routeFields(input) {
|
|
21
|
+
const route = {};
|
|
22
|
+
for (const key of ROUTE_FIELDS) if (input?.[key] !== undefined && input[key] !== null) route[key] = input[key];
|
|
23
|
+
if (!route.provider || !route.model) throw new Error('session: route provider and model are required');
|
|
24
|
+
return route;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* One route method on another session, for the setup tool
|
|
29
|
+
* (setup-tool/session-route-actions.mjs): `setRoute` is the session's own
|
|
30
|
+
* model pick (session-only, from its next turn: a running turn finishes on
|
|
31
|
+
* its model), the others its quota card's buttons. The caller's own session
|
|
32
|
+
* is changed by its runtime directly, never through here.
|
|
33
|
+
*/
|
|
34
|
+
export function createRouteControl(getSessionService) {
|
|
35
|
+
return async function routeControl(args, caller) {
|
|
36
|
+
const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
|
|
37
|
+
if (!SESSION_ID_PATTERN.test(sessionId)) throw new Error('session: session_id is required for action=route');
|
|
38
|
+
if (sessionId === caller) throw new Error('session: route control must target another session');
|
|
39
|
+
if (!ROUTE_METHODS.has(args.method)) throw new Error(`session: unsupported route method "${args.method}"`);
|
|
40
|
+
const service = getSessionService();
|
|
41
|
+
if (!service) throw new Error('session: session service is unavailable');
|
|
42
|
+
let input = [sessionId];
|
|
43
|
+
if (args.method === 'setRoute') input = [{ ...routeFields(args.input), sessionOnly: true }];
|
|
44
|
+
else if (args.method === 'continueSessionInModel') input = [sessionId, routeFields(args.input)];
|
|
45
|
+
const result = await service.configureSession({ sessionId, action: args.method, args: input }, { revision: 1 });
|
|
46
|
+
return result.value;
|
|
47
|
+
};
|
|
48
|
+
}
|