@gaunt-sloth/core 2.0.0-alpha.2 → 2.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/.gsloth.code.md +10 -0
  2. package/README.md +6 -6
  3. package/dist/config/defaults.d.ts +85 -0
  4. package/dist/config/defaults.js +103 -0
  5. package/dist/config/defaults.js.map +1 -0
  6. package/dist/config/jsonc.d.ts +12 -0
  7. package/dist/config/jsonc.js +41 -0
  8. package/dist/config/jsonc.js.map +1 -0
  9. package/dist/config/loader.d.ts +169 -0
  10. package/dist/config/loader.js +872 -0
  11. package/dist/config/loader.js.map +1 -0
  12. package/dist/config/schema.d.ts +673 -0
  13. package/dist/config/schema.js +524 -0
  14. package/dist/config/schema.js.map +1 -0
  15. package/dist/config/shell-policy.d.ts +331 -0
  16. package/dist/config/shell-policy.js +238 -0
  17. package/dist/config/shell-policy.js.map +1 -0
  18. package/dist/config/types.d.ts +596 -0
  19. package/dist/config/types.js +13 -0
  20. package/dist/config/types.js.map +1 -0
  21. package/dist/config.d.ts +18 -647
  22. package/dist/config.js +15 -516
  23. package/dist/config.js.map +1 -1
  24. package/dist/constants.d.ts +42 -0
  25. package/dist/constants.js +42 -0
  26. package/dist/constants.js.map +1 -1
  27. package/dist/core/GthAbstractAgent.d.ts +85 -2
  28. package/dist/core/GthAbstractAgent.js +398 -28
  29. package/dist/core/GthAbstractAgent.js.map +1 -1
  30. package/dist/core/GthAgentRunner.d.ts +158 -1
  31. package/dist/core/GthAgentRunner.js +367 -4
  32. package/dist/core/GthAgentRunner.js.map +1 -1
  33. package/dist/core/GthLangChainAgent.js +193 -16
  34. package/dist/core/GthLangChainAgent.js.map +1 -1
  35. package/dist/core/debugCapture.d.ts +59 -0
  36. package/dist/core/debugCapture.js +101 -0
  37. package/dist/core/debugCapture.js.map +1 -0
  38. package/dist/core/gthLeanAgentFactory.d.ts +9 -0
  39. package/dist/core/gthLeanAgentFactory.js +10 -0
  40. package/dist/core/gthLeanAgentFactory.js.map +1 -0
  41. package/dist/core/plainToolIndication.d.ts +15 -0
  42. package/dist/core/plainToolIndication.js +148 -0
  43. package/dist/core/plainToolIndication.js.map +1 -0
  44. package/dist/core/runStats.d.ts +41 -0
  45. package/dist/core/runStats.js +73 -0
  46. package/dist/core/runStats.js.map +1 -0
  47. package/dist/core/shell/ShellCommandFailedError.d.ts +54 -0
  48. package/dist/core/shell/ShellCommandFailedError.js +68 -0
  49. package/dist/core/shell/ShellCommandFailedError.js.map +1 -0
  50. package/dist/core/shell/allowlist.d.ts +75 -0
  51. package/dist/core/shell/allowlist.js +187 -0
  52. package/dist/core/shell/allowlist.js.map +1 -0
  53. package/dist/core/shell/arity.d.ts +75 -0
  54. package/dist/core/shell/arity.js +313 -0
  55. package/dist/core/shell/arity.js.map +1 -0
  56. package/dist/core/shell/judge.d.ts +161 -0
  57. package/dist/core/shell/judge.js +261 -0
  58. package/dist/core/shell/judge.js.map +1 -0
  59. package/dist/core/shell/normalize.d.ts +27 -0
  60. package/dist/core/shell/normalize.js +53 -0
  61. package/dist/core/shell/normalize.js.map +1 -0
  62. package/dist/core/toolDisplay.d.ts +101 -0
  63. package/dist/core/toolDisplay.js +374 -0
  64. package/dist/core/toolDisplay.js.map +1 -0
  65. package/dist/core/toolOutputChannel.d.ts +68 -0
  66. package/dist/core/toolOutputChannel.js +112 -0
  67. package/dist/core/toolOutputChannel.js.map +1 -0
  68. package/dist/core/types.d.ts +173 -0
  69. package/dist/core/types.js.map +1 -1
  70. package/dist/history/historyFormat.d.ts +28 -0
  71. package/dist/history/historyFormat.js +127 -0
  72. package/dist/history/historyFormat.js.map +1 -0
  73. package/dist/history/historyStore.d.ts +198 -0
  74. package/dist/history/historyStore.js +482 -0
  75. package/dist/history/historyStore.js.map +1 -0
  76. package/dist/history/recordSession.d.ts +37 -0
  77. package/dist/history/recordSession.js +56 -0
  78. package/dist/history/recordSession.js.map +1 -0
  79. package/dist/index.d.ts +4 -0
  80. package/dist/index.js +4 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/providers/anthropic.d.ts +1 -1
  83. package/dist/providers/anthropic.js +5 -10
  84. package/dist/providers/anthropic.js.map +1 -1
  85. package/dist/providers/deepseek.d.ts +1 -1
  86. package/dist/providers/deepseek.js +5 -10
  87. package/dist/providers/deepseek.js.map +1 -1
  88. package/dist/providers/geminiSchemaSanitizer.d.ts +52 -0
  89. package/dist/providers/geminiSchemaSanitizer.js +201 -0
  90. package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
  91. package/dist/providers/google-genai.d.ts +1 -1
  92. package/dist/providers/google-genai.js +9 -11
  93. package/dist/providers/google-genai.js.map +1 -1
  94. package/dist/providers/groq.d.ts +1 -1
  95. package/dist/providers/groq.js +5 -10
  96. package/dist/providers/groq.js.map +1 -1
  97. package/dist/providers/huggingface.d.ts +25 -0
  98. package/dist/providers/huggingface.js +69 -0
  99. package/dist/providers/huggingface.js.map +1 -0
  100. package/dist/providers/modelCatalog.d.ts +109 -0
  101. package/dist/providers/modelCatalog.js +245 -0
  102. package/dist/providers/modelCatalog.js.map +1 -0
  103. package/dist/providers/modelDiscovery.d.ts +111 -3
  104. package/dist/providers/modelDiscovery.js +183 -27
  105. package/dist/providers/modelDiscovery.js.map +1 -1
  106. package/dist/providers/ollama.d.ts +19 -5
  107. package/dist/providers/ollama.js +60 -52
  108. package/dist/providers/ollama.js.map +1 -1
  109. package/dist/providers/openai.d.ts +1 -1
  110. package/dist/providers/openai.js +5 -10
  111. package/dist/providers/openai.js.map +1 -1
  112. package/dist/providers/openrouter.d.ts +1 -1
  113. package/dist/providers/openrouter.js +12 -12
  114. package/dist/providers/openrouter.js.map +1 -1
  115. package/dist/providers/vertexai.d.ts +1 -1
  116. package/dist/providers/vertexai.js +9 -11
  117. package/dist/providers/vertexai.js.map +1 -1
  118. package/dist/providers/xai.d.ts +1 -1
  119. package/dist/providers/xai.js +5 -10
  120. package/dist/providers/xai.js.map +1 -1
  121. package/dist/runtime/askStructured.d.ts +62 -0
  122. package/dist/runtime/askStructured.js +76 -0
  123. package/dist/runtime/askStructured.js.map +1 -0
  124. package/dist/runtime/conversation.d.ts +59 -0
  125. package/dist/runtime/conversation.js +137 -0
  126. package/dist/runtime/conversation.js.map +1 -0
  127. package/dist/runtime/singleShot.d.ts +24 -5
  128. package/dist/runtime/singleShot.js +56 -9
  129. package/dist/runtime/singleShot.js.map +1 -1
  130. package/dist/utils/aiignoreUtils.js.map +1 -1
  131. package/dist/utils/binaryOutputUtils.js.map +1 -1
  132. package/dist/utils/consoleUtils.d.ts +22 -0
  133. package/dist/utils/consoleUtils.js +45 -0
  134. package/dist/utils/consoleUtils.js.map +1 -1
  135. package/dist/utils/debugDump.d.ts +57 -0
  136. package/dist/utils/debugDump.js +236 -0
  137. package/dist/utils/debugDump.js.map +1 -0
  138. package/dist/utils/debugUtils.d.ts +13 -4
  139. package/dist/utils/debugUtils.js +36 -13
  140. package/dist/utils/debugUtils.js.map +1 -1
  141. package/dist/utils/fileUtils.d.ts +24 -2
  142. package/dist/utils/fileUtils.js +54 -12
  143. package/dist/utils/fileUtils.js.map +1 -1
  144. package/dist/utils/llmUtils.js.map +1 -1
  145. package/dist/utils/redactSecrets.d.ts +63 -0
  146. package/dist/utils/redactSecrets.js +238 -0
  147. package/dist/utils/redactSecrets.js.map +1 -0
  148. package/dist/utils/systemPromptNotes.d.ts +181 -0
  149. package/dist/utils/systemPromptNotes.js +298 -0
  150. package/dist/utils/systemPromptNotes.js.map +1 -0
  151. package/dist/utils/systemUtils.d.ts +32 -1
  152. package/dist/utils/systemUtils.js +50 -3
  153. package/dist/utils/systemUtils.js.map +1 -1
  154. package/dist/utils/toolMatching.d.ts +30 -0
  155. package/dist/utils/toolMatching.js +44 -0
  156. package/dist/utils/toolMatching.js.map +1 -0
  157. package/package.json +17 -8
  158. package/schema/gsloth-config.schema.json +2111 -0
