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
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// The interpreter a built-in skill's bundled scripts run on.
|
|
2
|
+
//
|
|
3
|
+
// `${MIXDOG_SKILL_DIR}` is a real directory every process can read, but the
|
|
4
|
+
// pptx scripts import the runtime by relative path (../../../../runtime/...)
|
|
5
|
+
// and its native dependencies (sharp, @napi-rs/canvas, pdf.js). A packaged
|
|
6
|
+
// desktop keeps those only inside runtime.asar, which plain Node cannot open,
|
|
7
|
+
// so `node "<dir>/scripts/x.mjs"` works from an npm install and fails there.
|
|
8
|
+
//
|
|
9
|
+
// So the daemon that runs the model's shells hands them its OWN runtime: a
|
|
10
|
+
// per-installation directory, first on every model shell's PATH, holds
|
|
11
|
+
// `mixdog-skill-node` launchers that run this process's executable (Electron
|
|
12
|
+
// in node mode, or node) on src/skill-script-entry.mjs, which maps the script
|
|
13
|
+
// back into the archive before importing it. Each daemon builds its own shell
|
|
14
|
+
// env, so an SSH-remote or mobile session gets its execution host's runtime.
|
|
15
|
+
// Launchers carry two paths and nothing else: no credentials, no daemon
|
|
16
|
+
// identity.
|
|
17
|
+
import { createHash } from 'node:crypto';
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync } from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { writeFileAtomicSync } from './atomic-file.mjs';
|
|
22
|
+
import { resolvePluginData } from './plugin-paths.mjs';
|
|
23
|
+
|
|
24
|
+
/** This module's package root (src/runtime/shared → the package). */
|
|
25
|
+
export const SKILL_SCRIPT_PACKAGE_ROOT = path.resolve(fileURLToPath(new URL('.', import.meta.url)), '..', '..', '..');
|
|
26
|
+
|
|
27
|
+
function pathApi(platform) {
|
|
28
|
+
return platform === 'win32' ? path.win32 : path.posix;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The `…/runtime.asar` prefix of a location inside the runtime archive, or null. */
|
|
32
|
+
export function archiveRootOf(location, platform = process.platform) {
|
|
33
|
+
const sep = platform === 'win32' ? '[\\\\/]' : '/';
|
|
34
|
+
const match = new RegExp(`^(.*?${sep}runtime\\.asar)(?=${sep}|$)`).exec(String(location || ''));
|
|
35
|
+
return match ? pathApi(platform).normalize(match[1]) : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The path a script must be imported from. A built-in skill's directory is
|
|
40
|
+
* handed out under `runtime.asar.unpacked` (skillExecutionDir), but its
|
|
41
|
+
* relative imports only resolve from the archive, so a script under the
|
|
42
|
+
* sidecar of the archive that holds this package maps back into it. Source
|
|
43
|
+
* trees and npm installs keep the real path (symlinks resolved, as the ESM
|
|
44
|
+
* loader resolves them for import.meta.url).
|
|
45
|
+
*/
|
|
46
|
+
export function canonicalSkillScriptPath(
|
|
47
|
+
target,
|
|
48
|
+
{
|
|
49
|
+
packageRoot = SKILL_SCRIPT_PACKAGE_ROOT,
|
|
50
|
+
platform = process.platform,
|
|
51
|
+
cwd = process.cwd(),
|
|
52
|
+
realpath = realpathSync,
|
|
53
|
+
} = {}
|
|
54
|
+
) {
|
|
55
|
+
const api = pathApi(platform);
|
|
56
|
+
let location = api.resolve(cwd, String(target));
|
|
57
|
+
try {
|
|
58
|
+
location = realpath(location);
|
|
59
|
+
} catch {
|
|
60
|
+
// A missing script fails at import with its own path in the message.
|
|
61
|
+
}
|
|
62
|
+
const archive = archiveRootOf(packageRoot, platform);
|
|
63
|
+
if (!archive) return location;
|
|
64
|
+
const sidecar = `${archive}.unpacked${api.sep}`;
|
|
65
|
+
const fold = (value) => (platform === 'win32' ? value.replaceAll('/', '\\').toLowerCase() : value);
|
|
66
|
+
if (!fold(location).startsWith(fold(sidecar))) return location;
|
|
67
|
+
return `${archive}${api.sep}${location.slice(sidecar.length)}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The runtime this process would hand a skill script to. */
|
|
71
|
+
export function skillScriptRuntime({
|
|
72
|
+
execPath = process.execPath,
|
|
73
|
+
versions = process.versions,
|
|
74
|
+
packageRoot = SKILL_SCRIPT_PACKAGE_ROOT,
|
|
75
|
+
platform = process.platform,
|
|
76
|
+
} = {}) {
|
|
77
|
+
return {
|
|
78
|
+
platform,
|
|
79
|
+
interpreter: execPath,
|
|
80
|
+
// Electron's own version marker, not ELECTRON_RUN_AS_NODE: model shells
|
|
81
|
+
// scrub that variable, and a daemon may have inherited it without being
|
|
82
|
+
// Electron at all.
|
|
83
|
+
electron: Boolean(versions?.electron),
|
|
84
|
+
packageRoot,
|
|
85
|
+
entry: pathApi(platform).join(packageRoot, 'src', 'skill-script-entry.mjs'),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function batchQuote(value) {
|
|
90
|
+
return `"${String(value).replaceAll('%', '%%')}"`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function posixQuote(value) {
|
|
94
|
+
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The command `${MIXDOG_SKILL_NODE}` renders to. It is the same word in every
|
|
99
|
+
* shell: model shells find it on PATH (withSkillLauncherPath), as the
|
|
100
|
+
* extensionless sh launcher (bash, sh, zsh, Git Bash) or as the .cmd beside
|
|
101
|
+
* it (cmd through PATHEXT on Windows). PowerShell model commands define a
|
|
102
|
+
* function of the same name first (renderPowerShellSkillFunction), which
|
|
103
|
+
* takes precedence over both.
|
|
104
|
+
*/
|
|
105
|
+
export const SKILL_LAUNCHER_COMMAND = 'mixdog-skill-node';
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* sh launcher text. Electron node mode is set only inside the launcher, for
|
|
109
|
+
* the one child it execs; every argument and the exit code pass through.
|
|
110
|
+
*/
|
|
111
|
+
export function renderPosixSkillLauncher({ interpreter, entry, electron }) {
|
|
112
|
+
return `${[
|
|
113
|
+
'#!/bin/sh',
|
|
114
|
+
'# Mixdog skill-script launcher, generated for one installation. Do not edit.',
|
|
115
|
+
...(electron ? ['ELECTRON_RUN_AS_NODE=1', 'export ELECTRON_RUN_AS_NODE'] : []),
|
|
116
|
+
`exec ${posixQuote(interpreter)} ${posixQuote(entry)} "$@"`,
|
|
117
|
+
].join('\n')}\n`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** .cmd launcher text, same contract as the sh launcher. */
|
|
121
|
+
export function renderWindowsSkillLauncher({ interpreter, entry, electron }) {
|
|
122
|
+
// cmd decodes a batch file in the console code page line by line, so a
|
|
123
|
+
// non-ASCII path needs UTF-8 for the lines after chcp; the caller's code
|
|
124
|
+
// page is restored before exit.
|
|
125
|
+
const unicode = /\P{ASCII}/u.test(`${interpreter}${entry}`);
|
|
126
|
+
return `${[
|
|
127
|
+
'@echo off',
|
|
128
|
+
'rem Mixdog skill-script launcher, generated for one installation. Do not edit.',
|
|
129
|
+
'setlocal EnableExtensions DisableDelayedExpansion',
|
|
130
|
+
...(unicode
|
|
131
|
+
? ['for /f "tokens=2 delims=:." %%C in (\'chcp\') do set "MIXDOG_SKILL_CP=%%C"', 'chcp 65001 >nul']
|
|
132
|
+
: []),
|
|
133
|
+
...(electron ? ['set "ELECTRON_RUN_AS_NODE=1"'] : []),
|
|
134
|
+
`${batchQuote(interpreter)} ${batchQuote(entry)} %*`,
|
|
135
|
+
'set "MIXDOG_SKILL_EXIT=%ERRORLEVEL%"',
|
|
136
|
+
...(unicode ? ['if defined MIXDOG_SKILL_CP chcp %MIXDOG_SKILL_CP% >nul'] : []),
|
|
137
|
+
'exit /b %MIXDOG_SKILL_EXIT%',
|
|
138
|
+
].join('\r\n')}\r\n`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Environment variable carrying a PowerShell caller's arguments to the entry. */
|
|
142
|
+
export const SKILL_ARGV_ENV = 'MIXDOG_SKILL_ARGV';
|
|
143
|
+
const SKILL_ARGV_TAG = 'mixdog1';
|
|
144
|
+
|
|
145
|
+
function psQuote(value) {
|
|
146
|
+
// PowerShell also treats the typographic single quotes as quote characters.
|
|
147
|
+
return `'${String(value).replace(/['\u2018\u2019\u201a\u201b]/g, '$&$&')}'`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* One-line PowerShell definition of `mixdog-skill-node`, prepended to every
|
|
152
|
+
* PowerShell model command. Through PATH, PowerShell would reach the .cmd via
|
|
153
|
+
* `cmd.exe /c` with arguments it does not quote, so `'a&b'` ran `b`. The
|
|
154
|
+
* function runs this daemon's executable on the entry as a plain native
|
|
155
|
+
* command, so PowerShell's own native-command handling provides streaming,
|
|
156
|
+
* ordering, backpressure, line endings, `2>&1`/`2>$null`/`2>file`,
|
|
157
|
+
* `$LASTEXITCODE`, Ctrl-C and early stop. The caller's arguments never reach
|
|
158
|
+
* that command line: they travel in SKILL_ARGV_ENV (UTF-8 base64), because
|
|
159
|
+
* Windows PowerShell 5.1 drops empty native arguments and does not escape
|
|
160
|
+
* embedded double quotes. A failing script ends the command with its exit
|
|
161
|
+
* code (`exit`), because a PowerShell function cannot fail `$?` or the
|
|
162
|
+
* -Command exit code.
|
|
163
|
+
*
|
|
164
|
+
* SKILL_ARGV_ENV and (Electron daemons only) ELECTRON_RUN_AS_NODE are set on
|
|
165
|
+
* $env just for the call and restored in `finally`; the bootstrap deletes
|
|
166
|
+
* both before importing the script. Accepted tradeoff: while the call runs,
|
|
167
|
+
* a process a concurrent downstream pipeline stage starts can see them.
|
|
168
|
+
*/
|
|
169
|
+
export function renderPowerShellSkillFunction({ interpreter, entry, electron }) {
|
|
170
|
+
const names = [SKILL_ARGV_ENV, ...(electron ? ['ELECTRON_RUN_AS_NODE'] : [])];
|
|
171
|
+
const values = ['$__mxa', ...(electron ? ["'1'"] : [])];
|
|
172
|
+
const body = [
|
|
173
|
+
`$__mxa = '${SKILL_ARGV_TAG}'`,
|
|
174
|
+
"foreach ($__mxv in $args) { foreach ($__mxe in @($__mxv)) { if ($null -ne $__mxe) { $__mxa += ',' + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes([string]$__mxe)) } } }",
|
|
175
|
+
names.map((name, i) => `$__mxo${i} = $env:${name}`).join('; '),
|
|
176
|
+
[
|
|
177
|
+
'try {',
|
|
178
|
+
...names.map((name, i) => `$env:${name} = ${values[i]};`),
|
|
179
|
+
`& ${psQuote(interpreter)} ${psQuote(entry)}`,
|
|
180
|
+
'} finally {',
|
|
181
|
+
names.map((name, i) => `$env:${name} = $__mxo${i}`).join('; '),
|
|
182
|
+
'}',
|
|
183
|
+
].join(' '),
|
|
184
|
+
'if ($LASTEXITCODE) { exit $LASTEXITCODE }',
|
|
185
|
+
];
|
|
186
|
+
return `function ${SKILL_LAUNCHER_COMMAND} { ${body.join('; ')} }`;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The arguments a PowerShell caller sent through SKILL_ARGV_ENV, or null. */
|
|
190
|
+
export function decodeSkillArgv(value) {
|
|
191
|
+
const text = typeof value === 'string' ? value : '';
|
|
192
|
+
if (!text.startsWith(SKILL_ARGV_TAG)) return null;
|
|
193
|
+
const rest = text.slice(SKILL_ARGV_TAG.length);
|
|
194
|
+
if (!rest) return [];
|
|
195
|
+
return rest
|
|
196
|
+
.slice(1)
|
|
197
|
+
.split(',')
|
|
198
|
+
.map((item) => Buffer.from(item, 'base64').toString('utf8'));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Launcher files of `runtime`: the sh script everywhere, plus the .cmd on Windows. */
|
|
202
|
+
export function skillLauncherFiles(runtime) {
|
|
203
|
+
return [
|
|
204
|
+
{ name: SKILL_LAUNCHER_COMMAND, content: renderPosixSkillLauncher(runtime) },
|
|
205
|
+
...(runtime.platform === 'win32'
|
|
206
|
+
? [{ name: `${SKILL_LAUNCHER_COMMAND}.cmd`, content: renderWindowsSkillLauncher(runtime) }]
|
|
207
|
+
: []),
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Private per-installation launcher directory under the data dir. */
|
|
212
|
+
export function skillLauncherDir(runtime, dataDir = resolvePluginData()) {
|
|
213
|
+
const digest = createHash('sha256')
|
|
214
|
+
.update(JSON.stringify([runtime.platform, runtime.interpreter, runtime.entry, runtime.electron]))
|
|
215
|
+
.digest('hex')
|
|
216
|
+
.slice(0, 16);
|
|
217
|
+
return pathApi(runtime.platform).join(dataDir, 'skill-launchers', digest);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const ensuredDirs = new Set();
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Write (atomically) or refresh the launchers for `runtime`; returns their
|
|
224
|
+
* directory. A directory already verified by this process is only checked for
|
|
225
|
+
* presence, so the per-command shell env build stays a couple of stats.
|
|
226
|
+
*/
|
|
227
|
+
export function ensureSkillLauncherDir(runtime = skillScriptRuntime(), dataDir = resolvePluginData()) {
|
|
228
|
+
const dir = skillLauncherDir(runtime, dataDir);
|
|
229
|
+
const api = pathApi(runtime.platform);
|
|
230
|
+
const files = skillLauncherFiles(runtime);
|
|
231
|
+
if (ensuredDirs.has(dir) && files.every(({ name }) => existsSync(api.join(dir, name)))) return dir;
|
|
232
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
233
|
+
for (const { name, content } of files) {
|
|
234
|
+
const file = api.join(dir, name);
|
|
235
|
+
let current = null;
|
|
236
|
+
try {
|
|
237
|
+
current = readFileSync(file, 'utf8');
|
|
238
|
+
} catch {
|
|
239
|
+
// Not written yet.
|
|
240
|
+
}
|
|
241
|
+
if (current !== content) writeFileAtomicSync(file, content, { mode: 0o700 });
|
|
242
|
+
}
|
|
243
|
+
ensuredDirs.add(dir);
|
|
244
|
+
return dir;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* `env` with this installation's launcher directory first on PATH. Only model
|
|
249
|
+
* shell environments call this; the daemon's own env, hooks and internal
|
|
250
|
+
* workers keep their PATH. The directory holds nothing but the launchers, so
|
|
251
|
+
* no existing command is shadowed.
|
|
252
|
+
*/
|
|
253
|
+
export function withSkillLauncherPath(env, { runtime = skillScriptRuntime(), dataDir = resolvePluginData() } = {}) {
|
|
254
|
+
const dir = ensureSkillLauncherDir(runtime, dataDir);
|
|
255
|
+
const delimiter = runtime.platform === 'win32' ? ';' : ':';
|
|
256
|
+
// Windows env keys are case-insensitive and usually spelled `Path`.
|
|
257
|
+
const key =
|
|
258
|
+
(runtime.platform === 'win32' ? Object.keys(env).find((name) => name.toUpperCase() === 'PATH') : null) || 'PATH';
|
|
259
|
+
const entries = String(env[key] || '')
|
|
260
|
+
.split(delimiter)
|
|
261
|
+
.filter((entry) => entry && entry !== dir);
|
|
262
|
+
env[key] = [dir, ...entries].join(delimiter);
|
|
263
|
+
return env;
|
|
264
|
+
}
|
|
@@ -22,8 +22,39 @@ function rawToolName(name) {
|
|
|
22
22
|
return String(name || 'tool').replace(/^functions\./, '');
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
// Tool names shortened to fit provider limits -> their real server and tool,
|
|
26
|
+
// recorded when the name is built so name-only callers in this process
|
|
27
|
+
// resolve them. Bounded; the oldest entries go first.
|
|
28
|
+
const MCP_TOOL_IDENTITY_MAX = 4096;
|
|
29
|
+
const mcpToolIdentities = new Map();
|
|
30
|
+
|
|
31
|
+
export function rememberMcpToolIdentity(name, server, tool) {
|
|
32
|
+
const key = String(name || '');
|
|
33
|
+
if (!key) return;
|
|
34
|
+
mcpToolIdentities.delete(key);
|
|
35
|
+
mcpToolIdentities.set(key, { server: String(server || ''), tool: String(tool || '') });
|
|
36
|
+
if (mcpToolIdentities.size > MCP_TOOL_IDENTITY_MAX) mcpToolIdentities.delete(mcpToolIdentities.keys().next().value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Server and tool of a shortened MCP tool name, when this process built it. */
|
|
40
|
+
export function knownMcpToolIdentity(name) {
|
|
41
|
+
const known = mcpToolIdentities.get(rawToolName(name));
|
|
42
|
+
return known ? { ...known } : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** `{ server, tool }` of an MCP tool, or null. Takes a tool definition (its
|
|
46
|
+
* `mcpServerName`/`mcpToolName` metadata wins) or a name (a recorded
|
|
47
|
+
* shortened name first, else the `mcp__<server>__<tool>` form). */
|
|
48
|
+
export function parseMcpToolName(nameOrTool) {
|
|
49
|
+
if (nameOrTool && typeof nameOrTool === 'object') {
|
|
50
|
+
const server = String(nameOrTool.mcpServerName || '').trim();
|
|
51
|
+
const parsed = parseMcpToolName(nameOrTool.name);
|
|
52
|
+
if (!server) return parsed;
|
|
53
|
+
return { server, tool: String(nameOrTool.mcpToolName || '') || parsed?.tool || '' };
|
|
54
|
+
}
|
|
55
|
+
const text = rawToolName(nameOrTool);
|
|
56
|
+
const known = mcpToolIdentities.get(text);
|
|
57
|
+
if (known) return { ...known };
|
|
27
58
|
const match = /^mcp__(.+?)__(.+)$/.exec(text);
|
|
28
59
|
if (!match) return null;
|
|
29
60
|
return { server: match[1], tool: match[2] };
|
|
@@ -169,6 +200,8 @@ export function toolSearchDisplayLabel(args = {}) {
|
|
|
169
200
|
export function displayToolSearchTarget(value) {
|
|
170
201
|
const text = String(value || '').trim();
|
|
171
202
|
if (/^skill:/i.test(text)) return text.slice('skill:'.length);
|
|
203
|
+
const known = knownMcpToolIdentity(text);
|
|
204
|
+
if (known) return `${known.server}.${known.tool}`;
|
|
172
205
|
const mcp = /^mcp__(.*?)__(.+)$/.exec(text);
|
|
173
206
|
if (mcp) return `${mcp[1]}.${mcp[2]}`;
|
|
174
207
|
return stripToolPrefix(text);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
2
|
+
import { existsSync, lstatSync, realpathSync } from 'node:fs';
|
|
3
3
|
import { access, copyFile, lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
4
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
5
|
import { PATCH_EDIT_MAX_BYTES } from './bounded-file-read.mjs';
|
|
6
6
|
import { cleanString as clean } from './clean.mjs';
|
|
7
7
|
import { runGitOffThread } from './git-runner.mjs';
|
|
@@ -44,8 +44,26 @@ function pathKey(value) {
|
|
|
44
44
|
|
|
45
45
|
/** `resolve` plus symlinks of the nearest existing ancestor: git reports the
|
|
46
46
|
* worktree root canonically (macOS /var -> /private/var), callers may not. */
|
|
47
|
-
function canonicalPath(value) {
|
|
47
|
+
function canonicalPath(value, dirs = null) {
|
|
48
48
|
const full = resolve(value);
|
|
49
|
+
if (dirs) {
|
|
50
|
+
// Same result as the walk below without repeating it per file: the parent
|
|
51
|
+
// directory is canonicalized once per call; the final component only
|
|
52
|
+
// matters when it is itself a link (or its state is unknown).
|
|
53
|
+
const parent = dirname(full);
|
|
54
|
+
if (parent !== full) {
|
|
55
|
+
let canonicalParent = dirs.get(parent);
|
|
56
|
+
if (canonicalParent === undefined) {
|
|
57
|
+
canonicalParent = canonicalPath(parent);
|
|
58
|
+
dirs.set(parent, canonicalParent);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
if (!lstatSync(full).isSymbolicLink()) return join(canonicalParent, basename(full));
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') return join(canonicalParent, basename(full));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
49
67
|
let head = full;
|
|
50
68
|
while (!existsSync(head)) {
|
|
51
69
|
const parent = dirname(head);
|
|
@@ -139,8 +157,10 @@ function stateForRoot(root) {
|
|
|
139
157
|
/** Report written paths (or `null`: anything may have changed). The next
|
|
140
158
|
* review poll of an affected worktree captures instead of reusing. */
|
|
141
159
|
export function noteTurnWorktreeChange(paths = null) {
|
|
160
|
+
if (states.size === 0) return;
|
|
142
161
|
const list = Array.isArray(paths) ? paths.filter((value) => clean(value)) : [];
|
|
143
|
-
const
|
|
162
|
+
const dirs = new Map();
|
|
163
|
+
const keys = list.map((value) => pathKey(canonicalPath(value, dirs)));
|
|
144
164
|
for (const state of states.values()) {
|
|
145
165
|
const root = pathKey(state.root);
|
|
146
166
|
if (keys.length === 0 || keys.some((key) => key === root || key.startsWith(`${root}/`))) {
|
|
@@ -781,7 +801,7 @@ function scheduleShadowGc(state) {
|
|
|
781
801
|
async function removeShadowDir(gitDir, removed, kept) {
|
|
782
802
|
if (!(await exists(gitDir))) return;
|
|
783
803
|
await rm(gitDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }).catch(() => {});
|
|
784
|
-
(await exists(gitDir) ? kept : removed).push(gitDir);
|
|
804
|
+
((await exists(gitDir)) ? kept : removed).push(gitDir);
|
|
785
805
|
}
|
|
786
806
|
|
|
787
807
|
/** Drop every trace of one worktree root: serialize behind its pending capture
|
|
@@ -14,8 +14,11 @@ import { parentPort, Worker } from 'node:worker_threads';
|
|
|
14
14
|
* worker is detached before it is terminated, so no request is ever sent to
|
|
15
15
|
* (or lost with) a worker that is shutting down.
|
|
16
16
|
* `request.running()` reports whether a worker thread is currently attached.
|
|
17
|
+
*
|
|
18
|
+
* `onEvent` (optional): receives what the worker sends with postWorkerEvent
|
|
19
|
+
* (messages that answer no request).
|
|
17
20
|
*/
|
|
18
|
-
export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOptions } = {}) {
|
|
21
|
+
export function createWorkerRequestClient(url, { idleExitMs = 0, onEvent = null, ...workerOptions } = {}) {
|
|
19
22
|
let worker = null;
|
|
20
23
|
let nextRequestId = 0;
|
|
21
24
|
// Requests belong to the worker they were posted to: a replacement worker
|
|
@@ -50,7 +53,12 @@ export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOption
|
|
|
50
53
|
owned.clear();
|
|
51
54
|
for (const entry of entries) entry.reject(error);
|
|
52
55
|
};
|
|
53
|
-
created.on('message', (
|
|
56
|
+
created.on('message', (message) => {
|
|
57
|
+
if (message && Object.hasOwn(message, 'event')) {
|
|
58
|
+
onEvent?.(message.event);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const { id, result, error } = message;
|
|
54
62
|
const entry = owned.get(id);
|
|
55
63
|
if (!entry) return;
|
|
56
64
|
owned.delete(id);
|
|
@@ -109,6 +117,11 @@ export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOption
|
|
|
109
117
|
return request;
|
|
110
118
|
}
|
|
111
119
|
|
|
120
|
+
/** Worker side: send `event` to the client's `onEvent`, outside any request. */
|
|
121
|
+
export function postWorkerEvent(event) {
|
|
122
|
+
parentPort.postMessage({ event });
|
|
123
|
+
}
|
|
124
|
+
|
|
112
125
|
/** Worker side: answer every request with `handler(payload)` (sync or async). */
|
|
113
126
|
export function serveWorkerRequests(handler) {
|
|
114
127
|
parentPort.on('message', ({ id, payload }) => {
|
|
@@ -57,10 +57,16 @@ export function deliverAutomationCompletion(sessionId, text, meta = {}) {
|
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// Ask options for a follow-up turn fed by agent results: the runtime reporting
|
|
61
|
+
// worker output, not the user — stored as a task notification and never a
|
|
62
|
+
// source of explicit skill selection.
|
|
63
|
+
export const AUTOMATION_RESULT_TURN_OPTS = Object.freeze({ promptSource: 'task-notification' });
|
|
64
|
+
|
|
60
65
|
/**
|
|
61
66
|
* Runs an automation conversation to its end: the first turn, then one more
|
|
62
67
|
* turn per batch of agent results while agents it started are still out.
|
|
63
|
-
*
|
|
68
|
+
* `nextTurn(text, askOpts)` receives AUTOMATION_RESULT_TURN_OPTS as the
|
|
69
|
+
* results' provenance. Resolves with the last turn's result.
|
|
64
70
|
*/
|
|
65
71
|
export async function runAutomationTurns(sessionId, firstTurn, nextTurn, { waitMs = AGENT_RESULT_WAIT_MS } = {}) {
|
|
66
72
|
const run = { pending: new Set(), ready: [], wake: null };
|
|
@@ -79,7 +85,7 @@ export async function runAutomationTurns(sessionId, firstTurn, nextTurn, { waitM
|
|
|
79
85
|
run.wake = null;
|
|
80
86
|
if (!arrived) break;
|
|
81
87
|
}
|
|
82
|
-
result = await nextTurn(run.ready.splice(0).join('\n\n'));
|
|
88
|
+
result = await nextTurn(run.ready.splice(0).join('\n\n'), { ...AUTOMATION_RESULT_TURN_OPTS });
|
|
83
89
|
}
|
|
84
90
|
return result;
|
|
85
91
|
} finally {
|
|
@@ -60,6 +60,10 @@ import { createGoalFacadeApi } from '../goal-facade-api.mjs';
|
|
|
60
60
|
import { createRuntimeReviewApi } from '../runtime-review-api.mjs';
|
|
61
61
|
import { createWorkspaceTransitions, refreshWorkspacePrompt } from '../workspace/transitions.mjs';
|
|
62
62
|
import { saveSession } from '../../runtime/agent/orchestrator/session/store.mjs';
|
|
63
|
+
import { modelToolSchemaAllowlist } from '../../runtime/agent/orchestrator/runtime-core/tool-profile.mjs';
|
|
64
|
+
import { sessionOptions } from '../session-create/session-options.mjs';
|
|
65
|
+
import { createConversationImportApi } from '../conversation-import/service.mjs';
|
|
66
|
+
import { importError } from '../conversation-import/discovery.mjs';
|
|
63
67
|
import { bootProfile } from '../boot-profile.mjs';
|
|
64
68
|
import { STANDALONE_DATA_DIR } from '../runtime-paths.mjs';
|
|
65
69
|
import {
|
|
@@ -88,12 +92,22 @@ export function wireApis(boot) {
|
|
|
88
92
|
});
|
|
89
93
|
boot.providerAuthApi = providerAuthApiFor(boot);
|
|
90
94
|
boot.mediaApi = createMediaApi();
|
|
95
|
+
boot.conversationImportApi = conversationImportApiFor(boot);
|
|
91
96
|
boot.sessionTitles = createSessionTitleController({
|
|
92
97
|
dataRoot: () => dataDirOf(boot.cfgMod),
|
|
93
98
|
promoteGeneratedTitle: (sessionId, title, stage) => boot.mgr.updateSessionGeneratedTitle(sessionId, title, stage),
|
|
94
99
|
});
|
|
95
100
|
boot.lifecycleApi = lifecycleApiFor(boot);
|
|
96
101
|
boot.resourceApi = resourceApiFor(boot);
|
|
102
|
+
// Plugin MCP servers are derived live; copies an earlier enable action wrote
|
|
103
|
+
// into mcpServers are adopted once (an injected headless config is left as is).
|
|
104
|
+
if (!boot.params.initialConfig || typeof boot.params.initialConfig !== 'object') {
|
|
105
|
+
try {
|
|
106
|
+
boot.resourceApi.migratePluginMcpCopies();
|
|
107
|
+
} catch (error) {
|
|
108
|
+
bootProfile('plugin-mcp:adopt-failed', { error: error?.message || String(error) });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
97
111
|
boot.disposeGlobalExtensionSubscription = () => {
|
|
98
112
|
boot.disposeConfigSync?.();
|
|
99
113
|
boot.resourceApi.disposeGlobalExtensionSubscription?.();
|
|
@@ -282,6 +296,45 @@ function settingsApiFor(boot) {
|
|
|
282
296
|
});
|
|
283
297
|
}
|
|
284
298
|
|
|
299
|
+
// Conversation import: imported sessions are built with the Lead options a new
|
|
300
|
+
// session of this runtime would get (current route, tools, workflow, memory),
|
|
301
|
+
// minus its reservation, profile ownership and desktop placement.
|
|
302
|
+
function conversationImportApiFor(boot) {
|
|
303
|
+
const { rt, mgr } = boot;
|
|
304
|
+
return createConversationImportApi({
|
|
305
|
+
// The async catalog (the daemon's SQLite catalog worker, else JSON),
|
|
306
|
+
// loaded on first use so boot does not pull in the cold reader.
|
|
307
|
+
listSessionSummaries: async () => {
|
|
308
|
+
const reader = await import('../../runtime/agent/orchestrator/session/store-summary-reader.mjs');
|
|
309
|
+
return reader.listStoredSessionSummariesAsync();
|
|
310
|
+
},
|
|
311
|
+
createSession: (options) => mgr.createSession(options),
|
|
312
|
+
prepareLeadOptions: async () => {
|
|
313
|
+
await boot.awaitKeychainPrewarm();
|
|
314
|
+
const route = rt.route?.provider && rt.route?.model ? rt.route : resolveRoute(rt.config, {});
|
|
315
|
+
if (!route?.provider || !route?.model) throw importError('noModelRoute');
|
|
316
|
+
await boot.ensureProvidersReady(rt.config.providers || {});
|
|
317
|
+
const coreMemoryContext = await boot.loadCoreMemoryContext();
|
|
318
|
+
const leadView = Object.assign(Object.create(rt), {
|
|
319
|
+
route,
|
|
320
|
+
reservedSessionId: null,
|
|
321
|
+
sessionProfile: null,
|
|
322
|
+
desktopSession: null,
|
|
323
|
+
});
|
|
324
|
+
return sessionOptions(
|
|
325
|
+
{
|
|
326
|
+
rt: leadView,
|
|
327
|
+
cfgMod: boot.cfgMod,
|
|
328
|
+
activeWorkflowContext: workflowHelpers.activeWorkflowContext,
|
|
329
|
+
schemaAllowedTools: modelToolSchemaAllowlist(rt.toolProfile),
|
|
330
|
+
featureDisallowedTools: boot.featureDisallowedTools,
|
|
331
|
+
},
|
|
332
|
+
coreMemoryContext
|
|
333
|
+
);
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
285
338
|
function providerAuthApiFor(boot) {
|
|
286
339
|
const { rt, cfgMod, reg, ensureProvidersReady } = boot;
|
|
287
340
|
return createProviderAuthApi({
|
|
@@ -403,6 +456,8 @@ function resourceApiFor(boot) {
|
|
|
403
456
|
normalizeMcpServerInput: boot.normalizeMcpServerInput,
|
|
404
457
|
mcpStatus: boot.mcpStatus,
|
|
405
458
|
getMcpServerConfig: boot.getMcpServerConfig,
|
|
459
|
+
resolveMcpCatalog: boot.resolveMcpCatalog,
|
|
460
|
+
pluginMcpServer: boot.pluginMcpServer,
|
|
406
461
|
skillsStatus: boot.skillsStatus,
|
|
407
462
|
skillContent: boot.skillContent,
|
|
408
463
|
addGlobalSkill: boot.addGlobalSkill,
|
|
@@ -87,7 +87,8 @@ function initRuntimeState(rt, params) {
|
|
|
87
87
|
rt.activeTurnAbortControllers = new Set();
|
|
88
88
|
rt.firstTurnCompleted = false;
|
|
89
89
|
rt.codeGraphFirstTurnPrewarmDone = false;
|
|
90
|
-
|
|
90
|
+
// Last registry catalog-refresh promise this runtime attached handling to.
|
|
91
|
+
rt.startupProviderCatalogRefreshObserved = null;
|
|
91
92
|
// True while the boot-time provider-catalog refresh is in flight: warming a
|
|
92
93
|
// model cache it is about to invalidate only burns the load twice.
|
|
93
94
|
rt.startupProviderCatalogRefreshPending = false;
|
|
@@ -106,7 +107,9 @@ function activeTurnControls(rt) {
|
|
|
106
107
|
aborted = true;
|
|
107
108
|
try {
|
|
108
109
|
controller.abort(reason);
|
|
109
|
-
} catch {
|
|
110
|
+
} catch {
|
|
111
|
+
// Best-effort: one throwing abort listener must not stop the others.
|
|
112
|
+
}
|
|
110
113
|
}
|
|
111
114
|
return aborted;
|
|
112
115
|
},
|
|
@@ -7,11 +7,7 @@ import { createStandaloneHookBus } from '../services/hook-bus.mjs';
|
|
|
7
7
|
import { updateCurrentCwdOverride, writeLastSessionCwd } from '../../runtime/shared/user-cwd.mjs';
|
|
8
8
|
import { listRegisteredPlugins, pluginAdminStatus } from '../services/plugin-admin.mjs';
|
|
9
9
|
import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
10
|
-
import {
|
|
11
|
-
countSkillFiles,
|
|
12
|
-
pluginManifest,
|
|
13
|
-
pluginMcpServerName,
|
|
14
|
-
} from '../../runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs';
|
|
10
|
+
import { countSkillFiles, pluginManifest } from '../../runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs';
|
|
15
11
|
import { bootProfile } from '../boot-profile.mjs';
|
|
16
12
|
import { createMcpGlue } from '../mcp-glue.mjs';
|
|
17
13
|
import { createSelfUpdateController } from '../self-update.mjs';
|
|
@@ -41,20 +37,30 @@ function wireMcp(boot) {
|
|
|
41
37
|
mcpConnectGeneration: 0,
|
|
42
38
|
mcpConnectInFlight: null,
|
|
43
39
|
};
|
|
44
|
-
const {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
40
|
+
const {
|
|
41
|
+
mcpStatus,
|
|
42
|
+
resolveMcpCatalog,
|
|
43
|
+
pluginMcpServer,
|
|
44
|
+
pluginMcpSummary,
|
|
45
|
+
getMcpServerConfig,
|
|
46
|
+
connectConfiguredMcp,
|
|
47
|
+
awaitInitialMcpConnect,
|
|
48
|
+
normalizeMcpServerInput,
|
|
49
|
+
} = createMcpGlue({
|
|
50
|
+
mcpClient,
|
|
51
|
+
getConfig: () => rt.config,
|
|
52
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
53
|
+
getMcpScopeId: () => rt.mcpScopeId,
|
|
54
|
+
// Plugin-declared servers are derived from the registry on every resolve.
|
|
55
|
+
getPluginDataDir: () => dataDirOf(boot.cfgMod),
|
|
56
|
+
listPlugins: () => listRegisteredPlugins({ dataDir: boot.cfgMod.getPluginData() }),
|
|
57
|
+
state: mcpState,
|
|
58
|
+
});
|
|
56
59
|
Object.assign(boot, {
|
|
57
60
|
mcpStatus,
|
|
61
|
+
resolveMcpCatalog,
|
|
62
|
+
pluginMcpServer,
|
|
63
|
+
pluginMcpSummary,
|
|
58
64
|
getMcpServerConfig,
|
|
59
65
|
connectConfiguredMcp,
|
|
60
66
|
awaitInitialMcpConnect,
|
|
@@ -250,7 +256,12 @@ function wireCwdPlugins(boot) {
|
|
|
250
256
|
listRegisteredPlugins,
|
|
251
257
|
pluginAdminStatus,
|
|
252
258
|
pluginManifest,
|
|
253
|
-
|
|
259
|
+
pluginMcpSummary: boot.pluginMcpSummary,
|
|
260
|
+
// Plugin MCP servers without a declared cwd follow this runtime's cwd:
|
|
261
|
+
// reconnect just those (and refresh advertised roots) in place.
|
|
262
|
+
onCwdChanged: () => {
|
|
263
|
+
void boot.connectConfiguredMcp({ reconcile: true }).catch(() => {});
|
|
264
|
+
},
|
|
254
265
|
countSkillFiles,
|
|
255
266
|
writeLastSessionCwd,
|
|
256
267
|
updateCurrentCwdOverride,
|