@gaunt-sloth/core 2.0.0-alpha.9 → 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.
- package/.gsloth.review.md +2 -0
- package/README.md +71 -20
- package/dist/config/colour.d.ts +38 -0
- package/dist/config/colour.js +36 -0
- package/dist/config/colour.js.map +1 -0
- package/dist/config/configDiscovery.d.ts +79 -0
- package/dist/config/configDiscovery.js +80 -0
- package/dist/config/configDiscovery.js.map +1 -0
- package/dist/config/defaults.d.ts +20 -20
- package/dist/config/defaults.js +10 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/filesystem-tools.d.ts +41 -0
- package/dist/config/filesystem-tools.js +56 -0
- package/dist/config/filesystem-tools.js.map +1 -0
- package/dist/config/loader.d.ts +171 -19
- package/dist/config/loader.js +1054 -144
- package/dist/config/loader.js.map +1 -1
- package/dist/config/mouse.d.ts +50 -0
- package/dist/config/mouse.js +44 -0
- package/dist/config/mouse.js.map +1 -0
- package/dist/config/profiles.d.ts +68 -0
- package/dist/config/profiles.js +93 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/providerKeys.d.ts +69 -0
- package/dist/config/providerKeys.js +69 -0
- package/dist/config/providerKeys.js.map +1 -0
- package/dist/config/schema.d.ts +2671 -138
- package/dist/config/schema.js +1361 -85
- package/dist/config/schema.js.map +1 -1
- package/dist/config/shell-policy.d.ts +899 -111
- package/dist/config/shell-policy.js +800 -70
- package/dist/config/shell-policy.js.map +1 -1
- package/dist/config/tool-descriptions.d.ts +211 -0
- package/dist/config/tool-descriptions.js +272 -0
- package/dist/config/tool-descriptions.js.map +1 -0
- package/dist/config/types.d.ts +352 -41
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config.d.ts +35 -1
- package/dist/config.js +16 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +28 -1
- package/dist/constants.js +28 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +166 -11
- package/dist/core/GthAbstractAgent.js +484 -45
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +543 -57
- package/dist/core/GthAgentRunner.js +1494 -140
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +117 -2
- package/dist/core/GthLangChainAgent.js +602 -29
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/approvals/annotations.d.ts +122 -0
- package/dist/core/approvals/annotations.js +137 -0
- package/dist/core/approvals/annotations.js.map +1 -0
- package/dist/core/approvals/grants.d.ts +216 -0
- package/dist/core/approvals/grants.js +469 -0
- package/dist/core/approvals/grants.js.map +1 -0
- package/dist/core/approvals/matcher.d.ts +202 -0
- package/dist/core/approvals/matcher.js +267 -0
- package/dist/core/approvals/matcher.js.map +1 -0
- package/dist/core/approvals/mcpSubjects.d.ts +40 -0
- package/dist/core/approvals/mcpSubjects.js +99 -0
- package/dist/core/approvals/mcpSubjects.js.map +1 -0
- package/dist/core/approvals/promptHeader.d.ts +28 -0
- package/dist/core/approvals/promptHeader.js +62 -0
- package/dist/core/approvals/promptHeader.js.map +1 -0
- package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
- package/dist/core/approvals/toolAnnotationSources.js +277 -0
- package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
- package/dist/core/approvals/toolHost.d.ts +46 -0
- package/dist/core/approvals/toolHost.js +108 -0
- package/dist/core/approvals/toolHost.js.map +1 -0
- package/dist/core/debugCapture.d.ts +74 -0
- package/dist/core/debugCapture.js +100 -0
- package/dist/core/debugCapture.js.map +1 -0
- package/dist/core/gthLeanAgentFactory.d.ts +4 -4
- package/dist/core/gthLeanAgentFactory.js +4 -4
- package/dist/core/launchBanner.d.ts +127 -0
- package/dist/core/launchBanner.js +414 -0
- package/dist/core/launchBanner.js.map +1 -0
- package/dist/core/modelLabel.d.ts +19 -0
- package/dist/core/modelLabel.js +26 -0
- package/dist/core/modelLabel.js.map +1 -0
- package/dist/core/plainToolIndication.d.ts +15 -0
- package/dist/core/plainToolIndication.js +174 -0
- package/dist/core/plainToolIndication.js.map +1 -0
- package/dist/core/reasoningBlocks.d.ts +65 -0
- package/dist/core/reasoningBlocks.js +103 -0
- package/dist/core/reasoningBlocks.js.map +1 -0
- package/dist/core/refusal.d.ts +53 -0
- package/dist/core/refusal.js +133 -0
- package/dist/core/refusal.js.map +1 -0
- package/dist/core/runHeader.d.ts +38 -0
- package/dist/core/runHeader.js +42 -0
- package/dist/core/runHeader.js.map +1 -0
- package/dist/core/runStats.d.ts +14 -3
- package/dist/core/runStats.js +48 -3
- package/dist/core/runStats.js.map +1 -1
- package/dist/core/shell/ShellCommandFailedError.d.ts +3 -4
- package/dist/core/shell/ShellCommandFailedError.js +3 -4
- package/dist/core/shell/ShellCommandFailedError.js.map +1 -1
- package/dist/core/shell/abstention.d.ts +88 -0
- package/dist/core/shell/abstention.js +184 -0
- package/dist/core/shell/abstention.js.map +1 -0
- package/dist/core/shell/approvalCapture.d.ts +271 -0
- package/dist/core/shell/approvalCapture.js +108 -0
- package/dist/core/shell/approvalCapture.js.map +1 -0
- package/dist/core/shell/approvalStop.d.ts +123 -0
- package/dist/core/shell/approvalStop.js +269 -0
- package/dist/core/shell/approvalStop.js.map +1 -0
- package/dist/core/shell/arity.d.ts +6 -0
- package/dist/core/shell/arity.js +20 -6
- package/dist/core/shell/arity.js.map +1 -1
- package/dist/core/shell/denylist.d.ts +11 -0
- package/dist/core/shell/denylist.js +37 -0
- package/dist/core/shell/denylist.js.map +1 -0
- package/dist/core/shell/escalationSeverity.d.ts +141 -0
- package/dist/core/shell/escalationSeverity.js +89 -0
- package/dist/core/shell/escalationSeverity.js.map +1 -0
- package/dist/core/shell/framing.d.ts +190 -0
- package/dist/core/shell/framing.js +633 -0
- package/dist/core/shell/framing.js.map +1 -0
- package/dist/core/shell/hardline.d.ts +103 -0
- package/dist/core/shell/hardline.js +780 -0
- package/dist/core/shell/hardline.js.map +1 -0
- package/dist/core/shell/negotiation.d.ts +328 -0
- package/dist/core/shell/negotiation.js +488 -0
- package/dist/core/shell/negotiation.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +44 -4
- package/dist/core/shell/normalize.js +61 -7
- package/dist/core/shell/normalize.js.map +1 -1
- package/dist/core/shell/openWorld.d.ts +263 -0
- package/dist/core/shell/openWorld.js +1188 -0
- package/dist/core/shell/openWorld.js.map +1 -0
- package/dist/core/shell/rater.d.ts +873 -0
- package/dist/core/shell/rater.js +1454 -0
- package/dist/core/shell/rater.js.map +1 -0
- package/dist/core/shell/raterModel.d.ts +41 -0
- package/dist/core/shell/raterModel.js +51 -0
- package/dist/core/shell/raterModel.js.map +1 -0
- package/dist/core/shell/raterVocabulary.d.ts +121 -0
- package/dist/core/shell/raterVocabulary.js +116 -0
- package/dist/core/shell/raterVocabulary.js.map +1 -0
- package/dist/core/shell/rejection.d.ts +69 -0
- package/dist/core/shell/rejection.js +38 -0
- package/dist/core/shell/rejection.js.map +1 -0
- package/dist/core/toolCallRepair/grammar.d.ts +41 -0
- package/dist/core/toolCallRepair/grammar.js +116 -0
- package/dist/core/toolCallRepair/grammar.js.map +1 -0
- package/dist/core/toolCallRepair/index.d.ts +2 -0
- package/dist/core/toolCallRepair/index.js +7 -0
- package/dist/core/toolCallRepair/index.js.map +1 -0
- package/dist/core/toolCallRepair/payload.d.ts +36 -0
- package/dist/core/toolCallRepair/payload.js +341 -0
- package/dist/core/toolCallRepair/payload.js.map +1 -0
- package/dist/core/toolCallRepair/promote.d.ts +45 -0
- package/dist/core/toolCallRepair/promote.js +90 -0
- package/dist/core/toolCallRepair/promote.js.map +1 -0
- package/dist/core/toolDisplay.d.ts +123 -0
- package/dist/core/toolDisplay.js +451 -0
- package/dist/core/toolDisplay.js.map +1 -0
- package/dist/core/toolOutputChannel.d.ts +95 -0
- package/dist/core/toolOutputChannel.js +165 -0
- package/dist/core/toolOutputChannel.js.map +1 -0
- package/dist/core/types.d.ts +349 -16
- package/dist/core/types.js.map +1 -1
- package/dist/history/historyFormat.d.ts +12 -3
- package/dist/history/historyFormat.js +50 -8
- package/dist/history/historyFormat.js.map +1 -1
- package/dist/history/historyStore.d.ts +77 -0
- package/dist/history/historyStore.js +173 -6
- package/dist/history/historyStore.js.map +1 -1
- package/dist/history/recordSession.d.ts +10 -1
- package/dist/history/recordSession.js +27 -0
- package/dist/history/recordSession.js.map +1 -1
- package/dist/providers/anthropic.js +12 -0
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/configurationPassthrough.d.ts +107 -0
- package/dist/providers/configurationPassthrough.js +148 -0
- package/dist/providers/configurationPassthrough.js.map +1 -0
- package/dist/providers/geminiSchemaSanitizer.d.ts +55 -0
- package/dist/providers/geminiSchemaSanitizer.js +347 -0
- package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
- package/dist/providers/geminiThinking.d.ts +60 -0
- package/dist/providers/geminiThinking.js +92 -0
- package/dist/providers/geminiThinking.js.map +1 -0
- package/dist/providers/google-genai.js +18 -1
- package/dist/providers/google-genai.js.map +1 -1
- package/dist/providers/groq.js +12 -0
- package/dist/providers/groq.js.map +1 -1
- package/dist/providers/huggingface.d.ts +25 -0
- package/dist/providers/huggingface.js +69 -0
- package/dist/providers/huggingface.js.map +1 -0
- package/dist/providers/modelCatalog.d.ts +109 -0
- package/dist/providers/modelCatalog.js +245 -0
- package/dist/providers/modelCatalog.js.map +1 -0
- package/dist/providers/modelDiscovery.d.ts +99 -5
- package/dist/providers/modelDiscovery.js +191 -35
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/ollama.d.ts +18 -4
- package/dist/providers/ollama.js +67 -37
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +34 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +26 -4
- package/dist/providers/openrouter.js +83 -26
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/vertexai.js +19 -1
- package/dist/providers/vertexai.js.map +1 -1
- package/dist/providers/xai.js +20 -0
- package/dist/providers/xai.js.map +1 -1
- package/dist/runtime/askStructured.d.ts +105 -0
- package/dist/runtime/askStructured.js +120 -0
- package/dist/runtime/askStructured.js.map +1 -0
- package/dist/runtime/conversation.d.ts +64 -0
- package/dist/runtime/conversation.js +171 -0
- package/dist/runtime/conversation.js.map +1 -0
- package/dist/runtime/singleShot.d.ts +37 -6
- package/dist/runtime/singleShot.js +113 -67
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/runtime/structuredOutput.d.ts +104 -0
- package/dist/runtime/structuredOutput.js +393 -0
- package/dist/runtime/structuredOutput.js.map +1 -0
- package/dist/utils/ProgressIndicator.d.ts +21 -0
- package/dist/utils/ProgressIndicator.js +30 -3
- package/dist/utils/ProgressIndicator.js.map +1 -1
- package/dist/utils/aiignoreUtils.js.map +1 -1
- package/dist/utils/binaryOutputUtils.js.map +1 -1
- package/dist/utils/consoleUtils.d.ts +95 -0
- package/dist/utils/consoleUtils.js +112 -2
- package/dist/utils/consoleUtils.js.map +1 -1
- package/dist/utils/crashHandler.d.ts +87 -0
- package/dist/utils/crashHandler.js +128 -0
- package/dist/utils/crashHandler.js.map +1 -0
- package/dist/utils/debugDump.d.ts +134 -0
- package/dist/utils/debugDump.js +381 -0
- package/dist/utils/debugDump.js.map +1 -0
- package/dist/utils/debugUtils.d.ts +13 -4
- package/dist/utils/debugUtils.js +36 -13
- package/dist/utils/debugUtils.js.map +1 -1
- package/dist/utils/displayWidth.d.ts +53 -0
- package/dist/utils/displayWidth.js +195 -0
- package/dist/utils/displayWidth.js.map +1 -0
- package/dist/utils/fileUtils.js.map +1 -1
- package/dist/utils/globalConfigUtils.d.ts +14 -2
- package/dist/utils/globalConfigUtils.js +22 -4
- package/dist/utils/globalConfigUtils.js.map +1 -1
- package/dist/utils/llmUtils.d.ts +39 -8
- package/dist/utils/llmUtils.js +76 -8
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/redactSecrets.d.ts +63 -0
- package/dist/utils/redactSecrets.js +286 -0
- package/dist/utils/redactSecrets.js.map +1 -0
- package/dist/utils/systemPromptNotes.d.ts +222 -0
- package/dist/utils/systemPromptNotes.js +338 -0
- package/dist/utils/systemPromptNotes.js.map +1 -0
- package/dist/utils/systemUtils.d.ts +18 -1
- package/dist/utils/systemUtils.js +38 -3
- package/dist/utils/systemUtils.js.map +1 -1
- package/dist/utils/toolMatching.d.ts +30 -0
- package/dist/utils/toolMatching.js +44 -0
- package/dist/utils/toolMatching.js.map +1 -0
- package/dist/utils/untrustedText.d.ts +86 -0
- package/dist/utils/untrustedText.js +101 -0
- package/dist/utils/untrustedText.js.map +1 -0
- package/package.json +21 -7
- package/schema/gsloth-config.schema.json +1921 -326
- package/dist/core/shell/allowlist.d.ts +0 -75
- package/dist/core/shell/allowlist.js +0 -187
- package/dist/core/shell/allowlist.js.map +0 -1
- package/dist/core/shell/judge.d.ts +0 -161
- package/dist/core/shell/judge.js +0 -261
- 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
|
|
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
|
|
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
|
|
24
|
-
*
|
|
25
|
-
* @
|
|
26
|
-
*
|
|
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,
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
93
|
+
const s = runner.getRunStats?.();
|
|
94
|
+
if (s)
|
|
95
|
+
runStats = s;
|
|
87
96
|
}
|
|
88
|
-
catch
|
|
89
|
-
|
|
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":"AACA,OAAO,EACL,qBAAqB,EACrB,OAAO,EACP,YAAY,EACZ,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,
|
|
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>;
|