mixdog 1.4.1 → 1.4.2
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/package.json +3 -2
- package/scripts/build-shipped-history.mjs +115 -33
- package/scripts/gate-local.mjs +172 -61
- package/scripts/lib/gate-scheduler.mjs +138 -32
- package/scripts/lib/gate-timing-reporter.mjs +16 -0
- package/scripts/lib/run-node-tests.mjs +54 -29
- package/scripts/lib/scoped-temp.mjs +1 -0
- package/scripts/lib/test-failure-records.mjs +14 -1
- package/scripts/lib/test-failure-summary.mjs +16 -4
- package/scripts/lib/test-spawn-ledger.mjs +12 -3
- package/scripts/pptx-kit-index.mjs +5 -6
- package/scripts/suite-health.mjs +110 -28
- package/scripts/test.mjs +9 -14
- package/src/agents/advisor/AGENT.md +4 -4
- package/src/agents/advisor/agent.json +1 -1
- package/src/agents/explore/AGENT.md +22 -0
- package/src/agents/explore/agent.json +6 -0
- package/src/cli.mjs +7 -0
- package/src/defaults/agents.json +7 -0
- package/src/defaults/shipped-history.json +18 -2
- package/src/defaults/skills/browser-use/SKILL.md +4 -1
- package/src/defaults/skills/code-tidy/SKILL.md +71 -102
- package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
- package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
- package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
- package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
- package/src/defaults/skills/computer-use/SKILL.md +12 -2
- package/src/defaults/skills/goal-management/SKILL.md +1 -1
- package/src/defaults/skills/setup/references/actions.md +12 -6
- package/src/defaults/skills/setup/references/surfaces.md +2 -2
- package/src/defaults/skills/setup-guide/SKILL.md +5 -2
- package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
- package/src/defaults/skills/skill-creator/SKILL.md +85 -190
- package/src/defaults/skills/skill-creator/references/design.md +70 -162
- package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
- package/src/defaults/skills/test-suite/SKILL.md +194 -0
- package/src/defaults/skills/test-suite/references/case-management.md +51 -0
- package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
- package/src/defaults/skills/test-suite/references/reliability.md +49 -0
- package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
- package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
- package/src/defaults/skills/visualize/SKILL.md +6 -2
- package/src/lib/rules-builder.cjs +6 -30
- package/src/rules/lead/LEAD.md +3 -3
- package/src/rules/shared/20-research.md +3 -6
- package/src/rules/shared/40-editing.md +1 -0
- package/src/rules/shared/60-verification.md +10 -9
- package/src/rules/shared/70-delivery.md +4 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +130 -21
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
- package/src/runtime/agent/orchestrator/config.mjs +12 -7
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +7 -7
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
- package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
- package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +83 -52
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +43 -8
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +30 -28
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
- package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +48 -31
- package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
- package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +26 -28
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/registry.mjs +33 -2
- package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +2 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
- package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
- package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +94 -10
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +10 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +5 -5
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +3 -4
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -14
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +17 -14
- package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
- package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +47 -29
- package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
- package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +243 -38
- package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +36 -26
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
- package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
- package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
- package/src/runtime/channels/lib/whisper-port.mjs +19 -10
- package/src/runtime/channels/lib/whisper-server.mjs +10 -1
- package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
- package/src/runtime/connectors/gmail-app-password.mjs +3 -3
- package/src/runtime/explore/explorer.mjs +93 -0
- package/src/runtime/explore/tool-defs.mjs +35 -0
- package/src/runtime/explore/tool.mjs +123 -0
- package/src/runtime/memory/index.mjs +4 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
- package/src/runtime/memory/lib/embedding-worker.mjs +4 -0
- package/src/runtime/office/quality/assurance-structure-docx.mjs +1 -1
- package/src/runtime/shared/agent-route-config.mjs +17 -2
- package/src/runtime/shared/atomic-file.mjs +103 -2
- package/src/runtime/shared/background-tasks.mjs +15 -6
- package/src/runtime/shared/connector-accounts.mjs +5 -5
- package/src/runtime/shared/connector-catalog.d.mts +11 -4
- package/src/runtime/shared/connector-catalog.mjs +60 -52
- package/src/runtime/shared/custom-provider-keys.mjs +20 -0
- package/src/runtime/shared/file-has-data.mjs +26 -2
- package/src/runtime/shared/file-lock.mjs +13 -15
- package/src/runtime/shared/git-launch.d.mts +9 -0
- package/src/runtime/shared/git-launch.mjs +213 -0
- package/src/runtime/shared/git-runner-worker.mjs +2 -1
- package/src/runtime/shared/llm/cost.mjs +6 -1
- package/src/runtime/shared/llm/http-agent.mjs +313 -29
- package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
- package/src/runtime/shared/llm/model-catalog.mjs +46 -6
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
- package/src/runtime/shared/llm/system-proxy.mjs +452 -0
- package/src/runtime/shared/provider-accounts.mjs +52 -2
- package/src/runtime/shared/staged-install-worker.mjs +5 -0
- package/src/runtime/shared/staged-update.mjs +47 -2
- package/src/runtime/shared/tool-surface.mjs +110 -36
- package/src/runtime/shared/tool-work-units.mjs +4 -0
- package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
- package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
- package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
- package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
- package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
- package/src/runtime/shared/worktrees/git.mjs +57 -0
- package/src/runtime/shared/worktrees/manager.mjs +205 -137
- package/src/runtime/tidy/install.mjs +95 -15
- package/src/runtime/tidy/languages.mjs +17 -15
- package/src/runtime/tidy/process.mjs +7 -4
- package/src/runtime/tidy/resolve.mjs +19 -16
- package/src/runtime/tidy/runners/biome.mjs +3 -3
- package/src/runtime/tidy/tool.mjs +1 -1
- package/src/runtime/tidy/worktree.mjs +10 -6
- package/src/runtime/visualize-bridge/client.mjs +4 -1
- package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
- package/src/runtime/web-search/index.mjs +1 -1
- package/src/session-runtime/agent-task-sessions.mjs +410 -353
- package/src/session-runtime/boot/tools.mjs +2 -0
- package/src/session-runtime/bot-status.mjs +103 -103
- package/src/session-runtime/connector-merge-migration.mjs +18 -15
- package/src/session-runtime/conversation-import/discovery.mjs +159 -40
- package/src/session-runtime/conversation-import/service.mjs +9 -3
- package/src/session-runtime/handoff-attachments.mjs +1 -1
- package/src/session-runtime/held-requests.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +143 -116
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
- package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
- package/src/session-runtime/mixy-heartbeat.mjs +1 -1
- package/src/session-runtime/provider-auth/login.mjs +2 -2
- package/src/session-runtime/resource-mcp-api.mjs +98 -35
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
- package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
- package/src/session-runtime/resource-mcp-servers.mjs +5 -4
- package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
- package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
- package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
- package/src/session-runtime/services/daemon-candidate.mjs +4 -1
- package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
- package/src/session-runtime/services/plugin-admin.mjs +10 -6
- package/src/session-runtime/services/provider-admin.mjs +281 -10
- package/src/session-runtime/session-turn-api.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
- package/src/session-runtime/settings-system-api.mjs +1 -1
- package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
- package/src/session-runtime/setup-tool/executor.mjs +6 -2
- package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
- package/src/session-runtime/turn/agent-ops.mjs +23 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +10 -1
- package/src/session-runtime/workspace/activity-guards.mjs +176 -0
- package/src/session-runtime/workspace/session-view.mjs +149 -0
- package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
- package/src/session-runtime/workspace/transition-runner.mjs +188 -0
- package/src/session-runtime/workspace/transitions.mjs +108 -1066
- package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
- package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
- package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
- package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
- package/src/session-runtime/workspace/worktree-tool.mjs +54 -54
- package/src/standalone/daemon-desktop-runtime.mjs +2 -0
- package/src/standalone/daemon-session-control.mjs +151 -136
- package/src/standalone/daemon.mjs +7 -0
- package/src/standalone/desktop-service-registry.mjs +9 -0
- package/src/standalone/local-session-runtime.mjs +5 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
- package/src/standalone/session-client.mjs +5 -1
- package/src/standalone/session-frame-stream.mjs +83 -11
- package/src/standalone/session-protocol.mjs +4 -0
- package/src/standalone/session-service/agent-tree.mjs +45 -9
- package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
- package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
- package/src/standalone/session-service/stored-reader.mjs +127 -40
- package/src/tui/App.jsx +154 -164
- package/src/tui/app/app-view/floating-panel.jsx +3 -0
- package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
- package/src/tui/app/clipboard.mjs +21 -10
- package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
- package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
- package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
- package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
- package/src/tui/app/provider-setup/oauth-actions.mjs +25 -6
- package/src/tui/app/provider-setup-picker.mjs +20 -13
- package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
- package/src/tui/app/slash-dispatch.mjs +1 -1
- package/src/tui/app/use-btw-panel.mjs +29 -0
- package/src/tui/app/use-context-panel-refresh.mjs +31 -0
- package/src/tui/app/use-model-catalog-cache.mjs +13 -10
- package/src/tui/app/use-mouse-input.mjs +11 -0
- package/src/tui/app/use-onboarding-start.mjs +8 -3
- package/src/tui/app/use-panel-transition.mjs +10 -1
- package/src/tui/app/use-pasted-buffers.mjs +28 -20
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
- package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
- package/src/tui/app/use-prompt-queue-history.mjs +3 -2
- package/src/tui/app/use-selection-focus.mjs +11 -2
- package/src/tui/app/use-selection-geometry.mjs +18 -9
- package/src/tui/app/use-selection-paint.mjs +4 -4
- package/src/tui/app/use-selection-stitch.mjs +3 -3
- package/src/tui/app/use-slash-palette.mjs +62 -0
- package/src/tui/app/use-terminal-chrome.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +47 -32
- package/src/tui/app/use-transcript-follow.mjs +12 -4
- package/src/tui/app/use-transcript-scroll.mjs +46 -17
- package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
- package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
- package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
- package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
- package/src/tui/app/use-transcript-window.mjs +21 -11
- package/src/tui/app/use-tui-ready.mjs +18 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
- package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
- package/src/tui/components/Picker.jsx +27 -3
- package/src/tui/components/SignInUrl.jsx +68 -0
- package/src/tui/components/TextEntryPanel.jsx +76 -46
- package/src/tui/components/sign-in-url-layout.mjs +24 -0
- package/src/tui/dist/index.mjs +1896 -1341
- package/src/tui/session/goal-turn-state.mjs +6 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +8 -0
- package/src/tui/session/session-api/intake/abort.mjs +2 -1
- package/src/tui/session/session-api/intake.mjs +2 -0
- package/src/tui/session/session-api/integrations.mjs +2 -2
- package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
- package/src/tui/session/session-api/settings.mjs +2 -2
- package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
- package/src/tui/session/transcript-spill.mjs +92 -4
- package/src/tui/session/transcript-store.mjs +2 -0
- package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
|
@@ -42,6 +42,140 @@ function callerAttachmentParts(rt, args, callerCwd, surface) {
|
|
|
42
42
|
}).parts;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/** `session` actions that act on another session through session control
|
|
46
|
+
* (list, bots, read, send, cancel, delete, create, status of a target). */
|
|
47
|
+
async function runSessionControl(
|
|
48
|
+
{ rt, executeSessionControl, listBots, getSessionManager },
|
|
49
|
+
args,
|
|
50
|
+
{ action, target, callerSessionId, callerCwd }
|
|
51
|
+
) {
|
|
52
|
+
// `callerAgentId`: the bot whose conversation gets results reported
|
|
53
|
+
// back; `callerBotId`: the bot the caller acts as, attended or not,
|
|
54
|
+
// for what only a bot's own sessions allow (an effort change).
|
|
55
|
+
const callerAgentId = attendedAgentOf(rt.session);
|
|
56
|
+
const acting = resolveSessionMode({ session: rt.session });
|
|
57
|
+
const callerBotId = acting.mode === AGENT_MODE ? acting.agentId : '';
|
|
58
|
+
if (action === 'create' && !callerAgentId) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
'session: create belongs to a bot conversation; delegated workers, agent task sessions and schedule/webhook runs cannot create agent task sessions'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
// A bot id names that bot's home chat; every check below and in the
|
|
64
|
+
// control runs on the chat it resolves to.
|
|
65
|
+
const sessionTarget = BOT_TARGET_ACTIONS.has(action) && target ? await resolveBotTarget(target, listBots) : target;
|
|
66
|
+
// A task session reaches its agent only through its reported result.
|
|
67
|
+
const origin = rt.session?.taskOrigin;
|
|
68
|
+
if (
|
|
69
|
+
action === 'send' &&
|
|
70
|
+
origin &&
|
|
71
|
+
(sessionTarget === origin.sessionId || agentIdOfHomeSession(sessionTarget) === origin.agentId)
|
|
72
|
+
) {
|
|
73
|
+
throw new Error('session: a task session reports to its agent only through its result');
|
|
74
|
+
}
|
|
75
|
+
if (typeof executeSessionControl !== 'function') {
|
|
76
|
+
throw new Error('session: cross-session control is unavailable in this runtime');
|
|
77
|
+
}
|
|
78
|
+
const { attachments, ...rest } = args || {};
|
|
79
|
+
if (attachments !== undefined && action !== 'send' && action !== 'create') {
|
|
80
|
+
throw new Error(`session: attachments apply to send and create, not action=${action}`);
|
|
81
|
+
}
|
|
82
|
+
const attachmentParts = callerAttachmentParts(rt, args, callerCwd, 'session');
|
|
83
|
+
return JSON.stringify(
|
|
84
|
+
await executeSessionControl(
|
|
85
|
+
{ ...rest, action, ...(sessionTarget !== target ? { session_id: sessionTarget } : {}) },
|
|
86
|
+
{
|
|
87
|
+
callerSessionId,
|
|
88
|
+
callerAgentId,
|
|
89
|
+
callerBotId,
|
|
90
|
+
callerCwd,
|
|
91
|
+
...(action === 'send' || action === 'create'
|
|
92
|
+
? {
|
|
93
|
+
callerHop: turnHopOf(rt.session),
|
|
94
|
+
// The bot chain this turn acts in (bot-chain.mjs), checked
|
|
95
|
+
// against the target and carried to the turn it starts —
|
|
96
|
+
// only when the sender is a bot conversation (a bot's chat,
|
|
97
|
+
// room seat or run); any other session hands work to any
|
|
98
|
+
// bot as before, whatever started its turn (the hop limit
|
|
99
|
+
// still bounds a loop).
|
|
100
|
+
callerBotChain: callerBotId
|
|
101
|
+
? delegationChainOf(callerSessionId, (id) =>
|
|
102
|
+
id === rt.session?.id ? rt.session : getSessionManager()?.getSession?.(id) || null
|
|
103
|
+
)
|
|
104
|
+
: [],
|
|
105
|
+
}
|
|
106
|
+
: {}),
|
|
107
|
+
// Whether a create may provision a worktree (worktree-access.mjs).
|
|
108
|
+
...(action === 'create' ? { callerScope: worktreeCallerScope(rt.session) } : {}),
|
|
109
|
+
// Which bots and sessions it may see (bot-status.mjs).
|
|
110
|
+
...(action === 'bots' || action === 'list' ? { callerScope: scheduleCallerScope(rt.session) } : {}),
|
|
111
|
+
...(attachmentParts.length ? { attachmentParts } : {}),
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** `session action=rename`: the AI title of the Lead session, mirrored onto the live session. */
|
|
118
|
+
async function renameSession({ rt, getSessionManager }, action, sessionId, args) {
|
|
119
|
+
if (rt.config?.aiSessionTitles === false) throw new Error('session: AI session titles are disabled in settings');
|
|
120
|
+
const title = clean(args?.title);
|
|
121
|
+
if (!title) throw new Error('session: title is required for action=rename');
|
|
122
|
+
const result = await getSessionManager()?.updateSessionAiTitle(sessionId, title);
|
|
123
|
+
if (!result?.ok) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
result?.reason === 'user-title' ? USER_TITLE_ERROR : `session: rename failed (${result?.reason || 'unavailable'})`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
// Mirror the store's truth onto the live session, never the AI title
|
|
129
|
+
// itself: a manual rename that landed while the save was awaited wins.
|
|
130
|
+
const stored = getSessionManager()?.getSession?.(sessionId);
|
|
131
|
+
if (stored && rt.session?.id === sessionId && stored !== rt.session) {
|
|
132
|
+
rt.session.title = stored.title;
|
|
133
|
+
rt.session.titleLocked = stored.titleLocked;
|
|
134
|
+
rt.session.titleSource = stored.titleSource;
|
|
135
|
+
rt.session.titleUpdatedAt = stored.titleUpdatedAt;
|
|
136
|
+
}
|
|
137
|
+
if (stored && stored.titleSource === 'user') throw new Error(USER_TITLE_ERROR);
|
|
138
|
+
return JSON.stringify({ action, sessionId, title: result.title }, null, 2);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** `session action=fork`: inherit the Lead session into a new one. */
|
|
142
|
+
async function forkSession(getLifecycleApi, action, sessionId, args) {
|
|
143
|
+
const lifecycle = getLifecycleApi();
|
|
144
|
+
if (typeof lifecycle?.inheritToNewSession !== 'function') {
|
|
145
|
+
throw new Error('session: fork is unavailable in this runtime');
|
|
146
|
+
}
|
|
147
|
+
const result = await lifecycle.inheritToNewSession(sessionId, {
|
|
148
|
+
compact: args?.compact === true,
|
|
149
|
+
titleSuffix: ' (Fork)',
|
|
150
|
+
});
|
|
151
|
+
return JSON.stringify(
|
|
152
|
+
{ action, sourceSessionId: sessionId, sessionId: result.sessionId, compacted: result.compacted === true },
|
|
153
|
+
null,
|
|
154
|
+
2
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** `session action=status`: the current session's identity, folder and route. */
|
|
159
|
+
function sessionStatusJson(rt, callerCtx, currentCwd) {
|
|
160
|
+
const current = rt.session;
|
|
161
|
+
return JSON.stringify(
|
|
162
|
+
{
|
|
163
|
+
sessionId: callerCtx?.callerSessionId || current?.id || null,
|
|
164
|
+
title: current?.title || null,
|
|
165
|
+
titleSource: current?.titleSource || null,
|
|
166
|
+
cwd: currentCwd,
|
|
167
|
+
project: current?.workspace?.projectPath || current?.desktopSession?.projectPath || currentCwd,
|
|
168
|
+
provider: current?.provider || null,
|
|
169
|
+
model: current?.model || null,
|
|
170
|
+
effort: current?.effort || null,
|
|
171
|
+
autoEffort: current?.autoEffort !== false,
|
|
172
|
+
fast: current?.fast === true,
|
|
173
|
+
},
|
|
174
|
+
null,
|
|
175
|
+
2
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
45
179
|
export function createWorkspaceToolHandlers({
|
|
46
180
|
rt,
|
|
47
181
|
goalRuntime,
|
|
@@ -101,70 +235,12 @@ export function createWorkspaceToolHandlers({
|
|
|
101
235
|
(action === 'status' && target)
|
|
102
236
|
) {
|
|
103
237
|
const callerSessionId = assertLead(action, callerCtx);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (action === 'create' && !callerAgentId) {
|
|
111
|
-
throw new Error(
|
|
112
|
-
'session: create belongs to a bot conversation; delegated workers, agent task sessions and schedule/webhook runs cannot create agent task sessions'
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
// A bot id names that bot's home chat; every check below and in the
|
|
116
|
-
// control runs on the chat it resolves to.
|
|
117
|
-
const sessionTarget =
|
|
118
|
-
BOT_TARGET_ACTIONS.has(action) && target ? await resolveBotTarget(target, listBots) : target;
|
|
119
|
-
// A task session reaches its agent only through its reported result.
|
|
120
|
-
const origin = rt.session?.taskOrigin;
|
|
121
|
-
if (
|
|
122
|
-
action === 'send' &&
|
|
123
|
-
origin &&
|
|
124
|
-
(sessionTarget === origin.sessionId || agentIdOfHomeSession(sessionTarget) === origin.agentId)
|
|
125
|
-
) {
|
|
126
|
-
throw new Error('session: a task session reports to its agent only through its result');
|
|
127
|
-
}
|
|
128
|
-
if (typeof executeSessionControl !== 'function') {
|
|
129
|
-
throw new Error('session: cross-session control is unavailable in this runtime');
|
|
130
|
-
}
|
|
131
|
-
const { attachments, ...rest } = args || {};
|
|
132
|
-
if (attachments !== undefined && action !== 'send' && action !== 'create') {
|
|
133
|
-
throw new Error(`session: attachments apply to send and create, not action=${action}`);
|
|
134
|
-
}
|
|
135
|
-
const attachmentParts = callerAttachmentParts(rt, args, callerCwd, 'session');
|
|
136
|
-
return JSON.stringify(
|
|
137
|
-
await executeSessionControl(
|
|
138
|
-
{ ...rest, action, ...(sessionTarget !== target ? { session_id: sessionTarget } : {}) },
|
|
139
|
-
{
|
|
140
|
-
callerSessionId,
|
|
141
|
-
callerAgentId,
|
|
142
|
-
callerBotId,
|
|
143
|
-
callerCwd,
|
|
144
|
-
...(action === 'send' || action === 'create'
|
|
145
|
-
? {
|
|
146
|
-
callerHop: turnHopOf(rt.session),
|
|
147
|
-
// The bot chain this turn acts in (bot-chain.mjs), checked
|
|
148
|
-
// against the target and carried to the turn it starts —
|
|
149
|
-
// only when the sender is a bot conversation (a bot's chat,
|
|
150
|
-
// room seat or run); any other session hands work to any
|
|
151
|
-
// bot as before, whatever started its turn (the hop limit
|
|
152
|
-
// still bounds a loop).
|
|
153
|
-
callerBotChain: callerBotId
|
|
154
|
-
? delegationChainOf(callerSessionId, (id) =>
|
|
155
|
-
id === rt.session?.id ? rt.session : getSessionManager()?.getSession?.(id) || null
|
|
156
|
-
)
|
|
157
|
-
: [],
|
|
158
|
-
}
|
|
159
|
-
: {}),
|
|
160
|
-
// Whether a create may provision a worktree (worktree-access.mjs).
|
|
161
|
-
...(action === 'create' ? { callerScope: worktreeCallerScope(rt.session) } : {}),
|
|
162
|
-
// Which bots and sessions it may see (bot-status.mjs).
|
|
163
|
-
...(action === 'bots' || action === 'list' ? { callerScope: scheduleCallerScope(rt.session) } : {}),
|
|
164
|
-
...(attachmentParts.length ? { attachmentParts } : {}),
|
|
165
|
-
}
|
|
166
|
-
)
|
|
167
|
-
);
|
|
238
|
+
return runSessionControl({ rt, executeSessionControl, listBots, getSessionManager }, args, {
|
|
239
|
+
action,
|
|
240
|
+
target,
|
|
241
|
+
callerSessionId,
|
|
242
|
+
callerCwd,
|
|
243
|
+
});
|
|
168
244
|
}
|
|
169
245
|
// Mixy's proposals to the user (mixy-heartbeat.mjs): kept so none repeats.
|
|
170
246
|
if (action === 'suggest') {
|
|
@@ -184,28 +260,7 @@ export function createWorkspaceToolHandlers({
|
|
|
184
260
|
if (action === 'status' || action === 'list-projects' || action === 'set-project') assertLead(action, callerCtx);
|
|
185
261
|
if (action === 'rename') {
|
|
186
262
|
const sessionId = assertLead(action, callerCtx);
|
|
187
|
-
|
|
188
|
-
const title = clean(args?.title);
|
|
189
|
-
if (!title) throw new Error('session: title is required for action=rename');
|
|
190
|
-
const result = await getSessionManager()?.updateSessionAiTitle(sessionId, title);
|
|
191
|
-
if (!result?.ok) {
|
|
192
|
-
throw new Error(
|
|
193
|
-
result?.reason === 'user-title'
|
|
194
|
-
? USER_TITLE_ERROR
|
|
195
|
-
: `session: rename failed (${result?.reason || 'unavailable'})`
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
// Mirror the store's truth onto the live session, never the AI title
|
|
199
|
-
// itself: a manual rename that landed while the save was awaited wins.
|
|
200
|
-
const stored = getSessionManager()?.getSession?.(sessionId);
|
|
201
|
-
if (stored && rt.session?.id === sessionId && stored !== rt.session) {
|
|
202
|
-
rt.session.title = stored.title;
|
|
203
|
-
rt.session.titleLocked = stored.titleLocked;
|
|
204
|
-
rt.session.titleSource = stored.titleSource;
|
|
205
|
-
rt.session.titleUpdatedAt = stored.titleUpdatedAt;
|
|
206
|
-
}
|
|
207
|
-
if (stored && stored.titleSource === 'user') throw new Error(USER_TITLE_ERROR);
|
|
208
|
-
return JSON.stringify({ action, sessionId, title: result.title }, null, 2);
|
|
263
|
+
return renameSession({ rt, getSessionManager }, action, sessionId, args);
|
|
209
264
|
}
|
|
210
265
|
if (action === 'compact') {
|
|
211
266
|
const sessionId = assertLead(action, callerCtx);
|
|
@@ -219,38 +274,10 @@ export function createWorkspaceToolHandlers({
|
|
|
219
274
|
}
|
|
220
275
|
if (action === 'fork') {
|
|
221
276
|
const sessionId = assertLead(action, callerCtx);
|
|
222
|
-
|
|
223
|
-
if (typeof lifecycle?.inheritToNewSession !== 'function') {
|
|
224
|
-
throw new Error('session: fork is unavailable in this runtime');
|
|
225
|
-
}
|
|
226
|
-
const result = await lifecycle.inheritToNewSession(sessionId, {
|
|
227
|
-
compact: args?.compact === true,
|
|
228
|
-
titleSuffix: ' (Fork)',
|
|
229
|
-
});
|
|
230
|
-
return JSON.stringify(
|
|
231
|
-
{ action, sourceSessionId: sessionId, sessionId: result.sessionId, compacted: result.compacted === true },
|
|
232
|
-
null,
|
|
233
|
-
2
|
|
234
|
-
);
|
|
277
|
+
return forkSession(getLifecycleApi, action, sessionId, args);
|
|
235
278
|
}
|
|
236
279
|
if (action === 'status') {
|
|
237
|
-
|
|
238
|
-
return JSON.stringify(
|
|
239
|
-
{
|
|
240
|
-
sessionId: callerCtx?.callerSessionId || current?.id || null,
|
|
241
|
-
title: current?.title || null,
|
|
242
|
-
titleSource: current?.titleSource || null,
|
|
243
|
-
cwd: currentCwd,
|
|
244
|
-
project: current?.workspace?.projectPath || current?.desktopSession?.projectPath || currentCwd,
|
|
245
|
-
provider: current?.provider || null,
|
|
246
|
-
model: current?.model || null,
|
|
247
|
-
effort: current?.effort || null,
|
|
248
|
-
autoEffort: current?.autoEffort !== false,
|
|
249
|
-
fast: current?.fast === true,
|
|
250
|
-
},
|
|
251
|
-
null,
|
|
252
|
-
2
|
|
253
|
-
);
|
|
280
|
+
return sessionStatusJson(rt, callerCtx, currentCwd);
|
|
254
281
|
}
|
|
255
282
|
if (action === 'list-projects') {
|
|
256
283
|
return JSON.stringify(
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* tombstone barrier and the turn abort.
|
|
5
5
|
*/
|
|
6
6
|
import { SessionClosedError } from '../../../runtime/agent/orchestrator/session/manager/session-errors.mjs';
|
|
7
|
+
import { armTurnTimeout } from '../../../runtime/agent/orchestrator/session/manager/turn-reap.mjs';
|
|
7
8
|
import { isScratchSession } from '../shared.mjs';
|
|
8
9
|
|
|
9
10
|
// SessionEnd reason mapped to standard values ('clear'/'exit' where
|
|
@@ -88,11 +89,15 @@ export function closeCanonicalSession(deps, reason = 'canonical-session-close',
|
|
|
88
89
|
return true;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
/** `options.timeout` is the progress watchdog's explicit "this abort is a
|
|
93
|
+
* timeout": the reap is armed before either abort runs, while the turn is
|
|
94
|
+
* still un-aborted (the outer abort cascades into the manager's controller). */
|
|
95
|
+
export function abortRuntime(deps, reason = 'cli-abort', { timeout = false } = {}) {
|
|
92
96
|
const { getSession, getReservedSessionId, abortActiveTurns, mgr } = deps;
|
|
93
97
|
const session = getSession();
|
|
94
98
|
const sessionId = session?.id || getReservedSessionId?.() || 'pending';
|
|
95
99
|
const abortError = new SessionClosedError(sessionId, `runtime abort (reason=${reason})`, reason);
|
|
100
|
+
if (timeout && session?.id) armTurnTimeout(session.id);
|
|
96
101
|
let outerAborted = false;
|
|
97
102
|
try {
|
|
98
103
|
outerAborted = abortActiveTurns?.(abortError) === true;
|
|
@@ -281,6 +281,6 @@ export function createTeardown(deps, { ingestSessionIntoMemory, closeSurfaceSess
|
|
|
281
281
|
return {
|
|
282
282
|
closeCanonicalSession: (reason, options) => closeCanonicalSession(deps, reason, options),
|
|
283
283
|
close,
|
|
284
|
-
abort: (reason) => abortRuntime(deps, reason),
|
|
284
|
+
abort: (reason, options) => abortRuntime(deps, reason, options),
|
|
285
285
|
};
|
|
286
286
|
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
connectorOfServer,
|
|
10
10
|
gmailAppPasswordAccount,
|
|
11
11
|
} from '../../runtime/shared/connector-catalog.mjs';
|
|
12
|
-
import { connectorAccountHealth } from '../../runtime/connectors/connector-accounts-mcp.mjs';
|
|
12
|
+
import { connectorAccountHealth, connectorAccountInUse } from '../../runtime/connectors/connector-accounts-mcp.mjs';
|
|
13
13
|
import { googleOAuthClient } from '../../runtime/shared/google-oauth-client.mjs';
|
|
14
14
|
import { envFlag } from '../../runtime/shared/env.mjs';
|
|
15
15
|
import { resolveMcpServerCatalog } from './plugin-servers.mjs';
|
|
@@ -119,10 +119,12 @@ export function createMcpServerCatalog({
|
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/** One account of a
|
|
123
|
-
* `{ name, email, enabled?, appPassword?, oauth?, ownClient?, error?, authRequired? }`
|
|
124
|
-
* (`enabled: false` only for an account that is switched off
|
|
125
|
-
|
|
122
|
+
/** One account of a connector server, as its row lists it:
|
|
123
|
+
* `{ name, email, enabled?, inUse?, appPassword?, oauth?, ownClient?, error?, authRequired? }`
|
|
124
|
+
* (`enabled: false` only for an account that is switched off; `inUse:
|
|
125
|
+
* true` only for the connector's last-used account, the one a call
|
|
126
|
+
* without a conversation history uses). */
|
|
127
|
+
function accountRow(accountCfg, enabled, inUse) {
|
|
126
128
|
const address = gmailAppPasswordAccount(accountCfg);
|
|
127
129
|
const oauth = address ? null : mcpClient.getMcpServerOAuthState?.(accountCfg);
|
|
128
130
|
const state = connectorAccountHealth(accountCfg.name);
|
|
@@ -130,6 +132,7 @@ export function createMcpServerCatalog({
|
|
|
130
132
|
return {
|
|
131
133
|
name: accountCfg.name,
|
|
132
134
|
...(enabled ? {} : { enabled: false }),
|
|
135
|
+
...(inUse ? { inUse: true } : {}),
|
|
133
136
|
email: address || oauth?.account || state?.email || '',
|
|
134
137
|
...(address ? { appPassword: { account: address } } : {}),
|
|
135
138
|
...(oauth ? { oauth } : {}),
|
|
@@ -152,11 +155,14 @@ export function createMcpServerCatalog({
|
|
|
152
155
|
failures.get(name) || (dead ? { msg: live.error || live.lastError || 'MCP server is not alive' } : undefined);
|
|
153
156
|
const entry = entries[name] || {};
|
|
154
157
|
const loginPending = !(live && !dead) && mcpClient.isMcpLoginPending?.(name, getMcpScopeId()) === true;
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const stored = entry.owner ? [] : connectorAccountsOf(cfg);
|
|
159
|
+
// The connector's last-used account (none when that one was removed),
|
|
160
|
+
// else its topmost switched-on one.
|
|
161
|
+
const recorded = stored.length ? connectorAccountInUse(connectorOfServer(cfg)) : '';
|
|
162
|
+
const inUse = recorded || stored.find(connectorAccountEnabled)?.name;
|
|
163
|
+
const accounts = stored.map((account) =>
|
|
164
|
+
accountRow(connectorAccountServer(cfg, account), connectorAccountEnabled(account), account.name === inUse)
|
|
165
|
+
);
|
|
160
166
|
// A connector server none of whose accounts is signed in needs a sign-in, not a repair.
|
|
161
167
|
const accountsSignIn = accounts.length > 0 && accounts.every((account) => account.authRequired === true);
|
|
162
168
|
const status = mcpServerStatus(
|
|
@@ -79,8 +79,11 @@ export function createMcpServerInput({ mcpClient, getCurrentCwd }) {
|
|
|
79
79
|
const type = clean(input.type).toLowerCase();
|
|
80
80
|
if (url) {
|
|
81
81
|
const kind = URL_TRANSPORT_TYPES.get(type) || (/^wss?:\/\//i.test(url) ? 'ws' : 'http');
|
|
82
|
-
const
|
|
82
|
+
const normalizedUrl =
|
|
83
83
|
typeof mcpClient.normalizeMcpTransportUrl === 'function' ? mcpClient.normalizeMcpTransportUrl(url, kind) : url;
|
|
84
|
+
// URL parsing adds a "/" to a bare origin (`https://mcp.stripe.com`):
|
|
85
|
+
// keep the address as it was given, as the catalog and legacy entries do.
|
|
86
|
+
const secureUrl = normalizedUrl === `${url}/` ? url : normalizedUrl;
|
|
84
87
|
return { name, config: withOptionalHeaders({ type: kind, url: secureUrl }) };
|
|
85
88
|
}
|
|
86
89
|
const command = clean(input.command);
|
|
@@ -635,7 +635,7 @@ export function createMixyHeartbeat({
|
|
|
635
635
|
now = Date.now,
|
|
636
636
|
path = defaultStatePath(),
|
|
637
637
|
isEnabled = async () =>
|
|
638
|
-
(await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).heartbeat
|
|
638
|
+
(await import('../runtime/agent/orchestrator/config.mjs')).loadConfig({ secrets: false }).heartbeat === true,
|
|
639
639
|
// Whether the representative bot exists (mixy-presence.mjs, the rule the
|
|
640
640
|
// desktop and her routines follow): without it no check runs at all.
|
|
641
641
|
mixyExists = async () => (await import('./routine-owner.mjs')).readMixyExists(),
|
|
@@ -61,8 +61,8 @@ export function createLoginApi({ cfgMod, awaitKeychainPrewarm, reloadFullConfig,
|
|
|
61
61
|
reloadFullConfig();
|
|
62
62
|
return completeOAuthLogin(result);
|
|
63
63
|
},
|
|
64
|
-
async saveProviderApiKey(providerId, secret) {
|
|
65
|
-
const result = await saveProviderApiKey(cfgMod, providerId, secret);
|
|
64
|
+
async saveProviderApiKey(providerId, secret, options) {
|
|
65
|
+
const result = await saveProviderApiKey(cfgMod, providerId, secret, options);
|
|
66
66
|
refresh.afterCredentialChange();
|
|
67
67
|
return result;
|
|
68
68
|
},
|
|
@@ -14,9 +14,11 @@ import { migrateMergedConnectorServers } from './connector-merge-migration.mjs';
|
|
|
14
14
|
import {
|
|
15
15
|
connectorAccountServers,
|
|
16
16
|
connectorAccountsOf,
|
|
17
|
+
connectorAppPasswordOnly,
|
|
17
18
|
connectorById,
|
|
18
|
-
|
|
19
|
+
connectorServerInput,
|
|
19
20
|
gmailAppPasswordAccount,
|
|
21
|
+
gmailAppPasswordServerInput,
|
|
20
22
|
mergeConnectorAccountServers,
|
|
21
23
|
} from '../runtime/shared/connector-catalog.mjs';
|
|
22
24
|
import { saveMcpOAuthClientSecret } from '../runtime/agent/orchestrator/mcp/oauth-client.mjs';
|
|
@@ -75,14 +77,24 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
75
77
|
resolveMcpCatalog,
|
|
76
78
|
pluginMcpServer,
|
|
77
79
|
});
|
|
78
|
-
const {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
const {
|
|
81
|
+
storeOAuthClientSecret,
|
|
82
|
+
storeGmailAppPassword,
|
|
83
|
+
releaseOAuth,
|
|
84
|
+
forgetUnusedGmailAppPassword,
|
|
85
|
+
forgetStoredSecrets,
|
|
86
|
+
releaseAccount,
|
|
87
|
+
} = createMcpServerSecrets({
|
|
88
|
+
gmailAppPassword,
|
|
89
|
+
saveOAuthClientSecret,
|
|
90
|
+
clearMcpServerOAuth,
|
|
91
|
+
resolveConfiguredMcpCatalog,
|
|
92
|
+
});
|
|
93
|
+
const {
|
|
94
|
+
addConnectorAccount: joinConnectorAccount,
|
|
95
|
+
saveConnectorAccount,
|
|
96
|
+
setConnectorAccountEnabled,
|
|
97
|
+
} = createConnectorAccountEdits({
|
|
86
98
|
normalizeMcpServerInput,
|
|
87
99
|
pluginMcpServer,
|
|
88
100
|
sync,
|
|
@@ -91,16 +103,65 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
91
103
|
storeOAuthClientSecret,
|
|
92
104
|
storeGmailAppPassword,
|
|
93
105
|
forgetUnusedGmailAppPassword,
|
|
106
|
+
forgetStoredSecrets,
|
|
94
107
|
releaseOAuth,
|
|
95
108
|
});
|
|
96
109
|
// Peers reload config from disk on receipt: land the write before publishing.
|
|
97
110
|
const persisted = async () => {
|
|
98
111
|
await flushSkillsSave?.();
|
|
99
112
|
};
|
|
113
|
+
// One account addition of a connector at a time, each reading the servers
|
|
114
|
+
// the one before it saved: two at once never take the same name, nor does
|
|
115
|
+
// one's save drop the other's account.
|
|
116
|
+
const connectorAdds = new Map();
|
|
117
|
+
const oneAddAtATime = (connector, run) => {
|
|
118
|
+
const next = (connectorAdds.get(connector) || Promise.resolve()).then(run, run);
|
|
119
|
+
const settled = next.then(
|
|
120
|
+
() => {},
|
|
121
|
+
() => {}
|
|
122
|
+
);
|
|
123
|
+
connectorAdds.set(connector, settled);
|
|
124
|
+
void settled.then(() => {
|
|
125
|
+
if (connectorAdds.get(connector) === settled) connectorAdds.delete(connector);
|
|
126
|
+
});
|
|
127
|
+
return next;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
async function addMcpServer(input = {}) {
|
|
131
|
+
// A catalog connector's account (as its own server config) joins that
|
|
132
|
+
// connector's one server.
|
|
133
|
+
const connector = clean(input.connector);
|
|
134
|
+
if (connector && !Array.isArray(input.accounts)) return oneAddAtATime(connector, () => addServerNow(input));
|
|
135
|
+
return addServerNow(input);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function addServerNow(input) {
|
|
139
|
+
const { name, config: serverConfig } = normalizeMcpServerInput(input);
|
|
140
|
+
// A catalog connector's server keeps its catalog id (connector-catalog.mjs).
|
|
141
|
+
const connector = clean(input.connector);
|
|
142
|
+
if (connector) {
|
|
143
|
+
if (!connectorById(connector)) throw new Error(`Unknown connector: ${connector}`);
|
|
144
|
+
serverConfig.connector = connector;
|
|
145
|
+
}
|
|
146
|
+
const servers = userServers();
|
|
147
|
+
if (connector && !Array.isArray(input.accounts)) {
|
|
148
|
+
return joinConnectorAccount(servers, connector, name, serverConfig, input);
|
|
149
|
+
}
|
|
150
|
+
if (Object.hasOwn(servers, name) || accountTarget(servers, name)) {
|
|
151
|
+
throw nameTakenError(servers, name);
|
|
152
|
+
}
|
|
153
|
+
const owned = pluginMcpServer(name);
|
|
154
|
+
if (owned) throw new Error(`MCP server already exists: ${name} (provided by plugin "${owned.owner.id}")`);
|
|
155
|
+
await storeOAuthClientSecret(input, serverConfig);
|
|
156
|
+
await storeGmailAppPassword(input, serverConfig, true);
|
|
157
|
+
saveServers({ ...servers, [name]: serverConfig });
|
|
158
|
+
const status = await sync.reconnectMcpAndAnnounce({ only: name, enabled: serverConfig.enabled !== false });
|
|
159
|
+
return { name, status };
|
|
160
|
+
}
|
|
100
161
|
|
|
101
162
|
return {
|
|
102
|
-
/** One-time, idempotent: merge the one-account servers of a
|
|
103
|
-
* connector (`
|
|
163
|
+
/** One-time, idempotent: merge the one-account servers of a catalog
|
|
164
|
+
* connector (`notion`, `notion-2`…) into one server holding the accounts
|
|
104
165
|
* (connector-catalog.mjs mergeConnectorAccountServers). Every account
|
|
105
166
|
* keeps its address, so its stored sign-in and app password stay as
|
|
106
167
|
* they are. Settings keyed by a merged-away server name go with it:
|
|
@@ -155,30 +216,32 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
155
216
|
},
|
|
156
217
|
// Sign-in / sign-out (resource-mcp-oauth-api.mjs), in this key position.
|
|
157
218
|
...oauthApi,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
219
|
+
addMcpServer,
|
|
220
|
+
/** One more account of catalog connector `input.connector`, connected as
|
|
221
|
+
* its catalog entry says: `key` for an API-key service (Hugging Face,
|
|
222
|
+
* Stripe; Postgres takes its connection string), `email` and
|
|
223
|
+
* `appPassword` for Gmail, nothing more for a sign-in service (the
|
|
224
|
+
* account then signs in with beginMcpOAuthLogin(account)); the user's own
|
|
225
|
+
* OAuth client rides on `oauthClientId` / `oauthClientSecret`. The account
|
|
226
|
+
* gets a free name (`notion`, `notion-2`…) and joins the connector's one
|
|
227
|
+
* server, which is made when there is none. Resolves `{ name, account,
|
|
228
|
+
* status }`: the server's name and the new account's (for a Gmail
|
|
229
|
+
* address already connected, that account, which took the new password). */
|
|
230
|
+
async addConnectorAccount(input = {}) {
|
|
231
|
+
const entry = connectorById(clean(input.connector));
|
|
232
|
+
if (!entry) throw new Error(`Unknown connector: ${clean(input.connector)}`);
|
|
233
|
+
// Its name is picked from the servers the additions before it saved.
|
|
234
|
+
return oneAddAtATime(entry.id, () => {
|
|
235
|
+
const name = freeName(userServers(), entry.id);
|
|
236
|
+
const account = connectorAppPasswordOnly(entry)
|
|
237
|
+
? gmailAppPasswordServerInput(name, input.email, input.appPassword)
|
|
238
|
+
: connectorServerInput(entry, name, input.key);
|
|
239
|
+
const ownClient = clean(input.oauthClientId);
|
|
240
|
+
return addServerNow({
|
|
241
|
+
...account,
|
|
242
|
+
...(ownClient ? { oauthClientId: ownClient, oauthClientSecret: input.oauthClientSecret } : {}),
|
|
243
|
+
});
|
|
244
|
+
});
|
|
182
245
|
},
|
|
183
246
|
async saveMcpServer(input = {}) {
|
|
184
247
|
const servers = userServers();
|