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
|
@@ -2,19 +2,32 @@
|
|
|
2
2
|
* Keeps the PG session-search copy (session-search-db.mjs) in step with the
|
|
3
3
|
* session store: per-turn ingest, a once-per-run backfill of stored sessions,
|
|
4
4
|
* and deletion. Everything is best-effort and never touches the memory runtime.
|
|
5
|
+
*
|
|
6
|
+
* A session's writes run one at a time, in the order they were issued
|
|
7
|
+
* (serialized). That order is what keeps a value marked sensitive out of the
|
|
8
|
+
* index: the replacement that redacts the stored rows
|
|
9
|
+
* (replaceSessionSearchMessages) locks only rows that already exist, so an
|
|
10
|
+
* earlier plaintext append still in flight would otherwise land after it and
|
|
11
|
+
* bring the value back. A write also redacts with the values known when it
|
|
12
|
+
* runs, not when it was issued.
|
|
5
13
|
*/
|
|
6
14
|
import { readdirSync, statSync } from 'node:fs';
|
|
7
15
|
import { join } from 'node:path';
|
|
8
16
|
import { isAgentOnlySession } from '../agent/orchestrator/session/store-summary-visibility.mjs';
|
|
17
|
+
import { redactSensitiveText, sensitiveValuesVersion } from '../agent/orchestrator/session/sensitive-values.mjs';
|
|
18
|
+
import { redactSensitiveMessages } from '../agent/orchestrator/session/sensitive-projection.mjs';
|
|
9
19
|
import {
|
|
10
20
|
appendSessionSearchMessages,
|
|
11
21
|
deleteSessionSearchMessages,
|
|
12
22
|
readSessionSearchBackfillStamps,
|
|
13
23
|
readSessionSearchSessionIds,
|
|
14
24
|
recordSessionSearchBackfillStamp,
|
|
25
|
+
replaceSessionSearchMessages,
|
|
15
26
|
} from './session-search-db.mjs';
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
/** sessionId → { length, version }: the transcript length and the
|
|
29
|
+
* sensitive-values version (sensitive-values.mjs) of the last stored ingest. */
|
|
30
|
+
const lastIngested = new Map();
|
|
18
31
|
const LAST_INGESTED_LIMIT = 500;
|
|
19
32
|
|
|
20
33
|
function logFailure(what, error) {
|
|
@@ -23,6 +36,87 @@ function logFailure(what, error) {
|
|
|
23
36
|
} catch {}
|
|
24
37
|
}
|
|
25
38
|
|
|
39
|
+
/** sessionId → { tail, controller }: the tail of the session's write queue
|
|
40
|
+
* (see serialized) and the lifetime its writes run under (closeSessionSearch
|
|
41
|
+
* aborts it). Present only while a write of the session is queued or running. */
|
|
42
|
+
const writeQueues = new Map();
|
|
43
|
+
|
|
44
|
+
/** The reason a closed session's writes stop with; never reported as a failure. */
|
|
45
|
+
class SessionSearchClosedError extends Error {
|
|
46
|
+
constructor(sessionId) {
|
|
47
|
+
super(`session search writes of ${sessionId} closed`);
|
|
48
|
+
this.name = 'AbortError';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const reportUnlessClosed = (what) => (error) => {
|
|
53
|
+
if (!(error instanceof SessionSearchClosedError)) logFailure(what, error);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Runs `task(signal)` once every earlier task of `sessionId` has settled; the
|
|
57
|
+
* task's own promise is returned, so its failure reaches the caller to report.
|
|
58
|
+
* The queue moves on past a failed task: that failure is the caller's, not the
|
|
59
|
+
* next write's. `signal` is the session's write lifetime. */
|
|
60
|
+
function serialized(sessionId, task) {
|
|
61
|
+
const queue = writeQueues.get(sessionId);
|
|
62
|
+
const controller = queue && !queue.controller.signal.aborted ? queue.controller : new AbortController();
|
|
63
|
+
// A write cancelled while queued never starts (rejects with the close reason).
|
|
64
|
+
const run = (queue?.tail ?? Promise.resolve()).then(() => {
|
|
65
|
+
controller.signal.throwIfAborted();
|
|
66
|
+
return task(controller.signal);
|
|
67
|
+
});
|
|
68
|
+
const tail = run.then(
|
|
69
|
+
() => {},
|
|
70
|
+
() => {}
|
|
71
|
+
);
|
|
72
|
+
const entry = { tail, controller };
|
|
73
|
+
writeQueues.set(sessionId, entry);
|
|
74
|
+
tail.then(() => {
|
|
75
|
+
if (writeQueues.get(sessionId) === entry) writeQueues.delete(sessionId);
|
|
76
|
+
});
|
|
77
|
+
return run;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Stops the session's search writes for a runtime close: the queued ones never
|
|
82
|
+
* start and the running one is cancelled — a PostgreSQL runtime install or
|
|
83
|
+
* start it is waiting on included, once no other writer still waits on it
|
|
84
|
+
* (createSharedAbortable). Resolves when the queue has settled, so nothing this
|
|
85
|
+
* session issued writes into the data dir afterwards. The rows are derived:
|
|
86
|
+
* the stored-session backfill restores what a cancelled write left out.
|
|
87
|
+
*/
|
|
88
|
+
export async function closeSessionSearch(sessionId) {
|
|
89
|
+
const queue = writeQueues.get(sessionId);
|
|
90
|
+
if (!queue) return;
|
|
91
|
+
queue.controller.abort(new SessionSearchClosedError(sessionId));
|
|
92
|
+
await queue.tail;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Stores `messages`, the transcript as it was when the turn was ingested, as
|
|
96
|
+
* the session's search rows; runs in the session's write queue. */
|
|
97
|
+
async function storeTranscript(sessionId, messages, signal) {
|
|
98
|
+
// A turn always grows the transcript; an unchanged length means nothing
|
|
99
|
+
// new. This is only a shortcut: the insert itself is idempotent.
|
|
100
|
+
const length = messages.length;
|
|
101
|
+
const version = sensitiveValuesVersion(sessionId);
|
|
102
|
+
const last = lastIngested.get(sessionId);
|
|
103
|
+
if (last?.length === length && last.version === version) return;
|
|
104
|
+
// A value a sensitive fill marked is stored as a marker here too. While
|
|
105
|
+
// the session has values not yet applied to its stored rows (rows made
|
|
106
|
+
// before the mark, compacted ones included), the rows that hold one are
|
|
107
|
+
// replaced in one transaction instead of only appending.
|
|
108
|
+
const redacted = redactSensitiveMessages(sessionId, messages);
|
|
109
|
+
if (version && last?.version !== version) {
|
|
110
|
+
await replaceSessionSearchMessages(sessionId, messages, redacted, (text) => redactSensitiveText(sessionId, text), {
|
|
111
|
+
signal,
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
await appendSessionSearchMessages(sessionId, redacted, { signal });
|
|
115
|
+
}
|
|
116
|
+
if (lastIngested.size >= LAST_INGESTED_LIMIT) lastIngested.clear();
|
|
117
|
+
lastIngested.set(sessionId, { length, version });
|
|
118
|
+
}
|
|
119
|
+
|
|
26
120
|
/** Fire-and-forget: store a completed turn's transcript. Never throws. */
|
|
27
121
|
export function ingestSessionTurn(session) {
|
|
28
122
|
try {
|
|
@@ -30,30 +124,24 @@ export function ingestSessionTurn(session) {
|
|
|
30
124
|
const messages = session?.messages;
|
|
31
125
|
if (!sessionId || !Array.isArray(messages) || messages.length === 0) return;
|
|
32
126
|
if (isAgentOnlySession(session)) return;
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
() => {
|
|
39
|
-
if (lastIngestedLength.size >= LAST_INGESTED_LIMIT) lastIngestedLength.clear();
|
|
40
|
-
lastIngestedLength.set(sessionId, length);
|
|
41
|
-
},
|
|
42
|
-
(error) => logFailure(`ingest ${sessionId}`, error)
|
|
127
|
+
// The write runs after the session's earlier ones, so it keeps the
|
|
128
|
+
// transcript of this call: compaction may rewrite the live array before then.
|
|
129
|
+
const transcript = messages.slice();
|
|
130
|
+
serialized(sessionId, (signal) => storeTranscript(sessionId, transcript, signal)).catch(
|
|
131
|
+
reportUnlessClosed(`ingest ${sessionId}`)
|
|
43
132
|
);
|
|
44
133
|
} catch (error) {
|
|
45
134
|
logFailure('ingest', error);
|
|
46
135
|
}
|
|
47
136
|
}
|
|
48
137
|
|
|
49
|
-
/** Best-effort removal of a deleted session's search rows
|
|
138
|
+
/** Best-effort removal of a deleted session's search rows, after the
|
|
139
|
+
* session's pending writes. Never throws. */
|
|
50
140
|
export async function forgetSessionSearch(sessionId) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
await deleteSessionSearchMessages(sessionId);
|
|
54
|
-
}
|
|
55
|
-
logFailure(`delete ${sessionId}`, error);
|
|
56
|
-
}
|
|
141
|
+
await serialized(sessionId, async (signal) => {
|
|
142
|
+
lastIngested.delete(sessionId);
|
|
143
|
+
await deleteSessionSearchMessages(sessionId, { signal });
|
|
144
|
+
}).catch(reportUnlessClosed(`delete ${sessionId}`));
|
|
57
145
|
}
|
|
58
146
|
|
|
59
147
|
async function loadStoreDeps() {
|
|
@@ -89,8 +177,10 @@ export async function backfillSessionSearch({ deps } = {}) {
|
|
|
89
177
|
if (stamps.get(sessionId) === stamp) continue;
|
|
90
178
|
const session = readSession(dir, file);
|
|
91
179
|
if (session?.id && !isAgentOnlySession(session)) {
|
|
92
|
-
await
|
|
93
|
-
|
|
180
|
+
await serialized(session.id, async () => {
|
|
181
|
+
await appendSessionSearchMessages(session.id, redactSensitiveMessages(session.id, session.messages));
|
|
182
|
+
await recordSessionSearchBackfillStamp(session.id, stamp);
|
|
183
|
+
});
|
|
94
184
|
ingested += 1;
|
|
95
185
|
}
|
|
96
186
|
} catch (error) {
|
|
@@ -50,6 +50,95 @@ export async function runAbortable(signal, task, fallback) {
|
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/** Resolves after `ms`, or rejects with the abort reason once `signal` aborts. */
|
|
54
|
+
export function sleepUnlessAborted(ms, signal) {
|
|
55
|
+
if (!signal) return new Promise((resolve) => setTimeout(resolve, ms));
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
if (signal.aborted) return reject(abortReason(signal));
|
|
58
|
+
const onAbort = () => {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
reject(abortReason(signal));
|
|
61
|
+
};
|
|
62
|
+
const timer = setTimeout(() => {
|
|
63
|
+
signal.removeEventListener('abort', onAbort);
|
|
64
|
+
resolve();
|
|
65
|
+
}, ms);
|
|
66
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* One operation that several callers await. `start(signal)` runs once, when
|
|
72
|
+
* the first caller that still wants it joins — a live signal, or none — so a
|
|
73
|
+
* caller that is already aborted starts nothing. Its signal aborts only when
|
|
74
|
+
* every caller that joined with a signal has aborted and no caller joined
|
|
75
|
+
* without one (such a caller keeps it running). The caller whose abort cancels
|
|
76
|
+
* the work waits for the work to settle, its cleanup included, so nothing it
|
|
77
|
+
* started outlives it; any other aborted caller stops waiting at once and
|
|
78
|
+
* leaves the work to the callers still on it. `onSettled(failed)` runs once
|
|
79
|
+
* the started work settles; `promise` is that work, null until started.
|
|
80
|
+
*/
|
|
81
|
+
export function createSharedAbortable(start, { onSettled = null } = {}) {
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
let waiting = 0;
|
|
84
|
+
let pinned = false;
|
|
85
|
+
let promise = null;
|
|
86
|
+
const begin = () => {
|
|
87
|
+
if (!promise) {
|
|
88
|
+
promise = Promise.resolve().then(() => start(controller.signal));
|
|
89
|
+
promise.then(
|
|
90
|
+
() => onSettled?.(false),
|
|
91
|
+
() => onSettled?.(true)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return promise;
|
|
95
|
+
};
|
|
96
|
+
return {
|
|
97
|
+
get promise() {
|
|
98
|
+
return promise;
|
|
99
|
+
},
|
|
100
|
+
join(signal) {
|
|
101
|
+
if (!signal) {
|
|
102
|
+
pinned = true;
|
|
103
|
+
return begin();
|
|
104
|
+
}
|
|
105
|
+
if (signal.aborted) return Promise.reject(abortReason(signal));
|
|
106
|
+
waiting += 1;
|
|
107
|
+
const work = begin();
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
const onAbort = () => {
|
|
110
|
+
waiting -= 1;
|
|
111
|
+
const reason = abortReason(signal);
|
|
112
|
+
if (waiting > 0 || pinned) {
|
|
113
|
+
reject(reason);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
controller.abort(reason);
|
|
117
|
+
work.then(
|
|
118
|
+
() => reject(reason),
|
|
119
|
+
() => reject(reason)
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
123
|
+
work.then(
|
|
124
|
+
(value) => {
|
|
125
|
+
if (signal.aborted) return;
|
|
126
|
+
signal.removeEventListener('abort', onAbort);
|
|
127
|
+
waiting -= 1;
|
|
128
|
+
resolve(value);
|
|
129
|
+
},
|
|
130
|
+
(error) => {
|
|
131
|
+
if (signal.aborted) return;
|
|
132
|
+
signal.removeEventListener('abort', onAbort);
|
|
133
|
+
waiting -= 1;
|
|
134
|
+
reject(error);
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
53
142
|
/**
|
|
54
143
|
* Bound best-effort cleanup without cancelling the underlying work. The
|
|
55
144
|
* detached promise remains observed and may still finish in the background.
|
|
@@ -42,7 +42,6 @@ export const MASCOT_EXPRESSIONS: readonly string[];
|
|
|
42
42
|
export const MASCOT_INKS: Readonly<Record<string, string>>;
|
|
43
43
|
export const MIXY_AVATAR: Readonly<MascotAvatar>;
|
|
44
44
|
export const AGENT_AVATAR_STATES: readonly AgentAvatarState[];
|
|
45
|
-
export const AGENT_AVATAR_LATER_STATES: readonly string[];
|
|
46
45
|
export const AGENT_AVATAR_IMAGE_FILE: RegExp;
|
|
47
46
|
export const AGENT_AVATAR_STATE_FILE: RegExp;
|
|
48
47
|
export const AGENT_AVATAR_DIR: string;
|
|
@@ -45,8 +45,10 @@ const homeEvidenceCache = new Map();
|
|
|
45
45
|
|
|
46
46
|
/** What `agentId`'s home thread (sessions/sess_home_<id>.json) holds:
|
|
47
47
|
* whether it exists, whether it has the creation greeting, and whether the
|
|
48
|
-
* user
|
|
49
|
-
* or
|
|
48
|
+
* user talked in it: a message the user wrote (a user message that is not a
|
|
49
|
+
* runtime turn or the greeting). The agent's own messages never count: Mixy's
|
|
50
|
+
* heartbeat posts its reports there unasked. A missing or unreadable file
|
|
51
|
+
* holds nothing. */
|
|
50
52
|
export function homeThreadEvidence(dataDir, agentId) {
|
|
51
53
|
const sessionId = agentHomeSessionId(agentId);
|
|
52
54
|
if (!dataDir || !sessionId) return NO_HOME;
|
|
@@ -69,7 +71,6 @@ export function homeThreadEvidence(dataDir, agentId) {
|
|
|
69
71
|
let greeted = false;
|
|
70
72
|
let chatted = false;
|
|
71
73
|
for (const message of messages) {
|
|
72
|
-
if (message?.role === 'assistant') chatted = true;
|
|
73
74
|
if (message?.role !== 'user') continue;
|
|
74
75
|
const text = messageText(message.content);
|
|
75
76
|
if (CREATION_GREETING.test(text)) greeted = true;
|
|
@@ -20,16 +20,76 @@ export const hopOf = (value) => (Number.isInteger(value) && value > 0 ? value :
|
|
|
20
20
|
* depth: bot-descended work resumes where it was, independent work at 0. */
|
|
21
21
|
export const sendDepth = ({ hop, target } = {}) => Math.max(0, hopOf(hop) - (target === 'session' ? 1 : 0));
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// The bots of a chain (`botChain`): the agent ids that handed the work on,
|
|
24
|
+
// oldest first, the sender last. A bot's send carries the chain of its turn
|
|
25
|
+
// plus itself in the transcript meta of the message it delivers (like `hop`);
|
|
26
|
+
// the turn that message starts or joins is in that chain (`turnBotChain`), and
|
|
27
|
+
// a send or spawn from it may never reach a bot already in it, nor itself
|
|
28
|
+
// (assertBotChainTarget). A turn the user, a routine or a notification
|
|
29
|
+
// started begins a chain of its own.
|
|
30
|
+
export const botChainOf = (value) =>
|
|
31
|
+
Array.isArray(value) ? [...new Set(value.filter((id) => typeof id === 'string' && id))] : [];
|
|
32
|
+
|
|
33
|
+
// The chain of a submission the runtime takes without transcript meta of its
|
|
34
|
+
// own (a group-room member's turn, submitted by the desktop room host under
|
|
35
|
+
// the send's submission id), recorded by the sender before it is submitted.
|
|
36
|
+
const chainsBySubmission = new Map();
|
|
37
|
+
const MAX_SUBMISSION_CHAINS = 1000;
|
|
38
|
+
|
|
39
|
+
/** Record that submission `submissionId` starts or joins a turn of `chain`. */
|
|
40
|
+
export function rememberSubmissionBotChain(submissionId, chain) {
|
|
41
|
+
const id = String(submissionId || '').trim();
|
|
42
|
+
const bots = botChainOf(chain);
|
|
43
|
+
if (!id || !bots.length) return;
|
|
44
|
+
chainsBySubmission.set(id, bots);
|
|
45
|
+
while (chainsBySubmission.size > MAX_SUBMISSION_CHAINS) {
|
|
46
|
+
chainsBySubmission.delete(chainsBySubmission.keys().next().value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The chain a submission's transcript meta carries, else the one recorded for its ids. */
|
|
51
|
+
function submissionBotChain(transcriptMeta) {
|
|
52
|
+
const carried = botChainOf(transcriptMeta?.botChain);
|
|
53
|
+
if (carried.length) return carried;
|
|
54
|
+
const merged = Array.isArray(transcriptMeta?.submissionIds) ? transcriptMeta.submissionIds : [];
|
|
55
|
+
const ids = [transcriptMeta?.checkpointId, ...merged];
|
|
56
|
+
return botChainOf(ids.flatMap((id) => chainsBySubmission.get(String(id || '')) || []));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Stamp the hop and bot chain of the submission that starts the turn (else 0 and none) on `session`. */
|
|
24
60
|
export function stampTurnHop(session, transcriptMeta) {
|
|
25
61
|
session.turnHop = hopOf(transcriptMeta?.hop);
|
|
62
|
+
session.turnBotChain = submissionBotChain(transcriptMeta);
|
|
26
63
|
}
|
|
27
64
|
|
|
28
|
-
/** A bot's message steered into the running turn raises the turn's hop. */
|
|
65
|
+
/** A bot's message steered into the running turn raises the turn's hop and joins its chain. */
|
|
29
66
|
export function raiseTurnHop(session, transcriptMeta) {
|
|
30
67
|
const hop = hopOf(transcriptMeta?.hop);
|
|
31
68
|
if (session && hop > hopOf(session.turnHop)) session.turnHop = hop;
|
|
69
|
+
const chain = submissionBotChain(transcriptMeta);
|
|
70
|
+
if (session && chain.length) session.turnBotChain = botChainOf([...turnBotChainOf(session), ...chain]);
|
|
32
71
|
}
|
|
33
72
|
|
|
34
73
|
/** The hop of the turn `session` is in. */
|
|
35
74
|
export const turnHopOf = (session) => hopOf(session?.turnHop);
|
|
75
|
+
|
|
76
|
+
/** The bot chain of the turn `session` is in. */
|
|
77
|
+
export const turnBotChainOf = (session) => botChainOf(session?.turnBotChain);
|
|
78
|
+
|
|
79
|
+
/** The one check of every bot-to-bot hand-off (session send, a room seat,
|
|
80
|
+
* a contact agent spawn): `target`, the bot the work would reach, is
|
|
81
|
+
* neither `self` (the bot handing it on) nor already in `chain`. */
|
|
82
|
+
export function assertBotChainTarget(target, { chain = [], self = '' } = {}, prefix = 'session') {
|
|
83
|
+
if (!target) return;
|
|
84
|
+
if (target === self) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`${prefix}: the contact agent "${target}" cannot hand work to itself; do it yourself or delegate to a sub-agent`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (chain.includes(target)) {
|
|
90
|
+
const path = [...chain, ...(self && chain.at(-1) !== self ? [self] : []), target];
|
|
91
|
+
throw new Error(
|
|
92
|
+
`${prefix}: the contact agent "${target}" is already in this delegation chain (${path.join(' → ')}); do not hand the work back, answer or delegate to a sub-agent instead`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createWriteStream, rmSync } from 'node:fs';
|
|
2
2
|
import { Transform } from 'node:stream';
|
|
3
3
|
import { pipeline } from 'node:stream/promises';
|
|
4
|
-
import {
|
|
4
|
+
import { sleepUnlessAborted } from './abort-race.mjs';
|
|
5
5
|
|
|
6
6
|
export const MAX_NATIVE_BINARY_DOWNLOAD_BYTES = 256 * 1024 * 1024;
|
|
7
7
|
const DEFAULT_RETRY_DELAYS_MS = [1000, 3000, 9000];
|
|
@@ -118,13 +118,17 @@ export async function downloadToFileWithRetry(
|
|
|
118
118
|
timeoutMs = 180_000,
|
|
119
119
|
retryDelaysMs = DEFAULT_RETRY_DELAYS_MS,
|
|
120
120
|
onRetry = null,
|
|
121
|
+
// Cancels the request, its body and any retry wait; rejects with its reason.
|
|
122
|
+
signal = null,
|
|
121
123
|
} = {}
|
|
122
124
|
) {
|
|
123
125
|
const { maximum, expected } = downloadBounds(maxBytes, expectedBytes, label);
|
|
124
126
|
let lastError;
|
|
125
127
|
for (let attempt = 0; attempt <= retryDelaysMs.length; attempt += 1) {
|
|
128
|
+
signal?.throwIfAborted();
|
|
126
129
|
try {
|
|
127
|
-
const
|
|
130
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
131
|
+
const response = await fetchFn(url, { signal: signal ? AbortSignal.any([signal, timeout]) : timeout });
|
|
128
132
|
try {
|
|
129
133
|
if (!response.ok) throw new DownloadHttpError(response.status, httpLabel, url);
|
|
130
134
|
return await streamResponseToFile(response, destPath, {
|
|
@@ -142,12 +146,13 @@ export async function downloadToFileWithRetry(
|
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
148
|
} catch (error) {
|
|
149
|
+
signal?.throwIfAborted();
|
|
145
150
|
lastError = error;
|
|
146
151
|
if (error instanceof DownloadHttpError && error.terminal) throw error;
|
|
147
152
|
const delayMs = retryDelaysMs[attempt];
|
|
148
153
|
if (delayMs === undefined) break;
|
|
149
154
|
onRetry?.({ attempt: attempt + 1, delayMs, error });
|
|
150
|
-
await
|
|
155
|
+
await sleepUnlessAborted(delayMs, signal);
|
|
151
156
|
}
|
|
152
157
|
}
|
|
153
158
|
throw lastError;
|
|
@@ -35,6 +35,7 @@ const {
|
|
|
35
35
|
setSecret: _setSecret,
|
|
36
36
|
deleteSecret: _deleteSecret,
|
|
37
37
|
hasSecret: _hasSecret,
|
|
38
|
+
hasFallbackSecret: _hasFallbackSecret,
|
|
38
39
|
setSecretAsync: _setSecretAsyncImpl,
|
|
39
40
|
deleteSecretAsync: _deleteSecretAsyncImpl,
|
|
40
41
|
keychainStatus: _keychainStatus,
|
|
@@ -149,23 +150,12 @@ function readConfigRawCached() {
|
|
|
149
150
|
return raw;
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
// Canonical on-disk channel shape.
|
|
153
|
-
//
|
|
154
|
-
// so neither belongs in config anymore.
|
|
153
|
+
// Canonical on-disk channel shape. Schedules live in the scheduler DB and
|
|
154
|
+
// prompt injection moved to runtime hooks, so neither belongs in config.
|
|
155
155
|
export function canonicalizeStoredChannelsConfig(value = {}) {
|
|
156
156
|
const next = isPlainObject(value) ? { ...value } : {};
|
|
157
157
|
delete next.promptInjection;
|
|
158
158
|
delete next.schedules;
|
|
159
|
-
delete next.nonInteractive;
|
|
160
|
-
delete next.interactive;
|
|
161
|
-
|
|
162
|
-
// Messaging (Discord/Telegram) is retired: scrub the provider selector and
|
|
163
|
-
// per-provider routing sections from stored config.
|
|
164
|
-
delete next.backend;
|
|
165
|
-
delete next.provider;
|
|
166
|
-
delete next.discord;
|
|
167
|
-
delete next.telegram;
|
|
168
|
-
delete next.channel;
|
|
169
159
|
return next;
|
|
170
160
|
}
|
|
171
161
|
|
|
@@ -523,6 +513,18 @@ export function readSection(section) {
|
|
|
523
513
|
return stripGeneratedMarker(readAll()[section] ?? null) ?? {};
|
|
524
514
|
}
|
|
525
515
|
|
|
516
|
+
/**
|
|
517
|
+
* One section straight from the file, read-only: never served from the read
|
|
518
|
+
* cache, restored from a backup, quarantined or written. A missing, unreadable
|
|
519
|
+
* or malformed config throws. For checks that must fail closed, where a stale
|
|
520
|
+
* value restored from a backup must not count (developerOptionEnabledStrict).
|
|
521
|
+
*/
|
|
522
|
+
export function readSectionStrict(section) {
|
|
523
|
+
const parsed = parseJsonText(readFileSync(configPath(), 'utf8'));
|
|
524
|
+
if (!isPlainObject(parsed)) throw new SyntaxError('config root must be a JSON object');
|
|
525
|
+
return stripGeneratedMarker(parsed[section] ?? null) ?? {};
|
|
526
|
+
}
|
|
527
|
+
|
|
526
528
|
export function readConfig() {
|
|
527
529
|
return stripGeneratedMarker(readAll()) ?? {};
|
|
528
530
|
}
|
|
@@ -641,14 +643,17 @@ export function updateSectionAsync(section, updater) {
|
|
|
641
643
|
}
|
|
642
644
|
|
|
643
645
|
// ── Secret account names ─────────────────────────────────────────────────────
|
|
644
|
-
// Canonical account strings stored in the OS keychain
|
|
646
|
+
// Canonical account strings stored in the OS keychain (or, on a Linux host with
|
|
647
|
+
// no usable keychain provider, in the private secrets file behind the same
|
|
648
|
+
// get/set/delete calls: keychain-cjs.cjs "Linux fallback file").
|
|
645
649
|
export const SECRET_ACCOUNTS = Object.freeze({
|
|
646
650
|
agentApiKey: (provider) => `agent.${provider}.apiKey`,
|
|
647
651
|
openaiUsageSessionKey: 'agent.openai.usageSessionKey',
|
|
648
652
|
});
|
|
649
653
|
|
|
650
654
|
// ── Secret-aware getters ─────────────────────────────────────────────────────
|
|
651
|
-
// Read order: ENV MIXDOG_<UPPER_SNAKE> → OS keychain →
|
|
655
|
+
// Read order: ENV MIXDOG_<UPPER_SNAKE> → OS keychain → Linux fallback secrets
|
|
656
|
+
// file (read by _getSecret when the keychain has nothing) → null.
|
|
652
657
|
|
|
653
658
|
function _envKey(account) {
|
|
654
659
|
// 'agent.openai.apiKey' → 'MIXDOG_AGENT_OPENAI_APIKEY'
|
|
@@ -676,7 +681,9 @@ export function getOpenAIUsageSessionKey() {
|
|
|
676
681
|
}
|
|
677
682
|
|
|
678
683
|
/**
|
|
679
|
-
* Persist a secret to the OS keychain
|
|
684
|
+
* Persist a secret to the OS keychain; on a Linux host with no keychain provider
|
|
685
|
+
* (secret-tool/libsecret missing, no Secret Service) to the owner-only secrets
|
|
686
|
+
* file in the data dir instead. Throws on failure.
|
|
680
687
|
* Never writes to mixdog-config.json.
|
|
681
688
|
*/
|
|
682
689
|
export function saveSecret(account, value) {
|
|
@@ -697,25 +704,25 @@ export async function saveSecretAsync(account, value) {
|
|
|
697
704
|
}
|
|
698
705
|
|
|
699
706
|
function saveSecretError(account, err) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
707
|
+
// Reached for a locked keyring, a non-Linux keychain failure, or a Linux host
|
|
708
|
+
// with no keychain provider whose fallback secrets file could not be written
|
|
709
|
+
// (keychain-cjs.cjs). Surface an actionable message that points at the
|
|
710
|
+
// env-var read path the getters already honor, instead of silently writing
|
|
711
|
+
// the plaintext secret to mixdog-config.json. The fallback is a separate
|
|
712
|
+
// owner-only file, never the config.
|
|
713
|
+
const envKey = _envKey(account);
|
|
714
|
+
const agentMatch = String(account || '').match(/^agent\.([^.]+)\.apiKey$/);
|
|
715
|
+
const stdEnv = agentMatch ? AGENT_PROVIDER_ENV[agentMatch[1]] : null;
|
|
716
|
+
const envHint = stdEnv ? `${stdEnv} (or ${envKey})` : envKey;
|
|
717
|
+
const e = new Error(
|
|
718
|
+
`[config] could not save secret to the OS keychain: ${err?.message ? err.message : err}\n` +
|
|
719
|
+
` Set the ${envHint} environment variable instead — the runtime reads it directly.`
|
|
720
|
+
);
|
|
721
|
+
e.cause = err;
|
|
722
|
+
return e;
|
|
717
723
|
}
|
|
718
724
|
|
|
725
|
+
/** Remove a secret from the OS keychain (if present) and the Linux fallback file. */
|
|
719
726
|
export function deleteSecret(account) {
|
|
720
727
|
_deleteSecret(account);
|
|
721
728
|
}
|
|
@@ -741,13 +748,16 @@ export function onKeychainRecovered(listener) {
|
|
|
741
748
|
return typeof _onKeychainRecovered === 'function' ? _onKeychainRecovered(listener) : () => {};
|
|
742
749
|
}
|
|
743
750
|
|
|
744
|
-
/** Keyring health for the UI: { state: ok|locked|missing|error|unknown, message }.
|
|
751
|
+
/** Keyring health for the UI: { state: ok|locked|missing|unreachable|error|unknown, message, fallback }.
|
|
752
|
+
* `fallback` is true when no keychain provider exists on this Linux host and
|
|
753
|
+
* secrets live in the private secrets file: working storage, not a failure. */
|
|
745
754
|
export function getKeychainStatus() {
|
|
746
755
|
return typeof _keychainStatus === 'function' ? _keychainStatus() : { state: 'unknown', message: '' };
|
|
747
756
|
}
|
|
748
757
|
|
|
749
758
|
/**
|
|
750
|
-
* Whether a secret is stored in the OS keychain
|
|
759
|
+
* Whether a secret is stored in the OS keychain (or Linux fallback file) for
|
|
760
|
+
* `account` (ignores env).
|
|
751
761
|
* Lets the setup UI show "Set" WITHOUT ever sending the secret value to the
|
|
752
762
|
* browser.
|
|
753
763
|
*/
|
|
@@ -760,4 +770,10 @@ export function hasStoredSecret(account) {
|
|
|
760
770
|
}
|
|
761
771
|
}
|
|
762
772
|
|
|
773
|
+
/** Whether `account`'s stored secret currently lives in the Linux fallback file
|
|
774
|
+
* (never true on Windows/macOS). Lets the UI label where a key is kept. */
|
|
775
|
+
export function isFallbackSecret(account) {
|
|
776
|
+
return typeof _hasFallbackSecret === 'function' ? !!_hasFallbackSecret(account) : false;
|
|
777
|
+
}
|
|
778
|
+
|
|
763
779
|
export { configPath };
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
// What a multi-account connector server tells the model about itself (MCP
|
|
2
|
-
// `instructions`): the
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// `instructions`), in one line: the connected accounts, and that each tool
|
|
3
|
+
// takes `account` (asked of the user when several are connected and none was
|
|
4
|
+
// named). An account that cannot be used right now is listed with why.
|
|
5
5
|
|
|
6
|
-
/** The MCP instructions
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
/** The MCP instructions naming `accounts` (`{ email, note? }`, `note` a short
|
|
7
|
+
* reason it is unusable such as "needs sign-in"; one without an address is
|
|
8
|
+
* left out), or undefined when none has one. */
|
|
9
|
+
export function connectorAccountsInstructions(accounts) {
|
|
10
|
+
const named = (Array.isArray(accounts) ? accounts : []).filter((account) => account?.email);
|
|
11
|
+
if (!named.length) return undefined;
|
|
12
|
+
const list = named.map(({ email, note }) => (note ? `${email} (${note})` : email)).join(', ');
|
|
13
|
+
return named.length === 1
|
|
14
|
+
? `Accounts: ${list}. \`account\` may be left out.`
|
|
15
|
+
: `Accounts: ${list}. Pass \`account\`; if the user named none and there are several, ask which.`;
|
|
10
16
|
}
|
|
11
17
|
|
|
12
|
-
/** The account
|
|
13
|
-
export function
|
|
14
|
-
|
|
18
|
+
/** The account addresses instructions from connectorAccountsInstructions name. */
|
|
19
|
+
export function accountsOfInstructions(instructions) {
|
|
20
|
+
const list = /^Accounts: (.+?)\. (?:`account`|Pass `account`)/.exec(String(instructions || ''))?.[1] || '';
|
|
21
|
+
return list
|
|
22
|
+
.split(', ')
|
|
23
|
+
.map((part) => part.replace(/ \(.*\)$/, '').trim())
|
|
24
|
+
.filter(Boolean);
|
|
15
25
|
}
|