mixdog 1.2.5 → 1.3.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/simple-icons-CC0-1.0.txt +30 -0
- package/NOTICE.md +19 -0
- package/package.json +4 -1
- package/scripts/check-model-recommendations.mjs +89 -0
- package/scripts/gate-local.mjs +7 -0
- package/scripts/lib/gate-scheduler.mjs +2 -2
- package/scripts/lib/isolated-test-env.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +5 -14
- package/scripts/provider-pingpong-live.mjs +9 -9
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/reviewer/AGENT.md +5 -4
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +17 -0
- package/src/defaults/skills/browser-use/SKILL.md +7 -1
- package/src/defaults/skills/computer-use/SKILL.md +3 -1
- package/src/defaults/skills/memory-management/SKILL.md +4 -1
- package/src/defaults/skills/setup/references/actions.md +11 -4
- package/src/defaults/skills/setup-guide/SKILL.md +175 -0
- package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
- package/src/defaults/skills/visualize/SKILL.md +3 -2
- package/src/lib/rules-builder.cjs +56 -7
- package/src/output-styles/common.md +22 -6
- package/src/output-styles/detailed.md +7 -5
- package/src/output-styles/minimal.md +7 -3
- package/src/output-styles/simple.md +2 -3
- package/src/rules/agent/AGENT.md +7 -1
- package/src/rules/agent-mode/AGENT_MODE.md +24 -0
- package/src/rules/agent-mode/MIXY.md +21 -0
- package/src/rules/agent-mode/ROUTINE.md +6 -0
- package/src/rules/lead/LEAD.md +9 -4
- package/src/rules/shared/10-tool-workflow.md +2 -1
- package/src/rules/shared/60-verification.md +4 -5
- package/src/rules/shared/70-delivery.md +3 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
- package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
- package/src/runtime/agent/orchestrator/config.mjs +6 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
- package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
- package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
- package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
- package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
- package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
- 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-compat-models.mjs +49 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
- package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
- package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
- package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
- package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
- package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
- package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
- package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
- package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
- package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
- package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
- package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
- package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
- package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
- package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
- package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
- package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
- package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
- package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
- package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
- package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
- package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
- package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/attachments/office-extract.mjs +11 -9
- package/src/runtime/browser-bridge/client.mjs +24 -0
- package/src/runtime/channels/lib/scheduler.mjs +40 -12
- package/src/runtime/channels/lib/schedules-db.mjs +71 -12
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
- package/src/runtime/channels/lib/webhook.mjs +0 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
- package/src/runtime/channels/lib/worker-main.mjs +17 -20
- package/src/runtime/computer-bridge/client.mjs +28 -1
- package/src/runtime/connectors/gmail-app-password.mjs +690 -0
- package/src/runtime/connectors/google-mcp.mjs +446 -0
- package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
- package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
- package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
- package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
- package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
- package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
- package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
- package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
- package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
- package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
- package/src/runtime/memory/lib/memory.mjs +7 -1
- package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
- package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
- package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
- package/src/runtime/memory/tool-defs.mjs +5 -0
- package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
- package/src/runtime/office/core/tabular.mjs +1 -1
- package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
- package/src/runtime/office/portable/docx-formatting.mjs +1 -1
- package/src/runtime/office/portable/docx-runs.mjs +1 -1
- package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
- package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
- package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
- package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
- package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
- package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
- package/src/runtime/office/portable/portable-opc.mjs +27 -13
- package/src/runtime/office/portable/portable-pivot.mjs +3 -3
- package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
- package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
- package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
- package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
- package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
- package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
- package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
- package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
- package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
- package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
- package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
- package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
- package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
- package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
- package/src/runtime/office/portable/portable-xml.mjs +1 -1
- package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
- package/src/runtime/shared/agent-avatar.d.mts +64 -0
- package/src/runtime/shared/agent-avatar.mjs +320 -0
- package/src/runtime/shared/agent-route-config.d.mts +3 -0
- package/src/runtime/shared/agent-route-config.mjs +16 -0
- package/src/runtime/shared/atomic-file.mjs +36 -14
- package/src/runtime/shared/background-tasks.mjs +60 -20
- package/src/runtime/shared/child-spawn-gate.mjs +26 -16
- package/src/runtime/shared/config.mjs +8 -11
- package/src/runtime/shared/connector-accounts.mjs +15 -0
- package/src/runtime/shared/connector-catalog.d.mts +52 -0
- package/src/runtime/shared/connector-catalog.mjs +230 -0
- package/src/runtime/shared/connector-triggers.d.mts +3 -0
- package/src/runtime/shared/connector-triggers.mjs +43 -0
- package/src/runtime/shared/file-lock.mjs +7 -1
- package/src/runtime/shared/file-permissions-worker.mjs +38 -0
- package/src/runtime/shared/file-permissions.d.mts +19 -4
- package/src/runtime/shared/file-permissions.mjs +310 -4
- package/src/runtime/shared/git-runner-worker.mjs +6 -78
- package/src/runtime/shared/google-oauth-client.mjs +24 -0
- package/src/runtime/shared/held-request-host.d.mts +12 -0
- package/src/runtime/shared/held-request-host.mjs +71 -0
- package/src/runtime/shared/llm/http-agent.mjs +7 -3
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
- package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
- package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
- package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
- package/src/runtime/shared/memory-snapshot.mjs +5 -1
- package/src/runtime/shared/notify-trace.mjs +8 -1
- package/src/runtime/shared/plain-preview.d.mts +1 -0
- package/src/runtime/shared/plain-preview.mjs +28 -0
- package/src/runtime/shared/pristine-execution-contract.json +5 -1
- package/src/runtime/shared/process-runner-worker.mjs +7 -0
- package/src/runtime/shared/process-runner.mjs +38 -0
- package/src/runtime/shared/provider-accounts.mjs +2 -1
- package/src/runtime/shared/provider-api-key.mjs +0 -1
- package/src/runtime/shared/read-row-numbers.mjs +1 -1
- package/src/runtime/shared/resource-admission.mjs +60 -18
- package/src/runtime/shared/routine-limits.d.mts +11 -0
- package/src/runtime/shared/routine-limits.mjs +21 -0
- package/src/runtime/shared/routine-trigger.d.mts +10 -0
- package/src/runtime/shared/routine-trigger.mjs +44 -0
- package/src/runtime/shared/run-child.mjs +84 -0
- package/src/runtime/shared/schedule-model-ref.mjs +9 -10
- package/src/runtime/shared/session-activity-signal.mjs +23 -0
- package/src/runtime/shared/shell-output-summary.mjs +23 -0
- package/src/runtime/shared/singleton-owner.mjs +126 -42
- package/src/runtime/shared/slow-span.mjs +4 -12
- package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
- package/src/runtime/shared/tool-output-limit.mjs +4 -2
- package/src/runtime/shared/tool-output-marker.mjs +58 -0
- package/src/runtime/shared/transcript-metadata.mjs +10 -0
- package/src/runtime/shared/turn-changes.mjs +0 -8
- package/src/runtime/shared/usage-readout-totals.mjs +56 -0
- package/src/runtime/shared/user-data-guard.mjs +131 -34
- package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
- package/src/runtime/shared/worktrees/git-head.mjs +20 -2
- package/src/runtime/shared/worktrees/lock.mjs +3 -2
- package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
- package/src/runtime/tidy/runners/biome.mjs +3 -1
- package/src/runtime/tidy/shadow.mjs +2 -1
- package/src/runtime/web-search/index.mjs +5 -7
- package/src/runtime/web-search/lib/formatter.mjs +42 -6
- package/src/runtime/web-search/tool-defs.mjs +6 -1
- package/src/session-runtime/agent-home-delivery.mjs +459 -0
- package/src/session-runtime/agent-profile-changes.mjs +21 -0
- package/src/session-runtime/agent-room-delivery.mjs +92 -0
- package/src/session-runtime/agent-task-sessions.mjs +200 -0
- package/src/session-runtime/boot/apis.mjs +50 -18
- package/src/session-runtime/boot/facade.mjs +5 -8
- package/src/session-runtime/boot/infrastructure.mjs +28 -17
- package/src/session-runtime/boot/tools.mjs +3 -0
- package/src/session-runtime/channel-config-api.mjs +53 -5
- package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
- package/src/session-runtime/held-requests.mjs +228 -0
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
- package/src/session-runtime/internal-tool-executor.mjs +2 -0
- package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
- package/src/session-runtime/lifecycle/shared.mjs +25 -2
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +5 -2
- package/src/session-runtime/lifecycle-api.mjs +3 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
- package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
- package/src/session-runtime/mcp-oauth-login.mjs +112 -15
- package/src/session-runtime/model-route/set-route.mjs +14 -3
- package/src/session-runtime/owned-automation-run.mjs +229 -0
- package/src/session-runtime/provider-auth/login.mjs +10 -6
- package/src/session-runtime/provider-auth/setup.mjs +19 -2
- package/src/session-runtime/provider-catalog-cache.mjs +101 -25
- package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
- package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
- package/src/session-runtime/provider-models.mjs +76 -10
- package/src/session-runtime/provider-readiness.mjs +4 -0
- package/src/session-runtime/resource-api.mjs +5 -0
- package/src/session-runtime/resource-mcp-api.mjs +107 -13
- package/src/session-runtime/resource-skills-api.mjs +12 -0
- package/src/session-runtime/room-member-gate.mjs +48 -0
- package/src/session-runtime/routine-home-turn.mjs +156 -0
- package/src/session-runtime/routine-owner.mjs +48 -0
- package/src/session-runtime/schedule-session-run.mjs +30 -64
- package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
- package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
- package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
- package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
- package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
- package/src/session-runtime/services/agent-tool.mjs +20 -0
- package/src/session-runtime/services/channel-admin.mjs +93 -24
- package/src/session-runtime/services/memory-access.mjs +37 -0
- package/src/session-runtime/services/provider-admin.mjs +166 -59
- package/src/session-runtime/services/schedule-access.mjs +91 -0
- package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
- package/src/session-runtime/services/usage-dashboard.mjs +27 -69
- package/src/session-runtime/session-create/session-options.mjs +34 -7
- package/src/session-runtime/session-hooks.mjs +16 -8
- package/src/session-runtime/setup-tool/executor.mjs +68 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
- package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
- package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
- package/src/session-runtime/skills-api.mjs +16 -1
- package/src/session-runtime/tool-policy-refresh.mjs +238 -29
- package/src/session-runtime/turn/session-ops.mjs +10 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
- package/src/session-runtime/usage-stats-api.mjs +6 -1
- package/src/session-runtime/webhook-session-run.mjs +49 -67
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
- package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
- package/src/session-runtime/workflow-agents-api.mjs +4 -1
- package/src/standalone/channel-transport/http-routes.mjs +10 -9
- package/src/standalone/daemon-agent-control.mjs +1 -8
- package/src/standalone/daemon-desktop-runtime.mjs +20 -7
- package/src/standalone/daemon-owner.mjs +117 -25
- package/src/standalone/daemon-session-control.mjs +11 -2
- package/src/standalone/daemon.mjs +61 -17
- package/src/standalone/session-call-cache.mjs +6 -0
- package/src/standalone/session-protocol.mjs +28 -1
- package/src/standalone/session-service/retention/busy.mjs +7 -2
- package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
- package/src/standalone/session-service.mjs +19 -0
- package/src/standalone/session-transport/routes.mjs +7 -6
- package/src/tui/app/model-options.mjs +39 -2
- package/src/tui/app/model-picker.mjs +16 -2
- package/src/tui/app/provider-setup-picker.mjs +1 -2
- package/src/tui/app/slash-commands.mjs +20 -0
- package/src/tui/dist/index.mjs +487 -128
- package/src/tui/session/context-state.mjs +6 -1
- package/src/tui/session/notice-surface.mjs +35 -4
- package/src/tui/session/notice-text.mjs +0 -1
- package/src/tui/session/queue-helpers.mjs +2 -1
- package/src/tui/session/session-api/extensions.mjs +4 -0
- package/src/tui/session/session-api/intake/submission.mjs +12 -13
- package/src/tui/session/session-api/integrations.mjs +9 -5
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
- package/src/tui/session/session-api/lifecycle.mjs +17 -1
- package/src/tui/session/session-api/routes.mjs +48 -3
- package/src/tui/session/session-flow/drain.mjs +20 -3
- package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
- package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
- package/src/tui/session/session-flow/steering.mjs +104 -24
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/tool-approval.mjs +26 -5
- package/src/tui/session/transcript-intake.mjs +6 -2
- package/src/tui/session/transcript-spill.mjs +5 -1
- package/src/tui/session/tui-steering-persist.mjs +65 -42
- package/src/tui/session/turn-live-call.mjs +56 -0
- package/src/tui/session/turn-stream.mjs +33 -0
- package/src/tui/session/turn.mjs +50 -7
- package/src/ui/statusline.mjs +1 -6
- package/src/ui/streaming-markdown-heal.mjs +24 -7
- package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
- package/src/workflows/default/WORKFLOW.md +9 -13
- package/src/session-runtime/automation-agents.mjs +0 -94
- package/src/session-runtime/automation-workflow.mjs +0 -59
|
@@ -9,15 +9,17 @@
|
|
|
9
9
|
// tree (taskkill /T /F on Windows, SIGTERM->SIGKILL of every descendant on
|
|
10
10
|
// POSIX). No external dependencies.
|
|
11
11
|
import { spawn } from 'node:child_process';
|
|
12
|
+
import { runProcessOffThread } from '../../../shared/process-runner.mjs';
|
|
12
13
|
import { detachedSpawnOpts } from '../../../shared/spawn-flags.mjs';
|
|
13
14
|
|
|
14
15
|
const isWin = process.platform === 'win32';
|
|
15
16
|
|
|
17
|
+
// The SIGTERM → SIGKILL escalation wait. Kept ref'd: it runs right after the
|
|
18
|
+
// tree was signalled, when the dying children may be the only handles left,
|
|
19
|
+
// and an unref'd timer would then never fire, leaving the awaited kill (and
|
|
20
|
+
// every close awaiting it) pending forever. It is short and bounded.
|
|
16
21
|
function delay(ms) {
|
|
17
|
-
return new Promise((resolve) =>
|
|
18
|
-
const t = setTimeout(resolve, ms);
|
|
19
|
-
if (t.unref) t.unref();
|
|
20
|
-
});
|
|
22
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
// Best-effort signal: a pid that already exited (or cannot be signalled) is skipped.
|
|
@@ -44,54 +46,26 @@ function transportPid(transport) {
|
|
|
44
46
|
* settled, so enumerators never pile up whatever this value is. */
|
|
45
47
|
export const ENUMERATION_DEADLINE_MS = 20_000;
|
|
46
48
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (strict) {
|
|
66
|
-
reject(error);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
resolve('');
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
cp.stdout?.on('data', (d) => {
|
|
73
|
-
out += String(d);
|
|
74
|
-
});
|
|
75
|
-
cp.on('error', (error) => settle(() => (strict ? reject(error) : resolve(out))));
|
|
76
|
-
cp.on('close', (code) =>
|
|
77
|
-
settle(() => (strict && code !== 0 ? reject(new Error(`Process enumeration failed (${code}).`)) : resolve(out)))
|
|
78
|
-
);
|
|
79
|
-
if (timeoutMs > 0) {
|
|
80
|
-
timer = setTimeout(() => {
|
|
81
|
-
try {
|
|
82
|
-
cp.kill('SIGKILL');
|
|
83
|
-
} catch {
|
|
84
|
-
/* already gone */
|
|
85
|
-
}
|
|
86
|
-
cp.stdout?.destroy?.();
|
|
87
|
-
cp.stderr?.destroy?.();
|
|
88
|
-
settle(() =>
|
|
89
|
-
strict ? reject(new Error(`Process enumeration timed out after ${timeoutMs} ms.`)) : resolve('')
|
|
90
|
-
);
|
|
91
|
-
}, timeoutMs);
|
|
92
|
-
timer.unref?.();
|
|
49
|
+
// Output bound of one helper command: a process table is tens of KB.
|
|
50
|
+
const HELPER_MAX_BYTES = 64 * 1024 * 1024;
|
|
51
|
+
|
|
52
|
+
// Run a helper command off the event loop (process-runner.mjs: under load one
|
|
53
|
+
// PowerShell start blocked the daemon's loop for seconds) and resolve its
|
|
54
|
+
// stdout. Past `timeoutMs` (default ENUMERATION_DEADLINE_MS) the helper is
|
|
55
|
+
// killed and the call settles. Strict: a failed, non-zero or timed-out run
|
|
56
|
+
// rejects; otherwise it resolves '' (non-zero exits keep their stdout).
|
|
57
|
+
function run(cmd, args, { strict = false, timeoutMs = ENUMERATION_DEADLINE_MS } = {}) {
|
|
58
|
+
return runProcessOffThread(cmd, args, { timeoutMs, maxBytes: HELPER_MAX_BYTES }).then(
|
|
59
|
+
({ code, stdout }) => {
|
|
60
|
+
if (strict && code !== 0) throw new Error(`Process enumeration failed (${code}).`);
|
|
61
|
+
return stdout;
|
|
62
|
+
},
|
|
63
|
+
(error) => {
|
|
64
|
+
if (!strict) return '';
|
|
65
|
+
if (error?.code === 'ETIMEDOUT') throw new Error(`Process enumeration timed out after ${timeoutMs} ms.`);
|
|
66
|
+
throw error;
|
|
93
67
|
}
|
|
94
|
-
|
|
68
|
+
);
|
|
95
69
|
}
|
|
96
70
|
|
|
97
71
|
// Snapshot the whole process table once: parent map + a per-pid identity
|
|
@@ -114,21 +88,25 @@ async function enumerate({ strict = false } = {}) {
|
|
|
114
88
|
],
|
|
115
89
|
{ strict, timeoutMs: ENUMERATION_DEADLINE_MS }
|
|
116
90
|
)
|
|
117
|
-
: await run('ps', ['-A', '-o', 'pid=,ppid=,pgid=,comm=,lstart='], {
|
|
91
|
+
: await run('ps', ['-A', '-o', 'pid=,ppid=,pgid=,stat=,comm=,lstart='], {
|
|
118
92
|
strict,
|
|
119
93
|
timeoutMs: ENUMERATION_DEADLINE_MS,
|
|
120
94
|
});
|
|
121
|
-
const row = isWin ? /^(\d+)\s+(\d+)\s+()(\S+)(?:\s+(.+))?$/ : /^(\d+)\s+(\d+)\s+(\d+)\s+(\S+)(?:\s+(.+))?$/;
|
|
95
|
+
const row = isWin ? /^(\d+)\s+(\d+)\s+()()(\S+)(?:\s+(.+))?$/ : /^(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)(?:\s+(.+))?$/;
|
|
122
96
|
for (const line of out.split(/\r?\n/)) {
|
|
123
97
|
const m = line.trim().match(row);
|
|
124
98
|
if (!m) continue;
|
|
99
|
+
// A zombie runs nothing and holds no port or file; it only waits to be
|
|
100
|
+
// reaped (forever under an init that does not reap, e.g. a container
|
|
101
|
+
// without one). Counting it alive would hold a dead tree as running.
|
|
102
|
+
if (m[4].startsWith('Z')) continue;
|
|
125
103
|
const pid = Number(m[1]);
|
|
126
104
|
const ppid = Number(m[2]);
|
|
127
|
-
const token = (m[
|
|
105
|
+
const token = (m[6] || '').trim() || null;
|
|
128
106
|
if (!childrenOf.has(ppid)) childrenOf.set(ppid, []);
|
|
129
107
|
childrenOf.get(ppid).push(pid);
|
|
130
108
|
tokenOf.set(pid, token);
|
|
131
|
-
nameOf.set(pid, m[
|
|
109
|
+
nameOf.set(pid, m[5]);
|
|
132
110
|
if (m[3]) pgidOf.set(pid, Number(m[3]));
|
|
133
111
|
}
|
|
134
112
|
if (strict && !nameOf.has(process.pid)) throw new Error('The process snapshot could not be verified.');
|
|
@@ -142,17 +120,33 @@ async function enumerate({ strict = false } = {}) {
|
|
|
142
120
|
* `shared: true` (pre-kill captures) joins a read already in flight, so many
|
|
143
121
|
* jobs promoted together do not each spawn an enumerator; a capture taken
|
|
144
122
|
* while its root runs is valid whenever that read began. An observation after
|
|
145
|
-
* a kill
|
|
146
|
-
*
|
|
123
|
+
* a kill or exit must see a table taken AFTER it asked: a read that began
|
|
124
|
+
* before the kill would report the killed tree alive and defer the
|
|
125
|
+
* confirmation by a whole re-check. Such a caller starts a read when none
|
|
126
|
+
* runs, else joins the one queued to begin when the running read settles —
|
|
127
|
+
* every observer asking meanwhile shares it. At most one enumerator runs at
|
|
128
|
+
* a time, however many shells settle or watch at once (each one is a
|
|
129
|
+
* PowerShell process on Windows; one per settle and watch pass compounded
|
|
130
|
+
* machine load into multi-second daemon stalls). */
|
|
147
131
|
let tableInFlight = null;
|
|
132
|
+
let nextTable = null;
|
|
133
|
+
function startTableRead() {
|
|
134
|
+
const read = enumerate({ strict: true }).finally(() => {
|
|
135
|
+
if (tableInFlight === read) tableInFlight = null;
|
|
136
|
+
});
|
|
137
|
+
tableInFlight = read;
|
|
138
|
+
return read;
|
|
139
|
+
}
|
|
148
140
|
export function readProcessTable({ shared = false } = {}) {
|
|
149
|
-
if (
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
141
|
+
if (shared && tableInFlight) return tableInFlight;
|
|
142
|
+
if (nextTable) return nextTable;
|
|
143
|
+
if (!tableInFlight) return startTableRead();
|
|
144
|
+
const settled = () => {};
|
|
145
|
+
nextTable = tableInFlight.then(settled, settled).then(() => {
|
|
146
|
+
nextTable = null;
|
|
147
|
+
return startTableRead();
|
|
148
|
+
});
|
|
149
|
+
return nextTable;
|
|
156
150
|
}
|
|
157
151
|
|
|
158
152
|
// Walk every descendant pid of rootPid (excludes rootPid).
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { readServicePort } from '../../../shared/service-discovery.mjs';
|
|
7
7
|
import { createMcpOAuth } from './oauth.mjs';
|
|
8
|
+
import { mcpOAuthClientFor } from './oauth-client.mjs';
|
|
9
|
+
import { googleMcpService, withGoogleMcp } from '../../../connectors/google-mcp.mjs';
|
|
10
|
+
import { gmailAppPasswordService, withGmailAppPasswordMcp } from '../../../connectors/gmail-app-password.mjs';
|
|
8
11
|
import {
|
|
9
12
|
AUTO_DETECT_PORTS,
|
|
10
13
|
expandEnvVars,
|
|
@@ -138,12 +141,18 @@ export function createMcpTransport({ name, cfg, sdk, log, strictEnv, oauthOption
|
|
|
138
141
|
: createMcpOAuth({
|
|
139
142
|
url,
|
|
140
143
|
serverName: name,
|
|
144
|
+
oauthClient: mcpOAuthClientFor(cfg, url),
|
|
141
145
|
...oauthOptions,
|
|
146
|
+
// A built-in Google connector is answered in-process.
|
|
147
|
+
...(googleMcpService(url) ? { fetchImpl: withGoogleMcp(oauthOptions?.fetchImpl) } : {}),
|
|
142
148
|
// SSE advertises its POST endpoint on the authenticated event stream.
|
|
143
149
|
// No other path on the origin is allowed to receive this server's token.
|
|
144
150
|
isTransportEndpoint: (input) => kind === 'sse' && transport?._endpoint?.href === new URL(input).href,
|
|
145
151
|
});
|
|
146
|
-
|
|
152
|
+
// Gmail with an app password is answered in-process too (its account
|
|
153
|
+
// header is the configured authentication; the password is in the keychain).
|
|
154
|
+
const local = !oauth && gmailAppPasswordService(url) ? { ...opts, fetch: withGmailAppPasswordMcp() } : opts;
|
|
155
|
+
transport = new Transport(new URL(url), oauth ? { fetch: oauth.fetch } : local);
|
|
147
156
|
if (oauth) {
|
|
148
157
|
transport.oauth = oauth;
|
|
149
158
|
const close = transport.close.bind(transport);
|
|
@@ -14,7 +14,7 @@ import { basename, join, resolve } from 'node:path';
|
|
|
14
14
|
import { pathToFileURL } from 'node:url';
|
|
15
15
|
import { tmpdir } from 'node:os';
|
|
16
16
|
import { createHash, randomUUID } from 'node:crypto';
|
|
17
|
-
import {
|
|
17
|
+
import { smartReadTruncate } from '../tools/builtin/read-formatting.mjs';
|
|
18
18
|
import { shutdownStdioChild, killStdioChildTreeFast } from './child-tree.mjs';
|
|
19
19
|
import { makeToolEnvelope, normalizeToolEnvelope } from '../session/tool-envelope.mjs';
|
|
20
20
|
import { classifyResultKind } from '../session/result-classification.mjs';
|
|
@@ -33,11 +33,17 @@ import {
|
|
|
33
33
|
import { createLazyOutputValidator } from './lazy-output-validator.mjs';
|
|
34
34
|
import { appendMcpStderrTail, createMcpTransport, hasConfiguredMcpAuth } from './client-transport.mjs';
|
|
35
35
|
import { listAllMcpTools, MCP_CONNECT_CANCELLED, runBoundedHandshake } from './client-handshake.mjs';
|
|
36
|
+
import { rememberServerConnector } from './server-connectors.mjs';
|
|
37
|
+
import { mcpOAuthClientFor } from './oauth-client.mjs';
|
|
38
|
+
import { googleMcpService, withGoogleMcp } from '../../../connectors/google-mcp.mjs';
|
|
36
39
|
import {
|
|
37
40
|
clearMcpOAuth,
|
|
41
|
+
commitMcpOAuthSwitch,
|
|
38
42
|
hasStoredMcpOAuthTokens,
|
|
39
43
|
isMcpOAuthReauthenticationRequired,
|
|
40
44
|
MCP_OAUTH_CALLBACK_NOT_PENDING,
|
|
45
|
+
startMcpOAuthSwitch,
|
|
46
|
+
storedMcpOAuthIdentity,
|
|
41
47
|
} from './oauth.mjs';
|
|
42
48
|
export { probeMcpServerAuth } from './oauth-probe.mjs';
|
|
43
49
|
|
|
@@ -67,6 +73,14 @@ const _connectAbortGenerations = new Map();
|
|
|
67
73
|
const _pendingConnects = new Set();
|
|
68
74
|
// scopeId -> root path advertised by that scope's scope-following servers.
|
|
69
75
|
const _scopeRoots = new Map();
|
|
76
|
+
// One stdio process per server config, shared by every scope that connects
|
|
77
|
+
// the same server with the same resolved config: share key -> connection
|
|
78
|
+
// { key, client, transport, liveness, stderrTail, warnings, tools,
|
|
79
|
+
// capabilities, instructions, users: Set<registryKey> }. Every session
|
|
80
|
+
// runtime has its own scope, so per-scope processes multiplied with the
|
|
81
|
+
// number of retained sessions. A scope's close releases only its own use; the
|
|
82
|
+
// process tree is shut down when its last user releases it.
|
|
83
|
+
const _sharedStdio = new Map();
|
|
70
84
|
let mcpSdkPromise = null;
|
|
71
85
|
// Memo for mcpToolHasField(name, field) — keyed by `${scopeId}|${toolName}|${field}`.
|
|
72
86
|
// The lookup (regex parse + servers Map get + tools.find + schema property
|
|
@@ -99,6 +113,24 @@ function scopedServerEntries(scopeId) {
|
|
|
99
113
|
const normalized = normalizeMcpScopeId(scopeId);
|
|
100
114
|
return [...servers.entries()].filter(([, server]) => server?.scopeId === normalized);
|
|
101
115
|
}
|
|
116
|
+
// Share key of a stdio server, or null when its connection must stay private
|
|
117
|
+
// to one scope: remote transports, and servers whose advertised roots follow
|
|
118
|
+
// their scope's cwd.
|
|
119
|
+
function sharedStdioKey(name, cfg) {
|
|
120
|
+
if (cfg?._mixdogRootsFollowScope === true) return null;
|
|
121
|
+
try {
|
|
122
|
+
if (resolveMcpTransportKind(cfg) !== 'stdio') return null;
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
return `${String(name || '')}\u0000${JSON.stringify(cfg)}`;
|
|
127
|
+
}
|
|
128
|
+
// A connection that failed a call is never handed to a later connect; scopes
|
|
129
|
+
// still holding it keep it until they release it themselves.
|
|
130
|
+
function retireSharedConnection(server) {
|
|
131
|
+
const shared = server?.shared;
|
|
132
|
+
if (shared && _sharedStdio.get(shared.key) === shared) _sharedStdio.delete(shared.key);
|
|
133
|
+
}
|
|
102
134
|
function currentConnectAbortGeneration(scopeId) {
|
|
103
135
|
return _connectAbortGenerations.get(normalizeMcpScopeId(scopeId)) || 0;
|
|
104
136
|
}
|
|
@@ -308,7 +340,11 @@ function callAdmissionFor(server) {
|
|
|
308
340
|
const cfg = server?.cfg || {};
|
|
309
341
|
gate = createOwnerFairGate({
|
|
310
342
|
name: `MCP ${server.name}`,
|
|
311
|
-
|
|
343
|
+
// JSON-RPC multiplexes requests by id over one transport, so the client
|
|
344
|
+
// imposes no in-flight cap of its own; a server that needs one declares
|
|
345
|
+
// it (maxConcurrency) or the operator sets MIXDOG_MCP_MAX_INFLIGHT.
|
|
346
|
+
activeMax:
|
|
347
|
+
positiveInt(cfg.maxConcurrency ?? cfg.max_concurrency ?? process.env.MIXDOG_MCP_MAX_INFLIGHT, null) ?? Infinity,
|
|
312
348
|
queueMax: positiveInt(cfg.maxQueue ?? cfg.max_queue ?? process.env.MIXDOG_MCP_MAX_QUEUE, 256),
|
|
313
349
|
minOwnerQueue: 8,
|
|
314
350
|
waitTimeoutMs: positiveInt(
|
|
@@ -407,6 +443,7 @@ async function reconnectMcpServer(scopeId, serverName, failedServer) {
|
|
|
407
443
|
if (!current || genAtStart !== currentConnectAbortGeneration(scopeId) || current.liveness?.closing) {
|
|
408
444
|
throw new Error(`MCP server "${serverName}" was disconnected`);
|
|
409
445
|
}
|
|
446
|
+
retireSharedConnection(current);
|
|
410
447
|
await replaceServerConnection(scopeId, serverName, current, failedServer.cfg, genAtStart);
|
|
411
448
|
});
|
|
412
449
|
const replacement = servers.get(registryKey);
|
|
@@ -1037,7 +1074,7 @@ function capMcpOutputParts(content) {
|
|
|
1037
1074
|
const s = typeof content === 'string' ? content : String(content ?? '');
|
|
1038
1075
|
const bodyBytes = Buffer.byteLength(s, 'utf8');
|
|
1039
1076
|
const bodyLines = countTextLines(s);
|
|
1040
|
-
const { text, truncated } = smartReadTruncate(s, bodyLines, bodyBytes);
|
|
1077
|
+
const { text, truncated, marker } = smartReadTruncate(s, bodyLines, bodyBytes);
|
|
1041
1078
|
if (!truncated) return { text, spillNote: '', truncated: false };
|
|
1042
1079
|
// Spill the full body to a tmp file so the caller can recover content
|
|
1043
1080
|
// elided by the head/tail cap.
|
|
@@ -1056,7 +1093,6 @@ function capMcpOutputParts(content) {
|
|
|
1056
1093
|
/* spill best-effort */
|
|
1057
1094
|
}
|
|
1058
1095
|
const spillNote = spillPath ? `\n\n... [full output spilled to ${spillPath}] ...` : '';
|
|
1059
|
-
const marker = buildSmartReadTruncationMarker(bodyLines, bodyBytes);
|
|
1060
1096
|
const split = text.indexOf(`\n${marker}\n`);
|
|
1061
1097
|
return {
|
|
1062
1098
|
text,
|
|
@@ -1220,22 +1256,52 @@ export function getMcpServerOAuthState(cfg) {
|
|
|
1220
1256
|
return null;
|
|
1221
1257
|
}
|
|
1222
1258
|
if (!url || hasConfiguredMcpAuth(cfg)) return null;
|
|
1223
|
-
|
|
1259
|
+
const signedIn = hasStoredMcpOAuthTokens(url);
|
|
1260
|
+
if (!signedIn) return { signedIn };
|
|
1261
|
+
// Who it is signed in as (never guessed: empty when the server named nobody)
|
|
1262
|
+
// and whether 계정 바꾸기 can make the provider show its account choice.
|
|
1263
|
+
const who = storedMcpOAuthIdentity(url);
|
|
1264
|
+
return {
|
|
1265
|
+
signedIn,
|
|
1266
|
+
account: typeof who?.account === 'string' ? who.account : '',
|
|
1267
|
+
workspace: typeof who?.workspace === 'string' ? who.workspace : '',
|
|
1268
|
+
forcedAccountChoice: who?.forcedChoice === true,
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
/** 계정 바꾸기 of an HTTP/SSE server: a browser sign-in beside the stored one
|
|
1272
|
+
* (oauth.mjs startMcpOAuthSwitch); `launch` opens the authorization URL. */
|
|
1273
|
+
export function startMcpServerOAuthSwitch(cfg, launch, serverName) {
|
|
1274
|
+
const url = resolveMcpOAuthUrl(cfg);
|
|
1275
|
+
if (!url) throw new Error(`MCP server "${serverName}" does not use OAuth sign-in`);
|
|
1276
|
+
return startMcpOAuthSwitch({
|
|
1277
|
+
url,
|
|
1278
|
+
launch,
|
|
1279
|
+
serverName,
|
|
1280
|
+
oauthClient: mcpOAuthClientFor(cfg, url),
|
|
1281
|
+
...(googleMcpService(url) ? { fetchImpl: withGoogleMcp() } : {}),
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
/** Store a switch's sign-in for the server's URL; resolves its rollback. */
|
|
1285
|
+
export function commitMcpServerOAuthSwitch(cfg, signIn) {
|
|
1286
|
+
return commitMcpOAuthSwitch({ url: resolveMcpOAuthUrl(cfg), signIn });
|
|
1224
1287
|
}
|
|
1225
1288
|
/**
|
|
1226
1289
|
* Log out of an HTTP/SSE server URL: delete the OAuth tokens and client
|
|
1227
1290
|
* registration stored for it, shared by every server configured with that
|
|
1228
|
-
* exact URL, and drop them from live connections.
|
|
1291
|
+
* exact URL, and drop them from live connections. `options.revoke` also
|
|
1292
|
+
* revokes the grant where the authorization server documents how; resolves
|
|
1293
|
+
* `{ revoked }`.
|
|
1229
1294
|
*/
|
|
1230
|
-
export async function clearMcpServerOAuth(cfg) {
|
|
1295
|
+
export async function clearMcpServerOAuth(cfg, options = {}) {
|
|
1231
1296
|
let url;
|
|
1232
1297
|
try {
|
|
1233
1298
|
url = resolveMcpOAuthUrl(cfg);
|
|
1234
1299
|
} catch {
|
|
1235
1300
|
// A config without a valid remote URL never connected, so never logged in.
|
|
1236
|
-
return;
|
|
1301
|
+
return { revoked: false };
|
|
1237
1302
|
}
|
|
1238
|
-
if (url)
|
|
1303
|
+
if (url) return clearMcpOAuth({ url, revoke: options.revoke === true });
|
|
1304
|
+
return { revoked: false };
|
|
1239
1305
|
}
|
|
1240
1306
|
/**
|
|
1241
1307
|
* Close a single server: for stdio transports first shut down the full child
|
|
@@ -1245,6 +1311,13 @@ export async function clearMcpServerOAuth(cfg) {
|
|
|
1245
1311
|
* only kills the direct child and discards the pid we need to walk the tree.
|
|
1246
1312
|
*/
|
|
1247
1313
|
async function _closeServer(server) {
|
|
1314
|
+
// A shared stdio connection closes only when its last scope releases it.
|
|
1315
|
+
const shared = server?.shared;
|
|
1316
|
+
if (shared) {
|
|
1317
|
+
shared.users.delete(server.registryKey);
|
|
1318
|
+
if (shared.users.size > 0) return;
|
|
1319
|
+
retireSharedConnection(server);
|
|
1320
|
+
}
|
|
1248
1321
|
// A deliberate close is not a crash: suppress the unexpected-close report.
|
|
1249
1322
|
if (server?.liveness) server.liveness.closing = true;
|
|
1250
1323
|
const transport = server?.transport;
|
|
@@ -1263,6 +1336,19 @@ async function _closeServer(server) {
|
|
|
1263
1336
|
/* ignore */
|
|
1264
1337
|
}
|
|
1265
1338
|
}
|
|
1339
|
+
// Register `name` in `scopeId` on the live shared connection for `shareKey`,
|
|
1340
|
+
// when there is one. Returns whether it did.
|
|
1341
|
+
function adoptSharedConnection(shareKey, name, cfg, scopeId, genAtStart) {
|
|
1342
|
+
const shared = shareKey ? _sharedStdio.get(shareKey) : null;
|
|
1343
|
+
if (!shared || shared.liveness.alive === false || shared.liveness.closing) return false;
|
|
1344
|
+
const registryKey = mcpServerRegistryKey(scopeId, name);
|
|
1345
|
+
const { key: _key, users, ...connection } = shared;
|
|
1346
|
+
users.add(registryKey);
|
|
1347
|
+
rememberServerConnector(name, cfg);
|
|
1348
|
+
servers.set(registryKey, { scopeId, registryKey, name, ...connection, cfg, shared, generation: genAtStart });
|
|
1349
|
+
_invalidateMcpToolFieldMemo();
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1266
1352
|
// Connects whose handshake has not finished yet: disconnectAll() must be able
|
|
1267
1353
|
// to see (and tear down) their transports, because `servers` only lists fully
|
|
1268
1354
|
// handshaken entries. Generation token aborts a connect that outlives a
|
|
@@ -1282,6 +1368,8 @@ async function connectServer(
|
|
|
1282
1368
|
// abort before creating a transport/child at all.
|
|
1283
1369
|
throw new Error(`MCP server "${name}" connect aborted by shutdown`);
|
|
1284
1370
|
}
|
|
1371
|
+
const shareKey = sharedStdioKey(name, cfg);
|
|
1372
|
+
if (adoptSharedConnection(shareKey, name, cfg, scopeId, genAtStart)) return;
|
|
1285
1373
|
const client = createMcpClient(sdk, name, cfg, scopeId);
|
|
1286
1374
|
const { transport, autoDetectAdvert, stderrTail, warnings } = createMcpTransport({
|
|
1287
1375
|
name,
|
|
@@ -1326,26 +1414,42 @@ async function connectServer(
|
|
|
1326
1414
|
}
|
|
1327
1415
|
throw new Error(`MCP server "${name}" connect aborted by shutdown`);
|
|
1328
1416
|
}
|
|
1417
|
+
// Another scope finished connecting the same server meanwhile: use its
|
|
1418
|
+
// process and release this one.
|
|
1419
|
+
if (adoptSharedConnection(shareKey, name, cfg, scopeId, genAtStart)) {
|
|
1420
|
+
await _closeServer({ client, transport, liveness });
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1329
1423
|
const tools = mcpServerTools(name, capabilities, toolsResult.tools, cfg);
|
|
1330
1424
|
const registryKey = mcpServerRegistryKey(scopeId, name);
|
|
1331
|
-
|
|
1332
|
-
scopeId,
|
|
1333
|
-
registryKey,
|
|
1334
|
-
name,
|
|
1425
|
+
const connection = {
|
|
1335
1426
|
client,
|
|
1336
1427
|
transport,
|
|
1337
1428
|
tools,
|
|
1338
|
-
cfg,
|
|
1339
1429
|
instructions,
|
|
1340
1430
|
capabilities,
|
|
1341
1431
|
liveness,
|
|
1342
1432
|
stderrTail,
|
|
1343
1433
|
warnings: warnings || [],
|
|
1434
|
+
};
|
|
1435
|
+
const shared = shareKey ? { ...connection, key: shareKey, users: new Set([registryKey]) } : null;
|
|
1436
|
+
if (shared) _sharedStdio.set(shareKey, shared);
|
|
1437
|
+
rememberServerConnector(name, cfg);
|
|
1438
|
+
servers.set(registryKey, {
|
|
1439
|
+
scopeId,
|
|
1440
|
+
registryKey,
|
|
1441
|
+
name,
|
|
1442
|
+
...connection,
|
|
1443
|
+
cfg,
|
|
1444
|
+
shared,
|
|
1344
1445
|
generation: genAtStart,
|
|
1345
1446
|
});
|
|
1346
1447
|
if (capabilities?.tools?.listChanged && sdk.ToolListChangedNotificationSchema) {
|
|
1448
|
+
// A shared connection refreshes the tools of every scope using it.
|
|
1347
1449
|
client.setNotificationHandler(sdk.ToolListChangedNotificationSchema, () =>
|
|
1348
|
-
|
|
1450
|
+
Promise.all(
|
|
1451
|
+
[...(shared?.users ?? [registryKey])].map((key) => refreshServerTools(key, client, name, cfg, capabilities))
|
|
1452
|
+
)
|
|
1349
1453
|
);
|
|
1350
1454
|
}
|
|
1351
1455
|
_invalidateMcpToolFieldMemo();
|
|
@@ -1514,6 +1618,8 @@ async function runToolRefresh(registryKey, live, name, cfg, capabilities) {
|
|
|
1514
1618
|
}
|
|
1515
1619
|
if (servers.get(registryKey) !== live) return;
|
|
1516
1620
|
live.tools = mcpServerTools(name, capabilities, refreshed.tools, cfg);
|
|
1621
|
+
// A scope adopting the connection later starts from the current listing.
|
|
1622
|
+
if (live.shared) live.shared.tools = live.tools;
|
|
1517
1623
|
_invalidateMcpToolFieldMemo();
|
|
1518
1624
|
} catch (error) {
|
|
1519
1625
|
mcpLog(
|
|
@@ -1567,6 +1673,7 @@ export function _registerMcpServerForTest(scopeId, name, rawTools = [], options
|
|
|
1567
1673
|
generation: currentConnectAbortGeneration(normalizedScopeId),
|
|
1568
1674
|
};
|
|
1569
1675
|
_knownMcpScopes.add(normalizedScopeId);
|
|
1676
|
+
rememberServerConnector(name, server.cfg);
|
|
1570
1677
|
servers.set(registryKey, server);
|
|
1571
1678
|
_invalidateMcpToolFieldMemo();
|
|
1572
1679
|
return server;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// The account side of an MCP OAuth sign-in: which documented authorization
|
|
2
|
+
// parameter makes a provider show its account choice, and who a sign-in
|
|
3
|
+
// belongs to, read only from what the authorization server returned.
|
|
4
|
+
//
|
|
5
|
+
// Parameters are sent only where the provider documents them (keyed by the
|
|
6
|
+
// authorization endpoint's host); everywhere else nothing is added:
|
|
7
|
+
// Google prompt=select_account developers.google.com/identity/protocols/oauth2/web-server#creatingclient
|
|
8
|
+
// GitHub prompt=select_account docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
|
|
9
|
+
// Atlassian (Jira, Confluence, Trello) prompt=consent developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/
|
|
10
|
+
// Linear prompt=consent linear.app/developers/oauth-2-0-authentication
|
|
11
|
+
// Dropbox force_reapprove=true www.dropbox.com/developers/documentation/http/documentation#oauth2-authorize
|
|
12
|
+
|
|
13
|
+
const SELECT_ACCOUNT = Object.freeze({ prompt: 'select_account' });
|
|
14
|
+
// A normal connect shows the chooser only where it is the documented choice.
|
|
15
|
+
const CONNECT_PARAMS = Object.freeze({
|
|
16
|
+
'accounts.google.com': SELECT_ACCOUNT,
|
|
17
|
+
'github.com': SELECT_ACCOUNT,
|
|
18
|
+
});
|
|
19
|
+
// 계정 바꾸기: the provider's documented way to force the choice (or the grant).
|
|
20
|
+
const SWITCH_PARAMS = Object.freeze({
|
|
21
|
+
...CONNECT_PARAMS,
|
|
22
|
+
'auth.atlassian.com': Object.freeze({ prompt: 'consent' }),
|
|
23
|
+
'linear.app': Object.freeze({ prompt: 'consent' }),
|
|
24
|
+
'mcp.linear.app': Object.freeze({ prompt: 'consent' }),
|
|
25
|
+
'www.dropbox.com': Object.freeze({ force_reapprove: 'true' }),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const hostOf = (url) => {
|
|
29
|
+
try {
|
|
30
|
+
return new URL(url).host.toLowerCase();
|
|
31
|
+
} catch {
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** `authorizationUrl` with the account parameters of `mode` (`connect`,
|
|
37
|
+
* `reauth`, `switch`); a repair (`reauth`) adds nothing. A `prompt` the
|
|
38
|
+
* request already carries (the SDK's `consent` for offline access) is kept. */
|
|
39
|
+
export function withAccountParams(authorizationUrl, mode = 'connect') {
|
|
40
|
+
const target = new URL(authorizationUrl);
|
|
41
|
+
const table = mode === 'switch' ? SWITCH_PARAMS : mode === 'connect' ? CONNECT_PARAMS : {};
|
|
42
|
+
const params = table[target.host.toLowerCase()];
|
|
43
|
+
if (!params) return target;
|
|
44
|
+
for (const [key, value] of Object.entries(params)) {
|
|
45
|
+
if (key !== 'prompt') {
|
|
46
|
+
target.searchParams.set(key, value);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const values = new Set((target.searchParams.get('prompt') || '').split(' ').filter(Boolean));
|
|
50
|
+
values.add(value);
|
|
51
|
+
target.searchParams.set('prompt', [...values].join(' '));
|
|
52
|
+
}
|
|
53
|
+
return target;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Whether 계정 바꾸기 can make the provider of this authorization endpoint
|
|
57
|
+
* show its choice (otherwise the user switches at the provider first). */
|
|
58
|
+
export function canForceAccountChoice(authorizationEndpoint) {
|
|
59
|
+
return Object.hasOwn(SWITCH_PARAMS, hostOf(authorizationEndpoint));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const text = (value) => (typeof value === 'string' && value.trim() ? value.trim() : '');
|
|
63
|
+
|
|
64
|
+
function idTokenClaims(idToken) {
|
|
65
|
+
const payload = typeof idToken === 'string' ? idToken.split('.')[1] : '';
|
|
66
|
+
if (!payload) return {};
|
|
67
|
+
try {
|
|
68
|
+
const claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
|
|
69
|
+
return claims && typeof claims === 'object' ? claims : {};
|
|
70
|
+
} catch {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** `{ account, workspace }` from a token endpoint response: the OIDC id_token
|
|
76
|
+
* claims and the identity fields providers return beside the tokens (Notion's
|
|
77
|
+
* workspace and owner, Slack's team, a `user` / `organization` object). Null
|
|
78
|
+
* when the response names nobody: an identity is never guessed. */
|
|
79
|
+
export function identityFromTokenResponse(body) {
|
|
80
|
+
if (!body || typeof body !== 'object') return null;
|
|
81
|
+
const claims = idTokenClaims(body.id_token);
|
|
82
|
+
const user = body.user && typeof body.user === 'object' ? body.user : {};
|
|
83
|
+
const person = body.owner?.user?.person;
|
|
84
|
+
const account =
|
|
85
|
+
text(claims.email) ||
|
|
86
|
+
text(claims.preferred_username) ||
|
|
87
|
+
text(body.email) ||
|
|
88
|
+
text(user.email) ||
|
|
89
|
+
text(user.login) ||
|
|
90
|
+
text(user.username) ||
|
|
91
|
+
text(person?.email) ||
|
|
92
|
+
text(body.owner?.user?.name);
|
|
93
|
+
const workspace =
|
|
94
|
+
text(body.workspace_name) || text(body.team?.name) || text(body.organization?.name) || text(body.org?.name);
|
|
95
|
+
if (!account && !workspace) return null;
|
|
96
|
+
return { account, workspace };
|
|
97
|
+
}
|
|
@@ -70,8 +70,7 @@ export function answerOAuthCallback(req, res, { redirectUrl, state, finish, serv
|
|
|
70
70
|
});
|
|
71
71
|
res.end(html);
|
|
72
72
|
};
|
|
73
|
-
const invalid = () =>
|
|
74
|
-
page(400, oauthErrorHtml('This sign-in link is invalid or has expired.', 'Start the sign-in again from Mixdog.'));
|
|
73
|
+
const invalid = () => page(400, oauthErrorHtml('This sign-in link is invalid or has expired.'));
|
|
75
74
|
let target;
|
|
76
75
|
try {
|
|
77
76
|
target = new URL(req.url || '/', redirectUrl);
|
|
@@ -94,7 +93,7 @@ export function answerOAuthCallback(req, res, { redirectUrl, state, finish, serv
|
|
|
94
93
|
const server = serverName || 'the MCP server';
|
|
95
94
|
if (target.searchParams.has('error')) {
|
|
96
95
|
// The authorization server's error text is never reflected.
|
|
97
|
-
page(400, oauthErrorHtml(`Sign-in to ${server} was not completed
|
|
96
|
+
page(400, oauthErrorHtml(`Sign-in to ${server} was not completed.`));
|
|
98
97
|
finish(undefined);
|
|
99
98
|
return;
|
|
100
99
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// A pre-registered OAuth client for an MCP server, used as static client
|
|
2
|
+
// information in place of dynamic client registration:
|
|
3
|
+
// - the user's own (custom connector → 고급 설정 → 앱 번호/비밀값): the id
|
|
4
|
+
// in the server config (`oauthClientId`, not a secret), the secret only in
|
|
5
|
+
// the keychain under mcpOAuthClientSecretAccount(url);
|
|
6
|
+
// - else the catalog connector's (connector-catalog.mjs `oauthClient`), with
|
|
7
|
+
// its scopes and the provider's offline-access parameter.
|
|
8
|
+
import { createHash } from 'node:crypto';
|
|
9
|
+
import keychain from '../../../../lib/keychain-cjs.cjs';
|
|
10
|
+
import { connectorById, connectorOfServer } from '../../../shared/connector-catalog.mjs';
|
|
11
|
+
import { GOOGLE_AUTH_PARAMS, googleOAuthClient } from '../../../shared/google-oauth-client.mjs';
|
|
12
|
+
import { normalizeMcpTransportUrl } from './client-config.mjs';
|
|
13
|
+
|
|
14
|
+
const BUILT_IN_CLIENTS = {
|
|
15
|
+
google: () => {
|
|
16
|
+
const client = googleOAuthClient();
|
|
17
|
+
return client && { ...client, authParams: GOOGLE_AUTH_PARAMS };
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** The keychain account of the user's client secret for one server URL. */
|
|
22
|
+
export function mcpOAuthClientSecretAccount(url) {
|
|
23
|
+
const key = normalizeMcpTransportUrl(String(url));
|
|
24
|
+
return `mcp-oauth-client.${createHash('sha256').update(key).digest('hex')}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Store (or, with '', delete) the user's client secret for server `url`. */
|
|
28
|
+
export async function saveMcpOAuthClientSecret(url, secret, credentialStore = keychain) {
|
|
29
|
+
const account = mcpOAuthClientSecretAccount(url);
|
|
30
|
+
if (secret) await credentialStore.setSecretAsync(account, secret);
|
|
31
|
+
else await credentialStore.deleteSecretAsync(account);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* `{ clientId, clientSecret?, secretAccount?, scopes?, authParams? }` for
|
|
36
|
+
* server config `cfg` at `url`, or null to register a client dynamically.
|
|
37
|
+
*/
|
|
38
|
+
export function mcpOAuthClientFor(cfg, url) {
|
|
39
|
+
const backend = connectorById(connectorOfServer(cfg))?.backend;
|
|
40
|
+
const catalog = backend?.kind === 'oauth' ? backend : null;
|
|
41
|
+
const scopes = catalog?.scopes ? [...catalog.scopes] : undefined;
|
|
42
|
+
const own = typeof cfg?.oauthClientId === 'string' ? cfg.oauthClientId.trim() : '';
|
|
43
|
+
if (own) {
|
|
44
|
+
const authParams = catalog?.oauthClient === 'google' ? GOOGLE_AUTH_PARAMS : undefined;
|
|
45
|
+
return { clientId: own, secretAccount: mcpOAuthClientSecretAccount(url), scopes, authParams };
|
|
46
|
+
}
|
|
47
|
+
const builtIn = catalog?.oauthClient ? BUILT_IN_CLIENTS[catalog.oauthClient]?.() : null;
|
|
48
|
+
return builtIn ? { ...builtIn, scopes } : null;
|
|
49
|
+
}
|