@gaunt-sloth/core 2.0.0-alpha.3 → 2.0.0-alpha.31

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 (271) hide show
  1. package/README.md +52 -20
  2. package/dist/config/colour.d.ts +38 -0
  3. package/dist/config/colour.js +36 -0
  4. package/dist/config/colour.js.map +1 -0
  5. package/dist/config/defaults.d.ts +84 -0
  6. package/dist/config/defaults.js +99 -0
  7. package/dist/config/defaults.js.map +1 -0
  8. package/dist/config/filesystem-tools.d.ts +41 -0
  9. package/dist/config/filesystem-tools.js +56 -0
  10. package/dist/config/filesystem-tools.js.map +1 -0
  11. package/dist/config/jsonc.d.ts +12 -0
  12. package/dist/config/jsonc.js +41 -0
  13. package/dist/config/jsonc.js.map +1 -0
  14. package/dist/config/loader.d.ts +246 -0
  15. package/dist/config/loader.js +1321 -0
  16. package/dist/config/loader.js.map +1 -0
  17. package/dist/config/mouse.d.ts +50 -0
  18. package/dist/config/mouse.js +44 -0
  19. package/dist/config/mouse.js.map +1 -0
  20. package/dist/config/profiles.d.ts +68 -0
  21. package/dist/config/profiles.js +93 -0
  22. package/dist/config/profiles.js.map +1 -0
  23. package/dist/config/providerKeys.d.ts +69 -0
  24. package/dist/config/providerKeys.js +69 -0
  25. package/dist/config/providerKeys.js.map +1 -0
  26. package/dist/config/schema.d.ts +2999 -0
  27. package/dist/config/schema.js +1519 -0
  28. package/dist/config/schema.js.map +1 -0
  29. package/dist/config/shell-policy.d.ts +896 -0
  30. package/dist/config/shell-policy.js +750 -0
  31. package/dist/config/shell-policy.js.map +1 -0
  32. package/dist/config/tool-descriptions.d.ts +211 -0
  33. package/dist/config/tool-descriptions.js +272 -0
  34. package/dist/config/tool-descriptions.js.map +1 -0
  35. package/dist/config/types.d.ts +760 -0
  36. package/dist/config/types.js +13 -0
  37. package/dist/config/types.js.map +1 -0
  38. package/dist/config.d.ts +34 -827
  39. package/dist/config.js +25 -657
  40. package/dist/config.js.map +1 -1
  41. package/dist/constants.d.ts +46 -0
  42. package/dist/constants.js +46 -0
  43. package/dist/constants.js.map +1 -1
  44. package/dist/core/GthAbstractAgent.d.ts +135 -2
  45. package/dist/core/GthAbstractAgent.js +496 -30
  46. package/dist/core/GthAbstractAgent.js.map +1 -1
  47. package/dist/core/GthAgentRunner.d.ts +568 -54
  48. package/dist/core/GthAgentRunner.js +1537 -136
  49. package/dist/core/GthAgentRunner.js.map +1 -1
  50. package/dist/core/GthLangChainAgent.d.ts +115 -0
  51. package/dist/core/GthLangChainAgent.js +644 -17
  52. package/dist/core/GthLangChainAgent.js.map +1 -1
  53. package/dist/core/approvals/annotations.d.ts +122 -0
  54. package/dist/core/approvals/annotations.js +137 -0
  55. package/dist/core/approvals/annotations.js.map +1 -0
  56. package/dist/core/approvals/grants.d.ts +216 -0
  57. package/dist/core/approvals/grants.js +469 -0
  58. package/dist/core/approvals/grants.js.map +1 -0
  59. package/dist/core/approvals/matcher.d.ts +202 -0
  60. package/dist/core/approvals/matcher.js +267 -0
  61. package/dist/core/approvals/matcher.js.map +1 -0
  62. package/dist/core/approvals/mcpSubjects.d.ts +40 -0
  63. package/dist/core/approvals/mcpSubjects.js +99 -0
  64. package/dist/core/approvals/mcpSubjects.js.map +1 -0
  65. package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
  66. package/dist/core/approvals/toolAnnotationSources.js +277 -0
  67. package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
  68. package/dist/core/approvals/toolHost.d.ts +46 -0
  69. package/dist/core/approvals/toolHost.js +108 -0
  70. package/dist/core/approvals/toolHost.js.map +1 -0
  71. package/dist/core/debugCapture.d.ts +75 -0
  72. package/dist/core/debugCapture.js +101 -0
  73. package/dist/core/debugCapture.js.map +1 -0
  74. package/dist/core/gthLeanAgentFactory.d.ts +9 -0
  75. package/dist/core/gthLeanAgentFactory.js +10 -0
  76. package/dist/core/gthLeanAgentFactory.js.map +1 -0
  77. package/dist/core/launchBanner.d.ts +120 -0
  78. package/dist/core/launchBanner.js +418 -0
  79. package/dist/core/launchBanner.js.map +1 -0
  80. package/dist/core/plainToolIndication.d.ts +15 -0
  81. package/dist/core/plainToolIndication.js +174 -0
  82. package/dist/core/plainToolIndication.js.map +1 -0
  83. package/dist/core/reasoningBlocks.d.ts +60 -0
  84. package/dist/core/reasoningBlocks.js +98 -0
  85. package/dist/core/reasoningBlocks.js.map +1 -0
  86. package/dist/core/refusal.d.ts +53 -0
  87. package/dist/core/refusal.js +133 -0
  88. package/dist/core/refusal.js.map +1 -0
  89. package/dist/core/runStats.d.ts +52 -0
  90. package/dist/core/runStats.js +118 -0
  91. package/dist/core/runStats.js.map +1 -0
  92. package/dist/core/shell/ShellCommandFailedError.d.ts +54 -0
  93. package/dist/core/shell/ShellCommandFailedError.js +68 -0
  94. package/dist/core/shell/ShellCommandFailedError.js.map +1 -0
  95. package/dist/core/shell/abstention.d.ts +88 -0
  96. package/dist/core/shell/abstention.js +184 -0
  97. package/dist/core/shell/abstention.js.map +1 -0
  98. package/dist/core/shell/approvalCapture.d.ts +271 -0
  99. package/dist/core/shell/approvalCapture.js +108 -0
  100. package/dist/core/shell/approvalCapture.js.map +1 -0
  101. package/dist/core/shell/approvalStop.d.ts +84 -0
  102. package/dist/core/shell/approvalStop.js +119 -0
  103. package/dist/core/shell/approvalStop.js.map +1 -0
  104. package/dist/core/shell/arity.d.ts +6 -0
  105. package/dist/core/shell/arity.js +20 -6
  106. package/dist/core/shell/arity.js.map +1 -1
  107. package/dist/core/shell/denylist.d.ts +11 -0
  108. package/dist/core/shell/denylist.js +37 -0
  109. package/dist/core/shell/denylist.js.map +1 -0
  110. package/dist/core/shell/escalationSeverity.d.ts +141 -0
  111. package/dist/core/shell/escalationSeverity.js +89 -0
  112. package/dist/core/shell/escalationSeverity.js.map +1 -0
  113. package/dist/core/shell/framing.d.ts +190 -0
  114. package/dist/core/shell/framing.js +633 -0
  115. package/dist/core/shell/framing.js.map +1 -0
  116. package/dist/core/shell/hardline.d.ts +81 -0
  117. package/dist/core/shell/hardline.js +607 -0
  118. package/dist/core/shell/hardline.js.map +1 -0
  119. package/dist/core/shell/negotiation.d.ts +249 -0
  120. package/dist/core/shell/negotiation.js +355 -0
  121. package/dist/core/shell/negotiation.js.map +1 -0
  122. package/dist/core/shell/normalize.d.ts +44 -4
  123. package/dist/core/shell/normalize.js +61 -7
  124. package/dist/core/shell/normalize.js.map +1 -1
  125. package/dist/core/shell/openWorld.d.ts +263 -0
  126. package/dist/core/shell/openWorld.js +1188 -0
  127. package/dist/core/shell/openWorld.js.map +1 -0
  128. package/dist/core/shell/rater.d.ts +935 -0
  129. package/dist/core/shell/rater.js +1473 -0
  130. package/dist/core/shell/rater.js.map +1 -0
  131. package/dist/core/shell/raterModel.d.ts +41 -0
  132. package/dist/core/shell/raterModel.js +51 -0
  133. package/dist/core/shell/raterModel.js.map +1 -0
  134. package/dist/core/shell/rejection.d.ts +69 -0
  135. package/dist/core/shell/rejection.js +38 -0
  136. package/dist/core/shell/rejection.js.map +1 -0
  137. package/dist/core/toolCallRepair/grammar.d.ts +41 -0
  138. package/dist/core/toolCallRepair/grammar.js +116 -0
  139. package/dist/core/toolCallRepair/grammar.js.map +1 -0
  140. package/dist/core/toolCallRepair/index.d.ts +2 -0
  141. package/dist/core/toolCallRepair/index.js +7 -0
  142. package/dist/core/toolCallRepair/index.js.map +1 -0
  143. package/dist/core/toolCallRepair/payload.d.ts +36 -0
  144. package/dist/core/toolCallRepair/payload.js +341 -0
  145. package/dist/core/toolCallRepair/payload.js.map +1 -0
  146. package/dist/core/toolCallRepair/promote.d.ts +45 -0
  147. package/dist/core/toolCallRepair/promote.js +90 -0
  148. package/dist/core/toolCallRepair/promote.js.map +1 -0
  149. package/dist/core/toolDisplay.d.ts +123 -0
  150. package/dist/core/toolDisplay.js +451 -0
  151. package/dist/core/toolDisplay.js.map +1 -0
  152. package/dist/core/toolOutputChannel.d.ts +95 -0
  153. package/dist/core/toolOutputChannel.js +165 -0
  154. package/dist/core/toolOutputChannel.js.map +1 -0
  155. package/dist/core/types.d.ts +294 -12
  156. package/dist/core/types.js.map +1 -1
  157. package/dist/history/historyFormat.d.ts +28 -0
  158. package/dist/history/historyFormat.js +127 -0
  159. package/dist/history/historyFormat.js.map +1 -0
  160. package/dist/history/historyStore.d.ts +198 -0
  161. package/dist/history/historyStore.js +482 -0
  162. package/dist/history/historyStore.js.map +1 -0
  163. package/dist/history/recordSession.d.ts +37 -0
  164. package/dist/history/recordSession.js +56 -0
  165. package/dist/history/recordSession.js.map +1 -0
  166. package/dist/index.d.ts +4 -0
  167. package/dist/index.js +4 -0
  168. package/dist/index.js.map +1 -1
  169. package/dist/providers/anthropic.d.ts +1 -1
  170. package/dist/providers/anthropic.js +5 -10
  171. package/dist/providers/anthropic.js.map +1 -1
  172. package/dist/providers/deepseek.d.ts +1 -1
  173. package/dist/providers/deepseek.js +5 -10
  174. package/dist/providers/deepseek.js.map +1 -1
  175. package/dist/providers/geminiSchemaSanitizer.d.ts +55 -0
  176. package/dist/providers/geminiSchemaSanitizer.js +347 -0
  177. package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
  178. package/dist/providers/geminiThinking.d.ts +52 -0
  179. package/dist/providers/geminiThinking.js +72 -0
  180. package/dist/providers/geminiThinking.js.map +1 -0
  181. package/dist/providers/google-genai.d.ts +1 -1
  182. package/dist/providers/google-genai.js +12 -11
  183. package/dist/providers/google-genai.js.map +1 -1
  184. package/dist/providers/groq.d.ts +1 -1
  185. package/dist/providers/groq.js +5 -10
  186. package/dist/providers/groq.js.map +1 -1
  187. package/dist/providers/huggingface.d.ts +25 -0
  188. package/dist/providers/huggingface.js +69 -0
  189. package/dist/providers/huggingface.js.map +1 -0
  190. package/dist/providers/modelCatalog.d.ts +109 -0
  191. package/dist/providers/modelCatalog.js +245 -0
  192. package/dist/providers/modelCatalog.js.map +1 -0
  193. package/dist/providers/modelDiscovery.d.ts +153 -6
  194. package/dist/providers/modelDiscovery.js +295 -41
  195. package/dist/providers/modelDiscovery.js.map +1 -1
  196. package/dist/providers/ollama.d.ts +19 -5
  197. package/dist/providers/ollama.js +60 -52
  198. package/dist/providers/ollama.js.map +1 -1
  199. package/dist/providers/openai.d.ts +1 -1
  200. package/dist/providers/openai.js +39 -10
  201. package/dist/providers/openai.js.map +1 -1
  202. package/dist/providers/openrouter.d.ts +4 -5
  203. package/dist/providers/openrouter.js +20 -35
  204. package/dist/providers/openrouter.js.map +1 -1
  205. package/dist/providers/vertexai.d.ts +1 -1
  206. package/dist/providers/vertexai.js +12 -11
  207. package/dist/providers/vertexai.js.map +1 -1
  208. package/dist/providers/xai.d.ts +1 -1
  209. package/dist/providers/xai.js +5 -10
  210. package/dist/providers/xai.js.map +1 -1
  211. package/dist/runtime/askStructured.d.ts +105 -0
  212. package/dist/runtime/askStructured.js +120 -0
  213. package/dist/runtime/askStructured.js.map +1 -0
  214. package/dist/runtime/conversation.d.ts +60 -0
  215. package/dist/runtime/conversation.js +151 -0
  216. package/dist/runtime/conversation.js.map +1 -0
  217. package/dist/runtime/singleShot.d.ts +25 -5
  218. package/dist/runtime/singleShot.js +95 -36
  219. package/dist/runtime/singleShot.js.map +1 -1
  220. package/dist/runtime/structuredOutput.d.ts +104 -0
  221. package/dist/runtime/structuredOutput.js +393 -0
  222. package/dist/runtime/structuredOutput.js.map +1 -0
  223. package/dist/utils/ProgressIndicator.d.ts +21 -0
  224. package/dist/utils/ProgressIndicator.js +30 -3
  225. package/dist/utils/ProgressIndicator.js.map +1 -1
  226. package/dist/utils/aiignoreUtils.js.map +1 -1
  227. package/dist/utils/binaryOutputUtils.js.map +1 -1
  228. package/dist/utils/consoleUtils.d.ts +52 -0
  229. package/dist/utils/consoleUtils.js +72 -2
  230. package/dist/utils/consoleUtils.js.map +1 -1
  231. package/dist/utils/crashHandler.d.ts +87 -0
  232. package/dist/utils/crashHandler.js +128 -0
  233. package/dist/utils/crashHandler.js.map +1 -0
  234. package/dist/utils/debugDump.d.ts +134 -0
  235. package/dist/utils/debugDump.js +381 -0
  236. package/dist/utils/debugDump.js.map +1 -0
  237. package/dist/utils/debugUtils.d.ts +13 -4
  238. package/dist/utils/debugUtils.js +36 -13
  239. package/dist/utils/debugUtils.js.map +1 -1
  240. package/dist/utils/displayWidth.d.ts +53 -0
  241. package/dist/utils/displayWidth.js +195 -0
  242. package/dist/utils/displayWidth.js.map +1 -0
  243. package/dist/utils/fileUtils.d.ts +24 -2
  244. package/dist/utils/fileUtils.js +54 -12
  245. package/dist/utils/fileUtils.js.map +1 -1
  246. package/dist/utils/llmUtils.d.ts +39 -8
  247. package/dist/utils/llmUtils.js +76 -8
  248. package/dist/utils/llmUtils.js.map +1 -1
  249. package/dist/utils/redactSecrets.d.ts +63 -0
  250. package/dist/utils/redactSecrets.js +286 -0
  251. package/dist/utils/redactSecrets.js.map +1 -0
  252. package/dist/utils/systemPromptNotes.d.ts +225 -0
  253. package/dist/utils/systemPromptNotes.js +340 -0
  254. package/dist/utils/systemPromptNotes.js.map +1 -0
  255. package/dist/utils/systemUtils.d.ts +49 -1
  256. package/dist/utils/systemUtils.js +67 -3
  257. package/dist/utils/systemUtils.js.map +1 -1
  258. package/dist/utils/toolMatching.d.ts +30 -0
  259. package/dist/utils/toolMatching.js +44 -0
  260. package/dist/utils/toolMatching.js.map +1 -0
  261. package/dist/utils/untrustedText.d.ts +66 -0
  262. package/dist/utils/untrustedText.js +80 -0
  263. package/dist/utils/untrustedText.js.map +1 -0
  264. package/package.json +22 -6
  265. package/schema/gsloth-config.schema.json +3134 -0
  266. package/dist/core/shell/allowlist.d.ts +0 -75
  267. package/dist/core/shell/allowlist.js +0 -187
  268. package/dist/core/shell/allowlist.js.map +0 -1
  269. package/dist/core/shell/judge.d.ts +0 -161
  270. package/dist/core/shell/judge.js +0 -261
  271. package/dist/core/shell/judge.js.map +0 -1