@@ -0,0 +1,238 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * GS2-47 — a shared, reusable secret-redaction pass for diagnostic output.
4
+ *
5
+ * Built for `/debug-dump` ({@link file://./debugDump.ts}) but deliberately generic and
6
+ * dependency-free — it takes `env` as a PARAMETER rather than reading `process.env`, so the pass is
7
+ * pure, deterministic in tests, and reusable by GS2-48's crash-report handler (which hard-depends on
8
+ * this module). Nothing here reads ambient process state.
9
+ *
10
+ * DESIGN DECISION (settled by the coordinator, GS2-47): **PATTERN-ONLY** redaction — we do NOT scan
11
+ * for high-entropy strings. Rationale: pattern-only is safe to reason about and near-zero
12
+ * false-positive; entropy scanning catches marginally more but produces false-positive redactions
13
+ * the user has to squint through, degrading the dump's debug value. A high-entropy scanner is a
14
+ * DELIBERATELY-DEFERRED option (a config surface for it could be added later if ever warranted) and
15
+ * is intentionally NOT implemented here.
16
+ *
17
+ * Three techniques, in priority order:
18
+ * 1. [load-bearing] Known-secret VALUE substitution — collect the literal values of secret-named
19
+ * env vars + inline config secrets, then substitute every occurrence across ALL artifacts. This
20
+ * catches a leaked key wherever it surfaces (config, env, transcript, log) with no guessing.
21
+ * 2. Provider key/token PATTERNS — a tight, documented, prefix-anchored set of well-known key
22
+ * shapes plus explicit auth-header contexts. We never blanket-redact long alphanumeric strings.
23
+ * 3. Sensitive config-FIELD masking — mask the VALUES of secret-named config keys while preserving
24
+ * structure (the key stays; only the value becomes the marker).
25
+ *
26
+ * Fail-safe throughout: on any internal error the functions redact MORE (return the marker), never
27
+ * less — a redaction hiccup must never cause raw content to be emitted.
28
+ */
29
+ /** The visible, greppable placeholder substituted for every redacted secret value. */
30
+ export const REDACTED = '<redacted>';
31
+ /**
32
+ * Minimum length for a collected VALUE to be treated as a substitutable secret literal (technique
33
+ * 1). Guards against redacting trivial values ("1", "on", a short model name) that happen to be the
34
+ * value of a secret-named env var — substituting those everywhere would gut the dump's debug value
35
+ * with false positives (exactly the trap the pattern-only decision avoids). Short sensitive *config*
36
+ * fields are still masked structurally by key name (technique 3), so nothing sensitive slips through
37
+ * this floor.
38
+ */
39
+ const MIN_SECRET_LITERAL_LENGTH = 6;
40
+ /** Max recursion depth for the config walks — a guard against pathological/deep object graphs. */
41
+ const MAX_REDACT_DEPTH = 12;
42
+ /**
43
+ * env-var NAME shapes whose VALUE is a secret to substitute everywhere (technique 1): `*_API_KEY`,
44
+ * `*_TOKEN`, `*_SECRET`, `*_KEY`, and anything containing `PASSWORD`. Case-insensitive.
45
+ */
46
+ const SECRET_ENV_NAME_RE = /(?:_API_KEY|_TOKEN|_SECRET|_KEY)$|PASSWORD/i;
47
+ /**
48
+ * Config-FIELD NAME shapes whose VALUE is masked in place (technique 3), matched case-insensitively
49
+ * at any depth. Mirrors `configCommand.ts`'s `redactConfigForPrint` set for cross-surface
50
+ * consistency. NOTE: `apiKeyEnvironmentVariable` also matches `api?key`, but it holds a var NAME
51
+ * (not a secret) and is what technique 1 reads to find the real key's value — so it is EXCLUDED via
52
+ * {@link NON_SECRET_KEY_NAMES}.
53
+ */
54
+ const SECRET_KEY_RE = /(api[-_]?key|secret|token|password|passwd|authorization|bearer|credential)/i;
55
+ /** Field names that match {@link SECRET_KEY_RE} but must NOT be masked (they hold a var name). */
56
+ const NON_SECRET_KEY_NAMES = new Set(['apikeyenvironmentvariable']);
57
+ /**
58
+ * Provider key / auth-header PATTERNS (technique 2) — a tight, prefix-anchored set. Each entry is
59
+ * `[regex, replacement]`. Kept deliberately narrow (well-known key prefixes + explicit auth
60
+ * contexts); we do NOT blanket-redact long alphanumeric strings — that IS the entropy trap the
61
+ * pattern-only decision avoids. A long opaque token is only redacted when it sits in an auth context
62
+ * (`Bearer …` / `Authorization: …`), never on its own.
63
+ */
64
+ const PROVIDER_PATTERNS = [
65
+ // OpenAI (`sk-…`) and Anthropic (`sk-ant-…`, also `sk-`-prefixed) secret keys.
66
+ [/\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, REDACTED],
67
+ // Google API keys (`AIza…`).
68
+ [/\bAIza[0-9A-Za-z_-]{16,}/g, REDACTED],
69
+ // xAI (`xai-…`).
70
+ [/\bxai-[A-Za-z0-9_-]{16,}/g, REDACTED],
71
+ // Groq (`gsk_…`).
72
+ [/\bgsk_[A-Za-z0-9_-]{16,}/g, REDACTED],
73
+ // GS2-54 (gap 2) — GitHub tokens. Classic/scoped PATs share the `gh[oprsu]_` prefix (personal
74
+ // `ghp_`, OAuth `gho_`, user-to-server `ghu_`, server-to-server `ghs_`, refresh `ghr_`); the value
75
+ // is base62 (no `_`). Prefix-anchored + a length floor, same tight style as the provider keys —
76
+ // NOT a blanket long-alphanumeric redaction (that IS the entropy trap avoided).
77
+ [/\bgh[oprsu]_[A-Za-z0-9]{16,}/g, REDACTED],
78
+ // GS2-54 (gap 2) — GitHub fine-grained PATs (`github_pat_…`); the value carries an inner `_`, so
79
+ // its charset includes `_`. Distinct prefix from the classic rule above (`github_` ≠ `gh[oprsu]_`).
80
+ [/\bgithub_pat_[A-Za-z0-9_]{16,}/g, REDACTED],
81
+ // GS2-54 (gap 2) — credentials embedded in a URL (`scheme://user:password@host`). Redact the
82
+ // `user:password` userinfo, KEEP the scheme + host so the artifact stays debuggable. Bounded: the
83
+ // userinfo stops at `@` (and the password never spans `/` or whitespace), so it cannot swallow the
84
+ // rest of the URL/line. A URL without userinfo (`https://host:port/path`) has no `user:pass@` and
85
+ // is left untouched.
86
+ [/(\b[a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/gi, `$1${REDACTED}@`],
87
+ // GS2-54 (gap 1) — an `Authorization` header value (`Authorization: <scheme?> <credential>` /
88
+ // `"Authorization":"…"`): keep the header name + separator, redact the value REGARDLESS OF SCHEME.
89
+ // Generalized beyond the old fixed `Bearer|Basic|Token|Digest|Negotiate` list so a non-standard
90
+ // scheme (`ApiKey <secret>`, an unknown scheme, `AWS4-HMAC-SHA256 Credential=…`) no longer leaks
91
+ // its token beside the marker. Bounded to <scheme> + ONE credential token (two token runs max,
92
+ // separated by horizontal whitespace only — never a newline), so it can never swallow following
93
+ // prose the way the original `[^"]+` did. An optional value-quote is consumed so JSON
94
+ // `"Authorization":"…"` works too. Runs before the SigV4 and standalone-`Bearer` rules so a normal
95
+ // header collapses to a single marker; a SigV4 header's `Credential=…` is eaten here and its
96
+ // trailing `Signature=…` is mopped up by the next rule.
97
+ [
98
+ /(\bAuthorization["']?\s*[:=]\s*["']?)[A-Za-z0-9._~+/=-]+(?:[ \t]+[A-Za-z0-9._~+/=-]+)?/gi,
99
+ `$1${REDACTED}`,
100
+ ],
101
+ // GS2-54 (gap 1) — AWS SigV4 authorization components. The header is comma-separated `key=value`
102
+ // pairs; redact the SENSITIVE `Credential=…` (access-key id) and `Signature=…` values wherever they
103
+ // appear (header value OR a presigned-URL `X-Amz-Credential=…&X-Amz-Signature=…` query), keeping the
104
+ // key name for debuggability. `SignedHeaders=…` is just header names and is intentionally NOT
105
+ // touched. The value charset excludes whitespace/comma/quote/`&`, so it stops at the component
106
+ // boundary and never swallows the next pair or trailing prose.
107
+ [/\b(Credential|Signature)=[A-Za-z0-9._~+/=-]+/gi, `$1=${REDACTED}`],
108
+ // A standalone `Bearer <token>` — keep the scheme word, redact only the credential. A long opaque
109
+ // token is redacted ONLY in this auth context, never on its own (that is the entropy trap avoided).
110
+ [/\b(Bearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi, `$1${REDACTED}`],
111
+ ];
112
+ /**
113
+ * Collect the literal secret VALUES to substitute everywhere (technique 1), from two sources:
114
+ * - process-env vars whose NAME matches {@link SECRET_ENV_NAME_RE}, PLUS the specific var(s) named
115
+ * by any `apiKeyEnvironmentVariable` in the config;
116
+ * - non-empty inline secret field values in the config (technique 3's field names), so a key pasted
117
+ * inline is scrubbed wherever it *also* surfaces (transcript, log, env), not only in config.json.
118
+ *
119
+ * `env` is a PARAMETER — never read ambiently — so the pass is pure, reusable (GS2-48) and
120
+ * deterministic in tests. Values shorter than {@link MIN_SECRET_LITERAL_LENGTH} are skipped. The
121
+ * result is returned longest-first so an overlapping-substring secret can't leave a shorter one
122
+ * partially intact. Never throws (a hostile config getter is swallowed — patterns + structural
123
+ * masking still apply).
124
+ */
125
+ export function collectSecretValues(config, env) {
126
+ const values = new Set();
127
+ const add = (v) => {
128
+ if (typeof v === 'string' && v.length >= MIN_SECRET_LITERAL_LENGTH)
129
+ values.add(v);
130
+ };
131
+ // (a) env vars whose NAME looks secret → their VALUE is a literal to scrub.
132
+ try {
133
+ for (const [name, value] of Object.entries(env ?? {})) {
134
+ if (SECRET_ENV_NAME_RE.test(name))
135
+ add(value);
136
+ }
137
+ }
138
+ catch {
139
+ // ignore — a broken env object is not fatal to redaction.
140
+ }
141
+ // (b) walk the config for `apiKeyEnvironmentVariable` (→ read that env var's value) and inline
142
+ // secret-named field values.
143
+ const seen = new WeakSet();
144
+ const walk = (node, depth) => {
145
+ if (node === null || typeof node !== 'object' || depth > MAX_REDACT_DEPTH)
146
+ return;
147
+ if (seen.has(node))
148
+ return;
149
+ seen.add(node);
150
+ if (Array.isArray(node)) {
151
+ for (const item of node)
152
+ walk(item, depth + 1);
153
+ return;
154
+ }
155
+ for (const [key, value] of Object.entries(node)) {
156
+ if (key.toLowerCase() === 'apikeyenvironmentvariable' && typeof value === 'string') {
157
+ add(env?.[value]); // resolve the NAMED env var's VALUE (the var name itself is not a secret)
158
+ }
159
+ else if (SECRET_KEY_RE.test(key) && !NON_SECRET_KEY_NAMES.has(key.toLowerCase())) {
160
+ add(value);
161
+ }
162
+ walk(value, depth + 1);
163
+ }
164
+ };
165
+ try {
166
+ walk(config, 0);
167
+ }
168
+ catch {
169
+ // fail safe: a hostile getter threw — we simply have fewer literals; patterns + structural
170
+ // masking still cover the artifacts. Never rethrow (redaction must not break the dump).
171
+ }
172
+ return [...values].sort((a, b) => b.length - a.length);
173
+ }
174
+ /**
175
+ * Redact a STRING: substitute every known literal secret value (technique 1, longest-first), then
176
+ * apply the provider key/auth patterns (technique 2). Never throws — on any error it returns the
177
+ * fully-withheld marker (fail safe: redact MORE, never emit the raw text). Applying it twice is
178
+ * safe (re-redacting already-redacted text is a no-op).
179
+ */
180
+ export function redactText(text, secrets) {
181
+ try {
182
+ let out = text;
183
+ for (const secret of secrets) {
184
+ if (secret)
185
+ out = out.split(secret).join(REDACTED); // literal — no regex escaping needed
186
+ }
187
+ for (const [re, replacement] of PROVIDER_PATTERNS) {
188
+ out = out.replace(re, replacement);
189
+ }
190
+ return out;
191
+ }
192
+ catch {
193
+ return REDACTED; // fail safe
194
+ }
195
+ }
196
+ /**
197
+ * Deep-redact an arbitrary value for serialization: mask the VALUES of secret-named fields
198
+ * (technique 3 — structure preserved: the key stays, only its value becomes the marker) and run
199
+ * {@link redactText} over every string leaf (techniques 1 + 2). Circular refs are broken; functions
200
+ * and bigints are rendered the way `safeStringify` would, so the result is JSON-safe. Pure — never
201
+ * mutates the input. `apiKeyEnvironmentVariable` is intentionally NOT masked (it is a var name).
202
+ *
203
+ * Intended for the CONFIG artifact (where field-name masking is wanted). Non-config artifacts use
204
+ * {@link redactText} over their stringified form instead (literal + pattern only), so a legitimate
205
+ * `token`/`secret`-named field in tool output is not blanket-masked.
206
+ */
207
+ export function redactValue(value, secrets) {
208
+ const seen = new WeakSet();
209
+ const walk = (node, depth) => {
210
+ if (typeof node === 'string')
211
+ return redactText(node, secrets);
212
+ if (typeof node === 'function')
213
+ return `[Function: ${node.name || 'anonymous'}]`;
214
+ if (typeof node === 'bigint')
215
+ return node.toString();
216
+ if (node === null || typeof node !== 'object')
217
+ return node;
218
+ if (seen.has(node))
219
+ return '[Circular]';
220
+ if (depth > MAX_REDACT_DEPTH)
221
+ return '[Truncated]';
222
+ seen.add(node);
223
+ if (Array.isArray(node))
224
+ return node.map((item) => walk(item, depth + 1));
225
+ const out = {};
226
+ for (const [key, val] of Object.entries(node)) {
227
+ if (SECRET_KEY_RE.test(key) && !NON_SECRET_KEY_NAMES.has(key.toLowerCase())) {
228
+ out[key] = REDACTED; // mask the value, keep the key — shape is part of the debug signal
229
+ }
230
+ else {
231
+ out[key] = walk(val, depth + 1);
232
+ }
233
+ }
234
+ return out;
235
+ };
236
+ return walk(value, 0);
237
+ }
238
+ //# sourceMappingURL=redactSecrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redactSecrets.js","sourceRoot":"","sources":["../../src/utils/redactSecrets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AAErC;;;;;;;GAOG;AACH,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAEpC,kGAAkG;AAClG,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B;;;GAGG;AACH,MAAM,kBAAkB,GAAG,6CAA6C,CAAC;AAEzE;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,6EAA6E,CAAC;AAEpG,kGAAkG;AAClG,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC;AAEpE;;;;;;GAMG;AACH,MAAM,iBAAiB,GAA6C;IAClE,+EAA+E;IAC/E,CAAC,mCAAmC,EAAE,QAAQ,CAAC;IAC/C,6BAA6B;IAC7B,CAAC,2BAA2B,EAAE,QAAQ,CAAC;IACvC,iBAAiB;IACjB,CAAC,2BAA2B,EAAE,QAAQ,CAAC;IACvC,kBAAkB;IAClB,CAAC,2BAA2B,EAAE,QAAQ,CAAC;IACvC,8FAA8F;IAC9F,mGAAmG;IACnG,gGAAgG;IAChG,gFAAgF;IAChF,CAAC,+BAA+B,EAAE,QAAQ,CAAC;IAC3C,iGAAiG;IACjG,oGAAoG;IACpG,CAAC,iCAAiC,EAAE,QAAQ,CAAC;IAC7C,6FAA6F;IAC7F,kGAAkG;IAClG,mGAAmG;IACnG,kGAAkG;IAClG,qBAAqB;IACrB,CAAC,iDAAiD,EAAE,KAAK,QAAQ,GAAG,CAAC;IACrE,8FAA8F;IAC9F,mGAAmG;IACnG,gGAAgG;IAChG,iGAAiG;IACjG,+FAA+F;IAC/F,gGAAgG;IAChG,sFAAsF;IACtF,mGAAmG;IACnG,6FAA6F;IAC7F,wDAAwD;IACxD;QACE,0FAA0F;QAC1F,KAAK,QAAQ,EAAE;KAChB;IACD,iGAAiG;IACjG,oGAAoG;IACpG,qGAAqG;IACrG,8FAA8F;IAC9F,+FAA+F;IAC/F,+DAA+D;IAC/D,CAAC,gDAAgD,EAAE,MAAM,QAAQ,EAAE,CAAC;IACpE,kGAAkG;IAClG,oGAAoG;IACpG,CAAC,uCAAuC,EAAE,KAAK,QAAQ,EAAE,CAAC;CAC3D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAe,EACf,GAAuC;IAEvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IAEjC,MAAM,GAAG,GAAG,CAAC,CAAU,EAAQ,EAAE;QAC/B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,yBAAyB;YAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACpF,CAAC,CAAC;IAEF,4EAA4E;IAC5E,IAAI,CAAC;QACH,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC;YACtD,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,0DAA0D;IAC5D,CAAC;IAED,+FAA+F;IAC/F,iCAAiC;IACjC,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,KAAa,EAAQ,EAAE;QAClD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,GAAG,gBAAgB;YAAE,OAAO;QAClF,IAAI,IAAI,CAAC,GAAG,CAAC,IAAc,CAAC;YAAE,OAAO;QACrC,IAAI,CAAC,GAAG,CAAC,IAAc,CAAC,CAAC;QACzB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,KAAK,MAAM,IAAI,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAC/C,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;YAC3E,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,2BAA2B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACnF,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,0EAA0E;YAC/F,CAAC;iBAAM,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBACnF,GAAG,CAAC,KAAK,CAAC,CAAC;YACb,CAAC;YACD,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,2FAA2F;QAC3F,wFAAwF;IAC1F,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,OAA0B;IACjE,IAAI,CAAC;QACH,IAAI,GAAG,GAAG,IAAI,CAAC;QACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM;gBAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,qCAAqC;QAC3F,CAAC;QACD,KAAK,MAAM,CAAC,EAAE,EAAE,WAAW,CAAC,IAAI,iBAAiB,EAAE,CAAC;YAClD,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;QACrC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC,CAAC,YAAY;IAC/B,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,OAA0B;IACpE,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,KAAa,EAAW,EAAE;QACrD,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,OAAO,IAAI,KAAK,UAAU;YAC5B,OAAO,cAAe,IAA0B,CAAC,IAAI,IAAI,WAAW,GAAG,CAAC;QAC1E,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACrD,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC3D,IAAI,IAAI,CAAC,GAAG,CAAC,IAAc,CAAC;YAAE,OAAO,YAAY,CAAC;QAClD,IAAI,KAAK,GAAG,gBAAgB;YAAE,OAAO,aAAa,CAAC;QACnD,IAAI,CAAC,GAAG,CAAC,IAAc,CAAC,CAAC;QACzB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1E,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAA+B,CAAC,EAAE,CAAC;YACzE,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBAC5E,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,mEAAmE;YAC1F,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;YAClC,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IACF,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Shared code-mode system-prompt augmentations (GS2-27).
3
+ *
4
+ * These notes describe capabilities that are IDENTICAL across both agent backends — the lean
5
+ * {@link import('#src/core/GthLangChainAgent.js').GthLangChainAgent} (`createAgent`, in core) and
6
+ * the deep `GthDeepAgent` (`createDeepAgent`, in `@gaunt-sloth/agent`): both expose the opt-in
7
+ * `run_shell_command` tool and both operate on the real filesystem cwd. They were originally
8
+ * composed ONLY inside `GthDeepAgent.init()` (EXT-13/EXT-26), so the now-default lean backend never
9
+ * received them — accidental deep-only drift of the same class GS2-21 fixed for the main prompt.
10
+ *
11
+ * They live here in core so BOTH backends compose from ONE source. `GthDeepAgent` re-exports them
12
+ * for back-compat with existing importers. The deepagents virtual-fs-namespace notes
13
+ * (`appendVirtualCwdNote` / `PATH_NAMESPACE_GUIDANCE` / the correction middleware) are NOT here:
14
+ * they are genuinely deep-only (a deepagents artifact — lean never runs virtualMode) and stay in
15
+ * `GthDeepAgent`.
16
+ */
17
+ import type { McpServerInstruction } from '#src/core/types.js';
18
+ /**
19
+ * EXT-26: the platform-agnostic tail shared by both {@link appendOsShellNote} branches.
20
+ *
21
+ * The recurring failure mode on non-POSIX hosts is not just wrong command NAMES but shell
22
+ * REDIRECTION quoting: a grouped/multi-line `echo` redirect on cmd.exe reported success yet wrote
23
+ * a 0-byte file. So on every platform we steer file creation/mutation to the built-in
24
+ * `write_file`/`edit_file` tools (which never touch the shell's quoting) and keep each shell
25
+ * command a single line. Kept short — this is prompt text an LLM reads, not documentation.
26
+ */
27
+ export declare const OS_SHELL_GUIDANCE: string;
28
+ /**
29
+ * EXT-26: append an OS + shell-dialect note to the composed code-mode system prompt.
30
+ *
31
+ * The model was never told its host OS or which shell `run_shell_command` uses, so on
32
+ * non-POSIX hosts it defaulted to POSIX idioms that fail (ran `ls` where cmd.exe has `dir`, a
33
+ * multi-line echo-redirect that wrote 0 bytes, a PowerShell here-string, `python -c` multi-line).
34
+ * This is ORTHOGONAL to the EXT-13/16/22 path-namespace notes: those say WHERE the model is (path
35
+ * form); this says WHAT shell it speaks (dialect). Backend-agnostic — the lean backend also exposes
36
+ * `run_shell_command`, so GS2-27 composes it in the shared path.
37
+ *
38
+ * The shell is derived from the SAME rule Node's `spawn(command, { shell: true })` uses — exactly
39
+ * how `run_shell_command` spawns (GthDevToolkit spawn) — so on `win32` it is cmd.exe (via
40
+ * `%ComSpec%`) and on POSIX it is `/bin/sh` (POSIX sh, NOT guaranteed bash). Computed from
41
+ * `process.platform` at call time so the text is correct per host. Returns the note alone when
42
+ * there is no base prompt. A single injection is authoritative (nothing in the base prompt
43
+ * contradicts shell dialect), so unlike EXT-22 no correction middleware is needed.
44
+ */
45
+ export declare function appendOsShellNote(systemPrompt: string | undefined): string;
46
+ /**
47
+ * EXT-13 (part b): append a real-cwd / path-model note to the composed code-mode system prompt.
48
+ *
49
+ * Code mode runs in REAL-path mode, so the filesystem tools and `run_shell_command` share one
50
+ * real-absolute-path namespace rooted at `cwd`. The shared `.gsloth.code.md` prompt already tells
51
+ * the model "the current working directory is provided to you separately"; this note is what
52
+ * provides it — without it the model assumes `/` is cwd and hands `/`-rooted paths to the real-fs
53
+ * shell. The cwd is injected dynamically (never baked into the .md). Backend-agnostic — the lean
54
+ * backend also runs real-fs code sessions and previously received NO cwd value, so GS2-27 composes
55
+ * it in the shared path. Returns the note alone when there is no base prompt.
56
+ */
57
+ export declare function appendCwdNote(systemPrompt: string | undefined, cwd: string): string;
58
+ /** GS2-35 — the configured Git co-author identity (both fields optional; each defaults on its own). */
59
+ export interface CommitCoAuthor {
60
+ name?: string;
61
+ email?: string;
62
+ }
63
+ /**
64
+ * GS2-35: append the commit co-authoring rule to the composed code-mode system prompt.
65
+ *
66
+ * Gaunt Sloth has **no dedicated git-commit tool** — the agent commits by calling
67
+ * `run_shell_command` with `git commit`, composing the message (including any trailer) itself. Left
68
+ * unguided, models emit `Co-Authored-By: <their own model name>` (e.g. `Claude`, `GPT`, `Gemini`)
69
+ * from trained habit, which is **factually wrong**: the commit was produced by *Gaunt Sloth*, not by
70
+ * the model. This note is the fix at the correct layer — first-party prompt guidance that (a) states
71
+ * the exact trailer to emit and (b) forbids a model-name co-author.
72
+ *
73
+ * The identity is config-driven (`commit.coAuthor` in {@link import('#src/config/types.js').GthConfig}).
74
+ * Each field falls back INDEPENDENTLY to the Gaunt Sloth account
75
+ * ({@link DEFAULT_COMMIT_CO_AUTHOR_NAME} / {@link DEFAULT_COMMIT_CO_AUTHOR_EMAIL}) — so a partial
76
+ * override (name only, or a config that bypassed the loader) still yields a complete trailer, and a
77
+ * fully-absent config yields the default account. Blank/whitespace values are treated as unset.
78
+ *
79
+ * Backend-agnostic: composed through the shared code path so BOTH the lean `GthLangChainAgent` and
80
+ * the deep `GthDeepAgent` inject it (the git-commit capability rides on `run_shell_command`, which
81
+ * both backends expose in code mode). Returns the note alone when there is no base prompt.
82
+ */
83
+ export declare function appendCommitCoAuthorNote(systemPrompt: string | undefined, coAuthor?: CommitCoAuthor): string;
84
+ /**
85
+ * GS2-34/GS2-53 — the resolved active-model identity, as a STRUCTURED value.
86
+ *
87
+ * `hasProvider` is the authoritative "a real provider half was resolved" signal — carried
88
+ * explicitly rather than inferred from `identity.includes(':')`, because a bare model name can
89
+ * itself contain a colon (e.g. an Ollama/HF tag `gemma3:27b`) and would otherwise be mistaken for a
90
+ * `provider:model` string. It is true iff a non-empty provider (configured `type` OR a non-empty
91
+ * `_llmType()`) formed the leading `provider:` segment; false when `identity` is the bare model.
92
+ */
93
+ export interface ResolvedModelIdentity {
94
+ /** `provider:model` when a provider resolved, otherwise the bare `model`. */
95
+ identity: string;
96
+ /** True iff a real provider formed the `provider:` segment (never merely a colon in the model). */
97
+ hasProvider: boolean;
98
+ }
99
+ /**
100
+ * GS2-34: resolve the active model identity from the effective config, for injection into the
101
+ * system prompt by {@link appendModelContextNote}.
102
+ *
103
+ * Both halves are read the SAME way the rest of gsloth already surfaces the active model:
104
+ * - MODEL: `config.modelDisplayName` (the string the status line renders — set by the loader from
105
+ * `llm.model`), falling back to the live model's own `model` field.
106
+ * - PROVIDER: the configured `config.modelProviderType` (the raw `llm.type` the loader stashed —
107
+ * `openrouter`/`deepseek`/`xai`/`anthropic`/…) when present, otherwise the live LangChain
108
+ * model's `_llmType()` (the source the AG-UI `/info` endpoint reports). GS2-53 — preferring the
109
+ * configured `type` fixes the OpenAI-compatible shims (openrouter/deepseek/xai all extend
110
+ * ChatOpenAI, so their `_llmType()` reports `openai`): a `type: openrouter` config now injects
111
+ * `openrouter:<model>`, not `openai:<model>`. The `type` is absent for module configs (which
112
+ * hand us an already-built LLM), where we fall back to `_llmType()` unchanged. The MODEL half is
113
+ * always exact.
114
+ *
115
+ * Returns `{ identity: 'provider:model', hasProvider: true }` when both resolve, `{ identity:
116
+ * 'model', hasProvider: false }` when only the model resolves, and `undefined` when the model is
117
+ * unknown (a provider with no model is not a usable identity) — in which case {@link
118
+ * appendModelContextNote} injects nothing, leaving the prompt exactly as before. `hasProvider` is
119
+ * the authoritative provider-present flag (GS2-53), so a bare model whose NAME contains a colon
120
+ * (e.g. `gemma3:27b`) is correctly reported as `hasProvider: false`. `_llmType()` is called
121
+ * defensively (guarded) so a provider whose accessor throws can never break prompt assembly (and is
122
+ * skipped entirely when a configured `type` is present).
123
+ */
124
+ export declare function resolveModelIdentity(config: {
125
+ llm?: {
126
+ _llmType?: () => string;
127
+ model?: string;
128
+ };
129
+ modelDisplayName?: string;
130
+ modelProviderType?: string;
131
+ } | null | undefined): ResolvedModelIdentity | undefined;
132
+ /**
133
+ * GS2-34: append the active model-identity note to the composed system prompt.
134
+ *
135
+ * The agent otherwise has no reliable knowledge of which `provider:model` is serving it, so it
136
+ * cannot answer "what model are you?" accurately or reason about its own capabilities/limits. This
137
+ * injects a single first-party line naming the resolved identity (see {@link resolveModelIdentity}).
138
+ *
139
+ * Injected in EVERY mode (chat/ask/code/exec), NOT gated to `code` like the cwd/os-shell/commit
140
+ * notes: "which model are you?" can be asked in any session, so the identity must be visible
141
+ * everywhere. Config-gated by `injectModelContext` (default ON): a caller passes an `undefined`
142
+ * `modelIdentity` — because the config opted out (`injectModelContext: false`) or because no model
143
+ * could be resolved — and the base prompt is returned UNCHANGED (no line), preserving the current
144
+ * prompt byte-for-byte.
145
+ *
146
+ * A short capability note from the GS2-6 model catalog is a DEFERRED follow-up (GS2-6 has not
147
+ * landed): this injects the bare `provider:model` identity only.
148
+ */
149
+ export declare function appendModelContextNote(systemPrompt: string | undefined, modelIdentity: ResolvedModelIdentity | undefined): string | undefined;
150
+ /**
151
+ * EXT-32: hard per-server cap on injected MCP instruction length.
152
+ *
153
+ * A connected MCP server could advertise a very long `instructions` string that would then ride
154
+ * along in EVERY turn's system prompt and bloat context. This is a simple defensive constant (no
155
+ * config-schema plumbing — that stays out of this node): text beyond the cap is dropped and a
156
+ * truncation marker is appended so the model knows it was clipped. Generous enough that realistic
157
+ * server instructions pass through untouched.
158
+ */
159
+ export declare const MCP_INSTRUCTIONS_MAX_CHARS_PER_SERVER = 4000;
160
+ /** EXT-32: truncation marker appended when a server's instructions exceed the per-server cap. */
161
+ export declare const MCP_INSTRUCTIONS_TRUNCATION_MARKER = "\u2026 [truncated]";
162
+ /**
163
+ * EXT-32: append connected MCP servers' discovery `instructions` to the composed system prompt.
164
+ *
165
+ * Each connected MCP server may return an `instructions` string in its `initialize` handshake that
166
+ * describes how to use its tools. We surface those to the model — but the text is **server-supplied
167
+ * and therefore a prompt-injection surface**, so it is:
168
+ * - fenced in an explicit `[BEGIN/END MCP SERVER-PROVIDED CONTEXT]` block (never blended into
169
+ * first-party prompt text),
170
+ * - labelled per server (`--- Server: "name" ---`) so the model can attribute each block, and
171
+ * - bracketed by a leading framing line AND a trailing first-party reassertion, so the LAST thing
172
+ * the model reads is gsloth's own authority, not the server text — server instructions may not
173
+ * override the system instructions, safety rules, or the user's directives.
174
+ *
175
+ * Backend-agnostic: composed through the shared path so BOTH the lean `GthLangChainAgent` and the
176
+ * deep `GthDeepAgent` inject it. Empty/absent contributes NOTHING: when no server supplied
177
+ * (non-whitespace) instructions the base prompt is returned unchanged — no empty header, no dangling
178
+ * label. Each server's text is trimmed and capped at {@link MCP_INSTRUCTIONS_MAX_CHARS_PER_SERVER}.
179
+ * Returns the block alone when there is no base prompt.
180
+ */
181
+ export declare function appendMcpServerInstructionsNote(systemPrompt: string | undefined, instructions: McpServerInstruction[] | undefined): string | undefined;