@xenosystem/agent-conversation 0.1.36 → 0.1.37

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.
Files changed (86) hide show
  1. package/dist/adapters/xenoAgentHostCatalog.d.ts.map +1 -1
  2. package/dist/adapters/xenoAgentHostCatalog.js +20 -5
  3. package/dist/adapters/xenoAgentHostCatalog.js.map +1 -1
  4. package/dist/components/agent/AgentView.d.ts +9 -0
  5. package/dist/components/agent/AgentView.d.ts.map +1 -1
  6. package/dist/components/agent/AgentView.js +883 -1045
  7. package/dist/components/agent/AgentView.js.map +1 -1
  8. package/dist/components/agent/GhostTextArea.d.ts +6 -0
  9. package/dist/components/agent/GhostTextArea.d.ts.map +1 -1
  10. package/dist/components/agent/GhostTextArea.js +15 -5
  11. package/dist/components/agent/GhostTextArea.js.map +1 -1
  12. package/dist/components/agent/LiveConversationCollaboration.d.ts +3 -1
  13. package/dist/components/agent/LiveConversationCollaboration.d.ts.map +1 -1
  14. package/dist/components/agent/LiveConversationCollaboration.js +3 -1
  15. package/dist/components/agent/LiveConversationCollaboration.js.map +1 -1
  16. package/dist/components/agent/MessageText.d.ts +36 -0
  17. package/dist/components/agent/MessageText.d.ts.map +1 -0
  18. package/dist/components/agent/MessageText.js +76 -0
  19. package/dist/components/agent/MessageText.js.map +1 -0
  20. package/dist/components/agent/SdkSessionPanel.d.ts +2 -0
  21. package/dist/components/agent/SdkSessionPanel.d.ts.map +1 -1
  22. package/dist/components/agent/SdkSessionPanel.js +1 -1
  23. package/dist/components/agent/SdkSessionPanel.js.map +1 -1
  24. package/dist/components/agent/composer/Composer.d.ts +282 -0
  25. package/dist/components/agent/composer/Composer.d.ts.map +1 -0
  26. package/dist/components/agent/composer/Composer.js +393 -0
  27. package/dist/components/agent/composer/Composer.js.map +1 -0
  28. package/dist/components/agent/composer/EffortCells.d.ts +12 -0
  29. package/dist/components/agent/composer/EffortCells.d.ts.map +1 -0
  30. package/dist/components/agent/composer/EffortCells.js +146 -0
  31. package/dist/components/agent/composer/EffortCells.js.map +1 -0
  32. package/dist/components/agent/composer/Tip.d.ts +19 -0
  33. package/dist/components/agent/composer/Tip.d.ts.map +1 -0
  34. package/dist/components/agent/composer/Tip.js +78 -0
  35. package/dist/components/agent/composer/Tip.js.map +1 -0
  36. package/dist/components/agent/composer/TypedMenu.d.ts +56 -0
  37. package/dist/components/agent/composer/TypedMenu.d.ts.map +1 -0
  38. package/dist/components/agent/composer/TypedMenu.js +156 -0
  39. package/dist/components/agent/composer/TypedMenu.js.map +1 -0
  40. package/dist/components/agent/mainAssistant.d.ts +9 -2
  41. package/dist/components/agent/mainAssistant.d.ts.map +1 -1
  42. package/dist/components/agent/mainAssistant.js +9 -2
  43. package/dist/components/agent/mainAssistant.js.map +1 -1
  44. package/dist/components/agent/toolCallDerive.d.ts +82 -0
  45. package/dist/components/agent/toolCallDerive.d.ts.map +1 -0
  46. package/dist/components/agent/toolCallDerive.js +741 -0
  47. package/dist/components/agent/toolCallDerive.js.map +1 -0
  48. package/dist/components/agent/transcript/Transcript.d.ts +127 -0
  49. package/dist/components/agent/transcript/Transcript.d.ts.map +1 -0
  50. package/dist/components/agent/transcript/Transcript.js +544 -0
  51. package/dist/components/agent/transcript/Transcript.js.map +1 -0
  52. package/dist/components/agent/transcript/glyphs.d.ts +83 -0
  53. package/dist/components/agent/transcript/glyphs.d.ts.map +1 -0
  54. package/dist/components/agent/transcript/glyphs.js +124 -0
  55. package/dist/components/agent/transcript/glyphs.js.map +1 -0
  56. package/dist/components/agent/transcript/stepModel.d.ts +163 -0
  57. package/dist/components/agent/transcript/stepModel.d.ts.map +1 -0
  58. package/dist/components/agent/transcript/stepModel.js +701 -0
  59. package/dist/components/agent/transcript/stepModel.js.map +1 -0
  60. package/dist/components/agent/transcript/turnModel.d.ts +124 -0
  61. package/dist/components/agent/transcript/turnModel.d.ts.map +1 -0
  62. package/dist/components/agent/transcript/turnModel.js +166 -0
  63. package/dist/components/agent/transcript/turnModel.js.map +1 -0
  64. package/dist/components/agent/transcript/useAskActions.d.ts +23 -0
  65. package/dist/components/agent/transcript/useAskActions.d.ts.map +1 -0
  66. package/dist/components/agent/transcript/useAskActions.js +92 -0
  67. package/dist/components/agent/transcript/useAskActions.js.map +1 -0
  68. package/dist/parityFixture.d.ts +54 -0
  69. package/dist/parityFixture.d.ts.map +1 -0
  70. package/dist/parityFixture.js +195 -0
  71. package/dist/parityFixture.js.map +1 -0
  72. package/dist/platformApiTypes.d.ts +10 -1
  73. package/dist/platformApiTypes.d.ts.map +1 -1
  74. package/dist/stores/agentChatStore.d.ts +61 -1
  75. package/dist/stores/agentChatStore.d.ts.map +1 -1
  76. package/dist/stores/agentChatStore.js +63 -8
  77. package/dist/stores/agentChatStore.js.map +1 -1
  78. package/dist/types/agentPermissions.d.ts +3 -1
  79. package/dist/types/agentPermissions.d.ts.map +1 -1
  80. package/dist/utils/acpExecutionAuthority.d.ts.map +1 -1
  81. package/dist/utils/acpExecutionAuthority.js +2 -0
  82. package/dist/utils/acpExecutionAuthority.js.map +1 -1
  83. package/package.json +6 -5
  84. package/src/composer.css +334 -0
  85. package/src/styles.css +7 -0
  86. package/src/transcript.css +585 -0
