@nextclaw/ui 0.11.21 → 0.11.22
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 +14 -0
- package/dist/assets/{ChannelsList-ByHWHkQS.js → ChannelsList-Zeys_w43.js} +6 -6
- package/dist/assets/ChatPage-DWOU_8P6.js +43 -0
- package/dist/assets/DocBrowser-B9OaZjmg.js +1 -0
- package/dist/assets/{DocBrowser-3y_NHZ71.js → DocBrowser-BmtBLFU0.js} +1 -1
- package/dist/assets/{DocBrowserContext-CVJuwCcw.js → DocBrowserContext-YIKkPb76.js} +1 -1
- package/dist/assets/{LogoBadge-D8fyilO-.js → LogoBadge-F7ZWdxLT.js} +1 -1
- package/dist/assets/MarketplacePage-BfaTTqN6.js +1 -0
- package/dist/assets/{MarketplacePage-CmhsZXr1.js → MarketplacePage-Cd4faegU.js} +2 -2
- package/dist/assets/{McpMarketplacePage-C7PkCYbp.js → McpMarketplacePage-C09Ngs7O.js} +2 -2
- package/dist/assets/ModelConfig-DJgdcgvQ.js +1 -0
- package/dist/assets/ProvidersList-w0rVFIBf.js +1 -0
- package/dist/assets/RemoteAccessPage-BJ_ckkOV.js +1 -0
- package/dist/assets/RuntimeConfig-Cmn2xPQO.js +1 -0
- package/dist/assets/{SearchConfig-Dm7r2yfp.js → SearchConfig-BT13qpR_.js} +1 -1
- package/dist/assets/{SecretsConfig-BBP_mbQh.js → SecretsConfig-CvqEVn0B.js} +2 -2
- package/dist/assets/{SessionsConfig-6wNJloZN.js → SessionsConfig-DHHcYznk.js} +2 -2
- package/dist/assets/{book-open-B26jGBjY.js → book-open-CXoF5nQC.js} +1 -1
- package/dist/assets/chat-session-display-VW6ZMvZP.js +1 -0
- package/dist/assets/{chunk-JZWAC4HX-B-4B29RN.js → chunk-JZWAC4HX-CvRWvTy5.js} +1 -1
- package/dist/assets/{config-BaC29Qf-.js → config-DJswxxE8.js} +1 -1
- package/dist/assets/{createLucideIcon-DiFAvXmK.js → createLucideIcon-CjGHOWb6.js} +1 -1
- package/dist/assets/{dist-pCfWPG1A.js → dist-Cl2QB-2y.js} +1 -1
- package/dist/assets/{dist-kW_O3kyZ.js → dist-nqTTbVdA.js} +1 -1
- package/dist/assets/{external-link-D5-p-Gmm.js → external-link-tIO7zING.js} +1 -1
- package/dist/assets/{hash-BlwrSV0q.js → hash-JWUyl1pT.js} +1 -1
- package/dist/assets/i18n-CDHMXlRZ.js +1 -0
- package/dist/assets/index-BlH4-cBw.css +1 -0
- package/dist/assets/{index-DvKS3L9j.js → index-C6d0xmtm.js} +3 -3
- package/dist/assets/{label-RyXfZqkP.js → label-BIpeNu4r.js} +1 -1
- package/dist/assets/loader-circle-Cs8XVFTw.js +1 -0
- package/dist/assets/{logos-Bpl8QTgI.js → logos-DThdM9lk.js} +1 -1
- package/dist/assets/{page-layout--S0YBU0W.js → page-layout-D3Xo605Z.js} +1 -1
- package/dist/assets/plus-PHf8q-Ct.js +1 -0
- package/dist/assets/{popover-BEjfbEwy.js → popover-BJRUGA_H.js} +1 -1
- package/dist/assets/provider-models-bz5y28rq.js +1 -0
- package/dist/assets/{react-BuSP2-8B.js → react-7ZHqQtEV.js} +1 -1
- package/dist/assets/refresh-ccw-CC6-_QuL.js +1 -0
- package/dist/assets/{save-DPPPpD_c.js → save-DJM5RRWW.js} +1 -1
- package/dist/assets/search-C91yH_6y.js +1 -0
- package/dist/assets/{security-config-6t78Ph-I.js → security-config-T5zpg16O.js} +1 -1
- package/dist/assets/{select-CT50pzod.js → select-DSkTc61S.js} +1 -1
- package/dist/assets/skeleton-Dzg-HOiN.js +1 -0
- package/dist/assets/{status-dot-BbBqRHfh.js → status-dot-LNBlDu3q.js} +1 -1
- package/dist/assets/{switch-D3l6AcCk.js → switch-Bo-Y46HZ.js} +1 -1
- package/dist/assets/tabs-custom-DXv507_2.js +1 -0
- package/dist/assets/{trash-2-B2_AGVE3.js → trash-2-DFZmW6Gg.js} +1 -1
- package/dist/assets/useConfirmDialog-Bs5Ll17m.js +1 -0
- package/dist/assets/{useMutation-BzCrO8j-.js → useMutation-DrZrOgVL.js} +1 -1
- package/dist/assets/x-D7Q1yqSF.js +1 -0
- package/dist/index.html +18 -18
- package/package.json +3 -3
- package/src/api/ncp-session.test.ts +37 -0
- package/src/api/ncp-session.ts +29 -1
- package/src/api/server-path.ts +23 -0
- package/src/api/types.ts +41 -0
- package/src/components/chat/ChatConversationPanel.test.tsx +43 -7
- package/src/components/chat/ChatConversationPanel.tsx +23 -17
- package/src/components/chat/ChatSidebar.test.tsx +2 -2
- package/src/components/chat/ChatSidebar.tsx +2 -2
- package/src/components/chat/adapters/chat-input-bar.adapter.test.ts +1 -0
- package/src/components/chat/adapters/chat-input-bar.adapter.ts +7 -2
- package/src/components/chat/adapters/chat-message-part.adapter.ts +13 -9
- package/src/components/chat/adapters/chat-message.adapter.test.ts +76 -4
- package/src/components/chat/adapters/{chat-message.file-operation-card.ts → file-operation/card.ts} +74 -181
- package/src/components/chat/adapters/{chat-message.file-operation-diff.ts → file-operation/diff.ts} +178 -188
- package/src/components/chat/adapters/file-operation/line-builder.ts +249 -0
- package/src/components/chat/adapters/file-operation/record-readers.ts +233 -0
- package/src/components/chat/chat-composer-state.ts +3 -3
- package/src/components/chat/chat-session-display.test.ts +21 -0
- package/src/components/chat/chat-session-display.ts +6 -1
- package/src/components/chat/containers/chat-input-bar.container.tsx +21 -24
- package/src/components/chat/hooks/use-chat-session-label.ts +19 -0
- package/src/components/chat/hooks/use-chat-session-project.test.tsx +117 -0
- package/src/components/chat/hooks/use-chat-session-project.ts +40 -0
- package/src/components/chat/{chat-session-label.service.ts → hooks/use-chat-session-update.ts} +11 -7
- package/src/components/chat/managers/chat-session-list.manager.ts +5 -1
- package/src/components/chat/ncp/NcpChatPage.tsx +55 -17
- package/src/components/chat/ncp/ncp-chat-page-data.test.ts +33 -0
- package/src/components/chat/ncp/ncp-chat-page-data.ts +21 -15
- package/src/components/chat/ncp/ncp-session-adapter.test.ts +3 -0
- package/src/components/chat/ncp/ncp-session-adapter.ts +16 -0
- package/src/components/chat/session-header/chat-session-header-actions.test.tsx +63 -0
- package/src/components/chat/session-header/chat-session-header-actions.tsx +95 -0
- package/src/components/chat/session-header/chat-session-header-menu-item.tsx +35 -0
- package/src/components/chat/session-header/chat-session-project-badge.test.tsx +66 -0
- package/src/components/chat/session-header/chat-session-project-badge.tsx +102 -0
- package/src/components/chat/session-header/chat-session-project-dialog.tsx +34 -0
- package/src/components/chat/stores/chat-input.store.ts +6 -3
- package/src/components/chat/stores/chat-thread.store.ts +6 -2
- package/src/components/path-picker/server-path-picker-dialog.test.tsx +92 -0
- package/src/components/path-picker/server-path-picker-dialog.tsx +282 -0
- package/src/hooks/server-path/use-server-path-browse.ts +19 -0
- package/src/hooks/useConfig.ts +26 -1
- package/src/lib/i18n/i18n-language-owner.ts +94 -0
- package/src/lib/i18n/i18n.path-picker.ts +12 -0
- package/src/lib/i18n.chat.ts +23 -0
- package/src/lib/i18n.ts +21 -84
- package/src/lib/session-project/session-project.utils.ts +30 -0
- package/dist/assets/ChatPage-FdT3pDnw.js +0 -42
- package/dist/assets/DocBrowser-CMdPdbZj.js +0 -1
- package/dist/assets/MarketplacePage-9oKmxN2n.js +0 -1
- package/dist/assets/ModelConfig-DmCY6jWM.js +0 -1
- package/dist/assets/ProvidersList-ClT-34aX.js +0 -1
- package/dist/assets/RemoteAccessPage-B6hUZl1O.js +0 -1
- package/dist/assets/RuntimeConfig-C5aqliGk.js +0 -1
- package/dist/assets/chat-session-display-Bjmn4aIZ.js +0 -1
- package/dist/assets/i18n-CSytxMFI.js +0 -1
- package/dist/assets/index-CUy6doWo.css +0 -1
- package/dist/assets/loader-circle-B2J777gj.js +0 -1
- package/dist/assets/plus-CM9XJ0Tf.js +0 -1
- package/dist/assets/provider-models-C8JQUd1E.js +0 -1
- package/dist/assets/search-Ctaw34Kp.js +0 -1
- package/dist/assets/skeleton-Bycyb0zU.js +0 -1
- package/dist/assets/tabs-custom-TZQ5WPWP.js +0 -1
- package/dist/assets/useConfirmDialog-BDpdjfIO.js +0 -1
- package/dist/assets/x-CHOBE-63.js +0 -1
- /package/dist/assets/{config-hints-fGnUjDe9.js → config-hints-WtpHP_DW.js} +0 -0
- /package/dist/assets/{config-layout-B-7erZRN.js → config-layout-LQ10ozRC.js} +0 -0
- /package/dist/assets/{marketplace-localization-CXeGRf6E.js → marketplace-localization-CxSTG9wr.js} +0 -0
|
@@ -12,6 +12,7 @@ export type ChatThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' |
|
|
|
12
12
|
export type ChatSkillRecord = {
|
|
13
13
|
key: string;
|
|
14
14
|
label: string;
|
|
15
|
+
scopeLabel?: string;
|
|
15
16
|
description?: string;
|
|
16
17
|
descriptionZh?: string;
|
|
17
18
|
badgeLabel?: string;
|
|
@@ -43,6 +44,7 @@ const SLASH_ITEM_MATCH_SCORE = {
|
|
|
43
44
|
export type ChatInputBarAdapterTexts = {
|
|
44
45
|
slashSkillSubtitle: string;
|
|
45
46
|
slashSkillSpecLabel: string;
|
|
47
|
+
slashSkillScopeLabel: string;
|
|
46
48
|
noSkillDescription: string;
|
|
47
49
|
recentSkillsLabel: string;
|
|
48
50
|
allSkillsLabel: string;
|
|
@@ -171,7 +173,7 @@ function prioritizeSkillRecords(skillRecords: ChatSkillRecord[], recentSkillValu
|
|
|
171
173
|
export function buildChatSlashItems(
|
|
172
174
|
skillRecords: ChatSkillRecord[],
|
|
173
175
|
normalizedSlashQuery: string,
|
|
174
|
-
texts: Pick<ChatInputBarAdapterTexts, 'slashSkillSubtitle' | 'slashSkillSpecLabel' | 'noSkillDescription'>,
|
|
176
|
+
texts: Pick<ChatInputBarAdapterTexts, 'slashSkillSubtitle' | 'slashSkillSpecLabel' | 'slashSkillScopeLabel' | 'noSkillDescription'>,
|
|
175
177
|
recentSkillValues: string[] = []
|
|
176
178
|
): ChatSlashItem[] {
|
|
177
179
|
const skillSortCollator = new Intl.Collator(undefined, { sensitivity: 'base', numeric: true });
|
|
@@ -211,7 +213,10 @@ export function buildChatSlashItems(
|
|
|
211
213
|
title: record.label || record.key,
|
|
212
214
|
subtitle: texts.slashSkillSubtitle,
|
|
213
215
|
description: (record.descriptionZh ?? record.description ?? '').trim() || texts.noSkillDescription,
|
|
214
|
-
detailLines: [
|
|
216
|
+
detailLines: [
|
|
217
|
+
`${texts.slashSkillSpecLabel}: ${record.key}`,
|
|
218
|
+
...(record.scopeLabel ? [`${texts.slashSkillScopeLabel}: ${record.scopeLabel}`] : [])
|
|
219
|
+
],
|
|
215
220
|
value: record.key
|
|
216
221
|
}));
|
|
217
222
|
}
|
|
@@ -3,14 +3,12 @@ import {
|
|
|
3
3
|
summarizeToolArgs,
|
|
4
4
|
type ToolCard,
|
|
5
5
|
} from "@/lib/chat-message";
|
|
6
|
-
import {
|
|
7
|
-
type ChatInlineTokenSource,
|
|
8
|
-
} from "@/components/chat/chat-inline-token.utils";
|
|
6
|
+
import { type ChatInlineTokenSource } from "@/components/chat/chat-inline-token.utils";
|
|
9
7
|
import {
|
|
10
8
|
buildRenderableText,
|
|
11
9
|
buildTextPart,
|
|
12
10
|
} from "@/components/chat/adapters/chat-message-inline-content.adapter";
|
|
13
|
-
import { buildFileOperationCardData } from "@/components/chat/adapters/
|
|
11
|
+
import { buildFileOperationCardData } from "@/components/chat/adapters/file-operation/card";
|
|
14
12
|
import { buildSubagentToolCard } from "@/components/chat/adapters/chat-message.subagent-tool-card";
|
|
15
13
|
import type {
|
|
16
14
|
ChatMessagePartViewModel,
|
|
@@ -116,7 +114,9 @@ function readOptionalNumber(value: unknown): number | null {
|
|
|
116
114
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : null;
|
|
117
115
|
}
|
|
118
116
|
|
|
119
|
-
function isTerminalResultRecord(
|
|
117
|
+
function isTerminalResultRecord(
|
|
118
|
+
value: unknown,
|
|
119
|
+
): value is Record<string, unknown> {
|
|
120
120
|
if (!isRecord(value)) {
|
|
121
121
|
return false;
|
|
122
122
|
}
|
|
@@ -181,9 +181,10 @@ function buildToolCard(
|
|
|
181
181
|
toolName: toolCard.name,
|
|
182
182
|
summary: toolCard.detail,
|
|
183
183
|
inputLabel: texts.toolInputLabel,
|
|
184
|
-
input:
|
|
185
|
-
|
|
186
|
-
|
|
184
|
+
input:
|
|
185
|
+
"input" in toolCard && typeof toolCard.input === "string"
|
|
186
|
+
? toolCard.input
|
|
187
|
+
: undefined,
|
|
187
188
|
output: toolCard.text,
|
|
188
189
|
outputData: toolCard.outputData,
|
|
189
190
|
hasResult: Boolean(toolCard.hasResult),
|
|
@@ -269,7 +270,10 @@ function parseStructuredValue(value: unknown): unknown {
|
|
|
269
270
|
}
|
|
270
271
|
}
|
|
271
272
|
|
|
272
|
-
function buildToolInvocationInput(
|
|
273
|
+
function buildToolInvocationInput(
|
|
274
|
+
args?: unknown,
|
|
275
|
+
parsedArgs?: unknown,
|
|
276
|
+
): string | undefined {
|
|
273
277
|
const source = parsedArgs ?? parseStructuredValue(args);
|
|
274
278
|
const text = stringifyUnknown(source).trim();
|
|
275
279
|
return text || undefined;
|
|
@@ -488,6 +488,11 @@ it("builds edit-file previews from structured args before the tool finishes", ()
|
|
|
488
488
|
},
|
|
489
489
|
] as unknown as ChatMessageSource[]);
|
|
490
490
|
|
|
491
|
+
const editLines =
|
|
492
|
+
adapted[0]?.parts[0]?.type === "tool-card"
|
|
493
|
+
? (adapted[0].parts[0].card.fileOperation?.blocks[0]?.lines ?? [])
|
|
494
|
+
: [];
|
|
495
|
+
|
|
491
496
|
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
492
497
|
type: "tool-card",
|
|
493
498
|
card: {
|
|
@@ -513,6 +518,71 @@ it("builds edit-file previews from structured args before the tool finishes", ()
|
|
|
513
518
|
},
|
|
514
519
|
},
|
|
515
520
|
});
|
|
521
|
+
expect(editLines[0]).not.toHaveProperty("oldLineNumber");
|
|
522
|
+
expect(editLines[1]).not.toHaveProperty("newLineNumber");
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it("uses structured edit-file result line numbers after the tool finishes", () => {
|
|
526
|
+
const adapted = adapt([
|
|
527
|
+
{
|
|
528
|
+
id: "assistant-edit-result",
|
|
529
|
+
role: "assistant",
|
|
530
|
+
parts: [
|
|
531
|
+
{
|
|
532
|
+
type: "tool-invocation",
|
|
533
|
+
toolInvocation: {
|
|
534
|
+
status: ToolInvocationStatus.RESULT,
|
|
535
|
+
toolCallId: "edit-result-1",
|
|
536
|
+
toolName: "edit_file",
|
|
537
|
+
args: JSON.stringify({
|
|
538
|
+
path: "src/app.ts",
|
|
539
|
+
oldText: "const color = 'red';",
|
|
540
|
+
newText: "const color = 'blue';",
|
|
541
|
+
}),
|
|
542
|
+
parsedArgs: {
|
|
543
|
+
path: "src/app.ts",
|
|
544
|
+
oldText: "const color = 'red';",
|
|
545
|
+
newText: "const color = 'blue';",
|
|
546
|
+
},
|
|
547
|
+
result: {
|
|
548
|
+
path: "src/app.ts",
|
|
549
|
+
oldStartLine: 27,
|
|
550
|
+
newStartLine: 27,
|
|
551
|
+
message: "Edited src/app.ts",
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
],
|
|
556
|
+
},
|
|
557
|
+
] as unknown as ChatMessageSource[]);
|
|
558
|
+
|
|
559
|
+
expect(adapted[0]?.parts[0]).toMatchObject({
|
|
560
|
+
type: "tool-card",
|
|
561
|
+
card: {
|
|
562
|
+
toolName: "edit_file",
|
|
563
|
+
summary: "src/app.ts",
|
|
564
|
+
statusTone: "success",
|
|
565
|
+
fileOperation: {
|
|
566
|
+
blocks: [
|
|
567
|
+
{
|
|
568
|
+
path: "src/app.ts",
|
|
569
|
+
lines: [
|
|
570
|
+
{
|
|
571
|
+
kind: "remove",
|
|
572
|
+
text: "const color = 'red';",
|
|
573
|
+
oldLineNumber: 27,
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
kind: "add",
|
|
577
|
+
text: "const color = 'blue';",
|
|
578
|
+
newLineNumber: 27,
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
},
|
|
582
|
+
],
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
});
|
|
516
586
|
});
|
|
517
587
|
|
|
518
588
|
it("builds write-file previews from partial native args before the JSON is complete", () => {
|
|
@@ -577,7 +647,7 @@ it("keeps completed write-file cards in preview mode instead of falling back to
|
|
|
577
647
|
toolName: "write_file",
|
|
578
648
|
args: JSON.stringify({
|
|
579
649
|
path: "games/snake.html",
|
|
580
|
-
content:
|
|
650
|
+
content: '<!DOCTYPE html>\n<canvas id="game"></canvas>',
|
|
581
651
|
}),
|
|
582
652
|
result: "Wrote 3906 bytes to games/snake.html",
|
|
583
653
|
},
|
|
@@ -605,7 +675,7 @@ it("keeps completed write-file cards in preview mode instead of falling back to
|
|
|
605
675
|
},
|
|
606
676
|
{
|
|
607
677
|
kind: "add",
|
|
608
|
-
text:
|
|
678
|
+
text: '<canvas id="game"></canvas>',
|
|
609
679
|
newLineNumber: 2,
|
|
610
680
|
},
|
|
611
681
|
],
|
|
@@ -641,7 +711,7 @@ it("renders codex file_change results as structured diff previews", () => {
|
|
|
641
711
|
diff: [
|
|
642
712
|
"--- a/src/main.ts",
|
|
643
713
|
"+++ b/src/main.ts",
|
|
644
|
-
"@@",
|
|
714
|
+
"@@ -109,1 +109,1 @@",
|
|
645
715
|
"-console.log('old');",
|
|
646
716
|
"+console.log('new');",
|
|
647
717
|
].join("\n"),
|
|
@@ -656,7 +726,7 @@ it("renders codex file_change results as structured diff previews", () => {
|
|
|
656
726
|
diff: [
|
|
657
727
|
"--- a/src/main.ts",
|
|
658
728
|
"+++ b/src/main.ts",
|
|
659
|
-
"@@",
|
|
729
|
+
"@@ -109,1 +109,1 @@",
|
|
660
730
|
"-console.log('old');",
|
|
661
731
|
"+console.log('new');",
|
|
662
732
|
].join("\n"),
|
|
@@ -683,10 +753,12 @@ it("renders codex file_change results as structured diff previews", () => {
|
|
|
683
753
|
{
|
|
684
754
|
kind: "remove",
|
|
685
755
|
text: "console.log('old');",
|
|
756
|
+
oldLineNumber: 109,
|
|
686
757
|
},
|
|
687
758
|
{
|
|
688
759
|
kind: "add",
|
|
689
760
|
text: "console.log('new');",
|
|
761
|
+
newLineNumber: 109,
|
|
690
762
|
},
|
|
691
763
|
],
|
|
692
764
|
},
|
package/src/components/chat/adapters/{chat-message.file-operation-card.ts → file-operation/card.ts}
RENAMED
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ChatFileOperationBlockViewModel,
|
|
3
|
-
} from "@nextclaw/agent-chat-ui";
|
|
1
|
+
import type { ChatFileOperationBlockViewModel } from "@nextclaw/agent-chat-ui";
|
|
4
2
|
import {
|
|
5
3
|
buildFullReplaceBlock,
|
|
6
4
|
buildRawPreviewBlock,
|
|
7
5
|
parsePatchBlocks,
|
|
8
6
|
type ParsedBlock,
|
|
9
|
-
} from "@/components/chat/adapters/
|
|
7
|
+
} from "@/components/chat/adapters/file-operation/diff";
|
|
8
|
+
import {
|
|
9
|
+
isRecord,
|
|
10
|
+
readAfterText,
|
|
11
|
+
readBeforeText,
|
|
12
|
+
readNewStartLine,
|
|
13
|
+
readNonEmptyString,
|
|
14
|
+
readOldStartLine,
|
|
15
|
+
readOperation,
|
|
16
|
+
readPartialRecordPayload,
|
|
17
|
+
readPatchText,
|
|
18
|
+
readPath,
|
|
19
|
+
readRecordPayload,
|
|
20
|
+
} from "@/components/chat/adapters/file-operation/record-readers";
|
|
10
21
|
import { readPartialJsonStringField } from "@/components/chat/adapters/chat-message.partial-json";
|
|
11
22
|
|
|
12
23
|
type ToolInvocationSource = {
|
|
@@ -33,167 +44,9 @@ const FILE_TOOL_NAMES = new Set([
|
|
|
33
44
|
"apply_patch",
|
|
34
45
|
]);
|
|
35
46
|
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
function readNonEmptyString(value: unknown): string | null {
|
|
41
|
-
if (typeof value !== "string") {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
const trimmed = value.trim();
|
|
45
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function normalizePath(value: unknown): string | null {
|
|
49
|
-
if (typeof value === "string" && value.trim()) {
|
|
50
|
-
return value.trim();
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function readRecordPayload(value: unknown): Record<string, unknown> | null {
|
|
56
|
-
if (isRecord(value)) {
|
|
57
|
-
return value;
|
|
58
|
-
}
|
|
59
|
-
if (typeof value !== "string") {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
const trimmed = value.trim();
|
|
63
|
-
if (!trimmed.startsWith("{")) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
const parsed = JSON.parse(trimmed) as unknown;
|
|
68
|
-
return isRecord(parsed) ? parsed : null;
|
|
69
|
-
} catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function readPartialRecordPayload(value: unknown): Record<string, unknown> | null {
|
|
75
|
-
if (isRecord(value)) {
|
|
76
|
-
return value;
|
|
77
|
-
}
|
|
78
|
-
if (typeof value !== "string") {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
const trimmed = value.trim();
|
|
82
|
-
if (!trimmed.startsWith("{")) {
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
const path =
|
|
86
|
-
readPartialJsonStringField(trimmed, [
|
|
87
|
-
"path",
|
|
88
|
-
"filePath",
|
|
89
|
-
"file_path",
|
|
90
|
-
"targetPath",
|
|
91
|
-
"target_path",
|
|
92
|
-
"filename",
|
|
93
|
-
"name",
|
|
94
|
-
])?.value ?? null;
|
|
95
|
-
const content =
|
|
96
|
-
readPartialJsonStringField(
|
|
97
|
-
trimmed,
|
|
98
|
-
["content", "text", "afterText", "after_text"],
|
|
99
|
-
)?.value ?? null;
|
|
100
|
-
const oldText =
|
|
101
|
-
readPartialJsonStringField(
|
|
102
|
-
trimmed,
|
|
103
|
-
["oldText", "beforeText", "before_text"],
|
|
104
|
-
)?.value ?? null;
|
|
105
|
-
const newText =
|
|
106
|
-
readPartialJsonStringField(
|
|
107
|
-
trimmed,
|
|
108
|
-
["newText", "afterText", "after_text"],
|
|
109
|
-
)?.value ?? null;
|
|
110
|
-
const patch =
|
|
111
|
-
readPartialJsonStringField(
|
|
112
|
-
trimmed,
|
|
113
|
-
["patch", "diff", "unifiedDiff", "unified_diff"],
|
|
114
|
-
)?.value ?? null;
|
|
115
|
-
|
|
116
|
-
const partialRecord: Record<string, unknown> = {};
|
|
117
|
-
if (path) {
|
|
118
|
-
partialRecord.path = path;
|
|
119
|
-
}
|
|
120
|
-
if (content) {
|
|
121
|
-
partialRecord.content = content;
|
|
122
|
-
}
|
|
123
|
-
if (oldText) {
|
|
124
|
-
partialRecord.oldText = oldText;
|
|
125
|
-
}
|
|
126
|
-
if (newText) {
|
|
127
|
-
partialRecord.newText = newText;
|
|
128
|
-
}
|
|
129
|
-
if (patch) {
|
|
130
|
-
partialRecord.patch = patch;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return Object.keys(partialRecord).length > 0 ? partialRecord : null;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function readPath(record: Record<string, unknown>): string | null {
|
|
137
|
-
return (
|
|
138
|
-
normalizePath(record.path) ??
|
|
139
|
-
normalizePath(record.filePath) ??
|
|
140
|
-
normalizePath(record.file_path) ??
|
|
141
|
-
normalizePath(record.targetPath) ??
|
|
142
|
-
normalizePath(record.target_path) ??
|
|
143
|
-
normalizePath(record.filename) ??
|
|
144
|
-
normalizePath(record.name)
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function readOperation(record: Record<string, unknown>): string | null {
|
|
149
|
-
return (
|
|
150
|
-
readNonEmptyString(record.operation) ??
|
|
151
|
-
readNonEmptyString(record.op) ??
|
|
152
|
-
readNonEmptyString(record.action) ??
|
|
153
|
-
readNonEmptyString(record.kind) ??
|
|
154
|
-
readNonEmptyString(record.type) ??
|
|
155
|
-
readNonEmptyString(record.status)
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function readPatchText(record: Record<string, unknown>): string | null {
|
|
160
|
-
return (
|
|
161
|
-
readNonEmptyString(record.patch) ??
|
|
162
|
-
readNonEmptyString(record.diff) ??
|
|
163
|
-
readNonEmptyString(record.unifiedDiff) ??
|
|
164
|
-
readNonEmptyString(record.unified_diff)
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function readBeforeText(record: Record<string, unknown>): string | null {
|
|
169
|
-
return (
|
|
170
|
-
readNonEmptyString(record.beforeText) ??
|
|
171
|
-
readNonEmptyString(record.before_text) ??
|
|
172
|
-
readNonEmptyString(record.oldText) ??
|
|
173
|
-
readNonEmptyString(record.old_text) ??
|
|
174
|
-
readNonEmptyString(record.oldContent) ??
|
|
175
|
-
readNonEmptyString(record.old_content) ??
|
|
176
|
-
readNonEmptyString(record.before) ??
|
|
177
|
-
readNonEmptyString(record.previous)
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function readAfterText(record: Record<string, unknown>): string | null {
|
|
182
|
-
return (
|
|
183
|
-
readNonEmptyString(record.afterText) ??
|
|
184
|
-
readNonEmptyString(record.after_text) ??
|
|
185
|
-
readNonEmptyString(record.newText) ??
|
|
186
|
-
readNonEmptyString(record.new_text) ??
|
|
187
|
-
readNonEmptyString(record.newContent) ??
|
|
188
|
-
readNonEmptyString(record.new_content) ??
|
|
189
|
-
readNonEmptyString(record.content) ??
|
|
190
|
-
readNonEmptyString(record.text) ??
|
|
191
|
-
readNonEmptyString(record.after) ??
|
|
192
|
-
readNonEmptyString(record.updated)
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function finalizeParsedBlocks(blocks: ParsedBlock[]): FileOperationCardData | null {
|
|
47
|
+
function finalizeParsedBlocks(
|
|
48
|
+
blocks: ParsedBlock[],
|
|
49
|
+
): FileOperationCardData | null {
|
|
197
50
|
const normalizedBlocks = blocks
|
|
198
51
|
.map((block, index) => ({
|
|
199
52
|
key: `${block.path}-${index + 1}`,
|
|
@@ -224,9 +77,14 @@ function finalizeParsedBlocks(blocks: ParsedBlock[]): FileOperationCardData | nu
|
|
|
224
77
|
};
|
|
225
78
|
}
|
|
226
79
|
|
|
227
|
-
function buildBlockFromChangeRecord(
|
|
80
|
+
function buildBlockFromChangeRecord(
|
|
81
|
+
record: Record<string, unknown>,
|
|
82
|
+
fallbackPath: string,
|
|
83
|
+
): ParsedBlock | null {
|
|
228
84
|
const path = readPath(record) ?? fallbackPath;
|
|
229
85
|
const operation = readOperation(record);
|
|
86
|
+
const oldStartLine = readOldStartLine(record);
|
|
87
|
+
const newStartLine = readNewStartLine(record);
|
|
230
88
|
const patchText = readPatchText(record);
|
|
231
89
|
if (patchText) {
|
|
232
90
|
const parsedBlocks = parsePatchBlocks(patchText);
|
|
@@ -243,6 +101,8 @@ function buildBlockFromChangeRecord(record: Record<string, unknown>, fallbackPat
|
|
|
243
101
|
beforeText,
|
|
244
102
|
afterText,
|
|
245
103
|
operation,
|
|
104
|
+
oldStartLine,
|
|
105
|
+
newStartLine,
|
|
246
106
|
});
|
|
247
107
|
}
|
|
248
108
|
|
|
@@ -252,6 +112,8 @@ function buildBlockFromChangeRecord(record: Record<string, unknown>, fallbackPat
|
|
|
252
112
|
path,
|
|
253
113
|
text: previewText,
|
|
254
114
|
operation,
|
|
115
|
+
oldStartLine,
|
|
116
|
+
newStartLine,
|
|
255
117
|
});
|
|
256
118
|
}
|
|
257
119
|
|
|
@@ -281,7 +143,9 @@ function buildBlocksFromChanges(changes: unknown): ParsedBlock[] {
|
|
|
281
143
|
blocks.push(block);
|
|
282
144
|
return;
|
|
283
145
|
}
|
|
284
|
-
const nestedChanges = Array.isArray(entry.changes)
|
|
146
|
+
const nestedChanges = Array.isArray(entry.changes)
|
|
147
|
+
? buildBlocksFromChanges(entry.changes)
|
|
148
|
+
: [];
|
|
285
149
|
if (nestedChanges.length > 0) {
|
|
286
150
|
blocks.push(...nestedChanges);
|
|
287
151
|
}
|
|
@@ -289,7 +153,9 @@ function buildBlocksFromChanges(changes: unknown): ParsedBlock[] {
|
|
|
289
153
|
return blocks;
|
|
290
154
|
}
|
|
291
155
|
|
|
292
|
-
function buildFileChangeCardData(
|
|
156
|
+
function buildFileChangeCardData(
|
|
157
|
+
invocation: ToolInvocationSource,
|
|
158
|
+
): FileOperationCardData | null {
|
|
293
159
|
const sourceRecord =
|
|
294
160
|
readRecordPayload(invocation.result) ??
|
|
295
161
|
readRecordPayload(invocation.parsedArgs) ??
|
|
@@ -301,7 +167,9 @@ function buildFileChangeCardData(invocation: ToolInvocationSource): FileOperatio
|
|
|
301
167
|
return finalizeParsedBlocks(blocks);
|
|
302
168
|
}
|
|
303
169
|
|
|
304
|
-
function buildReadFileCardData(
|
|
170
|
+
function buildReadFileCardData(
|
|
171
|
+
invocation: ToolInvocationSource,
|
|
172
|
+
): FileOperationCardData | null {
|
|
305
173
|
const argsRecord =
|
|
306
174
|
readRecordPayload(invocation.parsedArgs) ??
|
|
307
175
|
readRecordPayload(invocation.args) ??
|
|
@@ -322,7 +190,9 @@ function buildReadFileCardData(invocation: ToolInvocationSource): FileOperationC
|
|
|
322
190
|
);
|
|
323
191
|
}
|
|
324
192
|
|
|
325
|
-
function buildWriteFileCardData(
|
|
193
|
+
function buildWriteFileCardData(
|
|
194
|
+
invocation: ToolInvocationSource,
|
|
195
|
+
): FileOperationCardData | null {
|
|
326
196
|
const isStreamingPartialCall = invocation.status === "partial-call";
|
|
327
197
|
if (isStreamingPartialCall && typeof invocation.args === "string") {
|
|
328
198
|
const pathField = readPartialJsonStringField(invocation.args, [
|
|
@@ -334,10 +204,12 @@ function buildWriteFileCardData(invocation: ToolInvocationSource): FileOperation
|
|
|
334
204
|
"filename",
|
|
335
205
|
"name",
|
|
336
206
|
]);
|
|
337
|
-
const contentField = readPartialJsonStringField(
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
207
|
+
const contentField = readPartialJsonStringField(invocation.args, [
|
|
208
|
+
"content",
|
|
209
|
+
"text",
|
|
210
|
+
"afterText",
|
|
211
|
+
"after_text",
|
|
212
|
+
]);
|
|
341
213
|
if (pathField?.value && contentField?.value) {
|
|
342
214
|
const previewBlock = buildRawPreviewBlock({
|
|
343
215
|
path: pathField.value,
|
|
@@ -373,17 +245,32 @@ function buildWriteFileCardData(invocation: ToolInvocationSource): FileOperation
|
|
|
373
245
|
);
|
|
374
246
|
}
|
|
375
247
|
|
|
376
|
-
function buildEditFileCardData(
|
|
248
|
+
function buildEditFileCardData(
|
|
249
|
+
invocation: ToolInvocationSource,
|
|
250
|
+
): FileOperationCardData | null {
|
|
251
|
+
const resultRecord = readRecordPayload(invocation.result);
|
|
377
252
|
const argsRecord =
|
|
378
253
|
readRecordPayload(invocation.parsedArgs) ??
|
|
379
254
|
readRecordPayload(invocation.args) ??
|
|
380
255
|
readPartialRecordPayload(invocation.args);
|
|
381
|
-
if (!argsRecord) {
|
|
256
|
+
if (!resultRecord && !argsRecord) {
|
|
382
257
|
return null;
|
|
383
258
|
}
|
|
384
|
-
const path =
|
|
385
|
-
|
|
386
|
-
|
|
259
|
+
const path =
|
|
260
|
+
(resultRecord ? readPath(resultRecord) : null) ??
|
|
261
|
+
(argsRecord ? readPath(argsRecord) : null);
|
|
262
|
+
const beforeText =
|
|
263
|
+
(resultRecord ? readBeforeText(resultRecord) : null) ??
|
|
264
|
+
(argsRecord ? readBeforeText(argsRecord) : null);
|
|
265
|
+
const afterText =
|
|
266
|
+
(resultRecord ? readAfterText(resultRecord) : null) ??
|
|
267
|
+
(argsRecord ? readAfterText(argsRecord) : null);
|
|
268
|
+
const oldStartLine =
|
|
269
|
+
(resultRecord ? readOldStartLine(resultRecord) : null) ??
|
|
270
|
+
(argsRecord ? readOldStartLine(argsRecord) : null);
|
|
271
|
+
const newStartLine =
|
|
272
|
+
(resultRecord ? readNewStartLine(resultRecord) : null) ??
|
|
273
|
+
(argsRecord ? readNewStartLine(argsRecord) : null);
|
|
387
274
|
if (!path || (beforeText == null && afterText == null)) {
|
|
388
275
|
return null;
|
|
389
276
|
}
|
|
@@ -394,14 +281,20 @@ function buildEditFileCardData(invocation: ToolInvocationSource): FileOperationC
|
|
|
394
281
|
beforeText,
|
|
395
282
|
afterText,
|
|
396
283
|
operation: "edit",
|
|
284
|
+
oldStartLine,
|
|
285
|
+
newStartLine,
|
|
397
286
|
}),
|
|
398
287
|
].filter((block): block is ParsedBlock => Boolean(block)),
|
|
399
288
|
);
|
|
400
289
|
}
|
|
401
290
|
|
|
402
|
-
function buildApplyPatchCardData(
|
|
291
|
+
function buildApplyPatchCardData(
|
|
292
|
+
invocation: ToolInvocationSource,
|
|
293
|
+
): FileOperationCardData | null {
|
|
403
294
|
const parsedArgsRecord = readRecordPayload(invocation.parsedArgs);
|
|
404
|
-
const argsRecord =
|
|
295
|
+
const argsRecord =
|
|
296
|
+
readRecordPayload(invocation.args) ??
|
|
297
|
+
readPartialRecordPayload(invocation.args);
|
|
405
298
|
const patchText =
|
|
406
299
|
readNonEmptyString(invocation.args) ??
|
|
407
300
|
(parsedArgsRecord ? readNonEmptyString(parsedArgsRecord.patch) : null) ??
|