scream-code 0.7.3 → 0.7.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/dist/{app-I2MMzEhj.mjs → app-B3aIIRzk.mjs} +476 -185
- package/dist/main.mjs +1 -1
- package/package.json +1 -1
|
@@ -97094,7 +97094,8 @@ function isMutatingTool(toolName) {
|
|
|
97094
97094
|
* to try a different approach.
|
|
97095
97095
|
* - Storm Breaker: when a mutating tool is called repeatedly on the same target
|
|
97096
97096
|
* within a sliding window (default size 6), the call is suppressed entirely
|
|
97097
|
-
* and
|
|
97097
|
+
* and a friendly advisory result is returned to the model (non-error, so the
|
|
97098
|
+
* TUI renders it as normal output rather than a failed tool call).
|
|
97098
97099
|
*/
|
|
97099
97100
|
var ToolCallDeduplicator = class {
|
|
97100
97101
|
stepDeferreds = /* @__PURE__ */ new Map();
|
|
@@ -97154,10 +97155,7 @@ var ToolCallDeduplicator = class {
|
|
|
97154
97155
|
const key = makeKey(toolName, args);
|
|
97155
97156
|
if (isMutatingTool(toolName)) {
|
|
97156
97157
|
const priorCount = this.stormWindow.filter((e) => e.key === key).length;
|
|
97157
|
-
if (priorCount >= this.stormThreshold - 1) return {
|
|
97158
|
-
output: `Storm Breaker: ${toolName} 对同一目标已连续调用 ${String(priorCount + 1)} 次。请制定计划后再执行,或尝试不同的方法。`,
|
|
97159
|
-
isError: true
|
|
97160
|
-
};
|
|
97158
|
+
if (priorCount >= this.stormThreshold - 1) return { output: `Storm Breaker(风暴守护者):检测到无效循环调用风险——${toolName} 对同一目标已连续调用 ${String(priorCount + 1)} 次。建议先制定计划方案,或更换其他方法后再继续。` };
|
|
97161
97159
|
}
|
|
97162
97160
|
const index = this.stepCalls.length;
|
|
97163
97161
|
this.stepCalls.push(key);
|
|
@@ -122177,6 +122175,7 @@ async function runPrompt(opts, version, io = {}) {
|
|
|
122177
122175
|
workDir
|
|
122178
122176
|
});
|
|
122179
122177
|
let restorePromptSessionPermission = async () => {};
|
|
122178
|
+
let cleanupEphemeralSession = async () => {};
|
|
122180
122179
|
let removeTerminationCleanup;
|
|
122181
122180
|
let cleanupPromise;
|
|
122182
122181
|
const cleanupPromptRun = async () => {
|
|
@@ -122185,7 +122184,11 @@ async function runPrompt(opts, version, io = {}) {
|
|
|
122185
122184
|
try {
|
|
122186
122185
|
await restorePromptSessionPermission();
|
|
122187
122186
|
} finally {
|
|
122188
|
-
|
|
122187
|
+
try {
|
|
122188
|
+
await cleanupEphemeralSession();
|
|
122189
|
+
} finally {
|
|
122190
|
+
await harness.close();
|
|
122191
|
+
}
|
|
122189
122192
|
}
|
|
122190
122193
|
})();
|
|
122191
122194
|
await cleanupPromise;
|
|
@@ -122193,13 +122196,19 @@ async function runPrompt(opts, version, io = {}) {
|
|
|
122193
122196
|
removeTerminationCleanup = installPromptTerminationCleanup(promptProcess, cleanupPromptRun);
|
|
122194
122197
|
try {
|
|
122195
122198
|
await harness.ensureConfigFile();
|
|
122196
|
-
const { session, restorePermission } = await resolvePromptSession(harness, opts, workDir, (await harness.getConfig()).defaultModel, stderr, (restorePermission) => {
|
|
122199
|
+
const { session, resumed, restorePermission } = await resolvePromptSession(harness, opts, workDir, (await harness.getConfig()).defaultModel, stderr, (restorePermission) => {
|
|
122197
122200
|
restorePromptSessionPermission = restorePermission;
|
|
122198
122201
|
});
|
|
122199
122202
|
restorePromptSessionPermission = restorePermission;
|
|
122203
|
+
if (process.env["SCREAM_FUSIONPLAN_SUBAGENT"] === "1" && !resumed) {
|
|
122204
|
+
const ephemeralSessionId = session.id;
|
|
122205
|
+
cleanupEphemeralSession = async () => {
|
|
122206
|
+
await harness.deleteSession(ephemeralSessionId).catch(() => {});
|
|
122207
|
+
};
|
|
122208
|
+
}
|
|
122200
122209
|
const outputFormat = opts.outputFormat ?? "text";
|
|
122201
122210
|
await runPromptTurn(session, opts.prompt, outputFormat, stdout, stderr);
|
|
122202
|
-
writeResumeHint(session.id, outputFormat, stdout, stderr);
|
|
122211
|
+
if (process.env["SCREAM_FUSIONPLAN_SUBAGENT"] !== "1") writeResumeHint(session.id, outputFormat, stdout, stderr);
|
|
122203
122212
|
} finally {
|
|
122204
122213
|
await cleanupPromptRun();
|
|
122205
122214
|
}
|
|
@@ -122631,13 +122640,21 @@ const TuiConfigFileSchema = z.object({
|
|
|
122631
122640
|
enabled: z.boolean().optional(),
|
|
122632
122641
|
notification_condition: NotificationConditionSchema.optional()
|
|
122633
122642
|
}).optional(),
|
|
122634
|
-
like: TuiLikePreferencesSchema.optional()
|
|
122643
|
+
like: TuiLikePreferencesSchema.optional(),
|
|
122644
|
+
fusionPlan: z.object({
|
|
122645
|
+
timeoutSeconds: z.number().int().min(30).max(3600).optional(),
|
|
122646
|
+
workerCount: z.number().int().min(1).max(8).optional()
|
|
122647
|
+
}).optional()
|
|
122635
122648
|
});
|
|
122636
122649
|
const TuiConfigSchema = z.object({
|
|
122637
122650
|
theme: TuiThemeSchema,
|
|
122638
122651
|
editorCommand: z.string().nullable(),
|
|
122639
122652
|
notifications: NotificationsConfigSchema,
|
|
122640
|
-
like: TuiLikePreferencesSchema
|
|
122653
|
+
like: TuiLikePreferencesSchema,
|
|
122654
|
+
fusionPlan: z.object({
|
|
122655
|
+
timeoutSeconds: z.number().int().min(30).max(3600),
|
|
122656
|
+
workerCount: z.number().int().min(1).max(8)
|
|
122657
|
+
})
|
|
122641
122658
|
});
|
|
122642
122659
|
const DEFAULT_NOTIFICATIONS_CONFIG = {
|
|
122643
122660
|
enabled: true,
|
|
@@ -122647,13 +122664,16 @@ const DEFAULT_TUI_CONFIG = TuiConfigSchema.parse({
|
|
|
122647
122664
|
theme: "auto",
|
|
122648
122665
|
editorCommand: null,
|
|
122649
122666
|
notifications: DEFAULT_NOTIFICATIONS_CONFIG,
|
|
122650
|
-
like: {}
|
|
122667
|
+
like: {},
|
|
122668
|
+
fusionPlan: {
|
|
122669
|
+
timeoutSeconds: 600,
|
|
122670
|
+
workerCount: 3
|
|
122671
|
+
}
|
|
122651
122672
|
});
|
|
122652
122673
|
/**
|
|
122653
122674
|
* Thrown by `loadTuiConfig` when the on-disk TOML cannot be parsed.
|
|
122654
122675
|
* Carries `fallback` so the caller can recover without re-running the
|
|
122655
|
-
*
|
|
122656
|
-
* user-facing notice.
|
|
122676
|
+
* discovery code.
|
|
122657
122677
|
*/
|
|
122658
122678
|
var TuiConfigParseError = class extends Error {
|
|
122659
122679
|
name = "TuiConfigParseError";
|
|
@@ -122689,6 +122709,7 @@ async function saveTuiConfig(config, filePath = getTuiConfigPath()) {
|
|
|
122689
122709
|
function normalizeTuiConfig(config) {
|
|
122690
122710
|
const command = config.editor?.command?.trim();
|
|
122691
122711
|
const like = config.like ?? {};
|
|
122712
|
+
const fusionPlan = config.fusionPlan ?? {};
|
|
122692
122713
|
return TuiConfigSchema.parse({
|
|
122693
122714
|
theme: config.theme ?? DEFAULT_TUI_CONFIG.theme,
|
|
122694
122715
|
editorCommand: command === void 0 || command.length === 0 ? null : command,
|
|
@@ -122700,6 +122721,10 @@ function normalizeTuiConfig(config) {
|
|
|
122700
122721
|
nickname: normalizeOptionalString(like.nickname),
|
|
122701
122722
|
tone: normalizeOptionalString(like.tone),
|
|
122702
122723
|
other: normalizeOptionalString(like.other)
|
|
122724
|
+
},
|
|
122725
|
+
fusionPlan: {
|
|
122726
|
+
timeoutSeconds: fusionPlan.timeoutSeconds ?? DEFAULT_TUI_CONFIG.fusionPlan.timeoutSeconds,
|
|
122727
|
+
workerCount: fusionPlan.workerCount ?? DEFAULT_TUI_CONFIG.fusionPlan.workerCount
|
|
122703
122728
|
}
|
|
122704
122729
|
});
|
|
122705
122730
|
}
|
|
@@ -122729,6 +122754,10 @@ notification_condition = "${config.notifications.condition}" # "unfocused" | "al
|
|
|
122729
122754
|
nickname = "${nickname}"
|
|
122730
122755
|
tone = "${tone}"
|
|
122731
122756
|
other = "${other}"
|
|
122757
|
+
|
|
122758
|
+
[fusionPlan]
|
|
122759
|
+
timeoutSeconds = ${config.fusionPlan.timeoutSeconds} # 30..3600, default 600
|
|
122760
|
+
workerCount = ${config.fusionPlan.workerCount} # 1..8, default 3
|
|
122732
122761
|
`;
|
|
122733
122762
|
}
|
|
122734
122763
|
function escapeTomlBasicString(value) {
|
|
@@ -124385,6 +124414,52 @@ var ThemeSelectorComponent = class extends ChoicePickerComponent {
|
|
|
124385
124414
|
}
|
|
124386
124415
|
};
|
|
124387
124416
|
//#endregion
|
|
124417
|
+
//#region src/tui/utils/app-state.ts
|
|
124418
|
+
/** True when a model turn is in progress (waiting / thinking / composing). */
|
|
124419
|
+
function isStreaming(state) {
|
|
124420
|
+
return state.streamingPhase !== "idle";
|
|
124421
|
+
}
|
|
124422
|
+
/** True when the session is busy and should reject state-changing operations.
|
|
124423
|
+
* Covers both active streaming and context compaction. */
|
|
124424
|
+
function isBusy(state) {
|
|
124425
|
+
return isStreaming(state) || state.isCompacting;
|
|
124426
|
+
}
|
|
124427
|
+
//#endregion
|
|
124428
|
+
//#region src/utils/usage/usage-format.ts
|
|
124429
|
+
/**
|
|
124430
|
+
* Formatting helpers for the `/usage` slash command.
|
|
124431
|
+
*
|
|
124432
|
+
* Kept pure + ANSI-free so they're trivial to unit-test; the slash
|
|
124433
|
+
* command itself chalks the colour afterwards.
|
|
124434
|
+
*/
|
|
124435
|
+
function formatTokenCount$1(n) {
|
|
124436
|
+
if (!Number.isFinite(n) || n < 0) return "0";
|
|
124437
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
124438
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
124439
|
+
return String(Math.round(n));
|
|
124440
|
+
}
|
|
124441
|
+
/**
|
|
124442
|
+
* Build a `[███░░░░░░░]` style bar. Returns a plain-ASCII string with
|
|
124443
|
+
* `filled`/`empty` glyphs — colouring is the caller's responsibility.
|
|
124444
|
+
*/
|
|
124445
|
+
function renderProgressBar(ratio, width = 20, filled = "█", empty = "░") {
|
|
124446
|
+
const clamped = safeUsageRatio(ratio);
|
|
124447
|
+
const filledCount = Math.round(clamped * width);
|
|
124448
|
+
return filled.repeat(filledCount) + empty.repeat(Math.max(0, width - filledCount));
|
|
124449
|
+
}
|
|
124450
|
+
function safeUsageRatio(ratio) {
|
|
124451
|
+
return Number.isFinite(ratio) ? Math.max(0, Math.min(ratio, 1)) : 0;
|
|
124452
|
+
}
|
|
124453
|
+
/**
|
|
124454
|
+
* Map a usage ratio to a semantic colour token — the `/usage` renderer
|
|
124455
|
+
* translates these into palette hex values.
|
|
124456
|
+
*/
|
|
124457
|
+
function ratioSeverity(ratio) {
|
|
124458
|
+
if (ratio >= .85) return "danger";
|
|
124459
|
+
if (ratio >= .5) return "warn";
|
|
124460
|
+
return "ok";
|
|
124461
|
+
}
|
|
124462
|
+
//#endregion
|
|
124388
124463
|
//#region src/tui/constant/terminal.ts
|
|
124389
124464
|
const QUERY_TERMINAL_THEME = `[?996n`;
|
|
124390
124465
|
const TERMINAL_THEME_DARK = `[?997;1n`;
|
|
@@ -124714,41 +124789,6 @@ function resolveThemeSync(theme) {
|
|
|
124714
124789
|
return theme;
|
|
124715
124790
|
}
|
|
124716
124791
|
//#endregion
|
|
124717
|
-
//#region src/utils/usage/usage-format.ts
|
|
124718
|
-
/**
|
|
124719
|
-
* Formatting helpers for the `/usage` slash command.
|
|
124720
|
-
*
|
|
124721
|
-
* Kept pure + ANSI-free so they're trivial to unit-test; the slash
|
|
124722
|
-
* command itself chalks the colour afterwards.
|
|
124723
|
-
*/
|
|
124724
|
-
function formatTokenCount$1(n) {
|
|
124725
|
-
if (!Number.isFinite(n) || n < 0) return "0";
|
|
124726
|
-
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
124727
|
-
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
124728
|
-
return String(Math.round(n));
|
|
124729
|
-
}
|
|
124730
|
-
/**
|
|
124731
|
-
* Build a `[███░░░░░░░]` style bar. Returns a plain-ASCII string with
|
|
124732
|
-
* `filled`/`empty` glyphs — colouring is the caller's responsibility.
|
|
124733
|
-
*/
|
|
124734
|
-
function renderProgressBar(ratio, width = 20, filled = "█", empty = "░") {
|
|
124735
|
-
const clamped = safeUsageRatio(ratio);
|
|
124736
|
-
const filledCount = Math.round(clamped * width);
|
|
124737
|
-
return filled.repeat(filledCount) + empty.repeat(Math.max(0, width - filledCount));
|
|
124738
|
-
}
|
|
124739
|
-
function safeUsageRatio(ratio) {
|
|
124740
|
-
return Number.isFinite(ratio) ? Math.max(0, Math.min(ratio, 1)) : 0;
|
|
124741
|
-
}
|
|
124742
|
-
/**
|
|
124743
|
-
* Map a usage ratio to a semantic colour token — the `/usage` renderer
|
|
124744
|
-
* translates these into palette hex values.
|
|
124745
|
-
*/
|
|
124746
|
-
function ratioSeverity(ratio) {
|
|
124747
|
-
if (ratio >= .85) return "danger";
|
|
124748
|
-
if (ratio >= .5) return "warn";
|
|
124749
|
-
return "ok";
|
|
124750
|
-
}
|
|
124751
|
-
//#endregion
|
|
124752
124792
|
//#region src/tui/components/messages/usage-panel.ts
|
|
124753
124793
|
const LEFT_MARGIN$1 = 2;
|
|
124754
124794
|
const SIDE_PADDING$1 = 1;
|
|
@@ -125041,6 +125081,46 @@ async function loadManagedUsageReport(host) {
|
|
|
125041
125081
|
}
|
|
125042
125082
|
//#endregion
|
|
125043
125083
|
//#region src/tui/commands/config.ts
|
|
125084
|
+
/**
|
|
125085
|
+
* Storm Breaker guard for model switches. Returns the (currentTokens,
|
|
125086
|
+
* maxContextTokens) pair when switching to `alias` would overflow its
|
|
125087
|
+
* context window, or `null` when the switch is safe / unknown.
|
|
125088
|
+
*
|
|
125089
|
+
* Exported (and kept pure) so the guard is unit-testable without spinning
|
|
125090
|
+
* up a full ScreamTUI + session mock.
|
|
125091
|
+
*/
|
|
125092
|
+
function contextOverflowForModel(state, alias) {
|
|
125093
|
+
const targetModel = state.availableModels[alias];
|
|
125094
|
+
if (targetModel === void 0) return null;
|
|
125095
|
+
const currentTokens = state.contextTokens;
|
|
125096
|
+
if (currentTokens <= 0) return null;
|
|
125097
|
+
if (currentTokens <= targetModel.maxContextSize) return null;
|
|
125098
|
+
return {
|
|
125099
|
+
currentTokens,
|
|
125100
|
+
maxContextTokens: targetModel.maxContextSize
|
|
125101
|
+
};
|
|
125102
|
+
}
|
|
125103
|
+
/**
|
|
125104
|
+
* Storm Breaker guard for /compact. Returns the (currentTokens,
|
|
125105
|
+
* maxContextTokens, ratio) triple when context usage is below 5% — compressing
|
|
125106
|
+
* at this point yields no benefit and discards useful history. Returns `null`
|
|
125107
|
+
* when compression is legitimate or when the window size is unknown.
|
|
125108
|
+
*
|
|
125109
|
+
* Exported (and kept pure) so the guard is unit-testable without a session.
|
|
125110
|
+
*/
|
|
125111
|
+
function shouldGuardCompaction(state) {
|
|
125112
|
+
const max = state.maxContextTokens;
|
|
125113
|
+
if (max <= 0) return null;
|
|
125114
|
+
const currentTokens = state.contextTokens;
|
|
125115
|
+
if (currentTokens <= 0) return null;
|
|
125116
|
+
const ratio = currentTokens / max;
|
|
125117
|
+
if (ratio >= .05) return null;
|
|
125118
|
+
return {
|
|
125119
|
+
currentTokens,
|
|
125120
|
+
maxContextTokens: max,
|
|
125121
|
+
ratio
|
|
125122
|
+
};
|
|
125123
|
+
}
|
|
125044
125124
|
async function handlePlanCommand(host, args) {
|
|
125045
125125
|
const session = host.session;
|
|
125046
125126
|
if (session === void 0) {
|
|
@@ -125211,6 +125291,12 @@ async function handleCompactCommand(host, args) {
|
|
|
125211
125291
|
return;
|
|
125212
125292
|
}
|
|
125213
125293
|
const customInstruction = args.trim() || void 0;
|
|
125294
|
+
const guard = shouldGuardCompaction(host.state.appState);
|
|
125295
|
+
if (guard !== null) {
|
|
125296
|
+
const pct = (guard.ratio * 100).toFixed(1);
|
|
125297
|
+
host.showNotice("Storm Breaker(风暴守护者)", `当前上下文仅 ${formatTokenCount$1(guard.currentTokens)} / ${formatTokenCount$1(guard.maxContextTokens)}(${pct}%),压缩无收益。建议继续对话,待上下文增长至 5% 以上再执行 /compact。`);
|
|
125298
|
+
return;
|
|
125299
|
+
}
|
|
125214
125300
|
await session.compact({ instruction: customInstruction });
|
|
125215
125301
|
}
|
|
125216
125302
|
async function handleEditorCommand(host, args) {
|
|
@@ -125270,7 +125356,8 @@ async function applyEditorChoice(host, value) {
|
|
|
125270
125356
|
theme: host.state.appState.theme,
|
|
125271
125357
|
editorCommand,
|
|
125272
125358
|
notifications: host.state.appState.notifications,
|
|
125273
|
-
like: host.state.appState.like
|
|
125359
|
+
like: host.state.appState.like,
|
|
125360
|
+
fusionPlan: host.state.appState.fusionPlan
|
|
125274
125361
|
});
|
|
125275
125362
|
} catch (error) {
|
|
125276
125363
|
host.showStatus(`Failed to save editor: ${formatErrorMessage(error)}`, host.state.theme.colors.error);
|
|
@@ -125301,13 +125388,18 @@ function showModelPicker(host, selectedValue = host.state.appState.model) {
|
|
|
125301
125388
|
}));
|
|
125302
125389
|
}
|
|
125303
125390
|
async function performModelSwitch(host, alias, thinkingLevel) {
|
|
125304
|
-
if (host.state.appState
|
|
125391
|
+
if (isBusy(host.state.appState)) {
|
|
125305
125392
|
host.showError("Cannot switch models while streaming — press Esc or Ctrl-C first.");
|
|
125306
125393
|
return;
|
|
125307
125394
|
}
|
|
125308
125395
|
const prevModel = host.state.appState.model;
|
|
125309
125396
|
const prevThinkingLevel = host.state.appState.thinkingLevel;
|
|
125310
125397
|
const runtimeChanged = alias !== prevModel || thinkingLevel !== prevThinkingLevel;
|
|
125398
|
+
const overflow = alias !== prevModel ? contextOverflowForModel(host.state.appState, alias) : null;
|
|
125399
|
+
if (overflow !== null) {
|
|
125400
|
+
host.showNotice("Storm Breaker(风暴守护者)", `无法切换到模型「${alias}」:当前会话上下文 ${formatTokenCount$1(overflow.currentTokens)} 已超出该模型上限 ${formatTokenCount$1(overflow.maxContextTokens)}。建议先执行 /compact 压缩上下文,或选择上下文窗口更大的模型。`);
|
|
125401
|
+
return;
|
|
125402
|
+
}
|
|
125311
125403
|
const session = host.session;
|
|
125312
125404
|
try {
|
|
125313
125405
|
if (session === void 0 && runtimeChanged) await host.authFlow.activateModelAfterLogin(alias, thinkingLevel);
|
|
@@ -125376,7 +125468,8 @@ async function applyThemeChoice(host, theme) {
|
|
|
125376
125468
|
theme,
|
|
125377
125469
|
editorCommand: host.state.appState.editorCommand,
|
|
125378
125470
|
notifications: host.state.appState.notifications,
|
|
125379
|
-
like: host.state.appState.like
|
|
125471
|
+
like: host.state.appState.like,
|
|
125472
|
+
fusionPlan: host.state.appState.fusionPlan
|
|
125380
125473
|
});
|
|
125381
125474
|
} catch (error) {
|
|
125382
125475
|
host.showStatus(`Failed to save theme: ${formatErrorMessage(error)}`, host.state.theme.colors.error);
|
|
@@ -125902,6 +125995,13 @@ var GoalStatusMessageComponent = class {
|
|
|
125902
125995
|
}
|
|
125903
125996
|
};
|
|
125904
125997
|
//#endregion
|
|
125998
|
+
//#region src/tui/utils/goal-loop-conflict.ts
|
|
125999
|
+
function detectGoalLoopConflict(state, action) {
|
|
126000
|
+
if (action === "enable_loop" && state.goalActive) return "goal_active";
|
|
126001
|
+
if (action === "enable_goal" && state.loopModeEnabled) return "loop_active";
|
|
126002
|
+
return null;
|
|
126003
|
+
}
|
|
126004
|
+
//#endregion
|
|
125905
126005
|
//#region src/tui/commands/goal.ts
|
|
125906
126006
|
const GOAL_STATUS_DISMISS_MS = 1e4;
|
|
125907
126007
|
let activeGoalPanel;
|
|
@@ -125973,10 +126073,14 @@ async function createGoal(host, parsed) {
|
|
|
125973
126073
|
host.showError("没有活跃的会话。");
|
|
125974
126074
|
return;
|
|
125975
126075
|
}
|
|
126076
|
+
if (detectGoalLoopConflict(host.state.appState, "enable_goal") === "loop_active") {
|
|
126077
|
+
host.showNotice("Storm Breaker(风暴守护者)", "当前已开启循环模式(/loop)。/goal 与 /loop 语义冲突:loop 每轮重置上下文,会破坏 goal 的工作笔记迭代。请先 /loop 关闭循环模式,再设置目标。");
|
|
126078
|
+
return;
|
|
126079
|
+
}
|
|
125976
126080
|
try {
|
|
125977
126081
|
await session.createGoal(parsed.objective, { replace: parsed.replace });
|
|
125978
126082
|
host.showStatus(`🎯 目标已设置:${parsed.objective}`);
|
|
125979
|
-
if (host.state.appState
|
|
126083
|
+
if (!isBusy(host.state.appState)) host.sendQueuedMessage(session, {
|
|
125980
126084
|
text: parsed.objective,
|
|
125981
126085
|
agentId: void 0
|
|
125982
126086
|
});
|
|
@@ -126020,7 +126124,7 @@ async function resumeGoal(host) {
|
|
|
126020
126124
|
}
|
|
126021
126125
|
await session.updateGoalStatus("active");
|
|
126022
126126
|
host.showStatus("🎯 目标已恢复。");
|
|
126023
|
-
if (host.state.appState
|
|
126127
|
+
if (!isBusy(host.state.appState)) host.sendQueuedMessage(session, {
|
|
126024
126128
|
text: "继续执行当前目标。",
|
|
126025
126129
|
agentId: void 0
|
|
126026
126130
|
});
|
|
@@ -126081,9 +126185,9 @@ function dismissGoalPanel(host) {
|
|
|
126081
126185
|
}
|
|
126082
126186
|
//#endregion
|
|
126083
126187
|
//#region src/tui/components/chrome/welcome.ts
|
|
126084
|
-
const HUE_STOPS = 24;
|
|
126085
|
-
const SUB_STEPS = 5;
|
|
126086
|
-
const BREATHE_STEPS = HUE_STOPS * SUB_STEPS;
|
|
126188
|
+
const HUE_STOPS$1 = 24;
|
|
126189
|
+
const SUB_STEPS$1 = 5;
|
|
126190
|
+
const BREATHE_STEPS$1 = HUE_STOPS$1 * SUB_STEPS$1;
|
|
126087
126191
|
const BREATHE_INTERVAL_MS$1 = 40;
|
|
126088
126192
|
const WELCOME_TIPS = [
|
|
126089
126193
|
"/config 配置模型",
|
|
@@ -126107,7 +126211,7 @@ function hexToRgb$2(hex) {
|
|
|
126107
126211
|
parseInt(hex.slice(5, 7), 16)
|
|
126108
126212
|
];
|
|
126109
126213
|
}
|
|
126110
|
-
function rgbToHsl$
|
|
126214
|
+
function rgbToHsl$2(r, g, b) {
|
|
126111
126215
|
const rf = r / 255, gf = g / 255, bf = b / 255;
|
|
126112
126216
|
const max = Math.max(rf, gf, bf), min = Math.min(rf, gf, bf);
|
|
126113
126217
|
const l = (max + min) / 2;
|
|
@@ -126128,7 +126232,7 @@ function rgbToHsl$1(r, g, b) {
|
|
|
126128
126232
|
l * 100
|
|
126129
126233
|
];
|
|
126130
126234
|
}
|
|
126131
|
-
function hslToRgb(h, s, l) {
|
|
126235
|
+
function hslToRgb$1(h, s, l) {
|
|
126132
126236
|
const hf = (h % 360 + 360) % 360 / 360;
|
|
126133
126237
|
const sf = s / 100, lf = l / 100;
|
|
126134
126238
|
if (sf === 0) {
|
|
@@ -126159,13 +126263,13 @@ function rgbToHex(r, g, b) {
|
|
|
126159
126263
|
const c = (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0");
|
|
126160
126264
|
return `#${c(r)}${c(g)}${c(b)}`;
|
|
126161
126265
|
}
|
|
126162
|
-
function buildBreathingPalette(primaryHex, hueStops, subSteps) {
|
|
126266
|
+
function buildBreathingPalette$1(primaryHex, hueStops, subSteps) {
|
|
126163
126267
|
const [r, g, b] = hexToRgb$2(primaryHex);
|
|
126164
|
-
const [baseHue] = rgbToHsl$
|
|
126268
|
+
const [baseHue] = rgbToHsl$2(r, g, b);
|
|
126165
126269
|
const steps = hueStops * subSteps;
|
|
126166
126270
|
const palette = [];
|
|
126167
126271
|
for (let i = 0; i < steps; i++) {
|
|
126168
|
-
const [rr, gg, bb] = hslToRgb((baseHue + i / steps * 360) % 360, 90, 70);
|
|
126272
|
+
const [rr, gg, bb] = hslToRgb$1((baseHue + i / steps * 360) % 360, 90, 70);
|
|
126169
126273
|
palette.push(rgbToHex(rr, gg, bb));
|
|
126170
126274
|
}
|
|
126171
126275
|
return palette;
|
|
@@ -126203,7 +126307,7 @@ var WelcomeComponent = class {
|
|
|
126203
126307
|
this.colors = colors;
|
|
126204
126308
|
this.ui = ui;
|
|
126205
126309
|
this.recentSessions = recentSessions;
|
|
126206
|
-
this.breathePalette = buildBreathingPalette(colors.primary, HUE_STOPS, SUB_STEPS);
|
|
126310
|
+
this.breathePalette = buildBreathingPalette$1(colors.primary, HUE_STOPS$1, SUB_STEPS$1);
|
|
126207
126311
|
this.startBreathing();
|
|
126208
126312
|
}
|
|
126209
126313
|
stopBreathing() {
|
|
@@ -126218,7 +126322,7 @@ var WelcomeComponent = class {
|
|
|
126218
126322
|
}
|
|
126219
126323
|
startBreathing() {
|
|
126220
126324
|
this.breatheTimer = setInterval(() => {
|
|
126221
|
-
this.breatheFrame = (this.breatheFrame + 1) % BREATHE_STEPS;
|
|
126325
|
+
this.breatheFrame = (this.breatheFrame + 1) % BREATHE_STEPS$1;
|
|
126222
126326
|
this.ui.requestRender();
|
|
126223
126327
|
}, BREATHE_INTERVAL_MS$1);
|
|
126224
126328
|
}
|
|
@@ -129342,7 +129446,7 @@ function getTranscriptComponentEntry(component) {
|
|
|
129342
129446
|
//#endregion
|
|
129343
129447
|
//#region src/tui/commands/revoke.ts
|
|
129344
129448
|
async function handleRevokeCommand(host, args = "") {
|
|
129345
|
-
if (host.state.appState
|
|
129449
|
+
if (isBusy(host.state.appState)) {
|
|
129346
129450
|
host.showError("无法在 streaming 中撤回 — 请先按 Esc 或 Ctrl-C 取消。");
|
|
129347
129451
|
return;
|
|
129348
129452
|
}
|
|
@@ -130083,7 +130187,7 @@ async function writeUpdateCache(value, filePath = getUpdateStateFile()) {
|
|
|
130083
130187
|
}
|
|
130084
130188
|
//#endregion
|
|
130085
130189
|
//#region src/cli/update/cdn.ts
|
|
130086
|
-
const NPM_TIMEOUT_MS =
|
|
130190
|
+
const NPM_TIMEOUT_MS = 3e3;
|
|
130087
130191
|
/**
|
|
130088
130192
|
* Resolve the npm executable name for the current platform.
|
|
130089
130193
|
*
|
|
@@ -130245,7 +130349,7 @@ async function runInstallStep(cmd, args, cwd, label, timeoutMs = INSTALL_TIMEOUT
|
|
|
130245
130349
|
});
|
|
130246
130350
|
}
|
|
130247
130351
|
async function handleUpdateCommand(host) {
|
|
130248
|
-
if (host.state.appState
|
|
130352
|
+
if (isBusy(host.state.appState)) {
|
|
130249
130353
|
host.showError("请在空闲时执行更新。");
|
|
130250
130354
|
return;
|
|
130251
130355
|
}
|
|
@@ -130941,7 +131045,7 @@ async function handleMakeSkillCommand(host, args) {
|
|
|
130941
131045
|
host.showError(NO_ACTIVE_SESSION_MESSAGE);
|
|
130942
131046
|
return;
|
|
130943
131047
|
}
|
|
130944
|
-
if (host.state.appState
|
|
131048
|
+
if (isBusy(host.state.appState)) {
|
|
130945
131049
|
host.showError("请等待当前回复完成后再使用 /make-skill");
|
|
130946
131050
|
return;
|
|
130947
131051
|
}
|
|
@@ -131966,6 +132070,10 @@ async function handleLoopCommand(host, args) {
|
|
|
131966
132070
|
host.showError(parsed);
|
|
131967
132071
|
return;
|
|
131968
132072
|
}
|
|
132073
|
+
if (detectGoalLoopConflict(host.state.appState, "enable_loop") === "goal_active") {
|
|
132074
|
+
host.showNotice("Storm Breaker(风暴守护者)", "当前已有激活的目标(/goal)。/loop 与 /goal 语义冲突:loop 每轮重置上下文,会破坏 goal 的工作笔记迭代。请先 /goal off 关闭目标,再开启循环模式。");
|
|
132075
|
+
return;
|
|
132076
|
+
}
|
|
131969
132077
|
const loopLimit = createLoopLimitRuntime(parsed.limit);
|
|
131970
132078
|
host.setAppState({
|
|
131971
132079
|
loopModeEnabled: true,
|
|
@@ -131973,7 +132081,8 @@ async function handleLoopCommand(host, args) {
|
|
|
131973
132081
|
loopLimit,
|
|
131974
132082
|
loopVerifier: parsed.verifier ? makeVerifier(parsed.verifier.command) : void 0,
|
|
131975
132083
|
loopIteration: 0,
|
|
131976
|
-
loopLastVerifyPassed: void 0
|
|
132084
|
+
loopLastVerifyPassed: void 0,
|
|
132085
|
+
loopVerifying: false
|
|
131977
132086
|
});
|
|
131978
132087
|
await ensureAutoPermission(host);
|
|
131979
132088
|
const limitSuffix = parsed.limit ? ` 限制:${describeLoopLimit(parsed.limit)}。` : "";
|
|
@@ -131998,7 +132107,8 @@ function disableLoopMode(host, message) {
|
|
|
131998
132107
|
loopLimit: void 0,
|
|
131999
132108
|
loopVerifier: void 0,
|
|
132000
132109
|
loopIteration: 0,
|
|
132001
|
-
loopLastVerifyPassed: void 0
|
|
132110
|
+
loopLastVerifyPassed: void 0,
|
|
132111
|
+
loopVerifying: false
|
|
132002
132112
|
});
|
|
132003
132113
|
if (message) host.showStatus(message);
|
|
132004
132114
|
}
|
|
@@ -133024,7 +133134,7 @@ var EditorKeyboardController = class {
|
|
|
133024
133134
|
this.cancelCurrentCompaction();
|
|
133025
133135
|
return;
|
|
133026
133136
|
}
|
|
133027
|
-
if (host.state.appState
|
|
133137
|
+
if (isStreaming(host.state.appState)) {
|
|
133028
133138
|
this.clearPendingExit();
|
|
133029
133139
|
this.cancelCurrentStream();
|
|
133030
133140
|
return;
|
|
@@ -133055,7 +133165,7 @@ var EditorKeyboardController = class {
|
|
|
133055
133165
|
this.cancelCurrentCompaction();
|
|
133056
133166
|
return;
|
|
133057
133167
|
}
|
|
133058
|
-
if (host.state.appState
|
|
133168
|
+
if (isStreaming(host.state.appState)) {
|
|
133059
133169
|
this.cancelCurrentStream();
|
|
133060
133170
|
return;
|
|
133061
133171
|
}
|
|
@@ -133081,7 +133191,7 @@ var EditorKeyboardController = class {
|
|
|
133081
133191
|
};
|
|
133082
133192
|
editor.onTogglePlanExpand = () => host.togglePlanExpansion();
|
|
133083
133193
|
editor.onCtrlS = () => {
|
|
133084
|
-
if (host.state.appState
|
|
133194
|
+
if (!isBusy(host.state.appState)) return;
|
|
133085
133195
|
const text = editor.getText().trim();
|
|
133086
133196
|
const queuedTexts = host.state.queuedMessages.map((m) => m.text);
|
|
133087
133197
|
host.clearQueuedMessages();
|
|
@@ -133104,7 +133214,7 @@ var EditorKeyboardController = class {
|
|
|
133104
133214
|
host.cancelPendingMemoryExtraction();
|
|
133105
133215
|
};
|
|
133106
133216
|
editor.onUpArrowEmpty = () => {
|
|
133107
|
-
if (
|
|
133217
|
+
if (!isBusy(host.state.appState)) return false;
|
|
133108
133218
|
const recalled = host.recallLastQueued();
|
|
133109
133219
|
if (recalled !== void 0) {
|
|
133110
133220
|
editor.setText(recalled);
|
|
@@ -133520,6 +133630,53 @@ function nextTranscriptId() {
|
|
|
133520
133630
|
return `entry-${String(transcriptIdCounter)}`;
|
|
133521
133631
|
}
|
|
133522
133632
|
//#endregion
|
|
133633
|
+
//#region src/tui/streaming-phase.ts
|
|
133634
|
+
/**
|
|
133635
|
+
* Returns true when transitioning `from → to` is a valid directed edge.
|
|
133636
|
+
* Self-loops are no-ops and return false so callers can use this as an
|
|
133637
|
+
* idempotency guard:
|
|
133638
|
+
* if (canTransitionTo(state.appState.streamingPhase, 'thinking')) {
|
|
133639
|
+
* setAppState({ streamingPhase: 'thinking' });
|
|
133640
|
+
* }
|
|
133641
|
+
*/
|
|
133642
|
+
function canTransitionTo(from, to) {
|
|
133643
|
+
return from !== to;
|
|
133644
|
+
}
|
|
133645
|
+
//#endregion
|
|
133646
|
+
//#region src/tui/utils/compaction-anomaly.ts
|
|
133647
|
+
/** Rapid-refire window: < 30s between end of previous compaction and start of next. */
|
|
133648
|
+
const RAPID_REFIRE_MS = 3e4;
|
|
133649
|
+
/** First-step-blowup: ratio of current tokens to window when first auto-compaction fires. */
|
|
133650
|
+
const FIRST_STEP_BLOWUP_RATIO = .7;
|
|
133651
|
+
/**
|
|
133652
|
+
* Inspects one auto-compaction start for pathological patterns. Returns `null`
|
|
133653
|
+
* when the compaction looks routine.
|
|
133654
|
+
*
|
|
133655
|
+
* Two signals:
|
|
133656
|
+
* - `rapid_refire`: previous auto-compaction finished < 30s ago — model is likely
|
|
133657
|
+
* emitting a runaway stream or looping on a tool that explodes context.
|
|
133658
|
+
* - `first_step_blowup`: very first auto-compaction of the session, and context
|
|
133659
|
+
* is already above 70% — likely a giant system prompt, a huge file read, or
|
|
133660
|
+
* similar one-shot inflation.
|
|
133661
|
+
*/
|
|
133662
|
+
function detectCompactionAnomaly(input) {
|
|
133663
|
+
if (input.lastFinishedAt !== void 0) {
|
|
133664
|
+
const elapsed = input.now - input.lastFinishedAt;
|
|
133665
|
+
if (elapsed >= 0 && elapsed < RAPID_REFIRE_MS) return {
|
|
133666
|
+
kind: "rapid_refire",
|
|
133667
|
+
detail: `上次压缩结束仅 ${(elapsed / 1e3).toFixed(1)} 秒后再次触发自动压缩。`
|
|
133668
|
+
};
|
|
133669
|
+
}
|
|
133670
|
+
if (input.autoCompactionCount === 0 && input.maxContextTokens > 0) {
|
|
133671
|
+
const ratio = input.currentTokens / input.maxContextTokens;
|
|
133672
|
+
if (ratio >= FIRST_STEP_BLOWUP_RATIO) return {
|
|
133673
|
+
kind: "first_step_blowup",
|
|
133674
|
+
detail: `会话首次自动压缩时上下文已达 ${(ratio * 100).toFixed(0)}%,可能存在巨型文件读取或初始 prompt 过大。`
|
|
133675
|
+
};
|
|
133676
|
+
}
|
|
133677
|
+
return null;
|
|
133678
|
+
}
|
|
133679
|
+
//#endregion
|
|
133523
133680
|
//#region src/tui/controllers/session-event-handler.ts
|
|
133524
133681
|
var SessionEventHandler = class {
|
|
133525
133682
|
host;
|
|
@@ -133528,6 +133685,17 @@ var SessionEventHandler = class {
|
|
|
133528
133685
|
}
|
|
133529
133686
|
backgroundAgentMetadata = /* @__PURE__ */ new Map();
|
|
133530
133687
|
backgroundTasks = /* @__PURE__ */ new Map();
|
|
133688
|
+
/**
|
|
133689
|
+
* Compaction trigger of the in-flight compaction, captured in
|
|
133690
|
+
* `handleCompactionBegin` (CompactionStartedEvent carries `trigger`) and
|
|
133691
|
+
* read in `handleCompactionEnd` (CompactionCompletedEvent does not). Used so
|
|
133692
|
+
* Storm Breaker cadence tracking only counts auto-compactions — manual
|
|
133693
|
+
* /compact must not inflate `autoCompactionCount` or set
|
|
133694
|
+
* `lastCompactionFinishedAt`, otherwise the first genuine auto-compaction
|
|
133695
|
+
* would silently fail to trigger `first_step_blowup`, and a manual compact
|
|
133696
|
+
* followed shortly by an auto one would falsely trip `rapid_refire`.
|
|
133697
|
+
*/
|
|
133698
|
+
lastCompactionTrigger;
|
|
133531
133699
|
backgroundTaskTranscriptedTerminal = /* @__PURE__ */ new Set();
|
|
133532
133700
|
subagentInfo = /* @__PURE__ */ new Map();
|
|
133533
133701
|
renderedSkillActivationIds = /* @__PURE__ */ new Set();
|
|
@@ -133777,14 +133945,10 @@ var SessionEventHandler = class {
|
|
|
133777
133945
|
this.host.streamingUI.resetToolUi();
|
|
133778
133946
|
this.host.streamingUI.setStep(0);
|
|
133779
133947
|
this.host.patchLivePane({
|
|
133780
|
-
mode: "waiting",
|
|
133781
133948
|
pendingApproval: null,
|
|
133782
133949
|
pendingQuestion: null
|
|
133783
133950
|
});
|
|
133784
|
-
this.host.setAppState({
|
|
133785
|
-
streamingPhase: "waiting",
|
|
133786
|
-
streamingStartTime: Date.now()
|
|
133787
|
-
});
|
|
133951
|
+
this.host.setAppState({ streamingPhase: "waiting" });
|
|
133788
133952
|
}
|
|
133789
133953
|
handleTurnEnd(event, sendQueued) {
|
|
133790
133954
|
this.host.streamingUI.flushNow();
|
|
@@ -133813,7 +133977,8 @@ var SessionEventHandler = class {
|
|
|
133813
133977
|
loopLimit: void 0,
|
|
133814
133978
|
loopVerifier: void 0,
|
|
133815
133979
|
loopIteration: 0,
|
|
133816
|
-
loopLastVerifyPassed: void 0
|
|
133980
|
+
loopLastVerifyPassed: void 0,
|
|
133981
|
+
loopVerifying: false
|
|
133817
133982
|
});
|
|
133818
133983
|
this.host.showStatus(message);
|
|
133819
133984
|
}
|
|
@@ -133824,14 +133989,22 @@ var SessionEventHandler = class {
|
|
|
133824
133989
|
this.host.setAppState({ loopIteration: currentIteration });
|
|
133825
133990
|
const verifier = state.loopVerifier;
|
|
133826
133991
|
if (verifier) {
|
|
133992
|
+
this.host.setAppState({ loopVerifying: true });
|
|
133827
133993
|
const result = await runShellVerifier(verifier, state.workDir);
|
|
133828
133994
|
const after = this.host.state.appState;
|
|
133829
|
-
if (!after.loopModeEnabled || after.loopPrompt !== loopPrompt)
|
|
133995
|
+
if (!after.loopModeEnabled || after.loopPrompt !== loopPrompt) {
|
|
133996
|
+
this.host.setAppState({ loopVerifying: false });
|
|
133997
|
+
return;
|
|
133998
|
+
}
|
|
133830
133999
|
if (result.passed) {
|
|
134000
|
+
this.host.setAppState({ loopVerifying: false });
|
|
133831
134001
|
this.disableLoop(`✓ 验证通过,循环结束(${currentIteration} 次迭代)。`);
|
|
133832
134002
|
return;
|
|
133833
134003
|
}
|
|
133834
|
-
this.host.setAppState({
|
|
134004
|
+
this.host.setAppState({
|
|
134005
|
+
loopVerifying: false,
|
|
134006
|
+
loopLastVerifyPassed: false
|
|
134007
|
+
});
|
|
133835
134008
|
}
|
|
133836
134009
|
if (!consumeLoopLimitIteration(this.host.state.appState.loopLimit)) {
|
|
133837
134010
|
const reason = this.host.state.appState.loopLimit?.kind === "duration" ? "时间" : "次数";
|
|
@@ -133845,16 +134018,12 @@ var SessionEventHandler = class {
|
|
|
133845
134018
|
this.host.streamingUI.flushNow();
|
|
133846
134019
|
this.host.streamingUI.setStep(event.step);
|
|
133847
134020
|
this.host.streamingUI.resetToolUi();
|
|
133848
|
-
this.host.streamingUI.finalizeLiveTextBuffers(
|
|
134021
|
+
this.host.streamingUI.finalizeLiveTextBuffers();
|
|
133849
134022
|
this.host.patchLivePane({
|
|
133850
|
-
mode: "waiting",
|
|
133851
134023
|
pendingApproval: null,
|
|
133852
134024
|
pendingQuestion: null
|
|
133853
134025
|
});
|
|
133854
|
-
this.host.setAppState({
|
|
133855
|
-
streamingPhase: "waiting",
|
|
133856
|
-
streamingStartTime: Date.now()
|
|
133857
|
-
});
|
|
134026
|
+
this.host.setAppState({ streamingPhase: "waiting" });
|
|
133858
134027
|
}
|
|
133859
134028
|
handleStepCompleted(event) {
|
|
133860
134029
|
this.host.streamingUI.flushNow();
|
|
@@ -133878,7 +134047,7 @@ var SessionEventHandler = class {
|
|
|
133878
134047
|
handleStepInterrupted(event) {
|
|
133879
134048
|
this.host.streamingUI.flushNow();
|
|
133880
134049
|
this.host.streamingUI.resetToolUi();
|
|
133881
|
-
this.host.streamingUI.finalizeLiveTextBuffers(
|
|
134050
|
+
this.host.streamingUI.finalizeLiveTextBuffers();
|
|
133882
134051
|
const reason = event.reason;
|
|
133883
134052
|
if (reason === "error") return;
|
|
133884
134053
|
if (reason === "aborted" || reason === void 0 || reason === "") {
|
|
@@ -133890,31 +134059,23 @@ var SessionEventHandler = class {
|
|
|
133890
134059
|
handleThinkingDelta(event) {
|
|
133891
134060
|
const { state, streamingUI } = this.host;
|
|
133892
134061
|
streamingUI.appendThinkingDelta(event.delta);
|
|
133893
|
-
this.host.
|
|
133894
|
-
if (state.appState.streamingPhase !== "thinking") this.host.setAppState({
|
|
133895
|
-
streamingPhase: "thinking",
|
|
133896
|
-
streamingStartTime: Date.now()
|
|
133897
|
-
});
|
|
134062
|
+
if (canTransitionTo(state.appState.streamingPhase, "thinking")) this.host.setAppState({ streamingPhase: "thinking" });
|
|
133898
134063
|
streamingUI.scheduleFlush();
|
|
133899
134064
|
}
|
|
133900
134065
|
handleAssistantDelta(event) {
|
|
133901
134066
|
const { state, streamingUI } = this.host;
|
|
133902
|
-
if (streamingUI.hasThinkingDraft()) streamingUI.flushThinkingToTranscript(
|
|
134067
|
+
if (streamingUI.hasThinkingDraft()) streamingUI.flushThinkingToTranscript();
|
|
133903
134068
|
streamingUI.appendAssistantDelta(event.delta);
|
|
133904
134069
|
this.host.patchLivePane({
|
|
133905
|
-
mode: "idle",
|
|
133906
134070
|
pendingApproval: null,
|
|
133907
134071
|
pendingQuestion: null
|
|
133908
134072
|
});
|
|
133909
|
-
if (state.appState.streamingPhase
|
|
133910
|
-
streamingPhase: "composing",
|
|
133911
|
-
streamingStartTime: Date.now()
|
|
133912
|
-
});
|
|
134073
|
+
if (canTransitionTo(state.appState.streamingPhase, "composing")) this.host.setAppState({ streamingPhase: "composing" });
|
|
133913
134074
|
streamingUI.scheduleFlush();
|
|
133914
134075
|
}
|
|
133915
134076
|
handleHookResult(event) {
|
|
133916
134077
|
this.host.streamingUI.flushNow();
|
|
133917
|
-
if (this.host.streamingUI.hasThinkingDraft()) this.host.streamingUI.flushThinkingToTranscript(
|
|
134078
|
+
if (this.host.streamingUI.hasThinkingDraft()) this.host.streamingUI.flushThinkingToTranscript();
|
|
133918
134079
|
this.host.streamingUI.finalizeAssistantStream();
|
|
133919
134080
|
this.host.appendTranscriptEntry({
|
|
133920
134081
|
id: nextTranscriptId(),
|
|
@@ -133924,7 +134085,6 @@ var SessionEventHandler = class {
|
|
|
133924
134085
|
content: formatHookResultMarkdown(event)
|
|
133925
134086
|
});
|
|
133926
134087
|
this.host.patchLivePane({
|
|
133927
|
-
mode: "idle",
|
|
133928
134088
|
pendingApproval: null,
|
|
133929
134089
|
pendingQuestion: null
|
|
133930
134090
|
});
|
|
@@ -133944,24 +134104,20 @@ var SessionEventHandler = class {
|
|
|
133944
134104
|
};
|
|
133945
134105
|
streamingUI.registerToolCall(toolCall);
|
|
133946
134106
|
this.host.patchLivePane({
|
|
133947
|
-
mode: "tool",
|
|
133948
134107
|
pendingApproval: null,
|
|
133949
134108
|
pendingQuestion: null
|
|
133950
134109
|
});
|
|
134110
|
+
if (canTransitionTo(this.host.state.appState.streamingPhase, "tool")) this.host.setAppState({ streamingPhase: "tool" });
|
|
133951
134111
|
}
|
|
133952
134112
|
handleToolCallDelta(event) {
|
|
133953
134113
|
if (event.toolCallId.length === 0) return;
|
|
133954
134114
|
const { state, streamingUI } = this.host;
|
|
133955
134115
|
streamingUI.accumulateToolCallDelta(event.toolCallId, event.name, event.argumentsPart);
|
|
133956
134116
|
this.host.patchLivePane({
|
|
133957
|
-
mode: "tool",
|
|
133958
134117
|
pendingApproval: null,
|
|
133959
134118
|
pendingQuestion: null
|
|
133960
134119
|
});
|
|
133961
|
-
if (state.appState.streamingPhase
|
|
133962
|
-
streamingPhase: "composing",
|
|
133963
|
-
streamingStartTime: Date.now()
|
|
133964
|
-
});
|
|
134120
|
+
if (canTransitionTo(state.appState.streamingPhase, "tool")) this.host.setAppState({ streamingPhase: "tool" });
|
|
133965
134121
|
streamingUI.scheduleFlush();
|
|
133966
134122
|
}
|
|
133967
134123
|
handleToolProgress(event) {
|
|
@@ -133993,7 +134149,7 @@ var SessionEventHandler = class {
|
|
|
133993
134149
|
streamingUI.setTodoList(sanitized);
|
|
133994
134150
|
}
|
|
133995
134151
|
}
|
|
133996
|
-
this.host.
|
|
134152
|
+
if (canTransitionTo(this.host.state.appState.streamingPhase, "waiting")) this.host.setAppState({ streamingPhase: "waiting" });
|
|
133997
134153
|
}
|
|
133998
134154
|
handleStatusUpdate(event) {
|
|
133999
134155
|
const patch = {};
|
|
@@ -134016,7 +134172,7 @@ var SessionEventHandler = class {
|
|
|
134016
134172
|
handleSessionError(event) {
|
|
134017
134173
|
this.host.streamingUI.flushNow();
|
|
134018
134174
|
this.host.streamingUI.resetToolUi();
|
|
134019
|
-
this.host.streamingUI.finalizeLiveTextBuffers(
|
|
134175
|
+
this.host.streamingUI.finalizeLiveTextBuffers();
|
|
134020
134176
|
this.host.showError(`[${event.code}] ${event.message}`);
|
|
134021
134177
|
}
|
|
134022
134178
|
handleSessionWarning(event) {
|
|
@@ -134130,22 +134286,38 @@ var SessionEventHandler = class {
|
|
|
134130
134286
|
});
|
|
134131
134287
|
}
|
|
134132
134288
|
handleCompactionBegin(event) {
|
|
134133
|
-
this.host.streamingUI.finalizeLiveTextBuffers(
|
|
134289
|
+
this.host.streamingUI.finalizeLiveTextBuffers();
|
|
134290
|
+
this.lastCompactionTrigger = event.trigger;
|
|
134291
|
+
if (event.trigger === "auto") {
|
|
134292
|
+
const anomaly = detectCompactionAnomaly({
|
|
134293
|
+
lastFinishedAt: this.host.state.appState.lastCompactionFinishedAt,
|
|
134294
|
+
autoCompactionCount: this.host.state.appState.autoCompactionCount,
|
|
134295
|
+
currentTokens: this.host.state.appState.contextTokens,
|
|
134296
|
+
maxContextTokens: this.host.state.appState.maxContextTokens,
|
|
134297
|
+
now: Date.now()
|
|
134298
|
+
});
|
|
134299
|
+
if (anomaly !== null) this.host.showNotice("Storm Breaker(风暴守护者)", `检测到异常压缩节奏:${anomaly.detail}可能存在工具调用循环或超长输出,建议查看最近几步的对话记录。`);
|
|
134300
|
+
}
|
|
134134
134301
|
this.host.setAppState({
|
|
134135
134302
|
isCompacting: true,
|
|
134136
|
-
streamingPhase: "waiting"
|
|
134137
|
-
streamingStartTime: Date.now()
|
|
134303
|
+
streamingPhase: "waiting"
|
|
134138
134304
|
});
|
|
134139
134305
|
this.host.streamingUI.beginCompaction(event.instruction);
|
|
134140
134306
|
}
|
|
134141
134307
|
handleCompactionEnd(event, sendQueued) {
|
|
134142
134308
|
this.host.streamingUI.endCompaction(event.result.tokensBefore, event.result.tokensAfter);
|
|
134143
134309
|
this.host.markMemoryExtracted();
|
|
134310
|
+
if (this.lastCompactionTrigger === "auto") this.host.setAppState({
|
|
134311
|
+
lastCompactionFinishedAt: Date.now(),
|
|
134312
|
+
autoCompactionCount: this.host.state.appState.autoCompactionCount + 1
|
|
134313
|
+
});
|
|
134314
|
+
this.lastCompactionTrigger = void 0;
|
|
134144
134315
|
this.finishCompaction(sendQueued);
|
|
134145
134316
|
}
|
|
134146
134317
|
handleCompactionCancel(event, sendQueued) {
|
|
134147
134318
|
if (event.reason) this.host.showStatus(event.reason, this.host.state.theme.colors.warning);
|
|
134148
134319
|
this.host.streamingUI.cancelCompaction();
|
|
134320
|
+
this.lastCompactionTrigger = void 0;
|
|
134149
134321
|
this.finishCompaction(sendQueued);
|
|
134150
134322
|
}
|
|
134151
134323
|
finishCompaction(sendQueued) {
|
|
@@ -135297,7 +135469,7 @@ var StreamingUIController = class {
|
|
|
135297
135469
|
const existingComponent = this._pendingToolComponents.get(toolCall.id);
|
|
135298
135470
|
if (existingComponent !== void 0) existingComponent.updateToolCall(toolCall);
|
|
135299
135471
|
else if (existing === void 0) {
|
|
135300
|
-
this.finalizeLiveTextBuffers(
|
|
135472
|
+
this.finalizeLiveTextBuffers();
|
|
135301
135473
|
if (toolCall.name !== "Agent") this.onToolCallStart(toolCall);
|
|
135302
135474
|
}
|
|
135303
135475
|
return existing === void 0;
|
|
@@ -135420,11 +135592,10 @@ var StreamingUIController = class {
|
|
|
135420
135592
|
markThinkingDirty() {
|
|
135421
135593
|
this.pendingThinkingFlush = true;
|
|
135422
135594
|
}
|
|
135423
|
-
flushThinkingToTranscript(
|
|
135595
|
+
flushThinkingToTranscript() {
|
|
135424
135596
|
this.flushNow();
|
|
135425
135597
|
this._thinkingDraft = "";
|
|
135426
135598
|
this.onThinkingEnd();
|
|
135427
|
-
this.host.patchLivePane({ mode: nextMode });
|
|
135428
135599
|
}
|
|
135429
135600
|
finalizeAssistantStream() {
|
|
135430
135601
|
this.flushNow();
|
|
@@ -135455,8 +135626,8 @@ var StreamingUIController = class {
|
|
|
135455
135626
|
resetToolCallState() {
|
|
135456
135627
|
this._activeToolCalls.clear();
|
|
135457
135628
|
}
|
|
135458
|
-
finalizeLiveTextBuffers(
|
|
135459
|
-
this.flushThinkingToTranscript(
|
|
135629
|
+
finalizeLiveTextBuffers() {
|
|
135630
|
+
this.flushThinkingToTranscript();
|
|
135460
135631
|
this.finalizeAssistantStream();
|
|
135461
135632
|
}
|
|
135462
135633
|
finalizeTurn(sendQueued) {
|
|
@@ -135464,7 +135635,7 @@ var StreamingUIController = class {
|
|
|
135464
135635
|
if (state.appState.streamingPhase === "idle") return;
|
|
135465
135636
|
this.host.deferUserMessages = false;
|
|
135466
135637
|
const completedTurnKey = this._currentTurnId ?? `local:${String(state.appState.streamingStartTime)}`;
|
|
135467
|
-
this.finalizeLiveTextBuffers(
|
|
135638
|
+
this.finalizeLiveTextBuffers();
|
|
135468
135639
|
this.resetToolCallState();
|
|
135469
135640
|
this._currentTurnId = void 0;
|
|
135470
135641
|
const next = this.host.shiftQueuedMessage();
|
|
@@ -135729,7 +135900,7 @@ var StreamingUIController = class {
|
|
|
135729
135900
|
turnId: this._currentTurnId
|
|
135730
135901
|
};
|
|
135731
135902
|
this._activeToolCalls.set(id, toolCall);
|
|
135732
|
-
if (this._thinkingDraft.length > 0 || this._streamingBlock !== null) this.finalizeLiveTextBuffers(
|
|
135903
|
+
if (this._thinkingDraft.length > 0 || this._streamingBlock !== null) this.finalizeLiveTextBuffers();
|
|
135733
135904
|
const existingComponent = this._pendingToolComponents.get(id);
|
|
135734
135905
|
if (existingComponent !== void 0) existingComponent.updateToolCall(toolCall);
|
|
135735
135906
|
else if (toolCall.name !== "Agent") this.onToolCallStart(toolCall);
|
|
@@ -137142,7 +137313,7 @@ var TranscriptController = class TranscriptController {
|
|
|
137142
137313
|
commit() {
|
|
137143
137314
|
this.host.batchUpdate(() => {
|
|
137144
137315
|
const { state } = this.host;
|
|
137145
|
-
if (state.appState
|
|
137316
|
+
if (isStreaming(state.appState)) return;
|
|
137146
137317
|
const container = state.transcriptContainer;
|
|
137147
137318
|
const children = container.children;
|
|
137148
137319
|
if (children.length <= TranscriptController.LIVE_LIMIT) return;
|
|
@@ -137824,7 +137995,7 @@ var LifecycleController = class LifecycleController {
|
|
|
137824
137995
|
async performIdleMemoryExtraction() {
|
|
137825
137996
|
if (Date.now() - this.lastMemoryExtractionTime < LifecycleController.MEMORY_EXTRACT_COOLDOWN_MS) return;
|
|
137826
137997
|
const { state, session } = this.host;
|
|
137827
|
-
if (state.appState
|
|
137998
|
+
if (isStreaming(state.appState)) return;
|
|
137828
137999
|
if (state.appState.isCompacting) return;
|
|
137829
138000
|
if (state.appState.isReplaying) return;
|
|
137830
138001
|
if (session === void 0) return;
|
|
@@ -137938,7 +138109,6 @@ var LifecycleController = class LifecycleController {
|
|
|
137938
138109
|
break;
|
|
137939
138110
|
}
|
|
137940
138111
|
case "idle":
|
|
137941
|
-
case "session":
|
|
137942
138112
|
this.stopActivitySpinner();
|
|
137943
138113
|
this.stopPulseWave();
|
|
137944
138114
|
break;
|
|
@@ -137952,10 +138122,8 @@ var LifecycleController = class LifecycleController {
|
|
|
137952
138122
|
if (state.appState.isCompacting) return "hidden";
|
|
137953
138123
|
if (state.livePane.pendingQuestion !== null) return "hidden";
|
|
137954
138124
|
const streamingPhase = state.appState.streamingPhase;
|
|
137955
|
-
if (
|
|
137956
|
-
|
|
137957
|
-
}
|
|
137958
|
-
return state.livePane.mode;
|
|
138125
|
+
if (streamingPhase === "waiting" || streamingPhase === "thinking" || streamingPhase === "composing" || streamingPhase === "tool") return streamingPhase;
|
|
138126
|
+
return "idle";
|
|
137959
138127
|
}
|
|
137960
138128
|
shouldShowTerminalProgress(effectiveMode) {
|
|
137961
138129
|
if (this.host.state.appState.isCompacting) return true;
|
|
@@ -138287,6 +138455,35 @@ function trySourceOrDist() {
|
|
|
138287
138455
|
};
|
|
138288
138456
|
}
|
|
138289
138457
|
const SCREAM_FUSIONPLAN_SUBAGENT_ENV = "SCREAM_FUSIONPLAN_SUBAGENT";
|
|
138458
|
+
/**
|
|
138459
|
+
* Terminate a child process tree reliably on both POSIX and Windows.
|
|
138460
|
+
* On Windows `proc.kill()` only signals the wrapper, so use `taskkill /T`.
|
|
138461
|
+
* On POSIX, kill the process group so grandchildren inherit the signal.
|
|
138462
|
+
*/
|
|
138463
|
+
function killProcessTree(proc, signal) {
|
|
138464
|
+
if (proc.pid === void 0) return;
|
|
138465
|
+
if (process.platform === "win32") {
|
|
138466
|
+
const child = spawn("taskkill", [
|
|
138467
|
+
...signal === "SIGKILL" ? ["/F"] : [],
|
|
138468
|
+
"/T",
|
|
138469
|
+
"/PID",
|
|
138470
|
+
String(proc.pid)
|
|
138471
|
+
], {
|
|
138472
|
+
stdio: "ignore",
|
|
138473
|
+
windowsHide: true,
|
|
138474
|
+
detached: true
|
|
138475
|
+
});
|
|
138476
|
+
if (typeof child.unref === "function") child.unref();
|
|
138477
|
+
return;
|
|
138478
|
+
}
|
|
138479
|
+
try {
|
|
138480
|
+
process.kill(-proc.pid, signal);
|
|
138481
|
+
} catch {
|
|
138482
|
+
try {
|
|
138483
|
+
proc.kill(signal);
|
|
138484
|
+
} catch {}
|
|
138485
|
+
}
|
|
138486
|
+
}
|
|
138290
138487
|
const WORKER_ANGLES = [
|
|
138291
138488
|
{
|
|
138292
138489
|
angle: "Focus on correctness and edge cases. Identify risks, invariants, and safety checks.",
|
|
@@ -138302,9 +138499,17 @@ const WORKER_ANGLES = [
|
|
|
138302
138499
|
}
|
|
138303
138500
|
];
|
|
138304
138501
|
const DEFAULT_WORKER_COUNT = 3;
|
|
138305
|
-
const DEFAULT_TIMEOUT_MS =
|
|
138502
|
+
const DEFAULT_TIMEOUT_MS = 6e5;
|
|
138306
138503
|
const DEFAULT_MAX_OUTPUT_BYTES = 8e3;
|
|
138307
138504
|
const DEFAULT_SYNTHESIS_MAX_OUTPUT_BYTES = 12e3;
|
|
138505
|
+
/** Override via `SCREAM_FUSIONPLAN_TIMEOUT_SECONDS` env var (30..3600). */
|
|
138506
|
+
function resolveDefaultTimeoutMs() {
|
|
138507
|
+
const env = process.env["SCREAM_FUSIONPLAN_TIMEOUT_SECONDS"];
|
|
138508
|
+
if (env === void 0) return DEFAULT_TIMEOUT_MS;
|
|
138509
|
+
const parsed = Number.parseInt(env, 10);
|
|
138510
|
+
if (Number.isNaN(parsed)) return DEFAULT_TIMEOUT_MS;
|
|
138511
|
+
return Math.max(3e4, Math.min(36e5, parsed * 1e3));
|
|
138512
|
+
}
|
|
138308
138513
|
function buildPlannerPrompt(input) {
|
|
138309
138514
|
return [
|
|
138310
138515
|
"You are a planning specialist. Create an implementation plan for the request below.",
|
|
@@ -138426,7 +138631,7 @@ async function runWorker(input) {
|
|
|
138426
138631
|
model: input.model
|
|
138427
138632
|
});
|
|
138428
138633
|
const { command, prefixArgs } = resolveScreamCommand(input.screamBin);
|
|
138429
|
-
const timeoutMs = input.timeoutMs ??
|
|
138634
|
+
const timeoutMs = input.timeoutMs ?? resolveDefaultTimeoutMs();
|
|
138430
138635
|
result.command = [
|
|
138431
138636
|
command,
|
|
138432
138637
|
...prefixArgs,
|
|
@@ -138488,12 +138693,13 @@ async function runWorker(input) {
|
|
|
138488
138693
|
});
|
|
138489
138694
|
timeout = setTimeout(() => {
|
|
138490
138695
|
result.timedOut = true;
|
|
138491
|
-
proc
|
|
138492
|
-
setTimeout(() => proc
|
|
138696
|
+
killProcessTree(proc, "SIGTERM");
|
|
138697
|
+
setTimeout(() => killProcessTree(proc, "SIGKILL"), 5e3).unref();
|
|
138493
138698
|
}, timeoutMs);
|
|
138494
138699
|
timeout.unref();
|
|
138495
138700
|
});
|
|
138496
138701
|
result.exitCode = exitCode;
|
|
138702
|
+
result.timeoutMs = timeoutMs;
|
|
138497
138703
|
result.ok = exitCode === 0 && !result.timedOut && result.output.trim().length > 0;
|
|
138498
138704
|
if (!result.output.trim()) result.output = result.stderr.trim() || "(worker produced no final assistant output)";
|
|
138499
138705
|
return result;
|
|
@@ -138539,7 +138745,7 @@ async function runFusionPlan(input) {
|
|
|
138539
138745
|
};
|
|
138540
138746
|
const workerCount = Math.max(1, Math.min(8, input.workerCount ?? DEFAULT_WORKER_COUNT));
|
|
138541
138747
|
const maxOutputBytes = input.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
138542
|
-
const timeoutMs = input.timeoutMs ??
|
|
138748
|
+
const timeoutMs = input.timeoutMs ?? resolveDefaultTimeoutMs();
|
|
138543
138749
|
const promptDir = await createPromptDir();
|
|
138544
138750
|
const workerStates = [];
|
|
138545
138751
|
for (let i = 0; i < workerCount; i += 1) {
|
|
@@ -138725,7 +138931,7 @@ function hexToRgb$1(hex) {
|
|
|
138725
138931
|
v & 255
|
|
138726
138932
|
];
|
|
138727
138933
|
}
|
|
138728
|
-
function rgbToHsl(r, g, b) {
|
|
138934
|
+
function rgbToHsl$1(r, g, b) {
|
|
138729
138935
|
const rf = r / 255, gf = g / 255, bf = b / 255;
|
|
138730
138936
|
const max = Math.max(rf, gf, bf), min = Math.min(rf, gf, bf);
|
|
138731
138937
|
const l = (max + min) / 2;
|
|
@@ -138853,6 +139059,8 @@ var InputController = class {
|
|
|
138853
139059
|
cwd: this.host.state.appState.workDir,
|
|
138854
139060
|
model: this.host.state.appState.model,
|
|
138855
139061
|
thinkingLevel: this.host.state.appState.thinkingLevel === "off" ? void 0 : this.host.state.appState.thinkingLevel,
|
|
139062
|
+
workerCount: this.host.state.appState.fusionPlan.workerCount,
|
|
139063
|
+
timeoutMs: this.host.state.appState.fusionPlan.timeoutSeconds * 1e3,
|
|
138856
139064
|
onProgress: (event) => {
|
|
138857
139065
|
if (this.fusionPlanEntry === void 0) {
|
|
138858
139066
|
this.fusionPlanEntry = {
|
|
@@ -138873,7 +139081,10 @@ var InputController = class {
|
|
|
138873
139081
|
if (!result.ok) {
|
|
138874
139082
|
const details = result.workerResults.map((r, i) => {
|
|
138875
139083
|
if (r.ok) return `worker ${i + 1}: ok`;
|
|
138876
|
-
if (r.timedOut)
|
|
139084
|
+
if (r.timedOut) {
|
|
139085
|
+
const timeoutS = r.timeoutMs !== void 0 ? Math.round(r.timeoutMs / 1e3) : 600;
|
|
139086
|
+
return `worker ${i + 1}: timed out after ${timeoutS}s`;
|
|
139087
|
+
}
|
|
138877
139088
|
const reason = r.exitCode !== null ? `exit ${r.exitCode}` : r.stderr.trim() || "no output";
|
|
138878
139089
|
const commandHint = r.command ? ` [${r.command}]` : "";
|
|
138879
139090
|
return `worker ${i + 1}: failed (${reason})${commandHint}`;
|
|
@@ -138939,7 +139150,7 @@ var InputController = class {
|
|
|
138939
139150
|
for (const part of input) this.enqueueMessage(part);
|
|
138940
139151
|
return;
|
|
138941
139152
|
}
|
|
138942
|
-
if (this.host.state.appState
|
|
139153
|
+
if (!isStreaming(this.host.state.appState)) {
|
|
138943
139154
|
for (const part of input) this.sendMessageInternal(session, part);
|
|
138944
139155
|
return;
|
|
138945
139156
|
}
|
|
@@ -138989,7 +139200,7 @@ var InputController = class {
|
|
|
138989
139200
|
if (this.breatheOnceStopped) return;
|
|
138990
139201
|
const primaryHex = this.host.state.theme.colors.primary;
|
|
138991
139202
|
const [r, g, b] = hexToRgb$1(primaryHex);
|
|
138992
|
-
const [baseHue] = rgbToHsl(r, g, b);
|
|
139203
|
+
const [baseHue] = rgbToHsl$1(r, g, b);
|
|
138993
139204
|
this.breatheFrame = 0;
|
|
138994
139205
|
const editor = this.host.state.editor;
|
|
138995
139206
|
const ui = this.host.state.ui;
|
|
@@ -139068,7 +139279,7 @@ var InputController = class {
|
|
|
139068
139279
|
});
|
|
139069
139280
|
}
|
|
139070
139281
|
sendMessage(session, input, options) {
|
|
139071
|
-
if (this.host.deferUserMessages || this.host.state.appState
|
|
139282
|
+
if (this.host.deferUserMessages || isBusy(this.host.state.appState)) {
|
|
139072
139283
|
this.enqueueMessage(input, options);
|
|
139073
139284
|
return;
|
|
139074
139285
|
}
|
|
@@ -139289,7 +139500,6 @@ var QuestionController = class extends ReverseRpcController {
|
|
|
139289
139500
|
//#endregion
|
|
139290
139501
|
//#region src/tui/types.ts
|
|
139291
139502
|
const INITIAL_LIVE_PANE = {
|
|
139292
|
-
mode: "idle",
|
|
139293
139503
|
pendingApproval: null,
|
|
139294
139504
|
pendingQuestion: null
|
|
139295
139505
|
};
|
|
@@ -139372,6 +139582,14 @@ var ErrorBannerComponent = class {
|
|
|
139372
139582
|
}
|
|
139373
139583
|
};
|
|
139374
139584
|
//#endregion
|
|
139585
|
+
//#region src/tui/loop-state.ts
|
|
139586
|
+
function resolveLoopSubstate(state) {
|
|
139587
|
+
if (!state.loopModeEnabled) return "idle";
|
|
139588
|
+
if (state.loopVerifying) return "verifying";
|
|
139589
|
+
if (state.loopPrompt === void 0) return "paused";
|
|
139590
|
+
return "running";
|
|
139591
|
+
}
|
|
139592
|
+
//#endregion
|
|
139375
139593
|
//#region src/tui/utils/shimmer.ts
|
|
139376
139594
|
const SHIMMER_SPEED_CELLS_PER_S = 30;
|
|
139377
139595
|
const PADDING = 10;
|
|
@@ -139924,10 +140142,10 @@ function lerpGradient(t) {
|
|
|
139924
140142
|
const b = Math.round(b0 + (b1 - b0) * localT);
|
|
139925
140143
|
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
139926
140144
|
}
|
|
139927
|
-
function buildStatusLine(streamingPhase,
|
|
139928
|
-
if (streamingPhase === "idle"
|
|
140145
|
+
function buildStatusLine(streamingPhase, streamingStartTime) {
|
|
140146
|
+
if (streamingPhase === "idle") return "○ 空闲";
|
|
139929
140147
|
let label;
|
|
139930
|
-
if (
|
|
140148
|
+
if (streamingPhase === "tool") label = "执行中";
|
|
139931
140149
|
else if (streamingPhase === "waiting") label = "等待响应";
|
|
139932
140150
|
else if (streamingPhase === "thinking") label = "思考中";
|
|
139933
140151
|
else if (streamingPhase === "composing") label = "输出中";
|
|
@@ -140042,6 +140260,7 @@ var FooterComponent = class {
|
|
|
140042
140260
|
const remainMs = Math.max(0, limit.deadlineMs - Date.now());
|
|
140043
140261
|
badge = remainMs >= 6e4 ? `loop ${Math.ceil(remainMs / 6e4)}m` : `loop ${Math.max(1, Math.ceil(remainMs / 1e3))}s`;
|
|
140044
140262
|
}
|
|
140263
|
+
if (resolveLoopSubstate(state) === "verifying") badge += " · 验证中";
|
|
140045
140264
|
if (state.loopLastVerifyPassed === false) badge += " · ✗";
|
|
140046
140265
|
left.push(chalk.hex(colors.primary).bold(badge));
|
|
140047
140266
|
}
|
|
@@ -140066,7 +140285,7 @@ var FooterComponent = class {
|
|
|
140066
140285
|
let rightText;
|
|
140067
140286
|
if (this.transientHint) rightText = chalk.hex(colors.warning).bold(this.transientHint);
|
|
140068
140287
|
else {
|
|
140069
|
-
const statusLine = buildStatusLine(state.streamingPhase, state.
|
|
140288
|
+
const statusLine = buildStatusLine(state.streamingPhase, state.streamingStartTime);
|
|
140070
140289
|
const ccDot = state.ccConnectActive ? chalk.hex(colors.success)("●") : chalk.hex(colors.textDim)("●");
|
|
140071
140290
|
const contextColor = pickContextColor(state.contextUsage, colors);
|
|
140072
140291
|
rightText = `${ccDot} ${chalk.hex(contextColor)(formatContextStatus(state.contextUsage, state.contextTokens, state.maxContextTokens))}${chalk.hex(colors.textDim)(` ${statusLine}`)}`;
|
|
@@ -141315,7 +141534,7 @@ var SessionManager = class {
|
|
|
141315
141534
|
this.host.showStatus("已在该会话中。");
|
|
141316
141535
|
return { switched: true };
|
|
141317
141536
|
}
|
|
141318
|
-
if (this.host.state.appState
|
|
141537
|
+
if (isBusy(this.host.state.appState)) {
|
|
141319
141538
|
this.host.showError("流式传输期间无法切换会话 — 请先按 Esc 或 Ctrl-C。");
|
|
141320
141539
|
return { switched: false };
|
|
141321
141540
|
}
|
|
@@ -143337,10 +143556,11 @@ function createInitialAppState(input) {
|
|
|
143337
143556
|
contextTokens: 0,
|
|
143338
143557
|
maxContextTokens: 0,
|
|
143339
143558
|
isCompacting: false,
|
|
143559
|
+
lastCompactionFinishedAt: void 0,
|
|
143560
|
+
autoCompactionCount: 0,
|
|
143340
143561
|
isReplaying: false,
|
|
143341
143562
|
streamingPhase: "idle",
|
|
143342
143563
|
streamingStartTime: 0,
|
|
143343
|
-
livePaneMode: "idle",
|
|
143344
143564
|
theme: input.tuiConfig.theme,
|
|
143345
143565
|
version: input.version,
|
|
143346
143566
|
hasNewVersion: false,
|
|
@@ -143348,6 +143568,7 @@ function createInitialAppState(input) {
|
|
|
143348
143568
|
editorCommand: input.tuiConfig.editorCommand,
|
|
143349
143569
|
notifications: input.tuiConfig.notifications,
|
|
143350
143570
|
like: input.tuiConfig.like,
|
|
143571
|
+
fusionPlan: input.tuiConfig.fusionPlan,
|
|
143351
143572
|
availableModels: {},
|
|
143352
143573
|
availableProviders: {},
|
|
143353
143574
|
sessionTitle: null,
|
|
@@ -143362,6 +143583,7 @@ function createInitialAppState(input) {
|
|
|
143362
143583
|
loopVerifier: void 0,
|
|
143363
143584
|
loopIteration: 0,
|
|
143364
143585
|
loopLastVerifyPassed: void 0,
|
|
143586
|
+
loopVerifying: false,
|
|
143365
143587
|
recentSessions: []
|
|
143366
143588
|
};
|
|
143367
143589
|
}
|
|
@@ -143382,6 +143604,7 @@ var ScreamTUI = class {
|
|
|
143382
143604
|
isShuttingDown = false;
|
|
143383
143605
|
reverseRpcDisposers = [];
|
|
143384
143606
|
startupNotice;
|
|
143607
|
+
updatePrefetched;
|
|
143385
143608
|
sessionManager;
|
|
143386
143609
|
dialogManager;
|
|
143387
143610
|
streamingUI;
|
|
@@ -143419,6 +143642,7 @@ var ScreamTUI = class {
|
|
|
143419
143642
|
};
|
|
143420
143643
|
this.options = tuiOptions;
|
|
143421
143644
|
this.startupNotice = startupInput.startupNotice;
|
|
143645
|
+
this.updatePrefetched = startupInput.updatePrefetched === true;
|
|
143422
143646
|
this.state = createTUIState(tuiOptions);
|
|
143423
143647
|
this.reverseRpcDisposers.push(...registerReverseRPCHandlers(this.approvalController, this.questionController, {
|
|
143424
143648
|
showApprovalPanel: (payload) => {
|
|
@@ -143491,6 +143715,7 @@ var ScreamTUI = class {
|
|
|
143491
143715
|
}
|
|
143492
143716
|
async initMainTui() {
|
|
143493
143717
|
const shouldReplayHistory = await this.init();
|
|
143718
|
+
await this.checkForUpdates();
|
|
143494
143719
|
try {
|
|
143495
143720
|
const sessions = await this.harness.listSessions({ workDir: this.state.appState.workDir });
|
|
143496
143721
|
this.state.appState.recentSessions = sessions.slice(0, 3).map((s) => ({
|
|
@@ -143524,11 +143749,7 @@ var ScreamTUI = class {
|
|
|
143524
143749
|
if (shouldReplayHistory) await this.sessionReplay.hydrateFromReplay(this.requireSession());
|
|
143525
143750
|
const resumeState = this.session?.getResumeState();
|
|
143526
143751
|
if (resumeState?.warning !== void 0) this.showStatus(`警告:${resumeState.warning}`, this.state.theme.colors.warning);
|
|
143527
|
-
if (this.session !== void 0) this.sessionEventHandler.startSubscription();
|
|
143528
|
-
this.fetchSessions();
|
|
143529
|
-
if (this.session !== void 0) this.refreshSessionTitle();
|
|
143530
143752
|
this.refreshSkillCommands(this.session);
|
|
143531
|
-
this.checkForUpdates();
|
|
143532
143753
|
}
|
|
143533
143754
|
async showTmuxKeyboardWarningIfNeeded() {
|
|
143534
143755
|
const warning = await detectTmuxKeyboardWarning();
|
|
@@ -143622,14 +143843,10 @@ var ScreamTUI = class {
|
|
|
143622
143843
|
this.streamingUI.resetToolUi();
|
|
143623
143844
|
this.streamingUI.resetToolCallState();
|
|
143624
143845
|
this.patchLivePane({
|
|
143625
|
-
mode: "waiting",
|
|
143626
143846
|
pendingApproval: null,
|
|
143627
143847
|
pendingQuestion: null
|
|
143628
143848
|
});
|
|
143629
|
-
this.setAppState({
|
|
143630
|
-
streamingPhase: "waiting",
|
|
143631
|
-
streamingStartTime: Date.now()
|
|
143632
|
-
});
|
|
143849
|
+
this.setAppState({ streamingPhase: "waiting" });
|
|
143633
143850
|
}
|
|
143634
143851
|
failSessionRequest(message) {
|
|
143635
143852
|
this.setAppState({ streamingPhase: "idle" });
|
|
@@ -143711,6 +143928,10 @@ var ScreamTUI = class {
|
|
|
143711
143928
|
}
|
|
143712
143929
|
}
|
|
143713
143930
|
setAppState(patch) {
|
|
143931
|
+
if ("streamingPhase" in patch && patch.streamingPhase !== void 0 && patch.streamingPhase !== this.state.appState.streamingPhase) patch = {
|
|
143932
|
+
...patch,
|
|
143933
|
+
streamingStartTime: patch.streamingPhase === "idle" ? 0 : Date.now()
|
|
143934
|
+
};
|
|
143714
143935
|
if (!hasPatchChanges(this.state.appState, patch)) return;
|
|
143715
143936
|
const busyChanged = "streamingPhase" in patch || "isCompacting" in patch;
|
|
143716
143937
|
Object.assign(this.state.appState, patch);
|
|
@@ -143728,10 +143949,6 @@ var ScreamTUI = class {
|
|
|
143728
143949
|
patchLivePane(patch) {
|
|
143729
143950
|
if (!hasPatchChanges(this.state.livePane, patch)) return;
|
|
143730
143951
|
Object.assign(this.state.livePane, patch);
|
|
143731
|
-
if ("mode" in patch) {
|
|
143732
|
-
this.state.appState.livePaneMode = patch.mode;
|
|
143733
|
-
this.state.footer.setState(this.state.appState);
|
|
143734
|
-
}
|
|
143735
143952
|
this.lifecycleController.updateActivityPane();
|
|
143736
143953
|
this.state.ui.requestRender();
|
|
143737
143954
|
}
|
|
@@ -143758,7 +143975,7 @@ var ScreamTUI = class {
|
|
|
143758
143975
|
}
|
|
143759
143976
|
async checkForUpdates() {
|
|
143760
143977
|
try {
|
|
143761
|
-
await refreshUpdateCache().catch(() => {});
|
|
143978
|
+
if (!this.updatePrefetched) await refreshUpdateCache().catch(() => {});
|
|
143762
143979
|
const cache = await readUpdateCache();
|
|
143763
143980
|
const target = selectUpdateTarget(this.state.appState.version, cache.latest);
|
|
143764
143981
|
if (target !== null) this.setAppState({
|
|
@@ -143902,10 +144119,10 @@ const SHADOW_CHARS = new Set([
|
|
|
143902
144119
|
"╩",
|
|
143903
144120
|
"╬"
|
|
143904
144121
|
]);
|
|
143905
|
-
const SHEEN_STEP =
|
|
143906
|
-
const SHEEN_INTERVAL_MS =
|
|
144122
|
+
const SHEEN_STEP = 4;
|
|
144123
|
+
const SHEEN_INTERVAL_MS = 60;
|
|
143907
144124
|
const LOADING_DURATION_MS = 1500;
|
|
143908
|
-
const
|
|
144125
|
+
const THEME_PRIMARY = {
|
|
143909
144126
|
dark: [
|
|
143910
144127
|
78,
|
|
143911
144128
|
200,
|
|
@@ -143932,39 +144149,98 @@ const DIM_RGB = [
|
|
|
143932
144149
|
85,
|
|
143933
144150
|
85
|
|
143934
144151
|
];
|
|
144152
|
+
const HUE_STOPS = 24;
|
|
144153
|
+
const SUB_STEPS = 5;
|
|
144154
|
+
const BREATHE_STEPS = HUE_STOPS * SUB_STEPS;
|
|
144155
|
+
function rgbToHsl(r, g, b) {
|
|
144156
|
+
const rf = r / 255, gf = g / 255, bf = b / 255;
|
|
144157
|
+
const max = Math.max(rf, gf, bf), min = Math.min(rf, gf, bf);
|
|
144158
|
+
const l = (max + min) / 2;
|
|
144159
|
+
if (max === min) return [
|
|
144160
|
+
0,
|
|
144161
|
+
0,
|
|
144162
|
+
l * 100
|
|
144163
|
+
];
|
|
144164
|
+
const d = max - min;
|
|
144165
|
+
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
144166
|
+
let h = 0;
|
|
144167
|
+
if (max === rf) h = ((gf - bf) / d + (gf < bf ? 6 : 0)) / 6;
|
|
144168
|
+
else if (max === gf) h = ((bf - rf) / d + 2) / 6;
|
|
144169
|
+
else h = ((rf - gf) / d + 4) / 6;
|
|
144170
|
+
return [
|
|
144171
|
+
h * 360,
|
|
144172
|
+
s * 100,
|
|
144173
|
+
l * 100
|
|
144174
|
+
];
|
|
144175
|
+
}
|
|
144176
|
+
function hslToRgb(h, s, l) {
|
|
144177
|
+
const hf = (h % 360 + 360) % 360 / 360;
|
|
144178
|
+
const sf = s / 100, lf = l / 100;
|
|
144179
|
+
if (sf === 0) {
|
|
144180
|
+
const v = Math.round(lf * 255);
|
|
144181
|
+
return [
|
|
144182
|
+
v,
|
|
144183
|
+
v,
|
|
144184
|
+
v
|
|
144185
|
+
];
|
|
144186
|
+
}
|
|
144187
|
+
const q = lf < .5 ? lf * (1 + sf) : lf + sf - lf * sf;
|
|
144188
|
+
const p = 2 * lf - q;
|
|
144189
|
+
const hue = (t) => {
|
|
144190
|
+
if (t < 0) t += 1;
|
|
144191
|
+
if (t > 1) t -= 1;
|
|
144192
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
144193
|
+
if (t < 1 / 2) return q;
|
|
144194
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
144195
|
+
return p;
|
|
144196
|
+
};
|
|
144197
|
+
return [
|
|
144198
|
+
Math.round(hue(hf + 1 / 3) * 255),
|
|
144199
|
+
Math.round(hue(hf) * 255),
|
|
144200
|
+
Math.round(hue(hf - 1 / 3) * 255)
|
|
144201
|
+
];
|
|
144202
|
+
}
|
|
144203
|
+
function buildBreathingPalette(primary) {
|
|
144204
|
+
const [r, g, b] = primary;
|
|
144205
|
+
const [baseHue] = rgbToHsl(r, g, b);
|
|
144206
|
+
const steps = HUE_STOPS * SUB_STEPS;
|
|
144207
|
+
const palette = [];
|
|
144208
|
+
for (let i = 0; i < steps; i++) {
|
|
144209
|
+
const hueAngle = (baseHue + i / steps * 360) % 360;
|
|
144210
|
+
palette.push(hslToRgb(hueAngle, 90, 70));
|
|
144211
|
+
}
|
|
144212
|
+
return palette;
|
|
144213
|
+
}
|
|
143935
144214
|
function fg(r, g, b) {
|
|
143936
144215
|
return `\u001B[38;2;${r};${g};${b}m`;
|
|
143937
144216
|
}
|
|
143938
144217
|
const RESET = "\x1B[0m";
|
|
143939
144218
|
const BOLD = "\x1B[1m";
|
|
143940
144219
|
const DIM = "\x1B[2m";
|
|
143941
|
-
function renderSheen(char, charIndex, sheenPos, isReversing,
|
|
144220
|
+
function renderSheen(char, charIndex, sheenPos, isReversing, breatheColor) {
|
|
143942
144221
|
if (char === " ") return " ";
|
|
143943
144222
|
if (char === "█") return `${fg(...BLOCK_RGB)}█${RESET}`;
|
|
143944
144223
|
if (!SHADOW_CHARS.has(char)) return `${fg(...LOGO_RGB)}${char}${RESET}`;
|
|
143945
|
-
|
|
143946
|
-
if (isReversing) color = charIndex <= sheenPos ? LOGO_RGB : accent;
|
|
143947
|
-
else color = charIndex <= sheenPos ? accent : LOGO_RGB;
|
|
143948
|
-
return `${fg(...color)}${char}${RESET}`;
|
|
144224
|
+
return `${fg(...isReversing ? charIndex <= sheenPos ? LOGO_RGB : breatheColor : charIndex <= sheenPos ? breatheColor : LOGO_RGB)}${char}${RESET}`;
|
|
143949
144225
|
}
|
|
143950
144226
|
const LOADING_TEXT = "Ai正在加载中...";
|
|
143951
|
-
function buildShimmerPalette(n,
|
|
144227
|
+
function buildShimmerPalette(n, breatheColor) {
|
|
143952
144228
|
const size = Math.max(8, Math.min(20, Math.ceil(n * 1.5)));
|
|
143953
144229
|
const palette = [];
|
|
143954
144230
|
for (let i = 0; i < size; i++) {
|
|
143955
144231
|
const t = i / (size - 1);
|
|
143956
144232
|
palette.push([
|
|
143957
|
-
Math.round(
|
|
143958
|
-
Math.round(
|
|
143959
|
-
Math.round(
|
|
144233
|
+
Math.round(breatheColor[0] - t * breatheColor[0] * .35),
|
|
144234
|
+
Math.round(breatheColor[1] - t * breatheColor[1] * .6),
|
|
144235
|
+
Math.round(breatheColor[2] - t * breatheColor[2] * .33)
|
|
143960
144236
|
]);
|
|
143961
144237
|
}
|
|
143962
144238
|
return palette;
|
|
143963
144239
|
}
|
|
143964
|
-
function renderShimmer(pulse,
|
|
144240
|
+
function renderShimmer(pulse, breatheColor) {
|
|
143965
144241
|
const chars = LOADING_TEXT.split("");
|
|
143966
144242
|
const n = chars.length;
|
|
143967
|
-
const palette = buildShimmerPalette(n,
|
|
144243
|
+
const palette = buildShimmerPalette(n, breatheColor);
|
|
143968
144244
|
let out = "";
|
|
143969
144245
|
for (let i = 0; i < n; i++) {
|
|
143970
144246
|
const phase = (pulse - i + n) % n;
|
|
@@ -144030,34 +144306,44 @@ function supportsAnsi() {
|
|
|
144030
144306
|
ansiSupported = false;
|
|
144031
144307
|
return false;
|
|
144032
144308
|
}
|
|
144033
|
-
function runLoadingAnimation(theme = "dark") {
|
|
144309
|
+
function runLoadingAnimation(theme = "dark", prefetch) {
|
|
144034
144310
|
if (!supportsAnsi()) {
|
|
144035
|
-
|
|
144036
|
-
|
|
144037
|
-
|
|
144311
|
+
const finish = () => {
|
|
144312
|
+
for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
|
|
144313
|
+
stdout.write(`${BOLD}${fg(...THEME_PRIMARY[theme])}正在唤醒核心...${RESET}\n`);
|
|
144314
|
+
};
|
|
144315
|
+
if (prefetch === void 0) {
|
|
144316
|
+
finish();
|
|
144317
|
+
return Promise.resolve();
|
|
144318
|
+
}
|
|
144319
|
+
return prefetch.catch(() => {}).finally(() => finish()).then(() => void 0);
|
|
144038
144320
|
}
|
|
144039
144321
|
return new Promise((resolve) => {
|
|
144040
144322
|
if (process$1.platform !== "win32") stdout.write("\x1B[?1049h");
|
|
144041
144323
|
stdout.write("\x1B[2J");
|
|
144042
144324
|
stdout.write("\x1B[?25l");
|
|
144043
|
-
const
|
|
144325
|
+
const primary = THEME_PRIMARY[theme];
|
|
144326
|
+
const breathePalette = buildBreathingPalette(primary);
|
|
144044
144327
|
let sheenPos = 0;
|
|
144045
144328
|
let isReversing = false;
|
|
144046
144329
|
let shimmerPulse = 0;
|
|
144330
|
+
let breatheFrame = 0;
|
|
144047
144331
|
let phase = "loading";
|
|
144048
144332
|
function render() {
|
|
144049
144333
|
const { cols, rows } = getTerminalSize();
|
|
144050
144334
|
const lines = [];
|
|
144051
|
-
const contentHeight = LOGO.length +
|
|
144335
|
+
const contentHeight = LOGO.length + 5;
|
|
144052
144336
|
const topPad = Math.max(0, Math.floor((rows - contentHeight) / 2));
|
|
144053
144337
|
for (let i = 0; i < topPad; i++) lines.push("");
|
|
144338
|
+
const breatheColor = breathePalette[breatheFrame] ?? primary;
|
|
144054
144339
|
for (const line of LOGO) {
|
|
144055
144340
|
let colored = "";
|
|
144056
|
-
for (let ci = 0; ci < line.length; ci++) colored += renderSheen(line[ci], ci, sheenPos, isReversing,
|
|
144341
|
+
for (let ci = 0; ci < line.length; ci++) colored += renderSheen(line[ci], ci, sheenPos, isReversing, breatheColor);
|
|
144057
144342
|
lines.push(centerPad(colored, cols));
|
|
144058
144343
|
}
|
|
144059
|
-
|
|
144060
|
-
|
|
144344
|
+
lines.push("");
|
|
144345
|
+
if (phase === "loading") lines.push(centerPad(renderShimmer(shimmerPulse, breatheColor), cols));
|
|
144346
|
+
else lines.push(centerPad(`${BOLD}${fg(...breatheColor)}按下 ENTER 唤醒核心${RESET}`, cols));
|
|
144061
144347
|
lines.push("");
|
|
144062
144348
|
lines.push("");
|
|
144063
144349
|
lines.push(centerPad(`${fg(...DIM_RGB)}按住 Ctrl+C 即可退出 Scream Code${RESET}`, cols));
|
|
@@ -144072,6 +144358,7 @@ function runLoadingAnimation(theme = "dark") {
|
|
|
144072
144358
|
sheenPos = 0;
|
|
144073
144359
|
}
|
|
144074
144360
|
shimmerPulse = (shimmerPulse + 1) % 10;
|
|
144361
|
+
breatheFrame = (breatheFrame + 1) % BREATHE_STEPS;
|
|
144075
144362
|
render();
|
|
144076
144363
|
}
|
|
144077
144364
|
function onData(data) {
|
|
@@ -144111,17 +144398,20 @@ function runLoadingAnimation(theme = "dark") {
|
|
|
144111
144398
|
stdout.write("\x1B[?25h");
|
|
144112
144399
|
if (process$1.platform !== "win32") stdout.write("\x1B[?1049l");
|
|
144113
144400
|
for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
|
|
144114
|
-
stdout.write(`${BOLD}${fg(...
|
|
144401
|
+
stdout.write(`${BOLD}${fg(...primary)}正在唤醒核心...${RESET}\n`);
|
|
144115
144402
|
resolve();
|
|
144116
144403
|
return;
|
|
144117
144404
|
}
|
|
144118
144405
|
stdin.on("data", onData);
|
|
144119
144406
|
render();
|
|
144120
144407
|
const timer = setInterval(tick, SHEEN_INTERVAL_MS);
|
|
144121
|
-
|
|
144408
|
+
const minDelay = new Promise((resolve) => {
|
|
144409
|
+
setTimeout(resolve, LOADING_DURATION_MS);
|
|
144410
|
+
});
|
|
144411
|
+
(prefetch === void 0 ? minDelay : Promise.all([minDelay, prefetch.catch(() => {})])).then(() => {
|
|
144122
144412
|
phase = "ready";
|
|
144123
144413
|
render();
|
|
144124
|
-
}
|
|
144414
|
+
});
|
|
144125
144415
|
});
|
|
144126
144416
|
}
|
|
144127
144417
|
//#endregion
|
|
@@ -144151,14 +144441,15 @@ async function runShell(opts, version) {
|
|
|
144151
144441
|
});
|
|
144152
144442
|
await harness.ensureConfigFile();
|
|
144153
144443
|
await harness.preflight();
|
|
144154
|
-
await runLoadingAnimation(resolvedTheme);
|
|
144444
|
+
await runLoadingAnimation(resolvedTheme, refreshUpdateCache().catch(() => {}));
|
|
144155
144445
|
const tui = new ScreamTUI(harness, {
|
|
144156
144446
|
cliOptions: opts,
|
|
144157
144447
|
tuiConfig,
|
|
144158
144448
|
version,
|
|
144159
144449
|
workDir,
|
|
144160
144450
|
startupNotice: configWarning,
|
|
144161
|
-
resolvedTheme
|
|
144451
|
+
resolvedTheme,
|
|
144452
|
+
updatePrefetched: true
|
|
144162
144453
|
});
|
|
144163
144454
|
tui.onExit = async (exitCode = 0) => {
|
|
144164
144455
|
const sessionId = tui.getCurrentSessionId();
|