@cydm/pie 2.6.10 → 2.6.12

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/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  createCliWebResearchHealthStore,
8
8
  formatSize,
9
9
  truncateHeadTail
10
- } from "./chunks/chunk-YUVGEY6X.js";
10
+ } from "./chunks/chunk-AAEBH6KX.js";
11
11
  import {
12
12
  AGENTS_CONTEXT_FILE_NAME,
13
13
  AgentSessionController,
@@ -63,8 +63,8 @@ import {
63
63
  setExtensionEnabledInSettings,
64
64
  shouldPreserveExecutionStateForUserText,
65
65
  supersedeExecutionState
66
- } from "./chunks/chunk-DFWWHVJL.js";
67
- import "./chunks/chunk-PAC62XU4.js";
66
+ } from "./chunks/chunk-PPEWRUMQ.js";
67
+ import "./chunks/chunk-HHNOENEH.js";
68
68
  import {
69
69
  Deref,
70
70
  EMPTY_ASSISTANT_CONTENT_ERROR,
@@ -63980,7 +63980,24 @@ var ProcessTerminal = class {
63980
63980
  process.stdout.write(sequence);
63981
63981
  }
63982
63982
  }
63983
+ /** Bounce the dock icon once (iTerm2's OSC 1337 extension; verified live
63984
+ * on iTerm2 — its docs list "Post a notification"-adjacent attention as
63985
+ * OSC 1337 ; RequestAttention). WezTerm documents OSC 1337 only for file
63986
+ * upload, not attention; other terminals ignore unknown OSC per the
63987
+ * xterm spec, so emitting unconditionally is safe and future-proof.
63988
+ * Value 1 bounces exactly once; 2 would bounce until the user activates
63989
+ * the app, which is too pushy for a per-turn attention signal. No
63990
+ * Notification Center permission is involved, so this works even when
63991
+ * the host app's notifications are muted — the banner channel's
63992
+ * fallback for "I just need a dot". */
63993
+ requestAttention() {
63994
+ if (!process.stdout.isTTY) return;
63995
+ process.stdout.write(buildDockAttentionSequence());
63996
+ }
63983
63997
  };
63998
+ function buildDockAttentionSequence() {
63999
+ return "\x1B]1337;RequestAttention=1\x07";
64000
+ }
63984
64001
  var NOTIFICATION_UNSAFE_CHARS = /[\x00-\x1f\x7f-\x9f;]/g;
63985
64002
  function sanitizeNotificationText(text) {
63986
64003
  return text.replace(NOTIFICATION_UNSAFE_CHARS, "");
@@ -70154,6 +70171,68 @@ var SessionTraceWriter = class extends SessionTraceSnapshotWriter {
70154
70171
  sessionManager;
70155
70172
  };
70156
70173
 
70174
+ // src/runtime/context-signals.ts
70175
+ import { createHash as createHash4 } from "crypto";
70176
+ function applyContextSignalsToFooter(target) {
70177
+ if (!target.agent?.state) return;
70178
+ const signals = computeContextSignals(
70179
+ target,
70180
+ target.agent.state.systemPrompt ?? "",
70181
+ target.agent.state.tools,
70182
+ target.sessionManager.getMessages()
70183
+ );
70184
+ target.footer.setState({
70185
+ prefixHash: signals.prefixHash,
70186
+ prefixHashChanged: signals.prefixHashChanged,
70187
+ cacheHitRate: signals.cacheHitRate
70188
+ });
70189
+ }
70190
+ function computePromptPrefixHash(systemPrompt, tools) {
70191
+ return createHash4("sha256").update(
70192
+ JSON.stringify({
70193
+ systemPrompt,
70194
+ tools: tools.map((tool) => ({
70195
+ name: tool.name,
70196
+ description: tool.description ?? "",
70197
+ parameters: tool.parameters ?? null
70198
+ }))
70199
+ })
70200
+ ).digest("hex").slice(0, 8);
70201
+ }
70202
+ function calculateCumulativeCacheHitRate(messages) {
70203
+ let input = 0;
70204
+ let cacheRead = 0;
70205
+ for (const message of messages) {
70206
+ if (message.role !== "assistant") continue;
70207
+ const usage = message.usage;
70208
+ if (!usage) continue;
70209
+ input += usage.input;
70210
+ cacheRead += usage.cacheRead;
70211
+ }
70212
+ const base = input + cacheRead;
70213
+ return base > 0 ? cacheRead / base : null;
70214
+ }
70215
+ var signalMemories = /* @__PURE__ */ new WeakMap();
70216
+ var lastSignals = /* @__PURE__ */ new WeakMap();
70217
+ function getLastContextSignals(hostKey) {
70218
+ return lastSignals.get(hostKey) ?? null;
70219
+ }
70220
+ function computeContextSignals(hostKey, systemPrompt, tools, messages) {
70221
+ const prefixHash = computePromptPrefixHash(systemPrompt, tools);
70222
+ const previous = signalMemories.get(hostKey);
70223
+ const prefixHashChanged = previous !== void 0 && previous.hash !== prefixHash;
70224
+ if (previous?.hash !== prefixHash) {
70225
+ signalMemories.set(hostKey, { hash: prefixHash });
70226
+ }
70227
+ const signals = {
70228
+ prefixHash,
70229
+ prefixHashChanged,
70230
+ cacheHitRate: calculateCumulativeCacheHitRate(messages)
70231
+ };
70232
+ lastSignals.set(hostKey, signals);
70233
+ return signals;
70234
+ }
70235
+
70157
70236
  // src/commands/interactive-session-commands.ts
70158
70237
  import * as os6 from "os";
70159
70238
 
@@ -70342,6 +70421,387 @@ var SessionSelectorComponent = class extends Container {
70342
70421
  }
70343
70422
  };
70344
70423
 
