@gaunt-sloth/core 2.0.0-alpha.8 → 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (275) hide show
  1. package/.gsloth.review.md +2 -0
  2. package/README.md +71 -20
  3. package/dist/config/colour.d.ts +38 -0
  4. package/dist/config/colour.js +36 -0
  5. package/dist/config/colour.js.map +1 -0
  6. package/dist/config/configDiscovery.d.ts +79 -0
  7. package/dist/config/configDiscovery.js +80 -0
  8. package/dist/config/configDiscovery.js.map +1 -0
  9. package/dist/config/defaults.d.ts +20 -20
  10. package/dist/config/defaults.js +10 -8
  11. package/dist/config/defaults.js.map +1 -1
  12. package/dist/config/filesystem-tools.d.ts +41 -0
  13. package/dist/config/filesystem-tools.js +56 -0
  14. package/dist/config/filesystem-tools.js.map +1 -0
  15. package/dist/config/loader.d.ts +171 -19
  16. package/dist/config/loader.js +1054 -144
  17. package/dist/config/loader.js.map +1 -1
  18. package/dist/config/mouse.d.ts +50 -0
  19. package/dist/config/mouse.js +44 -0
  20. package/dist/config/mouse.js.map +1 -0
  21. package/dist/config/profiles.d.ts +68 -0
  22. package/dist/config/profiles.js +93 -0
  23. package/dist/config/profiles.js.map +1 -0
  24. package/dist/config/providerKeys.d.ts +69 -0
  25. package/dist/config/providerKeys.js +69 -0
  26. package/dist/config/providerKeys.js.map +1 -0
  27. package/dist/config/schema.d.ts +2671 -138
  28. package/dist/config/schema.js +1361 -85
  29. package/dist/config/schema.js.map +1 -1
  30. package/dist/config/shell-policy.d.ts +899 -111
  31. package/dist/config/shell-policy.js +800 -70
  32. package/dist/config/shell-policy.js.map +1 -1
  33. package/dist/config/tool-descriptions.d.ts +211 -0
  34. package/dist/config/tool-descriptions.js +272 -0
  35. package/dist/config/tool-descriptions.js.map +1 -0
  36. package/dist/config/types.d.ts +352 -41
  37. package/dist/config/types.js +1 -0
  38. package/dist/config/types.js.map +1 -1
  39. package/dist/config.d.ts +35 -1
  40. package/dist/config.js +16 -1
  41. package/dist/config.js.map +1 -1
  42. package/dist/constants.d.ts +28 -1
  43. package/dist/constants.js +28 -1
  44. package/dist/constants.js.map +1 -1
  45. package/dist/core/GthAbstractAgent.d.ts +166 -11
  46. package/dist/core/GthAbstractAgent.js +484 -45
  47. package/dist/core/GthAbstractAgent.js.map +1 -1
  48. package/dist/core/GthAgentRunner.d.ts +543 -57
  49. package/dist/core/GthAgentRunner.js +1494 -140
  50. package/dist/core/GthAgentRunner.js.map +1 -1
  51. package/dist/core/GthLangChainAgent.d.ts +117 -2
  52. package/dist/core/GthLangChainAgent.js +602 -29
  53. package/dist/core/GthLangChainAgent.js.map +1 -1
  54. package/dist/core/approvals/annotations.d.ts +122 -0
  55. package/dist/core/approvals/annotations.js +137 -0
  56. package/dist/core/approvals/annotations.js.map +1 -0
  57. package/dist/core/approvals/grants.d.ts +216 -0
  58. package/dist/core/approvals/grants.js +469 -0
  59. package/dist/core/approvals/grants.js.map +1 -0
  60. package/dist/core/approvals/matcher.d.ts +202 -0
  61. package/dist/core/approvals/matcher.js +267 -0
  62. package/dist/core/approvals/matcher.js.map +1 -0
  63. package/dist/core/approvals/mcpSubjects.d.ts +40 -0
  64. package/dist/core/approvals/mcpSubjects.js +99 -0
  65. package/dist/core/approvals/mcpSubjects.js.map +1 -0
  66. package/dist/core/approvals/promptHeader.d.ts +28 -0
  67. package/dist/core/approvals/promptHeader.js +62 -0
  68. package/dist/core/approvals/promptHeader.js.map +1 -0
  69. package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
  70. package/dist/core/approvals/toolAnnotationSources.js +277 -0
  71. package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
  72. package/dist/core/approvals/toolHost.d.ts +46 -0
  73. package/dist/core/approvals/toolHost.js +108 -0
  74. package/dist/core/approvals/toolHost.js.map +1 -0
  75. package/dist/core/debugCapture.d.ts +74 -0
  76. package/dist/core/debugCapture.js +100 -0
  77. package/dist/core/debugCapture.js.map +1 -0
  78. package/dist/core/gthLeanAgentFactory.d.ts +4 -4
  79. package/dist/core/gthLeanAgentFactory.js +4 -4
  80. package/dist/core/launchBanner.d.ts +127 -0
  81. package/dist/core/launchBanner.js +414 -0
  82. package/dist/core/launchBanner.js.map +1 -0
  83. package/dist/core/modelLabel.d.ts +19 -0
  84. package/dist/core/modelLabel.js +26 -0
  85. package/dist/core/modelLabel.js.map +1 -0
  86. package/dist/core/plainToolIndication.d.ts +15 -0
  87. package/dist/core/plainToolIndication.js +174 -0
  88. package/dist/core/plainToolIndication.js.map +1 -0
  89. package/dist/core/reasoningBlocks.d.ts +65 -0
  90. package/dist/core/reasoningBlocks.js +103 -0
  91. package/dist/core/reasoningBlocks.js.map +1 -0
  92. package/dist/core/refusal.d.ts +53 -0
  93. package/dist/core/refusal.js +133 -0
  94. package/dist/core/refusal.js.map +1 -0
  95. package/dist/core/runHeader.d.ts +38 -0
  96. package/dist/core/runHeader.js +42 -0
  97. package/dist/core/runHeader.js.map +1 -0
  98. package/dist/core/runStats.d.ts +14 -3
  99. package/dist/core/runStats.js +48 -3
  100. package/dist/core/runStats.js.map +1 -1
  101. package/dist/core/shell/ShellCommandFailedError.d.ts +3 -4
  102. package/dist/core/shell/ShellCommandFailedError.js +3 -4
  103. package/dist/core/shell/ShellCommandFailedError.js.map +1 -1
  104. package/dist/core/shell/abstention.d.ts +88 -0
  105. package/dist/core/shell/abstention.js +184 -0
  106. package/dist/core/shell/abstention.js.map +1 -0
  107. package/dist/core/shell/approvalCapture.d.ts +271 -0
  108. package/dist/core/shell/approvalCapture.js +108 -0
  109. package/dist/core/shell/approvalCapture.js.map +1 -0
  110. package/dist/core/shell/approvalStop.d.ts +123 -0
  111. package/dist/core/shell/approvalStop.js +269 -0
  112. package/dist/core/shell/approvalStop.js.map +1 -0
  113. package/dist/core/shell/arity.d.ts +6 -0
  114. package/dist/core/shell/arity.js +20 -6
  115. package/dist/core/shell/arity.js.map +1 -1
  116. package/dist/core/shell/denylist.d.ts +11 -0
  117. package/dist/core/shell/denylist.js +37 -0
  118. package/dist/core/shell/denylist.js.map +1 -0
  119. package/dist/core/shell/escalationSeverity.d.ts +141 -0
  120. package/dist/core/shell/escalationSeverity.js +89 -0
  121. package/dist/core/shell/escalationSeverity.js.map +1 -0
  122. package/dist/core/shell/framing.d.ts +190 -0
  123. package/dist/core/shell/framing.js +633 -0
  124. package/dist/core/shell/framing.js.map +1 -0
  125. package/dist/core/shell/hardline.d.ts +103 -0
  126. package/dist/core/shell/hardline.js +780 -0
  127. package/dist/core/shell/hardline.js.map +1 -0
  128. package/dist/core/shell/negotiation.d.ts +328 -0
  129. package/dist/core/shell/negotiation.js +488 -0
  130. package/dist/core/shell/negotiation.js.map +1 -0
  131. package/dist/core/shell/normalize.d.ts +44 -4
  132. package/dist/core/shell/normalize.js +61 -7
  133. package/dist/core/shell/normalize.js.map +1 -1
  134. package/dist/core/shell/openWorld.d.ts +263 -0
  135. package/dist/core/shell/openWorld.js +1188 -0
  136. package/dist/core/shell/openWorld.js.map +1 -0
  137. package/dist/core/shell/rater.d.ts +873 -0
  138. package/dist/core/shell/rater.js +1454 -0
  139. package/dist/core/shell/rater.js.map +1 -0
  140. package/dist/core/shell/raterModel.d.ts +41 -0
  141. package/dist/core/shell/raterModel.js +51 -0
  142. package/dist/core/shell/raterModel.js.map +1 -0
  143. package/dist/core/shell/raterVocabulary.d.ts +121 -0
  144. package/dist/core/shell/raterVocabulary.js +116 -0
  145. package/dist/core/shell/raterVocabulary.js.map +1 -0
  146. package/dist/core/shell/rejection.d.ts +69 -0
  147. package/dist/core/shell/rejection.js +38 -0
  148. package/dist/core/shell/rejection.js.map +1 -0
  149. package/dist/core/toolCallRepair/grammar.d.ts +41 -0
  150. package/dist/core/toolCallRepair/grammar.js +116 -0
  151. package/dist/core/toolCallRepair/grammar.js.map +1 -0
  152. package/dist/core/toolCallRepair/index.d.ts +2 -0
  153. package/dist/core/toolCallRepair/index.js +7 -0
  154. package/dist/core/toolCallRepair/index.js.map +1 -0
  155. package/dist/core/toolCallRepair/payload.d.ts +36 -0
  156. package/dist/core/toolCallRepair/payload.js +341 -0
  157. package/dist/core/toolCallRepair/payload.js.map +1 -0
  158. package/dist/core/toolCallRepair/promote.d.ts +45 -0
  159. package/dist/core/toolCallRepair/promote.js +90 -0
  160. package/dist/core/toolCallRepair/promote.js.map +1 -0
  161. package/dist/core/toolDisplay.d.ts +123 -0
  162. package/dist/core/toolDisplay.js +451 -0
  163. package/dist/core/toolDisplay.js.map +1 -0
  164. package/dist/core/toolOutputChannel.d.ts +95 -0
  165. package/dist/core/toolOutputChannel.js +165 -0
  166. package/dist/core/toolOutputChannel.js.map +1 -0
  167. package/dist/core/types.d.ts +349 -16
  168. package/dist/core/types.js.map +1 -1
  169. package/dist/history/historyFormat.d.ts +12 -3
  170. package/dist/history/historyFormat.js +50 -8
  171. package/dist/history/historyFormat.js.map +1 -1
  172. package/dist/history/historyStore.d.ts +77 -0
  173. package/dist/history/historyStore.js +173 -6
  174. package/dist/history/historyStore.js.map +1 -1
  175. package/dist/history/recordSession.d.ts +10 -1
  176. package/dist/history/recordSession.js +27 -0
  177. package/dist/history/recordSession.js.map +1 -1
  178. package/dist/providers/anthropic.js +12 -0
  179. package/dist/providers/anthropic.js.map +1 -1
  180. package/dist/providers/configurationPassthrough.d.ts +107 -0
  181. package/dist/providers/configurationPassthrough.js +148 -0
  182. package/dist/providers/configurationPassthrough.js.map +1 -0
  183. package/dist/providers/geminiSchemaSanitizer.d.ts +55 -0
  184. package/dist/providers/geminiSchemaSanitizer.js +347 -0
  185. package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
  186. package/dist/providers/geminiThinking.d.ts +60 -0
  187. package/dist/providers/geminiThinking.js +92 -0
  188. package/dist/providers/geminiThinking.js.map +1 -0
  189. package/dist/providers/google-genai.js +18 -1
  190. package/dist/providers/google-genai.js.map +1 -1
  191. package/dist/providers/groq.js +12 -0
  192. package/dist/providers/groq.js.map +1 -1
  193. package/dist/providers/huggingface.d.ts +25 -0
  194. package/dist/providers/huggingface.js +69 -0
  195. package/dist/providers/huggingface.js.map +1 -0
  196. package/dist/providers/modelCatalog.d.ts +109 -0
  197. package/dist/providers/modelCatalog.js +245 -0
  198. package/dist/providers/modelCatalog.js.map +1 -0
  199. package/dist/providers/modelDiscovery.d.ts +99 -5
  200. package/dist/providers/modelDiscovery.js +191 -35
  201. package/dist/providers/modelDiscovery.js.map +1 -1
  202. package/dist/providers/ollama.d.ts +18 -4
  203. package/dist/providers/ollama.js +67 -37
  204. package/dist/providers/ollama.js.map +1 -1
  205. package/dist/providers/openai.js +34 -0
  206. package/dist/providers/openai.js.map +1 -1
  207. package/dist/providers/openrouter.d.ts +26 -4
  208. package/dist/providers/openrouter.js +83 -26
  209. package/dist/providers/openrouter.js.map +1 -1
  210. package/dist/providers/vertexai.js +19 -1
  211. package/dist/providers/vertexai.js.map +1 -1
  212. package/dist/providers/xai.js +20 -0
  213. package/dist/providers/xai.js.map +1 -1
  214. package/dist/runtime/askStructured.d.ts +105 -0
  215. package/dist/runtime/askStructured.js +120 -0
  216. package/dist/runtime/askStructured.js.map +1 -0
  217. package/dist/runtime/conversation.d.ts +64 -0
  218. package/dist/runtime/conversation.js +171 -0
  219. package/dist/runtime/conversation.js.map +1 -0
  220. package/dist/runtime/singleShot.d.ts +37 -6
  221. package/dist/runtime/singleShot.js +113 -67
  222. package/dist/runtime/singleShot.js.map +1 -1
  223. package/dist/runtime/structuredOutput.d.ts +104 -0
  224. package/dist/runtime/structuredOutput.js +393 -0
  225. package/dist/runtime/structuredOutput.js.map +1 -0
  226. package/dist/utils/ProgressIndicator.d.ts +21 -0
  227. package/dist/utils/ProgressIndicator.js +30 -3
  228. package/dist/utils/ProgressIndicator.js.map +1 -1
  229. package/dist/utils/aiignoreUtils.js.map +1 -1
  230. package/dist/utils/binaryOutputUtils.js.map +1 -1
  231. package/dist/utils/consoleUtils.d.ts +95 -0
  232. package/dist/utils/consoleUtils.js +112 -2
  233. package/dist/utils/consoleUtils.js.map +1 -1
  234. package/dist/utils/crashHandler.d.ts +87 -0
  235. package/dist/utils/crashHandler.js +128 -0
  236. package/dist/utils/crashHandler.js.map +1 -0
  237. package/dist/utils/debugDump.d.ts +134 -0
  238. package/dist/utils/debugDump.js +381 -0
  239. package/dist/utils/debugDump.js.map +1 -0
  240. package/dist/utils/debugUtils.d.ts +13 -4
  241. package/dist/utils/debugUtils.js +36 -13
  242. package/dist/utils/debugUtils.js.map +1 -1
  243. package/dist/utils/displayWidth.d.ts +53 -0
  244. package/dist/utils/displayWidth.js +195 -0
  245. package/dist/utils/displayWidth.js.map +1 -0
  246. package/dist/utils/fileUtils.js.map +1 -1
  247. package/dist/utils/globalConfigUtils.d.ts +14 -2
  248. package/dist/utils/globalConfigUtils.js +22 -4
  249. package/dist/utils/globalConfigUtils.js.map +1 -1
  250. package/dist/utils/llmUtils.d.ts +39 -8
  251. package/dist/utils/llmUtils.js +76 -8
  252. package/dist/utils/llmUtils.js.map +1 -1
  253. package/dist/utils/redactSecrets.d.ts +63 -0
  254. package/dist/utils/redactSecrets.js +286 -0
  255. package/dist/utils/redactSecrets.js.map +1 -0
  256. package/dist/utils/systemPromptNotes.d.ts +222 -0
  257. package/dist/utils/systemPromptNotes.js +338 -0
  258. package/dist/utils/systemPromptNotes.js.map +1 -0
  259. package/dist/utils/systemUtils.d.ts +18 -1
  260. package/dist/utils/systemUtils.js +38 -3
  261. package/dist/utils/systemUtils.js.map +1 -1
  262. package/dist/utils/toolMatching.d.ts +30 -0
  263. package/dist/utils/toolMatching.js +44 -0
  264. package/dist/utils/toolMatching.js.map +1 -0
  265. package/dist/utils/untrustedText.d.ts +86 -0
  266. package/dist/utils/untrustedText.js +101 -0
  267. package/dist/utils/untrustedText.js.map +1 -0
  268. package/package.json +21 -7
  269. package/schema/gsloth-config.schema.json +1921 -326
  270. package/dist/core/shell/allowlist.d.ts +0 -75
  271. package/dist/core/shell/allowlist.js +0 -187
  272. package/dist/core/shell/allowlist.js.map +0 -1
  273. package/dist/core/shell/judge.d.ts +0 -161
  274. package/dist/core/shell/judge.js +0 -261
  275. package/dist/core/shell/judge.js.map +0 -1
