scream-code 0.15.9 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{app-DQdNroOu.mjs → app-N_PRvDIk.mjs} +656 -100
- package/dist/{dispatch-CxGKRLcZ.mjs → dispatch-5S52okPl.mjs} +1 -1
- package/dist/{dispatch-D8ot3OUd.mjs → dispatch-CsrteWRw.mjs} +7 -6
- package/dist/main.mjs +1 -1
- package/dist/{text-input-dialog-2cG5VK1G.mjs → text-input-dialog-B0PROY20.mjs} +1 -1
- package/dist/{text-input-dialog-Dx7LJTG6.mjs → text-input-dialog-BVQaKy1Z.mjs} +24 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ When Scream wants to modify files or run commands, an approval panel pops up —
|
|
|
49
49
|
### Features
|
|
50
50
|
|
|
51
51
|
- **Goal Loop** — autonomous, goal-driven execution with an independent judge agent. Rejections must name concrete gaps; evidence gaps self-repair for up to 3 rounds, subjective calls park for your decision (with a written report), and token/time budgets bound the loop.
|
|
52
|
-
- **TUI Sidebar** —
|
|
52
|
+
- **TUI Sidebar** — five live panels: workspace Git diff summary (green adds / red deletes), per-session token accounting (cache-hit / cache-miss / output), 8 sub-agent slots with real-time states (idle / working / outputting / messaging / reworking / asking), Goal status with judge state (awaiting / judging / adjudicated), and a Network Hub — the measured provider response time plus round trips to a few well-known endpoints, probed only while the sidebar is visible, every 30 seconds.
|
|
53
53
|
- **Wolfpack** — unlimited parallel sub-agents (coder / explore / plan / verify / reviewer / oracle / writer / worker).
|
|
54
54
|
- **Subagent Collaboration** — delegate to specialist sub-agents and stay in control: they **ask the lead agent for guidance mid-task instead of guessing when blocked**, request a handoff when a different specialist fits, take mid-task redirects or follow-up notes, return structured JSON when asked, and can be confined to a safe mode (read-only / edit-only / commands-allowed) so they never overstep your limits.
|
|
55
55
|
- **Bot Mode (`/bot`)** — unattended execution: reversible work (reads, in-workspace edits, read-only commands) runs freely, while irreversible actions (push / publish / delete / sensitive files / unclassified operations) are automatically denied and queued for your review — with the reason fed back to the model so it stops retrying.
|
|
@@ -4,8 +4,8 @@ import { dirname as __cjsShimDirname } from 'node:path';
|
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
6
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
7
|
-
import { $n as
|
|
8
|
-
import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-
|
|
7
|
+
import { $n as isOrphanedToolCallError, $t as appendStreamingArgsPreview, A as renderDiffLinesClustered, An as saveTuiConfig, At as DISABLE_TERMINAL_THEME_REPORTING, B as BackgroundAgentStatusComponent, Bn as DEFAULT_CATALOG_URL, Bt as TERMINAL_THEME_DARK, Cn as isEmbeddingModelCached, Ct as createMarkdownTheme, D as toggleEmptySessionHint, Dn as TuiConfigParseError, Dt as detectTerminalTheme, E as isTurnElapsedEnabled, En as PULSE_WAVE_FRAMES, Et as getColorPalette, F as estimateTokens, Fn as detectInstallSource, Ft as OSC11_RESPONSE_PREFIX, G as getBreathingFrame, Gn as appendSessionIndexEntry, Gt as lerpGradient, H as AgentGroupComponent, Hn as saveCatalogCache, Ht as isBusy, I as getSharedSpeedTracker, In as CLI_COMMAND_NAME, It as OSC11_RESPONSE_PREFIX_NO_ESC, J as refineGoal, Jn as MemoryMemoStore, K as resetBreathingClock, L as SkillActivationComponent, Ln as CLI_UI_MODE, Lt as QUERY_TERMINAL_THEME, M as langFromPath, Mn as getDataDir, Mt as ENABLE_TERMINAL_THEME_REPORTING, N as CachedContainer, Nn as getInputHistoryFile, Nt as OSC11_QUERY, O as ToolCallComponent, On as TuiLikePreferencesSchema, Ot as parseOsc11BackgroundTheme, P as ThinkingComponent, Pn as getLogDir, Pt as OSC11_RESPONSE, Qn as isScreamError, Qt as STATUS_BULLET, R as ReadGroupComponent, Rn as CLI_USER_AGENT_PRODUCT, Rt as TERMINAL_FOCUS_IN, S as getDaemonInstructions, Sn as getKnowledgeStore, St as createEditorTheme, T as isEmptySessionHintDismissed, Tn as PIXEL_PULSE_FRAMES, Tt as contrastTextHex, U as WelcomeComponent, Un as ScreamHarness, Ut as isStreaming, V as AssistantMessageComponent, Vn as fetchCatalog, Vt as TERMINAL_THEME_LIGHT, W as BREATHE_CYCLE_MS, Wn as encodeWorkDirKey, Wt as FooterComponent, Xn as log, Xt as printableChar, Yn as flushDiagnosticLogs, Zn as resolveGlobalLogPath, Zt as INTERJECTION_BULLET, _ as refreshUpdateCache, _n as buildSkillSlashCommands, _t as supportsBalance, an as stringValue, b as readJsonlFile, bn as isExperimentalFlagEnabled, c as hasDispose, cn as EMPTY_SESSION_HINT_URL, d as formatMemoryMemoForInjection, dn as SESSION_TIPS, en as argsRecord, er as ErrorCodes, et as toTerminalHyperlink, fn as TIP_ROTATION_INTERVAL_MS, g as selectUpdateTarget, gn as getNoActiveSessionMessage, gt as refreshProviderBalance, hn as getLlmNotSetMessage, i as buildRoleAdditionalText, in as serializeToolResultOutput, j as highlightLines, jn as detectShellEnvironment, jt as ENABLE_TERMINAL_FOCUS_REPORTING, k as renderDiffLines, kn as loadTuiConfig, kt as DISABLE_TERMINAL_FOCUS_REPORTING, l as isPlanExpandable, ln as EXIT_CONFIRM_WINDOW_MS, lt as handlePlanCommand, mn as getCtrlDHint, n as clearEvalPanelState, nn as isTodoItemShape, nt as getModelCycleLevel, on as truncateErrorMessage, pn as getCtrlCHint, q as clearGoalState, qn as resolveScreamHome, r as openUrl, rn as parseStreamingArgs, s as disposeChildren, sn as CHARS_PER_TOKEN, st as handleFusionPlanCommand, t as dispatchInput, tn as formatErrorMessage, tr as SCREAM_ERROR_INFO, tt as changeThinkingLevel, u as MoonLoader, un as MAIN_AGENT_ID, v as readUpdateCache, vn as BUILTIN_SLASH_COMMANDS, vt as clearInfoPanelState, w as UserMessageComponent, wn as startManualEmbeddingDownload, wt as createThemeStyles, xn as setExperimentalFlags, xt as resolveThemeSync, y as appendJsonlLine, yn as sortSlashCommands, z as parseReadGroupOutput, zn as PRODUCT_NAME, zt as TERMINAL_FOCUS_OUT } from "./dispatch-CsrteWRw.mjs";
|
|
8
|
+
import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-BVQaKy1Z.mjs";
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
10
|
import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
11
11
|
import { access, chmod, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
@@ -32,7 +32,7 @@ function optionalBuildString(value) {
|
|
|
32
32
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
33
33
|
}
|
|
34
34
|
const SCREAM_BUILD_INFO = {
|
|
35
|
-
version: optionalBuildString("0.
|
|
35
|
+
version: optionalBuildString("0.16.0"),
|
|
36
36
|
channel: optionalBuildString(""),
|
|
37
37
|
commit: optionalBuildString(""),
|
|
38
38
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -1995,10 +1995,11 @@ var StatusMessageComponent = class extends Container {
|
|
|
1995
1995
|
}
|
|
1996
1996
|
};
|
|
1997
1997
|
var NoticeMessageComponent = class extends Container {
|
|
1998
|
-
constructor(title, detail, colors) {
|
|
1998
|
+
constructor(title, detail, colors, markerColor) {
|
|
1999
1999
|
super();
|
|
2000
|
+
const marker = markerColor === void 0 ? " " : chalk.hex(markerColor)(INTERJECTION_BULLET);
|
|
2000
2001
|
this.addChild(new Spacer(1));
|
|
2001
|
-
this.addChild(new Text(
|
|
2002
|
+
this.addChild(new Text(`${marker}${chalk.hex(colors.textStrong)(replaceTabs(title))}`, 0, 0));
|
|
2002
2003
|
if (detail !== void 0 && detail.length > 0) this.addChild(new Text(` ${chalk.hex(colors.textDim)(replaceTabs(detail))}`, 0, 0));
|
|
2003
2004
|
}
|
|
2004
2005
|
};
|
|
@@ -2025,6 +2026,158 @@ function formatBackgroundAgentTranscript(phase, meta, extras = void 0) {
|
|
|
2025
2026
|
};
|
|
2026
2027
|
}
|
|
2027
2028
|
//#endregion
|
|
2029
|
+
//#region src/tui/utils/child-request-notice.ts
|
|
2030
|
+
/**
|
|
2031
|
+
* Child→parent collaboration requests (`ContactParent`) shown in the main
|
|
2032
|
+
* transcript as a two-line notice: a bold title naming the requester, then a
|
|
2033
|
+
* dim detail line carrying the request itself.
|
|
2034
|
+
*
|
|
2035
|
+
* Two producers share this formatter so live and replay never disagree:
|
|
2036
|
+
* - the live path reads the subagent's `tool.call.started` args;
|
|
2037
|
+
* - session replay recovers the same fields from the `<notification>` XML the
|
|
2038
|
+
* kernel injects into the parent's context.
|
|
2039
|
+
*
|
|
2040
|
+
* Replay cannot recover the subagent's display name (spawn events are not part
|
|
2041
|
+
* of the persisted context), so it renders the name-less title variant.
|
|
2042
|
+
*/
|
|
2043
|
+
const CHILD_REQUEST_KINDS = [
|
|
2044
|
+
"info",
|
|
2045
|
+
"handoff",
|
|
2046
|
+
"escalate"
|
|
2047
|
+
];
|
|
2048
|
+
/** Cap each payload list so a 20-entry artifact array cannot explode the transcript. */
|
|
2049
|
+
const LIST_PREVIEW = 3;
|
|
2050
|
+
/**
|
|
2051
|
+
* Longest previewed value kept on a transcript row — request text, `needs`, or
|
|
2052
|
+
* a payload list. The kernel allows 8192 chars (`contact-parent.ts`), and
|
|
2053
|
+
* transcript folding counts components rather than lines, so an uncapped row
|
|
2054
|
+
* can never collapse. The full text stays available: it is in the parent's
|
|
2055
|
+
* context and in the agent card's sub-tool-call args.
|
|
2056
|
+
*/
|
|
2057
|
+
const TEXT_PREVIEW = 200;
|
|
2058
|
+
/** Collapse the request onto previewable single-paragraph form. */
|
|
2059
|
+
function previewText(value) {
|
|
2060
|
+
const flattened = value.replaceAll(/\s*\n\s*/g, " ").trim();
|
|
2061
|
+
return flattened.length > TEXT_PREVIEW ? `${flattened.slice(0, TEXT_PREVIEW)}…` : flattened;
|
|
2062
|
+
}
|
|
2063
|
+
const TYPE_KEY = {
|
|
2064
|
+
info: "transcript.child_request_type_info",
|
|
2065
|
+
handoff: "transcript.child_request_type_handoff",
|
|
2066
|
+
escalate: "transcript.child_request_type_escalate"
|
|
2067
|
+
};
|
|
2068
|
+
const LIST_KEY = {
|
|
2069
|
+
artifacts: "transcript.child_request_artifacts",
|
|
2070
|
+
evidence: "transcript.child_request_evidence",
|
|
2071
|
+
missing: "transcript.child_request_missing"
|
|
2072
|
+
};
|
|
2073
|
+
function kindOf(raw) {
|
|
2074
|
+
return typeof raw === "string" && CHILD_REQUEST_KINDS.includes(raw) ? raw : "info";
|
|
2075
|
+
}
|
|
2076
|
+
function textOf(raw) {
|
|
2077
|
+
if (typeof raw !== "string") return void 0;
|
|
2078
|
+
const trimmed = raw.trim();
|
|
2079
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
2080
|
+
}
|
|
2081
|
+
function stringifyList(raw) {
|
|
2082
|
+
const shown = previewText(raw.slice(0, LIST_PREVIEW).join(", "));
|
|
2083
|
+
const hidden = raw.length - Math.min(raw.length, LIST_PREVIEW);
|
|
2084
|
+
return hidden > 0 ? `${shown} +${hidden}` : shown;
|
|
2085
|
+
}
|
|
2086
|
+
function previewedList(field, items) {
|
|
2087
|
+
if (items === void 0 || items.length === 0) return void 0;
|
|
2088
|
+
return t(LIST_KEY[field], { value: stringifyList(items) });
|
|
2089
|
+
}
|
|
2090
|
+
/** Live producer: the args the model handed to `ContactParent`. */
|
|
2091
|
+
function childRequestFieldsFromArgs(args) {
|
|
2092
|
+
const message = textOf(args["message"]);
|
|
2093
|
+
if (message === void 0) return null;
|
|
2094
|
+
const payload = typeof args["payload"] === "object" && args["payload"] !== null && !Array.isArray(args["payload"]) ? args["payload"] : {};
|
|
2095
|
+
const listOf = (key) => {
|
|
2096
|
+
const raw = payload[key];
|
|
2097
|
+
if (!Array.isArray(raw)) return void 0;
|
|
2098
|
+
const items = raw.map((item) => textOf(item)).filter((item) => item !== void 0);
|
|
2099
|
+
return items.length > 0 ? items : void 0;
|
|
2100
|
+
};
|
|
2101
|
+
return {
|
|
2102
|
+
requestType: kindOf(args["request_type"]),
|
|
2103
|
+
message,
|
|
2104
|
+
needs: textOf(args["needs"]),
|
|
2105
|
+
artifacts: listOf("artifacts"),
|
|
2106
|
+
evidence: listOf("evidence"),
|
|
2107
|
+
missing: listOf("missing")
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
/** Parse `artifacts: [a, b]` back into its items. */
|
|
2111
|
+
function bracketedListOf(value) {
|
|
2112
|
+
return (value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value).split(",").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
2113
|
+
}
|
|
2114
|
+
/**
|
|
2115
|
+
* Replay producer: recover the fields from the injected notification. The body
|
|
2116
|
+
* shape is owned by `submitChildRequest` in agent-core:
|
|
2117
|
+
* `<type>: <message>` then optional `needs:` / `artifacts:` / `evidence:` /
|
|
2118
|
+
* `missing:` lines. A multi-line message keeps its extra lines attached.
|
|
2119
|
+
*/
|
|
2120
|
+
function childRequestFieldsFromNotification(text) {
|
|
2121
|
+
const lines = text.split("\n");
|
|
2122
|
+
const open = lines.findIndex((line) => line.startsWith("<notification ") && line.includes("type=\"child_request\""));
|
|
2123
|
+
if (open < 0) return null;
|
|
2124
|
+
const body = [];
|
|
2125
|
+
for (let index = open + 1; index < lines.length; index += 1) {
|
|
2126
|
+
const line = lines[index] ?? "";
|
|
2127
|
+
if (line === "</notification>") break;
|
|
2128
|
+
if (line.startsWith("Title: ") || line.startsWith("Severity: ")) continue;
|
|
2129
|
+
body.push(line);
|
|
2130
|
+
}
|
|
2131
|
+
const first = body[0];
|
|
2132
|
+
if (first === void 0) return null;
|
|
2133
|
+
const head = /^(info|handoff|escalate):\s([\s\S]*)$/.exec(first);
|
|
2134
|
+
const requestType = head !== null ? kindOf(head[1]) : "info";
|
|
2135
|
+
const messageParts = [head !== null && head[2] !== void 0 ? head[2] : first];
|
|
2136
|
+
let needs;
|
|
2137
|
+
const lists = {};
|
|
2138
|
+
for (const line of body.slice(1)) {
|
|
2139
|
+
const separator = line.indexOf(": ");
|
|
2140
|
+
const key = separator < 0 ? "" : line.slice(0, separator);
|
|
2141
|
+
const value = separator < 0 ? "" : line.slice(separator + 2).trim();
|
|
2142
|
+
const listKey = (key === "artifacts" || key === "evidence" || key === "missing") && value.startsWith("[") && value.endsWith("]") ? key : void 0;
|
|
2143
|
+
if (key === "needs" && value.length > 0) needs = value;
|
|
2144
|
+
else if (listKey !== void 0) lists[listKey] = bracketedListOf(value);
|
|
2145
|
+
else messageParts.push(line);
|
|
2146
|
+
}
|
|
2147
|
+
const message = messageParts.join("\n").trim();
|
|
2148
|
+
if (message.length === 0) return null;
|
|
2149
|
+
return {
|
|
2150
|
+
requestType,
|
|
2151
|
+
message,
|
|
2152
|
+
needs,
|
|
2153
|
+
artifacts: lists["artifacts"],
|
|
2154
|
+
evidence: lists["evidence"],
|
|
2155
|
+
missing: lists["missing"]
|
|
2156
|
+
};
|
|
2157
|
+
}
|
|
2158
|
+
/** Build the transcript notice. `name` is absent on replay. */
|
|
2159
|
+
function renderChildRequestNotice(fields, name) {
|
|
2160
|
+
const type = t(TYPE_KEY[fields.requestType]);
|
|
2161
|
+
const title = name === void 0 ? t("transcript.child_request_anon", { type }) : t("transcript.child_request", {
|
|
2162
|
+
name,
|
|
2163
|
+
type
|
|
2164
|
+
});
|
|
2165
|
+
const parts = [previewText(fields.message)];
|
|
2166
|
+
if (fields.needs !== void 0) parts.push(t("transcript.child_request_needs", { value: previewText(fields.needs) }));
|
|
2167
|
+
for (const entry of [
|
|
2168
|
+
["artifacts", fields.artifacts],
|
|
2169
|
+
["evidence", fields.evidence],
|
|
2170
|
+
["missing", fields.missing]
|
|
2171
|
+
]) {
|
|
2172
|
+
const preview = previewedList(entry[0], entry[1]);
|
|
2173
|
+
if (preview !== void 0) parts.push(preview);
|
|
2174
|
+
}
|
|
2175
|
+
return {
|
|
2176
|
+
title,
|
|
2177
|
+
detail: parts.join(" · ")
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
//#endregion
|
|
2028
2181
|
//#region src/tui/utils/subagent-slots.ts
|
|
2029
2182
|
/** Default subagent types, in the fixed display order of the slots. */
|
|
2030
2183
|
const DEFAULT_SUBAGENT_TYPES = [
|
|
@@ -2037,6 +2190,10 @@ const DEFAULT_SUBAGENT_TYPES = [
|
|
|
2037
2190
|
"worker",
|
|
2038
2191
|
"writer"
|
|
2039
2192
|
];
|
|
2193
|
+
/** How long a `requesting` overlay stays visible after a subagent asks the
|
|
2194
|
+
* parent for help. The request itself returns at once and the agent keeps
|
|
2195
|
+
* working, so this is a display window, not a blocked state. */
|
|
2196
|
+
const REQUESTING_WINDOW_MS = 5e3;
|
|
2040
2197
|
var SubagentSlots = class {
|
|
2041
2198
|
slots = /* @__PURE__ */ new Map();
|
|
2042
2199
|
instances = /* @__PURE__ */ new Map();
|
|
@@ -2050,6 +2207,10 @@ var SubagentSlots = class {
|
|
|
2050
2207
|
* the first open window; concurrent windows on the same type keep counting
|
|
2051
2208
|
* and the slot only exits messaging when the last window closes. */
|
|
2052
2209
|
messagingPrev = /* @__PURE__ */ new Map();
|
|
2210
|
+
/** Slot type → open `requesting` overlay. Keyed by type so several live
|
|
2211
|
+
* instances of one type share a single marker; the real activity state stays
|
|
2212
|
+
* untouched in the slot and shows back up once the overlay expires. */
|
|
2213
|
+
requesting = /* @__PURE__ */ new Map();
|
|
2053
2214
|
/** Count of open messaging windows per type (handles same-type siblings). */
|
|
2054
2215
|
messagingCount = /* @__PURE__ */ new Map();
|
|
2055
2216
|
reset() {
|
|
@@ -2059,6 +2220,7 @@ var SubagentSlots = class {
|
|
|
2059
2220
|
this.messagingByCall.clear();
|
|
2060
2221
|
this.messagingPrev.clear();
|
|
2061
2222
|
this.messagingCount.clear();
|
|
2223
|
+
this.requesting.clear();
|
|
2062
2224
|
}
|
|
2063
2225
|
/**
|
|
2064
2226
|
* subagent.spawned. A first spawn starts the slot as working; a second
|
|
@@ -2098,6 +2260,7 @@ var SubagentSlots = class {
|
|
|
2098
2260
|
onTerminated(agentId) {
|
|
2099
2261
|
const instance = this.instances.get(agentId);
|
|
2100
2262
|
if (instance === void 0) return;
|
|
2263
|
+
if (this.requesting.get(instance.type)?.agentId === agentId) this.requesting.delete(instance.type);
|
|
2101
2264
|
const remaining = (this.runningByType.get(instance.type) ?? 1) - 1;
|
|
2102
2265
|
if (remaining > 0) {
|
|
2103
2266
|
this.runningByType.set(instance.type, remaining);
|
|
@@ -2126,6 +2289,21 @@ var SubagentSlots = class {
|
|
|
2126
2289
|
}
|
|
2127
2290
|
this.setState(instance.type, kind === "tool" ? "working" : "outputting", agentId, detail);
|
|
2128
2291
|
}
|
|
2292
|
+
/** A subagent asked the parent for help. The request returns immediately and
|
|
2293
|
+
* the agent keeps working, so this only raises a transient overlay — the real
|
|
2294
|
+
* activity state stays in the slot and shows back up when it expires. While
|
|
2295
|
+
* `messaging` is on screen the overlay loses (see visibleSlot). */
|
|
2296
|
+
onRequesting(agentId, at = Date.now()) {
|
|
2297
|
+
const instance = this.instances.get(agentId);
|
|
2298
|
+
if (instance === void 0) return;
|
|
2299
|
+
this.requesting.set(instance.type, {
|
|
2300
|
+
until: at + REQUESTING_WINDOW_MS,
|
|
2301
|
+
agentId
|
|
2302
|
+
});
|
|
2303
|
+
const slot = this.ensureSlot(instance.type);
|
|
2304
|
+
slot.agentId = agentId;
|
|
2305
|
+
slot.lastActivityAt = at;
|
|
2306
|
+
}
|
|
2129
2307
|
/**
|
|
2130
2308
|
* Main-side SendSubagentMessage tool call started: the target subagent's
|
|
2131
2309
|
* slot shows messaging until the tool call returns.
|
|
@@ -2135,6 +2313,7 @@ var SubagentSlots = class {
|
|
|
2135
2313
|
const instance = this.instances.get(targetAgentId);
|
|
2136
2314
|
if (instance === void 0) return;
|
|
2137
2315
|
this.messagingByCall.set(toolCallId, targetAgentId);
|
|
2316
|
+
this.requesting.delete(instance.type);
|
|
2138
2317
|
const slot = this.ensureSlot(instance.type);
|
|
2139
2318
|
if (slot.status !== "messaging") this.messagingPrev.set(instance.type, slot.status);
|
|
2140
2319
|
this.messagingCount.set(instance.type, (this.messagingCount.get(instance.type) ?? 0) + 1);
|
|
@@ -2166,24 +2345,31 @@ var SubagentSlots = class {
|
|
|
2166
2345
|
* All slots in fixed display order: the eight default types first (always
|
|
2167
2346
|
* present, idle when never used), then extra types in spawn order.
|
|
2168
2347
|
*/
|
|
2169
|
-
getSlots() {
|
|
2348
|
+
getSlots(at = Date.now()) {
|
|
2349
|
+
this.dropExpiredOverlays(at);
|
|
2170
2350
|
const ordered = [];
|
|
2171
|
-
for (const type of DEFAULT_SUBAGENT_TYPES)
|
|
2172
|
-
const slot = this.ensureSlot(type);
|
|
2173
|
-
ordered.push({
|
|
2174
|
-
...slot,
|
|
2175
|
-
count: this.runningByType.get(type) ?? 0
|
|
2176
|
-
});
|
|
2177
|
-
}
|
|
2351
|
+
for (const type of DEFAULT_SUBAGENT_TYPES) ordered.push(this.visibleSlot(type));
|
|
2178
2352
|
for (const slot of this.slots.values()) {
|
|
2179
2353
|
if (DEFAULT_SUBAGENT_TYPES.includes(slot.type)) continue;
|
|
2180
|
-
ordered.push(
|
|
2181
|
-
...slot,
|
|
2182
|
-
count: this.runningByType.get(slot.type) ?? 0
|
|
2183
|
-
});
|
|
2354
|
+
ordered.push(this.visibleSlot(slot.type));
|
|
2184
2355
|
}
|
|
2185
2356
|
return ordered;
|
|
2186
2357
|
}
|
|
2358
|
+
/** Display view of one slot: the real state with an unexpired `requesting`
|
|
2359
|
+
* overlay on top. Read-path only — it never mutates. Callers must run
|
|
2360
|
+
* {@link dropExpiredOverlays} first, and messaging clears the overlay in
|
|
2361
|
+
* {@link onMessagingStart}; the `messaging` guard only covers same-tick ordering. */
|
|
2362
|
+
visibleSlot(type) {
|
|
2363
|
+
const copy = {
|
|
2364
|
+
...this.ensureSlot(type),
|
|
2365
|
+
count: this.runningByType.get(type) ?? 0
|
|
2366
|
+
};
|
|
2367
|
+
if (this.requesting.has(type) && copy.status !== "messaging") copy.status = "requesting";
|
|
2368
|
+
return copy;
|
|
2369
|
+
}
|
|
2370
|
+
dropExpiredOverlays(at) {
|
|
2371
|
+
for (const [type, overlay] of this.requesting) if (at >= overlay.until) this.requesting.delete(type);
|
|
2372
|
+
}
|
|
2187
2373
|
ensureSlot(type) {
|
|
2188
2374
|
let slot = this.slots.get(type);
|
|
2189
2375
|
if (slot === void 0) {
|
|
@@ -2580,6 +2766,15 @@ var SessionEventHandler = class {
|
|
|
2580
2766
|
subagentSlots = new SubagentSlots();
|
|
2581
2767
|
/** Tool callId of the in-flight UpdateGoal(complete) (judging), if any. */
|
|
2582
2768
|
goalJudgeCallId;
|
|
2769
|
+
/** Rolling first-token latencies of real requests, feeding the sidebar Hub
|
|
2770
|
+
* provider row. In-process only, and dropped whenever the model changes —
|
|
2771
|
+
* a different provider is a different network path. */
|
|
2772
|
+
providerLatencySamples = [];
|
|
2773
|
+
providerLatencyModel;
|
|
2774
|
+
providerLatencyAt;
|
|
2775
|
+
/** Enough samples to smooth one slow request, few enough that a recovered
|
|
2776
|
+
* endpoint reads as recovered again quickly. */
|
|
2777
|
+
providerLatencyWindow = 3;
|
|
2583
2778
|
renderedSkillActivationIds = /* @__PURE__ */ new Set();
|
|
2584
2779
|
renderedMcpServerStatusKeys = /* @__PURE__ */ new Map();
|
|
2585
2780
|
mcpServerStatusSpinners = /* @__PURE__ */ new Map();
|
|
@@ -2595,6 +2790,9 @@ var SessionEventHandler = class {
|
|
|
2595
2790
|
this.pendingSkillCandidates = [];
|
|
2596
2791
|
this.promptedSkillCandidates.clear();
|
|
2597
2792
|
this.goalJudgeCallId = void 0;
|
|
2793
|
+
this.providerLatencySamples = [];
|
|
2794
|
+
this.providerLatencyModel = void 0;
|
|
2795
|
+
this.providerLatencyAt = void 0;
|
|
2598
2796
|
this.subagentUsageSeen.clear();
|
|
2599
2797
|
this.host.setAppState({
|
|
2600
2798
|
subagentUsage: {},
|
|
@@ -2768,6 +2966,41 @@ var SessionEventHandler = class {
|
|
|
2768
2966
|
getSubagentSlots() {
|
|
2769
2967
|
return this.subagentSlots.getSlots();
|
|
2770
2968
|
}
|
|
2969
|
+
/** Keep the last few first-token latencies, per provider. Steps without a
|
|
2970
|
+
* measured latency (no stream, cached turn) are ignored rather than counted
|
|
2971
|
+
* as a zero. */
|
|
2972
|
+
recordProviderLatency(event) {
|
|
2973
|
+
const latency = event.llmFirstTokenLatencyMs;
|
|
2974
|
+
if (latency === void 0) return;
|
|
2975
|
+
const model = this.host.state.appState.model;
|
|
2976
|
+
if (this.providerLatencyModel !== void 0 && model !== this.providerLatencyModel) this.providerLatencySamples = [];
|
|
2977
|
+
this.providerLatencyModel = model;
|
|
2978
|
+
this.providerLatencySamples.push(latency);
|
|
2979
|
+
if (this.providerLatencySamples.length > this.providerLatencyWindow) this.providerLatencySamples.shift();
|
|
2980
|
+
this.providerLatencyAt = Date.now();
|
|
2981
|
+
}
|
|
2982
|
+
/** Sidebar Hub: average measured response time for the provider in use. The
|
|
2983
|
+
* caller owns tone and staleness — this stays a plain reading. */
|
|
2984
|
+
getProviderLatency() {
|
|
2985
|
+
if (this.providerLatencySamples.length === 0) return {
|
|
2986
|
+
ms: void 0,
|
|
2987
|
+
sampledAt: void 0
|
|
2988
|
+
};
|
|
2989
|
+
if (this.host.state.appState.model !== this.providerLatencyModel) {
|
|
2990
|
+
this.providerLatencySamples = [];
|
|
2991
|
+
this.providerLatencyModel = void 0;
|
|
2992
|
+
this.providerLatencyAt = void 0;
|
|
2993
|
+
return {
|
|
2994
|
+
ms: void 0,
|
|
2995
|
+
sampledAt: void 0
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2998
|
+
const total = this.providerLatencySamples.reduce((sum, value) => sum + value, 0);
|
|
2999
|
+
return {
|
|
3000
|
+
ms: Math.round(total / this.providerLatencySamples.length),
|
|
3001
|
+
sampledAt: this.providerLatencyAt
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
2771
3004
|
/** Feed routed subagent activity into the slot state machine. */
|
|
2772
3005
|
feedSubagentSlots(event, agentId) {
|
|
2773
3006
|
switch (event.type) {
|
|
@@ -2782,7 +3015,10 @@ var SessionEventHandler = class {
|
|
|
2782
3015
|
this.subagentSlots.onTerminated(event.subagentId);
|
|
2783
3016
|
break;
|
|
2784
3017
|
case "tool.call.started":
|
|
2785
|
-
|
|
3018
|
+
if (event.name === "ContactParent") {
|
|
3019
|
+
this.subagentSlots.onRequesting(agentId);
|
|
3020
|
+
this.appendChildRequestNotice(agentId, event.args);
|
|
3021
|
+
} else this.subagentSlots.onActivity(agentId, "tool", `tool: ${event.name}`);
|
|
2786
3022
|
break;
|
|
2787
3023
|
case "tool.call.delta": break;
|
|
2788
3024
|
case "tool.result":
|
|
@@ -2795,6 +3031,26 @@ var SessionEventHandler = class {
|
|
|
2795
3031
|
default: break;
|
|
2796
3032
|
}
|
|
2797
3033
|
}
|
|
3034
|
+
/**
|
|
3035
|
+
* Surface a child→parent collaboration request in the transcript the moment
|
|
3036
|
+
* the subagent asks, so the human sees it without expanding the agent card.
|
|
3037
|
+
* The request always targets the main agent (one subagent host per session),
|
|
3038
|
+
* which is why a top-level transcript entry is the right home for it.
|
|
3039
|
+
*/
|
|
3040
|
+
appendChildRequestNotice(agentId, args) {
|
|
3041
|
+
const fields = childRequestFieldsFromArgs(argsRecord(args));
|
|
3042
|
+
if (fields === null) return;
|
|
3043
|
+
const notice = renderChildRequestNotice(fields, this.subagentInfo.get(agentId)?.name);
|
|
3044
|
+
this.host.appendTranscriptEntry({
|
|
3045
|
+
id: nextTranscriptId(),
|
|
3046
|
+
kind: "status",
|
|
3047
|
+
turnId: this.host.streamingUI.getTurnContext().turnId,
|
|
3048
|
+
renderMode: "notice",
|
|
3049
|
+
content: notice.title,
|
|
3050
|
+
detail: notice.detail,
|
|
3051
|
+
noticeMarkerColor: this.host.state.theme.colors.warning
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
2798
3054
|
routeSubagentEvent(event) {
|
|
2799
3055
|
const subagentId = event.agentId;
|
|
2800
3056
|
if (subagentId === "main") return false;
|
|
@@ -2912,6 +3168,7 @@ var SessionEventHandler = class {
|
|
|
2912
3168
|
handleStepCompleted(event) {
|
|
2913
3169
|
this.host.streamingUI.flushNow();
|
|
2914
3170
|
this.maybeShowDebugTiming(event);
|
|
3171
|
+
this.recordProviderLatency(event);
|
|
2915
3172
|
this.drainQueuedMessagesIntoSteer();
|
|
2916
3173
|
const prev = this.host.state.appState.sessionUsage;
|
|
2917
3174
|
const apiCalls = this.host.state.appState.sessionApiCalls + 1;
|
|
@@ -4018,6 +4275,10 @@ var SessionReplayRenderer = class {
|
|
|
4018
4275
|
this.renderCronMissed(context, message.origin);
|
|
4019
4276
|
return;
|
|
4020
4277
|
}
|
|
4278
|
+
if (message.origin?.kind === "system_trigger") {
|
|
4279
|
+
this.renderSystemTrigger(context, message);
|
|
4280
|
+
return;
|
|
4281
|
+
}
|
|
4021
4282
|
this.flushAssistant(context);
|
|
4022
4283
|
const skill = skillActivationFromOrigin(message.origin);
|
|
4023
4284
|
if (skill !== void 0) {
|
|
@@ -4028,6 +4289,26 @@ var SessionReplayRenderer = class {
|
|
|
4028
4289
|
this.advanceTurn(context);
|
|
4029
4290
|
this.host.appendTranscriptEntry(replayEntry(context, "user", contentPartsToText(message.content), "plain"));
|
|
4030
4291
|
}
|
|
4292
|
+
/**
|
|
4293
|
+
* `system_trigger` messages are internal injections, not user speech: the
|
|
4294
|
+
* live transcript never prints them and markdown export filters them out, so
|
|
4295
|
+
* replay must not surface them as a user row either. The single exception is
|
|
4296
|
+
* a child→parent collaboration request, which gets the same notice the live
|
|
4297
|
+
* path shows. Replay has no spawn events, so the subagent's display name is
|
|
4298
|
+
* gone and the name-less title variant keeps the notice honest.
|
|
4299
|
+
*/
|
|
4300
|
+
renderSystemTrigger(context, message) {
|
|
4301
|
+
const origin = message.origin;
|
|
4302
|
+
if (origin?.kind !== "system_trigger" || origin.name !== "child_request") return;
|
|
4303
|
+
const fields = childRequestFieldsFromNotification(contentPartsToText(message.content));
|
|
4304
|
+
if (fields === null) return;
|
|
4305
|
+
const notice = renderChildRequestNotice(fields);
|
|
4306
|
+
this.flushAssistant(context);
|
|
4307
|
+
this.host.appendTranscriptEntry({
|
|
4308
|
+
...replayEntry(context, "status", notice.title, "notice", { detail: notice.detail }),
|
|
4309
|
+
noticeMarkerColor: this.host.state.theme.colors.warning
|
|
4310
|
+
});
|
|
4311
|
+
}
|
|
4031
4312
|
renderToolCalls(context, toolCalls) {
|
|
4032
4313
|
if (toolCalls.length === 0) return;
|
|
4033
4314
|
const { streamingUI } = this.host;
|
|
@@ -6267,7 +6548,7 @@ var CommittedMessageComponent = class {
|
|
|
6267
6548
|
return new Text(` ${chalk.hex(colors.textDim)(t("transcript.tool_name", { name }))}${summary}`, 0, 0).render(width);
|
|
6268
6549
|
}
|
|
6269
6550
|
case "status":
|
|
6270
|
-
if (entry.renderMode === "notice") return new NoticeMessageComponent(entry.content, entry.detail, colors).render(width);
|
|
6551
|
+
if (entry.renderMode === "notice") return new NoticeMessageComponent(entry.content, entry.detail, colors, entry.noticeMarkerColor).render(width);
|
|
6271
6552
|
return new StatusMessageComponent(entry.content, colors, entry.color).render(width);
|
|
6272
6553
|
case "skill_activation": {
|
|
6273
6554
|
const text = entry.skillName ?? entry.content;
|
|
@@ -6416,10 +6697,10 @@ var TranscriptController = class TranscriptController {
|
|
|
6416
6697
|
return tc;
|
|
6417
6698
|
}
|
|
6418
6699
|
if (entry.backgroundAgentStatus !== void 0) return new BackgroundAgentStatusComponent(entry.backgroundAgentStatus, state.theme.colors);
|
|
6419
|
-
return entry.renderMode === "notice" ? new NoticeMessageComponent(entry.content, entry.detail, state.theme.colors) : new StatusMessageComponent(entry.content, state.theme.colors, entry.color);
|
|
6700
|
+
return entry.renderMode === "notice" ? new NoticeMessageComponent(entry.content, entry.detail, state.theme.colors, entry.noticeMarkerColor) : new StatusMessageComponent(entry.content, state.theme.colors, entry.color);
|
|
6420
6701
|
case "status":
|
|
6421
6702
|
if (entry.backgroundAgentStatus !== void 0) return new BackgroundAgentStatusComponent(entry.backgroundAgentStatus, state.theme.colors);
|
|
6422
|
-
return entry.renderMode === "notice" ? new NoticeMessageComponent(entry.content, entry.detail, state.theme.colors) : new StatusMessageComponent(entry.content, state.theme.colors, entry.color);
|
|
6703
|
+
return entry.renderMode === "notice" ? new NoticeMessageComponent(entry.content, entry.detail, state.theme.colors, entry.noticeMarkerColor) : new StatusMessageComponent(entry.content, state.theme.colors, entry.color);
|
|
6423
6704
|
case "cron":
|
|
6424
6705
|
if (entry.cronData === void 0) return null;
|
|
6425
6706
|
return new CronMessageComponent(entry.content, entry.cronData, state.theme.colors);
|
|
@@ -6668,6 +6949,55 @@ function clampSidebarWidth(cols) {
|
|
|
6668
6949
|
return Math.min(60, Math.max(24, Math.round(cols)));
|
|
6669
6950
|
}
|
|
6670
6951
|
//#endregion
|
|
6952
|
+
//#region src/tui/utils/display-width.ts
|
|
6953
|
+
/** Display-column width of a string, matching the terminal layout engine
|
|
6954
|
+
* (ANSI sequences stripped, CJK/wide chars counted as 2 columns). */
|
|
6955
|
+
function displayWidth(text) {
|
|
6956
|
+
return visibleWidth(text);
|
|
6957
|
+
}
|
|
6958
|
+
/** Pad `label` to `target` display columns with spaces (CJK-aware). */
|
|
6959
|
+
function padLabel(label, target) {
|
|
6960
|
+
return label + " ".repeat(Math.max(0, target - displayWidth(label)));
|
|
6961
|
+
}
|
|
6962
|
+
function layoutSidebarGrid(rows, width) {
|
|
6963
|
+
const valueCols = Math.max(0, ...rows.map((row) => displayWidth(row.value ?? "")));
|
|
6964
|
+
const labelBudget = Math.max(1, width - 2 - Math.max(valueCols, 1) - 1);
|
|
6965
|
+
const labelCols = Math.min(12, labelBudget);
|
|
6966
|
+
const valueStart = Math.max(2 + labelCols + 1, width - valueCols);
|
|
6967
|
+
return rows.map((row) => {
|
|
6968
|
+
const marker = padLabel(row.marker ?? "", 2);
|
|
6969
|
+
const label = truncateToWidth(row.label, labelCols);
|
|
6970
|
+
const used = 2 + displayWidth(label);
|
|
6971
|
+
if (row.value !== void 0) {
|
|
6972
|
+
const gap = Math.max(1, width - used - displayWidth(row.value));
|
|
6973
|
+
const room = Math.max(1, width - used - gap);
|
|
6974
|
+
return {
|
|
6975
|
+
marker,
|
|
6976
|
+
label,
|
|
6977
|
+
gap,
|
|
6978
|
+
value: truncateToWidth(row.value, room)
|
|
6979
|
+
};
|
|
6980
|
+
}
|
|
6981
|
+
if (row.note !== void 0) {
|
|
6982
|
+
const noteCols = displayWidth(row.note);
|
|
6983
|
+
const aligned = Math.max(1, valueStart - used);
|
|
6984
|
+
const fits = Math.max(1, width - used - noteCols);
|
|
6985
|
+
const gap = Math.min(aligned, fits);
|
|
6986
|
+
return {
|
|
6987
|
+
marker,
|
|
6988
|
+
label,
|
|
6989
|
+
gap,
|
|
6990
|
+
note: truncateToWidth(row.note, Math.max(1, width - used - gap))
|
|
6991
|
+
};
|
|
6992
|
+
}
|
|
6993
|
+
return {
|
|
6994
|
+
marker,
|
|
6995
|
+
label,
|
|
6996
|
+
gap: 1
|
|
6997
|
+
};
|
|
6998
|
+
});
|
|
6999
|
+
}
|
|
7000
|
+
//#endregion
|
|
6671
7001
|
//#region src/tui/components/sidebar/panels/git-panel.ts
|
|
6672
7002
|
/**
|
|
6673
7003
|
* Summary-only Git panel (摘要式): the workdir name plus added (green) /
|
|
@@ -6683,12 +7013,13 @@ var GitPanelContent = class {
|
|
|
6683
7013
|
}
|
|
6684
7014
|
invalidate() {}
|
|
6685
7015
|
render(width) {
|
|
7016
|
+
const indent = " ".repeat(2);
|
|
6686
7017
|
const git = this.ctx.getData().git;
|
|
6687
|
-
if (git === void 0) return [chalk.hex(this.colors.textDim)(t("sidebar.git_no_repo"))];
|
|
7018
|
+
if (git === void 0) return [`${indent}${chalk.hex(this.colors.textDim)(t("sidebar.git_no_repo"))}`];
|
|
6688
7019
|
const dim = (s) => chalk.hex(this.colors.textDim)(s);
|
|
6689
|
-
const workDir = truncateToWidth(chalk.hex(this.colors.text)(basename(git.workDir))
|
|
6690
|
-
if (git.diffAdded === 0 && git.diffDeleted === 0 && git.filesCount === 0) return [workDir, dim(t("sidebar.git_clean"))];
|
|
6691
|
-
return [workDir, truncateToWidth(`${chalk.hex(this.colors.success)(`+${git.diffAdded}`)} ${chalk.hex(this.colors.error)(`−${git.diffDeleted}`)} ${dim(t("sidebar.git_files", { count: git.filesCount }))}`, width)];
|
|
7020
|
+
const workDir = truncateToWidth(`${indent}${chalk.hex(this.colors.text)(basename(git.workDir))}`, width);
|
|
7021
|
+
if (git.diffAdded === 0 && git.diffDeleted === 0 && git.filesCount === 0) return [workDir, dim(`${indent}${t("sidebar.git_clean")}`)];
|
|
7022
|
+
return [workDir, truncateToWidth(`${indent}${chalk.hex(this.colors.success)(`+${git.diffAdded}`)} ${chalk.hex(this.colors.error)(`−${git.diffDeleted}`)} ${dim(t("sidebar.git_files", { count: git.filesCount }))}`, width)];
|
|
6692
7023
|
}
|
|
6693
7024
|
};
|
|
6694
7025
|
const gitPanel = {
|
|
@@ -6702,35 +7033,6 @@ const gitPanel = {
|
|
|
6702
7033
|
}
|
|
6703
7034
|
};
|
|
6704
7035
|
//#endregion
|
|
6705
|
-
//#region src/tui/utils/display-width.ts
|
|
6706
|
-
/** Display-column width of a string, matching the terminal layout engine
|
|
6707
|
-
* (ANSI sequences stripped, CJK/wide chars counted as 2 columns). */
|
|
6708
|
-
function displayWidth(text) {
|
|
6709
|
-
return visibleWidth(text);
|
|
6710
|
-
}
|
|
6711
|
-
/** Pad `label` to `target` display columns with spaces (CJK-aware). */
|
|
6712
|
-
function padLabel(label, target) {
|
|
6713
|
-
return label + " ".repeat(Math.max(0, target - displayWidth(label)));
|
|
6714
|
-
}
|
|
6715
|
-
/** Pad `value` on the LEFT to `target` display columns (CJK-aware), which is
|
|
6716
|
-
* what right-aligning a numeric column means in terminal space. */
|
|
6717
|
-
function padValue(value, target) {
|
|
6718
|
-
return " ".repeat(Math.max(0, target - displayWidth(value))) + value;
|
|
6719
|
-
}
|
|
6720
|
-
function alignMetricRows(rows, opts = {}) {
|
|
6721
|
-
const gap = opts.gap ?? 2;
|
|
6722
|
-
const labels = [...rows.map(([label]) => label), ...opts.extraLabels ?? []];
|
|
6723
|
-
const labelCols = labels.length === 0 ? 0 : Math.max(...labels.map((label) => displayWidth(label)));
|
|
6724
|
-
const measured = rows.length === 0 ? 0 : Math.max(...rows.map(([, value]) => displayWidth(value)));
|
|
6725
|
-
const valueCols = opts.maxValueCols === void 0 ? measured : Math.min(measured, Math.max(0, opts.maxValueCols));
|
|
6726
|
-
const separator = " ".repeat(Math.max(0, gap));
|
|
6727
|
-
return {
|
|
6728
|
-
labelCols,
|
|
6729
|
-
valueCols,
|
|
6730
|
-
rows: rows.map(([label, value]) => [padLabel(label, labelCols) + separator, padValue(truncateToWidth(value, valueCols), valueCols)])
|
|
6731
|
-
};
|
|
6732
|
-
}
|
|
6733
|
-
//#endregion
|
|
6734
7036
|
//#region src/tui/components/sidebar/panels/session-panel.ts
|
|
6735
7037
|
function formatDuration(ms) {
|
|
6736
7038
|
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
@@ -6766,10 +7068,10 @@ function formatCompact(n) {
|
|
|
6766
7068
|
if (n >= 1e3) return `${Math.round(n / 1e3)}K`;
|
|
6767
7069
|
return String(n);
|
|
6768
7070
|
}
|
|
6769
|
-
/** True when
|
|
7071
|
+
/** True when the shared grid can show these values at full width: the label
|
|
7072
|
+
* column is fixed across panels, so only the value column decides the fit. */
|
|
6770
7073
|
function gridFits(rows, width) {
|
|
6771
|
-
|
|
6772
|
-
return grid.labelCols + 2 + grid.valueCols <= width;
|
|
7074
|
+
return 15 + Math.max(0, ...rows.map(([, value]) => displayWidth(value))) <= width;
|
|
6773
7075
|
}
|
|
6774
7076
|
/**
|
|
6775
7077
|
* Session summary, live-rendered each frame as a strict two-column table:
|
|
@@ -6806,10 +7108,11 @@ var SessionPanelContent = class {
|
|
|
6806
7108
|
];
|
|
6807
7109
|
const withTokens = (compact) => [...tokenRows.map(([name, n]) => [name, compact ? formatCompact(n) : formatCount$1(n)]), ...countRows];
|
|
6808
7110
|
const full = withTokens(false);
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
7111
|
+
return layoutSidebarGrid((gridFits(full, width) ? full : withTokens(true)).map(([name, reading]) => ({
|
|
7112
|
+
marker: "●",
|
|
7113
|
+
label: name,
|
|
7114
|
+
value: reading
|
|
7115
|
+
})), width).map((cell) => `${label(cell.marker)}${label(cell.label)}${" ".repeat(cell.gap)}${value(cell.value ?? "")}`);
|
|
6813
7116
|
}
|
|
6814
7117
|
};
|
|
6815
7118
|
const sessionPanel = {
|
|
@@ -6836,6 +7139,7 @@ function statusText(status) {
|
|
|
6836
7139
|
case "outputting": return t("sidebar.agent_outputting");
|
|
6837
7140
|
case "messaging": return t("sidebar.agent_messaging");
|
|
6838
7141
|
case "reworking": return t("sidebar.agent_reworking");
|
|
7142
|
+
case "requesting": return t("sidebar.agent_requesting");
|
|
6839
7143
|
}
|
|
6840
7144
|
}
|
|
6841
7145
|
/**
|
|
@@ -6854,17 +7158,22 @@ var AgentsPanelContent = class {
|
|
|
6854
7158
|
render(width) {
|
|
6855
7159
|
const agents = this.ctx.getData().agents;
|
|
6856
7160
|
if (agents === void 0 || agents.length === 0) return [];
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
7161
|
+
return layoutSidebarGrid(agents.map((slot) => ({
|
|
7162
|
+
marker: slot.status === "idle" ? "○" : "●",
|
|
7163
|
+
label: slot.type,
|
|
7164
|
+
value: statusText(slot.status) + (slot.count > 1 ? ` ×${slot.count}` : "")
|
|
7165
|
+
})), width).map((cell, index) => this.renderSlot(cell, agents[index], index, width));
|
|
6861
7166
|
}
|
|
6862
|
-
renderSlot(slot, index,
|
|
7167
|
+
renderSlot(cell, slot, index, width) {
|
|
6863
7168
|
const dim = (s) => chalk.hex(this.colors.textDim)(s);
|
|
6864
|
-
|
|
7169
|
+
const idle = slot.status === "idle";
|
|
6865
7170
|
const phase = (Date.now() % AGENT_GRADIENT_CYCLE_MS / AGENT_GRADIENT_CYCLE_MS + index * .125) % 1;
|
|
6866
7171
|
const active = slot.status === "working" || slot.status === "outputting" ? chalk.hex(lerpGradient(phase)).bold : void 0;
|
|
6867
|
-
|
|
7172
|
+
const marker = padLabel((active ?? chalk.hex(this.statusColor(slot.status)))(idle ? "○" : "●"), 2);
|
|
7173
|
+
const label = (idle ? dim : chalk.hex(this.colors.textStrong))(cell.label);
|
|
7174
|
+
const paint = active ?? (idle ? dim : slot.status === "requesting" ? chalk.hex(this.colors.warning) : chalk.hex(this.colors.text));
|
|
7175
|
+
const count = slot.count > 1 ? dim(` ×${slot.count}`) : "";
|
|
7176
|
+
return truncateToWidth(`${marker}${label}${" ".repeat(cell.gap)}${paint(statusText(slot.status))}${count}`, width);
|
|
6868
7177
|
}
|
|
6869
7178
|
statusColor(status) {
|
|
6870
7179
|
switch (status) {
|
|
@@ -6872,6 +7181,7 @@ var AgentsPanelContent = class {
|
|
|
6872
7181
|
case "outputting": return this.colors.primary;
|
|
6873
7182
|
case "messaging": return this.colors.accent;
|
|
6874
7183
|
case "reworking": return this.colors.warning;
|
|
7184
|
+
case "requesting": return this.colors.warning;
|
|
6875
7185
|
case "idle": return this.colors.textDim;
|
|
6876
7186
|
}
|
|
6877
7187
|
}
|
|
@@ -6929,26 +7239,48 @@ var GoalPanelContent = class {
|
|
|
6929
7239
|
const wallMs = goal.status === "active" ? goal.wallClockMs + Math.max(0, now - goal.wallClockBaseAt) : goal.wallClockMs;
|
|
6930
7240
|
const lines = [];
|
|
6931
7241
|
lines.push(this.renderBadge(goal.status, goal.judge, dim));
|
|
6932
|
-
lines.push(truncateToWidth(dim(goal.objective), width));
|
|
7242
|
+
lines.push(truncateToWidth(dim(`● ${goal.objective}`), width));
|
|
6933
7243
|
const rows = [
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
7244
|
+
{
|
|
7245
|
+
marker: "●",
|
|
7246
|
+
label: t("sidebar.goal_tokens"),
|
|
7247
|
+
value: formatCount(goal.tokensUsed)
|
|
7248
|
+
},
|
|
7249
|
+
{
|
|
7250
|
+
marker: "●",
|
|
7251
|
+
label: t("sidebar.goal_tokens_input"),
|
|
7252
|
+
value: goal.inputTokens === null ? "—" : formatCount(goal.inputTokens)
|
|
7253
|
+
},
|
|
7254
|
+
{
|
|
7255
|
+
marker: "●",
|
|
7256
|
+
label: t("sidebar.goal_tokens_output"),
|
|
7257
|
+
value: goal.outputTokens === null ? "—" : formatCount(goal.outputTokens)
|
|
7258
|
+
},
|
|
7259
|
+
{
|
|
7260
|
+
marker: "●",
|
|
7261
|
+
label: t("sidebar.goal_turns"),
|
|
7262
|
+
value: String(goal.turnsUsed)
|
|
7263
|
+
},
|
|
7264
|
+
{
|
|
7265
|
+
marker: "●",
|
|
7266
|
+
label: t("sidebar.goal_wall"),
|
|
7267
|
+
value: formatWallClock(wallMs)
|
|
7268
|
+
},
|
|
7269
|
+
{
|
|
7270
|
+
marker: "●",
|
|
7271
|
+
label: t("sidebar.goal_judge"),
|
|
7272
|
+
value: judgeText(goal.judge)
|
|
7273
|
+
}
|
|
6940
7274
|
];
|
|
6941
|
-
const
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
const
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
}
|
|
6950
|
-
for (const [label, value] of grid.rows) lines.push(dim(label) + text(value));
|
|
6951
|
-
if (hasCriterion) lines.push(dim(padLabel(criterionLabel, grid.labelCols) + " ".repeat(2)) + truncateToWidth(text(goal.completionCriterion), budget));
|
|
7275
|
+
const gridRows = goal.completionCriterion !== null ? [...rows, {
|
|
7276
|
+
marker: "●",
|
|
7277
|
+
label: t("sidebar.goal_criterion"),
|
|
7278
|
+
note: goal.completionCriterion ?? ""
|
|
7279
|
+
}] : rows;
|
|
7280
|
+
for (const cell of layoutSidebarGrid(gridRows, width)) {
|
|
7281
|
+
const reading = cell.value ?? cell.note ?? "";
|
|
7282
|
+
lines.push(`${dim(`${cell.marker}${cell.label}`)}${" ".repeat(cell.gap)}${text(reading)}`);
|
|
7283
|
+
}
|
|
6952
7284
|
return lines;
|
|
6953
7285
|
}
|
|
6954
7286
|
/** State badge: judging wins over everything; otherwise map the snapshot
|
|
@@ -6974,6 +7306,212 @@ const goalPanel = {
|
|
|
6974
7306
|
return new GoalPanelContent(ctx, ctx.colors);
|
|
6975
7307
|
}
|
|
6976
7308
|
};
|
|
7309
|
+
/** Per-request budget: long enough for a slow CDN, short enough to never hang. */
|
|
7310
|
+
const HUB_PROBE_TIMEOUT_MS = 1500;
|
|
7311
|
+
/** Row id for the model provider, whose latency is measured, never probed. */
|
|
7312
|
+
const HUB_MODEL_ROW_ID = "model";
|
|
7313
|
+
/** A measured provider reading older than this is dimmed: nothing was asked lately. */
|
|
7314
|
+
const HUB_MODEL_STALE_MS = 5 * 6e4;
|
|
7315
|
+
/** Default probe targets: the endpoints day-to-day development tends to need. */
|
|
7316
|
+
const HUB_ENDPOINTS = Object.freeze([
|
|
7317
|
+
{
|
|
7318
|
+
id: "github",
|
|
7319
|
+
url: "https://github.com/"
|
|
7320
|
+
},
|
|
7321
|
+
{
|
|
7322
|
+
id: "google",
|
|
7323
|
+
url: "https://www.google.com/"
|
|
7324
|
+
},
|
|
7325
|
+
{
|
|
7326
|
+
id: "x",
|
|
7327
|
+
url: "https://x.com/",
|
|
7328
|
+
label: "x (twitter)"
|
|
7329
|
+
},
|
|
7330
|
+
{
|
|
7331
|
+
id: "huggingface",
|
|
7332
|
+
url: "https://huggingface.co/"
|
|
7333
|
+
},
|
|
7334
|
+
{
|
|
7335
|
+
id: "alibaba",
|
|
7336
|
+
url: "https://www.aliyun.com/",
|
|
7337
|
+
label: "Alibaba"
|
|
7338
|
+
},
|
|
7339
|
+
{
|
|
7340
|
+
id: "tencent",
|
|
7341
|
+
url: "https://cloud.tencent.com/",
|
|
7342
|
+
label: "Tencent"
|
|
7343
|
+
}
|
|
7344
|
+
]);
|
|
7345
|
+
function defaultFetch(url, init) {
|
|
7346
|
+
return globalThis.fetch(url, init);
|
|
7347
|
+
}
|
|
7348
|
+
function createHubProbe(options = {}) {
|
|
7349
|
+
const endpoints = options.endpoints ?? HUB_ENDPOINTS;
|
|
7350
|
+
const now = options.now ?? Date.now;
|
|
7351
|
+
const fetchImpl = options.fetchImpl ?? defaultFetch;
|
|
7352
|
+
const sites = /* @__PURE__ */ new Map();
|
|
7353
|
+
for (const endpoint of endpoints) sites.set(endpoint.id, {
|
|
7354
|
+
ms: void 0,
|
|
7355
|
+
failures: 0
|
|
7356
|
+
});
|
|
7357
|
+
let roundAt;
|
|
7358
|
+
let inFlight = false;
|
|
7359
|
+
const runRound = async () => {
|
|
7360
|
+
const startedAt = now();
|
|
7361
|
+
const results = await Promise.allSettled(endpoints.map(async (endpoint) => {
|
|
7362
|
+
const response = await fetchImpl(endpoint.url, {
|
|
7363
|
+
method: "HEAD",
|
|
7364
|
+
redirect: "manual",
|
|
7365
|
+
signal: AbortSignal.timeout(HUB_PROBE_TIMEOUT_MS)
|
|
7366
|
+
});
|
|
7367
|
+
return {
|
|
7368
|
+
id: endpoint.id,
|
|
7369
|
+
ms: Math.max(0, now() - startedAt),
|
|
7370
|
+
status: response.status
|
|
7371
|
+
};
|
|
7372
|
+
}));
|
|
7373
|
+
for (const [index, result] of results.entries()) {
|
|
7374
|
+
const site = sites.get(endpoints[index].id);
|
|
7375
|
+
if (site === void 0) continue;
|
|
7376
|
+
if (result.status === "fulfilled") {
|
|
7377
|
+
site.ms = result.value.ms;
|
|
7378
|
+
site.failures = 0;
|
|
7379
|
+
} else {
|
|
7380
|
+
site.failures += 1;
|
|
7381
|
+
if (site.failures >= 2) site.ms = void 0;
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
roundAt = now();
|
|
7385
|
+
inFlight = false;
|
|
7386
|
+
};
|
|
7387
|
+
return {
|
|
7388
|
+
sampleIfStale: () => {
|
|
7389
|
+
if (inFlight) return;
|
|
7390
|
+
const at = now();
|
|
7391
|
+
if (roundAt !== void 0 && at - roundAt < 3e4) return;
|
|
7392
|
+
inFlight = true;
|
|
7393
|
+
runRound().catch(() => {
|
|
7394
|
+
inFlight = false;
|
|
7395
|
+
});
|
|
7396
|
+
},
|
|
7397
|
+
snapshot: () => {
|
|
7398
|
+
const at = now();
|
|
7399
|
+
const stale = roundAt === void 0 || at - roundAt >= 3e4 * 2;
|
|
7400
|
+
const samples = [];
|
|
7401
|
+
for (const endpoint of endpoints) {
|
|
7402
|
+
const site = sites.get(endpoint.id);
|
|
7403
|
+
samples.push({
|
|
7404
|
+
id: endpoint.id,
|
|
7405
|
+
label: endpoint.label ?? endpoint.id,
|
|
7406
|
+
ms: site.ms,
|
|
7407
|
+
tone: toneFor(site, stale)
|
|
7408
|
+
});
|
|
7409
|
+
}
|
|
7410
|
+
return {
|
|
7411
|
+
samples,
|
|
7412
|
+
roundAt,
|
|
7413
|
+
stale,
|
|
7414
|
+
pending: inFlight
|
|
7415
|
+
};
|
|
7416
|
+
}
|
|
7417
|
+
};
|
|
7418
|
+
}
|
|
7419
|
+
function toneFor(site, stale) {
|
|
7420
|
+
if (stale) return "dim";
|
|
7421
|
+
if (site.failures >= 2) return "down";
|
|
7422
|
+
return hubLatencyTone(site.ms, site.failures > 0);
|
|
7423
|
+
}
|
|
7424
|
+
/**
|
|
7425
|
+
* Merge the measured provider reading with the probe samples into Hub rows.
|
|
7426
|
+
* The provider row always leads (it answers "is the model I'm using reachable"),
|
|
7427
|
+
* and it staleness-checks against its own budget: a long idle stretch means
|
|
7428
|
+
* nothing was asked, not that the network died.
|
|
7429
|
+
*/
|
|
7430
|
+
function buildHubSamples(snapshot, provider, now = Date.now()) {
|
|
7431
|
+
const ageMs = provider.sampledAt === void 0 ? 0 : now - provider.sampledAt;
|
|
7432
|
+
return [{
|
|
7433
|
+
id: HUB_MODEL_ROW_ID,
|
|
7434
|
+
ms: provider.ms,
|
|
7435
|
+
tone: hubLatencyTone(provider.ms, ageMs >= HUB_MODEL_STALE_MS)
|
|
7436
|
+
}, ...snapshot.samples];
|
|
7437
|
+
}
|
|
7438
|
+
/** Shared tone rule for every Hub row (probe sites and the measured provider). */
|
|
7439
|
+
function hubLatencyTone(ms, stale = false) {
|
|
7440
|
+
if (ms === void 0 || stale) return "dim";
|
|
7441
|
+
return ms <= 100 ? "ok" : "warn";
|
|
7442
|
+
}
|
|
7443
|
+
/**
|
|
7444
|
+
* Compact latency reading: whole milliseconds below a second, one-decimal
|
|
7445
|
+
* seconds above — a probe that never answers must never read as `65400ms`.
|
|
7446
|
+
*/
|
|
7447
|
+
function formatHubLatency(ms) {
|
|
7448
|
+
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
7449
|
+
return `${(ms / 1e3).toFixed(1)}s`;
|
|
7450
|
+
}
|
|
7451
|
+
//#endregion
|
|
7452
|
+
//#region src/tui/components/sidebar/panels/hub-panel.ts
|
|
7453
|
+
/** Pending marker: three middle dots (Latin-1, so terminal width is stable). */
|
|
7454
|
+
const PENDING_MARK = "···";
|
|
7455
|
+
/** No reading at all — a site that stopped answering, or nothing measured yet. */
|
|
7456
|
+
const EMPTY_MARK = "--";
|
|
7457
|
+
/**
|
|
7458
|
+
* Hub panel: network health at a glance. One row per reading — the measured
|
|
7459
|
+
* model-provider response time first, then a probe round trip for each
|
|
7460
|
+
* well-known endpoint.
|
|
7461
|
+
*
|
|
7462
|
+
* Values share one right edge through the same two-column engine the Session
|
|
7463
|
+
* panel uses, so rows never jitter as numbers change width, and only the value
|
|
7464
|
+
* carries colour (green ≤100ms, amber above it, red when a site stopped
|
|
7465
|
+
* answering). The panel is a pure view: probing happens in the data provider,
|
|
7466
|
+
* which is what keeps a slow endpoint from ever touching a frame.
|
|
7467
|
+
*/
|
|
7468
|
+
var HubPanelContent = class {
|
|
7469
|
+
ctx;
|
|
7470
|
+
colors;
|
|
7471
|
+
constructor(ctx, colors) {
|
|
7472
|
+
this.ctx = ctx;
|
|
7473
|
+
this.colors = colors;
|
|
7474
|
+
}
|
|
7475
|
+
invalidate() {}
|
|
7476
|
+
render(width) {
|
|
7477
|
+
const hub = this.ctx.getData().hub;
|
|
7478
|
+
if (hub === void 0 || hub.samples.length === 0) return [];
|
|
7479
|
+
return layoutSidebarGrid(hub.samples.map((sample) => ({
|
|
7480
|
+
marker: "●",
|
|
7481
|
+
label: this.labelOf(sample),
|
|
7482
|
+
value: this.reading(sample, hub)
|
|
7483
|
+
})), width).map((cell, index) => {
|
|
7484
|
+
const tone = hub.samples[index]?.tone ?? "dim";
|
|
7485
|
+
return `${chalk.hex(this.colors.textDim)(`${cell.marker}${cell.label}`)}${" ".repeat(cell.gap)}${chalk.hex(this.toneColor(tone))(cell.value ?? "")}`;
|
|
7486
|
+
});
|
|
7487
|
+
}
|
|
7488
|
+
labelOf(sample) {
|
|
7489
|
+
return sample.id === "model" ? t("sidebar.hub_model") : sample.label ?? sample.id;
|
|
7490
|
+
}
|
|
7491
|
+
reading(sample, hub) {
|
|
7492
|
+
if (sample.ms !== void 0) return formatHubLatency(sample.ms);
|
|
7493
|
+
if (hub.pending && sample.tone === "dim" && sample.id !== "model") return PENDING_MARK;
|
|
7494
|
+
return EMPTY_MARK;
|
|
7495
|
+
}
|
|
7496
|
+
toneColor(tone) {
|
|
7497
|
+
switch (tone) {
|
|
7498
|
+
case "ok": return this.colors.success;
|
|
7499
|
+
case "warn": return this.colors.warning;
|
|
7500
|
+
case "down": return this.colors.error;
|
|
7501
|
+
case "dim": return this.colors.textDim;
|
|
7502
|
+
}
|
|
7503
|
+
}
|
|
7504
|
+
};
|
|
7505
|
+
const hubPanel = {
|
|
7506
|
+
id: "hub",
|
|
7507
|
+
get title() {
|
|
7508
|
+
return t("sidebar.hub");
|
|
7509
|
+
},
|
|
7510
|
+
width: 30,
|
|
7511
|
+
build(ctx) {
|
|
7512
|
+
return new HubPanelContent(ctx, ctx.colors);
|
|
7513
|
+
}
|
|
7514
|
+
};
|
|
6977
7515
|
//#endregion
|
|
6978
7516
|
//#region src/utils/git/git-status.ts
|
|
6979
7517
|
/**
|
|
@@ -7533,6 +8071,9 @@ var LifecycleController = class LifecycleController {
|
|
|
7533
8071
|
/** Sidebar-only git cache (the footer owns its own). Rebuilt when the
|
|
7534
8072
|
* workDir changes; `getStatus()` is TTL-cached so polling is cheap. */
|
|
7535
8073
|
sidebarGitCache;
|
|
8074
|
+
/** Sidebar Hub probe: read-through TTL, no timer — probing happens because a
|
|
8075
|
+
* frame asked, so a closed sidebar produces no traffic. */
|
|
8076
|
+
hubProbe = createHubProbe();
|
|
7536
8077
|
sidebarGitCacheWorkDir = "";
|
|
7537
8078
|
/** Session start time for the sidebar uptime counter. Reset on session
|
|
7538
8079
|
* switch so a resumed old session doesn't show the TUI process age. */
|
|
@@ -7729,6 +8270,7 @@ var LifecycleController = class LifecycleController {
|
|
|
7729
8270
|
if (!m.allPanels.some((p) => p.id === "session")) m.register(sessionPanel);
|
|
7730
8271
|
if (!m.allPanels.some((p) => p.id === "agents")) m.register(agentsPanel);
|
|
7731
8272
|
if (!m.allPanels.some((p) => p.id === "goal")) m.register(goalPanel);
|
|
8273
|
+
if (!m.allPanels.some((p) => p.id === "hub")) m.register(hubPanel);
|
|
7732
8274
|
m.setDataProvider(() => this.buildSidebarData());
|
|
7733
8275
|
}
|
|
7734
8276
|
const sidebar = this.host.state.sidebarPane;
|
|
@@ -7770,9 +8312,25 @@ var LifecycleController = class LifecycleController {
|
|
|
7770
8312
|
git: this.readSidebarGit(appState.workDir),
|
|
7771
8313
|
sessionStats: this.readSidebarSessionStats(appState),
|
|
7772
8314
|
agents: this.host.sessionEventHandler.getSubagentSlots(),
|
|
8315
|
+
hub: this.readSidebarHub(),
|
|
7773
8316
|
goal: this.readSidebarGoal(appState)
|
|
7774
8317
|
};
|
|
7775
8318
|
}
|
|
8319
|
+
/**
|
|
8320
|
+
* Hub block. Probe rounds are kicked off from the render path only (the
|
|
8321
|
+
* sidebar container stops rendering while closed), and the round is always
|
|
8322
|
+
* fire-and-forget — a dead endpoint can never hold up a frame. The model row
|
|
8323
|
+
* is different: it reads the first-token latency of real requests, so it is
|
|
8324
|
+
* measured rather than probed, and it goes stale on its own budget.
|
|
8325
|
+
*/
|
|
8326
|
+
readSidebarHub() {
|
|
8327
|
+
if (this.host.state.sidebarManager?.isOpen === true) this.hubProbe.sampleIfStale();
|
|
8328
|
+
const snapshot = this.hubProbe.snapshot();
|
|
8329
|
+
return {
|
|
8330
|
+
samples: buildHubSamples(snapshot, this.host.sessionEventHandler.getProviderLatency()),
|
|
8331
|
+
pending: snapshot.pending
|
|
8332
|
+
};
|
|
8333
|
+
}
|
|
7776
8334
|
readSidebarGit(workDir) {
|
|
7777
8335
|
if (this.sidebarGitCache === void 0 || this.sidebarGitCacheWorkDir !== workDir) {
|
|
7778
8336
|
this.sidebarGitCache = createGitStatusCache(workDir);
|
|
@@ -13632,7 +14190,7 @@ var ScreamTUI = class {
|
|
|
13632
14190
|
const card = new OnboardingCardComponent({
|
|
13633
14191
|
colors: this.state.theme.colors,
|
|
13634
14192
|
onConfigure: () => {
|
|
13635
|
-
import("./dispatch-
|
|
14193
|
+
import("./dispatch-5S52okPl.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
|
|
13636
14194
|
this.restoreEditor();
|
|
13637
14195
|
onDone?.();
|
|
13638
14196
|
});
|
|
@@ -14459,15 +15017,13 @@ async function runChannelSetup() {
|
|
|
14459
15017
|
//#endregion
|
|
14460
15018
|
//#region src/cli/run-stream-json.ts
|
|
14461
15019
|
/**
|
|
14462
|
-
*
|
|
14463
|
-
*
|
|
14464
|
-
* Reads stdin in Claude Code stream-json dialect, processes messages through
|
|
14465
|
-
* the ScreamCode agent, and writes stdout in Claude Code dialect. This lets
|
|
14466
|
-
* cc-connect (and any tool that speaks the Claude Code stdio protocol) use
|
|
14467
|
-
* ScreamCode as a drop-in agent backend.
|
|
15020
|
+
* Stream-json stdio adapter (cc-connect compat).
|
|
14468
15021
|
*
|
|
14469
|
-
*
|
|
14470
|
-
*
|
|
15022
|
+
* Reads stdin as line-based stream-json messages, processes them through the
|
|
15023
|
+
* ScreamCode agent, and writes stdout as stream-json events. This is the
|
|
15024
|
+
* integration surface cc-connect drives when it spawns `scream stream-json`,
|
|
15025
|
+
* and any tool that speaks the same stdio protocol can use ScreamCode as an
|
|
15026
|
+
* agent backend through it.
|
|
14471
15027
|
*/
|
|
14472
15028
|
function buildStreamJsonRuntimePrompt(input) {
|
|
14473
15029
|
const appendParts = [
|
|
@@ -14480,7 +15036,7 @@ function buildStreamJsonRuntimePrompt(input) {
|
|
|
14480
15036
|
append: appendParts.join("\n\n")
|
|
14481
15037
|
};
|
|
14482
15038
|
}
|
|
14483
|
-
var
|
|
15039
|
+
var StreamJsonWriter = class {
|
|
14484
15040
|
writeLine;
|
|
14485
15041
|
sessionId = "";
|
|
14486
15042
|
msgCounter = 0;
|
|
@@ -14611,7 +15167,7 @@ var ClaudeStreamJsonWriter = class {
|
|
|
14611
15167
|
output: 0
|
|
14612
15168
|
};
|
|
14613
15169
|
}
|
|
14614
|
-
/** Emit a resume hint as a meta message
|
|
15170
|
+
/** Emit a resume hint as a stream-json meta message. */
|
|
14615
15171
|
emitResumeHint(sessionId) {
|
|
14616
15172
|
this.writeJson({
|
|
14617
15173
|
role: "meta",
|
|
@@ -14686,7 +15242,7 @@ async function* readStdinMessages() {
|
|
|
14686
15242
|
else log.debug("stream-json: ignoring unknown stdin message type", { type: msg.type });
|
|
14687
15243
|
}
|
|
14688
15244
|
}
|
|
14689
|
-
/** Extract plain text from a
|
|
15245
|
+
/** Extract plain text from a stream-json user message. */
|
|
14690
15246
|
function extractUserText(msg) {
|
|
14691
15247
|
const content = msg.message.content;
|
|
14692
15248
|
if (typeof content === "string") return content;
|
|
@@ -14727,7 +15283,7 @@ function mapCcConnectMode(mode) {
|
|
|
14727
15283
|
}
|
|
14728
15284
|
/**
|
|
14729
15285
|
* Install the stdout EPIPE guard and return the line writer handed to
|
|
14730
|
-
* {@link
|
|
15286
|
+
* {@link StreamJsonWriter}.
|
|
14731
15287
|
*
|
|
14732
15288
|
* cc-connect closes our stdout when it kills/restarts the agent (idle
|
|
14733
15289
|
* timeout, /restart, config change). Writing after that raises EPIPE as an
|
|
@@ -14783,7 +15339,7 @@ async function runStreamJson(opts) {
|
|
|
14783
15339
|
}
|
|
14784
15340
|
const streamSubagentModels = streamTuiConfig.subagentModels;
|
|
14785
15341
|
harness.setSubagentModelBindings(() => streamSubagentModels);
|
|
14786
|
-
const writer = new
|
|
15342
|
+
const writer = new StreamJsonWriter(installStdoutEpipeGuard());
|
|
14787
15343
|
let session;
|
|
14788
15344
|
let currentSessionId;
|
|
14789
15345
|
let sessionKey = "cc-connect-main";
|
|
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
|
3
3
|
import { dirname as __cjsShimDirname } from 'node:path';
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
|
-
import { Jt as handleConnectCommand } from "./dispatch-
|
|
6
|
+
import { Jt as handleConnectCommand } from "./dispatch-CsrteWRw.mjs";
|
|
7
7
|
export { handleConnectCommand };
|
|
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
|
|
|
6
6
|
import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-D90kvbyJ.mjs";
|
|
7
7
|
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$1, x as dirname$2, y as KnowledgeStore } from "./src-tDEINaMV.mjs";
|
|
8
8
|
import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
|
|
9
|
-
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-
|
|
9
|
+
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-BVQaKy1Z.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
12
12
|
import * as fs$1 from "node:fs/promises";
|
|
@@ -128474,6 +128474,7 @@ var ApiKeyInputDialogComponent = class extends Container {
|
|
|
128474
128474
|
const STATUS_BULLET = "■ ";
|
|
128475
128475
|
const USER_MESSAGE_BULLET = "■ ";
|
|
128476
128476
|
const FAILURE_MARK = "✗ ";
|
|
128477
|
+
const INTERJECTION_BULLET = "▸ ";
|
|
128477
128478
|
//#endregion
|
|
128478
128479
|
//#region src/tui/utils/printable-key.ts
|
|
128479
128480
|
/**
|
|
@@ -133596,7 +133597,7 @@ async function guidedGoalSetup(host) {
|
|
|
133596
133597
|
host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
|
|
133597
133598
|
return;
|
|
133598
133599
|
}
|
|
133599
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
133600
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-B0PROY20.mjs");
|
|
133600
133601
|
const initialDesc = await promptText(host, TextInputDialogComponent, {
|
|
133601
133602
|
title: t("goal.setup_title_initial"),
|
|
133602
133603
|
subtitle: t("goal.setup_desc_hint"),
|
|
@@ -133617,7 +133618,7 @@ async function guidedGoalSetup(host) {
|
|
|
133617
133618
|
await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
|
|
133618
133619
|
}
|
|
133619
133620
|
async function showGoalConfigWizard(host, session, objective, replace) {
|
|
133620
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
133621
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-B0PROY20.mjs");
|
|
133621
133622
|
const turnInput = await promptNumber(host, TextInputDialogComponent, {
|
|
133622
133623
|
title: t("goal.wizard_title", { objective }),
|
|
133623
133624
|
subtitle: t("goal.budget_turns_hint"),
|
|
@@ -139531,7 +139532,7 @@ function detectScreamPath() {
|
|
|
139531
139532
|
* shell, so an unquoted path containing spaces would be split apart.
|
|
139532
139533
|
*/
|
|
139533
139534
|
function quoteShellPath(path) {
|
|
139534
|
-
return `"${path.replaceAll(
|
|
139535
|
+
return `"${path.replaceAll("\"", "\\\"")}"`;
|
|
139535
139536
|
}
|
|
139536
139537
|
/** Parse every configured platform type from config.toml content. */
|
|
139537
139538
|
function parseConfiguredTypes(content) {
|
|
@@ -139582,7 +139583,7 @@ function isVersionAtLeast(version, min) {
|
|
|
139582
139583
|
*/
|
|
139583
139584
|
function tomlString(value) {
|
|
139584
139585
|
if (!value.includes("'")) return `'${value}'`;
|
|
139585
|
-
return `"${value.replaceAll(
|
|
139586
|
+
return `"${value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
|
|
139586
139587
|
}
|
|
139587
139588
|
function generateConfig(platform) {
|
|
139588
139589
|
const dir = dirname$1(CONFIG_PATH);
|
|
@@ -143547,4 +143548,4 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
143547
143548
|
}
|
|
143548
143549
|
}
|
|
143549
143550
|
//#endregion
|
|
143550
|
-
export { handleTitleCommand as $,
|
|
143551
|
+
export { handleTitleCommand as $, isOrphanedToolCallError as $n, appendStreamingArgsPreview as $t, renderDiffLinesClustered as A, saveTuiConfig as An, DISABLE_TERMINAL_THEME_REPORTING as At, BackgroundAgentStatusComponent as B, DEFAULT_CATALOG_URL as Bn, TERMINAL_THEME_DARK as Bt, handleRevokeCommand as C, isEmbeddingModelCached as Cn, createMarkdownTheme as Ct, toggleEmptySessionHint as D, TuiConfigParseError as Dn, detectTerminalTheme as Dt, isTurnElapsedEnabled as E, PULSE_WAVE_FRAMES as En, getColorPalette as Et, estimateTokens as F, detectInstallSource as Fn, OSC11_RESPONSE_PREFIX as Ft, getBreathingFrame as G, appendSessionIndexEntry as Gn, lerpGradient as Gt, AgentGroupComponent as H, saveCatalogCache as Hn, isBusy as Ht, getSharedSpeedTracker as I, CLI_COMMAND_NAME as In, OSC11_RESPONSE_PREFIX_NO_ESC as It, refineGoal as J, MemoryMemoStore as Jn, handleConnectCommand as Jt, resetBreathingClock as K, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as Kn, handleTraceCommand as Kt, SkillActivationComponent as L, CLI_UI_MODE as Ln, QUERY_TERMINAL_THEME as Lt, langFromPath as M, getDataDir as Mn, ENABLE_TERMINAL_THEME_REPORTING as Mt, CachedContainer as N, getInputHistoryFile as Nn, OSC11_QUERY as Nt, ToolCallComponent as O, TuiLikePreferencesSchema as On, parseOsc11BackgroundTheme as Ot, ThinkingComponent as P, getLogDir as Pn, OSC11_RESPONSE as Pt, handleInitCommand as Q, isScreamError as Qn, STATUS_BULLET as Qt, ReadGroupComponent as R, CLI_USER_AGENT_PRODUCT as Rn, TERMINAL_FOCUS_IN as Rt, getDaemonInstructions as S, getKnowledgeStore as Sn, createEditorTheme as St, isEmptySessionHintDismissed as T, PIXEL_PULSE_FRAMES as Tn, contrastTextHex as Tt, WelcomeComponent as U, ScreamHarness as Un, isStreaming as Ut, AssistantMessageComponent as V, fetchCatalog as Vn, TERMINAL_THEME_LIGHT as Vt, BREATHE_CYCLE_MS as W, encodeWorkDirKey as Wn, FooterComponent as Wt, handleExportMdCommand as X, log as Xn, printableChar as Xt, handleExportDebugZipCommand as Y, flushDiagnosticLogs as Yn, handleLogoutCommand as Yt, handleForkCommand as Z, resolveGlobalLogPath as Zn, INTERJECTION_BULLET as Zt, refreshUpdateCache as _, buildSkillSlashCommands as _n, supportsBalance as _t, handleExtensionCommand as a, stringValue as an, handleCompactCommand as at, readJsonlFile as b, isExperimentalFlagEnabled as bn, showUsage as bt, hasDispose as c, EMPTY_SESSION_HINT_URL as cn, handleModelCommand as ct, formatMemoryMemoForInjection as d, SESSION_TIPS as dn, handleWolfpackCommand as dt, argsRecord as en, ErrorCodes as er, toTerminalHyperlink as et, handleMemoryCommand as f, TIP_ROTATION_INTERVAL_MS as fn, handleYoloCommand as ft, selectUpdateTarget as g, getNoActiveSessionMessage as gn, refreshProviderBalance as gt, handleUpdateCommand as h, getLlmNotSetMessage as hn, showSettingsSelector as ht, buildRoleAdditionalText as i, serializeToolResultOutput as in, handleBotCommand as it, highlightLines as j, detectShellEnvironment as jn, ENABLE_TERMINAL_FOCUS_REPORTING as jt, renderDiffLines as k, loadTuiConfig as kn, DISABLE_TERMINAL_FOCUS_REPORTING as kt, isPlanExpandable as l, EXIT_CONFIRM_WINDOW_MS as ln, handlePlanCommand as lt, handleMcpCommand as m, getCtrlDHint as mn, showPermissionPicker as mt, clearEvalPanelState as n, isTodoItemShape as nn, getModelCycleLevel as nt, handleSkillCommand as o, truncateErrorMessage as on, handleEditorCommand as ot, handleChannelCommand as p, getCtrlCHint as pn, showModelPicker as pt, clearGoalState as q, resolveScreamHome as qn, handleSearchCommand as qt, openUrl as r, parseStreamingArgs as rn, handleAutoCommand as rt, disposeChildren as s, CHARS_PER_TOKEN as sn, handleFusionPlanCommand as st, dispatchInput as t, formatErrorMessage as tn, SCREAM_ERROR_INFO as tr, changeThinkingLevel as tt, MoonLoader as u, MAIN_AGENT_ID$1 as un, handleThemeCommand as ut, readUpdateCache as v, BUILTIN_SLASH_COMMANDS as vn, clearInfoPanelState as vt, UserMessageComponent as w, startManualEmbeddingDownload as wn, createThemeStyles as wt, handleCcCommand as x, setExperimentalFlags as xn, resolveThemeSync as xt, appendJsonlLine as y, sortSlashCommands as yn, showStatusReport as yt, parseReadGroupOutput as z, PRODUCT_NAME as zn, TERMINAL_FOCUS_OUT as zt };
|
package/dist/main.mjs
CHANGED
|
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
|
|
|
6
6
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
7
7
|
//#region src/main.ts
|
|
8
8
|
try {
|
|
9
|
-
(await import("./app-
|
|
9
|
+
(await import("./app-N_PRvDIk.mjs")).main();
|
|
10
10
|
} catch (error) {
|
|
11
11
|
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
12
12
|
process.exit(1);
|
|
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
|
3
3
|
import { dirname as __cjsShimDirname } from 'node:path';
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
|
-
import { t as TextInputDialogComponent } from "./text-input-dialog-
|
|
6
|
+
import { t as TextInputDialogComponent } from "./text-input-dialog-BVQaKy1Z.mjs";
|
|
7
7
|
export { TextInputDialogComponent };
|
|
@@ -228,6 +228,9 @@ const dictionaries = {
|
|
|
228
228
|
"sidebar.agent_outputting": "输出中",
|
|
229
229
|
"sidebar.agent_messaging": "交谈中",
|
|
230
230
|
"sidebar.agent_reworking": "返工中",
|
|
231
|
+
"sidebar.agent_requesting": "求助中",
|
|
232
|
+
"sidebar.hub": "网络 Hub",
|
|
233
|
+
"sidebar.hub_model": "模型",
|
|
231
234
|
"sidebar.goal_idle": "空闲中",
|
|
232
235
|
"sidebar.goal_active": "进行中",
|
|
233
236
|
"sidebar.goal_completed": "已完成",
|
|
@@ -966,6 +969,15 @@ const dictionaries = {
|
|
|
966
969
|
"transcript.tool_name": "工具 {name}:",
|
|
967
970
|
"transcript.activated_skill": "已激活插件:{name}",
|
|
968
971
|
"transcript.more_history": "↑ 还有 {count} 条历史消息",
|
|
972
|
+
"transcript.child_request": "{name} 向主 Agent 求助({type})",
|
|
973
|
+
"transcript.child_request_anon": "子代理向主 Agent 求助({type})",
|
|
974
|
+
"transcript.child_request_type_info": "补充信息",
|
|
975
|
+
"transcript.child_request_type_handoff": "转交工作",
|
|
976
|
+
"transcript.child_request_type_escalate": "上报决策",
|
|
977
|
+
"transcript.child_request_needs": "需要 {value}",
|
|
978
|
+
"transcript.child_request_artifacts": "产出 {value}",
|
|
979
|
+
"transcript.child_request_evidence": "证据 {value}",
|
|
980
|
+
"transcript.child_request_missing": "缺项 {value}",
|
|
969
981
|
"compaction.done": "压缩完成",
|
|
970
982
|
"compaction.canceled": "压缩已取消",
|
|
971
983
|
"compaction.in_progress": "正在压缩上下文...",
|
|
@@ -1400,6 +1412,9 @@ const dictionaries = {
|
|
|
1400
1412
|
"sidebar.agent_outputting": "outputting",
|
|
1401
1413
|
"sidebar.agent_messaging": "messaging",
|
|
1402
1414
|
"sidebar.agent_reworking": "reworking",
|
|
1415
|
+
"sidebar.agent_requesting": "asking",
|
|
1416
|
+
"sidebar.hub": "Network Hub",
|
|
1417
|
+
"sidebar.hub_model": "model",
|
|
1403
1418
|
"sidebar.goal_idle": "no goal",
|
|
1404
1419
|
"sidebar.goal_active": "active",
|
|
1405
1420
|
"sidebar.goal_completed": "completed",
|
|
@@ -2138,6 +2153,15 @@ const dictionaries = {
|
|
|
2138
2153
|
"transcript.tool_name": "Tool {name}: ",
|
|
2139
2154
|
"transcript.activated_skill": "Activated plugin: {name}",
|
|
2140
2155
|
"transcript.more_history": "↑ {count} more history message(s)",
|
|
2156
|
+
"transcript.child_request": "{name} is asking the parent agent ({type})",
|
|
2157
|
+
"transcript.child_request_anon": "A subagent is asking the parent agent ({type})",
|
|
2158
|
+
"transcript.child_request_type_info": "info",
|
|
2159
|
+
"transcript.child_request_type_handoff": "handoff",
|
|
2160
|
+
"transcript.child_request_type_escalate": "escalate",
|
|
2161
|
+
"transcript.child_request_needs": "needs {value}",
|
|
2162
|
+
"transcript.child_request_artifacts": "artifacts {value}",
|
|
2163
|
+
"transcript.child_request_evidence": "evidence {value}",
|
|
2164
|
+
"transcript.child_request_missing": "missing {value}",
|
|
2141
2165
|
"compaction.done": "Compaction complete",
|
|
2142
2166
|
"compaction.canceled": "Compaction cancelled",
|
|
2143
2167
|
"compaction.in_progress": "Compacting context...",
|