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
|
@@ -5,6 +5,7 @@ import http2 from 'node:http2';
|
|
|
5
5
|
import { MAX_CONNECT_FRAME_BYTES, createCursorByteQueue } from './cursor-wire-guards.mjs';
|
|
6
6
|
import { cursorClientVersion, warmCursorClientVersion } from './cursor-client-version.mjs';
|
|
7
7
|
import { retryAfterMsFromError } from './retry-classifier.mjs';
|
|
8
|
+
import { providerTimeoutError } from '../stall-policy.mjs';
|
|
8
9
|
|
|
9
10
|
export { warmCursorClientVersion };
|
|
10
11
|
|
|
@@ -153,7 +154,9 @@ export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }
|
|
|
153
154
|
if (closed || session.closed || session.destroyed) return;
|
|
154
155
|
try {
|
|
155
156
|
session.ping(() => {});
|
|
156
|
-
} catch {
|
|
157
|
+
} catch {
|
|
158
|
+
// the session may have closed since the check above
|
|
159
|
+
}
|
|
157
160
|
}, H2_PING_INTERVAL_MS);
|
|
158
161
|
ping.unref?.();
|
|
159
162
|
|
|
@@ -175,20 +178,28 @@ export function openCursorStream({ accessToken, path = RUN_PATH, url = API_URL }
|
|
|
175
178
|
closeError = error || (status >= 400 ? httpStatusError(status, responseHeaders) : null);
|
|
176
179
|
try {
|
|
177
180
|
request.close();
|
|
178
|
-
} catch {
|
|
181
|
+
} catch {
|
|
182
|
+
// the request stream may already be closed
|
|
183
|
+
}
|
|
179
184
|
try {
|
|
180
185
|
session.close();
|
|
181
|
-
} catch {
|
|
186
|
+
} catch {
|
|
187
|
+
// the session may already be closed
|
|
188
|
+
}
|
|
182
189
|
closeHandler?.(closeError);
|
|
183
190
|
};
|
|
184
191
|
const close = (error = null) => {
|
|
185
192
|
if (closed) return;
|
|
186
193
|
try {
|
|
187
194
|
request.close(http2.constants.NGHTTP2_CANCEL);
|
|
188
|
-
} catch {
|
|
195
|
+
} catch {
|
|
196
|
+
// the request stream may already be closed
|
|
197
|
+
}
|
|
189
198
|
try {
|
|
190
199
|
session.destroy();
|
|
191
|
-
} catch {
|
|
200
|
+
} catch {
|
|
201
|
+
// the session may already be destroyed
|
|
202
|
+
}
|
|
192
203
|
finish(error);
|
|
193
204
|
};
|
|
194
205
|
|
|
@@ -243,17 +254,21 @@ export async function callCursorUnary({ accessToken, path, body, url = API_URL,
|
|
|
243
254
|
let status = 0;
|
|
244
255
|
let responseHeaders = null;
|
|
245
256
|
let settled = false;
|
|
246
|
-
const timeout = setTimeout(() => finish(
|
|
257
|
+
const timeout = setTimeout(() => finish(providerTimeoutError('Cursor request', timeoutMs)), timeoutMs);
|
|
247
258
|
const finish = (error = null) => {
|
|
248
259
|
if (settled) return;
|
|
249
260
|
settled = true;
|
|
250
261
|
clearTimeout(timeout);
|
|
251
262
|
try {
|
|
252
263
|
request.close();
|
|
253
|
-
} catch {
|
|
264
|
+
} catch {
|
|
265
|
+
// the request stream may already be closed
|
|
266
|
+
}
|
|
254
267
|
try {
|
|
255
268
|
session.close();
|
|
256
|
-
} catch {
|
|
269
|
+
} catch {
|
|
270
|
+
// the session may already be closed
|
|
271
|
+
}
|
|
257
272
|
if (error) reject(error);
|
|
258
273
|
else if (status >= 400) reject(httpStatusError(status, responseHeaders));
|
|
259
274
|
else resolve(Buffer.concat(chunks));
|
|
@@ -322,7 +337,8 @@ export function parseEndStream(bytes) {
|
|
|
322
337
|
if (!payload?.error) return null;
|
|
323
338
|
const connectCode = String(payload.error.code || 'error').toLowerCase();
|
|
324
339
|
const detail = String(payload.error.message || 'request failed');
|
|
325
|
-
const tooLarge =
|
|
340
|
+
const tooLarge =
|
|
341
|
+
connectCode === 'resource_exhausted' && !THROTTLE_CUE_RE.test(detail) && REQUEST_TOO_LARGE_RE.test(detail);
|
|
326
342
|
const retryAfter =
|
|
327
343
|
payload.error.retryAfter ??
|
|
328
344
|
payload.error.retry_after ??
|
|
@@ -132,7 +132,9 @@ async function* responseSseEvents(response, signal) {
|
|
|
132
132
|
if (signal) {
|
|
133
133
|
try {
|
|
134
134
|
signal.removeEventListener('abort', onAbort);
|
|
135
|
-
} catch {
|
|
135
|
+
} catch {
|
|
136
|
+
// the abort listener may already be gone
|
|
137
|
+
}
|
|
136
138
|
}
|
|
137
139
|
if (aborted) await reader.cancel().catch(() => {});
|
|
138
140
|
}
|
|
@@ -480,6 +482,7 @@ function selectParameterizedCursorVariant(group, requested, sendOpts = {}) {
|
|
|
480
482
|
availableVariants[0] ||
|
|
481
483
|
null;
|
|
482
484
|
const parameters = { ...(defaultVariant?.parameters || {}), ...aliasParameters };
|
|
485
|
+
const defaultContext = parameters.context;
|
|
483
486
|
const routeParameters =
|
|
484
487
|
sendOpts.modelParameters && typeof sendOpts.modelParameters === 'object' ? sendOpts.modelParameters : {};
|
|
485
488
|
for (const definition of group.definitions) {
|
|
@@ -501,9 +504,18 @@ function selectParameterizedCursorVariant(group, requested, sendOpts = {}) {
|
|
|
501
504
|
) {
|
|
502
505
|
parameters.fast = sendOpts.fast === true ? 'true' : 'false';
|
|
503
506
|
}
|
|
504
|
-
const
|
|
505
|
-
Object.entries(parameters).every(([id, value]) => String(variant.parameters?.[id] ?? '') === String(value))
|
|
506
|
-
);
|
|
507
|
+
const matches = (variant) =>
|
|
508
|
+
Object.entries(parameters).every(([id, value]) => String(variant.parameters?.[id] ?? '') === String(value));
|
|
509
|
+
let selected = availableVariants.find(matches);
|
|
510
|
+
// Keep a supported explicit context. A persisted context can exist in this
|
|
511
|
+
// mode but conflict with its requested effort/fast/other options. In that
|
|
512
|
+
// case try only the mode's default context, preserving every other choice
|
|
513
|
+
// and still rejecting the request unless the full combination is offered.
|
|
514
|
+
if (!selected && Object.hasOwn(routeParameters, 'context')) {
|
|
515
|
+
if (defaultContext === undefined) delete parameters.context;
|
|
516
|
+
else parameters.context = defaultContext;
|
|
517
|
+
selected = availableVariants.find(matches);
|
|
518
|
+
}
|
|
507
519
|
if (!selected && availableVariants.length) {
|
|
508
520
|
throw new Error(`Cursor model ${group.id} does not offer the selected parameter combination`);
|
|
509
521
|
}
|
|
@@ -642,7 +654,9 @@ class CursorProviderBase {
|
|
|
642
654
|
let emittedToolCall = false;
|
|
643
655
|
try {
|
|
644
656
|
sendOpts.onStageChange?.('requesting');
|
|
645
|
-
} catch {
|
|
657
|
+
} catch {
|
|
658
|
+
// the stage observer is advisory
|
|
659
|
+
}
|
|
646
660
|
try {
|
|
647
661
|
const response = await runtime.handleChatCompletion(body, accessToken, {
|
|
648
662
|
onStageChange: sendOpts.onStageChange,
|
|
@@ -657,7 +671,9 @@ class CursorProviderBase {
|
|
|
657
671
|
});
|
|
658
672
|
try {
|
|
659
673
|
sendOpts.onStageChange?.('streaming');
|
|
660
|
-
} catch {
|
|
674
|
+
} catch {
|
|
675
|
+
// the stage observer is advisory
|
|
676
|
+
}
|
|
661
677
|
return await consumeCompatChatCompletionStream(responseSseEvents(response, signal), {
|
|
662
678
|
signal,
|
|
663
679
|
label: this.name,
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync } from 'node:fs';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import { join, resolve } from 'node:path';
|
|
5
|
+
import { resolvePluginData } from '../../../shared/plugin-paths.mjs';
|
|
6
|
+
import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
7
|
+
import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
|
|
8
|
+
import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
|
|
9
|
+
import { openLoginUrl } from './lib/open-login-url.mjs';
|
|
10
|
+
import { OAUTH_PAGE_CONTENT_TYPE, oauthErrorHtml, oauthSuccessHtml } from './lib/oauth-page.mjs';
|
|
11
|
+
import { createOAuthPkce, parseOAuthCodeInput } from './lib/oauth-pkce.mjs';
|
|
12
|
+
import {
|
|
13
|
+
accountIdentityFields,
|
|
14
|
+
decodeJwtPayload,
|
|
15
|
+
expiryFromAccessToken,
|
|
16
|
+
normalizeAccountIdentity,
|
|
17
|
+
} from './lib/oauth-token-utils.mjs';
|
|
18
|
+
|
|
19
|
+
const AUTHORIZE_URL = 'https://app.devin.ai/auth/cli/continue';
|
|
20
|
+
const TOKEN_URL = 'https://api.devin.ai/auth/cli/token';
|
|
21
|
+
const CALLBACK_HOST = '127.0.0.1';
|
|
22
|
+
const CALLBACK_PORT = 59653;
|
|
23
|
+
const CALLBACK_PATH = '/callback';
|
|
24
|
+
const REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
25
|
+
const LOGIN_TIMEOUT_MS = 5 * 60_000;
|
|
26
|
+
const EXCHANGE_TIMEOUT_MS = 30_000;
|
|
27
|
+
// The login token is the only credential and cannot be refreshed. A token
|
|
28
|
+
// without a readable `exp` claim is assumed to live for a year.
|
|
29
|
+
const DEFAULT_TOKEN_LIFETIME_MS = 365 * 24 * 60 * 60_000;
|
|
30
|
+
export const REAUTH_GUIDANCE = 'Open /providers in Mixdog to sign in again.';
|
|
31
|
+
|
|
32
|
+
function reauthError(message) {
|
|
33
|
+
return Object.assign(new Error(message), { reauthRequired: true });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Sign-in failures the desktop words in the user's language: the message leads
|
|
37
|
+
// with `DEVIN_OAUTH_<CODE>:` and apps/desktop/src/renderer/oauth-failure-copy.ts
|
|
38
|
+
// maps each code to its cause and remedy (like GMAIL_APP_PASSWORD_<CODE>).
|
|
39
|
+
export const DEVIN_OAUTH_FAILURE_CODES = Object.freeze([
|
|
40
|
+
'DEVIN_OAUTH_CALLBACK_PORT_IN_USE',
|
|
41
|
+
'DEVIN_OAUTH_CALLBACK_SERVER_FAILED',
|
|
42
|
+
'DEVIN_OAUTH_LOGIN_TIMED_OUT',
|
|
43
|
+
'DEVIN_OAUTH_AUTHORIZATION_DENIED',
|
|
44
|
+
'DEVIN_OAUTH_STATE_MISMATCH',
|
|
45
|
+
'DEVIN_OAUTH_CODE_REQUIRED',
|
|
46
|
+
'DEVIN_OAUTH_TOKEN_EXCHANGE_FAILED',
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
function loginFailure(code, text, props = {}) {
|
|
50
|
+
const failureCode = `DEVIN_OAUTH_${code}`;
|
|
51
|
+
return Object.assign(new Error(`${failureCode}: ${text}`), { failureCode }, props);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function credentialsPath() {
|
|
55
|
+
const bound = boundProviderAuthPath('devin-oauth');
|
|
56
|
+
if (bound) return resolve(bound);
|
|
57
|
+
const explicit = process.env.DEVIN_OAUTH_CREDENTIALS_PATH;
|
|
58
|
+
if (explicit) return resolve(explicit);
|
|
59
|
+
const dir = resolvePluginData();
|
|
60
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
61
|
+
return join(dir, 'devin-oauth.json');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function devinTokenExpiry(token) {
|
|
65
|
+
return expiryFromAccessToken(token);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// The login token is a JWT whose `sub` is the Devin user; no request needed.
|
|
69
|
+
export function devinTokenIdentity(token) {
|
|
70
|
+
const payload = decodeJwtPayload(token);
|
|
71
|
+
return normalizeAccountIdentity({ id: payload?.sub, email: payload?.email });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function loadStoredCredentials() {
|
|
75
|
+
if (modelProxyClientMode()) return null;
|
|
76
|
+
const path = credentialsPath();
|
|
77
|
+
if (!existsSync(path)) return null;
|
|
78
|
+
try {
|
|
79
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
80
|
+
if (!raw?.access_token) return null;
|
|
81
|
+
return {
|
|
82
|
+
...raw,
|
|
83
|
+
expires_at: Number(raw.expires_at) || devinTokenExpiry(raw.access_token),
|
|
84
|
+
identity: normalizeAccountIdentity(raw.identity) || devinTokenIdentity(raw.access_token),
|
|
85
|
+
source: 'Mixdog token store',
|
|
86
|
+
};
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function saveCredentials(tokens) {
|
|
93
|
+
writeJsonAtomicSync(credentialsPath(), tokens, {
|
|
94
|
+
lock: true,
|
|
95
|
+
fsyncDir: true,
|
|
96
|
+
mode: 0o600,
|
|
97
|
+
secret: true,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function tokenRecord(accessToken) {
|
|
102
|
+
const identity = devinTokenIdentity(accessToken);
|
|
103
|
+
return {
|
|
104
|
+
access_token: accessToken,
|
|
105
|
+
refresh_token: null,
|
|
106
|
+
expires_at: devinTokenExpiry(accessToken) || Date.now() + DEFAULT_TOKEN_LIFETIME_MS,
|
|
107
|
+
...(identity ? { identity } : {}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function hasDevinOAuthCredentials() {
|
|
112
|
+
const tokens = loadStoredCredentials();
|
|
113
|
+
if (!tokens?.access_token) return false;
|
|
114
|
+
const expiresAt = Number(tokens.expires_at) || 0;
|
|
115
|
+
return expiresAt === 0 || expiresAt > Date.now();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function describeDevinOAuthCredentials() {
|
|
119
|
+
const tokens = loadStoredCredentials();
|
|
120
|
+
if (!tokens?.access_token) {
|
|
121
|
+
return {
|
|
122
|
+
authenticated: false,
|
|
123
|
+
usable: false,
|
|
124
|
+
refreshable: false,
|
|
125
|
+
reauthRequired: false,
|
|
126
|
+
status: 'Not Set',
|
|
127
|
+
detail: 'Mixdog token store',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
const expiresAt = Number(tokens.expires_at) || 0;
|
|
131
|
+
const expired = expiresAt > 0 && expiresAt <= Date.now();
|
|
132
|
+
return {
|
|
133
|
+
authenticated: !expired,
|
|
134
|
+
usable: !expired,
|
|
135
|
+
refreshable: false,
|
|
136
|
+
reauthRequired: expired,
|
|
137
|
+
status: expired ? 'Reauth Required' : 'Access Only',
|
|
138
|
+
detail: tokens.source || 'Devin OAuth',
|
|
139
|
+
expiresAt: expiresAt || null,
|
|
140
|
+
...accountIdentityFields(tokens.identity),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function forgetDevinOAuthCredentials() {
|
|
145
|
+
const path = credentialsPath();
|
|
146
|
+
if (!existsSync(path)) return { removed: false };
|
|
147
|
+
unlinkSync(path);
|
|
148
|
+
return { removed: true };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function generateDevinOAuthParams() {
|
|
152
|
+
const { verifier, challenge } = createOAuthPkce();
|
|
153
|
+
const state = randomUUID();
|
|
154
|
+
const params = new URLSearchParams({
|
|
155
|
+
response_type: 'code',
|
|
156
|
+
redirect_uri: REDIRECT_URI,
|
|
157
|
+
code_challenge: challenge,
|
|
158
|
+
code_challenge_method: 'S256',
|
|
159
|
+
state,
|
|
160
|
+
prompt: 'select_account',
|
|
161
|
+
});
|
|
162
|
+
return { verifier, challenge, state, loginUrl: `${AUTHORIZE_URL}?${params}` };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function exchangeDevinCode(code, verifier, { fetchFn = fetch, signal } = {}) {
|
|
166
|
+
const timeout = AbortSignal.timeout(EXCHANGE_TIMEOUT_MS);
|
|
167
|
+
let response;
|
|
168
|
+
try {
|
|
169
|
+
response = await fetchFn(TOKEN_URL, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
172
|
+
body: JSON.stringify({ code, code_verifier: verifier }),
|
|
173
|
+
signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
|
|
174
|
+
redirect: 'error',
|
|
175
|
+
});
|
|
176
|
+
} catch (error) {
|
|
177
|
+
// A cancelled login keeps its own abort; any other failure to get an
|
|
178
|
+
// answer (network, timeout) is a coded exchange failure. The cause is not
|
|
179
|
+
// quoted: only its kind is.
|
|
180
|
+
if (signal?.aborted) throw error;
|
|
181
|
+
throw loginFailure(
|
|
182
|
+
'TOKEN_EXCHANGE_FAILED',
|
|
183
|
+
error?.name === 'TimeoutError' ? 'Devin token exchange timed out' : 'Devin token exchange request failed'
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
// Response bodies may carry the login token itself, so none is ever quoted
|
|
187
|
+
// in an error: only the status is reported.
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
throw loginFailure('TOKEN_EXCHANGE_FAILED', `Devin token exchange failed (${response.status})`, {
|
|
190
|
+
status: response.status,
|
|
191
|
+
httpStatus: response.status,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
const data = await response.json().catch(() => null);
|
|
195
|
+
if (typeof data?.token !== 'string' || !data.token) {
|
|
196
|
+
throw loginFailure('TOKEN_EXCHANGE_FAILED', 'Devin token exchange returned no token');
|
|
197
|
+
}
|
|
198
|
+
return tokenRecord(data.token);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// The callback port is fixed, so at most one login can own it. A newer login
|
|
202
|
+
// in this process takes the port over from the one it supersedes.
|
|
203
|
+
let activeLogin = null;
|
|
204
|
+
|
|
205
|
+
function listenOnCallbackPort(server) {
|
|
206
|
+
return new Promise((resolveListen, rejectListen) => {
|
|
207
|
+
server.once('error', rejectListen);
|
|
208
|
+
server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
|
|
209
|
+
server.off('error', rejectListen);
|
|
210
|
+
resolveListen();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Logins start one at a time, in call order: ownership of the port is decided
|
|
216
|
+
// only once the previous start has fully settled, so overlapping calls
|
|
217
|
+
// supersede each other instead of racing for the bind.
|
|
218
|
+
let startQueue = Promise.resolve();
|
|
219
|
+
|
|
220
|
+
export function beginDevinOAuthLogin(options) {
|
|
221
|
+
const started = startQueue.then(() => startDevinOAuthLogin(options));
|
|
222
|
+
startQueue = started.catch(() => {});
|
|
223
|
+
return started;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function startDevinOAuthLogin({ openBrowser = true, timeoutMs = LOGIN_TIMEOUT_MS, fetchFn = fetch } = {}) {
|
|
227
|
+
activeLogin?.cancel();
|
|
228
|
+
const params = generateDevinOAuthParams();
|
|
229
|
+
const controller = new AbortController();
|
|
230
|
+
const server = createServer();
|
|
231
|
+
let timer = null;
|
|
232
|
+
let exchangePromise = null;
|
|
233
|
+
let settled = false;
|
|
234
|
+
let resolveLogin;
|
|
235
|
+
let rejectLogin;
|
|
236
|
+
const waitForCallback = new Promise((resolvePromise, rejectPromise) => {
|
|
237
|
+
resolveLogin = resolvePromise;
|
|
238
|
+
rejectLogin = rejectPromise;
|
|
239
|
+
});
|
|
240
|
+
// A caller may only ever cancel; that rejection must not go unhandled.
|
|
241
|
+
waitForCallback.catch(() => {});
|
|
242
|
+
|
|
243
|
+
const login = {
|
|
244
|
+
provider: 'devin-oauth',
|
|
245
|
+
url: params.loginUrl,
|
|
246
|
+
manualUrl: null,
|
|
247
|
+
waitForCallback,
|
|
248
|
+
// A remote client signs in on its own machine, where the redirect to this
|
|
249
|
+
// host's loopback listener cannot land: it pastes the final address (or
|
|
250
|
+
// code) back, and the state it carries must be this login's own.
|
|
251
|
+
completeCode: async (input) => {
|
|
252
|
+
const text = String(input || '').trim();
|
|
253
|
+
// An address carries its code and state as query parameters only: one
|
|
254
|
+
// without a code is not a bare code (the shared parser would take the
|
|
255
|
+
// whole address for one and spend it on the exchange).
|
|
256
|
+
const address = URL.canParse(text) ? new URL(text) : null;
|
|
257
|
+
const pasted = address
|
|
258
|
+
? { code: address.searchParams.get('code') || '', state: address.searchParams.get('state') || '' }
|
|
259
|
+
: parseOAuthCodeInput(text, { allowHashState: true });
|
|
260
|
+
if (pasted.state && pasted.state !== params.state) throw loginFailure('STATE_MISMATCH', 'OAuth state mismatch');
|
|
261
|
+
if (!pasted.code) throw loginFailure('CODE_REQUIRED', 'Authorization code is required');
|
|
262
|
+
return await acceptCode(pasted.code);
|
|
263
|
+
},
|
|
264
|
+
cancel: () => finish(null, new Error('Devin OAuth cancelled')),
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
function finish(value, error = null) {
|
|
268
|
+
if (settled) return;
|
|
269
|
+
settled = true;
|
|
270
|
+
if (activeLogin === login) activeLogin = null;
|
|
271
|
+
// Work arriving after the login settled (or was cancelled) is rejected.
|
|
272
|
+
controller.abort();
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
try {
|
|
275
|
+
server.close();
|
|
276
|
+
server.closeIdleConnections?.();
|
|
277
|
+
} catch {
|
|
278
|
+
/* already closed */
|
|
279
|
+
}
|
|
280
|
+
if (error) rejectLogin(error);
|
|
281
|
+
else resolveLogin(value);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// The authorization code is spent once: a duplicate callback joins the first
|
|
285
|
+
// exchange instead of failing the login.
|
|
286
|
+
const acceptCode = (code) => {
|
|
287
|
+
if (exchangePromise) return exchangePromise;
|
|
288
|
+
controller.signal.throwIfAborted();
|
|
289
|
+
// The browser deadline ends once a code arrives; the exchange has its own.
|
|
290
|
+
clearTimeout(timer);
|
|
291
|
+
exchangePromise = exchangeDevinCode(code, params.verifier, { fetchFn, signal: controller.signal }).then(
|
|
292
|
+
(tokens) => {
|
|
293
|
+
// A cancelled login never stores the account it was exchanging.
|
|
294
|
+
controller.signal.throwIfAborted();
|
|
295
|
+
saveCredentials(tokens);
|
|
296
|
+
return tokens;
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
exchangePromise.then(
|
|
300
|
+
(tokens) => finish(tokens),
|
|
301
|
+
(error) => finish(null, error instanceof Error ? error : new Error(String(error)))
|
|
302
|
+
);
|
|
303
|
+
return exchangePromise;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const reply = (res, status, html) => {
|
|
307
|
+
res.writeHead(status, { 'Content-Type': OAUTH_PAGE_CONTENT_TYPE, Connection: 'close' });
|
|
308
|
+
res.end(html);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const handleRequest = async (req, res) => {
|
|
312
|
+
const target = new URL(req.url || '/', REDIRECT_URI);
|
|
313
|
+
if (req.method !== 'GET' || target.pathname !== CALLBACK_PATH) {
|
|
314
|
+
res.writeHead(404);
|
|
315
|
+
res.end();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
// A request that is not this login's own is refused; the real callback
|
|
319
|
+
// may still arrive.
|
|
320
|
+
if (target.searchParams.get('state') !== params.state) {
|
|
321
|
+
reply(res, 400, oauthErrorHtml('The sign-in callback did not match this login.'));
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const authorizationError = target.searchParams.get('error');
|
|
325
|
+
if (authorizationError) {
|
|
326
|
+
// Only a plain OAuth error code is named; free text (error_description)
|
|
327
|
+
// may carry anything, secrets included, so it never reaches the message.
|
|
328
|
+
const code = /^[A-Za-z0-9_.-]{1,64}$/.test(authorizationError) ? ` (${authorizationError})` : '';
|
|
329
|
+
reply(res, 400, oauthErrorHtml('Devin did not authorize this sign-in.'));
|
|
330
|
+
// A late denial must not cancel an already accepted code.
|
|
331
|
+
if (!exchangePromise) {
|
|
332
|
+
finish(null, loginFailure('AUTHORIZATION_DENIED', `Authorization failed${code}`));
|
|
333
|
+
}
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const code = target.searchParams.get('code')?.trim();
|
|
337
|
+
if (!code) {
|
|
338
|
+
reply(res, 400, oauthErrorHtml('The sign-in callback carried no authorization code.'));
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
await acceptCode(code);
|
|
343
|
+
reply(res, 200, oauthSuccessHtml('Devin'));
|
|
344
|
+
} catch {
|
|
345
|
+
// The shared exchange reports its error through waitForCallback.
|
|
346
|
+
reply(res, 500, oauthErrorHtml('Devin could not complete the token exchange.'));
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// Whatever a stray local client sends (a malformed target included) is
|
|
351
|
+
// answered here and never escapes the listener.
|
|
352
|
+
server.on('request', async (req, res) => {
|
|
353
|
+
try {
|
|
354
|
+
await handleRequest(req, res);
|
|
355
|
+
} catch {
|
|
356
|
+
if (res.headersSent) res.destroy();
|
|
357
|
+
else reply(res, 400, oauthErrorHtml('The sign-in callback was malformed.'));
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
await listenOnCallbackPort(server);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
const failure =
|
|
365
|
+
error?.code === 'EADDRINUSE'
|
|
366
|
+
? loginFailure(
|
|
367
|
+
'CALLBACK_PORT_IN_USE',
|
|
368
|
+
`Callback port ${CALLBACK_HOST}:${CALLBACK_PORT} is already in use. Close the program using it (or another Devin sign-in) and try again.`,
|
|
369
|
+
{ code: 'EADDRINUSE' }
|
|
370
|
+
)
|
|
371
|
+
: loginFailure(
|
|
372
|
+
'CALLBACK_SERVER_FAILED',
|
|
373
|
+
`Callback server failed on ${CALLBACK_HOST}:${CALLBACK_PORT}: ${error?.message || error}`
|
|
374
|
+
);
|
|
375
|
+
finish(null, failure);
|
|
376
|
+
throw failure;
|
|
377
|
+
}
|
|
378
|
+
// Once bound, a runtime fault on the listener ends the login.
|
|
379
|
+
server.on('error', (error) =>
|
|
380
|
+
finish(null, loginFailure('CALLBACK_SERVER_FAILED', `Callback server failed: ${error?.message || error}`))
|
|
381
|
+
);
|
|
382
|
+
timer = setTimeout(
|
|
383
|
+
() => finish(null, loginFailure('LOGIN_TIMED_OUT', `Browser authentication timed out after ${timeoutMs}ms`)),
|
|
384
|
+
timeoutMs
|
|
385
|
+
);
|
|
386
|
+
timer.unref?.();
|
|
387
|
+
activeLogin = login;
|
|
388
|
+
|
|
389
|
+
process.stderr.write(`\n[devin-oauth] Open the Devin sign-in page:\n${params.loginUrl}\n\n`);
|
|
390
|
+
if (openBrowser) await openLoginUrl('devin-oauth', params.loginUrl);
|
|
391
|
+
return login;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export async function loginDevinOAuth() {
|
|
395
|
+
const login = await beginDevinOAuthLogin();
|
|
396
|
+
return await login.waitForCallback;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// The login token cannot be refreshed: once it expires the user signs in again.
|
|
400
|
+
export async function resolveDevinOAuthAccessToken() {
|
|
401
|
+
const tokens = loadStoredCredentials();
|
|
402
|
+
if (!tokens?.access_token) {
|
|
403
|
+
throw reauthError(`Devin OAuth is not connected. ${REAUTH_GUIDANCE}`);
|
|
404
|
+
}
|
|
405
|
+
if (tokens.expires_at > 0 && tokens.expires_at <= Date.now()) {
|
|
406
|
+
throw reauthError(`Devin login token expired and cannot be refreshed. ${REAUTH_GUIDANCE}`);
|
|
407
|
+
}
|
|
408
|
+
return tokens.access_token;
|
|
409
|
+
}
|