@gaunt-sloth/agent 2.0.0-alpha.22 → 2.0.0-alpha.24

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 (53) hide show
  1. package/README.md +73 -24
  2. package/dist/core/GthDeepAgent.js +49 -2
  3. package/dist/core/GthDeepAgent.js.map +1 -1
  4. package/dist/core/debugCapture.d.ts +1 -1
  5. package/dist/core/debugCapture.js.map +1 -1
  6. package/dist/core/subagentProfiles.d.ts +50 -0
  7. package/dist/core/subagentProfiles.js +81 -0
  8. package/dist/core/subagentProfiles.js.map +1 -0
  9. package/dist/middleware/binaryContentInjectionMiddleware.d.ts +8 -1
  10. package/dist/middleware/binaryContentInjectionMiddleware.js +11 -2
  11. package/dist/middleware/binaryContentInjectionMiddleware.js.map +1 -1
  12. package/dist/middleware/frontendImageInjectionMiddleware.d.ts +80 -0
  13. package/dist/middleware/frontendImageInjectionMiddleware.js +146 -0
  14. package/dist/middleware/frontendImageInjectionMiddleware.js.map +1 -0
  15. package/dist/middleware/registry.js +36 -1
  16. package/dist/middleware/registry.js.map +1 -1
  17. package/dist/middleware/types.d.ts +16 -2
  18. package/dist/modules/a2a/A2AClientWrapper.d.ts +1 -1
  19. package/dist/modules/a2a/A2AClientWrapper.js +19 -5
  20. package/dist/modules/a2a/A2AClientWrapper.js.map +1 -1
  21. package/dist/modules/apiAgUiModule.d.ts +68 -0
  22. package/dist/modules/apiAgUiModule.js +95 -4
  23. package/dist/modules/apiAgUiModule.js.map +1 -1
  24. package/dist/modules/interactiveSessionModule.js +110 -34
  25. package/dist/modules/interactiveSessionModule.js.map +1 -1
  26. package/dist/modules/slashCommands.d.ts +328 -0
  27. package/dist/modules/slashCommands.js +598 -0
  28. package/dist/modules/slashCommands.js.map +1 -0
  29. package/dist/resolvers.js +15 -0
  30. package/dist/resolvers.js.map +1 -1
  31. package/dist/tools/GthCustomToolkit.js +72 -3
  32. package/dist/tools/GthCustomToolkit.js.map +1 -1
  33. package/dist/tools/GthDevToolkit.d.ts +5 -2
  34. package/dist/tools/GthDevToolkit.js +28 -7
  35. package/dist/tools/GthDevToolkit.js.map +1 -1
  36. package/dist/tools/GthFileSystemToolkit.d.ts +16 -0
  37. package/dist/tools/GthFileSystemToolkit.js +218 -110
  38. package/dist/tools/GthFileSystemToolkit.js.map +1 -1
  39. package/dist/tools/McpResourceTool.d.ts +31 -0
  40. package/dist/tools/McpResourceTool.js +106 -0
  41. package/dist/tools/McpResourceTool.js.map +1 -0
  42. package/dist/tools/shell/hardline.js +3 -3
  43. package/dist/tools/shell/hardline.js.map +1 -1
  44. package/package.json +2 -2
  45. package/dist/tools/shell/allowlist.d.ts +0 -11
  46. package/dist/tools/shell/allowlist.js +0 -12
  47. package/dist/tools/shell/allowlist.js.map +0 -1
  48. package/dist/tools/shell/arity.d.ts +0 -11
  49. package/dist/tools/shell/arity.js +0 -12
  50. package/dist/tools/shell/arity.js.map +0 -1
  51. package/dist/tools/shell/normalize.d.ts +0 -10
  52. package/dist/tools/shell/normalize.js +0 -11
  53. package/dist/tools/shell/normalize.js.map +0 -1
