@gaunt-sloth/core 2.0.0-alpha.8 → 2.0.0-beta.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 (275) hide show
  1. package/.gsloth.review.md +2 -0
  2. package/README.md +71 -20
  3. package/dist/config/colour.d.ts +38 -0
  4. package/dist/config/colour.js +36 -0
  5. package/dist/config/colour.js.map +1 -0
  6. package/dist/config/configDiscovery.d.ts +79 -0
  7. package/dist/config/configDiscovery.js +80 -0
  8. package/dist/config/configDiscovery.js.map +1 -0
  9. package/dist/config/defaults.d.ts +20 -20
  10. package/dist/config/defaults.js +10 -8
  11. package/dist/config/defaults.js.map +1 -1
  12. package/dist/config/filesystem-tools.d.ts +41 -0
  13. package/dist/config/filesystem-tools.js +56 -0
  14. package/dist/config/filesystem-tools.js.map +1 -0
  15. package/dist/config/loader.d.ts +171 -19
  16. package/dist/config/loader.js +1054 -144
  17. package/dist/config/loader.js.map +1 -1
  18. package/dist/config/mouse.d.ts +50 -0
  19. package/dist/config/mouse.js +44 -0
  20. package/dist/config/mouse.js.map +1 -0
  21. package/dist/config/profiles.d.ts +68 -0
  22. package/dist/config/profiles.js +93 -0
  23. package/dist/config/profiles.js.map +1 -0
  24. package/dist/config/providerKeys.d.ts +69 -0
  25. package/dist/config/providerKeys.js +69 -0
  26. package/dist/config/providerKeys.js.map +1 -0
  27. package/dist/config/schema.d.ts +2671 -138
  28. package/dist/config/schema.js +1361 -85
  29. package/dist/config/schema.js.map +1 -1
  30. package/dist/config/shell-policy.d.ts +899 -111
  31. package/dist/config/shell-policy.js +800 -70
  32. package/dist/config/shell-policy.js.map +1 -1
  33. package/dist/config/tool-descriptions.d.ts +211 -0
  34. package/dist/config/tool-descriptions.js +272 -0
  35. package/dist/config/tool-descriptions.js.map +1 -0
  36. package/dist/config/types.d.ts +352 -41
  37. package/dist/config/types.js +1 -0
  38. package/dist/config/types.js.map +1 -1
  39. package/dist/config.d.ts +35 -1
  40. package/dist/config.js +16 -1
  41. package/dist/config.js.map +1 -1
  42. package/dist/constants.d.ts +28 -1
  43. package/dist/constants.js +28 -1
  44. package/dist/constants.js.map +1 -1
  45. package/dist/core/GthAbstractAgent.d.ts +166 -11
  46. package/dist/core/GthAbstractAgent.js +484 -45
  47. package/dist/core/GthAbstractAgent.js.map +1 -1
  48. package/dist/core/GthAgentRunner.d.ts +543 -57
  49. package/dist/core/GthAgentRunner.js +1494 -140
  50. package/dist/core/GthAgentRunner.js.map +1 -1
  51. package/dist/core/GthLangChainAgent.d.ts +117 -2
  52. package/dist/core/GthLangChainAgent.js +602 -29
  53. package/dist/core/GthLangChainAgent.js.map +1 -1
  54. package/dist/core/approvals/annotations.d.ts +122 -0
  55. package/dist/core/approvals/annotations.js +137 -0
  56. package/dist/core/approvals/annotations.js.map +1 -0
  57. package/dist/core/approvals/grants.d.ts +216 -0
  58. package/dist/core/approvals/grants.js +469 -0
  59. package/dist/core/approvals/grants.js.map +1 -0
  60. package/dist/core/approvals/matcher.d.ts +202 -0
  61. package/dist/core/approvals/matcher.js +267 -0
  62. package/dist/core/approvals/matcher.js.map +1 -0
  63. package/dist/core/approvals/mcpSubjects.d.ts +40 -0
  64. package/dist/core/approvals/mcpSubjects.js +99 -0
  65. package/dist/core/approvals/mcpSubjects.js.map +1 -0
  66. package/dist/core/approvals/promptHeader.d.ts +28 -0
  67. package/dist/core/approvals/promptHeader.js +62 -0
  68. package/dist/core/approvals/promptHeader.js.map +1 -0
  69. package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
  70. package/dist/core/approvals/toolAnnotationSources.js +277 -0
  71. package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
  72. package/dist/core/approvals/toolHost.d.ts +46 -0
  73. package/dist/core/approvals/toolHost.js +108 -0
  74. package/dist/core/approvals/toolHost.js.map +1 -0
  75. package/dist/core/debugCapture.d.ts +74 -0
  76. package/dist/core/debugCapture.js +100 -0
  77. package/dist/core/debugCapture.js.map +1 -0
  78. package/dist/core/gthLeanAgentFactory.d.ts +4 -4
  79. package/dist/core/gthLeanAgentFactory.js +4 -4
  80. package/dist/core/launchBanner.d.ts +127 -0
  81. package/dist/core/launchBanner.js +414 -0
  82. package/dist/core/launchBanner.js.map +1 -0
  83. package/dist/core/modelLabel.d.ts +19 -0
  84. package/dist/core/modelLabel.js +26 -0
  85. package/dist/core/modelLabel.js.map +1 -0
  86. package/dist/core/plainToolIndication.d.ts +15 -0
  87. package/dist/core/plainToolIndication.js +174 -0
  88. package/dist/core/plainToolIndication.js.map +1 -0
  89. package/dist/core/reasoningBlocks.d.ts +65 -0
  90. package/dist/core/reasoningBlocks.js +103 -0
  91. package/dist/core/reasoningBlocks.js.map +1 -0
  92. package/dist/core/refusal.d.ts +53 -0
  93. package/dist/core/refusal.js +133 -0
  94. package/dist/core/refusal.js.map +1 -0
  95. package/dist/core/runHeader.d.ts +38 -0
  96. package/dist/core/runHeader.js +42 -0
  97. package/dist/core/runHeader.js.map +1 -0
  98. package/dist/core/runStats.d.ts +14 -3
  99. package/dist/core/runStats.js +48 -3
  100. package/dist/core/runStats.js.map +1 -1
  101. package/dist/core/shell/ShellCommandFailedError.d.ts +3 -4
  102. package/dist/core/shell/ShellCommandFailedError.js +3 -4
  103. package/dist/core/shell/ShellCommandFailedError.js.map +1 -1
  104. package/dist/core/shell/abstention.d.ts +88 -0
  105. package/dist/core/shell/abstention.js +184 -0
  106. package/dist/core/shell/abstention.js.map +1 -0
  107. package/dist/core/shell/approvalCapture.d.ts +271 -0
  108. package/dist/core/shell/approvalCapture.js +108 -0
  109. package/dist/core/shell/approvalCapture.js.map +1 -0
  110. package/dist/core/shell/approvalStop.d.ts +123 -0
  111. package/dist/core/shell/approvalStop.js +269 -0
  112. package/dist/core/shell/approvalStop.js.map +1 -0
  113. package/dist/core/shell/arity.d.ts +6 -0
  114. package/dist/core/shell/arity.js +20 -6
  115. package/dist/core/shell/arity.js.map +1 -1
  116. package/dist/core/shell/denylist.d.ts +11 -0
  117. package/dist/core/shell/denylist.js +37 -0
  118. package/dist/core/shell/denylist.js.map +1 -0
  119. package/dist/core/shell/escalationSeverity.d.ts +141 -0
  120. package/dist/core/shell/escalationSeverity.js +89 -0
  121. package/dist/core/shell/escalationSeverity.js.map +1 -0
  122. package/dist/core/shell/framing.d.ts +190 -0
  123. package/dist/core/shell/framing.js +633 -0
  124. package/dist/core/shell/framing.js.map +1 -0
  125. package/dist/core/shell/hardline.d.ts +103 -0
  126. package/dist/core/shell/hardline.js +780 -0
  127. package/dist/core/shell/hardline.js.map +1 -0
  128. package/dist/core/shell/negotiation.d.ts +328 -0
  129. package/dist/core/shell/negotiation.js +488 -0
  130. package/dist/core/shell/negotiation.js.map +1 -0
  131. package/dist/core/shell/normalize.d.ts +44 -4
  132. package/dist/core/shell/normalize.js +61 -7
  133. package/dist/core/shell/normalize.js.map +1 -1
  134. package/dist/core/shell/openWorld.d.ts +263 -0
  135. package/dist/core/shell/openWorld.js +1188 -0
  136. package/dist/core/shell/openWorld.js.map +1 -0
  137. package/dist/core/shell/rater.d.ts +873 -0
  138. package/dist/core/shell/rater.js +1454 -0
  139. package/dist/core/shell/rater.js.map +1 -0
  140. package/dist/core/shell/raterModel.d.ts +41 -0
  141. package/dist/core/shell/raterModel.js +51 -0
  142. package/dist/core/shell/raterModel.js.map +1 -0
  143. package/dist/core/shell/raterVocabulary.d.ts +121 -0
  144. package/dist/core/shell/raterVocabulary.js +116 -0
  145. package/dist/core/shell/raterVocabulary.js.map +1 -0
  146. package/dist/core/shell/rejection.d.ts +69 -0
  147. package/dist/core/shell/rejection.js +38 -0
  148. package/dist/core/shell/rejection.js.map +1 -0
  149. package/dist/core/toolCallRepair/grammar.d.ts +41 -0
  150. package/dist/core/toolCallRepair/grammar.js +116 -0
  151. package/dist/core/toolCallRepair/grammar.js.map +1 -0
  152. package/dist/core/toolCallRepair/index.d.ts +2 -0
  153. package/dist/core/toolCallRepair/index.js +7 -0
  154. package/dist/core/toolCallRepair/index.js.map +1 -0
  155. package/dist/core/toolCallRepair/payload.d.ts +36 -0
  156. package/dist/core/toolCallRepair/payload.js +341 -0
  157. package/dist/core/toolCallRepair/payload.js.map +1 -0
  158. package/dist/core/toolCallRepair/promote.d.ts +45 -0
  159. package/dist/core/toolCallRepair/promote.js +90 -0
  160. package/dist/core/toolCallRepair/promote.js.map +1 -0
  161. package/dist/core/toolDisplay.d.ts +123 -0
  162. package/dist/core/toolDisplay.js +451 -0
  163. package/dist/core/toolDisplay.js.map +1 -0
  164. package/dist/core/toolOutputChannel.d.ts +95 -0
  165. package/dist/core/toolOutputChannel.js +165 -0
  166. package/dist/core/toolOutputChannel.js.map +1 -0
  167. package/dist/core/types.d.ts +349 -16
  168. package/dist/core/types.js.map +1 -1
  169. package/dist/history/historyFormat.d.ts +12 -3
  170. package/dist/history/historyFormat.js +50 -8
  171. package/dist/history/historyFormat.js.map +1 -1
  172. package/dist/history/historyStore.d.ts +77 -0
  173. package/dist/history/historyStore.js +173 -6
  174. package/dist/history/historyStore.js.map +1 -1
  175. package/dist/history/recordSession.d.ts +10 -1
  176. package/dist/history/recordSession.js +27 -0
  177. package/dist/history/recordSession.js.map +1 -1
  178. package/dist/providers/anthropic.js +12 -0
  179. package/dist/providers/anthropic.js.map +1 -1
  180. package/dist/providers/configurationPassthrough.d.ts +107 -0
  181. package/dist/providers/configurationPassthrough.js +148 -0
  182. package/dist/providers/configurationPassthrough.js.map +1 -0
  183. package/dist/providers/geminiSchemaSanitizer.d.ts +55 -0
  184. package/dist/providers/geminiSchemaSanitizer.js +347 -0
  185. package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
  186. package/dist/providers/geminiThinking.d.ts +60 -0
  187. package/dist/providers/geminiThinking.js +92 -0
  188. package/dist/providers/geminiThinking.js.map +1 -0
  189. package/dist/providers/google-genai.js +18 -1
  190. package/dist/providers/google-genai.js.map +1 -1
  191. package/dist/providers/groq.js +12 -0
  192. package/dist/providers/groq.js.map +1 -1
  193. package/dist/providers/huggingface.d.ts +25 -0
  194. package/dist/providers/huggingface.js +69 -0
  195. package/dist/providers/huggingface.js.map +1 -0
  196. package/dist/providers/modelCatalog.d.ts +109 -0
  197. package/dist/providers/modelCatalog.js +245 -0
  198. package/dist/providers/modelCatalog.js.map +1 -0
  199. package/dist/providers/modelDiscovery.d.ts +99 -5
  200. package/dist/providers/modelDiscovery.js +191 -35
  201. package/dist/providers/modelDiscovery.js.map +1 -1
  202. package/dist/providers/ollama.d.ts +18 -4
  203. package/dist/providers/ollama.js +67 -37
  204. package/dist/providers/ollama.js.map +1 -1
  205. package/dist/providers/openai.js +34 -0
  206. package/dist/providers/openai.js.map +1 -1
  207. package/dist/providers/openrouter.d.ts +26 -4
  208. package/dist/providers/openrouter.js +83 -26
  209. package/dist/providers/openrouter.js.map +1 -1
  210. package/dist/providers/vertexai.js +19 -1
  211. package/dist/providers/vertexai.js.map +1 -1
  212. package/dist/providers/xai.js +20 -0
  213. package/dist/providers/xai.js.map +1 -1
  214. package/dist/runtime/askStructured.d.ts +105 -0
  215. package/dist/runtime/askStructured.js +120 -0
  216. package/dist/runtime/askStructured.js.map +1 -0
  217. package/dist/runtime/conversation.d.ts +64 -0
  218. package/dist/runtime/conversation.js +171 -0
  219. package/dist/runtime/conversation.js.map +1 -0
  220. package/dist/runtime/singleShot.d.ts +37 -6
  221. package/dist/runtime/singleShot.js +113 -67
  222. package/dist/runtime/singleShot.js.map +1 -1
  223. package/dist/runtime/structuredOutput.d.ts +104 -0
  224. package/dist/runtime/structuredOutput.js +393 -0
  225. package/dist/runtime/structuredOutput.js.map +1 -0
  226. package/dist/utils/ProgressIndicator.d.ts +21 -0
  227. package/dist/utils/ProgressIndicator.js +30 -3
  228. package/dist/utils/ProgressIndicator.js.map +1 -1
  229. package/dist/utils/aiignoreUtils.js.map +1 -1
  230. package/dist/utils/binaryOutputUtils.js.map +1 -1
  231. package/dist/utils/consoleUtils.d.ts +95 -0
  232. package/dist/utils/consoleUtils.js +112 -2
  233. package/dist/utils/consoleUtils.js.map +1 -1
  234. package/dist/utils/crashHandler.d.ts +87 -0
  235. package/dist/utils/crashHandler.js +128 -0
  236. package/dist/utils/crashHandler.js.map +1 -0
  237. package/dist/utils/debugDump.d.ts +134 -0
  238. package/dist/utils/debugDump.js +381 -0
  239. package/dist/utils/debugDump.js.map +1 -0
  240. package/dist/utils/debugUtils.d.ts +13 -4
  241. package/dist/utils/debugUtils.js +36 -13
  242. package/dist/utils/debugUtils.js.map +1 -1
  243. package/dist/utils/displayWidth.d.ts +53 -0
  244. package/dist/utils/displayWidth.js +195 -0
  245. package/dist/utils/displayWidth.js.map +1 -0
  246. package/dist/utils/fileUtils.js.map +1 -1
  247. package/dist/utils/globalConfigUtils.d.ts +14 -2
  248. package/dist/utils/globalConfigUtils.js +22 -4
  249. package/dist/utils/globalConfigUtils.js.map +1 -1
  250. package/dist/utils/llmUtils.d.ts +39 -8
  251. package/dist/utils/llmUtils.js +76 -8
  252. package/dist/utils/llmUtils.js.map +1 -1
  253. package/dist/utils/redactSecrets.d.ts +63 -0
  254. package/dist/utils/redactSecrets.js +286 -0
  255. package/dist/utils/redactSecrets.js.map +1 -0
  256. package/dist/utils/systemPromptNotes.d.ts +222 -0
  257. package/dist/utils/systemPromptNotes.js +338 -0
  258. package/dist/utils/systemPromptNotes.js.map +1 -0
  259. package/dist/utils/systemUtils.d.ts +18 -1
  260. package/dist/utils/systemUtils.js +38 -3
  261. package/dist/utils/systemUtils.js.map +1 -1
  262. package/dist/utils/toolMatching.d.ts +30 -0
  263. package/dist/utils/toolMatching.js +44 -0
  264. package/dist/utils/toolMatching.js.map +1 -0
  265. package/dist/utils/untrustedText.d.ts +86 -0
  266. package/dist/utils/untrustedText.js +101 -0
  267. package/dist/utils/untrustedText.js.map +1 -0
  268. package/package.json +21 -7
  269. package/schema/gsloth-config.schema.json +1921 -326
  270. package/dist/core/shell/allowlist.d.ts +0 -75
  271. package/dist/core/shell/allowlist.js +0 -187
  272. package/dist/core/shell/allowlist.js.map +0 -1
  273. package/dist/core/shell/judge.d.ts +0 -161
  274. package/dist/core/shell/judge.js +0 -261
  275. package/dist/core/shell/judge.js.map +0 -1
