scream-code 0.17.9 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-DiPC-zEu.mjs → app-BQW2gDgm.mjs} +569 -302
- package/dist/{dispatch-DoaBG--u.mjs → dispatch-Byc9hdXF.mjs} +1 -1
- package/dist/{dispatch-BGYq0dnV.mjs → dispatch-FYZpdI9u.mjs} +1763 -566
- package/dist/main.mjs +1 -1
- package/dist/public/assets/{index-BTL8OnOo.js → index-DOnRKkt2.js} +36 -36
- package/dist/public/assets/index-DOnRKkt2.js.map +1 -0
- package/dist/public/assets/index-m8QsxSTy.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/{text-input-dialog-BlSqBYxO.mjs → text-input-dialog-Bu8GPguU.mjs} +22 -10
- package/dist/{text-input-dialog-HfsxLcQf.mjs → text-input-dialog-b-TqTP2z.mjs} +1 -1
- package/package.json +3 -3
- package/dist/public/assets/index-B1g39y1G.css +0 -1
- package/dist/public/assets/index-BTL8OnOo.js.map +0 -1
|
@@ -5,8 +5,8 @@ const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
6
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
7
7
|
import { A as sharedMemoStore } from "./src-C7C9e5Ds.mjs";
|
|
8
|
-
import { $n as encodeWorkDirKey, $t as lerpGradient, A as highlightLines, An as embeddingFailureDetail, At as isTurnElapsedEnabled, B as ActivityGroupComponent, Bn as detectShellEnvironment, Bt as ENABLE_TERMINAL_THEME_REPORTING, Cn as getLlmNotSetMessage, Ct as clearInfoPanelState, D as ToolCallComponent, Dn as sortSlashCommands, Dt as createEditorTheme, E as UserMessageComponent, En as BUILTIN_SLASH_COMMANDS, Et as resolveThemeSync, F as StatusMessageComponent, Fn as PULSE_WAVE_FRAMES, Ft as detectTerminalTheme, G as AgentGroupComponent, Gn as CLI_COMMAND_NAME, Gt as QUERY_TERMINAL_THEME, H as estimateTokens, Hn as getInputHistoryFile, Ht as OSC11_RESPONSE, I as replaceTabs, In as TuiConfigParseError, It as parseOsc11BackgroundTheme, J as getBreathingFrame, Jn as PRODUCT_NAME, Jt as TERMINAL_THEME_DARK, K as WelcomeComponent, Kn as CLI_UI_MODE, Kt as TERMINAL_FOCUS_IN, L as sanitizeShellOutput, Ln as TuiLikePreferencesSchema, Lt as DISABLE_TERMINAL_FOCUS_REPORTING, M as CachedContainer, Mn as hasEmbeddingModelCache, Mt as createThemeStyles, N as ThinkingComponent, Nn as startManualEmbeddingDownload, Nt as contrastTextHex, O as renderDiffLines, On as isExperimentalFlagEnabled, Ot as createMarkdownTheme, P as NoticeMessageComponent, Pn as PIXEL_PULSE_FRAMES, Pt as getColorPalette, Q as refineGoal, Qn as ScreamHarness, Qt as FooterComponent, R as SkillActivationComponent, Rn as loadTuiConfig, Rt as DISABLE_TERMINAL_THEME_REPORTING, S as readJsonlFile, Sn as getCtrlDHint, St as supportsBalance, Tn as buildSkillSlashCommands, U as getSharedSpeedTracker, Un as getLogDir, Ut as OSC11_RESPONSE_PREFIX, V as charsForTokenBudget, Vn as getDataDir, Vt as OSC11_QUERY, W as BackgroundAgentStatusComponent, Wn as detectInstallSource, Wt as OSC11_RESPONSE_PREFIX_NO_ESC, X as CompactionComponent, Xn as fetchCatalog, Xt as isBusy, Y as resetBreathingClock, Yn as DEFAULT_CATALOG_URL, Yt as TERMINAL_THEME_LIGHT, Z as clearGoalState, Zn as saveCatalogCache, Zt as isStreaming, _n as EXIT_CONFIRM_WINDOW_MS, a as syncDiagramCapabilityPrompt, an as printableChar, ar as isRealUserPrompt, at as toTerminalHyperlink, b as readUpdateCache, bn as TIP_ROTATION_INTERVAL_MS, c as disposeChildren, cn as appendStreamingArgsPreview, cr as flushDiagnosticLogs, d as isPlanExpandable, dn as isTodoItemShape, dr as isScreamError, en as SubagentSlots, er as normalizeWorkDir, f as MoonLoader, fn as parseStreamingArgs, fr as isOrphanedToolCallError, ft as handleFusionPlanCommand, gn as EMPTY_SESSION_HINT_URL, hn as truncateErrorMessage, i as buildRoleAdditionalText, it as isInternalMessage, j as langFromPath, jn as getKnowledgeStore, jt as toggleEmptySessionHint, k as renderDiffLinesClustered, kn as setExperimentalFlags, kt as isEmptySessionHintDismissed, l as hasDispose, ln as argsRecord, lr as log, mn as stringValue, mr as SCREAM_ERROR_INFO, mt as handlePlanCommand, n as clearEvalPanelState, nr as readSessionIndex, on as STATUS_BULLET, or as parseRlmMaxDepthArg, ot as changeThinkingLevel, p as formatMemoryMemoForInjection, pn as serializeToolResultOutput, pr as ErrorCodes, q as BREATHE_CYCLE_MS, qn as CLI_USER_AGENT_PRODUCT, qt as TERMINAL_FOCUS_OUT, r as openUrl, rr as removeSessionIndexEntry, sn as appendSessionHeaderHint, sr as resolveScreamHome, st as getModelCycleLevel, t as dispatchInput, tr as appendSessionIndexEntry, u as isExpandable, un as formatErrorMessage, ur as resolveGlobalLogPath, v as selectUpdateTarget, vn as MAIN_AGENT_ID, w as getDaemonInstructions, wn as getNoActiveSessionMessage, x as appendJsonlLine, xn as getCtrlCHint, xt as refreshProviderBalance, y as refreshUpdateCache, yn as SESSION_TIPS, z as AssistantMessageComponent, zn as saveTuiConfig, zt as ENABLE_TERMINAL_FOCUS_REPORTING } from "./dispatch-
|
|
9
|
-
import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-
|
|
8
|
+
import { $n as encodeWorkDirKey, $t as lerpGradient, A as highlightLines, An as embeddingFailureDetail, At as isTurnElapsedEnabled, B as ActivityGroupComponent, Bn as detectShellEnvironment, Bt as ENABLE_TERMINAL_THEME_REPORTING, Cn as getLlmNotSetMessage, Ct as clearInfoPanelState, D as ToolCallComponent, Dn as sortSlashCommands, Dt as createEditorTheme, E as UserMessageComponent, En as BUILTIN_SLASH_COMMANDS, Et as resolveThemeSync, F as StatusMessageComponent, Fn as PULSE_WAVE_FRAMES, Ft as detectTerminalTheme, G as AgentGroupComponent, Gn as CLI_COMMAND_NAME, Gt as QUERY_TERMINAL_THEME, H as estimateTokens, Hn as getInputHistoryFile, Ht as OSC11_RESPONSE, I as replaceTabs, In as TuiConfigParseError, It as parseOsc11BackgroundTheme, J as getBreathingFrame, Jn as PRODUCT_NAME, Jt as TERMINAL_THEME_DARK, K as WelcomeComponent, Kn as CLI_UI_MODE, Kt as TERMINAL_FOCUS_IN, L as sanitizeShellOutput, Ln as TuiLikePreferencesSchema, Lt as DISABLE_TERMINAL_FOCUS_REPORTING, M as CachedContainer, Mn as hasEmbeddingModelCache, Mt as createThemeStyles, N as ThinkingComponent, Nn as startManualEmbeddingDownload, Nt as contrastTextHex, O as renderDiffLines, On as isExperimentalFlagEnabled, Ot as createMarkdownTheme, P as NoticeMessageComponent, Pn as PIXEL_PULSE_FRAMES, Pt as getColorPalette, Q as refineGoal, Qn as ScreamHarness, Qt as FooterComponent, R as SkillActivationComponent, Rn as loadTuiConfig, Rt as DISABLE_TERMINAL_THEME_REPORTING, S as readJsonlFile, Sn as getCtrlDHint, St as supportsBalance, Tn as buildSkillSlashCommands, U as getSharedSpeedTracker, Un as getLogDir, Ut as OSC11_RESPONSE_PREFIX, V as charsForTokenBudget, Vn as getDataDir, Vt as OSC11_QUERY, W as BackgroundAgentStatusComponent, Wn as detectInstallSource, Wt as OSC11_RESPONSE_PREFIX_NO_ESC, X as CompactionComponent, Xn as fetchCatalog, Xt as isBusy, Y as resetBreathingClock, Yn as DEFAULT_CATALOG_URL, Yt as TERMINAL_THEME_LIGHT, Z as clearGoalState, Zn as saveCatalogCache, Zt as isStreaming, _n as EXIT_CONFIRM_WINDOW_MS, a as syncDiagramCapabilityPrompt, an as printableChar, ar as isRealUserPrompt, at as toTerminalHyperlink, b as readUpdateCache, bn as TIP_ROTATION_INTERVAL_MS, c as disposeChildren, cn as appendStreamingArgsPreview, cr as flushDiagnosticLogs, d as isPlanExpandable, dn as isTodoItemShape, dr as isScreamError, en as SubagentSlots, er as normalizeWorkDir, f as MoonLoader, fn as parseStreamingArgs, fr as isOrphanedToolCallError, ft as handleFusionPlanCommand, gn as EMPTY_SESSION_HINT_URL, hn as truncateErrorMessage, i as buildRoleAdditionalText, it as isInternalMessage, j as langFromPath, jn as getKnowledgeStore, jt as toggleEmptySessionHint, k as renderDiffLinesClustered, kn as setExperimentalFlags, kt as isEmptySessionHintDismissed, l as hasDispose, ln as argsRecord, lr as log, mn as stringValue, mr as SCREAM_ERROR_INFO, mt as handlePlanCommand, n as clearEvalPanelState, nr as readSessionIndex, on as STATUS_BULLET, or as parseRlmMaxDepthArg, ot as changeThinkingLevel, p as formatMemoryMemoForInjection, pn as serializeToolResultOutput, pr as ErrorCodes, q as BREATHE_CYCLE_MS, qn as CLI_USER_AGENT_PRODUCT, qt as TERMINAL_FOCUS_OUT, r as openUrl, rr as removeSessionIndexEntry, sn as appendSessionHeaderHint, sr as resolveScreamHome, st as getModelCycleLevel, t as dispatchInput, tr as appendSessionIndexEntry, u as isExpandable, un as formatErrorMessage, ur as resolveGlobalLogPath, v as selectUpdateTarget, vn as MAIN_AGENT_ID, w as getDaemonInstructions, wn as getNoActiveSessionMessage, x as appendJsonlLine, xn as getCtrlCHint, xt as refreshProviderBalance, y as refreshUpdateCache, yn as SESSION_TIPS, z as AssistantMessageComponent, zn as saveTuiConfig, zt as ENABLE_TERMINAL_FOCUS_REPORTING } from "./dispatch-FYZpdI9u.mjs";
|
|
9
|
+
import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-Bu8GPguU.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
12
12
|
import { access, appendFile, chmod, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
@@ -33,7 +33,7 @@ function optionalBuildString(value) {
|
|
|
33
33
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
34
34
|
}
|
|
35
35
|
const SCREAM_BUILD_INFO = {
|
|
36
|
-
version: optionalBuildString("0.
|
|
36
|
+
version: optionalBuildString("0.18.1"),
|
|
37
37
|
channel: optionalBuildString(""),
|
|
38
38
|
commit: optionalBuildString(""),
|
|
39
39
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -2014,20 +2014,33 @@ function formatBackgroundAgentTranscript(phase, meta, extras = void 0) {
|
|
|
2014
2014
|
};
|
|
2015
2015
|
}
|
|
2016
2016
|
//#endregion
|
|
2017
|
-
//#region src/tui/utils/
|
|
2017
|
+
//#region src/tui/utils/subagent-handoff.ts
|
|
2018
2018
|
/**
|
|
2019
|
-
*
|
|
2020
|
-
* transcript as a two-line notice: a bold title naming the requester, then a
|
|
2021
|
-
* dim detail line carrying the request itself.
|
|
2019
|
+
* Foreground→background handoff results, read by both UI faces.
|
|
2022
2020
|
*
|
|
2023
|
-
*
|
|
2024
|
-
*
|
|
2025
|
-
*
|
|
2026
|
-
*
|
|
2021
|
+
* When the Agent tool hands a still-running child to the background manager it
|
|
2022
|
+
* returns an ordinary (non-error) result, in two shapes that both carry the
|
|
2023
|
+
* protocol line `status: backgrounded`: one with a `task_id` after a
|
|
2024
|
+
* successful registration, and one with a `could not register` warning when
|
|
2025
|
+
* registration failed. The second shape emits no `background.task.started`
|
|
2026
|
+
* frame, so the result body is the only signal that names the state.
|
|
2027
2027
|
*
|
|
2028
|
-
*
|
|
2029
|
-
*
|
|
2028
|
+
* Live and replay therefore read the same fact: the protocol LINE, whether it
|
|
2029
|
+
* arrives in the `tool.result` frame or in the persisted tool-result message a
|
|
2030
|
+
* replay reconstructs.
|
|
2031
|
+
*/
|
|
2032
|
+
const BACKGROUNDED_STATUS_LINE = "status: backgrounded";
|
|
2033
|
+
/**
|
|
2034
|
+
* True when a tool-result output carries the handoff protocol line as its own
|
|
2035
|
+
* line. Matching is line-exact after trimming (tolerating `\r`) so an Agent
|
|
2036
|
+
* result that merely quotes the phrase inside prose — e.g. a completed
|
|
2037
|
+
* subagent's summary — is not mistaken for a handoff.
|
|
2030
2038
|
*/
|
|
2039
|
+
function isBackgroundedHandoffResult(output) {
|
|
2040
|
+
return output.split("\n").some((line) => line.trim() === BACKGROUNDED_STATUS_LINE);
|
|
2041
|
+
}
|
|
2042
|
+
//#endregion
|
|
2043
|
+
//#region src/tui/utils/child-request-notice.ts
|
|
2031
2044
|
const CHILD_REQUEST_KINDS = [
|
|
2032
2045
|
"info",
|
|
2033
2046
|
"handoff",
|
|
@@ -2075,22 +2088,26 @@ function previewedList(field, items) {
|
|
|
2075
2088
|
if (items === void 0 || items.length === 0) return void 0;
|
|
2076
2089
|
return t(LIST_KEY[field], { value: stringifyList(items) });
|
|
2077
2090
|
}
|
|
2078
|
-
/**
|
|
2079
|
-
|
|
2080
|
-
|
|
2091
|
+
/**
|
|
2092
|
+
* Live producer: the delivery event the kernel emits once a request landed in
|
|
2093
|
+
* the parent's turn. Events cross the wire as untyped JSON, so every field is
|
|
2094
|
+
* re-checked here rather than trusted from the declared type.
|
|
2095
|
+
*/
|
|
2096
|
+
function childRequestFieldsFromEvent(event) {
|
|
2097
|
+
const record = event;
|
|
2098
|
+
const message = textOf(record["message"]);
|
|
2081
2099
|
if (message === void 0) return null;
|
|
2082
|
-
const payload = typeof args["payload"] === "object" && args["payload"] !== null && !Array.isArray(args["payload"]) ? args["payload"] : {};
|
|
2083
2100
|
const listOf = (key) => {
|
|
2084
|
-
const raw =
|
|
2101
|
+
const raw = record[key];
|
|
2085
2102
|
if (!Array.isArray(raw)) return void 0;
|
|
2086
2103
|
const items = raw.map((item) => textOf(item)).filter((item) => item !== void 0);
|
|
2087
2104
|
return items.length > 0 ? items : void 0;
|
|
2088
2105
|
};
|
|
2089
2106
|
return {
|
|
2090
|
-
requestType: kindOf(
|
|
2107
|
+
requestType: kindOf(record["requestType"]),
|
|
2091
2108
|
message,
|
|
2092
|
-
needs: textOf(
|
|
2093
|
-
expecting: textOf(
|
|
2109
|
+
needs: textOf(record["needs"]),
|
|
2110
|
+
expecting: textOf(record["expecting"]),
|
|
2094
2111
|
artifacts: listOf("artifacts"),
|
|
2095
2112
|
evidence: listOf("evidence"),
|
|
2096
2113
|
missing: listOf("missing")
|
|
@@ -2387,8 +2404,7 @@ function truncate(value) {
|
|
|
2387
2404
|
}
|
|
2388
2405
|
function phaseFromStatus(status) {
|
|
2389
2406
|
switch (status) {
|
|
2390
|
-
case "running":
|
|
2391
|
-
case "awaiting_approval": return "started";
|
|
2407
|
+
case "running": return "started";
|
|
2392
2408
|
case "completed": return "completed";
|
|
2393
2409
|
case "failed":
|
|
2394
2410
|
case "killed":
|
|
@@ -2402,7 +2418,6 @@ function headlineFor(info) {
|
|
|
2402
2418
|
const subject = subjectFor(info.taskId);
|
|
2403
2419
|
switch (info.status) {
|
|
2404
2420
|
case "running": return t("bgtask.started_bg", { subject });
|
|
2405
|
-
case "awaiting_approval": return t("bgtask.awaiting_approval", { subject });
|
|
2406
2421
|
case "completed": return t("bgtask.completed_bg", { subject });
|
|
2407
2422
|
case "failed": return t("bgtask.failed_bg", { subject });
|
|
2408
2423
|
case "killed": return t("bgtask.killed", { subject });
|
|
@@ -2420,10 +2435,6 @@ function detailFor(info) {
|
|
|
2420
2435
|
const reason = truncate(info.stopReason);
|
|
2421
2436
|
parts.push(reason !== void 0 ? t("bgtask.stopped_reason", { reason }) : t("bgtask.stopped"));
|
|
2422
2437
|
}
|
|
2423
|
-
if (info.status === "awaiting_approval") {
|
|
2424
|
-
const reason = truncate(info.approvalReason);
|
|
2425
|
-
if (reason !== void 0) parts.push(t("bgtask.waiting", { reason }));
|
|
2426
|
-
}
|
|
2427
2438
|
if (info.status === "lost") parts.push(t("bgtask.session_restarted"));
|
|
2428
2439
|
if (info.timedOut === true) parts.push(t("bgtask.timed_out"));
|
|
2429
2440
|
return parts.length > 0 ? parts.join(" · ") : void 0;
|
|
@@ -2443,6 +2454,223 @@ function formatBackgroundTaskTranscript(info) {
|
|
|
2443
2454
|
};
|
|
2444
2455
|
}
|
|
2445
2456
|
//#endregion
|
|
2457
|
+
//#region src/tui/utils/media-url.ts
|
|
2458
|
+
function getMediaKindLabels() {
|
|
2459
|
+
return {
|
|
2460
|
+
audio: t("mediaurl.audio"),
|
|
2461
|
+
image: t("mediaurl.image"),
|
|
2462
|
+
video: t("mediaurl.video")
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
function mediaUrlPartToText(kind, url) {
|
|
2466
|
+
const labels = getMediaKindLabels();
|
|
2467
|
+
const summary = summarizeDataUrl(url);
|
|
2468
|
+
if (summary !== void 0) {
|
|
2469
|
+
const size = summary.bytes !== void 0 ? `, ${formatByteSize(summary.bytes)}` : "";
|
|
2470
|
+
return `[${labels[kind]} ${summary.mime}${size}]`;
|
|
2471
|
+
}
|
|
2472
|
+
return `<${labels[kind]} url="${escapeAttribute$1(url)}">`;
|
|
2473
|
+
}
|
|
2474
|
+
function summarizeDataUrl(url) {
|
|
2475
|
+
if (!url.startsWith("data:")) return void 0;
|
|
2476
|
+
const commaIndex = url.indexOf(",");
|
|
2477
|
+
const header = commaIndex >= 0 ? url.slice(5, commaIndex) : url.slice(5);
|
|
2478
|
+
const data = commaIndex >= 0 ? url.slice(commaIndex + 1) : "";
|
|
2479
|
+
const [rawMime, ...params] = header.split(";");
|
|
2480
|
+
return {
|
|
2481
|
+
mime: rawMime !== void 0 && rawMime.length > 0 ? rawMime : "application/octet-stream",
|
|
2482
|
+
bytes: params.some((param) => param.toLowerCase() === "base64") ? estimateBase64Bytes(data) : void 0
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
function estimateBase64Bytes(data) {
|
|
2486
|
+
const compact = data.replaceAll(/\s/g, "");
|
|
2487
|
+
if (compact.length === 0) return 0;
|
|
2488
|
+
const padding = compact.endsWith("==") ? 2 : compact.endsWith("=") ? 1 : 0;
|
|
2489
|
+
return Math.max(0, Math.floor(compact.length * 3 / 4) - padding);
|
|
2490
|
+
}
|
|
2491
|
+
function formatByteSize(bytes) {
|
|
2492
|
+
if (bytes < 1024) return `${String(bytes)} B`;
|
|
2493
|
+
const kib = bytes / 1024;
|
|
2494
|
+
if (kib < 1024) return `${formatOneDecimal(kib)} KB`;
|
|
2495
|
+
return `${formatOneDecimal(kib / 1024)} MB`;
|
|
2496
|
+
}
|
|
2497
|
+
function formatOneDecimal(value) {
|
|
2498
|
+
return value >= 10 ? value.toFixed(0) : value.toFixed(1);
|
|
2499
|
+
}
|
|
2500
|
+
function escapeAttribute$1(value) {
|
|
2501
|
+
return value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
2502
|
+
}
|
|
2503
|
+
//#endregion
|
|
2504
|
+
//#region src/tui/utils/transcript-id.ts
|
|
2505
|
+
let transcriptIdCounter = 0;
|
|
2506
|
+
function nextTranscriptId() {
|
|
2507
|
+
transcriptIdCounter += 1;
|
|
2508
|
+
return `entry-${String(transcriptIdCounter)}`;
|
|
2509
|
+
}
|
|
2510
|
+
//#endregion
|
|
2511
|
+
//#region src/tui/utils/message-replay.ts
|
|
2512
|
+
function appStateFromResumeAgent(agent) {
|
|
2513
|
+
const maxContextTokens = agent.config.modelCapabilities?.max_context_tokens ?? 0;
|
|
2514
|
+
const contextTokens = agent.context.tokenCount;
|
|
2515
|
+
const contextUsage = maxContextTokens > 0 ? contextTokens / maxContextTokens : 0;
|
|
2516
|
+
return {
|
|
2517
|
+
model: agent.config.modelAlias ?? agent.config.provider?.model ?? "",
|
|
2518
|
+
contextTokens,
|
|
2519
|
+
maxContextTokens,
|
|
2520
|
+
contextUsage,
|
|
2521
|
+
planMode: agent.plan !== null ? "plan" : "off",
|
|
2522
|
+
permissionMode: agent.permission.mode
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
function isTerminalBackgroundTask(info) {
|
|
2526
|
+
return info.status === "completed" || info.status === "failed" || info.status === "killed" || info.status === "lost";
|
|
2527
|
+
}
|
|
2528
|
+
function countActiveBackgroundTasks(tasks) {
|
|
2529
|
+
let bashTasks = 0;
|
|
2530
|
+
let agentTasks = 0;
|
|
2531
|
+
for (const info of tasks.values()) {
|
|
2532
|
+
if (isTerminalBackgroundTask(info)) continue;
|
|
2533
|
+
if (info.taskId.startsWith("agent-")) agentTasks += 1;
|
|
2534
|
+
else bashTasks += 1;
|
|
2535
|
+
}
|
|
2536
|
+
return {
|
|
2537
|
+
bashTasks,
|
|
2538
|
+
agentTasks
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
function replayBackgroundProjection(background) {
|
|
2542
|
+
const backgroundAgentMetadata = /* @__PURE__ */ new Map();
|
|
2543
|
+
for (const info of background) {
|
|
2544
|
+
if (!info.taskId.startsWith("agent-")) continue;
|
|
2545
|
+
if (isTerminalBackgroundTask(info)) continue;
|
|
2546
|
+
const subagentId = info.agentId;
|
|
2547
|
+
if (subagentId === void 0 || subagentId === info.taskId) continue;
|
|
2548
|
+
backgroundAgentMetadata.set(subagentId, {
|
|
2549
|
+
agentId: subagentId,
|
|
2550
|
+
parentToolCallId: "",
|
|
2551
|
+
description: info.description
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
return { backgroundAgentMetadata };
|
|
2555
|
+
}
|
|
2556
|
+
function createReplayRenderContext() {
|
|
2557
|
+
return {
|
|
2558
|
+
turnIndex: 0,
|
|
2559
|
+
stepIndex: 0,
|
|
2560
|
+
currentTurnId: void 0,
|
|
2561
|
+
assistant: {
|
|
2562
|
+
thinking: [],
|
|
2563
|
+
text: []
|
|
2564
|
+
},
|
|
2565
|
+
toolCalls: /* @__PURE__ */ new Map(),
|
|
2566
|
+
completedToolCallIds: /* @__PURE__ */ new Set(),
|
|
2567
|
+
skillActivationIds: /* @__PURE__ */ new Set(),
|
|
2568
|
+
suppressNextPlanModeOffNotice: false
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2571
|
+
function limitReplayRecordsByTurn(records, maxTurns) {
|
|
2572
|
+
if (maxTurns <= 0) return [];
|
|
2573
|
+
const turnStarts = records.flatMap((record, index) => record.type === "message" && isRealUserPrompt(record.message) ? [index] : []);
|
|
2574
|
+
if (turnStarts.length <= maxTurns) return records;
|
|
2575
|
+
return records.slice(turnStarts[turnStarts.length - maxTurns]);
|
|
2576
|
+
}
|
|
2577
|
+
function replayEntry(context, kind, content, renderMode, extras = {}) {
|
|
2578
|
+
return {
|
|
2579
|
+
id: nextTranscriptId(),
|
|
2580
|
+
kind,
|
|
2581
|
+
turnId: context.currentTurnId,
|
|
2582
|
+
renderMode,
|
|
2583
|
+
content,
|
|
2584
|
+
detail: extras.detail
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
function collectReplayMessageContent(target, content) {
|
|
2588
|
+
for (const part of content) switch (part.type) {
|
|
2589
|
+
case "think":
|
|
2590
|
+
target.thinking.push(part.think);
|
|
2591
|
+
break;
|
|
2592
|
+
case "text":
|
|
2593
|
+
target.text.push(part.text);
|
|
2594
|
+
break;
|
|
2595
|
+
case "audio_url":
|
|
2596
|
+
case "image_url":
|
|
2597
|
+
case "video_url": break;
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
function toolCallFromReplayMessage(rawToolCall, context) {
|
|
2601
|
+
const id = rawToolCall.id;
|
|
2602
|
+
const name = rawToolCall.name;
|
|
2603
|
+
if (id.length === 0 || name.length === 0) return void 0;
|
|
2604
|
+
return {
|
|
2605
|
+
id,
|
|
2606
|
+
name,
|
|
2607
|
+
args: parseReplayToolArguments(rawToolCall.arguments),
|
|
2608
|
+
step: context.stepIndex,
|
|
2609
|
+
turnId: context.currentTurnId
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
function toolResultOutput(content) {
|
|
2613
|
+
if (content.some((part) => part.type !== "text")) return JSON.stringify(content);
|
|
2614
|
+
return contentPartsToText(content);
|
|
2615
|
+
}
|
|
2616
|
+
function contentPartsToText(content) {
|
|
2617
|
+
return content.map(contentPartToText).join("");
|
|
2618
|
+
}
|
|
2619
|
+
function backgroundOrigin(message) {
|
|
2620
|
+
return message.origin?.kind === "background_task" ? message.origin : void 0;
|
|
2621
|
+
}
|
|
2622
|
+
function skillActivationFromOrigin(origin) {
|
|
2623
|
+
if (origin?.kind !== "skill_activation") return void 0;
|
|
2624
|
+
return {
|
|
2625
|
+
activationId: origin.activationId,
|
|
2626
|
+
skillName: origin.skillName,
|
|
2627
|
+
skillArgs: origin.skillArgs,
|
|
2628
|
+
trigger: origin.trigger
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
function formatHookResultMessageForTranscript(text, fallbackEvent, blocked) {
|
|
2632
|
+
const results = [];
|
|
2633
|
+
let lastIndex = 0;
|
|
2634
|
+
for (const match of text.matchAll(HOOK_RESULT_RE)) {
|
|
2635
|
+
if (text.slice(lastIndex, match.index).trim().length > 0) return formatHookResultBlock(fallbackEvent, text, blocked);
|
|
2636
|
+
const event = match[1];
|
|
2637
|
+
const body = match[2];
|
|
2638
|
+
if (event === void 0 || body === void 0) return formatHookResultBlock(fallbackEvent, text, blocked);
|
|
2639
|
+
results.push({
|
|
2640
|
+
event,
|
|
2641
|
+
body
|
|
2642
|
+
});
|
|
2643
|
+
lastIndex = match.index + match[0].length;
|
|
2644
|
+
}
|
|
2645
|
+
if (results.length === 0 || text.slice(lastIndex).trim().length > 0) return formatHookResultBlock(fallbackEvent, text, blocked);
|
|
2646
|
+
return results.map(({ event, body }) => formatHookResultBlock(event, body, blocked)).join("\n\n");
|
|
2647
|
+
}
|
|
2648
|
+
function parseReplayToolArguments(value) {
|
|
2649
|
+
if (value === null || value.length === 0) return {};
|
|
2650
|
+
try {
|
|
2651
|
+
const parsed = JSON.parse(value);
|
|
2652
|
+
return isRecord$2(parsed) ? parsed : {};
|
|
2653
|
+
} catch {
|
|
2654
|
+
return {};
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
function contentPartToText(part) {
|
|
2658
|
+
switch (part.type) {
|
|
2659
|
+
case "text": return part.text;
|
|
2660
|
+
case "think": return part.think;
|
|
2661
|
+
case "image_url": return mediaUrlPartToText("image", part.imageUrl.url);
|
|
2662
|
+
case "video_url": return mediaUrlPartToText("video", part.videoUrl.url);
|
|
2663
|
+
case "audio_url": return mediaUrlPartToText("audio", part.audioUrl.url);
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
const HOOK_RESULT_RE = /<hook_result\s+hook_event="([^"]+)">\n?([\s\S]*?)\n?<\/hook_result>/g;
|
|
2667
|
+
function formatHookResultBlock(event, body, blocked) {
|
|
2668
|
+
return `*${event} hook${blocked ? t("replayhook.blocked") : ""}*\n\n${body.trim() || t("replayhook.empty")}`;
|
|
2669
|
+
}
|
|
2670
|
+
function isRecord$2(value) {
|
|
2671
|
+
return typeof value === "object" && value !== null;
|
|
2672
|
+
}
|
|
2673
|
+
//#endregion
|
|
2446
2674
|
//#region src/tui/utils/hook-result-format.ts
|
|
2447
2675
|
function formatHookResultMarkdown(event) {
|
|
2448
2676
|
return `*${formatHookResultTitle(event)}*\n\n${formatHookResultBody(event)}`;
|
|
@@ -2541,7 +2769,7 @@ function parseMcpOAuthAuthorizationUrlUpdate(update) {
|
|
|
2541
2769
|
if (update.kind !== "custom") return void 0;
|
|
2542
2770
|
if (update.customKind !== "mcp.oauth.authorization_url") return void 0;
|
|
2543
2771
|
const data = update.customData;
|
|
2544
|
-
if (!isRecord$
|
|
2772
|
+
if (!isRecord$1(data)) return void 0;
|
|
2545
2773
|
const serverName = data["serverName"];
|
|
2546
2774
|
const authorizationUrl = data["authorizationUrl"];
|
|
2547
2775
|
if (typeof serverName !== "string" || serverName.length === 0) return void 0;
|
|
@@ -2552,7 +2780,7 @@ function parseMcpOAuthAuthorizationUrlUpdate(update) {
|
|
|
2552
2780
|
authorizationUrl
|
|
2553
2781
|
};
|
|
2554
2782
|
}
|
|
2555
|
-
function isRecord$
|
|
2783
|
+
function isRecord$1(value) {
|
|
2556
2784
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2557
2785
|
}
|
|
2558
2786
|
function isHttpUrl(value) {
|
|
@@ -2604,13 +2832,6 @@ function formatDuration(ms) {
|
|
|
2604
2832
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
2605
2833
|
}
|
|
2606
2834
|
//#endregion
|
|
2607
|
-
//#region src/tui/utils/transcript-id.ts
|
|
2608
|
-
let transcriptIdCounter = 0;
|
|
2609
|
-
function nextTranscriptId() {
|
|
2610
|
-
transcriptIdCounter += 1;
|
|
2611
|
-
return `entry-${String(transcriptIdCounter)}`;
|
|
2612
|
-
}
|
|
2613
|
-
//#endregion
|
|
2614
2835
|
//#region src/tui/streaming-phase.ts
|
|
2615
2836
|
/**
|
|
2616
2837
|
* Returns true when transitioning `from → to` is a valid directed edge.
|
|
@@ -2721,6 +2942,20 @@ function isZeroUsage(u) {
|
|
|
2721
2942
|
function isMainAgentStatusEvent(event) {
|
|
2722
2943
|
return event.agentId === void 0 || event.agentId === "main";
|
|
2723
2944
|
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Cap for the visible background-task registry (`backgroundTasks`).
|
|
2947
|
+
*
|
|
2948
|
+
* Eviction policy: terminal entries leave oldest-terminal-first; a
|
|
2949
|
+
* non-terminal task is never evicted. Terminal-time FIFO is chosen over a
|
|
2950
|
+
* per-owner cap because the registry is one flat map — a per-owner cap would
|
|
2951
|
+
* multiply the ceiling by the owner count (the number it is supposed to bound)
|
|
2952
|
+
* and still let one owner's history crowd out another's, while a single global
|
|
2953
|
+
* cap keeps the memory bound exact. Evicting also drops the task's owner tag
|
|
2954
|
+
* and its transcript-dedupe marker, so those two side maps stay bounded with
|
|
2955
|
+
* it. 200 is far above any plausible live set (the manager itself retires at
|
|
2956
|
+
* 20 per owner) and only ever trims scrolling history.
|
|
2957
|
+
*/
|
|
2958
|
+
const MAX_BACKGROUND_TASKS = 200;
|
|
2724
2959
|
var SessionEventHandler = class {
|
|
2725
2960
|
host;
|
|
2726
2961
|
constructor(host) {
|
|
@@ -2729,6 +2964,16 @@ var SessionEventHandler = class {
|
|
|
2729
2964
|
backgroundAgentMetadata = /* @__PURE__ */ new Map();
|
|
2730
2965
|
backgroundTasks = /* @__PURE__ */ new Map();
|
|
2731
2966
|
/**
|
|
2967
|
+
* Owner of every subagent-owned entry in {@link backgroundTasks}: the agent
|
|
2968
|
+
* id whose registry emitted the task's lifecycle events. Only subagent-owned
|
|
2969
|
+
* tasks are recorded — the interactive (main) agent is the implicit default,
|
|
2970
|
+
* so `has(taskId)` reads as "not main's". The /tasks browser annotates those
|
|
2971
|
+
* rows with their source subagent and routes output/stop to the owner,
|
|
2972
|
+
* because a subagent's task lives in that subagent's own registry: asked
|
|
2973
|
+
* through main, its output reads empty and its stop is a no-op.
|
|
2974
|
+
*/
|
|
2975
|
+
backgroundTaskOwners = /* @__PURE__ */ new Map();
|
|
2976
|
+
/**
|
|
2732
2977
|
* Compaction trigger of the in-flight compaction, captured in
|
|
2733
2978
|
* `handleCompactionBegin` (CompactionStartedEvent carries `trigger`) and
|
|
2734
2979
|
* read in `handleCompactionEnd` (CompactionCompletedEvent does not). Used so
|
|
@@ -2740,10 +2985,13 @@ var SessionEventHandler = class {
|
|
|
2740
2985
|
*/
|
|
2741
2986
|
lastCompactionTrigger;
|
|
2742
2987
|
backgroundTaskTranscriptedTerminal = /* @__PURE__ */ new Set();
|
|
2743
|
-
/** Active foreground (non-background) subagents
|
|
2744
|
-
* separately from background agents: foreground
|
|
2745
|
-
* (spawned by the current turn's Agent tool),
|
|
2746
|
-
|
|
2988
|
+
/** Active foreground (non-background) subagents, by subagent id. Footer
|
|
2989
|
+
* badge counts them separately from background agents: foreground
|
|
2990
|
+
* subagents are transient (spawned by the current turn's Agent tool),
|
|
2991
|
+
* background agents persist. Tracking ids instead of a bare count keeps
|
|
2992
|
+
* the releases idempotent and lets a foreground→background handoff move
|
|
2993
|
+
* exactly one run between the two badges. */
|
|
2994
|
+
foregroundSubagentIds = /* @__PURE__ */ new Set();
|
|
2747
2995
|
subagentInfo = /* @__PURE__ */ new Map();
|
|
2748
2996
|
/** Sidebar subagent slot state machine (see utils/subagent-slots.ts). */
|
|
2749
2997
|
subagentSlots = new SubagentSlots();
|
|
@@ -2780,8 +3028,10 @@ var SessionEventHandler = class {
|
|
|
2780
3028
|
resetRuntimeState() {
|
|
2781
3029
|
this.backgroundAgentMetadata.clear();
|
|
2782
3030
|
this.backgroundTasks.clear();
|
|
3031
|
+
this.backgroundTaskOwners.clear();
|
|
2783
3032
|
this.backgroundTaskTranscriptedTerminal.clear();
|
|
2784
3033
|
this.subagentInfo.clear();
|
|
3034
|
+
this.foregroundSubagentIds.clear();
|
|
2785
3035
|
this.subagentSlots.reset();
|
|
2786
3036
|
this.subagentInstances.clear();
|
|
2787
3037
|
this.recentSubagentInstances.length = 0;
|
|
@@ -2930,6 +3180,9 @@ var SessionEventHandler = class {
|
|
|
2930
3180
|
case "subagent.failed":
|
|
2931
3181
|
this.handleSubagentFailed(event);
|
|
2932
3182
|
break;
|
|
3183
|
+
case "subagent.child_request":
|
|
3184
|
+
this.handleSubagentChildRequest(event);
|
|
3185
|
+
break;
|
|
2933
3186
|
case "background.task.started":
|
|
2934
3187
|
case "background.task.updated":
|
|
2935
3188
|
case "background.task.terminated":
|
|
@@ -3061,11 +3314,11 @@ var SessionEventHandler = class {
|
|
|
3061
3314
|
this.subagentSlots.onTerminated(event.subagentId);
|
|
3062
3315
|
this.closeSubagentInstance(event.subagentId, event.type === "subagent.failed" ? "failed" : "completed");
|
|
3063
3316
|
break;
|
|
3317
|
+
case "subagent.child_request":
|
|
3318
|
+
this.handleSubagentChildRequest(event);
|
|
3319
|
+
break;
|
|
3064
3320
|
case "tool.call.started":
|
|
3065
|
-
if (event.name
|
|
3066
|
-
this.subagentSlots.onRequesting(agentId);
|
|
3067
|
-
this.appendChildRequestNotice(agentId, event.args);
|
|
3068
|
-
} else this.subagentSlots.onActivity(agentId, "tool", `tool: ${event.name}`);
|
|
3321
|
+
if (event.name !== "ContactParent") this.subagentSlots.onActivity(agentId, "tool", `tool: ${event.name}`);
|
|
3069
3322
|
break;
|
|
3070
3323
|
case "tool.call.delta": break;
|
|
3071
3324
|
case "tool.result":
|
|
@@ -3079,15 +3332,22 @@ var SessionEventHandler = class {
|
|
|
3079
3332
|
}
|
|
3080
3333
|
}
|
|
3081
3334
|
/**
|
|
3082
|
-
*
|
|
3083
|
-
* the
|
|
3084
|
-
*
|
|
3085
|
-
*
|
|
3335
|
+
* A child→parent collaboration request that the kernel actually delivered:
|
|
3336
|
+
* raise the requester's transient requesting marker and cut into the main
|
|
3337
|
+
* transcript, so the human sees it without expanding the agent card. The
|
|
3338
|
+
* kernel emits the frame at the steering success point only, so a request it
|
|
3339
|
+
* deduped or refused never reaches this handler — the live transcript paints
|
|
3340
|
+
* exactly what replay reconstructs from the parent's context.
|
|
3086
3341
|
*/
|
|
3087
|
-
|
|
3088
|
-
|
|
3342
|
+
handleSubagentChildRequest(event) {
|
|
3343
|
+
this.subagentSlots.onRequesting(event.subagentId);
|
|
3344
|
+
this.appendChildRequestNotice(event);
|
|
3345
|
+
}
|
|
3346
|
+
appendChildRequestNotice(event) {
|
|
3347
|
+
const fields = childRequestFieldsFromEvent(event);
|
|
3089
3348
|
if (fields === null) return;
|
|
3090
|
-
const notice = renderChildRequestNotice(fields, this.subagentInfo.get(
|
|
3349
|
+
const notice = renderChildRequestNotice(fields, event.subagentName ?? this.subagentInfo.get(event.subagentId)?.name);
|
|
3350
|
+
this.host.streamingUI.endActivityGroup();
|
|
3091
3351
|
this.host.appendTranscriptEntry({
|
|
3092
3352
|
id: nextTranscriptId(),
|
|
3093
3353
|
kind: "status",
|
|
@@ -3102,6 +3362,10 @@ var SessionEventHandler = class {
|
|
|
3102
3362
|
const subagentId = event.agentId;
|
|
3103
3363
|
if (subagentId === "main") return false;
|
|
3104
3364
|
this.feedSubagentSlots(event, subagentId);
|
|
3365
|
+
if (event.type === "background.task.started" || event.type === "background.task.updated" || event.type === "background.task.terminated") {
|
|
3366
|
+
this.recordSubagentBackgroundTask(subagentId, event);
|
|
3367
|
+
return true;
|
|
3368
|
+
}
|
|
3105
3369
|
const { streamingUI } = this.host;
|
|
3106
3370
|
const info = this.subagentInfo.get(subagentId);
|
|
3107
3371
|
if (info === void 0 || info.parentToolCallId.length === 0) return true;
|
|
@@ -3151,9 +3415,6 @@ var SessionEventHandler = class {
|
|
|
3151
3415
|
});
|
|
3152
3416
|
return true;
|
|
3153
3417
|
}
|
|
3154
|
-
case "background.task.started":
|
|
3155
|
-
case "background.task.updated":
|
|
3156
|
-
case "background.task.terminated":
|
|
3157
3418
|
case "compaction.blocked":
|
|
3158
3419
|
case "compaction.cancelled":
|
|
3159
3420
|
case "compaction.completed":
|
|
@@ -3440,6 +3701,7 @@ var SessionEventHandler = class {
|
|
|
3440
3701
|
display: event.display,
|
|
3441
3702
|
message: event.message
|
|
3442
3703
|
};
|
|
3704
|
+
if (isBackgroundedHandoffResult(resultData.output)) streamingUI.markSubagentBackgrounded({ toolCallId: event.toolCallId });
|
|
3443
3705
|
streamingUI.completeToolResult(event.toolCallId, resultData);
|
|
3444
3706
|
if (!streamingUI.hasPendingToolCalls() && canTransitionTo(this.host.state.appState.streamingPhase, "waiting")) this.host.setAppState({ streamingPhase: "waiting" });
|
|
3445
3707
|
}
|
|
@@ -3748,7 +4010,7 @@ var SessionEventHandler = class {
|
|
|
3748
4010
|
this.syncBackgroundAgentBadge();
|
|
3749
4011
|
return;
|
|
3750
4012
|
}
|
|
3751
|
-
this.
|
|
4013
|
+
this.foregroundSubagentIds.add(event.subagentId);
|
|
3752
4014
|
this.syncBackgroundAgentBadge();
|
|
3753
4015
|
let tc = streamingUI.getToolComponent(event.parentToolCallId);
|
|
3754
4016
|
if (tc === void 0) {
|
|
@@ -3794,7 +4056,7 @@ var SessionEventHandler = class {
|
|
|
3794
4056
|
this.appendBackgroundAgentEntry("completed", backgroundMeta, extras);
|
|
3795
4057
|
return;
|
|
3796
4058
|
}
|
|
3797
|
-
this.
|
|
4059
|
+
this.foregroundSubagentIds.delete(event.subagentId);
|
|
3798
4060
|
this.syncBackgroundAgentBadge();
|
|
3799
4061
|
const routingId = this.subagentInfo.get(event.subagentId)?.parentToolCallId ?? event.parentToolCallId;
|
|
3800
4062
|
const tc = streamingUI.getToolComponent(routingId);
|
|
@@ -3830,7 +4092,7 @@ var SessionEventHandler = class {
|
|
|
3830
4092
|
this.appendBackgroundAgentEntry("failed", backgroundMeta, { error: event.error });
|
|
3831
4093
|
return;
|
|
3832
4094
|
}
|
|
3833
|
-
this.
|
|
4095
|
+
this.foregroundSubagentIds.delete(event.subagentId);
|
|
3834
4096
|
this.syncBackgroundAgentBadge();
|
|
3835
4097
|
const tc = streamingUI.getToolComponent(this.subagentInfo.get(event.subagentId)?.parentToolCallId ?? event.parentToolCallId);
|
|
3836
4098
|
if (tc === void 0) return;
|
|
@@ -3880,6 +4142,7 @@ var SessionEventHandler = class {
|
|
|
3880
4142
|
let match;
|
|
3881
4143
|
for (const info of this.backgroundTasks.values()) {
|
|
3882
4144
|
if (!info.taskId.startsWith("agent-")) continue;
|
|
4145
|
+
if (this.backgroundTaskOwners.has(info.taskId)) continue;
|
|
3883
4146
|
if (info.description !== description) continue;
|
|
3884
4147
|
if (match !== void 0) return void 0;
|
|
3885
4148
|
match = info.taskId;
|
|
@@ -3915,12 +4178,13 @@ var SessionEventHandler = class {
|
|
|
3915
4178
|
const { state } = this.host;
|
|
3916
4179
|
const { info } = event;
|
|
3917
4180
|
const previous = this.backgroundTasks.get(info.taskId);
|
|
3918
|
-
this.
|
|
4181
|
+
this.rememberBackgroundTask(info);
|
|
3919
4182
|
const viewer = state.tasksBrowser?.viewer;
|
|
3920
4183
|
if (viewer !== void 0 && viewer.taskId === info.taskId) this.host.tasksBrowserController.refreshOutputViewer({ silent: true });
|
|
3921
4184
|
const isTerminal = info.status === "completed" || info.status === "failed" || info.status === "killed" || info.status === "lost";
|
|
3922
4185
|
if (event.type === "background.task.started") {
|
|
3923
4186
|
if (info.taskId.startsWith("agent-")) {
|
|
4187
|
+
if (info.agentId !== void 0 && this.foregroundSubagentIds.delete(info.agentId)) this.host.streamingUI.markSubagentBackgrounded({ agentId: info.agentId });
|
|
3924
4188
|
this.syncBackgroundTaskBadge();
|
|
3925
4189
|
this.host.tasksBrowserController.repaint();
|
|
3926
4190
|
return;
|
|
@@ -3947,6 +4211,43 @@ var SessionEventHandler = class {
|
|
|
3947
4211
|
if (previous?.status !== info.status) this.syncBackgroundTaskBadge();
|
|
3948
4212
|
this.host.tasksBrowserController.repaint();
|
|
3949
4213
|
}
|
|
4214
|
+
/**
|
|
4215
|
+
* Record a subagent-owned task in the visible registry. It stays out of the
|
|
4216
|
+
* transcript (a subagent's stream never joins the main body) and out of the
|
|
4217
|
+
* footer badge (see syncBackgroundTaskBadge), but /tasks lists it with its
|
|
4218
|
+
* source agent, and output/stop reach it through that owner.
|
|
4219
|
+
*/
|
|
4220
|
+
recordSubagentBackgroundTask(ownerId, event) {
|
|
4221
|
+
const { info } = event;
|
|
4222
|
+
this.backgroundTaskOwners.set(info.taskId, ownerId);
|
|
4223
|
+
this.rememberBackgroundTask(info);
|
|
4224
|
+
const viewer = this.host.state.tasksBrowser?.viewer;
|
|
4225
|
+
if (viewer !== void 0 && viewer.taskId === info.taskId) this.host.tasksBrowserController.refreshOutputViewer({ silent: true });
|
|
4226
|
+
this.host.tasksBrowserController.repaint();
|
|
4227
|
+
}
|
|
4228
|
+
/** Insert/refresh one visible task, then keep the registry within
|
|
4229
|
+
* {@link MAX_BACKGROUND_TASKS}. */
|
|
4230
|
+
rememberBackgroundTask(info) {
|
|
4231
|
+
this.backgroundTasks.set(info.taskId, info);
|
|
4232
|
+
this.evictExcessBackgroundTasks();
|
|
4233
|
+
}
|
|
4234
|
+
/**
|
|
4235
|
+
* Drop the oldest terminal entries once the registry is over the cap. A
|
|
4236
|
+
* non-terminal task is never evicted — it is still running, and a /tasks row
|
|
4237
|
+
* vanishing mid-run would misreport reality. The owner tag and the
|
|
4238
|
+
* transcript-dedupe marker leave with their entry, so those side maps stay
|
|
4239
|
+
* bounded by the same cap instead of growing per task forever.
|
|
4240
|
+
*/
|
|
4241
|
+
evictExcessBackgroundTasks() {
|
|
4242
|
+
if (this.backgroundTasks.size <= MAX_BACKGROUND_TASKS) return;
|
|
4243
|
+
const evictable = [...this.backgroundTasks.values()].filter(isTerminalBackgroundTask).toSorted((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt));
|
|
4244
|
+
for (const info of evictable) {
|
|
4245
|
+
if (this.backgroundTasks.size <= MAX_BACKGROUND_TASKS) break;
|
|
4246
|
+
this.backgroundTasks.delete(info.taskId);
|
|
4247
|
+
this.backgroundTaskOwners.delete(info.taskId);
|
|
4248
|
+
this.backgroundTaskTranscriptedTerminal.delete(info.taskId);
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
3950
4251
|
appendBackgroundTaskEntry(info) {
|
|
3951
4252
|
const status = formatBackgroundTaskTranscript(info);
|
|
3952
4253
|
const entry = {
|
|
@@ -3965,6 +4266,7 @@ var SessionEventHandler = class {
|
|
|
3965
4266
|
let bashTasks = 0;
|
|
3966
4267
|
let agentTasks = 0;
|
|
3967
4268
|
for (const info of this.backgroundTasks.values()) {
|
|
4269
|
+
if (this.backgroundTaskOwners.has(info.taskId)) continue;
|
|
3968
4270
|
if (info.status === "completed" || info.status === "failed" || info.status === "killed" || info.status === "lost") continue;
|
|
3969
4271
|
if (info.taskId.startsWith("agent-")) agentTasks += 1;
|
|
3970
4272
|
else bashTasks += 1;
|
|
@@ -3972,7 +4274,7 @@ var SessionEventHandler = class {
|
|
|
3972
4274
|
state.footer?.setBackgroundCounts({
|
|
3973
4275
|
bashTasks,
|
|
3974
4276
|
agentTasks,
|
|
3975
|
-
foregroundSubagents: this.
|
|
4277
|
+
foregroundSubagents: this.foregroundSubagentIds.size
|
|
3976
4278
|
});
|
|
3977
4279
|
state.ui?.requestRender();
|
|
3978
4280
|
}
|
|
@@ -3991,214 +4293,6 @@ function buildApprovalNotice(args) {
|
|
|
3991
4293
|
};
|
|
3992
4294
|
}
|
|
3993
4295
|
//#endregion
|
|
3994
|
-
//#region src/tui/utils/media-url.ts
|
|
3995
|
-
function getMediaKindLabels() {
|
|
3996
|
-
return {
|
|
3997
|
-
audio: t("mediaurl.audio"),
|
|
3998
|
-
image: t("mediaurl.image"),
|
|
3999
|
-
video: t("mediaurl.video")
|
|
4000
|
-
};
|
|
4001
|
-
}
|
|
4002
|
-
function mediaUrlPartToText(kind, url) {
|
|
4003
|
-
const labels = getMediaKindLabels();
|
|
4004
|
-
const summary = summarizeDataUrl(url);
|
|
4005
|
-
if (summary !== void 0) {
|
|
4006
|
-
const size = summary.bytes !== void 0 ? `, ${formatByteSize(summary.bytes)}` : "";
|
|
4007
|
-
return `[${labels[kind]} ${summary.mime}${size}]`;
|
|
4008
|
-
}
|
|
4009
|
-
return `<${labels[kind]} url="${escapeAttribute$1(url)}">`;
|
|
4010
|
-
}
|
|
4011
|
-
function summarizeDataUrl(url) {
|
|
4012
|
-
if (!url.startsWith("data:")) return void 0;
|
|
4013
|
-
const commaIndex = url.indexOf(",");
|
|
4014
|
-
const header = commaIndex >= 0 ? url.slice(5, commaIndex) : url.slice(5);
|
|
4015
|
-
const data = commaIndex >= 0 ? url.slice(commaIndex + 1) : "";
|
|
4016
|
-
const [rawMime, ...params] = header.split(";");
|
|
4017
|
-
return {
|
|
4018
|
-
mime: rawMime !== void 0 && rawMime.length > 0 ? rawMime : "application/octet-stream",
|
|
4019
|
-
bytes: params.some((param) => param.toLowerCase() === "base64") ? estimateBase64Bytes(data) : void 0
|
|
4020
|
-
};
|
|
4021
|
-
}
|
|
4022
|
-
function estimateBase64Bytes(data) {
|
|
4023
|
-
const compact = data.replaceAll(/\s/g, "");
|
|
4024
|
-
if (compact.length === 0) return 0;
|
|
4025
|
-
const padding = compact.endsWith("==") ? 2 : compact.endsWith("=") ? 1 : 0;
|
|
4026
|
-
return Math.max(0, Math.floor(compact.length * 3 / 4) - padding);
|
|
4027
|
-
}
|
|
4028
|
-
function formatByteSize(bytes) {
|
|
4029
|
-
if (bytes < 1024) return `${String(bytes)} B`;
|
|
4030
|
-
const kib = bytes / 1024;
|
|
4031
|
-
if (kib < 1024) return `${formatOneDecimal(kib)} KB`;
|
|
4032
|
-
return `${formatOneDecimal(kib / 1024)} MB`;
|
|
4033
|
-
}
|
|
4034
|
-
function formatOneDecimal(value) {
|
|
4035
|
-
return value >= 10 ? value.toFixed(0) : value.toFixed(1);
|
|
4036
|
-
}
|
|
4037
|
-
function escapeAttribute$1(value) {
|
|
4038
|
-
return value.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
4039
|
-
}
|
|
4040
|
-
//#endregion
|
|
4041
|
-
//#region src/tui/utils/message-replay.ts
|
|
4042
|
-
function appStateFromResumeAgent(agent) {
|
|
4043
|
-
const maxContextTokens = agent.config.modelCapabilities?.max_context_tokens ?? 0;
|
|
4044
|
-
const contextTokens = agent.context.tokenCount;
|
|
4045
|
-
const contextUsage = maxContextTokens > 0 ? contextTokens / maxContextTokens : 0;
|
|
4046
|
-
return {
|
|
4047
|
-
model: agent.config.modelAlias ?? agent.config.provider?.model ?? "",
|
|
4048
|
-
contextTokens,
|
|
4049
|
-
maxContextTokens,
|
|
4050
|
-
contextUsage,
|
|
4051
|
-
planMode: agent.plan !== null ? "plan" : "off",
|
|
4052
|
-
permissionMode: agent.permission.mode
|
|
4053
|
-
};
|
|
4054
|
-
}
|
|
4055
|
-
function isTerminalBackgroundTask(info) {
|
|
4056
|
-
return info.status === "completed" || info.status === "failed" || info.status === "killed" || info.status === "lost";
|
|
4057
|
-
}
|
|
4058
|
-
function countActiveBackgroundTasks(tasks) {
|
|
4059
|
-
let bashTasks = 0;
|
|
4060
|
-
let agentTasks = 0;
|
|
4061
|
-
for (const info of tasks.values()) {
|
|
4062
|
-
if (isTerminalBackgroundTask(info)) continue;
|
|
4063
|
-
if (info.taskId.startsWith("agent-")) agentTasks += 1;
|
|
4064
|
-
else bashTasks += 1;
|
|
4065
|
-
}
|
|
4066
|
-
return {
|
|
4067
|
-
bashTasks,
|
|
4068
|
-
agentTasks
|
|
4069
|
-
};
|
|
4070
|
-
}
|
|
4071
|
-
function replayBackgroundProjection(background) {
|
|
4072
|
-
const backgroundAgentMetadata = /* @__PURE__ */ new Map();
|
|
4073
|
-
for (const info of background) {
|
|
4074
|
-
if (!info.taskId.startsWith("agent-")) continue;
|
|
4075
|
-
if (isTerminalBackgroundTask(info)) continue;
|
|
4076
|
-
backgroundAgentMetadata.set(info.taskId, {
|
|
4077
|
-
agentId: info.taskId,
|
|
4078
|
-
parentToolCallId: info.taskId,
|
|
4079
|
-
description: info.description
|
|
4080
|
-
});
|
|
4081
|
-
}
|
|
4082
|
-
return { backgroundAgentMetadata };
|
|
4083
|
-
}
|
|
4084
|
-
function createReplayRenderContext() {
|
|
4085
|
-
return {
|
|
4086
|
-
turnIndex: 0,
|
|
4087
|
-
stepIndex: 0,
|
|
4088
|
-
currentTurnId: void 0,
|
|
4089
|
-
assistant: {
|
|
4090
|
-
thinking: [],
|
|
4091
|
-
text: []
|
|
4092
|
-
},
|
|
4093
|
-
toolCalls: /* @__PURE__ */ new Map(),
|
|
4094
|
-
completedToolCallIds: /* @__PURE__ */ new Set(),
|
|
4095
|
-
skillActivationIds: /* @__PURE__ */ new Set(),
|
|
4096
|
-
suppressNextPlanModeOffNotice: false
|
|
4097
|
-
};
|
|
4098
|
-
}
|
|
4099
|
-
function limitReplayRecordsByTurn(records, maxTurns) {
|
|
4100
|
-
if (maxTurns <= 0) return [];
|
|
4101
|
-
const turnStarts = records.flatMap((record, index) => record.type === "message" && isRealUserPrompt(record.message) ? [index] : []);
|
|
4102
|
-
if (turnStarts.length <= maxTurns) return records;
|
|
4103
|
-
return records.slice(turnStarts[turnStarts.length - maxTurns]);
|
|
4104
|
-
}
|
|
4105
|
-
function replayEntry(context, kind, content, renderMode, extras = {}) {
|
|
4106
|
-
return {
|
|
4107
|
-
id: nextTranscriptId(),
|
|
4108
|
-
kind,
|
|
4109
|
-
turnId: context.currentTurnId,
|
|
4110
|
-
renderMode,
|
|
4111
|
-
content,
|
|
4112
|
-
detail: extras.detail
|
|
4113
|
-
};
|
|
4114
|
-
}
|
|
4115
|
-
function collectReplayMessageContent(target, content) {
|
|
4116
|
-
for (const part of content) switch (part.type) {
|
|
4117
|
-
case "think":
|
|
4118
|
-
target.thinking.push(part.think);
|
|
4119
|
-
break;
|
|
4120
|
-
case "text":
|
|
4121
|
-
target.text.push(part.text);
|
|
4122
|
-
break;
|
|
4123
|
-
case "audio_url":
|
|
4124
|
-
case "image_url":
|
|
4125
|
-
case "video_url": break;
|
|
4126
|
-
}
|
|
4127
|
-
}
|
|
4128
|
-
function toolCallFromReplayMessage(rawToolCall, context) {
|
|
4129
|
-
const id = rawToolCall.id;
|
|
4130
|
-
const name = rawToolCall.name;
|
|
4131
|
-
if (id.length === 0 || name.length === 0) return void 0;
|
|
4132
|
-
return {
|
|
4133
|
-
id,
|
|
4134
|
-
name,
|
|
4135
|
-
args: parseReplayToolArguments(rawToolCall.arguments),
|
|
4136
|
-
step: context.stepIndex,
|
|
4137
|
-
turnId: context.currentTurnId
|
|
4138
|
-
};
|
|
4139
|
-
}
|
|
4140
|
-
function toolResultOutput(content) {
|
|
4141
|
-
if (content.some((part) => part.type !== "text")) return JSON.stringify(content);
|
|
4142
|
-
return contentPartsToText(content);
|
|
4143
|
-
}
|
|
4144
|
-
function contentPartsToText(content) {
|
|
4145
|
-
return content.map(contentPartToText).join("");
|
|
4146
|
-
}
|
|
4147
|
-
function backgroundOrigin(message) {
|
|
4148
|
-
return message.origin?.kind === "background_task" ? message.origin : void 0;
|
|
4149
|
-
}
|
|
4150
|
-
function skillActivationFromOrigin(origin) {
|
|
4151
|
-
if (origin?.kind !== "skill_activation") return void 0;
|
|
4152
|
-
return {
|
|
4153
|
-
activationId: origin.activationId,
|
|
4154
|
-
skillName: origin.skillName,
|
|
4155
|
-
skillArgs: origin.skillArgs,
|
|
4156
|
-
trigger: origin.trigger
|
|
4157
|
-
};
|
|
4158
|
-
}
|
|
4159
|
-
function formatHookResultMessageForTranscript(text, fallbackEvent, blocked) {
|
|
4160
|
-
const results = [];
|
|
4161
|
-
let lastIndex = 0;
|
|
4162
|
-
for (const match of text.matchAll(HOOK_RESULT_RE)) {
|
|
4163
|
-
if (text.slice(lastIndex, match.index).trim().length > 0) return formatHookResultBlock(fallbackEvent, text, blocked);
|
|
4164
|
-
const event = match[1];
|
|
4165
|
-
const body = match[2];
|
|
4166
|
-
if (event === void 0 || body === void 0) return formatHookResultBlock(fallbackEvent, text, blocked);
|
|
4167
|
-
results.push({
|
|
4168
|
-
event,
|
|
4169
|
-
body
|
|
4170
|
-
});
|
|
4171
|
-
lastIndex = match.index + match[0].length;
|
|
4172
|
-
}
|
|
4173
|
-
if (results.length === 0 || text.slice(lastIndex).trim().length > 0) return formatHookResultBlock(fallbackEvent, text, blocked);
|
|
4174
|
-
return results.map(({ event, body }) => formatHookResultBlock(event, body, blocked)).join("\n\n");
|
|
4175
|
-
}
|
|
4176
|
-
function parseReplayToolArguments(value) {
|
|
4177
|
-
if (value === null || value.length === 0) return {};
|
|
4178
|
-
try {
|
|
4179
|
-
const parsed = JSON.parse(value);
|
|
4180
|
-
return isRecord$1(parsed) ? parsed : {};
|
|
4181
|
-
} catch {
|
|
4182
|
-
return {};
|
|
4183
|
-
}
|
|
4184
|
-
}
|
|
4185
|
-
function contentPartToText(part) {
|
|
4186
|
-
switch (part.type) {
|
|
4187
|
-
case "text": return part.text;
|
|
4188
|
-
case "think": return part.think;
|
|
4189
|
-
case "image_url": return mediaUrlPartToText("image", part.imageUrl.url);
|
|
4190
|
-
case "video_url": return mediaUrlPartToText("video", part.videoUrl.url);
|
|
4191
|
-
case "audio_url": return mediaUrlPartToText("audio", part.audioUrl.url);
|
|
4192
|
-
}
|
|
4193
|
-
}
|
|
4194
|
-
const HOOK_RESULT_RE = /<hook_result\s+hook_event="([^"]+)">\n?([\s\S]*?)\n?<\/hook_result>/g;
|
|
4195
|
-
function formatHookResultBlock(event, body, blocked) {
|
|
4196
|
-
return `*${event} hook${blocked ? t("replayhook.blocked") : ""}*\n\n${body.trim() || t("replayhook.empty")}`;
|
|
4197
|
-
}
|
|
4198
|
-
function isRecord$1(value) {
|
|
4199
|
-
return typeof value === "object" && value !== null;
|
|
4200
|
-
}
|
|
4201
|
-
//#endregion
|
|
4202
4296
|
//#region src/tui/controllers/session-replay.ts
|
|
4203
4297
|
var SessionReplayRenderer = class {
|
|
4204
4298
|
host;
|
|
@@ -4206,6 +4300,7 @@ var SessionReplayRenderer = class {
|
|
|
4206
4300
|
this.host = host;
|
|
4207
4301
|
}
|
|
4208
4302
|
async hydrateFromReplay(session) {
|
|
4303
|
+
this.host.state.ui.resetTextSelection();
|
|
4209
4304
|
this.host.setAppState({ isReplaying: true });
|
|
4210
4305
|
try {
|
|
4211
4306
|
const main = session.getResumeState()?.agents["main"];
|
|
@@ -4276,9 +4371,10 @@ var SessionReplayRenderer = class {
|
|
|
4276
4371
|
sessionEventHandler.backgroundTasks = new Map(agent.background.map((info) => [info.taskId, info]));
|
|
4277
4372
|
sessionEventHandler.backgroundTaskTranscriptedTerminal.clear();
|
|
4278
4373
|
for (const info of agent.background) if (isTerminalBackgroundTask(info)) sessionEventHandler.backgroundTaskTranscriptedTerminal.add(info.taskId);
|
|
4374
|
+
sessionEventHandler.foregroundSubagentIds.clear();
|
|
4279
4375
|
state.footer.setBackgroundCounts({
|
|
4280
4376
|
...countActiveBackgroundTasks(sessionEventHandler.backgroundTasks),
|
|
4281
|
-
foregroundSubagents:
|
|
4377
|
+
foregroundSubagents: sessionEventHandler.foregroundSubagentIds.size
|
|
4282
4378
|
});
|
|
4283
4379
|
state.ui.requestRender();
|
|
4284
4380
|
}
|
|
@@ -4396,6 +4492,7 @@ var SessionReplayRenderer = class {
|
|
|
4396
4492
|
if (fields === null) return;
|
|
4397
4493
|
const notice = renderChildRequestNotice(fields);
|
|
4398
4494
|
this.flushAssistant(context);
|
|
4495
|
+
this.host.streamingUI.endActivityGroup();
|
|
4399
4496
|
this.host.appendTranscriptEntry({
|
|
4400
4497
|
...replayEntry(context, "status", notice.title, "notice", { detail: notice.detail }),
|
|
4401
4498
|
noticeMarkerColor: this.host.state.theme.colors.warning
|
|
@@ -4426,6 +4523,7 @@ var SessionReplayRenderer = class {
|
|
|
4426
4523
|
};
|
|
4427
4524
|
call.result = result;
|
|
4428
4525
|
this.applyStepContext(context);
|
|
4526
|
+
if (call.name === "Agent" && isBackgroundedHandoffResult(result.output)) this.host.streamingUI.markSubagentBackgrounded({ toolCallId });
|
|
4429
4527
|
this.host.streamingUI.onToolCallEnd(toolCallId, result);
|
|
4430
4528
|
this.host.streamingUI.removeActiveToolCall(toolCallId);
|
|
4431
4529
|
context.completedToolCallIds.add(toolCallId);
|
|
@@ -4573,9 +4671,10 @@ var SessionReplayRenderer = class {
|
|
|
4573
4671
|
sessionEventHandler.backgroundTaskTranscriptedTerminal.add(origin.taskId);
|
|
4574
4672
|
return;
|
|
4575
4673
|
}
|
|
4674
|
+
const subagentId = agentTaskSubagentId(task);
|
|
4576
4675
|
const meta = {
|
|
4577
|
-
agentId: origin.taskId,
|
|
4578
|
-
parentToolCallId:
|
|
4676
|
+
agentId: subagentId ?? origin.taskId,
|
|
4677
|
+
parentToolCallId: "",
|
|
4579
4678
|
description: task?.description
|
|
4580
4679
|
};
|
|
4581
4680
|
let status = formatBackgroundAgentTranscript(origin.status === "completed" ? "completed" : "failed", meta);
|
|
@@ -4592,9 +4691,20 @@ var SessionReplayRenderer = class {
|
|
|
4592
4691
|
detail: status.detail,
|
|
4593
4692
|
backgroundAgentStatus: status
|
|
4594
4693
|
});
|
|
4595
|
-
sessionEventHandler.backgroundAgentMetadata.delete(
|
|
4694
|
+
if (subagentId !== void 0) sessionEventHandler.backgroundAgentMetadata.delete(subagentId);
|
|
4596
4695
|
}
|
|
4597
4696
|
};
|
|
4697
|
+
/**
|
|
4698
|
+
* Subagent id behind an agent-class background task: `info.agentId` when a real
|
|
4699
|
+
* subagent was recorded, else `undefined`. The task-id fallback that
|
|
4700
|
+
* `registerAgentTask` writes when the caller passed no id is not a subagent
|
|
4701
|
+
* identity and must not be treated as one.
|
|
4702
|
+
*/
|
|
4703
|
+
function agentTaskSubagentId(info) {
|
|
4704
|
+
if (info === void 0 || !info.taskId.startsWith("agent-")) return void 0;
|
|
4705
|
+
const { agentId } = info;
|
|
4706
|
+
return agentId === void 0 || agentId === info.taskId ? void 0 : agentId;
|
|
4707
|
+
}
|
|
4598
4708
|
//#endregion
|
|
4599
4709
|
//#region src/tui/utils/terminal-notification.ts
|
|
4600
4710
|
function notifyTerminalOnce(state, key, notification) {
|
|
@@ -4964,6 +5074,37 @@ var StreamingUIController = class StreamingUIController {
|
|
|
4964
5074
|
target.setBackgroundTaskTerminalStatus(args.status, { errorText: args.errorText });
|
|
4965
5075
|
return true;
|
|
4966
5076
|
}
|
|
5077
|
+
/**
|
|
5078
|
+
* Marks the Agent card of a subagent that was handed from the foreground to
|
|
5079
|
+
* the background mid-run, so the card keeps reading as `backgrounded`
|
|
5080
|
+
* instead of turning into `done` when the handoff result lands.
|
|
5081
|
+
*
|
|
5082
|
+
* Live callers route by subagent id (the `background.task.started` frame
|
|
5083
|
+
* names it); replay routes by tool call id, the only key a replayed card
|
|
5084
|
+
* has. Returns true when a card was found and marked.
|
|
5085
|
+
*/
|
|
5086
|
+
markSubagentBackgrounded(ref) {
|
|
5087
|
+
let target = ref.agentId === void 0 ? void 0 : this.findAgentCardByAgentId(ref.agentId);
|
|
5088
|
+
if (target === void 0 && ref.toolCallId !== void 0) {
|
|
5089
|
+
const byCall = this._pendingToolComponents.get(ref.toolCallId);
|
|
5090
|
+
if (byCall !== void 0 && byCall.toolCallView.name === "Agent") target = byCall;
|
|
5091
|
+
}
|
|
5092
|
+
if (target === void 0) return false;
|
|
5093
|
+
target.setSubagentBackgrounded();
|
|
5094
|
+
return true;
|
|
5095
|
+
}
|
|
5096
|
+
/**
|
|
5097
|
+
* The mounted Agent card backed by the given subagent id — the in-flight
|
|
5098
|
+
* card, a standalone card, or one borrowed by an agent group.
|
|
5099
|
+
*/
|
|
5100
|
+
findAgentCardByAgentId(agentId) {
|
|
5101
|
+
for (const tc of this._pendingToolComponents.values()) if (tc.getSubagentAgentId() === agentId) return tc;
|
|
5102
|
+
for (const child of this.host.state.transcriptContainer.children) if (child instanceof ToolCallComponent) {
|
|
5103
|
+
if (child.getSubagentAgentId() === agentId) return child;
|
|
5104
|
+
} else if (child instanceof AgentGroupComponent) {
|
|
5105
|
+
for (const tc of child.getToolComponents()) if (tc.getSubagentAgentId() === agentId) return tc;
|
|
5106
|
+
}
|
|
5107
|
+
}
|
|
4967
5108
|
/** Registers a tool call that arrived via tool.call.started.
|
|
4968
5109
|
* Clears any pending streaming state for this id, updates or creates the
|
|
4969
5110
|
* component, and returns whether the call was new (no previous entry). */
|
|
@@ -5630,6 +5771,7 @@ var StreamingUIController = class StreamingUIController {
|
|
|
5630
5771
|
if (pending !== null && (pending.step !== step || pending.turnId !== turnId)) this._pendingAgentGroup = null;
|
|
5631
5772
|
const cur = this._pendingAgentGroup;
|
|
5632
5773
|
if (cur === null) {
|
|
5774
|
+
this.endActivityGroup();
|
|
5633
5775
|
this._pendingAgentGroup = {
|
|
5634
5776
|
step,
|
|
5635
5777
|
turnId,
|
|
@@ -5698,7 +5840,6 @@ var StreamingUIController = class StreamingUIController {
|
|
|
5698
5840
|
const ELLIPSIS$4 = "…";
|
|
5699
5841
|
const STATUS_LABEL$1 = {
|
|
5700
5842
|
running: "running",
|
|
5701
|
-
awaiting_approval: "awaiting",
|
|
5702
5843
|
completed: "completed",
|
|
5703
5844
|
failed: "failed",
|
|
5704
5845
|
killed: "killed",
|
|
@@ -5707,7 +5848,6 @@ const STATUS_LABEL$1 = {
|
|
|
5707
5848
|
function statusColor$1(colors, status) {
|
|
5708
5849
|
switch (status) {
|
|
5709
5850
|
case "running": return colors.success;
|
|
5710
|
-
case "awaiting_approval": return colors.warning;
|
|
5711
5851
|
case "completed": return colors.textMuted;
|
|
5712
5852
|
case "failed":
|
|
5713
5853
|
case "killed":
|
|
@@ -5889,7 +6029,6 @@ var TaskOutputViewer = class extends Container {
|
|
|
5889
6029
|
const ELLIPSIS$3 = "…";
|
|
5890
6030
|
const STATUS_LABEL = {
|
|
5891
6031
|
running: "running",
|
|
5892
|
-
awaiting_approval: "awaiting",
|
|
5893
6032
|
completed: "completed",
|
|
5894
6033
|
failed: "failed",
|
|
5895
6034
|
killed: "killed",
|
|
@@ -5907,7 +6046,6 @@ const LIST_COL_RATIO = .32;
|
|
|
5907
6046
|
function statusColor(colors, status) {
|
|
5908
6047
|
switch (status) {
|
|
5909
6048
|
case "running": return colors.success;
|
|
5910
|
-
case "awaiting_approval": return colors.warning;
|
|
5911
6049
|
case "completed": return colors.textMuted;
|
|
5912
6050
|
case "failed":
|
|
5913
6051
|
case "killed":
|
|
@@ -6028,7 +6166,6 @@ function compareTasks(a, b) {
|
|
|
6028
6166
|
function countByStatus(tasks) {
|
|
6029
6167
|
const counts = {
|
|
6030
6168
|
running: 0,
|
|
6031
|
-
awaiting: 0,
|
|
6032
6169
|
completed: 0,
|
|
6033
6170
|
terminalFailed: 0
|
|
6034
6171
|
};
|
|
@@ -6036,9 +6173,6 @@ function countByStatus(tasks) {
|
|
|
6036
6173
|
case "running":
|
|
6037
6174
|
counts.running += 1;
|
|
6038
6175
|
break;
|
|
6039
|
-
case "awaiting_approval":
|
|
6040
|
-
counts.awaiting += 1;
|
|
6041
|
-
break;
|
|
6042
6176
|
case "completed":
|
|
6043
6177
|
counts.completed += 1;
|
|
6044
6178
|
break;
|
|
@@ -6250,7 +6384,6 @@ var TasksBrowserApp = class extends Container {
|
|
|
6250
6384
|
} else {
|
|
6251
6385
|
const counts = countByStatus(this.props.tasks);
|
|
6252
6386
|
if (counts.running > 0) segments.push(chalk.hex(colors.success)(` ${String(counts.running)} ${t("taskbrowser.running")} `));
|
|
6253
|
-
if (counts.awaiting > 0) segments.push(chalk.hex(colors.warning)(` ${String(counts.awaiting)} ${t("taskbrowser.awaiting")} `));
|
|
6254
6387
|
if (counts.completed > 0) segments.push(chalk.hex(colors.textDim)(` ${String(counts.completed)} ${t("taskbrowser.completed")} `));
|
|
6255
6388
|
if (counts.terminalFailed > 0) segments.push(chalk.hex(colors.error)(` ${String(counts.terminalFailed)} ${t("taskbrowser.interrupted")} `));
|
|
6256
6389
|
segments.push(chalk.hex(colors.textMuted)(` ${String(this.props.tasks.length)} ${t("taskbrowser.total")} `));
|
|
@@ -6386,10 +6519,20 @@ var TasksBrowserApp = class extends Container {
|
|
|
6386
6519
|
const status = STATUS_LABEL[task.status];
|
|
6387
6520
|
const prefix = `${pointerStyled}${idText}${idPad} ${chalk.hex(statusColor(colors, task.status))(status)}`;
|
|
6388
6521
|
const prefixWidth = visibleWidth(prefix);
|
|
6389
|
-
const
|
|
6390
|
-
|
|
6391
|
-
const
|
|
6392
|
-
|
|
6522
|
+
const source = this.props.taskSourceNames.get(task.taskId);
|
|
6523
|
+
const tag = source === void 0 ? "" : ` ${t("taskbrowser.task_source_via", { name: source })}`;
|
|
6524
|
+
const remaining = Math.max(0, innerWidth - prefixWidth - 1);
|
|
6525
|
+
const descBudget = Math.max(0, remaining - visibleWidth(tag));
|
|
6526
|
+
const description = singleLine$2(task.description) || singleLine$2(task.command) || "(no description)";
|
|
6527
|
+
if (tag.length === 0) {
|
|
6528
|
+
if (descBudget < 4) return fitExactly$1(prefix, innerWidth);
|
|
6529
|
+
const desc = truncateToWidth(description, descBudget, ELLIPSIS$3);
|
|
6530
|
+
return fitExactly$1(`${prefix} ${chalk.hex(colors.text)(desc)}`, innerWidth);
|
|
6531
|
+
}
|
|
6532
|
+
if (descBudget >= 4) return fitExactly$1(`${prefix} ${chalk.hex(colors.text)(truncateToWidth(description, descBudget, ELLIPSIS$3))}${chalk.hex(colors.textDim)(tag)}`, innerWidth);
|
|
6533
|
+
if (remaining < 4) return fitExactly$1(prefix, innerWidth);
|
|
6534
|
+
const tagOnly = truncateToWidth(tag.slice(1), remaining, ELLIPSIS$3);
|
|
6535
|
+
return fitExactly$1(`${prefix} ${chalk.hex(colors.textDim)(tagOnly)}`, innerWidth);
|
|
6393
6536
|
}
|
|
6394
6537
|
adjustScroll(visibleRows) {
|
|
6395
6538
|
this.listScroll = clampListScroll(this.selectedIndex, this.listScroll, this.sortedVisible.length, visibleRows);
|
|
@@ -6416,14 +6559,15 @@ var TasksBrowserApp = class extends Container {
|
|
|
6416
6559
|
`${label(t("taskbrowser.status"))}${chalk.hex(statusColor(colors, task.status))(STATUS_LABEL[task.status])}`,
|
|
6417
6560
|
`${label(t("taskbrowser.description"))}${value(singleLine$2(task.description) || "—")}`
|
|
6418
6561
|
];
|
|
6562
|
+
const source = this.props.taskSourceNames.get(task.taskId);
|
|
6563
|
+
if (source !== void 0) lines.push(`${label(t("taskbrowser.agent_source"))}${value(singleLine$2(source))}`);
|
|
6419
6564
|
if (task.command && task.command !== task.description) lines.push(`${label(t("taskbrowser.command"))}${value(singleLine$2(task.command))}`);
|
|
6420
|
-
const timing = task.status === "running"
|
|
6565
|
+
const timing = task.status === "running" ? `${t("taskbrowser.running_time")} ${formatRelativeTime$2(task.startedAt)}` : task.endedAt !== null && task.endedAt !== void 0 ? `${t("taskbrowser.completed_time")} ${formatRelativeTime$2(task.endedAt)}` : "";
|
|
6421
6566
|
if (timing.length > 0) lines.push(`${label(t("taskbrowser.time"))}${chalk.hex(colors.textMuted)(timing)}`);
|
|
6422
6567
|
if (task.pid > 0) lines.push(`${label(t("taskbrowser.process_id"))}${chalk.hex(colors.textMuted)(String(task.pid))}`);
|
|
6423
6568
|
if (task.exitCode !== null && task.exitCode !== void 0) lines.push(`${label(t("taskbrowser.exit_code"))}${chalk.hex(colors.textMuted)(String(task.exitCode))}`);
|
|
6424
6569
|
if (task.stopReason !== void 0 && task.stopReason.length > 0) lines.push(`${label(t("taskbrowser.stop_reason"))}${chalk.hex(colors.textMuted)(task.stopReason)}`);
|
|
6425
6570
|
if (task.timedOut === true) lines.push(`${label(t("taskbrowser.timed_out"))}${chalk.hex(colors.warning)(t("taskbrowser.yes"))}`);
|
|
6426
|
-
if (task.approvalReason !== void 0 && task.approvalReason.length > 0) lines.push(`${label(t("taskbrowser.awaiting_label"))}${chalk.hex(colors.warning)(singleLine$2(task.approvalReason))}`);
|
|
6427
6571
|
while (lines.length < innerHeight) lines.push("");
|
|
6428
6572
|
return this.renderFrame(t("taskbrowser.detail"), lines, width, height);
|
|
6429
6573
|
}
|
|
@@ -6502,7 +6646,7 @@ var TasksBrowserController = class {
|
|
|
6502
6646
|
}
|
|
6503
6647
|
let tasks = [];
|
|
6504
6648
|
try {
|
|
6505
|
-
tasks = await session.listBackgroundTasks({ activeOnly: false });
|
|
6649
|
+
tasks = this.withSubagentTasks(await session.listBackgroundTasks({ activeOnly: false }));
|
|
6506
6650
|
} catch (error) {
|
|
6507
6651
|
this.host.showError(t("tasks.load_failed", { msg: error instanceof Error ? error.message : String(error) }));
|
|
6508
6652
|
return;
|
|
@@ -6512,6 +6656,7 @@ var TasksBrowserController = class {
|
|
|
6512
6656
|
const selectedTaskId = this.pickInitialSelection(tasks, filter);
|
|
6513
6657
|
const component = new TasksBrowserApp({
|
|
6514
6658
|
tasks,
|
|
6659
|
+
taskSourceNames: this.taskSourceNames(),
|
|
6515
6660
|
agents: this.snapshotAgentRows(),
|
|
6516
6661
|
filter,
|
|
6517
6662
|
selectedTaskId,
|
|
@@ -6571,7 +6716,7 @@ var TasksBrowserController = class {
|
|
|
6571
6716
|
const myRefreshId = ++viewer.refreshId;
|
|
6572
6717
|
let output;
|
|
6573
6718
|
try {
|
|
6574
|
-
output = await session.getBackgroundTaskOutput(viewer.taskId);
|
|
6719
|
+
output = await session.getBackgroundTaskOutput(viewer.taskId, { agentId: this.ownerOf(viewer.taskId) });
|
|
6575
6720
|
} catch (error) {
|
|
6576
6721
|
if (!opts.silent) {
|
|
6577
6722
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6598,7 +6743,44 @@ var TasksBrowserController = class {
|
|
|
6598
6743
|
pickInitialSelection(tasks, filter) {
|
|
6599
6744
|
const candidates = filter === "all" ? tasks : tasks.filter((t) => t.status !== "completed" && t.status !== "failed" && t.status !== "killed" && t.status !== "lost");
|
|
6600
6745
|
if (candidates.length === 0) return void 0;
|
|
6601
|
-
return candidates.find((t) => t.status === "running"
|
|
6746
|
+
return candidates.find((t) => t.status === "running")?.taskId ?? candidates[0].taskId;
|
|
6747
|
+
}
|
|
6748
|
+
/**
|
|
6749
|
+
* Merge the subagent-owned rows into a main-agent list. The polled list is
|
|
6750
|
+
* authoritative for main's own registry — it cannot list a subagent's
|
|
6751
|
+
* registry at all — so those rows come from the event-fed
|
|
6752
|
+
* {@link TasksBrowserHost.backgroundTasks}, tagged in
|
|
6753
|
+
* {@link TasksBrowserHost.backgroundTaskOwners}. Without this, opening /tasks
|
|
6754
|
+
* would show an empty list for work the transcript never mentions and the
|
|
6755
|
+
* subagent's own rows would be invisible.
|
|
6756
|
+
*/
|
|
6757
|
+
withSubagentTasks(fetched) {
|
|
6758
|
+
const owners = this.host.backgroundTaskOwners;
|
|
6759
|
+
if (owners.size === 0) return fetched;
|
|
6760
|
+
const merged = new Map(fetched.map((info) => [info.taskId, info]));
|
|
6761
|
+
for (const taskId of owners.keys()) {
|
|
6762
|
+
if (merged.has(taskId)) continue;
|
|
6763
|
+
const local = this.host.backgroundTasks.get(taskId);
|
|
6764
|
+
if (local !== void 0) merged.set(taskId, local);
|
|
6765
|
+
}
|
|
6766
|
+
return [...merged.values()];
|
|
6767
|
+
}
|
|
6768
|
+
/** Row/detail tag: the owning subagent's display name per subagent-owned task. */
|
|
6769
|
+
taskSourceNames() {
|
|
6770
|
+
const names = /* @__PURE__ */ new Map();
|
|
6771
|
+
for (const [taskId, ownerId] of this.host.backgroundTaskOwners) names.set(taskId, this.ownerDisplayName(ownerId));
|
|
6772
|
+
return names;
|
|
6773
|
+
}
|
|
6774
|
+
/** Owning subagent's profile name, falling back to the archived instance row
|
|
6775
|
+
* and finally to the raw agent id — an id is better than a wrong name. */
|
|
6776
|
+
ownerDisplayName(ownerId) {
|
|
6777
|
+
const live = this.host.subagentInstances.get(ownerId);
|
|
6778
|
+
if (live !== void 0) return live.type;
|
|
6779
|
+
return this.host.recentSubagentInstances.find((row) => row.instanceId === ownerId)?.type ?? ownerId;
|
|
6780
|
+
}
|
|
6781
|
+
/** Agent whose registry answers for this task; undefined ⇒ the main agent's. */
|
|
6782
|
+
ownerOf(taskId) {
|
|
6783
|
+
return this.host.backgroundTaskOwners.get(taskId);
|
|
6602
6784
|
}
|
|
6603
6785
|
async refresh(opts = {}) {
|
|
6604
6786
|
const { state } = this.host;
|
|
@@ -6614,13 +6796,14 @@ var TasksBrowserController = class {
|
|
|
6614
6796
|
return;
|
|
6615
6797
|
}
|
|
6616
6798
|
if (state.tasksBrowser !== browser) return;
|
|
6617
|
-
this.pushProps(tasks);
|
|
6799
|
+
this.pushProps(this.withSubagentTasks(tasks));
|
|
6618
6800
|
}
|
|
6619
6801
|
pushProps(tasks) {
|
|
6620
6802
|
const browser = this.host.state.tasksBrowser;
|
|
6621
6803
|
if (browser === void 0) return;
|
|
6622
6804
|
browser.component.setProps({
|
|
6623
6805
|
tasks,
|
|
6806
|
+
taskSourceNames: this.taskSourceNames(),
|
|
6624
6807
|
agents: this.snapshotAgentRows(),
|
|
6625
6808
|
filter: browser.filter,
|
|
6626
6809
|
selectedTaskId: browser.selectedTaskId,
|
|
@@ -6693,7 +6876,10 @@ var TasksBrowserController = class {
|
|
|
6693
6876
|
}
|
|
6694
6877
|
this.flash(t("tasks.stopping", { name: taskId }), 1500);
|
|
6695
6878
|
try {
|
|
6696
|
-
await session.stopBackgroundTask(taskId, {
|
|
6879
|
+
await session.stopBackgroundTask(taskId, {
|
|
6880
|
+
reason: t("tasks.user_stopped"),
|
|
6881
|
+
agentId: this.ownerOf(taskId)
|
|
6882
|
+
});
|
|
6697
6883
|
await this.refresh({ silent: true });
|
|
6698
6884
|
} catch (error) {
|
|
6699
6885
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6712,7 +6898,7 @@ var TasksBrowserController = class {
|
|
|
6712
6898
|
}
|
|
6713
6899
|
let output;
|
|
6714
6900
|
try {
|
|
6715
|
-
output = await session.getBackgroundTaskOutput(taskId);
|
|
6901
|
+
output = await session.getBackgroundTaskOutput(taskId, { agentId: this.ownerOf(taskId) });
|
|
6716
6902
|
} catch (error) {
|
|
6717
6903
|
const message = error instanceof Error ? error.message : String(error);
|
|
6718
6904
|
this.flash(t("tasks.open_output_failed", { msg: message }));
|
|
@@ -6720,6 +6906,7 @@ var TasksBrowserController = class {
|
|
|
6720
6906
|
}
|
|
6721
6907
|
const current = state.tasksBrowser;
|
|
6722
6908
|
if (current === void 0 || current !== browser) return;
|
|
6909
|
+
if (current.viewer !== void 0) return;
|
|
6723
6910
|
const viewer = new TaskOutputViewer({
|
|
6724
6911
|
taskId,
|
|
6725
6912
|
info: this.host.backgroundTasks.get(taskId),
|
|
@@ -6729,7 +6916,7 @@ var TasksBrowserController = class {
|
|
|
6729
6916
|
this.closeOutputViewer();
|
|
6730
6917
|
}
|
|
6731
6918
|
}, state.terminal);
|
|
6732
|
-
const savedBrowserLayout =
|
|
6919
|
+
const savedBrowserLayout = browser.component;
|
|
6733
6920
|
state.ui.setLayoutRoot(viewer);
|
|
6734
6921
|
state.ui.setFocus(viewer);
|
|
6735
6922
|
state.ui.requestRender(true);
|
|
@@ -6756,7 +6943,10 @@ var TasksBrowserController = class {
|
|
|
6756
6943
|
return;
|
|
6757
6944
|
}
|
|
6758
6945
|
const requestId = ++browser.tailRequestId;
|
|
6759
|
-
session.getBackgroundTaskOutput(taskId, {
|
|
6946
|
+
session.getBackgroundTaskOutput(taskId, {
|
|
6947
|
+
tail: 4e3,
|
|
6948
|
+
agentId: this.ownerOf(taskId)
|
|
6949
|
+
}).then((output) => {
|
|
6760
6950
|
const current = state.tasksBrowser;
|
|
6761
6951
|
if (current === void 0) return;
|
|
6762
6952
|
if (current !== browser || current.tailRequestId !== requestId) return;
|
|
@@ -7211,6 +7401,7 @@ var TranscriptController = class TranscriptController {
|
|
|
7211
7401
|
this.dropLiveComponent(component);
|
|
7212
7402
|
}
|
|
7213
7403
|
this.committedComponent.setCount(this.committedComponent.getCount() + toCommit.length);
|
|
7404
|
+
state.ui.resetTextSelection();
|
|
7214
7405
|
if (process.env["SCREAM_CODE_DEBUG"] === "1") this.host.showStatus(`[debug] committed=${this.committedComponent.getCount()} live=${this.getLiveCount()}`);
|
|
7215
7406
|
container.invalidate();
|
|
7216
7407
|
state.ui.requestRender();
|
|
@@ -7385,6 +7576,7 @@ var TranscriptController = class TranscriptController {
|
|
|
7385
7576
|
}
|
|
7386
7577
|
clearAndRedraw() {
|
|
7387
7578
|
const { state, streamingUI, imageStore } = this.host;
|
|
7579
|
+
state.ui.resetTextSelection();
|
|
7388
7580
|
streamingUI.discardPending();
|
|
7389
7581
|
streamingUI.endActivityGroup();
|
|
7390
7582
|
streamingUI.flushPendingApprovals();
|
|
@@ -7449,12 +7641,17 @@ var TranscriptController = class TranscriptController {
|
|
|
7449
7641
|
const { state } = this.host;
|
|
7450
7642
|
const next = !state.toolOutputExpanded;
|
|
7451
7643
|
const children = state.transcriptContainer.children;
|
|
7644
|
+
let resized = false;
|
|
7452
7645
|
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
7453
7646
|
const child = children[i];
|
|
7454
7647
|
if (child === void 0) continue;
|
|
7455
7648
|
if (child instanceof UserMessageComponent || child instanceof SkillActivationComponent || child instanceof CronMessageComponent || child instanceof CommittedTranscriptComponent) break;
|
|
7456
|
-
if (isExpandable(child))
|
|
7649
|
+
if (isExpandable(child)) {
|
|
7650
|
+
child.setExpanded(next);
|
|
7651
|
+
resized = true;
|
|
7652
|
+
}
|
|
7457
7653
|
}
|
|
7654
|
+
if (resized) state.ui.resetTextSelection();
|
|
7458
7655
|
state.toolOutputExpanded = next;
|
|
7459
7656
|
state.ui.requestRender();
|
|
7460
7657
|
}
|
|
@@ -7464,6 +7661,7 @@ var TranscriptController = class TranscriptController {
|
|
|
7464
7661
|
let toggled = false;
|
|
7465
7662
|
for (const child of state.transcriptContainer.children) if (isPlanExpandable(child) && child.setPlanExpanded(next)) toggled = true;
|
|
7466
7663
|
if (!toggled) return false;
|
|
7664
|
+
state.ui.resetTextSelection();
|
|
7467
7665
|
state.planExpanded = next;
|
|
7468
7666
|
state.ui.requestRender();
|
|
7469
7667
|
return true;
|
|
@@ -10935,6 +11133,7 @@ var CustomEditor = class extends Editor {
|
|
|
10935
11133
|
thinkingLevel: this.thinkingLevel,
|
|
10936
11134
|
toolPriority: this.toolPriority,
|
|
10937
11135
|
paint: this.borderColor ?? ((s) => s),
|
|
11136
|
+
modeHex: getPermissionModeColor(this.permissionMode, this.colors),
|
|
10938
11137
|
borderHex: this.borderHex
|
|
10939
11138
|
});
|
|
10940
11139
|
return lines;
|
|
@@ -11085,6 +11284,15 @@ function injectPromptSymbol(line) {
|
|
|
11085
11284
|
}
|
|
11086
11285
|
const THINK_LABEL_MIN_WIDTH = 14;
|
|
11087
11286
|
const MODE_BADGE_MIN_WIDTH = 10;
|
|
11287
|
+
function getPermissionModeColor(mode, colors) {
|
|
11288
|
+
return {
|
|
11289
|
+
manual: colors.permissionManual,
|
|
11290
|
+
yolo: colors.permissionYolo,
|
|
11291
|
+
auto: colors.permissionAuto,
|
|
11292
|
+
ask: colors.permissionAsk,
|
|
11293
|
+
bot: colors.permissionBot
|
|
11294
|
+
}[mode];
|
|
11295
|
+
}
|
|
11088
11296
|
function isLightBg(hex) {
|
|
11089
11297
|
const r = parseInt(hex.slice(1, 3), 16);
|
|
11090
11298
|
const g = parseInt(hex.slice(3, 5), 16);
|
|
@@ -11115,7 +11323,7 @@ function injectBorderBadges(lines, width, opts) {
|
|
|
11115
11323
|
let right = paint("─");
|
|
11116
11324
|
if (width >= MODE_BADGE_MIN_WIDTH) {
|
|
11117
11325
|
const badgeText = ` ${opts.mode} `;
|
|
11118
|
-
left = paint("──") +
|
|
11326
|
+
left = paint("──") + chalk.hex(opts.modeHex).bold(badgeText);
|
|
11119
11327
|
}
|
|
11120
11328
|
if (opts.thinking && width >= THINK_LABEL_MIN_WIDTH) right = makeBadge(opts.thinkingLevel !== "off" ? ` Think ${opts.thinkingLevel} ` : " Think ", opts.borderHex) + right;
|
|
11121
11329
|
if (opts.toolPriority === "skill" || opts.toolPriority === "mcp") right = makeBadge(` First ${opts.toolPriority} `, opts.borderHex) + paint("─") + right;
|
|
@@ -11538,6 +11746,7 @@ function getPlanRejectChoices() {
|
|
|
11538
11746
|
}
|
|
11539
11747
|
function adaptApprovalRequest(event) {
|
|
11540
11748
|
const resolved = resolveDisplay(event.toolName, event.display, event.action);
|
|
11749
|
+
const sourceLabel = formatApprovalSource(event);
|
|
11541
11750
|
return {
|
|
11542
11751
|
id: event.toolCallId,
|
|
11543
11752
|
tool_call_id: event.toolCallId,
|
|
@@ -11545,9 +11754,29 @@ function adaptApprovalRequest(event) {
|
|
|
11545
11754
|
action: event.action,
|
|
11546
11755
|
description: resolved.description,
|
|
11547
11756
|
display: resolved.blocks,
|
|
11548
|
-
choices: adaptChoices(event.toolName, event.display)
|
|
11757
|
+
choices: adaptChoices(event.toolName, event.display),
|
|
11758
|
+
...sourceLabel !== void 0 ? { source_label: sourceLabel } : {}
|
|
11549
11759
|
};
|
|
11550
11760
|
}
|
|
11761
|
+
/**
|
|
11762
|
+
* "来源:<代理名>(<agentId>)· <工具>" — who asked and what triggered it.
|
|
11763
|
+
* Returns undefined on payloads without attribution (older emitters), so
|
|
11764
|
+
* the panel can skip the row entirely.
|
|
11765
|
+
*
|
|
11766
|
+
* The label itself follows the sidebar's locale-conditional pattern
|
|
11767
|
+
* (`getLocale()`), not the i18n dictionary: this row renders inside the
|
|
11768
|
+
* approval panel's own text and needs no placeholder plumbing.
|
|
11769
|
+
*/
|
|
11770
|
+
function formatApprovalSource(event) {
|
|
11771
|
+
const agentId = event.sourceAgentId;
|
|
11772
|
+
const name = event.sourceAgentName;
|
|
11773
|
+
if (agentId === void 0 && name === void 0) return void 0;
|
|
11774
|
+
const tool = event.sourceToolName ?? event.toolName;
|
|
11775
|
+
const label = name ?? agentId ?? "";
|
|
11776
|
+
const agent = agentId !== void 0 && agentId !== "" && agentId !== label ? `${label}(${agentId})` : label;
|
|
11777
|
+
const toolSuffix = tool !== void 0 && tool !== "" ? ` · ${tool}` : "";
|
|
11778
|
+
return `${getLocale() === "zh" ? "来源:" : "Source: "}${agent}${toolSuffix}`;
|
|
11779
|
+
}
|
|
11551
11780
|
function resolveDisplay(toolName, display, action) {
|
|
11552
11781
|
if (display.kind === "generic" && isRecord(display.detail)) {
|
|
11553
11782
|
const extracted = extractFromArgs(toolName, display.detail);
|
|
@@ -12440,6 +12669,7 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
12440
12669
|
const indent = (s) => ` ${s}`;
|
|
12441
12670
|
const title = headerFor(data.tool_name);
|
|
12442
12671
|
const lines = [horizontalBar, indent(`${borderColorBold("→")} ${borderColorBold(title)}`)];
|
|
12672
|
+
if (data.source_label !== void 0 && data.source_label.length > 0) lines.push(indent(dim(truncateToWidth(data.source_label, Math.max(width - 4, 1)))));
|
|
12443
12673
|
const visibleBlocks = data.display.filter((block) => !isDuplicateBriefBlock(block, data.description)).slice(0, 5);
|
|
12444
12674
|
const hasPreviewable = visibleBlocks.some((block) => block.type === "diff" || block.type === "file_content");
|
|
12445
12675
|
if (visibleBlocks.length > 0) {
|
|
@@ -12693,6 +12923,10 @@ const DEFAULT_KEYBOARD_SHORTCUTS = [
|
|
|
12693
12923
|
keys: "Shift-Tab",
|
|
12694
12924
|
description: t("help.toggle_plan")
|
|
12695
12925
|
},
|
|
12926
|
+
{
|
|
12927
|
+
keys: "Tab",
|
|
12928
|
+
description: t("help.cycle_thinking")
|
|
12929
|
+
},
|
|
12696
12930
|
{
|
|
12697
12931
|
keys: "Ctrl-G",
|
|
12698
12932
|
description: t("help.external_editor")
|
|
@@ -12701,10 +12935,26 @@ const DEFAULT_KEYBOARD_SHORTCUTS = [
|
|
|
12701
12935
|
keys: "Ctrl-O",
|
|
12702
12936
|
description: t("help.toggle_output")
|
|
12703
12937
|
},
|
|
12938
|
+
{
|
|
12939
|
+
keys: "Ctrl-E",
|
|
12940
|
+
description: t("help.toggle_plan_view")
|
|
12941
|
+
},
|
|
12704
12942
|
{
|
|
12705
12943
|
keys: "Ctrl-S",
|
|
12706
12944
|
description: t("help.interrupt")
|
|
12707
12945
|
},
|
|
12946
|
+
{
|
|
12947
|
+
keys: "Ctrl-X",
|
|
12948
|
+
description: t("help.toggle_sidebar")
|
|
12949
|
+
},
|
|
12950
|
+
{
|
|
12951
|
+
keys: "Ctrl-B",
|
|
12952
|
+
description: t("help.toggle_hint")
|
|
12953
|
+
},
|
|
12954
|
+
{
|
|
12955
|
+
keys: "Ctrl-W",
|
|
12956
|
+
description: t("help.cancel_memory")
|
|
12957
|
+
},
|
|
12708
12958
|
{
|
|
12709
12959
|
keys: "Shift-Enter / Ctrl-J",
|
|
12710
12960
|
description: t("help.newline")
|
|
@@ -14746,6 +14996,11 @@ var ScreamTUI = class {
|
|
|
14746
14996
|
get backgroundTasks() {
|
|
14747
14997
|
return this.sessionEventHandler.backgroundTasks;
|
|
14748
14998
|
}
|
|
14999
|
+
/** Owner agent of each subagent-owned background task (task id → agent id) —
|
|
15000
|
+
* /tasks tags those rows and routes output/stop through the owner. */
|
|
15001
|
+
get backgroundTaskOwners() {
|
|
15002
|
+
return this.sessionEventHandler.backgroundTaskOwners;
|
|
15003
|
+
}
|
|
14749
15004
|
/** Live per-type subagent slots (sidebar state machine) — source of the
|
|
14750
15005
|
* /tasks browser's Agents view. */
|
|
14751
15006
|
get subagentSlots() {
|
|
@@ -14930,7 +15185,7 @@ var ScreamTUI = class {
|
|
|
14930
15185
|
const card = new OnboardingCardComponent({
|
|
14931
15186
|
colors: this.state.theme.colors,
|
|
14932
15187
|
onConfigure: () => {
|
|
14933
|
-
import("./dispatch-
|
|
15188
|
+
import("./dispatch-Byc9hdXF.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
|
|
14934
15189
|
this.restoreEditor();
|
|
14935
15190
|
onDone?.();
|
|
14936
15191
|
});
|
|
@@ -18524,13 +18779,21 @@ var WebSession = class {
|
|
|
18524
18779
|
resolve,
|
|
18525
18780
|
toolName: request.toolName,
|
|
18526
18781
|
action: request.action,
|
|
18527
|
-
display: request.display
|
|
18782
|
+
display: request.display,
|
|
18783
|
+
sourceAgentId: request.sourceAgentId,
|
|
18784
|
+
sourceAgentName: request.sourceAgentName,
|
|
18785
|
+
sourceToolName: request.sourceToolName,
|
|
18786
|
+
sourceCapabilityMode: request.sourceCapabilityMode
|
|
18528
18787
|
});
|
|
18529
18788
|
const payload = {
|
|
18530
18789
|
id,
|
|
18531
18790
|
toolName: request.toolName,
|
|
18532
18791
|
action: request.action,
|
|
18533
|
-
display: request.display
|
|
18792
|
+
display: request.display,
|
|
18793
|
+
sourceAgentId: request.sourceAgentId,
|
|
18794
|
+
sourceAgentName: request.sourceAgentName,
|
|
18795
|
+
sourceToolName: request.sourceToolName,
|
|
18796
|
+
sourceCapabilityMode: request.sourceCapabilityMode
|
|
18534
18797
|
};
|
|
18535
18798
|
this.broadcast({
|
|
18536
18799
|
type: "approval_request",
|
|
@@ -18578,7 +18841,11 @@ var WebSession = class {
|
|
|
18578
18841
|
id,
|
|
18579
18842
|
toolName: approval.toolName,
|
|
18580
18843
|
action: approval.action,
|
|
18581
|
-
display: approval.display
|
|
18844
|
+
display: approval.display,
|
|
18845
|
+
sourceAgentId: approval.sourceAgentId,
|
|
18846
|
+
sourceAgentName: approval.sourceAgentName,
|
|
18847
|
+
sourceToolName: approval.sourceToolName,
|
|
18848
|
+
sourceCapabilityMode: approval.sourceCapabilityMode
|
|
18582
18849
|
})),
|
|
18583
18850
|
status: this.cachedStatus ?? {
|
|
18584
18851
|
model: "unknown",
|