praana 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/bin/praana.js +17 -0
  4. package/bin/pran.js +17 -0
  5. package/dist/app-banner.d.ts +11 -0
  6. package/dist/app-banner.js +161 -0
  7. package/dist/app-controller.d.ts +44 -0
  8. package/dist/app-controller.js +143 -0
  9. package/dist/app-identity.d.ts +18 -0
  10. package/dist/app-identity.js +52 -0
  11. package/dist/auto-compact.d.ts +16 -0
  12. package/dist/auto-compact.js +101 -0
  13. package/dist/cli-args.d.ts +14 -0
  14. package/dist/cli-args.js +69 -0
  15. package/dist/compile-classic.d.ts +21 -0
  16. package/dist/compile-classic.js +106 -0
  17. package/dist/compiler.d.ts +75 -0
  18. package/dist/compiler.js +406 -0
  19. package/dist/config.d.ts +3 -0
  20. package/dist/config.js +433 -0
  21. package/dist/context-engine/activity-log.d.ts +9 -0
  22. package/dist/context-engine/activity-log.js +109 -0
  23. package/dist/context-engine/artifact-store.d.ts +32 -0
  24. package/dist/context-engine/artifact-store.js +272 -0
  25. package/dist/context-engine/bm25.d.ts +3 -0
  26. package/dist/context-engine/bm25.js +32 -0
  27. package/dist/context-engine/checkpoint.d.ts +34 -0
  28. package/dist/context-engine/checkpoint.js +430 -0
  29. package/dist/context-engine/classify.d.ts +3 -0
  30. package/dist/context-engine/classify.js +60 -0
  31. package/dist/context-engine/db.d.ts +73 -0
  32. package/dist/context-engine/db.js +505 -0
  33. package/dist/context-engine/distiller.d.ts +30 -0
  34. package/dist/context-engine/distiller.js +67 -0
  35. package/dist/context-engine/engine-compiler.d.ts +23 -0
  36. package/dist/context-engine/engine-compiler.js +297 -0
  37. package/dist/context-engine/error-tracker.d.ts +21 -0
  38. package/dist/context-engine/error-tracker.js +74 -0
  39. package/dist/context-engine/event-lineage.d.ts +26 -0
  40. package/dist/context-engine/event-lineage.js +120 -0
  41. package/dist/context-engine/extraction.d.ts +26 -0
  42. package/dist/context-engine/extraction.js +83 -0
  43. package/dist/context-engine/index.d.ts +82 -0
  44. package/dist/context-engine/index.js +238 -0
  45. package/dist/context-engine/scoring.d.ts +13 -0
  46. package/dist/context-engine/scoring.js +47 -0
  47. package/dist/context-engine/state-snapshot.d.ts +8 -0
  48. package/dist/context-engine/state-snapshot.js +50 -0
  49. package/dist/context-engine/summarize.d.ts +6 -0
  50. package/dist/context-engine/summarize.js +32 -0
  51. package/dist/context-engine/telemetry.d.ts +25 -0
  52. package/dist/context-engine/telemetry.js +64 -0
  53. package/dist/context-engine/turn-digest.d.ts +50 -0
  54. package/dist/context-engine/turn-digest.js +250 -0
  55. package/dist/context-engine/turn-ledger.d.ts +18 -0
  56. package/dist/context-engine/turn-ledger.js +184 -0
  57. package/dist/context-engine/turn-recorder.d.ts +24 -0
  58. package/dist/context-engine/turn-recorder.js +88 -0
  59. package/dist/context-engine/types.d.ts +201 -0
  60. package/dist/context-engine/types.js +4 -0
  61. package/dist/context-pressure.d.ts +19 -0
  62. package/dist/context-pressure.js +36 -0
  63. package/dist/distillers/generic.d.ts +14 -0
  64. package/dist/distillers/generic.js +93 -0
  65. package/dist/distillers/git-diff.d.ts +8 -0
  66. package/dist/distillers/git-diff.js +119 -0
  67. package/dist/distillers/index.d.ts +2 -0
  68. package/dist/distillers/index.js +16 -0
  69. package/dist/distillers/npm-test.d.ts +8 -0
  70. package/dist/distillers/npm-test.js +50 -0
  71. package/dist/distillers/rg-results.d.ts +8 -0
  72. package/dist/distillers/rg-results.js +28 -0
  73. package/dist/distillers/tsc-errors.d.ts +8 -0
  74. package/dist/distillers/tsc-errors.js +52 -0
  75. package/dist/event-log.d.ts +56 -0
  76. package/dist/event-log.js +214 -0
  77. package/dist/llm.d.ts +29 -0
  78. package/dist/llm.js +155 -0
  79. package/dist/logger.d.ts +94 -0
  80. package/dist/logger.js +287 -0
  81. package/dist/main.d.ts +1 -0
  82. package/dist/main.js +54 -0
  83. package/dist/memory/confidence.d.ts +7 -0
  84. package/dist/memory/confidence.js +37 -0
  85. package/dist/memory/consolidation.d.ts +26 -0
  86. package/dist/memory/consolidation.js +166 -0
  87. package/dist/memory/db.d.ts +40 -0
  88. package/dist/memory/db.js +283 -0
  89. package/dist/memory/dedup.d.ts +6 -0
  90. package/dist/memory/dedup.js +50 -0
  91. package/dist/memory/embedder-factory.d.ts +3 -0
  92. package/dist/memory/embedder-factory.js +81 -0
  93. package/dist/memory/embeddings.d.ts +15 -0
  94. package/dist/memory/embeddings.js +67 -0
  95. package/dist/memory/index.d.ts +9 -0
  96. package/dist/memory/index.js +11 -0
  97. package/dist/memory/ollama-summarizer.d.ts +19 -0
  98. package/dist/memory/ollama-summarizer.js +72 -0
  99. package/dist/memory/openai-summarizer.d.ts +21 -0
  100. package/dist/memory/openai-summarizer.js +51 -0
  101. package/dist/memory/store.d.ts +61 -0
  102. package/dist/memory/store.js +502 -0
  103. package/dist/memory/summarizer-factory.d.ts +3 -0
  104. package/dist/memory/summarizer-factory.js +69 -0
  105. package/dist/memory/summarizer.d.ts +4 -0
  106. package/dist/memory/summarizer.js +112 -0
  107. package/dist/memory/types.d.ts +87 -0
  108. package/dist/memory/types.js +17 -0
  109. package/dist/model-context.d.ts +15 -0
  110. package/dist/model-context.js +212 -0
  111. package/dist/project-detector.d.ts +37 -0
  112. package/dist/project-detector.js +604 -0
  113. package/dist/render.d.ts +15 -0
  114. package/dist/render.js +46 -0
  115. package/dist/session.d.ts +118 -0
  116. package/dist/session.js +809 -0
  117. package/dist/skills/index.d.ts +69 -0
  118. package/dist/skills/index.js +885 -0
  119. package/dist/skills/types.d.ts +93 -0
  120. package/dist/skills/types.js +8 -0
  121. package/dist/slash-commands.d.ts +14 -0
  122. package/dist/slash-commands.js +301 -0
  123. package/dist/state-graph.d.ts +38 -0
  124. package/dist/state-graph.js +255 -0
  125. package/dist/status-bar.d.ts +54 -0
  126. package/dist/status-bar.js +184 -0
  127. package/dist/thinking-display.d.ts +21 -0
  128. package/dist/thinking-display.js +37 -0
  129. package/dist/tool-summary.d.ts +4 -0
  130. package/dist/tool-summary.js +67 -0
  131. package/dist/tools/index.d.ts +925 -0
  132. package/dist/tools/index.js +86 -0
  133. package/dist/tools/knowledge.d.ts +140 -0
  134. package/dist/tools/knowledge.js +260 -0
  135. package/dist/tools/memory.d.ts +39 -0
  136. package/dist/tools/memory.js +300 -0
  137. package/dist/tools/search-code.d.ts +134 -0
  138. package/dist/tools/search-code.js +390 -0
  139. package/dist/tools/system.d.ts +16 -0
  140. package/dist/tools/system.js +499 -0
  141. package/dist/tools/tool-def.d.ts +6 -0
  142. package/dist/tools/tool-def.js +3 -0
  143. package/dist/turn-control.d.ts +51 -0
  144. package/dist/turn-control.js +210 -0
  145. package/dist/turn.d.ts +20 -0
  146. package/dist/turn.js +624 -0
  147. package/dist/types.d.ts +233 -0
  148. package/dist/types.js +4 -0
  149. package/dist/ui/readline-ui.d.ts +2 -0
  150. package/dist/ui/readline-ui.js +176 -0
  151. package/dist/ui/tui/app.d.ts +13 -0
  152. package/dist/ui/tui/app.js +270 -0
  153. package/dist/ui/tui/busy-indicator.d.ts +2 -0
  154. package/dist/ui/tui/busy-indicator.js +13 -0
  155. package/dist/ui/tui/components/gutter-rule.d.ts +5 -0
  156. package/dist/ui/tui/components/gutter-rule.js +9 -0
  157. package/dist/ui/tui/components/inline-tool-row.d.ts +10 -0
  158. package/dist/ui/tui/components/inline-tool-row.js +8 -0
  159. package/dist/ui/tui/components/prompt-input.d.ts +20 -0
  160. package/dist/ui/tui/components/prompt-input.js +120 -0
  161. package/dist/ui/tui/components/system-line.d.ts +5 -0
  162. package/dist/ui/tui/components/system-line.js +6 -0
  163. package/dist/ui/tui/components/thinking-block.d.ts +11 -0
  164. package/dist/ui/tui/components/thinking-block.js +31 -0
  165. package/dist/ui/tui/components/toast-line.d.ts +4 -0
  166. package/dist/ui/tui/components/toast-line.js +8 -0
  167. package/dist/ui/tui/components/tool-result-line.d.ts +5 -0
  168. package/dist/ui/tui/components/tool-result-line.js +6 -0
  169. package/dist/ui/tui/components/turn-footer.d.ts +5 -0
  170. package/dist/ui/tui/components/turn-footer.js +7 -0
  171. package/dist/ui/tui/components/user-block.d.ts +6 -0
  172. package/dist/ui/tui/components/user-block.js +6 -0
  173. package/dist/ui/tui/logo-banner.d.ts +5 -0
  174. package/dist/ui/tui/logo-banner.js +8 -0
  175. package/dist/ui/tui/markdown-render.d.ts +16 -0
  176. package/dist/ui/tui/markdown-render.js +218 -0
  177. package/dist/ui/tui/palette.d.ts +12 -0
  178. package/dist/ui/tui/palette.js +13 -0
  179. package/dist/ui/tui/reasoning-summary.d.ts +12 -0
  180. package/dist/ui/tui/reasoning-summary.js +27 -0
  181. package/dist/ui/tui/reducer.d.ts +92 -0
  182. package/dist/ui/tui/reducer.js +260 -0
  183. package/dist/ui/tui/run.d.ts +3 -0
  184. package/dist/ui/tui/run.js +40 -0
  185. package/dist/ui/tui/sink.d.ts +4 -0
  186. package/dist/ui/tui/sink.js +89 -0
  187. package/dist/ui/tui/status-bar-view.d.ts +5 -0
  188. package/dist/ui/tui/status-bar-view.js +44 -0
  189. package/dist/ui/tui/terminal-height.d.ts +12 -0
  190. package/dist/ui/tui/terminal-height.js +20 -0
  191. package/dist/ui/tui/terminal-width.d.ts +2 -0
  192. package/dist/ui/tui/terminal-width.js +5 -0
  193. package/dist/ui/tui/tool-display.d.ts +23 -0
  194. package/dist/ui/tui/tool-display.js +217 -0
  195. package/dist/ui/tui/transcript-line.d.ts +12 -0
  196. package/dist/ui/tui/transcript-line.js +43 -0
  197. package/dist/ui/tui/transcript-replay.d.ts +12 -0
  198. package/dist/ui/tui/transcript-replay.js +117 -0
  199. package/dist/ui-events.d.ts +39 -0
  200. package/dist/ui-events.js +33 -0
  201. package/dist/ui.d.ts +77 -0
  202. package/dist/ui.js +179 -0
  203. package/package.json +73 -0
  204. package/praana.config.example.toml +231 -0
