@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.
- package/README.md +73 -24
- package/dist/core/GthDeepAgent.js +49 -2
- package/dist/core/GthDeepAgent.js.map +1 -1
- package/dist/core/debugCapture.d.ts +1 -1
- package/dist/core/debugCapture.js.map +1 -1
- package/dist/core/subagentProfiles.d.ts +50 -0
- package/dist/core/subagentProfiles.js +81 -0
- package/dist/core/subagentProfiles.js.map +1 -0
- package/dist/middleware/binaryContentInjectionMiddleware.d.ts +8 -1
- package/dist/middleware/binaryContentInjectionMiddleware.js +11 -2
- package/dist/middleware/binaryContentInjectionMiddleware.js.map +1 -1
- package/dist/middleware/frontendImageInjectionMiddleware.d.ts +80 -0
- package/dist/middleware/frontendImageInjectionMiddleware.js +146 -0
- package/dist/middleware/frontendImageInjectionMiddleware.js.map +1 -0
- package/dist/middleware/registry.js +36 -1
- package/dist/middleware/registry.js.map +1 -1
- package/dist/middleware/types.d.ts +16 -2
- package/dist/modules/a2a/A2AClientWrapper.d.ts +1 -1
- package/dist/modules/a2a/A2AClientWrapper.js +19 -5
- package/dist/modules/a2a/A2AClientWrapper.js.map +1 -1
- package/dist/modules/apiAgUiModule.d.ts +68 -0
- package/dist/modules/apiAgUiModule.js +95 -4
- package/dist/modules/apiAgUiModule.js.map +1 -1
- package/dist/modules/interactiveSessionModule.js +110 -34
- package/dist/modules/interactiveSessionModule.js.map +1 -1
- package/dist/modules/slashCommands.d.ts +328 -0
- package/dist/modules/slashCommands.js +598 -0
- package/dist/modules/slashCommands.js.map +1 -0
- package/dist/resolvers.js +15 -0
- package/dist/resolvers.js.map +1 -1
- package/dist/tools/GthCustomToolkit.js +72 -3
- package/dist/tools/GthCustomToolkit.js.map +1 -1
- package/dist/tools/GthDevToolkit.d.ts +5 -2
- package/dist/tools/GthDevToolkit.js +28 -7
- package/dist/tools/GthDevToolkit.js.map +1 -1
- package/dist/tools/GthFileSystemToolkit.d.ts +16 -0
- package/dist/tools/GthFileSystemToolkit.js +218 -110
- package/dist/tools/GthFileSystemToolkit.js.map +1 -1
- package/dist/tools/McpResourceTool.d.ts +31 -0
- package/dist/tools/McpResourceTool.js +106 -0
- package/dist/tools/McpResourceTool.js.map +1 -0
- package/dist/tools/shell/hardline.js +3 -3
- package/dist/tools/shell/hardline.js.map +1 -1
- package/package.json +2 -2
- package/dist/tools/shell/allowlist.d.ts +0 -11
- package/dist/tools/shell/allowlist.js +0 -12
- package/dist/tools/shell/allowlist.js.map +0 -1
- package/dist/tools/shell/arity.d.ts +0 -11
- package/dist/tools/shell/arity.js +0 -12
- package/dist/tools/shell/arity.js.map +0 -1
- package/dist/tools/shell/normalize.d.ts +0 -10
- package/dist/tools/shell/normalize.js +0 -11
- 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}`);
|