zidane 7.0.22 → 7.0.24
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/dist/{approvals-BnDsgTtD.js → approvals-Deg8JhMI.js} +208 -66
- package/dist/approvals-Deg8JhMI.js.map +1 -0
- package/dist/chat.d.ts +1 -1
- package/dist/chat.js +1 -1
- package/dist/daemon-cli.js +1 -1
- package/dist/{index-D2JCY11Z.d.ts → index-DQn4tuAA.d.ts} +3 -1
- package/dist/{index-D2JCY11Z.d.ts.map → index-DQn4tuAA.d.ts.map} +1 -1
- package/dist/{ipc-Oro3uyMP.js → ipc-DLi4hMep.js} +2 -2
- package/dist/{ipc-Oro3uyMP.js.map → ipc-DLi4hMep.js.map} +1 -1
- package/dist/ipc.d.ts +1 -1
- package/dist/ipc.js +3 -3
- package/dist/{server-HZdjqDpS.js → server-BlE48mXo.js} +4 -4
- package/dist/{server-HZdjqDpS.js.map → server-BlE48mXo.js.map} +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +505 -378
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
- package/dist/approvals-BnDsgTtD.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -20,9 +20,9 @@ import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
|
|
|
20
20
|
import { n as JsonRpcRemoteError, t as JsonRpcConnectionClosedError } from "./json-rpc-rSWMUh9b.js";
|
|
21
21
|
import { $ as reorderKeyed, $t as formatTokenCount, A as chunkMarkdownForRender, B as streamingLabel, C as usageTone, Ct as retagUserPromptEvents, Dt as streamEventSearchText, E as filterModelCatalog, Et as selectionAfterTurnRemoval, F as stripHtmlCommentsOutsideCode, G as chainPositionOf, Gt as shortSessionId, H as DAEMON_HEALTH_STYLE, Ht as sessionEventExcerpt, I as cacheBreakChip, It as isMainSession, J as filterChainFallbacks, Jt as fitRunReceiptCells, K as chainsEqual, Kt as truncateTitle, L as compactingLabel, M as codeTokenHasClosingFence, Mt as SESSION_EVENT_STATUS_STYLE, N as normalizeMarkdownCodeFences, O as parseCustomModelQuery, Ot as turnSelectionOwnership, P as splitMarkdownCodeBlocks, Q as providerKeySetChanged, Qt as formatRunReceiptLine, R as liveSessionCost, S as resetLabel, Sn as summarizeOutcomes, T as buildModelCatalog, Tn as createChatsCompletionProvider, Tt as selectableTurnIds, U as buildRunModel, Vt as sessionDirLabel, Wt as sessionEventMeta, Y as pickChainPrimary, Yt as formatCostUsd, Zt as formatRunReceiptCells, _ as turnContextSize, _n as mergeApprovalAndBodyOutcomes, _t as isTurnHighlighted, a as TOOL_DISPLAY, an as buildUnifiedDiff, at as createSkillsCompletionProvider, b as formatUsagePercent, bn as rewriteMultiEditHeader, bt as isVisibleDuringSearch, ct as createFilesCompletionProvider, d as finalizeStreamingMarkdown, dn as previewEditPayload, dt as createCommandsCompletionProvider, et as restoreChainFallbacks, f as finalizeStreamingMarkdownForOwner, ft as blendHsl, g as revealEarlierTranscriptEvents, gt as isEditErrorResult, h as replaceTranscriptTurnEvents, hn as buildEditOutcomesAnnotation, ht as EDIT_TOOL_NAMES, i as turnAsText, in as buildContextualDiff, j as closeDanglingCodeFence, ln as extractEditPayload, mt as BASH_CALL_ID_PREFIX, n as deleteTurnSafely, nn as runReceiptFromRun, nt as toggleChainEntry, o as displayNameFor, on as clipDiffForRender, ot as uniqueSkillNamesFromReferences, p as hiddenEarlierTranscriptCount, pn as summarizeEditPayload, pt as buildLinearRamp, r as truncateTurnsAt, s as formatToolCall, tn as hasRunReceipt, u as capTranscriptEvents, un as filetypeFromPath, v as formatBalance, vn as parseEditOutcomesFromResult, vt as isUserBashEvent, w as splitPromptSegments, wt as retagUserPromptEventsFromRefs, x as mostConstrainedWindow, xn as stripEditOutcomesAnnotation, xt as promptHistoryFromEvents, yn as resolveApprovalForPayload, yt as isVisible } from "./turn-operations-BgqpLlOG.js";
|
|
22
22
|
import { a as searchHighlightRanges, c as searchSessionHistoryIndexAsync, i as highlightSegments, n as buildSessionSearchIndexAsync, r as filterSessionSearchCatalog, u as searchSessionIndexAsync } from "./history-search-C4bk1Wz_.js";
|
|
23
|
-
import { $ as supportsOAuth, $n as tryOpenBrowser, A as useSafeModeQueue, An as createDiscoverySlot, At as makeRequestInteraction, Ci as sumRunCosts, Ct as InteractionsProvider, Di as updateToolEventOutcomes, Dr as accentColor, Dt as createInteractionTools, Ei as toolResultText, Et as createAutomaticInteractionResponse, Fn as useConfig, G as usageChecksOptedOut, Gn as mcpsWritePath, Gr as setTodosForRun, H as fetchAllProviderUsage, Hn as countClaudeSessionFiles, Hr as pruneTodosByRun, In as resolveConfig, Ir as applyTodosSnapshot, J as fetchOAuthRedirect, Jn as buildMcpServers, Jt as buildPluginContributions, Kn as removeMcpServer, Kr as todoProgressLabel, Ln as resolveStoragePaths, M as addToSafelist, Mn as useDiscovery, N as getSafelist, Nn as useDiscoveryOptional, Nt as useInteractionsActions, O as SafeModeProvider, Oi as usageByModel, On as parseEditorCommand, P as isOnSafelist, Pn as ConfigProvider, Pr as TODO_STATUS_GLYPHS, Pt as useInteractionsQueue, Q as runOAuthLogin, R as suggestSafelistEntries, Si as stripSpawnTokensLine, St as HOST_INTERACTION_ID_PREFIX, Ti as toolCallPreview, Tt as buildResumedToolResultsTurn, U as fetchProviderUsage, Un as hasClaudeCodeSetup, Ut as createBuiltinCommands, V as clearProviderUsageCache, Vn as useCompletion, Vr as pickActiveRunId, W as providerUsageSupported, Wn as importFromClaudeCode, Wr as selectActiveTodos, Wt as createMainSession, X as redirectUriFromAuthUrl, Xt as parsePluginScanPathEnv, Yn as dedupMcpServersByName, Yt as discoverProjectPlugins, Z as oauthUsesManualCodePaste, Zn as discoverProjectMcps, Zt as attachMcpWarnLog, _ as buildSpawnEndEvent, _i as loadState, _r as groupArtifacts, _t as useMcpAuthState, ai as buildPlanSystem, at as subscribeMcpToolsCache, b as defaultSkillScanPaths, bn as resolveTheme, bt as ASK_USER_TOOL, c as useSelectStyle, ci as createChatTurnsReadTool, cn as DEFAULT_SETTINGS, d as useTheme, dn as SETTINGS_INPUTS, dt as parentServerName, en as EMPTY_HINTS, er as tryRevealPath, f as discoverSystemMd, fi as deriveSessionTitle, fn as SETTINGS_TOGGLES, ft as createFileMcpCredentialStore, g as useStreamBuffer, gi as listSessionMeta, gn as useSettings, gr as chatAutoCompactBehavior, gt as useMcpAuthDispatch, h as userSystemMdPath, hi as lastContextSizeFromTurns, ht as McpAuthProvider, ii as buildBuildSystem, in as buildHints, ir as useUpdateCheck, jn as DiscoveryProvider, jt as pendingInteractionsFromTurns, k as useSafeModeActions, kn as quoteForShell, kt as isModelInteraction, l as useSurfaces, ln as SETTINGS_CATEGORIES, lt as buildVisibleMcpRows, m as userScopeOverridesLoad, mi as getSessionMeta, mn as clampFps, nn as hintsLength, nr as bootTick, nt as mcpToolsCachePath, o as ThemeProvider, oi as envSection, on as listProjectFiles, p as renderSystemMdSeed, pi as eventsFromTurns, pn as SettingsProvider, q as formatPathForCwd, qn as upsertMcpServer, qr as useActiveTodos, qt as runEndNotice, r as computeTurnAnchors, rn as truncateTrailing, rr as buildUpdateHint, rt as refreshMcpToolsCatalog, s as useColors, si as chatReferenceTokensForPrompt, sn as useEnabledToggleSet, st as useMcpToolToggleMap, tn as clipHintsToWidth, tt as loadMcpToolsCache, ui as compactSummaryEvent, un as SETTINGS_CHOICES, ut as indexOfServerRow, v as buildSpawnStartEvent, vi as marginTopFor, vt as getMcpAuthStatus, w as writeSessionExport, wt as PRESENT_PLAN_TOOL, x as discoverProjectSkills, xi as serverToolResultSummary, y as buildSkillsConfig, yi as noticeTurnContent, yn as resolveChipColor, yr as discoverAgentsMd, zn as createArtifactsCompletionProvider } from "./approvals-
|
|
23
|
+
import { $ as supportsOAuth, $n as tryOpenBrowser, A as useSafeModeQueue, An as createDiscoverySlot, At as makeRequestInteraction, Ci as sumRunCosts, Ct as InteractionsProvider, Di as updateToolEventOutcomes, Dr as accentColor, Dt as createInteractionTools, Ei as toolResultText, Et as createAutomaticInteractionResponse, Fn as useConfig, G as usageChecksOptedOut, Gn as mcpsWritePath, Gr as setTodosForRun, H as fetchAllProviderUsage, Hn as countClaudeSessionFiles, Hr as pruneTodosByRun, In as resolveConfig, Ir as applyTodosSnapshot, J as fetchOAuthRedirect, Jn as buildMcpServers, Jt as buildPluginContributions, Kn as removeMcpServer, Kr as todoProgressLabel, Ln as resolveStoragePaths, M as addToSafelist, Mn as useDiscovery, N as getSafelist, Nn as useDiscoveryOptional, Nt as useInteractionsActions, O as SafeModeProvider, Oi as usageByModel, On as parseEditorCommand, P as isOnSafelist, Pn as ConfigProvider, Pr as TODO_STATUS_GLYPHS, Pt as useInteractionsQueue, Q as runOAuthLogin, R as suggestSafelistEntries, Si as stripSpawnTokensLine, St as HOST_INTERACTION_ID_PREFIX, Ti as toolCallPreview, Tt as buildResumedToolResultsTurn, U as fetchProviderUsage, Un as hasClaudeCodeSetup, Ut as createBuiltinCommands, V as clearProviderUsageCache, Vn as useCompletion, Vr as pickActiveRunId, W as providerUsageSupported, Wn as importFromClaudeCode, Wr as selectActiveTodos, Wt as createMainSession, X as redirectUriFromAuthUrl, Xt as parsePluginScanPathEnv, Yn as dedupMcpServersByName, Yt as discoverProjectPlugins, Z as oauthUsesManualCodePaste, Zn as discoverProjectMcps, Zt as attachMcpWarnLog, _ as buildSpawnEndEvent, _i as loadState, _r as groupArtifacts, _t as useMcpAuthState, ai as buildPlanSystem, at as subscribeMcpToolsCache, b as defaultSkillScanPaths, bn as resolveTheme, bt as ASK_USER_TOOL, c as useSelectStyle, ci as createChatTurnsReadTool, cn as DEFAULT_SETTINGS, d as useTheme, dn as SETTINGS_INPUTS, dt as parentServerName, en as EMPTY_HINTS, er as tryRevealPath, f as discoverSystemMd, fi as deriveSessionTitle, fn as SETTINGS_TOGGLES, ft as createFileMcpCredentialStore, g as useStreamBuffer, gi as listSessionMeta, gn as useSettings, gr as chatAutoCompactBehavior, gt as useMcpAuthDispatch, h as userSystemMdPath, hi as lastContextSizeFromTurns, ht as McpAuthProvider, ii as buildBuildSystem, in as buildHints, ir as useUpdateCheck, jn as DiscoveryProvider, jt as pendingInteractionsFromTurns, k as useSafeModeActions, kn as quoteForShell, kt as isModelInteraction, l as useSurfaces, ln as SETTINGS_CATEGORIES, lt as buildVisibleMcpRows, m as userScopeOverridesLoad, mi as getSessionMeta, mn as clampFps, nn as hintsLength, nr as bootTick, nt as mcpToolsCachePath, o as ThemeProvider, oi as envSection, on as listProjectFiles, p as renderSystemMdSeed, pi as eventsFromTurns, pn as SettingsProvider, q as formatPathForCwd, qn as upsertMcpServer, qr as useActiveTodos, qt as runEndNotice, r as computeTurnAnchors, rn as truncateTrailing, rr as buildUpdateHint, rt as refreshMcpToolsCatalog, s as useColors, si as chatReferenceTokensForPrompt, sn as useEnabledToggleSet, st as useMcpToolToggleMap, tn as clipHintsToWidth, tt as loadMcpToolsCache, ui as compactSummaryEvent, un as SETTINGS_CHOICES, ut as indexOfServerRow, v as buildSpawnStartEvent, vi as marginTopFor, vt as getMcpAuthStatus, w as writeSessionExport, wt as PRESENT_PLAN_TOOL, x as discoverProjectSkills, xi as serverToolResultSummary, y as buildSkillsConfig, yi as noticeTurnContent, yn as resolveChipColor, yr as discoverAgentsMd, zn as createArtifactsCompletionProvider } from "./approvals-Deg8JhMI.js";
|
|
24
24
|
import { E as renderDefaultCallbackPage, T as restoreModelOptions, b as modelSupportsReasoning, c as setPreferredCredential, g as getContextWindow, h as enabledModelOptions, l as setProviderCredential, n as applyApiKeyEnv, t as detectAuth, w as piIdOf, y as modelOptionsFor } from "./auth-Nq-tSMb9.js";
|
|
25
|
-
import { a as connectIpcClient, d as defaultSocketPath, m as selfDaemonSpawnCommand, o as connectOrSpawnDaemon, p as probeSocket } from "./server-
|
|
25
|
+
import { a as connectIpcClient, d as defaultSocketPath, m as selfDaemonSpawnCommand, o as connectOrSpawnDaemon, p as probeSocket } from "./server-BlE48mXo.js";
|
|
26
26
|
import { createDockerContext } from "./contexts/docker.js";
|
|
27
27
|
import { createTuiStore } from "./session/sqlite.js";
|
|
28
28
|
import { _ as buildVisibleAuthRows, a as editTodo, b as parentProviderKey, c as NOTICE_GLYPH, f as validateMcpForm, g as instructionPreviewLines, h as instructionOriginBadge, i as cycleTodoStatus, l as MCP_TRANSPORTS, m as buildVisibleInstructionRows, n as buildTodoRetryMessage, r as clearTodos, s as removeTodo, t as addTodo, v as indexOfProviderRow, x as selectableSources } from "./todo-edits-Dw2oO2ON.js";
|
|
@@ -7659,7 +7659,7 @@ function OptionRow({ option, isEnabled, isFocused, highlightBg }) {
|
|
|
7659
7659
|
* away from. Without it such an entry is invisible and re-persists forever.
|
|
7660
7660
|
*/
|
|
7661
7661
|
/** Visible rows in the windowed list. Keeps the modal a stable size on long catalogs. */
|
|
7662
|
-
const VISIBLE_ROWS =
|
|
7662
|
+
const VISIBLE_ROWS = 14;
|
|
7663
7663
|
/**
|
|
7664
7664
|
* Promote selected entries in SELECTION order, then append every unselected
|
|
7665
7665
|
* row in its original catalog order. Pure/non-mutating and collision-safe:
|
|
@@ -7835,7 +7835,7 @@ function ModelPickerModal({ providers, modelsFor, current, onPick, chain, onAppl
|
|
|
7835
7835
|
if (providers.length === 0) return /* @__PURE__ */ jsx$1(EmptyProvidersState, {});
|
|
7836
7836
|
return /* @__PURE__ */ jsxs(Modal, {
|
|
7837
7837
|
title: chainMode ? "select models · order is fallback priority" : "select model",
|
|
7838
|
-
maxWidth:
|
|
7838
|
+
maxWidth: 140,
|
|
7839
7839
|
onClose: handleClose,
|
|
7840
7840
|
children: [
|
|
7841
7841
|
/* @__PURE__ */ jsx$1("box", {
|
|
@@ -8210,12 +8210,12 @@ const METHOD_NOT_FOUND = -32601;
|
|
|
8210
8210
|
* Version-skewed daemons exist by construction (a daemon process is frozen
|
|
8211
8211
|
* at spawn time while windows come from newer builds mid-development).
|
|
8212
8212
|
*/
|
|
8213
|
-
async function optional(call) {
|
|
8213
|
+
async function optional(call, allowClosed = true) {
|
|
8214
8214
|
try {
|
|
8215
8215
|
return await call();
|
|
8216
8216
|
} catch (err) {
|
|
8217
8217
|
if (err instanceof JsonRpcRemoteError && err.code === METHOD_NOT_FOUND) return null;
|
|
8218
|
-
if (err instanceof JsonRpcConnectionClosedError) return null;
|
|
8218
|
+
if (allowClosed && err instanceof JsonRpcConnectionClosedError) return null;
|
|
8219
8219
|
throw err;
|
|
8220
8220
|
}
|
|
8221
8221
|
}
|
|
@@ -8227,12 +8227,15 @@ async function connectDaemonSession(options) {
|
|
|
8227
8227
|
spawnCommand: options.spawnCommand ?? selfDaemonSpawnCommand(),
|
|
8228
8228
|
...options.appVersion ? { appVersion: options.appVersion } : {},
|
|
8229
8229
|
onClose: () => {
|
|
8230
|
-
if (!disposed)
|
|
8230
|
+
if (!disposed) {
|
|
8231
|
+
disposed = true;
|
|
8232
|
+
callbacks.onDisconnect?.();
|
|
8233
|
+
}
|
|
8231
8234
|
}
|
|
8232
8235
|
});
|
|
8233
8236
|
let running = false;
|
|
8234
|
-
/**
|
|
8235
|
-
let
|
|
8237
|
+
/** Either live run-state signal wins over the initial RPC probe. */
|
|
8238
|
+
let sawRunState = false;
|
|
8236
8239
|
/** Sequence for `suggest.resolve` — see `resolveSuggestion`. */
|
|
8237
8240
|
let suggestSeq = 0;
|
|
8238
8241
|
/**
|
|
@@ -8275,6 +8278,7 @@ async function connectDaemonSession(options) {
|
|
|
8275
8278
|
});
|
|
8276
8279
|
};
|
|
8277
8280
|
const unsubscribe = client.onSignal((signal) => {
|
|
8281
|
+
if (disposed) return;
|
|
8278
8282
|
switch (signal.kind) {
|
|
8279
8283
|
case "approval:request":
|
|
8280
8284
|
surfaceApproval(signal.request);
|
|
@@ -8341,10 +8345,11 @@ async function connectDaemonSession(options) {
|
|
|
8341
8345
|
break;
|
|
8342
8346
|
case "run:start":
|
|
8343
8347
|
running = true;
|
|
8344
|
-
|
|
8348
|
+
sawRunState = true;
|
|
8345
8349
|
callbacks.onRunState(true);
|
|
8346
8350
|
break;
|
|
8347
8351
|
case "run:end":
|
|
8352
|
+
sawRunState = true;
|
|
8348
8353
|
running = false;
|
|
8349
8354
|
callbacks.onRunState(false, {
|
|
8350
8355
|
ok: signal.ok,
|
|
@@ -8439,25 +8444,34 @@ async function connectDaemonSession(options) {
|
|
|
8439
8444
|
default: break;
|
|
8440
8445
|
}
|
|
8441
8446
|
});
|
|
8447
|
+
const bootstrapRequest = async (method, params) => {
|
|
8448
|
+
if (disposed) throw new JsonRpcConnectionClosedError();
|
|
8449
|
+
const result = await client.request(method, params);
|
|
8450
|
+
if (disposed) throw new JsonRpcConnectionClosedError();
|
|
8451
|
+
return result;
|
|
8452
|
+
};
|
|
8442
8453
|
try {
|
|
8443
|
-
const initialEvents = await
|
|
8454
|
+
const initialEvents = await bootstrapRequest("sessions.loadEvents", { sessionId });
|
|
8444
8455
|
callbacks.onInitialTranscript(initialEvents);
|
|
8445
8456
|
transcriptReady = true;
|
|
8446
8457
|
for (const event of bufferedEvents.splice(0)) callbacks.onStreamEvent(event);
|
|
8447
|
-
if (!
|
|
8448
|
-
|
|
8449
|
-
if (
|
|
8458
|
+
if (!sawRunState) {
|
|
8459
|
+
const snapshot = await bootstrapRequest("chat.isRunning", { sessionId });
|
|
8460
|
+
if (!sawRunState) {
|
|
8461
|
+
running = snapshot;
|
|
8462
|
+
callbacks.onRunState(running);
|
|
8463
|
+
}
|
|
8450
8464
|
}
|
|
8451
|
-
for (const request of await
|
|
8452
|
-
await
|
|
8453
|
-
for (const pending of await
|
|
8465
|
+
for (const request of await bootstrapRequest("approvals.listPending", {})) surfaceApproval(request);
|
|
8466
|
+
await bootstrapRequest("interactions.rehydrate", { sessionId });
|
|
8467
|
+
for (const pending of await bootstrapRequest("interactions.list", { sessionId })) surfaceInteraction(pending.request);
|
|
8454
8468
|
client.notify("presence.watch", { sessionId });
|
|
8455
8469
|
const [presence, queued, draft, systemMd, todos] = await Promise.all([
|
|
8456
|
-
optional(() =>
|
|
8457
|
-
optional(() =>
|
|
8458
|
-
optional(() =>
|
|
8459
|
-
optional(() =>
|
|
8460
|
-
optional(() =>
|
|
8470
|
+
optional(() => bootstrapRequest("presence.list", {}), false),
|
|
8471
|
+
optional(() => bootstrapRequest("queue.list", { sessionId }), false),
|
|
8472
|
+
optional(() => bootstrapRequest("drafts.get", { sessionId }), false),
|
|
8473
|
+
optional(() => bootstrapRequest("sessions.systemMd", { sessionId }), false),
|
|
8474
|
+
optional(() => bootstrapRequest("todos.get", { sessionId }), false)
|
|
8461
8475
|
]);
|
|
8462
8476
|
if (presence) callbacks.onPresenceChanged?.(presence);
|
|
8463
8477
|
if (queued && queued.length > 0) callbacks.onQueueChanged?.(queued);
|
|
@@ -8470,6 +8484,7 @@ async function connectDaemonSession(options) {
|
|
|
8470
8484
|
callbacks.onSystemMd(systemMd?.file ?? null, systemMd?.shadowed ?? []);
|
|
8471
8485
|
if (todos) callbacks.onTodosChanged?.(todos);
|
|
8472
8486
|
} catch (err) {
|
|
8487
|
+
disposed = true;
|
|
8473
8488
|
unsubscribe();
|
|
8474
8489
|
client.close();
|
|
8475
8490
|
throw err;
|
|
@@ -8649,6 +8664,89 @@ async function connectDaemonSession(options) {
|
|
|
8649
8664
|
};
|
|
8650
8665
|
}
|
|
8651
8666
|
//#endregion
|
|
8667
|
+
//#region src/tui/runtime/session-connection.ts
|
|
8668
|
+
function guardConnectionCallbacks(attempt, callbacks) {
|
|
8669
|
+
const guarded = { ...callbacks };
|
|
8670
|
+
for (const key of Object.keys(callbacks)) guarded[key] = attempt.guard(callbacks[key]);
|
|
8671
|
+
return guarded;
|
|
8672
|
+
}
|
|
8673
|
+
/** Owns one activation and its transport attempts; absence of a controller is never local mode. */
|
|
8674
|
+
var SessionConnection = class {
|
|
8675
|
+
schedule;
|
|
8676
|
+
generation = 0;
|
|
8677
|
+
cancelRetry;
|
|
8678
|
+
controller;
|
|
8679
|
+
canSend = false;
|
|
8680
|
+
constructor(schedule = (run, delay) => {
|
|
8681
|
+
const timer = setTimeout(run, delay);
|
|
8682
|
+
return () => clearTimeout(timer);
|
|
8683
|
+
}) {
|
|
8684
|
+
this.schedule = schedule;
|
|
8685
|
+
}
|
|
8686
|
+
dispose() {
|
|
8687
|
+
this.generation++;
|
|
8688
|
+
this.canSend = false;
|
|
8689
|
+
this.cancelRetry?.();
|
|
8690
|
+
this.cancelRetry = void 0;
|
|
8691
|
+
const controller = this.controller;
|
|
8692
|
+
this.controller = void 0;
|
|
8693
|
+
controller?.dispose();
|
|
8694
|
+
}
|
|
8695
|
+
begin() {
|
|
8696
|
+
this.dispose();
|
|
8697
|
+
const generation = this.generation;
|
|
8698
|
+
const isCurrent = () => generation === this.generation;
|
|
8699
|
+
return {
|
|
8700
|
+
isCurrent,
|
|
8701
|
+
localReady: () => {
|
|
8702
|
+
if (isCurrent()) this.canSend = true;
|
|
8703
|
+
},
|
|
8704
|
+
connect: async (options) => {
|
|
8705
|
+
let failures = 0;
|
|
8706
|
+
const run = async () => {
|
|
8707
|
+
if (!isCurrent()) return;
|
|
8708
|
+
let live = true;
|
|
8709
|
+
const current = () => isCurrent() && live;
|
|
8710
|
+
const disconnect = (error) => {
|
|
8711
|
+
if (!current()) return;
|
|
8712
|
+
live = false;
|
|
8713
|
+
this.canSend = false;
|
|
8714
|
+
const controller = this.controller;
|
|
8715
|
+
this.controller = void 0;
|
|
8716
|
+
controller?.dispose();
|
|
8717
|
+
options.cleanup?.();
|
|
8718
|
+
options.unavailable(error);
|
|
8719
|
+
this.cancelRetry = this.schedule(() => {
|
|
8720
|
+
this.cancelRetry = void 0;
|
|
8721
|
+
run();
|
|
8722
|
+
}, Math.min(1500 * 2 ** Math.min(failures++, 3), 12e3));
|
|
8723
|
+
};
|
|
8724
|
+
try {
|
|
8725
|
+
const controller = await options.open({
|
|
8726
|
+
isCurrent: current,
|
|
8727
|
+
guard: (callback) => (...args) => {
|
|
8728
|
+
if (current()) callback(...args);
|
|
8729
|
+
},
|
|
8730
|
+
disconnect
|
|
8731
|
+
});
|
|
8732
|
+
if (!current()) {
|
|
8733
|
+
controller.dispose();
|
|
8734
|
+
return;
|
|
8735
|
+
}
|
|
8736
|
+
this.controller = controller;
|
|
8737
|
+
failures = 0;
|
|
8738
|
+
options.ready(controller);
|
|
8739
|
+
this.canSend = true;
|
|
8740
|
+
} catch (error) {
|
|
8741
|
+
disconnect(error);
|
|
8742
|
+
}
|
|
8743
|
+
};
|
|
8744
|
+
await run();
|
|
8745
|
+
}
|
|
8746
|
+
};
|
|
8747
|
+
}
|
|
8748
|
+
};
|
|
8749
|
+
//#endregion
|
|
8652
8750
|
//#region src/chat/external-editor.ts
|
|
8653
8751
|
/**
|
|
8654
8752
|
* External-editor handoff — open a piece of text in the user's `$EDITOR`,
|
|
@@ -22504,6 +22602,7 @@ function AppShell() {
|
|
|
22504
22602
|
*/
|
|
22505
22603
|
const activateSessionRef = useRef(async () => {});
|
|
22506
22604
|
const activationGenerationRef = useRef(0);
|
|
22605
|
+
const connectionRef = useRef(new SessionConnection());
|
|
22507
22606
|
useEffect(() => {
|
|
22508
22607
|
const daemon = daemonRef.current;
|
|
22509
22608
|
if (!daemon || !picked) return;
|
|
@@ -24303,14 +24402,14 @@ function AppShell() {
|
|
|
24303
24402
|
refreshSessionsRef.current = refreshSessions;
|
|
24304
24403
|
refreshSessionsForArtifactsRef.current = refreshSessions;
|
|
24305
24404
|
const teardown = useCallback(async () => {
|
|
24405
|
+
const teardownGeneration = ++activationGenerationRef.current;
|
|
24306
24406
|
try {
|
|
24307
|
-
|
|
24407
|
+
connectionRef.current.dispose();
|
|
24308
24408
|
} catch (err) {
|
|
24309
24409
|
debugLog("teardown: daemon dispose failed", err);
|
|
24310
24410
|
}
|
|
24311
24411
|
daemonRef.current = null;
|
|
24312
24412
|
setDaemonConnected(false);
|
|
24313
|
-
activationGenerationRef.current += 1;
|
|
24314
24413
|
setRemoteDraft(null);
|
|
24315
24414
|
setRemoteDraftAttachments(null);
|
|
24316
24415
|
setRemoteSuggest(null);
|
|
@@ -24348,10 +24447,12 @@ function AppShell() {
|
|
|
24348
24447
|
transcriptRevealHoldRef.current = false;
|
|
24349
24448
|
stream.reset();
|
|
24350
24449
|
await agentRef.current?.destroy().catch((err) => debugLog("agent.destroy failed", err));
|
|
24450
|
+
if (activationGenerationRef.current !== teardownGeneration) return;
|
|
24351
24451
|
agentRef.current = null;
|
|
24352
24452
|
sessionRef.current = null;
|
|
24353
24453
|
if (warnLogRef.current) {
|
|
24354
24454
|
await warnLogRef.current.close().catch((err) => debugLog("teardown: warnLog.close failed", err));
|
|
24455
|
+
if (activationGenerationRef.current !== teardownGeneration) return;
|
|
24355
24456
|
warnLogRef.current = null;
|
|
24356
24457
|
}
|
|
24357
24458
|
setCompacting(false);
|
|
@@ -24477,10 +24578,12 @@ function AppShell() {
|
|
|
24477
24578
|
}
|
|
24478
24579
|
}, [stream, autoCompactBehaviorFor]);
|
|
24479
24580
|
const activateSession = useCallback(async (id, key, opts) => {
|
|
24480
|
-
|
|
24481
|
-
|
|
24482
|
-
|
|
24581
|
+
const cleanup = teardown();
|
|
24582
|
+
const activation = connectionRef.current.begin();
|
|
24583
|
+
await cleanup;
|
|
24584
|
+
if (!activation.isCurrent()) return;
|
|
24483
24585
|
const loaded = id ? await loadSession(store, id) : null;
|
|
24586
|
+
if (!activation.isCurrent()) return;
|
|
24484
24587
|
const freshProfile = loaded ? null : defaultAgentProfile();
|
|
24485
24588
|
const session = loaded ?? await createSession({
|
|
24486
24589
|
store,
|
|
@@ -24489,6 +24592,7 @@ function AppShell() {
|
|
|
24489
24592
|
...id ? { id } : {},
|
|
24490
24593
|
...opts?.metadata ? { metadata: opts.metadata } : {}
|
|
24491
24594
|
});
|
|
24595
|
+
if (!activation.isCurrent()) return;
|
|
24492
24596
|
if (freshProfile && freshProfile.id !== pickedAgentRef.current.id) {
|
|
24493
24597
|
pickedAgentRef.current = freshProfile;
|
|
24494
24598
|
setPickedAgent(freshProfile);
|
|
@@ -24506,370 +24610,372 @@ function AppShell() {
|
|
|
24506
24610
|
stampedLegacyAgent = true;
|
|
24507
24611
|
}
|
|
24508
24612
|
}
|
|
24509
|
-
|
|
24510
|
-
|
|
24511
|
-
|
|
24512
|
-
|
|
24513
|
-
|
|
24514
|
-
|
|
24515
|
-
|
|
24516
|
-
|
|
24517
|
-
|
|
24518
|
-
|
|
24519
|
-
|
|
24520
|
-
|
|
24521
|
-
|
|
24522
|
-
if (activePick
|
|
24523
|
-
|
|
24524
|
-
|
|
24525
|
-
|
|
24526
|
-
|
|
24527
|
-
|
|
24528
|
-
|
|
24529
|
-
|
|
24530
|
-
|
|
24531
|
-
]);
|
|
24532
|
-
} else lastPushedModelRef.current = null;
|
|
24533
|
-
const sessionId = session.id;
|
|
24534
|
-
const refreshHeaderFromStore = () => {
|
|
24535
|
-
store.load(sessionId).then((data) => {
|
|
24536
|
-
if (!data || sessionRef.current?.id !== sessionId) return;
|
|
24537
|
-
setCurrentSession({
|
|
24538
|
-
id: data.id,
|
|
24539
|
-
title: deriveSessionTitle(data.turns, data.metadata),
|
|
24540
|
-
turnCount: data.turns.length,
|
|
24541
|
-
userMessageCount: data.turns.reduce((n, t) => t.role === "user" ? n + 1 : n, 0),
|
|
24542
|
-
runCount: data.runs.length,
|
|
24543
|
-
updatedAt: data.updatedAt,
|
|
24544
|
-
...data.projectRoot ? { projectRoot: data.projectRoot } : {}
|
|
24545
|
-
});
|
|
24546
|
-
}).catch(() => void 0);
|
|
24547
|
-
};
|
|
24548
|
-
let controllerHandle = null;
|
|
24549
|
-
const reflectRemoteModel = () => {
|
|
24550
|
-
store.load(sessionId).then((data) => {
|
|
24551
|
-
if (!data || sessionRef.current?.id !== sessionId) return;
|
|
24552
|
-
const providerKey = data.metadata.providerKey;
|
|
24553
|
-
const modelId = data.metadata.modelId;
|
|
24554
|
-
if (typeof providerKey !== "string" || typeof modelId !== "string") return;
|
|
24555
|
-
const auth = availableProvidersRef.current.find((p) => p.key === providerKey);
|
|
24556
|
-
if (!auth) return;
|
|
24557
|
-
const next = makePicked(auth, modelId);
|
|
24558
|
-
if (!next) return;
|
|
24559
|
-
const effort = data.metadata.effort;
|
|
24560
|
-
const modelOptions = data.metadata.modelOptions;
|
|
24561
|
-
const merged = {
|
|
24562
|
-
...next,
|
|
24563
|
-
...typeof effort === "string" ? { effort } : {},
|
|
24564
|
-
...modelOptions && typeof modelOptions === "object" ? { modelOptions } : {}
|
|
24565
|
-
};
|
|
24566
|
-
const tuple = JSON.stringify([
|
|
24567
|
-
merged.provider.key,
|
|
24568
|
-
merged.model,
|
|
24569
|
-
merged.effort ?? null,
|
|
24570
|
-
merged.modelOptions ?? null
|
|
24571
|
-
]);
|
|
24572
|
-
if (lastPushedModelRef.current === tuple) return;
|
|
24573
|
-
lastPushedModelRef.current = tuple;
|
|
24574
|
-
setPicked(merged);
|
|
24575
|
-
}).catch(() => void 0);
|
|
24576
|
-
};
|
|
24577
|
-
const reflectRemoteAgent = () => {
|
|
24578
|
-
(store.getMeta ? store.getMeta(sessionId) : store.load(sessionId)).then((meta) => {
|
|
24579
|
-
if (!meta || sessionRef.current?.id !== sessionId) return;
|
|
24580
|
-
const remote = meta.agentId;
|
|
24581
|
-
if (!remote || remote === pickedAgentRef.current.id) return;
|
|
24582
|
-
sessionRef.current.setAgentId(remote);
|
|
24583
|
-
const profile = liveAgentRegistryRef.current[remote];
|
|
24584
|
-
if (!profile) return;
|
|
24585
|
-
pickedAgentRef.current = profile;
|
|
24586
|
-
setPickedAgent(profile);
|
|
24587
|
-
}).catch(() => void 0);
|
|
24588
|
-
};
|
|
24589
|
-
const resyncTranscript = () => {
|
|
24590
|
-
controllerHandle?.fetchTranscript().then(async (canonical) => {
|
|
24591
|
-
if (!canonical || sessionRef.current?.id !== sessionId || runningRef.current) return;
|
|
24592
|
-
await streamRef.current?.whenDrained();
|
|
24593
|
-
if (sessionRef.current?.id !== sessionId || runningRef.current) return;
|
|
24594
|
-
streamRef.current?.reset();
|
|
24595
|
-
setEvents(capTranscriptEvents(canonical));
|
|
24596
|
-
}).catch(() => void 0);
|
|
24597
|
-
};
|
|
24598
|
-
const controller = await connectDaemonSession({
|
|
24599
|
-
sessionId,
|
|
24600
|
-
appVersion: "tui",
|
|
24601
|
-
callbacks: {
|
|
24602
|
-
onRetry: ({ attempt, max, source }) => setRetryInfo({
|
|
24603
|
-
attempt,
|
|
24604
|
-
max,
|
|
24605
|
-
source
|
|
24606
|
-
}),
|
|
24607
|
-
onRetryCleared: (source) => clearRetryInfo(source),
|
|
24608
|
-
onPromptTurns: (refs) => {
|
|
24609
|
-
stream.flushAndUpdate((events) => retagUserPromptEventsFromRefs(events, refs));
|
|
24610
|
-
},
|
|
24611
|
-
onCompactionProgress: (tokens) => setCompactionTokens(tokens),
|
|
24612
|
-
onStreamProgress: (progress) => setStreamProgress(progress),
|
|
24613
|
-
onModelFallback: (from, to) => {
|
|
24614
|
-
setActiveFallback({
|
|
24615
|
-
provider: to.provider,
|
|
24616
|
-
model: to.model
|
|
24617
|
-
});
|
|
24618
|
-
stream.appendImmediate({
|
|
24619
|
-
kind: "info",
|
|
24620
|
-
text: `model fallback: ${from.provider}/${from.model} failed, continuing on ${to.provider}/${to.model}`
|
|
24621
|
-
});
|
|
24622
|
-
},
|
|
24623
|
-
onCacheBreak: (info) => {
|
|
24624
|
-
const providerKey = pickedRef.current?.provider.key;
|
|
24625
|
-
const apply = (costUsd) => {
|
|
24626
|
-
setActiveCacheBreak(costUsd !== void 0 ? {
|
|
24627
|
-
...info,
|
|
24628
|
-
costUsd
|
|
24629
|
-
} : info);
|
|
24630
|
-
};
|
|
24631
|
-
if (!providerKey) {
|
|
24632
|
-
apply(void 0);
|
|
24633
|
-
return;
|
|
24634
|
-
}
|
|
24635
|
-
estimateRebilledCostUsd(providerKey, info.model, info.rebilledTokens).then(apply).catch(() => apply(void 0));
|
|
24636
|
-
},
|
|
24637
|
-
onSystemMd: (file) => {
|
|
24638
|
-
sessionSystemFileRef.current = file;
|
|
24639
|
-
announceProjectSystemOverride();
|
|
24640
|
-
},
|
|
24641
|
-
onSettingsChanged: (remote) => {
|
|
24642
|
-
const local = settingsRef.current;
|
|
24643
|
-
const entries = Object.entries(remote).filter(([key, value]) => key in DEFAULT_SETTINGS && JSON.stringify(local[key]) !== JSON.stringify(value));
|
|
24644
|
-
if (entries.length === 0) return;
|
|
24645
|
-
suppressSettingsPublishRef.current = true;
|
|
24646
|
-
for (const [key, value] of entries) setSetting(key, value);
|
|
24647
|
-
},
|
|
24648
|
-
onSessionsChanged: (op, changedId) => {
|
|
24649
|
-
if (op === "update" && changedId === sessionId) {
|
|
24650
|
-
refreshHeaderFromStore();
|
|
24651
|
-
resyncTranscript();
|
|
24652
|
-
reflectRemoteModel();
|
|
24653
|
-
reflectRemoteAgent();
|
|
24654
|
-
}
|
|
24655
|
-
},
|
|
24656
|
-
onDraftChanged: (text, shell) => {
|
|
24657
|
-
draftSeqRef.current += 1;
|
|
24658
|
-
setRemoteDraft({
|
|
24659
|
-
seq: draftSeqRef.current,
|
|
24660
|
-
text,
|
|
24661
|
-
...shell ? { shell: true } : {}
|
|
24662
|
-
});
|
|
24663
|
-
},
|
|
24664
|
-
onSuggestChanged: ({ basis, caret, suggestion, candidates }) => {
|
|
24665
|
-
remoteSuggestSeqRef.current += 1;
|
|
24666
|
-
setRemoteSuggest({
|
|
24667
|
-
seq: remoteSuggestSeqRef.current,
|
|
24668
|
-
basis,
|
|
24669
|
-
caret,
|
|
24670
|
-
suggestion,
|
|
24671
|
-
...candidates ? { candidates } : {}
|
|
24672
|
-
});
|
|
24673
|
-
},
|
|
24674
|
-
onSearchChanged: ({ active, query }) => {
|
|
24675
|
-
setTranscriptSearchMode(active);
|
|
24676
|
-
setTranscriptSearchQuery(query);
|
|
24677
|
-
if (!active) setSelectedTurnId(null);
|
|
24678
|
-
remoteSearchSeqRef.current += 1;
|
|
24679
|
-
setRemoteSearchQuery(active ? {
|
|
24680
|
-
seq: remoteSearchSeqRef.current,
|
|
24681
|
-
query
|
|
24682
|
-
} : null);
|
|
24683
|
-
},
|
|
24684
|
-
onDraftAttachmentsChanged: (attachments) => {
|
|
24685
|
-
draftAttachmentsSeqRef.current += 1;
|
|
24686
|
-
setRemoteDraftAttachments({
|
|
24687
|
-
seq: draftAttachmentsSeqRef.current,
|
|
24688
|
-
attachments: attachments.map((att) => ({
|
|
24689
|
-
name: att.name ?? "attachment",
|
|
24690
|
-
mediaType: att.mediaType,
|
|
24691
|
-
content: Buffer.from(att.data, "base64")
|
|
24692
|
-
}))
|
|
24693
|
-
});
|
|
24694
|
-
},
|
|
24695
|
-
onSkillsChanged: (active) => {
|
|
24696
|
-
setActiveSkillNames(new Set(active));
|
|
24697
|
-
},
|
|
24698
|
-
onMcpAuth: ({ name, reason }) => {
|
|
24699
|
-
dispatchAuthRef.current({
|
|
24700
|
-
type: "auth-required",
|
|
24701
|
-
name,
|
|
24702
|
-
reason: reason === "auto-promoted" ? "auto-promoted" : "no-tokens"
|
|
24703
|
-
});
|
|
24704
|
-
},
|
|
24705
|
-
onHudState: (state) => {
|
|
24706
|
-
setRemoteHudState(state);
|
|
24707
|
-
},
|
|
24708
|
-
onHudNotify: (n) => {
|
|
24709
|
-
const allow = enabledExtensionsRef.current;
|
|
24710
|
-
if (allow !== void 0 && !allow.includes(n.extension)) return;
|
|
24711
|
-
stream.appendImmediate({
|
|
24712
|
-
kind: n.tone === "error" ? "error" : "info",
|
|
24713
|
-
text: `${n.extension}: ${n.tone === "warn" ? "⚠ " : ""}${n.text}`
|
|
24714
|
-
});
|
|
24715
|
-
},
|
|
24716
|
-
onExtensionSignal: (envelope) => {
|
|
24717
|
-
getTuiBus()?.deliver(envelope);
|
|
24718
|
-
},
|
|
24719
|
-
onTurnEnd: ({ usage, childId }) => {
|
|
24720
|
-
if (childId === void 0) setStreamProgress(null);
|
|
24721
|
-
if (childId !== void 0) stream.flushAndUpdate((prev) => finalizeStreamingMarkdownForOwner(prev, childId));
|
|
24722
|
-
if (!usage) return;
|
|
24723
|
-
if (childId === void 0) {
|
|
24724
|
-
lastFinishReasonRef.current = usage.finishReason;
|
|
24725
|
-
const tokens = turnContextSize(usage);
|
|
24726
|
-
if (tokens > 0) {
|
|
24727
|
-
setLastInputTokens(tokens);
|
|
24728
|
-
lastInputTokensRef.current = tokens;
|
|
24729
|
-
}
|
|
24730
|
-
}
|
|
24731
|
-
const turnCost = usage.cost ?? 0;
|
|
24732
|
-
if (turnCost > 0) setSessionCost((prev) => prev + turnCost);
|
|
24733
|
-
},
|
|
24734
|
-
onArtifactRecorded: () => notifyArtifactsChanged(),
|
|
24735
|
-
onArtifactChanged: () => notifyArtifactsChanged(),
|
|
24736
|
-
onTodosChanged: (snapshot) => {
|
|
24737
|
-
todosApplySeqRef.current += 1;
|
|
24738
|
-
const seq = todosApplySeqRef.current;
|
|
24739
|
-
const open = sessionRef.current;
|
|
24740
|
-
if (open?.id === sessionId && pickActiveRunId(open) === snapshot.runId) {
|
|
24741
|
-
applyTodosSnapshot(open, snapshot);
|
|
24742
|
-
notifyTodosChanged();
|
|
24743
|
-
return;
|
|
24613
|
+
const useDaemon = settingsRef.current.useDaemon && !isDaemonDisabledByEnv();
|
|
24614
|
+
if (useDaemon) {
|
|
24615
|
+
setDaemonHealth("connecting");
|
|
24616
|
+
await activation.connect({
|
|
24617
|
+
open: async (attempt) => {
|
|
24618
|
+
agentRef.current = null;
|
|
24619
|
+
if (!loaded && typeof session.metadata.cwd !== "string") {
|
|
24620
|
+
session.setMeta("cwd", cwdRef.current ?? process.cwd());
|
|
24621
|
+
await session.save();
|
|
24622
|
+
if (!attempt.isCurrent()) throw new Error("activation superseded");
|
|
24623
|
+
}
|
|
24624
|
+
const activePick = pickedRef.current;
|
|
24625
|
+
let seedModelViaRpc = false;
|
|
24626
|
+
if (activePick) {
|
|
24627
|
+
if (typeof session.metadata.providerKey !== "string") if (loaded) seedModelViaRpc = true;
|
|
24628
|
+
else {
|
|
24629
|
+
session.setMeta("providerKey", activePick.provider.key);
|
|
24630
|
+
session.setMeta("modelId", activePick.model);
|
|
24631
|
+
if (activePick.effort) session.setMeta("effort", activePick.effort);
|
|
24632
|
+
if (activePick.modelOptions) session.setMeta("modelOptions", activePick.modelOptions);
|
|
24633
|
+
await session.save();
|
|
24634
|
+
if (!attempt.isCurrent()) throw new Error("activation superseded");
|
|
24744
24635
|
}
|
|
24745
|
-
|
|
24746
|
-
|
|
24747
|
-
|
|
24748
|
-
|
|
24749
|
-
|
|
24750
|
-
|
|
24751
|
-
|
|
24636
|
+
lastPushedModelRef.current = JSON.stringify([
|
|
24637
|
+
activePick.provider.key,
|
|
24638
|
+
activePick.model,
|
|
24639
|
+
activePick.effort ?? null,
|
|
24640
|
+
activePick.modelOptions ?? null
|
|
24641
|
+
]);
|
|
24642
|
+
} else lastPushedModelRef.current = null;
|
|
24643
|
+
if (!attempt.isCurrent()) throw new Error("activation superseded");
|
|
24644
|
+
const sessionId = session.id;
|
|
24645
|
+
const refreshHeaderFromStore = () => {
|
|
24646
|
+
store.load(sessionId).then((data) => {
|
|
24647
|
+
if (!attempt.isCurrent() || !data || sessionRef.current?.id !== sessionId) return;
|
|
24648
|
+
setCurrentSession({
|
|
24649
|
+
id: data.id,
|
|
24650
|
+
title: deriveSessionTitle(data.turns, data.metadata),
|
|
24651
|
+
turnCount: data.turns.length,
|
|
24652
|
+
userMessageCount: data.turns.reduce((n, t) => t.role === "user" ? n + 1 : n, 0),
|
|
24653
|
+
runCount: data.runs.length,
|
|
24654
|
+
updatedAt: data.updatedAt,
|
|
24655
|
+
...data.projectRoot ? { projectRoot: data.projectRoot } : {}
|
|
24656
|
+
});
|
|
24752
24657
|
}).catch(() => void 0);
|
|
24753
|
-
}
|
|
24754
|
-
|
|
24755
|
-
|
|
24756
|
-
|
|
24757
|
-
|
|
24758
|
-
|
|
24759
|
-
|
|
24760
|
-
|
|
24761
|
-
|
|
24762
|
-
|
|
24763
|
-
|
|
24764
|
-
|
|
24765
|
-
|
|
24766
|
-
|
|
24767
|
-
|
|
24768
|
-
|
|
24769
|
-
|
|
24770
|
-
|
|
24771
|
-
|
|
24772
|
-
|
|
24773
|
-
|
|
24774
|
-
|
|
24775
|
-
|
|
24776
|
-
|
|
24777
|
-
|
|
24778
|
-
|
|
24779
|
-
|
|
24780
|
-
|
|
24781
|
-
|
|
24782
|
-
|
|
24783
|
-
|
|
24784
|
-
|
|
24785
|
-
|
|
24786
|
-
|
|
24787
|
-
|
|
24788
|
-
|
|
24789
|
-
|
|
24790
|
-
|
|
24791
|
-
|
|
24792
|
-
|
|
24793
|
-
|
|
24794
|
-
|
|
24795
|
-
|
|
24796
|
-
|
|
24797
|
-
|
|
24798
|
-
|
|
24799
|
-
|
|
24800
|
-
|
|
24801
|
-
|
|
24802
|
-
|
|
24803
|
-
|
|
24804
|
-
|
|
24805
|
-
|
|
24806
|
-
|
|
24807
|
-
|
|
24808
|
-
|
|
24809
|
-
|
|
24810
|
-
|
|
24811
|
-
|
|
24812
|
-
|
|
24813
|
-
|
|
24814
|
-
|
|
24815
|
-
|
|
24816
|
-
|
|
24817
|
-
|
|
24818
|
-
|
|
24819
|
-
|
|
24820
|
-
|
|
24821
|
-
|
|
24822
|
-
|
|
24823
|
-
|
|
24824
|
-
runningRef.current = false;
|
|
24825
|
-
setBusy(false);
|
|
24826
|
-
setDaemonHealth("connecting");
|
|
24827
|
-
stream.appendImmediate({
|
|
24828
|
-
kind: "info",
|
|
24829
|
-
text: "daemon connection lost, reconnecting"
|
|
24830
|
-
});
|
|
24831
|
-
setTimeout(() => {
|
|
24832
|
-
if (sessionRef.current?.id === sessionId && settingsRef.current.useDaemon && daemonRef.current === controllerHandle) activateSessionRef.current(sessionId, pickedRef.current?.provider.key ?? "anthropic").catch(() => {
|
|
24658
|
+
};
|
|
24659
|
+
let controllerHandle = null;
|
|
24660
|
+
const reflectRemoteModel = () => {
|
|
24661
|
+
store.load(sessionId).then((data) => {
|
|
24662
|
+
if (!attempt.isCurrent() || !data || sessionRef.current?.id !== sessionId) return;
|
|
24663
|
+
const providerKey = data.metadata.providerKey;
|
|
24664
|
+
const modelId = data.metadata.modelId;
|
|
24665
|
+
if (typeof providerKey !== "string" || typeof modelId !== "string") return;
|
|
24666
|
+
const auth = availableProvidersRef.current.find((p) => p.key === providerKey);
|
|
24667
|
+
if (!auth) return;
|
|
24668
|
+
const next = makePicked(auth, modelId);
|
|
24669
|
+
if (!next) return;
|
|
24670
|
+
const effort = data.metadata.effort;
|
|
24671
|
+
const modelOptions = data.metadata.modelOptions;
|
|
24672
|
+
const merged = {
|
|
24673
|
+
...next,
|
|
24674
|
+
...typeof effort === "string" ? { effort } : {},
|
|
24675
|
+
...modelOptions && typeof modelOptions === "object" ? { modelOptions } : {}
|
|
24676
|
+
};
|
|
24677
|
+
const tuple = JSON.stringify([
|
|
24678
|
+
merged.provider.key,
|
|
24679
|
+
merged.model,
|
|
24680
|
+
merged.effort ?? null,
|
|
24681
|
+
merged.modelOptions ?? null
|
|
24682
|
+
]);
|
|
24683
|
+
if (lastPushedModelRef.current === tuple) return;
|
|
24684
|
+
lastPushedModelRef.current = tuple;
|
|
24685
|
+
setPicked(merged);
|
|
24686
|
+
}).catch(() => void 0);
|
|
24687
|
+
};
|
|
24688
|
+
const reflectRemoteAgent = () => {
|
|
24689
|
+
(store.getMeta ? store.getMeta(sessionId) : store.load(sessionId)).then((meta) => {
|
|
24690
|
+
if (!attempt.isCurrent() || !meta || sessionRef.current?.id !== sessionId) return;
|
|
24691
|
+
const remote = meta.agentId;
|
|
24692
|
+
if (!remote || remote === pickedAgentRef.current.id) return;
|
|
24693
|
+
sessionRef.current.setAgentId(remote);
|
|
24694
|
+
const profile = liveAgentRegistryRef.current[remote];
|
|
24695
|
+
if (!profile) return;
|
|
24696
|
+
pickedAgentRef.current = profile;
|
|
24697
|
+
setPickedAgent(profile);
|
|
24698
|
+
}).catch(() => void 0);
|
|
24699
|
+
};
|
|
24700
|
+
const resyncTranscript = () => {
|
|
24701
|
+
controllerHandle?.fetchTranscript().then(async (canonical) => {
|
|
24702
|
+
if (!attempt.isCurrent() || !canonical || sessionRef.current?.id !== sessionId || runningRef.current) return;
|
|
24703
|
+
await streamRef.current?.whenDrained();
|
|
24704
|
+
if (!attempt.isCurrent() || sessionRef.current?.id !== sessionId || runningRef.current) return;
|
|
24705
|
+
streamRef.current?.reset();
|
|
24706
|
+
setEvents(capTranscriptEvents(canonical));
|
|
24707
|
+
}).catch(() => void 0);
|
|
24708
|
+
};
|
|
24709
|
+
const controller = await connectDaemonSession({
|
|
24710
|
+
sessionId,
|
|
24711
|
+
appVersion: "tui",
|
|
24712
|
+
callbacks: guardConnectionCallbacks(attempt, {
|
|
24713
|
+
onRetry: ({ attempt, max, source }) => setRetryInfo({
|
|
24714
|
+
attempt,
|
|
24715
|
+
max,
|
|
24716
|
+
source
|
|
24717
|
+
}),
|
|
24718
|
+
onRetryCleared: (source) => clearRetryInfo(source),
|
|
24719
|
+
onPromptTurns: (refs) => {
|
|
24720
|
+
stream.flushAndUpdate((events) => retagUserPromptEventsFromRefs(events, refs));
|
|
24721
|
+
},
|
|
24722
|
+
onCompactionProgress: (tokens) => setCompactionTokens(tokens),
|
|
24723
|
+
onStreamProgress: (progress) => setStreamProgress(progress),
|
|
24724
|
+
onModelFallback: (from, to) => {
|
|
24725
|
+
setActiveFallback({
|
|
24726
|
+
provider: to.provider,
|
|
24727
|
+
model: to.model
|
|
24728
|
+
});
|
|
24833
24729
|
stream.appendImmediate({
|
|
24730
|
+
kind: "info",
|
|
24731
|
+
text: `model fallback: ${from.provider}/${from.model} failed, continuing on ${to.provider}/${to.model}`
|
|
24732
|
+
});
|
|
24733
|
+
},
|
|
24734
|
+
onCacheBreak: (info) => {
|
|
24735
|
+
const providerKey = pickedRef.current?.provider.key;
|
|
24736
|
+
const apply = (costUsd) => {
|
|
24737
|
+
if (!attempt.isCurrent()) return;
|
|
24738
|
+
setActiveCacheBreak(costUsd !== void 0 ? {
|
|
24739
|
+
...info,
|
|
24740
|
+
costUsd
|
|
24741
|
+
} : info);
|
|
24742
|
+
};
|
|
24743
|
+
if (!providerKey) {
|
|
24744
|
+
apply(void 0);
|
|
24745
|
+
return;
|
|
24746
|
+
}
|
|
24747
|
+
estimateRebilledCostUsd(providerKey, info.model, info.rebilledTokens).then(apply).catch(() => apply(void 0));
|
|
24748
|
+
},
|
|
24749
|
+
onSystemMd: (file) => {
|
|
24750
|
+
sessionSystemFileRef.current = file;
|
|
24751
|
+
announceProjectSystemOverride();
|
|
24752
|
+
},
|
|
24753
|
+
onSettingsChanged: (remote) => {
|
|
24754
|
+
const local = settingsRef.current;
|
|
24755
|
+
const entries = Object.entries(remote).filter(([key, value]) => key in DEFAULT_SETTINGS && JSON.stringify(local[key]) !== JSON.stringify(value));
|
|
24756
|
+
if (entries.length === 0) return;
|
|
24757
|
+
suppressSettingsPublishRef.current = true;
|
|
24758
|
+
for (const [key, value] of entries) setSetting(key, value);
|
|
24759
|
+
},
|
|
24760
|
+
onSessionsChanged: (op, changedId) => {
|
|
24761
|
+
if (op === "update" && changedId === sessionId) {
|
|
24762
|
+
refreshHeaderFromStore();
|
|
24763
|
+
resyncTranscript();
|
|
24764
|
+
reflectRemoteModel();
|
|
24765
|
+
reflectRemoteAgent();
|
|
24766
|
+
}
|
|
24767
|
+
},
|
|
24768
|
+
onDraftChanged: (text, shell) => {
|
|
24769
|
+
draftSeqRef.current += 1;
|
|
24770
|
+
setRemoteDraft({
|
|
24771
|
+
seq: draftSeqRef.current,
|
|
24772
|
+
text,
|
|
24773
|
+
...shell ? { shell: true } : {}
|
|
24774
|
+
});
|
|
24775
|
+
},
|
|
24776
|
+
onSuggestChanged: ({ basis, caret, suggestion, candidates }) => {
|
|
24777
|
+
remoteSuggestSeqRef.current += 1;
|
|
24778
|
+
setRemoteSuggest({
|
|
24779
|
+
seq: remoteSuggestSeqRef.current,
|
|
24780
|
+
basis,
|
|
24781
|
+
caret,
|
|
24782
|
+
suggestion,
|
|
24783
|
+
...candidates ? { candidates } : {}
|
|
24784
|
+
});
|
|
24785
|
+
},
|
|
24786
|
+
onSearchChanged: ({ active, query }) => {
|
|
24787
|
+
setTranscriptSearchMode(active);
|
|
24788
|
+
setTranscriptSearchQuery(query);
|
|
24789
|
+
if (!active) setSelectedTurnId(null);
|
|
24790
|
+
remoteSearchSeqRef.current += 1;
|
|
24791
|
+
setRemoteSearchQuery(active ? {
|
|
24792
|
+
seq: remoteSearchSeqRef.current,
|
|
24793
|
+
query
|
|
24794
|
+
} : null);
|
|
24795
|
+
},
|
|
24796
|
+
onDraftAttachmentsChanged: (attachments) => {
|
|
24797
|
+
draftAttachmentsSeqRef.current += 1;
|
|
24798
|
+
setRemoteDraftAttachments({
|
|
24799
|
+
seq: draftAttachmentsSeqRef.current,
|
|
24800
|
+
attachments: attachments.map((att) => ({
|
|
24801
|
+
name: att.name ?? "attachment",
|
|
24802
|
+
mediaType: att.mediaType,
|
|
24803
|
+
content: Buffer.from(att.data, "base64")
|
|
24804
|
+
}))
|
|
24805
|
+
});
|
|
24806
|
+
},
|
|
24807
|
+
onSkillsChanged: (active) => {
|
|
24808
|
+
setActiveSkillNames(new Set(active));
|
|
24809
|
+
},
|
|
24810
|
+
onMcpAuth: ({ name, reason }) => {
|
|
24811
|
+
dispatchAuthRef.current({
|
|
24812
|
+
type: "auth-required",
|
|
24813
|
+
name,
|
|
24814
|
+
reason: reason === "auto-promoted" ? "auto-promoted" : "no-tokens"
|
|
24815
|
+
});
|
|
24816
|
+
},
|
|
24817
|
+
onHudState: (state) => {
|
|
24818
|
+
setRemoteHudState(state);
|
|
24819
|
+
},
|
|
24820
|
+
onHudNotify: (n) => {
|
|
24821
|
+
const allow = enabledExtensionsRef.current;
|
|
24822
|
+
if (allow !== void 0 && !allow.includes(n.extension)) return;
|
|
24823
|
+
stream.appendImmediate({
|
|
24824
|
+
kind: n.tone === "error" ? "error" : "info",
|
|
24825
|
+
text: `${n.extension}: ${n.tone === "warn" ? "⚠ " : ""}${n.text}`
|
|
24826
|
+
});
|
|
24827
|
+
},
|
|
24828
|
+
onExtensionSignal: (envelope) => {
|
|
24829
|
+
getTuiBus()?.deliver(envelope);
|
|
24830
|
+
},
|
|
24831
|
+
onTurnEnd: ({ usage, childId }) => {
|
|
24832
|
+
if (childId === void 0) setStreamProgress(null);
|
|
24833
|
+
if (childId !== void 0) stream.flushAndUpdate((prev) => finalizeStreamingMarkdownForOwner(prev, childId));
|
|
24834
|
+
if (!usage) return;
|
|
24835
|
+
if (childId === void 0) {
|
|
24836
|
+
lastFinishReasonRef.current = usage.finishReason;
|
|
24837
|
+
const tokens = turnContextSize(usage);
|
|
24838
|
+
if (tokens > 0) {
|
|
24839
|
+
setLastInputTokens(tokens);
|
|
24840
|
+
lastInputTokensRef.current = tokens;
|
|
24841
|
+
}
|
|
24842
|
+
}
|
|
24843
|
+
const turnCost = usage.cost ?? 0;
|
|
24844
|
+
if (turnCost > 0) setSessionCost((prev) => prev + turnCost);
|
|
24845
|
+
},
|
|
24846
|
+
onArtifactRecorded: () => notifyArtifactsChanged(),
|
|
24847
|
+
onArtifactChanged: () => notifyArtifactsChanged(),
|
|
24848
|
+
onTodosChanged: (snapshot) => {
|
|
24849
|
+
todosApplySeqRef.current += 1;
|
|
24850
|
+
const seq = todosApplySeqRef.current;
|
|
24851
|
+
const open = sessionRef.current;
|
|
24852
|
+
if (open?.id === sessionId && pickActiveRunId(open) === snapshot.runId) {
|
|
24853
|
+
applyTodosSnapshot(open, snapshot);
|
|
24854
|
+
notifyTodosChanged();
|
|
24855
|
+
return;
|
|
24856
|
+
}
|
|
24857
|
+
loadSession(store, sessionId).then((fresh) => {
|
|
24858
|
+
if (!attempt.isCurrent() || !fresh || sessionRef.current?.id !== sessionId) return;
|
|
24859
|
+
if (seq !== todosApplySeqRef.current) return;
|
|
24860
|
+
applyTodosSnapshot(fresh, snapshot);
|
|
24861
|
+
sessionRef.current = fresh;
|
|
24862
|
+
refreshHeaderFromStore();
|
|
24863
|
+
notifyTodosChanged();
|
|
24864
|
+
}).catch(() => void 0);
|
|
24865
|
+
},
|
|
24866
|
+
onWorkChanged: (items) => {
|
|
24867
|
+
const mapped = items.map((item) => ({
|
|
24868
|
+
kind: item.kind,
|
|
24869
|
+
callId: item.id,
|
|
24870
|
+
tool: item.label,
|
|
24871
|
+
startedAt: item.startedAt
|
|
24872
|
+
}));
|
|
24873
|
+
setInFlightTools(mapped.filter((m) => m.kind === "tool"));
|
|
24874
|
+
setBackgroundTasks(mapped.filter((m) => m.kind === "task"));
|
|
24875
|
+
},
|
|
24876
|
+
onQueueChanged: (items) => {
|
|
24877
|
+
daemonQueueIdsRef.current = items.map((item) => item.id);
|
|
24878
|
+
const mirrored = items.map((item) => ({
|
|
24879
|
+
prompt: item.text,
|
|
24880
|
+
references: [],
|
|
24881
|
+
attachments: []
|
|
24882
|
+
}));
|
|
24883
|
+
messageQueueRef.current = mirrored;
|
|
24884
|
+
setMessageQueue(mirrored);
|
|
24885
|
+
setQueueSelectionIndex((prev) => prev == null ? prev : mirrored.length === 0 ? null : Math.min(prev, mirrored.length - 1));
|
|
24886
|
+
},
|
|
24887
|
+
onInitialTranscript: (initial) => {
|
|
24888
|
+
setEvents(capTranscriptEvents(initial));
|
|
24889
|
+
},
|
|
24890
|
+
onStreamEvent: (event) => {
|
|
24891
|
+
const source = {
|
|
24892
|
+
...event.turnId ? { turnId: event.turnId } : {},
|
|
24893
|
+
...event.childId ? { childId: event.childId } : {},
|
|
24894
|
+
...typeof event.depth === "number" ? { depth: event.depth } : {}
|
|
24895
|
+
};
|
|
24896
|
+
if (event.kind === "markdown" && event.streaming) stream.queueStreamDelta("markdown", event.text, source);
|
|
24897
|
+
else if (event.kind === "thinking") stream.queueStreamDelta("thinking", event.text, source);
|
|
24898
|
+
else stream.appendImmediate(event);
|
|
24899
|
+
},
|
|
24900
|
+
onRunState: (running, info) => {
|
|
24901
|
+
runningRef.current = running;
|
|
24902
|
+
setBusy(running);
|
|
24903
|
+
if (running) return;
|
|
24904
|
+
stream.flushAndUpdate(finalizeStreamingMarkdown);
|
|
24905
|
+
if (info && !info.ok && info.error) stream.appendImmediate({
|
|
24834
24906
|
kind: "error",
|
|
24835
|
-
text:
|
|
24907
|
+
text: info.error
|
|
24836
24908
|
});
|
|
24837
|
-
|
|
24838
|
-
|
|
24839
|
-
|
|
24909
|
+
refreshHeaderFromStore();
|
|
24910
|
+
resyncTranscript();
|
|
24911
|
+
},
|
|
24912
|
+
onApproval: (request, resolve) => {
|
|
24913
|
+
requestApproval(request.tool, request.input, request.originator, request.id).then(attempt.guard(resolve));
|
|
24914
|
+
},
|
|
24915
|
+
onApprovalResolved: (requestId) => {
|
|
24916
|
+
dropApprovalByCorrelation(requestId);
|
|
24917
|
+
},
|
|
24918
|
+
onInteraction: (request, actions) => {
|
|
24919
|
+
interactions.enqueue({
|
|
24920
|
+
request,
|
|
24921
|
+
resolve: attempt.guard(actions.respond),
|
|
24922
|
+
cancel: attempt.guard(() => actions.cancel())
|
|
24923
|
+
});
|
|
24924
|
+
},
|
|
24925
|
+
onInteractionResolved: (requestId) => {
|
|
24926
|
+
interactions.dropById(requestId);
|
|
24927
|
+
},
|
|
24928
|
+
onCompacting: (active, info) => {
|
|
24929
|
+
setCompacting(active);
|
|
24930
|
+
if (!active && info?.ok && info.effectiveTokens !== void 0) {
|
|
24931
|
+
setLastInputTokens(info.effectiveTokens);
|
|
24932
|
+
lastInputTokensRef.current = info.effectiveTokens;
|
|
24933
|
+
}
|
|
24934
|
+
},
|
|
24935
|
+
onDisconnect: attempt.disconnect
|
|
24936
|
+
})
|
|
24937
|
+
});
|
|
24938
|
+
if (!attempt.isCurrent()) return controller;
|
|
24939
|
+
controllerHandle = controller;
|
|
24940
|
+
daemonRef.current = controller;
|
|
24941
|
+
setDaemonConnected(true);
|
|
24942
|
+
if (stampedLegacyAgent) controller.setAgentId(pickedAgentRef.current.id);
|
|
24943
|
+
if (seedModelViaRpc && activePick) controller.applyModel({
|
|
24944
|
+
providerKey: activePick.provider.key,
|
|
24945
|
+
modelId: activePick.model,
|
|
24946
|
+
...activePick.effort ? { effort: activePick.effort } : {},
|
|
24947
|
+
...activePick.modelOptions ? { modelOptions: activePick.modelOptions } : {}
|
|
24948
|
+
});
|
|
24949
|
+
return controller;
|
|
24950
|
+
},
|
|
24951
|
+
ready: () => {},
|
|
24952
|
+
cleanup: () => {
|
|
24953
|
+
denyAll();
|
|
24954
|
+
interactions.cancelAll("daemon disconnected");
|
|
24955
|
+
},
|
|
24956
|
+
unavailable: (err) => {
|
|
24957
|
+
debugLog("daemon unavailable — retrying attachment", err);
|
|
24958
|
+
daemonRef.current = null;
|
|
24959
|
+
runningRef.current = false;
|
|
24960
|
+
setBusy(false);
|
|
24961
|
+
setDaemonHealth("connecting");
|
|
24962
|
+
stream.appendImmediate({
|
|
24963
|
+
kind: "info",
|
|
24964
|
+
text: "daemon unavailable, reconnecting; sends disabled until reattached. Disable “Use daemon runtime” explicitly to run locally."
|
|
24965
|
+
});
|
|
24840
24966
|
}
|
|
24841
24967
|
});
|
|
24842
|
-
|
|
24843
|
-
controller.dispose();
|
|
24844
|
-
return;
|
|
24845
|
-
}
|
|
24846
|
-
controllerHandle = controller;
|
|
24847
|
-
daemonRef.current = controller;
|
|
24848
|
-
setDaemonConnected(true);
|
|
24849
|
-
if (stampedLegacyAgent) controller.setAgentId(pickedAgentRef.current.id);
|
|
24850
|
-
if (seedModelViaRpc && activePick) controller.applyModel({
|
|
24851
|
-
providerKey: activePick.provider.key,
|
|
24852
|
-
modelId: activePick.model,
|
|
24853
|
-
...activePick.effort ? { effort: activePick.effort } : {},
|
|
24854
|
-
...activePick.modelOptions ? { modelOptions: activePick.modelOptions } : {}
|
|
24855
|
-
});
|
|
24856
|
-
} catch (err) {
|
|
24857
|
-
debugLog("daemon activation failed — falling back to in-process runtime", err);
|
|
24858
|
-
daemonRef.current = null;
|
|
24859
|
-
setDaemonConnected(false);
|
|
24968
|
+
} else {
|
|
24860
24969
|
agentRef.current = buildAgent(session, key);
|
|
24861
|
-
|
|
24862
|
-
kind: "info",
|
|
24863
|
-
text: `daemon unavailable (${errorMessage(err)}), running this session in-process. Turn off “Use daemon runtime” to silence this.`
|
|
24864
|
-
});
|
|
24970
|
+
activation.localReady();
|
|
24865
24971
|
}
|
|
24866
|
-
|
|
24972
|
+
if (!activation.isCurrent()) return;
|
|
24867
24973
|
if (extensionRegistrySwapKeyRef.current === null || extensionRegistrySwapKeyRef.current === extensionRegistry) pendingAgentRebuildRef.current = false;
|
|
24868
24974
|
announcedSystemOverrideRef.current = null;
|
|
24869
24975
|
announceProjectSystemOverride();
|
|
24870
24976
|
const persistedPins = readPinnedSkills(session.metadata["zidane.activeSkills"]);
|
|
24871
24977
|
setActiveSkillNames(persistedPins);
|
|
24872
|
-
if (!
|
|
24978
|
+
if (!useDaemon) setEvents(capTranscriptEvents(eventsFromTurns(session.turns, session.runs)));
|
|
24873
24979
|
setActiveFallback(null);
|
|
24874
24980
|
const replayedTokens = lastContextSizeFromTurns(session.turns, session.runs);
|
|
24875
24981
|
setLastInputTokens(replayedTokens);
|
|
@@ -24891,7 +24997,7 @@ function AppShell() {
|
|
|
24891
24997
|
lastProvider: key,
|
|
24892
24998
|
lastSessionId: session.id
|
|
24893
24999
|
});
|
|
24894
|
-
const pending =
|
|
25000
|
+
const pending = useDaemon ? [] : pendingInteractionsFromTurns(session.turns);
|
|
24895
25001
|
if (pending.length > 0) {
|
|
24896
25002
|
const collected = /* @__PURE__ */ new Map();
|
|
24897
25003
|
const automatic = interactionModeRef.current === "skip";
|
|
@@ -25407,6 +25513,7 @@ function AppShell() {
|
|
|
25407
25513
|
});
|
|
25408
25514
|
}, []);
|
|
25409
25515
|
const dropSelectedQueuedMessage = useCallback(() => {
|
|
25516
|
+
if (!connectionRef.current.canSend) return;
|
|
25410
25517
|
const idx = queueSelectionIndexRef.current;
|
|
25411
25518
|
if (idx == null) return;
|
|
25412
25519
|
const queue = messageQueueRef.current;
|
|
@@ -25438,6 +25545,7 @@ function AppShell() {
|
|
|
25438
25545
|
* it up as a regular prompt, so the message is never silently dropped.
|
|
25439
25546
|
*/
|
|
25440
25547
|
const pushSelectedQueuedMessage = useCallback(() => {
|
|
25548
|
+
if (!connectionRef.current.canSend) return;
|
|
25441
25549
|
const idx = queueSelectionIndexRef.current;
|
|
25442
25550
|
if (idx == null) return;
|
|
25443
25551
|
if (daemonRef.current) {
|
|
@@ -25848,6 +25956,7 @@ function AppShell() {
|
|
|
25848
25956
|
* `agent.run` so the single-flight agent is never re-entered.
|
|
25849
25957
|
*/
|
|
25850
25958
|
const runCommand = useCallback(async (invocation, attachments = []) => {
|
|
25959
|
+
if (!connectionRef.current.canSend) return;
|
|
25851
25960
|
const session = sessionRef.current;
|
|
25852
25961
|
const agent = agentRef.current;
|
|
25853
25962
|
if (!session) return;
|
|
@@ -26227,6 +26336,7 @@ function AppShell() {
|
|
|
26227
26336
|
* path as `/` commands (`commandAbortRef`).
|
|
26228
26337
|
*/
|
|
26229
26338
|
const runBashCommand = useCallback(async (command) => {
|
|
26339
|
+
if (!connectionRef.current.canSend) return;
|
|
26230
26340
|
const session = sessionRef.current;
|
|
26231
26341
|
if (!session) return;
|
|
26232
26342
|
const controller = new AbortController();
|
|
@@ -26321,6 +26431,7 @@ function AppShell() {
|
|
|
26321
26431
|
}, [stream, cwd]);
|
|
26322
26432
|
/** Route a submitted prompt to the command executor when it's a `/cmd`, otherwise to the model. */
|
|
26323
26433
|
const runPromptOrCommand = useCallback(async (prompt, references, attachments, opts) => {
|
|
26434
|
+
if (!connectionRef.current.canSend) return;
|
|
26324
26435
|
if (!opts?.wake) {
|
|
26325
26436
|
const invocation = parseCommandInvocation(prompt, commandsRef.current);
|
|
26326
26437
|
if (invocation) {
|
|
@@ -26384,6 +26495,20 @@ function AppShell() {
|
|
|
26384
26495
|
}
|
|
26385
26496
|
}, [runSingleMessage, runPromptOrCommand]);
|
|
26386
26497
|
const onSubmitPrompt = useCallback((prompt, references, attachments) => {
|
|
26498
|
+
if (!connectionRef.current.canSend) {
|
|
26499
|
+
const shell = parseBashInvocation(prompt);
|
|
26500
|
+
setRemoteDraft({
|
|
26501
|
+
seq: ++draftSeqRef.current,
|
|
26502
|
+
text: shell ?? prompt,
|
|
26503
|
+
...shell !== null ? { shell: true } : {}
|
|
26504
|
+
});
|
|
26505
|
+
setRemoteDraftAttachments({
|
|
26506
|
+
seq: ++draftAttachmentsSeqRef.current,
|
|
26507
|
+
attachments: [...attachments]
|
|
26508
|
+
});
|
|
26509
|
+
return;
|
|
26510
|
+
}
|
|
26511
|
+
const submitGeneration = activationGenerationRef.current;
|
|
26387
26512
|
if (!prompt.trim() && attachments.length === 0) return;
|
|
26388
26513
|
const agent = agentRef.current;
|
|
26389
26514
|
const daemon = daemonRef.current;
|
|
@@ -26397,6 +26522,7 @@ function AppShell() {
|
|
|
26397
26522
|
let effectivePrompt = prompt;
|
|
26398
26523
|
if (parseCommandInvocation(prompt, commandsRef.current) === null && parseBashInvocation(prompt) === null) for (const contribution of extensionRegistry.promptTransforms) try {
|
|
26399
26524
|
const out = await contribution.value(effectivePrompt);
|
|
26525
|
+
if (submitGeneration !== activationGenerationRef.current || !connectionRef.current.canSend || daemon !== daemonRef.current) return;
|
|
26400
26526
|
if (out === void 0) continue;
|
|
26401
26527
|
if (typeof out === "string") {
|
|
26402
26528
|
effectivePrompt = out;
|
|
@@ -26413,6 +26539,7 @@ function AppShell() {
|
|
|
26413
26539
|
debugLog(`prompt transform (${contribution.extensionName}) threw`, err);
|
|
26414
26540
|
}
|
|
26415
26541
|
const resolvedReferences = await resolveCompletionSubmit(effectivePrompt, completionProviders, { cursor: effectivePrompt.length }).catch(() => references);
|
|
26542
|
+
if (submitGeneration !== activationGenerationRef.current || !connectionRef.current.canSend || daemon !== daemonRef.current) return;
|
|
26416
26543
|
if (daemon) {
|
|
26417
26544
|
if (!wasRunning) runningRef.current = false;
|
|
26418
26545
|
const commandInvocation = parseCommandInvocation(effectivePrompt, commandsRef.current);
|