@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.
- 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
|
@@ -1,75 +0,0 @@
|
|
|
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;
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module core/shell/allowlist
|
|
3
|
-
*
|
|
4
|
-
* EXT-9 Tier-2: the persisted + session-scoped allow-list engine for the opt-in
|
|
5
|
-
* `run_shell_command` tool. Once a human approves a command at `session` or `always`
|
|
6
|
-
* scope, future commands with the same classified prefix ({@link classifyCommand}) are
|
|
7
|
-
* auto-approved without re-prompting — the ergonomics improvement mature agents
|
|
8
|
-
* (opencode/openclaw/hermes) ship.
|
|
9
|
-
*
|
|
10
|
-
* SECURITY — a naive "remember the prefix" allow-list is an injection vector. Two layers
|
|
11
|
-
* guard it:
|
|
12
|
-
* 1. **Classification fail-closed** (arity.ts): any command with shell composition,
|
|
13
|
-
* substitution, or redirection returns `null` and can NEVER match, so
|
|
14
|
-
* `git checkout x; rm -rf /` does not ride an approved `git checkout *`.
|
|
15
|
-
* 2. **Safe-bin anti-widening re-validation** (here, after opencode/openclaw): a stored
|
|
16
|
-
* approval is matched ONLY if the candidate command's first non-flag operand region
|
|
17
|
-
* does not introduce a flag that *widens or redirects* the approved operation. The
|
|
18
|
-
* exact rule is documented on {@link matchesApproval} / {@link hasWideningFlag}.
|
|
19
|
-
*
|
|
20
|
-
* No module-global mutable state: the session store is a per-instance class so concurrent
|
|
21
|
-
* sessions (ACP / AG-UI multi-session) cannot stomp each other. The persisted (`always`)
|
|
22
|
-
* store is a small JSON file the runner loads once per instance.
|
|
23
|
-
*/
|
|
24
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
25
|
-
import { classifyCommand, tokenize } from '#src/core/shell/arity.js';
|
|
26
|
-
import { normalizeCommand } from '#src/core/shell/normalize.js';
|
|
27
|
-
const PERSISTED_VERSION = 1;
|
|
28
|
-
/**
|
|
29
|
-
* Flags that, if present in a candidate command but not implied by the approved prefix,
|
|
30
|
-
* could change the *operation* the human approved (point it at a different transport,
|
|
31
|
-
* exec a hook, follow an attacker-controlled URL). Approving `git clone` must not
|
|
32
|
-
* green-light `git clone --upload-pack=evil`. This list is intentionally conservative:
|
|
33
|
-
* it is the openclaw "safe-bin" idea reduced to a deny-set of operation-changing flags
|
|
34
|
-
* that commonly appear in shell-injection / supply-chain abuse.
|
|
35
|
-
*
|
|
36
|
-
* Matched against the flag token with any `=value` stripped, case-insensitively.
|
|
37
|
-
*/
|
|
38
|
-
const WIDENING_FLAGS = new Set([
|
|
39
|
-
// git: remote command / hook overrides.
|
|
40
|
-
'--upload-pack',
|
|
41
|
-
'--receive-pack',
|
|
42
|
-
'--exec',
|
|
43
|
-
'-u', // git clone -u <upload-pack>
|
|
44
|
-
'--config', // git -c is the short form; --config can inject core.sshCommand etc.
|
|
45
|
-
'-c', // git -c core.sshCommand='...' — arbitrary command execution
|
|
46
|
-
// generic "run this program" escape hatches across tools.
|
|
47
|
-
'--exec-path',
|
|
48
|
-
'-exec', // find -exec <cmd>
|
|
49
|
-
'--use-askpass',
|
|
50
|
-
'--ssh-command',
|
|
51
|
-
// package managers: lifecycle-script / arbitrary-script toggles.
|
|
52
|
-
'--unsafe-perm',
|
|
53
|
-
'--ignore-scripts=false',
|
|
54
|
-
'--allow-scripts',
|
|
55
|
-
// curl/wget style follow/exec (in case a bare binary is approved).
|
|
56
|
-
'-o', // write to arbitrary path
|
|
57
|
-
'--output',
|
|
58
|
-
'-T', // upload
|
|
59
|
-
'--upload-file',
|
|
60
|
-
]);
|
|
61
|
-
/**
|
|
62
|
-
* Decide whether a candidate argv contains a flag that *widens* the approved operation.
|
|
63
|
-
*
|
|
64
|
-
* Rule implemented (documented for the coordinator):
|
|
65
|
-
* - Re-derive the meaningful prefix from the candidate's actual argv.
|
|
66
|
-
* - Inspect every token of the candidate that is a flag (starts with `-`).
|
|
67
|
-
* - If any flag token — normalized by stripping a trailing `=value` and lowercasing —
|
|
68
|
-
* is in {@link WIDENING_FLAGS}, the command is considered a widening of the approved
|
|
69
|
-
* operation and the match is REFUSED (returns true).
|
|
70
|
-
*
|
|
71
|
-
* This is purposely a deny-list of operation-changing flags rather than an allow-list of
|
|
72
|
-
* benign flags: benign flag variants (`-b`, `--oneline`, `-la`) are exactly what we WANT
|
|
73
|
-
* to keep auto-approving, while the handful of "run-an-arbitrary-program / redirect-the-
|
|
74
|
-
* transport" flags are what an injected approval must never silently enable.
|
|
75
|
-
*/
|
|
76
|
-
export function hasWideningFlag(argv) {
|
|
77
|
-
for (const tok of argv) {
|
|
78
|
-
if (!tok.startsWith('-'))
|
|
79
|
-
continue;
|
|
80
|
-
const bare = tok.split('=', 1)[0].toLowerCase();
|
|
81
|
-
if (WIDENING_FLAGS.has(bare))
|
|
82
|
-
return true;
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* A holder of approved prefixes with set semantics. Used for both the in-memory session
|
|
88
|
-
* store and the loaded persisted store. Pure data + membership; persistence is layered on
|
|
89
|
-
* top by {@link PersistedAllowlist}.
|
|
90
|
-
*/
|
|
91
|
-
export class AllowlistStore {
|
|
92
|
-
prefixes;
|
|
93
|
-
constructor(initial = []) {
|
|
94
|
-
this.prefixes = new Set(initial);
|
|
95
|
-
}
|
|
96
|
-
has(prefix) {
|
|
97
|
-
return this.prefixes.has(prefix);
|
|
98
|
-
}
|
|
99
|
-
add(prefix) {
|
|
100
|
-
this.prefixes.add(prefix);
|
|
101
|
-
}
|
|
102
|
-
list() {
|
|
103
|
-
return [...this.prefixes];
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* The persisted (`always`) allow-list, backed by a JSON file. The path is injected (the
|
|
108
|
-
* runner resolves it via fileUtils → `.gsloth/.gsloth-settings/shell-allowlist.json`) so
|
|
109
|
-
* tests can point it at a temp dir. Loads lazily/defensively: a missing or malformed file
|
|
110
|
-
* yields an empty store rather than throwing (fail-open on READ is safe — an empty
|
|
111
|
-
* allow-list just means "prompt"; it never auto-approves anything).
|
|
112
|
-
*/
|
|
113
|
-
export class PersistedAllowlist {
|
|
114
|
-
store;
|
|
115
|
-
filePath;
|
|
116
|
-
constructor(filePath) {
|
|
117
|
-
this.filePath = filePath;
|
|
118
|
-
this.store = new AllowlistStore(PersistedAllowlist.load(filePath));
|
|
119
|
-
}
|
|
120
|
-
static load(filePath) {
|
|
121
|
-
try {
|
|
122
|
-
if (!existsSync(filePath))
|
|
123
|
-
return [];
|
|
124
|
-
const raw = readFileSync(filePath, 'utf8');
|
|
125
|
-
const parsed = JSON.parse(raw);
|
|
126
|
-
if (!parsed || !Array.isArray(parsed.prefixes))
|
|
127
|
-
return [];
|
|
128
|
-
return parsed.prefixes.filter((p) => typeof p === 'string');
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
// Corrupt/unreadable file → behave as empty (fail-closed on auto-approval).
|
|
132
|
-
return [];
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
has(prefix) {
|
|
136
|
-
return this.store.has(prefix);
|
|
137
|
-
}
|
|
138
|
-
list() {
|
|
139
|
-
return this.store.list();
|
|
140
|
-
}
|
|
141
|
-
/** Add a prefix and persist the whole set to disk. */
|
|
142
|
-
add(prefix) {
|
|
143
|
-
if (this.store.has(prefix))
|
|
144
|
-
return;
|
|
145
|
-
this.store.add(prefix);
|
|
146
|
-
this.persist();
|
|
147
|
-
}
|
|
148
|
-
persist() {
|
|
149
|
-
const file = {
|
|
150
|
-
version: PERSISTED_VERSION,
|
|
151
|
-
prefixes: this.store.list().sort(),
|
|
152
|
-
};
|
|
153
|
-
writeFileSync(this.filePath, JSON.stringify(file, null, 2) + '\n', 'utf8');
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Decide whether `command` is already approved by the given stores — the gate the runner
|
|
158
|
-
* consults BEFORE prompting the human.
|
|
159
|
-
*
|
|
160
|
-
* Returns true ONLY when ALL of the following hold:
|
|
161
|
-
* 1. {@link classifyCommand} returns a non-null classification (so composition /
|
|
162
|
-
* substitution / redirection commands can never match — anti-injection layer 1);
|
|
163
|
-
* 2. the classified prefix is present in the session OR persisted (`always`) store; AND
|
|
164
|
-
* 3. the safe-bin re-validation passes: the candidate's actual argv contains no
|
|
165
|
-
* operation-widening flag (anti-injection layer 2 — see {@link hasWideningFlag}).
|
|
166
|
-
*
|
|
167
|
-
* Anything else (unclassifiable, unknown prefix, or a widening flag) → false → the human
|
|
168
|
-
* is prompted. Fail-closed by construction.
|
|
169
|
-
*/
|
|
170
|
-
export function matchesApproval(command, stores) {
|
|
171
|
-
const classification = classifyCommand(command, normalizeCommand);
|
|
172
|
-
if (!classification)
|
|
173
|
-
return false; // layer 1: composition/redirection/substitution.
|
|
174
|
-
const approved = stores.session.has(classification.prefix) ||
|
|
175
|
-
(stores.always?.has(classification.prefix) ?? false);
|
|
176
|
-
if (!approved)
|
|
177
|
-
return false;
|
|
178
|
-
// Layer 2: re-derive argv from the normalized command and refuse if an operation-
|
|
179
|
-
// widening flag is present that the human's prefix-level approval never implied.
|
|
180
|
-
const argv = tokenize(normalizeCommand(command));
|
|
181
|
-
if (!argv)
|
|
182
|
-
return false; // unbalanced quoting → fail-closed.
|
|
183
|
-
if (hasWideningFlag(argv))
|
|
184
|
-
return false;
|
|
185
|
-
return true;
|
|
186
|
-
}
|
|
187
|
-
//# sourceMappingURL=allowlist.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"allowlist.js","sourceRoot":"","sources":["../../../src/core/shell/allowlist.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module core/shell/judge
|
|
3
|
-
*
|
|
4
|
-
* EXT-10 — LLM-as-judge bash-safety gate. An optional, opt-in pre-filter that sits *in front
|
|
5
|
-
* of* the human approval prompt for `run_shell_command` (EXT-9). It is a tiered
|
|
6
|
-
* fatigue-reducer, NOT merely a blocker: clearly-safe commands auto-approve, the rest escalate
|
|
7
|
-
* to the human, and clearly-catastrophic ones may be rejected outright. Default OFF — it costs
|
|
8
|
-
* one LLM call per command — opt-in via {@link GthDevToolsConfig.shell}'s `judge` knob.
|
|
9
|
-
*
|
|
10
|
-
* Validated prior art (both place the judge in front of the human prompt as an auto-approve
|
|
11
|
-
* fatigue-reducer): openclaw `exec-auto-reviewer.ts` and hermes-agent `approval.py` "smart" mode.
|
|
12
|
-
*
|
|
13
|
-
* Two hardening guarantees are baked in here:
|
|
14
|
-
*
|
|
15
|
-
* 1. **Prompt-injection defense.** The command is attacker-controlled text. It is normalized
|
|
16
|
-
* (reusing {@link normalizeCommand} + home-path folding) and embedded inside an XML
|
|
17
|
-
* `<command_to_evaluate>` tag, behind a preamble that states the tagged text is UNTRUSTED
|
|
18
|
-
* DATA to be analyzed, never instructions to follow. See {@link buildJudgePrompt}.
|
|
19
|
-
* 2. **Fail-closed on error.** If the LLM call throws, times out, or returns unparseable
|
|
20
|
-
* output, the verdict returned NEVER auto-approves — it is `high`/escalate. A judge failure
|
|
21
|
-
* can never silently green-light a command. See {@link FAIL_CLOSED_VERDICT}.
|
|
22
|
-
*
|
|
23
|
-
* Fail-closed-on-AMBIGUITY (when the command's target can't be statically resolved) lives in the
|
|
24
|
-
* decision mapping ({@link mapVerdictToAction}), not here, so it applies regardless of what the
|
|
25
|
-
* judge says.
|
|
26
|
-
*
|
|
27
|
-
* Mirrors the QA-3 judge substrate (`packages/review/src/middleware/reviewRateMiddleware.ts`):
|
|
28
|
-
* structured-output evaluation over `config.llm`, wrapped in try/catch.
|
|
29
|
-
*/
|
|
30
|
-
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
31
|
-
import * as z from 'zod';
|
|
32
|
-
import type { GthConfig } from '#src/config.js';
|
|
33
|
-
/**
|
|
34
|
-
* Structured verdict the judge model must return. Kept small and conservative:
|
|
35
|
-
* - `risk` is the primary tier driving the decision (low → auto-approve, medium/high → escalate).
|
|
36
|
-
* - `destructive` flags data-loss / irreversible operations (rm, drop, format, force-push, …).
|
|
37
|
-
* - `outOfScope` flags actions outside the current project/work (network exfil, system mutation,
|
|
38
|
-
* touching paths well outside cwd) — a signal to escalate even when not strictly destructive.
|
|
39
|
-
* - `reason` is one short sentence surfaced to the human when escalating.
|
|
40
|
-
*/
|
|
41
|
-
export declare const ShellSafetyVerdictSchema: z.ZodObject<{
|
|
42
|
-
risk: z.ZodEnum<{
|
|
43
|
-
low: "low";
|
|
44
|
-
medium: "medium";
|
|
45
|
-
high: "high";
|
|
46
|
-
}>;
|
|
47
|
-
destructive: z.ZodBoolean;
|
|
48
|
-
outOfScope: z.ZodBoolean;
|
|
49
|
-
reason: z.ZodString;
|
|
50
|
-
}, z.core.$strip>;
|
|
51
|
-
/**
|
|
52
|
-
* The judge's structured verdict on a single shell command.
|
|
53
|
-
*/
|
|
54
|
-
export type ShellSafetyVerdict = z.infer<typeof ShellSafetyVerdictSchema>;
|
|
55
|
-
/**
|
|
56
|
-
* The verdict returned whenever the judge cannot produce a trustworthy answer (LLM throws,
|
|
57
|
-
* times out, or returns unparseable output). Fail-closed: `high` + escalate, never auto-approve.
|
|
58
|
-
*/
|
|
59
|
-
export declare const FAIL_CLOSED_VERDICT: ShellSafetyVerdict;
|
|
60
|
-
/**
|
|
61
|
-
* Default wall-clock budget (ms) for the judge LLM call. Kept low so a slow/hung judge can't
|
|
62
|
-
* wedge the approval flow — on timeout we fail closed and escalate. Mirrors openclaw's low
|
|
63
|
-
* exec-reviewer timeout minimum.
|
|
64
|
-
*/
|
|
65
|
-
export declare const JUDGE_DEFAULT_TIMEOUT_MS = 30000;
|
|
66
|
-
/**
|
|
67
|
-
* System preamble for the judge. States the role, the untrusted-input contract (the tagged
|
|
68
|
-
* command is DATA, not instructions), and the bias toward escalation when unsure. Patterned
|
|
69
|
-
* after openclaw's `DEFAULT_EXEC_REVIEWER_SYSTEM_PROMPT` and hermes' untrusted-input framing.
|
|
70
|
-
*/
|
|
71
|
-
export declare const JUDGE_SYSTEM_PROMPT: string;
|
|
72
|
-
/**
|
|
73
|
-
* Detect whether the command invokes an interpreter on a script target AND passes an
|
|
74
|
-
* `$ALL_CAPS` shell-variable expansion in its arguments — openclaw's "script preflight". Such a
|
|
75
|
-
* command can leak environment (often secrets) into the script, so it should bias toward
|
|
76
|
-
* escalation. Lightweight heuristic over the normalized command; a positive flag is fed to the
|
|
77
|
-
* judge prompt AND forces escalation in the decision mapping.
|
|
78
|
-
*
|
|
79
|
-
* @returns true when an interpreter+script invocation also expands an ALL_CAPS env var.
|
|
80
|
-
*/
|
|
81
|
-
export declare function hasScriptEnvLeakRisk(normalizedCommand: string): boolean;
|
|
82
|
-
/**
|
|
83
|
-
* Fold an absolute home path to `~` so the judge sees a stable, less-identifying form (mirrors
|
|
84
|
-
* hermes `_normalize_command_for_detection` path folding). Best-effort: only the literal home
|
|
85
|
-
* dir prefix is folded.
|
|
86
|
-
*/
|
|
87
|
-
export declare function foldHomePath(command: string, home: string | undefined): string;
|
|
88
|
-
/**
|
|
89
|
-
* Build the messages for the judge call: a system preamble ({@link JUDGE_SYSTEM_PROMPT}) plus a
|
|
90
|
-
* human message that embeds the NORMALIZED command inside an XML `<command_to_evaluate>` tag and
|
|
91
|
-
* (optionally) notes the script-env-leak preflight flag. The command text is only ever DATA in
|
|
92
|
-
* the tag — the builder never executes or interpolates it as instructions.
|
|
93
|
-
*
|
|
94
|
-
* Exposed (and returning plain strings) so tests can assert the structure: the tag is present,
|
|
95
|
-
* the untrusted-input preamble is present, and an injection string inside the command lands
|
|
96
|
-
* inside the tag rather than being acted on.
|
|
97
|
-
*/
|
|
98
|
-
export declare function buildJudgePrompt(command: string, options?: {
|
|
99
|
-
home?: string;
|
|
100
|
-
}): {
|
|
101
|
-
system: string;
|
|
102
|
-
user: string;
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
|
-
* Vet a single shell command with the judge model and return a structured {@link ShellSafetyVerdict}.
|
|
106
|
-
*
|
|
107
|
-
* - Builds an injection-hardened, normalized prompt ({@link buildJudgePrompt}).
|
|
108
|
-
* - Calls the judge model (defaults to `config.llm`) via `withStructuredOutput(schema)`.
|
|
109
|
-
* - Races the call against {@link JUDGE_DEFAULT_TIMEOUT_MS}.
|
|
110
|
-
* - **Fail-closed:** any throw / timeout / parse failure returns {@link FAIL_CLOSED_VERDICT}
|
|
111
|
-
* (`high`/escalate), never an auto-approve.
|
|
112
|
-
*
|
|
113
|
-
* Note: this only produces a verdict; the auto-approve / escalate / reject decision (including
|
|
114
|
-
* fail-closed-on-ambiguity) is made by {@link mapVerdictToAction} in the runner.
|
|
115
|
-
*/
|
|
116
|
-
export declare function judgeShellCommand(command: string, config: GthConfig, options?: {
|
|
117
|
-
model?: BaseChatModel;
|
|
118
|
-
home?: string;
|
|
119
|
-
timeoutMs?: number;
|
|
120
|
-
}): Promise<ShellSafetyVerdict>;
|
|
121
|
-
/**
|
|
122
|
-
* The action the judge gate resolves to for a single command, BEFORE the human prompt.
|
|
123
|
-
* - `auto-approve` — clearly safe; approve once, do NOT touch the human or the allow-list.
|
|
124
|
-
* - `escalate` — fall through to the existing human approval callback (carrying the verdict).
|
|
125
|
-
* - `reject` — refuse outright without prompting (reserved for clearly-catastrophic verdicts).
|
|
126
|
-
*/
|
|
127
|
-
export type JudgeAction = 'auto-approve' | 'escalate' | 'reject';
|
|
128
|
-
/**
|
|
129
|
-
* Behaviour knobs for the decision mapping, derived from config with safe defaults.
|
|
130
|
-
*/
|
|
131
|
-
export interface JudgeDecisionOptions {
|
|
132
|
-
/** Auto-approve `low`-risk, non-ambiguous, non-flagged commands. Default true. */
|
|
133
|
-
autoApproveLow: boolean;
|
|
134
|
-
/**
|
|
135
|
-
* Reject (without prompting) a clearly-catastrophic verdict (`high` + `destructive`). Default
|
|
136
|
-
* false — keep the gate conservative; EXT-9's hardline floor already refuses truly
|
|
137
|
-
* catastrophic commands at exec time, so the judge's main jobs are auto-approve-low + escalate.
|
|
138
|
-
*/
|
|
139
|
-
blockHigh: boolean;
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Pure, testable mapping from a {@link ShellSafetyVerdict} + ambiguity to a {@link JudgeAction}.
|
|
143
|
-
*
|
|
144
|
-
* Order of precedence (fail-closed first):
|
|
145
|
-
* 1. **Fail-closed on ambiguity:** when {@link classifyCommand} returns null — the command
|
|
146
|
-
* composes / substitutes / redirects so its target can't be statically resolved — NEVER
|
|
147
|
-
* auto-approve. Escalate (or reject if `blockHigh` and the verdict is catastrophic). This is
|
|
148
|
-
* enforced regardless of what the judge said, so an unresolvable command can't be slipped
|
|
149
|
-
* through by a manipulated `low` verdict.
|
|
150
|
-
* 2. **Script-env-leak preflight:** if the (normalized) command leaks an ALL_CAPS env var into a
|
|
151
|
-
* script/interpreter, never auto-approve — escalate.
|
|
152
|
-
* 3. `blockHigh` + catastrophic (`high` + `destructive`) → reject.
|
|
153
|
-
* 4. `low` + autoApproveLow + not ambiguous + not flagged → auto-approve.
|
|
154
|
-
* 5. otherwise → escalate.
|
|
155
|
-
*
|
|
156
|
-
* @param command The raw command string (used to recompute ambiguity + preflight independently
|
|
157
|
-
* of the judge, so the gate is robust even if the judge is wrong).
|
|
158
|
-
* @param verdict The judge's verdict (or the fail-closed verdict).
|
|
159
|
-
* @param opts Behaviour knobs.
|
|
160
|
-
*/
|
|
161
|
-
export declare function mapVerdictToAction(command: string, verdict: ShellSafetyVerdict, opts: JudgeDecisionOptions): JudgeAction;
|