@caupulican/pi-adaptative 0.80.31 → 0.80.37

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +13 -1
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/extensions/loader.d.ts +18 -1
  6. package/dist/core/extensions/loader.d.ts.map +1 -1
  7. package/dist/core/extensions/loader.js +130 -17
  8. package/dist/core/extensions/loader.js.map +1 -1
  9. package/dist/core/extensions/types.d.ts +6 -0
  10. package/dist/core/extensions/types.d.ts.map +1 -1
  11. package/dist/core/extensions/types.js.map +1 -1
  12. package/dist/core/session-manager.d.ts +2 -0
  13. package/dist/core/session-manager.d.ts.map +1 -1
  14. package/dist/core/session-manager.js +6 -1
  15. package/dist/core/session-manager.js.map +1 -1
  16. package/dist/modes/interactive/components/footer.d.ts +3 -6
  17. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  18. package/dist/modes/interactive/components/footer.js +45 -21
  19. package/dist/modes/interactive/components/footer.js.map +1 -1
  20. package/dist/modes/interactive/interactive-mode.d.ts +24 -1
  21. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  22. package/dist/modes/interactive/interactive-mode.js +412 -96
  23. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  24. package/docs/extensions.md +24 -0
  25. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  26. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  27. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  28. package/examples/extensions/sandbox/package-lock.json +2 -2
  29. package/examples/extensions/sandbox/package.json +1 -1
  30. package/examples/extensions/with-deps/package-lock.json +2 -2
  31. package/examples/extensions/with-deps/package.json +1 -1
  32. package/npm-shrinkwrap.json +12 -12
  33. package/package.json +5 -5
@@ -98,30 +98,27 @@ export function formatCwdForFooter(cwd, home) {
98
98
  return cwd;
99
99
  return relativeToHome === "" ? "~" : `~${sep}${relativeToHome}`;
100
100
  }
