scream-code 0.12.5 → 0.12.7
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.
|
@@ -7,7 +7,7 @@ import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } fr
|
|
|
7
7
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
8
8
|
import { C as join$1, D as resolve$1, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize, x as dirname$2, y as KnowledgeStore } from "./src-BH9W5k24.mjs";
|
|
9
9
|
import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
|
|
10
|
-
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-
|
|
10
|
+
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-9ywV88Lh.mjs";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
13
13
|
import * as fs$1 from "node:fs/promises";
|
|
@@ -54,7 +54,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
54
54
|
import { Command, Option } from "commander";
|
|
55
55
|
import { createInterface } from "node:readline/promises";
|
|
56
56
|
import chalk, { chalkStderr } from "chalk";
|
|
57
|
-
import { CombinedAutocompleteProvider, Container, Editor,
|
|
57
|
+
import { CombinedAutocompleteProvider, Container, Editor, HStack, Image, Input, Key, Markdown, ProcessTerminal, ScrollView, Spacer, Text, TuiAltScreen, VStack, decodeKittyPrintable, deleteAllKittyImages, fuzzyFilter, fuzzyMatch, getCapabilities, getImageDimensions, isKeyRelease, matchesKey, sliceByColumn, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@liutod-scream/pi-tui";
|
|
58
58
|
import { highlight, supportsLanguage } from "cli-highlight";
|
|
59
59
|
import { diffWords } from "diff";
|
|
60
60
|
import { gt, valid } from "semver";
|
|
@@ -5600,7 +5600,7 @@ var _BetaToolRunner_instances, _BetaToolRunner_consumed, _BetaToolRunner_mutated
|
|
|
5600
5600
|
/**
|
|
5601
5601
|
* Just Promise.withResolvers(), which is not available in all environments.
|
|
5602
5602
|
*/
|
|
5603
|
-
function promiseWithResolvers() {
|
|
5603
|
+
function promiseWithResolvers$1() {
|
|
5604
5604
|
let resolve;
|
|
5605
5605
|
let reject;
|
|
5606
5606
|
return {
|
|
@@ -5646,7 +5646,7 @@ var BetaToolRunner = class {
|
|
|
5646
5646
|
...options,
|
|
5647
5647
|
headers: buildHeaders$2([{ "x-stainless-helper": helperValue }, options?.headers])
|
|
5648
5648
|
}, "f");
|
|
5649
|
-
__classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers(), "f");
|
|
5649
|
+
__classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers$1(), "f");
|
|
5650
5650
|
if (params.compactionControl?.enabled) console.warn("Anthropic: The `compactionControl` parameter is deprecated and will be removed in a future version. Use server-side compaction instead by passing `edits: [{ type: \"compact_20260112\" }]` in the params passed to `toolRunner()`. See https://platform.claude.com/docs/en/build-with-claude/compaction");
|
|
5651
5651
|
}
|
|
5652
5652
|
async *[(_BetaToolRunner_consumed = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_mutated = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_state = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_options = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_message = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_toolResponse = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_completion = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_iterationCount = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_instances = /* @__PURE__ */ new WeakSet(), _BetaToolRunner_checkAndCompact = async function _BetaToolRunner_checkAndCompact() {
|
|
@@ -5742,7 +5742,7 @@ var BetaToolRunner = class {
|
|
|
5742
5742
|
__classPrivateFieldSet$1(this, _BetaToolRunner_consumed, false, "f");
|
|
5743
5743
|
__classPrivateFieldGet$1(this, _BetaToolRunner_completion, "f").promise.catch(() => {});
|
|
5744
5744
|
__classPrivateFieldGet$1(this, _BetaToolRunner_completion, "f").reject(error);
|
|
5745
|
-
__classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers(), "f");
|
|
5745
|
+
__classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers$1(), "f");
|
|
5746
5746
|
throw error;
|
|
5747
5747
|
}
|
|
5748
5748
|
}
|
|
@@ -76344,14 +76344,84 @@ function maybeStatusCode(error) {
|
|
|
76344
76344
|
}
|
|
76345
76345
|
//#endregion
|
|
76346
76346
|
//#region ../../packages/agent-core/src/agent/context/projector.ts
|
|
76347
|
-
|
|
76348
|
-
|
|
76347
|
+
/** Synthetic error text used when a tool result is missing and must be
|
|
76348
|
+
* filled in so the provider accepts the message sequence. */
|
|
76349
|
+
const SYNTHETIC_TOOL_RESULT_TEXT = "<system>ERROR: The tool call did not complete because of an interruption. Do not assume the tool executed successfully, and do not invent its result.</system>";
|
|
76350
|
+
function project(history, options) {
|
|
76351
|
+
return repairToolExchangeAdjacency(mergeAdjacentUserMessages(history.filter((message) => {
|
|
76349
76352
|
return message.partial !== true && !(message.role === "assistant" && message.content.length === 0 && message.toolCalls.length === 0);
|
|
76350
|
-
});
|
|
76351
|
-
|
|
76352
|
-
|
|
76353
|
+
}), options?.onAnomaly), options);
|
|
76354
|
+
}
|
|
76355
|
+
/**
|
|
76356
|
+
* Closes every tool exchange whose assistant `tool_use` is not fully answered
|
|
76357
|
+
* by a matching `tool_result`. A mid-history orphan (a later user/assistant
|
|
76358
|
+
* message follows) can never be in-flight, so it is always closed by
|
|
76359
|
+
* synthesizing an error result. A trailing orphan is closed only when
|
|
76360
|
+
* `synthesizeMissing` is set — otherwise it is left for the trim / replay
|
|
76361
|
+
* synthesis. This prevents the provider error "must be followed by tool
|
|
76362
|
+
* messages responding to each tool_call_id" after an interruption (e.g. a
|
|
76363
|
+
* network drop mid-batch).
|
|
76364
|
+
*/
|
|
76365
|
+
function repairToolExchangeAdjacency(messages, options) {
|
|
76366
|
+
let lastNonToolIndex = messages.length - 1;
|
|
76367
|
+
while (lastNonToolIndex >= 0 && messages[lastNonToolIndex]?.role === "tool") lastNonToolIndex -= 1;
|
|
76368
|
+
const out = [];
|
|
76369
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
76370
|
+
for (let i = 0; i < messages.length; i++) {
|
|
76371
|
+
if (consumed.has(i)) continue;
|
|
76372
|
+
const message = messages[i];
|
|
76373
|
+
if (message.role !== "assistant" || message.toolCalls.length === 0) {
|
|
76374
|
+
out.push(message);
|
|
76375
|
+
continue;
|
|
76376
|
+
}
|
|
76377
|
+
out.push(message);
|
|
76378
|
+
const pending = new Set(message.toolCalls.map((toolCall) => toolCall.id));
|
|
76379
|
+
let foreignBetween = false;
|
|
76380
|
+
for (let j = i + 1; j < messages.length && pending.size > 0; j++) {
|
|
76381
|
+
if (consumed.has(j)) continue;
|
|
76382
|
+
const next = messages[j];
|
|
76383
|
+
const toolCallId = next.toolCallId;
|
|
76384
|
+
if (next.role === "tool" && toolCallId !== void 0 && pending.has(toolCallId)) {
|
|
76385
|
+
out.push(next);
|
|
76386
|
+
consumed.add(j);
|
|
76387
|
+
pending.delete(toolCallId);
|
|
76388
|
+
if (foreignBetween) options?.onAnomaly?.({
|
|
76389
|
+
kind: "tool_result_reordered",
|
|
76390
|
+
toolCallId
|
|
76391
|
+
});
|
|
76392
|
+
} else foreignBetween = true;
|
|
76393
|
+
}
|
|
76394
|
+
const isMidHistory = i < lastNonToolIndex;
|
|
76395
|
+
if (options?.synthesizeMissing === true || isMidHistory) for (const missingId of pending) {
|
|
76396
|
+
out.push(makeSyntheticToolResult(missingId));
|
|
76397
|
+
options?.onAnomaly?.({
|
|
76398
|
+
kind: "tool_result_synthesized",
|
|
76399
|
+
toolCallId: missingId,
|
|
76400
|
+
trailing: !isMidHistory
|
|
76401
|
+
});
|
|
76402
|
+
}
|
|
76403
|
+
}
|
|
76404
|
+
return out;
|
|
76405
|
+
}
|
|
76406
|
+
function makeSyntheticToolResult(toolCallId) {
|
|
76407
|
+
return {
|
|
76408
|
+
role: "tool",
|
|
76409
|
+
content: [{
|
|
76410
|
+
type: "text",
|
|
76411
|
+
text: SYNTHETIC_TOOL_RESULT_TEXT
|
|
76412
|
+
}],
|
|
76413
|
+
toolCalls: [],
|
|
76414
|
+
toolCallId
|
|
76415
|
+
};
|
|
76353
76416
|
}
|
|
76354
|
-
|
|
76417
|
+
/**
|
|
76418
|
+
* Drops a trailing open tool exchange from a projected message list: when the
|
|
76419
|
+
* last assistant message carries tool calls whose results never arrived and
|
|
76420
|
+
* nothing follows, truncate from that batch so the compacted history does not
|
|
76421
|
+
* carry an unterminated exchange (which would be rejected or, if synthesized,
|
|
76422
|
+
* pollute the compaction prompt).
|
|
76423
|
+
*/
|
|
76424
|
+
function mergeAdjacentUserMessages(history, _onAnomaly) {
|
|
76355
76425
|
const out = [];
|
|
76356
76426
|
for (const message of history) {
|
|
76357
76427
|
const previous = out.at(-1);
|
|
@@ -76621,12 +76691,23 @@ function extractFileOpsFromMessage(message, ops) {
|
|
|
76621
76691
|
}
|
|
76622
76692
|
}
|
|
76623
76693
|
}
|
|
76694
|
+
/** Projects a `FileOperations` accumulator into the persistent, sorted
|
|
76695
|
+
* file lists stored on `CompactionResult`. Unlike `formatFileOperations`
|
|
76696
|
+
* this keeps the full list (no `FILE_LIMIT` elision) so later compactions
|
|
76697
|
+
* can merge the previous round's file context without losing entries. */
|
|
76698
|
+
function computeFileLists(ops) {
|
|
76699
|
+
const modified = new Set([...ops.edited, ...ops.written]);
|
|
76700
|
+
return {
|
|
76701
|
+
readFiles: [...ops.read].toSorted(),
|
|
76702
|
+
modifiedFiles: [...modified].toSorted()
|
|
76703
|
+
};
|
|
76704
|
+
}
|
|
76624
76705
|
const FILE_LIMIT = 20;
|
|
76625
76706
|
function formatFileOperations(ops) {
|
|
76626
76707
|
const modified = new Set([...ops.edited, ...ops.written]);
|
|
76627
|
-
const readOnly = [...ops.read].filter((f) => !modified.has(f)).
|
|
76628
|
-
const modifiedFiles = [...modified].
|
|
76629
|
-
const all = [...new Set([...readOnly, ...modifiedFiles])].
|
|
76708
|
+
const readOnly = [...ops.read].filter((f) => !modified.has(f)).toSorted();
|
|
76709
|
+
const modifiedFiles = [...modified].toSorted();
|
|
76710
|
+
const all = [...new Set([...readOnly, ...modifiedFiles])].toSorted();
|
|
76630
76711
|
if (all.length === 0) return "";
|
|
76631
76712
|
const mode = /* @__PURE__ */ new Map();
|
|
76632
76713
|
for (const f of readOnly) mode.set(f, "Read");
|
|
@@ -76769,6 +76850,10 @@ var FullCompaction = class {
|
|
|
76769
76850
|
* limit → overflow → compact cycle from consuming the entire
|
|
76770
76851
|
* maxCompactionPerTurn budget with marginal savings. */
|
|
76771
76852
|
reactiveAttempted = false;
|
|
76853
|
+
/** File lists from the most recent successful compaction. Merged into the
|
|
76854
|
+
* next compaction's file operations so file context accumulates across
|
|
76855
|
+
* repeated compactions instead of being reset each round. */
|
|
76856
|
+
lastCompactionFiles;
|
|
76772
76857
|
compacting = null;
|
|
76773
76858
|
_compactedHistory = [];
|
|
76774
76859
|
strategy;
|
|
@@ -77000,15 +77085,25 @@ var FullCompaction = class {
|
|
|
77000
77085
|
const recent = originalHistory.slice(compactedCount);
|
|
77001
77086
|
const messagesToCompactForOps = originalHistory.slice(0, compactedCount);
|
|
77002
77087
|
const fileOps = createFileOps();
|
|
77088
|
+
if (this.lastCompactionFiles !== void 0 && extractPreviousSummary(originalHistory) !== null) {
|
|
77089
|
+
for (const f of this.lastCompactionFiles.readFiles) fileOps.read.add(f);
|
|
77090
|
+
for (const f of this.lastCompactionFiles.modifiedFiles) fileOps.edited.add(f);
|
|
77091
|
+
} else this.lastCompactionFiles = void 0;
|
|
77003
77092
|
for (const msg of messagesToCompactForOps) extractFileOpsFromMessage(msg, fileOps);
|
|
77004
77093
|
const toolCallHistory = formatToolCallHistory(messagesToCompactForOps);
|
|
77005
77094
|
const processedSummary = this.postProcessSummary(summary, fileOps, toolCallHistory);
|
|
77006
77095
|
const tokensAfter = estimateTokens$1(processedSummary) + estimateTokensForMessages(recent);
|
|
77096
|
+
const fileLists = computeFileLists(fileOps);
|
|
77097
|
+
const MAX_PERSISTED_FILES = 100;
|
|
77098
|
+
const readFiles = fileLists.readFiles.slice(0, MAX_PERSISTED_FILES);
|
|
77099
|
+
const modifiedFiles = fileLists.modifiedFiles.slice(0, MAX_PERSISTED_FILES);
|
|
77007
77100
|
const result = {
|
|
77008
77101
|
summary: processedSummary,
|
|
77009
77102
|
compactedCount,
|
|
77010
77103
|
tokensBefore,
|
|
77011
77104
|
tokensAfter,
|
|
77105
|
+
...readFiles.length > 0 ? { readFiles } : {},
|
|
77106
|
+
...modifiedFiles.length > 0 ? { modifiedFiles } : {},
|
|
77012
77107
|
...isUpdate ? { isUpdate: true } : {}
|
|
77013
77108
|
};
|
|
77014
77109
|
this.markCompleted();
|
|
@@ -77017,6 +77112,7 @@ var FullCompaction = class {
|
|
|
77017
77112
|
result
|
|
77018
77113
|
});
|
|
77019
77114
|
this.agent.context.applyCompaction(result);
|
|
77115
|
+
this.lastCompactionFiles = fileLists;
|
|
77020
77116
|
this.lowWaterMark = Math.floor(this.effectiveTokenCount * 1.1);
|
|
77021
77117
|
await this.extractAndStoreMemos(processedSummary);
|
|
77022
77118
|
this.triggerPostCompactHook(data, result);
|
|
@@ -77145,7 +77241,9 @@ var FullCompaction = class {
|
|
|
77145
77241
|
*/
|
|
77146
77242
|
postProcessSummary(summary, fileOps, toolCallHistory) {
|
|
77147
77243
|
const todos = this.agent.tools.storeData()["todo"] ?? [];
|
|
77148
|
-
const
|
|
77244
|
+
const base = summary.trim().replaceAll(/<files>[\s\S]*?<\/files>\s*/g, "").trimEnd();
|
|
77245
|
+
const sections = [];
|
|
77246
|
+
if (base.length > 0) sections.push(base);
|
|
77149
77247
|
if (todos.length > 0) {
|
|
77150
77248
|
const lines = todos.map((t) => {
|
|
77151
77249
|
return `- [${t.status === "done" ? "x" : t.status === "in_progress" ? "-" : " "}] ${t.title}`;
|
|
@@ -78804,12 +78902,15 @@ var ContextMemory = class {
|
|
|
78804
78902
|
* message mutated (compaction summary, micro-compaction truncation, or a
|
|
78805
78903
|
* projection repair) and the cache broke from that index.
|
|
78806
78904
|
*
|
|
78807
|
-
*
|
|
78808
|
-
*
|
|
78905
|
+
* Unlike the read-only `messages` getter, this path closes any trailing
|
|
78906
|
+
* in-flight tool call by synthesizing an error result (synthesizeMissing):
|
|
78907
|
+
* these messages go straight to the provider, which rejects an assistant
|
|
78908
|
+
* tool_calls message with no matching tool result (e.g. after a network
|
|
78909
|
+
* drop mid-batch).
|
|
78809
78910
|
*/
|
|
78810
78911
|
messagesForLLM() {
|
|
78811
78912
|
this.agent.microCompaction.detect();
|
|
78812
|
-
const messages = project(this.agent.microCompaction.compact(this.history));
|
|
78913
|
+
const messages = project(this.agent.microCompaction.compact(this.history), { synthesizeMissing: true });
|
|
78813
78914
|
this.observePrefixStability(messages);
|
|
78814
78915
|
return messages;
|
|
78815
78916
|
}
|
|
@@ -78911,6 +79012,31 @@ var ContextMemory = class {
|
|
|
78911
79012
|
hasOpenToolExchange() {
|
|
78912
79013
|
return this.pendingToolResultIds.size > 0;
|
|
78913
79014
|
}
|
|
79015
|
+
/**
|
|
79016
|
+
* Defensive teardown for a live turn that ended — normally, cancelled, or
|
|
79017
|
+
* failed — while recorded tool calls were still awaiting results (e.g. the
|
|
79018
|
+
* batch's result dispatch died after a `tool.call` was already recorded,
|
|
79019
|
+
* like a network drop mid-execution). Synthesizes an error result for each
|
|
79020
|
+
* dangling call so the exchange closes: left open, the assistant tool_calls
|
|
79021
|
+
* message would have no matching tool message and the next request would be
|
|
79022
|
+
* rejected by the provider ("must be followed by tool messages responding
|
|
79023
|
+
* to each tool_call_id"). No-op when the exchange is already closed.
|
|
79024
|
+
*/
|
|
79025
|
+
closeAbandonedToolExchange(output) {
|
|
79026
|
+
if (this.pendingToolResultIds.size === 0) return 0;
|
|
79027
|
+
const interruptedToolCallIds = [...this.pendingToolResultIds];
|
|
79028
|
+
for (const toolCallId of interruptedToolCallIds) this.appendLoopEvent({
|
|
79029
|
+
type: "tool.result",
|
|
79030
|
+
parentUuid: toolCallId,
|
|
79031
|
+
toolCallId,
|
|
79032
|
+
result: {
|
|
79033
|
+
output,
|
|
79034
|
+
isError: true
|
|
79035
|
+
}
|
|
79036
|
+
});
|
|
79037
|
+
this.flushDeferredMessagesIfToolExchangeClosed();
|
|
79038
|
+
return interruptedToolCallIds.length;
|
|
79039
|
+
}
|
|
78914
79040
|
pushHistory(...messages) {
|
|
78915
79041
|
this._history.push(...messages);
|
|
78916
79042
|
for (const message of messages) {
|
|
@@ -96333,6 +96459,11 @@ var ToolCallDeduplicator = class {
|
|
|
96333
96459
|
};
|
|
96334
96460
|
//#endregion
|
|
96335
96461
|
//#region ../../packages/agent-core/src/agent/turn/index.ts
|
|
96462
|
+
/** Builds the error text synthesized for tool calls abandoned when a live
|
|
96463
|
+
* turn ends (cancelled, failed, or completed) before their results arrived. */
|
|
96464
|
+
function abandonedToolResultOutput(ended) {
|
|
96465
|
+
return `Tool call did not complete: ${ended.reason === "cancelled" ? "the turn was cancelled" : ended.reason === "failed" ? `the turn failed${ended.error !== void 0 ? ` (${ended.error.message})` : ""}` : "the turn ended"} before its result was recorded. Do not assume the tool completed successfully.`;
|
|
96466
|
+
}
|
|
96336
96467
|
const GOAL_CONTINUATION_PROMPT = [
|
|
96337
96468
|
"Continue working toward the active goal.",
|
|
96338
96469
|
"Keep the self-audit brief. Do not explore unrelated interpretations once the goal can be",
|
|
@@ -96567,6 +96698,11 @@ var TurnFlow = class {
|
|
|
96567
96698
|
turnId,
|
|
96568
96699
|
reason: "completed"
|
|
96569
96700
|
};
|
|
96701
|
+
try {
|
|
96702
|
+
this.agent.context.closeAbandonedToolExchange(abandonedToolResultOutput(ended));
|
|
96703
|
+
} catch (error) {
|
|
96704
|
+
console.error("closeAbandonedToolExchange failed", error);
|
|
96705
|
+
}
|
|
96570
96706
|
this.agent.usage.endTurn();
|
|
96571
96707
|
this.agent.emitEvent(ended);
|
|
96572
96708
|
return ended;
|
|
@@ -96646,6 +96782,11 @@ var TurnFlow = class {
|
|
|
96646
96782
|
};
|
|
96647
96783
|
}
|
|
96648
96784
|
}
|
|
96785
|
+
try {
|
|
96786
|
+
this.agent.context.closeAbandonedToolExchange(abandonedToolResultOutput(ended));
|
|
96787
|
+
} catch (error) {
|
|
96788
|
+
console.error("closeAbandonedToolExchange failed", error);
|
|
96789
|
+
}
|
|
96649
96790
|
if (this.currentId === turnId) this.agent.usage.endTurn();
|
|
96650
96791
|
this.agent.emitEvent(ended);
|
|
96651
96792
|
if (standalone && this.currentId === turnId) this.activeTurn = null;
|
|
@@ -102568,7 +102709,7 @@ var SessionSubagentHost = class {
|
|
|
102568
102709
|
text: childPrompt
|
|
102569
102710
|
}], origin);
|
|
102570
102711
|
await runChildTurnToCompletion(child, options.signal);
|
|
102571
|
-
let result = lastAssistantText(child);
|
|
102712
|
+
let result = lastAssistantText$1(child);
|
|
102572
102713
|
let remainingContinuations = SUMMARY_CONTINUATION_ATTEMPTS;
|
|
102573
102714
|
while (remainingContinuations > 0 && result.length < SUMMARY_MIN_LENGTH) {
|
|
102574
102715
|
remainingContinuations -= 1;
|
|
@@ -102578,7 +102719,7 @@ var SessionSubagentHost = class {
|
|
|
102578
102719
|
text: summary_continuation_default
|
|
102579
102720
|
}], origin);
|
|
102580
102721
|
await runChildTurnToCompletion(child, options.signal);
|
|
102581
|
-
result = lastAssistantText(child);
|
|
102722
|
+
result = lastAssistantText$1(child);
|
|
102582
102723
|
}
|
|
102583
102724
|
const usage = child.usage.data().total;
|
|
102584
102725
|
let findingsBlock = "";
|
|
@@ -102694,7 +102835,7 @@ async function runChildTurnToCompletion(child, signal) {
|
|
|
102694
102835
|
function throwIfSubagentStoppedAtMaxTokens(stopReason) {
|
|
102695
102836
|
if (stopReason === "max_tokens") throw new Error(`${SUBAGENT_MAX_TOKENS_ERROR}.`);
|
|
102696
102837
|
}
|
|
102697
|
-
function lastAssistantText(agent) {
|
|
102838
|
+
function lastAssistantText$1(agent) {
|
|
102698
102839
|
for (const message of [...agent.context.history].toReversed()) {
|
|
102699
102840
|
if (message.role !== "assistant") continue;
|
|
102700
102841
|
const text = message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
@@ -120191,10 +120332,10 @@ var ScreamAuthFacade = class {
|
|
|
120191
120332
|
};
|
|
120192
120333
|
//#endregion
|
|
120193
120334
|
//#region ../../packages/node-sdk/src/rpc.ts
|
|
120194
|
-
const MAIN_AGENT_ID$
|
|
120335
|
+
const MAIN_AGENT_ID$3 = "main";
|
|
120195
120336
|
var SDKRpcClient = class {
|
|
120196
120337
|
core;
|
|
120197
|
-
interactiveAgentId = MAIN_AGENT_ID$
|
|
120338
|
+
interactiveAgentId = MAIN_AGENT_ID$3;
|
|
120198
120339
|
ready;
|
|
120199
120340
|
rpc;
|
|
120200
120341
|
eventListeners = /* @__PURE__ */ new Set();
|
|
@@ -120772,7 +120913,7 @@ function errorMessage$2(error) {
|
|
|
120772
120913
|
}
|
|
120773
120914
|
//#endregion
|
|
120774
120915
|
//#region ../../packages/node-sdk/src/session.ts
|
|
120775
|
-
const MAIN_AGENT_ID$
|
|
120916
|
+
const MAIN_AGENT_ID$2 = "main";
|
|
120776
120917
|
var Session = class {
|
|
120777
120918
|
id;
|
|
120778
120919
|
workDir;
|
|
@@ -121212,7 +121353,7 @@ var Session = class {
|
|
|
121212
121353
|
this.emit({
|
|
121213
121354
|
type: "session.meta.updated",
|
|
121214
121355
|
sessionId: this.id,
|
|
121215
|
-
agentId: MAIN_AGENT_ID$
|
|
121356
|
+
agentId: MAIN_AGENT_ID$2,
|
|
121216
121357
|
title: patch.title,
|
|
121217
121358
|
patch
|
|
121218
121359
|
});
|
|
@@ -121634,7 +121775,7 @@ function optionalBuildString(value) {
|
|
|
121634
121775
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
121635
121776
|
}
|
|
121636
121777
|
const SCREAM_BUILD_INFO = {
|
|
121637
|
-
version: optionalBuildString("0.12.
|
|
121778
|
+
version: optionalBuildString("0.12.7"),
|
|
121638
121779
|
channel: optionalBuildString(""),
|
|
121639
121780
|
commit: optionalBuildString(""),
|
|
121640
121781
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -122906,6 +123047,12 @@ const BUILTIN_SLASH_COMMANDS = [
|
|
|
122906
123047
|
description: "registry.logout_desc",
|
|
122907
123048
|
priority: 93
|
|
122908
123049
|
},
|
|
123050
|
+
{
|
|
123051
|
+
name: "eval",
|
|
123052
|
+
aliases: [],
|
|
123053
|
+
description: "registry.eval_desc",
|
|
123054
|
+
priority: 92
|
|
123055
|
+
},
|
|
122909
123056
|
{
|
|
122910
123057
|
name: "exit",
|
|
122911
123058
|
aliases: ["quit", "q"],
|
|
@@ -123022,7 +123169,7 @@ function getCtrlDHint() {
|
|
|
123022
123169
|
function getCtrlCHint() {
|
|
123023
123170
|
return t("constant.ctrl_c_hint");
|
|
123024
123171
|
}
|
|
123025
|
-
const MAIN_AGENT_ID = "main";
|
|
123172
|
+
const MAIN_AGENT_ID$1 = "main";
|
|
123026
123173
|
const EXIT_CONFIRM_WINDOW_MS = 1500;
|
|
123027
123174
|
function isManagedUsageProvider(providerKey) {
|
|
123028
123175
|
return providerKey === DEFAULT_OAUTH_PROVIDER_NAME;
|
|
@@ -123993,7 +124140,7 @@ async function handleConnectCommand(host, args) {
|
|
|
123993
124140
|
ok: false,
|
|
123994
124141
|
label: "Failed to load catalog."
|
|
123995
124142
|
});
|
|
123996
|
-
host.showError(
|
|
124143
|
+
host.showError(t("auth.catalog_fetch_failed", { error: formatErrorMessage(error) }));
|
|
123997
124144
|
}
|
|
123998
124145
|
}
|
|
123999
124146
|
}
|
|
@@ -127448,7 +127595,7 @@ async function guidedGoalSetup(host) {
|
|
|
127448
127595
|
host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
|
|
127449
127596
|
return;
|
|
127450
127597
|
}
|
|
127451
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
127598
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-BIXonNNb.mjs");
|
|
127452
127599
|
const initialDesc = await promptText(host, TextInputDialogComponent, {
|
|
127453
127600
|
title: t("goal.setup_title_initial"),
|
|
127454
127601
|
subtitle: t("goal.setup_desc_hint"),
|
|
@@ -127469,7 +127616,7 @@ async function guidedGoalSetup(host) {
|
|
|
127469
127616
|
await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
|
|
127470
127617
|
}
|
|
127471
127618
|
async function showGoalConfigWizard(host, session, objective, replace) {
|
|
127472
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
127619
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-BIXonNNb.mjs");
|
|
127473
127620
|
const turnInput = await promptNumber(host, TextInputDialogComponent, {
|
|
127474
127621
|
title: t("goal.wizard_title", { objective }),
|
|
127475
127622
|
subtitle: t("goal.budget_turns_hint"),
|
|
@@ -128281,7 +128428,7 @@ var AssistantMessageComponent = class {
|
|
|
128281
128428
|
const age = Date.now() - (this.fadeStartMs ?? 0);
|
|
128282
128429
|
this.cachedWidth = void 0;
|
|
128283
128430
|
this.cachedLines = void 0;
|
|
128284
|
-
this.ui?.
|
|
128431
|
+
this.ui?.requestRender();
|
|
128285
128432
|
if (age >= 1200) this.stopFade();
|
|
128286
128433
|
}, FADE_TICK_MS);
|
|
128287
128434
|
this.ui.requestRender();
|
|
@@ -128775,7 +128922,7 @@ var ThinkingComponent = class {
|
|
|
128775
128922
|
if (this.ui === void 0 || this.spinnerInterval !== void 0) return;
|
|
128776
128923
|
this.spinnerInterval = setInterval(() => {
|
|
128777
128924
|
this.spinnerFrame = (this.spinnerFrame + 1) % BRAILLE_SPINNER_FRAMES.length;
|
|
128778
|
-
this.ui?.
|
|
128925
|
+
this.ui?.requestRender();
|
|
128779
128926
|
}, 80);
|
|
128780
128927
|
}
|
|
128781
128928
|
stopSpinner() {
|
|
@@ -133217,7 +133364,7 @@ var MoonLoader = class extends Text {
|
|
|
133217
133364
|
const frame = BRAILLE_SPINNER_FRAMES[this.currentFrame];
|
|
133218
133365
|
const coloredFrame = this.colorFn ? this.colorFn(frame) : frame;
|
|
133219
133366
|
this.setText(this.label ? `${coloredFrame} ${this.label}` : coloredFrame);
|
|
133220
|
-
this.ui.
|
|
133367
|
+
this.ui.requestRender();
|
|
133221
133368
|
}
|
|
133222
133369
|
};
|
|
133223
133370
|
//#endregion
|
|
@@ -133353,9 +133500,6 @@ function getFallbackSkillMarketplace() {
|
|
|
133353
133500
|
}
|
|
133354
133501
|
//#endregion
|
|
133355
133502
|
//#region src/tui/utils/component-capabilities.ts
|
|
133356
|
-
function isExpandable(obj) {
|
|
133357
|
-
return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
|
|
133358
|
-
}
|
|
133359
133503
|
function isPlanExpandable(obj) {
|
|
133360
133504
|
return typeof obj === "object" && obj !== null && "setPlanExpanded" in obj && typeof obj.setPlanExpanded === "function";
|
|
133361
133505
|
}
|
|
@@ -136035,6 +136179,429 @@ async function handleKnowledgeCommand(host, _args) {
|
|
|
136035
136179
|
showMenu();
|
|
136036
136180
|
}
|
|
136037
136181
|
//#endregion
|
|
136182
|
+
//#region ../../packages/evals/src/judge.ts
|
|
136183
|
+
/** Runs every rule against the input; a rule passes when its check is true. */
|
|
136184
|
+
function judgeOutput(input, rules) {
|
|
136185
|
+
const passed = [];
|
|
136186
|
+
const failed = [];
|
|
136187
|
+
for (const rule of rules) if (rule.check(input)) passed.push(rule.name);
|
|
136188
|
+
else failed.push(rule.name);
|
|
136189
|
+
return {
|
|
136190
|
+
passed,
|
|
136191
|
+
failed
|
|
136192
|
+
};
|
|
136193
|
+
}
|
|
136194
|
+
//#endregion
|
|
136195
|
+
//#region ../../packages/evals/src/harness.ts
|
|
136196
|
+
/**
|
|
136197
|
+
* Minimal end-to-end eval harness built on the public node-sdk
|
|
136198
|
+
* `ScreamHarness`. Each eval run gets an isolated temp **workspace** so runs
|
|
136199
|
+
* never touch real project files; the scream home defaults to the real one so
|
|
136200
|
+
* the provider/API-key configuration in `~/.scream-code/config.toml` is
|
|
136201
|
+
* honored (pass `screamHome` to override for fully isolated runs). Model
|
|
136202
|
+
* selection follows `SCREAM_EVAL_MODEL` (e.g. `provider/model`);
|
|
136203
|
+
* when unset the harness throws with a clear message.
|
|
136204
|
+
*/
|
|
136205
|
+
/** The main agent id; only its events settle the eval turn wait. */
|
|
136206
|
+
const MAIN_AGENT_ID = "main";
|
|
136207
|
+
/** The current scream default model, when resolvable from config. */
|
|
136208
|
+
const DEFAULT_EVAL_MODEL = process.env["SCREAM_EVAL_MODEL"];
|
|
136209
|
+
/**
|
|
136210
|
+
* Runs a single end-to-end prompt against a fresh isolated session and
|
|
136211
|
+
* returns the final assistant text plus token usage. The session, workspace
|
|
136212
|
+
* and scream home are torn down afterwards.
|
|
136213
|
+
*/
|
|
136214
|
+
async function runEvalPrompt(input, options = {}) {
|
|
136215
|
+
const workDir = options.workDir ?? await mkdtemp(join(tmpdir(), "scream-eval-ws-"));
|
|
136216
|
+
const screamHome = options.screamHome;
|
|
136217
|
+
const createdWorkDir = options.workDir === void 0;
|
|
136218
|
+
let harness;
|
|
136219
|
+
let session;
|
|
136220
|
+
try {
|
|
136221
|
+
for (const fixture of (typeof input === "string" ? [] : input.fixtures) ?? []) await writeFile(join(workDir, fixture.name), fixture.content, "utf-8");
|
|
136222
|
+
const prompt = typeof input === "string" ? input : input.prompt;
|
|
136223
|
+
const model = options.model ?? DEFAULT_EVAL_MODEL;
|
|
136224
|
+
if (model === void 0 || model.length === 0) throw new Error("No eval model configured. Set SCREAM_EVAL_MODEL, e.g. SCREAM_EVAL_MODEL=provider/model pnpm eval");
|
|
136225
|
+
const [provider, modelId, ...rest] = model.split("/");
|
|
136226
|
+
const usageProvider = rest.length > 0 ? model : provider ?? "unknown";
|
|
136227
|
+
const usageModel = modelId === void 0 || rest.length > 0 ? model : modelId;
|
|
136228
|
+
harness = new ScreamHarness({ homeDir: screamHome });
|
|
136229
|
+
session = await harness.createSession({
|
|
136230
|
+
workDir,
|
|
136231
|
+
model,
|
|
136232
|
+
thinking: options.thinking ?? "off",
|
|
136233
|
+
permission: "yolo"
|
|
136234
|
+
});
|
|
136235
|
+
const { timedOut } = await promptAndWaitForTurnEnd(session, prompt);
|
|
136236
|
+
let output = "";
|
|
136237
|
+
try {
|
|
136238
|
+
output = lastAssistantText((await session.getContext()).history);
|
|
136239
|
+
} catch {
|
|
136240
|
+
output = "";
|
|
136241
|
+
}
|
|
136242
|
+
const total = (await session.getUsage().catch(() => void 0))?.total;
|
|
136243
|
+
let verifiedFile = false;
|
|
136244
|
+
if (options.verifyFileAfterTurn !== void 0) try {
|
|
136245
|
+
verifiedFile = await readFile(join(workDir, options.verifyFileAfterTurn.path), "utf-8") === options.verifyFileAfterTurn.content;
|
|
136246
|
+
} catch {
|
|
136247
|
+
verifiedFile = false;
|
|
136248
|
+
}
|
|
136249
|
+
return {
|
|
136250
|
+
output,
|
|
136251
|
+
timedOut,
|
|
136252
|
+
verifiedFile,
|
|
136253
|
+
usage: {
|
|
136254
|
+
provider: usageProvider,
|
|
136255
|
+
model: usageModel,
|
|
136256
|
+
inputTokens: total?.inputOther ?? 0,
|
|
136257
|
+
outputTokens: total?.output ?? 0,
|
|
136258
|
+
totalTokens: (total?.inputOther ?? 0) + (total?.output ?? 0) + (total?.inputCacheRead ?? 0) + (total?.inputCacheCreation ?? 0)
|
|
136259
|
+
}
|
|
136260
|
+
};
|
|
136261
|
+
} finally {
|
|
136262
|
+
if (session !== void 0) await session.close({ extractMemories: false }).catch(() => {});
|
|
136263
|
+
if (createdWorkDir) await rm(workDir, {
|
|
136264
|
+
recursive: true,
|
|
136265
|
+
force: true
|
|
136266
|
+
}).catch(() => {});
|
|
136267
|
+
}
|
|
136268
|
+
}
|
|
136269
|
+
function lastAssistantText(history) {
|
|
136270
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
136271
|
+
const message = history[i];
|
|
136272
|
+
if (message.role !== "assistant") continue;
|
|
136273
|
+
const text = (message.content ?? []).filter((part) => part.type === "text" && part.text !== void 0).map((part) => part.text).join("");
|
|
136274
|
+
if (text.trim().length > 0) return text.trim();
|
|
136275
|
+
}
|
|
136276
|
+
return "";
|
|
136277
|
+
}
|
|
136278
|
+
/**
|
|
136279
|
+
* Sends a prompt and resolves when the main agent's turn ends. `Session.prompt`
|
|
136280
|
+
* only enqueues the RPC request; the assistant reply arrives asynchronously as
|
|
136281
|
+
* events, so we must wait for the `turn.ended` event to know the run finished.
|
|
136282
|
+
*
|
|
136283
|
+
* A hard timeout guards against a turn that never ends (e.g. a model looping
|
|
136284
|
+
* through tools instead of converging, or a network interruption). On timeout
|
|
136285
|
+
* we cancel the session so the caller's cleanup path still runs instead of
|
|
136286
|
+
* leaking the session and temp workspace. 90s is enough to complete any
|
|
136287
|
+
* legitimate single-turn tool round-trip; longer waits are almost always a
|
|
136288
|
+
* stuck model, so we fail fast instead of burning tokens.
|
|
136289
|
+
*/
|
|
136290
|
+
const TURN_WAIT_TIMEOUT_MS = 9e4;
|
|
136291
|
+
/**
|
|
136292
|
+
* Resolves with `timedOut: true` when the turn hit the timeout (model looping
|
|
136293
|
+
* or stuck) instead of throwing, so callers can still inspect side effects
|
|
136294
|
+
* (e.g. a file the Write tool already created). Real errors (turn failed /
|
|
136295
|
+
* error event) still reject.
|
|
136296
|
+
*/
|
|
136297
|
+
async function promptAndWaitForTurnEnd(session, prompt) {
|
|
136298
|
+
const { promise, resolve, reject } = promiseWithResolvers();
|
|
136299
|
+
let activeTurnId;
|
|
136300
|
+
let activeAgentId;
|
|
136301
|
+
let settled = false;
|
|
136302
|
+
let timeout;
|
|
136303
|
+
let graceTimer;
|
|
136304
|
+
/** Set once the timeout fires; guards against the cancel-triggered
|
|
136305
|
+
* `turn.ended('cancelled')` being misread as a real failure. */
|
|
136306
|
+
let timeoutFired = false;
|
|
136307
|
+
const finish = (result) => {
|
|
136308
|
+
if (settled) return;
|
|
136309
|
+
settled = true;
|
|
136310
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
136311
|
+
if (graceTimer !== void 0) clearTimeout(graceTimer);
|
|
136312
|
+
if (result instanceof Error) reject(result);
|
|
136313
|
+
else resolve(result);
|
|
136314
|
+
};
|
|
136315
|
+
const unsubscribe = session.onEvent((event) => {
|
|
136316
|
+
if (event.agentId !== MAIN_AGENT_ID) return;
|
|
136317
|
+
if (event.type === "error") {
|
|
136318
|
+
finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
|
|
136319
|
+
return;
|
|
136320
|
+
}
|
|
136321
|
+
if (event.type === "turn.started" && activeTurnId === void 0) {
|
|
136322
|
+
activeTurnId = event.turnId;
|
|
136323
|
+
activeAgentId = event.agentId;
|
|
136324
|
+
return;
|
|
136325
|
+
}
|
|
136326
|
+
if (activeTurnId === void 0 || activeAgentId === void 0 || !("turnId" in event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
|
|
136327
|
+
if (event.type === "turn.ended") if (event.reason === "completed") finish({ timedOut: false });
|
|
136328
|
+
else if (timeoutFired && event.reason === "cancelled") return;
|
|
136329
|
+
else finish(/* @__PURE__ */ new Error(`Turn ended with reason: ${event.reason}`));
|
|
136330
|
+
});
|
|
136331
|
+
try {
|
|
136332
|
+
await session.prompt(prompt);
|
|
136333
|
+
timeout = setTimeout(() => {
|
|
136334
|
+
timeoutFired = true;
|
|
136335
|
+
session.cancel().catch(() => {});
|
|
136336
|
+
graceTimer = setTimeout(() => {
|
|
136337
|
+
finish({ timedOut: true });
|
|
136338
|
+
}, 500);
|
|
136339
|
+
}, TURN_WAIT_TIMEOUT_MS);
|
|
136340
|
+
return await promise;
|
|
136341
|
+
} finally {
|
|
136342
|
+
unsubscribe();
|
|
136343
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
136344
|
+
if (graceTimer !== void 0) clearTimeout(graceTimer);
|
|
136345
|
+
}
|
|
136346
|
+
}
|
|
136347
|
+
function promiseWithResolvers() {
|
|
136348
|
+
let resolve;
|
|
136349
|
+
let reject;
|
|
136350
|
+
return {
|
|
136351
|
+
promise: new Promise((res, rej) => {
|
|
136352
|
+
resolve = res;
|
|
136353
|
+
reject = rej;
|
|
136354
|
+
}),
|
|
136355
|
+
resolve,
|
|
136356
|
+
reject
|
|
136357
|
+
};
|
|
136358
|
+
}
|
|
136359
|
+
const EVAL_CASES = [
|
|
136360
|
+
{
|
|
136361
|
+
id: "smoke",
|
|
136362
|
+
name: "Smoke — answers a trivial question",
|
|
136363
|
+
input: "What is the capital of France? Answer in one word.",
|
|
136364
|
+
rules: [{
|
|
136365
|
+
name: "answers the capital",
|
|
136366
|
+
check: (output) => {
|
|
136367
|
+
const lower = output.toLowerCase();
|
|
136368
|
+
return lower.includes("paris") || lower.includes("巴黎");
|
|
136369
|
+
}
|
|
136370
|
+
}],
|
|
136371
|
+
extra: (result) => {
|
|
136372
|
+
const failures = [];
|
|
136373
|
+
if (result.usage.totalTokens <= 0) failures.push("usage.totalTokens must be > 0");
|
|
136374
|
+
if (result.usage.model.length === 0) failures.push("usage.model must be non-empty");
|
|
136375
|
+
return failures;
|
|
136376
|
+
}
|
|
136377
|
+
},
|
|
136378
|
+
{
|
|
136379
|
+
id: "read-file",
|
|
136380
|
+
name: "Regression — reads a file via the Read tool",
|
|
136381
|
+
input: {
|
|
136382
|
+
prompt: "Read the file data.txt in the current directory using the Read tool, then tell me what it contains.",
|
|
136383
|
+
fixtures: [{
|
|
136384
|
+
name: "data.txt",
|
|
136385
|
+
content: "The quick brown fox jumps over the lazy dog."
|
|
136386
|
+
}]
|
|
136387
|
+
},
|
|
136388
|
+
rules: [{
|
|
136389
|
+
name: "reports file content",
|
|
136390
|
+
check: (output) => output.includes("quick brown fox")
|
|
136391
|
+
}]
|
|
136392
|
+
},
|
|
136393
|
+
{
|
|
136394
|
+
id: "write-file",
|
|
136395
|
+
name: "Regression — writes a file via the Write tool",
|
|
136396
|
+
input: { prompt: "Create a file named output.txt in the current directory using the Write tool. Write exactly the text: hello eval world. Make exactly one Write tool call and do not call any other tool afterwards. After the tool completes, immediately answer with the exact text you wrote and nothing else." },
|
|
136397
|
+
rules: [{
|
|
136398
|
+
name: "Write tool landed the file",
|
|
136399
|
+
check: (output) => output.includes("hello eval world")
|
|
136400
|
+
}],
|
|
136401
|
+
verifyFile: {
|
|
136402
|
+
path: "output.txt",
|
|
136403
|
+
content: "hello eval world"
|
|
136404
|
+
},
|
|
136405
|
+
extra: (result) => {
|
|
136406
|
+
const failures = [];
|
|
136407
|
+
if (!result.verifiedFile) failures.push("output.txt was not created with the exact content");
|
|
136408
|
+
return failures;
|
|
136409
|
+
}
|
|
136410
|
+
},
|
|
136411
|
+
{
|
|
136412
|
+
id: "isolated-workspace",
|
|
136413
|
+
name: "Regression — runs in an isolated temp workspace",
|
|
136414
|
+
input: {
|
|
136415
|
+
prompt: "Run \"pwd\" in a shell and answer with only the absolute path that the command printed.",
|
|
136416
|
+
fixtures: [{
|
|
136417
|
+
name: "marker.txt",
|
|
136418
|
+
content: "isolated"
|
|
136419
|
+
}]
|
|
136420
|
+
},
|
|
136421
|
+
rules: [{
|
|
136422
|
+
name: "prints an absolute path",
|
|
136423
|
+
check: (output) => output.match(/(?:\/[\w.\-/]+|[A-Za-z]:[\\/][\w.\\/-]*)/)?.[0] !== void 0
|
|
136424
|
+
}, {
|
|
136425
|
+
name: "path is not the repo",
|
|
136426
|
+
check: (output) => !output.match(/(?:\/[\w.\-/]+|[A-Za-z]:[\\/][\w.\\/-]*)/)?.[0]?.includes("scream-code")
|
|
136427
|
+
}]
|
|
136428
|
+
}
|
|
136429
|
+
];
|
|
136430
|
+
/** Runs every eval case sequentially and returns a structured report. */
|
|
136431
|
+
async function runAllEvals(options = {}) {
|
|
136432
|
+
const results = [];
|
|
136433
|
+
let passedCount = 0;
|
|
136434
|
+
let failedCount = 0;
|
|
136435
|
+
for (const caseDef of EVAL_CASES) {
|
|
136436
|
+
let result;
|
|
136437
|
+
try {
|
|
136438
|
+
result = await runEvalPrompt(caseDef.input, {
|
|
136439
|
+
model: options.model,
|
|
136440
|
+
verifyFileAfterTurn: caseDef.verifyFile
|
|
136441
|
+
});
|
|
136442
|
+
} catch (error) {
|
|
136443
|
+
results.push({
|
|
136444
|
+
id: caseDef.id,
|
|
136445
|
+
name: caseDef.name,
|
|
136446
|
+
passed: false,
|
|
136447
|
+
failedRules: [],
|
|
136448
|
+
passedRules: [],
|
|
136449
|
+
extraFailures: [],
|
|
136450
|
+
output: "",
|
|
136451
|
+
error: error instanceof Error ? error.message : String(error),
|
|
136452
|
+
timedOut: false,
|
|
136453
|
+
toolCheckFailed: false
|
|
136454
|
+
});
|
|
136455
|
+
failedCount += 1;
|
|
136456
|
+
options.onProgress?.({
|
|
136457
|
+
completed: results.length,
|
|
136458
|
+
total: EVAL_CASES.length,
|
|
136459
|
+
currentId: caseDef.id,
|
|
136460
|
+
currentName: caseDef.name,
|
|
136461
|
+
passed: passedCount,
|
|
136462
|
+
failed: failedCount
|
|
136463
|
+
});
|
|
136464
|
+
continue;
|
|
136465
|
+
}
|
|
136466
|
+
const judged = judgeOutput(result.output, caseDef.rules);
|
|
136467
|
+
const extraFailures = caseDef.extra?.(result) ?? [];
|
|
136468
|
+
const passed = judged.failed.length === 0 && extraFailures.length === 0;
|
|
136469
|
+
if (passed) passedCount += 1;
|
|
136470
|
+
else failedCount += 1;
|
|
136471
|
+
const toolCheckFailed = caseDef.verifyFile !== void 0 && !result.verifiedFile;
|
|
136472
|
+
results.push({
|
|
136473
|
+
id: caseDef.id,
|
|
136474
|
+
name: caseDef.name,
|
|
136475
|
+
passed,
|
|
136476
|
+
failedRules: judged.failed,
|
|
136477
|
+
passedRules: judged.passed,
|
|
136478
|
+
extraFailures,
|
|
136479
|
+
output: result.output,
|
|
136480
|
+
timedOut: result.timedOut,
|
|
136481
|
+
toolCheckFailed
|
|
136482
|
+
});
|
|
136483
|
+
options.onProgress?.({
|
|
136484
|
+
completed: results.length,
|
|
136485
|
+
total: EVAL_CASES.length,
|
|
136486
|
+
currentId: caseDef.id,
|
|
136487
|
+
currentName: caseDef.name,
|
|
136488
|
+
passed: passedCount,
|
|
136489
|
+
failed: failedCount
|
|
136490
|
+
});
|
|
136491
|
+
}
|
|
136492
|
+
return {
|
|
136493
|
+
results,
|
|
136494
|
+
passed: passedCount,
|
|
136495
|
+
failed: failedCount
|
|
136496
|
+
};
|
|
136497
|
+
}
|
|
136498
|
+
//#endregion
|
|
136499
|
+
//#region src/tui/commands/eval.ts
|
|
136500
|
+
const EVAL_PANEL_DISMISS_MS = 6e4;
|
|
136501
|
+
let activeEvalPanel;
|
|
136502
|
+
let activeEvalTimer;
|
|
136503
|
+
/** Guards against overlapping runs (each run calls a real model and costs
|
|
136504
|
+
* tokens; a second /eval while one is running is almost certainly a mistake). */
|
|
136505
|
+
let evalRunning = false;
|
|
136506
|
+
function dismissEvalPanel(state) {
|
|
136507
|
+
if (activeEvalTimer !== void 0) {
|
|
136508
|
+
clearTimeout(activeEvalTimer);
|
|
136509
|
+
activeEvalTimer = void 0;
|
|
136510
|
+
}
|
|
136511
|
+
if (activeEvalPanel !== void 0) {
|
|
136512
|
+
state.transcriptContainer.removeChild(activeEvalPanel);
|
|
136513
|
+
activeEvalPanel = void 0;
|
|
136514
|
+
state.ui.requestRender();
|
|
136515
|
+
}
|
|
136516
|
+
}
|
|
136517
|
+
function clearEvalPanelState(state) {
|
|
136518
|
+
dismissEvalPanel(state);
|
|
136519
|
+
}
|
|
136520
|
+
/** Model selector for eval runs; falls back to the active session model. */
|
|
136521
|
+
function resolveEvalModel(host) {
|
|
136522
|
+
return host.state.appState.model;
|
|
136523
|
+
}
|
|
136524
|
+
/**
|
|
136525
|
+
* Runs the end-to-end evals in the background (they call a real model and can
|
|
136526
|
+
* take minutes) and renders the per-case report when done. Returns immediately
|
|
136527
|
+
* so the TUI keeps responding.
|
|
136528
|
+
*/
|
|
136529
|
+
function runEvalCommand(host) {
|
|
136530
|
+
if (evalRunning) {
|
|
136531
|
+
host.showStatus(t("dispatch.eval_running"));
|
|
136532
|
+
return;
|
|
136533
|
+
}
|
|
136534
|
+
const model = resolveEvalModel(host);
|
|
136535
|
+
if (model === void 0 || model.length === 0) {
|
|
136536
|
+
host.showError(t("dispatch.eval_no_model"));
|
|
136537
|
+
return;
|
|
136538
|
+
}
|
|
136539
|
+
const sessionId = host.state.appState.sessionId;
|
|
136540
|
+
evalRunning = true;
|
|
136541
|
+
host.showStatus(t("dispatch.eval_started", { model }));
|
|
136542
|
+
(async () => {
|
|
136543
|
+
let summary;
|
|
136544
|
+
try {
|
|
136545
|
+
summary = await runAllEvals({
|
|
136546
|
+
model,
|
|
136547
|
+
onProgress: ({ completed, total, passed, failed, currentName }) => {
|
|
136548
|
+
host.showStatus(t("dispatch.eval_progress", {
|
|
136549
|
+
completed: String(completed),
|
|
136550
|
+
total: String(total),
|
|
136551
|
+
passed: String(passed),
|
|
136552
|
+
failed: String(failed),
|
|
136553
|
+
name: currentName
|
|
136554
|
+
}));
|
|
136555
|
+
}
|
|
136556
|
+
});
|
|
136557
|
+
} catch (error) {
|
|
136558
|
+
evalRunning = false;
|
|
136559
|
+
host.showError(t("dispatch.eval_failed", { error: error instanceof Error ? error.message : String(error) }));
|
|
136560
|
+
return;
|
|
136561
|
+
}
|
|
136562
|
+
evalRunning = false;
|
|
136563
|
+
if (sessionId !== void 0 && sessionId !== host.state.appState.sessionId) return;
|
|
136564
|
+
try {
|
|
136565
|
+
renderEvalSummary(host, summary);
|
|
136566
|
+
} catch (error) {
|
|
136567
|
+
host.showError(t("dispatch.eval_failed", { error: error instanceof Error ? error.message : String(error) }));
|
|
136568
|
+
}
|
|
136569
|
+
})();
|
|
136570
|
+
}
|
|
136571
|
+
function renderEvalSummary(host, summary) {
|
|
136572
|
+
const colors = host.state.theme.colors;
|
|
136573
|
+
const lines = [chalk.hex(colors.primary)(`${summary.passed} ${t("dispatch.eval_passed")} · ${summary.failed} ${t("dispatch.eval_failed_count")}`), ""];
|
|
136574
|
+
for (const result of summary.results) {
|
|
136575
|
+
const mark = result.passed ? "✓" : "×";
|
|
136576
|
+
const color = result.passed ? colors.success : colors.error;
|
|
136577
|
+
lines.push(`${chalk.hex(color)(mark)} ${result.name}`);
|
|
136578
|
+
if (result.passed) continue;
|
|
136579
|
+
lines.push(` ${chalk.hex(colors.error)(suggestionFor(result))}`);
|
|
136580
|
+
const details = [];
|
|
136581
|
+
for (const rule of result.failedRules) details.push(rule);
|
|
136582
|
+
for (const failure of result.extraFailures) details.push(failure);
|
|
136583
|
+
if (result.error !== void 0) details.push(result.error);
|
|
136584
|
+
if (result.output.length > 0) details.push(`output: ${result.output.slice(0, 160)}`);
|
|
136585
|
+
for (const detail of details) lines.push(` ${chalk.hex(colors.textDim)(`· ${detail}`)}`);
|
|
136586
|
+
}
|
|
136587
|
+
lines.push("");
|
|
136588
|
+
dismissEvalPanel(host.state);
|
|
136589
|
+
const panel = new UsagePanelComponent(lines, colors.primary, " Eval ");
|
|
136590
|
+
host.state.transcriptContainer.addChild(panel);
|
|
136591
|
+
activeEvalPanel = panel;
|
|
136592
|
+
activeEvalTimer = setTimeout(() => {
|
|
136593
|
+
dismissEvalPanel(host.state);
|
|
136594
|
+
}, EVAL_PANEL_DISMISS_MS);
|
|
136595
|
+
host.state.ui.requestRender();
|
|
136596
|
+
}
|
|
136597
|
+
/** Picks a user-facing self-check hint based on how the case failed. */
|
|
136598
|
+
function suggestionFor(result) {
|
|
136599
|
+
if (result.error !== void 0) return t("dispatch.eval_suggest_error");
|
|
136600
|
+
if (result.timedOut) return t("dispatch.eval_suggest_timeout");
|
|
136601
|
+
if (result.toolCheckFailed) return t("dispatch.eval_suggest_tool");
|
|
136602
|
+
return t("dispatch.eval_suggest_answer");
|
|
136603
|
+
}
|
|
136604
|
+
//#endregion
|
|
136038
136605
|
//#region src/tui/commands/dispatch.ts
|
|
136039
136606
|
function dispatchInput(host, text) {
|
|
136040
136607
|
if (parseSlashInput(text) !== null) {
|
|
@@ -136208,6 +136775,9 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
136208
136775
|
case "logout":
|
|
136209
136776
|
await handleLogoutCommand(host);
|
|
136210
136777
|
return;
|
|
136778
|
+
case "eval":
|
|
136779
|
+
runEvalCommand(host);
|
|
136780
|
+
return;
|
|
136211
136781
|
case "memory":
|
|
136212
136782
|
await handleMemoryCommand(host, args);
|
|
136213
136783
|
return;
|
|
@@ -136235,6 +136805,17 @@ var AuthFlowController = class {
|
|
|
136235
136805
|
availableModels: config.models ?? {},
|
|
136236
136806
|
availableProviders: config.providers ?? {}
|
|
136237
136807
|
});
|
|
136808
|
+
this.prefetchCatalogCache();
|
|
136809
|
+
}
|
|
136810
|
+
/**
|
|
136811
|
+
* Best-effort background catalog refresh. Fetches the remote catalog and,
|
|
136812
|
+
* on success, updates the on-disk cache. On failure it stays silent: the
|
|
136813
|
+
* existing cache (or built-in) keeps working. Never throws.
|
|
136814
|
+
*/
|
|
136815
|
+
async prefetchCatalogCache() {
|
|
136816
|
+
try {
|
|
136817
|
+
saveCatalogCache(await fetchCatalog(DEFAULT_CATALOG_URL), resolveScreamHome());
|
|
136818
|
+
} catch {}
|
|
136238
136819
|
}
|
|
136239
136820
|
async activateModelAfterLogin(model, thinkingLevel) {
|
|
136240
136821
|
const { host } = this;
|
|
@@ -140590,9 +141171,8 @@ var TasksBrowserController = class {
|
|
|
140590
141171
|
colors: state.theme.colors,
|
|
140591
141172
|
...this.buildCallbacks()
|
|
140592
141173
|
}, state.terminal);
|
|
140593
|
-
const
|
|
140594
|
-
state.ui.
|
|
140595
|
-
state.ui.addChild(component);
|
|
141174
|
+
const savedLayoutRoot = state.layoutRoot;
|
|
141175
|
+
state.ui.setLayoutRoot(component);
|
|
140596
141176
|
state.ui.setFocus(component);
|
|
140597
141177
|
state.ui.requestRender(true);
|
|
140598
141178
|
const pollTimer = setInterval(() => {
|
|
@@ -140600,7 +141180,7 @@ var TasksBrowserController = class {
|
|
|
140600
141180
|
}, 1e3);
|
|
140601
141181
|
this.host.setTasksBrowser({
|
|
140602
141182
|
component,
|
|
140603
|
-
|
|
141183
|
+
savedLayoutRoot,
|
|
140604
141184
|
filter,
|
|
140605
141185
|
selectedTaskId,
|
|
140606
141186
|
tailOutput: void 0,
|
|
@@ -140620,8 +141200,7 @@ var TasksBrowserController = class {
|
|
|
140620
141200
|
if (browser.viewer !== void 0) this.closeOutputViewer();
|
|
140621
141201
|
if (browser.pollTimer !== void 0) clearInterval(browser.pollTimer);
|
|
140622
141202
|
if (browser.flashTimer !== void 0) clearTimeout(browser.flashTimer);
|
|
140623
|
-
state.ui.
|
|
140624
|
-
for (const child of browser.savedChildren) state.ui.addChild(child);
|
|
141203
|
+
state.ui.setLayoutRoot(browser.savedLayoutRoot);
|
|
140625
141204
|
this.host.setTasksBrowser(void 0);
|
|
140626
141205
|
state.ui.setFocus(state.editor);
|
|
140627
141206
|
state.ui.requestRender(true);
|
|
@@ -140791,9 +141370,8 @@ var TasksBrowserController = class {
|
|
|
140791
141370
|
this.closeOutputViewer();
|
|
140792
141371
|
}
|
|
140793
141372
|
}, state.terminal);
|
|
140794
|
-
const
|
|
140795
|
-
state.ui.
|
|
140796
|
-
state.ui.addChild(viewer);
|
|
141373
|
+
const savedBrowserLayout = state.layoutRoot;
|
|
141374
|
+
state.ui.setLayoutRoot(viewer);
|
|
140797
141375
|
state.ui.setFocus(viewer);
|
|
140798
141376
|
state.ui.requestRender(true);
|
|
140799
141377
|
const pollTimer = setInterval(() => {
|
|
@@ -140801,7 +141379,7 @@ var TasksBrowserController = class {
|
|
|
140801
141379
|
}, 1e3);
|
|
140802
141380
|
browser.viewer = {
|
|
140803
141381
|
component: viewer,
|
|
140804
|
-
|
|
141382
|
+
savedLayoutRoot: savedBrowserLayout,
|
|
140805
141383
|
taskId,
|
|
140806
141384
|
output,
|
|
140807
141385
|
refreshId: 0,
|
|
@@ -140857,8 +141435,7 @@ var TasksBrowserController = class {
|
|
|
140857
141435
|
const viewer = browser.viewer;
|
|
140858
141436
|
clearInterval(viewer.pollTimer);
|
|
140859
141437
|
browser.viewer = void 0;
|
|
140860
|
-
this.host.state.ui.
|
|
140861
|
-
for (const child of viewer.savedChildren) this.host.state.ui.addChild(child);
|
|
141438
|
+
this.host.state.ui.setLayoutRoot(viewer.savedLayoutRoot);
|
|
140862
141439
|
this.host.state.ui.setFocus(browser.component);
|
|
140863
141440
|
this.host.state.ui.requestRender(true);
|
|
140864
141441
|
}
|
|
@@ -141294,13 +141871,6 @@ var TranscriptController = class TranscriptController {
|
|
|
141294
141871
|
toggleToolOutputExpansion() {
|
|
141295
141872
|
const { state } = this.host;
|
|
141296
141873
|
state.toolOutputExpanded = !state.toolOutputExpanded;
|
|
141297
|
-
const walk = (children) => {
|
|
141298
|
-
for (const child of children) {
|
|
141299
|
-
if (isExpandable(child)) child.setExpanded(state.toolOutputExpanded);
|
|
141300
|
-
if ("children" in child && Array.isArray(child.children)) walk(child.children);
|
|
141301
|
-
}
|
|
141302
|
-
};
|
|
141303
|
-
walk(state.transcriptContainer.children);
|
|
141304
141874
|
state.ui.requestRender();
|
|
141305
141875
|
}
|
|
141306
141876
|
togglePlanExpansion() {
|
|
@@ -141349,6 +141919,44 @@ var GutterContainer = class extends Container {
|
|
|
141349
141919
|
}
|
|
141350
141920
|
};
|
|
141351
141921
|
//#endregion
|
|
141922
|
+
//#region src/tui/components/panes/status-bar-pane.ts
|
|
141923
|
+
/**
|
|
141924
|
+
* A fixed one-line status bar shown directly above the input editor: it
|
|
141925
|
+
* displays the current work phase (idle / waiting / thinking / composing /
|
|
141926
|
+
* tool) with its spinner, pinned to the bottom of the terminal via the dock
|
|
141927
|
+
* VStack. Designed to hold more status widgets later — add children to the
|
|
141928
|
+
* container when needed.
|
|
141929
|
+
*/
|
|
141930
|
+
var StatusBarPaneComponent = class extends Container {
|
|
141931
|
+
constructor(options) {
|
|
141932
|
+
super();
|
|
141933
|
+
this.update(options);
|
|
141934
|
+
}
|
|
141935
|
+
/** Re-renders the bar for a new mode/label. The spinner/pulse are the
|
|
141936
|
+
* caller's shared instances (kept alive across mode changes), so this only
|
|
141937
|
+
* swaps which children are visible. */
|
|
141938
|
+
update(options) {
|
|
141939
|
+
this.clear();
|
|
141940
|
+
if (options.mode === "idle") return;
|
|
141941
|
+
if (options.mode === "waiting" || options.mode === "tool" || options.mode === "composing" || options.mode === "thinking") {
|
|
141942
|
+
if (options.pulseWave !== void 0) {
|
|
141943
|
+
if (options.label.length > 0) {
|
|
141944
|
+
const row = new HStack();
|
|
141945
|
+
row.addChild(options.pulseWave);
|
|
141946
|
+
row.addChild(new Text(` ${options.label}`, 1, 0));
|
|
141947
|
+
this.addChild(row);
|
|
141948
|
+
} else this.addChild(options.pulseWave);
|
|
141949
|
+
return;
|
|
141950
|
+
}
|
|
141951
|
+
if (options.spinner !== void 0) {
|
|
141952
|
+
this.addChild(options.spinner);
|
|
141953
|
+
return;
|
|
141954
|
+
}
|
|
141955
|
+
if (options.label.length > 0) this.addChild(new Text(options.label, 1, 0));
|
|
141956
|
+
}
|
|
141957
|
+
}
|
|
141958
|
+
};
|
|
141959
|
+
//#endregion
|
|
141352
141960
|
//#region src/tui/theme/bundle.ts
|
|
141353
141961
|
function createScreamTUIThemeBundle(theme, resolvedTheme) {
|
|
141354
141962
|
const actualTheme = resolvedTheme ?? resolveThemeSync(theme);
|
|
@@ -141640,36 +142248,11 @@ var PulseWaveLoader = class extends Text {
|
|
|
141640
142248
|
}
|
|
141641
142249
|
};
|
|
141642
142250
|
//#endregion
|
|
141643
|
-
//#region src/tui/components/panes/activity-pane.ts
|
|
141644
|
-
var ActivityPaneComponent = class extends Container {
|
|
141645
|
-
constructor(options) {
|
|
141646
|
-
super();
|
|
141647
|
-
if (options.mode === "waiting" || options.mode === "tool") {
|
|
141648
|
-
if (options.spinner !== void 0) {
|
|
141649
|
-
this.addChild(new Spacer(1));
|
|
141650
|
-
this.addChild(options.spinner);
|
|
141651
|
-
}
|
|
141652
|
-
if (options.pulseWave !== void 0) {
|
|
141653
|
-
this.addChild(new Spacer(1));
|
|
141654
|
-
this.addChild(options.pulseWave);
|
|
141655
|
-
}
|
|
141656
|
-
return;
|
|
141657
|
-
}
|
|
141658
|
-
if (options.mode === "composing" && options.spinner !== void 0) {
|
|
141659
|
-
this.addChild(new Spacer(1));
|
|
141660
|
-
this.addChild(options.spinner);
|
|
141661
|
-
if (options.pulseWave !== void 0) {
|
|
141662
|
-
this.addChild(new Spacer(1));
|
|
141663
|
-
this.addChild(options.pulseWave);
|
|
141664
|
-
}
|
|
141665
|
-
}
|
|
141666
|
-
}
|
|
141667
|
-
};
|
|
141668
|
-
//#endregion
|
|
141669
142251
|
//#region src/tui/controllers/lifecycle-controller.ts
|
|
141670
142252
|
var LifecycleController = class LifecycleController {
|
|
141671
142253
|
host;
|
|
141672
142254
|
signalCleanupHandlers = [];
|
|
142255
|
+
footerWrap;
|
|
141673
142256
|
ccConnectPollTimer;
|
|
141674
142257
|
memoryIdleTimer;
|
|
141675
142258
|
memoryCountdownTimer;
|
|
@@ -141677,6 +142260,9 @@ var LifecycleController = class LifecycleController {
|
|
|
141677
142260
|
terminalFocusTrackingDispose;
|
|
141678
142261
|
terminalThemeTrackingDispose;
|
|
141679
142262
|
lastActivityMode;
|
|
142263
|
+
/** The active status-bar loader (PulseWaveLoader or MoonLoader). Owned by
|
|
142264
|
+
* this controller; stopped before replacement to avoid leaking timers. */
|
|
142265
|
+
statusBarLoader;
|
|
141680
142266
|
static MEMORY_IDLE_MS = 900 * 1e3;
|
|
141681
142267
|
static MEMORY_COUNTDOWN_MS = 15 * 1e3;
|
|
141682
142268
|
static MEMORY_EXTRACT_COOLDOWN_MS = 600 * 1e3;
|
|
@@ -141816,19 +142402,72 @@ var LifecycleController = class LifecycleController {
|
|
|
141816
142402
|
buildLayout() {
|
|
141817
142403
|
const { ui } = this.host.state;
|
|
141818
142404
|
ui.clear();
|
|
141819
|
-
|
|
141820
|
-
|
|
141821
|
-
|
|
141822
|
-
|
|
141823
|
-
|
|
141824
|
-
|
|
141825
|
-
|
|
141826
|
-
|
|
142405
|
+
const scrollContent = new Container();
|
|
142406
|
+
scrollContent.addChild(this.host.state.transcriptContainer);
|
|
142407
|
+
scrollContent.addChild(this.host.state.activityContainer);
|
|
142408
|
+
scrollContent.addChild(this.host.state.queueContainer);
|
|
142409
|
+
const transcriptScrollView = new ScrollView(scrollContent, {
|
|
142410
|
+
follow: "end",
|
|
142411
|
+
primary: true,
|
|
142412
|
+
scrollbar: "auto"
|
|
142413
|
+
});
|
|
142414
|
+
const dock = new VStack([
|
|
142415
|
+
{
|
|
142416
|
+
component: this.host.state.todoPanelContainer,
|
|
142417
|
+
shrink: 1,
|
|
142418
|
+
minSize: 0,
|
|
142419
|
+
visible: (viewport) => viewport.width >= 90
|
|
142420
|
+
},
|
|
142421
|
+
{
|
|
142422
|
+
component: this.host.state.errorBannerContainer,
|
|
142423
|
+
shrink: 1,
|
|
142424
|
+
minSize: 0
|
|
142425
|
+
},
|
|
142426
|
+
{
|
|
142427
|
+
component: this.host.state.planModeBannerContainer,
|
|
142428
|
+
shrink: 1,
|
|
142429
|
+
minSize: 0
|
|
142430
|
+
},
|
|
142431
|
+
{
|
|
142432
|
+
component: this.host.state.statusBarContainer,
|
|
142433
|
+
shrink: 1,
|
|
142434
|
+
minSize: 1,
|
|
142435
|
+
visible: (viewport) => viewport.width >= 60
|
|
142436
|
+
},
|
|
142437
|
+
{
|
|
142438
|
+
component: this.host.state.editorContainer,
|
|
142439
|
+
shrink: 1,
|
|
142440
|
+
minSize: 3
|
|
142441
|
+
},
|
|
142442
|
+
{
|
|
142443
|
+
component: this.ensureFooterWrap(),
|
|
142444
|
+
shrink: 1,
|
|
142445
|
+
minSize: 1
|
|
142446
|
+
}
|
|
142447
|
+
]);
|
|
142448
|
+
const layoutRoot = new VStack([{
|
|
142449
|
+
component: transcriptScrollView,
|
|
142450
|
+
basis: 0,
|
|
142451
|
+
grow: 1,
|
|
142452
|
+
shrink: 1,
|
|
142453
|
+
minSize: 1
|
|
142454
|
+
}, {
|
|
142455
|
+
component: dock,
|
|
142456
|
+
basis: "auto",
|
|
142457
|
+
grow: 0,
|
|
142458
|
+
shrink: 1,
|
|
142459
|
+
minSize: 1
|
|
142460
|
+
}]);
|
|
142461
|
+
ui.setLayoutRoot(layoutRoot);
|
|
142462
|
+
this.host.state.layoutRoot = layoutRoot;
|
|
141827
142463
|
}
|
|
141828
142464
|
mountFooter() {
|
|
141829
|
-
|
|
141830
|
-
footerWrap.addChild(this.host.state.footer);
|
|
141831
|
-
|
|
142465
|
+
this.ensureFooterWrap();
|
|
142466
|
+
this.footerWrap.addChild(this.host.state.footer);
|
|
142467
|
+
}
|
|
142468
|
+
ensureFooterWrap() {
|
|
142469
|
+
if (this.footerWrap === void 0) this.footerWrap = new GutterContainer(1, 1);
|
|
142470
|
+
return this.footerWrap;
|
|
141832
142471
|
}
|
|
141833
142472
|
refreshTerminalThemeTracking() {
|
|
141834
142473
|
this.stopTerminalThemeTracking();
|
|
@@ -141863,48 +142502,57 @@ var LifecycleController = class LifecycleController {
|
|
|
141863
142502
|
this.syncTerminalProgress(this.shouldShowTerminalProgress(effectiveMode));
|
|
141864
142503
|
if (effectiveMode === this.lastActivityMode) return;
|
|
141865
142504
|
this.lastActivityMode = effectiveMode;
|
|
142505
|
+
this.updateStatusBar(effectiveMode);
|
|
141866
142506
|
const { state } = this.host;
|
|
141867
142507
|
state.activityContainer.clear();
|
|
141868
142508
|
switch (effectiveMode) {
|
|
141869
142509
|
case "hidden":
|
|
142510
|
+
case "idle":
|
|
141870
142511
|
this.stopActivitySpinner();
|
|
141871
142512
|
this.stopPulseWave();
|
|
141872
|
-
state.ui.requestRender();
|
|
141873
|
-
return;
|
|
141874
|
-
case "waiting": {
|
|
141875
|
-
this.stopActivitySpinner();
|
|
141876
|
-
const pulseWave = this.ensurePulseWave();
|
|
141877
|
-
state.activityContainer.addChild(new ActivityPaneComponent({
|
|
141878
|
-
mode: "waiting",
|
|
141879
|
-
pulseWave
|
|
141880
|
-
}));
|
|
141881
142513
|
break;
|
|
141882
|
-
|
|
142514
|
+
case "waiting":
|
|
141883
142515
|
case "thinking":
|
|
142516
|
+
case "composing":
|
|
142517
|
+
case "tool":
|
|
141884
142518
|
this.stopActivitySpinner();
|
|
141885
142519
|
this.stopPulseWave();
|
|
141886
142520
|
break;
|
|
141887
|
-
|
|
141888
|
-
|
|
141889
|
-
|
|
141890
|
-
|
|
141891
|
-
|
|
141892
|
-
|
|
141893
|
-
|
|
141894
|
-
|
|
141895
|
-
|
|
141896
|
-
|
|
141897
|
-
|
|
141898
|
-
|
|
141899
|
-
|
|
141900
|
-
|
|
141901
|
-
|
|
142521
|
+
}
|
|
142522
|
+
state.ui.requestRender();
|
|
142523
|
+
}
|
|
142524
|
+
/**
|
|
142525
|
+
* Updates the fixed status bar above the editor to reflect the current
|
|
142526
|
+
* work phase. Called only when the phase actually changes. Uses its OWN
|
|
142527
|
+
* spinner/pulse instances (a component can only have one parent, so the
|
|
142528
|
+
* activity pane's instances must not be shared). Recreating a loader on
|
|
142529
|
+
* every call would leak setInterval timers (each PulseWaveLoader.start()
|
|
142530
|
+
* registers one and the old instance is never stopped), so the previous
|
|
142531
|
+
* instance is explicitly stopped before a new one is mounted. Idle leaves
|
|
142532
|
+
* the bar empty.
|
|
142533
|
+
*/
|
|
142534
|
+
updateStatusBar(mode) {
|
|
142535
|
+
const { state } = this.host;
|
|
142536
|
+
state.statusBarContainer.clear();
|
|
142537
|
+
this.statusBarLoader?.stop();
|
|
142538
|
+
this.statusBarLoader = void 0;
|
|
142539
|
+
switch (mode) {
|
|
142540
|
+
case "waiting":
|
|
142541
|
+
case "composing":
|
|
142542
|
+
case "tool":
|
|
142543
|
+
{
|
|
142544
|
+
const loader = new PulseWaveLoader(state.ui, state.theme.colors.primary);
|
|
142545
|
+
this.statusBarLoader = loader;
|
|
142546
|
+
state.statusBarContainer.addChild(new StatusBarPaneComponent({
|
|
142547
|
+
mode,
|
|
142548
|
+
label: "",
|
|
142549
|
+
pulseWave: loader
|
|
142550
|
+
}));
|
|
142551
|
+
}
|
|
141902
142552
|
break;
|
|
141903
|
-
|
|
142553
|
+
case "thinking": break;
|
|
141904
142554
|
case "idle":
|
|
141905
|
-
|
|
141906
|
-
this.stopPulseWave();
|
|
141907
|
-
break;
|
|
142555
|
+
case "hidden": break;
|
|
141908
142556
|
}
|
|
141909
142557
|
state.ui.requestRender();
|
|
141910
142558
|
}
|
|
@@ -141927,28 +142575,12 @@ var LifecycleController = class LifecycleController {
|
|
|
141927
142575
|
this.host.state.terminal.setProgress(active);
|
|
141928
142576
|
this.host.state.terminalState.progressActive = active;
|
|
141929
142577
|
}
|
|
141930
|
-
ensureActivitySpinner(label = "", colorFn) {
|
|
141931
|
-
if (this.host.state.activitySpinner === null) {
|
|
141932
|
-
const instance = new MoonLoader(this.host.state.ui, colorFn, label);
|
|
141933
|
-
this.host.state.activitySpinner = { instance };
|
|
141934
|
-
return instance;
|
|
141935
|
-
}
|
|
141936
|
-
this.host.state.activitySpinner.instance.setLabel(label);
|
|
141937
|
-
if (colorFn !== void 0) this.host.state.activitySpinner.instance.setColorFn(colorFn);
|
|
141938
|
-
return this.host.state.activitySpinner.instance;
|
|
141939
|
-
}
|
|
141940
142578
|
stopActivitySpinner() {
|
|
141941
142579
|
if (this.host.state.activitySpinner !== null) {
|
|
141942
142580
|
this.host.state.activitySpinner.instance.stop();
|
|
141943
142581
|
this.host.state.activitySpinner = null;
|
|
141944
142582
|
}
|
|
141945
142583
|
}
|
|
141946
|
-
ensurePulseWave() {
|
|
141947
|
-
if (this.host.state.pulseWave !== null) return this.host.state.pulseWave;
|
|
141948
|
-
const instance = new PulseWaveLoader(this.host.state.ui, this.host.state.theme.colors.primary);
|
|
141949
|
-
this.host.state.pulseWave = instance;
|
|
141950
|
-
return instance;
|
|
141951
|
-
}
|
|
141952
142584
|
stopPulseWave() {
|
|
141953
142585
|
if (this.host.state.pulseWave !== null) {
|
|
141954
142586
|
this.host.state.pulseWave.stop();
|
|
@@ -142375,18 +143007,10 @@ var InputController = class InputController {
|
|
|
142375
143007
|
this.host = host;
|
|
142376
143008
|
}
|
|
142377
143009
|
setupAutocomplete() {
|
|
142378
|
-
const
|
|
142379
|
-
const slashCommands = visible.map((cmd) => cmd);
|
|
143010
|
+
const slashCommands = this.host.getSlashCommands().filter((cmd) => !(cmd.name.startsWith("skill:") && cmd.source === "builtin")).map((cmd) => cmd);
|
|
142380
143011
|
const { state } = this.host;
|
|
142381
143012
|
const provider = new FileMentionProvider(slashCommands, state.appState.workDir, state.fdPath, state.gitLsFilesCache);
|
|
142382
143013
|
state.editor.setAutocompleteProvider(provider);
|
|
142383
|
-
const argumentHints = /* @__PURE__ */ new Map();
|
|
142384
|
-
for (const cmd of visible) {
|
|
142385
|
-
if (cmd.argumentHint === void 0) continue;
|
|
142386
|
-
argumentHints.set(cmd.name, cmd.argumentHint);
|
|
142387
|
-
for (const alias of cmd.aliases) argumentHints.set(alias, cmd.argumentHint);
|
|
142388
|
-
}
|
|
142389
|
-
state.editor.setArgumentHints(argumentHints);
|
|
142390
143014
|
state.editor.onFirstInput = () => {
|
|
142391
143015
|
this.host.stopWelcomeBreathing();
|
|
142392
143016
|
this.#permanentlyStopBreathing();
|
|
@@ -143681,8 +144305,7 @@ function createTUIState(options) {
|
|
|
143681
144305
|
const initialAppState = options.initialAppState;
|
|
143682
144306
|
const theme = createScreamTUIThemeBundle(initialAppState.theme, options.resolvedTheme);
|
|
143683
144307
|
const terminal = new ProcessTerminal();
|
|
143684
|
-
const ui = new
|
|
143685
|
-
ui.setClearOnShrink(true);
|
|
144308
|
+
const ui = new TuiAltScreen(terminal);
|
|
143686
144309
|
const uiAny = ui;
|
|
143687
144310
|
const originalDoRender = uiAny["doRender"].bind(ui);
|
|
143688
144311
|
let recovering = false;
|
|
@@ -143708,6 +144331,7 @@ function createTUIState(options) {
|
|
|
143708
144331
|
};
|
|
143709
144332
|
const transcriptContainer = new GutterContainer(1, 1);
|
|
143710
144333
|
const activityContainer = new GutterContainer(1, 1);
|
|
144334
|
+
const statusBarContainer = new GutterContainer(1, 1);
|
|
143711
144335
|
const todoPanelContainer = new GutterContainer(1, 1);
|
|
143712
144336
|
const todoPanel = new TodoPanelComponent(theme.colors);
|
|
143713
144337
|
const queueContainer = new GutterContainer(1, 1);
|
|
@@ -143725,8 +144349,10 @@ function createTUIState(options) {
|
|
|
143725
144349
|
return {
|
|
143726
144350
|
ui,
|
|
143727
144351
|
terminal,
|
|
144352
|
+
layoutRoot: void 0,
|
|
143728
144353
|
transcriptContainer,
|
|
143729
144354
|
activityContainer,
|
|
144355
|
+
statusBarContainer,
|
|
143730
144356
|
todoPanelContainer,
|
|
143731
144357
|
todoPanel,
|
|
143732
144358
|
queueContainer,
|
|
@@ -144484,7 +145110,7 @@ var SessionManager$1 = class {
|
|
|
144484
145110
|
}
|
|
144485
145111
|
resetSessionRuntime() {
|
|
144486
145112
|
this.host.state.queuedMessages = [];
|
|
144487
|
-
this.host.harness.interactiveAgentId = MAIN_AGENT_ID;
|
|
145113
|
+
this.host.harness.interactiveAgentId = MAIN_AGENT_ID$1;
|
|
144488
145114
|
this.host.streamingUI.discardPending();
|
|
144489
145115
|
this.host.streamingUI.resetToolCallState();
|
|
144490
145116
|
this.host.streamingUI.resetToolUi();
|
|
@@ -144634,7 +145260,7 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
144634
145260
|
};
|
|
144635
145261
|
if (this.ui !== void 0) this.intervalId = setInterval(() => {
|
|
144636
145262
|
this.animFrame = (this.animFrame + 1) % PIXEL_PULSE_FRAMES.length;
|
|
144637
|
-
this.ui?.
|
|
145263
|
+
this.ui?.requestRender();
|
|
144638
145264
|
}, 100);
|
|
144639
145265
|
}
|
|
144640
145266
|
/** Stop the selection pulse. Idempotent; called on submit and on hide. */
|
|
@@ -145863,7 +146489,7 @@ var QuestionDialogComponent = class extends Container {
|
|
|
145863
146489
|
this.answers = Array.from({ length: total }, () => void 0);
|
|
145864
146490
|
if (this.ui !== void 0) this.intervalId = setInterval(() => {
|
|
145865
146491
|
this.animFrame = (this.animFrame + 1) % PIXEL_PULSE_FRAMES.length;
|
|
145866
|
-
this.ui?.
|
|
146492
|
+
this.ui?.requestRender();
|
|
145867
146493
|
}, 100);
|
|
145868
146494
|
}
|
|
145869
146495
|
/** Stop the wizard pulse. Idempotent; called on respond and on hide. */
|
|
@@ -146575,7 +147201,7 @@ var DialogManager = class {
|
|
|
146575
147201
|
}
|
|
146576
147202
|
openApprovalPreview(panel, block) {
|
|
146577
147203
|
if (this.approvalPreview !== void 0) return;
|
|
146578
|
-
const
|
|
147204
|
+
const savedLayoutRoot = this.host.state.layoutRoot;
|
|
146579
147205
|
const viewer = new ApprovalPreviewViewer({
|
|
146580
147206
|
block,
|
|
146581
147207
|
colors: this.host.state.theme.colors,
|
|
@@ -146583,13 +147209,12 @@ var DialogManager = class {
|
|
|
146583
147209
|
this.closeApprovalPreview();
|
|
146584
147210
|
}
|
|
146585
147211
|
}, this.host.state.terminal);
|
|
146586
|
-
this.host.state.ui.
|
|
146587
|
-
this.host.state.ui.addChild(viewer);
|
|
147212
|
+
this.host.state.ui.setLayoutRoot(viewer);
|
|
146588
147213
|
this.host.state.ui.setFocus(viewer);
|
|
146589
147214
|
this.host.state.ui.requestRender(true);
|
|
146590
147215
|
this.approvalPreview = {
|
|
146591
147216
|
component: viewer,
|
|
146592
|
-
|
|
147217
|
+
savedLayoutRoot,
|
|
146593
147218
|
panel
|
|
146594
147219
|
};
|
|
146595
147220
|
}
|
|
@@ -146597,8 +147222,7 @@ var DialogManager = class {
|
|
|
146597
147222
|
const preview = this.approvalPreview;
|
|
146598
147223
|
if (preview === void 0) return;
|
|
146599
147224
|
this.approvalPreview = void 0;
|
|
146600
|
-
this.host.state.ui.
|
|
146601
|
-
for (const child of preview.savedChildren) this.host.state.ui.addChild(child);
|
|
147225
|
+
this.host.state.ui.setLayoutRoot(preview.savedLayoutRoot);
|
|
146602
147226
|
this.host.state.ui.setFocus(preview.panel);
|
|
146603
147227
|
this.host.state.ui.requestRender(true);
|
|
146604
147228
|
}
|
|
@@ -146689,7 +147313,6 @@ var ScreamTUI = class {
|
|
|
146689
147313
|
deferUserMessages = false;
|
|
146690
147314
|
aborted = false;
|
|
146691
147315
|
isShuttingDown = false;
|
|
146692
|
-
tightModeHandler = null;
|
|
146693
147316
|
reverseRpcDisposers = [];
|
|
146694
147317
|
startupNotice;
|
|
146695
147318
|
/** Interval handle for the periodic provider-balance refresh. */
|
|
@@ -146788,11 +147411,6 @@ var ScreamTUI = class {
|
|
|
146788
147411
|
this.inputController.setupAutocomplete();
|
|
146789
147412
|
}
|
|
146790
147413
|
async start() {
|
|
146791
|
-
this.tightModeHandler = () => {
|
|
146792
|
-
setTightMode((process.stdout.columns ?? 80) < 60);
|
|
146793
|
-
};
|
|
146794
|
-
this.tightModeHandler();
|
|
146795
|
-
process.stdout.on("resize", this.tightModeHandler);
|
|
146796
147414
|
this.lifecycleController.installSignalHandlers();
|
|
146797
147415
|
try {
|
|
146798
147416
|
const shouldReplayHistory = await this.initMainTui();
|
|
@@ -146883,11 +147501,6 @@ var ScreamTUI = class {
|
|
|
146883
147501
|
async stop(exitCode) {
|
|
146884
147502
|
if (this.isShuttingDown) return;
|
|
146885
147503
|
this.isShuttingDown = true;
|
|
146886
|
-
if (this.tightModeHandler !== null) {
|
|
146887
|
-
process.stdout.off("resize", this.tightModeHandler);
|
|
146888
|
-
this.tightModeHandler = null;
|
|
146889
|
-
}
|
|
146890
|
-
setTightMode(false);
|
|
146891
147504
|
this.lifecycleController.stopCcConnectPolling();
|
|
146892
147505
|
if (this.balancePollTimer !== void 0) {
|
|
146893
147506
|
clearInterval(this.balancePollTimer);
|
|
@@ -147154,6 +147767,7 @@ var ScreamTUI = class {
|
|
|
147154
147767
|
this.sessionEventHandler.stopAllMcpServerStatusSpinners();
|
|
147155
147768
|
clearGoalState();
|
|
147156
147769
|
clearInfoPanelState(this.state);
|
|
147770
|
+
clearEvalPanelState(this.state);
|
|
147157
147771
|
this.state.terminal.write("\x1B[3J");
|
|
147158
147772
|
this.transcriptController.clearAndRedraw();
|
|
147159
147773
|
this.state.ui.requestRender(true);
|