scream-code 0.18.0 → 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-BuWHcggN.mjs → app-BQW2gDgm.mjs} +526 -299
- package/dist/{dispatch-CUfxgLwq.mjs → dispatch-Byc9hdXF.mjs} +1 -1
- package/dist/{dispatch-DfHVLpfA.mjs → dispatch-FYZpdI9u.mjs} +1130 -516
- 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-CK6jTftM.mjs → text-input-dialog-Bu8GPguU.mjs} +6 -8
- package/dist/{text-input-dialog-DZoGkbYZ.mjs → text-input-dialog-b-TqTP2z.mjs} +1 -1
- package/package.json +1 -1
- 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.18.
|
|
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;
|
|
@@ -4277,9 +4371,10 @@ var SessionReplayRenderer = class {
|
|
|
4277
4371
|
sessionEventHandler.backgroundTasks = new Map(agent.background.map((info) => [info.taskId, info]));
|
|
4278
4372
|
sessionEventHandler.backgroundTaskTranscriptedTerminal.clear();
|
|
4279
4373
|
for (const info of agent.background) if (isTerminalBackgroundTask(info)) sessionEventHandler.backgroundTaskTranscriptedTerminal.add(info.taskId);
|
|
4374
|
+
sessionEventHandler.foregroundSubagentIds.clear();
|
|
4280
4375
|
state.footer.setBackgroundCounts({
|
|
4281
4376
|
...countActiveBackgroundTasks(sessionEventHandler.backgroundTasks),
|
|
4282
|
-
foregroundSubagents:
|
|
4377
|
+
foregroundSubagents: sessionEventHandler.foregroundSubagentIds.size
|
|
4283
4378
|
});
|
|
4284
4379
|
state.ui.requestRender();
|
|
4285
4380
|
}
|
|
@@ -4397,6 +4492,7 @@ var SessionReplayRenderer = class {
|
|
|
4397
4492
|
if (fields === null) return;
|
|
4398
4493
|
const notice = renderChildRequestNotice(fields);
|
|
4399
4494
|
this.flushAssistant(context);
|
|
4495
|
+
this.host.streamingUI.endActivityGroup();
|
|
4400
4496
|
this.host.appendTranscriptEntry({
|
|
4401
4497
|
...replayEntry(context, "status", notice.title, "notice", { detail: notice.detail }),
|
|
4402
4498
|
noticeMarkerColor: this.host.state.theme.colors.warning
|
|
@@ -4427,6 +4523,7 @@ var SessionReplayRenderer = class {
|
|
|
4427
4523
|
};
|
|
4428
4524
|
call.result = result;
|
|
4429
4525
|
this.applyStepContext(context);
|
|
4526
|
+
if (call.name === "Agent" && isBackgroundedHandoffResult(result.output)) this.host.streamingUI.markSubagentBackgrounded({ toolCallId });
|
|
4430
4527
|
this.host.streamingUI.onToolCallEnd(toolCallId, result);
|
|
4431
4528
|
this.host.streamingUI.removeActiveToolCall(toolCallId);
|
|
4432
4529
|
context.completedToolCallIds.add(toolCallId);
|
|
@@ -4574,9 +4671,10 @@ var SessionReplayRenderer = class {
|
|
|
4574
4671
|
sessionEventHandler.backgroundTaskTranscriptedTerminal.add(origin.taskId);
|
|
4575
4672
|
return;
|
|
4576
4673
|
}
|
|
4674
|
+
const subagentId = agentTaskSubagentId(task);
|
|
4577
4675
|
const meta = {
|
|
4578
|
-
agentId: origin.taskId,
|
|
4579
|
-
parentToolCallId:
|
|
4676
|
+
agentId: subagentId ?? origin.taskId,
|
|
4677
|
+
parentToolCallId: "",
|
|
4580
4678
|
description: task?.description
|
|
4581
4679
|
};
|
|
4582
4680
|
let status = formatBackgroundAgentTranscript(origin.status === "completed" ? "completed" : "failed", meta);
|
|
@@ -4593,9 +4691,20 @@ var SessionReplayRenderer = class {
|
|
|
4593
4691
|
detail: status.detail,
|
|
4594
4692
|
backgroundAgentStatus: status
|
|
4595
4693
|
});
|
|
4596
|
-
sessionEventHandler.backgroundAgentMetadata.delete(
|
|
4694
|
+
if (subagentId !== void 0) sessionEventHandler.backgroundAgentMetadata.delete(subagentId);
|
|
4597
4695
|
}
|
|
4598
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
|
+
}
|
|
4599
4708
|
//#endregion
|
|
4600
4709
|
//#region src/tui/utils/terminal-notification.ts
|
|
4601
4710
|
function notifyTerminalOnce(state, key, notification) {
|
|
@@ -4965,6 +5074,37 @@ var StreamingUIController = class StreamingUIController {
|
|
|
4965
5074
|
target.setBackgroundTaskTerminalStatus(args.status, { errorText: args.errorText });
|
|
4966
5075
|
return true;
|
|
4967
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
|
+
}
|
|
4968
5108
|
/** Registers a tool call that arrived via tool.call.started.
|
|
4969
5109
|
* Clears any pending streaming state for this id, updates or creates the
|
|
4970
5110
|
* component, and returns whether the call was new (no previous entry). */
|
|
@@ -5631,6 +5771,7 @@ var StreamingUIController = class StreamingUIController {
|
|
|
5631
5771
|
if (pending !== null && (pending.step !== step || pending.turnId !== turnId)) this._pendingAgentGroup = null;
|
|
5632
5772
|
const cur = this._pendingAgentGroup;
|
|
5633
5773
|
if (cur === null) {
|
|
5774
|
+
this.endActivityGroup();
|
|
5634
5775
|
this._pendingAgentGroup = {
|
|
5635
5776
|
step,
|
|
5636
5777
|
turnId,
|
|
@@ -5699,7 +5840,6 @@ var StreamingUIController = class StreamingUIController {
|
|
|
5699
5840
|
const ELLIPSIS$4 = "…";
|
|
5700
5841
|
const STATUS_LABEL$1 = {
|
|
5701
5842
|
running: "running",
|
|
5702
|
-
awaiting_approval: "awaiting",
|
|
5703
5843
|
completed: "completed",
|
|
5704
5844
|
failed: "failed",
|
|
5705
5845
|
killed: "killed",
|
|
@@ -5708,7 +5848,6 @@ const STATUS_LABEL$1 = {
|
|
|
5708
5848
|
function statusColor$1(colors, status) {
|
|
5709
5849
|
switch (status) {
|
|
5710
5850
|
case "running": return colors.success;
|
|
5711
|
-
case "awaiting_approval": return colors.warning;
|
|
5712
5851
|
case "completed": return colors.textMuted;
|
|
5713
5852
|
case "failed":
|
|
5714
5853
|
case "killed":
|
|
@@ -5890,7 +6029,6 @@ var TaskOutputViewer = class extends Container {
|
|
|
5890
6029
|
const ELLIPSIS$3 = "…";
|
|
5891
6030
|
const STATUS_LABEL = {
|
|
5892
6031
|
running: "running",
|
|
5893
|
-
awaiting_approval: "awaiting",
|
|
5894
6032
|
completed: "completed",
|
|
5895
6033
|
failed: "failed",
|
|
5896
6034
|
killed: "killed",
|
|
@@ -5908,7 +6046,6 @@ const LIST_COL_RATIO = .32;
|
|
|
5908
6046
|
function statusColor(colors, status) {
|
|
5909
6047
|
switch (status) {
|
|
5910
6048
|
case "running": return colors.success;
|
|
5911
|
-
case "awaiting_approval": return colors.warning;
|
|
5912
6049
|
case "completed": return colors.textMuted;
|
|
5913
6050
|
case "failed":
|
|
5914
6051
|
case "killed":
|
|
@@ -6029,7 +6166,6 @@ function compareTasks(a, b) {
|
|
|
6029
6166
|
function countByStatus(tasks) {
|
|
6030
6167
|
const counts = {
|
|
6031
6168
|
running: 0,
|
|
6032
|
-
awaiting: 0,
|
|
6033
6169
|
completed: 0,
|
|
6034
6170
|
terminalFailed: 0
|
|
6035
6171
|
};
|
|
@@ -6037,9 +6173,6 @@ function countByStatus(tasks) {
|
|
|
6037
6173
|
case "running":
|
|
6038
6174
|
counts.running += 1;
|
|
6039
6175
|
break;
|
|
6040
|
-
case "awaiting_approval":
|
|
6041
|
-
counts.awaiting += 1;
|
|
6042
|
-
break;
|
|
6043
6176
|
case "completed":
|
|
6044
6177
|
counts.completed += 1;
|
|
6045
6178
|
break;
|
|
@@ -6251,7 +6384,6 @@ var TasksBrowserApp = class extends Container {
|
|
|
6251
6384
|
} else {
|
|
6252
6385
|
const counts = countByStatus(this.props.tasks);
|
|
6253
6386
|
if (counts.running > 0) segments.push(chalk.hex(colors.success)(` ${String(counts.running)} ${t("taskbrowser.running")} `));
|
|
6254
|
-
if (counts.awaiting > 0) segments.push(chalk.hex(colors.warning)(` ${String(counts.awaiting)} ${t("taskbrowser.awaiting")} `));
|
|
6255
6387
|
if (counts.completed > 0) segments.push(chalk.hex(colors.textDim)(` ${String(counts.completed)} ${t("taskbrowser.completed")} `));
|
|
6256
6388
|
if (counts.terminalFailed > 0) segments.push(chalk.hex(colors.error)(` ${String(counts.terminalFailed)} ${t("taskbrowser.interrupted")} `));
|
|
6257
6389
|
segments.push(chalk.hex(colors.textMuted)(` ${String(this.props.tasks.length)} ${t("taskbrowser.total")} `));
|
|
@@ -6387,10 +6519,20 @@ var TasksBrowserApp = class extends Container {
|
|
|
6387
6519
|
const status = STATUS_LABEL[task.status];
|
|
6388
6520
|
const prefix = `${pointerStyled}${idText}${idPad} ${chalk.hex(statusColor(colors, task.status))(status)}`;
|
|
6389
6521
|
const prefixWidth = visibleWidth(prefix);
|
|
6390
|
-
const
|
|
6391
|
-
|
|
6392
|
-
const
|
|
6393
|
-
|
|
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);
|
|
6394
6536
|
}
|
|
6395
6537
|
adjustScroll(visibleRows) {
|
|
6396
6538
|
this.listScroll = clampListScroll(this.selectedIndex, this.listScroll, this.sortedVisible.length, visibleRows);
|
|
@@ -6417,14 +6559,15 @@ var TasksBrowserApp = class extends Container {
|
|
|
6417
6559
|
`${label(t("taskbrowser.status"))}${chalk.hex(statusColor(colors, task.status))(STATUS_LABEL[task.status])}`,
|
|
6418
6560
|
`${label(t("taskbrowser.description"))}${value(singleLine$2(task.description) || "—")}`
|
|
6419
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))}`);
|
|
6420
6564
|
if (task.command && task.command !== task.description) lines.push(`${label(t("taskbrowser.command"))}${value(singleLine$2(task.command))}`);
|
|
6421
|
-
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)}` : "";
|
|
6422
6566
|
if (timing.length > 0) lines.push(`${label(t("taskbrowser.time"))}${chalk.hex(colors.textMuted)(timing)}`);
|
|
6423
6567
|
if (task.pid > 0) lines.push(`${label(t("taskbrowser.process_id"))}${chalk.hex(colors.textMuted)(String(task.pid))}`);
|
|
6424
6568
|
if (task.exitCode !== null && task.exitCode !== void 0) lines.push(`${label(t("taskbrowser.exit_code"))}${chalk.hex(colors.textMuted)(String(task.exitCode))}`);
|
|
6425
6569
|
if (task.stopReason !== void 0 && task.stopReason.length > 0) lines.push(`${label(t("taskbrowser.stop_reason"))}${chalk.hex(colors.textMuted)(task.stopReason)}`);
|
|
6426
6570
|
if (task.timedOut === true) lines.push(`${label(t("taskbrowser.timed_out"))}${chalk.hex(colors.warning)(t("taskbrowser.yes"))}`);
|
|
6427
|
-
if (task.approvalReason !== void 0 && task.approvalReason.length > 0) lines.push(`${label(t("taskbrowser.awaiting_label"))}${chalk.hex(colors.warning)(singleLine$2(task.approvalReason))}`);
|
|
6428
6571
|
while (lines.length < innerHeight) lines.push("");
|
|
6429
6572
|
return this.renderFrame(t("taskbrowser.detail"), lines, width, height);
|
|
6430
6573
|
}
|
|
@@ -6503,7 +6646,7 @@ var TasksBrowserController = class {
|
|
|
6503
6646
|
}
|
|
6504
6647
|
let tasks = [];
|
|
6505
6648
|
try {
|
|
6506
|
-
tasks = await session.listBackgroundTasks({ activeOnly: false });
|
|
6649
|
+
tasks = this.withSubagentTasks(await session.listBackgroundTasks({ activeOnly: false }));
|
|
6507
6650
|
} catch (error) {
|
|
6508
6651
|
this.host.showError(t("tasks.load_failed", { msg: error instanceof Error ? error.message : String(error) }));
|
|
6509
6652
|
return;
|
|
@@ -6513,6 +6656,7 @@ var TasksBrowserController = class {
|
|
|
6513
6656
|
const selectedTaskId = this.pickInitialSelection(tasks, filter);
|
|
6514
6657
|
const component = new TasksBrowserApp({
|
|
6515
6658
|
tasks,
|
|
6659
|
+
taskSourceNames: this.taskSourceNames(),
|
|
6516
6660
|
agents: this.snapshotAgentRows(),
|
|
6517
6661
|
filter,
|
|
6518
6662
|
selectedTaskId,
|
|
@@ -6572,7 +6716,7 @@ var TasksBrowserController = class {
|
|
|
6572
6716
|
const myRefreshId = ++viewer.refreshId;
|
|
6573
6717
|
let output;
|
|
6574
6718
|
try {
|
|
6575
|
-
output = await session.getBackgroundTaskOutput(viewer.taskId);
|
|
6719
|
+
output = await session.getBackgroundTaskOutput(viewer.taskId, { agentId: this.ownerOf(viewer.taskId) });
|
|
6576
6720
|
} catch (error) {
|
|
6577
6721
|
if (!opts.silent) {
|
|
6578
6722
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6599,7 +6743,44 @@ var TasksBrowserController = class {
|
|
|
6599
6743
|
pickInitialSelection(tasks, filter) {
|
|
6600
6744
|
const candidates = filter === "all" ? tasks : tasks.filter((t) => t.status !== "completed" && t.status !== "failed" && t.status !== "killed" && t.status !== "lost");
|
|
6601
6745
|
if (candidates.length === 0) return void 0;
|
|
6602
|
-
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);
|
|
6603
6784
|
}
|
|
6604
6785
|
async refresh(opts = {}) {
|
|
6605
6786
|
const { state } = this.host;
|
|
@@ -6615,13 +6796,14 @@ var TasksBrowserController = class {
|
|
|
6615
6796
|
return;
|
|
6616
6797
|
}
|
|
6617
6798
|
if (state.tasksBrowser !== browser) return;
|
|
6618
|
-
this.pushProps(tasks);
|
|
6799
|
+
this.pushProps(this.withSubagentTasks(tasks));
|
|
6619
6800
|
}
|
|
6620
6801
|
pushProps(tasks) {
|
|
6621
6802
|
const browser = this.host.state.tasksBrowser;
|
|
6622
6803
|
if (browser === void 0) return;
|
|
6623
6804
|
browser.component.setProps({
|
|
6624
6805
|
tasks,
|
|
6806
|
+
taskSourceNames: this.taskSourceNames(),
|
|
6625
6807
|
agents: this.snapshotAgentRows(),
|
|
6626
6808
|
filter: browser.filter,
|
|
6627
6809
|
selectedTaskId: browser.selectedTaskId,
|
|
@@ -6694,7 +6876,10 @@ var TasksBrowserController = class {
|
|
|
6694
6876
|
}
|
|
6695
6877
|
this.flash(t("tasks.stopping", { name: taskId }), 1500);
|
|
6696
6878
|
try {
|
|
6697
|
-
await session.stopBackgroundTask(taskId, {
|
|
6879
|
+
await session.stopBackgroundTask(taskId, {
|
|
6880
|
+
reason: t("tasks.user_stopped"),
|
|
6881
|
+
agentId: this.ownerOf(taskId)
|
|
6882
|
+
});
|
|
6698
6883
|
await this.refresh({ silent: true });
|
|
6699
6884
|
} catch (error) {
|
|
6700
6885
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6713,7 +6898,7 @@ var TasksBrowserController = class {
|
|
|
6713
6898
|
}
|
|
6714
6899
|
let output;
|
|
6715
6900
|
try {
|
|
6716
|
-
output = await session.getBackgroundTaskOutput(taskId);
|
|
6901
|
+
output = await session.getBackgroundTaskOutput(taskId, { agentId: this.ownerOf(taskId) });
|
|
6717
6902
|
} catch (error) {
|
|
6718
6903
|
const message = error instanceof Error ? error.message : String(error);
|
|
6719
6904
|
this.flash(t("tasks.open_output_failed", { msg: message }));
|
|
@@ -6758,7 +6943,10 @@ var TasksBrowserController = class {
|
|
|
6758
6943
|
return;
|
|
6759
6944
|
}
|
|
6760
6945
|
const requestId = ++browser.tailRequestId;
|
|
6761
|
-
session.getBackgroundTaskOutput(taskId, {
|
|
6946
|
+
session.getBackgroundTaskOutput(taskId, {
|
|
6947
|
+
tail: 4e3,
|
|
6948
|
+
agentId: this.ownerOf(taskId)
|
|
6949
|
+
}).then((output) => {
|
|
6762
6950
|
const current = state.tasksBrowser;
|
|
6763
6951
|
if (current === void 0) return;
|
|
6764
6952
|
if (current !== browser || current.tailRequestId !== requestId) return;
|
|
@@ -11558,6 +11746,7 @@ function getPlanRejectChoices() {
|
|
|
11558
11746
|
}
|
|
11559
11747
|
function adaptApprovalRequest(event) {
|
|
11560
11748
|
const resolved = resolveDisplay(event.toolName, event.display, event.action);
|
|
11749
|
+
const sourceLabel = formatApprovalSource(event);
|
|
11561
11750
|
return {
|
|
11562
11751
|
id: event.toolCallId,
|
|
11563
11752
|
tool_call_id: event.toolCallId,
|
|
@@ -11565,9 +11754,29 @@ function adaptApprovalRequest(event) {
|
|
|
11565
11754
|
action: event.action,
|
|
11566
11755
|
description: resolved.description,
|
|
11567
11756
|
display: resolved.blocks,
|
|
11568
|
-
choices: adaptChoices(event.toolName, event.display)
|
|
11757
|
+
choices: adaptChoices(event.toolName, event.display),
|
|
11758
|
+
...sourceLabel !== void 0 ? { source_label: sourceLabel } : {}
|
|
11569
11759
|
};
|
|
11570
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
|
+
}
|
|
11571
11780
|
function resolveDisplay(toolName, display, action) {
|
|
11572
11781
|
if (display.kind === "generic" && isRecord(display.detail)) {
|
|
11573
11782
|
const extracted = extractFromArgs(toolName, display.detail);
|
|
@@ -12460,6 +12669,7 @@ var ApprovalPanelComponent = class extends Container {
|
|
|
12460
12669
|
const indent = (s) => ` ${s}`;
|
|
12461
12670
|
const title = headerFor(data.tool_name);
|
|
12462
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)))));
|
|
12463
12673
|
const visibleBlocks = data.display.filter((block) => !isDuplicateBriefBlock(block, data.description)).slice(0, 5);
|
|
12464
12674
|
const hasPreviewable = visibleBlocks.some((block) => block.type === "diff" || block.type === "file_content");
|
|
12465
12675
|
if (visibleBlocks.length > 0) {
|
|
@@ -14786,6 +14996,11 @@ var ScreamTUI = class {
|
|
|
14786
14996
|
get backgroundTasks() {
|
|
14787
14997
|
return this.sessionEventHandler.backgroundTasks;
|
|
14788
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
|
+
}
|
|
14789
15004
|
/** Live per-type subagent slots (sidebar state machine) — source of the
|
|
14790
15005
|
* /tasks browser's Agents view. */
|
|
14791
15006
|
get subagentSlots() {
|
|
@@ -14970,7 +15185,7 @@ var ScreamTUI = class {
|
|
|
14970
15185
|
const card = new OnboardingCardComponent({
|
|
14971
15186
|
colors: this.state.theme.colors,
|
|
14972
15187
|
onConfigure: () => {
|
|
14973
|
-
import("./dispatch-
|
|
15188
|
+
import("./dispatch-Byc9hdXF.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
|
|
14974
15189
|
this.restoreEditor();
|
|
14975
15190
|
onDone?.();
|
|
14976
15191
|
});
|
|
@@ -18564,13 +18779,21 @@ var WebSession = class {
|
|
|
18564
18779
|
resolve,
|
|
18565
18780
|
toolName: request.toolName,
|
|
18566
18781
|
action: request.action,
|
|
18567
|
-
display: request.display
|
|
18782
|
+
display: request.display,
|
|
18783
|
+
sourceAgentId: request.sourceAgentId,
|
|
18784
|
+
sourceAgentName: request.sourceAgentName,
|
|
18785
|
+
sourceToolName: request.sourceToolName,
|
|
18786
|
+
sourceCapabilityMode: request.sourceCapabilityMode
|
|
18568
18787
|
});
|
|
18569
18788
|
const payload = {
|
|
18570
18789
|
id,
|
|
18571
18790
|
toolName: request.toolName,
|
|
18572
18791
|
action: request.action,
|
|
18573
|
-
display: request.display
|
|
18792
|
+
display: request.display,
|
|
18793
|
+
sourceAgentId: request.sourceAgentId,
|
|
18794
|
+
sourceAgentName: request.sourceAgentName,
|
|
18795
|
+
sourceToolName: request.sourceToolName,
|
|
18796
|
+
sourceCapabilityMode: request.sourceCapabilityMode
|
|
18574
18797
|
};
|
|
18575
18798
|
this.broadcast({
|
|
18576
18799
|
type: "approval_request",
|
|
@@ -18618,7 +18841,11 @@ var WebSession = class {
|
|
|
18618
18841
|
id,
|
|
18619
18842
|
toolName: approval.toolName,
|
|
18620
18843
|
action: approval.action,
|
|
18621
|
-
display: approval.display
|
|
18844
|
+
display: approval.display,
|
|
18845
|
+
sourceAgentId: approval.sourceAgentId,
|
|
18846
|
+
sourceAgentName: approval.sourceAgentName,
|
|
18847
|
+
sourceToolName: approval.sourceToolName,
|
|
18848
|
+
sourceCapabilityMode: approval.sourceCapabilityMode
|
|
18622
18849
|
})),
|
|
18623
18850
|
status: this.cachedStatus ?? {
|
|
18624
18851
|
model: "unknown",
|