@@ -0,0 +1,165 @@
1
+ import { displayError, displayInfo, displayWarning } from '#src/utils/consoleUtils.js';
2
+ import { stdout } from '#src/utils/systemUtils.js';
3
+ let activeListener = null;
4
+ /**
5
+ * TUI-C31 (d): true while a surface owns the terminal frame (the mounted Ink TUI) but no
6
+ * per-turn subscriber is attached — i.e. BETWEEN turns. In that window a straggler child that
7
+ * outlived the kill grace must NOT reach raw stdout (it would corrupt Ink's managed frame), so
8
+ * the default sink drops it instead. Off by default, so every headless surface is unaffected.
9
+ */
10
+ let suppressed = false;
11
+ /**
12
+ * Subscribe to live tool output, replacing the default stdout/`displayInfo` sink for as long
13
+ * as the subscription is active. Returns an unsubscribe function. Last subscriber wins; the
14
+ * returned unsubscribe only clears its OWN registration (a stale unsubscribe can never detach
15
+ * a newer subscriber).
16
+ */
17
+ export function subscribeToolOutput(listener) {
18
+ activeListener = listener;
19
+ return () => {
20
+ if (activeListener === listener) {
21
+ activeListener = null;
22
+ }
23
+ };
24
+ }
25
+ /**
26
+ * TUI-C31 (d): mark whether a terminal-owning surface (the Ink TUI) is mounted. While mounted,
27
+ * the default sink SUPPRESSES output that has no per-turn subscriber (a post-turn straggler)
28
+ * rather than writing it raw over the managed frame. The TUI session sets this `true` around the
29
+ * whole session (`render()` … `waitUntilExit()`) and back to `false` on unmount, so once the TUI
30
+ * is gone the default (headless) stdout sink is fully restored. An active per-turn subscriber
31
+ * always takes precedence, so legitimate in-turn output is untouched.
32
+ */
33
+ export function setToolOutputSuppressed(value) {
34
+ suppressed = value;
35
+ }
36
+ /**
37
+ * Emit one piece of live tool output. Routed to the active subscriber when present (the Ink
38
+ * TUI), otherwise to the DEFAULT SINK, which reproduces the pre-TUI-C17 behaviour exactly:
39
+ * the notice via `displayInfo` with its historical leading newline, chunks via `stdout.write`,
40
+ * a warning via `displayWarning` and an error via `displayError` (TUI-C31 a). While the TUI is
41
+ * mounted but no subscriber is attached (between turns), a no-subscriber emit is SUPPRESSED
42
+ * rather than written raw over the managed frame (TUI-C31 d).
43
+ */
44
+ export function emitToolOutput(chunk) {
45
+ if (activeListener) {
46
+ activeListener(chunk);
47
+ return;
48
+ }
49
+ if (suppressed) {
50
+ // TUI mounted, between turns: drop the straggler instead of corrupting the managed frame.
51
+ return;
52
+ }
53
+ switch (chunk.kind) {
54
+ case 'notice':
55
+ displayInfo(`\n${chunk.text}`);
56
+ break;
57
+ case 'warning':
58
+ displayWarning(chunk.text);
59
+ break;
60
+ case 'error':
61
+ displayError(chunk.text);
62
+ break;
63
+ default:
64
+ stdout.write(chunk.text);
65
+ }
66
+ }
67
+ /** Convert one channel chunk into its typed {@link AgentStreamEvent} representation. */
68
+ function toEvent(chunk) {
69
+ return {
70
+ type: 'tool_output',
71
+ ...(chunk.toolCallId !== undefined ? { id: chunk.toolCallId } : {}),
72
+ name: chunk.toolName,
73
+ chunk: chunk.text,
74
+ // A `notice` is chrome; a failure-path `warning`/`error` is also chrome relative to the child's
75
+ // own stdout/stderr `output`, so it lands on the view-model's separate `notice` field (never
76
+ // counted as a raw output line) rather than styled as command output.
77
+ ...(chunk.kind === 'notice' || chunk.kind === 'warning' || chunk.kind === 'error'
78
+ ? { isNotice: true }
79
+ : {}),
80
+ };
81
+ }
82
+ /**
83
+ * Merge live tool output into an agent event stream: subscribes to the channel for the
84
+ * lifetime of `inner` and yields each emitted chunk as a `tool_output` event, interleaved
85
+ * with `inner`'s own events in arrival order. Because tool child output arrives WHILE the
86
+ * graph stream is awaiting its next message, the merge is push-based (a woken queue), so a
87
+ * long-running command's output streams into the consumer live rather than batching until
88
+ * the tool finishes.
89
+ *
90
+ * Used by the TUI session around `processMessagesWithEvents`; always unsubscribes (restoring
91
+ * the default stdout sink) when the inner stream completes, throws, or the consumer stops
92
+ * early. Errors from `inner` (including aborts) propagate unchanged after the queue drains.
93
+ */
94
+ export async function* mergeToolOutputIntoEvents(inner) {
95
+ const queue = [];
96
+ let wake = null;
97
+ let done = false;
98
+ let error;
99
+ let failed = false;
100
+ const notify = () => {
101
+ const w = wake;
102
+ wake = null;
103
+ w?.();
104
+ };
105
+ const unsubscribe = subscribeToolOutput((chunk) => {
106
+ queue.push(toEvent(chunk));
107
+ notify();
108
+ });
109
+ // Pump the inner stream into the same queue so both sources serialize in arrival order.
110
+ const pump = (async () => {
111
+ try {
112
+ for await (const event of inner) {
113
+ queue.push(event);
114
+ notify();
115
+ }
116
+ }
117
+ catch (e) {
118
+ failed = true;
119
+ error = e;
120
+ }
121
+ finally {
122
+ done = true;
123
+ notify();
124
+ }
125
+ })();
126
+ try {
127
+ for (;;) {
128
+ while (queue.length > 0) {
129
+ yield queue.shift();
130
+ }
131
+ if (done)
132
+ break;
133
+ await new Promise((resolve) => {
134
+ wake = resolve;
135
+ });
136
+ }
137
+ await pump;
138
+ // TUI-C31 (b): the subscriber is still attached at this point, so a straggler chunk can be
139
+ // pushed in the microwindow between the loop's last drain and the unsubscribe (the child
140
+ // flushed one final line as the turn ended). Detach the subscriber FIRST — after which no
141
+ // further chunk can be queued — THEN drain whatever is already queued, so nothing enqueued is
142
+ // silently dropped.
143
+ unsubscribe();
144
+ while (queue.length > 0) {
145
+ yield queue.shift();
146
+ }
147
+ if (failed)
148
+ throw error;
149
+ }
150
+ finally {
151
+ // TUI-C31 (c): also reached on early-stop — a consumer `return()`/`throw` into this generator
152
+ // while it is suspended (e.g. at a yield, the way a `for await` aborts). Make that path clean:
153
+ // unsubscribe (idempotent — a stale unsubscribe is a no-op) so the subscription never leaks,
154
+ // settle any pending wake resolver, and best-effort release the inner stream. This is
155
+ // deliberately fire-and-forget: `inner` may be parked on an await that its own `return()`
156
+ // cannot interrupt, and AWAITING the pump here would hang the consumer's `return()` on exactly
157
+ // that unresolvable inner — so we release and let go rather than block.
158
+ unsubscribe();
159
+ notify();
160
+ if (!done) {
161
+ void inner.return?.(undefined).catch(() => { });
162
+ }
163
+ }
164
+ }
165
+ //# sourceMappingURL=toolOutputChannel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toolOutputChannel.js","sourceRoot":"","sources":["../../src/core/toolOutputChannel.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { GthConfig } from '#src/config.js';
2
- import type { ShellSafetyVerdict } from '#src/core/shell/judge.js';
2
+ import type { DeclaredToolAnnotations } from '#src/core/approvals/annotations.js';
3
+ import type { RaterNegotiationRound, ShellSafetyVerdict } from '#src/core/shell/rater.js';
3
4
  import type { BaseMessage } from '@langchain/core/messages';
