nolo-cli 0.37.0-alpha.3 → 0.37.0-alpha.4
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/index.js
CHANGED
|
@@ -824,7 +824,7 @@ async function runScript(script, forwardedArgs, env) {
|
|
|
824
824
|
return proc.exited;
|
|
825
825
|
}
|
|
826
826
|
async function launchTuiWorkspace(args2) {
|
|
827
|
-
const { startTuiWorkspace } = await import("./readlineWorkspace-
|
|
827
|
+
const { startTuiWorkspace } = await import("./readlineWorkspace-PLJJPDP2.js");
|
|
828
828
|
const { createTuiSummaryLlmCaller } = await import("./tuiSummaryLlmCaller-SHRB4RWM.js");
|
|
829
829
|
return startTuiWorkspace({
|
|
830
830
|
...args2,
|
package/package.json
CHANGED
|
@@ -7094,6 +7094,219 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
|
|
|
7094
7094
|
}
|
|
7095
7095
|
});
|
|
7096
7096
|
}
|
|
7097
|
+
async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, confirmDestructiveAction) {
|
|
7098
|
+
const scheduleTitlePatchSync = (runResult) => {
|
|
7099
|
+
if (!runResult.titlePatchPromise || !runResult.dialogId) return;
|
|
7100
|
+
const patchDialogId = runResult.dialogId;
|
|
7101
|
+
runResult.titlePatchPromise.then((patchedTitle) => {
|
|
7102
|
+
if (!patchedTitle || ctx.sessionEnded) return;
|
|
7103
|
+
if (ctx.state.dialogId !== patchDialogId) return;
|
|
7104
|
+
if (ctx.state.dialogLabel === patchedTitle) return;
|
|
7105
|
+
ctx.state = {
|
|
7106
|
+
...ctx.state,
|
|
7107
|
+
dialogLabel: patchedTitle,
|
|
7108
|
+
dialogTitle: patchedTitle
|
|
7109
|
+
};
|
|
7110
|
+
ctx.syncWindowTitle();
|
|
7111
|
+
}).catch(() => {
|
|
7112
|
+
});
|
|
7113
|
+
};
|
|
7114
|
+
let req = createTurnRequest(inputMsg);
|
|
7115
|
+
if (req.event.kind === "child-run-completed") {
|
|
7116
|
+
const nowMs = Date.now();
|
|
7117
|
+
const { remainingRuns, claimedRunIds } = filterUnclaimedChildRuns(
|
|
7118
|
+
req.event.runs,
|
|
7119
|
+
{ env: ctx.effectiveEnv, now: () => nowMs }
|
|
7120
|
+
);
|
|
7121
|
+
for (const runId of claimedRunIds) {
|
|
7122
|
+
ctx.runCompletionWatcher.markAcknowledged(runId);
|
|
7123
|
+
}
|
|
7124
|
+
if (remainingRuns.length === 0) {
|
|
7125
|
+
for (const r of req.event.runs) {
|
|
7126
|
+
ctx.runCompletionWatcher.markAcknowledged(r.runId);
|
|
7127
|
+
}
|
|
7128
|
+
return { ok: true, aborted: false };
|
|
7129
|
+
}
|
|
7130
|
+
for (const r of remainingRuns) {
|
|
7131
|
+
ctx.runCompletionWatcher.markAcknowledged(r.runId);
|
|
7132
|
+
}
|
|
7133
|
+
if (remainingRuns.length !== req.event.runs.length) {
|
|
7134
|
+
const text = buildWakeMessage(remainingRuns, nowMs);
|
|
7135
|
+
const displayText = buildWakeDisplayText(remainingRuns, nowMs);
|
|
7136
|
+
const updatedEvent = {
|
|
7137
|
+
kind: "child-run-completed",
|
|
7138
|
+
runs: remainingRuns,
|
|
7139
|
+
text,
|
|
7140
|
+
displayText
|
|
7141
|
+
};
|
|
7142
|
+
req = {
|
|
7143
|
+
text,
|
|
7144
|
+
event: updatedEvent
|
|
7145
|
+
};
|
|
7146
|
+
}
|
|
7147
|
+
}
|
|
7148
|
+
const message = req.text;
|
|
7149
|
+
ctx.forcedStop = false;
|
|
7150
|
+
ctx.turnEpoch += 1;
|
|
7151
|
+
const myEpoch = ctx.turnEpoch;
|
|
7152
|
+
ctx.history.followBottom = true;
|
|
7153
|
+
const isInternalEvent = req.event.kind !== "user";
|
|
7154
|
+
const transcriptText = req.event.kind === "child-run-completed" ? req.event.displayText ?? req.event.text : message;
|
|
7155
|
+
startTurn(ctx.history, isInternalEvent ? "assistant" : "user");
|
|
7156
|
+
appendToCurrentTurn(
|
|
7157
|
+
ctx.history,
|
|
7158
|
+
isInternalEvent ? dimCliText(transcriptText, resolveCliColorEnabled()) : transcriptText
|
|
7159
|
+
);
|
|
7160
|
+
finalizeCurrentTurn(ctx.history);
|
|
7161
|
+
ctx.renderHistoryToOutput();
|
|
7162
|
+
if (ctx.fixedInput.active) ctx.fixedInput.repaint(ctx.buffer, ctx.cursorPos);
|
|
7163
|
+
startTurn(ctx.history, "assistant");
|
|
7164
|
+
const agentOutput = isInteractiveInput(ctx.input) ? createHistoryOutputStream(ctx.history, () => {
|
|
7165
|
+
ctx.scheduleRender();
|
|
7166
|
+
}) : ctx.output;
|
|
7167
|
+
const requestUserChoice = isInteractiveInput(ctx.input) && ctx.dialogHost ? async (choiceReq) => {
|
|
7168
|
+
ctx.modalOwnsKeyboard = true;
|
|
7169
|
+
try {
|
|
7170
|
+
return await ctx.dialogHost.run(
|
|
7171
|
+
(anchor) => runAskChoiceDialog({
|
|
7172
|
+
request: choiceReq,
|
|
7173
|
+
input: ctx.input,
|
|
7174
|
+
output: ctx.output,
|
|
7175
|
+
...anchor
|
|
7176
|
+
})
|
|
7177
|
+
);
|
|
7178
|
+
} catch {
|
|
7179
|
+
return { kind: "cancelled" };
|
|
7180
|
+
} finally {
|
|
7181
|
+
ctx.composerDecoderDrain?.();
|
|
7182
|
+
ctx.modalOwnsKeyboard = false;
|
|
7183
|
+
}
|
|
7184
|
+
} : void 0;
|
|
7185
|
+
try {
|
|
7186
|
+
ctx.activeTurnAbort = new AbortController();
|
|
7187
|
+
ctx.activeTurnEpoch = myEpoch;
|
|
7188
|
+
const runResult = await runAgentChat(
|
|
7189
|
+
ctx.options.scriptDir,
|
|
7190
|
+
ctx.state,
|
|
7191
|
+
message,
|
|
7192
|
+
ctx.options.env ?? process.env,
|
|
7193
|
+
agentOutput,
|
|
7194
|
+
ctx.options.agentRunner,
|
|
7195
|
+
{
|
|
7196
|
+
...imageUrls.length > 0 ? { imageUrls } : {},
|
|
7197
|
+
actionGateHandler,
|
|
7198
|
+
...confirmDestructiveAction ? { confirmDestructiveAction } : {},
|
|
7199
|
+
...requestUserChoice ? { requestUserChoice } : {},
|
|
7200
|
+
abortSignal: ctx.activeTurnAbort.signal,
|
|
7201
|
+
pastedTextStore: ctx.pasteStore,
|
|
7202
|
+
activityReporter: ctx.activityReporter,
|
|
7203
|
+
onAgentRunStatus: (snapshot) => {
|
|
7204
|
+
if (snapshot) {
|
|
7205
|
+
ctx.activityIndicator.updateAgentRun(snapshot);
|
|
7206
|
+
ctx.runRegistryPoller.ensureRunning();
|
|
7207
|
+
} else {
|
|
7208
|
+
ctx.activityIndicator.clearAgentRun();
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7211
|
+
}
|
|
7212
|
+
);
|
|
7213
|
+
const wasForceStopped = ctx.forcedStopEpoch === myEpoch;
|
|
7214
|
+
if (wasForceStopped) {
|
|
7215
|
+
if (runResult.dialogId || runResult.turnTokens) {
|
|
7216
|
+
const nextDialogKey = runResult.dialogId ? runResult.dialogId === ctx.state.dialogId && ctx.state.dialogKey ? ctx.state.dialogKey : ctx.state.dialogOwnerId ? `dialog-${ctx.state.dialogOwnerId}-${runResult.dialogId}` : void 0 : ctx.state.dialogKey;
|
|
7217
|
+
ctx.state = {
|
|
7218
|
+
...ctx.state,
|
|
7219
|
+
...runResult.dialogId ? {
|
|
7220
|
+
dialogId: runResult.dialogId,
|
|
7221
|
+
dialogKey: nextDialogKey,
|
|
7222
|
+
dialogLabel: runResult.title || runResult.dialogId,
|
|
7223
|
+
...runResult.title ? { dialogTitle: runResult.title } : {}
|
|
7224
|
+
} : {},
|
|
7225
|
+
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
|
|
7226
|
+
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
|
|
7227
|
+
};
|
|
7228
|
+
if (runResult.dialogId && nextDialogKey) {
|
|
7229
|
+
ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
|
|
7230
|
+
}
|
|
7231
|
+
}
|
|
7232
|
+
scheduleTitlePatchSync(runResult);
|
|
7233
|
+
return { ok: false, aborted: true };
|
|
7234
|
+
}
|
|
7235
|
+
const wasAborted = ctx.activeTurnAbort.signal.aborted;
|
|
7236
|
+
ctx.activeTurnAbort = null;
|
|
7237
|
+
if (shouldEmitTerminalBell({
|
|
7238
|
+
wasAborted,
|
|
7239
|
+
streamInterrupted: runResult.streamInterrupted,
|
|
7240
|
+
exitCode: runResult.exitCode,
|
|
7241
|
+
interactive: isInteractiveInput(ctx.input)
|
|
7242
|
+
})) {
|
|
7243
|
+
emitTerminalBell(ctx.output);
|
|
7244
|
+
}
|
|
7245
|
+
if (isInteractiveInput(ctx.input)) {
|
|
7246
|
+
finalizeCurrentTurn(ctx.history);
|
|
7247
|
+
ctx.flushPendingRender();
|
|
7248
|
+
ctx.renderHistoryToOutput();
|
|
7249
|
+
if (ctx.fixedInput.active) ctx.fixedInput.repaint(ctx.buffer, ctx.cursorPos);
|
|
7250
|
+
}
|
|
7251
|
+
if (wasAborted) {
|
|
7252
|
+
if (runResult.pendingToolName) {
|
|
7253
|
+
ctx.emitCommandOutput(t("turnStoppedToolPending", runResult.pendingToolName));
|
|
7254
|
+
} else {
|
|
7255
|
+
ctx.emitCommandOutput(t("turnStopped"));
|
|
7256
|
+
}
|
|
7257
|
+
}
|
|
7258
|
+
if (runResult.dialogId || runResult.turnTokens || runResult.contextWindow) {
|
|
7259
|
+
const nextDialogKey = runResult.dialogId ? runResult.dialogId === ctx.state.dialogId && ctx.state.dialogKey ? ctx.state.dialogKey : ctx.state.dialogOwnerId ? `dialog-${ctx.state.dialogOwnerId}-${runResult.dialogId}` : void 0 : ctx.state.dialogKey;
|
|
7260
|
+
ctx.state = {
|
|
7261
|
+
...ctx.state,
|
|
7262
|
+
...runResult.dialogId ? {
|
|
7263
|
+
dialogId: runResult.dialogId,
|
|
7264
|
+
dialogKey: nextDialogKey,
|
|
7265
|
+
dialogLabel: runResult.title || runResult.dialogId,
|
|
7266
|
+
...runResult.title ? { dialogTitle: runResult.title } : {}
|
|
7267
|
+
} : {},
|
|
7268
|
+
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
|
|
7269
|
+
...runResult.contextWindow ? { contextWindow: runResult.contextWindow } : {},
|
|
7270
|
+
// input_tokens 是累计上下文输入(含历史消息),把它持久化到
|
|
7271
|
+
// estimatedContextTokens:下一轮若 provider 不返回 usage,context
|
|
7272
|
+
// chip 仍显示真实累计占用而不是回退到启动时的静态估算。
|
|
7273
|
+
...runResult.turnTokens && runResult.turnTokens.input > 0 ? { estimatedContextTokens: runResult.turnTokens.input } : {},
|
|
7274
|
+
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
|
|
7275
|
+
};
|
|
7276
|
+
if (runResult.dialogId && nextDialogKey) {
|
|
7277
|
+
ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
|
|
7278
|
+
}
|
|
7279
|
+
}
|
|
7280
|
+
scheduleTitlePatchSync(runResult);
|
|
7281
|
+
if (!wasAborted && runResult.exitCode !== 0) {
|
|
7282
|
+
if (isBalanceExhaustedError(runResult.localError) && runResult.dialogId) {
|
|
7283
|
+
ctx.emitCommandOutput(t("balanceExhaustedHint"));
|
|
7284
|
+
} else if (isQuotaExhaustedError(runResult.localError)) {
|
|
7285
|
+
ctx.emitCommandOutput(t("quotaExhaustedHint"));
|
|
7286
|
+
} else if (runResult.dialogId) {
|
|
7287
|
+
ctx.emitCommandOutput(t("dialogPreservedHint"));
|
|
7288
|
+
} else {
|
|
7289
|
+
ctx.emitCommandOutput(t("dialogNotSavedHint"));
|
|
7290
|
+
}
|
|
7291
|
+
}
|
|
7292
|
+
return { ok: !wasAborted, aborted: wasAborted };
|
|
7293
|
+
} finally {
|
|
7294
|
+
ctx.activityIndicator.stop();
|
|
7295
|
+
ctx.activeTurnAbort = null;
|
|
7296
|
+
}
|
|
7297
|
+
}
|
|
7298
|
+
function ensureChatQueueBinding(ctx, actionGateHandler, confirmDestructiveAction) {
|
|
7299
|
+
if (ctx.chatQueueBinding) return ctx.chatQueueBinding;
|
|
7300
|
+
ctx.chatQueueBinding = createChatQueueTuiBinding(async (text) => {
|
|
7301
|
+
return runOneAgentTurn(ctx, text, [], actionGateHandler, confirmDestructiveAction);
|
|
7302
|
+
});
|
|
7303
|
+
return ctx.chatQueueBinding;
|
|
7304
|
+
}
|
|
7305
|
+
function preemptAndAbortForDrain(binding, activeTurnAbort) {
|
|
7306
|
+
if (binding.preemptForDrain() && activeTurnAbort) {
|
|
7307
|
+
activeTurnAbort.abort();
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7097
7310
|
var latestWorkspaceThemeOwner = 0;
|
|
7098
7311
|
async function runTuiWorkspace(options) {
|
|
7099
7312
|
initCliLocale(options.env ?? process.env);
|
|
@@ -7373,220 +7586,6 @@ ${content}`);
|
|
|
7373
7586
|
});
|
|
7374
7587
|
};
|
|
7375
7588
|
let composerDecoderDrain = null;
|
|
7376
|
-
const runOneAgentTurn = async (inputMsg, imageUrls, actionGateHandler, confirmDestructiveAction) => {
|
|
7377
|
-
const scheduleTitlePatchSync = (runResult) => {
|
|
7378
|
-
if (!runResult.titlePatchPromise || !runResult.dialogId) return;
|
|
7379
|
-
const patchDialogId = runResult.dialogId;
|
|
7380
|
-
runResult.titlePatchPromise.then((patchedTitle) => {
|
|
7381
|
-
if (!patchedTitle || sessionEnded) return;
|
|
7382
|
-
if (state.dialogId !== patchDialogId) return;
|
|
7383
|
-
if (state.dialogLabel === patchedTitle) return;
|
|
7384
|
-
state = {
|
|
7385
|
-
...state,
|
|
7386
|
-
dialogLabel: patchedTitle,
|
|
7387
|
-
dialogTitle: patchedTitle
|
|
7388
|
-
};
|
|
7389
|
-
syncWindowTitle();
|
|
7390
|
-
}).catch(() => {
|
|
7391
|
-
});
|
|
7392
|
-
};
|
|
7393
|
-
let req = createTurnRequest(inputMsg);
|
|
7394
|
-
if (req.event.kind === "child-run-completed") {
|
|
7395
|
-
const nowMs = Date.now();
|
|
7396
|
-
const { remainingRuns, claimedRunIds } = filterUnclaimedChildRuns(
|
|
7397
|
-
req.event.runs,
|
|
7398
|
-
{ env: effectiveEnv, now: () => nowMs }
|
|
7399
|
-
);
|
|
7400
|
-
for (const runId of claimedRunIds) {
|
|
7401
|
-
runCompletionWatcher.markAcknowledged(runId);
|
|
7402
|
-
}
|
|
7403
|
-
if (remainingRuns.length === 0) {
|
|
7404
|
-
for (const r of req.event.runs) {
|
|
7405
|
-
runCompletionWatcher.markAcknowledged(r.runId);
|
|
7406
|
-
}
|
|
7407
|
-
return { ok: true, aborted: false };
|
|
7408
|
-
}
|
|
7409
|
-
for (const r of remainingRuns) {
|
|
7410
|
-
runCompletionWatcher.markAcknowledged(r.runId);
|
|
7411
|
-
}
|
|
7412
|
-
if (remainingRuns.length !== req.event.runs.length) {
|
|
7413
|
-
const text = buildWakeMessage(remainingRuns, nowMs);
|
|
7414
|
-
const displayText = buildWakeDisplayText(remainingRuns, nowMs);
|
|
7415
|
-
const updatedEvent = {
|
|
7416
|
-
kind: "child-run-completed",
|
|
7417
|
-
runs: remainingRuns,
|
|
7418
|
-
text,
|
|
7419
|
-
displayText
|
|
7420
|
-
};
|
|
7421
|
-
req = {
|
|
7422
|
-
text,
|
|
7423
|
-
event: updatedEvent
|
|
7424
|
-
};
|
|
7425
|
-
}
|
|
7426
|
-
}
|
|
7427
|
-
const message = req.text;
|
|
7428
|
-
forcedStop = false;
|
|
7429
|
-
turnEpoch += 1;
|
|
7430
|
-
const myEpoch = turnEpoch;
|
|
7431
|
-
history.followBottom = true;
|
|
7432
|
-
const isInternalEvent = req.event.kind !== "user";
|
|
7433
|
-
const transcriptText = req.event.kind === "child-run-completed" ? req.event.displayText ?? req.event.text : message;
|
|
7434
|
-
startTurn(history, isInternalEvent ? "assistant" : "user");
|
|
7435
|
-
appendToCurrentTurn(
|
|
7436
|
-
history,
|
|
7437
|
-
isInternalEvent ? dimCliText(transcriptText, resolveCliColorEnabled()) : transcriptText
|
|
7438
|
-
);
|
|
7439
|
-
finalizeCurrentTurn(history);
|
|
7440
|
-
renderHistoryToOutput();
|
|
7441
|
-
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
7442
|
-
startTurn(history, "assistant");
|
|
7443
|
-
const agentOutput = isInteractiveInput(input) ? createHistoryOutputStream(history, () => {
|
|
7444
|
-
scheduleRender();
|
|
7445
|
-
}) : output;
|
|
7446
|
-
const requestUserChoice = isInteractiveInput(input) && dialogHost ? async (req2) => {
|
|
7447
|
-
modalOwnsKeyboard = true;
|
|
7448
|
-
try {
|
|
7449
|
-
return await dialogHost.run(
|
|
7450
|
-
(anchor) => runAskChoiceDialog({
|
|
7451
|
-
request: req2,
|
|
7452
|
-
input,
|
|
7453
|
-
output,
|
|
7454
|
-
...anchor
|
|
7455
|
-
})
|
|
7456
|
-
);
|
|
7457
|
-
} catch {
|
|
7458
|
-
return { kind: "cancelled" };
|
|
7459
|
-
} finally {
|
|
7460
|
-
composerDecoderDrain?.();
|
|
7461
|
-
modalOwnsKeyboard = false;
|
|
7462
|
-
}
|
|
7463
|
-
} : void 0;
|
|
7464
|
-
try {
|
|
7465
|
-
activeTurnAbort = new AbortController();
|
|
7466
|
-
activeTurnEpoch = myEpoch;
|
|
7467
|
-
const runResult = await runAgentChat(
|
|
7468
|
-
options.scriptDir,
|
|
7469
|
-
state,
|
|
7470
|
-
message,
|
|
7471
|
-
options.env ?? process.env,
|
|
7472
|
-
agentOutput,
|
|
7473
|
-
options.agentRunner,
|
|
7474
|
-
{
|
|
7475
|
-
...imageUrls.length > 0 ? { imageUrls } : {},
|
|
7476
|
-
actionGateHandler,
|
|
7477
|
-
...confirmDestructiveAction ? { confirmDestructiveAction } : {},
|
|
7478
|
-
...requestUserChoice ? { requestUserChoice } : {},
|
|
7479
|
-
abortSignal: activeTurnAbort.signal,
|
|
7480
|
-
pastedTextStore: pasteStore,
|
|
7481
|
-
activityReporter,
|
|
7482
|
-
onAgentRunStatus: (snapshot) => {
|
|
7483
|
-
if (snapshot) {
|
|
7484
|
-
activityIndicator.updateAgentRun(snapshot);
|
|
7485
|
-
runRegistryPoller.ensureRunning();
|
|
7486
|
-
} else {
|
|
7487
|
-
activityIndicator.clearAgentRun();
|
|
7488
|
-
}
|
|
7489
|
-
}
|
|
7490
|
-
}
|
|
7491
|
-
);
|
|
7492
|
-
const wasForceStopped = forcedStopEpoch === myEpoch;
|
|
7493
|
-
if (wasForceStopped) {
|
|
7494
|
-
if (runResult.dialogId || runResult.turnTokens) {
|
|
7495
|
-
const nextDialogKey = runResult.dialogId ? runResult.dialogId === state.dialogId && state.dialogKey ? state.dialogKey : state.dialogOwnerId ? `dialog-${state.dialogOwnerId}-${runResult.dialogId}` : void 0 : state.dialogKey;
|
|
7496
|
-
state = {
|
|
7497
|
-
...state,
|
|
7498
|
-
...runResult.dialogId ? {
|
|
7499
|
-
dialogId: runResult.dialogId,
|
|
7500
|
-
dialogKey: nextDialogKey,
|
|
7501
|
-
dialogLabel: runResult.title || runResult.dialogId,
|
|
7502
|
-
...runResult.title ? { dialogTitle: runResult.title } : {}
|
|
7503
|
-
} : {},
|
|
7504
|
-
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
|
|
7505
|
-
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
|
|
7506
|
-
};
|
|
7507
|
-
if (runResult.dialogId && nextDialogKey) {
|
|
7508
|
-
refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
|
|
7509
|
-
}
|
|
7510
|
-
}
|
|
7511
|
-
scheduleTitlePatchSync(runResult);
|
|
7512
|
-
return { ok: false, aborted: true };
|
|
7513
|
-
}
|
|
7514
|
-
const wasAborted = activeTurnAbort.signal.aborted;
|
|
7515
|
-
activeTurnAbort = null;
|
|
7516
|
-
if (shouldEmitTerminalBell({
|
|
7517
|
-
wasAborted,
|
|
7518
|
-
streamInterrupted: runResult.streamInterrupted,
|
|
7519
|
-
exitCode: runResult.exitCode,
|
|
7520
|
-
interactive: isInteractiveInput(input)
|
|
7521
|
-
})) {
|
|
7522
|
-
emitTerminalBell(output);
|
|
7523
|
-
}
|
|
7524
|
-
if (isInteractiveInput(input)) {
|
|
7525
|
-
finalizeCurrentTurn(history);
|
|
7526
|
-
flushPendingRender();
|
|
7527
|
-
renderHistoryToOutput();
|
|
7528
|
-
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
7529
|
-
}
|
|
7530
|
-
if (wasAborted) {
|
|
7531
|
-
if (runResult.pendingToolName) {
|
|
7532
|
-
emitCommandOutput(t("turnStoppedToolPending", runResult.pendingToolName));
|
|
7533
|
-
} else {
|
|
7534
|
-
emitCommandOutput(t("turnStopped"));
|
|
7535
|
-
}
|
|
7536
|
-
}
|
|
7537
|
-
if (runResult.dialogId || runResult.turnTokens || runResult.contextWindow) {
|
|
7538
|
-
const nextDialogKey = runResult.dialogId ? runResult.dialogId === state.dialogId && state.dialogKey ? state.dialogKey : state.dialogOwnerId ? `dialog-${state.dialogOwnerId}-${runResult.dialogId}` : void 0 : state.dialogKey;
|
|
7539
|
-
state = {
|
|
7540
|
-
...state,
|
|
7541
|
-
...runResult.dialogId ? {
|
|
7542
|
-
dialogId: runResult.dialogId,
|
|
7543
|
-
dialogKey: nextDialogKey,
|
|
7544
|
-
dialogLabel: runResult.title || runResult.dialogId,
|
|
7545
|
-
...runResult.title ? { dialogTitle: runResult.title } : {}
|
|
7546
|
-
} : {},
|
|
7547
|
-
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
|
|
7548
|
-
...runResult.contextWindow ? { contextWindow: runResult.contextWindow } : {},
|
|
7549
|
-
// input_tokens 是累计上下文输入(含历史消息),把它持久化到
|
|
7550
|
-
// estimatedContextTokens:下一轮若 provider 不返回 usage,context
|
|
7551
|
-
// chip 仍显示真实累计占用而不是回退到启动时的静态估算。
|
|
7552
|
-
...runResult.turnTokens && runResult.turnTokens.input > 0 ? { estimatedContextTokens: runResult.turnTokens.input } : {},
|
|
7553
|
-
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
|
|
7554
|
-
};
|
|
7555
|
-
if (runResult.dialogId && nextDialogKey) {
|
|
7556
|
-
refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
|
|
7557
|
-
}
|
|
7558
|
-
}
|
|
7559
|
-
scheduleTitlePatchSync(runResult);
|
|
7560
|
-
if (!wasAborted && runResult.exitCode !== 0) {
|
|
7561
|
-
if (isBalanceExhaustedError(runResult.localError) && runResult.dialogId) {
|
|
7562
|
-
emitCommandOutput(t("balanceExhaustedHint"));
|
|
7563
|
-
} else if (isQuotaExhaustedError(runResult.localError)) {
|
|
7564
|
-
emitCommandOutput(t("quotaExhaustedHint"));
|
|
7565
|
-
} else if (runResult.dialogId) {
|
|
7566
|
-
emitCommandOutput(t("dialogPreservedHint"));
|
|
7567
|
-
} else {
|
|
7568
|
-
emitCommandOutput(t("dialogNotSavedHint"));
|
|
7569
|
-
}
|
|
7570
|
-
}
|
|
7571
|
-
return { ok: !wasAborted, aborted: wasAborted };
|
|
7572
|
-
} finally {
|
|
7573
|
-
activityIndicator.stop();
|
|
7574
|
-
activeTurnAbort = null;
|
|
7575
|
-
}
|
|
7576
|
-
};
|
|
7577
|
-
let chatQueueBinding = null;
|
|
7578
|
-
const ensureChatQueueBinding = (actionGateHandler, confirmDestructiveAction) => {
|
|
7579
|
-
if (chatQueueBinding) return chatQueueBinding;
|
|
7580
|
-
chatQueueBinding = createChatQueueTuiBinding(async (text) => {
|
|
7581
|
-
return runOneAgentTurn(text, [], actionGateHandler, confirmDestructiveAction);
|
|
7582
|
-
});
|
|
7583
|
-
return chatQueueBinding;
|
|
7584
|
-
};
|
|
7585
|
-
const preemptAndAbortForDrain = (binding) => {
|
|
7586
|
-
if (binding.preemptForDrain() && activeTurnAbort) {
|
|
7587
|
-
activeTurnAbort.abort();
|
|
7588
|
-
}
|
|
7589
|
-
};
|
|
7590
7589
|
const emitCommandOutput = (text, command = "") => {
|
|
7591
7590
|
if (!text) return;
|
|
7592
7591
|
if (!isInteractiveInput(input)) {
|
|
@@ -7599,6 +7598,92 @@ ${content}`);
|
|
|
7599
7598
|
renderHistoryToOutput();
|
|
7600
7599
|
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
7601
7600
|
};
|
|
7601
|
+
let chatQueueBinding = null;
|
|
7602
|
+
const turnCtx = {
|
|
7603
|
+
get state() {
|
|
7604
|
+
return state;
|
|
7605
|
+
},
|
|
7606
|
+
set state(v) {
|
|
7607
|
+
state = v;
|
|
7608
|
+
},
|
|
7609
|
+
get forcedStop() {
|
|
7610
|
+
return forcedStop;
|
|
7611
|
+
},
|
|
7612
|
+
set forcedStop(v) {
|
|
7613
|
+
forcedStop = v;
|
|
7614
|
+
},
|
|
7615
|
+
get forcedStopEpoch() {
|
|
7616
|
+
return forcedStopEpoch;
|
|
7617
|
+
},
|
|
7618
|
+
set forcedStopEpoch(v) {
|
|
7619
|
+
forcedStopEpoch = v;
|
|
7620
|
+
},
|
|
7621
|
+
get turnEpoch() {
|
|
7622
|
+
return turnEpoch;
|
|
7623
|
+
},
|
|
7624
|
+
set turnEpoch(v) {
|
|
7625
|
+
turnEpoch = v;
|
|
7626
|
+
},
|
|
7627
|
+
get activeTurnAbort() {
|
|
7628
|
+
return activeTurnAbort;
|
|
7629
|
+
},
|
|
7630
|
+
set activeTurnAbort(v) {
|
|
7631
|
+
activeTurnAbort = v;
|
|
7632
|
+
},
|
|
7633
|
+
get activeTurnEpoch() {
|
|
7634
|
+
return activeTurnEpoch;
|
|
7635
|
+
},
|
|
7636
|
+
set activeTurnEpoch(v) {
|
|
7637
|
+
activeTurnEpoch = v;
|
|
7638
|
+
},
|
|
7639
|
+
get modalOwnsKeyboard() {
|
|
7640
|
+
return modalOwnsKeyboard;
|
|
7641
|
+
},
|
|
7642
|
+
set modalOwnsKeyboard(v) {
|
|
7643
|
+
modalOwnsKeyboard = v;
|
|
7644
|
+
},
|
|
7645
|
+
get composerDecoderDrain() {
|
|
7646
|
+
return composerDecoderDrain;
|
|
7647
|
+
},
|
|
7648
|
+
set composerDecoderDrain(v) {
|
|
7649
|
+
composerDecoderDrain = v;
|
|
7650
|
+
},
|
|
7651
|
+
get chatQueueBinding() {
|
|
7652
|
+
return chatQueueBinding;
|
|
7653
|
+
},
|
|
7654
|
+
set chatQueueBinding(v) {
|
|
7655
|
+
chatQueueBinding = v;
|
|
7656
|
+
},
|
|
7657
|
+
get sessionEnded() {
|
|
7658
|
+
return sessionEnded;
|
|
7659
|
+
},
|
|
7660
|
+
get buffer() {
|
|
7661
|
+
return buffer;
|
|
7662
|
+
},
|
|
7663
|
+
get cursorPos() {
|
|
7664
|
+
return cursorPos;
|
|
7665
|
+
},
|
|
7666
|
+
get fixedInput() {
|
|
7667
|
+
return fixedInput;
|
|
7668
|
+
},
|
|
7669
|
+
options,
|
|
7670
|
+
effectiveEnv,
|
|
7671
|
+
history,
|
|
7672
|
+
activityIndicator,
|
|
7673
|
+
activityReporter,
|
|
7674
|
+
runRegistryPoller,
|
|
7675
|
+
runCompletionWatcher,
|
|
7676
|
+
pasteStore,
|
|
7677
|
+
dialogHost,
|
|
7678
|
+
input,
|
|
7679
|
+
output,
|
|
7680
|
+
syncWindowTitle,
|
|
7681
|
+
renderHistoryToOutput,
|
|
7682
|
+
scheduleRender,
|
|
7683
|
+
flushPendingRender,
|
|
7684
|
+
refreshDialogTotalCredits,
|
|
7685
|
+
emitCommandOutput
|
|
7686
|
+
};
|
|
7602
7687
|
const persistExplicitAgentSwitch = (previousAgentKey) => {
|
|
7603
7688
|
if (state.agentKey === previousAgentKey) return false;
|
|
7604
7689
|
persistAgentSelection(
|
|
@@ -8020,10 +8105,11 @@ ${stderrText.trim()}
|
|
|
8020
8105
|
attachedImages: []
|
|
8021
8106
|
};
|
|
8022
8107
|
history.followBottom = true;
|
|
8023
|
-
const binding = ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction);
|
|
8108
|
+
const binding = ensureChatQueueBinding(turnCtx, actionGateHandler, confirmDestructiveAction);
|
|
8024
8109
|
binding.notifyTurnStart();
|
|
8025
8110
|
try {
|
|
8026
8111
|
const outcome = await runOneAgentTurn(
|
|
8112
|
+
turnCtx,
|
|
8027
8113
|
result.action.message,
|
|
8028
8114
|
imageUrls,
|
|
8029
8115
|
actionGateHandler,
|
|
@@ -8182,12 +8268,13 @@ ${err.message}` : ""}`
|
|
|
8182
8268
|
const runIdleTextTurn = async (inputMsg) => {
|
|
8183
8269
|
const req = createTurnRequest(inputMsg);
|
|
8184
8270
|
const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
|
|
8185
|
-
const binding = ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction);
|
|
8271
|
+
const binding = ensureChatQueueBinding(turnCtx, actionGateHandler, confirmDestructiveAction);
|
|
8186
8272
|
binding.notifyTurnStart();
|
|
8187
8273
|
busy = true;
|
|
8188
8274
|
fixedInput.enterOutputMode(req.text);
|
|
8189
8275
|
try {
|
|
8190
8276
|
const outcome = await runOneAgentTurn(
|
|
8277
|
+
turnCtx,
|
|
8191
8278
|
req,
|
|
8192
8279
|
[],
|
|
8193
8280
|
actionGateHandler,
|
|
@@ -8211,7 +8298,7 @@ ${err.message}` : ""}`
|
|
|
8211
8298
|
if (done) return;
|
|
8212
8299
|
if (busy || fixedInput.isPaused()) {
|
|
8213
8300
|
const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
|
|
8214
|
-
ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction).enqueue(event);
|
|
8301
|
+
ensureChatQueueBinding(turnCtx, actionGateHandler, confirmDestructiveAction).enqueue(event);
|
|
8215
8302
|
if (fixedInput.active && !fixedInput.isPaused()) {
|
|
8216
8303
|
fixedInput.repaint(buffer, cursorPos);
|
|
8217
8304
|
}
|
|
@@ -8271,6 +8358,109 @@ ${err.message}` : ""}`
|
|
|
8271
8358
|
paintFrame(buffer);
|
|
8272
8359
|
}, 60);
|
|
8273
8360
|
};
|
|
8361
|
+
const handleCtrlCKey = async (busyLock) => {
|
|
8362
|
+
if (busyLock && activeTurnAbort) {
|
|
8363
|
+
const stopBinding = chatQueueBinding;
|
|
8364
|
+
if (stopBinding && stopBinding.queueLength() > 0) {
|
|
8365
|
+
stopBinding.preemptForStop();
|
|
8366
|
+
}
|
|
8367
|
+
activityIndicator.markStopping();
|
|
8368
|
+
activeTurnAbort.abort();
|
|
8369
|
+
return;
|
|
8370
|
+
}
|
|
8371
|
+
const now = Date.now();
|
|
8372
|
+
const hasSelection = selectionState.anchor !== null && selectionState.head !== null && !areSelectionPointsEqual(selectionState.anchor, selectionState.head);
|
|
8373
|
+
if (hasSelection) {
|
|
8374
|
+
const tty = output;
|
|
8375
|
+
const columns = tty.columns ?? 80;
|
|
8376
|
+
const contentWidth = Math.max(1, columns - 1);
|
|
8377
|
+
const textToCopy = extractSelectedText(
|
|
8378
|
+
history,
|
|
8379
|
+
selectionState.anchor,
|
|
8380
|
+
selectionState.head,
|
|
8381
|
+
contentWidth
|
|
8382
|
+
);
|
|
8383
|
+
clearSelection();
|
|
8384
|
+
if (textToCopy.length > 0) {
|
|
8385
|
+
try {
|
|
8386
|
+
await writeClipboard2(textToCopy);
|
|
8387
|
+
emitCommandOutput(t("copiedSelection"));
|
|
8388
|
+
} catch (error) {
|
|
8389
|
+
emitCommandOutput(
|
|
8390
|
+
`[nolo] ${t("copyFailed")}: ${toErrorMessage(error)}`
|
|
8391
|
+
);
|
|
8392
|
+
}
|
|
8393
|
+
}
|
|
8394
|
+
paintFrame(buffer);
|
|
8395
|
+
return;
|
|
8396
|
+
}
|
|
8397
|
+
if (selectionState.anchor) {
|
|
8398
|
+
clearSelection();
|
|
8399
|
+
}
|
|
8400
|
+
if (buffer.length > 0) {
|
|
8401
|
+
buffer = "";
|
|
8402
|
+
cursorPos = 0;
|
|
8403
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8404
|
+
emitCommandOutput(t("ctrlCClearedDraft"));
|
|
8405
|
+
return;
|
|
8406
|
+
}
|
|
8407
|
+
if (lastCtrlCDoublePress !== null && now - lastCtrlCDoublePress <= 1e3) {
|
|
8408
|
+
lastCtrlCDoublePress = null;
|
|
8409
|
+
fixedInput.disable();
|
|
8410
|
+
finish();
|
|
8411
|
+
return;
|
|
8412
|
+
}
|
|
8413
|
+
lastCtrlCDoublePress = now;
|
|
8414
|
+
emitCommandOutput(t("ctrlCExitHint"));
|
|
8415
|
+
};
|
|
8416
|
+
const handleBusyLocalSlash = async (submittedText, busySlashCommand) => {
|
|
8417
|
+
releaseCollapsedPasteReferences(submittedText, pasteStore);
|
|
8418
|
+
buffer = "";
|
|
8419
|
+
cursorPos = 0;
|
|
8420
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8421
|
+
const beforeAgentKey = state.agentKey;
|
|
8422
|
+
const res = handleTuiInput(submittedText, state);
|
|
8423
|
+
if (res.action?.type === "theme-refresh") {
|
|
8424
|
+
state = res.nextState;
|
|
8425
|
+
const detected = await detectTerminalBackground({
|
|
8426
|
+
stdin: input,
|
|
8427
|
+
stdout: output,
|
|
8428
|
+
allowSystemFallback: true
|
|
8429
|
+
});
|
|
8430
|
+
let refreshMsg = "";
|
|
8431
|
+
if (detected && applyDetectedBackground(detected)) {
|
|
8432
|
+
refreshMsg = t("themeRefreshed", detected.brightness);
|
|
8433
|
+
} else if (detected) {
|
|
8434
|
+
refreshMsg = t("themeRefreshed", detected.brightness);
|
|
8435
|
+
} else {
|
|
8436
|
+
refreshMsg = t("themeRefreshFailed");
|
|
8437
|
+
}
|
|
8438
|
+
if (refreshMsg) {
|
|
8439
|
+
output.write(`${refreshMsg}
|
|
8440
|
+
`);
|
|
8441
|
+
}
|
|
8442
|
+
} else if (res.action) {
|
|
8443
|
+
output.write(
|
|
8444
|
+
"Model picker isn't available while a reply is running. Use `/switch <name>` to switch now (takes effect on the next turn), or wait for the reply to finish.\n"
|
|
8445
|
+
);
|
|
8446
|
+
} else {
|
|
8447
|
+
state = res.nextState;
|
|
8448
|
+
let msg = res.output;
|
|
8449
|
+
if (busySlashCommand === "/switch" && persistExplicitAgentSwitch(beforeAgentKey)) {
|
|
8450
|
+
const hint = "Note: the new model takes effect on the next turn. Switching models may consume more tokens because the conversation context is re-sent to the new model.";
|
|
8451
|
+
msg = msg ? `${msg}
|
|
8452
|
+
${hint}` : hint;
|
|
8453
|
+
}
|
|
8454
|
+
if (msg) {
|
|
8455
|
+
output.write(`${msg}
|
|
8456
|
+
`);
|
|
8457
|
+
}
|
|
8458
|
+
}
|
|
8459
|
+
if (busySlashCommand === "/theme") {
|
|
8460
|
+
renderHistoryToOutput();
|
|
8461
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8462
|
+
}
|
|
8463
|
+
};
|
|
8274
8464
|
const handleInputToken = async (sequence) => {
|
|
8275
8465
|
if (done) return;
|
|
8276
8466
|
if (modalOwnsKeyboard) {
|
|
@@ -8293,7 +8483,7 @@ ${merged}` : merged;
|
|
|
8293
8483
|
t(busy ? "flushQueuedBusyHint" : "flushQueuedIdleHint", String(totalCount))
|
|
8294
8484
|
);
|
|
8295
8485
|
if (busy) {
|
|
8296
|
-
preemptAndAbortForDrain(chatQueueBinding);
|
|
8486
|
+
preemptAndAbortForDrain(chatQueueBinding, activeTurnAbort);
|
|
8297
8487
|
return;
|
|
8298
8488
|
}
|
|
8299
8489
|
buffer = "";
|
|
@@ -8400,59 +8590,7 @@ ${merged}` : merged;
|
|
|
8400
8590
|
return;
|
|
8401
8591
|
}
|
|
8402
8592
|
if (sequence === "") {
|
|
8403
|
-
|
|
8404
|
-
const stopBinding = chatQueueBinding;
|
|
8405
|
-
if (stopBinding && stopBinding.queueLength() > 0) {
|
|
8406
|
-
stopBinding.preemptForStop();
|
|
8407
|
-
}
|
|
8408
|
-
activityIndicator.markStopping();
|
|
8409
|
-
activeTurnAbort.abort();
|
|
8410
|
-
return;
|
|
8411
|
-
}
|
|
8412
|
-
const now = Date.now();
|
|
8413
|
-
const hasSelection = selectionState.anchor !== null && selectionState.head !== null && !areSelectionPointsEqual(selectionState.anchor, selectionState.head);
|
|
8414
|
-
if (hasSelection) {
|
|
8415
|
-
const tty = output;
|
|
8416
|
-
const columns = tty.columns ?? 80;
|
|
8417
|
-
const contentWidth = Math.max(1, columns - 1);
|
|
8418
|
-
const textToCopy = extractSelectedText(
|
|
8419
|
-
history,
|
|
8420
|
-
selectionState.anchor,
|
|
8421
|
-
selectionState.head,
|
|
8422
|
-
contentWidth
|
|
8423
|
-
);
|
|
8424
|
-
clearSelection();
|
|
8425
|
-
if (textToCopy.length > 0) {
|
|
8426
|
-
try {
|
|
8427
|
-
await writeClipboard2(textToCopy);
|
|
8428
|
-
emitCommandOutput(t("copiedSelection"));
|
|
8429
|
-
} catch (error) {
|
|
8430
|
-
emitCommandOutput(
|
|
8431
|
-
`[nolo] ${t("copyFailed")}: ${toErrorMessage(error)}`
|
|
8432
|
-
);
|
|
8433
|
-
}
|
|
8434
|
-
}
|
|
8435
|
-
paintFrame(buffer);
|
|
8436
|
-
return;
|
|
8437
|
-
}
|
|
8438
|
-
if (selectionState.anchor) {
|
|
8439
|
-
clearSelection();
|
|
8440
|
-
}
|
|
8441
|
-
if (buffer.length > 0) {
|
|
8442
|
-
buffer = "";
|
|
8443
|
-
cursorPos = 0;
|
|
8444
|
-
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8445
|
-
emitCommandOutput(t("ctrlCClearedDraft"));
|
|
8446
|
-
return;
|
|
8447
|
-
}
|
|
8448
|
-
if (lastCtrlCDoublePress !== null && now - lastCtrlCDoublePress <= 1e3) {
|
|
8449
|
-
lastCtrlCDoublePress = null;
|
|
8450
|
-
fixedInput.disable();
|
|
8451
|
-
finish();
|
|
8452
|
-
return;
|
|
8453
|
-
}
|
|
8454
|
-
lastCtrlCDoublePress = now;
|
|
8455
|
-
emitCommandOutput(t("ctrlCExitHint"));
|
|
8593
|
+
await handleCtrlCKey(busyLock);
|
|
8456
8594
|
return;
|
|
8457
8595
|
}
|
|
8458
8596
|
if (selectionState.anchor) {
|
|
@@ -8535,56 +8673,11 @@ ${merged}` : merged;
|
|
|
8535
8673
|
const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
|
|
8536
8674
|
const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/switch" || busySlashCommand === "/theme" || busySlashCommand === "/density" || busySlashCommand === "/runtime" || busySlashCommand === "/tools" || busySlashCommand === "/thinking" || busySlashCommand === "/auto" || busySlashCommand === "/tasks" || busySlashCommand === "/jobs" || busySlashCommand === "/procs" || busySlashCommand === "/agents" || busySlashCommand === "/doc" || busySlashCommand === "/skill" || busySlashCommand === "/customize" || busySlashCommand === "/login" || busySlashCommand === "/profile" || busySlashCommand === "/version";
|
|
8537
8675
|
if (isBusyLocalSlash) {
|
|
8538
|
-
|
|
8539
|
-
buffer = "";
|
|
8540
|
-
cursorPos = 0;
|
|
8541
|
-
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8542
|
-
const beforeAgentKey = state.agentKey;
|
|
8543
|
-
const res = handleTuiInput(submittedText, state);
|
|
8544
|
-
if (res.action?.type === "theme-refresh") {
|
|
8545
|
-
state = res.nextState;
|
|
8546
|
-
const detected = await detectTerminalBackground({
|
|
8547
|
-
stdin: input,
|
|
8548
|
-
stdout: output,
|
|
8549
|
-
allowSystemFallback: true
|
|
8550
|
-
});
|
|
8551
|
-
let refreshMsg = "";
|
|
8552
|
-
if (detected && applyDetectedBackground(detected)) {
|
|
8553
|
-
refreshMsg = t("themeRefreshed", detected.brightness);
|
|
8554
|
-
} else if (detected) {
|
|
8555
|
-
refreshMsg = t("themeRefreshed", detected.brightness);
|
|
8556
|
-
} else {
|
|
8557
|
-
refreshMsg = t("themeRefreshFailed");
|
|
8558
|
-
}
|
|
8559
|
-
if (refreshMsg) {
|
|
8560
|
-
output.write(`${refreshMsg}
|
|
8561
|
-
`);
|
|
8562
|
-
}
|
|
8563
|
-
} else if (res.action) {
|
|
8564
|
-
output.write(
|
|
8565
|
-
"Model picker isn't available while a reply is running. Use `/switch <name>` to switch now (takes effect on the next turn), or wait for the reply to finish.\n"
|
|
8566
|
-
);
|
|
8567
|
-
} else {
|
|
8568
|
-
state = res.nextState;
|
|
8569
|
-
let msg = res.output;
|
|
8570
|
-
if (busySlashCommand === "/switch" && persistExplicitAgentSwitch(beforeAgentKey)) {
|
|
8571
|
-
const hint = "Note: the new model takes effect on the next turn. Switching models may consume more tokens because the conversation context is re-sent to the new model.";
|
|
8572
|
-
msg = msg ? `${msg}
|
|
8573
|
-
${hint}` : hint;
|
|
8574
|
-
}
|
|
8575
|
-
if (msg) {
|
|
8576
|
-
output.write(`${msg}
|
|
8577
|
-
`);
|
|
8578
|
-
}
|
|
8579
|
-
}
|
|
8580
|
-
if (busySlashCommand === "/theme") {
|
|
8581
|
-
renderHistoryToOutput();
|
|
8582
|
-
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8583
|
-
}
|
|
8676
|
+
await handleBusyLocalSlash(submittedText, busySlashCommand);
|
|
8584
8677
|
return;
|
|
8585
8678
|
}
|
|
8586
8679
|
const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
|
|
8587
|
-
const binding = ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction);
|
|
8680
|
+
const binding = ensureChatQueueBinding(turnCtx, actionGateHandler, confirmDestructiveAction);
|
|
8588
8681
|
const decision = binding.resolveSubmit({
|
|
8589
8682
|
text: submittedText,
|
|
8590
8683
|
isRunning: true
|
|
@@ -8596,13 +8689,13 @@ ${hint}` : hint;
|
|
|
8596
8689
|
fixedInput.repaint(buffer, cursorPos);
|
|
8597
8690
|
} else if (decision.kind === "queue-blocked") {
|
|
8598
8691
|
} else if (decision.kind === "noop" && !submittedText.trim() && binding.queueLength() > 0) {
|
|
8599
|
-
preemptAndAbortForDrain(binding);
|
|
8692
|
+
preemptAndAbortForDrain(binding, activeTurnAbort);
|
|
8600
8693
|
}
|
|
8601
8694
|
return;
|
|
8602
8695
|
}
|
|
8603
8696
|
if (!submittedText.trim() && chatQueueBinding && chatQueueBinding.queueLength() > 0) {
|
|
8604
8697
|
const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
|
|
8605
|
-
const manualBinding = ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction);
|
|
8698
|
+
const manualBinding = ensureChatQueueBinding(turnCtx, actionGateHandler, confirmDestructiveAction);
|
|
8606
8699
|
const drainedText = manualBinding.drainHeadForManualTurn();
|
|
8607
8700
|
if (drainedText !== null) {
|
|
8608
8701
|
buffer = "";
|