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
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { refreshKeychainStatus, retryKeychain } from '../../runtime/shared/config.mjs';
|
|
5
5
|
import { keychainIssueFor } from '../services/provider-admin.mjs';
|
|
6
6
|
import { modelProxyClientMode } from '../../runtime/shared/model-proxy-mode.mjs';
|
|
7
|
+
import { providerCatalogFailure } from '../../runtime/agent/orchestrator/providers/catalog-failures.mjs';
|
|
7
8
|
|
|
8
9
|
// The keyring alert must reflect the live state (it can lock or unlock while a
|
|
9
10
|
// cached setup is served), so overlay it on every ready read.
|
|
@@ -14,6 +15,22 @@ function withLiveKeychain(setup) {
|
|
|
14
15
|
return keychain ? { ...rest, keychain } : rest;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
// A provider whose model list failed says why on its row (`catalogError`), so a
|
|
19
|
+
// view never shows its empty list as if nothing were wrong. Overlaid on every
|
|
20
|
+
// read like the keyring alert: the cached setup predates the failure.
|
|
21
|
+
export function withCatalogErrors(setup) {
|
|
22
|
+
if (!setup || typeof setup !== 'object') return setup;
|
|
23
|
+
const out = { ...setup };
|
|
24
|
+
for (const group of ['api', 'oauth', 'local']) {
|
|
25
|
+
if (!Array.isArray(setup[group])) continue;
|
|
26
|
+
out[group] = setup[group].map((row) => {
|
|
27
|
+
const { message, errorKind } = providerCatalogFailure(row?.id) ?? {};
|
|
28
|
+
return message ? { ...row, catalogError: { message, errorKind } } : row;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
|
|
17
34
|
export function createSetupApi({
|
|
18
35
|
awaitKeychainPrewarm,
|
|
19
36
|
isKeychainPrewarmReady,
|
|
@@ -41,7 +58,7 @@ export function createSetupApi({
|
|
|
41
58
|
void Promise.resolve(awaitKeychainPrewarm())
|
|
42
59
|
.then(() => cachedProviderSetup({}))
|
|
43
60
|
.catch(() => {});
|
|
44
|
-
return { ...quick, pendingSecrets: !isKeychainPrewarmReady() };
|
|
61
|
+
return withCatalogErrors({ ...quick, pendingSecrets: !isKeychainPrewarmReady() });
|
|
45
62
|
}
|
|
46
63
|
await awaitKeychainPrewarm();
|
|
47
64
|
// A keyring the user has since unlocked: rebuild the setup that was cached
|
|
@@ -53,7 +70,7 @@ export function createSetupApi({
|
|
|
53
70
|
// `recheckKeychain` (window focus) skips the snapshot TTL.
|
|
54
71
|
const changed = await refreshKeychainStatus({ force: options?.recheckKeychain === true });
|
|
55
72
|
if (changed) reloadFullConfig();
|
|
56
|
-
return withLiveKeychain(await cachedProviderSetup({ force: force || recovered || changed }));
|
|
73
|
+
return withCatalogErrors(withLiveKeychain(await cachedProviderSetup({ force: force || recovered || changed })));
|
|
57
74
|
},
|
|
58
75
|
async getUsageDashboard(options = {}) {
|
|
59
76
|
return await getUsageDashboard(options);
|
|
@@ -1,52 +1,128 @@
|
|
|
1
1
|
// Provider registries are process-global. Share raw catalog rows across
|
|
2
2
|
// sessions, while each session owns its saved-settings projection.
|
|
3
|
-
|
|
3
|
+
//
|
|
4
|
+
// One slow or offline provider must not hold, or force a reload of, everyone
|
|
5
|
+
// else's rows. Each provider's model list runs through catalog-deadline.mjs:
|
|
6
|
+
// bounded per provider (a timed-out list becomes an error entry and is
|
|
7
|
+
// aborted where listModels honors `{ signal }`), and single-flight per
|
|
8
|
+
// provider until the underlying work settles. When some but not all providers
|
|
9
|
+
// answer, the answered entries are kept for the catalog state, and each
|
|
10
|
+
// failed entry carries a process-wide `retryAt`: non-forced reads by any
|
|
11
|
+
// session reuse the partial answer and ask a missing provider again only once
|
|
12
|
+
// its backoff passed. Forced reads ask every provider. A load where every
|
|
13
|
+
// provider failed is never kept, so it stays immediately retryable. A
|
|
14
|
+
// provider whose previous list ignored its deadline and is still running is
|
|
15
|
+
// not asked at all (catalog-deadline.mjs): it gets a timed-out entry at
|
|
16
|
+
// once, with the same backoff, until that list settles.
|
|
17
|
+
//
|
|
18
|
+
// The catalog state is the registry revision plus a local epoch that
|
|
19
|
+
// invalidateSharedProviderCatalog() bumps on auth/account changes, so rows
|
|
20
|
+
// listed under the previous account are never served again.
|
|
21
|
+
import {
|
|
22
|
+
boundedProviderCatalogOp,
|
|
23
|
+
bumpProviderCatalogGeneration,
|
|
24
|
+
PROVIDER_CATALOG_TIMEOUT_MS,
|
|
25
|
+
} from '../runtime/agent/orchestrator/providers/catalog-deadline.mjs';
|
|
26
|
+
|
|
27
|
+
export { PROVIDER_CATALOG_TIMEOUT_MS };
|
|
28
|
+
|
|
29
|
+
/** Backoff before a provider missing from a partial catalog is asked again. */
|
|
30
|
+
export const PROVIDER_CATALOG_RETRY_MS = 30_000;
|
|
31
|
+
|
|
32
|
+
let catalogEpoch = 0;
|
|
33
|
+
let sharedCatalogKey = null;
|
|
4
34
|
let sharedCatalogEntries = null;
|
|
35
|
+
let sharedPartialKey = null;
|
|
36
|
+
let sharedPartialEntries = null;
|
|
5
37
|
let sharedCatalogPromise = null;
|
|
6
|
-
let
|
|
38
|
+
let sharedCatalogPromiseKey = null;
|
|
39
|
+
let sharedCatalogPromiseForced = false;
|
|
7
40
|
|
|
8
41
|
export function catalogRevision(registry) {
|
|
9
42
|
const value = Number(registry?.providerCatalogRevision?.());
|
|
10
43
|
return Number.isFinite(value) ? value : 0;
|
|
11
44
|
}
|
|
12
45
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
46
|
+
const catalogKey = (registry) => `${catalogRevision(registry)}:${catalogEpoch}`;
|
|
47
|
+
|
|
48
|
+
/** Drops every shared entry (complete or partial) and opens a new epoch: the
|
|
49
|
+
* next read asks every provider again. Called on auth/account changes. */
|
|
50
|
+
export function invalidateSharedProviderCatalog() {
|
|
51
|
+
catalogEpoch += 1;
|
|
52
|
+
// Pending refreshes and lists of the previous account, of either kind, must
|
|
53
|
+
// not block or fail the new account's work.
|
|
54
|
+
bumpProviderCatalogGeneration();
|
|
55
|
+
sharedCatalogKey = null;
|
|
56
|
+
sharedCatalogEntries = null;
|
|
57
|
+
sharedPartialKey = null;
|
|
58
|
+
sharedPartialEntries = null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const answered = (entry) => !Object.hasOwn(entry, 'error');
|
|
62
|
+
|
|
63
|
+
async function providerEntry(name, provider, timeoutMs, epoch) {
|
|
64
|
+
if (typeof provider?.listModels !== 'function') return { name, models: [], ms: 0 };
|
|
65
|
+
const startedAt = performance.now();
|
|
66
|
+
const outcome = await boundedProviderCatalogOp({
|
|
67
|
+
name,
|
|
68
|
+
provider,
|
|
69
|
+
kind: 'list',
|
|
70
|
+
run: (signal) => provider.listModels({ signal }),
|
|
71
|
+
timeoutMs,
|
|
72
|
+
epoch,
|
|
73
|
+
});
|
|
74
|
+
const ms = performance.now() - startedAt;
|
|
75
|
+
if (outcome.error) {
|
|
76
|
+
return { name, models: [], error: outcome.error, ms, retryAt: Date.now() + PROVIDER_CATALOG_RETRY_MS };
|
|
77
|
+
}
|
|
78
|
+
return { name, models: Array.isArray(outcome.value) ? outcome.value : [], ms };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function sharedProviderCatalog(registry, { force = false, timeoutMs = PROVIDER_CATALOG_TIMEOUT_MS } = {}) {
|
|
82
|
+
const key = catalogKey(registry);
|
|
83
|
+
if (!force && sharedCatalogKey === key && Array.isArray(sharedCatalogEntries)) {
|
|
16
84
|
return sharedCatalogEntries;
|
|
17
85
|
}
|
|
18
|
-
if (sharedCatalogPromise &&
|
|
86
|
+
if (sharedCatalogPromise && sharedCatalogPromiseKey === key && (sharedCatalogPromiseForced || !force)) {
|
|
19
87
|
return await sharedCatalogPromise;
|
|
20
88
|
}
|
|
89
|
+
const kept = new Map();
|
|
90
|
+
if (!force && sharedPartialKey === key) {
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
for (const entry of sharedPartialEntries) {
|
|
93
|
+
if (answered(entry) || now < entry.retryAt) kept.set(entry.name, entry);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
21
96
|
const providers = [...registry.getAllProviders()];
|
|
22
|
-
|
|
97
|
+
// Every missing provider is still inside its backoff: the partial answer is
|
|
98
|
+
// the answer, without asking anyone.
|
|
99
|
+
if (providers.length > 0 && providers.every(([name]) => kept.has(name))) {
|
|
100
|
+
return providers.map(([name]) => kept.get(name));
|
|
101
|
+
}
|
|
102
|
+
// A pending list from an older catalog state (revision or account epoch) is
|
|
103
|
+
// not joined: its answer may describe the previous catalog.
|
|
104
|
+
const epoch = key;
|
|
105
|
+
sharedCatalogPromiseKey = key;
|
|
106
|
+
sharedCatalogPromiseForced = force;
|
|
23
107
|
const request = Promise.all(
|
|
24
|
-
providers.map(
|
|
25
|
-
if (typeof provider?.listModels !== 'function') return { name, models: [], ms: 0 };
|
|
26
|
-
const startedAt = performance.now();
|
|
27
|
-
try {
|
|
28
|
-
const models = await provider.listModels();
|
|
29
|
-
return { name, models: Array.isArray(models) ? models : [], ms: performance.now() - startedAt };
|
|
30
|
-
} catch (error) {
|
|
31
|
-
return { name, models: [], error, ms: performance.now() - startedAt };
|
|
32
|
-
}
|
|
33
|
-
})
|
|
108
|
+
providers.map(([name, provider]) => kept.get(name) || providerEntry(name, provider, timeoutMs, epoch))
|
|
34
109
|
)
|
|
35
110
|
.then((entries) => {
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
111
|
+
if (sharedCatalogPromise === request && catalogKey(registry) === key) {
|
|
112
|
+
const complete = entries.every(answered);
|
|
113
|
+
const partial = !complete && entries.some(answered);
|
|
114
|
+
sharedCatalogKey = complete ? key : null;
|
|
115
|
+
sharedCatalogEntries = complete ? entries : null;
|
|
116
|
+
sharedPartialKey = partial ? key : null;
|
|
117
|
+
sharedPartialEntries = partial ? entries : null;
|
|
43
118
|
}
|
|
44
119
|
return entries;
|
|
45
120
|
})
|
|
46
121
|
.finally(() => {
|
|
47
122
|
if (sharedCatalogPromise === request) {
|
|
48
123
|
sharedCatalogPromise = null;
|
|
49
|
-
|
|
124
|
+
sharedCatalogPromiseKey = null;
|
|
125
|
+
sharedCatalogPromiseForced = false;
|
|
50
126
|
}
|
|
51
127
|
});
|
|
52
128
|
sharedCatalogPromise = request;
|
|
@@ -17,6 +17,7 @@ export function createCatalogLoader({
|
|
|
17
17
|
ensureFullConfig,
|
|
18
18
|
awaitKeychainPrewarm,
|
|
19
19
|
ensureProvidersReady,
|
|
20
|
+
providerTimeoutMs,
|
|
20
21
|
}) {
|
|
21
22
|
function rowsForEntry({ name, models, error, ms }) {
|
|
22
23
|
const out = [];
|
|
@@ -76,7 +77,10 @@ export function createCatalogLoader({
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
/** `request` ({ seq }) identifies the caller's load; `request.complete`
|
|
79
|
-
* reports whether every provider answered.
|
|
80
|
+
* reports whether every provider answered, `request.missing` names the
|
|
81
|
+
* providers that failed or timed out, `request.partial` marks a load
|
|
82
|
+
* where some but not all providers answered, and `request.retryAt` is when
|
|
83
|
+
* the first missing provider may be asked again. */
|
|
80
84
|
return async function loadProviderModelsFresh({ forceRefresh = false, loadSecrets = true, request = null } = {}) {
|
|
81
85
|
const startedAt = performance.now();
|
|
82
86
|
profile('load:start', { forceRefresh, loadSecrets });
|
|
@@ -93,8 +97,15 @@ export function createCatalogLoader({
|
|
|
93
97
|
// forward only for the still-current request, never for a superseded load.
|
|
94
98
|
if (ownsLoad) request.seq = caches.providerModelsLoadSeq;
|
|
95
99
|
const seq = request?.seq ?? caches.providerModelsLoadSeq;
|
|
96
|
-
const catalogEntries = await sharedProviderCatalog(reg());
|
|
97
|
-
if (request)
|
|
100
|
+
const catalogEntries = await sharedProviderCatalog(reg(), { force: forceRefresh, timeoutMs: providerTimeoutMs });
|
|
101
|
+
if (request) {
|
|
102
|
+
const failed = catalogEntries.filter((entry) => Object.hasOwn(entry, 'error'));
|
|
103
|
+
request.missing = failed.map((entry) => entry.name);
|
|
104
|
+
request.complete = failed.length === 0;
|
|
105
|
+
request.partial = !request.complete && failed.length < catalogEntries.length;
|
|
106
|
+
// The shared, process-wide backoff of the soonest missing provider.
|
|
107
|
+
request.retryAt = request.partial ? Math.min(...failed.map((entry) => entry.retryAt)) : null;
|
|
108
|
+
}
|
|
98
109
|
const results = [];
|
|
99
110
|
const seen = new Set();
|
|
100
111
|
for (const row of catalogEntries.flatMap(rowsForEntry)) {
|
|
@@ -69,6 +69,10 @@ export function hydrateProviderModelRow(cfg, row) {
|
|
|
69
69
|
savedModelParameters: saved.modelParameters || {},
|
|
70
70
|
savedContextPercent: Number(saved.contextPercent) || undefined,
|
|
71
71
|
savedContextTokens: Number(saved.contextTokens) || undefined,
|
|
72
|
+
// Model management: an explicit /model visibility choice and the favorite
|
|
73
|
+
// mark (absent = the picker's default listing, not a favorite).
|
|
74
|
+
...(typeof saved.hidden === 'boolean' ? { savedHidden: saved.hidden } : {}),
|
|
75
|
+
...(saved.favorite === true ? { favorite: true } : {}),
|
|
72
76
|
};
|
|
73
77
|
}
|
|
74
78
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// row-hydration — saved model settings layered onto catalog rows
|
|
11
11
|
// web-search-catalog — the native web-search model list
|
|
12
12
|
// catalog-load — one authoritative provider catalog load
|
|
13
|
+
import { PROVIDER_CATALOG_RETRY_MS } from './provider-catalog-cache.mjs';
|
|
13
14
|
import { createCatalogLoader } from './provider-models/catalog-load.mjs';
|
|
14
15
|
import { createModelMetaIndex, modelMetaKey } from './provider-models/model-meta.mjs';
|
|
15
16
|
import { createRowHydration } from './provider-models/row-hydration.mjs';
|
|
@@ -19,6 +20,25 @@ const PROVIDER_MODELS_PROFILE_ENABLED = /^(1|true|yes|on)$/i.test(
|
|
|
19
20
|
String(process.env.MIXDOG_PROVIDER_MODELS_PROFILE || process.env.MIXDOG_BOOT_PROFILE || '')
|
|
20
21
|
);
|
|
21
22
|
|
|
23
|
+
/** How long a partial catalog (some provider failed or timed out) is served
|
|
24
|
+
* as-is before a read retries the missing providers in the background. The
|
|
25
|
+
* backoff is process-wide (provider-catalog-cache.mjs), not per session. */
|
|
26
|
+
export const PARTIAL_CATALOG_RETRY_MS = PROVIDER_CATALOG_RETRY_MS;
|
|
27
|
+
|
|
28
|
+
/** Rows of a partial answer say so explicitly, on quick and full reads alike:
|
|
29
|
+
* `catalogPartial`, the `catalogMissing` providers and `catalogRetryAt`
|
|
30
|
+
* (epoch ms) after which a reader should ask again. Complete answers carry
|
|
31
|
+
* none of these. */
|
|
32
|
+
function withCatalogState(models, partial) {
|
|
33
|
+
if (!partial) return models;
|
|
34
|
+
return models.map((row) => ({
|
|
35
|
+
...row,
|
|
36
|
+
catalogPartial: true,
|
|
37
|
+
catalogMissing: [...partial.missing],
|
|
38
|
+
catalogRetryAt: partial.retryAt,
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
|
|
22
42
|
export function createProviderModels({
|
|
23
43
|
caches,
|
|
24
44
|
modelMetaByRoute,
|
|
@@ -37,6 +57,8 @@ export function createProviderModels({
|
|
|
37
57
|
scheduleProviderModelWarmup,
|
|
38
58
|
// Quick-row helpers wired in after createQuickModelRows resolves.
|
|
39
59
|
quickHelpers,
|
|
60
|
+
// Per-provider model-list bound; defaults to PROVIDER_CATALOG_TIMEOUT_MS.
|
|
61
|
+
providerCatalogTimeoutMs,
|
|
40
62
|
}) {
|
|
41
63
|
const config = () => getConfig();
|
|
42
64
|
const route = () => getRoute();
|
|
@@ -66,17 +88,56 @@ export function createProviderModels({
|
|
|
66
88
|
collectProviderModels,
|
|
67
89
|
...readiness,
|
|
68
90
|
});
|
|
69
|
-
const loadProviderModelsFresh = createCatalogLoader({
|
|
91
|
+
const loadProviderModelsFresh = createCatalogLoader({
|
|
92
|
+
caches,
|
|
93
|
+
config,
|
|
94
|
+
reg,
|
|
95
|
+
meta,
|
|
96
|
+
rows,
|
|
97
|
+
profile,
|
|
98
|
+
providerTimeoutMs: providerCatalogTimeoutMs,
|
|
99
|
+
...readiness,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Partial-catalog state of a loaded rows array, for readers that joined the
|
|
103
|
+
// shared load instead of reading the adopted cache.
|
|
104
|
+
const partialLoads = new WeakMap();
|
|
105
|
+
const partialOf = (request) => (request.partial ? { missing: request.missing, retryAt: request.retryAt } : null);
|
|
70
106
|
|
|
71
107
|
function adoptProviderModelCache(models, request, loadSecrets = true) {
|
|
72
108
|
const revision = meta.syncCatalogRevision();
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (request.seq
|
|
76
|
-
|
|
109
|
+
const partial = partialOf(request);
|
|
110
|
+
if (partial) partialLoads.set(models, partial);
|
|
111
|
+
if (request.seq !== caches.providerModelsLoadSeq || !loadSecrets) return;
|
|
112
|
+
const at = Date.now();
|
|
113
|
+
if (request.complete) {
|
|
114
|
+
caches.providerModelsCache = { models, at, revision };
|
|
115
|
+
} else if (partial) {
|
|
116
|
+
// Some providers answered. Serving that answer beats reloading every
|
|
117
|
+
// provider on each read while one is down; it stays marked partial and
|
|
118
|
+
// its missing providers are retried once the shared backoff passes. A
|
|
119
|
+
// no-secrets, empty or all-failed load stays uncached and retryable
|
|
120
|
+
// rather than becoming an authoritative empty catalog.
|
|
121
|
+
caches.providerModelsCache = { models, at, revision, partial: true, ...partial };
|
|
77
122
|
}
|
|
78
123
|
}
|
|
79
124
|
|
|
125
|
+
/** Background refill of a partial catalog: only its missing providers are
|
|
126
|
+
* asked again (provider-catalog-cache keeps the ones that answered, and
|
|
127
|
+
* holds back providers still inside the shared backoff), and readers keep
|
|
128
|
+
* the partial rows until the refill lands. */
|
|
129
|
+
function refillPartialCatalog(cached) {
|
|
130
|
+
if (!cached.partial || Date.now() < cached.retryAt || caches.providerModelsPromise) return;
|
|
131
|
+
profile('partial:retry', { missing: cached.missing.join(',') });
|
|
132
|
+
startSharedLoad({
|
|
133
|
+
loadSecrets: true,
|
|
134
|
+
onFailed: (err) => {
|
|
135
|
+
profile('partial:retry-failed', { error: err instanceof Error ? err.message : String(err) });
|
|
136
|
+
return cached.models;
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
80
141
|
/** The shared in-flight load every concurrent reader joins. `onFailed`, when
|
|
81
142
|
* given, is part of the shared promise so joiners see its value too;
|
|
82
143
|
* `caches.providerModelsPromise` is cleared once it settles so a later read
|
|
@@ -97,13 +158,17 @@ export function createProviderModels({
|
|
|
97
158
|
|
|
98
159
|
async function collectProviderModels({ force = false, quick = false } = {}) {
|
|
99
160
|
meta.syncCatalogRevision();
|
|
100
|
-
|
|
101
|
-
|
|
161
|
+
const cached = caches.providerModelsCache;
|
|
162
|
+
if (!force && Array.isArray(cached.models)) {
|
|
163
|
+
refillPartialCatalog(cached);
|
|
164
|
+
const models = rows.providerModelsFromCacheRows(cached.models);
|
|
102
165
|
// A quick read answered from the loaded catalog already IS the full
|
|
103
166
|
// catalog. Saying so lets a caller skip the full read it would otherwise
|
|
104
167
|
// follow up with (a phone paid the whole catalog twice at boot). Only
|
|
105
168
|
// this answer carries the mark: a full read is complete by definition,
|
|
106
|
-
// and an unmarked quick answer keeps meaning "follow up".
|
|
169
|
+
// and an unmarked quick answer keeps meaning "follow up". A partial
|
|
170
|
+
// catalog is never marked complete; it carries its partial state.
|
|
171
|
+
if (cached.partial) return withCatalogState(models, cached);
|
|
107
172
|
return quick ? models.map((row) => ({ ...row, catalogComplete: true })) : models;
|
|
108
173
|
}
|
|
109
174
|
if (!force && quick) {
|
|
@@ -117,10 +182,11 @@ export function createProviderModels({
|
|
|
117
182
|
const request = { seq: ++caches.providerModelsLoadSeq };
|
|
118
183
|
const models = await loadProviderModelsFresh({ forceRefresh: true, loadSecrets: true, request });
|
|
119
184
|
adoptProviderModelCache(models, request);
|
|
120
|
-
return rows.providerModelsFromCacheRows(models);
|
|
185
|
+
return withCatalogState(rows.providerModelsFromCacheRows(models), partialOf(request));
|
|
121
186
|
}
|
|
122
187
|
if (!caches.providerModelsPromise) startSharedLoad({ loadSecrets: true });
|
|
123
|
-
|
|
188
|
+
const loaded = await caches.providerModelsPromise;
|
|
189
|
+
return withCatalogState(rows.providerModelsFromCacheRows(loaded), partialLoads.get(loaded));
|
|
124
190
|
}
|
|
125
191
|
|
|
126
192
|
function warmProviderModelCache({ loadSecrets = false } = {}) {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// registry import and the provider-models factory resolve after this gate is
|
|
6
6
|
// created, while ensureProvidersReady only reaches them on demand.
|
|
7
7
|
import { bootProfile } from './boot-profile.mjs';
|
|
8
|
+
import { invalidateSharedProviderCatalog } from './provider-catalog-cache.mjs';
|
|
8
9
|
import { providerInitCacheKey } from './provider-init-key.mjs';
|
|
9
10
|
|
|
10
11
|
const KEYCHAIN_PREWARM_WAIT_MS = 5000;
|
|
@@ -52,6 +53,9 @@ export function createProviderReadiness({ rt, keychain, getReg, getWarmProviderM
|
|
|
52
53
|
const providerInitPromises = new Map();
|
|
53
54
|
|
|
54
55
|
function invalidateProviderCaches(options = {}) {
|
|
56
|
+
// Auth/account/config changes alter what providers list: the process-wide
|
|
57
|
+
// catalog (partial rows and retry backoffs included) starts a new epoch.
|
|
58
|
+
invalidateSharedProviderCatalog();
|
|
55
59
|
Object.assign(providerModelCaches, emptyModelCaches());
|
|
56
60
|
providerModelCaches.providerModelsLoadSeq += 1;
|
|
57
61
|
Object.assign(providerUsageCaches, emptyUsageCaches());
|
|
@@ -30,6 +30,11 @@ export function createResourceApi(deps) {
|
|
|
30
30
|
if (!EXTENSION_SCOPE_KINDS.includes(scopeKind)) {
|
|
31
31
|
throw new Error(`extension scope kind must be one of ${EXTENSION_SCOPE_KINDS.join(', ')}`);
|
|
32
32
|
}
|
|
33
|
+
// A connector is on in every project unless a project switches it off
|
|
34
|
+
// (setProjectConnectorEnabled); "only in this project" no longer exists.
|
|
35
|
+
if (scopeKind === 'mcp') {
|
|
36
|
+
throw new Error('MCP servers are switched off per project in Project settings (setProjectConnectorEnabled)');
|
|
37
|
+
}
|
|
33
38
|
let key = clean(name);
|
|
34
39
|
if (!key) throw new Error('extension name is required');
|
|
35
40
|
// Plugin scopes are keyed by registry id; a name must resolve to it.
|
|
@@ -11,6 +11,9 @@ import { resolveMcpOAuthUrl } from '../runtime/agent/orchestrator/mcp/client-con
|
|
|
11
11
|
import { withAdoptedCopies, withPinnedCollisionNames, withPluginServerIntent } from './mcp-glue/plugin-servers.mjs';
|
|
12
12
|
import { findRegisteredPlugin } from './resource-plugins-api.mjs';
|
|
13
13
|
import { createMcpOAuthLogins } from './mcp-oauth-login.mjs';
|
|
14
|
+
import { connectorById, gmailAppPasswordAccount, gmailAppPasswordValue } from '../runtime/shared/connector-catalog.mjs';
|
|
15
|
+
import { saveMcpOAuthClientSecret } from '../runtime/agent/orchestrator/mcp/oauth-client.mjs';
|
|
16
|
+
import { forgetGmailAppPassword, saveGmailAppPassword } from '../runtime/connectors/gmail-app-password.mjs';
|
|
14
17
|
|
|
15
18
|
/** The URL a server's OAuth credentials are stored under, or null. */
|
|
16
19
|
function oauthUrlOf(cfg) {
|
|
@@ -83,8 +86,17 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
83
86
|
completeMcpLogin,
|
|
84
87
|
getMcpServerOAuthState = () => null,
|
|
85
88
|
probeMcpServerAuth,
|
|
89
|
+
startMcpServerOAuthSwitch,
|
|
90
|
+
commitMcpServerOAuthSwitch,
|
|
91
|
+
gmailAppPassword = { save: saveGmailAppPassword, forget: forgetGmailAppPassword },
|
|
92
|
+
saveOAuthClientSecret = saveMcpOAuthClientSecret,
|
|
86
93
|
} = deps;
|
|
87
94
|
|
|
95
|
+
const reconnectOne = async (name) => {
|
|
96
|
+
const status = await connectConfiguredMcp({ only: name, enabled: true });
|
|
97
|
+
const row = status?.servers?.find((server) => server.name === name);
|
|
98
|
+
return { connected: row?.connected === true, error: row?.error || null };
|
|
99
|
+
};
|
|
88
100
|
// Explicit OAuth sign-in: the one interactive MCP connect. On success the
|
|
89
101
|
// other runtimes reconnect the server non-interactively with the new tokens.
|
|
90
102
|
const oauthLogins = createMcpOAuthLogins({
|
|
@@ -95,6 +107,22 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
95
107
|
},
|
|
96
108
|
cancel: (name) => cancelMcpLogin(name),
|
|
97
109
|
completeCallback: completeMcpLogin,
|
|
110
|
+
// 계정 바꾸기, transactionally: the working connection stays until the new
|
|
111
|
+
// sign-in succeeded and connects; a cancel or failure changes nothing, and
|
|
112
|
+
// a new sign-in that cannot connect is rolled back.
|
|
113
|
+
async switchAccount(name, launch, attempt) {
|
|
114
|
+
const cfg = resolveMcpCatalog().servers[name];
|
|
115
|
+
const switching = startMcpServerOAuthSwitch(cfg, launch, name);
|
|
116
|
+
attempt.abort = () => switching.cancel();
|
|
117
|
+
attempt.answer = (callbackUrl) => switching.answer(callbackUrl);
|
|
118
|
+
const signIn = await switching.done;
|
|
119
|
+
const rollback = await commitMcpServerOAuthSwitch(cfg, signIn);
|
|
120
|
+
const outcome = await reconnectOne(name);
|
|
121
|
+
if (outcome.connected) return outcome;
|
|
122
|
+
await rollback();
|
|
123
|
+
await reconnectOne(name);
|
|
124
|
+
return { connected: false, error: outcome.error || 'The new sign-in could not connect' };
|
|
125
|
+
},
|
|
98
126
|
onComplete: (name) => sync.announce('mcp', { detail: { mcpChanges: [{ name, enabled: true }] } }),
|
|
99
127
|
});
|
|
100
128
|
/** The effective config of a server that signs in with MCP OAuth (HTTP/SSE
|
|
@@ -130,6 +158,49 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
130
158
|
const owned = name ? pluginMcpServer(name) : null;
|
|
131
159
|
if (owned) throw pluginOwnedError(name, owned.owner);
|
|
132
160
|
};
|
|
161
|
+
// The user's OAuth client secret (custom connector → 고급 설정 → 비밀값)
|
|
162
|
+
// goes to the keychain only; clearing the client id forgets it.
|
|
163
|
+
const storeOAuthClientSecret = async (input, cfg, previous = {}) => {
|
|
164
|
+
if (!cfg?.url) return;
|
|
165
|
+
const secret = typeof input.oauthClientSecret === 'string' ? input.oauthClientSecret.trim() : '';
|
|
166
|
+
if (cfg.oauthClientId && secret) await saveOAuthClientSecret(cfg.url, secret);
|
|
167
|
+
else if (!cfg.oauthClientId && previous.oauthClientId) await saveOAuthClientSecret(previous.url || cfg.url, '');
|
|
168
|
+
};
|
|
169
|
+
// Gmail with an app password (connector-catalog.mjs GMAIL_APP_PASSWORD): the
|
|
170
|
+
// password is checked with Gmail before anything is saved and goes to the
|
|
171
|
+
// keychain only. A new connection needs one; an edit without one keeps it.
|
|
172
|
+
const storeGmailAppPassword = async (input, cfg, required) => {
|
|
173
|
+
const account = gmailAppPasswordAccount(cfg);
|
|
174
|
+
if (!account) return;
|
|
175
|
+
const password = gmailAppPasswordValue(input.appPassword);
|
|
176
|
+
if (password) await gmailAppPassword.save(account, password);
|
|
177
|
+
else if (required || input.appPassword !== undefined) {
|
|
178
|
+
throw new Error('GMAIL_APP_PASSWORD_FORMAT: An app password has 16 characters.');
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
// A server's OAuth tokens and client registration go with it, unless another
|
|
182
|
+
// configured server (plugin or disabled ones included, even with MCP
|
|
183
|
+
// disabled for this runtime) still uses them through the same exact URL.
|
|
184
|
+
// `signIn` tells the caller what happened to the sign-in: `removed` (local
|
|
185
|
+
// secrets deleted, `revoked` when the provider confirmed the revocation
|
|
186
|
+
// too), `shared` (another server still uses it), or none (null).
|
|
187
|
+
const releaseOAuth = async (cfg) => {
|
|
188
|
+
const url = oauthUrlOf(cfg);
|
|
189
|
+
if (!url) return null;
|
|
190
|
+
if (Object.values(resolveConfiguredMcpCatalog().servers).some((other) => oauthUrlOf(other) === url)) {
|
|
191
|
+
return { signIn: 'shared', revoked: false };
|
|
192
|
+
}
|
|
193
|
+
const { revoked = false } = (await clearMcpServerOAuth(cfg, { revoke: true })) || {};
|
|
194
|
+
return { signIn: 'removed', revoked };
|
|
195
|
+
};
|
|
196
|
+
// An app password is deleted once no configured server uses its account.
|
|
197
|
+
const forgetUnusedGmailAppPassword = async (account) => {
|
|
198
|
+
if (!account) return false;
|
|
199
|
+
const servers = Object.values(resolveConfiguredMcpCatalog().servers);
|
|
200
|
+
if (servers.some((cfg) => gmailAppPasswordAccount(cfg) === account)) return false;
|
|
201
|
+
await gmailAppPassword.forget(account);
|
|
202
|
+
return true;
|
|
203
|
+
};
|
|
133
204
|
// Peers reload config from disk on receipt: land the write before publishing.
|
|
134
205
|
const persisted = async () => {
|
|
135
206
|
await flushSkillsSave?.();
|
|
@@ -164,11 +235,18 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
164
235
|
async beginMcpOAuthLogin(name, options = {}) {
|
|
165
236
|
const { serverName, cfg } = await oauthServerConfig(name);
|
|
166
237
|
if (cfg.enabled === false) throw new Error(`MCP server is disabled: ${serverName}`);
|
|
238
|
+
// `mode`: `connect` (a normal authorization), `reauth` (repair the same
|
|
239
|
+
// connection; a refresh is tried first) or `switch` (계정 바꾸기).
|
|
240
|
+
if (options?.mode === 'switch' && !getMcpServerOAuthState(cfg)?.signedIn) {
|
|
241
|
+
throw new Error(`MCP server "${serverName}" is not signed in`);
|
|
242
|
+
}
|
|
167
243
|
return oauthLogins.begin(serverName, options);
|
|
168
244
|
},
|
|
169
|
-
/** `{ flowId, state: 'pending' | 'complete' | 'failed' | 'cancelled',
|
|
170
|
-
|
|
171
|
-
|
|
245
|
+
/** `{ flowId, state: 'pending' | 'complete' | 'failed' | 'cancelled',
|
|
246
|
+
* step?, error?, reason? }`; with `{ since, waitMs }` it resolves on the
|
|
247
|
+
* flow's next change (mcp-oauth-login.mjs). */
|
|
248
|
+
getMcpOAuthLoginStatus(flowId, options) {
|
|
249
|
+
return oauthLogins.status(flowId, options);
|
|
172
250
|
},
|
|
173
251
|
cancelMcpOAuthLogin(flowId) {
|
|
174
252
|
return oauthLogins.cancel(flowId);
|
|
@@ -186,8 +264,9 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
186
264
|
const { serverName, cfg } = await oauthServerConfig(name);
|
|
187
265
|
// A begin issued meanwhile waits until the credentials are gone.
|
|
188
266
|
return oauthLogins.cancelServer(serverName, async () => {
|
|
189
|
-
await clearMcpServerOAuth(cfg);
|
|
190
|
-
|
|
267
|
+
const { revoked = false } = (await clearMcpServerOAuth(cfg, { revoke: true })) || {};
|
|
268
|
+
const status = await sync.reconnectMcpAndAnnounce({ only: serverName, enabled: cfg.enabled !== false });
|
|
269
|
+
return { ...status, revoked };
|
|
191
270
|
});
|
|
192
271
|
},
|
|
193
272
|
/** `{ auth: 'oauth' | 'none' | 'configured' | 'unreachable', error? }` for an
|
|
@@ -203,10 +282,18 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
203
282
|
},
|
|
204
283
|
async addMcpServer(input = {}) {
|
|
205
284
|
const { name, config: serverConfig } = normalizeMcpServerInput(input);
|
|
285
|
+
// A catalog connector's server keeps its catalog id (connector-catalog.mjs).
|
|
286
|
+
const connector = clean(input.connector);
|
|
287
|
+
if (connector) {
|
|
288
|
+
if (!connectorById(connector)) throw new Error(`Unknown connector: ${connector}`);
|
|
289
|
+
serverConfig.connector = connector;
|
|
290
|
+
}
|
|
206
291
|
const servers = userServers();
|
|
207
292
|
if (Object.hasOwn(servers, name)) throw new Error(`MCP server already exists: ${name}`);
|
|
208
293
|
const owned = pluginMcpServer(name);
|
|
209
294
|
if (owned) throw new Error(`MCP server already exists: ${name} (provided by plugin "${owned.owner.id}")`);
|
|
295
|
+
await storeOAuthClientSecret(input, serverConfig);
|
|
296
|
+
await storeGmailAppPassword(input, serverConfig, true);
|
|
210
297
|
saveServers({ ...servers, [name]: serverConfig });
|
|
211
298
|
const status = await sync.reconnectMcpAndAnnounce({ only: name, enabled: serverConfig.enabled !== false });
|
|
212
299
|
return { name, status };
|
|
@@ -232,7 +319,14 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
232
319
|
}
|
|
233
320
|
if (originalName && originalName !== name) delete servers[originalName];
|
|
234
321
|
servers[name] = mergeMcpServerConfig(existing, normalizedInput.config);
|
|
322
|
+
await storeOAuthClientSecret(input, servers[name], existing);
|
|
323
|
+
await storeGmailAppPassword(input, servers[name], false);
|
|
235
324
|
saveServers(servers);
|
|
325
|
+
const previousAccount = gmailAppPasswordAccount(existing);
|
|
326
|
+
const account = gmailAppPasswordAccount(servers[name]);
|
|
327
|
+
if (previousAccount !== account) await forgetUnusedGmailAppPassword(previousAccount);
|
|
328
|
+
// Gmail switched from the Google sign-in to an app password keeps no sign-in.
|
|
329
|
+
if (account && !previousAccount) await releaseOAuth(existing);
|
|
236
330
|
const status = await sync.reconnectMcpAndAnnounce({
|
|
237
331
|
only: name,
|
|
238
332
|
enabled: servers[name].enabled !== false,
|
|
@@ -252,15 +346,15 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
252
346
|
delete servers[serverName];
|
|
253
347
|
saveServers(servers);
|
|
254
348
|
const status = await sync.reconnectMcpAndAnnounce({ drop: [serverName] });
|
|
255
|
-
// A removed server keeps no
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
await clearMcpServerOAuth(removed);
|
|
349
|
+
// A removed server keeps no sign-in another server does not share: an
|
|
350
|
+
// app password leaves the keychain (it is revoked at Google only).
|
|
351
|
+
const account = gmailAppPasswordAccount(removed);
|
|
352
|
+
if (account) {
|
|
353
|
+
const forgotten = await forgetUnusedGmailAppPassword(account);
|
|
354
|
+
return { ...status, signIn: forgotten ? 'removed' : 'shared', revoked: false };
|
|
262
355
|
}
|
|
263
|
-
|
|
356
|
+
const released = await releaseOAuth(removed);
|
|
357
|
+
return released ? { ...status, ...released } : status;
|
|
264
358
|
},
|
|
265
359
|
async setMcpServerEnabled(name, enabled) {
|
|
266
360
|
const serverName = clean(name);
|
|
@@ -24,6 +24,7 @@ export function createSkillsResourceApi({ deps, sync, decorate }) {
|
|
|
24
24
|
skillContent,
|
|
25
25
|
addGlobalSkill,
|
|
26
26
|
saveSkillDocument,
|
|
27
|
+
removeGlobalSkill,
|
|
27
28
|
invalidateSkills,
|
|
28
29
|
getDisabledSkills,
|
|
29
30
|
setDisabledSkills,
|
|
@@ -56,6 +57,17 @@ export function createSkillsResourceApi({ deps, sync, decorate }) {
|
|
|
56
57
|
await sync.announce('skills', { invalidate: false });
|
|
57
58
|
return { skill, status: skillsStatus() };
|
|
58
59
|
},
|
|
60
|
+
async removeSkill(input = {}) {
|
|
61
|
+
const removed = removeGlobalSkill(input);
|
|
62
|
+
// A removed skill leaves no stale disabled entry behind.
|
|
63
|
+
const disabled = getDisabledSkills?.().disabled;
|
|
64
|
+
if (Array.isArray(disabled) && disabled.includes(removed.name)) {
|
|
65
|
+
setDisabledSkills?.(disabled.filter((name) => name !== removed.name));
|
|
66
|
+
await flushSkillsSave?.();
|
|
67
|
+
}
|
|
68
|
+
await sync.announce('skills');
|
|
69
|
+
return { skill: removed, status: skillsStatus() };
|
|
70
|
+
},
|
|
59
71
|
async setDisabledSkills(names = []) {
|
|
60
72
|
const result = setDisabledSkills?.(names);
|
|
61
73
|
await flushSkillsSave?.();
|