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
|
@@ -91,6 +91,40 @@ export function createPrewarmSchedulers({
|
|
|
91
91
|
timer.unref?.();
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// Tool-runtime warmup: the per-call cold starts a session's FIRST tool call
|
|
95
|
+
// would otherwise pay — pwsh standby processes (~300ms each) and the o200k
|
|
96
|
+
// encoder + token-count worker (~100ms). Each part is independent and
|
|
97
|
+
// best-effort; failures only mark the boot profile.
|
|
98
|
+
function scheduleToolRuntimeWarmup(delayMs = 2500) {
|
|
99
|
+
if (envFlag('MIXDOG_DISABLE_TOOL_PREWARM')) {
|
|
100
|
+
bootProfile('tool-runtime:prewarm-skipped');
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const timer = setTimeout(() => void (async () => {
|
|
104
|
+
if (isCloseRequested()) return;
|
|
105
|
+
try {
|
|
106
|
+
const { prewarmShellStandbys } = await import('../runtime/agent/orchestrator/tools/builtin/bash-tool.mjs');
|
|
107
|
+
bootProfile('tool-runtime:shell-standby', { warmed: prewarmShellStandbys() === true });
|
|
108
|
+
} catch (error) {
|
|
109
|
+
bootProfile('tool-runtime:shell-standby-failed', { error: error?.message || String(error) });
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const { prewarmTokenEstimator } = await import('../runtime/agent/orchestrator/session/context-utils.mjs');
|
|
113
|
+
bootProfile('tool-runtime:token-estimator', { warmed: prewarmTokenEstimator() === true });
|
|
114
|
+
} catch (error) {
|
|
115
|
+
bootProfile('tool-runtime:token-estimator-failed', { error: error?.message || String(error) });
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const { prewarmAgentShard } = await import('../standalone/agent-shard/shard-pool.mjs');
|
|
119
|
+
const warmed = await prewarmAgentShard();
|
|
120
|
+
if (warmed) bootProfile('tool-runtime:agent-shard', { warmed: true });
|
|
121
|
+
} catch (error) {
|
|
122
|
+
bootProfile('tool-runtime:agent-shard-failed', { error: error?.message || String(error) });
|
|
123
|
+
}
|
|
124
|
+
})(), delayMs);
|
|
125
|
+
timer.unref?.();
|
|
126
|
+
}
|
|
127
|
+
|
|
94
128
|
function invokeChannelStart() {
|
|
95
129
|
if (state.channelStartPromise) return state.channelStartPromise;
|
|
96
130
|
const startedAt = performance.now();
|
|
@@ -142,6 +176,7 @@ export function createPrewarmSchedulers({
|
|
|
142
176
|
return {
|
|
143
177
|
scheduleCodeGraphPrewarm,
|
|
144
178
|
scheduleLeadSessionPrewarm,
|
|
179
|
+
scheduleToolRuntimeWarmup,
|
|
145
180
|
invokeChannelStart,
|
|
146
181
|
scheduleChannelStart,
|
|
147
182
|
};
|
|
@@ -31,6 +31,7 @@ export function createProviderAuthApi({
|
|
|
31
31
|
refreshProviderCatalogs,
|
|
32
32
|
cachedProviderSetup,
|
|
33
33
|
getUsageDashboard,
|
|
34
|
+
consumeCodexRateLimitResetCredit,
|
|
34
35
|
collectProviderModels,
|
|
35
36
|
}) {
|
|
36
37
|
function refreshProviderCatalogsSoon() {
|
|
@@ -79,6 +80,13 @@ export function createProviderAuthApi({
|
|
|
79
80
|
async getUsageDashboard(options = {}) {
|
|
80
81
|
return await getUsageDashboard(options);
|
|
81
82
|
},
|
|
83
|
+
async consumeCodexRateLimitResetCredit(options = {}) {
|
|
84
|
+
await awaitKeychainPrewarm();
|
|
85
|
+
if (typeof consumeCodexRateLimitResetCredit !== 'function') {
|
|
86
|
+
throw new Error('Codex reset is unavailable');
|
|
87
|
+
}
|
|
88
|
+
return await consumeCodexRateLimitResetCredit(options);
|
|
89
|
+
},
|
|
82
90
|
async authenticateProvider(providerId, secret) {
|
|
83
91
|
await awaitKeychainPrewarm();
|
|
84
92
|
const result = String(secret || '').trim()
|
|
@@ -14,6 +14,7 @@ export function createProviderUsage({
|
|
|
14
14
|
providerSetup,
|
|
15
15
|
createUsageDashboard,
|
|
16
16
|
fetchOAuthUsageSnapshot,
|
|
17
|
+
consumeOpenAICodexResetCredit,
|
|
17
18
|
isCloseRequested,
|
|
18
19
|
getProviderSetupWarmupTimer,
|
|
19
20
|
scheduleProviderSetupWarmup,
|
|
@@ -64,8 +65,9 @@ export function createProviderUsage({
|
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
async function getUsageDashboard(options = {}) {
|
|
67
|
-
const
|
|
68
|
-
|
|
68
|
+
const refreshUsage = options?.refresh === true;
|
|
69
|
+
const forceSetup = options?.force === true || (refreshUsage && options?.refreshSetup !== false);
|
|
70
|
+
if (!forceSetup && !refreshUsage && caches.usageDashboardCache.dashboard) {
|
|
69
71
|
const cached = {
|
|
70
72
|
...caches.usageDashboardCache.dashboard,
|
|
71
73
|
refresh: false,
|
|
@@ -78,7 +80,7 @@ export function createProviderUsage({
|
|
|
78
80
|
}
|
|
79
81
|
return cached;
|
|
80
82
|
}
|
|
81
|
-
if (!forceSetup && caches.usageDashboardPromise) return await caches.usageDashboardPromise;
|
|
83
|
+
if (!forceSetup && !refreshUsage && caches.usageDashboardPromise) return await caches.usageDashboardPromise;
|
|
82
84
|
const quickSetup = options?.quickSetup !== false;
|
|
83
85
|
const getProvider = (providerId) => reg().getProvider(providerId);
|
|
84
86
|
const log = (message) => {
|
|
@@ -98,16 +100,26 @@ export function createProviderUsage({
|
|
|
98
100
|
});
|
|
99
101
|
}
|
|
100
102
|
const buildDashboard = async () => {
|
|
103
|
+
let setup;
|
|
104
|
+
try {
|
|
105
|
+
setup = await cachedProviderSetup({ force: forceSetup, quick: false });
|
|
106
|
+
} catch {
|
|
107
|
+
// One unavailable keychain/provider descriptor must not take down the
|
|
108
|
+
// whole dashboard. The no-secrets/no-local snapshot still lets cached
|
|
109
|
+
// and provider-native quota windows refresh.
|
|
110
|
+
log('provider setup failed; falling back to quick setup');
|
|
111
|
+
setup = await cachedProviderSetup({ force: forceSetup, quick: true });
|
|
112
|
+
}
|
|
101
113
|
const dashboard = await createUsageDashboard(displayConfig(), {
|
|
102
114
|
...(options || {}),
|
|
103
|
-
setup
|
|
115
|
+
setup,
|
|
104
116
|
getProvider,
|
|
105
117
|
log,
|
|
106
118
|
});
|
|
107
119
|
caches.usageDashboardCache = { dashboard, at: Date.now() };
|
|
108
120
|
return dashboard;
|
|
109
121
|
};
|
|
110
|
-
if (forceSetup) return await buildDashboard();
|
|
122
|
+
if (forceSetup || refreshUsage) return await buildDashboard();
|
|
111
123
|
caches.usageDashboardPromise = buildDashboard()
|
|
112
124
|
.finally(() => {
|
|
113
125
|
caches.usageDashboardPromise = null;
|
|
@@ -115,6 +127,18 @@ export function createProviderUsage({
|
|
|
115
127
|
return await caches.usageDashboardPromise;
|
|
116
128
|
}
|
|
117
129
|
|
|
130
|
+
async function consumeCodexRateLimitResetCredit(options = {}) {
|
|
131
|
+
if (typeof consumeOpenAICodexResetCredit !== 'function') {
|
|
132
|
+
throw new Error('Codex reset is unavailable');
|
|
133
|
+
}
|
|
134
|
+
const providerObj = reg().getProvider('openai-oauth');
|
|
135
|
+
if (!providerObj) throw new Error('Codex is not signed in');
|
|
136
|
+
const result = await consumeOpenAICodexResetCredit(providerObj, options);
|
|
137
|
+
caches.usageDashboardCache = {};
|
|
138
|
+
const dashboard = await getUsageDashboard({ refresh: true, refreshSetup: false });
|
|
139
|
+
return { ...result, dashboard };
|
|
140
|
+
}
|
|
141
|
+
|
|
118
142
|
return {
|
|
119
143
|
refreshStatuslineUsageSnapshot,
|
|
120
144
|
cachedProviderSetup,
|
|
@@ -122,5 +146,6 @@ export function createProviderUsage({
|
|
|
122
146
|
// (settings hydration) can serve the quick snapshot until then.
|
|
123
147
|
hasProviderSetupCached: () => Boolean(caches.providerSetupCache.setup),
|
|
124
148
|
getUsageDashboard,
|
|
149
|
+
consumeCodexRateLimitResetCredit,
|
|
125
150
|
};
|
|
126
151
|
}
|
|
@@ -10,8 +10,6 @@ export function createRemoteControl({
|
|
|
10
10
|
setRemoteEnabled,
|
|
11
11
|
getRemoteSessionId,
|
|
12
12
|
setRemoteSessionId,
|
|
13
|
-
setRemoteClaimPending,
|
|
14
|
-
isRemoteClaimPending,
|
|
15
13
|
isCloseRequested,
|
|
16
14
|
clearChannelStartTimer,
|
|
17
15
|
remoteTransitions,
|
|
@@ -28,22 +26,11 @@ export function createRemoteControl({
|
|
|
28
26
|
bootProfile,
|
|
29
27
|
envFlag,
|
|
30
28
|
}) {
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (intent === 'auto') {
|
|
37
|
-
// Auto-start attempts a claim-if-vacant: remoteEnabled flips only when the
|
|
38
|
-
// worker reports it actually acquired the seat, so a live owner elsewhere
|
|
39
|
-
// leaves this session non-remote.
|
|
40
|
-
setRemoteClaimPending(true);
|
|
41
|
-
} else {
|
|
42
|
-
setRemoteEnabled(true);
|
|
43
|
-
// Explicit toggle: the CURRENT session owns the relay (lazy create adopts
|
|
44
|
-
// inside ensureRemoteTranscriptWriter when no session exists yet).
|
|
45
|
-
setRemoteSessionId(getSession()?.id || null);
|
|
46
|
-
}
|
|
29
|
+
// Manual ON always targets the current session and explicitly overrides any
|
|
30
|
+
// previous manual owner. There is no auto/claim-if-vacant path.
|
|
31
|
+
function startRemote() {
|
|
32
|
+
setRemoteEnabled(true);
|
|
33
|
+
setRemoteSessionId(getSession()?.id || null);
|
|
47
34
|
// The worker forwards transcript ingests to the memory service, so its port
|
|
48
35
|
// must be live before the first ingest.
|
|
49
36
|
startMemoryDaemonEagerly({ getMemoryModule, bootProfile });
|
|
@@ -65,7 +52,6 @@ export function createRemoteControl({
|
|
|
65
52
|
// boots the worker, which runs headless when messaging is off.
|
|
66
53
|
if (!channelsEnabled() && !(await hasActiveAutomation().catch(() => false))) {
|
|
67
54
|
bootProfile('channels:start-disabled');
|
|
68
|
-
setRemoteClaimPending(false);
|
|
69
55
|
return isRemoteEnabled();
|
|
70
56
|
}
|
|
71
57
|
// A backend switch may still be writing; drain it before the worker
|
|
@@ -83,39 +69,16 @@ export function createRemoteControl({
|
|
|
83
69
|
ensureRemoteTranscriptWriter();
|
|
84
70
|
// Re-check after the awaits: stop/supersede or runtime close may have
|
|
85
71
|
// landed mid-chain — never boot for a session that turned remote off.
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
// The fork reads process.env synchronously, so set the inherited intent
|
|
91
|
-
// immediately before it and restore right after: the pollution window is
|
|
92
|
-
// this fork instead of the whole boot chain.
|
|
93
|
-
const previousIntent = process.env.MIXDOG_REMOTE_INTENT;
|
|
94
|
-
try {
|
|
95
|
-
process.env.MIXDOG_REMOTE_INTENT = intent;
|
|
96
|
-
await invokeChannelStart();
|
|
97
|
-
} finally {
|
|
98
|
-
if (previousIntent === undefined) delete process.env.MIXDOG_REMOTE_INTENT;
|
|
99
|
-
else process.env.MIXDOG_REMOTE_INTENT = previousIntent;
|
|
100
|
-
}
|
|
101
|
-
if ((!isRemoteEnabled() && !isRemoteClaimPending()) || isCloseRequested()) {
|
|
102
|
-
setRemoteClaimPending(false);
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
// Explicit claims are unconditional; auto-start dispatches the same
|
|
106
|
-
// activation as claim-if-vacant so it promotes an automation-only daemon
|
|
107
|
-
// without stealing a live remote owner.
|
|
72
|
+
if (!isRemoteEnabled() || isCloseRequested()) return false;
|
|
73
|
+
await invokeChannelStart();
|
|
74
|
+
if (!isRemoteEnabled() || isCloseRequested()) return false;
|
|
108
75
|
await channels.execute('activate_channel_bridge', {
|
|
109
76
|
active: true,
|
|
110
|
-
claimIfVacant: intent === 'auto',
|
|
111
77
|
sessionId: getSession()?.id || getRemoteSessionId() || null,
|
|
112
78
|
transcriptPath: getTranscriptPath(),
|
|
113
79
|
});
|
|
114
|
-
// The worker's acquire/supersede notification now owns the transition.
|
|
115
|
-
setRemoteClaimPending(false);
|
|
116
80
|
return isRemoteEnabled();
|
|
117
81
|
} catch (error) {
|
|
118
|
-
setRemoteClaimPending(false);
|
|
119
82
|
if (isRemoteEnabled()) {
|
|
120
83
|
setRemoteEnabled(false);
|
|
121
84
|
setRemoteSessionId(null);
|
|
@@ -135,7 +98,6 @@ export function createRemoteControl({
|
|
|
135
98
|
const releasingSessionId = getRemoteSessionId() || getSession()?.id || null;
|
|
136
99
|
setRemoteEnabled(false);
|
|
137
100
|
setRemoteSessionId(null);
|
|
138
|
-
setRemoteClaimPending(false);
|
|
139
101
|
clearChannelStartTimer();
|
|
140
102
|
return remoteTransitions.run(async () => {
|
|
141
103
|
try {
|
|
@@ -156,7 +118,6 @@ export function createRemoteControl({
|
|
|
156
118
|
function stopRemote(reason) {
|
|
157
119
|
setRemoteEnabled(false);
|
|
158
120
|
setRemoteSessionId(null);
|
|
159
|
-
setRemoteClaimPending(false);
|
|
160
121
|
clearChannelStartTimer();
|
|
161
122
|
channels.stop(reason || 'remote-disabled').catch(() => {});
|
|
162
123
|
return true;
|
|
@@ -17,7 +17,6 @@ export function createRemoteTranscript({
|
|
|
17
17
|
isCloseRequested,
|
|
18
18
|
channelsEnabled,
|
|
19
19
|
channels,
|
|
20
|
-
mgr,
|
|
21
20
|
bootProfile,
|
|
22
21
|
}) {
|
|
23
22
|
let writer = null;
|
|
@@ -36,9 +35,8 @@ export function createRemoteTranscript({
|
|
|
36
35
|
flushPendingTranscriptRebind,
|
|
37
36
|
};
|
|
38
37
|
|
|
39
|
-
// Bind (or refresh) the
|
|
40
|
-
//
|
|
41
|
-
// every other session, and a dead owner hands the relay over.
|
|
38
|
+
// Bind (or refresh) only the manually selected session. Session creation,
|
|
39
|
+
// close, or liveness never transfers Remote ownership.
|
|
42
40
|
function ensureRemoteTranscriptWriter() {
|
|
43
41
|
const session = getSession();
|
|
44
42
|
if (!isRemoteEnabled() || !session?.id) return false;
|
|
@@ -46,9 +44,7 @@ export function createRemoteTranscript({
|
|
|
46
44
|
if (!owner) {
|
|
47
45
|
setRemoteSessionId(session.id);
|
|
48
46
|
} else if (session.id !== owner) {
|
|
49
|
-
|
|
50
|
-
if (ownerSession && ownerSession.closed !== true && ownerSession.status !== 'closed') return false;
|
|
51
|
-
setRemoteSessionId(session.id);
|
|
47
|
+
return false;
|
|
52
48
|
}
|
|
53
49
|
const cwd = getCwd();
|
|
54
50
|
const key = `${session.id}\u0000${cwd}`;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// One background route preparation can be associated with the currently
|
|
2
|
+
// resumed session. Resume/navigation reads do not wait for it; the next real
|
|
3
|
+
// turn does. Clearing replaces the visible gate without attempting to cancel
|
|
4
|
+
// provider initialization that may already be shared elsewhere.
|
|
5
|
+
export function createRoutePreparationGate({ onError } = {}) {
|
|
6
|
+
let pending = Promise.resolve();
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
clear() {
|
|
10
|
+
pending = Promise.resolve();
|
|
11
|
+
},
|
|
12
|
+
start(task) {
|
|
13
|
+
const operation = Promise.resolve().then(task);
|
|
14
|
+
pending = operation;
|
|
15
|
+
// Keep the original rejecting promise for waiters while still marking a
|
|
16
|
+
// background failure as observed until a submit reaches the same gate.
|
|
17
|
+
void operation.catch((error) => onError?.(error));
|
|
18
|
+
return operation;
|
|
19
|
+
},
|
|
20
|
+
wait() {
|
|
21
|
+
return pending;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|