@@ -0,0 +1,233 @@
1
+ export type StateObjectKind = "task" | "decision" | "constraint" | "note";
2
+ export type StateTier = "active" | "soft" | "hard";
3
+ export interface TaskPayload {
4
+ title: string;
5
+ description?: string;
6
+ status: "todo" | "doing" | "done";
7
+ }
8
+ export interface DecisionPayload {
9
+ summary: string;
10
+ rationale: string;
11
+ }
12
+ export interface ConstraintPayload {
13
+ text: string;
14
+ }
15
+ export interface NotePayload {
16
+ text: string;
17
+ }
18
+ export type StatePayload = TaskPayload | DecisionPayload | ConstraintPayload | NotePayload;
19
+ export interface StateObject {
20
+ id: string;
21
+ kind: StateObjectKind;
22
+ tier: StateTier;
23
+ payload: StatePayload;
24
+ created: number;
25
+ updated: number;
26
+ lastTouched: number;
27
+ focused?: boolean;
28
+ retracted?: boolean;
29
+ }
30
+ export type EventKind = "user_message" | "agent_message" | "tool_call" | "tool_result" | "context_action" | "system_note";
31
+ export type EventActor = "user" | "agent" | "kernel" | "tool";
32
+ export interface Event {
33
+ event_id: string;
34
+ session_id: string;
35
+ timestamp: number;
36
+ kind: EventKind;
37
+ actor: EventActor;
38
+ payload: Record<string, unknown>;
39
+ }
40
+ export interface ToolResult {
41
+ ok: boolean;
42
+ output?: string;
43
+ error?: string;
44
+ }
45
+ export interface CreateTaskResult extends ToolResult {
46
+ id?: string;
47
+ }
48
+ export interface ListStateResult extends ToolResult {
49
+ objects?: Array<{
50
+ id: string;
51
+ kind: StateObjectKind;
52
+ tier: StateTier;
53
+ summary: string;
54
+ }>;
55
+ }
56
+ export interface HydrateResult extends ToolResult {
57
+ payload?: Record<string, unknown>;
58
+ }
59
+ export interface RecallResult extends ToolResult {
60
+ entries?: Array<{
61
+ id: string;
62
+ kind: string;
63
+ content: string;
64
+ score?: number;
65
+ confidence?: number;
66
+ scopes?: string[];
67
+ }>;
68
+ }
69
+ export interface RememberResult extends ToolResult {
70
+ id?: string;
71
+ }
72
+ export interface LlmConfig {
73
+ provider: string;
74
+ model: string;
75
+ base_url?: string;
76
+ /** Override model context window (input tokens) for pressure and compaction. */
77
+ context_window?: number;
78
+ }
79
+ export type EmbedderStrategy = "auto" | "ollama" | "transformers" | "llama-cpp" | "hash";
80
+ export interface MemoryConfig {
81
+ enabled: boolean;
82
+ /** disabled | ollama | openrouter | openai */
83
+ summarizer: string;
84
+ db_path?: string;
85
+ embedder?: EmbedderStrategy;
86
+ ollama_url?: string;
87
+ /** Embedding model (e.g. nomic-embed-text) */
88
+ ollama_model?: string;
89
+ /** Chat model for session-end learnings (e.g. qwen3.5:4b). Falls back to first non-embed model from `ollama list`. */
90
+ ollama_summarizer_model?: string;
91
+ }
92
+ export interface CompilerConfig {
93
+ token_budget: number;
94
+ recent_turns: number;
95
+ recent_turns_token_budget?: number;
96
+ /** Minimum digest score for a memory entry to appear in the prompt. */
97
+ recall_min_score?: number;
98
+ /** Max share of usable prompt budget for cross-session memory section. */
99
+ memories_budget_ratio?: number;
100
+ /** Max share of usable prompt budget for project context (AGENTS.md). */
101
+ agents_budget_ratio?: number;
102
+ /** @deprecated Use agents_budget_ratio */
103
+ skills_budget_ratio?: number;
104
+ /** Tokens reserved for model output when computing section ceilings. */
105
+ reserved_output_tokens?: number;
106
+ /** Context fill ratio (0–1) that triggers auto-compaction. Default: 0.75. */
107
+ auto_compact_at?: number;
108
+ /** Disarm compaction hysteresis below this ratio. Default: 0.55. */
109
+ auto_compact_clear_at?: number;
110
+ /** Fraction (0–1) of oldest transcript events to compact per trigger. Default: 0.25. */
111
+ compact_chunk_fraction?: number;
112
+ /** Classic mode: never auto-compact (full verbatim until model limit). Default: false. */
113
+ verbatim_only?: boolean;
114
+ /** @deprecated Use auto_compact_at */
115
+ compression_watermark?: number;
116
+ /** @deprecated Use compact_chunk_fraction */
117
+ compression_flush_fraction?: number;
118
+ }
119
+ export interface TiersConfig {
120
+ idle_soft_after_turns: number;
121
+ idle_hard_after_turns: number;
122
+ }
123
+ export interface SessionConfig {
124
+ log_dir: string;
125
+ }
126
+ export interface ConsolidationConfig {
127
+ /** Enable/disable the background consolidation processor. */
128
+ enabled: boolean;
129
+ /** LLM model for consolidation (defaults to memory.summarizer). */
130
+ model?: string;
131
+ /** Number of confirmations needed to promote to Layer 2. Default: 3. */
132
+ promotion_threshold: number;
133
+ /** Delay in seconds after session end before running consolidation. Default: 30. */
134
+ run_delay_seconds: number;
135
+ }
136
+ export interface SandboxConfig {
137
+ enabled: boolean;
138
+ readonly allowed_paths: readonly string[];
139
+ }
140
+ export type { SkillMetadata, SkillRecord } from "./skills/types.js";
141
+ export interface SkillsConfig {
142
+ enabled: boolean;
143
+ max_token_budget_ratio: number;
144
+ active_skill_idle_turns: number;
145
+ warm_skill_eviction_turns: number;
146
+ max_depth: number;
147
+ }
148
+ export interface EditConfig {
149
+ confirm: boolean;
150
+ }
151
+ export interface SearchCodeConfig {
152
+ /** Absolute path to the ripgrep binary. Omit to use system "rg" via PATH. */
153
+ rg_path?: string;
154
+ }
155
+ export type UiMode = "readline" | "tui";
156
+ export type UiScreenMode = "preserve" | "alternate";
157
+ export interface UiConfig {
158
+ mode: UiMode;
159
+ screen: UiScreenMode;
160
+ markdown_rendering: boolean;
161
+ syntax_highlighting: boolean;
162
+ syntax_theme: string;
163
+ }
164
+ export type DistillerIntensity = "lite" | "full";
165
+ export interface ContextEngineDistillerConfig {
166
+ default_intensity: DistillerIntensity;
167
+ }
168
+ export interface ContextEngineScoringConfig {
169
+ w_pin: number;
170
+ w_recency: number;
171
+ w_relevance: number;
172
+ }
173
+ export interface ContextEnginePressureConfig {
174
+ compact_at: number;
175
+ emergency_at: number;
176
+ }
177
+ export interface ContextEngineConfig {
178
+ /** false = classic mode (full verbatim history, no StateGraph, skill metadata only). */
179
+ enabled: boolean;
180
+ /** Write context-engine telemetry when engine is off (debug / comparison). */
181
+ measurement_mode: boolean;
182
+ /** Tool outputs at or below this token count appear verbatim in the prompt. */
183
+ artifact_inline_threshold: number;
184
+ /** Turns without access before an artifact is eligible for eviction. */
185
+ artifact_ttl_turns: number;
186
+ distiller: ContextEngineDistillerConfig;
187
+ /** Use LLM for ambiguous userIntent extraction (default: first 120 chars). */
188
+ llm_digest: boolean;
189
+ /** Max rolling activity entries kept for checkpoint preview. */
190
+ activity_log_max_entries: number;
191
+ /** Enable structured SessionCheckpoint in the prompt. */
192
+ checkpoint_enabled: boolean;
193
+ scoring: ContextEngineScoringConfig;
194
+ pressure: ContextEnginePressureConfig;
195
+ }
196
+ export interface ProjectDetectionConfig {
197
+ enabled: boolean;
198
+ /** Override auto-detected languages (e.g. ["TypeScript", "Python"]) */
199
+ manual_languages?: string[];
200
+ /** Override auto-detected frameworks (e.g. ["React", "FastAPI"]) */
201
+ manual_frameworks?: string[];
202
+ }
203
+ export interface PraanaConfig {
204
+ llm: LlmConfig;
205
+ memory: MemoryConfig;
206
+ compiler: CompilerConfig;
207
+ tiers: TiersConfig;
208
+ session: SessionConfig;
209
+ consolidation: ConsolidationConfig;
210
+ shell: SandboxConfig;
211
+ edit: EditConfig;
212
+ search_code?: SearchCodeConfig;
213
+ skills: SkillsConfig;
214
+ ui: UiConfig;
215
+ context_engine: ContextEngineConfig;
216
+ project_detection: ProjectDetectionConfig;
217
+ }
218
+ export interface SessionMeta {
219
+ session_id: string;
220
+ started_at: number;
221
+ cwd: string;
222
+ agent: string;
223
+ }
224
+ export interface CompilerOptions {
225
+ stateGraph: any;
226
+ memoryDigest: string | null;
227
+ recentEvents: Event[];
228
+ userInput: string;
229
+ toolSchemas: string[];
230
+ cwd: string;
231
+ sessionId: string;
232
+ tokenBudget: number;
233
+ }
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ // ============================================================
2
+ // PRAANA — Core Types
3
+ // ============================================================
4
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { AppController, StartupInfo } from "../app-controller.js";
2
+ export declare function runReadlineUi(controller: AppController, info: StartupInfo): Promise<void>;
@@ -0,0 +1,176 @@
1
+ import * as readline from "node:readline";
2
+ import chalk from "chalk";
3
+ import { TurnAbortedError, EscInterruptListener } from "../turn-control.js";
4
+ import { formatEmojiStatusLine } from "../status-bar.js";
5
+ import { printSessionBanner, printSessionEndSummary } from "../app-banner.js";
6
+ import { createDefaultTurnSink } from "../ui-events.js";
7
+ import { createThinkingState, onThinkingDelta, closeThinking as closeThinkingBlock, toggleThinking, } from "../thinking-display.js";
8
+ import { startSpinner, stopSpinner } from "../ui.js";
9
+ import { writeMarkdown } from "../render.js";
10
+ export async function runReadlineUi(controller, info) {
11
+ const { session, cwd, model, isResume } = info;
12
+ printSessionBanner(session, cwd, model);
13
+ if (isResume) {
14
+ if (info.recentConversationLines.length > 0) {
15
+ console.log("\n" + info.recentConversationLines.join("\n") + "\n");
16
+ }
17
+ }
18
+ const rl = readline.createInterface({
19
+ input: process.stdin,
20
+ output: process.stdout,
21
+ prompt: "◆ ",
22
+ });
23
+ const refreshStatusBar = () => {
24
+ const line = formatEmojiStatusLine(controller.getStatusBarInput());
25
+ if (process.stderr.isTTY) {
26
+ process.stderr.write(line + "\n");
27
+ }
28
+ else {
29
+ console.log(line);
30
+ }
31
+ };
32
+ refreshStatusBar();
33
+ console.log();
34
+ rl.prompt();
35
+ const escListener = new EscInterruptListener();
36
+ const handleUserInterrupt = () => {
37
+ controller.handleUserInterrupt(() => {
38
+ console.log("\nUse /exit to save and quit.");
39
+ rl.prompt();
40
+ });
41
+ };
42
+ process.on("SIGINT", handleUserInterrupt);
43
+ rl.on("line", async (line) => {
44
+ const input = line.trim();
45
+ if (!input) {
46
+ rl.prompt();
47
+ return;
48
+ }
49
+ if (input.startsWith("/")) {
50
+ const result = await controller.executeSlashCommand(input);
51
+ for (const l of result.lines) {
52
+ if (l)
53
+ console.log(l);
54
+ }
55
+ if (result.action === "refresh_status")
56
+ refreshStatusBar();
57
+ if (result.action === "exit") {
58
+ rl.close();
59
+ return;
60
+ }
61
+ rl.prompt();
62
+ return;
63
+ }
64
+ startSpinner("thinking…");
65
+ let spinnerStopped = false;
66
+ const stopSpinnerOnce = () => {
67
+ if (spinnerStopped)
68
+ return;
69
+ stopSpinner();
70
+ spinnerStopped = true;
71
+ };
72
+ const thinking = createThinkingState(controller.showThinking);
73
+ const closeThinking = () => {
74
+ const summary = closeThinkingBlock(thinking);
75
+ if (summary) {
76
+ process.stdout.write(chalk.dim(`\n${summary}\n`));
77
+ }
78
+ else {
79
+ process.stdout.write("\n");
80
+ }
81
+ };
82
+ const onKeypress = (_, key) => {
83
+ if (key?.name === "t") {
84
+ const nowVisible = toggleThinking(thinking);
85
+ process.stdout.write(chalk.dim(nowVisible ? "\n[thinking on]" : "\n[thinking off]"));
86
+ refreshStatusBar();
87
+ }
88
+ };
89
+ const markdownRendering = controller.config.ui.markdown_rendering;
90
+ const syntaxTheme = controller.config.ui.syntax_theme;
91
+ let textBuffer = "";
92
+ const flushMarkdown = () => {
93
+ if (textBuffer) {
94
+ if (markdownRendering) {
95
+ // Strip trailing \n before rendering — marked adds its own paragraph breaks.
96
+ // Leaving it causes double newlines when successive deltas flush at \n boundaries.
97
+ const text = textBuffer.replace(/\n$/, "");
98
+ if (text)
99
+ writeMarkdown(text, process.stdout);
100
+ }
101
+ else {
102
+ process.stdout.write(textBuffer);
103
+ }
104
+ textBuffer = "";
105
+ }
106
+ };
107
+ const sink = createDefaultTurnSink({
108
+ onThinkingDelta: (delta) => {
109
+ stopSpinnerOnce();
110
+ const { printHeader, printDelta } = onThinkingDelta(thinking, delta);
111
+ if (!printDelta)
112
+ return;
113
+ if (printHeader) {
114
+ process.stdout.write(chalk.dim("\n\n[thinking]\n"));
115
+ }
116
+ process.stdout.write(chalk.dim(delta));
117
+ },
118
+ onTextDelta: (delta) => {
119
+ stopSpinnerOnce();
120
+ closeThinking();
121
+ if (!markdownRendering) {
122
+ process.stdout.write(delta);
123
+ return;
124
+ }
125
+ textBuffer += delta;
126
+ const lastNewline = textBuffer.lastIndexOf("\n");
127
+ if (lastNewline >= 0) {
128
+ const toFlush = textBuffer.slice(0, lastNewline);
129
+ textBuffer = textBuffer.slice(lastNewline + 1);
130
+ if (toFlush)
131
+ writeMarkdown(toFlush, process.stdout);
132
+ }
133
+ },
134
+ onToolCallsStart: () => {
135
+ flushMarkdown();
136
+ closeThinking();
137
+ },
138
+ });
139
+ sink.flushText = flushMarkdown;
140
+ process.stdin.on("keypress", onKeypress);
141
+ escListener.start(() => controller.abortTurn(), rl);
142
+ try {
143
+ await controller.runUserTurn(input, sink);
144
+ flushMarkdown();
145
+ closeThinking();
146
+ stopSpinnerOnce();
147
+ }
148
+ catch (err) {
149
+ stopSpinnerOnce();
150
+ closeThinking();
151
+ if (err instanceof TurnAbortedError) {
152
+ console.log(chalk.yellow("\n[interrupted]"));
153
+ }
154
+ else {
155
+ session.getLogger().error("Turn failed", {
156
+ code: "TURN_FAILED",
157
+ cause: err,
158
+ });
159
+ }
160
+ }
161
+ finally {
162
+ process.stdin.removeListener("keypress", onKeypress);
163
+ escListener.stop();
164
+ }
165
+ console.log();
166
+ refreshStatusBar();
167
+ rl.prompt();
168
+ });
169
+ rl.on("close", async () => {
170
+ console.log("\nShutting down...");
171
+ await controller.shutdown();
172
+ printSessionEndSummary(session);
173
+ process.exit(0);
174
+ });
175
+ rl.on("SIGINT", handleUserInterrupt);
176
+ }
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import type { AppController } from "../../app-controller.js";
3
+ export interface TuiAppProps {
4
+ controller: AppController;
5
+ initialStatus: import("../../status-bar.js").StatusBarInput;
6
+ recentLines: string[];
7
+ transcriptBootstrap?: import("./reducer.js").TranscriptEntry[];
8
+ bootSummary?: string;
9
+ markdownRendering?: boolean;
10
+ syntaxHighlighting?: boolean;
11
+ syntaxTheme?: string;
12
+ }
13
+ export declare function TuiApp({ controller, initialStatus, recentLines, transcriptBootstrap, bootSummary, markdownRendering, syntaxHighlighting, syntaxTheme, }: TuiAppProps): React.JSX.Element;