@robota-sdk/agent-transport 3.0.0-beta.75 → 3.0.0-beta.76
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 +10 -10
- package/dist/node/headless/index.cjs +1 -1
- package/dist/node/{headless-CT2ibQnr.cjs → headless-OnpVk4-k.cjs} +7 -7
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.d.ts +1 -6
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +7 -75
- package/src/index.ts +1 -5
- package/src/transport-registry.ts +0 -9
- package/dist/node/http/index.cjs +0 -1
- package/dist/node/http/index.d.ts +0 -2
- package/dist/node/http/index.js +0 -1
- package/dist/node/http-2Jiuflc1.js +0 -2
- package/dist/node/http-2Jiuflc1.js.map +0 -1
- package/dist/node/http-CBAvefLw.cjs +0 -1
- package/dist/node/index-BNccqSpv.d.ts +0 -86
- package/dist/node/index-BNccqSpv.d.ts.map +0 -1
- package/dist/node/index-BUhHIf7X.d.ts +0 -86
- package/dist/node/index-BUhHIf7X.d.ts.map +0 -1
- package/dist/node/index-BnAGE-u9.d.ts +0 -33
- package/dist/node/index-BnAGE-u9.d.ts.map +0 -1
- package/dist/node/index-BrQ4gGw0.d.ts +0 -213
- package/dist/node/index-BrQ4gGw0.d.ts.map +0 -1
- package/dist/node/index-CoeBF21y.d.ts +0 -213
- package/dist/node/index-CoeBF21y.d.ts.map +0 -1
- package/dist/node/index-DHt-2VQ-.d.ts +0 -46
- package/dist/node/index-DHt-2VQ-.d.ts.map +0 -1
- package/dist/node/index-DMwKN5Le.d.ts +0 -33
- package/dist/node/index-DMwKN5Le.d.ts.map +0 -1
- package/dist/node/index-c0M42fsA.d.ts +0 -46
- package/dist/node/index-c0M42fsA.d.ts.map +0 -1
- package/dist/node/mcp/index.cjs +0 -1
- package/dist/node/mcp/index.d.ts +0 -2
- package/dist/node/mcp/index.js +0 -1
- package/dist/node/mcp-BOglBJNy.cjs +0 -1
- package/dist/node/mcp-D3BBVK7C.js +0 -2
- package/dist/node/mcp-D3BBVK7C.js.map +0 -1
- package/dist/node/rolldown-runtime-CMqjfN_6.cjs +0 -1
- package/dist/node/tui/index.cjs +0 -1
- package/dist/node/tui/index.d.ts +0 -2
- package/dist/node/tui/index.js +0 -1
- package/dist/node/tui-CcH5EsQh.js +0 -25
- package/dist/node/tui-CcH5EsQh.js.map +0 -1
- package/dist/node/tui-DznRbcku.cjs +0 -24
- package/dist/node/ws/index.cjs +0 -1
- package/dist/node/ws/index.d.ts +0 -2
- package/dist/node/ws/index.js +0 -1
- package/dist/node/ws-Dc2RUwVs.js +0 -2
- package/dist/node/ws-Dc2RUwVs.js.map +0 -1
- package/dist/node/ws-QNMQn5kg.cjs +0 -1
- package/src/http/__tests__/http-transport.test.ts +0 -55
- package/src/http/__tests__/routes.test.ts +0 -168
- package/src/http/http-transport.ts +0 -41
- package/src/http/index.ts +0 -4
- package/src/http/routes.ts +0 -152
- package/src/mcp/__tests__/mcp-server.test.ts +0 -66
- package/src/mcp/__tests__/mcp-transport.test.ts +0 -46
- package/src/mcp/index.ts +0 -4
- package/src/mcp/mcp-server.ts +0 -163
- package/src/mcp/mcp-transport.ts +0 -48
- package/src/tui/App.tsx +0 -491
- package/src/tui/BackgroundTaskPanel.tsx +0 -36
- package/src/tui/CjkTextInput.tsx +0 -199
- package/src/tui/ConfirmPrompt.tsx +0 -70
- package/src/tui/ContextWarningBanner.tsx +0 -34
- package/src/tui/ExecutionWorkspaceDetailPane.tsx +0 -64
- package/src/tui/ExecutionWorkspaceSwitcher.tsx +0 -187
- package/src/tui/InputArea.tsx +0 -310
- package/src/tui/InteractivePrompt.tsx +0 -59
- package/src/tui/ListPicker.tsx +0 -95
- package/src/tui/MenuSelect.tsx +0 -104
- package/src/tui/MessageList.tsx +0 -284
- package/src/tui/PermissionPrompt.tsx +0 -86
- package/src/tui/PluginTUI.tsx +0 -258
- package/src/tui/SessionPicker.tsx +0 -68
- package/src/tui/SessionStatusBar.tsx +0 -73
- package/src/tui/SlashAutocomplete.tsx +0 -110
- package/src/tui/StatusBar.tsx +0 -236
- package/src/tui/StreamingIndicator.tsx +0 -93
- package/src/tui/TextPrompt.tsx +0 -81
- package/src/tui/ToolCommandOutput.tsx +0 -39
- package/src/tui/ToolDiffBlock.tsx +0 -32
- package/src/tui/TransportTUI.tsx +0 -117
- package/src/tui/TuiInteractionChannel.ts +0 -495
- package/src/tui/UpdateNotice.tsx +0 -14
- package/src/tui/UsageSummaryEntry.tsx +0 -39
- package/src/tui/WaveText.tsx +0 -44
- package/src/tui/__tests__/InteractivePrompt.test.tsx +0 -82
- package/src/tui/__tests__/ListPicker.test.tsx +0 -159
- package/src/tui/__tests__/MenuSelect.test.tsx +0 -103
- package/src/tui/__tests__/PluginTUI.test.tsx +0 -167
- package/src/tui/__tests__/SlashAutocomplete.test.tsx +0 -140
- package/src/tui/__tests__/TextPrompt.test.tsx +0 -98
- package/src/tui/__tests__/TuiInteractionChannel.display-contract.test.ts +0 -239
- package/src/tui/__tests__/TuiInteractionChannel.lifecycle.test.ts +0 -297
- package/src/tui/__tests__/TuiInteractionChannel.requestAction.test.ts +0 -124
- package/src/tui/__tests__/UpdateNotice.test.tsx +0 -15
- package/src/tui/__tests__/abort-after-permission.test.tsx +0 -169
- package/src/tui/__tests__/abort-streaming-e2e.test.tsx +0 -183
- package/src/tui/__tests__/background-task-panel.test.tsx +0 -53
- package/src/tui/__tests__/background-task-row-format.test.ts +0 -59
- package/src/tui/__tests__/channel-factory-integration.test.ts +0 -138
- package/src/tui/__tests__/cjk-text-input-flow.test.ts +0 -109
- package/src/tui/__tests__/cjk-text-input.test.ts +0 -191
- package/src/tui/__tests__/command-effect-handler.test.ts +0 -127
- package/src/tui/__tests__/command-output-summary.test.ts +0 -95
- package/src/tui/__tests__/compact-event-bridge.test.ts +0 -20
- package/src/tui/__tests__/confirm-permission-flow.test.ts +0 -130
- package/src/tui/__tests__/confirm-prompt.test.tsx +0 -87
- package/src/tui/__tests__/execution-workspace-switcher.test.tsx +0 -110
- package/src/tui/__tests__/execution-workspace-view-model.test.ts +0 -93
- package/src/tui/__tests__/fixtures/provider-setup-prompt-driver.tsx +0 -125
- package/src/tui/__tests__/input-area-flow.test.ts +0 -164
- package/src/tui/__tests__/message-list-rendering.test.tsx +0 -353
- package/src/tui/__tests__/prompt-queue.test.tsx +0 -255
- package/src/tui/__tests__/provider-setup-pty-e2e.test.ts +0 -233
- package/src/tui/__tests__/pty/pty-driver.ts +0 -135
- package/src/tui/__tests__/pty/tui-pty.ptytest.ts +0 -61
- package/src/tui/__tests__/render-channel-options.test.ts +0 -32
- package/src/tui/__tests__/render-markdown.test.ts +0 -72
- package/src/tui/__tests__/selection-flow.test.ts +0 -61
- package/src/tui/__tests__/session-init-poller.test.ts +0 -102
- package/src/tui/__tests__/session-naming.test.ts +0 -64
- package/src/tui/__tests__/session-switch-channel.test.tsx +0 -307
- package/src/tui/__tests__/slash-routing-effects.test.ts +0 -228
- package/src/tui/__tests__/status-activity.test.ts +0 -71
- package/src/tui/__tests__/status-bar.test.tsx +0 -177
- package/src/tui/__tests__/streaming-indicator.test.tsx +0 -137
- package/src/tui/__tests__/text-prompt-flow.test.ts +0 -77
- package/src/tui/__tests__/tui-channel-init-failure.test.ts +0 -57
- package/src/tui/__tests__/tui-state-manager.test.ts +0 -401
- package/src/tui/background-task-row-format.ts +0 -53
- package/src/tui/command-interaction.ts +0 -9
- package/src/tui/command-output-summary.ts +0 -122
- package/src/tui/create-default-tui-cli-adapter.ts +0 -41
- package/src/tui/execution-workspace-view-model.ts +0 -123
- package/src/tui/flows/cjk-text-input-flow.ts +0 -285
- package/src/tui/flows/confirm-prompt-flow.ts +0 -45
- package/src/tui/flows/input-area-flow.ts +0 -189
- package/src/tui/flows/permission-prompt-flow.ts +0 -85
- package/src/tui/flows/selection-flow.ts +0 -126
- package/src/tui/flows/session-init-poller.ts +0 -77
- package/src/tui/flows/text-prompt-flow.ts +0 -98
- package/src/tui/hooks/command-effect-handler.ts +0 -97
- package/src/tui/hooks/command-effect-queue.ts +0 -39
- package/src/tui/hooks/side-effects-types.ts +0 -35
- package/src/tui/hooks/useAutocomplete.ts +0 -87
- package/src/tui/hooks/usePluginCallbacks.ts +0 -31
- package/src/tui/hooks/usePluginScreenData.ts +0 -85
- package/src/tui/hooks/useSideEffects.ts +0 -175
- package/src/tui/hooks/useSlashRouting.ts +0 -118
- package/src/tui/hooks/useStatusLineSettings.ts +0 -37
- package/src/tui/hooks/useTuiChannel.ts +0 -95
- package/src/tui/index.ts +0 -14
- package/src/tui/interactions/CommandConfirm.tsx +0 -36
- package/src/tui/interactions/CommandPicker.tsx +0 -77
- package/src/tui/interactions/__tests__/CommandConfirm.test.tsx +0 -124
- package/src/tui/interactions/__tests__/CommandPicker.test.tsx +0 -138
- package/src/tui/plugin-tui-handlers.ts +0 -163
- package/src/tui/render-markdown.ts +0 -130
- package/src/tui/render.tsx +0 -129
- package/src/tui/session-naming.ts +0 -33
- package/src/tui/status-activity.ts +0 -63
- package/src/tui/tui-cli-adapter-context.tsx +0 -13
- package/src/tui/tui-cli-adapter.ts +0 -25
- package/src/tui/tui-state-manager.ts +0 -226
- package/src/tui/tui-transport.ts +0 -35
- package/src/tui/types.ts +0 -15
- package/src/tui/utils/__tests__/edit-diff.test.ts +0 -426
- package/src/tui/utils/__tests__/paste-detection.test.ts +0 -116
- package/src/tui/utils/__tests__/paste-labels.test.ts +0 -46
- package/src/tui/utils/__tests__/tool-call-extractor.test.ts +0 -227
- package/src/tui/utils/__tests__/tool-diff-summary.test.ts +0 -104
- package/src/tui/utils/edit-diff.ts +0 -153
- package/src/tui/utils/paste-labels.ts +0 -9
- package/src/tui/utils/tool-call-extractor.ts +0 -92
- package/src/tui/utils/tool-diff-summary.ts +0 -75
- package/src/ws/__tests__/ws-handler.test.ts +0 -409
- package/src/ws/__tests__/ws-transport.test.ts +0 -53
- package/src/ws/index.ts +0 -13
- package/src/ws/ws-background-messages.ts +0 -170
- package/src/ws/ws-handler.ts +0 -280
- package/src/ws/ws-protocol.ts +0 -78
- package/src/ws/ws-transport-configurable.ts +0 -128
- package/src/ws/ws-transport.ts +0 -42
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { extractToolCalls, extractToolCallsWithDiff } from '../tool-call-extractor.js';
|
|
3
|
-
|
|
4
|
-
describe('extractToolCalls', () => {
|
|
5
|
-
it('returns empty array for empty history', () => {
|
|
6
|
-
expect(extractToolCalls([], 0)).toEqual([]);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('returns empty array when no assistant messages have toolCalls', () => {
|
|
10
|
-
const history = [{ role: 'user' }, { role: 'assistant' }];
|
|
11
|
-
expect(extractToolCalls(history, 0)).toEqual([]);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('extracts tool call with string first argument', () => {
|
|
15
|
-
const history = [
|
|
16
|
-
{
|
|
17
|
-
role: 'assistant',
|
|
18
|
-
toolCalls: [{ function: { name: 'Read', arguments: '{"filePath":"/src/index.ts"}' } }],
|
|
19
|
-
},
|
|
20
|
-
];
|
|
21
|
-
expect(extractToolCalls(history, 0)).toEqual(['Read(/src/index.ts)']);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('extracts multiple tool calls from one message', () => {
|
|
25
|
-
const history = [
|
|
26
|
-
{
|
|
27
|
-
role: 'assistant',
|
|
28
|
-
toolCalls: [
|
|
29
|
-
{ function: { name: 'Bash', arguments: '{"command":"ls -la"}' } },
|
|
30
|
-
{ function: { name: 'Glob', arguments: '{"pattern":"**/*.md"}' } },
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
];
|
|
34
|
-
expect(extractToolCalls(history, 0)).toEqual(['Bash(ls -la)', 'Glob(**/*.md)']);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('extracts tool calls from multiple assistant messages', () => {
|
|
38
|
-
const history = [
|
|
39
|
-
{
|
|
40
|
-
role: 'assistant',
|
|
41
|
-
toolCalls: [{ function: { name: 'Read', arguments: '{"filePath":"a.ts"}' } }],
|
|
42
|
-
},
|
|
43
|
-
{ role: 'user' },
|
|
44
|
-
{
|
|
45
|
-
role: 'assistant',
|
|
46
|
-
toolCalls: [{ function: { name: 'Write', arguments: '{"filePath":"b.ts"}' } }],
|
|
47
|
-
},
|
|
48
|
-
];
|
|
49
|
-
expect(extractToolCalls(history, 0)).toEqual(['Read(a.ts)', 'Write(b.ts)']);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('skips messages before startIndex', () => {
|
|
53
|
-
const history = [
|
|
54
|
-
{
|
|
55
|
-
role: 'assistant',
|
|
56
|
-
toolCalls: [{ function: { name: 'Old', arguments: '{"x":"skip"}' } }],
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
role: 'assistant',
|
|
60
|
-
toolCalls: [{ function: { name: 'New', arguments: '{"x":"keep"}' } }],
|
|
61
|
-
},
|
|
62
|
-
];
|
|
63
|
-
expect(extractToolCalls(history, 1)).toEqual(['New(keep)']);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('truncates long argument values with middle ellipsis', () => {
|
|
67
|
-
const longPath =
|
|
68
|
-
'/Users/jungyoun/Documents/dev/robota/packages/agent-sdk/src/plugins/very-long-directory-name/file.ts';
|
|
69
|
-
const history = [
|
|
70
|
-
{
|
|
71
|
-
role: 'assistant',
|
|
72
|
-
toolCalls: [
|
|
73
|
-
{ function: { name: 'Read', arguments: JSON.stringify({ filePath: longPath }) } },
|
|
74
|
-
],
|
|
75
|
-
},
|
|
76
|
-
];
|
|
77
|
-
const result = extractToolCalls(history, 0);
|
|
78
|
-
expect(result).toHaveLength(1);
|
|
79
|
-
expect(result[0]).toContain('...');
|
|
80
|
-
// Should keep the tail (file name visible)
|
|
81
|
-
expect(result[0]).toContain('file.ts');
|
|
82
|
-
// Total display length should be reasonable
|
|
83
|
-
expect(result[0].length).toBeLessThanOrEqual(90);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('handles non-string first argument (JSON stringified)', () => {
|
|
87
|
-
const history = [
|
|
88
|
-
{
|
|
89
|
-
role: 'assistant',
|
|
90
|
-
toolCalls: [{ function: { name: 'Tool', arguments: '{"count":42}' } }],
|
|
91
|
-
},
|
|
92
|
-
];
|
|
93
|
-
expect(extractToolCalls(history, 0)).toEqual(['Tool(42)']);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('handles invalid JSON arguments gracefully', () => {
|
|
97
|
-
const history = [
|
|
98
|
-
{
|
|
99
|
-
role: 'assistant',
|
|
100
|
-
toolCalls: [{ function: { name: 'Tool', arguments: 'not json' } }],
|
|
101
|
-
},
|
|
102
|
-
];
|
|
103
|
-
expect(extractToolCalls(history, 0)).toEqual(['Tool(not json)']);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('skips non-assistant messages', () => {
|
|
107
|
-
const history = [
|
|
108
|
-
{ role: 'user' },
|
|
109
|
-
{ role: 'system' },
|
|
110
|
-
{
|
|
111
|
-
role: 'assistant',
|
|
112
|
-
toolCalls: [{ function: { name: 'Bash', arguments: '{"command":"echo hi"}' } }],
|
|
113
|
-
},
|
|
114
|
-
];
|
|
115
|
-
expect(extractToolCalls(history, 0)).toEqual(['Bash(echo hi)']);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('extractToolCallsWithDiff', () => {
|
|
120
|
-
it('non-Edit tool has no diffLines', () => {
|
|
121
|
-
const history = [
|
|
122
|
-
{
|
|
123
|
-
role: 'assistant',
|
|
124
|
-
toolCalls: [{ function: { name: 'Read', arguments: '{"filePath":"/src/a.ts"}' } }],
|
|
125
|
-
},
|
|
126
|
-
];
|
|
127
|
-
const summaries = extractToolCallsWithDiff(history, 0);
|
|
128
|
-
expect(summaries).toHaveLength(1);
|
|
129
|
-
expect(summaries[0].line).toBe('Read(/src/a.ts)');
|
|
130
|
-
expect(summaries[0].diffLines).toBeUndefined();
|
|
131
|
-
expect(summaries[0].diffFile).toBeUndefined();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('Edit tool includes diffLines and diffFile', () => {
|
|
135
|
-
const args = JSON.stringify({
|
|
136
|
-
file_path: '/src/index.ts',
|
|
137
|
-
old_string: 'const a = 1;',
|
|
138
|
-
new_string: 'const a = 2;',
|
|
139
|
-
});
|
|
140
|
-
const history = [
|
|
141
|
-
{
|
|
142
|
-
role: 'assistant',
|
|
143
|
-
toolCalls: [{ function: { name: 'Edit', arguments: args } }],
|
|
144
|
-
},
|
|
145
|
-
];
|
|
146
|
-
const summaries = extractToolCallsWithDiff(history, 0);
|
|
147
|
-
expect(summaries).toHaveLength(1);
|
|
148
|
-
expect(summaries[0].diffFile).toBe('/src/index.ts');
|
|
149
|
-
expect(summaries[0].diffLines).toEqual([
|
|
150
|
-
{ type: 'remove', text: 'const a = 1;', lineNumber: 1 },
|
|
151
|
-
{ type: 'add', text: 'const a = 2;', lineNumber: 1 },
|
|
152
|
-
]);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('Edit tool with identical old/new has no diffLines', () => {
|
|
156
|
-
const args = JSON.stringify({
|
|
157
|
-
file_path: '/src/index.ts',
|
|
158
|
-
old_string: 'same',
|
|
159
|
-
new_string: 'same',
|
|
160
|
-
});
|
|
161
|
-
const history = [
|
|
162
|
-
{
|
|
163
|
-
role: 'assistant',
|
|
164
|
-
toolCalls: [{ function: { name: 'Edit', arguments: args } }],
|
|
165
|
-
},
|
|
166
|
-
];
|
|
167
|
-
const summaries = extractToolCallsWithDiff(history, 0);
|
|
168
|
-
expect(summaries).toHaveLength(1);
|
|
169
|
-
expect(summaries[0].diffLines).toBeUndefined();
|
|
170
|
-
expect(summaries[0].diffFile).toBeUndefined();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('multiple tools including Edit: only Edit has diff', () => {
|
|
174
|
-
const editArgs = JSON.stringify({
|
|
175
|
-
file_path: '/src/main.ts',
|
|
176
|
-
old_string: 'foo',
|
|
177
|
-
new_string: 'bar',
|
|
178
|
-
});
|
|
179
|
-
const history = [
|
|
180
|
-
{
|
|
181
|
-
role: 'assistant',
|
|
182
|
-
toolCalls: [
|
|
183
|
-
{ function: { name: 'Read', arguments: '{"filePath":"/src/a.ts"}' } },
|
|
184
|
-
{ function: { name: 'Edit', arguments: editArgs } },
|
|
185
|
-
{ function: { name: 'Bash', arguments: '{"command":"ls"}' } },
|
|
186
|
-
],
|
|
187
|
-
},
|
|
188
|
-
];
|
|
189
|
-
const summaries = extractToolCallsWithDiff(history, 0);
|
|
190
|
-
expect(summaries).toHaveLength(3);
|
|
191
|
-
expect(summaries[0].diffLines).toBeUndefined();
|
|
192
|
-
expect(summaries[1].diffLines).toBeDefined();
|
|
193
|
-
expect(summaries[1].diffFile).toBe('/src/main.ts');
|
|
194
|
-
expect(summaries[2].diffLines).toBeUndefined();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('extractToolCalls (plain) returns string[] without diff info', () => {
|
|
198
|
-
const editArgs = JSON.stringify({
|
|
199
|
-
file_path: '/src/index.ts',
|
|
200
|
-
old_string: 'a',
|
|
201
|
-
new_string: 'b',
|
|
202
|
-
});
|
|
203
|
-
const history = [
|
|
204
|
-
{
|
|
205
|
-
role: 'assistant',
|
|
206
|
-
toolCalls: [{ function: { name: 'Edit', arguments: editArgs } }],
|
|
207
|
-
},
|
|
208
|
-
];
|
|
209
|
-
const result = extractToolCalls(history, 0);
|
|
210
|
-
expect(result).toHaveLength(1);
|
|
211
|
-
expect(typeof result[0]).toBe('string');
|
|
212
|
-
// No diff properties on plain strings
|
|
213
|
-
expect(result[0]).toContain('Edit(');
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('handles Edit tool with invalid JSON arguments gracefully', () => {
|
|
217
|
-
const history = [
|
|
218
|
-
{
|
|
219
|
-
role: 'assistant',
|
|
220
|
-
toolCalls: [{ function: { name: 'Edit', arguments: 'not json' } }],
|
|
221
|
-
},
|
|
222
|
-
];
|
|
223
|
-
const summaries = extractToolCallsWithDiff(history, 0);
|
|
224
|
-
expect(summaries).toHaveLength(1);
|
|
225
|
-
expect(summaries[0].diffLines).toBeUndefined();
|
|
226
|
-
});
|
|
227
|
-
});
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { buildToolDiffSummary } from '../tool-diff-summary.js';
|
|
3
|
-
import type { IDiffLine } from '../edit-diff.js';
|
|
4
|
-
|
|
5
|
-
describe('buildToolDiffSummary', () => {
|
|
6
|
-
it('builds a markdown diff fenced body while preserving file metadata', () => {
|
|
7
|
-
const lines: IDiffLine[] = [
|
|
8
|
-
{ type: 'hunk', lineNumber: 10, text: '@@ -10,2 +10,2 @@' },
|
|
9
|
-
{ type: 'context', lineNumber: 10, text: 'const before = true;' },
|
|
10
|
-
{ type: 'remove', lineNumber: 11, text: 'const value = false;' },
|
|
11
|
-
{ type: 'add', lineNumber: 11, text: 'const value = true;' },
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
const summary = buildToolDiffSummary({ file: '/src/index.ts', lines });
|
|
15
|
-
|
|
16
|
-
expect(summary.file).toBe('/src/index.ts');
|
|
17
|
-
expect(summary.truncated).toBe(false);
|
|
18
|
-
expect(summary.remainingLineCount).toBe(0);
|
|
19
|
-
expect(summary.markdown).toBe(
|
|
20
|
-
[
|
|
21
|
-
'```diff',
|
|
22
|
-
'@@ -10,2 +10,2 @@',
|
|
23
|
-
' 10 | const before = true;',
|
|
24
|
-
'- 11 | const value = false;',
|
|
25
|
-
'+ 11 | const value = true;',
|
|
26
|
-
'```',
|
|
27
|
-
].join('\n'),
|
|
28
|
-
);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('truncates long diff bodies while keeping truncation metadata outside markdown', () => {
|
|
32
|
-
const lines = Array.from({ length: 13 }, (_, index): IDiffLine => {
|
|
33
|
-
const lineNumber = index + 1;
|
|
34
|
-
return {
|
|
35
|
-
type: index % 2 === 0 ? 'remove' : 'add',
|
|
36
|
-
lineNumber,
|
|
37
|
-
text: `line ${lineNumber}`,
|
|
38
|
-
};
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const summary = buildToolDiffSummary({ lines });
|
|
42
|
-
|
|
43
|
-
expect(summary.truncated).toBe(true);
|
|
44
|
-
expect(summary.remainingLineCount).toBe(3);
|
|
45
|
-
expect(summary.markdown).toContain('- 1 | line 1');
|
|
46
|
-
expect(summary.markdown).toContain('+ 10 | line 10');
|
|
47
|
-
expect(summary.markdown).not.toContain('line 11');
|
|
48
|
-
expect(summary.markdown).not.toContain('more lines');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('preserves the first hunk when truncating multi-hunk diffs', () => {
|
|
52
|
-
const lines: IDiffLine[] = [
|
|
53
|
-
{ type: 'hunk', lineNumber: 1, text: '@@ -1,4 +1,4 @@' },
|
|
54
|
-
{ type: 'context', lineNumber: 1, text: 'one' },
|
|
55
|
-
{ type: 'remove', lineNumber: 2, text: 'two-old' },
|
|
56
|
-
{ type: 'add', lineNumber: 2, text: 'two-new' },
|
|
57
|
-
{ type: 'context', lineNumber: 3, text: 'three' },
|
|
58
|
-
{ type: 'hunk', lineNumber: 20, text: '@@ -20,4 +20,4 @@' },
|
|
59
|
-
{ type: 'context', lineNumber: 20, text: 'twenty' },
|
|
60
|
-
{ type: 'remove', lineNumber: 21, text: 'twenty-one-old' },
|
|
61
|
-
{ type: 'add', lineNumber: 21, text: 'twenty-one-new' },
|
|
62
|
-
{ type: 'context', lineNumber: 22, text: 'twenty-two' },
|
|
63
|
-
{ type: 'hunk', lineNumber: 40, text: '@@ -40,4 +40,4 @@' },
|
|
64
|
-
{ type: 'context', lineNumber: 40, text: 'forty' },
|
|
65
|
-
{ type: 'remove', lineNumber: 41, text: 'forty-one-old' },
|
|
66
|
-
{ type: 'add', lineNumber: 41, text: 'forty-one-new' },
|
|
67
|
-
{ type: 'context', lineNumber: 42, text: 'forty-two' },
|
|
68
|
-
];
|
|
69
|
-
|
|
70
|
-
const summary = buildToolDiffSummary({ lines });
|
|
71
|
-
|
|
72
|
-
expect(summary.truncated).toBe(true);
|
|
73
|
-
expect(summary.remainingLineCount).toBe(5);
|
|
74
|
-
expect(summary.markdown).toContain('@@ -1,4 +1,4 @@');
|
|
75
|
-
expect(summary.markdown).toContain('two-new');
|
|
76
|
-
expect(summary.markdown).toContain('@@ -20,4 +20,4 @@');
|
|
77
|
-
expect(summary.markdown).not.toContain('@@ -40,4 +40,4 @@');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
it('counts omitted lines from the actual rendered diff lines', () => {
|
|
81
|
-
const lines: IDiffLine[] = [
|
|
82
|
-
{ type: 'hunk', lineNumber: 1, text: '@@ -1,5 +1,5 @@' },
|
|
83
|
-
{ type: 'context', lineNumber: 1, text: 'one' },
|
|
84
|
-
{ type: 'remove', lineNumber: 2, text: 'two-old' },
|
|
85
|
-
{ type: 'add', lineNumber: 2, text: 'two-new' },
|
|
86
|
-
{ type: 'context', lineNumber: 3, text: 'three' },
|
|
87
|
-
{ type: 'context', lineNumber: 4, text: 'four' },
|
|
88
|
-
{ type: 'hunk', lineNumber: 20, text: '@@ -20,4 +20,4 @@' },
|
|
89
|
-
{ type: 'context', lineNumber: 20, text: 'twenty' },
|
|
90
|
-
{ type: 'remove', lineNumber: 21, text: 'twenty-one-old' },
|
|
91
|
-
{ type: 'add', lineNumber: 21, text: 'twenty-one-new' },
|
|
92
|
-
{ type: 'context', lineNumber: 22, text: 'twenty-two' },
|
|
93
|
-
{ type: 'hunk', lineNumber: 40, text: '@@ -40,1 +40,1 @@' },
|
|
94
|
-
{ type: 'context', lineNumber: 40, text: 'forty' },
|
|
95
|
-
];
|
|
96
|
-
|
|
97
|
-
const summary = buildToolDiffSummary({ lines });
|
|
98
|
-
|
|
99
|
-
expect(summary.truncated).toBe(true);
|
|
100
|
-
expect(summary.remainingLineCount).toBe(7);
|
|
101
|
-
expect(summary.markdown).toContain('@@ -1,5 +1,5 @@');
|
|
102
|
-
expect(summary.markdown).not.toContain('@@ -20,4 +20,4 @@');
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate diff lines from Edit tool's old_string and new_string.
|
|
3
|
-
* Includes absolute line numbers and optional context lines from the file.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { readFileSync } from 'node:fs';
|
|
7
|
-
|
|
8
|
-
import type { TUniversalValue } from '@robota-sdk/agent-core';
|
|
9
|
-
|
|
10
|
-
export interface IDiffLine {
|
|
11
|
-
type: 'add' | 'remove' | 'context' | 'hunk';
|
|
12
|
-
text: string;
|
|
13
|
-
/** Absolute line number in the file */
|
|
14
|
-
lineNumber: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Number of context lines to show before and after the change */
|
|
18
|
-
const CONTEXT_LINES = 3;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Generate diff lines from old and new strings with absolute line numbers.
|
|
22
|
-
* @param oldStr - The text being replaced
|
|
23
|
-
* @param newStr - The replacement text
|
|
24
|
-
* @param startLine - The 1-based line number where oldStr starts in the file
|
|
25
|
-
*/
|
|
26
|
-
export function generateDiffLines(
|
|
27
|
-
oldStr: string,
|
|
28
|
-
newStr: string,
|
|
29
|
-
startLine: number = 1,
|
|
30
|
-
): IDiffLine[] {
|
|
31
|
-
if (oldStr === newStr) return [];
|
|
32
|
-
|
|
33
|
-
const lines: IDiffLine[] = [];
|
|
34
|
-
|
|
35
|
-
const oldLines = oldStr.split('\n');
|
|
36
|
-
const newLines = newStr.split('\n');
|
|
37
|
-
|
|
38
|
-
for (let i = 0; i < oldLines.length; i++) {
|
|
39
|
-
lines.push({ type: 'remove', text: oldLines[i], lineNumber: startLine + i });
|
|
40
|
-
}
|
|
41
|
-
for (let i = 0; i < newLines.length; i++) {
|
|
42
|
-
lines.push({ type: 'add', text: newLines[i], lineNumber: startLine + i });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return lines;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Generate diff lines with context from the modified file.
|
|
50
|
-
* Reads the file (already modified) to get surrounding lines.
|
|
51
|
-
*/
|
|
52
|
-
export function generateDiffLinesWithContext(
|
|
53
|
-
oldStr: string,
|
|
54
|
-
newStr: string,
|
|
55
|
-
startLine: number,
|
|
56
|
-
filePath: string,
|
|
57
|
-
): IDiffLine[] {
|
|
58
|
-
if (oldStr === newStr) return [];
|
|
59
|
-
|
|
60
|
-
const diffLines = generateDiffLines(oldStr, newStr, startLine);
|
|
61
|
-
|
|
62
|
-
// Read modified file for context lines
|
|
63
|
-
let fileLines: string[];
|
|
64
|
-
try {
|
|
65
|
-
fileLines = readFileSync(filePath, 'utf-8').split('\n');
|
|
66
|
-
} catch {
|
|
67
|
-
return diffLines; // Can't read file — return without context
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Context BEFORE: lines before startLine in the file
|
|
71
|
-
const contextStart = Math.max(0, startLine - 1 - CONTEXT_LINES);
|
|
72
|
-
const beforeContext: IDiffLine[] = [];
|
|
73
|
-
for (let i = contextStart; i < startLine - 1; i++) {
|
|
74
|
-
if (i < fileLines.length) {
|
|
75
|
-
beforeContext.push({ type: 'context', text: fileLines[i], lineNumber: i + 1 });
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Context AFTER: lines after the new content in the modified file
|
|
80
|
-
const newLineCount = newStr.split('\n').length;
|
|
81
|
-
const afterStart = startLine - 1 + newLineCount;
|
|
82
|
-
const afterContext: IDiffLine[] = [];
|
|
83
|
-
for (let i = afterStart; i < afterStart + CONTEXT_LINES; i++) {
|
|
84
|
-
if (i < fileLines.length) {
|
|
85
|
-
afterContext.push({ type: 'context', text: fileLines[i], lineNumber: i + 1 });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const hunkStart =
|
|
90
|
-
beforeContext[0]?.lineNumber ??
|
|
91
|
-
diffLines[0]?.lineNumber ??
|
|
92
|
-
afterContext[0]?.lineNumber ??
|
|
93
|
-
startLine;
|
|
94
|
-
const oldLineCount = oldStr.split('\n').length;
|
|
95
|
-
const newLineCountInHunk = newStr.split('\n').length;
|
|
96
|
-
const oldHunkLineCount = beforeContext.length + oldLineCount + afterContext.length;
|
|
97
|
-
const newHunkLineCount = beforeContext.length + newLineCountInHunk + afterContext.length;
|
|
98
|
-
|
|
99
|
-
return [
|
|
100
|
-
{
|
|
101
|
-
type: 'hunk',
|
|
102
|
-
text: `@@ -${hunkStart},${oldHunkLineCount} +${hunkStart},${newHunkLineCount} @@`,
|
|
103
|
-
lineNumber: hunkStart,
|
|
104
|
-
},
|
|
105
|
-
...beforeContext,
|
|
106
|
-
...diffLines,
|
|
107
|
-
...afterContext,
|
|
108
|
-
];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Extract Edit tool diff info from tool arguments.
|
|
113
|
-
* @param toolName - Tool name (must be 'Edit')
|
|
114
|
-
* @param toolArgs - Tool arguments (filePath, oldString, newString)
|
|
115
|
-
* @param startLine - Start line number from Edit tool result (optional)
|
|
116
|
-
*/
|
|
117
|
-
export function extractEditDiff(
|
|
118
|
-
toolName: string,
|
|
119
|
-
toolArgs?: Record<string, TUniversalValue>,
|
|
120
|
-
startLine?: number,
|
|
121
|
-
): { file: string; lines: IDiffLine[] } | null {
|
|
122
|
-
if (toolName !== 'Edit' || !toolArgs) return null;
|
|
123
|
-
|
|
124
|
-
const filePath = toolArgs.file_path ?? toolArgs.filePath;
|
|
125
|
-
const oldStr = toolArgs.old_string ?? toolArgs.oldString;
|
|
126
|
-
const newStr = toolArgs.new_string ?? toolArgs.newString;
|
|
127
|
-
|
|
128
|
-
if (typeof filePath !== 'string') return null;
|
|
129
|
-
if (typeof oldStr !== 'string' || typeof newStr !== 'string') return null;
|
|
130
|
-
|
|
131
|
-
// Resolve start line: from tool result, or by finding newString in the modified file
|
|
132
|
-
let sl = startLine ?? 0;
|
|
133
|
-
if (!sl) {
|
|
134
|
-
try {
|
|
135
|
-
const fileContent = readFileSync(filePath, 'utf-8');
|
|
136
|
-
const idx = fileContent.indexOf(newStr);
|
|
137
|
-
if (idx >= 0) {
|
|
138
|
-
sl = fileContent.substring(0, idx).split('\n').length;
|
|
139
|
-
} else {
|
|
140
|
-
sl = 1;
|
|
141
|
-
}
|
|
142
|
-
} catch {
|
|
143
|
-
sl = 1;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Always try to include context from the file
|
|
148
|
-
const lines = generateDiffLinesWithContext(oldStr, newStr, sl, filePath);
|
|
149
|
-
|
|
150
|
-
if (lines.length === 0) return null;
|
|
151
|
-
|
|
152
|
-
return { file: filePath, lines };
|
|
153
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const PASTE_LABEL_RE = /\[Pasted text #(\d+)(?: \+\d+ lines)?\]/g;
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Replace paste label placeholders with their original content from the store.
|
|
5
|
-
* Labels that have no matching store entry are replaced with empty string.
|
|
6
|
-
*/
|
|
7
|
-
export function expandPasteLabels(text: string, store: Map<number, string>): string {
|
|
8
|
-
return text.replace(PASTE_LABEL_RE, (_, id: string) => store.get(Number(id)) ?? '');
|
|
9
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Extracts tool call summaries from session history messages.
|
|
3
|
-
* Pure function — no side effects, no framework dependencies.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { extractEditDiff } from './edit-diff.js';
|
|
7
|
-
|
|
8
|
-
import type { IDiffLine } from './edit-diff.js';
|
|
9
|
-
import type { TUniversalValue } from '@robota-sdk/agent-core';
|
|
10
|
-
|
|
11
|
-
const TOOL_ARG_MAX_LENGTH = 80;
|
|
12
|
-
const TAIL_KEEP = 30;
|
|
13
|
-
const ELLIPSIS_LEN = 3;
|
|
14
|
-
|
|
15
|
-
interface IHistoryMessage {
|
|
16
|
-
role: string;
|
|
17
|
-
toolCalls?: Array<{
|
|
18
|
-
function: { name: string; arguments: string };
|
|
19
|
-
}>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** A tool call summary with optional diff for Edit tools */
|
|
23
|
-
export interface IToolCallSummary {
|
|
24
|
-
line: string;
|
|
25
|
-
diffLines?: IDiffLine[];
|
|
26
|
-
diffFile?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Extract tool call display lines from history messages.
|
|
31
|
-
* Format: `ToolName(firstArgValue)` — first argument value truncated to 80 chars.
|
|
32
|
-
* Edit tools include diff information.
|
|
33
|
-
*/
|
|
34
|
-
export function extractToolCalls(history: IHistoryMessage[], startIndex: number): string[] {
|
|
35
|
-
return extractToolCallsWithDiff(history, startIndex).map((s) => s.line);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Extract tool call summaries with diff info from history messages.
|
|
40
|
-
*/
|
|
41
|
-
export function extractToolCallsWithDiff(
|
|
42
|
-
history: IHistoryMessage[],
|
|
43
|
-
startIndex: number,
|
|
44
|
-
): IToolCallSummary[] {
|
|
45
|
-
const summaries: IToolCallSummary[] = [];
|
|
46
|
-
for (let i = startIndex; i < history.length; i++) {
|
|
47
|
-
const msg = history[i];
|
|
48
|
-
if (msg.role === 'assistant' && msg.toolCalls) {
|
|
49
|
-
for (const tc of msg.toolCalls) {
|
|
50
|
-
const value = parseFirstArgValue(tc.function.arguments);
|
|
51
|
-
const truncated =
|
|
52
|
-
value.length > TOOL_ARG_MAX_LENGTH
|
|
53
|
-
? value.slice(0, TOOL_ARG_MAX_LENGTH - TAIL_KEEP - ELLIPSIS_LEN) +
|
|
54
|
-
'...' +
|
|
55
|
-
value.slice(-TAIL_KEEP)
|
|
56
|
-
: value;
|
|
57
|
-
|
|
58
|
-
const summary: IToolCallSummary = {
|
|
59
|
-
line: `${tc.function.name}(${truncated})`,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// Extract diff for Edit tool
|
|
63
|
-
if (tc.function.name === 'Edit') {
|
|
64
|
-
try {
|
|
65
|
-
const args = JSON.parse(tc.function.arguments) as Record<string, TUniversalValue>;
|
|
66
|
-
const diff = extractEditDiff('Edit', args);
|
|
67
|
-
if (diff) {
|
|
68
|
-
summary.diffLines = diff.lines;
|
|
69
|
-
summary.diffFile = diff.file;
|
|
70
|
-
}
|
|
71
|
-
} catch {
|
|
72
|
-
// ignore parse errors
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
summaries.push(summary);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return summaries;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Parse the first argument value from a JSON arguments string. */
|
|
84
|
-
function parseFirstArgValue(argsJson: string): string {
|
|
85
|
-
try {
|
|
86
|
-
const parsed = JSON.parse(argsJson) as Record<string, TUniversalValue>;
|
|
87
|
-
const firstVal = Object.values(parsed)[0];
|
|
88
|
-
return typeof firstVal === 'string' ? firstVal : JSON.stringify(firstVal);
|
|
89
|
-
} catch {
|
|
90
|
-
return argsJson;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { IDiffLine } from './edit-diff.js';
|
|
2
|
-
|
|
3
|
-
const MAX_DIFF_LINES = 12;
|
|
4
|
-
const TRUNCATED_SHOW = 10;
|
|
5
|
-
|
|
6
|
-
export interface IToolDiffSummaryInput {
|
|
7
|
-
file?: string;
|
|
8
|
-
lines: readonly IDiffLine[];
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface IToolDiffSummary {
|
|
12
|
-
file?: string;
|
|
13
|
-
markdown: string;
|
|
14
|
-
truncated: boolean;
|
|
15
|
-
remainingLineCount: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function buildToolDiffSummary(input: IToolDiffSummaryInput): IToolDiffSummary {
|
|
19
|
-
const visibleLines =
|
|
20
|
-
input.lines.length > MAX_DIFF_LINES ? selectVisibleDiffLines(input.lines) : input.lines;
|
|
21
|
-
const lineNumberWidth = Math.max(...visibleLines.map((line) => line.lineNumber), 0).toString()
|
|
22
|
-
.length;
|
|
23
|
-
const body = visibleLines.map((line) => formatDiffLine(line, lineNumberWidth));
|
|
24
|
-
const truncated = input.lines.length > MAX_DIFF_LINES;
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
file: input.file,
|
|
28
|
-
markdown: ['```diff', ...body, '```'].join('\n'),
|
|
29
|
-
truncated,
|
|
30
|
-
remainingLineCount: truncated ? input.lines.length - visibleLines.length : 0,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function formatDiffLine(line: IDiffLine, lineNumberWidth: number): string {
|
|
35
|
-
if (line.type === 'hunk') return line.text;
|
|
36
|
-
const lineNumber = line.lineNumber.toString().padStart(lineNumberWidth, ' ');
|
|
37
|
-
if (line.type === 'remove') return `- ${lineNumber} | ${line.text}`;
|
|
38
|
-
if (line.type === 'add') return `+ ${lineNumber} | ${line.text}`;
|
|
39
|
-
return ` ${lineNumber} | ${line.text}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function selectVisibleDiffLines(lines: readonly IDiffLine[]): readonly IDiffLine[] {
|
|
43
|
-
const groups = groupByHunk(lines);
|
|
44
|
-
const visible: IDiffLine[] = [];
|
|
45
|
-
|
|
46
|
-
for (const group of groups) {
|
|
47
|
-
if (visible.length === 0 && group.length > TRUNCATED_SHOW) {
|
|
48
|
-
return group.slice(0, TRUNCATED_SHOW);
|
|
49
|
-
}
|
|
50
|
-
if (visible.length + group.length > TRUNCATED_SHOW) break;
|
|
51
|
-
visible.push(...group);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return visible.length > 0 ? visible : lines.slice(0, TRUNCATED_SHOW);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function groupByHunk(lines: readonly IDiffLine[]): IDiffLine[][] {
|
|
58
|
-
const groups: IDiffLine[][] = [];
|
|
59
|
-
let current: IDiffLine[] = [];
|
|
60
|
-
|
|
61
|
-
for (const line of lines) {
|
|
62
|
-
if (line.type === 'hunk' && current.length > 0) {
|
|
63
|
-
groups.push(current);
|
|
64
|
-
current = [line];
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
current.push(line);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (current.length > 0) {
|
|
71
|
-
groups.push(current);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return groups;
|
|
75
|
-
}
|