@nextclaw/ui 0.11.20 → 0.11.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/assets/{ChannelsList-DAx7wv0_.js → ChannelsList-ByHWHkQS.js} +1 -1
- package/dist/assets/ChatPage-FdT3pDnw.js +42 -0
- package/dist/assets/{DocBrowser-DKkE3Y4I.js → DocBrowser-3y_NHZ71.js} +1 -1
- package/dist/assets/DocBrowser-CMdPdbZj.js +1 -0
- package/dist/assets/{DocBrowserContext-BcZRBsCg.js → DocBrowserContext-CVJuwCcw.js} +1 -1
- package/dist/assets/{LogoBadge-BIPDLEwK.js → LogoBadge-D8fyilO-.js} +1 -1
- package/dist/assets/MarketplacePage-9oKmxN2n.js +1 -0
- package/dist/assets/{MarketplacePage-Dlp5BgCh.js → MarketplacePage-CmhsZXr1.js} +1 -1
- package/dist/assets/{McpMarketplacePage-CwKtAil8.js → McpMarketplacePage-C7PkCYbp.js} +1 -1
- package/dist/assets/{ModelConfig-Dg6F3Ldb.js → ModelConfig-DmCY6jWM.js} +1 -1
- package/dist/assets/{ProvidersList-f7bQdRxA.js → ProvidersList-ClT-34aX.js} +1 -1
- package/dist/assets/RemoteAccessPage-B6hUZl1O.js +1 -0
- package/dist/assets/{RuntimeConfig-M4OKjmgU.js → RuntimeConfig-C5aqliGk.js} +1 -1
- package/dist/assets/{SearchConfig-v46R5a2U.js → SearchConfig-Dm7r2yfp.js} +1 -1
- package/dist/assets/{SecretsConfig-CXvUpbB_.js → SecretsConfig-BBP_mbQh.js} +1 -1
- package/dist/assets/{SessionsConfig-7vUHMtOh.js → SessionsConfig-6wNJloZN.js} +1 -1
- package/dist/assets/{book-open-DzSduAaw.js → book-open-B26jGBjY.js} +1 -1
- package/dist/assets/{chat-session-display-CGfXhJoT.js → chat-session-display-Bjmn4aIZ.js} +1 -1
- package/dist/assets/{chunk-JZWAC4HX-C1vpvW4r.js → chunk-JZWAC4HX-B-4B29RN.js} +1 -1
- package/dist/assets/{config-Df97LeLR.js → config-BaC29Qf-.js} +1 -1
- package/dist/assets/{createLucideIcon-CcR5wVoU.js → createLucideIcon-DiFAvXmK.js} +1 -1
- package/dist/assets/{dist-BMlnBah3.js → dist-kW_O3kyZ.js} +1 -1
- package/dist/assets/{dist-Dii9v3X9.js → dist-pCfWPG1A.js} +1 -1
- package/dist/assets/{external-link-CnSDrvJE.js → external-link-D5-p-Gmm.js} +1 -1
- package/dist/assets/{hash-CAnX6PNt.js → hash-BlwrSV0q.js} +1 -1
- package/dist/assets/i18n-CSytxMFI.js +1 -0
- package/dist/assets/{index-BahpXJg8.css → index-CUy6doWo.css} +1 -1
- package/dist/assets/{index-B0DzQqwv.js → index-DvKS3L9j.js} +3 -3
- package/dist/assets/{label-CtIFj7_6.js → label-RyXfZqkP.js} +1 -1
- package/dist/assets/loader-circle-B2J777gj.js +1 -0
- package/dist/assets/{logos-3KFNiOej.js → logos-Bpl8QTgI.js} +1 -1
- package/dist/assets/{page-layout-BMwpn87D.js → page-layout--S0YBU0W.js} +1 -1
- package/dist/assets/plus-CM9XJ0Tf.js +1 -0
- package/dist/assets/{popover-BIzq25oH.js → popover-BEjfbEwy.js} +1 -1
- package/dist/assets/{react-ji6GGP_j.js → react-BuSP2-8B.js} +1 -1
- package/dist/assets/{save-CMgYkJ-y.js → save-DPPPpD_c.js} +1 -1
- package/dist/assets/search-Ctaw34Kp.js +1 -0
- package/dist/assets/{security-config-Xi5DYW7j.js → security-config-6t78Ph-I.js} +1 -1
- package/dist/assets/{select-Cz82gl01.js → select-CT50pzod.js} +1 -1
- package/dist/assets/skeleton-Bycyb0zU.js +1 -0
- package/dist/assets/{status-dot-C7q1HvLH.js → status-dot-BbBqRHfh.js} +1 -1
- package/dist/assets/{switch-DYswvkYj.js → switch-D3l6AcCk.js} +1 -1
- package/dist/assets/{tabs-custom-DKYQxrx1.js → tabs-custom-TZQ5WPWP.js} +1 -1
- package/dist/assets/{trash-2-DfXI7-ap.js → trash-2-B2_AGVE3.js} +1 -1
- package/dist/assets/{useConfirmDialog-CXDAxtRL.js → useConfirmDialog-BDpdjfIO.js} +1 -1
- package/dist/assets/{useMutation-s2sn2yzh.js → useMutation-BzCrO8j-.js} +1 -1
- package/dist/assets/x-CHOBE-63.js +1 -0
- package/dist/index.html +18 -18
- package/package.json +6 -6
- package/src/components/chat/adapters/chat-message-part.adapter.ts +74 -3
- package/src/components/chat/adapters/chat-message.adapter.test.ts +321 -3
- package/src/components/chat/adapters/chat-message.file-operation-card.ts +437 -0
- package/src/components/chat/adapters/chat-message.file-operation-diff.ts +408 -0
- package/src/components/chat/adapters/chat-message.partial-json.ts +89 -0
- package/src/components/chat/containers/chat-input-bar.container.tsx +8 -8
- package/src/components/chat/containers/chat-message-list.container.tsx +1 -0
- package/src/components/chat/ncp/ncp-session-adapter.test.ts +173 -0
- package/src/components/chat/useNcpAgentRuntime.test.tsx +90 -0
- package/src/lib/i18n.chat.ts +2 -1
- package/src/remote/remote-access-feedback.service.test.ts +18 -0
- package/src/remote/remote-access-feedback.service.ts +10 -1
- package/dist/assets/ChatPage-l2PYwCeB.js +0 -38
- package/dist/assets/DocBrowser-CIHLqoIm.js +0 -1
- package/dist/assets/MarketplacePage-TVeyVOuO.js +0 -1
- package/dist/assets/RemoteAccessPage-w_dY7P4T.js +0 -1
- package/dist/assets/i18n-CXBpwAwA.js +0 -1
- package/dist/assets/loader-circle-qgU4zQDw.js +0 -1
- package/dist/assets/plus-C9cYVbL-.js +0 -1
- package/dist/assets/search-sl1OeJFl.js +0 -1
- package/dist/assets/skeleton-rgIt7a5q.js +0 -1
- package/dist/assets/x-MIimOGs6.js +0 -1
package/dist/index.html
CHANGED
|
@@ -6,24 +6,24 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>NextClaw</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/i18n-
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/chunk-JZWAC4HX-
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/dist-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/createLucideIcon-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/select-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/dist-
|
|
16
|
-
<link rel="modulepreload" crossorigin href="/assets/react-
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/useMutation-
|
|
18
|
-
<link rel="modulepreload" crossorigin href="/assets/book-open-
|
|
19
|
-
<link rel="modulepreload" crossorigin href="/assets/external-link-
|
|
20
|
-
<link rel="modulepreload" crossorigin href="/assets/plus-
|
|
21
|
-
<link rel="modulepreload" crossorigin href="/assets/search-
|
|
22
|
-
<link rel="modulepreload" crossorigin href="/assets/x-
|
|
23
|
-
<link rel="modulepreload" crossorigin href="/assets/DocBrowserContext-
|
|
24
|
-
<link rel="modulepreload" crossorigin href="/assets/DocBrowser-
|
|
25
|
-
<link rel="modulepreload" crossorigin href="/assets/config-
|
|
26
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-DvKS3L9j.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/i18n-CSytxMFI.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/chunk-JZWAC4HX-B-4B29RN.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/dist-pCfWPG1A.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/createLucideIcon-DiFAvXmK.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/select-CT50pzod.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/dist-kW_O3kyZ.js">
|
|
16
|
+
<link rel="modulepreload" crossorigin href="/assets/react-BuSP2-8B.js">
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/useMutation-BzCrO8j-.js">
|
|
18
|
+
<link rel="modulepreload" crossorigin href="/assets/book-open-B26jGBjY.js">
|
|
19
|
+
<link rel="modulepreload" crossorigin href="/assets/external-link-D5-p-Gmm.js">
|
|
20
|
+
<link rel="modulepreload" crossorigin href="/assets/plus-CM9XJ0Tf.js">
|
|
21
|
+
<link rel="modulepreload" crossorigin href="/assets/search-Ctaw34Kp.js">
|
|
22
|
+
<link rel="modulepreload" crossorigin href="/assets/x-CHOBE-63.js">
|
|
23
|
+
<link rel="modulepreload" crossorigin href="/assets/DocBrowserContext-CVJuwCcw.js">
|
|
24
|
+
<link rel="modulepreload" crossorigin href="/assets/DocBrowser-3y_NHZ71.js">
|
|
25
|
+
<link rel="modulepreload" crossorigin href="/assets/config-BaC29Qf-.js">
|
|
26
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CUy6doWo.css">
|
|
27
27
|
</head>
|
|
28
28
|
|
|
29
29
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ui",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.21",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"tailwind-merge": "^2.5.4",
|
|
29
29
|
"zod": "^3.23.8",
|
|
30
30
|
"zustand": "^5.0.2",
|
|
31
|
-
"@nextclaw/ncp": "0.4.
|
|
32
|
-
"@nextclaw/
|
|
33
|
-
"@nextclaw/ncp-http-agent-client": "0.3.
|
|
34
|
-
"@nextclaw/agent-chat": "0.
|
|
35
|
-
"@nextclaw/
|
|
31
|
+
"@nextclaw/ncp-react": "0.4.12",
|
|
32
|
+
"@nextclaw/ncp": "0.4.6",
|
|
33
|
+
"@nextclaw/ncp-http-agent-client": "0.3.10",
|
|
34
|
+
"@nextclaw/agent-chat-ui": "0.2.19",
|
|
35
|
+
"@nextclaw/agent-chat": "0.1.6"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@testing-library/react": "^16.3.0",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
buildRenderableText,
|
|
11
11
|
buildTextPart,
|
|
12
12
|
} from "@/components/chat/adapters/chat-message-inline-content.adapter";
|
|
13
|
+
import { buildFileOperationCardData } from "@/components/chat/adapters/chat-message.file-operation-card";
|
|
13
14
|
import { buildSubagentToolCard } from "@/components/chat/adapters/chat-message.subagent-tool-card";
|
|
14
15
|
import type {
|
|
15
16
|
ChatMessagePartViewModel,
|
|
@@ -27,6 +28,7 @@ export type ChatMessageAdapterTexts = {
|
|
|
27
28
|
reasoningLabel: string;
|
|
28
29
|
toolCallLabel: string;
|
|
29
30
|
toolResultLabel: string;
|
|
31
|
+
toolInputLabel: string;
|
|
30
32
|
toolNoOutputLabel: string;
|
|
31
33
|
toolOutputLabel: string;
|
|
32
34
|
toolStatusPreparingLabel: string;
|
|
@@ -77,6 +79,8 @@ export type ChatMessagePartSource =
|
|
|
77
79
|
type ToolCardViewSource = ToolCard & {
|
|
78
80
|
statusTone: ChatToolPartViewModel["statusTone"];
|
|
79
81
|
statusLabel: string;
|
|
82
|
+
fileOperation?: ChatToolPartViewModel["fileOperation"];
|
|
83
|
+
outputData?: unknown;
|
|
80
84
|
};
|
|
81
85
|
|
|
82
86
|
type ChatMessagePartAdapterParams = {
|
|
@@ -112,6 +116,21 @@ function readOptionalNumber(value: unknown): number | null {
|
|
|
112
116
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
113
117
|
}
|
|
114
118
|
|
|
119
|
+
function isTerminalResultRecord(value: unknown): value is Record<string, unknown> {
|
|
120
|
+
if (!isRecord(value)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return (
|
|
124
|
+
"command" in value ||
|
|
125
|
+
"workingDir" in value ||
|
|
126
|
+
"exitCode" in value ||
|
|
127
|
+
"stdout" in value ||
|
|
128
|
+
"stderr" in value ||
|
|
129
|
+
"aggregated_output" in value ||
|
|
130
|
+
"combinedOutput" in value
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
115
134
|
function extractAssetFileView(
|
|
116
135
|
value: unknown,
|
|
117
136
|
texts: ChatMessageAdapterTexts,
|
|
@@ -161,7 +180,12 @@ function buildToolCard(
|
|
|
161
180
|
kind: toolCard.kind,
|
|
162
181
|
toolName: toolCard.name,
|
|
163
182
|
summary: toolCard.detail,
|
|
183
|
+
inputLabel: texts.toolInputLabel,
|
|
184
|
+
input: "input" in toolCard && typeof toolCard.input === "string"
|
|
185
|
+
? toolCard.input
|
|
186
|
+
: undefined,
|
|
164
187
|
output: toolCard.text,
|
|
188
|
+
outputData: toolCard.outputData,
|
|
165
189
|
hasResult: Boolean(toolCard.hasResult),
|
|
166
190
|
statusTone: toolCard.statusTone,
|
|
167
191
|
statusLabel: toolCard.statusLabel,
|
|
@@ -169,6 +193,9 @@ function buildToolCard(
|
|
|
169
193
|
toolCard.kind === "call" ? texts.toolCallLabel : texts.toolResultLabel,
|
|
170
194
|
outputLabel: texts.toolOutputLabel,
|
|
171
195
|
emptyLabel: texts.toolNoOutputLabel,
|
|
196
|
+
...("fileOperation" in toolCard && toolCard.fileOperation
|
|
197
|
+
? { fileOperation: toolCard.fileOperation }
|
|
198
|
+
: {}),
|
|
172
199
|
};
|
|
173
200
|
}
|
|
174
201
|
|
|
@@ -216,7 +243,7 @@ function resolveToolCardStatus(params: {
|
|
|
216
243
|
kind: "call",
|
|
217
244
|
hasResult: false,
|
|
218
245
|
statusTone: "running",
|
|
219
|
-
statusLabel: params.texts.
|
|
246
|
+
statusLabel: params.texts.toolStatusRunningLabel,
|
|
220
247
|
};
|
|
221
248
|
}
|
|
222
249
|
return {
|
|
@@ -227,6 +254,27 @@ function resolveToolCardStatus(params: {
|
|
|
227
254
|
};
|
|
228
255
|
}
|
|
229
256
|
|
|
257
|
+
function parseStructuredValue(value: unknown): unknown {
|
|
258
|
+
if (typeof value !== "string") {
|
|
259
|
+
return value;
|
|
260
|
+
}
|
|
261
|
+
const trimmed = value.trim();
|
|
262
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
return JSON.parse(trimmed) as unknown;
|
|
267
|
+
} catch {
|
|
268
|
+
return value;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function buildToolInvocationInput(args?: unknown, parsedArgs?: unknown): string | undefined {
|
|
273
|
+
const source = parsedArgs ?? parseStructuredValue(args);
|
|
274
|
+
const text = stringifyUnknown(source).trim();
|
|
275
|
+
return text || undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
230
278
|
function buildReasoningPart(
|
|
231
279
|
part: Extract<ChatMessagePartSource, { type: "reasoning" }>,
|
|
232
280
|
texts: ChatMessageAdapterTexts,
|
|
@@ -296,22 +344,45 @@ function buildToolInvocationPart(
|
|
|
296
344
|
result: invocation.result,
|
|
297
345
|
texts,
|
|
298
346
|
});
|
|
299
|
-
const
|
|
347
|
+
const fileOperationCardData = buildFileOperationCardData({
|
|
348
|
+
toolName: invocation.toolName,
|
|
349
|
+
status: invocation.status,
|
|
350
|
+
toolCallId: invocation.toolCallId,
|
|
351
|
+
args: invocation.args,
|
|
352
|
+
parsedArgs: invocation.parsedArgs,
|
|
353
|
+
result: invocation.result,
|
|
354
|
+
});
|
|
355
|
+
const detail =
|
|
356
|
+
fileOperationCardData?.summary ??
|
|
357
|
+
summarizeToolArgs(invocation.parsedArgs ?? invocation.args);
|
|
358
|
+
const input = fileOperationCardData
|
|
359
|
+
? undefined
|
|
360
|
+
: buildToolInvocationInput(invocation.args, invocation.parsedArgs);
|
|
300
361
|
const rawResult =
|
|
301
362
|
typeof invocation.error === "string" && invocation.error.trim()
|
|
302
363
|
? invocation.error.trim()
|
|
303
364
|
: invocation.result != null
|
|
304
365
|
? stringifyUnknown(invocation.result).trim()
|
|
305
366
|
: "";
|
|
367
|
+
const shouldHideStructuredTerminalJson =
|
|
368
|
+
!invocation.error && isTerminalResultRecord(invocation.result);
|
|
369
|
+
const shouldShowRawResult =
|
|
370
|
+
(!fileOperationCardData?.fileOperation || Boolean(invocation.error)) &&
|
|
371
|
+
!shouldHideStructuredTerminalJson;
|
|
306
372
|
const card: ToolCardViewSource = {
|
|
307
373
|
kind: statusView.kind,
|
|
308
374
|
name: invocation.toolName,
|
|
309
375
|
detail,
|
|
310
|
-
|
|
376
|
+
...(input ? { input } : {}),
|
|
377
|
+
text: shouldShowRawResult && rawResult ? rawResult : undefined,
|
|
378
|
+
outputData: invocation.result,
|
|
311
379
|
callId: invocation.toolCallId || undefined,
|
|
312
380
|
hasResult: statusView.hasResult,
|
|
313
381
|
statusTone: statusView.statusTone,
|
|
314
382
|
statusLabel: statusView.statusLabel,
|
|
383
|
+
...(fileOperationCardData?.fileOperation
|
|
384
|
+
? { fileOperation: fileOperationCardData.fileOperation }
|
|
385
|
+
: {}),
|
|
315
386
|
};
|
|
316
387
|
return {
|
|
317
388
|
type: "tool-card",
|
|
@@ -17,8 +17,9 @@ const defaultTexts = {
|
|
|
17
17
|
reasoningLabel: "Reasoning",
|
|
18
18
|
toolCallLabel: "Tool Call",
|
|
19
19
|
toolResultLabel: "Tool Result",
|
|
20
|
+
toolInputLabel: "Input",
|
|
20
21
|
toolNoOutputLabel: "No output",
|
|
21
|
-
toolOutputLabel: "
|
|
22
|
+
toolOutputLabel: "Output",
|
|
22
23
|
toolStatusPreparingLabel: "Preparing",
|
|
23
24
|
toolStatusRunningLabel: "Running",
|
|
24
25
|
toolStatusCompletedLabel: "Completed",
|
|
@@ -88,7 +89,7 @@ it("maps markdown, reasoning, and tool parts into UI view models", () => {
|
|
|
88
89
|
statusLabel: "Completed",
|
|
89
90
|
statusTone: "success",
|
|
90
91
|
titleLabel: "Tool Result",
|
|
91
|
-
outputLabel: "
|
|
92
|
+
outputLabel: "Output",
|
|
92
93
|
},
|
|
93
94
|
});
|
|
94
95
|
});
|
|
@@ -126,7 +127,7 @@ it("maps tool lifecycle statuses into visible card state feedback", () => {
|
|
|
126
127
|
type: "tool-card",
|
|
127
128
|
card: {
|
|
128
129
|
statusTone: "running",
|
|
129
|
-
statusLabel: "
|
|
130
|
+
statusLabel: "Running",
|
|
130
131
|
titleLabel: "Tool Call",
|
|
131
132
|
},
|
|
132
133
|
});
|
|
@@ -141,6 +142,87 @@ it("maps tool lifecycle statuses into visible card state feedback", () => {
|
|
|
141
142
|
});
|
|
142
143
|
});
|
|
143
144
|
|
|
145
|
+
it("preserves full generic tool args for the expanded body while keeping the header summary short", () => {
|
|
146
|
+
const adapted = adapt([
|
|
147
|
+
{
|
|
148
|
+
id: "assistant-generic-tool-input",
|
|
149
|
+
role: "assistant",
|
|
150
|
+
parts: [
|
|
151
|
+
{
|
|
152
|
+
type: "tool-invocation",
|
|
153
|
+
toolInvocation: {
|
|
154
|
+
status: ToolInvocationStatus.PARTIAL_CALL,
|
|
155
|
+
toolCallId: "call-generic-input",
|
|
156
|
+
toolName: "open_url",
|
|
157
|
+
args: JSON.stringify({
|
|
158
|
+
url: "https://example.com/really/long/path",
|
|
159
|
+
headers: {
|
|
160
|
+
authorization: "Bearer secret-token",
|
|
161
|
+
},
|
|
162
|
+
mode: "reader",
|
|
163
|
+
}),
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
] as unknown as ChatMessageSource[]);
|
|
169
|
+
|
|
170
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
171
|
+
type: "tool-card",
|
|
172
|
+
card: {
|
|
173
|
+
toolName: "open_url",
|
|
174
|
+
statusTone: "running",
|
|
175
|
+
summary: "url: https://example.com/really/long/path",
|
|
176
|
+
input: `{
|
|
177
|
+
"url": "https://example.com/really/long/path",
|
|
178
|
+
"headers": {
|
|
179
|
+
"authorization": "Bearer secret-token"
|
|
180
|
+
},
|
|
181
|
+
"mode": "reader"
|
|
182
|
+
}`,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it("keeps structured terminal results as structured data instead of raw json output", () => {
|
|
188
|
+
const terminalResult = {
|
|
189
|
+
status: "completed",
|
|
190
|
+
command: "python3 -m http.server 8765",
|
|
191
|
+
aggregated_output: "",
|
|
192
|
+
exit_code: 0,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const adapted = adapt([
|
|
196
|
+
{
|
|
197
|
+
id: "assistant-terminal-result",
|
|
198
|
+
role: "assistant",
|
|
199
|
+
parts: [
|
|
200
|
+
{
|
|
201
|
+
type: "tool-invocation",
|
|
202
|
+
toolInvocation: {
|
|
203
|
+
status: ToolInvocationStatus.RESULT,
|
|
204
|
+
toolCallId: "call-terminal-result",
|
|
205
|
+
toolName: "command_execution",
|
|
206
|
+
args: '{"command":"python3 -m http.server 8765"}',
|
|
207
|
+
result: terminalResult,
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
] as unknown as ChatMessageSource[]);
|
|
213
|
+
|
|
214
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
215
|
+
type: "tool-card",
|
|
216
|
+
card: {
|
|
217
|
+
toolName: "command_execution",
|
|
218
|
+
summary: "command: python3 -m http.server 8765",
|
|
219
|
+
output: undefined,
|
|
220
|
+
outputData: terminalResult,
|
|
221
|
+
statusTone: "success",
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
144
226
|
it("renders spawn tool cards from structured subagent status updates", () => {
|
|
145
227
|
const adapted = adapt([
|
|
146
228
|
{
|
|
@@ -377,3 +459,239 @@ it("renders asset tool results as previewable files", () => {
|
|
|
377
459
|
},
|
|
378
460
|
});
|
|
379
461
|
});
|
|
462
|
+
|
|
463
|
+
it("builds edit-file previews from structured args before the tool finishes", () => {
|
|
464
|
+
const adapted = adapt([
|
|
465
|
+
{
|
|
466
|
+
id: "assistant-edit-preview",
|
|
467
|
+
role: "assistant",
|
|
468
|
+
parts: [
|
|
469
|
+
{
|
|
470
|
+
type: "tool-invocation",
|
|
471
|
+
toolInvocation: {
|
|
472
|
+
status: ToolInvocationStatus.CALL,
|
|
473
|
+
toolCallId: "edit-call-1",
|
|
474
|
+
toolName: "edit_file",
|
|
475
|
+
args: JSON.stringify({
|
|
476
|
+
path: "src/app.ts",
|
|
477
|
+
oldText: "const color = 'red';",
|
|
478
|
+
newText: "const color = 'blue';",
|
|
479
|
+
}),
|
|
480
|
+
parsedArgs: {
|
|
481
|
+
path: "src/app.ts",
|
|
482
|
+
oldText: "const color = 'red';",
|
|
483
|
+
newText: "const color = 'blue';",
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
},
|
|
489
|
+
] as unknown as ChatMessageSource[]);
|
|
490
|
+
|
|
491
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
492
|
+
type: "tool-card",
|
|
493
|
+
card: {
|
|
494
|
+
toolName: "edit_file",
|
|
495
|
+
summary: "src/app.ts",
|
|
496
|
+
statusTone: "running",
|
|
497
|
+
fileOperation: {
|
|
498
|
+
blocks: [
|
|
499
|
+
{
|
|
500
|
+
path: "src/app.ts",
|
|
501
|
+
lines: [
|
|
502
|
+
{
|
|
503
|
+
kind: "remove",
|
|
504
|
+
text: "const color = 'red';",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
kind: "add",
|
|
508
|
+
text: "const color = 'blue';",
|
|
509
|
+
},
|
|
510
|
+
],
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it("builds write-file previews from partial native args before the JSON is complete", () => {
|
|
519
|
+
const adapted = adapt([
|
|
520
|
+
{
|
|
521
|
+
id: "assistant-write-preview",
|
|
522
|
+
role: "assistant",
|
|
523
|
+
parts: [
|
|
524
|
+
{
|
|
525
|
+
type: "tool-invocation",
|
|
526
|
+
toolInvocation: {
|
|
527
|
+
status: ToolInvocationStatus.PARTIAL_CALL,
|
|
528
|
+
toolCallId: "write-call-1",
|
|
529
|
+
toolName: "write_file",
|
|
530
|
+
args: '{"path":"games/snake.html","content":"<!DOCTYPE html>\\n<canvas id=\\"game\\"></canvas>\\n<script>const score = 1;',
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
},
|
|
535
|
+
] as unknown as ChatMessageSource[]);
|
|
536
|
+
|
|
537
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
538
|
+
type: "tool-card",
|
|
539
|
+
card: {
|
|
540
|
+
toolName: "write_file",
|
|
541
|
+
summary: "games/snake.html",
|
|
542
|
+
statusTone: "running",
|
|
543
|
+
statusLabel: "Running",
|
|
544
|
+
fileOperation: {
|
|
545
|
+
blocks: [
|
|
546
|
+
{
|
|
547
|
+
display: "preview",
|
|
548
|
+
path: "games/snake.html",
|
|
549
|
+
lines: expect.arrayContaining([
|
|
550
|
+
expect.objectContaining({
|
|
551
|
+
kind: "add",
|
|
552
|
+
text: "<!DOCTYPE html>",
|
|
553
|
+
}),
|
|
554
|
+
expect.objectContaining({
|
|
555
|
+
kind: "add",
|
|
556
|
+
text: '<canvas id="game"></canvas>',
|
|
557
|
+
}),
|
|
558
|
+
]),
|
|
559
|
+
},
|
|
560
|
+
],
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it("keeps completed write-file cards in preview mode instead of falling back to raw byte summaries", () => {
|
|
567
|
+
const adapted = adapt([
|
|
568
|
+
{
|
|
569
|
+
id: "assistant-write-result",
|
|
570
|
+
role: "assistant",
|
|
571
|
+
parts: [
|
|
572
|
+
{
|
|
573
|
+
type: "tool-invocation",
|
|
574
|
+
toolInvocation: {
|
|
575
|
+
status: ToolInvocationStatus.RESULT,
|
|
576
|
+
toolCallId: "write-result-1",
|
|
577
|
+
toolName: "write_file",
|
|
578
|
+
args: JSON.stringify({
|
|
579
|
+
path: "games/snake.html",
|
|
580
|
+
content: "<!DOCTYPE html>\n<canvas id=\"game\"></canvas>",
|
|
581
|
+
}),
|
|
582
|
+
result: "Wrote 3906 bytes to games/snake.html",
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
],
|
|
586
|
+
},
|
|
587
|
+
] as unknown as ChatMessageSource[]);
|
|
588
|
+
|
|
589
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
590
|
+
type: "tool-card",
|
|
591
|
+
card: {
|
|
592
|
+
toolName: "write_file",
|
|
593
|
+
summary: "games/snake.html",
|
|
594
|
+
statusTone: "success",
|
|
595
|
+
fileOperation: {
|
|
596
|
+
blocks: [
|
|
597
|
+
{
|
|
598
|
+
display: "preview",
|
|
599
|
+
path: "games/snake.html",
|
|
600
|
+
lines: [
|
|
601
|
+
{
|
|
602
|
+
kind: "add",
|
|
603
|
+
text: "<!DOCTYPE html>",
|
|
604
|
+
newLineNumber: 1,
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
kind: "add",
|
|
608
|
+
text: "<canvas id=\"game\"></canvas>",
|
|
609
|
+
newLineNumber: 2,
|
|
610
|
+
},
|
|
611
|
+
],
|
|
612
|
+
},
|
|
613
|
+
],
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
});
|
|
617
|
+
expect(adapted[0]?.parts[0]).not.toMatchObject({
|
|
618
|
+
type: "tool-card",
|
|
619
|
+
card: {
|
|
620
|
+
output: "Wrote 3906 bytes to games/snake.html",
|
|
621
|
+
},
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
it("renders codex file_change results as structured diff previews", () => {
|
|
626
|
+
const adapted = adapt([
|
|
627
|
+
{
|
|
628
|
+
id: "assistant-file-change",
|
|
629
|
+
role: "assistant",
|
|
630
|
+
parts: [
|
|
631
|
+
{
|
|
632
|
+
type: "tool-invocation",
|
|
633
|
+
toolInvocation: {
|
|
634
|
+
status: ToolInvocationStatus.RESULT,
|
|
635
|
+
toolCallId: "file-change-1",
|
|
636
|
+
toolName: "file_change",
|
|
637
|
+
args: JSON.stringify({
|
|
638
|
+
changes: [
|
|
639
|
+
{
|
|
640
|
+
path: "src/main.ts",
|
|
641
|
+
diff: [
|
|
642
|
+
"--- a/src/main.ts",
|
|
643
|
+
"+++ b/src/main.ts",
|
|
644
|
+
"@@",
|
|
645
|
+
"-console.log('old');",
|
|
646
|
+
"+console.log('new');",
|
|
647
|
+
].join("\n"),
|
|
648
|
+
},
|
|
649
|
+
],
|
|
650
|
+
}),
|
|
651
|
+
result: {
|
|
652
|
+
status: "completed",
|
|
653
|
+
changes: [
|
|
654
|
+
{
|
|
655
|
+
path: "src/main.ts",
|
|
656
|
+
diff: [
|
|
657
|
+
"--- a/src/main.ts",
|
|
658
|
+
"+++ b/src/main.ts",
|
|
659
|
+
"@@",
|
|
660
|
+
"-console.log('old');",
|
|
661
|
+
"+console.log('new');",
|
|
662
|
+
].join("\n"),
|
|
663
|
+
},
|
|
664
|
+
],
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
],
|
|
669
|
+
},
|
|
670
|
+
] as unknown as ChatMessageSource[]);
|
|
671
|
+
|
|
672
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
673
|
+
type: "tool-card",
|
|
674
|
+
card: {
|
|
675
|
+
toolName: "file_change",
|
|
676
|
+
summary: "src/main.ts",
|
|
677
|
+
statusTone: "success",
|
|
678
|
+
fileOperation: {
|
|
679
|
+
blocks: [
|
|
680
|
+
{
|
|
681
|
+
path: "src/main.ts",
|
|
682
|
+
lines: [
|
|
683
|
+
{
|
|
684
|
+
kind: "remove",
|
|
685
|
+
text: "console.log('old');",
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
kind: "add",
|
|
689
|
+
text: "console.log('new');",
|
|
690
|
+
},
|
|
691
|
+
],
|
|
692
|
+
},
|
|
693
|
+
],
|
|
694
|
+
},
|
|
695
|
+
},
|
|
696
|
+
});
|
|
697
|
+
});
|