deepclause-sdk 0.0.88 → 0.1.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 (318) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +418 -914
  3. package/dist/agent.d.ts +19 -2
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +221 -4
  6. package/dist/agent.js.map +1 -1
  7. package/dist/cli/config.d.ts +323 -176
  8. package/dist/cli/config.d.ts.map +1 -1
  9. package/dist/cli/config.js +146 -188
  10. package/dist/cli/config.js.map +1 -1
  11. package/dist/cli/index.d.ts +3 -2
  12. package/dist/cli/index.d.ts.map +1 -1
  13. package/dist/cli/index.js +61 -425
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/cli/login.d.ts +29 -0
  16. package/dist/cli/login.d.ts.map +1 -0
  17. package/dist/cli/login.js +184 -0
  18. package/dist/cli/login.js.map +1 -0
  19. package/dist/cli/readline.d.ts +6 -0
  20. package/dist/cli/readline.d.ts.map +1 -0
  21. package/dist/cli/readline.js +15 -0
  22. package/dist/cli/readline.js.map +1 -0
  23. package/dist/cli/repl-bin.d.ts +9 -0
  24. package/dist/cli/repl-bin.d.ts.map +1 -0
  25. package/dist/cli/repl-bin.js +16 -0
  26. package/dist/cli/repl-bin.js.map +1 -0
  27. package/dist/cli/repl.d.ts +191 -0
  28. package/dist/cli/repl.d.ts.map +1 -0
  29. package/dist/cli/repl.js +1232 -0
  30. package/dist/cli/repl.js.map +1 -0
  31. package/dist/cli/run.d.ts +17 -18
  32. package/dist/cli/run.d.ts.map +1 -1
  33. package/dist/cli/run.js +196 -183
  34. package/dist/cli/run.js.map +1 -1
  35. package/dist/cli/set-backend.d.ts +53 -0
  36. package/dist/cli/set-backend.d.ts.map +1 -0
  37. package/dist/cli/set-backend.js +259 -0
  38. package/dist/cli/set-backend.js.map +1 -0
  39. package/dist/cli/visualize.d.ts +38 -0
  40. package/dist/cli/visualize.d.ts.map +1 -0
  41. package/dist/cli/visualize.js +125 -0
  42. package/dist/cli/visualize.js.map +1 -0
  43. package/dist/compiler.d.ts +5 -52
  44. package/dist/compiler.d.ts.map +1 -1
  45. package/dist/compiler.js +4 -768
  46. package/dist/compiler.js.map +1 -1
  47. package/dist/index.d.ts +29 -2
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +20 -2
  50. package/dist/index.js.map +1 -1
  51. package/dist/judge/index.d.ts +9 -0
  52. package/dist/judge/index.d.ts.map +1 -0
  53. package/dist/judge/index.js +6 -0
  54. package/dist/judge/index.js.map +1 -0
  55. package/dist/judge/jev.d.ts +21 -0
  56. package/dist/judge/jev.d.ts.map +1 -0
  57. package/dist/judge/jev.js +218 -0
  58. package/dist/judge/jev.js.map +1 -0
  59. package/dist/judge/llm.d.ts +23 -0
  60. package/dist/judge/llm.d.ts.map +1 -0
  61. package/dist/judge/llm.js +120 -0
  62. package/dist/judge/llm.js.map +1 -0
  63. package/dist/judge/marshal.d.ts +26 -0
  64. package/dist/judge/marshal.d.ts.map +1 -0
  65. package/dist/judge/marshal.js +266 -0
  66. package/dist/judge/marshal.js.map +1 -0
  67. package/dist/judge/mock.d.ts +28 -0
  68. package/dist/judge/mock.d.ts.map +1 -0
  69. package/dist/judge/mock.js +98 -0
  70. package/dist/judge/mock.js.map +1 -0
  71. package/dist/judge/types.d.ts +95 -0
  72. package/dist/judge/types.d.ts.map +1 -0
  73. package/dist/judge/types.js +23 -0
  74. package/dist/judge/types.js.map +1 -0
  75. package/dist/prolog/bridge.d.ts +5 -1
  76. package/dist/prolog/bridge.d.ts.map +1 -1
  77. package/dist/prolog/bridge.js +29 -1
  78. package/dist/prolog/bridge.js.map +1 -1
  79. package/dist/prolog-src/deepclause_analysis.pl +10 -2
  80. package/dist/prolog-src/deepclause_mi.pl +2522 -118
  81. package/dist/repl/agent.d.ts +72 -0
  82. package/dist/repl/agent.d.ts.map +1 -0
  83. package/dist/repl/agent.js +366 -0
  84. package/dist/repl/agent.js.map +1 -0
  85. package/dist/repl/auth.d.ts +45 -0
  86. package/dist/repl/auth.d.ts.map +1 -0
  87. package/dist/repl/auth.js +116 -0
  88. package/dist/repl/auth.js.map +1 -0
  89. package/dist/repl/backend.d.ts +290 -0
  90. package/dist/repl/backend.d.ts.map +1 -0
  91. package/dist/repl/backend.js +370 -0
  92. package/dist/repl/backend.js.map +1 -0
  93. package/dist/repl/compaction.d.ts +122 -0
  94. package/dist/repl/compaction.d.ts.map +1 -0
  95. package/dist/repl/compaction.js +318 -0
  96. package/dist/repl/compaction.js.map +1 -0
  97. package/dist/repl/export.d.ts +30 -0
  98. package/dist/repl/export.d.ts.map +1 -0
  99. package/dist/repl/export.js +23 -0
  100. package/dist/repl/export.js.map +1 -0
  101. package/dist/repl/frames.d.ts +21 -0
  102. package/dist/repl/frames.d.ts.map +1 -0
  103. package/dist/repl/frames.js +79 -0
  104. package/dist/repl/frames.js.map +1 -0
  105. package/dist/repl/harness-tools.d.ts +74 -0
  106. package/dist/repl/harness-tools.d.ts.map +1 -0
  107. package/dist/repl/harness-tools.js +121 -0
  108. package/dist/repl/harness-tools.js.map +1 -0
  109. package/dist/repl/highlight.d.ts +30 -0
  110. package/dist/repl/highlight.d.ts.map +1 -0
  111. package/dist/repl/highlight.js +285 -0
  112. package/dist/repl/highlight.js.map +1 -0
  113. package/dist/repl/history.d.ts +234 -0
  114. package/dist/repl/history.d.ts.map +1 -0
  115. package/dist/repl/history.js +661 -0
  116. package/dist/repl/history.js.map +1 -0
  117. package/dist/repl/index.d.ts +120 -0
  118. package/dist/repl/index.d.ts.map +1 -0
  119. package/dist/repl/index.js +107 -0
  120. package/dist/repl/index.js.map +1 -0
  121. package/dist/repl/judge.d.ts +51 -0
  122. package/dist/repl/judge.d.ts.map +1 -0
  123. package/dist/repl/judge.js +58 -0
  124. package/dist/repl/judge.js.map +1 -0
  125. package/dist/repl/markdown.d.ts +25 -0
  126. package/dist/repl/markdown.d.ts.map +1 -0
  127. package/dist/repl/markdown.js +103 -0
  128. package/dist/repl/markdown.js.map +1 -0
  129. package/dist/repl/picker.d.ts +34 -0
  130. package/dist/repl/picker.d.ts.map +1 -0
  131. package/dist/repl/picker.js +81 -0
  132. package/dist/repl/picker.js.map +1 -0
  133. package/dist/repl/project-context.d.ts +16 -0
  134. package/dist/repl/project-context.d.ts.map +1 -0
  135. package/dist/repl/project-context.js +23 -0
  136. package/dist/repl/project-context.js.map +1 -0
  137. package/dist/repl/render.d.ts +160 -0
  138. package/dist/repl/render.d.ts.map +1 -0
  139. package/dist/repl/render.js +715 -0
  140. package/dist/repl/render.js.map +1 -0
  141. package/dist/repl/session.d.ts +356 -0
  142. package/dist/repl/session.d.ts.map +1 -0
  143. package/dist/repl/session.js +1275 -0
  144. package/dist/repl/session.js.map +1 -0
  145. package/dist/repl/status.d.ts +100 -0
  146. package/dist/repl/status.d.ts.map +1 -0
  147. package/dist/repl/status.js +218 -0
  148. package/dist/repl/status.js.map +1 -0
  149. package/dist/repl/tools.d.ts +53 -0
  150. package/dist/repl/tools.d.ts.map +1 -0
  151. package/dist/repl/tools.js +307 -0
  152. package/dist/repl/tools.js.map +1 -0
  153. package/dist/repl/toolset.d.ts +32 -0
  154. package/dist/repl/toolset.d.ts.map +1 -0
  155. package/dist/repl/toolset.js +82 -0
  156. package/dist/repl/toolset.js.map +1 -0
  157. package/dist/repl/tui/editor.d.ts +17 -0
  158. package/dist/repl/tui/editor.d.ts.map +1 -0
  159. package/dist/repl/tui/editor.js +39 -0
  160. package/dist/repl/tui/editor.js.map +1 -0
  161. package/dist/repl/tui/status-bar.d.ts +26 -0
  162. package/dist/repl/tui/status-bar.d.ts.map +1 -0
  163. package/dist/repl/tui/status-bar.js +47 -0
  164. package/dist/repl/tui/status-bar.js.map +1 -0
  165. package/dist/repl/tui/theme.d.ts +30 -0
  166. package/dist/repl/tui/theme.d.ts.map +1 -0
  167. package/dist/repl/tui/theme.js +81 -0
  168. package/dist/repl/tui/theme.js.map +1 -0
  169. package/dist/repl/tui/transcript.d.ts +39 -0
  170. package/dist/repl/tui/transcript.d.ts.map +1 -0
  171. package/dist/repl/tui/transcript.js +123 -0
  172. package/dist/repl/tui/transcript.js.map +1 -0
  173. package/dist/runner.d.ts +145 -13
  174. package/dist/runner.d.ts.map +1 -1
  175. package/dist/runner.js +600 -282
  176. package/dist/runner.js.map +1 -1
  177. package/dist/sdk.d.ts.map +1 -1
  178. package/dist/sdk.js +56 -32
  179. package/dist/sdk.js.map +1 -1
  180. package/dist/system/assets/docs/AGENT_PROMPT.md +1373 -0
  181. package/dist/system/assets/docs/DML_AUTHORING.md +195 -0
  182. package/dist/system/assets/docs/DML_REFERENCE.md +393 -72
  183. package/dist/system/assets/docs/HARNESS_EXAMPLES.md +172 -0
  184. package/dist/system/assets/docs/HARNESS_PATTERNS.md +562 -0
  185. package/dist/system/assets/docs/META_PROMPT.md +68 -0
  186. package/dist/system/assets/docs/PROLOG_SNIPPETS.md +117 -0
  187. package/dist/system/assets/index.d.ts +9 -17
  188. package/dist/system/assets/index.d.ts.map +1 -1
  189. package/dist/system/assets/index.js +26 -85
  190. package/dist/system/assets/index.js.map +1 -1
  191. package/dist/system/runtime/agentvm-manager.d.ts +9 -2
  192. package/dist/system/runtime/agentvm-manager.d.ts.map +1 -1
  193. package/dist/system/runtime/agentvm-manager.js +70 -44
  194. package/dist/system/runtime/agentvm-manager.js.map +1 -1
  195. package/dist/system/runtime/media.d.ts +23 -0
  196. package/dist/system/runtime/media.d.ts.map +1 -0
  197. package/dist/system/runtime/media.js +55 -0
  198. package/dist/system/runtime/media.js.map +1 -0
  199. package/dist/system/runtime/shell-limits.d.ts +21 -0
  200. package/dist/system/runtime/shell-limits.d.ts.map +1 -0
  201. package/dist/system/runtime/shell-limits.js +39 -0
  202. package/dist/system/runtime/shell-limits.js.map +1 -0
  203. package/dist/system/runtime/shell-manager.d.ts +8 -3
  204. package/dist/system/runtime/shell-manager.d.ts.map +1 -1
  205. package/dist/system/runtime/shell-manager.js +56 -39
  206. package/dist/system/runtime/shell-manager.js.map +1 -1
  207. package/dist/system/runtime/shell-tool-events.js +5 -0
  208. package/dist/system/runtime/shell-tool-events.js.map +1 -1
  209. package/dist/system/runtime/token-usage.d.ts +2 -0
  210. package/dist/system/runtime/token-usage.d.ts.map +1 -1
  211. package/dist/system/runtime/token-usage.js +7 -0
  212. package/dist/system/runtime/token-usage.js.map +1 -1
  213. package/dist/tools.d.ts +3 -21
  214. package/dist/tools.d.ts.map +1 -1
  215. package/dist/tools.js +14 -168
  216. package/dist/tools.js.map +1 -1
  217. package/dist/types.d.ts +149 -118
  218. package/dist/types.d.ts.map +1 -1
  219. package/dist/validate.d.ts +19 -0
  220. package/dist/validate.d.ts.map +1 -0
  221. package/dist/validate.js +338 -0
  222. package/dist/validate.js.map +1 -0
  223. package/dist/visualize/index.d.ts +52 -0
  224. package/dist/visualize/index.d.ts.map +1 -0
  225. package/dist/visualize/index.js +189 -0
  226. package/dist/visualize/index.js.map +1 -0
  227. package/package.json +17 -15
  228. package/src/prolog-src/deepclause_analysis.pl +10 -2
  229. package/src/prolog-src/deepclause_mi.pl +2522 -118
  230. package/dist/cli/commands.d.ts +0 -42
  231. package/dist/cli/commands.d.ts.map +0 -1
  232. package/dist/cli/commands.js +0 -246
  233. package/dist/cli/commands.js.map +0 -1
  234. package/dist/cli/compile.d.ts +0 -99
  235. package/dist/cli/compile.d.ts.map +0 -1
  236. package/dist/cli/compile.js +0 -363
  237. package/dist/cli/compile.js.map +0 -1
  238. package/dist/cli/default-skills.d.ts +0 -10
  239. package/dist/cli/default-skills.d.ts.map +0 -1
  240. package/dist/cli/default-skills.js +0 -206
  241. package/dist/cli/default-skills.js.map +0 -1
  242. package/dist/cli/interactive.d.ts +0 -2
  243. package/dist/cli/interactive.d.ts.map +0 -1
  244. package/dist/cli/interactive.js +0 -20
  245. package/dist/cli/interactive.js.map +0 -1
  246. package/dist/cli/mcp.d.ts +0 -56
  247. package/dist/cli/mcp.d.ts.map +0 -1
  248. package/dist/cli/mcp.js +0 -138
  249. package/dist/cli/mcp.js.map +0 -1
  250. package/dist/cli/prompt.d.ts +0 -2
  251. package/dist/cli/prompt.d.ts.map +0 -1
  252. package/dist/cli/prompt.js +0 -2
  253. package/dist/cli/prompt.js.map +0 -1
  254. package/dist/cli/search.d.ts +0 -19
  255. package/dist/cli/search.d.ts.map +0 -1
  256. package/dist/cli/search.js +0 -189
  257. package/dist/cli/search.js.map +0 -1
  258. package/dist/cli/tool-args.d.ts +0 -2
  259. package/dist/cli/tool-args.d.ts.map +0 -1
  260. package/dist/cli/tool-args.js +0 -39
  261. package/dist/cli/tool-args.js.map +0 -1
  262. package/dist/cli/tools.d.ts +0 -36
  263. package/dist/cli/tools.d.ts.map +0 -1
  264. package/dist/cli/tools.js +0 -229
  265. package/dist/cli/tools.js.map +0 -1
  266. package/dist/cli/tui/index.d.ts +0 -22
  267. package/dist/cli/tui/index.d.ts.map +0 -1
  268. package/dist/cli/tui/index.js +0 -29
  269. package/dist/cli/tui/index.js.map +0 -1
  270. package/dist/cli/tui.d.ts +0 -167
  271. package/dist/cli/tui.d.ts.map +0 -1
  272. package/dist/cli/tui.js +0 -5228
  273. package/dist/cli/tui.js.map +0 -1
  274. package/dist/compiler_prompt.d.ts +0 -20
  275. package/dist/compiler_prompt.d.ts.map +0 -1
  276. package/dist/compiler_prompt.js +0 -870
  277. package/dist/compiler_prompt.js.map +0 -1
  278. package/dist/deepclause-cli.skill +0 -0
  279. package/dist/prolog-src/deepclause_mi.pl.bak +0 -570
  280. package/dist/system/assets/compactors/default-memory-compactor.dml +0 -6
  281. package/dist/system/assets/docs/CONDUCTOR_PROMPT.md +0 -105
  282. package/dist/system/assets/docs/DML_COMPILER_PROMPT.md +0 -700
  283. package/dist/system/assets/docs/TUI.md +0 -150
  284. package/dist/system/assets/recipes/deepclause-coding-workflow/SKILL.md +0 -100
  285. package/dist/system/assets/skills/conductor.dml +0 -76
  286. package/dist/system/assets/skills/deep-planner.dml +0 -412
  287. package/dist/system/assets/skills/plan.dml +0 -429
  288. package/dist/system/assets/skills/security-planner.dml +0 -337
  289. package/dist/system/assets/skills/skill-creator.dml +0 -187
  290. package/dist/system/runtime/catalog-recipes.d.ts +0 -24
  291. package/dist/system/runtime/catalog-recipes.d.ts.map +0 -1
  292. package/dist/system/runtime/catalog-recipes.js +0 -273
  293. package/dist/system/runtime/catalog-recipes.js.map +0 -1
  294. package/dist/system/runtime/catalog-skills.d.ts +0 -50
  295. package/dist/system/runtime/catalog-skills.d.ts.map +0 -1
  296. package/dist/system/runtime/catalog-skills.js +0 -152
  297. package/dist/system/runtime/catalog-skills.js.map +0 -1
  298. package/dist/system/runtime/conductor.d.ts +0 -105
  299. package/dist/system/runtime/conductor.d.ts.map +0 -1
  300. package/dist/system/runtime/conductor.js +0 -732
  301. package/dist/system/runtime/conductor.js.map +0 -1
  302. package/dist/system/runtime/console-capture.d.ts +0 -9
  303. package/dist/system/runtime/console-capture.d.ts.map +0 -1
  304. package/dist/system/runtime/console-capture.js +0 -67
  305. package/dist/system/runtime/console-capture.js.map +0 -1
  306. package/dist/system/runtime/dml-executor.d.ts +0 -52
  307. package/dist/system/runtime/dml-executor.d.ts.map +0 -1
  308. package/dist/system/runtime/dml-executor.js +0 -203
  309. package/dist/system/runtime/dml-executor.js.map +0 -1
  310. package/dist/system/runtime/runtime-tools.d.ts +0 -31
  311. package/dist/system/runtime/runtime-tools.d.ts.map +0 -1
  312. package/dist/system/runtime/runtime-tools.js +0 -312
  313. package/dist/system/runtime/runtime-tools.js.map +0 -1
  314. package/dist/system/runtime/skill-creator.d.ts +0 -38
  315. package/dist/system/runtime/skill-creator.d.ts.map +0 -1
  316. package/dist/system/runtime/skill-creator.js +0 -763
  317. package/dist/system/runtime/skill-creator.js.map +0 -1
  318. package/src/prolog-src/deepclause_mi.pl.bak +0 -570