70424
+ // src/core/context-report.ts
70425
+ var APPROX_BYTES_PER_TOKEN = 4;
70426
+ function estimateStringTokens(text) {
70427
+ if (!text) return 0;
70428
+ return Math.ceil(Buffer.byteLength(text, "utf8") / APPROX_BYTES_PER_TOKEN);
70429
+ }
70430
+ function formatReportTokens(count) {
70431
+ if (count < 1e3) return String(count);
70432
+ if (count < 1e4) return `${(count / 1e3).toFixed(1)}k`;
70433
+ if (count < 1e6) return `${Math.round(count / 1e3)}k`;
70434
+ if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
70435
+ return `${Math.round(count / 1e6)}M`;
70436
+ }
70437
+ function buildSystemPromptBlocks(systemPrompt, skillsSection, knowledgeSection) {
70438
+ const totalTokens = estimateStringTokens(systemPrompt);
70439
+ const skillsTokens = skillsSection && systemPrompt.includes(skillsSection.trim()) ? estimateStringTokens(skillsSection) : null;
70440
+ const knowledgeTokens = knowledgeSection && systemPrompt.includes(knowledgeSection.trim()) ? estimateStringTokens(knowledgeSection) : null;
70441
+ const accounted = (skillsTokens ?? 0) + (knowledgeTokens ?? 0);
70442
+ return {
70443
+ totalTokens,
70444
+ baseTokens: Math.max(0, totalTokens - accounted),
70445
+ skillsTokens,
70446
+ knowledgeTokens
70447
+ };
70448
+ }
70449
+ function escapeXmlName(name) {
70450
+ return name.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
70451
+ }
70452
+ function extractSkillBlocks(section) {
70453
+ if (!section) return [];
70454
+ return section.split(/(?=<skill>)/).map((block) => block.trim()).filter((block) => block.startsWith("<skill>"));
70455
+ }
70456
+ function buildSkillRows(skillsSection, skills) {
70457
+ const blocks = extractSkillBlocks(skillsSection);
70458
+ if (blocks.length === 0) {
70459
+ return [
70460
+ {
70461
+ id: "skills:none",
70462
+ label: "no skills in prompt",
70463
+ estTokens: 0
70464
+ }
70465
+ ];
70466
+ }
70467
+ return blocks.map((block, index) => {
70468
+ const escapedNameMatch = block.match(/<name>([\s\S]*?)<\/name>/);
70469
+ const escapedName = escapedNameMatch?.[1] ?? "";
70470
+ const skill = skills.find((candidate) => escapeXmlName(candidate.name) === escapedName) ?? skills.find((candidate) => block.includes(escapeXmlName(candidate.name)));
70471
+ const estTokens = estimateStringTokens(block);
70472
+ const source = skill?.source ? `source:${skill.source}` : void 0;
70473
+ const detail = skill?.inlineContent !== void 0 ? "inline content (already loaded)" : skill?.filePath ?? void 0;
70474
+ return {
70475
+ id: `skill:${skill?.name ?? index}`,
70476
+ label: skill?.name ?? `<skill #${index + 1}>`,
70477
+ estTokens,
70478
+ note: source,
70479
+ detail,
70480
+ drill: {
70481
+ kind: "text",
70482
+ title: `Skill \xB7 ${skill?.name ?? `#${index + 1}`}`,
70483
+ text: block
70484
+ }
70485
+ };
70486
+ }).sort((a, b2) => b2.estTokens - a.estTokens);
70487
+ }
70488
+ function parseProjectContextSection(section) {
70489
+ const parts = section.split(/^### (.+)$/m);
70490
+ const files = [];
70491
+ for (let i = 1; i + 1 < parts.length + 1; i += 2) {
70492
+ const path34 = (parts[i] ?? "").trim();
70493
+ const content = (parts[i + 1] ?? "").trim();
70494
+ if (!path34 || !content) continue;
70495
+ files.push({ path: path34, content });
70496
+ }
70497
+ return files;
70498
+ }
70499
+ function buildKnowledgeRows(files) {
70500
+ if (files.length === 0) {
70501
+ return [
70502
+ {
70503
+ id: "knowledge:none",
70504
+ label: "no AGENTS.md files found",
70505
+ estTokens: 0
70506
+ }
70507
+ ];
70508
+ }
70509
+ return files.map((file) => {
70510
+ const lineCount = file.content.split("\n").length;
70511
+ return {
70512
+ id: `knowledge:${file.path}`,
70513
+ label: file.path,
70514
+ estTokens: estimateStringTokens(file.content),
70515
+ note: `${lineCount} lines`,
70516
+ detail: `AGENTS.md`,
70517
+ drill: {
70518
+ kind: "text",
70519
+ title: `Project Context \xB7 ${file.path}`,
70520
+ text: file.content
70521
+ }
70522
+ };
70523
+ });
70524
+ }
70525
+ function estimateToolTokens(tool) {
70526
+ const parameters = tool.parameters === void 0 ? "" : JSON.stringify(tool.parameters);
70527
+ return estimateStringTokens(`${tool.name}
70528
+ ${tool.description ?? ""}
70529
+ ${parameters}`);
70530
+ }
70531
+ var MAX_TOOL_DRILL_CHARS = 6e3;
70532
+ function buildToolRows(tools) {
70533
+ if (tools.length === 0) {
70534
+ return [{ id: "tools:none", label: "no tools registered", estTokens: 0 }];
70535
+ }
70536
+ return tools.map((tool) => {
70537
+ const paramsJson = tool.parameters === void 0 ? "" : JSON.stringify(tool.parameters, null, 2);
70538
+ const drillText = [
70539
+ tool.description ? `# description
70540
+ ${tool.description}` : "",
70541
+ paramsJson ? `# parameters (schema)
70542
+ ${paramsJson}` : ""
70543
+ ].filter(Boolean).join("\n\n").slice(0, MAX_TOOL_DRILL_CHARS);
70544
+ return {
70545
+ id: `tool:${tool.name}`,
70546
+ label: tool.name,
70547
+ estTokens: estimateToolTokens(tool),
70548
+ note: tool.label && tool.label !== tool.name ? tool.label : void 0,
70549
+ detail: tool.description?.split("\n")[0]?.slice(0, 120),
70550
+ drill: { kind: "text", title: `Tool \xB7 ${tool.name}`, text: drillText }
70551
+ };
70552
+ }).sort((a, b2) => b2.estTokens - a.estTokens);
70553
+ }
70554
+ function entryRoleTag(entry) {
70555
+ return entry.role ?? entry.type;
70556
+ }
70557
+ function buildMessageRows(entries, compactionTokensBefore) {
70558
+ if (entries.length === 0) {
70559
+ return [{ id: "messages:none", label: "active context is empty", estTokens: 0 }];
70560
+ }
70561
+ return entries.map((entry) => {
70562
+ const roleTag = entryRoleTag(entry);
70563
+ const isCompaction = entry.type === "compaction";
70564
+ const tokensBefore = isCompaction ? compactionTokensBefore?.(entry.id) : void 0;
70565
+ return {
70566
+ id: `entry:${entry.id}`,
70567
+ label: `#${entry.seq} ${roleTag}`,
70568
+ estTokens: entry.estTokens,
70569
+ roleTag,
70570
+ marker: isCompaction ? "\u2691" : void 0,
70571
+ note: isCompaction ? tokensBefore !== void 0 ? `checkpoint \xB7 context was ~${formatReportTokens(tokensBefore)} before` : "checkpoint" : entry.bodyBytes > 0 ? `${formatReportTokens(entry.bodyBytes)}B body` : void 0,
70572
+ detail: entry.preview,
70573
+ drill: { kind: "entry", entryId: entry.id }
70574
+ };
70575
+ });
70576
+ }
70577
+ var MAX_PROMPT_DRILL_CHARS = 12e3;
70578
+ function buildContextReportData(inputs) {
70579
+ const knowledgeSectionText = inputs.knowledgeFiles.map((file) => `### ${file.path}
70580
+ ${file.content}`).join("\n\n");
70581
+ const blocks = buildSystemPromptBlocks(
70582
+ inputs.systemPrompt,
70583
+ inputs.skillsSection,
70584
+ knowledgeSectionText
70585
+ );
70586
+ const systemPromptRows = [
70587
+ {
70588
+ id: "prompt:base",
70589
+ label: "base prompt (persona + tool guidance + guidelines + docs)",
70590
+ estTokens: blocks.baseTokens,
70591
+ drill: {
70592
+ kind: "text",
70593
+ title: "System Prompt",
70594
+ text: inputs.systemPrompt.slice(0, MAX_PROMPT_DRILL_CHARS)
70595
+ }
70596
+ }
70597
+ ];
70598
+ if (blocks.skillsTokens !== null || inputs.skills.length > 0) {
70599
+ systemPromptRows.push({
70600
+ id: "prompt:skills",
70601
+ label: "skills section",
70602
+ estTokens: blocks.skillsTokens ?? 0,
70603
+ note: `${inputs.skills.length} skills`,
70604
+ drill: {
70605
+ kind: "rows",
70606
+ title: "System Prompt \u203A Skills",
70607
+ rows: buildSkillRows(inputs.skillsSection, inputs.skills),
70608
+ rowKind: "plain"
70609
+ }
70610
+ });
70611
+ }
70612
+ if (blocks.knowledgeTokens !== null || inputs.knowledgeFiles.length > 0) {
70613
+ systemPromptRows.push({
70614
+ id: "prompt:knowledge",
70615
+ label: "project context (AGENTS.md)",
70616
+ estTokens: blocks.knowledgeTokens ?? 0,
70617
+ note: `${inputs.knowledgeFiles.length} files`,
70618
+ drill: {
70619
+ kind: "rows",
70620
+ title: "System Prompt \u203A Project Context",
70621
+ rows: buildKnowledgeRows(inputs.knowledgeFiles),
70622
+ rowKind: "plain"
70623
+ }
70624
+ });
70625
+ }
70626
+ const systemPromptSection = {
70627
+ id: "system-prompt",
70628
+ title: "System prompt",
70629
+ estTokens: blocks.totalTokens,
70630
+ note: `base ~${formatReportTokens(blocks.baseTokens)}`,
70631
+ rows: systemPromptRows
70632
+ };
70633
+ const toolRows = buildToolRows(inputs.tools);
70634
+ const toolsTokens = toolRows.reduce((sum, row) => sum + row.estTokens, 0);
70635
+ const toolsSection = {
70636
+ id: "tools",
70637
+ title: `Tools (${inputs.tools.length})`,
70638
+ estTokens: toolsTokens,
70639
+ note: toolRows[0] ? `largest: ${toolRows[0].label} ~${formatReportTokens(toolRows[0].estTokens)}` : void 0,
70640
+ rows: toolRows
70641
+ };
70642
+ const messageRows = buildMessageRows(inputs.entries, inputs.compactionTokensBefore);
70643
+ const messagesTokens = inputs.entries.reduce((sum, entry) => sum + entry.estTokens, 0);
70644
+ const messagesSection = {
70645
+ id: "messages",
70646
+ title: `Messages (${inputs.entries.length})`,
70647
+ estTokens: messagesTokens,
70648
+ rows: messageRows
70649
+ };
70650
+ const dynamicContextSection = {
70651
+ id: "dynamic-context",
70652
+ title: "Dynamic context",
70653
+ estTokens: 0,
70654
+ note: "unmetered",
70655
+ rows: [
70656
+ {
70657
+ id: "dynamic:runtime-mode",
70658
+ label: "runtime-mode per-turn injections are not counted in these estimates",
70659
+ estTokens: 0,
70660
+ detail: "the host injects a <runtime-mode-context> block (mode flags, date, active tool names) on every request via transformContext; it is part of the est\u2212actual gap below"
70661
+ }
70662
+ ]
70663
+ };
70664
+ const roleCountsMap = /* @__PURE__ */ new Map();
70665
+ for (const entry of inputs.entries) {
70666
+ const tag = entryRoleTag(entry);
70667
+ roleCountsMap.set(tag, (roleCountsMap.get(tag) ?? 0) + 1);
70668
+ }
70669
+ const roleCounts = [...roleCountsMap.entries()].map(([role, count]) => ({ role, count })).sort((a, b2) => b2.count - a.count || a.role.localeCompare(b2.role));
70670
+ const topEntries = [...inputs.entries].sort((a, b2) => b2.estTokens - a.estTokens).slice(0, 5).map((entry) => ({
70671
+ seq: entry.seq,
70672
+ label: `${entryRoleTag(entry)} \xB7 ${entry.preview}`.trim(),
70673
+ estTokens: entry.estTokens
70674
+ }));
70675
+ let latestCompaction = null;
70676
+ for (const entry of inputs.entries) {
70677
+ if (entry.type === "compaction") {
70678
+ latestCompaction = {
70679
+ seq: entry.seq,
70680
+ tokensBefore: inputs.compactionTokensBefore?.(entry.id)
70681
+ };
70682
+ }
70683
+ }
70684
+ const estimatedTotalTokens = blocks.totalTokens + toolsTokens + messagesTokens;
70685
+ return {
70686
+ sections: [systemPromptSection, toolsSection, messagesSection, dynamicContextSection],
70687
+ systemPromptTokens: blocks.totalTokens,
70688
+ toolsTokens,
70689
+ messagesTokens,
70690
+ dynamicContextTokens: 0,
70691
+ estimatedTotalTokens,
70692
+ actualTokens: inputs.actualTokens,
70693
+ contextWindow: inputs.contextWindow,
70694
+ autoCompactLimitTokens: inputs.autoCompactLimitTokens,
70695
+ reserveTokens: inputs.reserveTokens,
70696
+ promptPrefixHash: inputs.promptPrefixHash,
70697
+ promptPrefixChanged: inputs.promptPrefixChanged,
70698
+ roleCounts,
70699
+ topEntries,
70700
+ latestCompaction,
70701
+ entryCount: inputs.entries.length
70702
+ };
70703
+ }
70704
+ var identityTheme = {
70705
+ bold: (text) => text,
70706
+ fg: (_token, text) => text
70707
+ };
70708
+ function padEndVisible(text, width) {
70709
+ return text.length >= width ? text : text + " ".repeat(width - text.length);
70710
+ }
70711
+ function formatReportHeadline(data) {
70712
+ const est = `~${formatReportTokens(data.estimatedTotalTokens)}`;
70713
+ if (data.contextWindow > 0) {
70714
+ const percent = (data.estimatedTotalTokens / data.contextWindow * 100).toFixed(1);
70715
+ return `est ${est} / ${formatReportTokens(data.contextWindow)} (${percent}%)`;
70716
+ }
70717
+ return `est ${est} / window unknown`;
70718
+ }
70719
+ function buildContextOverviewText(data, themeInput) {
70720
+ const theme2 = themeInput ?? identityTheme;
70721
+ const lines = [];
70722
+ const sections = data.sections;
70723
+ lines.push(theme2.bold("Context Report") + theme2.fg("dim", ` \xB7 ${formatReportHeadline(data)}`));
70724
+ const labelWidth = 20;
70725
+ const branch = (index) => index === sections.length ? "\u2514" : "\u251C";
70726
+ sections.forEach((section, index) => {
70727
+ const label = padEndVisible(` ${branch(index)} ${section.title}`, labelWidth);
70728
+ const tokens = `~${formatReportTokens(section.estTokens)}`;
70729
+ const note = section.note ? theme2.fg("dim", ` ${section.note}`) : "";
70730
+ lines.push(`${label}${theme2.fg("accent", tokens)}${note}`);
70731
+ if (section.id === "messages") {
70732
+ const counts = data.roleCounts.map((rc) => `${rc.role} ${rc.count}`).join(" \xB7 ");
70733
+ if (counts) {
70734
+ lines.push(`${" ".repeat(labelWidth + 1)}${theme2.fg("dim", counts)}`);
70735
+ }
70736
+ for (const top of data.topEntries.slice(0, 3)) {
70737
+ const preview = top.label.length > 60 ? `${top.label.slice(0, 59)}\u2026` : top.label;
70738
+ lines.push(
70739
+ `${" ".repeat(labelWidth + 1)}${theme2.fg("dim", `top: #${top.seq} ${preview} (~${formatReportTokens(top.estTokens)})`)}`
70740
+ );
70741
+ }
70742
+ if (data.latestCompaction) {
70743
+ const before = data.latestCompaction.tokensBefore !== void 0 ? ` \xB7 context was ~${formatReportTokens(data.latestCompaction.tokensBefore)} before` : "";
70744
+ lines.push(
70745
+ `${" ".repeat(labelWidth + 1)}${theme2.fg("dim", `\u2691 compact checkpoint @#${data.latestCompaction.seq}${before}`)}`
70746
+ );
70747
+ }
70748
+ }
70749
+ });
70750
+ const autoCompact = data.autoCompactLimitTokens ? `triggers > ~${formatReportTokens(data.autoCompactLimitTokens)} (reserve ${formatReportTokens(data.reserveTokens)})` : "disabled";
70751
+ lines.push(
70752
+ `${padEndVisible(` ${branch(sections.length)} auto-compact`, labelWidth)}${theme2.fg("dim", autoCompact)}`
70753
+ );
70754
+ const reconciliation = data.actualTokens !== null ? `actual ${formatReportTokens(data.actualTokens)} (last response) \xB7 est\u2212actual \u2248 ${formatReportTokens(
70755
+ Math.abs(data.estimatedTotalTokens - data.actualTokens)
70756
+ )}${data.estimatedTotalTokens >= data.actualTokens ? " overhead" : " undercount"}` : "actual unknown until the next model response";
70757
+ lines.push(` ${theme2.fg("dim", reconciliation)}`);
70758
+ lines.push(theme2.fg("dim", " /debugs/context \xB7 Enter to drill into a section, Esc to go back"));
70759
+ return lines.join("\n");
70760
+ }
70761
+ function buildWindowRows(data) {
70762
+ const rows = [
70763
+ {
70764
+ id: "window:window",
70765
+ label: "context window",
70766
+ estTokens: data.contextWindow,
70767
+ valueText: data.contextWindow > 0 ? formatReportTokens(data.contextWindow) : "?",
70768
+ note: data.contextWindow > 0 ? `${(data.estimatedTotalTokens / data.contextWindow * 100).toFixed(1)}% used (est)` : "unknown"
70769
+ },
70770
+ {
70771
+ id: "window:estimated",
70772
+ label: "estimated total (prompt + tools + messages)",
70773
+ estTokens: data.estimatedTotalTokens,
70774
+ note: `prompt ~${formatReportTokens(data.systemPromptTokens)} \xB7 tools ~${formatReportTokens(data.toolsTokens)} \xB7 messages ~${formatReportTokens(data.messagesTokens)}`
70775
+ }
70776
+ ];
70777
+ if (data.actualTokens !== null) {
70778
+ rows.push({
70779
+ id: "window:actual",
70780
+ label: "actual (last model response)",
70781
+ estTokens: data.actualTokens,
70782
+ valueText: formatReportTokens(data.actualTokens),
70783
+ note: `gap \u2248 ${formatReportTokens(Math.abs(data.estimatedTotalTokens - data.actualTokens))}`
70784
+ });
70785
+ }
70786
+ rows.push({
70787
+ id: "window:auto-compact",
70788
+ label: "auto-compact threshold",
70789
+ estTokens: data.autoCompactLimitTokens ?? 0,
70790
+ valueText: data.autoCompactLimitTokens ? `>${formatReportTokens(data.autoCompactLimitTokens)}` : "off",
70791
+ note: data.autoCompactLimitTokens ? `reserve ${formatReportTokens(data.reserveTokens)}` : "disabled"
70792
+ });
70793
+ if (data.promptPrefixHash) {
70794
+ rows.push({
70795
+ id: "window:prefix-hash",
70796
+ label: "prompt+tools hash (cache prefix)",
70797
+ estTokens: 0,
70798
+ valueText: data.promptPrefixHash,
70799
+ note: data.promptPrefixChanged ? "changed since last turn \u2014 provider cache invalidated \u26A1" : "stable \u2014 cache-friendly"
70800
+ });
70801
+ }
70802
+ return rows;
70803
+ }
70804
+
70345
70805
  // src/components/tree-selector.ts
70346
70806
  var RESET = "\x1B[0m";
70347
70807
  var INVERSE = "\x1B[7m";
@@ -70812,12 +71272,13 @@ var TreeSelectorComponent = class {
70812
71272
  const activeIndicator = this.options.activePathSeqs.has(row.summary.seq) ? theme.fg("success", TREE_CHARS.active) : DIM + TREE_CHARS.inactive + RESET;
70813
71273
  const expandIndicator = row.inWindowChildren ? theme.fg("dim", this.collapsed.has(row.summary.id) ? "\u25B6 " : "\u25BC ") : row.hasChildren ? theme.fg("dim", "\xB7\xB7") : " ";
70814
71274
  const role = roleColor(roleName(row.summary));
71275
+ const tokenNote = row.summary.estTokens > 0 ? theme.fg("dim", ` ~${formatReportTokens(row.summary.estTokens)}`) : "";
70815
71276
  const indicatorWidth = visibleWidth(activeIndicator) + visibleWidth(expandIndicator) + visibleWidth(role) + 2;
70816
- const prefixBudget = Math.max(0, Math.min(Math.floor(width * 0.35), width - indicatorWidth - MIN_BODY_WIDTH));
71277
+ const prefixBudget = Math.max(0, Math.min(Math.floor(width * 0.35), width - indicatorWidth - MIN_BODY_WIDTH - visibleWidth(tokenNote)));
70817
71278
  const displayPrefix = clampPrefixForDisplay(row.prefix, prefixBudget);
70818
- const availableWidth = Math.max(0, width - visibleWidth(displayPrefix) - indicatorWidth);
71279
+ const availableWidth = Math.max(0, width - visibleWidth(displayPrefix) - indicatorWidth - visibleWidth(tokenNote) - 2);
70819
71280
  const content = truncateToWidth(row.summary.preview ?? "", availableWidth);
70820
- lines.push(createLine(`${displayPrefix}${activeIndicator}${expandIndicator}${role}: ${content}`, width, i === this.selectedIndex));
71281
+ lines.push(createLine(`${displayPrefix}${activeIndicator}${expandIndicator}${role}: ${content}${tokenNote}`, width, i === this.selectedIndex));
70821
71282
  }
70822
71283
  for (let i = endIndex - startIndex; i < this.maxVisibleLines; i++) {
70823
71284
  lines.push(createLine("", width, false));
@@ -72156,6 +72617,29 @@ function deriveSessionTitle(options) {
72156
72617
  }
72157
72618
  return `S:${activeSession.id.slice(-6)}`;
72158
72619
  }
72620
+ function calculateSessionUsageTotals(messages) {
72621
+ let input = 0;
72622
+ let output = 0;
72623
+ let cacheRead = 0;
72624
+ let cacheWrite = 0;
72625
+ let cost = 0;
72626
+ for (const message of messages) {
72627
+ if (message.role !== "assistant") continue;
72628
+ const usage = message.usage;
72629
+ if (!usage) continue;
72630
+ input += usage.input;
72631
+ output += usage.output;
72632
+ cacheRead += usage.cacheRead;
72633
+ cacheWrite += usage.cacheWrite;
72634
+ cost += usage.cost?.total ?? 0;
72635
+ }
72636
+ return { input, output, cacheRead, cacheWrite, cost };
72637
+ }
72638
+ function formatTokensCompact(count) {
72639
+ if (count < 1e3) return String(count);
72640
+ if (count < 1e6) return `${(count / 1e3).toFixed(1)}k`;
72641
+ return `${(count / 1e6).toFixed(2)}M`;
72642
+ }
72159
72643
  function buildSessionInfoText(options) {
72160
72644
  const displayCwd = options.homeDir && options.cwd.startsWith(options.homeDir) ? `~${options.cwd.slice(options.homeDir.length)}` : options.cwd;
72161
72645
  let info = options.theme.bold("Session Info") + "\n\n";
@@ -72178,6 +72662,39 @@ function buildSessionInfoText(options) {
72178
72662
  `;
72179
72663
  info += `${options.theme.fg("dim", "Thinking Level:")} ${options.thinkingLevel}
72180
72664
  `;
72665
+ if (options.messages && options.messages.length > 0) {
72666
+ const totals = calculateSessionUsageTotals(options.messages);
72667
+ const turnCount = options.messages.filter((m2) => m2.role === "assistant").length;
72668
+ if (turnCount > 0) {
72669
+ info += `
72670
+ ${options.theme.bold("Usage (active context)")}
72671
+ `;
72672
+ info += `${options.theme.fg("dim", "Turns:")} ${turnCount}
72673
+ `;
72674
+ info += `${options.theme.fg("dim", "Input:")} ${totals.input.toLocaleString()}
72675
+ `;
72676
+ info += `${options.theme.fg("dim", "Output:")} ${totals.output.toLocaleString()}
72677
+ `;
72678
+ if (totals.cacheRead > 0) {
72679
+ info += `${options.theme.fg("dim", "Cache Read:")} ${totals.cacheRead.toLocaleString()}
72680
+ `;
72681
+ }
72682
+ if (totals.cacheWrite > 0) {
72683
+ info += `${options.theme.fg("dim", "Cache Write:")} ${totals.cacheWrite.toLocaleString()}
72684
+ `;
72685
+ }
72686
+ if (totals.cost > 0) {
72687
+ info += `${options.theme.fg("dim", "Cost:")} $${totals.cost.toFixed(4)}
72688
+ `;
72689
+ }
72690
+ const hitRateBase = totals.input + totals.cacheRead;
72691
+ if (hitRateBase > 0 && totals.cacheRead > 0) {
72692
+ const hitRate = (totals.cacheRead / hitRateBase * 100).toFixed(1);
72693
+ info += `${options.theme.fg("dim", "Cache Hit Rate:")} ${hitRate}% (~${formatTokensCompact(hitRateBase)} served)
72694
+ `;
72695
+ }
72696
+ }
72697
+ }
72181
72698
  return info;
72182
72699
  }
72183
72700
  function extractUserText(msg) {
@@ -72309,6 +72826,7 @@ async function loadSession(host, sessionId) {
72309
72826
  contextPercent,
72310
72827
  contextWindow
72311
72828
  });
72829
+ applyContextSignalsToFooter(host);
72312
72830
  host.ui.requestRender();
72313
72831
  setTimeout(() => host.ui.requestRender(), 100);
72314
72832
  } catch (error) {
@@ -73086,8 +73604,9 @@ var BUILTIN_SLASH_COMMANDS = [
73086
73604
  { name: "settings/calm", description: "Toggle calm mode: show only questions and final answers" },
73087
73605
  { name: "settings/yolo", description: "Toggle or set YOLO mode" },
73088
73606
  { name: "debugs/cache-test", description: "Run provider cache diagnostics with a dedicated 2-request probe" },
73089
- { name: "debugs/context", description: "Append a context summary to ~/.pie/logs/pie-cli.log" },
73090
- { name: "debugs/package", description: "Append a package summary to ~/.pie/logs/pie-cli.log" },
73607
+ { name: "debugs/context", description: "Context composition report: sections, tokens, drill-down" },
73608
+ { name: "debugs/trace", description: "Session trace: timeline, tool stats, runtime events" },
73609
+ { name: "debugs/boot", description: "Boot composition pipeline: how the runtime was assembled" },
73091
73610
  { name: "settings/quit", description: "Exit Pie" },
73092
73611
  { name: "skills", description: "Manage skills: list, toggle enable/disable, invoke" },
73093
73612
  { name: "skills/list", description: "List available skills (plain text)" },
@@ -73345,6 +73864,7 @@ function bindInteractiveRuntimeEvents(host) {
73345
73864
  contextPercent,
73346
73865
  contextWindow
73347
73866
  });
73867
+ applyContextSignalsToFooter(host);
73348
73868
  host.safeRequestRender();
73349
73869
  host.sessionManager.save().catch((err) => {
73350
73870
  console.error("[Session] Auto-save failed:", err);
@@ -73793,7 +74313,7 @@ function assistantDisplayBlocks(content) {
73793
74313
  }
73794
74314
 
73795
74315
  // src/commands/interactive-debug-commands.ts
73796
- import { createHash as createHash4 } from "crypto";
74316
+ import { createHash as createHash5 } from "crypto";
73797
74317
  function errorMessage(error) {
73798
74318
  return error instanceof Error ? error.message : String(error);
73799
74319
  }
@@ -73831,7 +74351,7 @@ async function handleCacheTest(host) {
73831
74351
  timestamp: fixedTimestamp
73832
74352
  }
73833
74353
  ];
73834
- const prefixHash = createHash4("sha256").update(
74354
+ const prefixHash = createHash5("sha256").update(
73835
74355
  JSON.stringify({
73836
74356
  systemPrompt: stablePrefix,
73837
74357
  tools: toolDefs.map((tool) => ({
@@ -73940,126 +74460,6 @@ async function handleCacheTest(host) {
73940
74460
  host.chatContainer.addChild(new Spacer(1));
73941
74461
  host.ui.requestRender();
73942
74462
  }
73943
- async function handleDebugContextCommand(host) {
73944
- host.chatContainer.addChild(new Spacer(1));
73945
- host.chatContainer.addChild(new Text(theme.bold("Debug Context"), 1, 0));
73946
- host.ui.requestRender();
73947
- try {
73948
- const state = host.agent.state;
73949
- const debugInfo = {
73950
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
73951
- systemPrompt: state.systemPrompt,
73952
- model: state.model,
73953
- thinkingLevel: state.thinkingLevel,
73954
- tools: state.tools.map((t) => ({
73955
- name: t.name,
73956
- description: t.description,
73957
- label: t.label
73958
- })),
73959
- messageCount: state.messages.length,
73960
- messages: state.messages,
73961
- isStreaming: state.isStreaming,
73962
- pendingToolCalls: Array.from(state.pendingToolCalls)
73963
- };
73964
- getLogger().child({ module: "context_dump" }).info("Agent context snapshot", {
73965
- snapshot: {
73966
- timestamp: debugInfo.timestamp,
73967
- modelId: debugInfo.model?.id ?? "",
73968
- provider: debugInfo.model?.provider ?? "",
73969
- thinkingLevel: debugInfo.thinkingLevel,
73970
- toolCount: debugInfo.tools.length,
73971
- messageCount: debugInfo.messageCount,
73972
- isStreaming: debugInfo.isStreaming,
73973
- pendingToolCalls: debugInfo.pendingToolCalls.length
73974
- }
73975
- });
73976
- host.chatContainer.addChild(new Text(
73977
- theme.fg("success", "\u2713 Context summary appended to ~/.pie/logs/pie-cli.log"),
73978
- 1,
73979
- 0
73980
- ));
73981
- host.chatContainer.addChild(new Text(
73982
- theme.fg("dim", ` Messages: ${state.messages.length}, Tools: ${state.tools.length}`),
73983
- 1,
73984
- 0
73985
- ));
73986
- } catch (err) {
73987
- host.showError(`Failed to save context: ${errorMessage(err)}`);
73988
- }
73989
- host.chatContainer.addChild(new Spacer(1));
73990
- host.ui.requestRender();
73991
- }
73992
- async function handleDebugPackageCommand(host) {
73993
- host.chatContainer.addChild(new Spacer(1));
73994
- host.chatContainer.addChild(new Text(theme.bold("Debug Package"), 1, 0));
73995
- host.ui.requestRender();
73996
- try {
73997
- const messages = host.agent.state.messages;
73998
- const packageInfo = {
73999
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
74000
- totalMessages: messages.length,
74001
- conversation: messages.map((msg, index) => {
74002
- const baseInfo = {
74003
- index,
74004
- role: msg.role
74005
- };
74006
- if (msg.role === "user") {
74007
- return {
74008
- ...baseInfo,
74009
- direction: "\u2192 SEND",
74010
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2)
74011
- };
74012
- } else if (msg.role === "assistant") {
74013
- const assistantMsg = msg;
74014
- const toolCalls = assistantMsg.content.filter((c) => c.type === "toolCall").map((c) => ({ name: c.name, arguments: c.arguments, id: c.id }));
74015
- return {
74016
- ...baseInfo,
74017
- direction: "\u2190 RECEIVE",
74018
- content: assistantMsg.content,
74019
- toolCalls,
74020
- usage: assistantMsg.usage
74021
- };
74022
- } else if (msg.role === "toolResult") {
74023
- const toolMsg = msg;
74024
- return {
74025
- ...baseInfo,
74026
- direction: "\u2192 TOOL RESULT",
74027
- toolCallId: toolMsg.toolCallId,
74028
- content: toolMsg.content
74029
- };
74030
- }
74031
- return baseInfo;
74032
- })
74033
- };
74034
- getLogger().child({ module: "package_dump" }).info("Agent package snapshot", {
74035
- snapshot: {
74036
- timestamp: packageInfo.timestamp,
74037
- totalMessages: packageInfo.totalMessages,
74038
- userMessages: packageInfo.conversation.filter((m2) => m2.role === "user").length,
74039
- assistantMessages: packageInfo.conversation.filter((m2) => m2.role === "assistant").length,
74040
- toolMessages: packageInfo.conversation.filter((m2) => m2.role === "toolResult").length,
74041
- lastMessageRole: packageInfo.conversation.length > 0 ? packageInfo.conversation[packageInfo.conversation.length - 1].role : ""
74042
- }
74043
- });
74044
- host.chatContainer.addChild(new Text(
74045
- theme.fg("success", "\u2713 Package summary appended to ~/.pie/logs/pie-cli.log"),
74046
- 1,
74047
- 0
74048
- ));
74049
- const userCount = messages.filter((m2) => m2.role === "user").length;
74050
- const assistantCount = messages.filter((m2) => m2.role === "assistant").length;
74051
- const toolCount = messages.filter((m2) => m2.role === "toolResult").length;
74052
- host.chatContainer.addChild(new Text(
74053
- theme.fg("dim", ` User: ${userCount}, Assistant: ${assistantCount}, Tool: ${toolCount}`),
74054
- 1,
74055
- 0
74056
- ));
74057
- } catch (err) {
74058
- host.showError(`Failed to save package log: ${errorMessage(err)}`);
74059
- }
74060
- host.chatContainer.addChild(new Spacer(1));
74061
- host.ui.requestRender();
74062
- }
74063
74463
 
74064
74464
  // src/commands/interactive-skill-commands.ts
74065
74465
  import * as fs24 from "fs";
@@ -74636,6 +75036,944 @@ function handleToolsCommand(host, args) {
74636
75036
  showToolsManager(host);
74637
75037
  }
74638
75038
 
75039
+ // src/components/context-report-selector.ts
75040
+ var RESET2 = "\x1B[0m";
75041
+ var INVERSE2 = "\x1B[7m";
75042
+ var ROLE_FILTER_CYCLE = ["all", "user", "assistant", "toolResult", "compaction"];
75043
+ function createLine2(content, width, isSelected) {
75044
+ const normalized = content.replace(/[\r\n]+/g, " ");
75045
+ const body = isSelected ? INVERSE2 + normalized : normalized;
75046
+ return truncateToWidth(body + RESET2, width, "...", true);
75047
+ }
75048
+ function buildRootRows(sections, windowSection) {
75049
+ const rows = sections.map((section) => ({
75050
+ id: `section:${section.id}`,
75051
+ label: section.title,
75052
+ estTokens: section.estTokens,
75053
+ note: section.note,
75054
+ detail: section.id === "messages" ? "active context entries (Enter to list)" : void 0,
75055
+ drill: {
75056
+ kind: "rows",
75057
+ title: `${section.title}`,
75058
+ rows: section.rows,
75059
+ rowKind: section.id === "messages" ? "messages" : "plain"
75060
+ }
75061
+ }));
75062
+ if (windowSection) {
75063
+ rows.push({
75064
+ id: "section:window",
75065
+ label: windowSection.label,
75066
+ estTokens: windowSection.estTokens,
75067
+ valueText: windowSection.valueText,
75068
+ detail: windowSection.detail,
75069
+ drill: {
75070
+ kind: "rows",
75071
+ title: windowSection.title ?? windowSection.label,
75072
+ rows: windowSection.rows,
75073
+ rowKind: "plain"
75074
+ }
75075
+ });
75076
+ }
75077
+ return rows;
75078
+ }
75079
+ var ContextReportSelectorComponent = class {
75080
+ options;
75081
+ maxVisibleLines;
75082
+ views = [];
75083
+ selectedIndex = 0;
75084
+ searchQuery = "";
75085
+ statusMessage = null;
75086
+ _focused = false;
75087
+ get focused() {
75088
+ return this._focused;
75089
+ }
75090
+ set focused(value) {
75091
+ this._focused = value;
75092
+ }
75093
+ constructor(options) {
75094
+ this.options = options;
75095
+ this.maxVisibleLines = Math.max(15, Math.floor(options.terminalHeight * 0.6));
75096
+ this.views = [
75097
+ {
75098
+ kind: "list",
75099
+ title: options.title,
75100
+ rows: buildRootRows(options.sections, options.windowSection),
75101
+ rowKind: "root",
75102
+ roleFilter: "all",
75103
+ sortBySize: false
75104
+ }
75105
+ ];
75106
+ }
75107
+ // ---------------------------------------------------------------------------
75108
+ // Filtering / sorting
75109
+ // ---------------------------------------------------------------------------
75110
+ get currentView() {
75111
+ return this.views[this.views.length - 1];
75112
+ }
75113
+ visibleListRows(view) {
75114
+ let rows = view.rows;
75115
+ if (view.roleFilter !== "all") {
75116
+ rows = rows.filter((row) => row.roleTag === view.roleFilter);
75117
+ }
75118
+ if (view.rowKind === "messages" && view.sortBySize) {
75119
+ rows = [...rows].sort((a, b2) => b2.estTokens - a.estTokens);
75120
+ }
75121
+ if (this.searchQuery) {
75122
+ const query = this.searchQuery.toLowerCase();
75123
+ rows = rows.filter(
75124
+ (row) => `${row.label} ${row.detail ?? ""} ${row.note ?? ""} ${row.roleTag ?? ""}`.toLowerCase().includes(query)
75125
+ );
75126
+ }
75127
+ return rows;
75128
+ }
75129
+ applyFilterChange() {
75130
+ this.selectedIndex = 0;
75131
+ this.statusMessage = null;
75132
+ }
75133
+ // ---------------------------------------------------------------------------
75134
+ // Navigation
75135
+ // ---------------------------------------------------------------------------
75136
+ moveUp() {
75137
+ const view = this.currentView;
75138
+ if (view.kind === "list") {
75139
+ const rows = this.visibleListRows(view);
75140
+ if (rows.length > 0) {
75141
+ this.selectedIndex = (this.selectedIndex - 1 + rows.length) % rows.length;
75142
+ }
75143
+ } else {
75144
+ view.scroll = Math.max(0, view.scroll - 1);
75145
+ }
75146
+ }
75147
+ moveDown() {
75148
+ const view = this.currentView;
75149
+ if (view.kind === "list") {
75150
+ const rows = this.visibleListRows(view);
75151
+ if (rows.length > 0) {
75152
+ this.selectedIndex = (this.selectedIndex + 1) % rows.length;
75153
+ }
75154
+ } else {
75155
+ view.scroll = Math.min(Math.max(0, view.lines.length - 1), view.scroll + 1);
75156
+ }
75157
+ }
75158
+ pageUp() {
75159
+ const view = this.currentView;
75160
+ if (view.kind === "list") {
75161
+ this.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisibleLines);
75162
+ } else {
75163
+ view.scroll = Math.max(0, view.scroll - this.maxVisibleLines);
75164
+ }
75165
+ }
75166
+ pageDown() {
75167
+ const view = this.currentView;
75168
+ if (view.kind === "list") {
75169
+ const rows = this.visibleListRows(view);
75170
+ this.selectedIndex = Math.min(Math.max(0, rows.length - 1), this.selectedIndex + this.maxVisibleLines);
75171
+ } else {
75172
+ view.scroll = Math.min(
75173
+ Math.max(0, view.lines.length - 1),
75174
+ view.scroll + this.maxVisibleLines
75175
+ );
75176
+ }
75177
+ }
75178
+ pushTextView(title, text) {
75179
+ this.searchQuery = "";
75180
+ this.views.push({
75181
+ kind: "text",
75182
+ title,
75183
+ lines: text.replace(/\r\n/g, "\n").split("\n"),
75184
+ scroll: 0
75185
+ });
75186
+ this.selectedIndex = 0;
75187
+ this.statusMessage = null;
75188
+ }
75189
+ drillIntoSelected() {
75190
+ const view = this.currentView;
75191
+ if (view.kind !== "list") return;
75192
+ const rows = this.visibleListRows(view);
75193
+ const row = rows[this.selectedIndex];
75194
+ if (!row) return;
75195
+ if (!row.drill) {
75196
+ this.statusMessage = "no further detail";
75197
+ return;
75198
+ }
75199
+ switch (row.drill.kind) {
75200
+ case "rows":
75201
+ this.searchQuery = "";
75202
+ this.views.push({
75203
+ kind: "list",
75204
+ title: row.drill.title,
75205
+ rows: row.drill.rows,
75206
+ rowKind: row.drill.rowKind,
75207
+ roleFilter: "all",
75208
+ sortBySize: false
75209
+ });
75210
+ this.selectedIndex = 0;
75211
+ this.statusMessage = null;
75212
+ break;
75213
+ case "text":
75214
+ this.pushTextView(row.drill.title, row.drill.text);
75215
+ break;
75216
+ case "entry": {
75217
+ const text = this.options.getEntryText?.(row.drill.entryId) ?? null;
75218
+ if (text === null) {
75219
+ this.statusMessage = "entry body unavailable";
75220
+ } else {
75221
+ this.pushTextView(`Entry \xB7 ${row.label}`, text);
75222
+ }
75223
+ break;
75224
+ }
75225
+ }
75226
+ }
75227
+ popView() {
75228
+ if (this.views.length > 1) {
75229
+ this.views.pop();
75230
+ this.selectedIndex = 0;
75231
+ this.statusMessage = null;
75232
+ } else {
75233
+ this.options.onDone();
75234
+ }
75235
+ }
75236
+ // ---------------------------------------------------------------------------
75237
+ // Input
75238
+ // ---------------------------------------------------------------------------
75239
+ handleInput(keyData) {
75240
+ const kb = getEditorKeybindings();
75241
+ if (kb.matches(keyData, "selectUp")) {
75242
+ this.moveUp();
75243
+ } else if (kb.matches(keyData, "selectDown")) {
75244
+ this.moveDown();
75245
+ } else if (kb.matches(keyData, "selectPageUp")) {
75246
+ this.pageUp();
75247
+ } else if (kb.matches(keyData, "selectPageDown")) {
75248
+ this.pageDown();
75249
+ } else if (kb.matches(keyData, "selectConfirm")) {
75250
+ this.drillIntoSelected();
75251
+ } else if (kb.matches(keyData, "selectCancel")) {
75252
+ if (this.searchQuery) {
75253
+ this.searchQuery = "";
75254
+ this.applyFilterChange();
75255
+ } else {
75256
+ this.popView();
75257
+ }
75258
+ } else if (matchesKey(keyData, "ctrl+u")) {
75259
+ const view = this.currentView;
75260
+ if (view.kind === "list" && view.rowKind === "messages") {
75261
+ const next = ROLE_FILTER_CYCLE[(ROLE_FILTER_CYCLE.indexOf(view.roleFilter) + 1) % ROLE_FILTER_CYCLE.length];
75262
+ view.roleFilter = next;
75263
+ this.applyFilterChange();
75264
+ }
75265
+ } else if ((keyData === "s" || keyData === "S") && this.searchQuery === "") {
75266
+ const view = this.currentView;
75267
+ if (view.kind === "list" && view.rowKind === "messages") {
75268
+ view.sortBySize = !view.sortBySize;
75269
+ this.applyFilterChange();
75270
+ }
75271
+ } else if (kb.matches(keyData, "deleteCharBackward")) {
75272
+ if (this.searchQuery.length > 0) {
75273
+ this.searchQuery = this.searchQuery.slice(0, -1);
75274
+ this.applyFilterChange();
75275
+ }
75276
+ } else {
75277
+ const hasControlChars = [...keyData].some((ch) => {
75278
+ const code = ch.charCodeAt(0);
75279
+ return code < 32 || code === 127 || code >= 128 && code <= 159;
75280
+ });
75281
+ if (!hasControlChars && keyData.length > 0 && this.searchQuery.length < 100) {
75282
+ this.searchQuery += keyData;
75283
+ this.applyFilterChange();
75284
+ }
75285
+ }
75286
+ }
75287
+ // ---------------------------------------------------------------------------
75288
+ // Rendering
75289
+ // ---------------------------------------------------------------------------
75290
+ invalidate() {
75291
+ }
75292
+ hintsLine(view) {
75293
+ if (view.kind === "text") {
75294
+ return " \u2191\u2193:scroll Esc:back";
75295
+ }
75296
+ const base = " \u2191\u2193:move Enter:drill Esc:back type:search PgUp/PgDn:page";
75297
+ if (view.rowKind === "messages") {
75298
+ return `${base} ctrl+u:role(${view.roleFilter}) s:sort(${view.sortBySize ? "size" : "seq"})`;
75299
+ }
75300
+ return base;
75301
+ }
75302
+ renderRowLine(row, width, selected) {
75303
+ const valueText = row.valueText ?? `~${formatReportTokens(row.estTokens)}`;
75304
+ const rightParts = [theme.fg("accent", valueText)];
75305
+ if (row.note) {
75306
+ rightParts.push(theme.fg("dim", row.note));
75307
+ }
75308
+ const right = rightParts.join(" ");
75309
+ const rightWidth = visibleWidth(right);
75310
+ const marker = row.marker ? theme.fg("warning", `${row.marker} `) : "";
75311
+ const detail = row.detail ? theme.fg("dim", ` \xB7 ${row.detail}`) : "";
75312
+ let left = ` ${marker}${row.label}${detail}`;
75313
+ const leftBudget = Math.max(12, width - rightWidth - 3);
75314
+ if (visibleWidth(left) > leftBudget) {
75315
+ left = truncateToWidth(left, leftBudget, "\u2026");
75316
+ }
75317
+ const gap = Math.max(1, width - visibleWidth(left) - rightWidth - 1);
75318
+ return createLine2(`${left}${" ".repeat(gap)}${right}`, width, selected);
75319
+ }
75320
+ render(width) {
75321
+ const lines = [];
75322
+ const totalLines = this.maxVisibleLines + 5;
75323
+ const view = this.currentView;
75324
+ const depthSuffix = this.views.length > 1 ? theme.fg("dim", ` (${this.views.length} levels)`) : "";
75325
+ lines.push(createLine2(` ${theme.bold(view.title)}${depthSuffix}`, width, false));
75326
+ lines.push(createLine2(this.hintsLine(view), width, false));
75327
+ const searchPart = this.searchQuery ? `Search: ${this.searchQuery}` : "";
75328
+ const statusPart = this.statusMessage ? theme.fg("warning", this.statusMessage) : "";
75329
+ const statusLine = [searchPart, statusPart].filter(Boolean).join(" \xB7 ");
75330
+ lines.push(createLine2(statusLine, width, false));
75331
+ lines.push(createLine2("", width, false));
75332
+ if (view.kind === "list") {
75333
+ const rows = this.visibleListRows(view);
75334
+ if (rows.length === 0) {
75335
+ const notice = this.searchQuery || view.roleFilter !== "all" ? " (no matching rows)" : " (empty)";
75336
+ lines.push(createLine2(notice, width, false));
75337
+ for (let i = 1; i < this.maxVisibleLines; i++) {
75338
+ lines.push(createLine2("", width, false));
75339
+ }
75340
+ } else {
75341
+ this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, rows.length - 1));
75342
+ let startIndex = Math.max(
75343
+ 0,
75344
+ Math.min(
75345
+ this.selectedIndex - Math.floor(this.maxVisibleLines / 2),
75346
+ rows.length - this.maxVisibleLines
75347
+ )
75348
+ );
75349
+ const endIndex = Math.min(startIndex + this.maxVisibleLines, rows.length);
75350
+ if (endIndex - startIndex < this.maxVisibleLines && startIndex > 0) {
75351
+ startIndex = Math.max(0, endIndex - this.maxVisibleLines);
75352
+ }
75353
+ for (let i = startIndex; i < endIndex; i++) {
75354
+ lines.push(this.renderRowLine(rows[i], width, i === this.selectedIndex));
75355
+ }
75356
+ for (let i = endIndex - startIndex; i < this.maxVisibleLines; i++) {
75357
+ lines.push(createLine2("", width, false));
75358
+ }
75359
+ }
75360
+ const filterSuffix = view.roleFilter !== "all" ? ` [${view.roleFilter}]` : "";
75361
+ const sortSuffix = view.rowKind === "messages" && view.sortBySize ? " [size]" : "";
75362
+ lines.push(
75363
+ createLine2(
75364
+ ` (${this.selectedIndex + 1}/${this.visibleListRows(view).length})${filterSuffix}${sortSuffix}`,
75365
+ width,
75366
+ false
75367
+ )
75368
+ );
75369
+ } else {
75370
+ const total = view.lines.length;
75371
+ const start = Math.min(view.scroll, Math.max(0, total - 1));
75372
+ const end = Math.min(start + this.maxVisibleLines, total);
75373
+ for (let i = start; i < end; i++) {
75374
+ lines.push(createLine2(` ${view.lines[i]}`, width, false));
75375
+ }
75376
+ for (let i = end - start; i < this.maxVisibleLines; i++) {
75377
+ lines.push(createLine2("", width, false));
75378
+ }
75379
+ lines.push(
75380
+ createLine2(` (lines ${total > 0 ? start + 1 : 0}-${end} of ${total})`, width, false)
75381
+ );
75382
+ }
75383
+ while (lines.length < totalLines) {
75384
+ lines.push(createLine2("", width, false));
75385
+ }
75386
+ return lines.slice(0, totalLines);
75387
+ }
75388
+ };
75389
+
75390
+ // src/commands/interactive-context-command.ts
75391
+ var MAX_ENTRY_BODY_CHARS = 1e4;
75392
+ var MAX_ARCHIVED_ROWS = 200;
75393
+ function fetchArchivedSummaries(sessionManager, firstKeptSeq) {
75394
+ const result = [];
75395
+ let afterSeq;
75396
+ while (result.length < MAX_ARCHIVED_ROWS) {
75397
+ const page = sessionManager.getEntrySummariesPage({ limit: 100, afterSeq });
75398
+ if (page.summaries.length === 0) break;
75399
+ let crossedCutPoint = false;
75400
+ for (const summary of page.summaries) {
75401
+ if (summary.seq >= firstKeptSeq) {
75402
+ crossedCutPoint = true;
75403
+ break;
75404
+ }
75405
+ if (result.length >= MAX_ARCHIVED_ROWS) break;
75406
+ result.push(summary);
75407
+ }
75408
+ if (crossedCutPoint || !page.hasMore) break;
75409
+ afterSeq = page.summaries[page.summaries.length - 1].seq;
75410
+ }
75411
+ return result;
75412
+ }
75413
+ function enhanceCheckpointRowWithArchive(report, host, entries) {
75414
+ const messagesSection = report.sections.find((section) => section.id === "messages");
75415
+ const checkpoint = [...entries].reverse().find((entry) => entry.type === "compaction");
75416
+ if (!messagesSection || !checkpoint) return;
75417
+ const compactionEntry = host.sessionManager.getEntry(checkpoint.id);
75418
+ const firstKeptEntryId = compactionEntry?.compactionData?.firstKeptEntryId;
75419
+ if (!firstKeptEntryId) return;
75420
+ const firstKeptSeq = host.sessionManager.getEntrySummary(firstKeptEntryId)?.seq;
75421
+ if (firstKeptSeq === void 0) return;
75422
+ const archived = fetchArchivedSummaries(host.sessionManager, firstKeptSeq);
75423
+ if (archived.length === 0) return;
75424
+ const row = messagesSection.rows.find((candidate) => candidate.id === `entry:${checkpoint.id}`);
75425
+ if (!row) return;
75426
+ const truncated = archived.length >= MAX_ARCHIVED_ROWS;
75427
+ row.drill = {
75428
+ kind: "rows",
75429
+ title: `Archived @ checkpoint #${checkpoint.seq}`,
75430
+ rowKind: "plain",
75431
+ rows: [
75432
+ {
75433
+ id: "checkpoint-summary",
75434
+ label: "checkpoint summary",
75435
+ estTokens: checkpoint.estTokens,
75436
+ drill: {
75437
+ kind: "text",
75438
+ title: "Checkpoint Summary",
75439
+ text: compactionEntry?.compactionData?.summary ?? ""
75440
+ }
75441
+ },
75442
+ ...archived.map((summary) => ({
75443
+ id: `archived:${summary.id}`,
75444
+ label: `#${summary.seq} ${summary.role ?? summary.type}`,
75445
+ estTokens: summary.estTokens,
75446
+ detail: summary.preview,
75447
+ drill: { kind: "entry", entryId: summary.id }
75448
+ })),
75449
+ ...truncated ? [{
75450
+ id: "archived:truncated",
75451
+ label: `\u2026 archived list capped at ${MAX_ARCHIVED_ROWS} entries (oldest first)`,
75452
+ estTokens: 0
75453
+ }] : []
75454
+ ]
75455
+ };
75456
+ }
75457
+ function formatEntryBodyText(entry) {
75458
+ if (!entry) return null;
75459
+ if (entry.type === "compaction") {
75460
+ const summary = entry.compactionData?.summary ?? "";
75461
+ const before = entry.compactionData?.tokensBefore;
75462
+ const header = before !== void 0 ? `# compaction checkpoint (context was ~${formatReportTokens(before)} before)
75463
+ ` : "# compaction checkpoint\n";
75464
+ return `${header}
75465
+ ${summary}`.slice(0, MAX_ENTRY_BODY_CHARS) || null;
75466
+ }
75467
+ const message = entry.message;
75468
+ if (!message) return null;
75469
+ if (message.role === "user") {
75470
+ const parts = ["# user message"];
75471
+ const content = message.content;
75472
+ if (typeof content === "string") {
75473
+ parts.push(content);
75474
+ } else if (Array.isArray(content)) {
75475
+ for (const block of content) {
75476
+ if (block && block.type === "text" && typeof block.text === "string") {
75477
+ parts.push(block.text);
75478
+ } else if (block && "type" in block) {
75479
+ parts.push(`[${block.type}]`);
75480
+ }
75481
+ }
75482
+ }
75483
+ return parts.join("\n").slice(0, MAX_ENTRY_BODY_CHARS) || null;
75484
+ }
75485
+ if (message.role === "assistant") {
75486
+ const parts = ["# assistant message"];
75487
+ const usage = message.usage;
75488
+ if (usage) {
75489
+ parts.push(
75490
+ `usage: input ${usage.input} \xB7 output ${usage.output} \xB7 cacheRead ${usage.cacheRead} \xB7 cacheWrite ${usage.cacheWrite}`
75491
+ );
75492
+ }
75493
+ if (Array.isArray(message.content)) {
75494
+ for (const block of message.content) {
75495
+ if (block.type === "text") {
75496
+ parts.push(block.text);
75497
+ } else if (block.type === "thinking") {
75498
+ parts.push("[thinking omitted from context on most providers]");
75499
+ } else if (block.type === "toolCall") {
75500
+ parts.push(`\u2192 ${block.name}(${JSON.stringify(block.arguments)})`);
75501
+ }
75502
+ }
75503
+ }
75504
+ return parts.join("\n").slice(0, MAX_ENTRY_BODY_CHARS) || null;
75505
+ }
75506
+ if (message.role === "toolResult") {
75507
+ const parts = [`# tool result (${message.toolCallId})`];
75508
+ if (Array.isArray(message.content)) {
75509
+ for (const block of message.content) {
75510
+ if (block.type === "text") {
75511
+ parts.push(block.text);
75512
+ } else if (block.type === "image") {
75513
+ parts.push("[image]");
75514
+ } else if (block.type === "audio") {
75515
+ parts.push("[audio]");
75516
+ } else if (block.type === "video") {
75517
+ parts.push("[video]");
75518
+ } else {
75519
+ parts.push(`[${block.type}]`);
75520
+ }
75521
+ }
75522
+ }
75523
+ return parts.join("\n").slice(0, MAX_ENTRY_BODY_CHARS) || null;
75524
+ }
75525
+ return JSON.stringify(message, null, 2).slice(0, MAX_ENTRY_BODY_CHARS) || null;
75526
+ }
75527
+ function handleContextReportCommand(host) {
75528
+ const systemPrompt = host.agent.state.systemPrompt ?? "";
75529
+ const knowledgeFiles = parseProjectContextSection(host.knowledgeSection);
75530
+ const entries = host.sessionManager.getActiveContextSummaries();
75531
+ const usage = host.calculateContextUsage();
75532
+ const contextWindow = host.options.model?.contextWindow ?? 0;
75533
+ const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
75534
+ const autoCompactLimit = contextWindow > 0 ? getAutoCompactTokenLimit(contextWindow, reserveTokens) ?? null : null;
75535
+ const prefixHash = computePromptPrefixHash(
75536
+ systemPrompt,
75537
+ host.agent.state.tools.map((tool) => ({
75538
+ name: tool.name,
75539
+ description: tool.description,
75540
+ parameters: tool.parameters
75541
+ }))
75542
+ );
75543
+ const lastSignals2 = getLastContextSignals(host);
75544
+ const promptPrefixChanged = lastSignals2 !== null && lastSignals2.prefixHash !== prefixHash;
75545
+ const report = buildContextReportData({
75546
+ systemPrompt,
75547
+ skillsSection: host.skillsSection,
75548
+ knowledgeFiles,
75549
+ skills: host.skills.map((skill) => ({
75550
+ name: skill.name,
75551
+ description: skill.description,
75552
+ filePath: skill.filePath,
75553
+ baseDir: skill.baseDir,
75554
+ source: skill.source
75555
+ })),
75556
+ tools: host.agent.state.tools.map((tool) => ({
75557
+ name: tool.name,
75558
+ label: tool.label,
75559
+ description: tool.description,
75560
+ parameters: tool.parameters
75561
+ })),
75562
+ entries,
75563
+ compactionTokensBefore: (entryId) => host.sessionManager.getEntry(entryId)?.compactionData?.tokensBefore,
75564
+ contextWindow,
75565
+ actualTokens: usage.tokens,
75566
+ autoCompactLimitTokens: autoCompactLimit,
75567
+ reserveTokens,
75568
+ promptPrefixHash: prefixHash,
75569
+ promptPrefixChanged
75570
+ });
75571
+ enhanceCheckpointRowWithArchive(report, host, entries);
75572
+ host.chatContainer.addChild(new Spacer(1));
75573
+ host.chatContainer.addChild(new Text(buildContextOverviewText(report, theme), 1, 0));
75574
+ host.ui.requestRender();
75575
+ host.showSelector((done) => {
75576
+ const selector = new ContextReportSelectorComponent({
75577
+ title: "Context Report",
75578
+ sections: report.sections,
75579
+ windowSection: {
75580
+ label: "window & thresholds",
75581
+ estTokens: report.contextWindow,
75582
+ valueText: report.contextWindow > 0 ? formatReportTokens(report.contextWindow) : "?",
75583
+ title: "Window & Thresholds",
75584
+ detail: "context window, estimate vs actual, auto-compact",
75585
+ rows: buildWindowRows(report)
75586
+ },
75587
+ terminalHeight: host.ui.terminal.rows,
75588
+ getEntryText: (entryId) => formatEntryBodyText(host.sessionManager.getEntry(entryId)),
75589
+ onDone: done
75590
+ });
75591
+ return { component: selector, focus: selector };
75592
+ });
75593
+ }
75594
+
75595
+ // src/core/trace-report.ts
75596
+ var MAX_DRILL_CHARS = 8e3;
75597
+ function formatTime(at) {
75598
+ return new Date(at).toLocaleTimeString([], { hour12: false });
75599
+ }
75600
+ function formatDurationMs(ms) {
75601
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
75602
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
75603
+ return `${Math.floor(ms / 6e4)}m${Math.round(ms % 6e4 / 1e3)}s`;
75604
+ }
75605
+ function detailsToText(event) {
75606
+ const header = [
75607
+ `# ${event.type}`,
75608
+ `at: ${new Date(event.at).toISOString()}`,
75609
+ event.summary ? `summary: ${event.summary}` : "",
75610
+ event.failure ? "failure: true" : ""
75611
+ ].filter(Boolean).join("\n");
75612
+ const details = event.details === void 0 ? "" : `
75613
+
75614
+ # details
75615
+ ${safeStringify(event.details).slice(0, MAX_DRILL_CHARS)}`;
75616
+ return `${header}${details}`;
75617
+ }
75618
+ function safeStringify(value) {
75619
+ try {
75620
+ return typeof value === "string" ? value : JSON.stringify(value, null, 2);
75621
+ } catch {
75622
+ return String(value);
75623
+ }
75624
+ }
75625
+ function buildTimelineRows(timeline) {
75626
+ if (timeline.length === 0) {
75627
+ return [{ id: "timeline:none", label: "no runtime events recorded yet", estTokens: 0 }];
75628
+ }
75629
+ return [...timeline].sort((a, b2) => b2.at - a.at).map((event, index) => ({
75630
+ id: `timeline:${event.at}:${index}`,
75631
+ label: `${formatTime(event.at)} ${event.type}`,
75632
+ estTokens: 0,
75633
+ detail: event.summary || void 0,
75634
+ marker: event.failure ? "\u2716" : void 0,
75635
+ drill: {
75636
+ kind: "text",
75637
+ title: `Event \xB7 ${event.type}`,
75638
+ text: detailsToText(event)
75639
+ }
75640
+ }));
75641
+ }
75642
+ function aggregateToolStats(turns) {
75643
+ const stats = /* @__PURE__ */ new Map();
75644
+ for (const turn of turns) {
75645
+ for (const call of turn.toolCalls) {
75646
+ const stat2 = stats.get(call.toolName) ?? {
75647
+ name: call.toolName,
75648
+ calls: 0,
75649
+ errors: 0,
75650
+ totalMs: 0
75651
+ };
75652
+ stat2.calls += 1;
75653
+ if (call.status === "failed") stat2.errors += 1;
75654
+ if (call.endedAt !== void 0 && call.endedAt >= call.startedAt) {
75655
+ stat2.totalMs += call.endedAt - call.startedAt;
75656
+ }
75657
+ stats.set(call.toolName, stat2);
75658
+ }
75659
+ }
75660
+ return [...stats.values()].sort((a, b2) => b2.totalMs - a.totalMs || b2.calls - a.calls);
75661
+ }
75662
+ function buildToolStatRows(turns) {
75663
+ const stats = aggregateToolStats(turns);
75664
+ if (stats.length === 0) {
75665
+ return [{ id: "tools:none", label: "no tool calls in recent turns", estTokens: 0 }];
75666
+ }
75667
+ return stats.map((stat2) => ({
75668
+ id: `toolstat:${stat2.name}`,
75669
+ label: stat2.name,
75670
+ estTokens: 0,
75671
+ valueText: formatDurationMs(stat2.totalMs),
75672
+ note: `${stat2.calls} calls${stat2.errors > 0 ? ` \xB7 ${stat2.errors} errors` : ""}`,
75673
+ drill: {
75674
+ kind: "rows",
75675
+ title: `Tool Calls \xB7 ${stat2.name}`,
75676
+ rowKind: "plain",
75677
+ rows: collectCalls(turns, stat2.name)
75678
+ }
75679
+ }));
75680
+ }
75681
+ function collectCalls(turns, toolName) {
75682
+ const rows = [];
75683
+ for (const turn of turns) {
75684
+ for (const call of turn.toolCalls) {
75685
+ if (call.toolName !== toolName) continue;
75686
+ const duration = call.endedAt !== void 0 && call.endedAt >= call.startedAt ? formatDurationMs(call.endedAt - call.startedAt) : "running";
75687
+ rows.push({
75688
+ id: `call:${call.toolCallId ?? `${call.startedAt}:${rows.length}`}`,
75689
+ label: `${formatTime(call.startedAt)} ${call.status}`,
75690
+ estTokens: 0,
75691
+ valueText: duration,
75692
+ detail: call.resultSummary || void 0,
75693
+ marker: call.status === "failed" ? "\u2716" : void 0,
75694
+ drill: {
75695
+ kind: "text",
75696
+ title: `Call \xB7 ${toolName}`,
75697
+ text: [
75698
+ `# ${toolName} (${call.status})`,
75699
+ `started: ${new Date(call.startedAt).toISOString()}`,
75700
+ call.endedAt !== void 0 ? `ended: ${new Date(call.endedAt).toISOString()}` : "",
75701
+ call.outcome !== void 0 ? `
75702
+ # outcome
75703
+ ${safeStringify(call.outcome)}` : ""
75704
+ ].filter(Boolean).join("\n").slice(0, MAX_DRILL_CHARS)
75705
+ }
75706
+ });
75707
+ }
75708
+ }
75709
+ return rows.sort((a, b2) => a.id.localeCompare(b2.id));
75710
+ }
75711
+ function buildTurnRows(turns) {
75712
+ if (turns.length === 0) {
75713
+ return [{ id: "turns:none", label: "no completed turns recorded yet", estTokens: 0 }];
75714
+ }
75715
+ return [...turns].sort((a, b2) => b2.turnIndex - a.turnIndex).map((turn) => {
75716
+ const duration = turn.completedAt !== void 0 && turn.startedAt !== void 0 && turn.completedAt >= turn.startedAt ? formatDurationMs(turn.completedAt - turn.startedAt) : "\u2014";
75717
+ return {
75718
+ id: `turn:${turn.turnIndex}`,
75719
+ label: `#${turn.turnIndex} ${turn.state}`,
75720
+ estTokens: 0,
75721
+ valueText: duration,
75722
+ note: `${turn.toolCalls.length} tool calls`,
75723
+ detail: turn.userText?.slice(0, 80) || void 0,
75724
+ marker: turn.state === "failed" ? "\u2716" : void 0,
75725
+ drill: {
75726
+ kind: "text",
75727
+ title: `Turn #${turn.turnIndex}`,
75728
+ text: turnToText(turn)
75729
+ }
75730
+ };
75731
+ });
75732
+ }
75733
+ function turnToText(turn) {
75734
+ const parts = [
75735
+ `# turn ${turn.turnIndex} (${turn.state})`,
75736
+ turn.startedAt !== void 0 ? `started: ${new Date(turn.startedAt).toISOString()}` : "",
75737
+ turn.completedAt !== void 0 ? `completed: ${new Date(turn.completedAt).toISOString()}` : ""
75738
+ ];
75739
+ if (turn.userText) parts.push(`
75740
+ # user
75741
+ ${turn.userText.slice(0, 2e3)}`);
75742
+ if (turn.assistantText) parts.push(`
75743
+ # assistant
75744
+ ${turn.assistantText.slice(0, 2e3)}`);
75745
+ if (turn.errorMessage) parts.push(`
75746
+ # error
75747
+ ${turn.errorMessage}`);
75748
+ if (turn.toolCalls.length > 0) {
75749
+ parts.push(
75750
+ `
75751
+ # tool calls
75752
+ ${turn.toolCalls.map(
75753
+ (call) => `- ${call.toolName} (${call.status})${call.endedAt !== void 0 && call.endedAt >= call.startedAt ? ` ${formatDurationMs(call.endedAt - call.startedAt)}` : ""}${call.resultSummary ? ` \u2014 ${call.resultSummary.slice(0, 200)}` : ""}`
75754
+ ).join("\n")}`
75755
+ );
75756
+ }
75757
+ return parts.filter(Boolean).join("\n").slice(0, MAX_DRILL_CHARS);
75758
+ }
75759
+ function buildRuntimeSummaryRows(snapshot) {
75760
+ const summary = snapshot.runtimeSummary;
75761
+ if (!summary) {
75762
+ return [{ id: "runtime:none", label: "no runtime summary recorded yet", estTokens: 0 }];
75763
+ }
75764
+ const entries = [
75765
+ ["tool results", summary.toolResults, "completed tool calls (recent turns)"],
75766
+ ["tool errors", summary.toolErrors, "failed tool calls (recent turns)"],
75767
+ ["retries", summary.retryEvents, "provider/overflow retries"],
75768
+ ["auto-compactions", summary.autoCompactionEvents, "context checkpoints this session"],
75769
+ ["runtime guard events", summary.runtimeGuardEvents, "guard advisories triggered"],
75770
+ ["queued turn events", summary.queuedTurnEvents, "steer/followUp queue dispatches"],
75771
+ ["subagent events", summary.subagentEvents, "subagent lifecycle events"]
75772
+ ];
75773
+ return entries.map(([label, count, detail]) => ({
75774
+ id: `runtime:${label}`,
75775
+ label,
75776
+ estTokens: count,
75777
+ valueText: String(count),
75778
+ note: count > 0 ? void 0 : "none",
75779
+ detail
75780
+ }));
75781
+ }
75782
+ function buildSubagentRows(snapshot) {
75783
+ const summaries = snapshot.subagentSummaries ?? [];
75784
+ if (summaries.length === 0) {
75785
+ return [{ id: "subagents:none", label: "no subagent tasks recorded yet", estTokens: 0 }];
75786
+ }
75787
+ return summaries.map((sub, index) => ({
75788
+ id: `subagent:${sub.toolCallId ?? index}`,
75789
+ label: sub.name,
75790
+ estTokens: 0,
75791
+ valueText: sub.status,
75792
+ note: sub.modelId ? `${sub.modelClass ? `${sub.modelClass}/` : ""}${sub.modelId}` : void 0,
75793
+ detail: [
75794
+ sub.evidenceCount > 0 ? `${sub.evidenceCount} evidence` : "",
75795
+ sub.failureReason ? `failed: ${sub.failureReason}` : "",
75796
+ sub.modelFallbackReason ? `fallback: ${sub.modelFallbackReason}` : ""
75797
+ ].filter(Boolean).join(" \xB7 ") || void 0,
75798
+ marker: sub.status === "failed" || sub.failureReason ? "\u2716" : void 0,
75799
+ drill: {
75800
+ kind: "text",
75801
+ title: `Subagent \xB7 ${sub.name}`,
75802
+ text: safeStringify(sub).slice(0, MAX_DRILL_CHARS)
75803
+ }
75804
+ }));
75805
+ }
75806
+ function buildTraceSections(snapshot) {
75807
+ const turns = [...snapshot.recentTurns];
75808
+ if (snapshot.activeTurn) turns.push(snapshot.activeTurn);
75809
+ const timeline = snapshot.recentTimeline ?? [];
75810
+ return [
75811
+ {
75812
+ id: "trace-timeline",
75813
+ title: `Timeline (${timeline.length})`,
75814
+ estTokens: 0,
75815
+ rows: buildTimelineRows(timeline)
75816
+ },
75817
+ {
75818
+ id: "trace-tools",
75819
+ title: "Tool stats (recent turns)",
75820
+ estTokens: 0,
75821
+ note: aggregateToolStats(turns)[0] ? `slowest: ${aggregateToolStats(turns)[0].name} ${formatDurationMs(aggregateToolStats(turns)[0].totalMs)}` : void 0,
75822
+ rows: buildToolStatRows(turns)
75823
+ },
75824
+ {
75825
+ id: "trace-turns",
75826
+ title: `Recent turns (${snapshot.recentTurns.length}${snapshot.activeTurn ? " +1 active" : ""})`,
75827
+ estTokens: 0,
75828
+ rows: buildTurnRows(
75829
+ snapshot.activeTurn ? [...snapshot.recentTurns, snapshot.activeTurn] : snapshot.recentTurns
75830
+ )
75831
+ },
75832
+ {
75833
+ id: "trace-runtime",
75834
+ title: "Runtime summary",
75835
+ estTokens: 0,
75836
+ rows: buildRuntimeSummaryRows(snapshot)
75837
+ },
75838
+ {
75839
+ id: "trace-subagents",
75840
+ title: `Subagents (${(snapshot.subagentSummaries ?? []).length})`,
75841
+ estTokens: 0,
75842
+ rows: buildSubagentRows(snapshot)
75843
+ }
75844
+ ];
75845
+ }
75846
+ function buildTraceOverviewText(snapshot, theme2) {
75847
+ const t = theme2 ?? { bold: (s) => s, fg: (_k, s) => s };
75848
+ const lines = [];
75849
+ const turns = snapshot.recentTurns.length + (snapshot.activeTurn ? 1 : 0);
75850
+ const timeline = snapshot.recentTimeline ?? [];
75851
+ lines.push(t.bold("Session Trace") + t.fg("dim", ` \xB7 ${turns} recent turns \xB7 ${timeline.length} events`));
75852
+ const summary = snapshot.runtimeSummary;
75853
+ if (summary) {
75854
+ const notable = [];
75855
+ if (summary.retryEvents > 0) notable.push(`retries ${summary.retryEvents}`);
75856
+ if (summary.autoCompactionEvents > 0) notable.push(`compactions ${summary.autoCompactionEvents}`);
75857
+ if (summary.runtimeGuardEvents > 0) notable.push(`guard ${summary.runtimeGuardEvents}`);
75858
+ if (summary.toolErrors > 0) notable.push(`tool errors ${summary.toolErrors}`);
75859
+ if (notable.length > 0) {
75860
+ lines.push(t.fg("dim", ` events: ${notable.join(" \xB7 ")}`));
75861
+ } else {
75862
+ lines.push(t.fg("dim", " no retries, compactions, or guard events"));
75863
+ }
75864
+ }
75865
+ const stats = aggregateToolStats([...snapshot.recentTurns, ...snapshot.activeTurn ? [snapshot.activeTurn] : []]);
75866
+ if (stats.length > 0) {
75867
+ const top = stats.slice(0, 3).map((s) => `${s.name} ${formatDurationMs(s.totalMs)} (${s.calls})`);
75868
+ lines.push(t.fg("dim", ` tools: ${top.join(" \xB7 ")}`));
75869
+ }
75870
+ lines.push(t.fg("dim", " /debugs/trace \xB7 Enter to drill, Esc to go back"));
75871
+ return lines.join("\n");
75872
+ }
75873
+
75874
+ // src/commands/interactive-trace-command.ts
75875
+ function handleTraceCommand(host, snapshot) {
75876
+ if (!snapshot) {
75877
+ host.showStatus("No session trace yet \u2014 it starts with the first turn");
75878
+ return;
75879
+ }
75880
+ const sections = buildTraceSections(snapshot);
75881
+ host.chatContainer.addChild(new Spacer(1));
75882
+ host.chatContainer.addChild(new Text(buildTraceOverviewText(snapshot, theme), 1, 0));
75883
+ host.ui.requestRender();
75884
+ host.showSelector((done) => {
75885
+ const selector = new ContextReportSelectorComponent({
75886
+ title: "Session Trace",
75887
+ sections,
75888
+ terminalHeight: host.ui.terminal.rows,
75889
+ onDone: done
75890
+ });
75891
+ return { component: selector, focus: selector };
75892
+ });
75893
+ }
75894
+
75895
+ // src/core/boot-report.ts
75896
+ function stepToText(step) {
75897
+ const lines = [
75898
+ `# ${step.id}`,
75899
+ `input: ${step.inputSummary}`,
75900
+ `output: ${step.outputSummary}`
75901
+ ];
75902
+ if (step.toolCount !== void 0) lines.push(`tools: ${step.toolCount}`);
75903
+ if (step.policyDecisions?.length) {
75904
+ lines.push("", "# policy decisions", ...step.policyDecisions.map((decision) => `- ${decision}`));
75905
+ }
75906
+ if (step.extensionWrapping) {
75907
+ lines.push(
75908
+ "",
75909
+ "# extension wrapping",
75910
+ `extensions: ${step.extensionWrapping.extensionCount}`,
75911
+ `extension tools: ${step.extensionWrapping.extensionToolCount}`,
75912
+ `wrapped tools: ${step.extensionWrapping.wrappedToolCount}`
75913
+ );
75914
+ }
75915
+ return lines.join("\n");
75916
+ }
75917
+ function buildBootRows(trace) {
75918
+ if (trace.steps.length === 0) {
75919
+ return [{ id: "boot:none", label: "no composition steps recorded", estTokens: 0 }];
75920
+ }
75921
+ return trace.steps.map((step, index) => ({
75922
+ id: `boot:${step.id}`,
75923
+ label: `${index + 1}. ${step.id}`,
75924
+ estTokens: 0,
75925
+ note: step.toolCount !== void 0 ? `${step.toolCount} tools` : void 0,
75926
+ detail: step.outputSummary,
75927
+ drill: {
75928
+ kind: "text",
75929
+ title: `Step \xB7 ${step.id}`,
75930
+ text: stepToText(step)
75931
+ }
75932
+ }));
75933
+ }
75934
+ function buildBootSections(trace) {
75935
+ return [
75936
+ {
75937
+ id: "boot-pipeline",
75938
+ title: `Composition pipeline (${trace.steps.length} steps)`,
75939
+ estTokens: 0,
75940
+ rows: buildBootRows(trace)
75941
+ }
75942
+ ];
75943
+ }
75944
+ function buildBootOverviewText(trace, theme2) {
75945
+ const t = theme2 ?? { bold: (s) => s, fg: (_k, s) => s };
75946
+ const lines = [];
75947
+ lines.push(t.bold("Boot Composition") + t.fg("dim", ` \xB7 ${trace.steps.length} steps`));
75948
+ for (const step of trace.steps) {
75949
+ lines.push(t.fg("dim", ` ${step.id} \u2014 ${step.outputSummary}`));
75950
+ }
75951
+ lines.push(t.fg("dim", " /debugs/boot \xB7 Enter to drill into a step"));
75952
+ return lines.join("\n");
75953
+ }
75954
+
75955
+ // src/commands/interactive-boot-command.ts
75956
+ function handleBootCommand(host) {
75957
+ const trace = host.options.compositionTrace;
75958
+ if (!trace || trace.steps.length === 0) {
75959
+ host.showStatus("No composition trace available for this runtime");
75960
+ return;
75961
+ }
75962
+ const sections = buildBootSections(trace);
75963
+ host.chatContainer.addChild(new Spacer(1));
75964
+ host.chatContainer.addChild(new Text(buildBootOverviewText(trace, theme), 1, 0));
75965
+ host.ui.requestRender();
75966
+ host.showSelector((done) => {
75967
+ const selector = new ContextReportSelectorComponent({
75968
+ title: "Boot Composition",
75969
+ sections,
75970
+ terminalHeight: host.ui.terminal.rows,
75971
+ onDone: done
75972
+ });
75973
+ return { component: selector, focus: selector };
75974
+ });
75975
+ }
75976
+
74639
75977
  // src/commands/interactive-command-handlers.ts
74640
75978
  function showSettingsSelector(host, focusItem) {
74641
75979
  host.showSelector((done) => {
@@ -75060,6 +76398,7 @@ async function handleClearCommand(host) {
75060
76398
  contextPercent: contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0,
75061
76399
  contextWindow
75062
76400
  });
76401
+ applyContextSignalsToFooter(host);
75063
76402
  host.updateTerminalTitle();
75064
76403
  host.chatContainer.clear();
75065
76404
  host.streamingComponent = void 0;
@@ -75106,6 +76445,7 @@ function handleSessionCommand(host) {
75106
76445
  homeDir: process.env.HOME || process.env.USERPROFILE || "",
75107
76446
  modelId: host.options.model?.id,
75108
76447
  thinkingLevel: host.thinkingLevel,
76448
+ messages: host.sessionManager.getMessages(),
75109
76449
  theme
75110
76450
  });
75111
76451
  host.chatContainer.addChild(new Spacer(1));
@@ -75339,12 +76679,16 @@ function registerBuiltinCommands(host) {
75339
76679
  handler: () => host.handleCacheTest()
75340
76680
  });
75341
76681
  commandTree.register(["debugs", "context"], {
75342
- description: "Append a context summary to ~/.pie/logs/pie-cli.log",
75343
- handler: () => host.handleDebugContextCommand()
76682
+ description: "Context composition report: sections, tokens, drill-down",
76683
+ handler: () => host.handleDebugReportCommand("context")
75344
76684
  });
75345
- commandTree.register(["debugs", "package"], {
75346
- description: "Append a package summary to ~/.pie/logs/pie-cli.log",
75347
- handler: () => host.handleDebugPackageCommand()
76685
+ commandTree.register(["debugs", "trace"], {
76686
+ description: "Session trace: timeline, tool stats, runtime events",
76687
+ handler: () => host.handleDebugReportCommand("trace")
76688
+ });
76689
+ commandTree.register(["debugs", "boot"], {
76690
+ description: "Boot composition pipeline: how the runtime was assembled",
76691
+ handler: () => host.handleDebugReportCommand("boot")
75348
76692
  });
75349
76693
  commandTree.register(["settings", "quit"], {
75350
76694
  description: "Exit Pie",
@@ -75355,6 +76699,36 @@ function registerBuiltinCommands(host) {
75355
76699
  });
75356
76700
  }
75357
76701
 
76702
+ // src/runtime/compaction-diff.ts
76703
+ function summarizeActiveContext(summaries) {
76704
+ let tokens = 0;
76705
+ for (const summary of summaries) {
76706
+ tokens += summary.estTokens;
76707
+ }
76708
+ return { count: summaries.length, tokens };
76709
+ }
76710
+ function formatCompactionTokenLine(stats) {
76711
+ const saved = Math.max(0, stats.before.tokens - stats.after.tokens);
76712
+ const percent = stats.before.tokens > 0 ? Math.round(saved / stats.before.tokens * 100) : 0;
76713
+ return `~${formatReportTokens(stats.before.tokens)} \u2192 ~${formatReportTokens(stats.after.tokens)} tokens (saved ~${formatReportTokens(saved)}, \u2212${percent}%)`;
76714
+ }
76715
+ function renderCompactionDiffPanel(target, stats) {
76716
+ const archived = Math.max(0, stats.before.count - stats.after.count);
76717
+ target.chatContainer.addChild(new Spacer(1));
76718
+ target.chatContainer.addChild(new Text(theme.fg("accent", "\u2691 Context checkpoint"), 1, 0));
76719
+ target.chatContainer.addChild(
76720
+ new Text(
76721
+ theme.fg(
76722
+ "dim",
76723
+ ` kept ${stats.after.count} entries \xB7 archived ${archived} entries \xB7 ${formatCompactionTokenLine(stats)}`
76724
+ ),
76725
+ 1,
76726
+ 0
76727
+ )
76728
+ );
76729
+ target.safeRequestRender();
76730
+ }
76731
+
75358
76732
  // src/commands/interactive-compact-command.ts
75359
76733
  async function handleCompactCommand(host) {
75360
76734
  if (host.submitLock || host.isProcessing || host.isCompacting) {
@@ -75373,6 +76747,7 @@ async function handleCompactCommand(host) {
75373
76747
  host.chatContainer.addChild(new Spacer(1));
75374
76748
  host.chatContainer.addChild(new Text(theme.fg("dim", "[/sessions/compact]"), 1, 0));
75375
76749
  host.safeRequestRender();
76750
+ const beforeStats = summarizeActiveContext(host.sessionManager.getActiveContextSummaries());
75376
76751
  try {
75377
76752
  const activeEntryId = host.sessionManager.getActiveEntryId();
75378
76753
  if (!activeEntryId) {
@@ -75415,8 +76790,10 @@ async function handleCompactCommand(host) {
75415
76790
  host.lastStatusSpacer = void 0;
75416
76791
  host.lastStatusText = void 0;
75417
76792
  host.renderMessageHistory(compactedMessages);
75418
- host.chatContainer.addChild(new Spacer(1));
75419
- host.chatContainer.addChild(new Text(theme.fg("accent", "Context compacted"), 1, 0));
76793
+ renderCompactionDiffPanel(host, {
76794
+ before: beforeStats,
76795
+ after: summarizeActiveContext(host.sessionManager.getActiveContextSummaries())
76796
+ });
75420
76797
  const contextUsage = host.calculateContextUsage();
75421
76798
  const contextWindow = host.options.model?.contextWindow ?? 0;
75422
76799
  const contextPercent = contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0;
@@ -75431,6 +76808,7 @@ async function handleCompactCommand(host) {
75431
76808
  contextPercent,
75432
76809
  contextWindow
75433
76810
  });
76811
+ applyContextSignalsToFooter(host);
75434
76812
  await host.sessionManager.save();
75435
76813
  host.safeRequestRender();
75436
76814
  } catch (error) {
@@ -77807,6 +79185,14 @@ var FooterComponent = class {
77807
79185
  if (state.cacheRead) statsParts.push(`R${formatTokens(state.cacheRead)}`);
77808
79186
  if (state.cacheWrite) statsParts.push(`W${formatTokens(state.cacheWrite)}`);
77809
79187
  if (state.cost) statsParts.push(`$${state.cost.toFixed(3)}`);
79188
+ if (state.cacheHitRate != null) {
79189
+ statsParts.push(theme.fg("dim", `ca ${(state.cacheHitRate * 100).toFixed(0)}%`));
79190
+ }
79191
+ if (state.prefixHash) {
79192
+ statsParts.push(
79193
+ state.prefixHashChanged ? theme.fg("warning", `\u26A1${state.prefixHash}`) : theme.fg("dim", `\u2301${state.prefixHash}`)
79194
+ );
79195
+ }
77810
79196
  if (state.contextWindow) {
77811
79197
  const autoIndicator = this.autoCompactEnabled ? " (auto)" : "";
77812
79198
  const percent = state.contextPercent ?? 0;
@@ -77879,27 +79265,13 @@ var FooterComponent = class {
77879
79265
  // src/components/todo-widget.ts
77880
79266
  var TodoWidgetComponent = class extends Container {
77881
79267
  state;
77882
- calmMode = false;
77883
79268
  constructor(state) {
77884
79269
  super();
77885
79270
  this.state = state;
77886
79271
  void this.refresh();
77887
79272
  }
77888
- /** Calm mode hides the progress widget (questions/answers only). */
77889
- setCalmMode(calm) {
77890
- if (this.calmMode === calm) return;
77891
- this.calmMode = calm;
77892
- if (calm) {
77893
- this.clear();
77894
- } else {
77895
- void this.refresh();
77896
- }
77897
- }
77898
79273
  async refresh() {
77899
79274
  this.clear();
77900
- if (this.calmMode) {
77901
- return;
77902
- }
77903
79275
  const lines = renderExecutionWidgetLines(this.state.getWidgetSteps());
77904
79276
  if (lines.length === 0) {
77905
79277
  return;
@@ -78658,6 +80030,22 @@ function showStatus(host, message) {
78658
80030
  host.lastStatusText = text;
78659
80031
  host.ui.requestRender();
78660
80032
  }
80033
+ function clearStatusLine(host) {
80034
+ const spacer = host.lastStatusSpacer;
80035
+ const text = host.lastStatusText;
80036
+ host.lastStatusSpacer = void 0;
80037
+ host.lastStatusText = void 0;
80038
+ let removed = false;
80039
+ if (spacer && host.chatContainer.children.includes(spacer)) {
80040
+ host.chatContainer.removeChild(spacer);
80041
+ removed = true;
80042
+ }
80043
+ if (text && host.chatContainer.children.includes(text)) {
80044
+ host.chatContainer.removeChild(text);
80045
+ removed = true;
80046
+ }
80047
+ if (removed) host.ui.requestRender();
80048
+ }
78661
80049
  function showError(host, errorMessage4) {
78662
80050
  host.chatContainer.addChild(new Spacer(1));
78663
80051
  host.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage4}`), 1, 0));
@@ -78874,6 +80262,7 @@ function createInteractiveAgentSessionController(host) {
78874
80262
  }
78875
80263
  let retryElapsedTicker;
78876
80264
  let retryElapsedStartedAt = 0;
80265
+ let autoCompactBeforeStats = null;
78877
80266
  const attention = cliAttention(host);
78878
80267
  const stopRetryElapsedTicker = () => {
78879
80268
  if (retryElapsedTicker !== void 0) {
@@ -78939,6 +80328,7 @@ function createInteractiveAgentSessionController(host) {
78939
80328
  if (!firstKeptEntryId) return { aborted: true };
78940
80329
  return { summary: beforeCompact.summary, firstKeptEntryId };
78941
80330
  }
80331
+ autoCompactBeforeStats = summarizeActiveContext(context.sessionManager.getActiveContextSummaries());
78942
80332
  return createCliAutoCompactionResult({
78943
80333
  context,
78944
80334
  model: host.options.model,
@@ -79107,6 +80497,7 @@ function createInteractiveAgentSessionController(host) {
79107
80497
  host.showError(`${prefix}: ${event.errorMessage}`);
79108
80498
  host.footer.setState({ status: "ready" });
79109
80499
  host.setControllerBusy(false);
80500
+ autoCompactBeforeStats = null;
79110
80501
  return;
79111
80502
  }
79112
80503
  host.resetFileObservationState();
@@ -79132,10 +80523,20 @@ function createInteractiveAgentSessionController(host) {
79132
80523
  contextPercent,
79133
80524
  contextWindow
79134
80525
  });
80526
+ applyContextSignalsToFooter(host);
79135
80527
  host.safeRequestRender();
79136
- if (!event.willRetry && !event.result?.aborted) {
79137
- host.showStatus("Context auto-compacted");
80528
+ if (!event.result?.aborted) {
80529
+ if (autoCompactBeforeStats !== null) {
80530
+ renderCompactionDiffPanel(host, {
80531
+ before: autoCompactBeforeStats,
80532
+ after: summarizeActiveContext(host.sessionManager.getActiveContextSummaries())
80533
+ });
80534
+ }
80535
+ if (!event.willRetry) {
80536
+ host.showStatus("Context auto-compacted");
80537
+ }
79138
80538
  }
80539
+ autoCompactBeforeStats = null;
79139
80540
  } else if (event.type === "auto_continue_scheduled") {
79140
80541
  host.setControllerBusy(true);
79141
80542
  if (event.reason === "todo_closure") {
@@ -79382,6 +80783,7 @@ function sendDesktopNotification(reason, options = {}) {
79382
80783
  lastNotificationAtMs = atMs;
79383
80784
  const { title, body } = attentionMessage(reason);
79384
80785
  terminal.notify(title, body);
80786
+ terminal.requestAttention?.();
79385
80787
  return true;
79386
80788
  }
79387
80789
 
@@ -79567,13 +80969,17 @@ function installInteractiveInteractionHandler(host) {
79567
80969
  host.footer.setState({ lifecycleEvent: "\u25B6 resuming" });
79568
80970
  host.safeRequestRender();
79569
80971
  };
79570
- const withTimeout2 = async (promise) => {
80972
+ const withTimeout2 = async (createPromise) => {
80973
+ const controller = new AbortController();
79571
80974
  if (!("timeoutMs" in request) || !request.timeoutMs || request.timeoutMs <= 0) {
79572
- return { value: await promise, timedOut: false };
80975
+ return { value: await createPromise(controller.signal), timedOut: false };
79573
80976
  }
79574
80977
  return await new Promise((resolve7, reject) => {
79575
- const timer = setTimeout(() => resolve7({ timedOut: true }), request.timeoutMs);
79576
- promise.then((value) => {
80978
+ const timer = setTimeout(() => {
80979
+ controller.abort();
80980
+ resolve7({ timedOut: true });
80981
+ }, request.timeoutMs);
80982
+ createPromise(controller.signal).then((value) => {
79577
80983
  clearTimeout(timer);
79578
80984
  resolve7({ value, timedOut: false });
79579
80985
  }).catch((error) => {
@@ -79586,7 +80992,7 @@ function installInteractiveInteractionHandler(host) {
79586
80992
  beginWaitingForUser();
79587
80993
  let resolution = "cancelled";
79588
80994
  try {
79589
- const result = await withTimeout2(createPromise());
80995
+ const result = await withTimeout2(createPromise);
79590
80996
  resolution = interactionAttentionResolution(result);
79591
80997
  return result;
79592
80998
  } finally {
@@ -79609,7 +81015,8 @@ function installInteractiveInteractionHandler(host) {
79609
81015
  optionCount: request.options.length
79610
81016
  });
79611
81017
  const { value: selection, timedOut } = await waitForUser(
79612
- () => showExtensionSelector2(host, request.prompt, request.options, {
81018
+ (signal) => showExtensionSelector2(host, request.prompt, request.options, {
81019
+ signal,
79613
81020
  shortcuts: { left: request.leftShortcut, right: request.rightShortcut }
79614
81021
  })
79615
81022
  );
@@ -79628,7 +81035,7 @@ function installInteractiveInteractionHandler(host) {
79628
81035
  detail: request.detail
79629
81036
  });
79630
81037
  const { value: confirmed, timedOut } = await waitForUser(
79631
- () => showExtensionConfirm(host, request.prompt, request.detail ?? "")
81038
+ (signal) => showExtensionConfirm(host, request.prompt, request.detail ?? "", { signal })
79632
81039
  );
79633
81040
  host.logDebug("interaction:confirm:end", {
79634
81041
  id: request.id,
@@ -79644,7 +81051,7 @@ function installInteractiveInteractionHandler(host) {
79644
81051
  placeholder: request.placeholder
79645
81052
  });
79646
81053
  const { value, timedOut } = await waitForUser(
79647
- () => showExtensionInput(host, request.prompt, request.placeholder)
81054
+ (signal) => showExtensionInput(host, request.prompt, request.placeholder, { signal })
79648
81055
  );
79649
81056
  host.logDebug("interaction:text_input:end", {
79650
81057
  id: request.id,
@@ -79661,7 +81068,7 @@ function installInteractiveInteractionHandler(host) {
79661
81068
  prefillLength: request.prefill?.length ?? 0
79662
81069
  });
79663
81070
  const { value, timedOut } = await waitForUser(
79664
- () => showExtensionEditor(host, request.prompt, request.prefill)
81071
+ (signal) => showExtensionEditor(host, request.prompt, request.prefill, { signal })
79665
81072
  );
79666
81073
  host.logDebug("interaction:multiline_input:end", {
79667
81074
  id: request.id,
@@ -79682,12 +81089,24 @@ function showExtensionNotification(host, message, type = "info") {
79682
81089
  }
79683
81090
  function showExtensionSelector2(host, title, options, opts) {
79684
81091
  return new Promise((resolve7) => {
79685
- host.showStatus(`Waiting for selection: ${title}`);
81092
+ const promptLine = title.split("\n")[0].trim() || title.trim();
81093
+ host.showStatus(`Waiting for selection: ${promptLine}`);
79686
81094
  if (opts?.signal?.aborted) {
81095
+ host.clearStatusLine?.();
79687
81096
  resolve7(void 0);
79688
81097
  return;
79689
81098
  }
81099
+ const settle = (option) => {
81100
+ if (option === void 0) {
81101
+ host.clearStatusLine?.();
81102
+ } else {
81103
+ host.showStatus(`${promptLine} \u2192 ${option}`);
81104
+ }
81105
+ host.restoreEditor();
81106
+ resolve7(option);
81107
+ };
79690
81108
  const onAbort = () => {
81109
+ host.clearStatusLine?.();
79691
81110
  host.restoreEditor();
79692
81111
  resolve7(void 0);
79693
81112
  };
@@ -79697,13 +81116,11 @@ function showExtensionSelector2(host, title, options, opts) {
79697
81116
  options,
79698
81117
  (option) => {
79699
81118
  opts?.signal?.removeEventListener("abort", onAbort);
79700
- host.restoreEditor();
79701
- resolve7(option);
81119
+ settle(option);
79702
81120
  },
79703
81121
  () => {
79704
81122
  opts?.signal?.removeEventListener("abort", onAbort);
79705
- host.restoreEditor();
79706
- resolve7(void 0);
81123
+ settle(void 0);
79707
81124
  },
79708
81125
  opts?.shortcuts
79709
81126
  );
@@ -79723,26 +81140,27 @@ function showExtensionInput(host, title, placeholder, opts) {
79723
81140
  return new Promise((resolve7) => {
79724
81141
  host.showStatus(`Waiting for input: ${title}`);
79725
81142
  if (opts?.signal?.aborted) {
81143
+ host.clearStatusLine?.();
79726
81144
  resolve7(void 0);
79727
81145
  return;
79728
81146
  }
79729
- const onAbort = () => {
81147
+ const settle = (value) => {
81148
+ host.clearStatusLine?.();
79730
81149
  host.restoreEditor();
79731
- resolve7(void 0);
81150
+ resolve7(value);
79732
81151
  };
81152
+ const onAbort = () => settle(void 0);
79733
81153
  opts?.signal?.addEventListener("abort", onAbort, { once: true });
79734
81154
  const input = new ExtensionInputComponent(
79735
81155
  title,
79736
81156
  placeholder,
79737
81157
  (value) => {
79738
81158
  opts?.signal?.removeEventListener("abort", onAbort);
79739
- host.restoreEditor();
79740
- resolve7(value);
81159
+ settle(value);
79741
81160
  },
79742
81161
  () => {
79743
81162
  opts?.signal?.removeEventListener("abort", onAbort);
79744
- host.restoreEditor();
79745
- resolve7(void 0);
81163
+ settle(void 0);
79746
81164
  }
79747
81165
  );
79748
81166
  host.editorContainer.clear();
@@ -79752,9 +81170,20 @@ function showExtensionInput(host, title, placeholder, opts) {
79752
81170
  host.ui.requestRender();
79753
81171
  });
79754
81172
  }
79755
- function showExtensionEditor(host, title, prefill) {
81173
+ function showExtensionEditor(host, title, prefill, opts) {
79756
81174
  return new Promise((resolve7) => {
79757
81175
  host.showStatus(`Waiting for input: ${title}`);
81176
+ const settle = (value) => {
81177
+ host.clearStatusLine?.();
81178
+ host.restoreEditor();
81179
+ resolve7(value);
81180
+ };
81181
+ const onAbort = () => settle(void 0);
81182
+ opts?.signal?.addEventListener("abort", onAbort, { once: true });
81183
+ if (opts?.signal?.aborted) {
81184
+ onAbort();
81185
+ return;
81186
+ }
79758
81187
  const container = new Container();
79759
81188
  container.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
79760
81189
  container.addChild(new Spacer(1));
@@ -79774,13 +81203,13 @@ function showExtensionEditor(host, title, prefill) {
79774
81203
  valueLength: value.length,
79775
81204
  preview: value.slice(0, 120)
79776
81205
  });
79777
- host.restoreEditor();
79778
- resolve7(value);
81206
+ opts?.signal?.removeEventListener("abort", onAbort);
81207
+ settle(value);
79779
81208
  };
79780
81209
  editor.onEscape = () => {
79781
81210
  host.logDebug("extension:editor:cancel", { title });
79782
- host.restoreEditor();
79783
- resolve7(void 0);
81211
+ opts?.signal?.removeEventListener("abort", onAbort);
81212
+ settle(void 0);
79784
81213
  };
79785
81214
  container.addChild(editor);
79786
81215
  container.addChild(new Spacer(1));
@@ -80545,7 +81974,7 @@ var InteractiveMode = class {
80545
81974
  async init() {
80546
81975
  const activeSession = this.sessionManager.getActiveSession();
80547
81976
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
80548
- const { Agent } = await import("./chunks/src-HPIBCE2I.js");
81977
+ const { Agent } = await import("./chunks/src-OWQI5M6W.js");
80549
81978
  this.agent = new Agent({
80550
81979
  initialState: {
80551
81980
  systemPrompt: this.buildSystemPrompt(),
@@ -80771,7 +82200,6 @@ var InteractiveMode = class {
80771
82200
  this.options.settingsManager.setCalmModeEnabled(enabled);
80772
82201
  this.footer.setState({ calmMode: enabled });
80773
82202
  applyCalmMode(this, enabled);
80774
- this.todoWidget.setCalmMode(enabled);
80775
82203
  this.extensionWidgets.setCalmMode(enabled);
80776
82204
  this.showStatus(`Calm mode ${enabled ? "on \u2014 showing questions and final answers only" : "off"}`);
80777
82205
  this.safeRequestRender(true);
@@ -80809,11 +82237,11 @@ var InteractiveMode = class {
80809
82237
  async handleCacheTest() {
80810
82238
  return handleCacheTest(this);
80811
82239
  }
80812
- async handleDebugContextCommand() {
80813
- return handleDebugContextCommand(this);
82240
+ async handleTreeCommand() {
82241
+ return handleTreeCommand(this);
80814
82242
  }
80815
- async handleDebugPackageCommand() {
80816
- return handleDebugPackageCommand(this);
82243
+ handleDebugReportCommand(kind) {
82244
+ return kind === "context" ? handleContextReportCommand(this) : kind === "trace" ? handleTraceCommand(this, this.sessionTrace.getLiveSnapshot()) : handleBootCommand(this);
80817
82245
  }
80818
82246
  async handleResumeCommand() {
80819
82247
  return handleResumeCommand(this);
@@ -80833,9 +82261,6 @@ var InteractiveMode = class {
80833
82261
  setWaitingForUser(waiting) {
80834
82262
  return setWaitingForUser(this, waiting);
80835
82263
  }
80836
- async handleTreeCommand() {
80837
- return handleTreeCommand(this);
80838
- }
80839
82264
  async forkAtEntry(entryId) {
80840
82265
  return forkAtEntry(this, entryId);
80841
82266
  }
@@ -80909,6 +82334,9 @@ var InteractiveMode = class {
80909
82334
  showStatus(message) {
80910
82335
  return showStatus(this.uiHelperHost, message);
80911
82336
  }
82337
+ clearStatusLine() {
82338
+ return clearStatusLine(this.uiHelperHost);
82339
+ }
80912
82340
  showError(errorMessage4) {
80913
82341
  return showError(this.uiHelperHost, errorMessage4);
80914
82342
  }
@@ -81377,7 +82805,7 @@ async function runPrintMode(params) {
81377
82805
  `);
81378
82806
  console.log("Assistant: ");
81379
82807
  }
81380
- const { Agent: AgentClass } = await import("./chunks/src-HPIBCE2I.js");
82808
+ const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
81381
82809
  let agent;
81382
82810
  const printModeExtensions = await loadPrintModeExtensions({
81383
82811
  cwd: runtime.cwd,
@@ -82238,7 +83666,7 @@ async function runJsonEventsMode(params) {
82238
83666
  return;
82239
83667
  }
82240
83668
  const parent = agent;
82241
- const { Agent: AgentClass } = await import("./chunks/src-HPIBCE2I.js");
83669
+ const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
82242
83670
  const sideAgent = new AgentClass({
82243
83671
  initialState: {
82244
83672
  systemPrompt: parent.state.systemPrompt,
@@ -82582,7 +84010,7 @@ async function runJsonEventsMode(params) {
82582
84010
  if (runtime.initialModel.unconfigured || controller) {
82583
84011
  return;
82584
84012
  }
82585
- const { Agent: AgentClass } = await import("./chunks/src-HPIBCE2I.js");
84013
+ const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
82586
84014
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
82587
84015
  extensionRunner = jsonModeExtensions.runner;
82588
84016
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];