mixdog 0.9.86 → 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/package.json +2 -2
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +125 -16
- package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +9 -2
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/token-manifest.json +23 -2
- package/src/runtime/memory/lib/memory-action-handlers.mjs +4 -1
- package/src/runtime/memory/tool-defs.mjs +19 -3
- package/src/session-runtime/lifecycle-api.mjs +15 -0
- package/src/session-runtime/provider-auth-api.mjs +8 -0
- package/src/session-runtime/provider-usage.mjs +30 -5
- package/src/session-runtime/runtime-core.mjs +267 -481
- package/src/session-runtime/session-lifecycle.mjs +378 -0
- package/src/standalone/agent-shard/shard-pool.mjs +6 -5
- 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 +100 -1151
- package/src/standalone/usage-dashboard.mjs +23 -1
- package/src/tui/App.jsx +362 -2576
- package/src/tui/app/app-view.jsx +504 -0
- package/src/tui/app/create-app-pickers.mjs +313 -0
- package/src/tui/app/prompt-submit.mjs +501 -0
- package/src/tui/app/shell-layout.mjs +563 -0
- 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/dist/index.mjs +9802 -9024
- package/src/tui/engine/session-api-ext.mjs +23 -0
|
@@ -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,
|
|
@@ -320,19 +324,22 @@ export async function createMixdogSessionRuntime({
|
|
|
320
324
|
remote = false,
|
|
321
325
|
desktopSession: initialDesktopSession = null,
|
|
322
326
|
} = {}) {
|
|
323
|
-
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;
|
|
324
331
|
bootProfile('session-runtime:start', { provider, model, toolMode, cwd });
|
|
325
|
-
|
|
332
|
+
rt.remoteEnabled = remote === true;
|
|
326
333
|
// SESSION-SCOPED Remote is manual-only. Explicit ON selects/overrides; no
|
|
327
334
|
// registration, turn, session death, or process death transfers ownership.
|
|
328
|
-
|
|
335
|
+
rt.remoteSessionId = null;
|
|
329
336
|
// Desktop/TUI Remote toggles are desired-state transitions. Serialize the
|
|
330
337
|
// daemon deactivate/detach and reconnect/activate chains so an OFF that is
|
|
331
338
|
// still stopping can never tear down the worker created by the following ON.
|
|
332
339
|
const remoteTransitions = createRemoteTransitionQueue();
|
|
333
340
|
// Last assistant text handed to the transcript writer (via onAssistantText),
|
|
334
341
|
// so the post-turn final-content append can skip an exact duplicate.
|
|
335
|
-
|
|
342
|
+
rt._lastAppendedAssistant = '';
|
|
336
343
|
process.env.MIXDOG_QUIET_SESSION_LOG ??= '1';
|
|
337
344
|
const standaloneStartedAt = performance.now();
|
|
338
345
|
ensureStandaloneEnvironment({
|
|
@@ -341,11 +348,11 @@ export async function createMixdogSessionRuntime({
|
|
|
341
348
|
});
|
|
342
349
|
bootProfile('standalone-env:ready', { ms: (performance.now() - standaloneStartedAt).toFixed(1) });
|
|
343
350
|
const keychainPrewarmPromise = keychain.prewarmSecrets();
|
|
344
|
-
|
|
345
|
-
|
|
351
|
+
rt.keychainPrewarmWaitDone = false;
|
|
352
|
+
rt.keychainPrewarmWaitPromise = null;
|
|
346
353
|
function awaitKeychainPrewarm() {
|
|
347
|
-
if (keychainPrewarmWaitDone) return Promise.resolve();
|
|
348
|
-
keychainPrewarmWaitPromise ??= (async () => {
|
|
354
|
+
if (rt.keychainPrewarmWaitDone) return Promise.resolve();
|
|
355
|
+
rt.keychainPrewarmWaitPromise ??= (async () => {
|
|
349
356
|
let timeoutId;
|
|
350
357
|
const deadline = new Promise((resolveDeadline) => {
|
|
351
358
|
timeoutId = setTimeout(resolveDeadline, KEYCHAIN_PREWARM_WAIT_MS);
|
|
@@ -355,13 +362,13 @@ export async function createMixdogSessionRuntime({
|
|
|
355
362
|
await Promise.race([keychainPrewarmPromise, deadline]);
|
|
356
363
|
} finally {
|
|
357
364
|
clearTimeout(timeoutId);
|
|
358
|
-
keychainPrewarmWaitDone = true;
|
|
365
|
+
rt.keychainPrewarmWaitDone = true;
|
|
359
366
|
}
|
|
360
367
|
// Invoked here: the assignment used to store the async FUNCTION, so every
|
|
361
368
|
// `await awaitKeychainPrewarm()` resolved instantly (awaiting a function is
|
|
362
369
|
// a no-op) and callers silently skipped the wait they asked for.
|
|
363
370
|
})();
|
|
364
|
-
return keychainPrewarmWaitPromise;
|
|
371
|
+
return rt.keychainPrewarmWaitPromise;
|
|
365
372
|
}
|
|
366
373
|
const routePreparation = createRoutePreparationGate({
|
|
367
374
|
onError: (error) => bootProfile('route-preparation:failed', {
|
|
@@ -413,22 +420,22 @@ export async function createMixdogSessionRuntime({
|
|
|
413
420
|
dataDir: pluginDataDir,
|
|
414
421
|
cwd,
|
|
415
422
|
});
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
423
|
+
rt.memoryModPromise = null;
|
|
424
|
+
rt.searchModPromise = null;
|
|
425
|
+
rt.codeGraphModPromise = null;
|
|
419
426
|
|
|
420
427
|
// Memory module is always-on. `memoryEnabled()` is kept as a thin alias that
|
|
421
428
|
// now always returns true (callers/compaction helpers still reference it);
|
|
422
429
|
// the user-facing toggle is `recap` (background cycles only), read via
|
|
423
430
|
// recapEnabled(config).
|
|
424
431
|
const memoryEnabled = () => true;
|
|
425
|
-
const recapEnabledFn = () => recapEnabled(config, true);
|
|
426
|
-
const channelsEnabled = () => moduleEnabled(config, 'channels', true);
|
|
432
|
+
const recapEnabledFn = () => recapEnabled(rt.config, true);
|
|
433
|
+
const channelsEnabled = () => moduleEnabled(rt.config, 'channels', true);
|
|
427
434
|
|
|
428
435
|
async function getMemoryModule() {
|
|
429
436
|
const startedAt = performance.now();
|
|
430
|
-
memoryModPromise ??= Promise.resolve(memoryRuntime);
|
|
431
|
-
const mod = await memoryModPromise;
|
|
437
|
+
rt.memoryModPromise ??= Promise.resolve(memoryRuntime);
|
|
438
|
+
const mod = await rt.memoryModPromise;
|
|
432
439
|
if (typeof mod?.init === 'function') {
|
|
433
440
|
await mod.init();
|
|
434
441
|
}
|
|
@@ -438,16 +445,16 @@ export async function createMixdogSessionRuntime({
|
|
|
438
445
|
|
|
439
446
|
async function getSearchModule() {
|
|
440
447
|
const startedAt = performance.now();
|
|
441
|
-
searchModPromise ??= import(SEARCH_RUNTIME);
|
|
442
|
-
const mod = await searchModPromise;
|
|
448
|
+
rt.searchModPromise ??= import(SEARCH_RUNTIME);
|
|
449
|
+
const mod = await rt.searchModPromise;
|
|
443
450
|
bootProfile('search-runtime:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
444
451
|
return mod;
|
|
445
452
|
}
|
|
446
453
|
|
|
447
454
|
async function getCodeGraphModule() {
|
|
448
455
|
const startedAt = performance.now();
|
|
449
|
-
codeGraphModPromise ??= import(CODE_GRAPH_RUNTIME);
|
|
450
|
-
const mod = await codeGraphModPromise;
|
|
456
|
+
rt.codeGraphModPromise ??= import(CODE_GRAPH_RUNTIME);
|
|
457
|
+
const mod = await rt.codeGraphModPromise;
|
|
451
458
|
bootProfile('code-graph-runtime:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
452
459
|
return mod;
|
|
453
460
|
}
|
|
@@ -456,7 +463,7 @@ export async function createMixdogSessionRuntime({
|
|
|
456
463
|
const leadRoute = normalizeWorkflowRoute(routeLike);
|
|
457
464
|
if (!leadRoute) return null;
|
|
458
465
|
|
|
459
|
-
const nextConfig = { ...(config || {}) };
|
|
466
|
+
const nextConfig = { ...(rt.config || {}) };
|
|
460
467
|
nextConfig.presets = upsertWorkflowPreset(nextConfig.presets, 'lead', leadRoute);
|
|
461
468
|
nextConfig.workflowRoutes = {
|
|
462
469
|
...(nextConfig.workflowRoutes || {}),
|
|
@@ -483,21 +490,21 @@ export async function createMixdogSessionRuntime({
|
|
|
483
490
|
}
|
|
484
491
|
|
|
485
492
|
const configStartedAt = performance.now();
|
|
486
|
-
|
|
487
|
-
setConfiguredShell(normalizeSystemShellConfig(config.shell).command);
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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);
|
|
491
498
|
bootProfile('config:ready', { ms: (performance.now() - configStartedAt).toFixed(1) });
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
499
|
+
rt.mode = normalizeToolMode(toolMode);
|
|
500
|
+
rt.session = null;
|
|
501
|
+
rt.sessionCreatePromise = null;
|
|
502
|
+
rt.currentCwd = cwd;
|
|
503
|
+
rt.sessionNeedsCwdRefresh = false;
|
|
497
504
|
// session_manage tool: reset request scheduled by the model mid-turn,
|
|
498
505
|
// consumed by the TUI engine at turn end ('clear' | 'compact_clear').
|
|
499
|
-
|
|
500
|
-
|
|
506
|
+
rt.pendingSessionReset = null;
|
|
507
|
+
rt.closeRequested = false;
|
|
501
508
|
const warmupTimers = {
|
|
502
509
|
providerSetupWarmupTimer: null,
|
|
503
510
|
providerWarmupTimer: null,
|
|
@@ -518,8 +525,8 @@ export async function createMixdogSessionRuntime({
|
|
|
518
525
|
codeGraphPrewarmQueuedCwd: '',
|
|
519
526
|
channelStartPromise: null,
|
|
520
527
|
};
|
|
521
|
-
|
|
522
|
-
|
|
528
|
+
rt.activeTurnCount = 0;
|
|
529
|
+
rt.firstTurnCompleted = false;
|
|
523
530
|
function hookTranscriptPath(sessionId) {
|
|
524
531
|
const id = clean(sessionId);
|
|
525
532
|
if (!id || !/^[A-Za-z0-9_-]+$/.test(id)) return null;
|
|
@@ -527,15 +534,15 @@ export async function createMixdogSessionRuntime({
|
|
|
527
534
|
return join(dataDir, 'sessions', `${id}.json`);
|
|
528
535
|
}
|
|
529
536
|
function hookEffortPayload() {
|
|
530
|
-
const level = clean(route.effectiveEffort || route.effort);
|
|
537
|
+
const level = clean(rt.route.effectiveEffort || rt.route.effort);
|
|
531
538
|
return level ? { level: level.toLowerCase() } : undefined;
|
|
532
539
|
}
|
|
533
540
|
function hookCommonPayload(extra = {}) {
|
|
534
|
-
const sid = clean(extra.session_id || extra.sessionId || session?.id);
|
|
541
|
+
const sid = clean(extra.session_id || extra.sessionId || rt.session?.id);
|
|
535
542
|
return {
|
|
536
543
|
...(sid ? { session_id: sid, transcript_path: hookTranscriptPath(sid) } : {}),
|
|
537
|
-
cwd: currentCwd,
|
|
538
|
-
permission_mode: session?.permissionMode || 'default',
|
|
544
|
+
cwd: rt.currentCwd,
|
|
545
|
+
permission_mode: rt.session?.permissionMode || 'default',
|
|
539
546
|
...(hookEffortPayload() ? { effort: hookEffortPayload() } : {}),
|
|
540
547
|
...extra,
|
|
541
548
|
};
|
|
@@ -560,7 +567,7 @@ export async function createMixdogSessionRuntime({
|
|
|
560
567
|
modelCatalogWarmupEnabled,
|
|
561
568
|
codeGraphPrewarmLazy,
|
|
562
569
|
} = readRuntimeTunables();
|
|
563
|
-
|
|
570
|
+
rt.codeGraphFirstTurnPrewarmDone = false;
|
|
564
571
|
const modelMetaByRoute = new Map();
|
|
565
572
|
const notificationListeners = new Set();
|
|
566
573
|
// Remote seat listeners (TUI): fired when remote mode flips outside a direct
|
|
@@ -585,11 +592,11 @@ export async function createMixdogSessionRuntime({
|
|
|
585
592
|
providerSetupPromise: null,
|
|
586
593
|
};
|
|
587
594
|
const providerInitPromises = new Map();
|
|
588
|
-
|
|
595
|
+
rt.startupProviderCatalogRefreshStarted = false;
|
|
589
596
|
// True while the boot-time provider-catalog refresh is in flight: warming a
|
|
590
597
|
// model cache it is about to invalidate only burns the load twice.
|
|
591
|
-
|
|
592
|
-
|
|
598
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
599
|
+
rt.lastProjectMcpKey = null;
|
|
593
600
|
// MCP connect state, owned here so teardown/reconnect paths still observe it;
|
|
594
601
|
// the mcp-glue factory mutates this object in place (see createMcpGlue).
|
|
595
602
|
const mcpState = {
|
|
@@ -608,15 +615,15 @@ export async function createMixdogSessionRuntime({
|
|
|
608
615
|
normalizeMcpServerInput,
|
|
609
616
|
} = createMcpGlue({
|
|
610
617
|
mcpClient,
|
|
611
|
-
getConfig: () => config,
|
|
612
|
-
getCurrentCwd: () => currentCwd,
|
|
613
|
-
getDesktopSession: () => desktopSession,
|
|
614
|
-
setDesktopSession: (v) => { desktopSession = v; },
|
|
618
|
+
getConfig: () => rt.config,
|
|
619
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
620
|
+
getDesktopSession: () => rt.desktopSession,
|
|
621
|
+
setDesktopSession: (v) => { rt.desktopSession = v; },
|
|
615
622
|
state: mcpState,
|
|
616
623
|
});
|
|
617
624
|
const hooksStartedAt = performance.now();
|
|
618
625
|
const hooks = createStandaloneHookBus({ dataDir: cfgMod.getPluginData() });
|
|
619
|
-
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 });
|
|
620
627
|
bootProfile('hooks:ready', { ms: (performance.now() - hooksStartedAt).toFixed(1) });
|
|
621
628
|
|
|
622
629
|
// Self-update: registry check + background staging live in self-update.mjs;
|
|
@@ -624,7 +631,7 @@ export async function createMixdogSessionRuntime({
|
|
|
624
631
|
// boot check is deferred past this constructor so a hanging registry request
|
|
625
632
|
// can never delay session boot.
|
|
626
633
|
const selfUpdate = createSelfUpdateController({
|
|
627
|
-
getConfig: () => config,
|
|
634
|
+
getConfig: () => rt.config,
|
|
628
635
|
getDataDir: () => cfgMod.getPluginData?.() || STANDALONE_DATA_DIR,
|
|
629
636
|
emitNotification: (...a) => emitRuntimeNotification(...a),
|
|
630
637
|
});
|
|
@@ -647,7 +654,7 @@ export async function createMixdogSessionRuntime({
|
|
|
647
654
|
skillContent,
|
|
648
655
|
skillToolContent,
|
|
649
656
|
addProjectSkill,
|
|
650
|
-
} = createSkillsApi({ contextMod, getCwd: () => currentCwd });
|
|
657
|
+
} = createSkillsApi({ contextMod, getCwd: () => rt.currentCwd });
|
|
651
658
|
|
|
652
659
|
// cwd resolution/apply + plugins-status + core-memory context. Extracted to
|
|
653
660
|
// session-runtime/cwd-plugins.mjs; the facade keeps ownership of the mutable
|
|
@@ -661,17 +668,17 @@ export async function createMixdogSessionRuntime({
|
|
|
661
668
|
pluginsStatus,
|
|
662
669
|
loadCoreMemoryContext,
|
|
663
670
|
} = createCwdPlugins({
|
|
664
|
-
getCurrentCwd: () => currentCwd,
|
|
665
|
-
setCurrentCwd: (next) => { currentCwd = next; },
|
|
666
|
-
getConfig: () => config,
|
|
667
|
-
getSession: () => session,
|
|
668
|
-
getRoute: () => route,
|
|
669
|
-
getLastProjectMcpKey: () => lastProjectMcpKey,
|
|
670
|
-
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; },
|
|
671
678
|
isCodeGraphPrewarmLazy: () => codeGraphPrewarmLazy,
|
|
672
|
-
isCodeGraphFirstTurnPrewarmDone: () => codeGraphFirstTurnPrewarmDone,
|
|
679
|
+
isCodeGraphFirstTurnPrewarmDone: () => rt.codeGraphFirstTurnPrewarmDone,
|
|
673
680
|
getCodeGraphPrewarmDelayMs: () => codeGraphPrewarmDelayMs,
|
|
674
|
-
setSessionNeedsCwdRefresh: (next) => { sessionNeedsCwdRefresh = next; },
|
|
681
|
+
setSessionNeedsCwdRefresh: (next) => { rt.sessionNeedsCwdRefresh = next; },
|
|
675
682
|
connectConfiguredMcp,
|
|
676
683
|
invalidatePreSessionToolSurface: (...a) => invalidatePreSessionToolSurface(...a),
|
|
677
684
|
scheduleCodeGraphPrewarm: (...a) => scheduleCodeGraphPrewarm(...a),
|
|
@@ -703,7 +710,7 @@ export async function createMixdogSessionRuntime({
|
|
|
703
710
|
dataDir: cfgMod.getPluginData(),
|
|
704
711
|
cwd,
|
|
705
712
|
awaitKeychainPrewarm,
|
|
706
|
-
isKeychainPrewarmReady: () => keychainPrewarmWaitDone,
|
|
713
|
+
isKeychainPrewarmReady: () => rt.keychainPrewarmWaitDone,
|
|
707
714
|
// SubagentStart/SubagentStop: bridge internal worker spawn/finish to the
|
|
708
715
|
// standard hook bus. agent_type is passed top-level via hookCommonPayload
|
|
709
716
|
// (added to hook-bus buildEventPayload passthrough). Best-effort.
|
|
@@ -720,7 +727,7 @@ export async function createMixdogSessionRuntime({
|
|
|
720
727
|
bootProfile('agent:ready', { ms: (performance.now() - agentToolStartedAt).toFixed(1) });
|
|
721
728
|
const agentStatusState = () => {
|
|
722
729
|
try {
|
|
723
|
-
const status = agentTool.getStatus?.({ clientHostPid: session?.clientHostPid || process.pid }) || {};
|
|
730
|
+
const status = agentTool.getStatus?.({ clientHostPid: rt.session?.clientHostPid || process.pid }) || {};
|
|
724
731
|
return {
|
|
725
732
|
agentWorkers: Array.isArray(status.workers) ? status.workers : [],
|
|
726
733
|
agentJobs: Array.isArray(status.jobs) ? status.jobs : [],
|
|
@@ -741,7 +748,7 @@ export async function createMixdogSessionRuntime({
|
|
|
741
748
|
// newer remote session. Drop remote mode entirely on this session (no
|
|
742
749
|
// handover, no retry) and tell UI listeners so the indicator updates.
|
|
743
750
|
if (msg?.method === 'notifications/mixdog/remote') {
|
|
744
|
-
if (msg?.params?.state === 'superseded' && remoteEnabled) {
|
|
751
|
+
if (msg?.params?.state === 'superseded' && rt.remoteEnabled) {
|
|
745
752
|
stopRemote('superseded-by-newer-remote-session');
|
|
746
753
|
emitRemoteStateChange(false, 'superseded');
|
|
747
754
|
}
|
|
@@ -753,8 +760,8 @@ export async function createMixdogSessionRuntime({
|
|
|
753
760
|
const content = channelNotificationModelContent(params);
|
|
754
761
|
if (!content) return;
|
|
755
762
|
const { handled } = emitRuntimeNotification(content, meta);
|
|
756
|
-
if (!handled && session?.id && shouldMirrorChannelNotificationToPending(meta)) {
|
|
757
|
-
try { mgr.enqueuePendingMessage(session.id, content); } catch {}
|
|
763
|
+
if (!handled && rt.session?.id && shouldMirrorChannelNotificationToPending(meta)) {
|
|
764
|
+
try { mgr.enqueuePendingMessage(rt.session.id, content); } catch {}
|
|
758
765
|
}
|
|
759
766
|
},
|
|
760
767
|
});
|
|
@@ -790,12 +797,12 @@ export async function createMixdogSessionRuntime({
|
|
|
790
797
|
applyPreSessionToolSelection,
|
|
791
798
|
} = createToolSurface({
|
|
792
799
|
mgr,
|
|
793
|
-
mode,
|
|
800
|
+
mode: rt.mode,
|
|
794
801
|
standaloneTools,
|
|
795
802
|
agentToolNames,
|
|
796
|
-
getSession: () => session,
|
|
797
|
-
getRoute: () => route,
|
|
798
|
-
getConfig: () => config,
|
|
803
|
+
getSession: () => rt.session,
|
|
804
|
+
getRoute: () => rt.route,
|
|
805
|
+
getConfig: () => rt.config,
|
|
799
806
|
cfgMod,
|
|
800
807
|
loadWorkflowPack,
|
|
801
808
|
activeWorkflowId,
|
|
@@ -803,20 +810,20 @@ export async function createMixdogSessionRuntime({
|
|
|
803
810
|
});
|
|
804
811
|
|
|
805
812
|
const { contextStatus: computeContextStatus, invalidateContextStatusCache } = createContextStatus({
|
|
806
|
-
getSession: () => session,
|
|
807
|
-
getRoute: () => route,
|
|
808
|
-
getCurrentCwd: () => currentCwd,
|
|
809
|
-
getMode: () => mode,
|
|
813
|
+
getSession: () => rt.session,
|
|
814
|
+
getRoute: () => rt.route,
|
|
815
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
816
|
+
getMode: () => rt.mode,
|
|
810
817
|
});
|
|
811
818
|
internalTools.setInternalToolsProvider({
|
|
812
819
|
tools: [...standaloneTools, ...searchRuntimeTools.filter((tool) => tool?.public === false)],
|
|
813
820
|
executor: async (name, args, callerCtx = {}) => {
|
|
814
|
-
const callerCwd = callerCtx?.callerCwd || currentCwd;
|
|
821
|
+
const callerCwd = callerCtx?.callerCwd || rt.currentCwd;
|
|
815
822
|
if (name === 'search' || name === 'web_fetch' || name === 'local_fetch' || name === 'image_fetch') {
|
|
816
823
|
return dispatchSearchRuntimeTool(name, args, callerCtx, {
|
|
817
824
|
getSearchModule,
|
|
818
|
-
getCurrentCwd: () => currentCwd,
|
|
819
|
-
getSession: () => session,
|
|
825
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
826
|
+
getSession: () => rt.session,
|
|
820
827
|
notifyFnForSession,
|
|
821
828
|
runNativeWebSearch,
|
|
822
829
|
});
|
|
@@ -827,7 +834,7 @@ export async function createMixdogSessionRuntime({
|
|
|
827
834
|
return await memoryMod.handleToolCall(
|
|
828
835
|
name,
|
|
829
836
|
memoryToolArgsForCaller(args, callerCwd),
|
|
830
|
-
callerCtx?.signal || session?.controller?.signal || null,
|
|
837
|
+
callerCtx?.signal || rt.session?.controller?.signal || null,
|
|
831
838
|
);
|
|
832
839
|
}
|
|
833
840
|
if (name === 'code_graph') {
|
|
@@ -836,20 +843,20 @@ export async function createMixdogSessionRuntime({
|
|
|
836
843
|
return await codeGraphMod.executeCodeGraphTool(name, args || {}, args?.cwd || callerCwd);
|
|
837
844
|
}
|
|
838
845
|
if (name === 'tool_search' || name === 'load_tool') {
|
|
839
|
-
return renderToolSearch(args, activeToolSurface(), mode, { mcpStatus });
|
|
846
|
+
return renderToolSearch(args, activeToolSurface(), rt.mode, { mcpStatus });
|
|
840
847
|
}
|
|
841
848
|
if (name === 'explore') {
|
|
842
|
-
const callerSessionId = callerCtx?.callerSessionId || session?.id || null;
|
|
849
|
+
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
843
850
|
return await runExplore(args || {}, {
|
|
844
851
|
callerCwd: args?.cwd ? resolveCwdPath(args.cwd) : callerCwd,
|
|
845
852
|
callerSessionId,
|
|
846
853
|
routingSessionId: callerSessionId,
|
|
847
|
-
clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
|
|
854
|
+
clientHostPid: callerCtx?.clientHostPid || rt.session?.clientHostPid || process.pid,
|
|
848
855
|
notifyFn: notifyFnForSession(callerSessionId),
|
|
849
856
|
// Cascade caller cancellation (ESC / owner abort) into the explore
|
|
850
857
|
// fan-out so every child dispatch tears down immediately — same
|
|
851
858
|
// signal the adjacent native search already forwards.
|
|
852
|
-
signal: callerCtx?.signal || session?.controller?.signal,
|
|
859
|
+
signal: callerCtx?.signal || rt.session?.controller?.signal,
|
|
853
860
|
});
|
|
854
861
|
}
|
|
855
862
|
if (name === 'cwd') {
|
|
@@ -859,17 +866,17 @@ export async function createMixdogSessionRuntime({
|
|
|
859
866
|
} else if (action !== 'get') {
|
|
860
867
|
throw new Error(`cwd: unknown action "${action}"`);
|
|
861
868
|
}
|
|
862
|
-
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);
|
|
863
870
|
}
|
|
864
871
|
if (name === 'session_manage') {
|
|
865
872
|
// Lead/owner sessions only: an agent worker resetting its own
|
|
866
873
|
// transcript mid-task would corrupt the delegation contract, and it
|
|
867
874
|
// must never reach the owner conversation either.
|
|
868
875
|
const callerSessionId = callerCtx?.callerSessionId || null;
|
|
869
|
-
if (callerSessionId && session?.id && callerSessionId !== session.id) {
|
|
876
|
+
if (callerSessionId && rt.session?.id && callerSessionId !== rt.session.id) {
|
|
870
877
|
throw new Error('session_manage: only the lead session may reset the conversation');
|
|
871
878
|
}
|
|
872
|
-
if (!session?.id) throw new Error('session_manage: no active session');
|
|
879
|
+
if (!rt.session?.id) throw new Error('session_manage: no active session');
|
|
873
880
|
const action = clean(args?.action).toLowerCase();
|
|
874
881
|
if (action !== 'clear' && action !== 'compact_clear') {
|
|
875
882
|
throw new Error(`session_manage: unknown action "${action}" (use clear | compact_clear)`);
|
|
@@ -879,7 +886,7 @@ export async function createMixdogSessionRuntime({
|
|
|
879
886
|
// boundary the idle auto-clear uses).
|
|
880
887
|
// Pin to the current session id so a resume/new-session between
|
|
881
888
|
// scheduling and consumption can never clear the wrong conversation.
|
|
882
|
-
pendingSessionReset = { action, sessionId: session.id };
|
|
889
|
+
rt.pendingSessionReset = { action, sessionId: rt.session.id };
|
|
883
890
|
return action === 'clear'
|
|
884
891
|
? 'Session reset scheduled: full clear will run when this turn ends. All prior context will be gone.'
|
|
885
892
|
: 'Session reset scheduled: the conversation will be summarized (compact) and cleared when this turn ends; key context carries forward in the summary.';
|
|
@@ -888,12 +895,12 @@ export async function createMixdogSessionRuntime({
|
|
|
888
895
|
return skillToolContent(args?.name);
|
|
889
896
|
}
|
|
890
897
|
if (name === 'agent') {
|
|
891
|
-
const callerSessionId = callerCtx?.callerSessionId || session?.id || null;
|
|
898
|
+
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
892
899
|
return await agentTool.execute(args, {
|
|
893
900
|
callerCwd,
|
|
894
901
|
invocationSource: 'model-tool',
|
|
895
902
|
callerSessionId,
|
|
896
|
-
clientHostPid: callerCtx?.clientHostPid || session?.clientHostPid || process.pid,
|
|
903
|
+
clientHostPid: callerCtx?.clientHostPid || rt.session?.clientHostPid || process.pid,
|
|
897
904
|
signal: callerCtx?.signal,
|
|
898
905
|
notifyFn: notifyFnForSession(callerSessionId),
|
|
899
906
|
});
|
|
@@ -906,7 +913,7 @@ export async function createMixdogSessionRuntime({
|
|
|
906
913
|
},
|
|
907
914
|
});
|
|
908
915
|
internalTools.markBootReady?.();
|
|
909
|
-
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; }
|
|
910
917
|
void connectConfiguredMcp()
|
|
911
918
|
.then((status) => bootProfile('mcp:ready', {
|
|
912
919
|
connected: Number(status?.connectedCount || 0),
|
|
@@ -955,13 +962,13 @@ export async function createMixdogSessionRuntime({
|
|
|
955
962
|
displayConfig,
|
|
956
963
|
ensureConfigForRouteProvider,
|
|
957
964
|
} = createConfigLifecycle({
|
|
958
|
-
getConfig: () => config,
|
|
959
|
-
setConfig: (next) => { config = next; },
|
|
960
|
-
getSearchRoute: () => searchRoute,
|
|
961
|
-
setSearchRoute: (next) => { searchRoute = next; },
|
|
962
|
-
getConfigHasSecrets: () => configHasSecrets,
|
|
963
|
-
setConfigHasSecrets: (next) => { configHasSecrets = next; },
|
|
964
|
-
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,
|
|
965
972
|
cfgMod,
|
|
966
973
|
sharedCfgMod,
|
|
967
974
|
setBackend,
|
|
@@ -976,7 +983,7 @@ export async function createMixdogSessionRuntime({
|
|
|
976
983
|
STANDALONE_DATA_DIR,
|
|
977
984
|
});
|
|
978
985
|
|
|
979
|
-
async function ensureProvidersReady(providerConfig = config.providers || {}) {
|
|
986
|
+
async function ensureProvidersReady(providerConfig = rt.config.providers || {}) {
|
|
980
987
|
await awaitKeychainPrewarm();
|
|
981
988
|
const initKey = providerInitCacheKey(providerConfig);
|
|
982
989
|
const existing = providerInitPromises.get(initKey);
|
|
@@ -996,16 +1003,16 @@ export async function createMixdogSessionRuntime({
|
|
|
996
1003
|
if (providerInitPromises.get(initKey) === providerInitPromise) providerInitPromises.delete(initKey);
|
|
997
1004
|
throw error;
|
|
998
1005
|
}
|
|
999
|
-
if (!startupProviderCatalogRefreshStarted && !closeRequested) {
|
|
1000
|
-
startupProviderCatalogRefreshStarted = true;
|
|
1001
|
-
startupProviderCatalogRefreshPending = true;
|
|
1006
|
+
if (!rt.startupProviderCatalogRefreshStarted && !rt.closeRequested) {
|
|
1007
|
+
rt.startupProviderCatalogRefreshStarted = true;
|
|
1008
|
+
rt.startupProviderCatalogRefreshPending = true;
|
|
1002
1009
|
try {
|
|
1003
1010
|
void Promise.resolve(reg.refreshProviderCatalogsOnStartup())
|
|
1004
1011
|
.then(() => {
|
|
1005
1012
|
// Fresh catalog rows invalidate model-derived caches, but the
|
|
1006
1013
|
// already initialized provider registry remains valid.
|
|
1007
1014
|
invalidateProviderCaches({ preserveProviderInit: true });
|
|
1008
|
-
startupProviderCatalogRefreshPending = false;
|
|
1015
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
1009
1016
|
// Secrets-aware: a no-secrets rewarm bumps the load sequence and
|
|
1010
1017
|
// is never adopted, so it discarded the in-flight authoritative
|
|
1011
1018
|
// load and left the picker cache empty — every later consumer then
|
|
@@ -1014,12 +1021,12 @@ export async function createMixdogSessionRuntime({
|
|
|
1014
1021
|
bootProfile('provider-catalogs:refresh-ready');
|
|
1015
1022
|
})
|
|
1016
1023
|
.catch((error) => {
|
|
1017
|
-
startupProviderCatalogRefreshPending = false;
|
|
1024
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
1018
1025
|
bootProfile('provider-catalogs:refresh-failed', { error: error?.message || String(error) });
|
|
1019
1026
|
});
|
|
1020
1027
|
bootProfile('provider-catalogs:refresh-started');
|
|
1021
1028
|
} catch (error) {
|
|
1022
|
-
startupProviderCatalogRefreshPending = false;
|
|
1029
|
+
rt.startupProviderCatalogRefreshPending = false;
|
|
1023
1030
|
bootProfile('provider-catalogs:refresh-failed', { error: error?.message || String(error) });
|
|
1024
1031
|
}
|
|
1025
1032
|
}
|
|
@@ -1030,11 +1037,11 @@ export async function createMixdogSessionRuntime({
|
|
|
1030
1037
|
currentMainSearchModelMeta,
|
|
1031
1038
|
runNativeWebSearch,
|
|
1032
1039
|
} = createNativeSearch({
|
|
1033
|
-
getRoute: () => route,
|
|
1034
|
-
getSearchRoute: () => searchRoute,
|
|
1035
|
-
setSearchRoute: (next) => { searchRoute = next; },
|
|
1036
|
-
getConfig: () => config,
|
|
1037
|
-
getSession: () => session,
|
|
1040
|
+
getRoute: () => rt.route,
|
|
1041
|
+
getSearchRoute: () => rt.searchRoute,
|
|
1042
|
+
setSearchRoute: (next) => { rt.searchRoute = next; },
|
|
1043
|
+
getConfig: () => rt.config,
|
|
1044
|
+
getSession: () => rt.session,
|
|
1038
1045
|
getReg: () => reg,
|
|
1039
1046
|
ensureFullConfig,
|
|
1040
1047
|
awaitKeychainPrewarm,
|
|
@@ -1051,23 +1058,25 @@ export async function createMixdogSessionRuntime({
|
|
|
1051
1058
|
// cachedProviderSetup(quick) may nudge scheduleProviderSetupWarmup on a cold
|
|
1052
1059
|
// quick-cache fill. Thread it by reference so the scheduler is reachable once
|
|
1053
1060
|
// it exists (a pre-scheduler quick fill simply skips the warmup nudge).
|
|
1054
|
-
|
|
1061
|
+
rt.scheduleProviderSetupWarmupRef = () => {};
|
|
1055
1062
|
const {
|
|
1056
1063
|
refreshStatuslineUsageSnapshot,
|
|
1057
1064
|
cachedProviderSetup,
|
|
1058
1065
|
hasProviderSetupCached,
|
|
1059
1066
|
getUsageDashboard,
|
|
1067
|
+
consumeCodexRateLimitResetCredit,
|
|
1060
1068
|
} = createProviderUsage({
|
|
1061
1069
|
caches: providerUsageCaches,
|
|
1062
|
-
getConfig: () => config,
|
|
1070
|
+
getConfig: () => rt.config,
|
|
1063
1071
|
getReg: () => reg,
|
|
1064
1072
|
displayConfig,
|
|
1065
1073
|
providerSetup,
|
|
1066
1074
|
createUsageDashboard,
|
|
1067
1075
|
fetchOAuthUsageSnapshot,
|
|
1068
|
-
|
|
1076
|
+
consumeOpenAICodexResetCredit,
|
|
1077
|
+
isCloseRequested: () => rt.closeRequested,
|
|
1069
1078
|
getProviderSetupWarmupTimer: () => warmupTimers.providerSetupWarmupTimer,
|
|
1070
|
-
scheduleProviderSetupWarmup: (delayMs) => scheduleProviderSetupWarmupRef(delayMs),
|
|
1079
|
+
scheduleProviderSetupWarmup: (delayMs) => rt.scheduleProviderSetupWarmupRef(delayMs),
|
|
1071
1080
|
});
|
|
1072
1081
|
|
|
1073
1082
|
// Holder filled after createQuickModelRows resolves; provider-models and
|
|
@@ -1079,7 +1088,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1079
1088
|
// lookupModelMeta may fire scheduleProviderModelWarmup on a cache miss. Thread
|
|
1080
1089
|
// it by reference so the scheduler is called once it exists (miss handling is
|
|
1081
1090
|
// best-effort; a pre-scheduler miss simply skips the warmup nudge).
|
|
1082
|
-
|
|
1091
|
+
rt.scheduleProviderModelWarmupRef = () => {};
|
|
1083
1092
|
const {
|
|
1084
1093
|
modelMetaKey,
|
|
1085
1094
|
lookupModelMeta,
|
|
@@ -1092,8 +1101,8 @@ export async function createMixdogSessionRuntime({
|
|
|
1092
1101
|
} = createProviderModels({
|
|
1093
1102
|
caches: providerModelCaches,
|
|
1094
1103
|
modelMetaByRoute,
|
|
1095
|
-
getRoute: () => route,
|
|
1096
|
-
getConfig: () => config,
|
|
1104
|
+
getRoute: () => rt.route,
|
|
1105
|
+
getConfig: () => rt.config,
|
|
1097
1106
|
getReg: () => reg,
|
|
1098
1107
|
searchCapableFor,
|
|
1099
1108
|
sortProviderModelsRaw,
|
|
@@ -1104,7 +1113,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1104
1113
|
awaitKeychainPrewarm,
|
|
1105
1114
|
ensureProvidersReady,
|
|
1106
1115
|
bootProfile,
|
|
1107
|
-
scheduleProviderModelWarmup: () => scheduleProviderModelWarmupRef(),
|
|
1116
|
+
scheduleProviderModelWarmup: () => rt.scheduleProviderModelWarmupRef(),
|
|
1108
1117
|
quickHelpers: providerModelQuickHelpers,
|
|
1109
1118
|
});
|
|
1110
1119
|
|
|
@@ -1115,8 +1124,8 @@ export async function createMixdogSessionRuntime({
|
|
|
1115
1124
|
searchModelsFromRows,
|
|
1116
1125
|
searchRowsWithDefault,
|
|
1117
1126
|
} = createQuickModelRows({
|
|
1118
|
-
getRoute: () => route,
|
|
1119
|
-
getSearchRoute: () => searchRoute,
|
|
1127
|
+
getRoute: () => rt.route,
|
|
1128
|
+
getSearchRoute: () => rt.searchRoute,
|
|
1120
1129
|
displayConfig,
|
|
1121
1130
|
providerModelCacheRow,
|
|
1122
1131
|
providerModelsFromCacheRows,
|
|
@@ -1137,295 +1146,71 @@ export async function createMixdogSessionRuntime({
|
|
|
1137
1146
|
searchRowsWithDefault,
|
|
1138
1147
|
});
|
|
1139
1148
|
|
|
1140
|
-
|
|
1141
|
-
if (routeHasModel()) return route;
|
|
1142
|
-
const models = await collectProviderModels();
|
|
1143
|
-
const picked = models[0] || null;
|
|
1144
|
-
if (!picked) {
|
|
1145
|
-
throw new Error('No provider models available. Open /providers to sign in, then /model to choose a model.');
|
|
1146
|
-
}
|
|
1147
|
-
route = {
|
|
1148
|
-
...route,
|
|
1149
|
-
provider: picked.provider,
|
|
1150
|
-
model: picked.id,
|
|
1151
|
-
preset: null,
|
|
1152
|
-
};
|
|
1153
|
-
return route;
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
async function refreshRouteEffort(modelMetaOverride = null, expectedRoute = null) {
|
|
1157
|
-
const targetRoute = expectedRoute || route;
|
|
1158
|
-
await ensureProvidersReady(ensureProviderEnabled(config, targetRoute.provider));
|
|
1159
|
-
const modelMeta = modelMetaOverride || await lookupModelMeta(targetRoute.provider, targetRoute.model);
|
|
1160
|
-
// A rapid second resume/model change can replace the route while provider
|
|
1161
|
-
// metadata is loading. Never let the older completion overwrite it.
|
|
1162
|
-
if (expectedRoute && route !== expectedRoute) return null;
|
|
1163
|
-
const requested = hasOwn(targetRoute, 'effort')
|
|
1164
|
-
? targetRoute.effort
|
|
1165
|
-
: (targetRoute.preset?.effort || null);
|
|
1166
|
-
const effectiveEffort = coerceEffortFor(targetRoute.provider, modelMeta, requested);
|
|
1167
|
-
const fastCapable = fastCapableFor(targetRoute.provider, modelMeta);
|
|
1168
|
-
// Carry the catalog display name onto the route so the statusline shows a
|
|
1169
|
-
// human label (e.g. "Claude Fable 5") for preset-less direct models instead
|
|
1170
|
-
// of the raw id. `name` is only trusted when it differs from the raw model
|
|
1171
|
-
// id (some providers echo the id as `name`), so it can't clobber a better
|
|
1172
|
-
// already-resolved label. Falls back to existing route.modelDisplay, then unset.
|
|
1173
|
-
const metaName = clean(modelMeta?.name);
|
|
1174
|
-
const modelDisplay = clean(modelMeta?.display) || clean(modelMeta?.displayName)
|
|
1175
|
-
|| (metaName && metaName !== clean(targetRoute.model) ? metaName : '')
|
|
1176
|
-
|| clean(targetRoute.modelDisplay);
|
|
1177
|
-
route = {
|
|
1178
|
-
...targetRoute,
|
|
1179
|
-
fast: fastCapable ? targetRoute.fast === true : false,
|
|
1180
|
-
fastCapable,
|
|
1181
|
-
effectiveEffort,
|
|
1182
|
-
effortOptions: effortItemsFor(route.provider, modelMeta, effectiveEffort),
|
|
1183
|
-
...(modelDisplay ? { modelDisplay } : {}),
|
|
1184
|
-
};
|
|
1185
|
-
return route;
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
function routeHasModel() {
|
|
1189
|
-
return !!clean(route?.model);
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
function requireModelRoute() {
|
|
1193
|
-
if (routeHasModel()) return;
|
|
1194
|
-
throw new Error('No model configured. Open /providers to sign in, then /model to choose a model.');
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
async function recreateCurrentSessionIfReady() {
|
|
1198
|
-
if (!routeHasModel()) {
|
|
1199
|
-
session = null;
|
|
1200
|
-
return null;
|
|
1201
|
-
}
|
|
1202
|
-
return await createCurrentSession();
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
async function createCurrentSession(reason = 'demand') {
|
|
1206
|
-
if (sessionCreatePromise) return await sessionCreatePromise;
|
|
1207
|
-
if (session?.id && !sessionNeedsCwdRefresh) {
|
|
1208
|
-
const liveSession = mgr.getSession(session.id);
|
|
1209
|
-
if (liveSession && liveSession.closed !== true && liveSession.status !== 'closed') {
|
|
1210
|
-
session = liveSession;
|
|
1211
|
-
return session;
|
|
1212
|
-
}
|
|
1213
|
-
session = null;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
const startedAt = performance.now();
|
|
1217
|
-
bootProfile('session:create:start', { mode, reason });
|
|
1218
|
-
const promise = (async () => {
|
|
1219
|
-
// Demand-only: this starts only after the user submits (unless an
|
|
1220
|
-
// explicitly enabled prewarm caller asks for a session). Core-memory
|
|
1221
|
-
// startup does not depend on keychain/provider readiness, so overlap the
|
|
1222
|
-
// two cold paths instead of paying their bounded waits serially.
|
|
1223
|
-
const coreMemoryContextPromise = loadCoreMemoryContext();
|
|
1224
|
-
await awaitKeychainPrewarm();
|
|
1225
|
-
ensureConfigForRouteProvider();
|
|
1226
|
-
await resolveMissingRouteModelForFirstTurn();
|
|
1227
|
-
requireModelRoute();
|
|
1228
|
-
bootProfile('session:create:route-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
1229
|
-
// Route effort waits on provider readiness while the already-started
|
|
1230
|
-
// memory load continues independently.
|
|
1231
|
-
const [, coreMemoryContext] = await Promise.all([
|
|
1232
|
-
refreshRouteEffort(),
|
|
1233
|
-
coreMemoryContextPromise,
|
|
1234
|
-
]);
|
|
1235
|
-
bootProfile('session:create:effort-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
1236
|
-
const providerImpl = reg.getProvider(route.provider);
|
|
1237
|
-
if (!providerImpl) {
|
|
1238
|
-
throw new Error(`Provider "${route.provider}" is not configured.`);
|
|
1239
|
-
}
|
|
1240
|
-
bootProfile('session:create:provider-ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
1241
|
-
if (closeRequested) throw new Error('runtime is closing');
|
|
1242
|
-
const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
|
|
1243
|
-
// Load the active WORKFLOW.md pack once for both summary + context block.
|
|
1244
|
-
const { summary: workflow, context: workflowContext } = activeWorkflowContext(config, dataDir);
|
|
1245
|
-
const sessionOpts = {
|
|
1246
|
-
provider: route.provider,
|
|
1247
|
-
model: route.model,
|
|
1248
|
-
preset: route.preset || undefined,
|
|
1249
|
-
tools: toolSpecForMode(mode),
|
|
1250
|
-
owner: 'cli',
|
|
1251
|
-
agent: 'lead',
|
|
1252
|
-
lane: 'cli',
|
|
1253
|
-
sourceType: 'lead',
|
|
1254
|
-
sourceName: 'main',
|
|
1255
|
-
clientHostPid: process.pid,
|
|
1256
|
-
disallowedTools: LEAD_DISALLOWED_TOOLS,
|
|
1257
|
-
cwd: currentCwd,
|
|
1258
|
-
...(desktopSession && typeof desktopSession === 'object' ? { desktopSession } : {}),
|
|
1259
|
-
coreMemoryContext,
|
|
1260
|
-
workflow,
|
|
1261
|
-
workflowContext,
|
|
1262
|
-
fast: route.fast === true,
|
|
1263
|
-
compaction: config.compaction && typeof config.compaction === 'object'
|
|
1264
|
-
? normalizeCompactionConfig(config.compaction, { memoryEnabled: memoryEnabled() })
|
|
1265
|
-
: undefined,
|
|
1266
|
-
};
|
|
1267
|
-
if (hasOwn(route, 'effort') || route.effectiveEffort) {
|
|
1268
|
-
sessionOpts.effort = route.effectiveEffort || null;
|
|
1269
|
-
}
|
|
1270
|
-
session = mgr.createSession(sessionOpts);
|
|
1271
|
-
sessionNeedsCwdRefresh = false;
|
|
1272
|
-
attachSessionHooks(session, { hooks, hookCommonPayload, getCwd: () => currentCwd });
|
|
1273
|
-
// Every-create MCP fold (NO blocking): seed the INITIAL provider-visible
|
|
1274
|
-
// surface (and native BP1 manifest) from MCP servers connected at create
|
|
1275
|
-
// time. There is no await — a boot connect still mid-handshake is caught on
|
|
1276
|
-
// the first user turn by refreshInitialDeferredMcpSurface (session-turn-api),
|
|
1277
|
-
// which re-folds the live registry into the first-turn surface before the
|
|
1278
|
-
// prompt renders. This fold keeps recreate paths (cwd change with MCP
|
|
1279
|
-
// already connected) seeding their manifest instead of re-announcing late.
|
|
1280
|
-
let connectedMcpTools = [];
|
|
1281
|
-
try { connectedMcpTools = mcpClient.getMcpTools?.() || []; }
|
|
1282
|
-
catch { connectedMcpTools = []; }
|
|
1283
|
-
applyDeferredToolSurface(
|
|
1284
|
-
session,
|
|
1285
|
-
deferredSurfaceModeForLead(mode),
|
|
1286
|
-
connectedMcpTools.length ? [...modelStandaloneTools(), ...connectedMcpTools] : modelStandaloneTools(),
|
|
1287
|
-
{ provider: route.provider },
|
|
1288
|
-
);
|
|
1289
|
-
// Session-local one-shot: mark this FRESH session eligible for the
|
|
1290
|
-
// first-turn deferred-surface refresh (session-turn-api). A resumed
|
|
1291
|
-
// session (prior transcript) is NEVER marked, so its already-baked BP1 is
|
|
1292
|
-
// never rebuilt or re-announced — the gate is per-session, not the
|
|
1293
|
-
// process-wide firstTurnCompleted.
|
|
1294
|
-
session.deferredInitialRefreshPending = !/resume/i.test(String(reason || ''));
|
|
1295
|
-
applyPreSessionToolSelection();
|
|
1296
|
-
writeStatuslineRoute(statusRoutes, session, route);
|
|
1297
|
-
hooks.emit('session:create', { sessionId: session.id, provider: route.provider, model: route.model, toolMode: mode, cwd: currentCwd });
|
|
1298
|
-
// SessionStart: bridge to the standard project hook bus. Best-effort;
|
|
1299
|
-
// a hook error must never break session creation. additionalContext is
|
|
1300
|
-
// injected before the first user turn as a system-reminder context pair.
|
|
1301
|
-
try {
|
|
1302
|
-
const startSource = /resume/i.test(String(reason || ''))
|
|
1303
|
-
? 'resume'
|
|
1304
|
-
: (/clear/i.test(String(reason || '')) ? 'clear' : 'startup');
|
|
1305
|
-
const startDispatch = await hooks.dispatch('SessionStart', hookCommonPayload({ session_id: session.id, source: startSource, model: route.model }));
|
|
1306
|
-
const startContext = Array.isArray(startDispatch?.additionalContext)
|
|
1307
|
-
? startDispatch.additionalContext.join('\n\n')
|
|
1308
|
-
: String(startDispatch?.additionalContext || '');
|
|
1309
|
-
if (startContext.trim()) {
|
|
1310
|
-
session.messages.push({ role: 'user', content: `<system-reminder>\n# SessionStart Hook Context\n${startContext.trim()}\n</system-reminder>` });
|
|
1311
|
-
session.messages.push({ role: 'assistant', content: '.' });
|
|
1312
|
-
session.updatedAt = Date.now();
|
|
1313
|
-
}
|
|
1314
|
-
} catch { /* best-effort: never break session create */ }
|
|
1315
|
-
bootProfile('session:create:ready', {
|
|
1316
|
-
ms: (performance.now() - startedAt).toFixed(1),
|
|
1317
|
-
reason,
|
|
1318
|
-
tools: Array.isArray(session.tools) ? session.tools.length : 0,
|
|
1319
|
-
catalog: Array.isArray(session.deferredToolCatalog) ? session.deferredToolCatalog.length : 0,
|
|
1320
|
-
});
|
|
1321
|
-
// A rebind push may have been deferred (e.g. 'acquired' landed before this
|
|
1322
|
-
// session existed). The writer is now bindable — flush it exactly once.
|
|
1323
|
-
flushPendingTranscriptRebind();
|
|
1324
|
-
return session;
|
|
1325
|
-
})();
|
|
1326
|
-
|
|
1327
|
-
sessionCreatePromise = promise;
|
|
1328
|
-
try {
|
|
1329
|
-
return await promise;
|
|
1330
|
-
} finally {
|
|
1331
|
-
if (sessionCreatePromise === promise) sessionCreatePromise = null;
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1149
|
+
// Route resolution + createCurrentSession: session-lifecycle.mjs.
|
|
1335
1150
|
const {
|
|
1151
|
+
resolveMissingRouteModelForFirstTurn,
|
|
1336
1152
|
scheduleProviderWarmup,
|
|
1337
1153
|
scheduleProviderSetupWarmup,
|
|
1338
1154
|
scheduleProviderModelWarmup,
|
|
1339
1155
|
scheduleModelCatalogWarmup,
|
|
1340
1156
|
scheduleStatuslineUsageWarmup,
|
|
1341
1157
|
scheduleStatuslineUsageRefresh,
|
|
1342
|
-
} = createWarmupSchedulers({
|
|
1343
|
-
timers: warmupTimers,
|
|
1344
|
-
bootProfile,
|
|
1345
|
-
getRoute: () => route,
|
|
1346
|
-
getConfig: () => config,
|
|
1347
|
-
isCloseRequested: () => closeRequested,
|
|
1348
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1349
|
-
getSessionCreatePromise: () => sessionCreatePromise,
|
|
1350
|
-
getProviderModelsCache: () => providerModelCaches.providerModelsCache,
|
|
1351
|
-
getProviderModelsPromise: () => providerModelCaches.providerModelsPromise,
|
|
1352
|
-
reloadFullConfig,
|
|
1353
|
-
ensureConfigForRouteProvider,
|
|
1354
|
-
awaitKeychainPrewarm,
|
|
1355
|
-
ensureProvidersReady,
|
|
1356
|
-
ensureProviderEnabled,
|
|
1357
|
-
refreshStatuslineUsageSnapshot,
|
|
1358
|
-
warmProviderModelCache,
|
|
1359
|
-
cachedProviderSetup,
|
|
1360
|
-
warmCatalogsInBackground,
|
|
1361
|
-
isFirstTurnCompleted: () => firstTurnCompleted,
|
|
1362
|
-
isCatalogRefreshPending: () => startupProviderCatalogRefreshPending,
|
|
1363
|
-
envFlag,
|
|
1364
|
-
delays: {
|
|
1365
|
-
providerWarmupDelayMs,
|
|
1366
|
-
providerSetupWarmupDelayMs,
|
|
1367
|
-
providerModelWarmupDelayMs,
|
|
1368
|
-
modelCatalogWarmupDelayMs,
|
|
1369
|
-
statuslineUsageWarmupDelayMs,
|
|
1370
|
-
statuslineUsageRefreshDelayMs,
|
|
1371
|
-
backgroundBusyRetryMs,
|
|
1372
|
-
},
|
|
1373
|
-
flags: {
|
|
1374
|
-
providerWarmupEnabled,
|
|
1375
|
-
modelPrefetchEnabled,
|
|
1376
|
-
modelCatalogWarmupEnabled,
|
|
1377
|
-
},
|
|
1378
|
-
});
|
|
1379
|
-
scheduleProviderModelWarmupRef = scheduleProviderModelWarmup;
|
|
1380
|
-
scheduleProviderSetupWarmupRef = scheduleProviderSetupWarmup;
|
|
1381
|
-
|
|
1382
|
-
const {
|
|
1383
1158
|
scheduleCodeGraphPrewarm,
|
|
1384
1159
|
scheduleLeadSessionPrewarm,
|
|
1385
1160
|
scheduleToolRuntimeWarmup,
|
|
1386
1161
|
invokeChannelStart,
|
|
1387
1162
|
scheduleChannelStart,
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
isCloseRequested: () => closeRequested,
|
|
1393
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1394
|
-
getSessionCreatePromise: () => sessionCreatePromise,
|
|
1395
|
-
getSession: () => session,
|
|
1396
|
-
isRemoteEnabled: () => remoteEnabled,
|
|
1397
|
-
channelsEnabled,
|
|
1398
|
-
hasActiveAutomation,
|
|
1399
|
-
getCodeGraphModule,
|
|
1163
|
+
refreshRouteEffort,
|
|
1164
|
+
routeHasModel,
|
|
1165
|
+
requireModelRoute,
|
|
1166
|
+
recreateCurrentSessionIfReady,
|
|
1400
1167
|
createCurrentSession,
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
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,
|
|
1426
1205
|
channelsEnabled,
|
|
1206
|
+
getCodeGraphModule,
|
|
1427
1207
|
channels,
|
|
1428
|
-
|
|
1208
|
+
codeGraphPrewarmDelayMs,
|
|
1209
|
+
channelStartDelayMs,
|
|
1210
|
+
sessionPrewarmDelayMs,
|
|
1211
|
+
codeGraphPrewarmEnabled,
|
|
1212
|
+
sessionPrewarmEnabled,
|
|
1213
|
+
prewarmState,
|
|
1429
1214
|
});
|
|
1430
1215
|
const ensureRemoteTranscriptWriter = () => remoteTranscript.ensureRemoteTranscriptWriter();
|
|
1431
1216
|
const pushTranscriptRebind = () => remoteTranscript.pushTranscriptRebind();
|
|
@@ -1434,12 +1219,12 @@ export async function createMixdogSessionRuntime({
|
|
|
1434
1219
|
// Remote (channel relay) claim/release/stop live in remote-control.mjs; the
|
|
1435
1220
|
// facade injects the mutable session + remote state they mutate.
|
|
1436
1221
|
const remoteControl = createRemoteControl({
|
|
1437
|
-
getSession: () => session,
|
|
1438
|
-
isRemoteEnabled: () => remoteEnabled,
|
|
1439
|
-
setRemoteEnabled: (next) => { remoteEnabled = next; },
|
|
1440
|
-
getRemoteSessionId: () => remoteSessionId,
|
|
1441
|
-
setRemoteSessionId: (next) => { remoteSessionId = next; },
|
|
1442
|
-
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,
|
|
1443
1228
|
clearChannelStartTimer: () => {
|
|
1444
1229
|
if (prewarmTimers.channelStartTimer) {
|
|
1445
1230
|
clearTimeout(prewarmTimers.channelStartTimer);
|
|
@@ -1465,7 +1250,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1465
1250
|
const stopRemote = (reason) => remoteControl.stopRemote(reason);
|
|
1466
1251
|
|
|
1467
1252
|
function isRemoteEnabled() {
|
|
1468
|
-
return remoteEnabled;
|
|
1253
|
+
return rt.remoteEnabled;
|
|
1469
1254
|
}
|
|
1470
1255
|
|
|
1471
1256
|
function withTeardownDeadline(promise, ms, fallback = false) {
|
|
@@ -1516,11 +1301,11 @@ export async function createMixdogSessionRuntime({
|
|
|
1516
1301
|
// early /remote (startRemote clears it), stopRemote(), and close() all
|
|
1517
1302
|
// cancel it through the existing clearTimeout paths. Runtime /remote calls
|
|
1518
1303
|
// still start immediately (user-initiated, UI already painted).
|
|
1519
|
-
if (remoteEnabled) {
|
|
1304
|
+
if (rt.remoteEnabled) {
|
|
1520
1305
|
bootProfile('channels:manual-start-deferred', { delayMs: remoteAutoStartDelayMs });
|
|
1521
1306
|
prewarmTimers.channelStartTimer = setTimeout(() => {
|
|
1522
1307
|
prewarmTimers.channelStartTimer = null;
|
|
1523
|
-
if (closeRequested || !remoteEnabled) return;
|
|
1308
|
+
if (rt.closeRequested || !rt.remoteEnabled) return;
|
|
1524
1309
|
startRemote();
|
|
1525
1310
|
}, remoteAutoStartDelayMs);
|
|
1526
1311
|
prewarmTimers.channelStartTimer.unref?.();
|
|
@@ -1531,10 +1316,10 @@ export async function createMixdogSessionRuntime({
|
|
|
1531
1316
|
// daemon without restarting schedules or webhooks.
|
|
1532
1317
|
prewarmTimers.channelStartTimer = setTimeout(() => {
|
|
1533
1318
|
prewarmTimers.channelStartTimer = null;
|
|
1534
|
-
if (closeRequested || remoteEnabled) return;
|
|
1319
|
+
if (rt.closeRequested || rt.remoteEnabled) return;
|
|
1535
1320
|
void hasActiveAutomation()
|
|
1536
1321
|
.then((active) => {
|
|
1537
|
-
if (!active || closeRequested || remoteEnabled) return;
|
|
1322
|
+
if (!active || rt.closeRequested || rt.remoteEnabled) return;
|
|
1538
1323
|
bootProfile('channels:automation-autostart');
|
|
1539
1324
|
void invokeChannelStart();
|
|
1540
1325
|
})
|
|
@@ -1548,10 +1333,10 @@ export async function createMixdogSessionRuntime({
|
|
|
1548
1333
|
// token save/forget, setBackend). Extracted to session-runtime/settings-api.mjs
|
|
1549
1334
|
// and SPREAD into the API object below so the external surface is unchanged.
|
|
1550
1335
|
const settingsApi = createSettingsApi({
|
|
1551
|
-
getConfig: () => config,
|
|
1552
|
-
getRoute: () => route,
|
|
1553
|
-
getSession: () => session,
|
|
1554
|
-
getRemoteEnabled: () => remoteEnabled,
|
|
1336
|
+
getConfig: () => rt.config,
|
|
1337
|
+
getRoute: () => rt.route,
|
|
1338
|
+
getSession: () => rt.session,
|
|
1339
|
+
getRemoteEnabled: () => rt.remoteEnabled,
|
|
1555
1340
|
adoptConfig,
|
|
1556
1341
|
saveConfigAndAdopt,
|
|
1557
1342
|
scheduleBackendSave,
|
|
@@ -1610,35 +1395,36 @@ export async function createMixdogSessionRuntime({
|
|
|
1610
1395
|
});
|
|
1611
1396
|
const providerAuthApi = createProviderAuthApi({
|
|
1612
1397
|
cfgMod,
|
|
1613
|
-
getConfig: () => config,
|
|
1398
|
+
getConfig: () => rt.config,
|
|
1614
1399
|
saveConfigAndAdopt,
|
|
1615
1400
|
displayConfig,
|
|
1616
1401
|
reloadFullConfig,
|
|
1617
1402
|
awaitKeychainPrewarm,
|
|
1618
|
-
isKeychainPrewarmReady: () => keychainPrewarmWaitDone,
|
|
1403
|
+
isKeychainPrewarmReady: () => rt.keychainPrewarmWaitDone,
|
|
1619
1404
|
hasProviderSetupCached,
|
|
1620
1405
|
invalidateProviderCaches,
|
|
1621
1406
|
warmProviderModelCache,
|
|
1622
|
-
refreshProviderCatalogs: () => ensureProvidersReady(config.providers || {}).then(() => reg.refreshCatalogs()),
|
|
1407
|
+
refreshProviderCatalogs: () => ensureProvidersReady(rt.config.providers || {}).then(() => reg.refreshCatalogs()),
|
|
1623
1408
|
cachedProviderSetup,
|
|
1624
1409
|
getUsageDashboard,
|
|
1410
|
+
consumeCodexRateLimitResetCredit,
|
|
1625
1411
|
collectProviderModels,
|
|
1626
1412
|
});
|
|
1627
1413
|
const mediaApi = createMediaApi();
|
|
1628
1414
|
const lifecycleApi = createLifecycleApi({
|
|
1629
|
-
getSession: () => session,
|
|
1630
|
-
setSession: (v) => { session = v; },
|
|
1631
|
-
getRoute: () => route,
|
|
1632
|
-
setRoute: (v) => { route = v; },
|
|
1633
|
-
getConfig: () => config,
|
|
1634
|
-
getMode: () => mode,
|
|
1635
|
-
getCurrentCwd: () => currentCwd,
|
|
1636
|
-
getDesktopSession: () => desktopSession,
|
|
1637
|
-
setDesktopSession: (v) => { desktopSession = v; },
|
|
1638
|
-
setCloseRequested: (v) => { closeRequested = v; },
|
|
1639
|
-
getMemoryModPromise: () => memoryModPromise,
|
|
1640
|
-
setMemoryModPromise: (v) => { memoryModPromise = v; },
|
|
1641
|
-
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; },
|
|
1642
1428
|
hooks,
|
|
1643
1429
|
hookCommonPayload,
|
|
1644
1430
|
mgr,
|
|
@@ -1667,12 +1453,12 @@ export async function createMixdogSessionRuntime({
|
|
|
1667
1453
|
// Live getter: cwd-refresh session rebuilds must re-evaluate the
|
|
1668
1454
|
// workflow's agent-tool gate, not reuse the boot-time array.
|
|
1669
1455
|
getStandaloneTools: modelStandaloneTools,
|
|
1670
|
-
desktopSession,
|
|
1456
|
+
desktopSession: rt.desktopSession,
|
|
1671
1457
|
});
|
|
1672
1458
|
const resourceApi = createResourceApi({
|
|
1673
|
-
getConfig: () => config,
|
|
1674
|
-
getSession: () => session,
|
|
1675
|
-
getCurrentCwd: () => currentCwd,
|
|
1459
|
+
getConfig: () => rt.config,
|
|
1460
|
+
getSession: () => rt.session,
|
|
1461
|
+
getCurrentCwd: () => rt.currentCwd,
|
|
1676
1462
|
cfgMod,
|
|
1677
1463
|
mgr,
|
|
1678
1464
|
hooks,
|
|
@@ -1690,17 +1476,17 @@ export async function createMixdogSessionRuntime({
|
|
|
1690
1476
|
getMemoryModule,
|
|
1691
1477
|
reloadFullConfig,
|
|
1692
1478
|
awaitKeychainPrewarm,
|
|
1693
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1479
|
+
getActiveTurnCount: () => rt.activeTurnCount,
|
|
1694
1480
|
});
|
|
1695
1481
|
const modelRouteApi = createModelRouteApi({
|
|
1696
|
-
getConfig: () => config,
|
|
1697
|
-
getRoute: () => route,
|
|
1698
|
-
setRouteState: (v) => { route = v; },
|
|
1699
|
-
getSession: () => session,
|
|
1700
|
-
setSession: (v) => { session = v; },
|
|
1701
|
-
getConfigHasSecrets: () => configHasSecrets,
|
|
1702
|
-
getSearchRouteState: () => searchRoute,
|
|
1703
|
-
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; },
|
|
1704
1490
|
cfgMod,
|
|
1705
1491
|
reg,
|
|
1706
1492
|
mgr,
|
|
@@ -1725,12 +1511,12 @@ export async function createMixdogSessionRuntime({
|
|
|
1725
1511
|
collectSearchProviderModels,
|
|
1726
1512
|
});
|
|
1727
1513
|
const workflowAgentsApi = createWorkflowAgentsApi({
|
|
1728
|
-
getConfig: () => config,
|
|
1729
|
-
getRoute: () => route,
|
|
1730
|
-
setRouteState: (v) => { route = v; },
|
|
1731
|
-
getSession: () => session,
|
|
1732
|
-
setSession: (v) => { session = v; },
|
|
1733
|
-
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,
|
|
1734
1520
|
cfgMod,
|
|
1735
1521
|
reg,
|
|
1736
1522
|
mgr,
|
|
@@ -1756,26 +1542,26 @@ export async function createMixdogSessionRuntime({
|
|
|
1756
1542
|
invalidateContextStatusCache,
|
|
1757
1543
|
});
|
|
1758
1544
|
const sessionTurnApi = createSessionTurnApi({
|
|
1759
|
-
getSession: () => session,
|
|
1760
|
-
setSession: (v) => { session = v; },
|
|
1761
|
-
getCurrentCwd: () => currentCwd,
|
|
1762
|
-
getMode: () => mode,
|
|
1763
|
-
setMode: (v) => { mode = v; },
|
|
1764
|
-
getActiveTurnCount: () => activeTurnCount,
|
|
1765
|
-
setActiveTurnCount: (v) => { activeTurnCount = v; },
|
|
1766
|
-
isFirstTurnCompleted: () => firstTurnCompleted,
|
|
1767
|
-
setFirstTurnCompleted: (v) => { firstTurnCompleted = v; },
|
|
1768
|
-
getCodeGraphFirstTurnPrewarmDone: () => codeGraphFirstTurnPrewarmDone,
|
|
1769
|
-
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; },
|
|
1770
1556
|
codeGraphPrewarmLazy,
|
|
1771
|
-
getRemoteEnabled: () => remoteEnabled,
|
|
1772
|
-
getCloseRequested: () => closeRequested,
|
|
1773
|
-
getPendingSessionReset: () => pendingSessionReset,
|
|
1774
|
-
setPendingSessionReset: (v) => { pendingSessionReset = v; },
|
|
1557
|
+
getRemoteEnabled: () => rt.remoteEnabled,
|
|
1558
|
+
getCloseRequested: () => rt.closeRequested,
|
|
1559
|
+
getPendingSessionReset: () => rt.pendingSessionReset,
|
|
1560
|
+
setPendingSessionReset: (v) => { rt.pendingSessionReset = v; },
|
|
1775
1561
|
getTranscriptWriter: () => remoteTranscript.transcriptWriter,
|
|
1776
1562
|
getTwKey: () => remoteTranscript.transcriptKey,
|
|
1777
|
-
getLastAppendedAssistant: () => _lastAppendedAssistant,
|
|
1778
|
-
setLastAppendedAssistant: (v) => { _lastAppendedAssistant = v; },
|
|
1563
|
+
getLastAppendedAssistant: () => rt._lastAppendedAssistant,
|
|
1564
|
+
setLastAppendedAssistant: (v) => { rt._lastAppendedAssistant = v; },
|
|
1779
1565
|
scheduleCodeGraphPrewarm,
|
|
1780
1566
|
refreshSessionForCwdIfNeeded,
|
|
1781
1567
|
createCurrentSession,
|
|
@@ -1813,55 +1599,55 @@ export async function createMixdogSessionRuntime({
|
|
|
1813
1599
|
...mediaApi,
|
|
1814
1600
|
// Turn-scoped attributed review: child apply_patch diffs for this Lead turn.
|
|
1815
1601
|
// Lead patches are read from its transcript by the renderer.
|
|
1816
|
-
getTurnReviewDiff: () => getTurnSnapshotReviewDiff(currentCwd, session?.id),
|
|
1602
|
+
getTurnReviewDiff: () => getTurnSnapshotReviewDiff(rt.currentCwd, rt.session?.id),
|
|
1817
1603
|
get id() {
|
|
1818
|
-
return session?.id || null;
|
|
1604
|
+
return rt.session?.id || null;
|
|
1819
1605
|
},
|
|
1820
1606
|
get provider() {
|
|
1821
|
-
return route.provider;
|
|
1607
|
+
return rt.route.provider;
|
|
1822
1608
|
},
|
|
1823
1609
|
get model() {
|
|
1824
|
-
return route.model;
|
|
1610
|
+
return rt.route.model;
|
|
1825
1611
|
},
|
|
1826
1612
|
get effort() {
|
|
1827
|
-
return route.effectiveEffort || route.effort || route.preset?.effort || null;
|
|
1613
|
+
return rt.route.effectiveEffort || rt.route.effort || rt.route.preset?.effort || null;
|
|
1828
1614
|
},
|
|
1829
1615
|
get fast() {
|
|
1830
|
-
return route.fast === true;
|
|
1616
|
+
return rt.route.fast === true;
|
|
1831
1617
|
},
|
|
1832
1618
|
get fastCapable() {
|
|
1833
|
-
return route.fastCapable === true;
|
|
1619
|
+
return rt.route.fastCapable === true;
|
|
1834
1620
|
},
|
|
1835
1621
|
get effortOptions() {
|
|
1836
|
-
return route.effortOptions || [];
|
|
1622
|
+
return rt.route.effortOptions || [];
|
|
1837
1623
|
},
|
|
1838
1624
|
get contextWindow() {
|
|
1839
|
-
return session?.contextWindow || null;
|
|
1625
|
+
return rt.session?.contextWindow || null;
|
|
1840
1626
|
},
|
|
1841
1627
|
get rawContextWindow() {
|
|
1842
|
-
return session?.rawContextWindow || session?.contextWindow || null;
|
|
1628
|
+
return rt.session?.rawContextWindow || rt.session?.contextWindow || null;
|
|
1843
1629
|
},
|
|
1844
1630
|
get effectiveContextWindowPercent() {
|
|
1845
|
-
return session?.effectiveContextWindowPercent || null;
|
|
1631
|
+
return rt.session?.effectiveContextWindowPercent || null;
|
|
1846
1632
|
},
|
|
1847
1633
|
get toolMode() {
|
|
1848
|
-
return mode;
|
|
1634
|
+
return rt.mode;
|
|
1849
1635
|
},
|
|
1850
1636
|
get autoClear() {
|
|
1851
1637
|
return this.getAutoClear();
|
|
1852
1638
|
},
|
|
1853
1639
|
get systemShell() {
|
|
1854
|
-
return normalizeSystemShellConfig(config.shell);
|
|
1640
|
+
return normalizeSystemShellConfig(rt.config.shell);
|
|
1855
1641
|
},
|
|
1856
1642
|
get searchRoute() {
|
|
1857
|
-
searchRoute = normalizeSearchRouteConfig(config.searchRoute) || normalizeSearchRouteConfig(searchRoute);
|
|
1858
|
-
return searchRoute;
|
|
1643
|
+
rt.searchRoute = normalizeSearchRouteConfig(rt.config.searchRoute) || normalizeSearchRouteConfig(rt.searchRoute);
|
|
1644
|
+
return rt.searchRoute;
|
|
1859
1645
|
},
|
|
1860
1646
|
get workflow() {
|
|
1861
1647
|
const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
|
|
1862
|
-
const active = activeWorkflowSummary(config, dataDir);
|
|
1863
|
-
if (session?.workflow && typeof session.workflow === 'object') {
|
|
1864
|
-
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);
|
|
1865
1651
|
return current?.id && active?.id && current.id !== active.id
|
|
1866
1652
|
? { ...active, currentSession: current, appliedToCurrentSession: false }
|
|
1867
1653
|
: active;
|
|
@@ -1872,10 +1658,10 @@ export async function createMixdogSessionRuntime({
|
|
|
1872
1658
|
return getOutputStyleStatusCached().current;
|
|
1873
1659
|
},
|
|
1874
1660
|
get cwd() {
|
|
1875
|
-
return currentCwd;
|
|
1661
|
+
return rt.currentCwd;
|
|
1876
1662
|
},
|
|
1877
1663
|
get session() {
|
|
1878
|
-
return session;
|
|
1664
|
+
return rt.session;
|
|
1879
1665
|
},
|
|
1880
1666
|
contextStatus() {
|
|
1881
1667
|
// Prefer the in-flight working transcript while a turn is running so the
|
|
@@ -1900,7 +1686,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1900
1686
|
},
|
|
1901
1687
|
// Session-scoped remote: the owning session id (null when off/unassigned).
|
|
1902
1688
|
getRemoteSessionId() {
|
|
1903
|
-
return remoteSessionId;
|
|
1689
|
+
return rt.remoteSessionId;
|
|
1904
1690
|
},
|
|
1905
1691
|
// Back-compat alias for an explicit manual ON in the current session.
|
|
1906
1692
|
claimRemoteForCurrentSession() {
|
|
@@ -1915,7 +1701,7 @@ export async function createMixdogSessionRuntime({
|
|
|
1915
1701
|
return () => remoteStateListeners.delete(listener);
|
|
1916
1702
|
},
|
|
1917
1703
|
get clientHostPid() {
|
|
1918
|
-
return session?.clientHostPid || process.pid;
|
|
1704
|
+
return rt.session?.clientHostPid || process.pid;
|
|
1919
1705
|
},
|
|
1920
1706
|
...lifecycleApi,
|
|
1921
1707
|
...resourceApi,
|