@@ -0,0 +1,715 @@
1
+ /**
2
+ * Line-oriented REPL renderer.
3
+ *
4
+ * Three modes:
5
+ * - `rich` (default on a TTY): coloured bindings and labels, no live rewriting.
6
+ * - `plain` (default when stdout is not a TTY): one static line per event, no ANSI.
7
+ * - `json`: one JSON object per event for tooling.
8
+ *
9
+ * This is the M1 surface (solutions, output, answer, errors). Live streaming
10
+ * of model text/tool calls is added in M3.
11
+ */
12
+ import { formatListing } from './highlight.js';
13
+ import { MarkdownStream } from './markdown.js';
14
+ /** Activity events hidden by `--no-events` (output/answer/input/error stay). */
15
+ const ACTIVITY_EVENTS = new Set([
16
+ 'task_activity',
17
+ 'tool_call',
18
+ 'model_call',
19
+ 'usage',
20
+ 'log',
21
+ 'memory_compaction',
22
+ 'meta_result',
23
+ ]);
24
+ function formatArgs(args) {
25
+ if (!args || Object.keys(args).length === 0) {
26
+ return '';
27
+ }
28
+ const parts = Object.entries(args).map(([key, value]) => {
29
+ const text = (typeof value === 'string' ? value : JSON.stringify(value)).replace(/\s+/g, ' ').trim();
30
+ const compact = text.length > 60 ? `${text.slice(0, 57)}…` : text;
31
+ return `${key}=${compact}`;
32
+ });
33
+ return `(${parts.join(', ')})`;
34
+ }
35
+ /** Max captured output lines previewed under a completed tool call. */
36
+ const TOOL_OUTPUT_LINES = 3;
37
+ /** Max physical lines of a single `output` event before it is collapsed. */
38
+ const OUTPUT_LINES = 24;
39
+ /**
40
+ * Bound a potentially huge `output` event (e.g. a `write/1` of a large term).
41
+ * Keeps the head and tail and collapses the middle; `--verbose` shows it whole.
42
+ */
43
+ function boundOutput(text, max = OUTPUT_LINES) {
44
+ const lines = text.replace(/\r\n?/g, '\n').split('\n');
45
+ if (lines.length <= max) {
46
+ return lines;
47
+ }
48
+ const head = Math.ceil((max - 1) / 2);
49
+ const tail = max - 1 - head;
50
+ const omitted = lines.length - head - tail;
51
+ return [
52
+ ...lines.slice(0, head),
53
+ `… (+${omitted} more line${omitted === 1 ? '' : 's'})`,
54
+ ...lines.slice(lines.length - tail),
55
+ ];
56
+ }
57
+ /** Pull a few output lines from a tool result (bash stdout/stderr). */
58
+ function extractToolOutput(result) {
59
+ if (!result || typeof result !== 'object')
60
+ return [];
61
+ const r = result;
62
+ const text = typeof r.stdout === 'string' && r.stdout.trim()
63
+ ? r.stdout
64
+ : (typeof r.stderr === 'string' ? r.stderr : '');
65
+ if (!text.trim())
66
+ return [];
67
+ return text
68
+ .replace(/\r\n?/g, '\n')
69
+ .split('\n')
70
+ .map((line) => line.replace(/\t/g, ' ').trimEnd())
71
+ .filter((line) => line.trim() !== '');
72
+ }
73
+ /** Word-wrap a single line to `width`, hard-breaking tokens that are longer. */
74
+ function wrapLine(text, width) {
75
+ if (width <= 0 || text.length <= width) {
76
+ return [text];
77
+ }
78
+ const out = [];
79
+ let remaining = text;
80
+ while (remaining.length > width) {
81
+ let breakAt = remaining.lastIndexOf(' ', width);
82
+ if (breakAt <= 0) {
83
+ breakAt = width; // no space: hard-break a long token (URL, path, flag)
84
+ }
85
+ out.push(remaining.slice(0, breakAt));
86
+ remaining = remaining.slice(breakAt).replace(/^ +/, '');
87
+ }
88
+ out.push(remaining);
89
+ return out;
90
+ }
91
+ function truncate(text, max) {
92
+ const singleLine = text.replace(/\s+/g, ' ').trim();
93
+ return singleLine.length > max ? `${singleLine.slice(0, max - 1)}…` : singleLine;
94
+ }
95
+ /** Human-readable wall-clock duration: `450ms`, `1.2s`, `2m05s`. */
96
+ export function formatDuration(ms) {
97
+ if (!Number.isFinite(ms) || ms < 0) {
98
+ return '';
99
+ }
100
+ if (ms < 1000) {
101
+ return `${Math.round(ms)}ms`;
102
+ }
103
+ const seconds = ms / 1000;
104
+ if (seconds < 60) {
105
+ return `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;
106
+ }
107
+ const minutes = Math.floor(seconds / 60);
108
+ const remainder = Math.round(seconds % 60);
109
+ return `${minutes}m${String(remainder).padStart(2, '0')}s`;
110
+ }
111
+ const ANSI = {
112
+ reset: '\u001b[0m',
113
+ bold: '\u001b[1m',
114
+ dim: '\u001b[2m',
115
+ cyan: '\u001b[36m',
116
+ green: '\u001b[32m',
117
+ red: '\u001b[31m',
118
+ /** Model reasoning: italic grey, like pi's thinking text. */
119
+ thinking: '\u001b[3;38;5;245m',
120
+ /** DML `output/1` intermediate results. */
121
+ output: '\u001b[38;5;150m',
122
+ /** The final `answer/1`. */
123
+ answer: '\u001b[1;38;5;118m',
124
+ };
125
+ /** True when the terminal advertises 24-bit colour (COLORTERM). */
126
+ const TRUECOLOR = /^(truecolor|24bit)$/i.test(process.env.COLORTERM ?? '');
127
+ /**
128
+ * Tool-call panel: a subtle, near-transparent green tint behind activity
129
+ * (truecolor when available, else a 256-colour fallback). Kept light so it
130
+ * reads as a faint wash over the terminal background rather than a solid fill.
131
+ */
132
+ const TOOL_PANEL_GREEN_BG = TRUECOLOR ? '\u001b[48;2;40;54;44m' : '\u001b[48;5;22m';
133
+ /** Blue tint for `execute` (a goal, not a shell command). */
134
+ const TOOL_PANEL_BLUE_BG = TRUECOLOR ? '\u001b[48;2;34;44;62m' : '\u001b[48;5;24m';
135
+ const TOOL_PANEL_FG = '\u001b[38;5;252m';
136
+ const TOOL_PANEL_ERROR_BG = TRUECOLOR ? '\u001b[48;2;58;36;36m' : '\u001b[48;5;52m';
137
+ const TOOL_PANEL_ERROR_FG = '\u001b[38;5;224m';
138
+ /**
139
+ * Tools whose main argument is a long literal worth showing in full (wrapped
140
+ * and indented under the header), with the panel colour for that tool.
141
+ */
142
+ const TOOL_DETAIL = {
143
+ bash: { arg: 'command', tone: 'green' },
144
+ execute: { arg: 'goal', tone: 'blue' },
145
+ };
146
+ /** A copy of `args` without `key` (for the compact header of a detail tool). */
147
+ function omitKey(args, key) {
148
+ if (!args)
149
+ return undefined;
150
+ const rest = {};
151
+ for (const [k, v] of Object.entries(args)) {
152
+ if (k !== key)
153
+ rest[k] = v;
154
+ }
155
+ return rest;
156
+ }
157
+ /** Spinner frames for the live model-call indicator. */
158
+ const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
159
+ /** Strip ANSI SGR sequences (for measuring visible width). */
160
+ function stripAnsi(text) {
161
+ return text.replace(/\u001b\[[0-9;]*m/g, '');
162
+ }
163
+ /** Format solution bindings as `Name = value` lines (or `true`). */
164
+ export function formatBindings(bindings) {
165
+ if (bindings.length === 0) {
166
+ return 'true';
167
+ }
168
+ return bindings.map((binding) => `${binding.name} = ${binding.value}`).join('\n');
169
+ }
170
+ /** Max characters of reasoning shown in `summary` mode. */
171
+ const REASONING_SUMMARY_CHARS = 600;
172
+ export class ReplRenderer {
173
+ mode;
174
+ color;
175
+ verbose;
176
+ showStream;
177
+ showEvents;
178
+ reasoningMode;
179
+ markdownEnabled;
180
+ markdown = new MarkdownStream();
181
+ pendingTextEvent;
182
+ width;
183
+ sink;
184
+ errorSink;
185
+ eventSink;
186
+ /** Frame key of the currently open streamed-text region, or null. */
187
+ lastStreamFrame = null;
188
+ /** Reasoning region key and characters shown so far (for `summary` mode). */
189
+ reasoningRegion = '';
190
+ reasoningChars = 0;
191
+ reasoningTruncated = false;
192
+ atLineStart = true;
193
+ /** Last detail body printed for a running call (dedupes start/running). */
194
+ lastToolDetail;
195
+ /** Live model-call indicator state. */
196
+ spinnerTimer;
197
+ spinnerFrame = 0;
198
+ spinnerPrefix = '';
199
+ spinnerLabel = '';
200
+ spinnerVisible = false;
201
+ constructor(options = {}) {
202
+ this.mode = options.mode ?? 'plain';
203
+ this.color = (options.color ?? true) && this.mode === 'rich';
204
+ this.verbose = options.verbose ?? false;
205
+ this.showStream = options.stream ?? true;
206
+ this.showEvents = options.events ?? true;
207
+ this.reasoningMode = options.reasoning ?? 'full';
208
+ this.markdownEnabled = options.markdown ?? true;
209
+ this.width = options.width ?? (process.stdout.columns || 80);
210
+ this.sink = options.sink ?? ((text) => process.stdout.write(text));
211
+ this.errorSink = options.errorSink ?? ((text) => process.stderr.write(text));
212
+ this.eventSink = options.onEvent;
213
+ }
214
+ /** Replace the output sinks (used when moving into a TUI transcript). */
215
+ setSink(sink, errorSink) {
216
+ this.sink = sink;
217
+ if (errorSink)
218
+ this.errorSink = errorSink;
219
+ }
220
+ /** Update the rendering width (TUI resize). */
221
+ setWidth(width) {
222
+ if (width > 0)
223
+ this.width = width;
224
+ }
225
+ /** Clean up and syntax-highlight a Prolog listing for display. */
226
+ listing(text) {
227
+ return formatListing(text, { color: this.color && this.mode === 'rich' });
228
+ }
229
+ /**
230
+ * Suppress `input_required` prompt lines. The TUI editor shows the question in
231
+ * its border, so a transcript line would be a duplicate.
232
+ */
233
+ setSuppressInputPrompt(suppress) {
234
+ this.suppressInputPrompt = suppress;
235
+ }
236
+ /** Stop the live indicator (call before the renderer goes out of scope). */
237
+ dispose() {
238
+ this.stopSpinner();
239
+ }
240
+ /** Last rendered block group, used to separate tool/text/output blocks. */
241
+ currentBlock = null;
242
+ toolBlockCounter = 0;
243
+ currentToolBlock = null;
244
+ taskBlockCounter = 0;
245
+ currentTaskBlock = null;
246
+ /** When true, `input_required` events are rendered by the input UI instead. */
247
+ suppressInputPrompt = false;
248
+ /** Write a raw chunk to the normal sink. */
249
+ write(text) {
250
+ this.stopSpinner();
251
+ this.sink(text);
252
+ }
253
+ /** Write a line to the normal sink. */
254
+ writeLine(text = '') {
255
+ this.stopSpinner();
256
+ this.flushStream();
257
+ this.sink(`${text}\n`);
258
+ this.lastStreamFrame = null;
259
+ this.atLineStart = true;
260
+ }
261
+ /** Flush any buffered streamed text (e.g. an unterminated markdown line). */
262
+ flush() {
263
+ this.flushStream();
264
+ }
265
+ writeErrorLine(text) {
266
+ this.stopSpinner();
267
+ this.errorSink(`${text}\n`);
268
+ }
269
+ /**
270
+ * The visual block an event belongs to. Events in the same block are printed
271
+ * contiguously; switching blocks inserts one blank line. Returns null for
272
+ * events that do not participate (solutions, spinners, metadata).
273
+ */
274
+ blockKeyFor(event) {
275
+ switch (event.type) {
276
+ case 'stream':
277
+ return `text:${event.frameKind ?? 'run'}@${event.depth ?? 0}`;
278
+ case 'reasoning':
279
+ return event.content ? `reasoning:${event.frameKind ?? 'run'}@${event.depth ?? 0}` : null;
280
+ case 'tool_call': {
281
+ if (this.currentToolBlock === null || event.toolState === 'starting') {
282
+ this.toolBlockCounter += 1;
283
+ this.currentToolBlock = `tool:${this.toolBlockCounter}`;
284
+ }
285
+ return this.currentToolBlock;
286
+ }
287
+ case 'task_activity': {
288
+ if (this.currentTaskBlock === null || event.taskState === 'started') {
289
+ this.taskBlockCounter += 1;
290
+ this.currentTaskBlock = `task:${this.taskBlockCounter}`;
291
+ }
292
+ return this.currentTaskBlock;
293
+ }
294
+ case 'output':
295
+ case 'answer':
296
+ case 'error':
297
+ return 'output';
298
+ case 'memory_compaction':
299
+ return 'compaction';
300
+ default:
301
+ return null;
302
+ }
303
+ }
304
+ /** Insert a separating blank line when the block changes. */
305
+ beginBlock(key) {
306
+ if (!key)
307
+ return;
308
+ if (this.currentBlock !== null && this.currentBlock !== key) {
309
+ this.writeLine('');
310
+ }
311
+ this.currentBlock = key;
312
+ }
313
+ /** Render one runtime event. */
314
+ render(event) {
315
+ this.eventSink?.(event);
316
+ if (!this.showStream && (event.type === 'stream' || event.type === 'reasoning')) {
317
+ return;
318
+ }
319
+ if (!this.showEvents && ACTIVITY_EVENTS.has(event.type)) {
320
+ return;
321
+ }
322
+ if (this.mode === 'json') {
323
+ this.sink(`${JSON.stringify(event)}\n`);
324
+ return;
325
+ }
326
+ // The top-level prompt task and the meta frame simply echo work that is
327
+ // already shown by the user message and the streamed authoring text, and
328
+ // skipped compactions are noise. Hide them unless verbose; the status
329
+ // tracker still observes every event via `eventSink`.
330
+ if (event.type === 'task_activity'
331
+ && !event.frameKind) {
332
+ return;
333
+ }
334
+ if (event.type === 'memory_compaction' && event.compactionAction === 'skipped' && !this.verbose) {
335
+ return;
336
+ }
337
+ if (event.type !== 'stream' && event.type !== 'reasoning') {
338
+ this.stopSpinner();
339
+ this.flushStream();
340
+ }
341
+ this.beginBlock(this.blockKeyFor(event));
342
+ const prefix = this.frameLabel(event);
343
+ switch (event.type) {
344
+ case 'solution':
345
+ this.solution(event.solution?.bindings ?? [], undefined, prefix,
346
+ // Nested (`execute` child) bindings often carry a whole result term;
347
+ // keep the transcript readable by truncating them.
348
+ event.frameKind === 'nested' ? 160 : undefined);
349
+ break;
350
+ case 'output': {
351
+ // A single event can carry a large multi-line term (`write/1` of an
352
+ // `exec(...)` result). Bound the line count, then fit each line to the
353
+ // terminal width so nothing wraps (which would desync the TUI). Nested
354
+ // activity is truncated to one line each; top-level output wraps.
355
+ const max = event.frameKind === 'nested' ? 6 : OUTPUT_LINES;
356
+ const bounded = this.verbose
357
+ ? (event.content ?? '')
358
+ : boundOutput(event.content ?? '', max).join('\n');
359
+ this.writeFrameText(prefix, bounded, ANSI.output, event.frameKind === 'nested');
360
+ break;
361
+ }
362
+ case 'answer':
363
+ this.writeFrameText(prefix, event.content ?? '', ANSI.answer, false);
364
+ break;
365
+ case 'model_call':
366
+ this.renderModelCall(event, prefix);
367
+ break;
368
+ case 'stream':
369
+ // Visible model text supersedes the working indicator.
370
+ this.stopSpinner();
371
+ this.writeStream(event, event.content ?? '', 'text');
372
+ break;
373
+ case 'reasoning':
374
+ if (event.content) {
375
+ if (this.writeReasoning(event, event.content)) {
376
+ this.stopSpinner();
377
+ }
378
+ else {
379
+ // Reasoning is hidden (`off`) or truncated (`summary`): commit any
380
+ // visible reasoning, then keep a spinner so the user still sees the
381
+ // model working.
382
+ this.flushStream();
383
+ this.ensureThinkingSpinner(prefix);
384
+ }
385
+ }
386
+ break;
387
+ case 'log':
388
+ if (this.verbose) {
389
+ this.writeLine(this.paint(`${prefix}[log] ${event.content ?? ''}`, ANSI.dim));
390
+ }
391
+ break;
392
+ case 'task_activity':
393
+ this.renderTaskActivity(event, prefix);
394
+ break;
395
+ case 'tool_call':
396
+ this.renderToolCall(event, prefix);
397
+ break;
398
+ case 'input_required':
399
+ if (!this.suppressInputPrompt) {
400
+ this.writeLine(this.paint(`${prefix}? ${event.prompt ?? ''}`, ANSI.bold));
401
+ }
402
+ break;
403
+ case 'error':
404
+ this.writeErrorLine(this.paint(`${prefix}ERROR: ${event.content ?? ''}`, ANSI.red));
405
+ break;
406
+ case 'usage':
407
+ if (this.verbose && event.usage) {
408
+ this.writeLine(this.paint(`${prefix}[usage] in=${event.usage.inputTokens} out=${event.usage.outputTokens} total=${event.usage.totalTokens}`, ANSI.dim));
409
+ }
410
+ break;
411
+ case 'memory_compaction':
412
+ this.writeLine(this.paint(`${prefix}${event.content ?? 'memory compacted'}`, ANSI.dim));
413
+ break;
414
+ case 'finished':
415
+ case 'meta_result':
416
+ default:
417
+ break;
418
+ }
419
+ }
420
+ renderTaskActivity(event, prefix) {
421
+ const state = event.taskState ?? 'started';
422
+ if (state === 'started') {
423
+ this.writeLine(this.paint(`${prefix}▸ Task ${this.describeTask(event)}`, ANSI.dim));
424
+ return;
425
+ }
426
+ const ok = state === 'completed';
427
+ this.writeLine(this.paint(`${prefix}${ok ? '✔' : '✖'} Task ${ok ? 'done' : 'failed'}`, ok ? ANSI.dim : ANSI.red));
428
+ }
429
+ describeTask(event) {
430
+ const description = (event.taskDescription ?? '').replace(/\s+/g, ' ').trim();
431
+ if (!description)
432
+ return 'task';
433
+ return description.length > 72 ? `${description.slice(0, 69)}…` : description;
434
+ }
435
+ renderToolCall(event, prefix) {
436
+ const name = event.toolName ?? 'tool';
437
+ const state = event.toolState;
438
+ // Some tools have a long literal argument (a shell script, a DML goal) that
439
+ // is shown in full, wrapped and indented, rather than truncated inline.
440
+ const spec = TOOL_DETAIL[name];
441
+ const rawDetail = spec ? event.toolArgs?.[spec.arg] : undefined;
442
+ const detail = typeof rawDetail === 'string' ? rawDetail : undefined;
443
+ const tone = spec?.tone ?? 'green';
444
+ const args = detail === undefined ? formatArgs(event.toolArgs) : '';
445
+ if (state === 'completed') {
446
+ const duration = typeof event.toolDurationMs === 'number' ? ` · ${formatDuration(event.toolDurationMs)}` : '';
447
+ const summary = event.toolSummary ? ` — ${truncate(event.toolSummary, 80)}` : '';
448
+ this.lastToolDetail = undefined;
449
+ this.writeLine(this.toolPanel(`${prefix} ✔ ${name}${args}${duration}${summary}`, tone));
450
+ // A short tail of the captured output (bash). Full output is --verbose.
451
+ if (!this.verbose) {
452
+ for (const line of this.toolOutputLines(prefix, tone, event.toolResult)) {
453
+ this.writeLine(line);
454
+ }
455
+ }
456
+ return;
457
+ }
458
+ if (state === 'failed') {
459
+ const duration = typeof event.toolDurationMs === 'number' ? ` · ${formatDuration(event.toolDurationMs)}` : '';
460
+ const reason = event.toolError ?? event.toolSummary ?? 'failed';
461
+ this.lastToolDetail = undefined;
462
+ this.writeLine(this.toolPanel(`${prefix} ✖ ${name}${args}${duration} — ${truncate(reason, 120)}`, 'error'));
463
+ return;
464
+ }
465
+ if (detail !== undefined) {
466
+ // A call emits `starting` then `running`; print the body once.
467
+ if (detail !== this.lastToolDetail) {
468
+ this.lastToolDetail = detail;
469
+ const others = formatArgs(omitKey(event.toolArgs, spec.arg));
470
+ this.writeLine(this.toolPanel(`${prefix} ⚙ ${name}${others}`, tone));
471
+ // Print the body on panel lines, word-wrapped to the available width
472
+ // and indented under the header so wrapped bodies stay aligned.
473
+ const indent = ' ';
474
+ const avail = Math.max(20, this.width - stripAnsi(prefix).length - indent.length);
475
+ for (const rawLine of detail.replace(/\r\n?/g, '\n').split('\n')) {
476
+ for (const segment of wrapLine(rawLine, avail)) {
477
+ this.writeLine(this.toolPanel(`${prefix}${indent}${segment}`, tone));
478
+ }
479
+ }
480
+ }
481
+ return;
482
+ }
483
+ this.writeLine(this.toolPanel(`${prefix} ⚙ ${name}${args}`, tone));
484
+ }
485
+ /** Up to TOOL_OUTPUT_LINES captured output lines, as tinted panel lines. */
486
+ toolOutputLines(prefix, tone, result) {
487
+ const lines = extractToolOutput(result);
488
+ if (lines.length === 0) {
489
+ return [];
490
+ }
491
+ const indent = ' ';
492
+ const avail = Math.max(20, this.width - stripAnsi(prefix).length - indent.length);
493
+ const shown = lines.slice(-TOOL_OUTPUT_LINES);
494
+ const omitted = lines.length - shown.length;
495
+ const out = [];
496
+ if (omitted > 0) {
497
+ out.push(this.toolPanel(`${prefix}${indent}… (+${omitted} more line${omitted === 1 ? '' : 's'})`, tone));
498
+ }
499
+ for (const line of shown) {
500
+ out.push(this.toolPanel(`${prefix}${indent}${line.length > avail ? `${line.slice(0, avail - 1)}…` : line}`, tone));
501
+ }
502
+ return out;
503
+ }
504
+ /** Show/hide the live model-call indicator. */
505
+ renderModelCall(event, prefix) {
506
+ if (event.modelCallState === 'started') {
507
+ this.startSpinner(prefix, 'working…');
508
+ return;
509
+ }
510
+ this.stopSpinner();
511
+ }
512
+ /** Start a `\r`-based spinner on the current line (rich TTY only). */
513
+ startSpinner(prefix, label) {
514
+ if (!this.color || this.mode !== 'rich') {
515
+ return;
516
+ }
517
+ this.stopSpinner();
518
+ this.spinnerPrefix = prefix;
519
+ this.spinnerLabel = label;
520
+ this.spinnerFrame = 0;
521
+ this.paintSpinner();
522
+ this.spinnerTimer = setInterval(() => {
523
+ this.spinnerFrame += 1;
524
+ this.paintSpinner();
525
+ }, 90);
526
+ }
527
+ paintSpinner() {
528
+ const frame = SPINNER_FRAMES[this.spinnerFrame % SPINNER_FRAMES.length];
529
+ this.sink(`\r\u001b[K${this.spinnerPrefix}${this.paint(`${frame} ${this.spinnerLabel}`, ANSI.dim)}`);
530
+ this.spinnerVisible = true;
531
+ }
532
+ /** Clear the spinner line and stop its timer (no-op when inactive). */
533
+ stopSpinner() {
534
+ if (this.spinnerTimer) {
535
+ clearInterval(this.spinnerTimer);
536
+ this.spinnerTimer = undefined;
537
+ }
538
+ if (this.spinnerVisible) {
539
+ this.sink('\r\u001b[K');
540
+ this.spinnerVisible = false;
541
+ }
542
+ }
543
+ /**
544
+ * A full-width, tinted line for tool activity (pi-style panel). Falls back to
545
+ * the bare text when colour is disabled. Existing ANSI in `text` is preserved
546
+ * and the visible width (ignoring SGR) is padded out to the terminal width.
547
+ */
548
+ toolPanel(text, tone) {
549
+ if (!this.color) {
550
+ return text;
551
+ }
552
+ const bg = tone === 'error'
553
+ ? TOOL_PANEL_ERROR_BG
554
+ : tone === 'blue'
555
+ ? TOOL_PANEL_BLUE_BG
556
+ : TOOL_PANEL_GREEN_BG;
557
+ const fg = tone === 'error' ? TOOL_PANEL_ERROR_FG : TOOL_PANEL_FG;
558
+ const width = this.width - stripAnsi(text).length;
559
+ const pad = width > 0 ? ' '.repeat(width) : '';
560
+ return `${bg}${fg}${text}${pad}${ANSI.reset}`;
561
+ }
562
+ /**
563
+ * Write `text` as frame-prefixed lines that each fit the terminal width:
564
+ * wrap by default, or truncate each physical line (nested activity).
565
+ */
566
+ writeFrameText(prefix, text, code, truncate) {
567
+ const avail = Math.max(20, this.width - stripAnsi(prefix).length);
568
+ const normalized = text.replace(/\r\n?/g, '\n').replace(/\t/g, ' ');
569
+ for (const rawLine of normalized.split('\n')) {
570
+ const segments = truncate
571
+ ? [rawLine.length > avail ? `${rawLine.slice(0, avail - 1)}…` : rawLine]
572
+ : wrapLine(rawLine, avail);
573
+ for (const segment of segments) {
574
+ this.writeLine(`${prefix}${this.paint(segment, code)}`);
575
+ }
576
+ }
577
+ }
578
+ /**
579
+ * Stream reasoning according to `showReasoning`: `off` hides it, `summary`
580
+ * shows a bounded preview, `full` shows everything (all dimmed).
581
+ */
582
+ /**
583
+ * Stream reasoning according to `showReasoning`: `off` hides it, `summary`
584
+ * shows a bounded preview, `full` shows everything (all dimmed). Returns
585
+ * whether anything was made visible (so the caller can keep a spinner when it
586
+ * is hidden).
587
+ */
588
+ writeReasoning(event, chunk) {
589
+ if (this.reasoningMode === 'off') {
590
+ return false;
591
+ }
592
+ const region = `${event.frameKind ?? 'plain'}@${event.depth ?? 0}`;
593
+ if (region !== this.reasoningRegion) {
594
+ this.reasoningRegion = region;
595
+ this.reasoningChars = 0;
596
+ this.reasoningTruncated = false;
597
+ }
598
+ const limit = this.reasoningMode === 'summary' ? REASONING_SUMMARY_CHARS : Infinity;
599
+ if (this.reasoningChars >= limit) {
600
+ this.announceReasoningTruncated(event);
601
+ return false;
602
+ }
603
+ const remaining = limit - this.reasoningChars;
604
+ const text = chunk.length > remaining ? chunk.slice(0, remaining) : chunk;
605
+ this.reasoningChars += text.length;
606
+ this.writeStream(event, this.paint(text, ANSI.thinking), 'reasoning');
607
+ if (this.reasoningChars >= limit) {
608
+ this.announceReasoningTruncated(event);
609
+ }
610
+ return text.length > 0;
611
+ }
612
+ /** Keep a `thinking…` spinner running (restart once if the label differs). */
613
+ ensureThinkingSpinner(prefix) {
614
+ if (this.spinnerTimer && this.spinnerLabel === 'thinking…') {
615
+ return;
616
+ }
617
+ this.startSpinner(prefix, 'thinking…');
618
+ }
619
+ announceReasoningTruncated(event) {
620
+ if (this.reasoningTruncated) {
621
+ return;
622
+ }
623
+ this.reasoningTruncated = true;
624
+ this.writeStream(event, this.paint(' … (reasoning truncated; set showReasoning=full to see it all)', ANSI.dim), 'reasoning');
625
+ }
626
+ /** Write a streamed chunk, prefixing it with its frame label on first use. */
627
+ writeStream(event, chunk, kind) {
628
+ if (kind === 'text' && this.markdownEnabled) {
629
+ this.pendingTextEvent = event;
630
+ for (const line of this.markdown.push(chunk)) {
631
+ this.writeStreamChunk(event, `${line}\n`, kind);
632
+ }
633
+ return;
634
+ }
635
+ this.writeStreamChunk(event, chunk, kind);
636
+ }
637
+ writeStreamChunk(event, chunk, kind) {
638
+ // Include the kind so switching reasoning <-> text starts a fresh region.
639
+ const key = `${event.frameKind ?? 'plain'}@${event.depth ?? 0}:${kind}`;
640
+ if (key !== this.lastStreamFrame) {
641
+ if (!this.atLineStart) {
642
+ this.sink('\n');
643
+ }
644
+ const prefix = this.frameLabel(event);
645
+ if (prefix)
646
+ this.sink(prefix);
647
+ this.lastStreamFrame = key;
648
+ this.atLineStart = false;
649
+ }
650
+ this.sink(chunk);
651
+ this.atLineStart = chunk.endsWith('\n');
652
+ }
653
+ /** Close an open streamed-text region before writing a line. */
654
+ flushStream() {
655
+ if (this.markdownEnabled && this.pendingTextEvent) {
656
+ const event = this.pendingTextEvent;
657
+ this.pendingTextEvent = undefined;
658
+ for (const line of this.markdown.flush()) {
659
+ this.writeStreamChunk(event, `${line}\n`, 'text');
660
+ }
661
+ }
662
+ if (this.lastStreamFrame !== null) {
663
+ if (!this.atLineStart)
664
+ this.sink('\n');
665
+ this.lastStreamFrame = null;
666
+ this.atLineStart = true;
667
+ }
668
+ }
669
+ /** `run │ ` for an object frame; nested (`execute` child) events use indentation + `│`. */
670
+ frameLabel(event) {
671
+ // `--no-events` also drops the frame label, so output/answer lines are plain.
672
+ if (!this.showEvents || !event.frameKind)
673
+ return '';
674
+ const depth = event.depth ?? 0;
675
+ // Nested (`execute` child) activity reads as belonging to the parent tool
676
+ // call through indentation plus a hanging `│`, with no `nested` label.
677
+ if (event.frameKind === 'nested') {
678
+ return `${' '.repeat(depth + 1)}│ `;
679
+ }
680
+ const label = event.frameKind === 'object' ? 'run' : event.frameKind;
681
+ const indent = ' '.repeat(depth);
682
+ return `${indent}${label} │ `;
683
+ }
684
+ /**
685
+ * Render one solution's bindings. `terminator` is appended after the last
686
+ * binding line ('.' for the final answer, ' ' while more may follow).
687
+ */
688
+ solution(bindings, terminator, prefix = '', maxValue) {
689
+ if (this.mode === 'json') {
690
+ this.sink(`${JSON.stringify({ type: 'solution', solution: { bindings, deterministic: false } })}\n`);
691
+ return;
692
+ }
693
+ this.flushStream();
694
+ if (bindings.length === 0) {
695
+ this.writeLine(`${prefix}true${terminator ?? '.'}`);
696
+ return;
697
+ }
698
+ const lines = bindings.map((binding) => {
699
+ const value = maxValue !== undefined ? truncate(binding.value, maxValue) : binding.value;
700
+ const name = this.paint(binding.name, ANSI.cyan);
701
+ return `${prefix}${name} = ${value}`;
702
+ });
703
+ const last = lines.length - 1;
704
+ lines.forEach((line, index) => {
705
+ this.writeLine(index === last ? `${line}${terminator ?? '.'}` : line);
706
+ });
707
+ }
708
+ paint(text, code) {
709
+ if (!this.color) {
710
+ return text;
711
+ }
712
+ return `${code}${text}${ANSI.reset}`;
713
+ }
714
+ }
715
+ //# sourceMappingURL=render.js.map