@gaunt-sloth/core 2.0.0-alpha.1 → 2.0.0-alpha.10
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.code.md +10 -0
- package/LICENSE +7 -0
- package/README.md +3 -4
- package/dist/config/defaults.d.ts +85 -0
- package/dist/config/defaults.js +101 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/jsonc.d.ts +12 -0
- package/dist/config/jsonc.js +41 -0
- package/dist/config/jsonc.js.map +1 -0
- package/dist/config/loader.d.ts +109 -0
- package/dist/config/loader.js +663 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +509 -0
- package/dist/config/schema.js +349 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/shell-policy.d.ts +212 -0
- package/dist/config/shell-policy.js +142 -0
- package/dist/config/shell-policy.js.map +1 -0
- package/dist/config/types.d.ts +490 -0
- package/dist/config/types.js +12 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config.d.ts +18 -647
- package/dist/config.js +15 -516
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +24 -0
- package/dist/constants.js +24 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +57 -1
- package/dist/core/GthAbstractAgent.js +171 -4
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +158 -1
- package/dist/core/GthAgentRunner.js +367 -4
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.js +94 -5
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/debugCapture.d.ts +59 -0
- package/dist/core/debugCapture.js +101 -0
- package/dist/core/debugCapture.js.map +1 -0
- package/dist/core/gthLeanAgentFactory.d.ts +9 -0
- package/dist/core/gthLeanAgentFactory.js +10 -0
- package/dist/core/gthLeanAgentFactory.js.map +1 -0
- package/dist/core/runStats.d.ts +41 -0
- package/dist/core/runStats.js +73 -0
- package/dist/core/runStats.js.map +1 -0
- package/dist/core/shell/ShellCommandFailedError.d.ts +54 -0
- package/dist/core/shell/ShellCommandFailedError.js +68 -0
- package/dist/core/shell/ShellCommandFailedError.js.map +1 -0
- package/dist/core/shell/allowlist.d.ts +75 -0
- package/dist/core/shell/allowlist.js +187 -0
- package/dist/core/shell/allowlist.js.map +1 -0
- package/dist/core/shell/arity.d.ts +75 -0
- package/dist/core/shell/arity.js +313 -0
- package/dist/core/shell/arity.js.map +1 -0
- package/dist/core/shell/judge.d.ts +161 -0
- package/dist/core/shell/judge.js +261 -0
- package/dist/core/shell/judge.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +27 -0
- package/dist/core/shell/normalize.js +53 -0
- package/dist/core/shell/normalize.js.map +1 -0
- package/dist/core/types.d.ts +104 -0
- package/dist/core/types.js.map +1 -1
- package/dist/history/historyFormat.d.ts +19 -0
- package/dist/history/historyFormat.js +85 -0
- package/dist/history/historyFormat.js.map +1 -0
- package/dist/history/historyStore.d.ts +121 -0
- package/dist/history/historyStore.js +315 -0
- package/dist/history/historyStore.js.map +1 -0
- package/dist/history/recordSession.d.ts +28 -0
- package/dist/history/recordSession.js +29 -0
- package/dist/history/recordSession.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +5 -10
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/deepseek.d.ts +1 -1
- package/dist/providers/deepseek.js +5 -10
- package/dist/providers/deepseek.js.map +1 -1
- package/dist/providers/google-genai.d.ts +1 -1
- package/dist/providers/google-genai.js +5 -10
- package/dist/providers/google-genai.js.map +1 -1
- package/dist/providers/groq.d.ts +1 -1
- package/dist/providers/groq.js +5 -10
- package/dist/providers/groq.js.map +1 -1
- package/dist/providers/modelDiscovery.d.ts +54 -1
- package/dist/providers/modelDiscovery.js +106 -8
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/ollama.d.ts +1 -1
- package/dist/providers/ollama.js +5 -15
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +5 -10
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +7 -12
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/vertexai.d.ts +1 -1
- package/dist/providers/vertexai.js +5 -10
- package/dist/providers/vertexai.js.map +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +5 -10
- package/dist/providers/xai.js.map +1 -1
- package/dist/runtime/singleShot.d.ts +5 -2
- package/dist/runtime/singleShot.js +36 -3
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/utils/fileUtils.d.ts +24 -2
- package/dist/utils/fileUtils.js +54 -12
- package/dist/utils/fileUtils.js.map +1 -1
- package/dist/utils/systemUtils.d.ts +31 -0
- package/dist/utils/systemUtils.js +38 -0
- package/dist/utils/systemUtils.js.map +1 -1
- package/package.json +16 -11
- package/schema/gsloth-config.schema.json +1579 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
2
|
+
/**
|
|
3
|
+
* The non-message parts of a `wrapModelCall` request that also shape a turn: the tool
|
|
4
|
+
* definitions, the system prompt, and the model params. Captured alongside the message
|
|
5
|
+
* history so the `/debug` panel can show the full picture of what was sent to the model.
|
|
6
|
+
*
|
|
7
|
+
* SECURITY: these are assembled from an explicit allowlist at the capture site
|
|
8
|
+
* (see {@link extractDebugRequestExtras}); the raw model instance (which can carry an
|
|
9
|
+
* `apiKey`) is never passed through here.
|
|
10
|
+
*/
|
|
11
|
+
export interface DebugRequestExtras {
|
|
12
|
+
/** The composed system prompt string sent to the model for this call. */
|
|
13
|
+
systemPrompt?: string;
|
|
14
|
+
/** The tool definitions made available for this call (name + description + JSON schema). */
|
|
15
|
+
tools?: DebugToolDef[];
|
|
16
|
+
/** Scalar model params (model id, temperature, …) — a key-free allowlist. */
|
|
17
|
+
modelParams?: Record<string, unknown>;
|
|
18
|
+
/** The tool-choice configuration for this call, if any. */
|
|
19
|
+
toolChoice?: unknown;
|
|
20
|
+
}
|
|
21
|
+
/** A single tool's debug-renderable definition. */
|
|
22
|
+
export interface DebugToolDef {
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
/** The tool's JSON-schema parameters, when resolvable. */
|
|
26
|
+
schema?: unknown;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Debug-capture sink for the TUI's `/debug` panel.
|
|
30
|
+
*
|
|
31
|
+
* A {@link DebugCapture} is an OPT-IN callback the TUI sets on a live agent after `init`
|
|
32
|
+
* (on the shared {@link import('#src/core/GthAbstractAgent.js').GthAbstractAgent} base, so
|
|
33
|
+
* both the lean and deep backends support it). When present, the agent's `wrapModelCall`
|
|
34
|
+
* middleware reports, per model call:
|
|
35
|
+
*
|
|
36
|
+
* - `request` — `request.messages: BaseMessage[]`, the real history sent to the model at
|
|
37
|
+
* call time (post-summarization / post-middleware), plus the {@link DebugRequestExtras}
|
|
38
|
+
* (tools, system prompt, model params) that also shape the turn. This is exactly what the
|
|
39
|
+
* model saw.
|
|
40
|
+
* - `response` — the resolved `AIMessage` returned by the handler, captured as a whole
|
|
41
|
+
* (resolved message, not per-chunk frames; keeps the streaming core untouched).
|
|
42
|
+
*
|
|
43
|
+
* The sink is read lazily inside the middleware (per call), so when no TUI debug panel is
|
|
44
|
+
* attached the middleware is a transparent pass-through and the normal path pays nothing.
|
|
45
|
+
*/
|
|
46
|
+
export interface DebugCapture {
|
|
47
|
+
/** The full message history sent to the model for this call, plus the request extras. */
|
|
48
|
+
onRequest?(messages: BaseMessage[], extras?: DebugRequestExtras): void;
|
|
49
|
+
/** The resolved model response for this call (an `AIMessage`). */
|
|
50
|
+
onResponse?(response: unknown): void;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Assemble the non-message request parts ({@link DebugRequestExtras}) for the `/debug`
|
|
54
|
+
* panel from a `wrapModelCall` request, defensively and key-free. Never throws (the caller
|
|
55
|
+
* already guards, but a debug sink must never break a run) and never dumps the raw model.
|
|
56
|
+
* Shared by both the lean ({@link import('#src/core/GthLangChainAgent.js').GthLangChainAgent})
|
|
57
|
+
* and deep backends so the panel behaves identically on either.
|
|
58
|
+
*/
|
|
59
|
+
export declare function extractDebugRequestExtras(request: unknown): DebugRequestExtras | undefined;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scalar model-param fields worth surfacing in the `/debug` panel. Deliberately an
|
|
3
|
+
* allowlist (NOT a whole-object dump) so no credential field (`apiKey`, `accessToken`, …)
|
|
4
|
+
* can ever leak into the rendered debug view.
|
|
5
|
+
*
|
|
6
|
+
* `streaming` is intentionally NOT here: it is the model instance's static flag, which is
|
|
7
|
+
* usually `false` even when the turn streams — the GthAgentRunner decides streaming by calling
|
|
8
|
+
* `.stream()` vs `.invoke()`, not by this property — so surfacing it just misleads.
|
|
9
|
+
*/
|
|
10
|
+
const DEBUG_MODEL_PARAM_KEYS = [
|
|
11
|
+
'model',
|
|
12
|
+
'modelName',
|
|
13
|
+
'modelId',
|
|
14
|
+
'deploymentName',
|
|
15
|
+
'temperature',
|
|
16
|
+
'topP',
|
|
17
|
+
'topK',
|
|
18
|
+
'maxTokens',
|
|
19
|
+
'maxOutputTokens',
|
|
20
|
+
'maxReasoningTokens',
|
|
21
|
+
'reasoningEffort',
|
|
22
|
+
'thinkingBudget',
|
|
23
|
+
'stop',
|
|
24
|
+
'provider',
|
|
25
|
+
];
|
|
26
|
+
/** Pull the key-free scalar model params from the (provider-specific) model instance. */
|
|
27
|
+
function extractModelParams(model) {
|
|
28
|
+
if (!model || typeof model !== 'object')
|
|
29
|
+
return undefined;
|
|
30
|
+
const src = model;
|
|
31
|
+
const out = {};
|
|
32
|
+
for (const key of DEBUG_MODEL_PARAM_KEYS) {
|
|
33
|
+
const value = src[key];
|
|
34
|
+
if (value === undefined || value === null)
|
|
35
|
+
continue;
|
|
36
|
+
// Only scalars / scalar arrays — never nested objects that could carry credentials.
|
|
37
|
+
if (typeof value === 'object' && !Array.isArray(value))
|
|
38
|
+
continue;
|
|
39
|
+
out[key] = value;
|
|
40
|
+
}
|
|
41
|
+
// `model` / `modelName` / `modelId` are langchain aliases for the same value; collapse the
|
|
42
|
+
// duplicates so the panel shows the model id once instead of two identical lines.
|
|
43
|
+
if (typeof out.model !== 'string' && typeof out.modelName === 'string') {
|
|
44
|
+
out.model = out.modelName;
|
|
45
|
+
}
|
|
46
|
+
if (out.modelName === out.model)
|
|
47
|
+
delete out.modelName;
|
|
48
|
+
if (out.modelId === out.model)
|
|
49
|
+
delete out.modelId;
|
|
50
|
+
return Object.keys(out).length > 0 ? out : undefined;
|
|
51
|
+
}
|
|
52
|
+
/** Best-effort tool definition (name + description + schema) for the debug view. */
|
|
53
|
+
function extractToolDefs(tools) {
|
|
54
|
+
if (!Array.isArray(tools) || tools.length === 0)
|
|
55
|
+
return undefined;
|
|
56
|
+
const defs = [];
|
|
57
|
+
for (const tool of tools) {
|
|
58
|
+
if (!tool || typeof tool !== 'object')
|
|
59
|
+
continue;
|
|
60
|
+
const t = tool;
|
|
61
|
+
const name = typeof t.name === 'string' ? t.name : undefined;
|
|
62
|
+
if (!name)
|
|
63
|
+
continue;
|
|
64
|
+
const description = typeof t.description === 'string' ? t.description : undefined;
|
|
65
|
+
// LangChain StructuredTools expose a Zod/JSON `schema`; some carry it on `lc_kwargs`.
|
|
66
|
+
const schema = t.schema ?? undefined;
|
|
67
|
+
defs.push({ name, description, schema });
|
|
68
|
+
}
|
|
69
|
+
return defs.length > 0 ? defs : undefined;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Assemble the non-message request parts ({@link DebugRequestExtras}) for the `/debug`
|
|
73
|
+
* panel from a `wrapModelCall` request, defensively and key-free. Never throws (the caller
|
|
74
|
+
* already guards, but a debug sink must never break a run) and never dumps the raw model.
|
|
75
|
+
* Shared by both the lean ({@link import('#src/core/GthLangChainAgent.js').GthLangChainAgent})
|
|
76
|
+
* and deep backends so the panel behaves identically on either.
|
|
77
|
+
*/
|
|
78
|
+
export function extractDebugRequestExtras(request) {
|
|
79
|
+
if (!request || typeof request !== 'object')
|
|
80
|
+
return undefined;
|
|
81
|
+
const req = request;
|
|
82
|
+
const systemMessage = req.systemMessage;
|
|
83
|
+
const systemPrompt = typeof req.systemPrompt === 'string' && req.systemPrompt
|
|
84
|
+
? req.systemPrompt
|
|
85
|
+
: typeof systemMessage?.content === 'string'
|
|
86
|
+
? systemMessage.content
|
|
87
|
+
: undefined;
|
|
88
|
+
const extras = {
|
|
89
|
+
systemPrompt,
|
|
90
|
+
tools: extractToolDefs(req.tools),
|
|
91
|
+
modelParams: extractModelParams(req.model),
|
|
92
|
+
toolChoice: req.toolChoice,
|
|
93
|
+
};
|
|
94
|
+
// Return undefined when nothing useful was captured so the renderer can show a clear empty state.
|
|
95
|
+
const hasAny = extras.systemPrompt !== undefined ||
|
|
96
|
+
extras.tools !== undefined ||
|
|
97
|
+
extras.modelParams !== undefined ||
|
|
98
|
+
extras.toolChoice !== undefined;
|
|
99
|
+
return hasAny ? extras : undefined;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=debugCapture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debugCapture.js","sourceRoot":"","sources":["../../src/core/debugCapture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GthAgentFactory } from '#src/core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The lean backend factory — a {@link GthAgentFactory} that produces a plain
|
|
4
|
+
* {@link GthLangChainAgent} (no deepagents graph). This is identical to the built-in
|
|
5
|
+
* default {@link GthAgentRunner} constructs when no factory is passed; exporting it lets
|
|
6
|
+
* `@gaunt-sloth/agent`'s {@link resolveAgentFactory} pick lean vs deep from config without
|
|
7
|
+
* duplicating the constructor call.
|
|
8
|
+
*/
|
|
9
|
+
export declare const gthLeanAgentFactory: GthAgentFactory;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GthLangChainAgent } from '#src/core/GthLangChainAgent.js';
|
|
2
|
+
/**
|
|
3
|
+
* The lean backend factory — a {@link GthAgentFactory} that produces a plain
|
|
4
|
+
* {@link GthLangChainAgent} (no deepagents graph). This is identical to the built-in
|
|
5
|
+
* default {@link GthAgentRunner} constructs when no factory is passed; exporting it lets
|
|
6
|
+
* `@gaunt-sloth/agent`'s {@link resolveAgentFactory} pick lean vs deep from config without
|
|
7
|
+
* duplicating the constructor call.
|
|
8
|
+
*/
|
|
9
|
+
export const gthLeanAgentFactory = (statusUpdate, resolvers) => new GthLangChainAgent(statusUpdate, resolvers);
|
|
10
|
+
//# sourceMappingURL=gthLeanAgentFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gthLeanAgentFactory.js","sourceRoot":"","sources":["../../src/core/gthLeanAgentFactory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAoB,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,CAC9E,IAAI,iBAAiB,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* GS2-16 — pure, fail-soft helpers that harvest per-run analytics (token usage + invoked tool
|
|
4
|
+
* names) from LangChain messages, so the opt-in history recorder can populate `gth insights`
|
|
5
|
+
* with real numbers instead of zeros.
|
|
6
|
+
*
|
|
7
|
+
* The extraction is deliberately structural and defensive (duck-typed reads guarded by a
|
|
8
|
+
* try/catch) rather than `instanceof`-based: the same accumulator serves both the non-streaming
|
|
9
|
+
* `invoke` path (a full `messages[]` from graph state) and the streaming paths (individual
|
|
10
|
+
* message chunks / `ToolMessage`s as they arrive), across providers whose message shapes vary.
|
|
11
|
+
* Nothing here may throw into a run — a missing/odd field just means that datum is skipped.
|
|
12
|
+
*/
|
|
13
|
+
import type { GthRunStats } from '#src/core/types.js';
|
|
14
|
+
/** Mutable tally behind {@link finalizeRunStats}; see {@link createRunStatsAccumulator}. */
|
|
15
|
+
export interface RunStatsAccumulator {
|
|
16
|
+
/** Running sum of input/prompt tokens. */
|
|
17
|
+
input: number;
|
|
18
|
+
/** Running sum of output/completion tokens. */
|
|
19
|
+
output: number;
|
|
20
|
+
/** Whether ANY message reported `usage_metadata` — gates whether tokens are recorded at all. */
|
|
21
|
+
sawUsage: boolean;
|
|
22
|
+
/** Deduplicated set of invoked tool names. */
|
|
23
|
+
tools: Set<string>;
|
|
24
|
+
}
|
|
25
|
+
/** A fresh, empty accumulator. */
|
|
26
|
+
export declare function createRunStatsAccumulator(): RunStatsAccumulator;
|
|
27
|
+
/**
|
|
28
|
+
* Fold one LangChain message (or message chunk) into the accumulator. Fail-soft: any unexpected
|
|
29
|
+
* shape is swallowed so a run is never affected. Harvests, when present:
|
|
30
|
+
* - `usage_metadata.input_tokens` / `.output_tokens` (summed; marks `sawUsage`), and
|
|
31
|
+
* - tool names from an AIMessage's requested `tool_calls[].name` AND from a `ToolMessage`'s own
|
|
32
|
+
* `.name` (the executed tool), so both "requested" and "executed" tools are captured.
|
|
33
|
+
*/
|
|
34
|
+
export declare function accumulateMessage(acc: RunStatsAccumulator, message: unknown): void;
|
|
35
|
+
/** Freeze the accumulator into the public {@link GthRunStats}. Tokens omitted unless observed. */
|
|
36
|
+
export declare function finalizeRunStats(acc: RunStatsAccumulator): GthRunStats;
|
|
37
|
+
/**
|
|
38
|
+
* One-shot convenience for the non-streaming path: fold a full `messages[]` (e.g. the final graph
|
|
39
|
+
* state) into a fresh accumulator and finalize. Fail-soft (a non-iterable input yields empties).
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractRunStats(messages: unknown): GthRunStats;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** A fresh, empty accumulator. */
|
|
2
|
+
export function createRunStatsAccumulator() {
|
|
3
|
+
return { input: 0, output: 0, sawUsage: false, tools: new Set() };
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Fold one LangChain message (or message chunk) into the accumulator. Fail-soft: any unexpected
|
|
7
|
+
* shape is swallowed so a run is never affected. Harvests, when present:
|
|
8
|
+
* - `usage_metadata.input_tokens` / `.output_tokens` (summed; marks `sawUsage`), and
|
|
9
|
+
* - tool names from an AIMessage's requested `tool_calls[].name` AND from a `ToolMessage`'s own
|
|
10
|
+
* `.name` (the executed tool), so both "requested" and "executed" tools are captured.
|
|
11
|
+
*/
|
|
12
|
+
export function accumulateMessage(acc, message) {
|
|
13
|
+
try {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
const m = message;
|
|
16
|
+
if (!m || typeof m !== 'object')
|
|
17
|
+
return;
|
|
18
|
+
const usage = m.usage_metadata;
|
|
19
|
+
if (usage && typeof usage === 'object') {
|
|
20
|
+
acc.sawUsage = true;
|
|
21
|
+
if (typeof usage.input_tokens === 'number' && Number.isFinite(usage.input_tokens)) {
|
|
22
|
+
acc.input += usage.input_tokens;
|
|
23
|
+
}
|
|
24
|
+
if (typeof usage.output_tokens === 'number' && Number.isFinite(usage.output_tokens)) {
|
|
25
|
+
acc.output += usage.output_tokens;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Requested tool calls (AIMessage / AIMessageChunk). Continuation chunks in a streamed
|
|
29
|
+
// tool call carry an empty name, so guard on a non-empty string; the Set dedupes repeats.
|
|
30
|
+
const toolCalls = m.tool_calls;
|
|
31
|
+
if (Array.isArray(toolCalls)) {
|
|
32
|
+
for (const tc of toolCalls) {
|
|
33
|
+
const name = tc?.name;
|
|
34
|
+
if (typeof name === 'string' && name.length > 0)
|
|
35
|
+
acc.tools.add(name);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Executed tool result (ToolMessage). Its `.name` is the tool that produced the result.
|
|
39
|
+
const type = typeof m.getType === 'function' ? m.getType() : m._getType?.();
|
|
40
|
+
if (type === 'tool' && typeof m.name === 'string' && m.name.length > 0) {
|
|
41
|
+
acc.tools.add(m.name);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
/* fail-soft: never let stats capture affect a run */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Freeze the accumulator into the public {@link GthRunStats}. Tokens omitted unless observed. */
|
|
49
|
+
export function finalizeRunStats(acc) {
|
|
50
|
+
return {
|
|
51
|
+
tokensInput: acc.sawUsage ? acc.input : undefined,
|
|
52
|
+
tokensOutput: acc.sawUsage ? acc.output : undefined,
|
|
53
|
+
tools: [...acc.tools],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* One-shot convenience for the non-streaming path: fold a full `messages[]` (e.g. the final graph
|
|
58
|
+
* state) into a fresh accumulator and finalize. Fail-soft (a non-iterable input yields empties).
|
|
59
|
+
*/
|
|
60
|
+
export function extractRunStats(messages) {
|
|
61
|
+
const acc = createRunStatsAccumulator();
|
|
62
|
+
try {
|
|
63
|
+
if (Array.isArray(messages)) {
|
|
64
|
+
for (const m of messages)
|
|
65
|
+
accumulateMessage(acc, m);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* fail-soft */
|
|
70
|
+
}
|
|
71
|
+
return finalizeRunStats(acc);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=runStats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runStats.js","sourceRoot":"","sources":["../../src/core/runStats.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/shell/ShellCommandFailedError
|
|
3
|
+
*
|
|
4
|
+
* The typed error a `run_*` shell/dev tool throws when a command did NOT exit cleanly
|
|
5
|
+
* (non-zero exit code, or killed for exceeding the timeout). It carries the FULL model-facing
|
|
6
|
+
* body so a softening middleware can hand the model the exact observation it saw before — the
|
|
7
|
+
* only change being that the tool result's status flips to `'error'` (→ `isError` → the ✗ glyph).
|
|
8
|
+
*
|
|
9
|
+
* Canonical home is **core** so BOTH agents can recognise a shell failure without violating the
|
|
10
|
+
* `agent → core` dependency direction:
|
|
11
|
+
* - the deep agent (`code` mode, `@gaunt-sloth/agent` `GthDeepShellExitSoftening`) and
|
|
12
|
+
* - the lean agent (`exec` / `ask --write`, core `GthLangChainAgent` `GthLeanShellExitSoftening`).
|
|
13
|
+
*
|
|
14
|
+
* The throw site (`GthDevToolkit.executeCommand`) lives in the `agent` package and re-exports this
|
|
15
|
+
* class, so its `throw new ShellCommandFailedError(...)` is one and the same type both agents catch.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* A `run_*` command that did NOT exit cleanly (non-zero exit code, or was killed for exceeding the
|
|
19
|
+
* timeout). Carries the FULL model-facing body text ({@link output}) so a softening middleware can
|
|
20
|
+
* hand the model the exact same observation it saw before — the only change is the tool result's
|
|
21
|
+
* status flips to `'error'`, which drives the ✗ (`isError`) glyph.
|
|
22
|
+
*
|
|
23
|
+
* `executeCommand` previously `resolve()`d on a non-zero exit, so the LangChain `ToolMessage` stayed
|
|
24
|
+
* `status: 'success'` and every failure rendered a ✓. Throwing this typed error instead lets each
|
|
25
|
+
* agent's softening middleware convert it into an error `ToolMessage`. A clean exit (`code === 0`)
|
|
26
|
+
* still `resolve()`s; a spawn-level `child.on('error')` still rejects with a plain `Error`.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ShellCommandFailedError extends Error {
|
|
29
|
+
/** The full model-facing body (command echo + `<COMMAND_OUTPUT>` + the failure/timeout tail). */
|
|
30
|
+
readonly output: string;
|
|
31
|
+
/** The process exit code; `null` when the command was killed (timeout) and never exited cleanly. */
|
|
32
|
+
readonly exitCode: number | null;
|
|
33
|
+
/** The exact command string that was executed. */
|
|
34
|
+
readonly command: string;
|
|
35
|
+
/** The run_* tool name that invoked the command (e.g. `run_tests`, `run_shell_command`). */
|
|
36
|
+
readonly toolName: string;
|
|
37
|
+
constructor(params: {
|
|
38
|
+
output: string;
|
|
39
|
+
exitCode: number | null;
|
|
40
|
+
command: string;
|
|
41
|
+
toolName: string;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Recognise a {@link ShellCommandFailedError} for the softening middleware in either agent.
|
|
46
|
+
*
|
|
47
|
+
* Prefers a plain `instanceof` (both agents share this one core module, so the class identity is
|
|
48
|
+
* the same), but falls back to a STRUCTURAL check keyed on `name === 'ShellCommandFailedError'`
|
|
49
|
+
* plus the carried fields. The structural arm is deliberate defence against a dual-package /
|
|
50
|
+
* realm-boundary hazard: if the error ever crossed a module boundary that broke `instanceof`, we
|
|
51
|
+
* would otherwise silently rethrow a real shell failure and regress the ✗ signal. Every field the
|
|
52
|
+
* softener reads (`output`) is asserted so a narrowed value is safe to use.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isShellCommandFailedError(e: unknown): e is ShellCommandFailedError;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module core/shell/ShellCommandFailedError
|
|
3
|
+
*
|
|
4
|
+
* The typed error a `run_*` shell/dev tool throws when a command did NOT exit cleanly
|
|
5
|
+
* (non-zero exit code, or killed for exceeding the timeout). It carries the FULL model-facing
|
|
6
|
+
* body so a softening middleware can hand the model the exact observation it saw before — the
|
|
7
|
+
* only change being that the tool result's status flips to `'error'` (→ `isError` → the ✗ glyph).
|
|
8
|
+
*
|
|
9
|
+
* Canonical home is **core** so BOTH agents can recognise a shell failure without violating the
|
|
10
|
+
* `agent → core` dependency direction:
|
|
11
|
+
* - the deep agent (`code` mode, `@gaunt-sloth/agent` `GthDeepShellExitSoftening`) and
|
|
12
|
+
* - the lean agent (`exec` / `ask --write`, core `GthLangChainAgent` `GthLeanShellExitSoftening`).
|
|
13
|
+
*
|
|
14
|
+
* The throw site (`GthDevToolkit.executeCommand`) lives in the `agent` package and re-exports this
|
|
15
|
+
* class, so its `throw new ShellCommandFailedError(...)` is one and the same type both agents catch.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* A `run_*` command that did NOT exit cleanly (non-zero exit code, or was killed for exceeding the
|
|
19
|
+
* timeout). Carries the FULL model-facing body text ({@link output}) so a softening middleware can
|
|
20
|
+
* hand the model the exact same observation it saw before — the only change is the tool result's
|
|
21
|
+
* status flips to `'error'`, which drives the ✗ (`isError`) glyph.
|
|
22
|
+
*
|
|
23
|
+
* `executeCommand` previously `resolve()`d on a non-zero exit, so the LangChain `ToolMessage` stayed
|
|
24
|
+
* `status: 'success'` and every failure rendered a ✓. Throwing this typed error instead lets each
|
|
25
|
+
* agent's softening middleware convert it into an error `ToolMessage`. A clean exit (`code === 0`)
|
|
26
|
+
* still `resolve()`s; a spawn-level `child.on('error')` still rejects with a plain `Error`.
|
|
27
|
+
*/
|
|
28
|
+
export class ShellCommandFailedError extends Error {
|
|
29
|
+
/** The full model-facing body (command echo + `<COMMAND_OUTPUT>` + the failure/timeout tail). */
|
|
30
|
+
output;
|
|
31
|
+
/** The process exit code; `null` when the command was killed (timeout) and never exited cleanly. */
|
|
32
|
+
exitCode;
|
|
33
|
+
/** The exact command string that was executed. */
|
|
34
|
+
command;
|
|
35
|
+
/** The run_* tool name that invoked the command (e.g. `run_tests`, `run_shell_command`). */
|
|
36
|
+
toolName;
|
|
37
|
+
constructor(params) {
|
|
38
|
+
// Use the full body as the Error message so any generic logger/handler still surfaces the
|
|
39
|
+
// real command output rather than an opaque wrapper string.
|
|
40
|
+
super(params.output);
|
|
41
|
+
this.name = 'ShellCommandFailedError';
|
|
42
|
+
this.output = params.output;
|
|
43
|
+
this.exitCode = params.exitCode;
|
|
44
|
+
this.command = params.command;
|
|
45
|
+
this.toolName = params.toolName;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Recognise a {@link ShellCommandFailedError} for the softening middleware in either agent.
|
|
50
|
+
*
|
|
51
|
+
* Prefers a plain `instanceof` (both agents share this one core module, so the class identity is
|
|
52
|
+
* the same), but falls back to a STRUCTURAL check keyed on `name === 'ShellCommandFailedError'`
|
|
53
|
+
* plus the carried fields. The structural arm is deliberate defence against a dual-package /
|
|
54
|
+
* realm-boundary hazard: if the error ever crossed a module boundary that broke `instanceof`, we
|
|
55
|
+
* would otherwise silently rethrow a real shell failure and regress the ✗ signal. Every field the
|
|
56
|
+
* softener reads (`output`) is asserted so a narrowed value is safe to use.
|
|
57
|
+
*/
|
|
58
|
+
export function isShellCommandFailedError(e) {
|
|
59
|
+
if (e instanceof ShellCommandFailedError)
|
|
60
|
+
return true;
|
|
61
|
+
return (typeof e === 'object' &&
|
|
62
|
+
e !== null &&
|
|
63
|
+
e.name === 'ShellCommandFailedError' &&
|
|
64
|
+
typeof e.output === 'string' &&
|
|
65
|
+
typeof e.command === 'string' &&
|
|
66
|
+
typeof e.toolName === 'string');
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=ShellCommandFailedError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShellCommandFailedError.js","sourceRoot":"","sources":["../../../src/core/shell/ShellCommandFailedError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,iGAAiG;IACxF,MAAM,CAAS;IACxB,oGAAoG;IAC3F,QAAQ,CAAgB;IACjC,kDAAkD;IACzC,OAAO,CAAS;IACzB,4FAA4F;IACnF,QAAQ,CAAS;IAE1B,YAAY,MAKX;QACC,0FAA0F;QAC1F,4DAA4D;QAC5D,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAClC,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,yBAAyB,CAAC,CAAU;IAClD,IAAI,CAAC,YAAY,uBAAuB;QAAE,OAAO,IAAI,CAAC;IACtD,OAAO,CACL,OAAO,CAAC,KAAK,QAAQ;QACrB,CAAC,KAAK,IAAI;QACT,CAAwB,CAAC,IAAI,KAAK,yBAAyB;QAC5D,OAAQ,CAA0B,CAAC,MAAM,KAAK,QAAQ;QACtD,OAAQ,CAA2B,CAAC,OAAO,KAAK,QAAQ;QACxD,OAAQ,CAA4B,CAAC,QAAQ,KAAK,QAAQ,CAC3D,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ToolApprovalScope } from '#src/core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Approval persistence scopes, widest-lived last. Alias of the canonical
|
|
4
|
+
* {@link ToolApprovalScope} so the allow-list engine and the decision vocabulary stay in sync.
|
|
5
|
+
*/
|
|
6
|
+
export type ApprovalScope = ToolApprovalScope;
|
|
7
|
+
/**
|
|
8
|
+
* Decide whether a candidate argv contains a flag that *widens* the approved operation.
|
|
9
|
+
*
|
|
10
|
+
* Rule implemented (documented for the coordinator):
|
|
11
|
+
* - Re-derive the meaningful prefix from the candidate's actual argv.
|
|
12
|
+
* - Inspect every token of the candidate that is a flag (starts with `-`).
|
|
13
|
+
* - If any flag token — normalized by stripping a trailing `=value` and lowercasing —
|
|
14
|
+
* is in {@link WIDENING_FLAGS}, the command is considered a widening of the approved
|
|
15
|
+
* operation and the match is REFUSED (returns true).
|
|
16
|
+
*
|
|
17
|
+
* This is purposely a deny-list of operation-changing flags rather than an allow-list of
|
|
18
|
+
* benign flags: benign flag variants (`-b`, `--oneline`, `-la`) are exactly what we WANT
|
|
19
|
+
* to keep auto-approving, while the handful of "run-an-arbitrary-program / redirect-the-
|
|
20
|
+
* transport" flags are what an injected approval must never silently enable.
|
|
21
|
+
*/
|
|
22
|
+
export declare function hasWideningFlag(argv: string[]): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* A holder of approved prefixes with set semantics. Used for both the in-memory session
|
|
25
|
+
* store and the loaded persisted store. Pure data + membership; persistence is layered on
|
|
26
|
+
* top by {@link PersistedAllowlist}.
|
|
27
|
+
*/
|
|
28
|
+
export declare class AllowlistStore {
|
|
29
|
+
private readonly prefixes;
|
|
30
|
+
constructor(initial?: Iterable<string>);
|
|
31
|
+
has(prefix: string): boolean;
|
|
32
|
+
add(prefix: string): void;
|
|
33
|
+
list(): string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The persisted (`always`) allow-list, backed by a JSON file. The path is injected (the
|
|
37
|
+
* runner resolves it via fileUtils → `.gsloth/.gsloth-settings/shell-allowlist.json`) so
|
|
38
|
+
* tests can point it at a temp dir. Loads lazily/defensively: a missing or malformed file
|
|
39
|
+
* yields an empty store rather than throwing (fail-open on READ is safe — an empty
|
|
40
|
+
* allow-list just means "prompt"; it never auto-approves anything).
|
|
41
|
+
*/
|
|
42
|
+
export declare class PersistedAllowlist {
|
|
43
|
+
private readonly store;
|
|
44
|
+
private readonly filePath;
|
|
45
|
+
constructor(filePath: string);
|
|
46
|
+
private static load;
|
|
47
|
+
has(prefix: string): boolean;
|
|
48
|
+
list(): string[];
|
|
49
|
+
/** Add a prefix and persist the whole set to disk. */
|
|
50
|
+
add(prefix: string): void;
|
|
51
|
+
private persist;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Read-only view over the stores consulted for an auto-approval check. The runner passes
|
|
55
|
+
* its per-instance session store and (optionally) the persisted store.
|
|
56
|
+
*/
|
|
57
|
+
export interface ApprovalStores {
|
|
58
|
+
session: Pick<AllowlistStore, 'has'>;
|
|
59
|
+
always?: Pick<PersistedAllowlist, 'has'>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Decide whether `command` is already approved by the given stores — the gate the runner
|
|
63
|
+
* consults BEFORE prompting the human.
|
|
64
|
+
*
|
|
65
|
+
* Returns true ONLY when ALL of the following hold:
|
|
66
|
+
* 1. {@link classifyCommand} returns a non-null classification (so composition /
|
|
67
|
+
* substitution / redirection commands can never match — anti-injection layer 1);
|
|
68
|
+
* 2. the classified prefix is present in the session OR persisted (`always`) store; AND
|
|
69
|
+
* 3. the safe-bin re-validation passes: the candidate's actual argv contains no
|
|
70
|
+
* operation-widening flag (anti-injection layer 2 — see {@link hasWideningFlag}).
|
|
71
|
+
*
|
|
72
|
+
* Anything else (unclassifiable, unknown prefix, or a widening flag) → false → the human
|
|
73
|
+
* is prompted. Fail-closed by construction.
|
|
74
|
+
*/
|
|
75
|
+
export declare function matchesApproval(command: string, stores: ApprovalStores): boolean;
|