@@ -0,0 +1,381 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { mkdirSync, writeFileSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { inspect } from 'node:util';
5
+ import { ensureGlobalGslothDir } from '#src/utils/globalConfigUtils.js';
6
+ import { env, getSlothVersion } from '#src/utils/systemUtils.js';
7
+ import { getDebugLogBuffer } from '#src/utils/debugUtils.js';
8
+ import { REDACTED, collectSecretValues, redactText, redactValue, } from '#src/utils/redactSecrets.js';
9
+ /**
10
+ * Best-effort git repo state (branch/remote/dirty). Uses `execFileSync` — NOT the existing
11
+ * `execAsync` helper in systemUtils.ts (a promisified wrapper used elsewhere for `gh` calls) —
12
+ * because `SlashCommand.run()` is fully synchronous (no Promise support in the TUI's registry or
13
+ * dispatcher). Fails soft: any failure (not inside a repo, `git` missing, a detached/bare repo
14
+ * with no remote, etc.) returns `undefined` rather than throwing, so the command never errors —
15
+ * the git-state file is simply omitted from the archive.
16
+ */
17
+ function collectGitState(cwd) {
18
+ try {
19
+ const run = (args) => execFileSync('git', args, {
20
+ cwd,
21
+ encoding: 'utf8',
22
+ stdio: ['ignore', 'pipe', 'ignore'],
23
+ }).trim();
24
+ const branch = run(['rev-parse', '--abbrev-ref', 'HEAD']);
25
+ let remote;
26
+ try {
27
+ remote = run(['remote', 'get-url', 'origin']);
28
+ }
29
+ catch {
30
+ remote = undefined; // no `origin` remote configured — not fatal, just omit it
31
+ }
32
+ const status = run(['status', '--porcelain']);
33
+ return { branch, remote, dirty: status.length > 0 };
34
+ }
35
+ catch {
36
+ return undefined; // not inside a git repo (or git unavailable) — omit the file entirely
37
+ }
38
+ }
39
+ /**
40
+ * Serialize a value as pretty JSON, tolerating the non-JSON-safe shapes real session state can
41
+ * contain (e.g. the resolved `GthConfig.llm` is a live LangChain client, not plain data):
42
+ * functions/bigints are stringified and circular references are broken rather than throwing.
43
+ * Falls back to `util.inspect` if `JSON.stringify` still fails for some other reason, so writing
44
+ * the dump can never throw partway through the archive.
45
+ *
46
+ * QA-6: `JSON.stringify` returns `undefined` (not a string) for a handful of top-level inputs —
47
+ * notably `undefined` itself, which `DebugDumpInput.config` legitimately is when the caller has
48
+ * no resolved config (verified live via the e2e fixture harness, which wires the real writer with
49
+ * `config` omitted). Passing `undefined` straight to `writeFileSync` throws
50
+ * `ERR_INVALID_ARG_TYPE`, defeating the "never throw partway through the archive" contract this
51
+ * function documents. Fall back to the literal `'null'` in that case, same as `JSON.stringify`
52
+ * would for a *nested* `undefined` value.
53
+ */
54
+ function safeStringify(value) {
55
+ const seen = new WeakSet();
56
+ try {
57
+ const json = JSON.stringify(value, (_key, val) => {
58
+ if (typeof val === 'function')
59
+ return `[Function: ${val.name || 'anonymous'}]`;
60
+ if (typeof val === 'bigint')
61
+ return val.toString();
62
+ if (val && typeof val === 'object') {
63
+ if (seen.has(val))
64
+ return '[Circular]';
65
+ seen.add(val);
66
+ }
67
+ return val;
68
+ }, 2);
69
+ return json ?? 'null';
70
+ }
71
+ catch {
72
+ return inspect(value, { showHidden: false, depth: 5, colors: false });
73
+ }
74
+ }
75
+ /**
76
+ * The filesystem-safe directory-name segment for one dump: the ISO timestamp with `:` and `.`
77
+ * (illegal on Windows, noisy everywhere) replaced by `-`. This is the ONLY path component
78
+ * debugDump generates — and the only one it is responsible for sanitizing. The parent it is joined
79
+ * under (the global `~/.gsloth` dir) is supplied by the environment and may legitimately carry a
80
+ * drive-letter colon on Windows (`C:\…`), which is not ours to strip. Exported so the invariant
81
+ * "the generated segment is colon-free" is testable on any platform without asserting anything
82
+ * about the (platform-dependent) parent path (GS2-50).
83
+ */
84
+ export function debugDumpDirName(date = new Date()) {
85
+ return date.toISOString().replace(/[:.]/g, '-');
86
+ }
87
+ /**
88
+ * GS2-54 (gap 3) — a live LangChain chat model is detected structurally, NOT by field name: a real
89
+ * `BaseChatModel` exposes `_llmType()` and `invoke()` as functions. This duck-type is the LOAD-BEARING
90
+ * invariant that lets us strip a *live* model to a descriptor (below) WITHOUT also flattening a plain
91
+ * `{ type, model, apiKey }` config object — the latter must keep flowing through {@link redactValue}'s
92
+ * field-masking (key kept, value masked). Do NOT "simplify" this into an unconditional strip.
93
+ */
94
+ function isLiveChatModel(llm) {
95
+ if (llm == null || typeof llm !== 'object')
96
+ return false;
97
+ const m = llm;
98
+ return typeof m._llmType === 'function' || typeof m.invoke === 'function';
99
+ }
100
+ /**
101
+ * GS2-54 (gap 3) — a short, secret-free descriptor for a live chat model, mirroring the spirit of
102
+ * `configCommand.ts`'s `redactConfigForPrint`. Never returns the live object or any of its internals
103
+ * (client instances, cached kwargs, inline keys) — only a `type` + `model` label. Each field probe
104
+ * is guarded so a hostile/exotic getter can't throw out of here.
105
+ */
106
+ function describeLiveModel(config, llm) {
107
+ const m = llm;
108
+ let type = 'unknown';
109
+ try {
110
+ const t = (typeof m._llmType === 'function' ? m._llmType() : undefined) ??
111
+ (Array.isArray(m.lc_namespace) ? m.lc_namespace.join('/') : undefined) ??
112
+ (typeof config.type === 'string'
113
+ ? config.type
114
+ : undefined) ??
115
+ m.constructor?.name;
116
+ if (typeof t === 'string' && t.length > 0)
117
+ type = t;
118
+ }
119
+ catch {
120
+ // keep 'unknown' — a throwing getter must not defeat the strip
121
+ }
122
+ let model = 'unknown';
123
+ try {
124
+ const md = config.modelDisplayName;
125
+ const mdl = (typeof md === 'string' && md.length > 0 ? md : undefined) ??
126
+ (typeof m.model === 'string' ? m.model : undefined) ??
127
+ (typeof m.modelName === 'string' ? m.modelName : undefined);
128
+ if (typeof mdl === 'string' && mdl.length > 0)
129
+ model = mdl;
130
+ }
131
+ catch {
132
+ // keep 'unknown'
133
+ }
134
+ return { type, model };
135
+ }
136
+ /**
137
+ * GS2-54 (gap 3) + GS2-66 (residual 3) — defense-in-depth for the CONFIG artifact: return a shallow
138
+ * clone of `config` with the LIVE instances stripped to short descriptors so their large internal
139
+ * surface (client objects, cached kwargs, inline keys, and non-secret-NAMED opaque fields the
140
+ * `caCert` class that field-masking cannot catch) never reaches disk — rather than relying on
141
+ * {@link redactValue}'s field-masking alone. Mirrors `configCommand.ts`'s `redactConfigForPrint`:
142
+ * - a *live* `llm` (a `BaseChatModel`) → a `{ type, model }` descriptor;
143
+ * - a `tools` array → `[N tool instance(s)]`;
144
+ * - a `middleware` array → `[N middleware]`.
145
+ * PURE: never mutates the caller's config (a fresh clone is made only if something is stripped).
146
+ * FAIL-SAFE: on any error, or when a field is absent / not a live instance, that field is left as-is
147
+ * so {@link redactValue}'s existing GS2-47 masking still applies (never less-redacted). The `llm`,
148
+ * `tools` and `middleware` strips are independent — `tools`/`middleware` are stripped even when the
149
+ * config carries no live `llm`.
150
+ */
151
+ function stripLiveInstancesForConfig(config) {
152
+ try {
153
+ if (config == null || typeof config !== 'object' || Array.isArray(config))
154
+ return config;
155
+ const record = config;
156
+ // Clone lazily: only spread into a new object once we know a field is actually being replaced,
157
+ // so a config with no live instances is returned untouched (===).
158
+ let out = record;
159
+ const clone = () => {
160
+ if (out === record)
161
+ out = { ...record };
162
+ return out;
163
+ };
164
+ if ('llm' in record && isLiveChatModel(record.llm)) {
165
+ clone().llm = describeLiveModel(record, record.llm);
166
+ }
167
+ // Hoist before the mutating clone() call so TS keeps the `Array` narrowing on `.length`.
168
+ const tools = record.tools;
169
+ if (Array.isArray(tools)) {
170
+ clone().tools = `[${tools.length} tool instance(s)]`;
171
+ }
172
+ const middleware = record.middleware;
173
+ if (Array.isArray(middleware)) {
174
+ clone().middleware = `[${middleware.length} middleware]`;
175
+ }
176
+ return out;
177
+ }
178
+ catch {
179
+ return config; // fail safe: fall back to redactValue over the untouched config
180
+ }
181
+ }
182
+ /**
183
+ * GS2-47 — render the CONFIG artifact: {@link redactValue} applies sensitive-field masking (keys
184
+ * kept, values masked) plus literal/pattern string redaction, then {@link safeStringify}. Fail-safe:
185
+ * any throw yields the fully-withheld marker rather than raw content ("redact more on error, never
186
+ * write a raw artifact because redaction hiccuped").
187
+ *
188
+ * GS2-54 (gap 3) + GS2-66 (residual 3) — before redaction, {@link stripLiveInstancesForConfig} swaps
189
+ * a live `llm` for a `{ type, model }` descriptor and the `tools`/`middleware` arrays for count
190
+ * descriptors, so those live instances' internals never serialize. This runs on a fresh clone only;
191
+ * the caller's config (and the {@link collectSecretValues} harvest already taken from it) is
192
+ * untouched, so inline secrets inside those instances are still scrubbed from the OTHER artifacts.
193
+ */
194
+ function renderConfig(config, redact, secrets) {
195
+ if (!redact)
196
+ return safeStringify(config);
197
+ try {
198
+ return safeStringify(redactValue(stripLiveInstancesForConfig(config), secrets));
199
+ }
200
+ catch {
201
+ return REDACTED;
202
+ }
203
+ }
204
+ /**
205
+ * GS2-47 — render a non-config STRUCTURED artifact (transcript, env, git-state): stringify (never
206
+ * throws), then literal/pattern-redact the text. Structural field-masking is intentionally NOT
207
+ * applied here (config-only, per the brief) so a legitimately `token`/`secret`-named field in tool
208
+ * output is not blanket-masked. {@link redactText} is itself fail-safe.
209
+ */
210
+ function renderStructured(value, redact, secrets) {
211
+ const raw = safeStringify(value);
212
+ return redact ? redactText(raw, secrets) : raw;
213
+ }
214
+ /**
215
+ * Write one timestamped `/debug-dump` archive under the GLOBAL `~/.gsloth/debug-dumps/<timestamp>/`
216
+ * (via `ensureGlobalGslothDir()` — mirrors how `resolveHistoryDbPath()` builds its path under the
217
+ * same dir — NOT the per-project cwd-relative helper of the same name elsewhere in this codebase).
218
+ * Contains: the full transcript, the resolved config, env/version info, the in-memory debugLog
219
+ * ring buffer, and (best-effort) git repo state.
220
+ *
221
+ * GS2-47 — unless `input.redact === false`, the shared secret-redaction pass
222
+ * ({@link file://./redactSecrets.ts}) is applied to EVERY artifact before it is written: the literal
223
+ * values of secret-named env vars + inline config secrets are substituted everywhere, a tight set of
224
+ * provider-key/auth-header patterns is masked, and the config's sensitive fields are masked in place.
225
+ * On opt-out the archive is raw and the caller surfaces the loud "may contain secrets" warning.
226
+ */
227
+ export function writeDebugDump(input) {
228
+ const redact = input.redact !== false; // default ON; only an explicit `false` opts out
229
+ const timestamp = debugDumpDirName();
230
+ const archiveDir = resolve(ensureGlobalGslothDir(), 'debug-dumps', timestamp);
231
+ mkdirSync(archiveDir, { recursive: true });
232
+ // Technique 1 (load-bearing): the literal secret values to scrub across ALL artifacts, gathered
233
+ // from env (by secret-named var) + the config (apiKeyEnvironmentVariable's target + inline
234
+ // secrets). `env` is the systemUtils accessor (process.env); collection is itself fail-safe.
235
+ const secrets = redact ? collectSecretValues(input.config, env) : [];
236
+ writeFileSync(resolve(archiveDir, 'transcript.json'), renderStructured(input.transcript, redact, secrets), 'utf8');
237
+ writeFileSync(resolve(archiveDir, 'config.json'), renderConfig(input.config, redact, secrets), 'utf8');
238
+ // GS2-56 — the always-on model-request snapshot: WHAT was actually fed to the model (the thing a
239
+ // bug report needs, previously omitted whenever the TUI `/debug` panel wasn't open). Written only
240
+ // when the caller threaded `agent.lastModelRequest` (a model call has happened), so before the
241
+ // first turn these files are simply absent. Both go through `renderStructured` (literal+pattern
242
+ // redaction, like transcript.json/env.json) rather than `renderConfig`'s field-masking, so a
243
+ // legitimately `token`/`secret`-named field inside a tool result or a schema isn't blanket-masked.
244
+ if (input.modelRequest) {
245
+ // model-request.json — the composed system prompt + tool defs (with JSON schema) + model params
246
+ // + tool-choice. `?? {}` keeps the artifact present-but-empty rather than serializing `undefined`.
247
+ writeFileSync(resolve(archiveDir, 'model-request.json'), renderStructured(input.modelRequest.extras ?? {}, redact, secrets), 'utf8');
248
+ // model-messages.json — the EXACT messages sent to the model for the last call (post-
249
+ // summarization / middleware), i.e. what the model actually saw. Deliberately a separate
250
+ // artifact from transcript.json (the conversation view): the two are distinct arrays.
251
+ writeFileSync(resolve(archiveDir, 'model-messages.json'), renderStructured(input.modelRequest.messages ?? [], redact, secrets), 'utf8');
252
+ }
253
+ // [[TUI-C27]] — approvals.json: the gate's own record of every gated decision, INCLUDING the
254
+ // approving branch, which previously left no trace anywhere in this archive. Each entry carries
255
+ // the exact prompt the rater was sent and its raw answer, so a reader can state what the rater was
256
+ // shown as well as what it said — specifically whether the user's own messages were in its view at
257
+ // round N, which is the first question anyone asks of a negotiation and the one no artifact here
258
+ // could answer. Same `renderStructured` pass as transcript.json (see `WriteDebugDumpInput`).
259
+ // Written only when there is something to write, so a session that gated nothing omits the file
260
+ // rather than shipping an empty array that reads as "the gate recorded nothing".
261
+ if (input.approvals && input.approvals.length > 0) {
262
+ writeFileSync(resolve(archiveDir, 'approvals.json'), renderStructured(input.approvals, redact, secrets), 'utf8');
263
+ }
264
+ // getSlothVersion() reads the installed package.json via the install dir set at CLI startup
265
+ // (setEntryPoint); guarded so a dump can never fail just because that wasn't wired (e.g. an
266
+ // unusual embedding), falling back to 'unknown'.
267
+ let gthVersion;
268
+ try {
269
+ gthVersion = getSlothVersion();
270
+ }
271
+ catch {
272
+ gthVersion = 'unknown';
273
+ }
274
+ const envInfo = {
275
+ gthVersion,
276
+ nodeVersion: process.version,
277
+ platform: process.platform,
278
+ model: input.modelDisplayName ?? 'unknown',
279
+ };
280
+ writeFileSync(resolve(archiveDir, 'env.json'), renderStructured(envInfo, redact, secrets), 'utf8');
281
+ writeFileSync(resolve(archiveDir, 'debug-log.txt'), redact ? redactText(getDebugLogBuffer().join('\n'), secrets) : getDebugLogBuffer().join('\n'), 'utf8');
282
+ const gitState = collectGitState(input.cwd ?? process.cwd());
283
+ if (gitState) {
284
+ writeFileSync(resolve(archiveDir, 'git-state.json'), renderStructured(gitState, redact, secrets), 'utf8');
285
+ }
286
+ return { archiveDir };
287
+ }
288
+ /**
289
+ * GS2-48 — how many trailing lines of the always-on in-memory debugLog ring buffer
290
+ * ({@link file://./debugUtils.ts}, cap {@link DEBUG_LOG_BUFFER_MAX} = 1000) the crash snapshot keeps.
291
+ * The buffer itself is the "always-on capture" the crash handler relies on (nothing is written to
292
+ * disk until a crash — O(N) memory, zero steady-state I/O); the snapshot copies only this TAIL to
293
+ * stay MINIMAL (a crash file is a triage artifact, not the full session). 200 is enough to show what
294
+ * the agent was doing in the moments before death without bloating the file.
295
+ */
296
+ const CRASH_DEBUG_LOG_TAIL_LINES = 200;
297
+ /**
298
+ * GS2-48 — flatten a thrown value (or a rejection reason, which need not be an `Error`) into a plain
299
+ * object BEFORE redaction. `Error.message`/`Error.stack` are NON-ENUMERABLE, so `Object.entries`
300
+ * (which {@link redactValue} walks with) would silently drop them — the crash file would carry no
301
+ * error and no stack. Explicitly copying them here is what makes the error survive the redaction pass.
302
+ * Tolerates a non-Error reason (string / number / object / undefined) without throwing.
303
+ */
304
+ function normalizeError(error) {
305
+ if (error instanceof Error) {
306
+ return { name: error.name, message: error.message, stack: error.stack };
307
+ }
308
+ try {
309
+ return {
310
+ name: 'NonError',
311
+ message: typeof error === 'string' ? error : safeStringify(error),
312
+ };
313
+ }
314
+ catch {
315
+ return { name: 'NonError', message: String(error) };
316
+ }
317
+ }
318
+ /**
319
+ * GS2-48 — write a MINIMAL, ALWAYS-REDACTED crash snapshot to
320
+ * `~/.gsloth/debug-dumps/crash-<timestamp>/crash.json` and return its paths.
321
+ *
322
+ * This is the unattended sibling of {@link writeDebugDump}: it fires from the process-level crash
323
+ * handler ({@link file://./crashHandler.ts}) with no human present, so — unlike `/debug-dump` — it
324
+ * exposes NO `redact` opt-out. Redaction (GS2-47, hard-dep) is MANDATORY and applied to the whole
325
+ * snapshot. It reuses this module's location convention (`ensureGlobalGslothDir()` + `debug-dumps/`)
326
+ * and rendering helpers, but deliberately stays minimal and crash-safe:
327
+ * - a SINGLE `crash.json` (not the multi-file interactive archive);
328
+ * - it does NOT shell out for git state (a subprocess is unsafe/slow while the process is dying);
329
+ * - it keeps only the last {@link CRASH_DEBUG_LOG_TAIL_LINES} debugLog lines and the transcript TAIL.
330
+ *
331
+ * Redaction shape (conscious choice): the entire normalized snapshot is passed through
332
+ * {@link redactValue} in one pass. That is stricter than {@link writeDebugDump}, which uses
333
+ * {@link redactText} (literal+pattern only) for its non-config artifacts to avoid field-masking a
334
+ * legitimately `token`/`secret`-named field in tool output. For an unattended crash file the
335
+ * over-masking is the SAFE direction and buys uniform circular-ref / depth / function handling in
336
+ * one place, so it is intended, not an oversight. The config subtree is stripped of live instances
337
+ * first (as `writeDebugDump` does) so a live LLM client's internals never serialize.
338
+ *
339
+ * Callers wrap this in their own try/catch (see {@link file://./crashHandler.ts}); it does its best
340
+ * to be fail-safe internally (`redactValue`/`safeStringify` never throw), but the ONE unavoidably
341
+ * throwing operation is the `mkdirSync`/`writeFileSync` to disk — an unwritable `~/.gsloth` is what
342
+ * the handler's degraded path is for.
343
+ */
344
+ export function writeCrashSnapshot(input) {
345
+ const crashDir = resolve(ensureGlobalGslothDir(), 'debug-dumps', `crash-${debugDumpDirName()}`);
346
+ mkdirSync(crashDir, { recursive: true });
347
+ // Redaction is unconditional here (no opt-out) — gather the literal secret values to scrub from
348
+ // env + config, exactly as writeDebugDump does. `env` is the systemUtils accessor (process.env).
349
+ const secrets = collectSecretValues(input.config, env);
350
+ let gthVersion;
351
+ try {
352
+ gthVersion = getSlothVersion();
353
+ }
354
+ catch {
355
+ gthVersion = 'unknown';
356
+ }
357
+ const snapshot = {
358
+ kind: 'crash',
359
+ origin: input.origin,
360
+ timestamp: new Date().toISOString(),
361
+ // Flattened to plain enumerable fields so message/stack survive the redaction walk.
362
+ error: normalizeError(input.error),
363
+ env: {
364
+ gthVersion,
365
+ nodeVersion: process.version,
366
+ platform: process.platform,
367
+ model: input.modelDisplayName ?? 'unknown',
368
+ },
369
+ // Strip live LLM/tool/middleware instances to short descriptors before redaction (as the
370
+ // interactive config artifact does), so their internals never reach disk. `?? null` so an
371
+ // absent config serializes as an explicit `null` (JSON drops an `undefined`-valued key).
372
+ config: stripLiveInstancesForConfig(input.config) ?? null,
373
+ debugLogTail: getDebugLogBuffer().slice(-CRASH_DEBUG_LOG_TAIL_LINES),
374
+ transcriptTail: input.transcriptTail ?? null,
375
+ };
376
+ // Single fail-safe redaction pass over the whole normalized snapshot (see doc above).
377
+ const crashFile = resolve(crashDir, 'crash.json');
378
+ writeFileSync(crashFile, safeStringify(redactValue(snapshot, secrets)), 'utf8');
379
+ return { crashDir, crashFile };
380
+ }
381
+ //# sourceMappingURL=debugDump.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debugDump.js","sourceRoot":"","sources":["../../src/utils/debugDump.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,29 @@
1
+ /**
2
+ * GS2-46 — read the current debug-log ring buffer (oldest first). Returns a copy so callers
3
+ * (e.g. the `/debug-dump` writer) can't mutate the live buffer.
4
+ */
5
+ export declare function getDebugLogBuffer(): string[];
1
6
  /**
2
7
  * Initialize debug logging based on config
3
8
  */
4
9
  export declare function initDebugLogging(enabled: boolean): void;
5
10
  /**
6
- * Log a debug message to the log file
11
+ * Log a debug message. Always pushed into the in-memory ring buffer (GS2-46); only appended to
12
+ * the on-disk log file when debug logging is enabled.
7
13
  */
8
14
  export declare function debugLog(message: string): void;
9
15
  /**
10
- * Log multiple lines with proper formatting
16
+ * Log multiple lines with proper formatting. Always buffered (GS2-46); delegates to `debugLog`
17
+ * for the on-disk gating.
11
18
  */
12
19
  export declare function debugLogMultiline(title: string, content: string): void;
13
20
  /**
14
- * Log an object using Node.js inspect with reasonable depth
21
+ * Log an object using Node.js inspect with reasonable depth. Always buffered (GS2-46); delegates
22
+ * to `debugLogMultiline`/`debugLog` for the on-disk gating.
15
23
  */
16
24
  export declare function debugLogObject(title: string, obj: unknown): void;
17
25
  /**
18
- * Log error with stack trace
26
+ * Log error with stack trace. Always buffered (GS2-46); delegates to `debugLog` for the on-disk
27
+ * gating.
19
28
  */
20
29
  export declare function debugLogError(context: string, error: unknown): void;
@@ -3,6 +3,30 @@ import { dirname, resolve } from 'node:path';
3
3
  import { inspect } from 'node:util';
4
4
  const DEBUG_LOG_FILE = 'gaunt-sloth.log';
5
5
  let debugEnabled = false;
6
+ /**
7
+ * GS2-46 — bounded in-memory ring buffer of debug-log lines, populated by every `debugLog*`
8
+ * variant UNCONDITIONALLY (independent of `debugEnabled`, which only gates the on-disk write
9
+ * below). This is deliberately always-on and forward-looking: GS2-48 (crash handler) expects
10
+ * "the last-N debugLog buffer lines already held in memory" to exist regardless of whether
11
+ * `config.debugLog` was ever turned on for the session, and `/debug-dump` (GS2-46) surfaces it
12
+ * verbatim. Capped so a long session can't grow this without bound.
13
+ */
14
+ const DEBUG_LOG_BUFFER_MAX = 1000;
15
+ const debugLogBuffer = [];
16
+ /** Push one formatted line into the ring buffer, evicting the oldest entry once at capacity. */
17
+ function pushToDebugLogBuffer(entry) {
18
+ debugLogBuffer.push(entry);
19
+ if (debugLogBuffer.length > DEBUG_LOG_BUFFER_MAX) {
20
+ debugLogBuffer.shift();
21
+ }
22
+ }
23
+ /**
24
+ * GS2-46 — read the current debug-log ring buffer (oldest first). Returns a copy so callers
25
+ * (e.g. the `/debug-dump` writer) can't mutate the live buffer.
26
+ */
27
+ export function getDebugLogBuffer() {
28
+ return [...debugLogBuffer];
29
+ }
6
30
  /**
7
31
  * Initialize debug logging based on config
8
32
  */
@@ -27,15 +51,17 @@ export function initDebugLogging(enabled) {
27
51
  }
28
52
  }