4
5
  import type { RunnableConfig } from '@langchain/core/runnables';
5
6
  import type { StructuredToolInterface } from '@langchain/core/tools';
@@ -22,6 +23,45 @@ export declare enum StatusLevel {
22
23
  STREAM = 6
23
24
  }
24
25
  export type GthCommand = 'ask' | 'pr' | 'review' | 'chat' | 'code' | 'api' | 'exec';
26
+ /**
27
+ * GS2-16 — per-run analytics harvested from a finished agent turn, threaded into the opt-in
28
+ * history recorder ({@link recordSessionSafe}) so `gth insights` reports real numbers instead of
29
+ * zeros. All fields are best-effort: token counts are only present when the provider actually
30
+ * reported `usage_metadata` (otherwise omitted so the recorder stores NULL and the insights
31
+ * formatter suppresses the misleading `0`), and `tools` lists the names of tools invoked during
32
+ * the run (deduplicated, order-insensitive). There is no `costUsd` — cost requires a reliable
33
+ * price table this project does not carry, so it is deliberately never invented here.
34
+ */
35
+ export interface GthRunStats {
36
+ /** Total prompt/input tokens across the run's LLM calls, when the provider reported usage. */
37
+ tokensInput?: number;
38
+ /** Total completion/output tokens across the run's LLM calls, when the provider reported usage. */
39
+ tokensOutput?: number;
40
+ /** Names of tools invoked during the run (deduplicated); empty when no tools were used. */
41
+ tools: string[];
42
+ /**
43
+ * BATCH-21 — one record per executed tool result (`ToolMessage`) observed during the run, in
44
+ * arrival order and NOT deduplicated (a tool called twice yields two records), so `gth eval`'s
45
+ * tool-RESULT assertions (`must_error` / `tool_result_json_path`) can grade what a tool
46
+ * *returned*, not just that it was called. Optional (additive): producers that predate the field
47
+ * simply omit it; {@link runStats.js finalizeRunStats} always sets it.
48
+ */
49
+ toolResults?: GthToolResult[];
50
+ }
51
+ /**
52
+ * BATCH-21 — one executed tool call's result, harvested from its `ToolMessage` by the GS2-16
53
+ * run-stats accumulator (`core/runStats.ts`). Fail-soft like everything else there: `content` is
54
+ * omitted when no text payload could be derived, and is size-capped
55
+ * ({@link runStats.js TOOL_RESULT_CONTENT_CAP}) so a giant payload can't bloat run stats.
56
+ */
57
+ export interface GthToolResult {
58
+ /** The tool that produced the result (`ToolMessage.name`). */
59
+ name: string;
60
+ /** `true` iff the result carried LangChain's real error signal (`ToolMessage.status === 'error'`). */
61
+ isError: boolean;
62
+ /** The result payload as text (a non-string payload is JSON-stringified), capped in length. */
63
+ content?: string;
64
+ }
25
65
  /**
26
66
  * Typed events emitted by the agent's {@link GthAgentInterface#streamWithEvents} path.
27
67
  * This is the renderer contract shared by every consumer of an agent run — the AG-UI
@@ -49,10 +89,45 @@ export type AgentStreamEvent = {
49
89
  } | {
50
90
  type: 'tool_end';
51
91
  id: string;
92
+ } | {
93
+ /**
94
+ * TUI-C17 — one live output chunk from an EXECUTING tool (a custom/dev toolkit child
95
+ * process's stdout/stderr, or its "Executing …" announcement), surfaced through the managed
96
+ * event stream instead of raw `process.stdout` so a renderer (the Ink TUI) can fold it into
97
+ * its view-model. Emitted by the tool-output channel merge
98
+ * (see `core/toolOutputChannel.js#mergeToolOutputIntoEvents`), NOT by `processEventStream`
99
+ * itself — consumers that don't opt into the merge (e.g. the AG-UI SSE encoder) never see it
100
+ * and the toolkits keep writing to stdout for them (today's headless behaviour).
101
+ */
102
+ type: 'tool_output';
103
+ /**
104
+ * The tool call this chunk belongs to (LangChain's `ToolRunnableConfig.toolCall.id`,
105
+ * threaded through the toolkits), so a renderer can nest output under the exact call —
106
+ * TUI-C30 consumes this for per-call output previews. Optional only defensively: absent
107
+ * when the executing framework did not supply a tool call, in which case consumers should
108
+ * fall back to `name` attribution.
109
+ */
110
+ id?: string;
111
+ /** The gth tool name (e.g. `run_shell_command`, a custom tool's name). Always known. */
112
+ name: string;
113
+ /** One verbatim streamed chunk of the child's stdout/stderr (or the notice text). */
114
+ chunk: string;
115
+ /**
116
+ * True when this chunk is the "🔧 Executing …" announcement rather than child output, so
117
+ * a richer renderer (TUI-C30) can style or strip it when previewing raw output lines.
118
+ */
119
+ isNotice?: boolean;
52
120
  } | {
53
121
  type: 'tool_result';
54
122
  id: string;
55
123
  content: string;
124
+ /**
125
+ * True when the underlying `ToolMessage.status` is `'error'` (LangChain's real
126
+ * tool-result error signal). Absent/undefined means success — consumers must not
127
+ * sniff the result text to infer failure. Optional for backward compatibility with
128
+ * producers that predate the field.
129
+ */
130
+ isError?: boolean;
56
131
  };
