@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
@@ -2,10 +2,11 @@ import { defaultStatusCallback, display, displayError, displaySuccess, flushSess
2
2
  import { getCommandOutputFilePath } from '#src/utils/fileUtils.js';
3
3
  import { GthAgentRunner } from '#src/core/GthAgentRunner.js';
4
4
  import { MemorySaver } from '@langchain/langgraph';
5
- import { HumanMessage, SystemMessage } from '@langchain/core/messages';
5
+ import { HumanMessage } from '@langchain/core/messages';
6
6
  import { ProgressIndicator } from '#src/utils/ProgressIndicator.js';
7
7
  import { recordSessionSafe } from '#src/history/recordSession.js';
8
- import { getProjectDir } from '#src/utils/systemUtils.js';
8
+ import { getProjectDir, stdout } from '#src/utils/systemUtils.js';
9
+ import { ApprovalStopError, approvalStopRows } from '#src/core/shell/approvalStop.js';
9
10
  /**
10
11
  * Ask a question and get an answer from the LLM.
11
12
  *
@@ -14,82 +15,127 @@ import { getProjectDir } from '#src/utils/systemUtils.js';
14
15
  * is forwarded to the agent so it can pick the right mode prompt (e.g. exec-mode for `exec`).
15
16
  *
16
17
  * @param source - The source of the question (used for file naming)
17
- * @param preamble - The preamble to send to the LLM
18
+ * @param _preamble - Deprecated/ignored (BATCH-13): the agent composes the system prompt itself;
19
+ * see the body comment. Retained positionally so existing callers need no change.
18
20
  * @param content - The content of the question
19
21
  * @param config - The resolved config
20
22
  * @param resolvers - Optional agent resolvers (tools/middleware)
21
23
  * @param command - The originating command (defaults to `ask`); selects the agent mode prompt
22
24
  * @param agentFactory - Optional backend factory (B5). When omitted the runner uses its built-in
23
- * lean {@link GthLangChainAgent} default (unchanged behavior for existing callers). The app
24
- * layer passes `resolveAgentFactory(config, 'lean')` so an explicit `agent.backend` is honored.
25
- * @returns `true` when the run completed without error, `false` when it failed (so callers
26
- * such as `exec` can set a non-zero exit code).
25
+ * lean {@link GthLangChainAgent} default. The app layer passes `resolveAgentFactory(config,
26
+ * 'lean')`, which resolves to the same agent through the shared backend seam.
27
+ * @param options - GS2-95: `displayCommand` names the run in the header when the caller's own name
28
+ * differs from the `command` it runs under (`gth eval` runs cases in `ask` mode). Header only —
29
+ * it never reaches the mode prompt.
30
+ * @returns A {@link SingleShotResult}: `ok` is `true` when the run completed without error, `false`
31
+ * when it failed (so callers such as `exec` can set a non-zero exit code); `answer`/`tokensInput`/
32
+ * `tokensOutput`/`tools` carry the SUT's answer text and run stats for callers that need them
33
+ * (e.g. `gth batch`/`gth eval`).
27
34
  */
28
- export async function runSingleShot(source, preamble, content, config, resolvers, command = 'ask', agentFactory) {
35
+ export async function runSingleShot(source,
36
+ // BATCH-13: `_preamble` is retained for signature stability but is NO LONGER injected as a
37
+ // SystemMessage. The agent (lean `GthLangChainAgent`, since GS2-21) COMPOSES the full system
38
+ // prompt itself from the config — backstory + guidelines +
39
+ // per-command mode prompt + system prompt, PLUS the model-identity (GS2-34) and MCP-instructions
40
+ // (EXT-32) notes — and hand it to `createAgent` as `systemPrompt`. Also passing this preamble as a
41
+ // leading SystemMessage produced TWO system messages, which `@langchain/anthropic` rejects
42
+ // ("System messages are only permitted as the first passed message"), breaking EVERY single-shot
43
+ // run (ask/exec/batch/eval) on Anthropic on BOTH backends (Google/OpenAI silently merged them).
44
+ // The agent's composed prompt is a superset of this preamble, so dropping it is content-preserving.
45
+ _preamble, content, config, resolvers, command = 'ask', agentFactory, options) {
29
46
  const progressIndicator = config.streamOutput ? undefined : new ProgressIndicator('Thinking.');
30
- const messages = [new SystemMessage(preamble), new HumanMessage(content)];
31
- // Resolve output path and initialize session logging if enabled
32
- const filePath = getCommandOutputFilePath(config, source);
33
- if (filePath) {
34
- initSessionLogging(filePath, config.streamSessionInferenceLog);
35
- }
36
- // Run via Agent Runner (consistent with interactive session)
37
- const runner = new GthAgentRunner(defaultStatusCallback, resolvers, agentFactory);
38
- let succeeded = true;
39
- let responseText = '';
40
- const startedAt = Date.now();
41
47
  try {
42
- await runner.init(command, config, new MemorySaver());
43
- responseText = await runner.processMessages(messages);
44
- }
45
- catch (err) {
46
- succeeded = false;
47
- displayError(`Failed to get answer: ${err instanceof Error ? err.message : String(err)}`);
48
- }
49
- finally {
50
- await runner.cleanup();
51
- }
52
- // GS2-16: live token usage + invoked tool names for this run (fail-soft; empty when the
53
- // provider reported no usage / the runner has no stats). Read post-cleanup — the runner
54
- // snapshots stats at cleanup() so this still reflects the finished run.
55
- let runStats = { tools: [] };
56
- try {
57
- const s = runner.getRunStats?.();
58
- if (s)
59
- runStats = s;
60
- }
61
- catch {
62
- /* fail-soft: analytics must never affect this run */
63
- }
64
- // GS2-7 (B20): opt-in, fail-soft session history. A no-op unless `history.enabled`; never throws
65
- // (recordSessionSafe is fully guarded) so a DB problem can't abort or alter this run.
66
- // GS2-16 threads token/tool analytics; costUsd is intentionally left unset (no reliable price).
67
- recordSessionSafe(config, {
68
- command,
69
- project: getProjectDir(),
70
- model: config.modelDisplayName,
71
- prompt: content,
72
- response: responseText,
73
- tokensInput: runStats.tokensInput,
74
- tokensOutput: runStats.tokensOutput,
75
- tools: runStats.tools.length > 0 ? runStats.tools : undefined,
76
- durationMs: Date.now() - startedAt,
77
- });
78
- progressIndicator?.stop();
79
- if (config.writeOutputToFile === false) {
80
- display('\n'); // something going on in some terminals, they swallow last line of output
81
- }
82
- if (filePath) {
48
+ // Only the human turn: the agent supplies the system prompt via `createAgent({ systemPrompt })`.
49
+ const messages = [new HumanMessage(content)];
50
+ // Resolve output path and initialize session logging if enabled
51
+ const filePath = getCommandOutputFilePath(config, source);
52
+ if (filePath) {
53
+ initSessionLogging(filePath, config.streamSessionInferenceLog);
54
+ }
55
+ // Run via Agent Runner (consistent with interactive session)
56
+ const runner = new GthAgentRunner(defaultStatusCallback, resolvers, agentFactory);
57
+ let succeeded = true;
58
+ let responseText = '';
59
+ const startedAt = Date.now();
60
+ try {
61
+ await runner.init(command, config, new MemorySaver(), {
62
+ displayCommand: options?.displayCommand,
63
+ });
64
+ responseText = await runner.processMessages(messages);
65
+ }
66
+ catch (err) {
67
+ succeeded = false;
68
+ // [[TUI-C71]] §6.2's escalation and §4.2's halt reach a person HERE and nowhere else on
69
+ // this path: there is no prompt to attach anything to, so this message is the whole
70
+ // explanation and it is built almost entirely out of model-authored text. Its untrusted
71
+ // halves go through the [[TUI-C26]] gutter, one row per line, so a payload crafted to forge
72
+ // terminal chrome cannot reach column 0 even on a line the terminal would have wrapped. The
73
+ // rows are printed on the same channel as the wrapper so a redirected stderr still carries
74
+ // the whole thing.
75
+ if (err instanceof ApprovalStopError) {
76
+ displayError('Failed to get answer:');
77
+ for (const row of approvalStopRows(err.parts, { columns: stdout.columns })) {
78
+ displayError(row);
79
+ }
80
+ }
81
+ else {
82
+ displayError(`Failed to get answer: ${err instanceof Error ? err.message : String(err)}`);
83
+ }
84
+ }
85
+ finally {
86
+ await runner.cleanup();
87
+ }
88
+ // GS2-16: live token usage + invoked tool names for this run (fail-soft; empty when the
89
+ // provider reported no usage / the runner has no stats). Read post-cleanup — the runner
90
+ // snapshots stats at cleanup() so this still reflects the finished run.
91
+ let runStats = { tools: [] };
83
92
  try {
84
- flushSessionLog();
85
- stopSessionLogging();
86
- displaySuccess(`\n\nThis report can be found in ${filePath}`);
93
+ const s = runner.getRunStats?.();
94
+ if (s)
95
+ runStats = s;
87
96
  }
88
- catch (error) {
89
- displayError(`Failed to write answer to file: ${filePath}`);
90
- displayError(error instanceof Error ? error.message : String(error));
97
+ catch {
98
+ /* fail-soft: analytics must never affect this run */
91
99
  }
100
+ // GS2-7 (B20): opt-in, fail-soft session history. A no-op unless `history.enabled`; never throws
101
+ // (recordSessionSafe is fully guarded) so a DB problem can't abort or alter this run.
102
+ // GS2-16 threads token/tool analytics; costUsd is intentionally left unset (no reliable price).
103
+ recordSessionSafe(config, {
104
+ command,
105
+ project: getProjectDir(),
106
+ model: config.modelDisplayName,
107
+ prompt: content,
108
+ response: responseText,
109
+ tokensInput: runStats.tokensInput,
110
+ tokensOutput: runStats.tokensOutput,
111
+ tools: runStats.tools.length > 0 ? runStats.tools : undefined,
112
+ durationMs: Date.now() - startedAt,
113
+ });
114
+ progressIndicator?.stop();
115
+ if (config.writeOutputToFile === false) {
116
+ display('\n'); // something going on in some terminals, they swallow last line of output
117
+ }
118
+ if (filePath) {
119
+ try {
120
+ flushSessionLog();
121
+ stopSessionLogging();
122
+ displaySuccess(`\n\nThis report can be found in ${filePath}`);
123
+ }
124
+ catch (error) {
125
+ displayError(`Failed to write answer to file: ${filePath}`);
126
+ displayError(error instanceof Error ? error.message : String(error));
127
+ }
128
+ }
129
+ return { ok: succeeded, answer: responseText, ...runStats };
130
+ }
131
+ finally {
132
+ // EXT-53: the indicator owns a 1s setInterval — an active libuv handle that keeps Node's event
133
+ // loop from ever draining, so leaking it hangs the CLI forever after the work is done. The
134
+ // `stop()` above sits where it does for output ordering (before the trailing newline / the
135
+ // "report can be found in …" line); this `finally` guarantees the handle is also released when
136
+ // anything above throws — notably `runner.cleanup()`, which is outside the inner catch.
137
+ // `stop()` is idempotent, so the normal path's second call is a no-op.
138
+ progressIndicator?.stop();
92
139
  }
93
- return succeeded;
94
140
  }
95
141
  //# sourceMappingURL=singleShot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"singleShot.js","sourceRoot":"","sources":["../../src/runtime/singleShot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"singleShot.js","sourceRoot":"","sources":["../../src/runtime/singleShot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @module runtime/structuredOutput
3
+ *
4
+ * EXT-88 — the **`withStructuredOutput` boundary**. Every `withStructuredOutput` call in this
5
+ * project goes through {@link structuredOutputBoundary}: `rateShellCommand` (the approvals rater),
6
+ * {@link askStructured} (arbitrary caller schemas, including `gth workflow` scripts) and
7
+ * `@gaunt-sloth/batch`'s eval judge. Review's rating step is deliberately not one of them — it
8
+ * reaches its schema through a bound TOOL, which is a different path with different rules.
9
+ *
10
+ * ## The problem it exists to solve
11
+ *
12
+ * A single Zod object normally does two jobs at once — it is converted into the JSON Schema the
13
+ * provider is sent, and it validates the answer that comes back. Those two jobs want **opposite**
14
+ * things from an optional field, and Zod has only one knob for both:
15
+ *
16
+ * - **On the wire**, OpenAI's strict `json_schema` rule is *"`required` must be supplied and must
17
+ * include every key in `properties`"*. A `.optional()` field is left out of `required`, and the
18
+ * OpenAI API rejects the whole request with `400 Invalid schema for response_format`. Optionality
19
+ * there is spelled as a **nullable type on a required key**, not as an absent key.
20
+ * - **On the way back**, providers do not agree. Some (ChatGroq) rewrite the schema themselves so
21
+ * every property is required-and-nullable, and their models answer `null`. Others (Anthropic,
22
+ * Google GenAI, Ollama) leave the field genuinely optional and their models may simply omit it.
23
+ * Zod's `.optional()` admits `undefined` and **not** `null`, so the first group's answer is
24
+ * rejected by the very schema that asked for it — and the caller sees a parse failure that looks
25
+ * like a bad model rather than a self-contradicting request.
26
+ *
27
+ * ## The shape that satisfies both
28
+ *
29
+ * For every field the caller declared `.optional()`, the boundary sends
30
+ * `inner.nullable().prefault(null)` instead. That one node is the wire/validation twin:
31
+ *
32
+ * - **Wire** — `prefault` does not change the parsed *output* type, so the key lands in `required`
33
+ * and its type is `["string","null"]` / `anyOf: [T, null]`. Strict mode is satisfied, and the
34
+ * model is given a legal way to say "nothing here". Unlike `.default()`, `prefault` emits **no
35
+ * `default` keyword** into the JSON Schema, so nothing extra is added to what the provider sees.
36
+ * - **Validation** — the same node accepts the value, accepts `null`, and accepts the key being
37
+ * **missing** (which is what `prefault` supplies the `null` for). All three arms are needed: the
38
+ * third is what keeps the providers that do *not* hoist working.
39
+ *
40
+ * The `null` never escapes: {@link StructuredOutputBoundary.safeParse} strips it back to the key
41
+ * being **absent** and then validates with the caller's **original** schema, so the parsed type is
42
+ * exactly what the caller declared — an optional string stays `string | undefined` and never
43
+ * becomes `string | null | undefined`.
44
+ *
45
+ * ## Two properties that must not be lost in a "simplification"
46
+ *
47
+ * - **Every `.describe()` survives into the emitted JSON Schema.** The descriptions are the only
48
+ * place the model is told what the fields mean. Expressing the null-to-absent step with
49
+ * `.transform()` on the field is the obvious-looking alternative and it costs the field its
50
+ * description — worse, the conversion OpenAI's path uses refuses to represent a transform at all.
51
+ * Normalization therefore happens in **code**, after the parse, never in the schema.
52
+ * - **A genuinely malformed answer still fails.** This boundary removes a *false* parse failure; it
53
+ * is not a blanket "accept anything". A wrong type in an optional field is still rejected, because
54
+ * the final validation is the caller's own untouched schema. In particular `.catch()` must not be
55
+ * used to express any of this — it would swallow real failures.
56
+ *
57
+ * ## Coverage, and the deliberate limits
58
+ *
59
+ * The rewrite descends through objects, arrays, tuples, records and the single-child wrappers
60
+ * (`nullable`, `readonly`, `default`, `prefault`, `nonoptional`), so an optional nested inside an
61
+ * object inside an array is handled.
62
+ *
63
+ * It deliberately stops at **unions** (including discriminated unions), **intersections**, `lazy`,
64
+ * `map`, `set`, `pipe`/`transform` and `custom`, leaving those subtrees exactly as the caller wrote
65
+ * them, and it leaves a **recursive** schema alone in its entirety. The reason is one rule: the
66
+ * rewrite and the normalization must cover **precisely the same set**. At a union the incoming value
67
+ * gives no reliable answer to *which branch was taken*, so a `null` could not be stripped back out;
68
+ * rewriting there while being unable to normalize would manufacture the very parse failure this
69
+ * module removes. Recursion is refused for the neighbouring reason — a rewrite that stopped at the
70
+ * cycle would make a key required at one depth and optional at the next, which satisfies no
71
+ * provider's strict rule and is harder to reason about than the caller's own schema.
72
+ *
73
+ * An optional inside one of those constructs keeps today's behaviour — correct everywhere it is
74
+ * correct today, and still rejected by OpenAI's strict rule, which is a visible error rather than a
75
+ * silent one.
76
+ */
77
+ import * as z from 'zod';
78
+ /**
79
+ * The two halves of one structured-output call: what to send, and how to read the answer.
80
+ *
81
+ * @typeParam T The caller's own parsed type — unchanged by the boundary.
82
+ */
83
+ export interface StructuredOutputBoundary<T> {
84
+ /**
85
+ * The schema to hand to `model.withStructuredOutput(...)`. Identical (by reference) to the
86
+ * caller's schema when it contains no optional field, so a schema that never had the problem is
87
+ * sent byte-for-byte as it is today.
88
+ */
89
+ wireSchema: z.ZodType<Record<string, unknown>>;
90
+ /**
91
+ * Validate a model answer: normalize every `null` that stands for "no value" back to the key being
92
+ * absent, then validate with the caller's **original** schema. Never throws — a malformed answer
93
+ * comes back as `success: false`, exactly as `schema.safeParse` would.
94
+ */
95
+ safeParse(raw: unknown): z.ZodSafeParseResult<T>;
96
+ }
97
+ /**
98
+ * Build the {@link StructuredOutputBoundary} for a schema — the one entry point every
99
+ * `withStructuredOutput` call in this project goes through. See the module doc for what it does and
100
+ * why.
101
+ *
102
+ * @param schema The caller's schema, used unchanged for the final validation.
103
+ */
104
+ export declare function structuredOutputBoundary<T>(schema: z.ZodType<T>): StructuredOutputBoundary<T>;