mixdog 1.2.0 → 1.2.1
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/README.md +7 -5
- package/package.json +1 -1
- package/src/defaults/shipped-history.json +4 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +13 -3
- package/src/defaults/skills/visualize/SKILL.md +12 -9
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -2
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config.mjs +10 -4
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +86 -8
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +5 -1
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +65 -7
- package/src/runtime/agent/orchestrator/mcp/client.mjs +128 -44
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +19 -11
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +9 -3
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +267 -84
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +6 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +19 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +21 -2
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/shared/atomic-file.mjs +25 -4
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
- package/src/runtime/shared/llm/model-catalog.mjs +0 -12
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +6 -0
- package/src/runtime/shared/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +124 -20
- package/src/runtime/shared/provider-api-key.mjs +12 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/turn-worktree-snapshot.mjs +25 -5
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +55 -0
- package/src/session-runtime/boot/config.mjs +5 -2
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +29 -18
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/mcp-glue/connect.mjs +57 -10
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +115 -30
- package/src/session-runtime/mcp-glue.mjs +22 -2
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +9 -2
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/resource-extension-sync.mjs +20 -5
- package/src/session-runtime/resource-mcp-api.mjs +101 -77
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/setup-tool/executor.mjs +12 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/daemon-stored-session-views.mjs +11 -3
- package/src/standalone/daemon.mjs +41 -4
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
- package/src/tui/App.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +6 -0
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +507 -171
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/session-api/extensions.mjs +4 -2
- package/src/tui/session/session-api/settings.mjs +7 -0
- package/src/tui/session/session-flow/drain.mjs +14 -4
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn.mjs +17 -0
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
// Effective MCP server set and its status projection:
|
|
2
|
-
//
|
|
1
|
+
// Effective MCP server set and its status projection: the user's configured
|
|
2
|
+
// servers plus the servers registered plugins declare (resolved live, with
|
|
3
|
+
// explicit owner metadata), per-server config reads, and the live/failed rollup.
|
|
3
4
|
import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
4
5
|
import { envFlag } from '../../runtime/shared/env.mjs';
|
|
6
|
+
import { resolveMcpServerCatalog } from './plugin-servers.mjs';
|
|
5
7
|
|
|
6
8
|
function mcpServerStatus(cfg, live, fail) {
|
|
7
9
|
if (cfg?.enabled === false) return 'disabled';
|
|
@@ -9,7 +11,23 @@ function mcpServerStatus(cfg, live, fail) {
|
|
|
9
11
|
return fail ? 'failed' : 'disconnected';
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
const EMPTY_CATALOG = { servers: {}, sources: {}, entries: {}, plugins: {}, warnings: [], adoption: null };
|
|
15
|
+
|
|
16
|
+
// Internal markers on a resolved plugin config; never part of an editable config.
|
|
17
|
+
function withoutMarkers(cfg) {
|
|
18
|
+
const { _mixdogStrictEnv, _mixdogRootsFollowScope, _mixdogPlugin, ...rest } = cfg || {};
|
|
19
|
+
return rest;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function createMcpServerCatalog({
|
|
23
|
+
mcpClient,
|
|
24
|
+
getConfig,
|
|
25
|
+
getCurrentCwd = () => '',
|
|
26
|
+
getMcpScopeId,
|
|
27
|
+
getPluginDataDir = () => '',
|
|
28
|
+
listPlugins = () => [],
|
|
29
|
+
state,
|
|
30
|
+
}) {
|
|
13
31
|
function mcpTransportLabel(cfg = {}) {
|
|
14
32
|
if (cfg.autoDetect) return `autoDetect:${cfg.autoDetect}`;
|
|
15
33
|
try {
|
|
@@ -19,33 +37,59 @@ export function createMcpServerCatalog({ mcpClient, getConfig, getMcpScopeId, st
|
|
|
19
37
|
}
|
|
20
38
|
}
|
|
21
39
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
servers[name] = {
|
|
35
|
-
...cfg,
|
|
36
|
-
...(cfg?._mixdogPluginDisabled === true ? { enabled: false } : {}),
|
|
37
|
-
};
|
|
38
|
-
sources[name] = cfg?.env?.MIXDOG_PLUGIN_ROOT ? 'plugin' : 'config';
|
|
40
|
+
/** Everything this runtime would connect, resolved now. MCP is
|
|
41
|
+
* machine-global: project `.mcp.json` files and per-project overrides stay
|
|
42
|
+
* outside the chain; only plugin servers without a declared cwd follow this
|
|
43
|
+
* runtime's session cwd. */
|
|
44
|
+
function resolveCatalog() {
|
|
45
|
+
if (envFlag('MIXDOG_DISABLE_MCP')) return EMPTY_CATALOG;
|
|
46
|
+
let plugins = [];
|
|
47
|
+
let registryWarning = null;
|
|
48
|
+
try {
|
|
49
|
+
plugins = listPlugins() || [];
|
|
50
|
+
} catch (error) {
|
|
51
|
+
registryWarning = { message: `plugin MCP servers unavailable: ${error?.message || String(error)}` };
|
|
39
52
|
}
|
|
53
|
+
const catalog = resolveMcpServerCatalog({
|
|
54
|
+
config: getConfig() || {},
|
|
55
|
+
plugins,
|
|
56
|
+
cwd: clean(getCurrentCwd?.()),
|
|
57
|
+
dataDir: clean(getPluginDataDir?.()),
|
|
58
|
+
});
|
|
59
|
+
if (registryWarning) catalog.warnings.unshift(registryWarning);
|
|
60
|
+
return catalog;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function resolveEffectiveMcpServers() {
|
|
64
|
+
const { servers, sources } = resolveCatalog();
|
|
40
65
|
return { servers, sources };
|
|
41
66
|
}
|
|
42
67
|
|
|
68
|
+
/** Plugin owner of an effective server name, or null for a configured one. */
|
|
69
|
+
function pluginServer(name) {
|
|
70
|
+
const catalog = resolveCatalog();
|
|
71
|
+
const entry = catalog.entries[clean(name)];
|
|
72
|
+
return entry?.owner ? { ...entry, plugin: catalog.plugins[entry.owner.id] } : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
43
75
|
function getMcpServerConfig(name) {
|
|
44
76
|
const serverName = clean(name);
|
|
45
77
|
if (!serverName) throw new Error('MCP server name is required');
|
|
46
|
-
const { servers, sources } =
|
|
78
|
+
const { servers, sources, entries } = resolveCatalog();
|
|
47
79
|
const effective = servers[serverName];
|
|
48
80
|
if (!effective) throw new Error(`MCP server not configured: ${serverName}`);
|
|
81
|
+
// An unadopted plugin copy has an owner but stays the user's editable config.
|
|
82
|
+
const owner = sources[serverName] === 'plugin' ? entries[serverName]?.owner || null : null;
|
|
83
|
+
if (owner) {
|
|
84
|
+
return {
|
|
85
|
+
name: serverName,
|
|
86
|
+
source: 'plugin',
|
|
87
|
+
enabled: effective.enabled !== false,
|
|
88
|
+
owner,
|
|
89
|
+
readOnly: true,
|
|
90
|
+
config: withoutMarkers({ ...effective, enabled: undefined }),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
49
93
|
const raw = getConfig()?.mcpServers?.[serverName];
|
|
50
94
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
51
95
|
throw new Error(`MCP server config is unavailable: ${serverName}`);
|
|
@@ -58,11 +102,8 @@ export function createMcpServerCatalog({ mcpClient, getConfig, getMcpScopeId, st
|
|
|
58
102
|
};
|
|
59
103
|
}
|
|
60
104
|
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
return { servers: [], configuredCount: 0, connectedCount: 0, failedCount: 0 };
|
|
64
|
-
}
|
|
65
|
-
const { servers: configured, sources } = resolveEffectiveMcpServers();
|
|
105
|
+
function statusRows(catalog) {
|
|
106
|
+
const { servers: configured, sources, entries } = catalog;
|
|
66
107
|
const connected = new Map((mcpClient.getMcpServerStatus?.(getMcpScopeId()) || []).map((row) => [row.name, row]));
|
|
67
108
|
const failures = new Map((state.mcpFailures || []).map((row) => [row.name, row]));
|
|
68
109
|
const servers = [];
|
|
@@ -73,7 +114,8 @@ export function createMcpServerCatalog({ mcpClient, getConfig, getMcpScopeId, st
|
|
|
73
114
|
const dead = live && (live.alive === false || live.connected === false);
|
|
74
115
|
const fail =
|
|
75
116
|
failures.get(name) || (dead ? { msg: live.error || live.lastError || 'MCP server is not alive' } : undefined);
|
|
76
|
-
|
|
117
|
+
const entry = entries[name] || {};
|
|
118
|
+
const row = {
|
|
77
119
|
name,
|
|
78
120
|
configured: true,
|
|
79
121
|
enabled: cfg?.enabled !== false,
|
|
@@ -84,21 +126,64 @@ export function createMcpServerCatalog({ mcpClient, getConfig, getMcpScopeId, st
|
|
|
84
126
|
tools: live?.tools || [],
|
|
85
127
|
capabilities: live?.capabilities || { tools: false, prompts: false, resources: false },
|
|
86
128
|
error: fail?.msg || null,
|
|
129
|
+
warnings: [...(entry.warnings || []), ...(live?.warnings || [])],
|
|
87
130
|
source: sources[name] || 'config',
|
|
88
|
-
}
|
|
131
|
+
};
|
|
132
|
+
if (entry.owner) {
|
|
133
|
+
row.owner = entry.owner;
|
|
134
|
+
row.pluginEnabled = entry.pluginEnabled;
|
|
135
|
+
row.serverEnabled = entry.serverEnabled;
|
|
136
|
+
if (!entry.pluginEnabled) row.disabledReason = 'plugin disabled';
|
|
137
|
+
else if (!entry.serverEnabled) row.disabledReason = 'server disabled';
|
|
138
|
+
}
|
|
139
|
+
servers.push(row);
|
|
89
140
|
connected.delete(name);
|
|
90
141
|
}
|
|
91
142
|
for (const live of connected.values()) {
|
|
92
143
|
servers.push({ ...live, configured: false, status: 'connected' });
|
|
93
144
|
}
|
|
94
145
|
servers.sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
146
|
+
return servers;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function mcpStatus() {
|
|
150
|
+
if (envFlag('MIXDOG_DISABLE_MCP')) {
|
|
151
|
+
return { servers: [], configuredCount: 0, connectedCount: 0, failedCount: 0 };
|
|
152
|
+
}
|
|
153
|
+
const catalog = resolveCatalog();
|
|
154
|
+
const servers = statusRows(catalog);
|
|
155
|
+
const declarationErrors = Object.values(catalog.plugins).flatMap((plugin) =>
|
|
156
|
+
plugin.errors.map((error) => ({ plugin: plugin.id, ...error }))
|
|
157
|
+
);
|
|
95
158
|
return {
|
|
96
159
|
servers,
|
|
97
|
-
configuredCount: Object.keys(
|
|
160
|
+
configuredCount: Object.keys(catalog.servers).length,
|
|
98
161
|
connectedCount: servers.filter((row) => row.connected).length,
|
|
99
162
|
failedCount: servers.filter((row) => row.status === 'failed').length,
|
|
163
|
+
warnings: catalog.warnings,
|
|
164
|
+
declarationErrors,
|
|
100
165
|
};
|
|
101
166
|
}
|
|
102
167
|
|
|
103
|
-
|
|
168
|
+
/** Per plugin id: declaration format/errors and its server status rows. */
|
|
169
|
+
function pluginMcpSummary() {
|
|
170
|
+
const catalog = resolveCatalog();
|
|
171
|
+
const rows = new Map(statusRows(catalog).map((row) => [row.name, row]));
|
|
172
|
+
return new Map(
|
|
173
|
+
Object.values(catalog.plugins).map((plugin) => [
|
|
174
|
+
plugin.id,
|
|
175
|
+
{ format: plugin.format, errors: plugin.errors, servers: plugin.servers.map((name) => rows.get(name)) },
|
|
176
|
+
])
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
mcpTransportLabel,
|
|
182
|
+
resolveCatalog,
|
|
183
|
+
resolveEffectiveMcpServers,
|
|
184
|
+
pluginServer,
|
|
185
|
+
getMcpServerConfig,
|
|
186
|
+
mcpStatus,
|
|
187
|
+
pluginMcpSummary,
|
|
188
|
+
};
|
|
104
189
|
}
|
|
@@ -6,11 +6,28 @@ import { createMcpServerCatalog } from './mcp-glue/server-catalog.mjs';
|
|
|
6
6
|
import { createMcpConnect } from './mcp-glue/connect.mjs';
|
|
7
7
|
import { createMcpServerInput } from './mcp-glue/server-input.mjs';
|
|
8
8
|
|
|
9
|
-
export function createMcpGlue({
|
|
10
|
-
|
|
9
|
+
export function createMcpGlue({
|
|
10
|
+
mcpClient,
|
|
11
|
+
getConfig,
|
|
12
|
+
getCurrentCwd,
|
|
13
|
+
getMcpScopeId = () => null,
|
|
14
|
+
getPluginDataDir,
|
|
15
|
+
listPlugins,
|
|
16
|
+
state,
|
|
17
|
+
}) {
|
|
18
|
+
const catalog = createMcpServerCatalog({
|
|
19
|
+
mcpClient,
|
|
20
|
+
getConfig,
|
|
21
|
+
getCurrentCwd,
|
|
22
|
+
getMcpScopeId,
|
|
23
|
+
getPluginDataDir,
|
|
24
|
+
listPlugins,
|
|
25
|
+
state,
|
|
26
|
+
});
|
|
11
27
|
const connect = createMcpConnect({
|
|
12
28
|
mcpClient,
|
|
13
29
|
getMcpScopeId,
|
|
30
|
+
getCurrentCwd,
|
|
14
31
|
state,
|
|
15
32
|
resolveEffectiveMcpServers: catalog.resolveEffectiveMcpServers,
|
|
16
33
|
mcpStatus: catalog.mcpStatus,
|
|
@@ -19,6 +36,9 @@ export function createMcpGlue({ mcpClient, getConfig, getCurrentCwd, getMcpScope
|
|
|
19
36
|
|
|
20
37
|
return {
|
|
21
38
|
mcpStatus: catalog.mcpStatus,
|
|
39
|
+
resolveMcpCatalog: catalog.resolveCatalog,
|
|
40
|
+
pluginMcpServer: catalog.pluginServer,
|
|
41
|
+
pluginMcpSummary: catalog.pluginMcpSummary,
|
|
22
42
|
getMcpServerConfig: catalog.getMcpServerConfig,
|
|
23
43
|
connectConfiguredMcp: connect.connectConfiguredMcp,
|
|
24
44
|
awaitInitialMcpConnect: connect.awaitInitialMcpConnect,
|
|
@@ -21,7 +21,8 @@ export function createNewSessionConfig({
|
|
|
21
21
|
await flushPendingSessionConfigWrites();
|
|
22
22
|
await sharedCfgMod.pendingConfigWrites();
|
|
23
23
|
sharedCfgMod.invalidateConfigReadCache();
|
|
24
|
-
const
|
|
24
|
+
const mcpSettings = () => [rt.config.mcpServers, rt.config.pluginMcp];
|
|
25
|
+
const previousMcp = mcpSettings();
|
|
25
26
|
// Headless callers inject a deliberately isolated config. Never replace
|
|
26
27
|
// their explicit policy with the interactive profile on disk.
|
|
27
28
|
if (!initialConfig || typeof initialConfig !== 'object') {
|
|
@@ -34,7 +35,7 @@ export function createNewSessionConfig({
|
|
|
34
35
|
invalidatePreSessionToolSurface();
|
|
35
36
|
invalidateSkills();
|
|
36
37
|
await configureEmbedding(sharedCfgMod.readSection('memory')?.embedding || {});
|
|
37
|
-
if (!firstSession || !isDeepStrictEqual(previousMcp,
|
|
38
|
+
if (!firstSession || !isDeepStrictEqual(previousMcp, mcpSettings())) {
|
|
38
39
|
// Reused runtimes may still own old connections. This reset is scoped to
|
|
39
40
|
// the runtime that is constructing the new session, not to its peers.
|
|
40
41
|
await connectConfiguredMcp({ reset: true });
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
// pending queue so the model sees it next turn. The delivered registry keeps
|
|
4
4
|
// those two paths from double-injecting the same completion.
|
|
5
5
|
import { modelVisibleToolCompletionMessage } from '../runtime/shared/tool-execution-contract.mjs';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
PENDING_MODE_TASK_NOTIFICATION,
|
|
8
|
+
markCompletionEntry,
|
|
9
|
+
} from '../runtime/agent/orchestrator/session/manager/pending-messages.mjs';
|
|
7
10
|
import {
|
|
8
11
|
isDeliveredCompletion,
|
|
9
12
|
logDuplicateSkip,
|
|
@@ -144,7 +147,11 @@ export function createNotificationBus({ listeners, mgr, onCompletionQueued, getO
|
|
|
144
147
|
if (!open) return false;
|
|
145
148
|
if (typeof mgr.enqueuePendingMessage !== 'function') return false;
|
|
146
149
|
try {
|
|
147
|
-
|
|
150
|
+
// A channel notification (webhook event rules, schedule injects, relays)
|
|
151
|
+
// is the runtime reporting, exactly as an attached TUI queues it: a
|
|
152
|
+
// task notification, so text inside it — fenced external webhook values
|
|
153
|
+
// included — never drives explicit skill selection.
|
|
154
|
+
return mgr.enqueuePendingMessage(targetSessionId, { content, mode: PENDING_MODE_TASK_NOTIFICATION }) > 0;
|
|
148
155
|
} catch {
|
|
149
156
|
return false;
|
|
150
157
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// provider-auth/accounts.mjs — the per-provider account roster and account
|
|
2
2
|
// switching, with the bounded background usage sweep that refreshes quotas.
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
listProviderAccounts,
|
|
5
|
+
materializeProviderAccounts,
|
|
6
|
+
updateProviderAccounts,
|
|
7
|
+
} from '../services/provider-admin.mjs';
|
|
4
8
|
import { getProvider } from '../../runtime/agent/orchestrator/providers/registry.mjs';
|
|
5
9
|
import { fetchOAuthUsageSnapshot } from '../../runtime/agent/orchestrator/providers/oauth-usage.mjs';
|
|
6
10
|
|
|
@@ -11,21 +15,23 @@ export function createAccountApi(
|
|
|
11
15
|
// One in-flight usage sweep per provider; a reopened picker reuses it.
|
|
12
16
|
const accountUsageSweeps = new Map();
|
|
13
17
|
|
|
18
|
+
async function measureAccountUsage(providerId, provider, accounts, force = false) {
|
|
19
|
+
await awaitKeychainPrewarm();
|
|
20
|
+
for (let offset = 0; offset < accounts.length; offset += 4) {
|
|
21
|
+
await Promise.all(
|
|
22
|
+
accounts.slice(offset, offset + 4).map((account) => {
|
|
23
|
+
const target = { provider: providerId, accountId: account.id };
|
|
24
|
+
return fetchOAuthUsageSnapshot(target, provider.forAccount(account.id), () => {}, { force }).catch(
|
|
25
|
+
() => null
|
|
26
|
+
);
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
14
32
|
async function sweepAccountUsage(providerId, provider, accounts) {
|
|
15
33
|
try {
|
|
16
|
-
await
|
|
17
|
-
for (let offset = 0; offset < accounts.length; offset += 4) {
|
|
18
|
-
await Promise.all(
|
|
19
|
-
accounts
|
|
20
|
-
.slice(offset, offset + 4)
|
|
21
|
-
.map((account) =>
|
|
22
|
-
fetchOAuthUsageSnapshot(
|
|
23
|
-
{ provider: providerId, accountId: account.id },
|
|
24
|
-
provider.forAccount(account.id)
|
|
25
|
-
).catch(() => null)
|
|
26
|
-
)
|
|
27
|
-
);
|
|
28
|
-
}
|
|
34
|
+
await measureAccountUsage(providerId, provider, accounts);
|
|
29
35
|
} finally {
|
|
30
36
|
accountUsageSweeps.delete(providerId);
|
|
31
37
|
}
|
|
@@ -69,8 +75,20 @@ export function createAccountApi(
|
|
|
69
75
|
return pool;
|
|
70
76
|
},
|
|
71
77
|
async updateProviderAccounts(providerId, change) {
|
|
78
|
+
// `{ refreshUsage: true }` re-measures every account now (the roster's
|
|
79
|
+
// "Refresh usage") and changes nothing else in the roster. The implicit
|
|
80
|
+
// signed-in account is stored first so its measurement has a row to land on.
|
|
81
|
+
if (change?.refreshUsage === true && Object.keys(change).length === 1) {
|
|
82
|
+
const provider = getProvider(providerId);
|
|
83
|
+
if (typeof provider?.forAccount === 'function') {
|
|
84
|
+
await awaitKeychainPrewarm();
|
|
85
|
+
const { accounts } = await materializeProviderAccounts(providerId);
|
|
86
|
+
await measureAccountUsage(providerId, provider, accounts, true);
|
|
87
|
+
}
|
|
88
|
+
return listProviderAccounts(providerId);
|
|
89
|
+
}
|
|
72
90
|
await awaitKeychainPrewarm();
|
|
73
|
-
const result = updateProviderAccounts(providerId, change);
|
|
91
|
+
const result = await updateProviderAccounts(providerId, change);
|
|
74
92
|
reloadFullConfig();
|
|
75
93
|
invalidateProviderCaches();
|
|
76
94
|
warmProviderModelCache();
|
|
@@ -38,7 +38,11 @@ export function createWebSearchCatalog({
|
|
|
38
38
|
try {
|
|
39
39
|
let models = null;
|
|
40
40
|
if (forceRefresh && typeof provider._refreshModelCache === 'function') {
|
|
41
|
-
|
|
41
|
+
try {
|
|
42
|
+
models = await provider._refreshModelCache();
|
|
43
|
+
} catch {
|
|
44
|
+
models = null; // fall back to listModels below
|
|
45
|
+
}
|
|
42
46
|
}
|
|
43
47
|
if (!Array.isArray(models)) models = await provider.listModels();
|
|
44
48
|
if (!Array.isArray(models)) return [];
|
|
@@ -67,16 +67,17 @@ export function createProviderReadiness({ rt, keychain, getReg, getWarmProviderM
|
|
|
67
67
|
await awaitKeychainPrewarm();
|
|
68
68
|
if (rt.closeRequested) throw new Error('runtime is closing');
|
|
69
69
|
const initKey = providerInitCacheKey(providerConfig);
|
|
70
|
-
const existing = providerInitPromises.get(initKey);
|
|
71
|
-
if (existing) return await existing;
|
|
72
70
|
// Provider initialization is idempotent for one normalized config. Keep
|
|
73
71
|
// the fulfilled promise, not only the in-flight one: session resume used
|
|
74
72
|
// to rerun registry/keychain setup on every click even though neither the
|
|
75
73
|
// provider config nor the registry had changed. Auth/config mutations call
|
|
76
74
|
// invalidateProviderCaches(), which clears this gate and preserves the
|
|
77
75
|
// existing refresh semantics.
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
let providerInitPromise = providerInitPromises.get(initKey);
|
|
77
|
+
if (!providerInitPromise) {
|
|
78
|
+
providerInitPromise = Promise.resolve().then(() => getReg().initProviders(providerConfig));
|
|
79
|
+
providerInitPromises.set(initKey, providerInitPromise);
|
|
80
|
+
}
|
|
80
81
|
let result;
|
|
81
82
|
try {
|
|
82
83
|
result = await providerInitPromise;
|
|
@@ -84,13 +85,18 @@ export function createProviderReadiness({ rt, keychain, getReg, getWarmProviderM
|
|
|
84
85
|
if (providerInitPromises.get(initKey) === providerInitPromise) providerInitPromises.delete(initKey);
|
|
85
86
|
throw error;
|
|
86
87
|
}
|
|
87
|
-
if (!rt.
|
|
88
|
-
rt.startupProviderCatalogRefreshStarted = true;
|
|
89
|
-
rt.startupProviderCatalogRefreshPending = true;
|
|
88
|
+
if (!rt.closeRequested) {
|
|
90
89
|
try {
|
|
91
|
-
|
|
90
|
+
// The registry memoizes the refresh per catalog epoch; a provider set
|
|
91
|
+
// change yields a new promise, which gets its own completion handling.
|
|
92
|
+
const refresh = getReg().refreshProviderCatalogsOnStartup();
|
|
93
|
+
if (refresh === rt.startupProviderCatalogRefreshObserved) return result;
|
|
94
|
+
rt.startupProviderCatalogRefreshObserved = refresh;
|
|
95
|
+
rt.startupProviderCatalogRefreshPending = true;
|
|
96
|
+
const isLatest = () => rt.startupProviderCatalogRefreshObserved === refresh;
|
|
97
|
+
void Promise.resolve(refresh)
|
|
92
98
|
.then(() => {
|
|
93
|
-
rt.startupProviderCatalogRefreshPending = false;
|
|
99
|
+
if (isLatest()) rt.startupProviderCatalogRefreshPending = false;
|
|
94
100
|
if (rt.closeRequested) return;
|
|
95
101
|
// Fresh catalog rows invalidate model-derived caches, but the
|
|
96
102
|
// already initialized provider registry remains valid.
|
|
@@ -103,7 +109,7 @@ export function createProviderReadiness({ rt, keychain, getReg, getWarmProviderM
|
|
|
103
109
|
bootProfile('provider-catalogs:refresh-ready');
|
|
104
110
|
})
|
|
105
111
|
.catch((error) => {
|
|
106
|
-
rt.startupProviderCatalogRefreshPending = false;
|
|
112
|
+
if (isLatest()) rt.startupProviderCatalogRefreshPending = false;
|
|
107
113
|
bootProfile('provider-catalogs:refresh-failed', { error: error?.message || String(error) });
|
|
108
114
|
});
|
|
109
115
|
bootProfile('provider-catalogs:refresh-started');
|
|
@@ -28,11 +28,13 @@ export function createExtensionSync({
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
// Apply per-server MCP changes in order
|
|
31
|
+
// Apply per-server MCP changes in order, then reconcile: a configured server
|
|
32
|
+
// added, renamed or removed can move a plugin server to another name, and
|
|
33
|
+
// only the reconcile connects it there and drops the stale name. Servers
|
|
34
|
+
// whose resolved config is unchanged stay untouched.
|
|
32
35
|
async function applyMcpChanges(changes) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return status;
|
|
36
|
+
for (const { name, enabled } of changes) await connectConfiguredMcp({ only: name, enabled });
|
|
37
|
+
return connectConfiguredMcp({ reconcile: true });
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
async function refreshState(kind, detail = null) {
|
|
@@ -41,7 +43,11 @@ export function createExtensionSync({
|
|
|
41
43
|
const mcpChanges = kind === 'mcp' ? detail?.mcpChanges : null;
|
|
42
44
|
if (Array.isArray(mcpChanges)) {
|
|
43
45
|
await applyMcpChanges(mcpChanges);
|
|
44
|
-
} else if (kind === '
|
|
46
|
+
} else if (kind === 'plugins') {
|
|
47
|
+
// Plugin servers are derived from the registry: connect what is new or
|
|
48
|
+
// changed, drop what is gone or disabled, leave the rest connected.
|
|
49
|
+
await connectConfiguredMcp({ reconcile: true });
|
|
50
|
+
} else if (kind === 'mcp') {
|
|
45
51
|
await connectConfiguredMcp({ reset: true });
|
|
46
52
|
}
|
|
47
53
|
await refreshSurface(kind);
|
|
@@ -101,12 +107,21 @@ export function createExtensionSync({
|
|
|
101
107
|
return status;
|
|
102
108
|
}
|
|
103
109
|
|
|
110
|
+
/** After a plugin registry change: reconcile this runtime's MCP connections
|
|
111
|
+
* with the derived server set first, then let peers do the same. */
|
|
112
|
+
async function reconcilePluginsAndAnnounce() {
|
|
113
|
+
const status = await connectConfiguredMcp({ reconcile: true });
|
|
114
|
+
await announce('plugins');
|
|
115
|
+
return status;
|
|
116
|
+
}
|
|
117
|
+
|
|
104
118
|
return {
|
|
105
119
|
refreshSurface,
|
|
106
120
|
publish,
|
|
107
121
|
scheduleMcpToggle,
|
|
108
122
|
announce,
|
|
109
123
|
reconnectMcpAndAnnounce,
|
|
124
|
+
reconcilePluginsAndAnnounce,
|
|
110
125
|
dispose: () => subscription.unsubscribe(),
|
|
111
126
|
};
|
|
112
127
|
}
|