@gaunt-sloth/core 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 +43 -18
- package/dist/config/defaults.d.ts +5 -7
- package/dist/config/defaults.js +3 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +40 -0
- package/dist/config/loader.js +179 -6
- package/dist/config/loader.js.map +1 -1
- package/dist/config/profiles.d.ts +68 -0
- package/dist/config/profiles.js +93 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/schema.d.ts +69 -2
- package/dist/config/schema.js +87 -5
- package/dist/config/schema.js.map +1 -1
- package/dist/config/types.d.ts +103 -5
- package/dist/config/types.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +26 -1
- package/dist/core/GthAbstractAgent.js +122 -1
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.js +40 -0
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +99 -0
- package/dist/core/GthLangChainAgent.js +333 -2
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/debugCapture.d.ts +16 -0
- package/dist/core/debugCapture.js.map +1 -1
- package/dist/core/plainToolIndication.js +48 -28
- package/dist/core/plainToolIndication.js.map +1 -1
- package/dist/core/refusal.d.ts +53 -0
- package/dist/core/refusal.js +129 -0
- package/dist/core/refusal.js.map +1 -0
- package/dist/core/runStats.d.ts +14 -3
- package/dist/core/runStats.js +48 -3
- package/dist/core/runStats.js.map +1 -1
- package/dist/core/toolCallRepair/grammar.d.ts +41 -0
- package/dist/core/toolCallRepair/grammar.js +116 -0
- package/dist/core/toolCallRepair/grammar.js.map +1 -0
- package/dist/core/toolCallRepair/index.d.ts +2 -0
- package/dist/core/toolCallRepair/index.js +7 -0
- package/dist/core/toolCallRepair/index.js.map +1 -0
- package/dist/core/toolCallRepair/payload.d.ts +36 -0
- package/dist/core/toolCallRepair/payload.js +341 -0
- package/dist/core/toolCallRepair/payload.js.map +1 -0
- package/dist/core/toolCallRepair/promote.d.ts +45 -0
- package/dist/core/toolCallRepair/promote.js +90 -0
- package/dist/core/toolCallRepair/promote.js.map +1 -0
- package/dist/core/toolDisplay.d.ts +14 -1
- package/dist/core/toolDisplay.js +69 -12
- package/dist/core/toolDisplay.js.map +1 -1
- package/dist/core/toolOutputChannel.d.ts +33 -6
- package/dist/core/toolOutputChannel.js +61 -8
- package/dist/core/toolOutputChannel.js.map +1 -1
- package/dist/core/types.d.ts +22 -0
- package/dist/providers/geminiSchemaSanitizer.d.ts +6 -3
- package/dist/providers/geminiSchemaSanitizer.js +152 -6
- package/dist/providers/geminiSchemaSanitizer.js.map +1 -1
- package/dist/providers/modelDiscovery.d.ts +18 -4
- package/dist/providers/modelDiscovery.js +67 -17
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/openai.js +34 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/utils/crashHandler.d.ts +87 -0
- package/dist/utils/crashHandler.js +128 -0
- package/dist/utils/crashHandler.js.map +1 -0
- package/dist/utils/debugDump.d.ts +58 -0
- package/dist/utils/debugDump.js +149 -15
- package/dist/utils/debugDump.js.map +1 -1
- package/dist/utils/llmUtils.d.ts +22 -8
- package/dist/utils/llmUtils.js +48 -8
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/redactSecrets.js +68 -20
- package/dist/utils/redactSecrets.js.map +1 -1
- package/package.json +1 -1
- package/schema/gsloth-config.schema.json +224 -5
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// EXT-35 — plain-text tool-call repair payload parser.
|
|
2
|
+
//
|
|
3
|
+
// Ported (TypeScript, gaunt-sloth house style) from the openclaw
|
|
4
|
+
// `@openclaw/tool-call-repair` reference (`packages/tool-call-repair/src/payload.ts`).
|
|
5
|
+
// Carries the three text-emitted tool-call dialects small/local models produce:
|
|
6
|
+
// • bracket: `[tool:name]{...json...}` / `[name]\n{...json...}[/name]`
|
|
7
|
+
// • XML-ish: `<function=name><parameter=key>value</parameter></function>`
|
|
8
|
+
// • Harmony: `<|channel|>commentary to=name code<|message|>{...json...}<|call|>`
|
|
9
|
+
// — plus the real gpt-oss variant (EXT-43) with a NAMESPACED name and a `<|constrain|>` marker:
|
|
10
|
+
// `<|channel|>commentary to=functions.get_weather<|constrain|>json<|message|>{...}<|call|>`
|
|
11
|
+
//
|
|
12
|
+
// {@link parseStandalonePlainTextToolCallBlocks} is STANDALONE-only by construction: it walks the
|
|
13
|
+
// WHOLE input and returns null the moment it hits text that is not a tool-call block, so prose that
|
|
14
|
+
// merely mentions a tool or contains brackets never parses as a call. Allow-list + payload-size
|
|
15
|
+
// gating are threaded through {@link PlainTextToolCallParseOptions}. The reference's streaming
|
|
16
|
+
// normalizer and user-visible strip helpers are intentionally omitted (not needed for promotion).
|
|
17
|
+
import { consumeLineBreak, END_TOOL_REQUEST, finalDotSegment, findJsonObjectEnd, HARMONY_CALL_MARKER, HARMONY_CHANNEL_MARKER, HARMONY_CONSTRAIN_MARKER, HARMONY_MESSAGE_MARKER, isHarmonyToolNameChar, isPlainTextToolNameChar, skipHorizontalWhitespace, skipWhitespace, } from './grammar.js';
|
|
18
|
+
/**
|
|
19
|
+
* The single source of truth for the plain-text tool-call payload cap (256 KB). A serialized call
|
|
20
|
+
* larger than this is never treated as a tool call. Re-exported from `./promote.js` (and the module
|
|
21
|
+
* index) as `MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES` — the public name — so there is ONE constant,
|
|
22
|
+
* not two duplicated literals. (EXT-43 unified the former `DEFAULT_MAX_PLAIN_TEXT_TOOL_PAYLOAD_BYTES`
|
|
23
|
+
* here with `promote.ts`'s copy.)
|
|
24
|
+
*/
|
|
25
|
+
export const MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES = 256_000;
|
|
26
|
+
const utf8Encoder = new TextEncoder();
|
|
27
|
+
function utf8ByteLengthWithinLimit(text, start, end, maxBytes) {
|
|
28
|
+
if (end - start > maxBytes) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const byteLength = utf8Encoder.encode(text.slice(start, end)).byteLength;
|
|
32
|
+
return byteLength <= maxBytes ? byteLength : null;
|
|
33
|
+
}
|
|
34
|
+
function parseBracketOpening(text, start) {
|
|
35
|
+
if (text[start] !== '[') {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
let cursor = start + 1;
|
|
39
|
+
if (text.startsWith('tool:', cursor)) {
|
|
40
|
+
cursor += 'tool:'.length;
|
|
41
|
+
const nameStart = cursor;
|
|
42
|
+
while (isPlainTextToolNameChar(text[cursor])) {
|
|
43
|
+
cursor += 1;
|
|
44
|
+
}
|
|
45
|
+
if (cursor === nameStart || text[cursor] !== ']') {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
allowsOptionalXmlishClose: true,
|
|
50
|
+
end: cursor + 1,
|
|
51
|
+
name: text.slice(nameStart, cursor),
|
|
52
|
+
requiresClosing: false,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const nameStart = cursor;
|
|
56
|
+
while (isPlainTextToolNameChar(text[cursor])) {
|
|
57
|
+
cursor += 1;
|
|
58
|
+
}
|
|
59
|
+
if (cursor === nameStart || text[cursor] !== ']') {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const name = text.slice(nameStart, cursor);
|
|
63
|
+
cursor += 1;
|
|
64
|
+
cursor = skipHorizontalWhitespace(text, cursor);
|
|
65
|
+
const afterLineBreak = consumeLineBreak(text, cursor);
|
|
66
|
+
if (afterLineBreak === null) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return { end: afterLineBreak, name, requiresClosing: true };
|
|
70
|
+
}
|
|
71
|
+
function parseHarmonyOpening(text, start) {
|
|
72
|
+
let cursor = start;
|
|
73
|
+
if (text.startsWith(HARMONY_CHANNEL_MARKER, cursor)) {
|
|
74
|
+
cursor += HARMONY_CHANNEL_MARKER.length;
|
|
75
|
+
}
|
|
76
|
+
const channelStart = cursor;
|
|
77
|
+
while (/[A-Za-z_]/.test(text[cursor] ?? '')) {
|
|
78
|
+
cursor += 1;
|
|
79
|
+
}
|
|
80
|
+
const channel = text.slice(channelStart, cursor);
|
|
81
|
+
if (channel !== 'commentary' && channel !== 'analysis' && channel !== 'final') {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
cursor = skipHorizontalWhitespace(text, cursor);
|
|
85
|
+
if (!text.startsWith('to=', cursor)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
cursor += 3;
|
|
89
|
+
// Tool name. EXT-43: accept the real gpt-oss NAMESPACED form (`to=functions.get_weather`) by
|
|
90
|
+
// scanning dot-inclusive chars, then take the FINAL dot-segment as the allow-listed tool name.
|
|
91
|
+
// The reference dotless form (`to=get_weather`) is the single-segment degenerate case.
|
|
92
|
+
const nameStart = cursor;
|
|
93
|
+
while (isHarmonyToolNameChar(text[cursor])) {
|
|
94
|
+
cursor += 1;
|
|
95
|
+
}
|
|
96
|
+
if (cursor === nameStart) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
const name = finalDotSegment(text.slice(nameStart, cursor));
|
|
100
|
+
if (!name) {
|
|
101
|
+
// Trailing-dot / empty final segment (e.g. `to=functions.`) is not a valid tool name.
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
cursor = skipHorizontalWhitespace(text, cursor);
|
|
105
|
+
// Payload-format marker between the header and the JSON. The reference grammar requires the
|
|
106
|
+
// literal `code` token; real gpt-oss instead emits `<|constrain|>json` (EXT-43). Accept EITHER —
|
|
107
|
+
// but a marker is still REQUIRED, so the widening does not relax prose-safety (a bare
|
|
108
|
+
// `commentary to=x {…}` never promotes).
|
|
109
|
+
if (text.startsWith('code', cursor)) {
|
|
110
|
+
cursor += 4;
|
|
111
|
+
}
|
|
112
|
+
else if (text.startsWith(HARMONY_CONSTRAIN_MARKER, cursor)) {
|
|
113
|
+
cursor += HARMONY_CONSTRAIN_MARKER.length;
|
|
114
|
+
// Optional constraint-format token that follows the marker (e.g. `json`).
|
|
115
|
+
cursor = skipHorizontalWhitespace(text, cursor);
|
|
116
|
+
while (isPlainTextToolNameChar(text[cursor])) {
|
|
117
|
+
cursor += 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
cursor = skipWhitespace(text, cursor);
|
|
124
|
+
if (text.startsWith(HARMONY_MESSAGE_MARKER, cursor)) {
|
|
125
|
+
cursor = skipWhitespace(text, cursor + HARMONY_MESSAGE_MARKER.length);
|
|
126
|
+
}
|
|
127
|
+
return { end: cursor, name, requiresClosing: false };
|
|
128
|
+
}
|
|
129
|
+
function parseXmlishFunctionOpening(text, start) {
|
|
130
|
+
const match = /^<function=([A-Za-z0-9_.:-]{1,120})>\s*/i.exec(text.slice(start));
|
|
131
|
+
if (!match?.[1]) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return { end: start + match[0].length, name: match[1], requiresClosing: false };
|
|
135
|
+
}
|
|
136
|
+
function parseOpening(text, start) {
|
|
137
|
+
return parseBracketOpening(text, start) ?? parseHarmonyOpening(text, start);
|
|
138
|
+
}
|
|
139
|
+
function consumeJsonObject(text, start, maxPayloadBytes) {
|
|
140
|
+
const cursor = skipWhitespace(text, start);
|
|
141
|
+
if (text[cursor] !== '{') {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const end = findJsonObjectEnd(text, cursor, maxPayloadBytes);
|
|
145
|
+
if (end === null || utf8ByteLengthWithinLimit(text, cursor, end, maxPayloadBytes) === null) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const rawJson = text.slice(cursor, end);
|
|
149
|
+
try {
|
|
150
|
+
const parsed = JSON.parse(rawJson);
|
|
151
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return { end, value: parsed };
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function parseClosing(text, start, name) {
|
|
161
|
+
const cursor = skipWhitespace(text, start);
|
|
162
|
+
if (text.startsWith(END_TOOL_REQUEST, cursor)) {
|
|
163
|
+
return cursor + END_TOOL_REQUEST.length;
|
|
164
|
+
}
|
|
165
|
+
const namedClosing = `[/${name}]`;
|
|
166
|
+
if (text.startsWith(namedClosing, cursor)) {
|
|
167
|
+
return cursor + namedClosing.length;
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
function parseOptionalHarmonyClosing(text, start) {
|
|
172
|
+
const cursor = skipWhitespace(text, start);
|
|
173
|
+
if (text.startsWith(HARMONY_CALL_MARKER, cursor)) {
|
|
174
|
+
return cursor + HARMONY_CALL_MARKER.length;
|
|
175
|
+
}
|
|
176
|
+
return start;
|
|
177
|
+
}
|
|
178
|
+
function parsePlainTextToolCallBlockAt(text, start, options) {
|
|
179
|
+
const opening = parseOpening(text, start);
|
|
180
|
+
if (!opening) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const allowedToolNames = options?.allowedToolNames
|
|
184
|
+
? new Set(options.allowedToolNames)
|
|
185
|
+
: undefined;
|
|
186
|
+
if (allowedToolNames && !allowedToolNames.has(opening.name)) {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const payload = consumeJsonObject(text, opening.end, options?.maxPayloadBytes ?? MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES);
|
|
190
|
+
if (!payload) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
const closingEnd = opening.requiresClosing
|
|
194
|
+
? parseClosing(text, payload.end, opening.name)
|
|
195
|
+
: parseOptionalHarmonyClosing(text, payload.end);
|
|
196
|
+
if (closingEnd === null) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
arguments: payload.value,
|
|
201
|
+
end: closingEnd,
|
|
202
|
+
name: opening.name,
|
|
203
|
+
raw: text.slice(start, closingEnd),
|
|
204
|
+
start,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function findXmlishParameterBlock(text, start) {
|
|
208
|
+
const cursor = skipWhitespace(text, start);
|
|
209
|
+
const openMatch = /^<parameter=([A-Za-z0-9_.:-]{1,120})>/i.exec(text.slice(cursor));
|
|
210
|
+
if (!openMatch?.[1]) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const payloadStart = cursor + openMatch[0].length;
|
|
214
|
+
const closeMatch = /<\/parameter>/i.exec(text.slice(payloadStart));
|
|
215
|
+
if (!closeMatch) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
const closeStart = payloadStart + closeMatch.index;
|
|
219
|
+
const closeEnd = closeStart + closeMatch[0].length;
|
|
220
|
+
return {
|
|
221
|
+
closeStart,
|
|
222
|
+
end: closeEnd,
|
|
223
|
+
name: openMatch[1],
|
|
224
|
+
payloadStart,
|
|
225
|
+
start: cursor,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function extractXmlishParameterValue(text, start, end) {
|
|
229
|
+
let payloadStart = start;
|
|
230
|
+
let payloadEnd = end;
|
|
231
|
+
const afterOpeningLineBreak = consumeLineBreak(text, payloadStart);
|
|
232
|
+
if (afterOpeningLineBreak !== null) {
|
|
233
|
+
payloadStart = afterOpeningLineBreak;
|
|
234
|
+
if (payloadEnd > payloadStart && text[payloadEnd - 1] === '\n') {
|
|
235
|
+
payloadEnd -= 1;
|
|
236
|
+
if (payloadEnd > payloadStart && text[payloadEnd - 1] === '\r') {
|
|
237
|
+
payloadEnd -= 1;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (payloadEnd > payloadStart && text[payloadEnd - 1] === '\r') {
|
|
241
|
+
payloadEnd -= 1;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return text.slice(payloadStart, payloadEnd);
|
|
245
|
+
}
|
|
246
|
+
function consumeXmlishParameterBlock(text, start, maxPayloadBytes) {
|
|
247
|
+
const bounds = findXmlishParameterBlock(text, start);
|
|
248
|
+
if (!bounds) {
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
const byteLength = utf8ByteLengthWithinLimit(text, start, bounds.end, maxPayloadBytes);
|
|
252
|
+
if (byteLength === null) {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
byteLength,
|
|
257
|
+
end: bounds.end,
|
|
258
|
+
name: bounds.name,
|
|
259
|
+
value: extractXmlishParameterValue(text, bounds.payloadStart, bounds.closeStart),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function consumeXmlishFunctionClose(text, start) {
|
|
263
|
+
const cursor = skipWhitespace(text, start);
|
|
264
|
+
return text.slice(cursor).toLowerCase().startsWith('</function>')
|
|
265
|
+
? cursor + '</function>'.length
|
|
266
|
+
: null;
|
|
267
|
+
}
|
|
268
|
+
function consumeOptionalXmlishFunctionClose(text, start) {
|
|
269
|
+
return consumeXmlishFunctionClose(text, start) ?? start;
|
|
270
|
+
}
|
|
271
|
+
function parseXmlishOpening(text, start) {
|
|
272
|
+
return parseBracketOpening(text, start) ?? parseXmlishFunctionOpening(text, start);
|
|
273
|
+
}
|
|
274
|
+
function parseXmlishPlainTextToolCallBlockAt(text, start, options) {
|
|
275
|
+
const opening = parseXmlishOpening(text, start);
|
|
276
|
+
if (!opening) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
const allowedToolNames = options?.allowedToolNames
|
|
280
|
+
? new Set(options.allowedToolNames)
|
|
281
|
+
: undefined;
|
|
282
|
+
if (allowedToolNames && !allowedToolNames.has(opening.name)) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
const maxPayloadBytes = options?.maxPayloadBytes ?? MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES;
|
|
286
|
+
const args = {};
|
|
287
|
+
let cursor = opening.end;
|
|
288
|
+
let parameterCount = 0;
|
|
289
|
+
let payloadBytes = 0;
|
|
290
|
+
while (true) {
|
|
291
|
+
const parameter = consumeXmlishParameterBlock(text, cursor, maxPayloadBytes);
|
|
292
|
+
if (!parameter) {
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
payloadBytes += parameter.byteLength;
|
|
296
|
+
if (payloadBytes > maxPayloadBytes) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
args[parameter.name] = parameter.value;
|
|
300
|
+
parameterCount += 1;
|
|
301
|
+
cursor = parameter.end;
|
|
302
|
+
}
|
|
303
|
+
if (parameterCount === 0) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const end = opening.allowsOptionalXmlishClose
|
|
307
|
+
? consumeOptionalXmlishFunctionClose(text, cursor)
|
|
308
|
+
: consumeXmlishFunctionClose(text, cursor);
|
|
309
|
+
if (end === null) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
return {
|
|
313
|
+
arguments: args,
|
|
314
|
+
end,
|
|
315
|
+
name: opening.name,
|
|
316
|
+
raw: text.slice(start, end),
|
|
317
|
+
start,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Parse the WHOLE input into standalone plain-text tool-call blocks, or return null if any part of
|
|
322
|
+
* it is not such a block. This all-or-nothing walk is the standalone-only gate: prose that merely
|
|
323
|
+
* mentions a tool name, contains brackets, or embeds a call inside a sentence never parses (the
|
|
324
|
+
* first non-block character aborts the parse). Allow-list + payload-cap gating are applied per
|
|
325
|
+
* block via {@link PlainTextToolCallParseOptions}.
|
|
326
|
+
*/
|
|
327
|
+
export function parseStandalonePlainTextToolCallBlocks(text, options) {
|
|
328
|
+
const blocks = [];
|
|
329
|
+
let cursor = skipWhitespace(text, 0);
|
|
330
|
+
while (cursor < text.length) {
|
|
331
|
+
const block = parsePlainTextToolCallBlockAt(text, cursor, options) ??
|
|
332
|
+
parseXmlishPlainTextToolCallBlockAt(text, cursor, options);
|
|
333
|
+
if (!block) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
blocks.push(block);
|
|
337
|
+
cursor = skipWhitespace(text, block.end);
|
|
338
|
+
}
|
|
339
|
+
return blocks.length > 0 ? blocks : null;
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../../../src/core/toolCallRepair/payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AIMessage } from '@langchain/core/messages';
|
|
2
|
+
/**
|
|
3
|
+
* A sane default cap on a single repaired call's serialized payload. A text blob larger than this
|
|
4
|
+
* is never treated as a tool call. Overridable per call so a test can assert the cap with a small
|
|
5
|
+
* value; production leaves it at the default. The single source of truth lives in `./payload.js`
|
|
6
|
+
* and is re-exported here under its public name (EXT-43 unified the former duplicated literal).
|
|
7
|
+
*/
|
|
8
|
+
export { MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES } from './payload.js';
|
|
9
|
+
/** LangChain native tool-call shape (structurally a `ToolCall` from `@langchain/core/messages`). */
|
|
10
|
+
type NativeToolCall = {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
args: Record<string, unknown>;
|
|
14
|
+
type: 'tool_call';
|
|
15
|
+
};
|
|
16
|
+
/** Gates for {@link textToNativeToolCalls} / {@link promoteTextEmittedToolCallMessage}. */
|
|
17
|
+
export interface TextToolCallRepairOptions {
|
|
18
|
+
/**
|
|
19
|
+
* The names of the tools bound to this run (the allow-list). Only a text-emitted call whose name
|
|
20
|
+
* is one of these promotes; an unknown name is left as text. An EMPTY set promotes nothing — the
|
|
21
|
+
* prose-safe default (no toolset ⇒ nothing is a call).
|
|
22
|
+
*/
|
|
23
|
+
allowedToolNames: Iterable<string>;
|
|
24
|
+
/** Payload-size cap; defaults to {@link MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES}. */
|
|
25
|
+
maxPayloadBytes?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse assistant `text` as one-or-more STANDALONE text-emitted tool calls and return them as
|
|
29
|
+
* native LangChain tool_calls, or `undefined` when the text is not a standalone call, names a tool
|
|
30
|
+
* outside the allow-list, or exceeds the payload cap. Fresh `id`s are minted per call (the model
|
|
31
|
+
* emitted none). Empty allow-list ⇒ `undefined` (never promote).
|
|
32
|
+
*/
|
|
33
|
+
export declare function textToNativeToolCalls(text: string | undefined, options: TextToolCallRepairOptions): NativeToolCall[] | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Repair one assistant {@link AIMessage}: if it has NO native tool_calls but its content is a
|
|
36
|
+
* standalone text-emitted call (allow-listed, within the payload cap), return a NEW AIMessage that
|
|
37
|
+
* carries the promoted native tool_calls; otherwise return `undefined` (the caller keeps the
|
|
38
|
+
* original, so the native happy path is untouched).
|
|
39
|
+
*
|
|
40
|
+
* The returned message PRESERVES the original `id`. That is load-bearing: LangGraph's message-state
|
|
41
|
+
* reducer merges by id, so a same-id message REPLACES the original in graph state (rather than
|
|
42
|
+
* appending a duplicate/dangling assistant message). Content is cleared to '' since the raw
|
|
43
|
+
* text-call is now represented natively as tool_calls.
|
|
44
|
+
*/
|
|
45
|
+
export declare function promoteTextEmittedToolCallMessage(message: AIMessage, options: TextToolCallRepairOptions): AIMessage | undefined;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// EXT-35 — promote a text-emitted tool call to a native LangChain tool_call.
|
|
2
|
+
//
|
|
3
|
+
// The LangChain-message adaptation of the openclaw `@openclaw/tool-call-repair` reference
|
|
4
|
+
// (`packages/tool-call-repair/src/promote.ts`'s `promoteStandalonePlainTextToolCallMessage`).
|
|
5
|
+
// Small/local models (Gemma, lmstudio, gpt-oss) often serialise a tool call as assistant TEXT
|
|
6
|
+
// rather than a native `tool_call`; the ReAct graph then sees no tool_calls and ends the turn
|
|
7
|
+
// ("no tool calls = done"), stalling the loop. This module rescues a STANDALONE text-emitted call
|
|
8
|
+
// by rewriting the assistant message into one carrying a native `tool_call`, so the graph routes to
|
|
9
|
+
// the tools node and the loop continues.
|
|
10
|
+
//
|
|
11
|
+
// Three hard gates keep prose from ever being misread as a call:
|
|
12
|
+
// 1. name allow-list — only a call whose tool name is in the bound toolset promotes (an empty
|
|
13
|
+
// allow-list promotes nothing, the prose-safe default);
|
|
14
|
+
// 2. payload-size cap — an oversized JSON blob is not treated as a call;
|
|
15
|
+
// 3. standalone-only — the message content must be ESSENTIALLY JUST the call (enforced by
|
|
16
|
+
// {@link parseStandalonePlainTextToolCallBlocks}'s whole-input walk).
|
|
17
|
+
//
|
|
18
|
+
// Unlike the reference we do NOT set a `stopReason`/`toolUse` flag: langchain's ReAct router keys
|
|
19
|
+
// purely off the presence of `tool_calls` on the last AIMessage, so promoting the tool_calls array
|
|
20
|
+
// is sufficient to continue the loop.
|
|
21
|
+
import { AIMessage } from '@langchain/core/messages';
|
|
22
|
+
import { randomUUID } from 'node:crypto';
|
|
23
|
+
import { MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES, parseStandalonePlainTextToolCallBlocks, } from './payload.js';
|
|
24
|
+
/**
|
|
25
|
+
* A sane default cap on a single repaired call's serialized payload. A text blob larger than this
|
|
26
|
+
* is never treated as a tool call. Overridable per call so a test can assert the cap with a small
|
|
27
|
+
* value; production leaves it at the default. The single source of truth lives in `./payload.js`
|
|
28
|
+
* and is re-exported here under its public name (EXT-43 unified the former duplicated literal).
|
|
29
|
+
*/
|
|
30
|
+
export { MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES } from './payload.js';
|
|
31
|
+
/**
|
|
32
|
+
* Parse assistant `text` as one-or-more STANDALONE text-emitted tool calls and return them as
|
|
33
|
+
* native LangChain tool_calls, or `undefined` when the text is not a standalone call, names a tool
|
|
34
|
+
* outside the allow-list, or exceeds the payload cap. Fresh `id`s are minted per call (the model
|
|
35
|
+
* emitted none). Empty allow-list ⇒ `undefined` (never promote).
|
|
36
|
+
*/
|
|
37
|
+
export function textToNativeToolCalls(text, options) {
|
|
38
|
+
const allowedToolNames = new Set(options.allowedToolNames);
|
|
39
|
+
// Prose-safety default: with no bound tools nothing can be a call.
|
|
40
|
+
if (allowedToolNames.size === 0) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const trimmed = (text ?? '').trim();
|
|
44
|
+
if (!trimmed) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const parseOptions = {
|
|
48
|
+
allowedToolNames,
|
|
49
|
+
maxPayloadBytes: options.maxPayloadBytes ?? MAX_TEXT_EMITTED_TOOL_CALL_PAYLOAD_BYTES,
|
|
50
|
+
};
|
|
51
|
+
const blocks = parseStandalonePlainTextToolCallBlocks(trimmed, parseOptions);
|
|
52
|
+
if (!blocks) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return blocks.map((block) => ({
|
|
56
|
+
id: randomUUID(),
|
|
57
|
+
name: block.name,
|
|
58
|
+
args: block.arguments,
|
|
59
|
+
type: 'tool_call',
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Repair one assistant {@link AIMessage}: if it has NO native tool_calls but its content is a
|
|
64
|
+
* standalone text-emitted call (allow-listed, within the payload cap), return a NEW AIMessage that
|
|
65
|
+
* carries the promoted native tool_calls; otherwise return `undefined` (the caller keeps the
|
|
66
|
+
* original, so the native happy path is untouched).
|
|
67
|
+
*
|
|
68
|
+
* The returned message PRESERVES the original `id`. That is load-bearing: LangGraph's message-state
|
|
69
|
+
* reducer merges by id, so a same-id message REPLACES the original in graph state (rather than
|
|
70
|
+
* appending a duplicate/dangling assistant message). Content is cleared to '' since the raw
|
|
71
|
+
* text-call is now represented natively as tool_calls.
|
|
72
|
+
*/
|
|
73
|
+
export function promoteTextEmittedToolCallMessage(message, options) {
|
|
74
|
+
// Happy path untouched: repair engages ONLY when there are no native tool_calls.
|
|
75
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
const toolCalls = textToNativeToolCalls(message.text, options);
|
|
79
|
+
if (!toolCalls) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
return new AIMessage({
|
|
83
|
+
id: message.id,
|
|
84
|
+
content: '',
|
|
85
|
+
tool_calls: toolCalls,
|
|
86
|
+
additional_kwargs: message.additional_kwargs,
|
|
87
|
+
response_metadata: message.response_metadata,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=promote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote.js","sourceRoot":"","sources":["../../../src/core/toolCallRepair/promote.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,0FAA0F;AAC1F,8FAA8F;AAC9F,8FAA8F;AAC9F,8FAA8F;AAC9F,kGAAkG;AAClG,oGAAoG;AACpG,yCAAyC;AACzC,EAAE;AACF,iEAAiE;AACjE,gGAAgG;AAChG,6DAA6D;AAC7D,2EAA2E;AAC3E,4FAA4F;AAC5F,2EAA2E;AAC3E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,sCAAsC;AAEtC,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,wCAAwC,EACxC,sCAAsC,GAEvC,MAAM,cAAc,CAAC;AAEtB;;;;;GAKG;AACH,OAAO,EAAE,wCAAwC,EAAE,MAAM,cAAc,CAAC;AAsBxE;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAwB,EACxB,OAAkC;IAElC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3D,mEAAmE;IACnE,IAAI,gBAAgB,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,YAAY,GAAkC;QAClD,gBAAgB;QAChB,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,wCAAwC;KACrF,CAAC;IACF,MAAM,MAAM,GAAG,sCAAsC,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC7E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,EAAE,EAAE,UAAU,EAAE;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,SAAS;QACrB,IAAI,EAAE,WAAoB;KAC3B,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iCAAiC,CAC/C,OAAkB,EAClB,OAAkC;IAElC,iFAAiF;IACjF,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxD,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,SAAS,GAAG,qBAAqB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,SAAS,CAAC;QACnB,EAAE,EAAE,OAAO,CAAC,EAAE;QACd,OAAO,EAAE,EAAE;QACX,UAAU,EAAE,SAAS;QACrB,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;QAC5C,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;KAC7C,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -46,7 +46,13 @@ export interface ToolCallDisplayInput {
|
|
|
46
46
|
* defensive posture: a half-streamed or malformed buffer never throws — it returns `null`.
|
|
47
47
|
*/
|
|
48
48
|
export declare function parseToolArgsSafe(argsText: string | undefined): Record<string, unknown> | null;
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Register the live config for inline-secret collection (TUI-C32 residual a). Resets the secret
|
|
51
|
+
* cache so the next {@link getDefaultSecrets} recomputes with the config's inline literals folded
|
|
52
|
+
* in. Idempotent; a later call with a fresh config supersedes the previous one.
|
|
53
|
+
*/
|
|
54
|
+
export declare function setToolDisplayConfig(config: unknown): void;
|
|
55
|
+
/** Test seam: drop the cached secret literals + registered config so specs can vary both. */
|
|
50
56
|
export declare function resetToolDisplaySecretsCacheForTests(): void;
|
|
51
57
|
/**
|
|
52
58
|
* Parse the shared shell-result shape (`Executing '…'…\n\n<COMMAND_OUTPUT>…</COMMAND_OUTPUT>\n\n
|
|
@@ -57,6 +63,13 @@ export declare function parseCommandOutputResult(result: string | undefined): {
|
|
|
57
63
|
body: string;
|
|
58
64
|
tail: string;
|
|
59
65
|
} | null;
|
|
66
|
+
/**
|
|
67
|
+
* TUI-C32 residual c — is THIS tool call genuinely a shell-shaped result (name allows it AND the
|
|
68
|
+
* result actually carries the `<COMMAND_OUTPUT>` shape)? Used by the plain surface to decide
|
|
69
|
+
* `liveOutputAlreadyShown` (the child streamed live via the tool-output channel's default sink),
|
|
70
|
+
* so a non-shell tool whose result merely quotes the marker no longer has its body suppressed.
|
|
71
|
+
*/
|
|
72
|
+
export declare function isShellShapedResult(name: string, result: string | undefined): boolean;
|
|
60
73
|
/** The registry glyph for a tool name (generic `⚙` when unknown). */
|
|
61
74
|
export declare function getToolGlyph(name: string): string;
|
|
62
75
|
/**
|
package/dist/core/toolDisplay.js
CHANGED
|
@@ -64,16 +64,35 @@ export function parseToolArgsSafe(argsText) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
67
|
+
* The live gth config, registered once per run so {@link getDefaultSecrets} can harvest INLINE
|
|
68
|
+
* config secrets (a pasted `apiKey`/`token` value) via the GS2-47 config walk — not only the
|
|
69
|
+
* env-derived literals. `GthAgentRunner.init` sets this next to the crash-context hand-off, so
|
|
70
|
+
* both render surfaces (the plain observer and the Ink TUI) see it. `undefined` (no config yet)
|
|
71
|
+
* degrades to env-only collection + the provider patterns, exactly as before.
|
|
72
|
+
*/
|
|
73
|
+
let displayConfig = undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Register the live config for inline-secret collection (TUI-C32 residual a). Resets the secret
|
|
76
|
+
* cache so the next {@link getDefaultSecrets} recomputes with the config's inline literals folded
|
|
77
|
+
* in. Idempotent; a later call with a fresh config supersedes the previous one.
|
|
78
|
+
*/
|
|
79
|
+
export function setToolDisplayConfig(config) {
|
|
80
|
+
displayConfig = config;
|
|
81
|
+
cachedSecrets = null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Lazily-computed default secret literals for redaction (GS2-47 technique 1), harvested from the
|
|
85
|
+
* process env AND the registered config (inline `apiKey`/`token` values) the same way
|
|
86
|
+
* `/debug-dump` does. Cached because `collectSecretValues` walks the whole env + config; the set
|
|
87
|
+
* cannot change mid-process in any way this render path must react to (a new config resets it via
|
|
88
|
+
* {@link setToolDisplayConfig}). `redactText` additionally always applies the provider-key
|
|
89
|
+
* patterns (technique 2).
|
|
71
90
|
*/
|
|
72
91
|
let cachedSecrets = null;
|
|
73
92
|
function getDefaultSecrets() {
|
|
74
93
|
if (cachedSecrets === null) {
|
|
75
94
|
try {
|
|
76
|
-
cachedSecrets = collectSecretValues(
|
|
95
|
+
cachedSecrets = collectSecretValues(displayConfig, env ?? {});
|
|
77
96
|
}
|
|
78
97
|
catch {
|
|
79
98
|
cachedSecrets = []; // patterns still apply via redactText
|
|
@@ -81,9 +100,10 @@ function getDefaultSecrets() {
|
|
|
81
100
|
}
|
|
82
101
|
return cachedSecrets;
|
|
83
102
|
}
|
|
84
|
-
/** Test seam: drop the cached
|
|
103
|
+
/** Test seam: drop the cached secret literals + registered config so specs can vary both. */
|
|
85
104
|
export function resetToolDisplaySecretsCacheForTests() {
|
|
86
105
|
cachedSecrets = null;
|
|
106
|
+
displayConfig = undefined;
|
|
87
107
|
}
|
|
88
108
|
/** Collapse whitespace runs (incl. newlines) so a value stays a one-line token. */
|
|
89
109
|
function inline(value) {
|
|
@@ -154,6 +174,26 @@ export function parseCommandOutputResult(result) {
|
|
|
154
174
|
return null;
|
|
155
175
|
return { body: match[1] ?? '', tail: (match[2] ?? '').trim() };
|
|
156
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* TUI-C32 residual c — may this tool NAME be treated as shell-shaped at all? A registered entry
|
|
179
|
+
* must be explicitly {@link ToolDisplayEntry.shellShaped}; an unregistered/custom name falls back
|
|
180
|
+
* to shape-detection (custom toolkit tools own user-defined names yet share the result shape). A
|
|
181
|
+
* registered non-shell tool (`read_file`, `list_directory`, …) is therefore NEVER shell-parsed
|
|
182
|
+
* just because its result happens to contain the `<COMMAND_OUTPUT>` marker.
|
|
183
|
+
*/
|
|
184
|
+
function nameAllowsShellShape(name) {
|
|
185
|
+
const entry = TOOL_DISPLAY_REGISTRY[name];
|
|
186
|
+
return entry ? entry.shellShaped === true : true;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* TUI-C32 residual c — is THIS tool call genuinely a shell-shaped result (name allows it AND the
|
|
190
|
+
* result actually carries the `<COMMAND_OUTPUT>` shape)? Used by the plain surface to decide
|
|
191
|
+
* `liveOutputAlreadyShown` (the child streamed live via the tool-output channel's default sink),
|
|
192
|
+
* so a non-shell tool whose result merely quotes the marker no longer has its body suppressed.
|
|
193
|
+
*/
|
|
194
|
+
export function isShellShapedResult(name, result) {
|
|
195
|
+
return nameAllowsShellShape(name) && parseCommandOutputResult(result) !== null;
|
|
196
|
+
}
|
|
157
197
|
/**
|
|
158
198
|
* Body formatter for shell-shaped calls: the child's output (preferring the LIVE streamed
|
|
159
199
|
* output — verbatim what the child printed — over the result's `<COMMAND_OUTPUT>` copy of it,
|
|
@@ -170,7 +210,15 @@ function formatShellBody(input) {
|
|
|
170
210
|
return null;
|
|
171
211
|
const lines = [];
|
|
172
212
|
if (!input.liveOutputAlreadyShown) {
|
|
173
|
-
|
|
213
|
+
// TUI-C32 residual d — the streamed live output is normally the verbatim, most-complete copy,
|
|
214
|
+
// so it is preferred (TUI-C17 dedupe). But if the live channel dropped straggler/tail chunks
|
|
215
|
+
// it can be a strict PREFIX of the result's `<COMMAND_OUTPUT>` copy (which the model always
|
|
216
|
+
// receives in full) — in that case fall back to the fuller copy so the expanded view can still
|
|
217
|
+
// recover what the stream missed. The common case (live === result, an independent/capped
|
|
218
|
+
// result, or an empty live output) is unchanged: only a genuine dropped-tail prefix overrides.
|
|
219
|
+
const live = input.output ?? '';
|
|
220
|
+
const resultBody = parsed?.body ?? '';
|
|
221
|
+
const body = resultBody.length > live.length && resultBody.startsWith(live) ? resultBody : live;
|
|
174
222
|
if (body.trim().length > 0)
|
|
175
223
|
lines.push(...styled(toLines(body), 'dim'));
|
|
176
224
|
}
|
|
@@ -249,11 +297,17 @@ const TOOL_DISPLAY_REGISTRY = {
|
|
|
249
297
|
glyph: SHELL_GLYPH,
|
|
250
298
|
summariseArgs: ['command'],
|
|
251
299
|
formatBody: formatShellBody,
|
|
300
|
+
shellShaped: true,
|
|
301
|
+
},
|
|
302
|
+
run_tests: { glyph: SHELL_GLYPH, formatBody: formatShellBody, shellShaped: true },
|
|
303
|
+
run_single_test: {
|
|
304
|
+
glyph: SHELL_GLYPH,
|
|
305
|
+
summariseArgs: ['testPath'],
|
|
306
|
+
formatBody: formatShellBody,
|
|
307
|
+
shellShaped: true,
|
|
252
308
|
},
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
run_lint: { glyph: SHELL_GLYPH, formatBody: formatShellBody },
|
|
256
|
-
run_build: { glyph: SHELL_GLYPH, formatBody: formatShellBody },
|
|
309
|
+
run_lint: { glyph: SHELL_GLYPH, formatBody: formatShellBody, shellShaped: true },
|
|
310
|
+
run_build: { glyph: SHELL_GLYPH, formatBody: formatShellBody, shellShaped: true },
|
|
257
311
|
task: { glyph: '🤖', summariseArgs: ['subagent_type', 'description'] },
|
|
258
312
|
};
|
|
259
313
|
/** The registry glyph for a tool name (generic `⚙` when unknown). */
|
|
@@ -311,7 +365,10 @@ export function buildToolBodyLines(input, secrets = getDefaultSecrets()) {
|
|
|
311
365
|
const args = parseToolArgsSafe(input.argsText);
|
|
312
366
|
const entry = TOOL_DISPLAY_REGISTRY[input.name];
|
|
313
367
|
let lines = entry?.formatBody?.(input, args) ?? null;
|
|
314
|
-
|
|
368
|
+
// TUI-C32 residual c — only fall back to the shape-based shell formatter for names that may be
|
|
369
|
+
// shell-shaped (flagged registry entries + unregistered custom tools), so a registered non-shell
|
|
370
|
+
// tool whose result merely contains `<COMMAND_OUTPUT>` is rendered generically, not shell-parsed.
|
|
371
|
+
if (lines === null && nameAllowsShellShape(input.name))
|
|
315
372
|
lines = formatShellBody(input);
|
|
316
373
|
if (lines === null) {
|
|
317
374
|
lines = [];
|