@@ -1,10 +1,39 @@
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 { ApprovalSubject } from '#src/core/approvals/matcher.js';
4
+ import type { RaterNegotiationRound, ShellSafetyVerdict } from '#src/core/shell/rater.js';
3
5
  import type { BaseMessage } from '@langchain/core/messages';
4
6
  import type { RunnableConfig } from '@langchain/core/runnables';
5
7
  import type { StructuredToolInterface } from '@langchain/core/tools';
6
8
  import type { IterableReadableStream } from '@langchain/core/utils/stream';
7
9
  import type { BaseCheckpointSaver } from '@langchain/langgraph';
10
+ /**
11
+ * The approval and shell types this module's own surface is written in, re-exported so a consumer
12
+ * of the root barrel can NAME each of them. Every one is the declared type of something already
13
+ * reachable here — {@link PendingToolInterrupt}'s `subject`, `safetyVerdict` and
14
+ * `negotiationRounds`, and {@link GthAgentInterface#getDeclaredMcpToolAnnotations} — so without
15
+ * them an embedder receives values it cannot type, which is precisely what writing a typed
16
+ * {@link ToolApprovalCallback} asks of it.
17
+ *
18
+ * They stay declared where they are: the matcher owns the subject it matches on and the rater owns
19
+ * the verdict it returns, so a type moved here to become exportable would sit apart from the code
20
+ * that decides it. The re-export is type-only and erases at emit, so the barrel gains no runtime
21
+ * edge.
22
+ *
23
+ * The two modules' **runtime** surface is deliberately not re-exported, including the closed
24
+ * vocabularies `RATER_OUTCOMES` and `ShellSafetyVerdictSchema` that {@link RaterOutcome} and
25
+ * {@link ShellSafetyVerdict} are derived from: a type alias resolves its referent inside the
26
+ * declaration file it is emitted into, so naming either type needs neither value in scope. Values
27
+ * stay reachable at their own deep paths (`@gaunt-sloth/core/core/shell/rater.js`,
28
+ * `@gaunt-sloth/core/core/approvals/matcher.js`) at the usual deep-path risk.
29
+ *
30
+ * `packages/core/spec/coreBarrelTypeSurface.spec.ts` pins this: it type-checks a probe against the
31
+ * built `dist/` declarations, so dropping a name here fails as a named missing export rather than
32
+ * as a later consumer's problem.
33
+ */
34
+ export type { DeclaredToolAnnotations } from '#src/core/approvals/annotations.js';
35
+ export type { ApprovalSubject, McpToolApprovalSubject, ShellApprovalSubject, ToolApprovalSubject, } from '#src/core/approvals/matcher.js';
36
+ export type { RaterNegotiationRound, RaterOutcome, ShellSafetyVerdict, } from '#src/core/shell/rater.js';
8
37
  export type Message = BaseMessage;