@@ -0,0 +1,598 @@
1
+ /**
2
+ * Pure, React-free slash-command layer shared by BOTH interactive surfaces (GS2-8): the Ink
3
+ * TUI (`packages/app/src/tui/`, which re-exports this module) and the readline `--no-tui`
4
+ * session (`interactiveSessionModule.ts` in this package). One registry, one source of truth —
5
+ * a command added here appears in `/help` on both surfaces automatically.
6
+ *
7
+ * Mirrors how the TUI's `viewModel.ts` keeps its fold logic out of the components: the registry
8
+ * and the parse/dispatch helpers here are unit-testable in isolation, and each surface is the
9
+ * only place that turns the resulting {@link SlashCommandResult} into its own state / side
10
+ * effects (the TUI's `<App>` clears the transcript / pushes notices / quits; the readline loop
11
+ * prints notices and degrades TUI-only effects with a clear "needs the TUI" message).
12
+ *
13
+ * The registry is a plain array so later layers (e.g. extension-registered commands, EXT-5)
14
+ * can append more entries via {@link createCommandRegistry} without this module changing.
15
+ */
16
+ /**
17
+ * Build the compact, read-only `/config` summary (GS2-1): a handful of the most orienting
18
+ * resolved-config fields, one per line, with a pointer to `gth config print` for the full view.
19
+ * Pure and secret-free — it only reads non-sensitive scalar fields (never API keys / the live
20
+ * llm instance). Used by the App to fill {@link SlashCommandContext.configSummary}.
21
+ */
22
+ export function formatConfigSummary(config) {
23
+ const fmt = (v) => typeof v === 'string' ? v : Array.isArray(v) ? JSON.stringify(v) : String(v);
24
+ const lines = [];
25
+ lines.push(`Model: ${config.modelDisplayName || 'unknown'}`);
26
+ lines.push(`Agent backend: ${config.agent?.backend ?? 'lean'}`);
27
+ if (config.filesystem !== undefined)
28
+ lines.push(`Filesystem: ${fmt(config.filesystem)}`);
29
+ if (config.streamOutput !== undefined)
30
+ lines.push(`Stream output: ${config.streamOutput}`);
31
+ if (config.useColour !== undefined)
32
+ lines.push(`Colour: ${config.useColour}`);
33
+ const commandNames = config.commands ? Object.keys(config.commands) : [];
34
+ if (commandNames.length > 0)
35
+ lines.push(`Commands configured: ${commandNames.join(', ')}`);
36
+ lines.push('Run `gth config print` for the full resolved config (secrets redacted).');
37
+ return lines;
38
+ }
39
+ /**
40
+ * The `/config` notice, from the pre-rendered summary lines (or an unavailable fallback).
41
+ *
42
+ * TUI-C19 — when config-validation `warnings` are present (unknown keys / deprecated names), they
43
+ * are rendered FIRST, as the details the standing "config has problems" advisory line points at,
44
+ * then a blank spacer, then the resolved summary. A clean config (no warnings) reads exactly as
45
+ * before. Tone flips to `warn` (yellow) while there are warnings so the block reads as caution.
46
+ */
47
+ export function configNotice(summary, warnings) {
48
+ const summaryLines = summary && summary.length > 0
49
+ ? summary
50
+ : ['Configuration details are not available in this session.'];
51
+ const hasWarnings = !!warnings && warnings.length > 0;
52
+ const lines = hasWarnings
53
+ ? [
54
+ `${warnings.length === 1 ? 'Config warning' : `Config warnings (${warnings.length})`}:`,
55
+ ...warnings.map((w) => ` • ${w}`),
56
+ '',
57
+ ...summaryLines,
58
+ ]
59
+ : summaryLines;
60
+ return {
61
+ title: 'Resolved configuration',
62
+ lines,
63
+ ...(hasWarnings ? { tone: 'warn' } : {}),
64
+ };
65
+ }
66
+ /** Shared "history is unavailable" body (history off / DB missing), reused by all three commands. */
67
+ const HISTORY_UNAVAILABLE_LINES = [
68
+ 'No local session history is available in this session.',
69
+ 'Enable it with `history.enabled: true` in your gsloth config (local only, opt-in).',
70
+ ];
71
+ /** The `/history` notice (GS2-7): recent recorded sessions, or an "unavailable" fallback. */
72
+ export function historyNotice(summary) {
73
+ return {
74
+ title: 'Recent sessions',
75
+ lines: summary && summary.length > 0 ? summary : HISTORY_UNAVAILABLE_LINES,
76
+ };
77
+ }
78
+ /** The `/insights` notice (GS2-7): local analytics summary, or an "unavailable" fallback. */
79
+ export function insightsNotice(summary) {
80
+ return {
81
+ title: 'Session insights (local only)',
82
+ lines: summary && summary.length > 0 ? summary : HISTORY_UNAVAILABLE_LINES,
83
+ };
84
+ }
85
+ /**
86
+ * The `/search` notice (GS2-7). With no query it prints usage; otherwise it runs the injected
87
+ * fail-soft {@link SlashCommandContext.historySearch} provider and renders its result lines. When
88
+ * no provider is bound (no store), it reports history as unavailable.
89
+ */
90
+ export function searchNotice(args, search) {
91
+ const query = args.join(' ').trim();
92
+ if (!query) {
93
+ return {
94
+ title: 'Search session history',
95
+ lines: ['Usage: /search <terms> — full-text search across your recorded sessions.'],
96
+ };
97
+ }
98
+ if (!search) {
99
+ return { title: `Search: "${query}"`, lines: HISTORY_UNAVAILABLE_LINES };
100
+ }
101
+ return { title: `Search: "${query}"`, lines: search(query) };
102
+ }
103
+ /**
104
+ * Parse a raw input line into a slash command, or `null` if it is not one. A line is a slash
105
+ * command iff its first non-whitespace character is `/` AND no further `/` appears after the
106
+ * leading one (GS2-8, Mari's dogfood addendum): a pasted filesystem path like
107
+ * `/usr/home/bob/test.md` contains later slashes, so it falls through as ordinary prompt text
108
+ * instead of being swallowed as an unknown command. The name is lower-cased; remaining
109
+ * whitespace-separated tokens are the args.
110
+ */
111
+ export function parseSlashCommand(input) {
112
+ const trimmed = input.trim();
113
+ if (!trimmed.startsWith('/'))
114
+ return null;
115
+ // The `/`-vs-path heuristic: a real command has NO further `/` after the leading one.
116
+ if (trimmed.indexOf('/', 1) !== -1)
117
+ return null;
118
+ const tokens = trimmed.slice(1).split(/\s+/).filter(Boolean);
119
+ if (tokens.length === 0)
120
+ return null; // a bare "/" is not a command
121
+ const [name, ...args] = tokens;
122
+ return { name: name.toLowerCase(), args };
123
+ }
124
+ /**
125
+ * TUI-C10 — the discovery-menu trigger test. The Ink `<PromptInput>` shows the slash-command menu
126
+ * while the user is typing a bare command: the raw input is a menu query iff its first character is
127
+ * `/` and it contains no whitespace yet (once a space is typed the user is entering args, so the
128
+ * menu closes and normal dispatch takes over). Returns the lower-cased query AFTER the slash (so a
129
+ * bare `/` yields `''` = "show everything"), or `null` when the input is not a menu trigger.
130
+ *
131
+ * Kept pure and next to the registry (like {@link parseSlashCommand}) so the menu's show/hide and
132
+ * filter logic is unit-testable without React.
133
+ *
134
+ * GS2-8 — mirrors {@link parseSlashCommand}'s `/`-vs-path heuristic: input with a later `/`
135
+ * (a pasted path like `/usr/bin`) is not a command, so it never triggers the menu either.
136
+ */
137
+ export function slashMenuQuery(input) {
138
+ if (!/^\/\S*$/.test(input))
139
+ return null;
140
+ if (input.indexOf('/', 1) !== -1)
141
+ return null; // later `/` ⇒ a path, not a command query
142
+ return input.slice(1).toLowerCase();
143
+ }
144
+ /**
145
+ * TUI-C10 — filter the registry down to the commands that match a menu query, most-relevant first.
146
+ * Prefix matches (the name starts with the query) rank above looser substring matches; within each
147
+ * bucket the registry's own order is preserved (so extension-registered commands — appended to the
148
+ * array — naturally sort after the built-ins). An empty query returns the whole registry, so a bare
149
+ * `/` lists every command including any the extensions added (never a hardcoded list).
150
+ *
151
+ * Pure: takes the registry the caller already built via {@link createCommandRegistry}, so the menu
152
+ * automatically reflects extension commands without this layer knowing they exist.
153
+ */
154
+ export function filterSlashCommands(registry, query) {
155
+ const q = query.toLowerCase();
156
+ if (!q)
157
+ return [...registry];
158
+ const prefix = registry.filter((c) => c.name.startsWith(q));
159
+ const substring = registry.filter((c) => !c.name.startsWith(q) && c.name.includes(q));
160
+ return [...prefix, ...substring];
161
+ }
162
+ /**
163
+ * The notice for the tool-detail toggle, given the RESULTING (post-toggle) state. Shared by the
164
+ * `/verbose` command (GS2-8 rename of `/tools`) and the Ctrl+T key handler so the copy
165
+ * is single-sourced (TUI-C14).
166
+ */
167
+ export function toolsToggleNotice(expanded) {
168
+ return expanded
169
+ ? {
170
+ title: 'Tool details: on',
171
+ lines: [
172
+ 'Tool calls now show their full inputs and results in the chat history.',
173
+ 'Applies to new turns — run /verbose again to collapse them to summaries.',
174
+ ],
175
+ }
176
+ : {
177
+ title: 'Tool details: off',
178
+ lines: [
179
+ 'Tool calls now show as a single summary line in the chat history.',
180
+ 'Applies to new turns — run /verbose again to show full inputs and results.',
181
+ ],
182
+ };
183
+ }
184
+ /**
185
+ * The notice for the debug-panel toggle, given the RESULTING (post-toggle) state. Shared so the
186
+ * command reports exactly the state the component will apply.
187
+ */
188
+ export function debugToggleNotice(visible) {
189
+ return visible
190
+ ? {
191
+ title: 'Debug panel: shown',
192
+ lines: [
193
+ 'Docked panel with the subagent tree and sent-to-model / raw-response views.',
194
+ 'Run /debug again to hide it; Tab cycles its views.',
195
+ ],
196
+ }
197
+ : {
198
+ title: 'Debug panel: hidden',
199
+ lines: [
200
+ 'The docked subagent + debug views are now closed.',
201
+ 'Run /debug again to bring them back.',
202
+ ],
203
+ };
204
+ }
205
+ /**
206
+ * The notice for the `/auto-approve` toggle (EXT-12), given the RESULTING (post-apply) state.
207
+ * Shared so the command reports exactly the state the App applies. ON is rendered 'warn' (yellow)
208
+ * because it disables the approval gate for the session; OFF is 'info'.
209
+ */
210
+ export function autoApproveNotice(on) {
211
+ return on
212
+ ? {
213
+ title: 'Auto-approve ON — shell commands run without asking',
214
+ lines: [
215
+ 'run_shell_command will now execute WITHOUT the per-command approval prompt.',
216
+ 'Session-scoped only (not saved); run /auto-approve off to require approvals.',
217
+ 'The hardline safety floor still blocks catastrophic commands.',
218
+ ],
219
+ tone: 'warn',
220
+ }
221
+ : {
222
+ title: 'Auto-approve OFF — approvals required',
223
+ lines: [
224
+ 'run_shell_command will prompt for approval again before each command.',
225
+ 'Run /auto-approve (or /auto-approve on) to re-enable session-wide auto-approval.',
226
+ ],
227
+ };
228
+ }
229
+ /**
230
+ * EXT-12 — parse the `/auto-approve` argument: no arg (or `toggle`) flips; `on`/`off` (and the
231
+ * friendly synonyms `enable`/`disable`, `true`/`false`) set explicitly. Returns `null` for an
232
+ * unrecognized argument so the command can render a usage hint instead of guessing.
233
+ */
234
+ export function parseAutoApproveArg(args) {
235
+ if (args.length === 0)
236
+ return 'toggle';
237
+ const arg = args[0].toLowerCase();
238
+ if (arg === 'toggle')
239
+ return 'toggle';
240
+ if (arg === 'on' || arg === 'enable' || arg === 'true')
241
+ return 'on';
242
+ if (arg === 'off' || arg === 'disable' || arg === 'false')
243
+ return 'off';
244
+ return null;
245
+ }
246
+ /**
247
+ * TUI-C18 — resolve a `/reasoning` invocation against the committed turns' reasoning (in transcript
248
+ * order, index 0 = turn 1). Pure, so the whole selection + friendly-notice logic is unit-testable
249
+ * without React:
250
+ *
251
+ * - **no arg** → the most recent turn that actually recorded thinking; if none exists, a friendly
252
+ * info notice (nothing to show).
253
+ * - **`<n>`** → turn `n` (1-based). A non-positive / non-integer / out-of-range `n` → a warn notice;
254
+ * a valid turn that recorded no thinking → an info notice. Otherwise a `reprintReasoning` request.
255
+ *
256
+ * The App renders a `reprintReasoning` result as a fresh reasoning block (reusing the TUI-C15
257
+ * styling) and a `notice` result via the shared `CommandNotice`.
258
+ */
259
+ export function resolveReasoning(reasonings, args) {
260
+ const count = reasonings.length;
261
+ const has = (i) => (reasonings[i] ?? '').trim().length > 0;
262
+ if (args.length > 0) {
263
+ // `Number(...)` (not parseInt) so "2x"/"1.5"/"" don't silently coerce to a valid index.
264
+ const raw = args[0];
265
+ const n = Number(raw);
266
+ if (!Number.isInteger(n) || n < 1 || n > count) {
267
+ return {
268
+ notice: {
269
+ title: `No turn ${raw}`,
270
+ lines: count === 0
271
+ ? [
272
+ 'This session has no committed turns yet.',
273
+ 'Ask something first, then run /reasoning.',
274
+ ]
275
+ : [
276
+ `Pick a turn between 1 and ${count} (this session has ${count} so far).`,
277
+ 'Run /reasoning with no number for the most recent turn that recorded thinking.',
278
+ ],
279
+ tone: 'warn',
280
+ },
281
+ };
282
+ }
283
+ const idx = n - 1;
284
+ if (!has(idx)) {
285
+ return {
286
+ notice: {
287
+ title: `Turn ${n} has no thinking`,
288
+ lines: [
289
+ `Turn ${n} didn't record a thinking layer (only some models stream one).`,
290
+ 'Run /reasoning (no number) to jump to the most recent turn that did.',
291
+ ],
292
+ },
293
+ };
294
+ }
295
+ return { reprintReasoning: { reasoning: reasonings[idx], turnNumber: n } };
296
+ }
297
+ // No arg: walk back to the most recent turn that recorded thinking.
298
+ for (let i = count - 1; i >= 0; i--) {
299
+ if (has(i))
300
+ return { reprintReasoning: { reasoning: reasonings[i], turnNumber: i + 1 } };
301
+ }
302
+ return {
303
+ notice: {
304
+ title: 'No thinking to show',
305
+ lines: [
306
+ 'No turn in this session has recorded a thinking layer yet.',
307
+ 'Reasoning appears for models that stream a thinking / chain-of-thought layer.',
308
+ ],
309
+ },
310
+ };
311
+ }
312
+ /**
313
+ * `/debug-dump` when no `dumpDebugSession` writer is injected — the fixture agent, or the readline
314
+ * (`--no-tui`) session, which shares this registry (GS2-8) but has no session archive writer.
315
+ */
316
+ const DEBUG_DUMP_UNAVAILABLE_LINES = [
317
+ 'No debug-dump writer is available in this session.',
318
+ 'This is only available in a real TUI session (not the fixture agent or the --no-tui fallback).',
319
+ ];
320
+ /**
321
+ * GS2-47 — resolve whether the `/debug-dump` archive should be redacted. ON by default; opt out via
322
+ * the config (`debugDump.redact: false`) OR the `--unsafe-no-redact` command flag. Any uncertainty
323
+ * (no/non-object config) defaults to redacting — fail safe. `resolvedConfig` is opaque here, so this
324
+ * reads the flag structurally without depending on the `GthConfig` type.
325
+ */
326
+ export function resolveDebugDumpRedact(resolvedConfig, args) {
327
+ if (args.some((a) => a === '--unsafe-no-redact' || a === '--no-redact'))
328
+ return false;
329
+ const debugDump = resolvedConfig?.debugDump;
330
+ if (debugDump &&
331
+ typeof debugDump === 'object' &&
332
+ debugDump.redact === false) {
333
+ return false;
334
+ }
335
+ return true;
336
+ }
337
+ /**
338
+ * The `/debug-dump` success notice (a standard 3-line CommandNotice — DL-1: no command reads as
339
+ * "does nothing"). GS2-47 flips the default to REDACTED: when redaction ran (the default) the note
340
+ * is softened ("secrets redacted; review before sharing") and points at the opt-out. When the user
341
+ * opted OUT (raw archive) it is the loud, impossible-to-miss UNSANITIZED warning. Colour follows
342
+ * DL-8 / the tone rule in maintenance/ux-guidelines.md: the safe, redacted default is normal
343
+ * feedback (no `tone` ⇒ info), while the raw opt-out is caution and so `tone: 'warn'` (yellow) —
344
+ * mirroring how `autoApproveNotice` reserves yellow for the dangerous (gate-off) state. Redaction is
345
+ * best-effort pattern-based, so even the softened note still says review-before-sharing.
346
+ */
347
+ export function debugDumpNotice(archiveDir, redacted) {
348
+ if (redacted) {
349
+ return {
350
+ title: 'Debug dump written — secrets redacted',
351
+ lines: [
352
+ `Archive: ${archiveDir}`,
353
+ '',
354
+ 'Secrets were redacted (API keys, tokens and auth headers replaced with <redacted>).',
355
+ 'Redaction is best-effort and pattern-based — review before sharing.',
356
+ '',
357
+ 'To write a raw, unredacted archive: set `debugDump.redact: false` in your gsloth config,',
358
+ 'or run `/debug-dump --unsafe-no-redact`.',
359
+ ],
360
+ };
361
+ }
362
+ return {
363
+ title: '⚠️ Debug dump written — UNSANITIZED, review before sharing',
364
+ lines: [
365
+ `Archive: ${archiveDir}`,
366
+ '',
367
+ 'This archive contains the full transcript, resolved config, env info, debug log and git',
368
+ 'state AS-IS — it may include secrets: API keys, tokens, file contents, env vars.',
369
+ 'Review it carefully before sending it anywhere.',
370
+ ],
371
+ tone: 'warn',
372
+ };
373
+ }
374
+ /**
375
+ * Build the default command registry. Returns a fresh array each call so callers may push
376
+ * extension commands onto it (EXT-5) without sharing mutable module state.
377
+ */
378
+ export function createCommandRegistry() {
379
+ return [
380
+ {
381
+ name: 'help',
382
+ description: 'List available slash commands',
383
+ // The help body needs the whole registry, so dispatch special-cases `/help` and
384
+ // calls formatHelp; this stub keeps `/help` listed and self-described.
385
+ run: () => ({ message: 'Available commands (see /help).' }),
386
+ },
387
+ {
388
+ name: 'clear',
389
+ description: 'Clear the transcript',
390
+ // The visible feedback is the live-frame <ClearBanner> (rendered outside <Static> so it
391
+ // survives the transcript wipe), so no committed notice here.
392
+ run: () => ({ clearTranscript: true }),
393
+ },
394
+ {
395
+ name: 'debug',
396
+ description: 'Toggle the docked subagents + debug panel',
397
+ availableDuringRun: true,
398
+ // State-aware: report the notice for the state the toggle will land on (the inverse of now).
399
+ run: (ctx) => ({ toggleDebug: true, notice: debugToggleNotice(!ctx.debugVisible) }),
400
+ },
401
+ {
402
+ name: 'verbose',
403
+ description: 'Toggle tool-call detail (collapsed summary ⇄ expanded args/result)',
404
+ availableDuringRun: true,
405
+ // State-aware: report the notice for the state the toggle will land on (the inverse of now).
406
+ run: (ctx) => ({ toggleTools: true, notice: toolsToggleNotice(!ctx.toolsExpanded) }),
407
+ },
408
+ {
409
+ name: 'auto-approve',
410
+ description: 'Auto-approve shell commands this session (/auto-approve on|off; no arg toggles)',
411
+ // Available mid-turn so the user can stop being prompted for the run's remaining tool calls
412
+ // (EXT-12). The App owns the runner flag, so it applies the change and commits the notice for
413
+ // the landed state (the command can't read the flag here).
414
+ availableDuringRun: true,
415
+ run: (_ctx, args) => {
416
+ const action = parseAutoApproveArg(args);
417
+ if (action === null) {
418
+ return {
419
+ notice: {
420
+ title: `Unknown option: ${args[0]}`,
421
+ lines: [
422
+ 'Usage: /auto-approve [on|off] — with no argument it toggles.',
423
+ 'When ON, shell commands run this session without the per-command prompt.',
424
+ ],
425
+ tone: 'warn',
426
+ },
427
+ };
428
+ }
429
+ return { autoApprove: action };
430
+ },
431
+ },
432
+ {
433
+ name: 'yolo',
434
+ description: 'Alias for /auto-approve (toggles session-wide shell auto-approval)',
435
+ availableDuringRun: true,
436
+ // Back-compat alias: a bare toggle, routed through the same auto-approve apply path. EXT-12.
437
+ run: () => ({ autoApprove: 'toggle' }),
438
+ },
439
+ {
440
+ name: 'exit',
441
+ description: 'Quit the session',
442
+ run: () => ({ exit: true }),
443
+ },
444
+ {
445
+ name: 'quit',
446
+ description: 'Quit the session (alias of /exit)',
447
+ // GS2-8 — an equal-citizen alias, no deprecation: both names quit.
448
+ run: () => ({ exit: true }),
449
+ },
450
+ {
451
+ name: 'status',
452
+ description: 'Show session status (mode, model, turns)',
453
+ availableDuringRun: true,
454
+ // GS2-8 — absorbs the old `/mode` command: the mode line (and how to change it) now reads
455
+ // as part of one status block alongside the model and turn count already in context.
456
+ run: (ctx) => ({
457
+ notice: {
458
+ title: 'Session status',
459
+ lines: [
460
+ `Mode: ${ctx.mode} — how the agent handles your messages this session.`,
461
+ `Model: ${ctx.modelDisplayName || 'unknown'}`,
462
+ `Turns so far: ${ctx.turnCount}`,
463
+ 'Restart with a different subcommand to change the mode (e.g. `gth chat`).',
464
+ ],
465
+ },
466
+ }),
467
+ },
468
+ {
469
+ name: 'config',
470
+ description: 'Show the resolved configuration (read-only)',
471
+ availableDuringRun: true,
472
+ // Read-only discovery: surface the pre-rendered, secret-free summary the App computed from
473
+ // the resolved config, prefixed with any load-time validation warnings (TUI-C19 — the
474
+ // details the standing advisory line points at). Editing lives in `gth init` / the config
475
+ // file, not here (GS2-1).
476
+ run: (ctx) => ({ notice: configNotice(ctx.configSummary, ctx.configWarnings) }),
477
+ },
478
+ {
479
+ name: 'history',
480
+ description: 'Show recent recorded sessions (local, opt-in history)',
481
+ availableDuringRun: true,
482
+ // Read-only discovery, mirroring /config: render the App's fail-soft, pre-built summary.
483
+ run: (ctx) => ({ notice: historyNotice(ctx.historySummary) }),
484
+ },
485
+ {
486
+ name: 'search',
487
+ description: 'Search recorded session history (/search <terms>)',
488
+ availableDuringRun: true,
489
+ // Dynamic query, so it calls the App-injected fail-soft search provider (stubbable in tests).
490
+ run: (ctx, args) => ({ notice: searchNotice(args, ctx.historySearch) }),
491
+ },
492
+ {
493
+ name: 'insights',
494
+ description: 'Show local analytics over recorded sessions (tokens, cost, top tools)',
495
+ availableDuringRun: true,
496
+ run: (ctx) => ({ notice: insightsNotice(ctx.insightsSummary) }),
497
+ },
498
+ {
499
+ name: 'model',
500
+ description: 'Show the current model / provider',
501
+ availableDuringRun: true,
502
+ run: (ctx) => ({
503
+ notice: {
504
+ title: `Model: ${ctx.modelDisplayName || 'unknown'}`,
505
+ lines: [
506
+ 'This is the model answering your messages this session.',
507
+ 'Change the default via `gth init` or your gsloth config.',
508
+ ],
509
+ },
510
+ }),
511
+ },
512
+ {
513
+ name: 'reasoning',
514
+ description: "Reprint a turn's thinking (/reasoning [n]; no number = latest with thinking)",
515
+ // Read-only recall of a past turn's thinking — safe to run mid-turn, like /history and /config.
516
+ availableDuringRun: true,
517
+ // Pure: resolve the target from the App-provided committed reasonings; the App renders the
518
+ // reprint (reusing TUI-C15 styling) or the friendly notice.
519
+ run: (ctx, args) => resolveReasoning(ctx.turnReasonings ?? [], args),
520
+ },
521
+ {
522
+ name: 'debug-dump',
523
+ description: 'Dump transcript + config + env + debug log to ~/.gsloth/debug-dumps (secrets redacted; --unsafe-no-redact keeps raw)',
524
+ // Read-only from the transcript/thread's perspective (it only writes a diagnostic archive,
525
+ // never mutates session state), so it's useful precisely when something is going wrong
526
+ // mid-turn — mirrors /history, /config, /debug being availableDuringRun.
527
+ availableDuringRun: true,
528
+ run: (ctx, args) => {
529
+ if (!ctx.dumpDebugSession) {
530
+ return {
531
+ notice: {
532
+ title: 'Debug dump unavailable',
533
+ lines: DEBUG_DUMP_UNAVAILABLE_LINES,
534
+ tone: 'warn',
535
+ },
536
+ };
537
+ }
538
+ // GS2-47 — redact by default; opt out via config `debugDump.redact: false` or the
539
+ // `--unsafe-no-redact` flag. The resolved flag is threaded into the writer AND picks the
540
+ // notice (softened when redacted, loud "unsanitized" warning when raw).
541
+ const redact = resolveDebugDumpRedact(ctx.resolvedConfig, args);
542
+ const { archiveDir } = ctx.dumpDebugSession({
543
+ transcript: ctx.transcript ?? [],
544
+ config: ctx.resolvedConfig,
545
+ modelDisplayName: ctx.modelDisplayName,
546
+ redact,
547
+ });
548
+ return { notice: debugDumpNotice(archiveDir, redact) };
549
+ },
550
+ },
551
+ ];
552
+ }
553
+ /** Build the `/help` notice from a registry: one body line per command (`/name — description`). */
554
+ export function formatHelp(registry) {
555
+ return {
556
+ title: 'Slash commands',
557
+ lines: registry.map((c) => `/${c.name} — ${c.description}`),
558
+ };
559
+ }
560
+ /**
561
+ * Dispatch a parsed command against a registry. Unknown commands return a friendly hint
562
+ * rather than throwing, so the component can render it as a system line and never forward
563
+ * the text to the model.
564
+ *
565
+ * EXT-12 — when `options.duringRun` is set (a turn is streaming), commands that are not marked
566
+ * {@link SlashCommand.availableDuringRun} are refused with a friendly notice rather than run,
567
+ * so mid-turn input can only reach the safe, non-mutating commands (`/auto-approve`, `/verbose`,
568
+ * `/debug`, …). `/help` is always allowed.
569
+ */
570
+ export function dispatchSlashCommand(parsed, registry, ctx, options = {}) {
571
+ if (parsed.name === 'help') {
572
+ return { notice: formatHelp(registry) };
573
+ }
574
+ const command = registry.find((c) => c.name === parsed.name);
575
+ if (!command) {
576
+ return {
577
+ notice: {
578
+ title: `Unknown command: /${parsed.name}`,
579
+ lines: ["That isn't a recognized slash command.", 'Run /help to see everything available.'],
580
+ tone: 'warn',
581
+ },
582
+ };
583
+ }
584
+ if (options.duringRun && !command.availableDuringRun) {
585
+ return {
586
+ notice: {
587
+ title: `/${command.name} is not available while the agent is working`,
588
+ lines: [
589
+ 'Wait for the current turn to finish, then run it again.',
590
+ 'Commands like /auto-approve, /verbose and /debug do work mid-turn.',
591
+ ],
592
+ tone: 'warn',
593
+ },
594
+ };
595
+ }
596
+ return command.run(ctx, parsed.args);
597
+ }
598
+ //# sourceMappingURL=slashCommands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slashCommands.js","sourceRoot":"","sources":["../../src/modules/slashCommands.ts"],"names":[],"mappings":"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"}
package/dist/resolvers.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import { debugLog } from '@gaunt-sloth/core/utils/debugUtils.js';
12
12
  import { displayInfo, displayWarning } from '@gaunt-sloth/core/utils/consoleUtils.js';
