mixdog 0.9.85 → 0.9.87
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 -1
- package/package.json +4 -3
- package/src/defaults/agents.json +12 -0
- package/src/defaults/skills/setup/SKILL.md +44 -12
- package/src/help.mjs +40 -9
- package/src/lib/keychain-cjs.cjs +11 -1
- package/src/rules/agent/43-title-agent.md +22 -0
- package/src/rules/shared/01-tool.md +3 -3
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +4 -2
- package/src/runtime/agent/orchestrator/mcp/client.mjs +24 -0
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +84 -3
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +125 -16
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +73 -45
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +15 -18
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +135 -9
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +31 -12
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +170 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +45 -6
- package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +26 -0
- package/src/runtime/agent/orchestrator/session/token-bpe.mjs +42 -0
- package/src/runtime/agent/orchestrator/session/token-native.mjs +186 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +15 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +20 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +24 -0
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +286 -0
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +152 -18
- package/src/runtime/agent/orchestrator/tools/token-binary-fetcher.mjs +201 -0
- package/src/runtime/agent/orchestrator/tools/token-manifest.json +26 -0
- package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +3 -3
- package/src/runtime/media/adapters/gemini-video.mjs +1 -1
- package/src/runtime/media/renditions.mjs +1 -1
- package/src/runtime/media/store.mjs +1 -1
- package/src/runtime/memory/lib/memory-action-handlers.mjs +4 -1
- package/src/runtime/memory/tool-defs.mjs +19 -3
- package/src/runtime/shared/atomic-file.mjs +25 -14
- package/src/runtime/shared/automation-attachments.mjs +3 -3
- package/src/runtime/shared/child-guardian.mjs +15 -5
- package/src/runtime/shared/child-spawn-gate.mjs +6 -12
- package/src/runtime/shared/resource-admission.mjs +7 -3
- package/src/runtime/shared/turn-snapshot.mjs +0 -3
- package/src/session-runtime/lifecycle-api.mjs +49 -9
- package/src/session-runtime/prewarm.mjs +35 -0
- package/src/session-runtime/provider-auth-api.mjs +8 -0
- package/src/session-runtime/provider-usage.mjs +30 -5
- package/src/session-runtime/remote-control.mjs +8 -47
- package/src/session-runtime/remote-transcript.mjs +3 -7
- package/src/session-runtime/route-preparation.mjs +24 -0
- package/src/session-runtime/runtime-core.mjs +286 -548
- package/src/session-runtime/session-lifecycle.mjs +378 -0
- package/src/session-runtime/session-turn-api.mjs +9 -2
- package/src/session-runtime/workflow-agents-api.mjs +3 -0
- package/src/standalone/agent-shard/shard-child.mjs +300 -0
- package/src/standalone/agent-shard/shard-pool.mjs +443 -0
- package/src/standalone/agent-tool/job-views.mjs +329 -0
- package/src/standalone/agent-tool/spawn-flow.mjs +629 -0
- package/src/standalone/agent-tool/tag-registry.mjs +340 -0
- package/src/standalone/agent-tool.mjs +112 -941
- package/src/standalone/channel-daemon-transport.mjs +126 -185
- package/src/standalone/channel-daemon.mjs +9 -10
- package/src/standalone/usage-dashboard.mjs +23 -1
- package/src/tui/App.jsx +362 -2569
- package/src/tui/app/app-view.jsx +504 -0
- package/src/tui/app/channel-pickers.mjs +3 -8
- package/src/tui/app/create-app-pickers.mjs +313 -0
- package/src/tui/app/prompt-submit.mjs +501 -0
- package/src/tui/app/route-pickers.mjs +117 -152
- package/src/tui/app/settings-picker.mjs +90 -89
- package/src/tui/app/shell-layout.mjs +563 -0
- package/src/tui/app/slash-commands.mjs +7 -7
- package/src/tui/app/slash-dispatch.mjs +0 -45
- package/src/tui/app/usage-context-panels.mjs +288 -0
- package/src/tui/app/use-copy-selection.mjs +56 -0
- package/src/tui/app/use-global-key-input.mjs +151 -0
- package/src/tui/app/use-pasted-buffers.mjs +115 -0
- package/src/tui/app/use-prompt-draft-flow.mjs +167 -0
- package/src/tui/app/use-prompt-hint.mjs +58 -0
- package/src/tui/app/use-prompt-queue-history.mjs +100 -0
- package/src/tui/app/use-terminal-chrome.mjs +72 -0
- package/src/tui/app/use-transcript-activity.mjs +131 -0
- package/src/tui/app/use-welcome-prompt-hint.mjs +96 -0
- package/src/tui/components/StatusLine.jsx +1 -1
- package/src/tui/components/tool-execution/ResultBody.jsx +1 -1
- package/src/tui/dist/index.mjs +9857 -9159
- package/src/tui/engine/session-api-ext.mjs +47 -9
- package/src/tui/engine.mjs +7 -1
- package/src/tui/figures.mjs +0 -1
- package/src/ui/statusline-format.mjs +0 -1
- package/src/hooks/lib/permission-evaluator.cjs +0 -24
- package/src/lib/config-cjs.cjs +0 -61
- package/src/runtime/shared/launcher-control.mjs +0 -258
- package/src/runtime/shared/workspace-router.mjs +0 -259
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createSessionLifecycle } from './session-lifecycle.mjs';
|
|
1
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
2
3
|
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
3
4
|
import { performance } from 'node:perf_hooks';
|
|
@@ -41,7 +42,10 @@ import {
|
|
|
41
42
|
setLocalProvider,
|
|
42
43
|
} from '../standalone/provider-admin.mjs';
|
|
43
44
|
import { createUsageDashboard } from '../standalone/usage-dashboard.mjs';
|
|
44
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
consumeOpenAICodexResetCredit,
|
|
47
|
+
fetchOAuthUsageSnapshot,
|
|
48
|
+
} from '../runtime/agent/orchestrator/providers/oauth-usage.mjs';
|
|
45
49
|
import {
|
|
46
50
|
getModelMetadataSync,
|
|
47
51
|
warmCatalogsInBackground,
|
|
@@ -246,6 +250,7 @@ import { createToolSurface } from './tool-surface.mjs';
|
|
|
246
250
|
import { readRuntimeTunables } from './runtime-tunables.mjs';
|
|
247
251
|
import { createSessionTurnApi } from './session-turn-api.mjs';
|
|
248
252
|
import { providerInitCacheKey } from './provider-init-key.mjs';
|
|
253
|
+
import { createRoutePreparationGate } from './route-preparation.mjs';
|
|
249
254
|
import {
|
|
250
255
|
RUNTIME,
|
|
251
256
|
SEARCH_RUNTIME,
|
|
@@ -319,28 +324,22 @@ export async function createMixdogSessionRuntime({
|
|
|
319
324
|
remote = false,
|
|
320
325
|
desktopSession: initialDesktopSession = null,
|
|
321
326
|
} = {}) {
|
|
322
|
-
let
|
|
327
|
+
// Shared mutable runtime state, promoted from closure `let`s so extracted
|
|
328
|
+
// modules can read/write live values through one reference.
|
|
329
|
+
const rt = {};
|
|
330
|
+
rt.desktopSession = initialDesktopSession;
|
|
323
331
|
bootProfile('session-runtime:start', { provider, model, toolMode, cwd });
|
|
324
|
-
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
// seat (the 'acquired' notification). Lets the deferred start chain proceed
|
|
329
|
-
// past its remoteEnabled guards without prematurely showing this session as
|
|
330
|
-
// remote (single-holder: a live owner must not be stolen by autoStart).
|
|
331
|
-
let remoteClaimPending = false;
|
|
332
|
-
// SESSION-SCOPED remote (user decision): the session that toggled remote
|
|
333
|
-
// OWNS the channel relay. Other sessions' turns never rebind or relay; a
|
|
334
|
-
// dead/replaced owner (clear, delete) hands the seat to the next current
|
|
335
|
-
// session that asks.
|
|
336
|
-
let remoteSessionId = null;
|
|
332
|
+
rt.remoteEnabled = remote === true;
|
|
333
|
+
// SESSION-SCOPED Remote is manual-only. Explicit ON selects/overrides; no
|
|
334
|
+
// registration, turn, session death, or process death transfers ownership.
|
|
335
|
+
rt.remoteSessionId = null;
|
|
337
336
|
// Desktop/TUI Remote toggles are desired-state transitions. Serialize the
|
|
338
337
|
// daemon deactivate/detach and reconnect/activate chains so an OFF that is
|
|
339
338
|
// still stopping can never tear down the worker created by the following ON.
|
|
340
339
|
const remoteTransitions = createRemoteTransitionQueue();
|
|
341
340
|
// Last assistant text handed to the transcript writer (via onAssistantText),
|
|
342
341
|
// so the post-turn final-content append can skip an exact duplicate.
|
|
343
|
-
|
|
342
|
+
rt._lastAppendedAssistant = '';
|
|
344
343
|
process.env.MIXDOG_QUIET_SESSION_LOG ??= '1';
|
|
345
344
|
const standaloneStartedAt = performance.now();
|
|
346
345
|
ensureStandaloneEnvironment({
|
|
@@ -349,11 +348,11 @@ export async function createMixdogSessionRuntime({
|
|
|
349
348
|
});
|
|
350
349
|
bootProfile('standalone-env:ready', { ms: (performance.now() - standaloneStartedAt).toFixed(1) });
|
|
351
350
|
const keychainPrewarmPromise = keychain.prewarmSecrets();
|
|
352
|
-
|
|
353
|
-
|
|
351
|
+
rt.keychainPrewarmWaitDone = false;
|
|
352
|
+
rt.keychainPrewarmWaitPromise = null;
|
|
354
353
|
function awaitKeychainPrewarm() {
|
|
355
|
-
if (keychainPrewarmWaitDone) return Promise.resolve();
|
|
356
|
-
keychainPrewarmWaitPromise ??= (async () => {
|
|
354
|
+
if (rt.keychainPrewarmWaitDone) return Promise.resolve();
|
|
355
|
+
rt.keychainPrewarmWaitPromise ??= (async () => {
|
|
357
356
|
let timeoutId;
|
|
358
357
|
const deadline = new Promise((resolveDeadline) => {
|
|
359
358
|
timeoutId = setTimeout(resolveDeadline, KEYCHAIN_PREWARM_WAIT_MS);
|
|
@@ -363,14 +362,19 @@ export async function createMixdogSessionRuntime({
|
|
|
363
362
|
await Promise.race([keychainPrewarmPromise, deadline]);
|
|
364
363
|
} finally {
|
|
365
364
|
clearTimeout(timeoutId);
|
|
366
|
-
keychainPrewarmWaitDone = true;
|
|
365
|
+
rt.keychainPrewarmWaitDone = true;
|
|
367
366
|
}
|
|
368
367
|
// Invoked here: the assignment used to store the async FUNCTION, so every
|
|
369
368
|
// `await awaitKeychainPrewarm()` resolved instantly (awaiting a function is
|
|
370
369
|
// a no-op) and callers silently skipped the wait they asked for.
|
|
371
370
|
})();
|
|
372
|
-
return keychainPrewarmWaitPromise;
|
|
371
|
+
return rt.keychainPrewarmWaitPromise;
|
|
373
372
|
}
|
|
373
|
+
const routePreparation = createRoutePreparationGate({
|
|
374
|
+
onError: (error) => bootProfile('route-preparation:failed', {
|
|
375
|
+
error: error?.message || String(error),
|
|
376
|
+
}),
|
|
377
|
+
});
|
|
374
378
|
|
|
375
379
|
const importsStartedAt = performance.now();
|
|
376
380
|
const [
|
|
@@ -416,22 +420,22 @@ export async function createMixdogSessionRuntime({
|
|
|
416
420
|
dataDir: pluginDataDir,
|
|
417
421
|
cwd,
|
|
418
422
|
});
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
423
|
+
rt.memoryModPromise = null;
|
|
424
|
+
rt.searchModPromise = null;
|
|
425
|
+
rt.codeGraphModPromise = null;
|
|
422
426
|
|
|
423
427
|
// Memory module is always-on. `memoryEnabled()` is kept as a thin alias that
|
|
424
428
|
// now always returns true (callers/compaction helpers still reference it);
|
|
425
429
|
// the user-facing toggle is `recap` (background cycles only), read via
|
|
426
430
|
// recapEnabled(config).
|
|
427
431
|
const memoryEnabled = () => true;
|
|
428
|
-
const recapEnabledFn = () => recapEnabled(config, true);
|
|
429
|
-
const channelsEnabled = () => moduleEnabled(config, 'channels', true);
|
|
432
|
+
const recapEnabledFn = () => recapEnabled(rt.config, true);
|
|
433
|
+
const channelsEnabled = () => moduleEnabled(rt.config, 'channels', true);
|
|
430
434
|
|
|
431
435
|
async function getMemoryModule() {
|
|
432
436
|
const startedAt = performance.now();
|
|
433
|
-
memoryModPromise ??= Promise.resolve(memoryRuntime);
|
|
434
|
-
const mod = await memoryModPromise;
|
|
437
|
+
rt.memoryModPromise ??= Promise.resolve(memoryRuntime);
|
|
438
|
+
const mod = await rt.memoryModPromise;
|
|
435
439
|
if (typeof mod?.init === 'function') {
|
|
436
440
|
await mod.init();
|
|
437
441
|
}
|
|
@@ -441,16 +445,16 @@ export async function createMixdogSessionRuntime({
|
|
|
441
445
|
|
|
442
446
|
async function getSearchModule() {
|
|
443
447
|
const startedAt = performance.now();
|
|
444
|
-
searchModPromise ??= import(SEARCH_RUNTIME);
|
|
445
|
-
const mod = await searchModPromise;
|
|
448
|
+
rt.searchModPromise ??= import(SEARCH_RUNTIME);
|
|
449
|
+
const mod = await rt.searchModPromise;
|
|
446
450
|
bootProfile('search-runtime:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
447
451
|
return mod;
|
|
448
452
|
}
|
|
449
453
|
|
|
450
454
|
async function getCodeGraphModule() {
|
|
451
455
|
const startedAt = performance.now();
|
|
452
|
-
codeGraphModPromise ??= import(CODE_GRAPH_RUNTIME);
|
|
453
|
-
const mod = await codeGraphModPromise;
|
|
456
|
+
rt.codeGraphModPromise ??= import(CODE_GRAPH_RUNTIME);
|
|
457
|
+
const mod = await rt.codeGraphModPromise;
|
|
454
458
|
bootProfile('code-graph-runtime:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
455
459
|
return mod;
|
|
456
460
|
}
|
|
@@ -459,7 +463,7 @@ export async function createMixdogSessionRuntime({
|
|
|
459
463
|
const leadRoute = normalizeWorkflowRoute(routeLike);
|
|
460
464
|
if (!leadRoute) return null;
|
|
461
465
|
|
|
462
|
-
const nextConfig = { ...(config || {}) };
|
|
466
|
+
const nextConfig = { ...(rt.config || {}) };
|
|
463
467
|
nextConfig.presets = upsertWorkflowPreset(nextConfig.presets, 'lead', leadRoute);
|
|
464
468
|
nextConfig.workflowRoutes = {
|
|
465
469
|
...(nextConfig.workflowRoutes || {}),
|
|
@@ -486,21 +490,21 @@ export async function createMixdogSessionRuntime({
|
|
|
486
490
|
}
|
|
487
491
|
|
|
488
492
|
const configStartedAt = performance.now();
|
|
489
|
-
|
|
490
|
-
setConfiguredShell(normalizeSystemShellConfig(config.shell).command);
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
493
|
+
rt.config = cfgMod.loadConfig({ secrets: false });
|
|
494
|
+
setConfiguredShell(normalizeSystemShellConfig(rt.config.shell).command);
|
|
495
|
+
rt.configHasSecrets = false;
|
|
496
|
+
rt.route = resolveRoute(rt.config, { provider, model });
|
|
497
|
+
rt.searchRoute = normalizeSearchRouteConfig(rt.config.searchRoute);
|
|
494
498
|
bootProfile('config:ready', { ms: (performance.now() - configStartedAt).toFixed(1) });
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
499
|
+
rt.mode = normalizeToolMode(toolMode);
|
|
500
|
+
rt.session = null;
|
|
501
|
+
rt.sessionCreatePromise = null;
|
|
502
|
+
rt.currentCwd = cwd;
|
|
503
|
+
rt.sessionNeedsCwdRefresh = false;
|
|
500
504
|
// session_manage tool: reset request scheduled by the model mid-turn,
|
|
501
505
|
// consumed by the TUI engine at turn end ('clear' | 'compact_clear').
|
|
502
|
-
|
|
503
|
-
|
|
506
|
+
rt.pendingSessionReset = null;
|
|
507
|
+
rt.closeRequested = false;
|
|
504
508
|
const warmupTimers = {
|
|
505
509
|
providerSetupWarmupTimer: null,
|
|
506
510
|
providerWarmupTimer: null,
|
|
@@ -521,8 +525,8 @@ export async function createMixdogSessionRuntime({
|
|
|
521
525
|
codeGraphPrewarmQueuedCwd: '',
|
|
522
526
|
channelStartPromise: null,
|
|
523
527
|
};
|
|
524
|
-
|
|
525
|
-
|
|
528
|
+
rt.activeTurnCount = 0;
|
|
529
|
+
rt.firstTurnCompleted = false;
|
|
526
530
|
function hookTranscriptPath(sessionId) {
|
|
527
531
|
const id = clean(sessionId);
|
|
528
532
|
if (!id || !/^[A-Za-z0-9_-]+$/.test(id)) return null;
|
|
@@ -530,15 +534,15 @@ export async function createMixdogSessionRuntime({
|
|
|
530
534
|
return join(dataDir, 'sessions', `${id}.json`);
|
|
531
535
|
}
|
|
532
536
|
function hookEffortPayload() {
|
|
533
|
-
const level = clean(route.effectiveEffort || route.effort);
|
|
537
|
+
const level = clean(rt.route.effectiveEffort || rt.route.effort);
|
|
534
538
|
return level ? { level: level.toLowerCase() } : undefined;
|
|
535
539
|
}
|
|
536
540
|
function hookCommonPayload(extra = {}) {
|
|
537
|
-
const sid = clean(extra.session_id || extra.sessionId || session?.id);
|
|
541
|
+
const sid = clean(extra.session_id || extra.sessionId || rt.session?.id);
|
|
538
542
|
return {
|
|
539
543
|
...(sid ? { session_id: sid, transcript_path: hookTranscriptPath(sid) } : {}),
|
|
540
|
-
cwd: currentCwd,
|
|
541
|
-
permission_mode: session?.permissionMode || 'default',
|
|
544
|
+
cwd: rt.currentCwd,
|
|
545
|
+
permission_mode: rt.session?.permissionMode || 'default',
|
|
542
546
|
...(hookEffortPayload() ? { effort: hookEffortPayload() } : {}),
|
|
543
547
|
...extra,
|
|
544
548
|
};
|
|
@@ -563,7 +567,7 @@ export async function createMixdogSessionRuntime({
|
|
|
563
567
|
modelCatalogWarmupEnabled,
|
|
564
568
|
codeGraphPrewarmLazy,
|
|
565
569
|
} = readRuntimeTunables();
|
|
566
|
-
|
|
570
|
+
rt.codeGraphFirstTurnPrewarmDone = false;
|
|
567
571
|
const modelMetaByRoute = new Map();
|
|
568
572
|
const notificationListeners = new Set();
|
|
569
573
|
// Remote seat listeners (TUI): fired when remote mode flips outside a direct
|
|
@@ -588,11 +592,11 @@ export async function createMixdogSessionRuntime({
|
|
|
588
592
|
providerSetupPromise: null,
|
|
589
593
|
};
|
|
590
594
|
const providerInitPromises = new Map();
|
|
591
|
-
|
|
595
|
+
rt.startupProviderCatalogRefreshStarted = false;
|
|
592
596
|
// True while the boot-time provider-catalog refresh is in flight: warming a
|
|
593
597
|
// model cache it is about to invalidate only burns the load twice.
|
|
594
|
-
|
|
595
|
-
|
|
598
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
599
|
+
rt.lastProjectMcpKey = null;
|
|
596
600
|
// MCP connect state, owned here so teardown/reconnect paths still observe it;
|
|
597
601
|
// the mcp-glue factory mutates this object in place (see createMcpGlue).
|
|
598
602
|
const mcpState = {
|
|
@@ -611,15 +615,15 @@ export async function createMixdogSessionRuntime({
|
|
|
611
615
|
normalizeMcpServerInput,
|
|
612
616
|
} = createMcpGlue({
|
|
613
617
|
mcpClient,
|
|
614
|
-
getConfig: () => config,
|
|
615
|
-
getCurrentCwd: () => currentCwd,
|
|
616
|
-
getDesktopSession: () => desktopSession,
|
|
617
|
-
setDesktopSession: (v) => { desktopSession = v; },
|
|
618
|
+
getConfig: () => rt.config,
|
|
619
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
620
|
+
getDesktopSession: () => rt.desktopSession,
|
|
621
|
+
setDesktopSession: (v) => { rt.desktopSession = v; },
|
|
618
622
|
state: mcpState,
|
|
619
623
|
});
|
|
620
624
|
const hooksStartedAt = performance.now();
|
|
621
625
|
const hooks = createStandaloneHookBus({ dataDir: cfgMod.getPluginData() });
|
|
622
|
-
hooks.emit('runtime:start', { cwd: currentCwd, provider: route.provider, model: route.model, toolMode: mode });
|
|
626
|
+
hooks.emit('runtime:start', { cwd: rt.currentCwd, provider: rt.route.provider, model: rt.route.model, toolMode: rt.mode });
|
|
623
627
|
bootProfile('hooks:ready', { ms: (performance.now() - hooksStartedAt).toFixed(1) });
|
|
624
628
|
|
|
625
629
|
// Self-update: registry check + background staging live in self-update.mjs;
|
|
@@ -627,7 +631,7 @@ export async function createMixdogSessionRuntime({
|
|
|
627
631
|
// boot check is deferred past this constructor so a hanging registry request
|
|
628
632
|
// can never delay session boot.
|
|
629
633
|
const selfUpdate = createSelfUpdateController({
|
|
630
|
-
getConfig: () => config,
|
|
634
|
+
getConfig: () => rt.config,
|
|
631
635
|
getDataDir: () => cfgMod.getPluginData?.() || STANDALONE_DATA_DIR,
|
|
632
636
|
emitNotification: (...a) => emitRuntimeNotification(...a),
|
|
633
637
|
});
|
|
@@ -650,7 +654,7 @@ export async function createMixdogSessionRuntime({
|
|
|
650
654
|
skillContent,
|
|
651
655
|
skillToolContent,
|
|
652
656
|
addProjectSkill,
|
|
653
|
-
} = createSkillsApi({ contextMod, getCwd: () => currentCwd });
|
|
657
|
+
} = createSkillsApi({ contextMod, getCwd: () => rt.currentCwd });
|
|
654
658
|
|
|
655
659
|
// cwd resolution/apply + plugins-status + core-memory context. Extracted to
|
|
656
660
|
// session-runtime/cwd-plugins.mjs; the facade keeps ownership of the mutable
|
|
@@ -664,17 +668,17 @@ export async function createMixdogSessionRuntime({
|
|
|
664
668
|
pluginsStatus,
|
|
665
669
|
loadCoreMemoryContext,
|
|
666
670
|
} = createCwdPlugins({
|
|
667
|
-
getCurrentCwd: () => currentCwd,
|
|
668
|
-
setCurrentCwd: (next) => { currentCwd = next; },
|
|
669
|
-
getConfig: () => config,
|
|
670
|
-
getSession: () => session,
|
|
671
|
-
getRoute: () => route,
|
|
672
|
-
getLastProjectMcpKey: () => lastProjectMcpKey,
|
|
673
|
-
setLastProjectMcpKey: (next) => { lastProjectMcpKey = next; },
|
|
671
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
672
|
+
setCurrentCwd: (next) => { rt.currentCwd = next; },
|
|
673
|
+
getConfig: () => rt.config,
|
|
674
|
+
getSession: () => rt.session,
|
|
675
|
+
getRoute: () => rt.route,
|
|
676
|
+
getLastProjectMcpKey: () => rt.lastProjectMcpKey,
|
|
677
|
+
setLastProjectMcpKey: (next) => { rt.lastProjectMcpKey = next; },
|
|
674
678
|
isCodeGraphPrewarmLazy: () => codeGraphPrewarmLazy,
|
|
675
|
-
isCodeGraphFirstTurnPrewarmDone: () => codeGraphFirstTurnPrewarmDone,
|
|
679
|
+
isCodeGraphFirstTurnPrewarmDone: () => rt.codeGraphFirstTurnPrewarmDone,
|
|
676
680
|
getCodeGraphPrewarmDelayMs: () => codeGraphPrewarmDelayMs,
|
|
677
|
-
setSessionNeedsCwdRefresh: (next) => { sessionNeedsCwdRefresh = next; },
|
|
681
|
+
setSessionNeedsCwdRefresh: (next) => { rt.sessionNeedsCwdRefresh = next; },
|
|
678
682
|
connectConfiguredMcp,
|
|
679
683
|
invalidatePreSessionToolSurface: (...a) => invalidatePreSessionToolSurface(...a),
|
|
680
684
|
scheduleCodeGraphPrewarm: (...a) => scheduleCodeGraphPrewarm(...a),
|
|
@@ -706,7 +710,7 @@ export async function createMixdogSessionRuntime({
|
|
|
706
710
|
dataDir: cfgMod.getPluginData(),
|
|
707
711
|
cwd,
|
|
708
712
|
awaitKeychainPrewarm,
|
|
709
|
-
isKeychainPrewarmReady: () => keychainPrewarmWaitDone,
|
|
713
|
+
isKeychainPrewarmReady: () => rt.keychainPrewarmWaitDone,
|
|
710
714
|
// SubagentStart/SubagentStop: bridge internal worker spawn/finish to the
|
|
711
715
|
// standard hook bus. agent_type is passed top-level via hookCommonPayload
|
|
712
716
|
// (added to hook-bus buildEventPayload passthrough). Best-effort.
|
|
@@ -723,7 +727,7 @@ export async function createMixdogSessionRuntime({
|
|
|
723
727
|
bootProfile('agent:ready', { ms: (performance.now() - agentToolStartedAt).toFixed(1) });
|
|
724
728
|
const agentStatusState = () => {
|
|
725
729
|
try {
|
|
726
|
-
const status = agentTool.getStatus?.({ clientHostPid: session?.clientHostPid || process.pid }) || {};
|
|
730
|
+
const status = agentTool.getStatus?.({ clientHostPid: rt.session?.clientHostPid || process.pid }) || {};
|
|
727
731
|
return {
|
|
728
732
|
agentWorkers: Array.isArray(status.workers) ? status.workers : [],
|
|
729
733
|
agentJobs: Array.isArray(status.jobs) ? status.jobs : [],
|
|
@@ -744,28 +748,10 @@ export async function createMixdogSessionRuntime({
|
|
|
744
748
|
// newer remote session. Drop remote mode entirely on this session (no
|
|
745
749
|
// handover, no retry) and tell UI listeners so the indicator updates.
|
|
746
750
|
if (msg?.method === 'notifications/mixdog/remote') {
|
|
747
|
-
|
|
748
|
-
remoteClaimPending = false;
|
|
749
|
-
if (msg?.params?.state === 'superseded' && remoteEnabled) {
|
|
751
|
+
if (msg?.params?.state === 'superseded' && rt.remoteEnabled) {
|
|
750
752
|
stopRemote('superseded-by-newer-remote-session');
|
|
751
753
|
emitRemoteStateChange(false, 'superseded');
|
|
752
754
|
}
|
|
753
|
-
// Symmetric acquire: the worker took the bridge seat (boot make-before-
|
|
754
|
-
// break or activate). Flip remote ON via the same side-state a user
|
|
755
|
-
// /remote toggles — remoteEnabled + transcript writer — but WITHOUT
|
|
756
|
-
// re-invoking channel start (the worker is already running; startRemote
|
|
757
|
-
// would re-fork/activate). Idempotent: no-op when already enabled.
|
|
758
|
-
if (msg?.params?.state === 'acquired' && !remoteEnabled) {
|
|
759
|
-
remoteEnabled = true;
|
|
760
|
-
if (!remoteSessionId) remoteSessionId = session?.id || null;
|
|
761
|
-
ensureRemoteTranscriptWriter();
|
|
762
|
-
// Auto-acquire: the worker restored yesterday's transcript from
|
|
763
|
-
// persisted status and we just created the CURRENT writer. Push the
|
|
764
|
-
// repoint now instead of waiting for the next inbound parent-chain
|
|
765
|
-
// steal, so outbound forwarding never tails a stale transcript.
|
|
766
|
-
pushTranscriptRebind();
|
|
767
|
-
emitRemoteStateChange(true, 'acquired');
|
|
768
|
-
}
|
|
769
755
|
return;
|
|
770
756
|
}
|
|
771
757
|
if (msg?.method !== 'notifications/claude/channel') return;
|
|
@@ -774,8 +760,8 @@ export async function createMixdogSessionRuntime({
|
|
|
774
760
|
const content = channelNotificationModelContent(params);
|
|
775
761
|
if (!content) return;
|
|
776
762
|
const { handled } = emitRuntimeNotification(content, meta);
|
|
777
|
-
if (!handled && session?.id && shouldMirrorChannelNotificationToPending(meta)) {
|
|
778
|
-
try { mgr.enqueuePendingMessage(session.id, content); } catch {}
|
|
763
|
+
if (!handled && rt.session?.id && shouldMirrorChannelNotificationToPending(meta)) {
|
|
764
|
+
try { mgr.enqueuePendingMessage(rt.session.id, content); } catch {}
|
|
779
765
|
}
|
|
780
766
|
},
|
|
781
767
|
});
|
|
@@ -811,12 +797,12 @@ export async function createMixdogSessionRuntime({
|
|
|
811
797
|
applyPreSessionToolSelection,
|
|
812
798
|
} = createToolSurface({
|
|
813
799
|
mgr,
|
|
814
|
-
mode,
|
|
800
|
+
mode: rt.mode,
|
|
815
801
|
standaloneTools,
|
|
816
802
|
agentToolNames,
|
|
817
|
-
getSession: () => session,
|
|
818
|
-
getRoute: () => route,
|
|
819
|
-
getConfig: () => config,
|
|
803
|
+
getSession: () => rt.session,
|
|
804
|
+
getRoute: () => rt.route,
|
|
805
|
+
getConfig: () => rt.config,
|
|
820
806
|
cfgMod,
|
|
821
807
|
loadWorkflowPack,
|
|
822
808
|
activeWorkflowId,
|
|
@@ -824,20 +810,20 @@ export async function createMixdogSessionRuntime({
|
|
|
824
810
|
});
|
|
825
811
|
|
|
826
812
|
const { contextStatus: computeContextStatus, invalidateContextStatusCache } = createContextStatus({
|
|
827
|
-
getSession: () => session,
|
|
828
|
-
getRoute: () => route,
|
|
829
|
-
getCurrentCwd: () => currentCwd,
|
|
830
|
-
getMode: () => mode,
|
|
813
|
+
getSession: () => rt.session,
|
|
814
|
+
getRoute: () => rt.route,
|
|
815
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
816
|
+
getMode: () => rt.mode,
|
|
831
817
|
});
|
|
832
818
|
internalTools.setInternalToolsProvider({
|
|
833
819
|
tools: [...standaloneTools, ...searchRuntimeTools.filter((tool) => tool?.public === false)],
|
|
834
820
|
executor: async (name, args, callerCtx = {}) => {
|
|
835
|
-
const callerCwd = callerCtx?.callerCwd || currentCwd;
|
|
821
|
+
const callerCwd = callerCtx?.callerCwd || rt.currentCwd;
|
|
836
822
|
if (name === 'search' || name === 'web_fetch' || name === 'local_fetch' || name === 'image_fetch') {
|
|
837
823
|
return dispatchSearchRuntimeTool(name, args, callerCtx, {
|
|
838
824
|
getSearchModule,
|
|
839
|
-
getCurrentCwd: () => currentCwd,
|
|
840
|
-
getSession: () => session,
|
|
825
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
826
|
+
getSession: () => rt.session,
|
|
841
827
|
notifyFnForSession,
|
|
842
828
|
runNativeWebSearch,
|
|
843
829
|
});
|
|
@@ -848,7 +834,7 @@ export async function createMixdogSessionRuntime({
|
|
|
848
834
|
return await memoryMod.handleToolCall(
|
|
849
835
|
name,
|
|
850
836
|
memoryToolArgsForCaller(args, callerCwd),
|
|
851
|
-
callerCtx?.signal || session?.controller?.signal || null,
|
|
837
|
+
callerCtx?.signal || rt.session?.controller?.signal || null,
|
|
852
838
|
);
|
|
853
839
|
}
|
|
854
840
|
if (name === 'code_graph') {
|
|
@@ -857,20 +843,20 @@ export async function createMixdogSessionRuntime({
|
|
|
857
843
|
return await codeGraphMod.executeCodeGraphTool(name, args || {}, args?.cwd || callerCwd);
|
|
858
844
|
}
|
|
859
845
|
if (name === 'tool_search' || name === 'load_tool') {
|
|
860
|
-
return renderToolSearch(args, activeToolSurface(), mode, { mcpStatus });
|
|
846
|
+
return renderToolSearch(args, activeToolSurface(), rt.mode, { mcpStatus });
|
|
861
847
|
}
|
|
862
848
|
if (name === 'explore') {
|
|
863
|
-
const callerSessionId = callerCtx?.callerSessionId || session?.id || null;
|
|
849
|
+
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
864
850
|
return await runExplore(args || {}, {
|
|
865
851
|
callerCwd: args?.cwd ? resolveCwdPath(args.cwd) : callerCwd,
|
|
866
852
|
callerSessionId,
|
|
867
853
|
routingSessionId: callerSessionId,
|
|
868
|
-
clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
|
|
854
|
+
clientHostPid: callerCtx?.clientHostPid || rt.session?.clientHostPid || process.pid,
|
|
869
855
|
notifyFn: notifyFnForSession(callerSessionId),
|
|
870
856
|
// Cascade caller cancellation (ESC / owner abort) into the explore
|
|
871
857
|
// fan-out so every child dispatch tears down immediately — same
|
|
872
858
|
// signal the adjacent native search already forwards.
|
|
873
|
-
signal: callerCtx?.signal || session?.controller?.signal,
|
|
859
|
+
signal: callerCtx?.signal || rt.session?.controller?.signal,
|
|
874
860
|
});
|
|
875
861
|
}
|
|
876
862
|
if (name === 'cwd') {
|
|
@@ -880,17 +866,17 @@ export async function createMixdogSessionRuntime({
|
|
|
880
866
|
} else if (action !== 'get') {
|
|
881
867
|
throw new Error(`cwd: unknown action "${action}"`);
|
|
882
868
|
}
|
|
883
|
-
return JSON.stringify({ cwd: currentCwd, sessionId: session?.id || null }, null, 2);
|
|
869
|
+
return JSON.stringify({ cwd: rt.currentCwd, sessionId: rt.session?.id || null }, null, 2);
|
|
884
870
|
}
|
|
885
871
|
if (name === 'session_manage') {
|
|
886
872
|
// Lead/owner sessions only: an agent worker resetting its own
|
|
887
873
|
// transcript mid-task would corrupt the delegation contract, and it
|
|
888
874
|
// must never reach the owner conversation either.
|
|
889
875
|
const callerSessionId = callerCtx?.callerSessionId || null;
|
|
890
|
-
if (callerSessionId && session?.id && callerSessionId !== session.id) {
|
|
876
|
+
if (callerSessionId && rt.session?.id && callerSessionId !== rt.session.id) {
|
|
891
877
|
throw new Error('session_manage: only the lead session may reset the conversation');
|
|
892
878
|
}
|
|
893
|
-
if (!session?.id) throw new Error('session_manage: no active session');
|
|
879
|
+
if (!rt.session?.id) throw new Error('session_manage: no active session');
|
|
894
880
|
const action = clean(args?.action).toLowerCase();
|
|
895
881
|
if (action !== 'clear' && action !== 'compact_clear') {
|
|
896
882
|
throw new Error(`session_manage: unknown action "${action}" (use clear | compact_clear)`);
|
|
@@ -900,7 +886,7 @@ export async function createMixdogSessionRuntime({
|
|
|
900
886
|
// boundary the idle auto-clear uses).
|
|
901
887
|
// Pin to the current session id so a resume/new-session between
|
|
902
888
|
// scheduling and consumption can never clear the wrong conversation.
|
|
903
|
-
pendingSessionReset = { action, sessionId: session.id };
|
|
889
|
+
rt.pendingSessionReset = { action, sessionId: rt.session.id };
|
|
904
890
|
return action === 'clear'
|
|
905
891
|
? 'Session reset scheduled: full clear will run when this turn ends. All prior context will be gone.'
|
|
906
892
|
: 'Session reset scheduled: the conversation will be summarized (compact) and cleared when this turn ends; key context carries forward in the summary.';
|
|
@@ -909,12 +895,12 @@ export async function createMixdogSessionRuntime({
|
|
|
909
895
|
return skillToolContent(args?.name);
|
|
910
896
|
}
|
|
911
897
|
if (name === 'agent') {
|
|
912
|
-
const callerSessionId = callerCtx?.callerSessionId || session?.id || null;
|
|
898
|
+
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
913
899
|
return await agentTool.execute(args, {
|
|
914
900
|
callerCwd,
|
|
915
901
|
invocationSource: 'model-tool',
|
|
916
902
|
callerSessionId,
|
|
917
|
-
clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
|
|
903
|
+
clientHostPid: callerCtx?.clientHostPid || rt.session?.clientHostPid || process.pid,
|
|
918
904
|
signal: callerCtx?.signal,
|
|
919
905
|
notifyFn: notifyFnForSession(callerSessionId),
|
|
920
906
|
});
|
|
@@ -927,7 +913,7 @@ export async function createMixdogSessionRuntime({
|
|
|
927
913
|
},
|
|
928
914
|
});
|
|
929
915
|
internalTools.markBootReady?.();
|
|
930
|
-
try { lastProjectMcpKey = resolve(currentCwd) + '\u0000' + JSON.stringify(readProjectMcpServers(currentCwd)); } catch { lastProjectMcpKey = null; }
|
|
916
|
+
try { rt.lastProjectMcpKey = resolve(rt.currentCwd) + '\u0000' + JSON.stringify(readProjectMcpServers(rt.currentCwd)); } catch { rt.lastProjectMcpKey = null; }
|
|
931
917
|
void connectConfiguredMcp()
|
|
932
918
|
.then((status) => bootProfile('mcp:ready', {
|
|
933
919
|
connected: Number(status?.connectedCount || 0),
|
|
@@ -976,13 +962,13 @@ export async function createMixdogSessionRuntime({
|
|
|
976
962
|
displayConfig,
|
|
977
963
|
ensureConfigForRouteProvider,
|
|
978
964
|
} = createConfigLifecycle({
|
|
979
|
-
getConfig: () => config,
|
|
980
|
-
setConfig: (next) => { config = next; },
|
|
981
|
-
getSearchRoute: () => searchRoute,
|
|
982
|
-
setSearchRoute: (next) => { searchRoute = next; },
|
|
983
|
-
getConfigHasSecrets: () => configHasSecrets,
|
|
984
|
-
setConfigHasSecrets: (next) => { configHasSecrets = next; },
|
|
985
|
-
getRoute: () => route,
|
|
965
|
+
getConfig: () => rt.config,
|
|
966
|
+
setConfig: (next) => { rt.config = next; },
|
|
967
|
+
getSearchRoute: () => rt.searchRoute,
|
|
968
|
+
setSearchRoute: (next) => { rt.searchRoute = next; },
|
|
969
|
+
getConfigHasSecrets: () => rt.configHasSecrets,
|
|
970
|
+
setConfigHasSecrets: (next) => { rt.configHasSecrets = next; },
|
|
971
|
+
getRoute: () => rt.route,
|
|
986
972
|
cfgMod,
|
|
987
973
|
sharedCfgMod,
|
|
988
974
|
setBackend,
|
|
@@ -997,7 +983,7 @@ export async function createMixdogSessionRuntime({
|
|
|
997
983
|
STANDALONE_DATA_DIR,
|
|
998
984
|
});
|
|
999
985
|
|
|
1000
|
-
async function ensureProvidersReady(providerConfig = config.providers || {}) {
|
|
986
|
+
async function ensureProvidersReady(providerConfig = rt.config.providers || {}) {
|
|
1001
987
|
await awaitKeychainPrewarm();
|
|
1002
988
|
const initKey = providerInitCacheKey(providerConfig);
|
|
1003
989
|
const existing = providerInitPromises.get(initKey);
|
|
@@ -1017,16 +1003,16 @@ export async function createMixdogSessionRuntime({
|
|
|
1017
1003
|
if (providerInitPromises.get(initKey) === providerInitPromise) providerInitPromises.delete(initKey);
|
|
1018
1004
|
throw error;
|
|
1019
1005
|
}
|
|
1020
|
-
if (!startupProviderCatalogRefreshStarted && !closeRequested) {
|
|
1021
|
-
startupProviderCatalogRefreshStarted = true;
|
|
1022
|
-
startupProviderCatalogRefreshPending = true;
|
|
1006
|
+
if (!rt.startupProviderCatalogRefreshStarted && !rt.closeRequested) {
|
|
1007
|
+
rt.startupProviderCatalogRefreshStarted = true;
|
|
1008
|
+
rt.startupProviderCatalogRefreshPending = true;
|
|
1023
1009
|
try {
|
|
1024
1010
|
void Promise.resolve(reg.refreshProviderCatalogsOnStartup())
|
|
1025
1011
|
.then(() => {
|
|
1026
1012
|
// Fresh catalog rows invalidate model-derived caches, but the
|
|
1027
1013
|
// already initialized provider registry remains valid.
|
|
1028
1014
|
invalidateProviderCaches({ preserveProviderInit: true });
|
|
1029
|
-
startupProviderCatalogRefreshPending = false;
|
|
1015
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
1030
1016
|
// Secrets-aware: a no-secrets rewarm bumps the load sequence and
|
|
1031
1017
|
// is never adopted, so it discarded the in-flight authoritative
|
|
1032
1018
|
// load and left the picker cache empty — every later consumer then
|
|
@@ -1035,12 +1021,12 @@ export async function createMixdogSessionRuntime({
|
|
|
1035
1021
|
bootProfile('provider-catalogs:refresh-ready');
|
|
1036
1022
|
})
|
|
1037
1023
|
.catch((error) => {
|
|
1038
|
-
startupProviderCatalogRefreshPending = false;
|
|
1024
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
1039
1025
|
bootProfile('provider-catalogs:refresh-failed', { error: error?.message || String(error) });
|
|
1040
1026
|
});
|
|
1041
1027
|
bootProfile('provider-catalogs:refresh-started');
|
|
1042
1028
|
} catch (error) {
|
|
1043
|
-
startupProviderCatalogRefreshPending = false;
|
|
1029
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
1044
1030
|
bootProfile('provider-catalogs:refresh-failed', { error: error?.message || String(error) });
|
|
1045
1031
|
}
|
|
1046
1032
|
}
|
|
@@ -1051,11 +1037,11 @@ export async function createMixdogSessionRuntime({
|
|
|
1051
1037
|
currentMainSearchModelMeta,
|
|
1052
1038
|
runNativeWebSearch,
|
|
1053
1039
|
} = createNativeSearch({
|
|
1054
|
-
getRoute: () => route,
|
|
1055
|
-
getSearchRoute: () => searchRoute,
|
|
1056
|
-
setSearchRoute: (next) => { searchRoute = next; },
|
|
1057
|
-
getConfig: () => config,
|
|
1058
|
-
getSession: () => session,
|
|
1040
|
+
getRoute: () => rt.route,
|
|
1041
|
+
getSearchRoute: () => rt.searchRoute,
|
|
1042
|
+
setSearchRoute: (next) => { rt.searchRoute = next; },
|
|
1043
|
+
getConfig: () => rt.config,
|
|
1044
|
+
getSession: () => rt.session,
|
|
1059
1045
|
getReg: () => reg,
|
|
1060
1046
|
ensureFullConfig,
|
|
1061
1047
|
awaitKeychainPrewarm,
|
|
@@ -1072,23 +1058,25 @@ export async function createMixdogSessionRuntime({
|
|
|
1072
1058
|
// cachedProviderSetup(quick) may nudge scheduleProviderSetupWarmup on a cold
|
|
1073
1059
|
// quick-cache fill. Thread it by reference so the scheduler is reachable once
|
|
1074
1060
|
// it exists (a pre-scheduler quick fill simply skips the warmup nudge).
|
|
1075
|
-
|
|
1061
|
+
rt.scheduleProviderSetupWarmupRef = () => {};
|
|
1076
1062
|
const {
|
|
1077
1063
|
refreshStatuslineUsageSnapshot,
|
|
1078
1064
|
cachedProviderSetup,
|
|
1079
1065
|
hasProviderSetupCached,
|
|
1080
1066
|
getUsageDashboard,
|
|
1067
|
+
consumeCodexRateLimitResetCredit,
|
|
1081
1068
|
} = createProviderUsage({
|
|
1082
1069
|
caches: providerUsageCaches,
|
|
1083
|
-
getConfig: () => config,
|
|
1070
|
+
getConfig: () => rt.config,
|
|
1084
1071
|
getReg: () => reg,
|
|
1085
1072
|
displayConfig,
|
|
1086
1073
|
providerSetup,
|
|
1087
1074
|
createUsageDashboard,
|
|
1088
1075
|
fetchOAuthUsageSnapshot,
|
|
1089
|
-
|
|
1076
|
+
consumeOpenAICodexResetCredit,
|
|
1077
|
+
isCloseRequested: () => rt.closeRequested,
|
|
1090
1078
|
getProviderSetupWarmupTimer: () => warmupTimers.providerSetupWarmupTimer,
|
|
1091
|
-
scheduleProviderSetupWarmup: (delayMs) => scheduleProviderSetupWarmupRef(delayMs),
|
|
1079
|
+
scheduleProviderSetupWarmup: (delayMs) => rt.scheduleProviderSetupWarmupRef(delayMs),
|
|
1092
1080
|
});
|
|
1093
1081
|
|
|
1094
1082
|
// Holder filled after createQuickModelRows resolves; provider-models and
|
|
@@ -1100,7 +1088,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1100
1088
|
// lookupModelMeta may fire scheduleProviderModelWarmup on a cache miss. Thread
|
|
1101
1089
|
// it by reference so the scheduler is called once it exists (miss handling is
|
|
1102
1090
|
// best-effort; a pre-scheduler miss simply skips the warmup nudge).
|
|
1103
|
-
|
|
1091
|
+
rt.scheduleProviderModelWarmupRef = () => {};
|
|
1104
1092
|
const {
|
|
1105
1093
|
modelMetaKey,
|
|
1106
1094
|
lookupModelMeta,
|
|
@@ -1113,8 +1101,8 @@ export async function createMixdogSessionRuntime({
|
|
|
1113
1101
|
} = createProviderModels({
|
|
1114
1102
|
caches: providerModelCaches,
|
|
1115
1103
|
modelMetaByRoute,
|
|
1116
|
-
getRoute: () => route,
|
|
1117
|
-
getConfig: () => config,
|
|
1104
|
+
getRoute: () => rt.route,
|
|
1105
|
+
getConfig: () => rt.config,
|
|
1118
1106
|
getReg: () => reg,
|
|
1119
1107
|
searchCapableFor,
|
|
1120
1108
|
sortProviderModelsRaw,
|
|
@@ -1125,7 +1113,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1125
1113
|
awaitKeychainPrewarm,
|
|
1126
1114
|
ensureProvidersReady,
|
|
1127
1115
|
bootProfile,
|
|
1128
|
-
scheduleProviderModelWarmup: () => scheduleProviderModelWarmupRef(),
|
|
1116
|
+
scheduleProviderModelWarmup: () => rt.scheduleProviderModelWarmupRef(),
|
|
1129
1117
|
quickHelpers: providerModelQuickHelpers,
|
|
1130
1118
|
});
|
|
1131
1119
|
|
|
@@ -1136,8 +1124,8 @@ export async function createMixdogSessionRuntime({
|
|
|
1136
1124
|
searchModelsFromRows,
|
|
1137
1125
|
searchRowsWithDefault,
|
|
1138
1126
|
} = createQuickModelRows({
|
|
1139
|
-
getRoute: () => route,
|
|
1140
|
-
getSearchRoute: () => searchRoute,
|
|
1127
|
+
getRoute: () => rt.route,
|
|
1128
|
+
getSearchRoute: () => rt.searchRoute,
|
|
1141
1129
|
displayConfig,
|
|
1142
1130
|
providerModelCacheRow,
|
|
1143
1131
|
providerModelsFromCacheRows,
|
|
@@ -1158,289 +1146,71 @@ export async function createMixdogSessionRuntime({
|
|
|
1158
1146
|
searchRowsWithDefault,
|
|
1159
1147
|
});
|
|
1160
1148
|
|
|
1161
|
-
|
|
1162
|
-
if (routeHasModel()) return route;
|
|
1163
|
-
const models = await collectProviderModels();
|
|
1164
|
-
const picked = models[0] || null;
|
|
1165
|
-
if (!picked) {
|
|
1166
|
-
throw new Error('No provider models available. Open /providers to sign in, then /model to choose a model.');
|
|
1167
|
-
}
|
|
1168
|
-
route = {
|
|
1169
|
-
...route,
|
|
1170
|
-
provider: picked.provider,
|
|
1171
|
-
model: picked.id,
|
|
1172
|
-
preset: null,
|
|
1173
|
-
};
|
|
1174
|
-
return route;
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
|
-
async function refreshRouteEffort(modelMetaOverride = null) {
|
|
1178
|
-
await ensureProvidersReady(ensureProviderEnabled(config, route.provider));
|
|
1179
|
-
const modelMeta = modelMetaOverride || await lookupModelMeta(route.provider, route.model);
|
|
1180
|
-
const requested = hasOwn(route, 'effort') ? route.effort : (route.preset?.effort || null);
|
|
1181
|
-
const effectiveEffort = coerceEffortFor(route.provider, modelMeta, requested);
|
|
1182
|
-
const fastCapable = fastCapableFor(route.provider, modelMeta);
|
|
1183
|
-
// Carry the catalog display name onto the route so the statusline shows a
|
|
1184
|
-
// human label (e.g. "Claude Fable 5") for preset-less direct models instead
|
|
1185
|
-
// of the raw id. `name` is only trusted when it differs from the raw model
|
|
1186
|
-
// id (some providers echo the id as `name`), so it can't clobber a better
|
|
1187
|
-
// already-resolved label. Falls back to existing route.modelDisplay, then unset.
|
|
1188
|
-
const metaName = clean(modelMeta?.name);
|
|
1189
|
-
const modelDisplay = clean(modelMeta?.display) || clean(modelMeta?.displayName)
|
|
1190
|
-
|| (metaName && metaName !== clean(route.model) ? metaName : '')
|
|
1191
|
-
|| clean(route.modelDisplay);
|
|
1192
|
-
route = {
|
|
1193
|
-
...route,
|
|
1194
|
-
fast: fastCapable ? route.fast === true : false,
|
|
1195
|
-
fastCapable,
|
|
1196
|
-
effectiveEffort,
|
|
1197
|
-
effortOptions: effortItemsFor(route.provider, modelMeta, effectiveEffort),
|
|
1198
|
-
...(modelDisplay ? { modelDisplay } : {}),
|
|
1199
|
-
};
|
|
1200
|
-
return route;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
function routeHasModel() {
|
|
1204
|
-
return !!clean(route?.model);
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
function requireModelRoute() {
|
|
1208
|
-
if (routeHasModel()) return;
|
|
1209
|
-
throw new Error('No model configured. Open /providers to sign in, then /model to choose a model.');
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
async function recreateCurrentSessionIfReady() {
|
|
1213
|
-
if (!routeHasModel()) {
|
|
1214
|
-
session = null;
|
|
1215
|
-
return null;
|
|
1216
|
-
}
|
|
1217
|
-
return await createCurrentSession();
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
async function createCurrentSession(reason = 'demand') {
|
|
1221
|
-
if (sessionCreatePromise) return await sessionCreatePromise;
|
|
1222
|
-
if (session?.id && !sessionNeedsCwdRefresh) {
|
|
1223
|
-
const liveSession = mgr.getSession(session.id);
|
|
1224
|
-
if (liveSession && liveSession.closed !== true && liveSession.status !== 'closed') {
|
|
1225
|
-
session = liveSession;
|
|
1226
|
-
return session;
|
|
1227
|
-
}
|
|
1228
|
-
session = null;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
const startedAt = performance.now();
|
|
1232
|
-
bootProfile('session:create:start', { mode, reason });
|
|
1233
|
-
const promise = (async () => {
|
|
1234
|
-
// Demand-only: this starts only after the user submits (unless an
|
|
1235
|
-
// explicitly enabled prewarm caller asks for a session). Core-memory
|
|
1236
|
-
// startup does not depend on keychain/provider readiness, so overlap the
|
|
1237
|
-
// two cold paths instead of paying their bounded waits serially.
|
|
1238
|
-
const coreMemoryContextPromise = loadCoreMemoryContext();
|
|
1239
|
-
await awaitKeychainPrewarm();
|
|
1240
|
-
ensureConfigForRouteProvider();
|
|
1241
|
-
await resolveMissingRouteModelForFirstTurn();
|
|
1242
|
-
requireModelRoute();
|
|
1243
|
-
bootProfile('session:create:route-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
1244
|
-
// Route effort waits on provider readiness while the already-started
|
|
1245
|
-
// memory load continues independently.
|
|
1246
|
-
const [, coreMemoryContext] = await Promise.all([
|
|
1247
|
-
refreshRouteEffort(),
|
|
1248
|
-
coreMemoryContextPromise,
|
|
1249
|
-
]);
|
|
1250
|
-
bootProfile('session:create:effort-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
1251
|
-
const providerImpl = reg.getProvider(route.provider);
|
|
1252
|
-
if (!providerImpl) {
|
|
1253
|
-
throw new Error(`Provider "${route.provider}" is not configured.`);
|
|
1254
|
-
}
|
|
1255
|
-
bootProfile('session:create:provider-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
1256
|
-
if (closeRequested) throw new Error('runtime is closing');
|
|
1257
|
-
const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
|
|
1258
|
-
// Load the active WORKFLOW.md pack once for both summary + context block.
|
|
1259
|
-
const { summary: workflow, context: workflowContext } = activeWorkflowContext(config, dataDir);
|
|
1260
|
-
const sessionOpts = {
|
|
1261
|
-
provider: route.provider,
|
|
1262
|
-
model: route.model,
|
|
1263
|
-
preset: route.preset || undefined,
|
|
1264
|
-
tools: toolSpecForMode(mode),
|
|
1265
|
-
owner: 'cli',
|
|
1266
|
-
agent: 'lead',
|
|
1267
|
-
lane: 'cli',
|
|
1268
|
-
sourceType: 'lead',
|
|
1269
|
-
sourceName: 'main',
|
|
1270
|
-
clientHostPid: process.pid,
|
|
1271
|
-
disallowedTools: LEAD_DISALLOWED_TOOLS,
|
|
1272
|
-
cwd: currentCwd,
|
|
1273
|
-
...(desktopSession && typeof desktopSession === 'object' ? { desktopSession } : {}),
|
|
1274
|
-
coreMemoryContext,
|
|
1275
|
-
workflow,
|
|
1276
|
-
workflowContext,
|
|
1277
|
-
fast: route.fast === true,
|
|
1278
|
-
compaction: config.compaction && typeof config.compaction === 'object'
|
|
1279
|
-
? normalizeCompactionConfig(config.compaction, { memoryEnabled: memoryEnabled() })
|
|
1280
|
-
: undefined,
|
|
1281
|
-
};
|
|
1282
|
-
if (hasOwn(route, 'effort') || route.effectiveEffort) {
|
|
1283
|
-
sessionOpts.effort = route.effectiveEffort || null;
|
|
1284
|
-
}
|
|
1285
|
-
session = mgr.createSession(sessionOpts);
|
|
1286
|
-
sessionNeedsCwdRefresh = false;
|
|
1287
|
-
attachSessionHooks(session, { hooks, hookCommonPayload, getCwd: () => currentCwd });
|
|
1288
|
-
// Every-create MCP fold (NO blocking): seed the INITIAL provider-visible
|
|
1289
|
-
// surface (and native BP1 manifest) from MCP servers connected at create
|
|
1290
|
-
// time. There is no await — a boot connect still mid-handshake is caught on
|
|
1291
|
-
// the first user turn by refreshInitialDeferredMcpSurface (session-turn-api),
|
|
1292
|
-
// which re-folds the live registry into the first-turn surface before the
|
|
1293
|
-
// prompt renders. This fold keeps recreate paths (cwd change with MCP
|
|
1294
|
-
// already connected) seeding their manifest instead of re-announcing late.
|
|
1295
|
-
let connectedMcpTools = [];
|
|
1296
|
-
try { connectedMcpTools = mcpClient.getMcpTools?.() || []; }
|
|
1297
|
-
catch { connectedMcpTools = []; }
|
|
1298
|
-
applyDeferredToolSurface(
|
|
1299
|
-
session,
|
|
1300
|
-
deferredSurfaceModeForLead(mode),
|
|
1301
|
-
connectedMcpTools.length ? [...modelStandaloneTools(), ...connectedMcpTools] : modelStandaloneTools(),
|
|
1302
|
-
{ provider: route.provider },
|
|
1303
|
-
);
|
|
1304
|
-
// Session-local one-shot: mark this FRESH session eligible for the
|
|
1305
|
-
// first-turn deferred-surface refresh (session-turn-api). A resumed
|
|
1306
|
-
// session (prior transcript) is NEVER marked, so its already-baked BP1 is
|
|
1307
|
-
// never rebuilt or re-announced — the gate is per-session, not the
|
|
1308
|
-
// process-wide firstTurnCompleted.
|
|
1309
|
-
session.deferredInitialRefreshPending = !/resume/i.test(String(reason || ''));
|
|
1310
|
-
applyPreSessionToolSelection();
|
|
1311
|
-
writeStatuslineRoute(statusRoutes, session, route);
|
|
1312
|
-
hooks.emit('session:create', { sessionId: session.id, provider: route.provider, model: route.model, toolMode: mode, cwd: currentCwd });
|
|
1313
|
-
// SessionStart: bridge to the standard project hook bus. Best-effort;
|
|
1314
|
-
// a hook error must never break session creation. additionalContext is
|
|
1315
|
-
// injected before the first user turn as a system-reminder context pair.
|
|
1316
|
-
try {
|
|
1317
|
-
const startSource = /resume/i.test(String(reason || ''))
|
|
1318
|
-
? 'resume'
|
|
1319
|
-
: (/clear/i.test(String(reason || '')) ? 'clear' : 'startup');
|
|
1320
|
-
const startDispatch = await hooks.dispatch('SessionStart', hookCommonPayload({ session_id: session.id, source: startSource, model: route.model }));
|
|
1321
|
-
const startContext = Array.isArray(startDispatch?.additionalContext)
|
|
1322
|
-
? startDispatch.additionalContext.join('\n\n')
|
|
1323
|
-
: String(startDispatch?.additionalContext || '');
|
|
1324
|
-
if (startContext.trim()) {
|
|
1325
|
-
session.messages.push({ role: 'user', content: `<system-reminder>\n# SessionStart Hook Context\n${startContext.trim()}\n</system-reminder>` });
|
|
1326
|
-
session.messages.push({ role: 'assistant', content: '.' });
|
|
1327
|
-
session.updatedAt = Date.now();
|
|
1328
|
-
}
|
|
1329
|
-
} catch { /* best-effort: never break session create */ }
|
|
1330
|
-
bootProfile('session:create:ready', {
|
|
1331
|
-
ms: (performance.now() - startedAt).toFixed(1),
|
|
1332
|
-
reason,
|
|
1333
|
-
tools: Array.isArray(session.tools) ? session.tools.length : 0,
|
|
1334
|
-
catalog: Array.isArray(session.deferredToolCatalog) ? session.deferredToolCatalog.length : 0,
|
|
1335
|
-
});
|
|
1336
|
-
// A rebind push may have been deferred (e.g. 'acquired' landed before this
|
|
1337
|
-
// session existed). The writer is now bindable — flush it exactly once.
|
|
1338
|
-
flushPendingTranscriptRebind();
|
|
1339
|
-
return session;
|
|
1340
|
-
})();
|
|
1341
|
-
|
|
1342
|
-
sessionCreatePromise = promise;
|
|
1343
|
-
try {
|
|
1344
|
-
return await promise;
|
|
1345
|
-
} finally {
|
|
1346
|
-
if (sessionCreatePromise === promise) sessionCreatePromise = null;
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1149
|
+
// Route resolution + createCurrentSession: session-lifecycle.mjs.
|
|
1350
1150
|
const {
|
|
1151
|
+
resolveMissingRouteModelForFirstTurn,
|
|
1351
1152
|
scheduleProviderWarmup,
|
|
1352
1153
|
scheduleProviderSetupWarmup,
|
|
1353
1154
|
scheduleProviderModelWarmup,
|
|
1354
1155
|
scheduleModelCatalogWarmup,
|
|
1355
1156
|
scheduleStatuslineUsageWarmup,
|
|
1356
1157
|
scheduleStatuslineUsageRefresh,
|
|
1357
|
-
} = createWarmupSchedulers({
|
|
1358
|
-
timers: warmupTimers,
|
|
1359
|
-
bootProfile,
|
|
1360
|
-
getRoute: () => route,
|
|
1361
|
-
getConfig: () => config,
|
|
1362
|
-
isCloseRequested: () => closeRequested,
|
|
1363
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1364
|
-
getSessionCreatePromise: () => sessionCreatePromise,
|
|
1365
|
-
getProviderModelsCache: () => providerModelCaches.providerModelsCache,
|
|
1366
|
-
getProviderModelsPromise: () => providerModelCaches.providerModelsPromise,
|
|
1367
|
-
reloadFullConfig,
|
|
1368
|
-
ensureConfigForRouteProvider,
|
|
1369
|
-
awaitKeychainPrewarm,
|
|
1370
|
-
ensureProvidersReady,
|
|
1371
|
-
ensureProviderEnabled,
|
|
1372
|
-
refreshStatuslineUsageSnapshot,
|
|
1373
|
-
warmProviderModelCache,
|
|
1374
|
-
cachedProviderSetup,
|
|
1375
|
-
warmCatalogsInBackground,
|
|
1376
|
-
isFirstTurnCompleted: () => firstTurnCompleted,
|
|
1377
|
-
isCatalogRefreshPending: () => startupProviderCatalogRefreshPending,
|
|
1378
|
-
envFlag,
|
|
1379
|
-
delays: {
|
|
1380
|
-
providerWarmupDelayMs,
|
|
1381
|
-
providerSetupWarmupDelayMs,
|
|
1382
|
-
providerModelWarmupDelayMs,
|
|
1383
|
-
modelCatalogWarmupDelayMs,
|
|
1384
|
-
statuslineUsageWarmupDelayMs,
|
|
1385
|
-
statuslineUsageRefreshDelayMs,
|
|
1386
|
-
backgroundBusyRetryMs,
|
|
1387
|
-
},
|
|
1388
|
-
flags: {
|
|
1389
|
-
providerWarmupEnabled,
|
|
1390
|
-
modelPrefetchEnabled,
|
|
1391
|
-
modelCatalogWarmupEnabled,
|
|
1392
|
-
},
|
|
1393
|
-
});
|
|
1394
|
-
scheduleProviderModelWarmupRef = scheduleProviderModelWarmup;
|
|
1395
|
-
scheduleProviderSetupWarmupRef = scheduleProviderSetupWarmup;
|
|
1396
|
-
|
|
1397
|
-
const {
|
|
1398
1158
|
scheduleCodeGraphPrewarm,
|
|
1399
1159
|
scheduleLeadSessionPrewarm,
|
|
1160
|
+
scheduleToolRuntimeWarmup,
|
|
1400
1161
|
invokeChannelStart,
|
|
1401
1162
|
scheduleChannelStart,
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
isCloseRequested: () => closeRequested,
|
|
1407
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1408
|
-
getSessionCreatePromise: () => sessionCreatePromise,
|
|
1409
|
-
getSession: () => session,
|
|
1410
|
-
isRemoteEnabled: () => remoteEnabled,
|
|
1411
|
-
channelsEnabled,
|
|
1412
|
-
hasActiveAutomation,
|
|
1413
|
-
getCodeGraphModule,
|
|
1163
|
+
refreshRouteEffort,
|
|
1164
|
+
routeHasModel,
|
|
1165
|
+
requireModelRoute,
|
|
1166
|
+
recreateCurrentSessionIfReady,
|
|
1414
1167
|
createCurrentSession,
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1168
|
+
remoteTranscript,
|
|
1169
|
+
} = createSessionLifecycle({
|
|
1170
|
+
rt,
|
|
1171
|
+
collectProviderModels,
|
|
1172
|
+
ensureProvidersReady,
|
|
1173
|
+
lookupModelMeta,
|
|
1174
|
+
mgr,
|
|
1175
|
+
loadCoreMemoryContext,
|
|
1176
|
+
awaitKeychainPrewarm,
|
|
1177
|
+
ensureConfigForRouteProvider,
|
|
1178
|
+
reg,
|
|
1179
|
+
cfgMod,
|
|
1180
|
+
activeWorkflowContext,
|
|
1181
|
+
memoryEnabled,
|
|
1182
|
+
hooks,
|
|
1183
|
+
hookCommonPayload,
|
|
1184
|
+
mcpClient,
|
|
1185
|
+
modelStandaloneTools,
|
|
1186
|
+
applyPreSessionToolSelection,
|
|
1187
|
+
statusRoutes,
|
|
1188
|
+
warmupTimers,
|
|
1189
|
+
providerModelCaches,
|
|
1190
|
+
reloadFullConfig,
|
|
1191
|
+
refreshStatuslineUsageSnapshot,
|
|
1192
|
+
warmProviderModelCache,
|
|
1193
|
+
cachedProviderSetup,
|
|
1194
|
+
providerWarmupDelayMs,
|
|
1195
|
+
providerSetupWarmupDelayMs,
|
|
1196
|
+
providerModelWarmupDelayMs,
|
|
1197
|
+
modelCatalogWarmupDelayMs,
|
|
1198
|
+
statuslineUsageWarmupDelayMs,
|
|
1199
|
+
statuslineUsageRefreshDelayMs,
|
|
1200
|
+
backgroundBusyRetryMs,
|
|
1201
|
+
providerWarmupEnabled,
|
|
1202
|
+
modelPrefetchEnabled,
|
|
1203
|
+
modelCatalogWarmupEnabled,
|
|
1204
|
+
prewarmTimers,
|
|
1440
1205
|
channelsEnabled,
|
|
1206
|
+
getCodeGraphModule,
|
|
1441
1207
|
channels,
|
|
1442
|
-
|
|
1443
|
-
|
|
1208
|
+
codeGraphPrewarmDelayMs,
|
|
1209
|
+
channelStartDelayMs,
|
|
1210
|
+
sessionPrewarmDelayMs,
|
|
1211
|
+
codeGraphPrewarmEnabled,
|
|
1212
|
+
sessionPrewarmEnabled,
|
|
1213
|
+
prewarmState,
|
|
1444
1214
|
});
|
|
1445
1215
|
const ensureRemoteTranscriptWriter = () => remoteTranscript.ensureRemoteTranscriptWriter();
|
|
1446
1216
|
const pushTranscriptRebind = () => remoteTranscript.pushTranscriptRebind();
|
|
@@ -1449,14 +1219,12 @@ export async function createMixdogSessionRuntime({
|
|
|
1449
1219
|
// Remote (channel relay) claim/release/stop live in remote-control.mjs; the
|
|
1450
1220
|
// facade injects the mutable session + remote state they mutate.
|
|
1451
1221
|
const remoteControl = createRemoteControl({
|
|
1452
|
-
getSession: () => session,
|
|
1453
|
-
isRemoteEnabled: () => remoteEnabled,
|
|
1454
|
-
setRemoteEnabled: (next) => { remoteEnabled = next; },
|
|
1455
|
-
getRemoteSessionId: () => remoteSessionId,
|
|
1456
|
-
setRemoteSessionId: (next) => { remoteSessionId = next; },
|
|
1457
|
-
|
|
1458
|
-
setRemoteClaimPending: (next) => { remoteClaimPending = next; },
|
|
1459
|
-
isCloseRequested: () => closeRequested,
|
|
1222
|
+
getSession: () => rt.session,
|
|
1223
|
+
isRemoteEnabled: () => rt.remoteEnabled,
|
|
1224
|
+
setRemoteEnabled: (next) => { rt.remoteEnabled = next; },
|
|
1225
|
+
getRemoteSessionId: () => rt.remoteSessionId,
|
|
1226
|
+
setRemoteSessionId: (next) => { rt.remoteSessionId = next; },
|
|
1227
|
+
isCloseRequested: () => rt.closeRequested,
|
|
1460
1228
|
clearChannelStartTimer: () => {
|
|
1461
1229
|
if (prewarmTimers.channelStartTimer) {
|
|
1462
1230
|
clearTimeout(prewarmTimers.channelStartTimer);
|
|
@@ -1482,7 +1250,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1482
1250
|
const stopRemote = (reason) => remoteControl.stopRemote(reason);
|
|
1483
1251
|
|
|
1484
1252
|
function isRemoteEnabled() {
|
|
1485
|
-
return remoteEnabled;
|
|
1253
|
+
return rt.remoteEnabled;
|
|
1486
1254
|
}
|
|
1487
1255
|
|
|
1488
1256
|
function withTeardownDeadline(promise, ms, fallback = false) {
|
|
@@ -1504,6 +1272,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1504
1272
|
codeGraphPrewarm: codeGraphPrewarmEnabled,
|
|
1505
1273
|
});
|
|
1506
1274
|
scheduleLeadSessionPrewarm();
|
|
1275
|
+
scheduleToolRuntimeWarmup();
|
|
1507
1276
|
// Lazy mode (default): skip the startup prewarm entirely; the first turn
|
|
1508
1277
|
// triggers it instead (see ask()). Eager mode keeps the old startup schedule.
|
|
1509
1278
|
if (!codeGraphPrewarmLazy) {
|
|
@@ -1523,32 +1292,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1523
1292
|
// Channels are opt-in: only boot the worker when this session started in (or
|
|
1524
1293
|
// was toggled into) remote mode. Non-remote sessions never contend for the
|
|
1525
1294
|
// channel; see startRemote()/stopRemote() and the `/remote` toggle.
|
|
1526
|
-
// `remote
|
|
1527
|
-
// remote claim at boot. Desktop sessions are explicit-only: opening the app
|
|
1528
|
-
// must never claim or spawn the channel bridge until its Remote button is
|
|
1529
|
-
// pressed, while the shared owner-state watcher still reflects a terminal
|
|
1530
|
-
// that already owns it.
|
|
1531
|
-
// The flag lives in the TOP-LEVEL `remote` section of mixdog-config.json
|
|
1532
|
-
// (sibling of agent/ui/channels), not inside the agent section that
|
|
1533
|
-
// cfgMod.loadConfig returns — read it via the shared whole-file reader.
|
|
1534
|
-
// `config?.remote?.autoStart` is kept for back-compat with agent-section
|
|
1535
|
-
// placement.
|
|
1536
|
-
// `remote` (from `mixdog --remote`) is EXPLICIT force-takeover. Config
|
|
1537
|
-
// `remote.autoStart` is AUTO: it must claim the seat ONLY if no live owner
|
|
1538
|
-
// exists (claim-if-vacant) and back off silently otherwise — so it does NOT
|
|
1539
|
-
// set remoteEnabled up front (that would assert remote before the claim is
|
|
1540
|
-
// known to have won). Track it as a separate deferred auto request; the
|
|
1541
|
-
// worker's acquire notification flips remoteEnabled if/when it wins the seat.
|
|
1542
|
-
let remoteAutoStartRequested = false;
|
|
1543
|
-
if (!remoteEnabled && !desktopSession) {
|
|
1544
|
-
try {
|
|
1545
|
-
if (config?.remote?.autoStart === true
|
|
1546
|
-
|| sharedCfgMod?.readSection?.('remote')?.autoStart === true) {
|
|
1547
|
-
remoteAutoStartRequested = true;
|
|
1548
|
-
}
|
|
1549
|
-
} catch { /* unreadable config never blocks boot */ }
|
|
1550
|
-
}
|
|
1551
|
-
// Boot-time remote start (autoStart or --remote) is DEFERRED past the TUI's
|
|
1295
|
+
// A manual `mixdog --remote` request is deferred past the TUI's
|
|
1552
1296
|
// first frame. startRemote() front-loads heavy work — memory daemon fork
|
|
1553
1297
|
// (PG + forced ONNX embed warmup in the child), eager session create, and
|
|
1554
1298
|
// the channel-worker fork — and running it inline here interleaves that
|
|
@@ -1557,29 +1301,25 @@ export async function createMixdogSessionRuntime({
|
|
|
1557
1301
|
// early /remote (startRemote clears it), stopRemote(), and close() all
|
|
1558
1302
|
// cancel it through the existing clearTimeout paths. Runtime /remote calls
|
|
1559
1303
|
// still start immediately (user-initiated, UI already painted).
|
|
1560
|
-
if (remoteEnabled
|
|
1561
|
-
|
|
1562
|
-
bootProfile('channels:autostart-deferred', { delayMs: remoteAutoStartDelayMs, intent: remoteStartIntent });
|
|
1304
|
+
if (rt.remoteEnabled) {
|
|
1305
|
+
bootProfile('channels:manual-start-deferred', { delayMs: remoteAutoStartDelayMs });
|
|
1563
1306
|
prewarmTimers.channelStartTimer = setTimeout(() => {
|
|
1564
1307
|
prewarmTimers.channelStartTimer = null;
|
|
1565
|
-
if (closeRequested) return;
|
|
1566
|
-
|
|
1567
|
-
// Auto: always attempt (claim-if-vacant is safe when a live owner exists).
|
|
1568
|
-
if (remoteStartIntent === 'explicit' && !remoteEnabled) return;
|
|
1569
|
-
startRemote({ intent: remoteStartIntent });
|
|
1308
|
+
if (rt.closeRequested || !rt.remoteEnabled) return;
|
|
1309
|
+
startRemote();
|
|
1570
1310
|
}, remoteAutoStartDelayMs);
|
|
1571
1311
|
prewarmTimers.channelStartTimer.unref?.();
|
|
1572
1312
|
} else {
|
|
1573
1313
|
// Automation decoupling (user decision): enabled schedules/webhooks boot
|
|
1574
1314
|
// the worker on their own — no remote flag, no remote.autoStart, and no
|
|
1575
|
-
// messaging backend. A later explicit
|
|
1315
|
+
// messaging backend. A later explicit Remote ON promotes the same
|
|
1576
1316
|
// daemon without restarting schedules or webhooks.
|
|
1577
1317
|
prewarmTimers.channelStartTimer = setTimeout(() => {
|
|
1578
1318
|
prewarmTimers.channelStartTimer = null;
|
|
1579
|
-
if (closeRequested || remoteEnabled) return;
|
|
1319
|
+
if (rt.closeRequested || rt.remoteEnabled) return;
|
|
1580
1320
|
void hasActiveAutomation()
|
|
1581
1321
|
.then((active) => {
|
|
1582
|
-
if (!active || closeRequested || remoteEnabled) return;
|
|
1322
|
+
if (!active || rt.closeRequested || rt.remoteEnabled) return;
|
|
1583
1323
|
bootProfile('channels:automation-autostart');
|
|
1584
1324
|
void invokeChannelStart();
|
|
1585
1325
|
})
|
|
@@ -1593,10 +1333,10 @@ export async function createMixdogSessionRuntime({
|
|
|
1593
1333
|
// token save/forget, setBackend). Extracted to session-runtime/settings-api.mjs
|
|
1594
1334
|
// and SPREAD into the API object below so the external surface is unchanged.
|
|
1595
1335
|
const settingsApi = createSettingsApi({
|
|
1596
|
-
getConfig: () => config,
|
|
1597
|
-
getRoute: () => route,
|
|
1598
|
-
getSession: () => session,
|
|
1599
|
-
getRemoteEnabled: () => remoteEnabled,
|
|
1336
|
+
getConfig: () => rt.config,
|
|
1337
|
+
getRoute: () => rt.route,
|
|
1338
|
+
getSession: () => rt.session,
|
|
1339
|
+
getRemoteEnabled: () => rt.remoteEnabled,
|
|
1600
1340
|
adoptConfig,
|
|
1601
1341
|
saveConfigAndAdopt,
|
|
1602
1342
|
scheduleBackendSave,
|
|
@@ -1655,35 +1395,36 @@ export async function createMixdogSessionRuntime({
|
|
|
1655
1395
|
});
|
|
1656
1396
|
const providerAuthApi = createProviderAuthApi({
|
|
1657
1397
|
cfgMod,
|
|
1658
|
-
getConfig: () => config,
|
|
1398
|
+
getConfig: () => rt.config,
|
|
1659
1399
|
saveConfigAndAdopt,
|
|
1660
1400
|
displayConfig,
|
|
1661
1401
|
reloadFullConfig,
|
|
1662
1402
|
awaitKeychainPrewarm,
|
|
1663
|
-
isKeychainPrewarmReady: () => keychainPrewarmWaitDone,
|
|
1403
|
+
isKeychainPrewarmReady: () => rt.keychainPrewarmWaitDone,
|
|
1664
1404
|
hasProviderSetupCached,
|
|
1665
1405
|
invalidateProviderCaches,
|
|
1666
1406
|
warmProviderModelCache,
|
|
1667
|
-
refreshProviderCatalogs: () => ensureProvidersReady(config.providers || {}).then(() => reg.refreshCatalogs()),
|
|
1407
|
+
refreshProviderCatalogs: () => ensureProvidersReady(rt.config.providers || {}).then(() => reg.refreshCatalogs()),
|
|
1668
1408
|
cachedProviderSetup,
|
|
1669
1409
|
getUsageDashboard,
|
|
1410
|
+
consumeCodexRateLimitResetCredit,
|
|
1670
1411
|
collectProviderModels,
|
|
1671
1412
|
});
|
|
1672
1413
|
const mediaApi = createMediaApi();
|
|
1673
1414
|
const lifecycleApi = createLifecycleApi({
|
|
1674
|
-
getSession: () => session,
|
|
1675
|
-
setSession: (v) => { session = v; },
|
|
1676
|
-
getRoute: () => route,
|
|
1677
|
-
setRoute: (v) => { route = v; },
|
|
1678
|
-
getConfig: () => config,
|
|
1679
|
-
getMode: () => mode,
|
|
1680
|
-
getCurrentCwd: () => currentCwd,
|
|
1681
|
-
getDesktopSession: () => desktopSession,
|
|
1682
|
-
setDesktopSession: (v) => { desktopSession = v; },
|
|
1683
|
-
setCloseRequested: (v) => { closeRequested = v; },
|
|
1684
|
-
getMemoryModPromise: () => memoryModPromise,
|
|
1685
|
-
setMemoryModPromise: (v) => { memoryModPromise = v; },
|
|
1686
|
-
setSessionNeedsCwdRefresh: (v) => { sessionNeedsCwdRefresh = v; },
|
|
1415
|
+
getSession: () => rt.session,
|
|
1416
|
+
setSession: (v) => { rt.session = v; },
|
|
1417
|
+
getRoute: () => rt.route,
|
|
1418
|
+
setRoute: (v) => { rt.route = v; },
|
|
1419
|
+
getConfig: () => rt.config,
|
|
1420
|
+
getMode: () => rt.mode,
|
|
1421
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
1422
|
+
getDesktopSession: () => rt.desktopSession,
|
|
1423
|
+
setDesktopSession: (v) => { rt.desktopSession = v; },
|
|
1424
|
+
setCloseRequested: (v) => { rt.closeRequested = v; },
|
|
1425
|
+
getMemoryModPromise: () => rt.memoryModPromise,
|
|
1426
|
+
setMemoryModPromise: (v) => { rt.memoryModPromise = v; },
|
|
1427
|
+
setSessionNeedsCwdRefresh: (v) => { rt.sessionNeedsCwdRefresh = v; },
|
|
1687
1428
|
hooks,
|
|
1688
1429
|
hookCommonPayload,
|
|
1689
1430
|
mgr,
|
|
@@ -1707,15 +1448,17 @@ export async function createMixdogSessionRuntime({
|
|
|
1707
1448
|
applyResolvedCwd,
|
|
1708
1449
|
resolveRoute,
|
|
1709
1450
|
applyDeferredToolSurface,
|
|
1451
|
+
beginRoutePreparation: (task) => routePreparation.start(task),
|
|
1452
|
+
clearRoutePreparation: () => routePreparation.clear(),
|
|
1710
1453
|
// Live getter: cwd-refresh session rebuilds must re-evaluate the
|
|
1711
1454
|
// workflow's agent-tool gate, not reuse the boot-time array.
|
|
1712
1455
|
getStandaloneTools: modelStandaloneTools,
|
|
1713
|
-
desktopSession,
|
|
1456
|
+
desktopSession: rt.desktopSession,
|
|
1714
1457
|
});
|
|
1715
1458
|
const resourceApi = createResourceApi({
|
|
1716
|
-
getConfig: () => config,
|
|
1717
|
-
getSession: () => session,
|
|
1718
|
-
getCurrentCwd: () => currentCwd,
|
|
1459
|
+
getConfig: () => rt.config,
|
|
1460
|
+
getSession: () => rt.session,
|
|
1461
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
1719
1462
|
cfgMod,
|
|
1720
1463
|
mgr,
|
|
1721
1464
|
hooks,
|
|
@@ -1733,17 +1476,17 @@ export async function createMixdogSessionRuntime({
|
|
|
1733
1476
|
getMemoryModule,
|
|
1734
1477
|
reloadFullConfig,
|
|
1735
1478
|
awaitKeychainPrewarm,
|
|
1736
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1479
|
+
getActiveTurnCount: () => rt.activeTurnCount,
|
|
1737
1480
|
});
|
|
1738
1481
|
const modelRouteApi = createModelRouteApi({
|
|
1739
|
-
getConfig: () => config,
|
|
1740
|
-
getRoute: () => route,
|
|
1741
|
-
setRouteState: (v) => { route = v; },
|
|
1742
|
-
getSession: () => session,
|
|
1743
|
-
setSession: (v) => { session = v; },
|
|
1744
|
-
getConfigHasSecrets: () => configHasSecrets,
|
|
1745
|
-
getSearchRouteState: () => searchRoute,
|
|
1746
|
-
setSearchRouteState: (v) => { searchRoute = v; },
|
|
1482
|
+
getConfig: () => rt.config,
|
|
1483
|
+
getRoute: () => rt.route,
|
|
1484
|
+
setRouteState: (v) => { rt.route = v; },
|
|
1485
|
+
getSession: () => rt.session,
|
|
1486
|
+
setSession: (v) => { rt.session = v; },
|
|
1487
|
+
getConfigHasSecrets: () => rt.configHasSecrets,
|
|
1488
|
+
getSearchRouteState: () => rt.searchRoute,
|
|
1489
|
+
setSearchRouteState: (v) => { rt.searchRoute = v; },
|
|
1747
1490
|
cfgMod,
|
|
1748
1491
|
reg,
|
|
1749
1492
|
mgr,
|
|
@@ -1768,12 +1511,12 @@ export async function createMixdogSessionRuntime({
|
|
|
1768
1511
|
collectSearchProviderModels,
|
|
1769
1512
|
});
|
|
1770
1513
|
const workflowAgentsApi = createWorkflowAgentsApi({
|
|
1771
|
-
getConfig: () => config,
|
|
1772
|
-
getRoute: () => route,
|
|
1773
|
-
setRouteState: (v) => { route = v; },
|
|
1774
|
-
getSession: () => session,
|
|
1775
|
-
setSession: (v) => { session = v; },
|
|
1776
|
-
getConfigHasSecrets: () => configHasSecrets,
|
|
1514
|
+
getConfig: () => rt.config,
|
|
1515
|
+
getRoute: () => rt.route,
|
|
1516
|
+
setRouteState: (v) => { rt.route = v; },
|
|
1517
|
+
getSession: () => rt.session,
|
|
1518
|
+
setSession: (v) => { rt.session = v; },
|
|
1519
|
+
getConfigHasSecrets: () => rt.configHasSecrets,
|
|
1777
1520
|
cfgMod,
|
|
1778
1521
|
reg,
|
|
1779
1522
|
mgr,
|
|
@@ -1799,26 +1542,26 @@ export async function createMixdogSessionRuntime({
|
|
|
1799
1542
|
invalidateContextStatusCache,
|
|
1800
1543
|
});
|
|
1801
1544
|
const sessionTurnApi = createSessionTurnApi({
|
|
1802
|
-
getSession: () => session,
|
|
1803
|
-
setSession: (v) => { session = v; },
|
|
1804
|
-
getCurrentCwd: () => currentCwd,
|
|
1805
|
-
getMode: () => mode,
|
|
1806
|
-
setMode: (v) => { mode = v; },
|
|
1807
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1808
|
-
setActiveTurnCount: (v) => { activeTurnCount = v; },
|
|
1809
|
-
isFirstTurnCompleted: () => firstTurnCompleted,
|
|
1810
|
-
setFirstTurnCompleted: (v) => { firstTurnCompleted = v; },
|
|
1811
|
-
getCodeGraphFirstTurnPrewarmDone: () => codeGraphFirstTurnPrewarmDone,
|
|
1812
|
-
setCodeGraphFirstTurnPrewarmDone: (v) => { codeGraphFirstTurnPrewarmDone = v; },
|
|
1545
|
+
getSession: () => rt.session,
|
|
1546
|
+
setSession: (v) => { rt.session = v; },
|
|
1547
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
1548
|
+
getMode: () => rt.mode,
|
|
1549
|
+
setMode: (v) => { rt.mode = v; },
|
|
1550
|
+
getActiveTurnCount: () => rt.activeTurnCount,
|
|
1551
|
+
setActiveTurnCount: (v) => { rt.activeTurnCount = v; },
|
|
1552
|
+
isFirstTurnCompleted: () => rt.firstTurnCompleted,
|
|
1553
|
+
setFirstTurnCompleted: (v) => { rt.firstTurnCompleted = v; },
|
|
1554
|
+
getCodeGraphFirstTurnPrewarmDone: () => rt.codeGraphFirstTurnPrewarmDone,
|
|
1555
|
+
setCodeGraphFirstTurnPrewarmDone: (v) => { rt.codeGraphFirstTurnPrewarmDone = v; },
|
|
1813
1556
|
codeGraphPrewarmLazy,
|
|
1814
|
-
getRemoteEnabled: () => remoteEnabled,
|
|
1815
|
-
getCloseRequested: () => closeRequested,
|
|
1816
|
-
getPendingSessionReset: () => pendingSessionReset,
|
|
1817
|
-
setPendingSessionReset: (v) => { pendingSessionReset = v; },
|
|
1557
|
+
getRemoteEnabled: () => rt.remoteEnabled,
|
|
1558
|
+
getCloseRequested: () => rt.closeRequested,
|
|
1559
|
+
getPendingSessionReset: () => rt.pendingSessionReset,
|
|
1560
|
+
setPendingSessionReset: (v) => { rt.pendingSessionReset = v; },
|
|
1818
1561
|
getTranscriptWriter: () => remoteTranscript.transcriptWriter,
|
|
1819
1562
|
getTwKey: () => remoteTranscript.transcriptKey,
|
|
1820
|
-
getLastAppendedAssistant: () => _lastAppendedAssistant,
|
|
1821
|
-
setLastAppendedAssistant: (v) => { _lastAppendedAssistant = v; },
|
|
1563
|
+
getLastAppendedAssistant: () => rt._lastAppendedAssistant,
|
|
1564
|
+
setLastAppendedAssistant: (v) => { rt._lastAppendedAssistant = v; },
|
|
1822
1565
|
scheduleCodeGraphPrewarm,
|
|
1823
1566
|
refreshSessionForCwdIfNeeded,
|
|
1824
1567
|
createCurrentSession,
|
|
@@ -1846,6 +1589,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1846
1589
|
notificationListeners,
|
|
1847
1590
|
remoteStateListeners,
|
|
1848
1591
|
awaitInitialMcpConnect,
|
|
1592
|
+
awaitRoutePreparation: () => routePreparation.wait(),
|
|
1849
1593
|
});
|
|
1850
1594
|
|
|
1851
1595
|
return {
|
|
@@ -1855,55 +1599,55 @@ export async function createMixdogSessionRuntime({
|
|
|
1855
1599
|
...mediaApi,
|
|
1856
1600
|
// Turn-scoped attributed review: child apply_patch diffs for this Lead turn.
|
|
1857
1601
|
// Lead patches are read from its transcript by the renderer.
|
|
1858
|
-
getTurnReviewDiff: () => getTurnSnapshotReviewDiff(currentCwd, session?.id),
|
|
1602
|
+
getTurnReviewDiff: () => getTurnSnapshotReviewDiff(rt.currentCwd, rt.session?.id),
|
|
1859
1603
|
get id() {
|
|
1860
|
-
return session?.id || null;
|
|
1604
|
+
return rt.session?.id || null;
|
|
1861
1605
|
},
|
|
1862
1606
|
get provider() {
|
|
1863
|
-
return route.provider;
|
|
1607
|
+
return rt.route.provider;
|
|
1864
1608
|
},
|
|
1865
1609
|
get model() {
|
|
1866
|
-
return route.model;
|
|
1610
|
+
return rt.route.model;
|
|
1867
1611
|
},
|
|
1868
1612
|
get effort() {
|
|
1869
|
-
return route.effectiveEffort || route.effort || route.preset?.effort || null;
|
|
1613
|
+
return rt.route.effectiveEffort || rt.route.effort || rt.route.preset?.effort || null;
|
|
1870
1614
|
},
|
|
1871
1615
|
get fast() {
|
|
1872
|
-
return route.fast === true;
|
|
1616
|
+
return rt.route.fast === true;
|
|
1873
1617
|
},
|
|
1874
1618
|
get fastCapable() {
|
|
1875
|
-
return route.fastCapable === true;
|
|
1619
|
+
return rt.route.fastCapable === true;
|
|
1876
1620
|
},
|
|
1877
1621
|
get effortOptions() {
|
|
1878
|
-
return route.effortOptions || [];
|
|
1622
|
+
return rt.route.effortOptions || [];
|
|
1879
1623
|
},
|
|
1880
1624
|
get contextWindow() {
|
|
1881
|
-
return session?.contextWindow || null;
|
|
1625
|
+
return rt.session?.contextWindow || null;
|
|
1882
1626
|
},
|
|
1883
1627
|
get rawContextWindow() {
|
|
1884
|
-
return session?.rawContextWindow || session?.contextWindow || null;
|
|
1628
|
+
return rt.session?.rawContextWindow || rt.session?.contextWindow || null;
|
|
1885
1629
|
},
|
|
1886
1630
|
get effectiveContextWindowPercent() {
|
|
1887
|
-
return session?.effectiveContextWindowPercent || null;
|
|
1631
|
+
return rt.session?.effectiveContextWindowPercent || null;
|
|
1888
1632
|
},
|
|
1889
1633
|
get toolMode() {
|
|
1890
|
-
return mode;
|
|
1634
|
+
return rt.mode;
|
|
1891
1635
|
},
|
|
1892
1636
|
get autoClear() {
|
|
1893
1637
|
return this.getAutoClear();
|
|
1894
1638
|
},
|
|
1895
1639
|
get systemShell() {
|
|
1896
|
-
return normalizeSystemShellConfig(config.shell);
|
|
1640
|
+
return normalizeSystemShellConfig(rt.config.shell);
|
|
1897
1641
|
},
|
|
1898
1642
|
get searchRoute() {
|
|
1899
|
-
searchRoute = normalizeSearchRouteConfig(config.searchRoute) || normalizeSearchRouteConfig(searchRoute);
|
|
1900
|
-
return searchRoute;
|
|
1643
|
+
rt.searchRoute = normalizeSearchRouteConfig(rt.config.searchRoute) || normalizeSearchRouteConfig(rt.searchRoute);
|
|
1644
|
+
return rt.searchRoute;
|
|
1901
1645
|
},
|
|
1902
1646
|
get workflow() {
|
|
1903
1647
|
const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
|
|
1904
|
-
const active = activeWorkflowSummary(config, dataDir);
|
|
1905
|
-
if (session?.workflow && typeof session.workflow === 'object') {
|
|
1906
|
-
const current = workflowSummary(session.workflow);
|
|
1648
|
+
const active = activeWorkflowSummary(rt.config, dataDir);
|
|
1649
|
+
if (rt.session?.workflow && typeof rt.session.workflow === 'object') {
|
|
1650
|
+
const current = workflowSummary(rt.session.workflow);
|
|
1907
1651
|
return current?.id && active?.id && current.id !== active.id
|
|
1908
1652
|
? { ...active, currentSession: current, appliedToCurrentSession: false }
|
|
1909
1653
|
: active;
|
|
@@ -1914,10 +1658,10 @@ export async function createMixdogSessionRuntime({
|
|
|
1914
1658
|
return getOutputStyleStatusCached().current;
|
|
1915
1659
|
},
|
|
1916
1660
|
get cwd() {
|
|
1917
|
-
return currentCwd;
|
|
1661
|
+
return rt.currentCwd;
|
|
1918
1662
|
},
|
|
1919
1663
|
get session() {
|
|
1920
|
-
return session;
|
|
1664
|
+
return rt.session;
|
|
1921
1665
|
},
|
|
1922
1666
|
contextStatus() {
|
|
1923
1667
|
// Prefer the in-flight working transcript while a turn is running so the
|
|
@@ -1942,17 +1686,11 @@ export async function createMixdogSessionRuntime({
|
|
|
1942
1686
|
},
|
|
1943
1687
|
// Session-scoped remote: the owning session id (null when off/unassigned).
|
|
1944
1688
|
getRemoteSessionId() {
|
|
1945
|
-
return remoteSessionId;
|
|
1689
|
+
return rt.remoteSessionId;
|
|
1946
1690
|
},
|
|
1947
|
-
//
|
|
1948
|
-
// engine): rebind the transcript writer + forwarder without restarting
|
|
1949
|
-
// the worker. No-op when remote is off or no session exists.
|
|
1691
|
+
// Back-compat alias for an explicit manual ON in the current session.
|
|
1950
1692
|
claimRemoteForCurrentSession() {
|
|
1951
|
-
|
|
1952
|
-
remoteSessionId = session.id;
|
|
1953
|
-
ensureRemoteTranscriptWriter();
|
|
1954
|
-
pushTranscriptRebind();
|
|
1955
|
-
return true;
|
|
1693
|
+
return startRemote();
|
|
1956
1694
|
},
|
|
1957
1695
|
// Subscribe to non-user-initiated remote flips (seat superseded). Returns
|
|
1958
1696
|
// an unsubscribe function. TUI uses this to sync its Remote indicator and
|
|
@@ -1963,7 +1701,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1963
1701
|
return () => remoteStateListeners.delete(listener);
|
|
1964
1702
|
},
|
|
1965
1703
|
get clientHostPid() {
|
|
1966
|
-
return session?.clientHostPid || process.pid;
|
|
1704
|
+
return rt.session?.clientHostPid || process.pid;
|
|
1967
1705
|
},
|
|
1968
1706
|
...lifecycleApi,
|
|
1969
1707
|
...resourceApi,
|