101
- /**
102
- * Footer component that shows pwd, token stats, and context usage.
103
- * Computes token/context stats from session, gets git branch and extension statuses from provider.
104
- */
105
101
  export class FooterComponent {
106
102
  autoCompactEnabled = true;
107
103
  session;
108
104
  footerData;
105
+ usageSnapshot;
109
106
  constructor(session, footerData) {
110
107
  this.session = session;
111
108
  this.footerData = footerData;
112
109
  }
113
110
  setSession(session) {
114
111
  this.session = session;
112
+ this.usageSnapshot = undefined;
115
113
  }
116
114
  setAutoCompactEnabled(enabled) {
117
115
  this.autoCompactEnabled = enabled;
118
116
  }
119
117
  /**
120
- * No-op: git branch caching now handled by provider.
121
- * Kept for compatibility with existing call sites in interactive-mode.
118
+ * Invalidate cached footer stats when session state changes.
122
119
  */
123
120
  invalidate() {
124
- // No-op: git branch is cached/invalidated by provider
121
+ this.usageSnapshot = undefined;
125
122
  }
126
123
  /**
127
124
  * Clean up resources.
@@ -130,26 +127,53 @@ export class FooterComponent {
130
127
  dispose() {
131
128
  // Git watcher cleanup handled by provider
132
129
  }
133
- render(width) {
134
- const state = this.session.state;
135
- // Calculate cumulative usage from ALL session entries (not just post-compaction messages)
130
+ getUsageSnapshot(messageCount) {
131
+ const sessionManager = this.session.sessionManager;
132
+ const entryCount = sessionManager.getEntryCount?.() ?? sessionManager.getEntries().length;
133
+ const cached = this.usageSnapshot;
134
+ if (cached && cached.entryCount === entryCount && cached.messageCount === messageCount) {
135
+ return cached;
136
+ }
137
+ // Calculate cumulative usage from ALL session entries in one batched pass.
138
+ // This avoids per-frame defensive array allocation when only the TUI redraws.
136
139
  let totalInput = 0;
137
140
  let totalOutput = 0;
138
141
  let totalCacheRead = 0;
139
142
  let totalCacheWrite = 0;
140
143
  let totalCost = 0;
141
- for (const entry of this.session.sessionManager.getEntries()) {
142
- if (entry.type === "message" && entry.message.role === "assistant") {
143
- totalInput += entry.message.usage.input;
144
- totalOutput += entry.message.usage.output;
145
- totalCacheRead += entry.message.usage.cacheRead;
146
- totalCacheWrite += entry.message.usage.cacheWrite;
147
- totalCost += entry.message.usage.cost.total;
148
- }
144
+ const entries = this.session.sessionManager.getEntries();
145
+ for (let i = 0; i < entries.length; i++) {
146
+ const entry = entries[i];
147
+ if (entry.type !== "message" || entry.message.role !== "assistant")
148
+ continue;
149
+ const usage = entry.message.usage;
150
+ if (!usage)
151
+ continue;
152
+ totalInput += usage.input;
153
+ totalOutput += usage.output;
154
+ totalCacheRead += usage.cacheRead;
155
+ totalCacheWrite += usage.cacheWrite;
156
+ totalCost += usage.cost.total;
149
157
  }
150
- // Calculate context usage from session (handles compaction correctly).
151
- // After compaction, tokens are unknown until the next LLM response.
152
- const contextUsage = this.session.getContextUsage();
158
+ const snapshot = {
159
+ entryCount,
160
+ messageCount,
161
+ totalInput,
162
+ totalOutput,
163
+ totalCacheRead,
164
+ totalCacheWrite,
165
+ totalCost,
166
+ // Calculate context usage from session (handles compaction correctly).
167
+ // After compaction, tokens are unknown until the next LLM response.
168
+ contextUsage: this.session.getContextUsage(),
169
+ };
170
+ this.usageSnapshot = snapshot;
171
+ return snapshot;
172
+ }
173
+ render(width) {
174
+ const state = this.session.state;
175
+ const usageSnapshot = this.getUsageSnapshot(state.messages?.length ?? 0);
176
+ const { totalInput, totalOutput, totalCacheRead, totalCacheWrite, totalCost, contextUsage } = usageSnapshot;
153
177
  const contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;
154
178
  const contextPercentValue = contextUsage?.percent ?? 0;
155
179
  const contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
@@ -1 +1 @@
1
- {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@caupulican/pi-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\nfunction stripAnsi(text: string): string {\n\treturn text.replace(/\\u001b\\[[0-?]*[ -/]*[@-~]/g, \"\");\n}\n\nfunction normalizeLearningPhase(phase: string): string {\n\tconst normalized = phase\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9_-]+/g, \"\")\n\t\t.trim();\n\tif (!normalized) return \"active\";\n\tif (normalized === \"starting\") return \"start\";\n\tif (normalized === \"mapping\") return \"map\";\n\tif (normalized === \"scanning\") return \"scan\";\n\tif (normalized === \"auditing\") return \"audit\";\n\tif (normalized === \"learning\") return \"run\";\n\tif (normalized === \"pruning\") return \"prune\";\n\treturn normalized.slice(0, 16);\n}\n\nfunction formatExtensionStatuses(statuses: ReadonlyMap<string, string>): string[] {\n\tconst regularStatuses: string[] = [];\n\tconst learningPhases = new Set<string>();\n\tlet sawLearningStatus = false;\n\n\tfor (const [key, rawText] of Array.from(statuses.entries()).sort(([a], [b]) => a.localeCompare(b))) {\n\t\tconst text = sanitizeStatusText(rawText);\n\t\tconst plain = stripAnsi(text).trim();\n\t\tconst plainLower = plain.toLowerCase();\n\t\tlet phase: string | undefined;\n\n\t\tif (plainLower.startsWith(\"(learning)\")) {\n\t\t\tphase = plain.slice(\"(learning)\".length).trim();\n\t\t} else if (plainLower === \"learning\") {\n\t\t\tphase = \"active\";\n\t\t} else if (/^learn(?:ing)?\\s*[: ]/.test(plainLower)) {\n\t\t\tphase = plain.replace(/^learn(?:ing)?\\s*[: ]/i, \"\").trim();\n\t\t}\n\n\t\tif (phase !== undefined) {\n\t\t\tsawLearningStatus = true;\n\t\t\tlearningPhases.add(normalizeLearningPhase(phase));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (key === \"auto-learn\" || key === \"continuous-learning\") {\n\t\t\tsawLearningStatus = true;\n\t\t\tlearningPhases.add(\"active\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tregularStatuses.push(text);\n\t}\n\n\tif (!sawLearningStatus) return regularStatuses;\n\tconst phases = Array.from(learningPhases).filter((phase) => phase !== \"active\");\n\tconst phaseText = phases.length > 0 ? phases.join(\"/\") : \"active\";\n\treturn [theme.fg(\"warning\", \"learn\") + theme.fg(\"dim\", `:${phaseText}`), ...regularStatuses];\n}\n\n/**\n * Format token counts for compact footer display.\n */\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~${sep}${relativeToHome}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\n\tconstructor(session: AgentSession, footerData: ReadonlyFooterDataProvider) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model display name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.name || state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line. Learning-related statuses are\n\t\t// folded into one compact chip so independent learning systems do not render\n\t\t// brittle duplicates like \"(learning) (learning) auto\".\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst statusLine = formatExtensionStatuses(extensionStatuses).join(\" \");\n\t\t\tif (statusLine) {\n\t\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t\t}\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@caupulican/pi-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\nfunction stripAnsi(text: string): string {\n\treturn text.replace(/\\u001b\\[[0-?]*[ -/]*[@-~]/g, \"\");\n}\n\nfunction normalizeLearningPhase(phase: string): string {\n\tconst normalized = phase\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9_-]+/g, \"\")\n\t\t.trim();\n\tif (!normalized) return \"active\";\n\tif (normalized === \"starting\") return \"start\";\n\tif (normalized === \"mapping\") return \"map\";\n\tif (normalized === \"scanning\") return \"scan\";\n\tif (normalized === \"auditing\") return \"audit\";\n\tif (normalized === \"learning\") return \"run\";\n\tif (normalized === \"pruning\") return \"prune\";\n\treturn normalized.slice(0, 16);\n}\n\nfunction formatExtensionStatuses(statuses: ReadonlyMap<string, string>): string[] {\n\tconst regularStatuses: string[] = [];\n\tconst learningPhases = new Set<string>();\n\tlet sawLearningStatus = false;\n\n\tfor (const [key, rawText] of Array.from(statuses.entries()).sort(([a], [b]) => a.localeCompare(b))) {\n\t\tconst text = sanitizeStatusText(rawText);\n\t\tconst plain = stripAnsi(text).trim();\n\t\tconst plainLower = plain.toLowerCase();\n\t\tlet phase: string | undefined;\n\n\t\tif (plainLower.startsWith(\"(learning)\")) {\n\t\t\tphase = plain.slice(\"(learning)\".length).trim();\n\t\t} else if (plainLower === \"learning\") {\n\t\t\tphase = \"active\";\n\t\t} else if (/^learn(?:ing)?\\s*[: ]/.test(plainLower)) {\n\t\t\tphase = plain.replace(/^learn(?:ing)?\\s*[: ]/i, \"\").trim();\n\t\t}\n\n\t\tif (phase !== undefined) {\n\t\t\tsawLearningStatus = true;\n\t\t\tlearningPhases.add(normalizeLearningPhase(phase));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (key === \"auto-learn\" || key === \"continuous-learning\") {\n\t\t\tsawLearningStatus = true;\n\t\t\tlearningPhases.add(\"active\");\n\t\t\tcontinue;\n\t\t}\n\n\t\tregularStatuses.push(text);\n\t}\n\n\tif (!sawLearningStatus) return regularStatuses;\n\tconst phases = Array.from(learningPhases).filter((phase) => phase !== \"active\");\n\tconst phaseText = phases.length > 0 ? phases.join(\"/\") : \"active\";\n\treturn [theme.fg(\"warning\", \"learn\") + theme.fg(\"dim\", `:${phaseText}`), ...regularStatuses];\n}\n\n/**\n * Format token counts for compact footer display.\n */\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~${sep}${relativeToHome}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\ntype FooterUsageSnapshot = {\n\tentryCount: number;\n\tmessageCount: number;\n\ttotalInput: number;\n\ttotalOutput: number;\n\ttotalCacheRead: number;\n\ttotalCacheWrite: number;\n\ttotalCost: number;\n\tcontextUsage: ReturnType<AgentSession[\"getContextUsage\"]>;\n};\n\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\tprivate usageSnapshot?: FooterUsageSnapshot;\n\n\tconstructor(session: AgentSession, footerData: ReadonlyFooterDataProvider) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t\tthis.usageSnapshot = undefined;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Invalidate cached footer stats when session state changes.\n\t */\n\tinvalidate(): void {\n\t\tthis.usageSnapshot = undefined;\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\tprivate getUsageSnapshot(messageCount: number): FooterUsageSnapshot {\n\t\tconst sessionManager = this.session.sessionManager as AgentSession[\"sessionManager\"] & {\n\t\t\tgetEntryCount?: () => number;\n\t\t};\n\t\tconst entryCount = sessionManager.getEntryCount?.() ?? sessionManager.getEntries().length;\n\t\tconst cached = this.usageSnapshot;\n\t\tif (cached && cached.entryCount === entryCount && cached.messageCount === messageCount) {\n\t\t\treturn cached;\n\t\t}\n\n\t\t// Calculate cumulative usage from ALL session entries in one batched pass.\n\t\t// This avoids per-frame defensive array allocation when only the TUI redraws.\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tconst entries = this.session.sessionManager.getEntries();\n\t\tfor (let i = 0; i < entries.length; i++) {\n\t\t\tconst entry = entries[i];\n\t\t\tif (entry.type !== \"message\" || entry.message.role !== \"assistant\") continue;\n\t\t\tconst usage = entry.message.usage;\n\t\t\tif (!usage) continue;\n\t\t\ttotalInput += usage.input;\n\t\t\ttotalOutput += usage.output;\n\t\t\ttotalCacheRead += usage.cacheRead;\n\t\t\ttotalCacheWrite += usage.cacheWrite;\n\t\t\ttotalCost += usage.cost.total;\n\t\t}\n\n\t\tconst snapshot: FooterUsageSnapshot = {\n\t\t\tentryCount,\n\t\t\tmessageCount,\n\t\t\ttotalInput,\n\t\t\ttotalOutput,\n\t\t\ttotalCacheRead,\n\t\t\ttotalCacheWrite,\n\t\t\ttotalCost,\n\t\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\t\tcontextUsage: this.session.getContextUsage(),\n\t\t};\n\t\tthis.usageSnapshot = snapshot;\n\t\treturn snapshot;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\t\tconst usageSnapshot = this.getUsageSnapshot(state.messages?.length ?? 0);\n\t\tconst { totalInput, totalOutput, totalCacheRead, totalCacheWrite, totalCost, contextUsage } = usageSnapshot;\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model display name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.name || state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line. Learning-related statuses are\n\t\t// folded into one compact chip so independent learning systems do not render\n\t\t// brittle duplicates like \"(learning) (learning) auto\".\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst statusLine = formatExtensionStatuses(extensionStatuses).join(\" \");\n\t\t\tif (statusLine) {\n\t\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t\t}\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
@@ -94,8 +94,14 @@ export declare class InteractiveMode {
94
94
  private anthropicSubscriptionWarningShown;
95
95
  private lastStatusSpacer;
96
96
  private lastStatusText;
97
+ private liveHistoryHiddenNotice;
98
+ private liveHistoryHiddenComponents;
99
+ private tuiHistoryLoaded;
100
+ private tuiHistoryLoadInProgress;
97
101
  private streamingComponent;
98
102
  private streamingMessage;
103
+ private streamingUiUpdateTimer;
104
+ private lastStreamingUiUpdateAt;
99
105
  private toolPanels;
100
106
  private toolOutputExpanded;
101
107
  private hideThinkingBlock;
@@ -284,6 +290,15 @@ export declare class InteractiveMode {
284
290
  private handleEvent;
285
291
  /** Extract text content from a user message */
286
292
  private getUserMessageText;
293
+ private resetLiveTuiHistoryTrim;
294
+ private clearPendingStreamingUiUpdate;
295
+ private getSessionEntryCount;
296
+ private showDeferredHistoryPlaceholder;
297
+ private loadTuiHistoryOnDemand;
298
+ private attachStreamingToolPanels;
299
+ private applyStreamingMessageUpdate;
300
+ private trimLiveTuiHistory;
301
+ private appendStatusToChat;
287
302
  /**
288
303
  * Show a status message in the chat.
289
304
  *
@@ -292,6 +307,12 @@ export declare class InteractiveMode {
292
307
  */
293
308
  private showStatus;
294
309
  private addMessageToChat;
310
+ private getContentText;
311
+ private getTuiHistoryMessageText;
312
+ private estimateTuiHistoryLines;
313
+ private trimTextToTuiHistoryTail;
314
+ private trimMessageToTuiHistoryTail;
315
+ private messagesForTuiHistoryReload;
295
316
  /**
296
317
  * Render session context to chat. Used for initial load and rebuild after compaction.
297
318
  * @param sessionContext Session context to render
@@ -300,7 +321,9 @@ export declare class InteractiveMode {
300
321
  */
301
322
  private renderGeneration;
302
323
  private renderSessionContext;
303
- renderInitialMessages(): Promise<void>;
324
+ renderInitialMessages(options?: {
325
+ forceHistoryLoad?: boolean;
326
+ }): Promise<void>;
304
327
  getUserInput(): Promise<UserInputSubmission>;
305
328
  private rebuildChatFromMessages;
306
329
  private handleCtrlC;