13
13
  import { createA2AAgentTool } from '#src/tools/A2AAgentTool.js';
14
+ import { createMcpResourceTools } from '#src/tools/McpResourceTool.js';
14
15
  import { prepareMcpTools } from '#src/utils/mcpUtils.js';
15
16
  import { formatMcpConnectFailureMessage } from '#src/utils/mcpAuthError.js';
16
17
  import { createAuthProviderAndAuthenticate } from '#src/mcp/OAuthClientProviderImpl.js';
@@ -98,6 +99,9 @@ export function createResolvers() {
98
99
  // instructions contribute nothing (trimmed, then omitted). The captured value is injected —
99
100
  // fenced + per-server-labelled — into the composed system prompt on BOTH backends.
100
101
  if (mcpClientInstance) {
102
+ // Narrow the mutable `| null` field once so the synthesized resource tools can close over a
103
+ // non-null client (like A2A tools close over their wrapper).
104
+ const activeClient = mcpClientInstance;
101
105
  const serverNames = Object.keys(config.mcpServers || {});
102
106
  for (const serverName of serverNames) {
103
107
  try {
@@ -106,6 +110,17 @@ export function createResolvers() {
106
110
  if (instructions) {
107
111
  mcpServerInstructions.push({ server: serverName, instructions });
108
112
  }
113
+ // EXT-48: if this server advertises the `resources` capability, synthesize two
114
+ // agent-callable tools (mcp__<server>__list_resources / __read_resource) bound to the
115
+ // live client + server name, and push them into the SAME tools array both backends
116
+ // consume — no agent-backend edit. On-by-default when advertised; opt-out is the
117
+ // existing allowedTools glob (mcp__<server>__*). Concrete-URI list/read only —
118
+ // resource TEMPLATES are deferred. Reuses this loop's per-server try/catch: a server
119
+ // that lacks resources or throws on capability/synthesis contributes no resource tools
120
+ // and does not abort the rest.
121
+ if (client?.getServerCapabilities()?.resources) {
122
+ tools.push(...createMcpResourceTools(activeClient, serverName));
123
+ }
109
124
  }
110
125
  catch (error) {
111
126
  debugLog(`MCP instructions capture error for '${serverName}': ${error}`);