@@ -0,0 +1,741 @@
1
+ import { extractWindowsFilePathsFromToolResult } from '../../utils/markdownWindowsPaths.js';
2
+ import { compactCommandPreview, hideDuplicateToolTitle, isCommandToolCall } from './toolCardDisplay.js';
3
+ export function countNormalizedTextLines(content) {
4
+ const normalized = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
5
+ if (!normalized.length)
6
+ return 0;
7
+ return normalized.replace(/\n$/, '').split('\n').length;
8
+ }
9
+ export function normalizeEditorCandidatePath(raw) {
10
+ return raw
11
+ .trim()
12
+ .replace(/^["'`]+|["'`]+$/g, '')
13
+ .replace(/[)\].,;:]+$/g, '');
14
+ }
15
+ export function extractPathsFromResultText(text) {
16
+ return extractWindowsFilePathsFromToolResult(text)
17
+ .map(normalizeEditorCandidatePath)
18
+ .filter(Boolean);
19
+ }
20
+ export function deriveToolCallFilePath(toolCall) {
21
+ if (toolCall.diff?.path)
22
+ return normalizeEditorCandidatePath(toolCall.diff.path);
23
+ const artifactPath = toolCall.artifacts?.find((artifact) => artifact.type === 'file' && artifact.openInEditor)?.value;
24
+ if (artifactPath)
25
+ return normalizeEditorCandidatePath(artifactPath);
26
+ const params = toolCall.params || {};
27
+ const paramCandidates = [
28
+ params.path,
29
+ params.file_path,
30
+ params.targetPath,
31
+ params.resolvedTargetPath,
32
+ ];
33
+ for (const candidate of paramCandidates) {
34
+ if (typeof candidate === 'string') {
35
+ const normalized = normalizeEditorCandidatePath(candidate);
36
+ if (/^[A-Za-z]:\\/.test(normalized))
37
+ return normalized;
38
+ }
39
+ }
40
+ const resultCandidates = extractPathsFromResultText(toolCall.result || '');
41
+ return resultCandidates[0] || null;
42
+ }
43
+ export function humanizeToolName(toolName) {
44
+ return toolName
45
+ .replace(/_/g, ' ')
46
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
47
+ .replace(/\b\w/g, (char) => char.toUpperCase());
48
+ }
49
+ export function stripToolDisplayPrefix(value) {
50
+ return value
51
+ .replace(/^(Selected folder:|Selecting folder:?|Workspace placement:|Running:|Searching:|Reading:|Writing:|Editing:)\s*/i, '')
52
+ .trim();
53
+ }
54
+ export function isToolDisplayNoise(value) {
55
+ const trimmed = (value || '').trim();
56
+ if (!trimmed)
57
+ return true;
58
+ if (/^[{}\[\],:]+$/.test(trimmed))
59
+ return true;
60
+ if (trimmed === '{}' || trimmed === '[]' || trimmed === 'null')
61
+ return true;
62
+ try {
63
+ const parsed = JSON.parse(trimmed);
64
+ if (parsed === null)
65
+ return true;
66
+ if (Array.isArray(parsed))
67
+ return parsed.length === 0;
68
+ if (typeof parsed === 'object')
69
+ return Object.keys(parsed).length === 0;
70
+ }
71
+ catch {
72
+ // Not JSON, so only the punctuation-only check above applies.
73
+ }
74
+ return false;
75
+ }
76
+ export function deriveToolCardDisplay(toolCall) {
77
+ const params = toolCall.params || {};
78
+ const normalizedToolName = toolCall.toolName.toLowerCase();
79
+ const fallbackLabel = isToolDisplayNoise(toolCall.summary)
80
+ ? stripToolDisplayPrefix(toolCallLabel(toolCall.toolName, params))
81
+ : stripToolDisplayPrefix(toolCall.summary || '');
82
+ const pr = toolCall.permissionRequest;
83
+ const fileName = deriveToolCallFileName(toolCall);
84
+ const fileActionLabel = deriveFileToolActionLabel(toolCall);
85
+ const filePrimaryText = deriveFileToolPrimaryText(toolCall);
86
+ const query = typeof params.query === 'string' ? params.query.trim() : '';
87
+ const app = typeof params.app === 'string'
88
+ ? params.app.trim()
89
+ : (typeof params.appPath === 'string' ? params.appPath.trim() : '');
90
+ const directoryArtifact = toolCall.artifacts?.find((artifact) => artifact.type === 'directory')?.value;
91
+ const directory = typeof params.directory === 'string' && params.directory.trim()
92
+ ? params.directory.trim()
93
+ : directoryArtifact;
94
+ const choice = typeof params.choice === 'string' ? mapWorkspaceChoice(params.choice.trim()) : '';
95
+ const command = typeof params.command === 'string' ? params.command.trim() : '';
96
+ if (pr?.status === 'pending') {
97
+ return hideDuplicateToolTitle({
98
+ eyebrow: 'Approval required',
99
+ title: normalizedToolName === 'choosedirectory'
100
+ ? 'Open folder picker'
101
+ : (pr.directory || pr.path || fallbackLabel || humanizeToolName(toolCall.toolName)),
102
+ });
103
+ }
104
+ if (fileActionLabel) {
105
+ return hideDuplicateToolTitle({
106
+ eyebrow: filePrimaryText || `${fileActionLabel} file`,
107
+ title: `${fileActionLabel} ${fileName}`,
108
+ });
109
+ }
110
+ if (isCommandToolCall(toolCall)) {
111
+ return hideDuplicateToolTitle({
112
+ eyebrow: toolCall.status === 'running' ? 'Running command' : 'Command',
113
+ title: command ? compactCommandPreview(command) : 'Terminal command',
114
+ });
115
+ }
116
+ switch (normalizedToolName) {
117
+ case 'choosedirectory':
118
+ return hideDuplicateToolTitle({
119
+ eyebrow: toolCall.status === 'running' ? 'Selecting folder' : 'Selected folder',
120
+ title: toolCall.status === 'running'
121
+ ? (directory || 'Selecting folder')
122
+ : (directory || fallbackLabel || 'Choose a folder'),
123
+ });
124
+ case 'workspaceselection':
125
+ return hideDuplicateToolTitle({
126
+ eyebrow: 'Workspace action',
127
+ title: choice || fallbackLabel || 'Update workspace placement',
128
+ });
129
+ case 'chooseworkspaceplacement':
130
+ return {
131
+ eyebrow: toolCall.status === 'running' ? 'Choose workspace placement' : 'Workspace placement',
132
+ title: '',
133
+ };
134
+ case 'xenoworkspacemanage':
135
+ return hideDuplicateToolTitle({
136
+ eyebrow: 'Workspace registry',
137
+ title: fallbackLabel || 'Manage workspace hierarchy',
138
+ });
139
+ case 'glob':
140
+ return hideDuplicateToolTitle({
141
+ eyebrow: toolCall.status === 'running' ? 'Finding files' : 'Glob',
142
+ title: '',
143
+ });
144
+ case 'terminallist':
145
+ return hideDuplicateToolTitle({
146
+ eyebrow: toolCall.status === 'running' ? 'Terminal command' : 'Terminal command',
147
+ title: '',
148
+ });
149
+ case 'terminalreadoutput':
150
+ return hideDuplicateToolTitle({
151
+ eyebrow: toolCall.status === 'running' ? 'Terminal command' : 'Terminal command',
152
+ title: '',
153
+ });
154
+ case 'terminalwriteinput':
155
+ return hideDuplicateToolTitle({
156
+ eyebrow: 'Terminal command',
157
+ title: '',
158
+ });
159
+ case 'askuser':
160
+ return hideDuplicateToolTitle({
161
+ eyebrow: 'User input',
162
+ title: '',
163
+ });
164
+ case 'websearch':
165
+ case 'web_search':
166
+ return hideDuplicateToolTitle({
167
+ eyebrow: toolCall.status === 'running' ? 'Searching web' : 'Web search',
168
+ title: query || fallbackLabel || 'Search query',
169
+ });
170
+ case 'openapp':
171
+ case 'open_app':
172
+ return hideDuplicateToolTitle({
173
+ eyebrow: toolCall.status === 'running' ? 'Opening app' : 'Opened app',
174
+ title: app || fallbackLabel || 'App launch',
175
+ });
176
+ default:
177
+ return hideDuplicateToolTitle({
178
+ eyebrow: humanizeToolName(toolCall.toolName),
179
+ title: fallbackLabel || humanizeToolName(toolCall.toolName),
180
+ });
181
+ }
182
+ }
183
+ export function summarizeCommand(cmd) {
184
+ const lower = cmd.toLowerCase();
185
+ if (lower.startsWith('web_search:')) {
186
+ const query = cmd.slice(11).trim();
187
+ return { icon: 'search', label: `Searched web: ${query}`, detail: `Web search for "${query}"` };
188
+ }
189
+ if (lower.includes('select-string') || lower.includes('grep') || lower.includes('findstr')) {
190
+ const patternMatch = cmd.match(/[-"]Pattern["']?\s+["']?([^"'\s]+)/i) || cmd.match(/Select-String.*["']([^"']+)["']/i);
191
+ const pathMatch = cmd.match(/-Path\s+["']?([^"'\s|]+)/i);
192
+ const caseSensitive = lower.includes('casesensitive:$false') ? 'Case insensitive' : '';
193
+ const detail = `Searched with query ${patternMatch?.[1] || 'pattern'}${pathMatch ? ` in ${pathMatch[1]}` : ''}${caseSensitive ? `\n${caseSensitive}` : ''}`;
194
+ return { icon: 'search', label: `Searched ${patternMatch?.[1] || 'pattern'}`, detail };
195
+ }
196
+ if (lower.includes('get-childitem') || lower.includes('ls') || lower.includes('dir')) {
197
+ const pathMatch = cmd.match(/-Path\s+["']?([^"'\s|]+)/i) || cmd.match(/(?:ls|dir)\s+["']?([^"'\s|]+)/i);
198
+ const path = pathMatch?.[1] || '';
199
+ const folder = path.split(/[/\\]/).filter(Boolean).pop() || path;
200
+ const flags = [lower.includes('-recurse') && 'Recursive', lower.includes('-force') && 'Including hidden'].filter(Boolean).join(', ');
201
+ const detail = `Listed directory ${path}${flags ? `\n${flags}` : ''}`;
202
+ return { icon: 'file', label: `Listed ${folder || 'directory'}`, detail };
203
+ }
204
+ if (lower.includes('get-content') || lower.includes('cat') || lower.includes('type')) {
205
+ const pathMatch = cmd.match(/-Path\s+["']?([^"'\s|]+)/i) || cmd.match(/(?:cat|type)\s+["']?([^"'\s|]+)/i);
206
+ const path = pathMatch?.[1] || '';
207
+ const file = path.split(/[/\\]/).pop() || 'file';
208
+ return { icon: 'file', label: `Read ${file}`, detail: `Read file ${path}` };
209
+ }
210
+ const short = cmd.length > 60 ? cmd.slice(0, 57) + '...' : cmd;
211
+ return { icon: 'terminal', label: short, detail: cmd };
212
+ }
213
+ export function parseStructuredCommandContent(content) {
214
+ const raw = content.replace(/^```\n?/gm, '').replace(/```$/gm, '').trim();
215
+ if (!raw) {
216
+ return {
217
+ command: '',
218
+ cwd: '',
219
+ stdout: '',
220
+ stderr: '',
221
+ error: '',
222
+ exit: '',
223
+ outputLineCount: 0,
224
+ };
225
+ }
226
+ const lines = raw.split('\n');
227
+ let command = '';
228
+ let cwd = '';
229
+ let error = '';
230
+ let exit = '';
231
+ let inStderr = false;
232
+ const stdoutLines = [];
233
+ const stderrLines = [];
234
+ for (const line of lines) {
235
+ if (!command && line.startsWith('$ ')) {
236
+ command = line.slice(2).trim();
237
+ continue;
238
+ }
239
+ if (!cwd && line.startsWith('cwd: ')) {
240
+ cwd = line.slice(5).trim();
241
+ continue;
242
+ }
243
+ if (line === '[stderr]') {
244
+ inStderr = true;
245
+ continue;
246
+ }
247
+ if (!error && line.startsWith('[error]')) {
248
+ error = line.replace(/^\[error\]\s*/i, '').trim();
249
+ continue;
250
+ }
251
+ if (!exit && line.startsWith('exit: ')) {
252
+ exit = line.replace(/^exit:\s*/i, 'exit ').trim();
253
+ continue;
254
+ }
255
+ if (inStderr) {
256
+ stderrLines.push(line);
257
+ }
258
+ else {
259
+ stdoutLines.push(line);
260
+ }
261
+ }
262
+ const stdout = stdoutLines.join('\n').trim();
263
+ const stderr = stderrLines.join('\n').trim();
264
+ return {
265
+ command,
266
+ cwd,
267
+ stdout,
268
+ stderr,
269
+ error,
270
+ exit,
271
+ outputLineCount: stdout ? stdout.split(/\r?\n/).length : 0,
272
+ };
273
+ }
274
+ export function formatToolDuration(durationMs) {
275
+ if (typeof durationMs !== 'number' || Number.isNaN(durationMs))
276
+ return null;
277
+ if (durationMs < 1000)
278
+ return null;
279
+ return `${(durationMs / 1000).toFixed(durationMs >= 10_000 ? 0 : 1)}s`;
280
+ }
281
+ export function countTextLines(value) {
282
+ if (!value)
283
+ return 0;
284
+ return value.split(/\r?\n/).length;
285
+ }
286
+ export function deriveEditorDiffStats(diff) {
287
+ // Older persisted diff payloads counted empty original content as one removed
288
+ // line because ''.split('\n') returns ['']. Normalize display stats so a new
289
+ // file reads as +N -0 and the inline preview matches the counters.
290
+ if (diff.originalContent.length === 0) {
291
+ return {
292
+ added: countNormalizedTextLines(diff.modifiedContent),
293
+ removed: 0,
294
+ };
295
+ }
296
+ if (diff.modifiedContent.length === 0) {
297
+ return {
298
+ added: 0,
299
+ removed: countNormalizedTextLines(diff.originalContent),
300
+ };
301
+ }
302
+ return {
303
+ added: diff.addedLines,
304
+ removed: diff.removedLines,
305
+ };
306
+ }
307
+ export function parseReplacementCount(result) {
308
+ const match = result?.match(/Replaced\s+(\d+)\s+occurrence/i);
309
+ const count = match ? Number.parseInt(match[1], 10) : 1;
310
+ return Number.isFinite(count) && count > 0 ? count : 1;
311
+ }
312
+ export function deriveToolDiffStats(toolCall) {
313
+ if (toolCall.status !== 'done' || toolCall.success === false) {
314
+ return null;
315
+ }
316
+ if (toolCall.diff) {
317
+ return deriveEditorDiffStats(toolCall.diff);
318
+ }
319
+ const toolName = toolCall.toolName.toLowerCase();
320
+ const params = toolCall.params || {};
321
+ if (toolName === 'edit' || toolName === 'edit_file') {
322
+ const oldString = typeof params.old_string === 'string' ? params.old_string : '';
323
+ const newString = typeof params.new_string === 'string' ? params.new_string : '';
324
+ if (!oldString && !newString)
325
+ return null;
326
+ const replacements = parseReplacementCount(toolCall.result);
327
+ return {
328
+ added: countTextLines(newString) * replacements,
329
+ removed: countTextLines(oldString) * replacements
330
+ };
331
+ }
332
+ if (toolName === 'write' || toolName === 'write_file') {
333
+ const content = typeof params.content === 'string' ? params.content : '';
334
+ if (!content)
335
+ return null;
336
+ return {
337
+ added: countTextLines(content),
338
+ removed: 0
339
+ };
340
+ }
341
+ return null;
342
+ }
343
+ export function deriveToolCallFileName(toolCall) {
344
+ const filePath = deriveToolCallFilePath(toolCall);
345
+ if (filePath) {
346
+ const normalized = filePath.replace(/[\\/]+$/, '');
347
+ const parts = normalized.split(/[/\\]/).filter(Boolean);
348
+ if (parts.length > 0)
349
+ return parts[parts.length - 1];
350
+ }
351
+ if (toolCall.diff?.name)
352
+ return toolCall.diff.name;
353
+ return 'file';
354
+ }
355
+ export function deriveFileToolPrimaryText(toolCall) {
356
+ const toolName = toolCall.toolName.toLowerCase();
357
+ if (!['write', 'write_file', 'edit', 'edit_file', 'read', 'read_file'].includes(toolName)) {
358
+ return null;
359
+ }
360
+ if (toolCall.status === 'running') {
361
+ return null;
362
+ }
363
+ if (toolCall.success === false) {
364
+ return null;
365
+ }
366
+ if (toolName === 'read' || toolName === 'read_file') {
367
+ return 'Read file';
368
+ }
369
+ if (toolName === 'edit' || toolName === 'edit_file') {
370
+ return 'Edited file';
371
+ }
372
+ if (toolCall.diff) {
373
+ const original = toolCall.diff.originalContent || '';
374
+ const { removed } = deriveEditorDiffStats(toolCall.diff);
375
+ if (!original.trim() && removed === 0) {
376
+ return 'Created file';
377
+ }
378
+ return 'Edited file';
379
+ }
380
+ return 'Created file';
381
+ }
382
+ export function deriveFileToolActionLabel(toolCall) {
383
+ const toolName = toolCall.toolName.toLowerCase();
384
+ if (toolName === 'writefiles')
385
+ return 'Write';
386
+ if (toolName === 'write' || toolName === 'write_file')
387
+ return 'Write';
388
+ if (toolName === 'edit' || toolName === 'edit_file')
389
+ return 'Edit';
390
+ if (toolName === 'read' || toolName === 'read_file')
391
+ return 'Read';
392
+ return null;
393
+ }
394
+ export function splitPreviewLines(value) {
395
+ if (!value)
396
+ return [];
397
+ return value.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
398
+ }
399
+ export function buildInlineDiffPreviewLines(diff, maxChangedLines = 12) {
400
+ const originalLines = splitPreviewLines(diff.originalContent);
401
+ const modifiedLines = splitPreviewLines(diff.modifiedContent);
402
+ if (originalLines.length === 0 && modifiedLines.length === 0) {
403
+ return [];
404
+ }
405
+ let prefix = 0;
406
+ while (prefix < originalLines.length &&
407
+ prefix < modifiedLines.length &&
408
+ originalLines[prefix] === modifiedLines[prefix]) {
409
+ prefix += 1;
410
+ }
411
+ let suffix = 0;
412
+ while (suffix < originalLines.length - prefix &&
413
+ suffix < modifiedLines.length - prefix &&
414
+ originalLines[originalLines.length - 1 - suffix] === modifiedLines[modifiedLines.length - 1 - suffix]) {
415
+ suffix += 1;
416
+ }
417
+ const originalChanged = originalLines.slice(prefix, originalLines.length - suffix);
418
+ const modifiedChanged = modifiedLines.slice(prefix, modifiedLines.length - suffix);
419
+ const contextBeforeStart = Math.max(0, prefix - 2);
420
+ const contextBefore = originalLines.slice(contextBeforeStart, prefix);
421
+ const contextAfterStart = modifiedLines.length - suffix;
422
+ const contextAfter = modifiedLines.slice(contextAfterStart, Math.min(modifiedLines.length, contextAfterStart + 2));
423
+ const lines = [];
424
+ for (let index = 0; index < contextBefore.length; index += 1) {
425
+ const oldLineNumber = contextBeforeStart + index + 1;
426
+ lines.push({
427
+ kind: 'context',
428
+ oldLineNumber,
429
+ newLineNumber: oldLineNumber,
430
+ text: contextBefore[index],
431
+ });
432
+ }
433
+ const changedLineCount = originalChanged.length + modifiedChanged.length;
434
+ const truncateChanged = changedLineCount > maxChangedLines;
435
+ const allowedChangedLines = truncateChanged
436
+ ? Math.max(2, Math.floor(maxChangedLines / 2))
437
+ : Number.MAX_SAFE_INTEGER;
438
+ const removedToShow = truncateChanged ? originalChanged.slice(0, allowedChangedLines) : originalChanged;
439
+ const addedToShow = truncateChanged ? modifiedChanged.slice(0, allowedChangedLines) : modifiedChanged;
440
+ for (let index = 0; index < removedToShow.length; index += 1) {
441
+ lines.push({
442
+ kind: 'removed',
443
+ oldLineNumber: prefix + index + 1,
444
+ text: removedToShow[index],
445
+ });
446
+ }
447
+ for (let index = 0; index < addedToShow.length; index += 1) {
448
+ lines.push({
449
+ kind: 'added',
450
+ newLineNumber: prefix + index + 1,
451
+ text: addedToShow[index],
452
+ });
453
+ }
454
+ if (truncateChanged) {
455
+ lines.push({
456
+ kind: 'ellipsis',
457
+ text: `... ${changedLineCount - (removedToShow.length + addedToShow.length)} more changed lines`
458
+ });
459
+ }
460
+ for (let index = 0; index < contextAfter.length; index += 1) {
461
+ const newLineNumber = contextAfterStart + index + 1;
462
+ lines.push({
463
+ kind: 'context',
464
+ oldLineNumber: newLineNumber,
465
+ newLineNumber,
466
+ text: contextAfter[index],
467
+ });
468
+ }
469
+ if (lines.length === 0) {
470
+ const fallbackLines = modifiedLines.length > 0 ? modifiedLines : originalLines;
471
+ return fallbackLines.slice(0, 6).map((line, index) => ({
472
+ kind: 'context',
473
+ oldLineNumber: index + 1,
474
+ newLineNumber: index + 1,
475
+ text: line,
476
+ }));
477
+ }
478
+ return lines;
479
+ }
480
+ export function mapWorkspaceChoice(value) {
481
+ switch (value) {
482
+ case 'inbox':
483
+ return 'Keep in Inbox';
484
+ case 'reuse':
485
+ return 'Move to existing workspace';
486
+ case 'create':
487
+ return 'Create new workspace';
488
+ default:
489
+ return value;
490
+ }
491
+ }
492
+ export function deriveTerminalToolOutput(toolCall) {
493
+ const toolName = toolCall.toolName.toLowerCase();
494
+ if (toolName !== 'terminalwriteinput' && toolName !== 'terminalreadoutput')
495
+ return null;
496
+ const raw = (toolCall.result || '').trim();
497
+ if (!raw)
498
+ return null;
499
+ try {
500
+ const parsed = JSON.parse(raw);
501
+ const output = typeof parsed.output === 'string' ? parsed.output : '';
502
+ if (!output.trim())
503
+ return null;
504
+ return {
505
+ output,
506
+ terminalId: typeof parsed.terminalId === 'string' ? parsed.terminalId : undefined,
507
+ cwd: typeof parsed.cwd === 'string' ? parsed.cwd : undefined,
508
+ };
509
+ }
510
+ catch {
511
+ if (/^Wrote \d+ characters to terminal/i.test(raw))
512
+ return null;
513
+ return { output: raw };
514
+ }
515
+ }
516
+ export function readToolStringParam(params, keys) {
517
+ if (!params)
518
+ return '';
519
+ for (const key of keys) {
520
+ const value = params[key];
521
+ if (typeof value === 'string' && value.trim())
522
+ return value.trim();
523
+ }
524
+ return '';
525
+ }
526
+ export function isPseudoPermissionDirectory(directory) {
527
+ return /^__.*__$/.test((directory || '').trim());
528
+ }
529
+ export function tokenizeShellCommand(command) {
530
+ return (command.match(/"[^"]*"|'[^']*'|[^\s]+/g) || [])
531
+ .map((token) => token.replace(/^["']|["']$/g, '').trim())
532
+ .filter(Boolean);
533
+ }
534
+ export function deriveShellCommandNames(command) {
535
+ const tokens = tokenizeShellCommand(command);
536
+ const commands = [];
537
+ let expectCommand = true;
538
+ for (const token of tokens) {
539
+ const normalized = token.toLowerCase();
540
+ if (['&&', '||', '|', ';'].includes(normalized)) {
541
+ expectCommand = true;
542
+ continue;
543
+ }
544
+ if (!expectCommand)
545
+ continue;
546
+ if (/^[a-zA-Z][\w.-]*$/.test(token)) {
547
+ commands.push(token);
548
+ expectCommand = false;
549
+ }
550
+ }
551
+ return Array.from(new Set(commands));
552
+ }
553
+ export function deriveApprovalCardInfo(toolCall, pr) {
554
+ const params = toolCall.params || {};
555
+ const normalizedToolName = toolCall.toolName.toLowerCase();
556
+ const command = readToolStringParam(params, ['command']);
557
+ const terminalInput = readToolStringParam(params, ['data']);
558
+ const browserUrl = readToolStringParam(params, ['url']);
559
+ const browserSelector = readToolStringParam(params, ['selector']);
560
+ const browserText = readToolStringParam(params, ['text']);
561
+ const browserKey = readToolStringParam(params, ['key']);
562
+ const permissionText = (pr.path || pr.directory || '').trim();
563
+ const body = (() => {
564
+ if (command)
565
+ return command;
566
+ if (terminalInput)
567
+ return terminalInput.replace(/\r/g, '\\r').replace(/\n/g, '\\n');
568
+ if (browserUrl)
569
+ return browserUrl;
570
+ if (browserSelector && browserText)
571
+ return `${browserSelector}\n${browserText}`;
572
+ if (browserSelector)
573
+ return browserSelector;
574
+ if (browserKey)
575
+ return browserKey;
576
+ return permissionText || humanizeToolName(toolCall.toolName);
577
+ })();
578
+ const description = (() => {
579
+ if (permissionText)
580
+ return permissionText;
581
+ if (normalizedToolName === 'bash')
582
+ return 'Run shell command';
583
+ if (normalizedToolName === 'terminalwriteinput')
584
+ return 'Write to PowerShell terminal';
585
+ if (normalizedToolName.startsWith('browser'))
586
+ return humanizeToolName(toolCall.toolName);
587
+ if (normalizedToolName === 'choosedirectory')
588
+ return 'Open folder picker';
589
+ return humanizeToolName(toolCall.toolName);
590
+ })();
591
+ const shellNames = deriveShellCommandNames(command || terminalInput);
592
+ const summary = shellNames.length > 0
593
+ ? shellNames.slice(0, 3).join(', ')
594
+ : humanizeToolName(toolCall.toolName);
595
+ const allowlistLabel = shellNames.length > 0
596
+ ? `Allowlist '${shellNames[0]}'${shellNames.length > 1 ? ` + ${shellNames.length - 1}` : ''}`
597
+ : `Allowlist ${humanizeToolName(toolCall.toolName)}`;
598
+ return {
599
+ description,
600
+ summary,
601
+ body,
602
+ prompt: normalizedToolName === 'bash' || normalizedToolName === 'terminalwriteinput' ? '$ ' : '',
603
+ allowlistLabel,
604
+ runLabel: normalizedToolName === 'choosedirectory' ? 'Open' : 'Run',
605
+ };
606
+ }
607
+ export function toolCallLabel(toolName, params) {
608
+ switch (toolName.toLowerCase()) {
609
+ case 'choosedirectory': {
610
+ const directory = String(params?.directory || params?.path || '');
611
+ return directory
612
+ ? `Selected folder: ${directory}`
613
+ : 'Selected folder for this conversation';
614
+ }
615
+ case 'workspaceselection': {
616
+ const choice = String(params?.choice || '');
617
+ return choice ? `Workspace placement: ${choice}` : 'Workspace placement selected';
618
+ }
619
+ case 'xenoworkspacemanage': {
620
+ const action = String(params?.action || 'manage');
621
+ return `Workspace registry: ${action}`;
622
+ }
623
+ case 'bash':
624
+ case 'run_command': {
625
+ const cmd = String(params?.command || '');
626
+ return cmd.length > 60 ? `Running: ${cmd.slice(0, 57)}...` : `Running: ${cmd}`;
627
+ }
628
+ case 'runcommand':
629
+ case 'shell':
630
+ case 'shell_command':
631
+ case 'shellcommand':
632
+ case 'terminal': {
633
+ const cmd = String(params?.command || '');
634
+ return cmd ? `Running: ${compactCommandPreview(cmd, 60)}` : 'Terminal command';
635
+ }
636
+ case 'websearch':
637
+ case 'web_search':
638
+ return `Searching: ${String(params?.query || '')}`;
639
+ case 'read':
640
+ case 'read_file':
641
+ return `Reading: ${String(params?.path || '').split(/[/\\]/).pop() || 'file'}`;
642
+ case 'write':
643
+ case 'write_file':
644
+ return `Writing: ${String(params?.file_path || params?.path || '').split(/[/\\]/).pop() || 'file'}`;
645
+ case 'edit':
646
+ case 'edit_file':
647
+ return `Editing: ${String(params?.file_path || params?.path || '').split(/[/\\]/).pop() || 'file'}`;
648
+ case 'glob':
649
+ return `Finding files in ${String(params?.path || '.')}`;
650
+ case 'grep':
651
+ return `Searching files for ${String(params?.pattern || params?.query || '')}`;
652
+ case 'openapp':
653
+ case 'open_app':
654
+ return `Opening: ${String(params?.app || params?.appPath || '')}`;
655
+ default:
656
+ return `${toolName}(${Object.keys(params || {}).join(', ')})`;
657
+ }
658
+ }
659
+ export function normalizeTerminalApprovalCommand(raw) {
660
+ return raw
661
+ .replace(/\r/g, '\n')
662
+ .split('\n')
663
+ .map((line) => line.trim())
664
+ .filter(Boolean)
665
+ .join(' && ');
666
+ }
667
+ export function safeUrlOrigin(raw) {
668
+ try {
669
+ const url = new URL(raw);
670
+ return url.protocol === 'http:' || url.protocol === 'https:' ? url.origin : '';
671
+ }
672
+ catch {
673
+ return '';
674
+ }
675
+ }
676
+ export function buildSurfaceApprovalRule(toolCall, pr) {
677
+ const params = toolCall.params || {};
678
+ const action = toolCall.toolName;
679
+ if (action === 'TerminalWriteInput') {
680
+ const command = normalizeTerminalApprovalCommand(readToolStringParam(params, ['data', 'command']));
681
+ if (!command)
682
+ return null;
683
+ const names = deriveShellCommandNames(command);
684
+ return {
685
+ surface: 'terminal',
686
+ action,
687
+ matcher: command,
688
+ command,
689
+ label: names.length > 0 ? names.slice(0, 3).join(', ') : command.slice(0, 80),
690
+ };
691
+ }
692
+ if (action === 'BrowserGoto') {
693
+ const url = readToolStringParam(params, ['url']);
694
+ const origin = safeUrlOrigin(url);
695
+ if (!origin)
696
+ return null;
697
+ return {
698
+ surface: 'browser',
699
+ action,
700
+ matcher: origin,
701
+ browserOrigin: origin,
702
+ urlPattern: origin,
703
+ label: origin,
704
+ };
705
+ }
706
+ if (action === 'BrowserClick' || action === 'BrowserType') {
707
+ const selector = readToolStringParam(params, ['selector']);
708
+ if (!selector)
709
+ return null;
710
+ const browserOrigin = pr.browserOrigin || '';
711
+ return {
712
+ surface: 'browser',
713
+ action,
714
+ matcher: browserOrigin ? `${browserOrigin} ${selector}` : selector,
715
+ browserOrigin: browserOrigin || undefined,
716
+ selector,
717
+ label: browserOrigin ? `${humanizeToolName(toolCall.toolName)} ${browserOrigin}` : `${humanizeToolName(toolCall.toolName)} ${selector}`,
718
+ };
719
+ }
720
+ if (action === 'BrowserKey') {
721
+ const key = readToolStringParam(params, ['key']);
722
+ if (!key)
723
+ return null;
724
+ const browserOrigin = pr.browserOrigin || '';
725
+ return {
726
+ surface: 'browser',
727
+ action,
728
+ matcher: browserOrigin ? `${browserOrigin} ${key}` : key,
729
+ browserOrigin: browserOrigin || undefined,
730
+ key,
731
+ label: browserOrigin ? `Browser key ${key} on ${browserOrigin}` : `Browser key ${key}`,
732
+ };
733
+ }
734
+ if (!pr.directory || isPseudoPermissionDirectory(pr.directory))
735
+ return null;
736
+ return null;
737
+ }
738
+ export function canBuildSurfaceApprovalRule(toolCall, pr) {
739
+ return !!buildSurfaceApprovalRule(toolCall, pr);
740
+ }
741
+ //# sourceMappingURL=toolCallDerive.js.map