9
38
  export type StatusUpdateCallback = (level: StatusLevel, message: string) => void;
10
39
  /**
@@ -38,6 +67,28 @@ export interface GthRunStats {
38
67
  tokensOutput?: number;
39
68
  /** Names of tools invoked during the run (deduplicated); empty when no tools were used. */
40
69
  tools: string[];
70
+ /**
71
+ * BATCH-21 — one record per executed tool result (`ToolMessage`) observed during the run, in
72
+ * arrival order and NOT deduplicated (a tool called twice yields two records), so `gth eval`'s
73
+ * tool-RESULT assertions (`must_error` / `tool_result_json_path`) can grade what a tool
74
+ * *returned*, not just that it was called. Optional (additive): producers that predate the field
75
+ * simply omit it; {@link runStats.js finalizeRunStats} always sets it.
76
+ */
77
+ toolResults?: GthToolResult[];
78
+ }
79
+ /**
80
+ * BATCH-21 — one executed tool call's result, harvested from its `ToolMessage` by the GS2-16
81
+ * run-stats accumulator (`core/runStats.ts`). Fail-soft like everything else there: `content` is
82
+ * omitted when no text payload could be derived, and is size-capped
83
+ * ({@link runStats.js TOOL_RESULT_CONTENT_CAP}) so a giant payload can't bloat run stats.
84
+ */
85
+ export interface GthToolResult {
86
+ /** The tool that produced the result (`ToolMessage.name`). */
87
+ name: string;
88
+ /** `true` iff the result carried LangChain's real error signal (`ToolMessage.status === 'error'`). */
89
+ isError: boolean;
90
+ /** The result payload as text (a non-string payload is JSON-stringified), capped in length. */
91
+ content?: string;
41
92
  }
