mixdog 0.9.151 → 0.9.152
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 +110 -68
- package/package.json +3 -2
- package/scripts/tool-stress.mjs +0 -2
- package/src/defaults/skills/setup/SKILL.md +18 -27
- package/src/headless-exec.mjs +3 -10
- package/src/headless-exec.test.mjs +2 -7
- package/src/runtime/agent/orchestrator/config.mjs +6 -66
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +153 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +49 -45
- package/src/runtime/agent/orchestrator/mcp/client.mjs +242 -15
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +66 -0
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +36 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +54 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -8
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +10 -2
- package/src/runtime/agent/orchestrator/providers/lib/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/lib/sse-framing.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +35 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -2
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +4 -7
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +235 -0
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +719 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +399 -39
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +26 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +24 -1
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +161 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +31 -18
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +556 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +457 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +105 -186
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -10
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +30 -14
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +15 -4
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +73 -15
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +76 -2
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +24 -683
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +64 -72
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.mjs +75 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +24 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +24 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +35 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +28 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +54 -0
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/browser-bridge/client.mjs +103 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +42 -0
- package/src/runtime/computer-bridge/client.mjs +101 -0
- package/src/runtime/computer-bridge/tool-defs.mjs +53 -0
- package/src/runtime/media/store.mjs +18 -8
- package/src/runtime/media/store.test.mjs +14 -0
- package/src/runtime/memory/index.mjs +1 -1
- package/src/runtime/memory/lib/transcript-ingest.mjs +8 -6
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +52 -0
- package/src/runtime/shared/child-spawn-gate.mjs +17 -0
- package/src/runtime/shared/child-spawn-remote.mjs +69 -7
- package/src/runtime/shared/child-spawn-remote.test.mjs +124 -0
- package/src/runtime/shared/pristine-execution-contract.json +2 -1
- package/src/runtime/shared/provider-api-key.mjs +1 -0
- package/src/runtime/shared/session-runtime-health.mjs +91 -0
- package/src/runtime/shared/session-runtime-health.test.mjs +53 -0
- package/src/runtime/shared/skill-document.mjs +86 -0
- package/src/runtime/shared/skill-document.test.mjs +81 -0
- package/src/runtime/shared/tool-surface.mjs +18 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +1 -0
- package/src/runtime/shared/turn-snapshot.mjs +32 -9
- package/src/runtime/shared/user-cwd.mjs +33 -14
- package/src/runtime/shared/user-cwd.test.mjs +43 -0
- package/src/session-runtime/cwd-plugins.mjs +26 -26
- package/src/session-runtime/cwd-plugins.test.mjs +75 -0
- package/src/session-runtime/cwd-tool-routing.test.mjs +5 -0
- package/src/session-runtime/global-extensions.mjs +26 -0
- package/src/session-runtime/global-extensions.test.mjs +28 -0
- package/src/session-runtime/goal-runtime.mjs +721 -0
- package/src/session-runtime/goal-runtime.test.mjs +108 -0
- package/src/session-runtime/lifecycle-api.mjs +18 -4
- package/src/session-runtime/lifecycle-api.test.mjs +21 -0
- package/src/session-runtime/mcp-glue.mjs +49 -47
- package/src/session-runtime/plugin-mcp-standard.test.mjs +177 -0
- package/src/session-runtime/plugin-mcp.mjs +96 -22
- package/src/session-runtime/prewarm.mjs +1 -10
- package/src/session-runtime/resource-api-global.test.mjs +69 -0
- package/src/session-runtime/resource-api.mjs +173 -58
- package/src/session-runtime/runtime-core.mjs +148 -20
- package/src/session-runtime/runtime-tunables.mjs +6 -8
- package/src/session-runtime/session-turn-api.mjs +22 -11
- package/src/session-runtime/skills-api.mjs +79 -25
- package/src/session-runtime/skills-api.test.mjs +99 -0
- package/src/session-runtime/tool-catalog-schema.mjs +0 -5
- package/src/session-runtime/tool-catalog.mjs +4 -13
- package/src/session-runtime/tool-policy-surface.test.mjs +33 -11
- package/src/session-runtime/tool-surface.mjs +12 -0
- package/src/standalone/agent-dispatch-broker.mjs +5 -9
- package/src/standalone/agent-tool/notify.mjs +6 -8
- package/src/standalone/agent-tool/notify.test.mjs +37 -0
- package/src/standalone/daemon.mjs +31 -15
- package/src/standalone/hook-bus/config.mjs +1 -0
- package/src/standalone/plugin-admin.mjs +17 -0
- package/src/standalone/plugin-admin.test.mjs +7 -0
- package/src/standalone/provider-admin.mjs +1 -0
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-runtime-agent-control-client.mjs +133 -0
- package/src/standalone/session-runtime-agent-control-client.test.mjs +71 -0
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +139 -0
- package/src/standalone/session-runtime-host-factory.mjs +17 -0
- package/src/standalone/session-runtime-host-factory.test.mjs +31 -0
- package/src/standalone/session-runtime-host-health.test.mjs +5 -0
- package/src/standalone/session-runtime-host.mjs +696 -58
- package/src/standalone/session-runtime-inline-host.mjs +351 -0
- package/src/standalone/session-runtime-inline-host.test.mjs +83 -0
- package/src/standalone/session-runtime-provider-cooldown.mjs +78 -0
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +80 -0
- package/src/standalone/session-runtime-shard-host.test.mjs +569 -0
- package/src/standalone/session-runtime-shard-router.mjs +149 -0
- package/src/standalone/session-runtime-shard-router.test.mjs +101 -0
- package/src/standalone/session-runtime-worker.mjs +476 -18
- package/src/standalone/session-service.mjs +81 -6
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/core-memory-picker.mjs +2 -0
- package/src/tui/app/extension-pickers.mjs +1 -1
- package/src/tui/app/maintenance-pickers.mjs +3 -3
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/app/model-picker.mjs +14 -4
- package/src/tui/app/panel-handoff.test.mjs +311 -0
- package/src/tui/app/project-picker.mjs +1 -0
- package/src/tui/app/route-pickers.mjs +21 -6
- package/src/tui/app/settings-picker.mjs +26 -3
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch.mjs +73 -22
- package/src/tui/app/theme-effort-pickers.mjs +7 -2
- package/src/tui/components/Picker.jsx +10 -1
- package/src/tui/dist/index.mjs +163 -44
- package/src/tui/session/context-state.mjs +1 -0
- package/src/tui/session/goal-continuation.mjs +110 -0
- package/src/tui/session/goal-continuation.test.mjs +84 -0
- package/src/tui/session/queue-helpers.mjs +5 -2
- package/src/tui/session/session-action-surface.test.mjs +108 -0
- package/src/tui/session/session-api-ext.mjs +39 -8
- package/src/tui/session/session-api.mjs +58 -0
- package/src/tui/session/session-flow.mjs +6 -0
- package/src/tui/session/turn.mjs +1 -0
- package/src/tui/session-local.mjs +15 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -301
|
@@ -10,11 +10,18 @@ import keychain from '../lib/keychain-cjs.cjs';
|
|
|
10
10
|
import './hitch-profile.mjs';
|
|
11
11
|
import { ensureStandaloneEnvironment } from '../standalone/seeds.mjs';
|
|
12
12
|
import { createStandaloneAgent } from '../standalone/agent-tool.mjs';
|
|
13
|
+
import {
|
|
14
|
+
executeRemoteAgentControl,
|
|
15
|
+
remoteAgentControlEnabled,
|
|
16
|
+
} from '../standalone/session-runtime-agent-control-client.mjs';
|
|
13
17
|
import { isAgentOwner } from '../runtime/agent/orchestrator/agent-owner.mjs';
|
|
14
18
|
import { createStandaloneChannelWorker } from '../standalone/channel-worker.mjs';
|
|
15
19
|
import { createStandaloneHookBus } from '../standalone/hook-bus.mjs';
|
|
16
20
|
import { getStandaloneMemoryRuntime } from '../standalone/memory-runtime-proxy.mjs';
|
|
17
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
updateCurrentCwdOverride,
|
|
23
|
+
writeLastSessionCwd,
|
|
24
|
+
} from '../runtime/shared/user-cwd.mjs';
|
|
18
25
|
import { cancelBackgroundTasks } from '../runtime/shared/background-tasks.mjs';
|
|
19
26
|
import { createTranscriptWriter } from '../runtime/shared/transcript-writer.mjs';
|
|
20
27
|
import { mixdogHome } from '../runtime/shared/plugin-paths.mjs';
|
|
@@ -149,7 +156,6 @@ import {
|
|
|
149
156
|
} from './output-styles.mjs';
|
|
150
157
|
import { readJsonSafe, readTextSafe } from './fs-utils.mjs';
|
|
151
158
|
import {
|
|
152
|
-
readProjectMcpServers,
|
|
153
159
|
countSkillFiles,
|
|
154
160
|
mcpScriptForPlugin,
|
|
155
161
|
normalizePluginMcpServerConfig,
|
|
@@ -187,7 +193,6 @@ import {
|
|
|
187
193
|
estimateToolSchemaBreakdown,
|
|
188
194
|
measuredToolUsage,
|
|
189
195
|
parseToolSelection,
|
|
190
|
-
parseToolSearchQuerySelection,
|
|
191
196
|
sortedCatalogByMeasuredUsage,
|
|
192
197
|
filterDisallowedTools,
|
|
193
198
|
sortedNamesByMeasuredUsage,
|
|
@@ -247,6 +252,7 @@ import { createToolSurface } from './tool-surface.mjs';
|
|
|
247
252
|
import { createToolPolicyRefresh } from './tool-policy-refresh.mjs';
|
|
248
253
|
import { readRuntimeTunables } from './runtime-tunables.mjs';
|
|
249
254
|
import { createSessionTurnApi } from './session-turn-api.mjs';
|
|
255
|
+
import { createGoalRuntime } from './goal-runtime.mjs';
|
|
250
256
|
import { providerInitCacheKey } from './provider-init-key.mjs';
|
|
251
257
|
import { createRoutePreparationGate } from './route-preparation.mjs';
|
|
252
258
|
import {
|
|
@@ -262,6 +268,19 @@ import {
|
|
|
262
268
|
STANDALONE_ROOT,
|
|
263
269
|
STANDALONE_DATA_DIR,
|
|
264
270
|
} from './runtime-paths.mjs';
|
|
271
|
+
// Desktop-app browser bridge: tiny fs/fetch client, so a static import adds
|
|
272
|
+
// no meaningful boot cost. The tool itself is gated per session by the sync
|
|
273
|
+
// bridge-availability probe (headless runs never see it).
|
|
274
|
+
import {
|
|
275
|
+
browserBridgeAvailableSync,
|
|
276
|
+
executeBrowserTool,
|
|
277
|
+
} from '../runtime/browser-bridge/client.mjs';
|
|
278
|
+
import { TOOL_DEFS as BROWSER_BRIDGE_TOOL_DEFS } from '../runtime/browser-bridge/tool-defs.mjs';
|
|
279
|
+
import {
|
|
280
|
+
computerBridgeAvailableSync,
|
|
281
|
+
executeComputerTool,
|
|
282
|
+
} from '../runtime/computer-bridge/client.mjs';
|
|
283
|
+
import { TOOL_DEFS as COMPUTER_BRIDGE_TOOL_DEFS } from '../runtime/computer-bridge/tool-defs.mjs';
|
|
265
284
|
import {
|
|
266
285
|
dispatchWebSearchRuntimeTool,
|
|
267
286
|
memoryToolArgsForCaller,
|
|
@@ -416,9 +435,15 @@ export async function createMixdogSessionRuntime({
|
|
|
416
435
|
const webSearchEnabled = () => featureEnvOverride('MIXDOG_FEATURE_WEB_SEARCH')
|
|
417
436
|
?? moduleEnabled(rt.config, 'webSearch', true);
|
|
418
437
|
const channelsEnabled = () => moduleEnabled(rt.config, 'channels', true);
|
|
438
|
+
const browserToolEnabled = () => featureEnvOverride('MIXDOG_FEATURE_BROWSER')
|
|
439
|
+
?? browserBridgeAvailableSync();
|
|
440
|
+
const computerToolEnabled = () => featureEnvOverride('MIXDOG_FEATURE_COMPUTER')
|
|
441
|
+
?? computerBridgeAvailableSync();
|
|
419
442
|
const featureDisallowedTools = () => [
|
|
420
443
|
...(webSearchEnabled() ? [] : ['web_search', 'web_fetch']),
|
|
421
444
|
...(memoryToolsEnabledFn() ? [] : ['memory', 'recall']),
|
|
445
|
+
...(browserToolEnabled() ? [] : ['browser']),
|
|
446
|
+
...(computerToolEnabled() ? [] : ['computer']),
|
|
422
447
|
];
|
|
423
448
|
|
|
424
449
|
async function getMemoryModule() {
|
|
@@ -618,7 +643,6 @@ export async function createMixdogSessionRuntime({
|
|
|
618
643
|
// True while the boot-time provider-catalog refresh is in flight: warming a
|
|
619
644
|
// model cache it is about to invalidate only burns the load twice.
|
|
620
645
|
rt.startupProviderCatalogRefreshPending = false;
|
|
621
|
-
rt.lastProjectMcpKey = null;
|
|
622
646
|
// MCP connect state, owned here so teardown/reconnect paths still observe it;
|
|
623
647
|
// the mcp-glue factory mutates this object in place (see createMcpGlue).
|
|
624
648
|
const mcpState = {
|
|
@@ -632,6 +656,7 @@ export async function createMixdogSessionRuntime({
|
|
|
632
656
|
mcpTransportLabel,
|
|
633
657
|
resolveEffectiveMcpServers,
|
|
634
658
|
mcpStatus,
|
|
659
|
+
getMcpServerConfig,
|
|
635
660
|
connectConfiguredMcp,
|
|
636
661
|
awaitInitialMcpConnect,
|
|
637
662
|
normalizeMcpServerInput,
|
|
@@ -685,6 +710,7 @@ export async function createMixdogSessionRuntime({
|
|
|
685
710
|
// Notification fan-out (listener broadcast + pending-queue mirroring of
|
|
686
711
|
// terminal completions) lives in notification-bus.mjs.
|
|
687
712
|
let sessionTurnApi = null;
|
|
713
|
+
let goalRuntime = null;
|
|
688
714
|
const completionWakeups = new Set();
|
|
689
715
|
const wakeQueuedCompletion = ({ sessionId, executionId, enqueuedAt } = {}) => {
|
|
690
716
|
const ownerSessionId = String(sessionId || '').trim();
|
|
@@ -743,6 +769,7 @@ export async function createMixdogSessionRuntime({
|
|
|
743
769
|
if (v?.id) {
|
|
744
770
|
rt.reservedSessionId = null;
|
|
745
771
|
bindRuntimeNotificationSession(v.id);
|
|
772
|
+
goalRuntime?.watchSession(v.id);
|
|
746
773
|
}
|
|
747
774
|
};
|
|
748
775
|
|
|
@@ -752,12 +779,14 @@ export async function createMixdogSessionRuntime({
|
|
|
752
779
|
skillsStatus,
|
|
753
780
|
skillContent,
|
|
754
781
|
skillToolContent,
|
|
755
|
-
|
|
782
|
+
addGlobalSkill,
|
|
783
|
+
saveSkillDocument,
|
|
784
|
+
invalidateSkills,
|
|
756
785
|
} = createSkillsApi({ contextMod, getCwd: () => rt.currentCwd });
|
|
757
786
|
|
|
758
787
|
// cwd resolution/apply + plugins-status + core-memory context. Extracted to
|
|
759
788
|
// session-runtime/cwd-plugins.mjs; the facade keeps ownership of the mutable
|
|
760
|
-
// currentCwd/session/config
|
|
789
|
+
// currentCwd/session/config locals via getter/setter
|
|
761
790
|
// injection and passes the later-defined callbacks (prewarm/tool-surface/
|
|
762
791
|
// memory) as closures.
|
|
763
792
|
const {
|
|
@@ -771,9 +800,9 @@ export async function createMixdogSessionRuntime({
|
|
|
771
800
|
setCurrentCwd: (next) => { rt.currentCwd = next; },
|
|
772
801
|
getConfig: () => rt.config,
|
|
773
802
|
getSession: () => rt.session,
|
|
803
|
+
getDesktopSession: () => rt.desktopSession,
|
|
804
|
+
setDesktopSession: (next) => { rt.desktopSession = next; },
|
|
774
805
|
getRoute: () => rt.route,
|
|
775
|
-
getLastProjectMcpKey: () => rt.lastProjectMcpKey,
|
|
776
|
-
setLastProjectMcpKey: (next) => { rt.lastProjectMcpKey = next; },
|
|
777
806
|
isCodeGraphPrewarmLazy: () => codeGraphPrewarmLazy,
|
|
778
807
|
isCodeGraphFirstTurnPrewarmDone: () => rt.codeGraphFirstTurnPrewarmDone,
|
|
779
808
|
getCodeGraphPrewarmDelayMs: () => codeGraphPrewarmDelayMs,
|
|
@@ -791,8 +820,8 @@ export async function createMixdogSessionRuntime({
|
|
|
791
820
|
pluginMcpServerName,
|
|
792
821
|
mcpScriptForPlugin,
|
|
793
822
|
countSkillFiles,
|
|
794
|
-
readProjectMcpServers,
|
|
795
823
|
writeLastSessionCwd,
|
|
824
|
+
updateCurrentCwdOverride,
|
|
796
825
|
clean,
|
|
797
826
|
resolve,
|
|
798
827
|
statSync,
|
|
@@ -825,6 +854,27 @@ export async function createMixdogSessionRuntime({
|
|
|
825
854
|
} catch { /* best-effort: subagent hook must never affect worker lifecycle */ }
|
|
826
855
|
},
|
|
827
856
|
});
|
|
857
|
+
const routedAgentTool = {
|
|
858
|
+
...agentTool,
|
|
859
|
+
execute(args, context = {}) {
|
|
860
|
+
return remoteAgentControlEnabled()
|
|
861
|
+
? executeRemoteAgentControl(args, context)
|
|
862
|
+
: agentTool.execute(args, context);
|
|
863
|
+
},
|
|
864
|
+
closeAll(reason, scope = {}) {
|
|
865
|
+
if (!remoteAgentControlEnabled()) return agentTool.closeAll(reason, scope);
|
|
866
|
+
void executeRemoteAgentControl({
|
|
867
|
+
type: '__close_all',
|
|
868
|
+
reason: String(reason || 'agent owner closed'),
|
|
869
|
+
}, {
|
|
870
|
+
callerCwd: rt.currentCwd,
|
|
871
|
+
invocationSource: 'runtime-lifecycle',
|
|
872
|
+
callerSessionId: scope?.callerSessionId || rt.session?.id || null,
|
|
873
|
+
clientHostPid: rt.session?.clientHostPid || process.pid,
|
|
874
|
+
}).catch(() => {});
|
|
875
|
+
return undefined;
|
|
876
|
+
},
|
|
877
|
+
};
|
|
828
878
|
bootProfile('agent:ready', { ms: (performance.now() - agentToolStartedAt).toFixed(1) });
|
|
829
879
|
const agentStatusState = () => {
|
|
830
880
|
try {
|
|
@@ -841,6 +891,12 @@ export async function createMixdogSessionRuntime({
|
|
|
841
891
|
return { agentWorkers: [], agentJobs: [], agentScope: null };
|
|
842
892
|
}
|
|
843
893
|
};
|
|
894
|
+
goalRuntime = createGoalRuntime({
|
|
895
|
+
dataDir: cfgMod.getPluginData?.() || STANDALONE_DATA_DIR,
|
|
896
|
+
});
|
|
897
|
+
if (rt.session?.id || rt.reservedSessionId) {
|
|
898
|
+
goalRuntime.watchSession(rt.session?.id || rt.reservedSessionId);
|
|
899
|
+
}
|
|
844
900
|
const channelsStartedAt = performance.now();
|
|
845
901
|
const channels = createStandaloneChannelWorker({
|
|
846
902
|
rootDir: STANDALONE_ROOT,
|
|
@@ -876,6 +932,9 @@ export async function createMixdogSessionRuntime({
|
|
|
876
932
|
...(memoryToolDefs?.TOOL_DEFS || []).filter((tool) => tool?.name === 'recall' || tool?.name === 'memory'),
|
|
877
933
|
...(channelToolDefs?.TOOL_DEFS || []).filter((tool) => channels.isChannelTool(tool?.name)),
|
|
878
934
|
...(codeGraphToolDefs?.CODE_GRAPH_TOOL_DEFS || []).filter((tool) => tool?.name === 'code_graph'),
|
|
935
|
+
...BROWSER_BRIDGE_TOOL_DEFS.filter((tool) => tool?.name === 'browser'),
|
|
936
|
+
...COMPUTER_BRIDGE_TOOL_DEFS.filter((tool) => tool?.name === 'computer'),
|
|
937
|
+
...goalRuntime.tools,
|
|
879
938
|
...agentTool.tools,
|
|
880
939
|
].map(applyStandaloneToolDefaults);
|
|
881
940
|
bootProfile('tools:ready', { ms: (performance.now() - toolsStartedAt).toFixed(1), count: standaloneTools.length });
|
|
@@ -889,6 +948,7 @@ export async function createMixdogSessionRuntime({
|
|
|
889
948
|
// replay) lives in tool-surface.mjs.
|
|
890
949
|
const {
|
|
891
950
|
modelStandaloneTools,
|
|
951
|
+
activateTools: activateRuntimeTools,
|
|
892
952
|
invalidatePreSessionToolSurface,
|
|
893
953
|
activeToolSurface,
|
|
894
954
|
applyPreSessionToolSelection,
|
|
@@ -941,6 +1001,18 @@ export async function createMixdogSessionRuntime({
|
|
|
941
1001
|
throw new Error('memory tools are disabled in settings; background memory and manual core memory remain available');
|
|
942
1002
|
}
|
|
943
1003
|
}
|
|
1004
|
+
if (name === 'browser') {
|
|
1005
|
+
if (callerCtx?.invocationSource === 'model-tool' && featureEnvOverride('MIXDOG_FEATURE_BROWSER') === false) {
|
|
1006
|
+
throw new Error('the browser tool is disabled in this environment');
|
|
1007
|
+
}
|
|
1008
|
+
return await executeBrowserTool(args);
|
|
1009
|
+
}
|
|
1010
|
+
if (name === 'computer') {
|
|
1011
|
+
if (callerCtx?.invocationSource === 'model-tool' && featureEnvOverride('MIXDOG_FEATURE_COMPUTER') === false) {
|
|
1012
|
+
throw new Error('the computer tool is disabled in this environment');
|
|
1013
|
+
}
|
|
1014
|
+
return await executeComputerTool(args);
|
|
1015
|
+
}
|
|
944
1016
|
if (name === 'web_search' || name === 'web_fetch' || name === 'local_fetch' || name === 'image_fetch') {
|
|
945
1017
|
return dispatchWebSearchRuntimeTool(name, args, callerCtx, {
|
|
946
1018
|
getWebSearchModule,
|
|
@@ -978,7 +1050,7 @@ export async function createMixdogSessionRuntime({
|
|
|
978
1050
|
if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${nextCwd}`);
|
|
979
1051
|
currentCwd = typeof callerCtx?.setCallerCwd === 'function'
|
|
980
1052
|
? clean(await callerCtx.setCallerCwd(nextCwd)) || nextCwd
|
|
981
|
-
: applyResolvedCwd(nextCwd);
|
|
1053
|
+
: applyResolvedCwd(nextCwd, { persistProjectSelection: true });
|
|
982
1054
|
} else if (action !== 'get') {
|
|
983
1055
|
throw new Error(`cwd: unknown action "${action}"`);
|
|
984
1056
|
}
|
|
@@ -990,9 +1062,14 @@ export async function createMixdogSessionRuntime({
|
|
|
990
1062
|
if (name === 'Skill') {
|
|
991
1063
|
return skillToolContent(args?.name);
|
|
992
1064
|
}
|
|
1065
|
+
if (name === 'get_goal' || name === 'create_goal' || name === 'update_goal') {
|
|
1066
|
+
return await goalRuntime.executeTool(name, args || {}, {
|
|
1067
|
+
callerSessionId: callerCtx?.callerSessionId || rt.session?.id || rt.reservedSessionId || null,
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
993
1070
|
if (name === 'agent') {
|
|
994
1071
|
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
995
|
-
return await
|
|
1072
|
+
return await routedAgentTool.execute(args, {
|
|
996
1073
|
callerCwd,
|
|
997
1074
|
invocationSource: 'model-tool',
|
|
998
1075
|
callerSessionId,
|
|
@@ -1009,7 +1086,6 @@ export async function createMixdogSessionRuntime({
|
|
|
1009
1086
|
},
|
|
1010
1087
|
});
|
|
1011
1088
|
internalTools.markBootReady?.();
|
|
1012
|
-
try { rt.lastProjectMcpKey = resolve(rt.currentCwd) + '\u0000' + JSON.stringify(readProjectMcpServers(rt.currentCwd)); } catch { rt.lastProjectMcpKey = null; }
|
|
1013
1089
|
void connectConfiguredMcp()
|
|
1014
1090
|
.then((status) => bootProfile('mcp:ready', {
|
|
1015
1091
|
connected: Number(status?.connectedCount || 0),
|
|
@@ -1301,7 +1377,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1301
1377
|
channelStartDelayMs,
|
|
1302
1378
|
codeGraphPrewarmEnabled,
|
|
1303
1379
|
prewarmState,
|
|
1304
|
-
agentTool,
|
|
1380
|
+
agentTool: routedAgentTool,
|
|
1305
1381
|
});
|
|
1306
1382
|
const ensureSessionTranscriptWriter = () => remoteTranscript.ensureSessionTranscriptWriter();
|
|
1307
1383
|
|
|
@@ -1322,8 +1398,8 @@ export async function createMixdogSessionRuntime({
|
|
|
1322
1398
|
providerWarmup: providerWarmupEnabled,
|
|
1323
1399
|
codeGraphPrewarm: codeGraphPrewarmEnabled,
|
|
1324
1400
|
});
|
|
1325
|
-
// Heavy
|
|
1326
|
-
//
|
|
1401
|
+
// Heavy work remains demand-driven. Native helpers overlap provider.send;
|
|
1402
|
+
// memory and code-graph parsing stay cold until their feature is used.
|
|
1327
1403
|
bootProfile('runtime:prewarm-deferred', { reason: 'first-turn' });
|
|
1328
1404
|
scheduleProviderWarmup();
|
|
1329
1405
|
scheduleProviderModelWarmup();
|
|
@@ -1443,6 +1519,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1443
1519
|
mgr.updateSessionGeneratedTitle(sessionId, title, stage)
|
|
1444
1520
|
),
|
|
1445
1521
|
});
|
|
1522
|
+
let disposeGlobalExtensionSubscription = () => {};
|
|
1446
1523
|
const lifecycleApi = createLifecycleApi({
|
|
1447
1524
|
getSession: () => rt.session,
|
|
1448
1525
|
setSession: adoptSession,
|
|
@@ -1464,7 +1541,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1464
1541
|
mgr,
|
|
1465
1542
|
statusRoutes,
|
|
1466
1543
|
channels,
|
|
1467
|
-
agentTool,
|
|
1544
|
+
agentTool: routedAgentTool,
|
|
1468
1545
|
mcpClient,
|
|
1469
1546
|
warmupTimers,
|
|
1470
1547
|
prewarmTimers,
|
|
@@ -1489,7 +1566,9 @@ export async function createMixdogSessionRuntime({
|
|
|
1489
1566
|
// workflow's agent-tool gate, not reuse the boot-time array.
|
|
1490
1567
|
getStandaloneTools: modelStandaloneTools,
|
|
1491
1568
|
clearRuntimeNotifications,
|
|
1569
|
+
goalRuntime,
|
|
1492
1570
|
disposeSessionTitles: () => sessionTitles.disposeAll(),
|
|
1571
|
+
disposeGlobalExtensionSubscription: () => disposeGlobalExtensionSubscription(),
|
|
1493
1572
|
});
|
|
1494
1573
|
const resourceApi = createResourceApi({
|
|
1495
1574
|
getConfig: () => rt.config,
|
|
@@ -1505,15 +1584,22 @@ export async function createMixdogSessionRuntime({
|
|
|
1505
1584
|
recreateCurrentSessionIfReady,
|
|
1506
1585
|
normalizeMcpServerInput,
|
|
1507
1586
|
mcpStatus,
|
|
1587
|
+
getMcpServerConfig,
|
|
1508
1588
|
skillsStatus,
|
|
1509
1589
|
skillContent,
|
|
1510
|
-
|
|
1590
|
+
addGlobalSkill,
|
|
1591
|
+
saveSkillDocument,
|
|
1592
|
+
invalidateSkills,
|
|
1593
|
+
getDisabledSkills: () => settingsApi.getDisabledSkills(),
|
|
1594
|
+
setDisabledSkills: (names) => settingsApi.setDisabledSkills(names),
|
|
1511
1595
|
pluginsStatus,
|
|
1512
1596
|
getMemoryModule,
|
|
1513
1597
|
reloadFullConfig,
|
|
1598
|
+
flushSkillsSave,
|
|
1514
1599
|
awaitKeychainPrewarm,
|
|
1515
1600
|
getActiveTurnCount: () => rt.activeTurnCount,
|
|
1516
1601
|
});
|
|
1602
|
+
disposeGlobalExtensionSubscription = () => resourceApi.disposeGlobalExtensionSubscription?.();
|
|
1517
1603
|
const modelRouteApi = createModelRouteApi({
|
|
1518
1604
|
getConfig: () => rt.config,
|
|
1519
1605
|
getRoute: () => rt.route,
|
|
@@ -1609,7 +1695,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1609
1695
|
scheduleProviderWarmup,
|
|
1610
1696
|
scheduleProviderModelWarmup,
|
|
1611
1697
|
invalidateContextStatusCache,
|
|
1612
|
-
agentTool,
|
|
1698
|
+
agentTool: routedAgentTool,
|
|
1613
1699
|
recreateCurrentSessionIfReady,
|
|
1614
1700
|
invalidatePreSessionToolSurface,
|
|
1615
1701
|
activeToolSurface,
|
|
@@ -1636,18 +1722,26 @@ export async function createMixdogSessionRuntime({
|
|
|
1636
1722
|
rt.session?.id,
|
|
1637
1723
|
options,
|
|
1638
1724
|
),
|
|
1639
|
-
revertTurnReview: () => revertTurnSnapshotReview(
|
|
1725
|
+
revertTurnReview: (checkpointId) => revertTurnSnapshotReview(
|
|
1640
1726
|
rt.currentCwd,
|
|
1641
1727
|
rt.session?.id,
|
|
1728
|
+
checkpointId,
|
|
1642
1729
|
),
|
|
1643
|
-
revertTurnReviewFile: (file) => revertTurnSnapshotReviewFile(
|
|
1730
|
+
revertTurnReviewFile: (file, checkpointId) => revertTurnSnapshotReviewFile(
|
|
1644
1731
|
rt.currentCwd,
|
|
1645
1732
|
rt.session?.id,
|
|
1646
1733
|
file,
|
|
1734
|
+
checkpointId,
|
|
1647
1735
|
),
|
|
1648
1736
|
get id() {
|
|
1649
1737
|
return rt.session?.id || rt.reservedSessionId || null;
|
|
1650
1738
|
},
|
|
1739
|
+
deliverToolCompletion(sessionId, text, meta = {}) {
|
|
1740
|
+
const target = String(sessionId || '').trim();
|
|
1741
|
+
const current = String(rt.session?.id || rt.reservedSessionId || '').trim();
|
|
1742
|
+
if (!target || target !== current) return false;
|
|
1743
|
+
return notifySessionCompletion(target, text, meta);
|
|
1744
|
+
},
|
|
1651
1745
|
reserveSessionId(sessionId) {
|
|
1652
1746
|
const id = String(sessionId || '').trim();
|
|
1653
1747
|
if (!id || !/^[A-Za-z0-9_-]+$/.test(id)) {
|
|
@@ -1658,6 +1752,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1658
1752
|
}
|
|
1659
1753
|
rt.reservedSessionId = id;
|
|
1660
1754
|
bindRuntimeNotificationSession(id);
|
|
1755
|
+
goalRuntime?.watchSession(id);
|
|
1661
1756
|
// Reservation is the earliest safe point to prepare keychain, memory,
|
|
1662
1757
|
// provider metadata, hooks, and the provider transport. This starts no
|
|
1663
1758
|
// model response and therefore incurs no inference/token usage. The first
|
|
@@ -1669,6 +1764,39 @@ export async function createMixdogSessionRuntime({
|
|
|
1669
1764
|
});
|
|
1670
1765
|
return id;
|
|
1671
1766
|
},
|
|
1767
|
+
goalStatus() {
|
|
1768
|
+
const sessionId = rt.session?.id || rt.reservedSessionId || null;
|
|
1769
|
+
return sessionId ? goalRuntime.snapshot(sessionId) : null;
|
|
1770
|
+
},
|
|
1771
|
+
async goalControl(args = {}) {
|
|
1772
|
+
let sessionId = rt.session?.id || rt.reservedSessionId || null;
|
|
1773
|
+
if (!sessionId) {
|
|
1774
|
+
await createCurrentSession('goal');
|
|
1775
|
+
sessionId = rt.session?.id || rt.reservedSessionId || null;
|
|
1776
|
+
}
|
|
1777
|
+
if (!sessionId) throw new Error('goal: session could not be created');
|
|
1778
|
+
const result = await goalRuntime.control(sessionId, args);
|
|
1779
|
+
if (result?.goal?.status === 'active') {
|
|
1780
|
+
activateRuntimeTools(goalRuntime.tools.map((tool) => tool.name));
|
|
1781
|
+
}
|
|
1782
|
+
return result;
|
|
1783
|
+
},
|
|
1784
|
+
goalContinuation() {
|
|
1785
|
+
const sessionId = rt.session?.id || rt.reservedSessionId || null;
|
|
1786
|
+
if (!sessionId) return { run: false, reason: 'missing-session', goal: null };
|
|
1787
|
+
const continuation = goalRuntime.continuation(sessionId, { agentStatus: agentStatusState() });
|
|
1788
|
+
if (continuation.run) {
|
|
1789
|
+
activateRuntimeTools(goalRuntime.tools.map((tool) => tool.name));
|
|
1790
|
+
}
|
|
1791
|
+
return continuation;
|
|
1792
|
+
},
|
|
1793
|
+
archiveCompletedGoalOnUserInput() {
|
|
1794
|
+
const sessionId = rt.session?.id || rt.reservedSessionId || null;
|
|
1795
|
+
return sessionId ? goalRuntime.archiveCompletedOnUserInput(sessionId) : null;
|
|
1796
|
+
},
|
|
1797
|
+
onGoalStatusChange(listener) {
|
|
1798
|
+
return goalRuntime.subscribe(listener);
|
|
1799
|
+
},
|
|
1672
1800
|
get provider() {
|
|
1673
1801
|
return rt.route.provider;
|
|
1674
1802
|
},
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
import { envDelayMs, envFlag, envPresent } from './env.mjs';
|
|
6
6
|
|
|
7
7
|
export function readRuntimeTunables() {
|
|
8
|
-
const
|
|
8
|
+
const codeGraphPrewarmEager = envFlag('MIXDOG_CODE_GRAPH_PREWARM_EAGER');
|
|
9
|
+
const codeGraphPrewarmEnabled = !envFlag('MIXDOG_DISABLE_CODE_GRAPH_PREWARM')
|
|
10
|
+
&& (envFlag('MIXDOG_ENABLE_CODE_GRAPH_PREWARM') || codeGraphPrewarmEager);
|
|
9
11
|
const daemonHosted = envFlag('MIXDOG_DAEMON_HOST');
|
|
10
12
|
return {
|
|
11
13
|
providerSetupWarmupDelayMs: envDelayMs('MIXDOG_PROVIDER_SETUP_WARMUP_DELAY_MS', 300, { min: 0, max: 60_000 }),
|
|
@@ -50,12 +52,8 @@ export function readRuntimeTunables() {
|
|
|
50
52
|
&& !envFlag('MIXDOG_DISABLE_MODEL_PREFETCH'),
|
|
51
53
|
codeGraphPrewarmEnabled,
|
|
52
54
|
modelCatalogWarmupEnabled: !envFlag('MIXDOG_DISABLE_MODEL_CATALOG_WARMUP'),
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
// Instead prewarm ONCE on the first real turn, when a code lookup is
|
|
57
|
-
// actually imminent. MIXDOG_CODE_GRAPH_PREWARM_EAGER=1 restores the old
|
|
58
|
-
// eager behavior.
|
|
59
|
-
codeGraphPrewarmLazy: codeGraphPrewarmEnabled && !envFlag('MIXDOG_CODE_GRAPH_PREWARM_EAGER'),
|
|
55
|
+
// Code-graph parsing is demand-driven by default. Operators may opt into
|
|
56
|
+
// first-turn warming, or explicitly restore eager cwd warming.
|
|
57
|
+
codeGraphPrewarmLazy: codeGraphPrewarmEnabled && !codeGraphPrewarmEager,
|
|
60
58
|
};
|
|
61
59
|
}
|
|
@@ -15,6 +15,7 @@ import { beginTurnSnapshot, cancelTurnSnapshot, completeTurnSnapshot } from '../
|
|
|
15
15
|
import { isVisibleStreamProgress } from '../runtime/shared/stream-progress.mjs';
|
|
16
16
|
import { runAbortable, settleWithin, throwIfAborted } from '../runtime/shared/abort-race.mjs';
|
|
17
17
|
import { interruptTaskWaitForSession } from '../runtime/agent/orchestrator/session/task-wait-control.mjs';
|
|
18
|
+
import { runWithCwdOverride } from '../runtime/shared/user-cwd.mjs';
|
|
18
19
|
|
|
19
20
|
export function splitToolStatusCounts(rows) {
|
|
20
21
|
const list = Array.isArray(rows) ? rows : [];
|
|
@@ -78,6 +79,7 @@ export function createSessionTurnApi(deps) {
|
|
|
78
79
|
};
|
|
79
80
|
},
|
|
80
81
|
async ask(prompt, options = {}) {
|
|
82
|
+
return runWithCwdOverride(getCurrentCwd(), async () => {
|
|
81
83
|
// Remote-attach: this surface is a viewer on a session that another
|
|
82
84
|
// live process owns. Never run a turn here — persist the prompt into
|
|
83
85
|
// the shared pending spool; the owner's injection poller submits it as
|
|
@@ -123,24 +125,26 @@ export function createSessionTurnApi(deps) {
|
|
|
123
125
|
// path. Every actual tool execution joins this same promise below, so
|
|
124
126
|
// shell/apply_patch cannot mutate the worktree before the baseline.
|
|
125
127
|
turnSnapshotPromise = Promise.resolve()
|
|
126
|
-
.then(() => beginTurnSnapshotForTurn(getCurrentCwd(), id
|
|
128
|
+
.then(() => beginTurnSnapshotForTurn(getCurrentCwd(), id, {
|
|
129
|
+
// The first submitted row is the outer prompt. Mid-loop steering
|
|
130
|
+
// is drained inside this ask() and deliberately keeps this ID.
|
|
131
|
+
checkpointId: String(options.id || '').trim(),
|
|
132
|
+
}))
|
|
127
133
|
.catch(() => undefined);
|
|
128
134
|
};
|
|
129
135
|
const routeStartedAt = performance.now();
|
|
130
136
|
startTurnSnapshot(getSession()?.id || getReservedSessionId?.());
|
|
131
137
|
let routeWaitMs = 0;
|
|
132
138
|
setActiveTurnCount(getActiveTurnCount() + 1);
|
|
133
|
-
try { scheduleSearchRuntimeWarmup?.(0); } catch { /* search warmup is best-effort */ }
|
|
134
139
|
let mcpWaitMs = 0;
|
|
135
140
|
let providerStartedAt = 0;
|
|
136
141
|
let turnTimingStatus = 'error';
|
|
137
142
|
let turnTimingEmitted = false;
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
// workers compete with the actual first-token critical path.
|
|
143
|
+
// First-turn fallback for hosts that disabled or have not completed the
|
|
144
|
+
// post-connect idle warmup. Do not start it until the provider has
|
|
145
|
+
// produced visible text/reasoning/tool progress: transport headers and
|
|
146
|
+
// response-created acknowledgements arrive earlier and would make
|
|
147
|
+
// PowerShell + graph workers compete with the first-token critical path.
|
|
144
148
|
const heavyRuntimeWarmupPending = typeof getCodeGraphFirstTurnPrewarmDone === 'function'
|
|
145
149
|
&& !getCodeGraphFirstTurnPrewarmDone();
|
|
146
150
|
if (heavyRuntimeWarmupPending) {
|
|
@@ -211,7 +215,7 @@ export function createSessionTurnApi(deps) {
|
|
|
211
215
|
// can update the caller instead of whichever runtime registered the
|
|
212
216
|
// shared internal-tool executor most recently.
|
|
213
217
|
Object.defineProperty(session0, '_applyResolvedCwdForCaller', {
|
|
214
|
-
value: applyResolvedCwd,
|
|
218
|
+
value: (nextCwd) => applyResolvedCwd(nextCwd, { persistProjectSelection: true }),
|
|
215
219
|
enumerable: false,
|
|
216
220
|
configurable: true,
|
|
217
221
|
writable: true,
|
|
@@ -354,7 +358,13 @@ export function createSessionTurnApi(deps) {
|
|
|
354
358
|
// after session locking, history shaping and pre-send compact.
|
|
355
359
|
// The old timestamp above mgr.askSession mislabeled all of that
|
|
356
360
|
// Mixdog work as provider latency.
|
|
357
|
-
if (!providerStartedAt)
|
|
361
|
+
if (!providerStartedAt) {
|
|
362
|
+
providerStartedAt = performance.now();
|
|
363
|
+
// Warm the two small native helpers only after provider.send
|
|
364
|
+
// owns the request. Their startup overlaps network/model time
|
|
365
|
+
// without competing with route, snapshot, or compact work.
|
|
366
|
+
try { scheduleSearchRuntimeWarmup?.(0); } catch { /* best-effort */ }
|
|
367
|
+
}
|
|
358
368
|
return options.onProviderSendStarted?.(...args);
|
|
359
369
|
},
|
|
360
370
|
onToolPhaseStarted: options.onToolPhaseStarted,
|
|
@@ -444,6 +454,7 @@ export function createSessionTurnApi(deps) {
|
|
|
444
454
|
scheduleProviderModelWarmup();
|
|
445
455
|
}
|
|
446
456
|
}
|
|
457
|
+
});
|
|
447
458
|
},
|
|
448
459
|
async clear(options = {}) {
|
|
449
460
|
const session = getSession();
|
|
@@ -622,7 +633,7 @@ export function createSessionTurnApi(deps) {
|
|
|
622
633
|
return { ...result, status: this.toolsStatus() };
|
|
623
634
|
},
|
|
624
635
|
setCwd(path) {
|
|
625
|
-
applyResolvedCwd(resolveCwdPath(path));
|
|
636
|
+
applyResolvedCwd(resolveCwdPath(path), { persistProjectSelection: true });
|
|
626
637
|
return getCurrentCwd();
|
|
627
638
|
},
|
|
628
639
|
};
|
|
@@ -1,34 +1,31 @@
|
|
|
1
|
-
// Skill surface (status listing, resource load, tool envelope,
|
|
2
|
-
// creation). Extracted from runtime-core so the facade only wires the mutable
|
|
1
|
+
// Skill surface (status listing, resource load, tool envelope, global skill
|
|
2
|
+
// creation/editing). Extracted from runtime-core so the facade only wires the mutable
|
|
3
3
|
// cwd and the context module into it.
|
|
4
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
5
|
-
import { join } from 'node:path';
|
|
4
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
6
|
+
import { resolvePluginData } from '../runtime/shared/plugin-paths.mjs';
|
|
7
|
+
import {
|
|
8
|
+
createSkillDocument,
|
|
9
|
+
updateSkillDocument,
|
|
10
|
+
validateSkillDescription,
|
|
11
|
+
validateSkillName,
|
|
12
|
+
} from '../runtime/shared/skill-document.mjs';
|
|
6
13
|
import { clean } from './session-text.mjs';
|
|
7
14
|
|
|
8
|
-
const
|
|
9
|
-
'---',
|
|
10
|
-
`name: ${name}`,
|
|
11
|
-
`description: ${description}`,
|
|
12
|
-
'---',
|
|
13
|
-
'',
|
|
14
|
-
'# Instructions',
|
|
15
|
-
'',
|
|
16
|
-
'Describe when and how to use this skill.',
|
|
17
|
-
'',
|
|
18
|
-
].join('\n');
|
|
15
|
+
const DEFAULT_SKILL_BODY = '# Instructions\n\nDescribe how to use this skill.';
|
|
19
16
|
|
|
20
17
|
export function createSkillsApi({ contextMod, getCwd }) {
|
|
18
|
+
const globalSkillsRoot = () => resolve(resolvePluginData(), 'skills');
|
|
19
|
+
|
|
21
20
|
function skillsStatus() {
|
|
22
21
|
const cwd = getCwd();
|
|
23
22
|
const skills = typeof contextMod.collectSkillsCached === 'function'
|
|
24
23
|
? contextMod.collectSkillsCached(cwd)
|
|
25
24
|
: [];
|
|
26
25
|
const norm = (value) => String(value || '').replace(/\\/g, '/').toLowerCase();
|
|
27
|
-
const
|
|
28
|
-
// A skill under <cwd>/.mixdog/skills is project-scoped; everything else
|
|
29
|
-
// comes from the user/global tree.
|
|
26
|
+
const globalRoot = `${norm(globalSkillsRoot())}/`;
|
|
30
27
|
const sourceForSkill = (filePath) => (
|
|
31
|
-
|
|
28
|
+
norm(filePath).startsWith(globalRoot) ? 'global' : 'plugin'
|
|
32
29
|
);
|
|
33
30
|
return {
|
|
34
31
|
cwd,
|
|
@@ -38,6 +35,7 @@ export function createSkillsApi({ contextMod, getCwd }) {
|
|
|
38
35
|
description: skill.description || '',
|
|
39
36
|
filePath: skill.filePath || null,
|
|
40
37
|
source: sourceForSkill(skill.filePath),
|
|
38
|
+
editable: sourceForSkill(skill.filePath) === 'global',
|
|
41
39
|
})),
|
|
42
40
|
};
|
|
43
41
|
}
|
|
@@ -65,16 +63,72 @@ export function createSkillsApi({ contextMod, getCwd }) {
|
|
|
65
63
|
return contextMod.buildSkillToolEnvelope(skill.name, skill.content, skill.dir);
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
function
|
|
69
|
-
const name = clean(input.name)
|
|
70
|
-
|
|
71
|
-
const
|
|
66
|
+
function addGlobalSkill(input = {}) {
|
|
67
|
+
const name = validateSkillName(clean(input.name));
|
|
68
|
+
const description = validateSkillDescription(input.description);
|
|
69
|
+
const body = String(input.instructions || input.body || DEFAULT_SKILL_BODY);
|
|
70
|
+
const dir = join(globalSkillsRoot(), name);
|
|
72
71
|
const filePath = join(dir, 'SKILL.md');
|
|
73
72
|
if (existsSync(filePath)) throw new Error(`skill already exists: ${name}`);
|
|
74
73
|
mkdirSync(dir, { recursive: true });
|
|
75
|
-
writeFileSync(filePath,
|
|
74
|
+
writeFileSync(filePath, createSkillDocument({ name, description, body }), 'utf8');
|
|
75
|
+
contextMod.invalidateSkillsCache?.(getCwd());
|
|
76
76
|
return { name, filePath };
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
function saveSkillDocument(input = {}) {
|
|
80
|
+
const originalName = validateSkillName(input.originalName);
|
|
81
|
+
const name = validateSkillName(input.name);
|
|
82
|
+
const description = validateSkillDescription(input.description);
|
|
83
|
+
const body = String(input.instructions || input.body || '');
|
|
84
|
+
const resource = contextMod.loadSkillResource?.(originalName, getCwd());
|
|
85
|
+
if (!resource?.filePath) throw new Error(`skill not found: ${originalName}`);
|
|
86
|
+
const resourcePath = resolve(resource.filePath);
|
|
87
|
+
const resourceRelative = relative(globalSkillsRoot(), resourcePath);
|
|
88
|
+
if (!resourceRelative || resourceRelative.startsWith('..') || isAbsolute(resourceRelative)) {
|
|
89
|
+
throw new Error(`plugin skill is read-only: ${originalName}`);
|
|
90
|
+
}
|
|
91
|
+
const collision = skillsStatus().skills.find((skill) =>
|
|
92
|
+
skill.name === name && skill.filePath !== resource.filePath);
|
|
93
|
+
if (collision) throw new Error(`skill already exists: ${name}`);
|
|
94
|
+
|
|
95
|
+
const currentDir = dirname(resource.filePath);
|
|
96
|
+
if (basename(currentDir) !== originalName) {
|
|
97
|
+
throw new Error(`skill folder does not match its name: ${originalName}`);
|
|
98
|
+
}
|
|
99
|
+
const nextDir = join(dirname(currentDir), name);
|
|
100
|
+
if (nextDir !== currentDir && existsSync(nextDir)) {
|
|
101
|
+
throw new Error(`skill folder already exists: ${name}`);
|
|
102
|
+
}
|
|
103
|
+
const source = readFileSync(resource.filePath, 'utf8');
|
|
104
|
+
const updated = updateSkillDocument(source, { name, description, body });
|
|
105
|
+
let filePath = resource.filePath;
|
|
106
|
+
if (nextDir !== currentDir) {
|
|
107
|
+
renameSync(currentDir, nextDir);
|
|
108
|
+
filePath = join(nextDir, 'SKILL.md');
|
|
109
|
+
try {
|
|
110
|
+
writeFileSync(filePath, updated, 'utf8');
|
|
111
|
+
} catch (error) {
|
|
112
|
+
renameSync(nextDir, currentDir);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
writeFileSync(filePath, updated, 'utf8');
|
|
117
|
+
}
|
|
118
|
+
contextMod.invalidateSkillsCache?.(getCwd());
|
|
119
|
+
return { originalName, name, filePath };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function invalidateSkills() {
|
|
123
|
+
contextMod.invalidateSkillsCache?.(getCwd());
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
skillsStatus,
|
|
128
|
+
skillContent,
|
|
129
|
+
skillToolContent,
|
|
130
|
+
addGlobalSkill,
|
|
131
|
+
saveSkillDocument,
|
|
132
|
+
invalidateSkills,
|
|
133
|
+
};
|
|
80
134
|
}
|