29
53
  /**
30
- * Log a debug message to the log file
54
+ * Log a debug message. Always pushed into the in-memory ring buffer (GS2-46); only appended to
55
+ * the on-disk log file when debug logging is enabled.
31
56
  */
32
57
  export function debugLog(message) {
58
+ const timestamp = new Date().toISOString();
59
+ const logEntry = `[${timestamp}] ${message}`;
60
+ pushToDebugLogBuffer(logEntry);
33
61
  if (!debugEnabled)
34
62
  return;
35
- const timestamp = new Date().toISOString();
36
- const logEntry = `[${timestamp}] ${message}\n`;
37
63
  try {
38
- appendFileSync(resolve(DEBUG_LOG_FILE), logEntry, 'utf8');
64
+ appendFileSync(resolve(DEBUG_LOG_FILE), `${logEntry}\n`, 'utf8');
39
65
  }
40
66
  catch (error) {
41
67
  // Ignore logging errors to prevent breaking the main flow
@@ -43,21 +69,19 @@ export function debugLog(message) {
43
69
  }
44
70
  }
45
71
  /**
46
- * Log multiple lines with proper formatting
72
+ * Log multiple lines with proper formatting. Always buffered (GS2-46); delegates to `debugLog`
73
+ * for the on-disk gating.
47
74
  */
48
75
  export function debugLogMultiline(title, content) {
49
- if (!debugEnabled)
50
- return;
51
76
  debugLog(`=== ${title} ===`);
52
77
  debugLog(content);
53
78
  debugLog(`=== End ${title} ===\n`);
54
79
  }
55
80
  /**
56
- * Log an object using Node.js inspect with reasonable depth
81
+ * Log an object using Node.js inspect with reasonable depth. Always buffered (GS2-46); delegates
82
+ * to `debugLogMultiline`/`debugLog` for the on-disk gating.
57
83
  */
58
84
  export function debugLogObject(title, obj) {
59
- if (!debugEnabled)
60
- return;
61
85
  try {
62
86
  // Use Node.js inspect with reasonable depth and no colors for log files
63
87
  const formatted = inspect(obj, { showHidden: false, depth: 3, colors: false });
@@ -68,11 +92,10 @@ export function debugLogObject(title, obj) {
68
92
  }
69
93
  }
70
94
  /**
71
- * Log error with stack trace
95
+ * Log error with stack trace. Always buffered (GS2-46); delegates to `debugLog` for the on-disk
96
+ * gating.
72
97
  */
73
98
  export function debugLogError(context, error) {
74
- if (!debugEnabled)
75
- return;
76
99
  debugLog(`❌ Error in ${context}:`);
77
100
  if (error instanceof Error) {
78
101
  debugLog(` Message: ${error.message}`);
@@ -1 +1 @@
1
- {"version":3,"file":"debugUtils.js","sourceRoot":"","sources":["../../src/utils/debugUtils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"debugUtils.js","sourceRoot":"","sources":["../../src/utils/debugUtils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Terminal columns `text` occupies. Zero-width clusters (combining marks, default-ignorables)
3
+ * count 0, wide clusters (CJK, emoji, fullwidth forms) count 2, everything else 1. ANSI escape
4
+ * sequences are not counted, so a pre-coloured string measures as what the user sees — but the
5
+ * slices below do not share that awareness, so do not read this as a licence to feed them one.
6
+ */
7
+ export declare function displayWidth(text: string): number;
8
+ /**
9
+ * Terminal columns `text` occupies **at most** — the same measurement as {@link displayWidth} but
10
+ * counting East-Asian Ambiguous characters as two columns rather than one, which is the widest any
11
+ * terminal will render them.
12
+ *
13
+ * For a budget that must hold on a terminal whose ambiguous-width policy is unknown, this is the
14
+ * only sound ruler: it is the supremum over the policies a terminal can be configured with, so a
15
+ * string that fits under it fits under all of them. See the module docblock for when to reach for
16
+ * this rather than {@link displayWidth}.
17
+ */
18
+ export declare function maxDisplayWidth(text: string): number;
19
+ /**
20
+ * The first grapheme cluster of `text`, or `''` when it is empty — i.e. the smallest piece a cut
21
+ * can leave behind, for a caller that has to make progress through a string one drawable glyph at
22
+ * a time.
23
+ */
24
+ export declare function firstCluster(text: string): string;
25
+ /**
26
+ * The longest LEADING run of whole clusters whose total width is at most `maxWidth` — i.e. keep
27
+ * the head, lose the tail. Total (never throws): a non-positive `maxWidth` yields `''`.
28
+ *
29
+ * A cluster is kept only if it fits ENTIRELY, so the result can come back one column short of
30
+ * `maxWidth` when the next cluster is two columns wide. That is the point: half a wide glyph
31
+ * cannot be drawn, and spending the column anyway is how text over-runs its budget.
32
+ *
33
+ * The clusters are produced one at a time and the walk stops at the budget, so the cost is the
34
+ * length of the RESULT rather than the length of the input — the input here is a whole tool-output
35
+ * line, which is unbounded.
36
+ */
37
+ export declare function sliceToWidth(text: string, maxWidth: number): string;
38
+ /**
39
+ * {@link sliceToWidth} measured by {@link maxDisplayWidth} — the head that fits the budget on any
40
+ * terminal, whatever it does with Ambiguous characters. The cut for a budget that must hold rather
41
+ * than merely look right.
42
+ */
43
+ export declare function sliceToMaxWidth(text: string, maxWidth: number): string;
44
+ /**
45
+ * The longest TRAILING run of whole clusters whose total width is at most `maxWidth` — i.e. keep
46
+ * the tail, lose the head. The mirror of {@link sliceToWidth}, for values whose end is the
47
+ * informative part (a path's leaf directory).
48
+ *
49
+ * This one materialises the clusters: it walks backwards, and cluster boundaries are only
50
+ * derivable from the front. Its callers pass a path or a model id, so the input is a line rather
51
+ * than a file.
52
+ */
53
+ export declare function sliceEndToWidth(text: string, maxWidth: number): string;