42
93
  /**
43
94
  * Typed events emitted by the agent's {@link GthAgentInterface#streamWithEvents} path.
@@ -66,6 +117,34 @@ export type AgentStreamEvent = {
66
117
  } | {
67
118
  type: 'tool_end';
68
119
  id: string;
120
+ } | {
121
+ /**
122
+ * TUI-C17 — one live output chunk from an EXECUTING tool (a custom/dev toolkit child
123
+ * process's stdout/stderr, or its "Executing …" announcement), surfaced through the managed
124
+ * event stream instead of raw `process.stdout` so a renderer (the Ink TUI) can fold it into
125
+ * its view-model. Emitted by the tool-output channel merge
126
+ * (see `core/toolOutputChannel.js#mergeToolOutputIntoEvents`), NOT by `processEventStream`
127
+ * itself — consumers that don't opt into the merge (e.g. the AG-UI SSE encoder) never see it
128
+ * and the toolkits keep writing to stdout for them (today's headless behaviour).
129
+ */
130
+ type: 'tool_output';
131
+ /**
132
+ * The tool call this chunk belongs to (LangChain's `ToolRunnableConfig.toolCall.id`,
133
+ * threaded through the toolkits), so a renderer can nest output under the exact call —
134
+ * TUI-C30 consumes this for per-call output previews. Optional only defensively: absent
135
+ * when the executing framework did not supply a tool call, in which case consumers should
136
+ * fall back to `name` attribution.
137
+ */
138
+ id?: string;
139
+ /** The gth tool name (e.g. `run_shell_command`, a custom tool's name). Always known. */
140
+ name: string;
141
+ /** One verbatim streamed chunk of the child's stdout/stderr (or the notice text). */
142
+ chunk: string;
143
+ /**
144
+ * True when this chunk is the "🔧 Executing …" announcement rather than child output, so
145
+ * a richer renderer (TUI-C30) can style or strip it when previewing raw output lines.
146
+ */
147
+ isNotice?: boolean;
69
148
  } | {
70
149
  type: 'tool_result';
71
150
  id: string;
@@ -108,29 +187,168 @@ export interface PendingToolInterrupt {
108
187
  name: string;
109
188
  args: Record<string, unknown>;
110
189
  /**
111
- * EXT-10when the LLM-as-judge safety gate escalated this `run_shell_command` to the human
112
- * (rather than auto-approving it), the judge's verdict is attached here so the approval surface
113
- * can show a "safety judge flagged: <reason>" notice. Absent when the judge is disabled (the
114
- * default) or when the command reached the human without going through the judge.
190
+ * [[TUI-C67]]**what kind of call this is, as the gate itself decided it** (§3.1/§4.7.5): a
191
+ * `shell` subject carrying the command, a `tool` subject, or an `mcpTool` subject carrying the
192
+ * user's own `mcpServers` key. It is the discriminator `decideToolApprovalInner` matched rules
193
+ * on, travelling to the surface so the prompt can branch on the same one.
194
+ *
195
+ * Both terminal approval surfaces used to open with *the agent wants to run a shell command*,
196
+ * which EXT-80 made false for the most common prompt in `manual` and `write` — a file write, an
197
+ * MCP call or a custom tool, each announced as a shell command.
198
+ * `core/approvals/promptHeader.ts` renders the sentence from this field for both of them;
199
+ * re-deriving the kind from {@link name} on each surface would be a second classifier, free to
200
+ * disagree with the one that gated the call.
201
+ *
202
+ * **The ACP server is currently that second classifier, and the rule above still stands.**
203
+ * `acpPermissions.ts` titles its permission request from its own `shellCommandOf`, keyed on
204
+ * {@link name} rather than on this field, which it never reads — a divergence pinned by an
205
+ * assertion in `packages/agent/spec/acpServer.spec.ts`. Closing it means reading this field there
206
+ * rather than the tool name, and that is [[TUI-C89]]'s work; do not read the divergence's
207
+ * existence as licence to re-derive the kind anywhere else.
208
+ *
209
+ * **The runner attaches it to every interrupt it hands the approval callback**, including the
210
+ * plainest case where nothing else on this interface is set. Optional only because an interrupt
211
+ * read back out of graph state (`getPendingToolInterrupts`) is assembled before any subject
212
+ * exists; a surface that renders through `promptHeader` and is handed one of those renders the
213
+ * generic tool sentence, never a wrong one.
214
+ */
215
+ subject?: ApprovalSubject;
216
+ /**
217
+ * CFG-26 — when the AI rater escalated this `run_shell_command` to the human (rather than
218
+ * approving it or bouncing it back to the model), the rater's verdict is attached here so the
219
+ * approval surface can show an "AI rater (<tier>): <reason>" notice. Absent when the rater is
220
+ * off, or when the command reached the human without being rated.
115
221
  */
116
222
  safetyVerdict?: ShellSafetyVerdict;
223
+ /**
224
+ * EXT-71 §3.2 — when this call reached the human because a declared `approvals.escalate` entry
225
+ * matched it, the entry that fired, rendered for display. It is the provenance the prompt shows:
226
+ * an escalation the user cannot trace to the line they wrote reads as the gate malfunctioning.
227
+ * Absent whenever the escalation came from the rung or the rater instead.
228
+ */
229
+ escalatedBy?: string;
230
+ /**
231
+ * EXT-71 §6 — **what a sticky choice will store**, rendered in the object form the user would
232
+ * write in a config file, e.g. `{ "type": "shell", "matcher": "exact", "pattern": "npm test" }`.
233
+ * The menu MUST show this at the moment of the choice, on every surface: the user is shown the
234
+ * thing they are agreeing to rather than a generalization of it, which is what makes the display
235
+ * honest and cheap at once.
236
+ *
237
+ * **For a tool call the stored thing is the tool, not the arguments** (§4.7.4), so this reads
238
+ * e.g. `{ "type": "mcpTool", "server": "fetcher", "matcher": "exact", "pattern": "fetch_url",
239
+ * "host": "docs.internal.example" }`. That is the one place a grant is deliberately broader than
240
+ * what the human was shown, which is why the display carries the most weight there.
241
+ *
242
+ * Absent exactly where no sticky grant is on offer — a `catastrophic` outcome (§4.2 withdraws
243
+ * the persistent grants), a rung that remembers nothing, a command that does not statically
244
+ * resolve, or a tool call naming more than one host (which has no honest single-host entry) — so
245
+ * a prompt never advertises a control that has already been withdrawn.
246
+ */
247
+ grantPreview?: string;
248
+ /**
249
+ * §6 — **the same grant in the words a menu control is written in**: `npm test` for a shell
250
+ * command, `tool gth_web_fetch (host docs.internal.example)` or `mcpTool jira/create_issue` for a
251
+ * tool call. It is what the *always approve* control names, so the control reads as
252
+ * *always approve this tool for this host* rather than as a bare key.
253
+ *
254
+ * It is rendered by `describeApprovalEntry` — the one-liner every other provenance message uses,
255
+ * including the §4.7.4 notice that later withdraws the grant. Sharing the renderer is the point:
256
+ * a menu that describes a grant differently from the notice that withdraws it is how a user stops
257
+ * trusting either.
258
+ *
259
+ * Present exactly when {@link grantPreview} is, since both are rendered from the one entry
260
+ * `recordApproval` would write.
261
+ */
262
+ grantSummary?: string;
263
+ /**
264
+ * [[TUI-C26]] §6 — **what the menu's *always reject* choice will record**, in the object form the
265
+ * user would write in a config file. The deny mirror of {@link grantPreview}, shown at the moment
266
+ * of the choice for the same reason: a control that does not say what it stores is one the user
267
+ * has to guess at.
268
+ *
269
+ * **Its availability is NOT the grant's**, and reading one off the other is the mistake this
270
+ * field exists to prevent. The matcher's own rule is *undecidable → no match on the allow side, a
271
+ * match on the deny side*, so a command the gate cannot statically resolve **can** be refused
272
+ * permanently even though it can never be allowed permanently. `always reject` also stays offered
273
+ * on a `catastrophic` verdict, where §4.2 withdraws every sticky grant: sticky refusal is safe in
274
+ * every direction, and it is only the grant that is ever made harder.
275
+ *
276
+ * Absent only where the entry grammar cannot hold one at all — today, an MCP call whose server
277
+ * could not be attributed (the grammar's `server` cannot be empty). Absent means the control is
278
+ * **not offered**, never offered-and-disabled.
279
+ */
280
+ denyPreview?: string;
281
+ /**
282
+ * §6 — the same deny entry in the words the control is written in, through the one-liner every
283
+ * other provenance message uses. Present exactly when {@link denyPreview} is, since both are
284
+ * rendered from the one entry the runner would record.
285
+ */
286
+ denySummary?: string;
287
+ /**
288
+ * [[EXT-29]] §6 — **every round of the §5 negotiation that preceded this escalation**, oldest
289
+ * first, when one did.
290
+ *
291
+ * The user is not asked to rule on the final command in isolation: *that the agent proposed
292
+ * `git reset --hard origin/main` three times unchanged, against two rejections that each told it
293
+ * what to fix, is itself the most important thing on the screen, and it is invisible if only the
294
+ * last attempt is shown.* `core/shell/negotiation.ts`'s `renderNegotiationTranscript` is the
295
+ * shared renderer, so two surfaces cannot describe one exchange two ways.
296
+ *
297
+ * Absent for every escalation that had no negotiation — `catastrophic` (§4.2 gives it no rounds
298
+ * at all), a declared `approvals.escalate` entry, an unrated rung, a tool subject.
299
+ */
300
+ negotiationRounds?: readonly RaterNegotiationRound[];
301
+ /**
302
+ * [[EXT-29]] §5.3 — **how many attempts the agent actually made**, counted since a human was last
303
+ * involved rather than since the last approved call.
304
+ *
305
+ * It is a separate number from `negotiationRounds.length` because an approved call clears the
306
+ * transcript, and a surface reading the array's length reports only the attempts that happened
307
+ * after the last one. On the escalation this was measured from, the agent proposed the same
308
+ * command five times, was refused every time, and the human was shown three — the two calls it
309
+ * made in between were `git stash` and `git status`, both approved, both erasing the rounds
310
+ * before them. Persistence is the single most decision-relevant fact this block carries, so
311
+ * under-reporting it by nearly half is not a cosmetic count.
312
+ *
313
+ * Present exactly when {@link negotiationRounds} is, and never smaller than its length.
314
+ */
315
+ negotiationAttempts?: number;
117
316
  }
118
317
  /**
119
- * Persistence scope for an `approve` decision (EXT-9 Tier-2 allow-list ergonomics):
318
+ * Persistence scope for an `approve` decision (spec §6):
120
319
  * - `once` — run this single invocation only; remember nothing (the default).
121
- * - `session` — remember the command's classified prefix for the life of this runner
122
- * instance, so flag-variants of the same operation auto-approve without re-prompting.
123
- * - `always` — additionally persist the prefix to the project allow-list
320
+ * - `session` — remember **this command**, as an `exact` entry (§3.1), for the life of this runner
321
+ * instance, so the same command stops re-prompting. A longer variant of it still asks: the menu
322
+ * never widens, and breadth is something a human writes in a config file.
323
+ * - `always` — additionally persist that entry to the project store
124
324
  * (`.gsloth/.gsloth-settings/shell-allowlist.json`) so it survives across runs.
125
325
  */
126
326
  export type ToolApprovalScope = 'once' | 'session' | 'always';
327
+ /**
328
+ * §6 — persistence scope for a `reject` decision, the *always reject* half of the escalation menu:
329
+ * - `once` — refuse this single invocation; remember nothing (the default).
330
+ * - `session` — additionally record a **deny** entry for this call, in the one entry grammar, for
331
+ * the life of this runner instance. The matcher consults it before anything else, so the next
332
+ * identical call is refused without reaching a person at all.
333
+ *
334
+ * **There is deliberately no `always`.** An `approve` can persist because there is a project store
335
+ * to persist it to; there is no persisted deny store, and inventing one is a decision about a file
336
+ * users will have to live with rather than a rendering choice. So the scopes here are the ones the
337
+ * gate can actually honour, and a control that promised more would be §6's own failure mode — an
338
+ * affordance offered and then quietly refused.
339
+ */
340
+ export type ToolRejectScope = 'once' | 'session';
127
341
  /**
128
342
  * A consumer-supplied decision on a {@link PendingToolInterrupt}: approve runs the tool,
129
343
  * reject feeds the model a tool-rejected message (with the optional reason).
130
344
  *
131
- * `approve` carries an optional {@link ToolApprovalScope}; when absent it means `once`
132
- * (backward compatible a bare `{ type: 'approve' }` still type-checks and behaves as
133
- * a single-shot approval that persists nothing).
345
+ * Each arm carries an optional scope; when absent it means `once` (backward compatible — a bare
346
+ * `{ type: 'approve' }` or `{ type: 'reject' }` still type-checks and behaves as the single-shot
347
+ * decision that persists nothing).
348
+ *
349
+ * The two scopes are **different types on purpose**. Sharing {@link ToolApprovalScope} would let a
350
+ * surface send `{ type: 'reject', scope: 'always' }`, which type-checks, promises a persistence
351
+ * nothing implements, and degrades to a session refusal with no diagnostic.
134
352
  */
135
353
  export type ToolApprovalDecision = {
136
354
  type: 'approve';
@@ -138,6 +356,7 @@ export type ToolApprovalDecision = {
138
356
  } | {
139
357
  type: 'reject';
140
358
  message?: string;
359
+ scope?: ToolRejectScope;
141
360
  };
142
361
  /**
143
362
  * Callback the {@link GthAgentRunner} invokes when a run suspends on a tool-approval
@@ -146,8 +365,62 @@ export type ToolApprovalDecision = {
146
365
  * silently hang or auto-approve.
147
366
  */
148
367
  export type ToolApprovalCallback = (pending: PendingToolInterrupt) => Promise<ToolApprovalDecision> | ToolApprovalDecision;
368
+ /**
369
+ * [[TUI-C68]] §6.1 — what the **attack banner** is about: the command whose own structure the
370
+ * rater called hostile, and the rater's explanation of what it saw. Both are untrusted text and a
371
+ * surface must paint them through `core/shell/framing`.
372
+ *
373
+ * A separate shape from {@link PendingToolInterrupt} because it is a separate question. An approval
374
+ * prompt asks *may this run*, and every control on its menu is a legitimate answer carrying a
375
+ * scope; this asks *do you believe the rating is wrong*, has exactly one way through, and offers no
376
+ * scope at all — so a surface cannot reach for a menu control that does not exist here.
377
+ */
378
+ export interface PendingAttackHalt {
379
+ /** The command the rater rated an attack, as the model wrote it. Untrusted text. */
380
+ command: string;
381
+ /** The rater's own explanation of what the command's structure showed. Untrusted text. */
382
+ reason: string;
383
+ }
384
+ /**
385
+ * §6.1 — the human's answer at the attack banner.
386
+ *
387
+ * **A string union rather than a boolean, and the polarity is deliberate.** `run-anyway` is the
388
+ * only value that runs anything; every other value — one a future surface invents, or `undefined`
389
+ * from a surface that forgot to return — stops the run. A boolean would put the irreversible answer
390
+ * one inverted comparison away, and there is nothing here worth being one typo from.
391
+ */
392
+ export type AttackHaltAnswer = 'run-anyway' | 'stop';
393
+ /**
394
+ * §6.1 — callback the {@link GthAgentRunner} invokes when the rater rates a command an `attack`, so
395
+ * an interactive surface can show the red banner and let a human type their way past it.
396
+ *
397
+ * **Absent means halt**, exactly as an absent {@link ToolApprovalCallback} means the §6.2
398
+ * non-interactive exit. A surface that never wires this — a CI run, an AG-UI server, a surface
399
+ * nobody has written yet — keeps the halt, so forgetting fails safe. It may never block a
400
+ * non-interactive run, and no timeout turns waiting into a grant: waiting is something only a wired
401
+ * surface can cause.
402
+ */
403
+ export type AttackHaltCallback = (halt: PendingAttackHalt) => Promise<AttackHaltAnswer> | AttackHaltAnswer;
404
+ /**
405
+ * GS2-95 — options for {@link GthAgentInterface#init} that name the run without changing how it
406
+ * behaves.
407
+ */
408
+ export interface GthAgentInitOptions {
409
+ /**
410
+ * The name of the command the USER typed, for the run header only (`eval`, `batch`, `workflow`,
411
+ * `gth-batch`). Supplied by the command; the agent never invents it.
412
+ *
413
+ * It is a SEPARATE input from `command` because `command` is not a label: it selects the mode
414
+ * prompt (`readModePrompt`), the per-command approvals posture and the command-specific
415
+ * filesystem config. A command that runs its work through another verb's prompt — `gth eval`
416
+ * through `ask`, `gth batch` through `exec` — must be able to say its own name without moving the
417
+ * prompt it runs under. Omitted, the header falls back to the init verb, which is the right
418
+ * answer for every command whose verb IS its name.
419
+ */
420
+ displayCommand?: string;
421
+ }
149
422
  export interface GthAgentInterface {
150
- init(command: GthCommand | undefined, configIn: GthConfig, checkpointSaver?: BaseCheckpointSaver | undefined): Promise<void>;
423
+ init(command: GthCommand | undefined, configIn: GthConfig, checkpointSaver?: BaseCheckpointSaver | undefined, options?: GthAgentInitOptions): Promise<void>;
151
424
  invoke(messages: Message[], runConfig: RunnableConfig): Promise<string>;
152
425
  stream(messages: Message[], runConfig: RunnableConfig): Promise<IterableReadableStream<string>>;
153
426
  /**
@@ -172,6 +445,26 @@ export interface GthAgentInterface {
172
445
  * approve/reject confirmation loop.
173
446
  */
174
447
  getPendingToolInterrupts?(runConfig: RunnableConfig): Promise<PendingToolInterrupt[]>;
448
+ /**
449
+ * EXT-58 (spec §4.4) — the names of the tools registered with the graph at `init`. The runner
450
+ * intersects them with the built-in summaries table to tell the rater which already-granted
451
+ * built-ins it may offer as an alternative, so a suggestion can never name a tool the model does
452
+ * not have. Optional: an agent that does not track its tools simply omits it, and the rater then
453
+ * receives no granted list (and so suggests nothing).
454
+ */
455
+ getRegisteredToolNames?(): string[];
456
+ /**
457
+ * EXT-70 (spec §4.7.1) — what the connected MCP servers declared about their own tools in their
458
+ * `tools/list` responses, keyed by the REGISTERED tool name (`mcp__<server>__<tool>`). The runner
459
+ * reads it as the `mcp` half of a declared-annotation lookup when it computes a call's effective
460
+ * annotation set.
461
+ *
462
+ * These are **claims, not credentials**: nothing here has been trusted, and an entry only becomes
463
+ * load-bearing where the user's `approvals.mcp` block believes that hint from that server.
464
+ * Optional — an agent that tracks no tools omits it, and every tool is then fail-closed, which is
465
+ * exactly what a fully distrustful configuration computes anyway.
466
+ */
467
+ getDeclaredMcpToolAnnotations?(): ReadonlyMap<string, DeclaredToolAnnotations>;
175
468
  /**
176
469
  * GS2-16 — reset the per-run analytics accumulator so the NEXT turn's token/tool totals start
177
470
  * from zero. Called by {@link GthAgentRunner} at each turn boundary (the runner is reused across
@@ -188,18 +481,58 @@ export interface GthAgentInterface {
188
481
  }
189
482
  /**
190
483
  * Factory that produces a {@link GthAgentInterface} implementation. Injected into
191
- * {@link GthAgentRunner} so embedders can swap the lean `GthLangChainAgent` (default,
192
- * in core) for a deep `GthDeepAgent` (in `@gaunt-sloth/agent`) without core ever
193
- * importing deepagents.
484
+ * {@link GthAgentRunner} so embedders can swap the lean `GthLangChainAgent` (the default, in
485
+ * core) for another graph builder without core ever importing it.
194
486
  */
195
487
  export type GthAgentFactory = (statusUpdate: StatusUpdateCallback, resolvers?: AgentResolvers) => GthAgentInterface;
196
488
  export type ToolsResolver = (config: GthConfig, command?: GthCommand) => Promise<StructuredToolInterface[]>;
197
489
  export type ToolsCleanup = () => Promise<void>;
198
490
  export type MiddlewareResolver = (middleware: any[] | undefined, config: GthConfig) => Promise<any[]>;
199
491
  export type MiddlewareCleanup = () => Promise<void>;
492
+ /**
493
+ * EXT-32 — one connected MCP server's discovery `instructions` string (from its MCP `initialize`
494
+ * handshake), paired with the server name it came from. Captured once during tool resolution and
495
+ * reused: injected (fenced + per-server-labelled) into the composed system prompt, and available
496
+ * for [[TUI-C20]]'s MCP debug tab to render the same captured text. Only servers that actually
497
+ * supplied non-empty instructions appear here.
498
+ */
499
+ export interface McpServerInstruction {
500
+ /** The configured MCP server name (the key under `config.mcpServers`). */
501
+ server: string;
502
+ /** The server-provided instructions text (trimmed, non-empty). */
503
+ instructions: string;
504
+ }
505
+ /**
506
+ * A per-server MCP connection failure captured during the most recent {@link ToolsResolver} call.
507
+ * Recorded when a configured MCP server can't be reached (connection/handshake/auth error), so the
508
+ * failure — otherwise a transient `displayWarning` that scrolls away the moment the Ink TUI takes
509
+ * over the screen — can be re-surfaced persistently in the chrome AND named in the /debug MCP tab
510
+ * (which renders per configured server and would otherwise show only a bare "no tools" line, with
511
+ * no hint that the server never connected). Mirrors {@link McpServerInstruction}.
512
+ */
513
+ export interface McpConnectionFailure {
514
+ /** The configured MCP server name (the key under `config.mcpServers`). */
515
+ server: string;
516
+ /** A concise, human-readable reason (the underlying connection error's message). */
517
+ reason: string;
518
+ }
200
519
  export interface AgentResolvers {
201
520
  resolveTools?: ToolsResolver;
202
521
  cleanupTools?: ToolsCleanup;
203
522
  resolveMiddleware?: MiddlewareResolver;
204
523
  cleanupMiddleware?: MiddlewareCleanup;
524
+ /**
525
+ * EXT-32 — the per-server MCP discovery instructions captured during the most recent
526
+ * {@link ToolsResolver} call (empty when no MCP servers are configured or none supplied
527
+ * instructions). Optional: resolvers without MCP support simply omit it, and the prompt
528
+ * composition treats an absent accessor as "no instructions" (no MCP section is emitted).
529
+ */
530
+ getMcpServerInstructions?(): McpServerInstruction[];
531
+ /**
532
+ * The per-server MCP connection failures captured during the most recent {@link ToolsResolver}
533
+ * call (empty when every configured server connected, or none is configured). Optional: resolvers
534
+ * without MCP support omit it, and callers treat an absent accessor as "no failures". Read by the
535
+ * TUI to surface a persistent notice and to annotate the /debug MCP tab.
536
+ */
537
+ getMcpConnectionFailures?(): McpConnectionFailure[];
205
538
  }
@@ -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":"AAmDA;;;;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"}
@@ -7,13 +7,22 @@
7
7
  * DB or a terminal. Every function is a pure `data -> string[]` transform (one display line per
8
8
  * element); no I/O, no colour codes.
9
9
  */
10
- import type { HistoryInsights, SessionSearchResult } from '#src/history/historyStore.js';
10
+ import type { ConversationSummary, HistoryInsights, SessionRecord, SessionSearchResult } from '#src/history/historyStore.js';
11
11
  /**
12
12
  * Render FTS search hits: a header line per hit plus its snippet (or a prompt preview when the
13
13
  * snippet is empty). Returns a friendly single line when there are none.
14
14
  */
15
15
  export declare function formatSearchResults(results: SessionSearchResult[]): string[];
16
- /** Render a recent-sessions listing: one header + prompt-preview pair per session. */
17
- export declare function formatHistoryList(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[];
18
27
  /** Render the analytics summary: totals, top tools, per-command breakdown. */
19
28
  export declare function formatInsightsSummary(insights: HistoryInsights): string[];
@@ -3,13 +3,17 @@ function oneLine(text, max = 80) {
3
3
  const s = (text ?? '').replace(/\s+/g, ' ').trim();
4
4
  return s.length > max ? s.slice(0, max - 1) + '…' : s;
5
5
  }
6
- /** A compact `#id <ts> [command] model` header line for one session. */
6
+ /** A compact `#id <ts> [command] model (conversation #cid)` header line for one turn. */
7
7
  function headerLine(r) {
8
8
  const parts = [`#${r.id}`, r.ts];
9
9
  if (r.command)
10
10
  parts.push(`[${r.command}]`);
11
11
  if (r.model)
12
12
  parts.push(r.model);
13
+ // GS2-19: a search hit resolves to the conversation it belongs to, so the reader can pull up the
14
+ // whole thread (`gth history show <cid>`). Older rows migrated from GS2-7 always have one now.
15
+ if (r.conversationId != null)
16
+ parts.push(`(conversation #${r.conversationId})`);
13
17
  return parts.join(' ');
14
18
  }
15
19
  /**
@@ -29,22 +33,60 @@ export function formatSearchResults(results) {
29
33
  }
30
34
  return lines;
31
35
  }
32
- /** Render a recent-sessions listing: one header + prompt-preview pair per session. */
33
- export function formatHistoryList(results) {
34
- if (results.length === 0) {
36
+ /**
37
+ * GS2-19 — render a conversation-grained listing: one header + last-turn preview per conversation.
38
+ * The header carries the count / timespan / last message that make the conversation the top-level
39
+ * unit (`gth history list`), replacing the old flat per-turn list.
40
+ */
41
+ export function formatConversationList(conversations) {
42
+ if (conversations.length === 0) {
35
43
  return [
36
- 'No sessions recorded yet. Enable history with `history.enabled: true` in your config.',
44
+ 'No conversations recorded yet. Enable history with `history.enabled: true` in your config.',
37
45
  ];
38
46
  }
39
47
  const lines = [];
40
- for (const r of results) {
41
- lines.push(headerLine(r));
42
- const preview = oneLine(r.prompt, 100);
48
+ for (const c of conversations) {
49
+ const parts = [`#${c.id}`];
50
+ // Timespan across the conversation's turns; a 1-turn (or not-yet-started) conversation collapses
51
+ // to a single instant, so show one timestamp rather than an `a → a` range.
52
+ if (c.firstTs && c.lastTs && c.firstTs !== c.lastTs) {
53
+ parts.push(`${c.firstTs} → ${c.lastTs}`);
54
+ }
55
+ else {
56
+ parts.push(c.lastTs ?? c.firstTs ?? c.startedTs);
57
+ }
58
+ if (c.command)
59
+ parts.push(`[${c.command}]`);
60
+ if (c.model)
61
+ parts.push(c.model);
62
+ parts.push(`(${c.turnCount} ${c.turnCount === 1 ? 'turn' : 'turns'})`);
63
+ lines.push(parts.join(' '));
64
+ const preview = oneLine(c.lastPrompt, 100);
43
65
  if (preview)
44
66
  lines.push(` ${preview}`);
45
67
  }
46
68
  return lines;
47
69
  }
70
+ /**
71
+ * GS2-19 — render one conversation's full thread (all turns in order) for `gth history show <id>`.
72
+ * Each turn shows its prompt and response preview so a search hit can be expanded into context.
73
+ */
74
+ export function formatConversationThread(turns) {
75
+ if (turns.length === 0)
76
+ return ['No turns found for that conversation.'];
77
+ const lines = [];
78
+ turns.forEach((t, i) => {
79
+ const header = t.ts ? `Turn ${i + 1} ${t.ts}` : `Turn ${i + 1}`;
80
+ lines.push(header);
81
+ const prompt = oneLine(t.prompt, 200);
82
+ if (prompt)
83
+ lines.push(` > ${prompt}`);
84
+ const response = oneLine(t.response, 200);
85
+ if (response)
86
+ lines.push(` ${response}`);
87
+ });
88
+ return lines;
89
+ }
48
90
  /** Render the analytics summary: totals, top tools, per-command breakdown. */
49
91
  export function formatInsightsSummary(insights) {
50
92
  if (insights.sessionCount === 0) {
@@ -1 +1 @@
1
- {"version":3,"file":"historyFormat.js","sourceRoot":"","sources":["../../src/history/historyFormat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"historyFormat.js","sourceRoot":"","sources":["../../src/history/historyFormat.ts"],"names":[],"mappings":"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"}