57
132
  /**
58
133
  * The minimal structural surface of a compiled LangGraph agent that the shared agent
@@ -84,29 +159,126 @@ export interface PendingToolInterrupt {
84
159
  name: string;
85
160
  args: Record<string, unknown>;
86
161
  /**
87
- * EXT-10 — when the LLM-as-judge safety gate escalated this `run_shell_command` to the human
88
- * (rather than auto-approving it), the judge's verdict is attached here so the approval surface
89
- * can show a "safety judge flagged: <reason>" notice. Absent when the judge is disabled (the
90
- * default) or when the command reached the human without going through the judge.
162
+ * CFG-26 — when the AI rater escalated this `run_shell_command` to the human (rather than
163
+ * approving it or bouncing it back to the model), the rater's verdict is attached here so the
164
+ * approval surface can show an "AI rater (<tier>): <reason>" notice. Absent when the rater is
165
+ * off, or when the command reached the human without being rated.
91
166
  */
92
167
  safetyVerdict?: ShellSafetyVerdict;
168
+ /**
169
+ * EXT-71 §3.2 — when this call reached the human because a declared `approvals.escalate` entry
170
+ * matched it, the entry that fired, rendered for display. It is the provenance the prompt shows:
171
+ * an escalation the user cannot trace to the line they wrote reads as the gate malfunctioning.
172
+ * Absent whenever the escalation came from the rung or the rater instead.
173
+ */
174
+ escalatedBy?: string;
175
+ /**
176
+ * EXT-71 §6 — **what a sticky choice will store**, rendered in the object form the user would
177
+ * write in a config file, e.g. `{ "type": "shell", "matcher": "exact", "pattern": "npm test" }`.
178
+ * The menu MUST show this at the moment of the choice, on every surface: the user is shown the
179
+ * thing they are agreeing to rather than a generalization of it, which is what makes the display
180
+ * honest and cheap at once.
181
+ *
182
+ * **For a tool call the stored thing is the tool, not the arguments** (§4.7.4), so this reads
183
+ * e.g. `{ "type": "mcpTool", "server": "fetcher", "matcher": "exact", "pattern": "fetch_url",
184
+ * "host": "docs.internal.example" }`. That is the one place a grant is deliberately broader than
185
+ * what the human was shown, which is why the display carries the most weight there.
186
+ *
187
+ * Absent exactly where no sticky grant is on offer — a `catastrophic` outcome (§4.2 withdraws
188
+ * the persistent grants), a rung that remembers nothing, a command that does not statically
189
+ * resolve, or a tool call naming more than one host (which has no honest single-host entry) — so
190
+ * a prompt never advertises a control that has already been withdrawn.
191
+ */
192
+ grantPreview?: string;
193
+ /**
194
+ * §6 — **the same grant in the words a menu control is written in**: `npm test` for a shell
195
+ * command, `tool gth_web_fetch (host docs.internal.example)` or `mcpTool jira/create_issue` for a
196
+ * tool call. It is what the *always approve* control names, so the control reads as
197
+ * *always approve this tool for this host* rather than as a bare key.
198
+ *
199
+ * It is rendered by `describeApprovalEntry` — the one-liner every other provenance message uses,
200
+ * including the §4.7.4 notice that later withdraws the grant. Sharing the renderer is the point:
201
+ * a menu that describes a grant differently from the notice that withdraws it is how a user stops
202
+ * trusting either.
203
+ *
204
+ * Present exactly when {@link grantPreview} is, since both are rendered from the one entry
205
+ * `recordApproval` would write.
206
+ */
207
+ grantSummary?: string;
208
+ /**
209
+ * [[TUI-C26]] §6 — **what the menu's *always reject* choice will record**, in the object form the
210
+ * user would write in a config file. The deny mirror of {@link grantPreview}, shown at the moment
211
+ * of the choice for the same reason: a control that does not say what it stores is one the user
212
+ * has to guess at.
213
+ *
214
+ * **Its availability is NOT the grant's**, and reading one off the other is the mistake this
215
+ * field exists to prevent. The matcher's own rule is *undecidable → no match on the allow side, a
216
+ * match on the deny side*, so a command the gate cannot statically resolve **can** be refused
217
+ * permanently even though it can never be allowed permanently. `always reject` also stays offered
218
+ * on a `catastrophic` verdict, where §4.2 withdraws every sticky grant: sticky refusal is safe in
219
+ * every direction, and it is only the grant that is ever made harder.
220
+ *
221
+ * Absent only where the entry grammar cannot hold one at all — today, an MCP call whose server
222
+ * could not be attributed (the grammar's `server` cannot be empty). Absent means the control is
223
+ * **not offered**, never offered-and-disabled.
224
+ */
225
+ denyPreview?: string;
226
+ /**
227
+ * §6 — the same deny entry in the words the control is written in, through the one-liner every
228
+ * other provenance message uses. Present exactly when {@link denyPreview} is, since both are
229
+ * rendered from the one entry the runner would record.
230
+ */
231
+ denySummary?: string;
232
+ /**
233
+ * [[EXT-29]] §6 — **every round of the §5 negotiation that preceded this escalation**, oldest
234
+ * first, when one did.
235
+ *
236
+ * The user is not asked to rule on the final command in isolation: *that the agent proposed
237
+ * `git reset --hard origin/main` three times unchanged, against two rejections that each told it
238
+ * what to fix, is itself the most important thing on the screen, and it is invisible if only the
239
+ * last attempt is shown.* `core/shell/negotiation.ts`'s `renderNegotiationTranscript` is the
240
+ * shared renderer, so two surfaces cannot describe one exchange two ways.
241
+ *
242
+ * Absent for every escalation that had no negotiation — `catastrophic` (§4.2 gives it no rounds
243
+ * at all), a declared `approvals.escalate` entry, an unrated rung, a tool subject.
244
+ */
245
+ negotiationRounds?: readonly RaterNegotiationRound[];
93
246
  }
94
247
  /**
95
- * Persistence scope for an `approve` decision (EXT-9 Tier-2 allow-list ergonomics):
248
+ * Persistence scope for an `approve` decision (spec §6):
96
249
  * - `once` — run this single invocation only; remember nothing (the default).
97
- * - `session` — remember the command's classified prefix for the life of this runner
98
- * instance, so flag-variants of the same operation auto-approve without re-prompting.
99
- * - `always` — additionally persist the prefix to the project allow-list
250
+ * - `session` — remember **this command**, as an `exact` entry (§3.1), for the life of this runner
251
+ * instance, so the same command stops re-prompting. A longer variant of it still asks: the menu
252
+ * never widens, and breadth is something a human writes in a config file.
253
+ * - `always` — additionally persist that entry to the project store
100
254
  * (`.gsloth/.gsloth-settings/shell-allowlist.json`) so it survives across runs.
101
255
  */
102
256
  export type ToolApprovalScope = 'once' | 'session' | 'always';
257
+ /**
258
+ * §6 — persistence scope for a `reject` decision, the *always reject* half of the escalation menu:
259
+ * - `once` — refuse this single invocation; remember nothing (the default).
260
+ * - `session` — additionally record a **deny** entry for this call, in the one entry grammar, for
261
+ * the life of this runner instance. The matcher consults it before anything else, so the next
262
+ * identical call is refused without reaching a person at all.
263
+ *
264
+ * **There is deliberately no `always`.** An `approve` can persist because there is a project store
265
+ * to persist it to; there is no persisted deny store, and inventing one is a decision about a file
266
+ * users will have to live with rather than a rendering choice. So the scopes here are the ones the
267
+ * gate can actually honour, and a control that promised more would be §6's own failure mode — an
268
+ * affordance offered and then quietly refused.
269
+ */
270
+ export type ToolRejectScope = 'once' | 'session';
103
271
  /**
104
272
  * A consumer-supplied decision on a {@link PendingToolInterrupt}: approve runs the tool,
105
273
  * reject feeds the model a tool-rejected message (with the optional reason).
106
274
  *
107
- * `approve` carries an optional {@link ToolApprovalScope}; when absent it means `once`
108
- * (backward compatible a bare `{ type: 'approve' }` still type-checks and behaves as
109
- * a single-shot approval that persists nothing).
275
+ * Each arm carries an optional scope; when absent it means `once` (backward compatible — a bare
276
+ * `{ type: 'approve' }` or `{ type: 'reject' }` still type-checks and behaves as the single-shot
277
+ * decision that persists nothing).
278
+ *
279
+ * The two scopes are **different types on purpose**. Sharing {@link ToolApprovalScope} would let a
280
+ * surface send `{ type: 'reject', scope: 'always' }`, which type-checks, promises a persistence
281
+ * nothing implements, and degrades to a session refusal with no diagnostic.
110
282
  */
111
283
  export type ToolApprovalDecision = {
112
284
  type: 'approve';
@@ -114,6 +286,7 @@ export type ToolApprovalDecision = {
114
286
  } | {
115
287
  type: 'reject';
116
288
  message?: string;
289
+ scope?: ToolRejectScope;
117
290
  };
118
291
  /**
119
292
  * Callback the {@link GthAgentRunner} invokes when a run suspends on a tool-approval
@@ -122,6 +295,42 @@ export type ToolApprovalDecision = {
122
295
  * silently hang or auto-approve.
123
296
  */
124
297
  export type ToolApprovalCallback = (pending: PendingToolInterrupt) => Promise<ToolApprovalDecision> | ToolApprovalDecision;
298
+ /**
299
+ * [[TUI-C68]] §6.1 — what the **attack banner** is about: the command whose own structure the
300
+ * rater called hostile, and the rater's explanation of what it saw. Both are untrusted text and a
301
+ * surface must paint them through `core/shell/framing`.
302
+ *
303
+ * A separate shape from {@link PendingToolInterrupt} because it is a separate question. An approval
304
+ * prompt asks *may this run*, and every control on its menu is a legitimate answer carrying a
305
+ * scope; this asks *do you believe the rating is wrong*, has exactly one way through, and offers no
306
+ * scope at all — so a surface cannot reach for a menu control that does not exist here.
307
+ */
308
+ export interface PendingAttackHalt {
309
+ /** The command the rater rated an attack, as the model wrote it. Untrusted text. */
310
+ command: string;
311
+ /** The rater's own explanation of what the command's structure showed. Untrusted text. */
312
+ reason: string;
313
+ }
314
+ /**
315
+ * §6.1 — the human's answer at the attack banner.
316
+ *
317
+ * **A string union rather than a boolean, and the polarity is deliberate.** `run-anyway` is the
318
+ * only value that runs anything; every other value — one a future surface invents, or `undefined`
319
+ * from a surface that forgot to return — stops the run. A boolean would put the irreversible answer
320
+ * one inverted comparison away, and there is nothing here worth being one typo from.
321
+ */
322
+ export type AttackHaltAnswer = 'run-anyway' | 'stop';
323
+ /**
324
+ * §6.1 — callback the {@link GthAgentRunner} invokes when the rater rates a command an `attack`, so
325
+ * an interactive surface can show the red banner and let a human type their way past it.
326
+ *
327
+ * **Absent means halt**, exactly as an absent {@link ToolApprovalCallback} means the §6.2
328
+ * non-interactive exit. A surface that never wires this — a CI run, an AG-UI server, a surface
329
+ * nobody has written yet — keeps the halt, so forgetting fails safe. It may never block a
330
+ * non-interactive run, and no timeout turns waiting into a grant: waiting is something only a wired
331
+ * surface can cause.
332
+ */
333
+ export type AttackHaltCallback = (halt: PendingAttackHalt) => Promise<AttackHaltAnswer> | AttackHaltAnswer;
125
334
  export interface GthAgentInterface {
126
335
  init(command: GthCommand | undefined, configIn: GthConfig, checkpointSaver?: BaseCheckpointSaver | undefined): Promise<void>;
127
336
  invoke(messages: Message[], runConfig: RunnableConfig): Promise<string>;
@@ -148,6 +357,38 @@ export interface GthAgentInterface {
148
357
  * approve/reject confirmation loop.
149
358
  */
150
359
  getPendingToolInterrupts?(runConfig: RunnableConfig): Promise<PendingToolInterrupt[]>;
360
+ /**
361
+ * EXT-58 (spec §4.4) — the names of the tools registered with the graph at `init`. The runner
362
+ * intersects them with the built-in summaries table to tell the rater which already-granted
363
+ * built-ins it may offer as an alternative, so a suggestion can never name a tool the model does
364
+ * not have. Optional: an agent that does not track its tools simply omits it, and the rater then
365
+ * receives no granted list (and so suggests nothing).
366
+ */
367
+ getRegisteredToolNames?(): string[];
368
+ /**
369
+ * EXT-70 (spec §4.7.1) — what the connected MCP servers declared about their own tools in their
370
+ * `tools/list` responses, keyed by the REGISTERED tool name (`mcp__<server>__<tool>`). The runner
371
+ * reads it as the `mcp` half of a declared-annotation lookup when it computes a call's effective
372
+ * annotation set.
373
+ *
374
+ * These are **claims, not credentials**: nothing here has been trusted, and an entry only becomes
375
+ * load-bearing where the user's `approvals.mcp` block believes that hint from that server.
376
+ * Optional — an agent that tracks no tools omits it, and every tool is then fail-closed, which is
377
+ * exactly what a fully distrustful configuration computes anyway.
378
+ */
379
+ getDeclaredMcpToolAnnotations?(): ReadonlyMap<string, DeclaredToolAnnotations>;
380
+ /**
381
+ * GS2-16 — reset the per-run analytics accumulator so the NEXT turn's token/tool totals start
382
+ * from zero. Called by {@link GthAgentRunner} at each turn boundary (the runner is reused across
383
+ * turns in interactive sessions). Optional: agents that don't collect stats simply omit it.
384
+ */
385
+ resetRunStats?(): void;
386
+ /**
387
+ * GS2-16 — the analytics harvested from the run(s) since the last {@link resetRunStats}. Used by
388
+ * the runner to thread token/tool data into the opt-in history recorder. Optional; when absent
389
+ * the runner records no analytics for that turn. Reading must never throw.
390
+ */
391
+ getRunStats?(): GthRunStats;
151
392
  cleanup?(): Promise<void>;
152
393
  }
153
394
  /**
@@ -161,9 +402,50 @@ export type ToolsResolver = (config: GthConfig, command?: GthCommand) => Promise
161
402
  export type ToolsCleanup = () => Promise<void>;
162
403
  export type MiddlewareResolver = (middleware: any[] | undefined, config: GthConfig) => Promise<any[]>;
163
404
  export type MiddlewareCleanup = () => Promise<void>;
405
+ /**
406
+ * EXT-32 — one connected MCP server's discovery `instructions` string (from its MCP `initialize`
407
+ * handshake), paired with the server name it came from. Captured once during tool resolution and
408
+ * reused: injected (fenced + per-server-labelled) into the composed system prompt, and available
409
+ * for [[TUI-C20]]'s MCP debug tab to render the same captured text. Only servers that actually
410
+ * supplied non-empty instructions appear here.
411
+ */
412
+ export interface McpServerInstruction {
413
+ /** The configured MCP server name (the key under `config.mcpServers`). */
414
+ server: string;
415
+ /** The server-provided instructions text (trimmed, non-empty). */
416
+ instructions: string;
417
+ }
418
+ /**
419
+ * A per-server MCP connection failure captured during the most recent {@link ToolsResolver} call.
420
+ * Recorded when a configured MCP server can't be reached (connection/handshake/auth error), so the
421
+ * failure — otherwise a transient `displayWarning` that scrolls away the moment the Ink TUI takes
422
+ * over the screen — can be re-surfaced persistently in the chrome AND named in the /debug MCP tab
423
+ * (which renders per configured server and would otherwise show only a bare "no tools" line, with
424
+ * no hint that the server never connected). Mirrors {@link McpServerInstruction}.
425
+ */
426
+ export interface McpConnectionFailure {
427
+ /** The configured MCP server name (the key under `config.mcpServers`). */
428
+ server: string;
429
+ /** A concise, human-readable reason (the underlying connection error's message). */
430
+ reason: string;
431
+ }
164
432
  export interface AgentResolvers {
165
433
  resolveTools?: ToolsResolver;
166
434
  cleanupTools?: ToolsCleanup;
167
435
  resolveMiddleware?: MiddlewareResolver;
168
436
  cleanupMiddleware?: MiddlewareCleanup;
437
+ /**
438
+ * EXT-32 — the per-server MCP discovery instructions captured during the most recent
439
+ * {@link ToolsResolver} call (empty when no MCP servers are configured or none supplied
440
+ * instructions). Optional: resolvers without MCP support simply omit it, and the prompt
441
+ * composition treats an absent accessor as "no instructions" (no MCP section is emitted).
442
+ */
443
+ getMcpServerInstructions?(): McpServerInstruction[];
444
+ /**
445
+ * The per-server MCP connection failures captured during the most recent {@link ToolsResolver}
446
+ * call (empty when every configured server connected, or none is configured). Optional: resolvers
447
+ * without MCP support omit it, and callers treat an absent accessor as "no failures". Read by the
448
+ * TUI to surface a persistent notice and to annotate the /debug MCP tab.
449
+ */
450
+ getMcpConnectionFailures?(): McpConnectionFailure[];
169
451
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,+CAAS,CAAA;IACT,6CAAQ,CAAA;IACR,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,+CAAS,CAAA;IACT,iDAAU,CAAA;AACZ,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB,+CAAS,CAAA;IACT,6CAAQ,CAAA;IACR,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,mDAAW,CAAA;IACX,+CAAS,CAAA;IACT,iDAAU,CAAA;AACZ,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * GS2-7 (B20) — pure, presentation-only formatters for the history surfaces.
4
+ *
5
+ * Shared by the `gth history` / `gth insights` CLI commands AND the `/history` `/search`
6
+ * `/insights` TUI slash commands so both render identically and both are unit-testable without a
7
+ * DB or a terminal. Every function is a pure `data -> string[]` transform (one display line per
8
+ * element); no I/O, no colour codes.
9
+ */
10
+ import type { ConversationSummary, HistoryInsights, SessionRecord, SessionSearchResult } from '#src/history/historyStore.js';
11
+ /**
12
+ * Render FTS search hits: a header line per hit plus its snippet (or a prompt preview when the
13
+ * snippet is empty). Returns a friendly single line when there are none.
14
+ */
15
+ export declare function formatSearchResults(results: SessionSearchResult[]): string[];
16
+ /**
17
+ * GS2-19 — render a conversation-grained listing: one header + last-turn preview per conversation.
18
+ * The header carries the count / timespan / last message that make the conversation the top-level
19
+ * unit (`gth history list`), replacing the old flat per-turn list.
20
+ */
21
+ export declare function formatConversationList(conversations: ConversationSummary[]): string[];
22
+ /**
23
+ * GS2-19 — render one conversation's full thread (all turns in order) for `gth history show <id>`.
24
+ * Each turn shows its prompt and response preview so a search hit can be expanded into context.
25
+ */
26
+ export declare function formatConversationThread(turns: SessionRecord[]): string[];
27
+ /** Render the analytics summary: totals, top tools, per-command breakdown. */
28
+ export declare function formatInsightsSummary(insights: HistoryInsights): string[];