openhorse 0.2.13 → 0.2.17

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 (103) hide show
  1. package/dist/commands/index.d.ts.map +1 -1
  2. package/dist/commands/index.js +217 -49
  3. package/dist/commands/index.js.map +1 -1
  4. package/dist/commands/types.d.ts +4 -1
  5. package/dist/commands/types.d.ts.map +1 -1
  6. package/dist/commands/types.js.map +1 -1
  7. package/dist/core/checkpoint.d.ts +7 -0
  8. package/dist/core/checkpoint.d.ts.map +1 -1
  9. package/dist/core/checkpoint.js +10 -0
  10. package/dist/core/checkpoint.js.map +1 -1
  11. package/dist/core/cost-tracker.d.ts.map +1 -1
  12. package/dist/core/cost-tracker.js +11 -0
  13. package/dist/core/cost-tracker.js.map +1 -1
  14. package/dist/ink-ui/components/PromptInput.d.ts.map +1 -1
  15. package/dist/ink-ui/components/PromptInput.js +5 -3
  16. package/dist/ink-ui/components/PromptInput.js.map +1 -1
  17. package/dist/ink-ui/components/SelectList.d.ts.map +1 -1
  18. package/dist/ink-ui/components/SelectList.js +10 -4
  19. package/dist/ink-ui/components/SelectList.js.map +1 -1
  20. package/dist/ink-ui/components/StatusLine.d.ts +3 -1
  21. package/dist/ink-ui/components/StatusLine.d.ts.map +1 -1
  22. package/dist/ink-ui/components/StatusLine.js +5 -3
  23. package/dist/ink-ui/components/StatusLine.js.map +1 -1
  24. package/dist/ink-ui/components/ToolActivity.d.ts +3 -1
  25. package/dist/ink-ui/components/ToolActivity.d.ts.map +1 -1
  26. package/dist/ink-ui/components/ToolActivity.js +35 -9
  27. package/dist/ink-ui/components/ToolActivity.js.map +1 -1
  28. package/dist/ink-ui/launch.d.ts.map +1 -1
  29. package/dist/ink-ui/launch.js +39 -10
  30. package/dist/ink-ui/launch.js.map +1 -1
  31. package/dist/ink-ui/screens/ReplScreen.d.ts +3 -1
  32. package/dist/ink-ui/screens/ReplScreen.d.ts.map +1 -1
  33. package/dist/ink-ui/screens/ReplScreen.js +63 -93
  34. package/dist/ink-ui/screens/ReplScreen.js.map +1 -1
  35. package/dist/print-ui/launch.d.ts.map +1 -1
  36. package/dist/print-ui/launch.js +1 -0
  37. package/dist/print-ui/launch.js.map +1 -1
  38. package/dist/runtime/agent-runtime-controller.d.ts +5 -0
  39. package/dist/runtime/agent-runtime-controller.d.ts.map +1 -1
  40. package/dist/runtime/agent-runtime-controller.js +15 -3
  41. package/dist/runtime/agent-runtime-controller.js.map +1 -1
  42. package/dist/runtime/agent-status.d.ts +5 -0
  43. package/dist/runtime/agent-status.d.ts.map +1 -1
  44. package/dist/runtime/agent-status.js +28 -2
  45. package/dist/runtime/agent-status.js.map +1 -1
  46. package/dist/runtime/chat-controller.d.ts +4 -0
  47. package/dist/runtime/chat-controller.d.ts.map +1 -1
  48. package/dist/runtime/chat-controller.js +199 -67
  49. package/dist/runtime/chat-controller.js.map +1 -1
  50. package/dist/runtime/turn-controller.d.ts +4 -0
  51. package/dist/runtime/turn-controller.d.ts.map +1 -1
  52. package/dist/runtime/turn-controller.js +8 -0
  53. package/dist/runtime/turn-controller.js.map +1 -1
  54. package/dist/runtime/ui-events.d.ts +7 -0
  55. package/dist/runtime/ui-events.d.ts.map +1 -1
  56. package/dist/runtime/ui-events.js.map +1 -1
  57. package/dist/runtime/ui-view-model.d.ts +287 -0
  58. package/dist/runtime/ui-view-model.d.ts.map +1 -0
  59. package/dist/runtime/ui-view-model.js +642 -0
  60. package/dist/runtime/ui-view-model.js.map +1 -0
  61. package/dist/services/file-glob.d.ts +6 -2
  62. package/dist/services/file-glob.d.ts.map +1 -1
  63. package/dist/services/file-glob.js +10 -4
  64. package/dist/services/file-glob.js.map +1 -1
  65. package/dist/services/model-catalog.d.ts +14 -0
  66. package/dist/services/model-catalog.d.ts.map +1 -0
  67. package/dist/services/model-catalog.js +73 -0
  68. package/dist/services/model-catalog.js.map +1 -0
  69. package/dist/services/model-context.d.ts.map +1 -1
  70. package/dist/services/model-context.js +3 -0
  71. package/dist/services/model-context.js.map +1 -1
  72. package/dist/services/session-storage.d.ts +1 -0
  73. package/dist/services/session-storage.d.ts.map +1 -1
  74. package/dist/services/session-storage.js.map +1 -1
  75. package/dist/services/verification-profile.d.ts +15 -0
  76. package/dist/services/verification-profile.d.ts.map +1 -1
  77. package/dist/services/verification-profile.js +67 -0
  78. package/dist/services/verification-profile.js.map +1 -1
  79. package/dist/terminal-ui/completion.d.ts.map +1 -1
  80. package/dist/terminal-ui/completion.js +9 -6
  81. package/dist/terminal-ui/completion.js.map +1 -1
  82. package/dist/terminal-ui/launch.d.ts +15 -1
  83. package/dist/terminal-ui/launch.d.ts.map +1 -1
  84. package/dist/terminal-ui/launch.js +300 -56
  85. package/dist/terminal-ui/launch.js.map +1 -1
  86. package/dist/terminal-ui/raw-editor.d.ts +4 -0
  87. package/dist/terminal-ui/raw-editor.d.ts.map +1 -1
  88. package/dist/terminal-ui/raw-editor.js +29 -0
  89. package/dist/terminal-ui/raw-editor.js.map +1 -1
  90. package/dist/tui-ui/launch.d.ts.map +1 -1
  91. package/dist/tui-ui/launch.js +1 -0
  92. package/dist/tui-ui/launch.js.map +1 -1
  93. package/dist/tui-ui/layout.d.ts.map +1 -1
  94. package/dist/tui-ui/layout.js +21 -28
  95. package/dist/tui-ui/layout.js.map +1 -1
  96. package/dist/tui-ui/pickers.d.ts.map +1 -1
  97. package/dist/tui-ui/pickers.js +21 -62
  98. package/dist/tui-ui/pickers.js.map +1 -1
  99. package/dist/tui-ui/state.d.ts +2 -4
  100. package/dist/tui-ui/state.d.ts.map +1 -1
  101. package/dist/tui-ui/state.js +13 -11
  102. package/dist/tui-ui/state.js.map +1 -1
  103. package/package.json +1 -1
@@ -0,0 +1,642 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rendererStatus = rendererStatus;
4
+ exports.rendererCapabilityLabels = rendererCapabilityLabels;
5
+ exports.createRuntimeCapabilitySummary = createRuntimeCapabilitySummary;
6
+ exports.createSessionRestoredView = createSessionRestoredView;
7
+ exports.sessionPickerTitle = sessionPickerTitle;
8
+ exports.clampPromptCursor = clampPromptCursor;
9
+ exports.createPromptState = createPromptState;
10
+ exports.createCommandPickerState = createCommandPickerState;
11
+ exports.getFileMentionQuery = getFileMentionQuery;
12
+ exports.createFilePickerState = createFilePickerState;
13
+ exports.createPermissionDecisionPickerState = createPermissionDecisionPickerState;
14
+ exports.createEditPreviewPickerState = createEditPreviewPickerState;
15
+ exports.createModelPickerState = createModelPickerState;
16
+ exports.permissionScopeFromArgs = permissionScopeFromArgs;
17
+ exports.permissionScopeDisplayValue = permissionScopeDisplayValue;
18
+ exports.permissionRiskDisplayValue = permissionRiskDisplayValue;
19
+ exports.createPermissionPromptState = createPermissionPromptState;
20
+ exports.createSessionPickerState = createSessionPickerState;
21
+ exports.movePickerPageOffset = movePickerPageOffset;
22
+ exports.createStatusSnapshot = createStatusSnapshot;
23
+ exports.transcriptEntryToBlock = transcriptEntryToBlock;
24
+ exports.toolActivityBatchLabel = toolActivityBatchLabel;
25
+ exports.toolActivityFromStarted = toolActivityFromStarted;
26
+ exports.toolActivityFromFinished = toolActivityFromFinished;
27
+ exports.formatToolActivityTranscript = formatToolActivityTranscript;
28
+ const ui_events_1 = require("./ui-events");
29
+ const format_1 = require("../services/format");
30
+ const verification_profile_1 = require("../services/verification-profile");
31
+ const COMMAND_CATEGORY_LABELS = {
32
+ workflow: 'Workflow',
33
+ session: 'Session',
34
+ context: 'Context',
35
+ tools: 'Tools',
36
+ model: 'Model',
37
+ system: 'System',
38
+ diagnostics: 'Diagnostics',
39
+ legacy: 'Legacy',
40
+ };
41
+ function rendererStatus(renderer) {
42
+ if (renderer === 'terminal')
43
+ return 'stable';
44
+ if (renderer === 'tui' || renderer === 'ink')
45
+ return 'beta';
46
+ if (renderer === 'print')
47
+ return 'non-interactive';
48
+ return 'custom';
49
+ }
50
+ function rendererCapabilityLabels(capabilities) {
51
+ return [
52
+ capabilities.structuredPickers ? 'pickers' : 'text-pickers',
53
+ capabilities.inlineProgress ? 'inline-progress' : 'legacy-progress',
54
+ capabilities.suppressLegacyTokenMeta ? 'clean-meta' : 'legacy-meta',
55
+ capabilities.extraAssistantSpacing ? 'assistant-spacing' : 'compact-spacing',
56
+ capabilities.suppressAbortNotice ? 'quiet-abort' : 'abort-notice',
57
+ ];
58
+ }
59
+ function createRuntimeCapabilitySummary(input = {}) {
60
+ const hasProjectRules = Boolean(input.projectInstructionsContent?.trim());
61
+ const hasSkills = Boolean(input.skillsContent?.trim());
62
+ const hasMemory = Boolean(input.memoryContent?.trim());
63
+ const hasMcp = Boolean(input.tools?.some(tool => tool.name.startsWith('mcp__')));
64
+ const hasWebSearch = Boolean(input.webSearchConfigured);
65
+ const labels = [
66
+ 'scrollback',
67
+ 'CJK input',
68
+ 'paste/edit',
69
+ 'trace',
70
+ ];
71
+ if (hasProjectRules)
72
+ labels.push('repo rules');
73
+ if (hasSkills)
74
+ labels.push('skills');
75
+ if (hasMemory)
76
+ labels.push('memory');
77
+ if (hasMcp)
78
+ labels.push('MCP');
79
+ if (hasWebSearch)
80
+ labels.push('web search');
81
+ return {
82
+ labels,
83
+ text: labels.join(', '),
84
+ hasProjectRules,
85
+ hasSkills,
86
+ hasMemory,
87
+ hasMcp,
88
+ hasWebSearch,
89
+ };
90
+ }
91
+ function normalizeSingleLineText(value) {
92
+ return value.replace(/\s+/g, ' ').trim();
93
+ }
94
+ function createSessionRestoredView(event) {
95
+ const shortId = event.sessionId.slice(0, 8);
96
+ const total = typeof event.messageCount === 'number'
97
+ ? `/${event.messageCount}`
98
+ : '';
99
+ const summary = event.summary ? normalizeSingleLineText(event.summary) : undefined;
100
+ return {
101
+ sessionId: event.sessionId,
102
+ shortId,
103
+ projectPath: event.projectPath,
104
+ model: event.model,
105
+ restoredMessages: event.restoredMessages,
106
+ messageCount: event.messageCount,
107
+ summary: summary || undefined,
108
+ headline: `Resumed session ${shortId} · restored ${event.restoredMessages}${total} messages`,
109
+ };
110
+ }
111
+ function sessionPickerTitle(session) {
112
+ return session.name || session.taskSummary || '(untitled)';
113
+ }
114
+ function clampPromptCursor(value, cursor) {
115
+ if (typeof cursor !== 'number' || !Number.isFinite(cursor))
116
+ return value.length;
117
+ return Math.min(Math.max(0, Math.floor(cursor)), value.length);
118
+ }
119
+ function promptCompletionState(value) {
120
+ const commandMatch = value.match(/^\/([^\s]*)$/u);
121
+ if (commandMatch) {
122
+ return { kind: 'command', query: commandMatch[1] ?? '' };
123
+ }
124
+ const fileQuery = getFileMentionQuery(value);
125
+ if (fileQuery) {
126
+ return { kind: 'file', base: fileQuery.base, query: fileQuery.query };
127
+ }
128
+ return { kind: 'none' };
129
+ }
130
+ function promptHistoryState(index, size) {
131
+ const safeSize = typeof size === 'number' && Number.isFinite(size) && size > 0
132
+ ? Math.floor(size)
133
+ : 0;
134
+ const safeIndex = typeof index === 'number' && Number.isFinite(index)
135
+ ? Math.floor(index)
136
+ : -1;
137
+ const active = safeIndex >= 0 && safeIndex < safeSize;
138
+ return {
139
+ index: active ? safeIndex : -1,
140
+ size: safeSize,
141
+ active,
142
+ canMovePrevious: safeSize > 0 && (!active || safeIndex < safeSize - 1),
143
+ canMoveNext: active,
144
+ };
145
+ }
146
+ function createPromptState(input) {
147
+ const cursor = clampPromptCursor(input.value, input.cursor);
148
+ const before = input.value.slice(0, cursor);
149
+ const after = input.value.slice(cursor);
150
+ const currentLineIndex = before.split('\n').length - 1;
151
+ const lineStart = before.lastIndexOf('\n') + 1;
152
+ const nextLineBreak = input.value.indexOf('\n', cursor);
153
+ const lineEnd = nextLineBreak === -1 ? input.value.length : nextLineBreak;
154
+ const currentLine = input.value.slice(lineStart, lineEnd);
155
+ return {
156
+ value: input.value,
157
+ cursor,
158
+ modeText: input.modeText,
159
+ running: Boolean(input.running),
160
+ isEmpty: input.value.length === 0,
161
+ lineCount: input.value.split('\n').length,
162
+ currentLineIndex,
163
+ currentLine,
164
+ cursorInCurrentLine: cursor - lineStart,
165
+ textBeforeCursor: before,
166
+ textAfterCursor: after,
167
+ completion: promptCompletionState(input.value),
168
+ history: promptHistoryState(input.historyIndex, input.historySize),
169
+ };
170
+ }
171
+ function commandCategoryLabel(category) {
172
+ return COMMAND_CATEGORY_LABELS[category ?? 'system'];
173
+ }
174
+ function commandPickerQuery(input) {
175
+ return input.startsWith('/') ? input.slice(1).toLowerCase() : '';
176
+ }
177
+ function commandMatchRank(command, query) {
178
+ if (!query)
179
+ return 0;
180
+ const name = command.name.toLowerCase();
181
+ const aliases = command.aliases?.map(alias => alias.toLowerCase()) ?? [];
182
+ if (name === query)
183
+ return 0;
184
+ if (aliases.some(alias => alias === query))
185
+ return 1;
186
+ if (name.startsWith(query))
187
+ return 2;
188
+ if (aliases.some(alias => alias.startsWith(query)))
189
+ return 3;
190
+ return 4;
191
+ }
192
+ function commandMatchesQuery(command, query) {
193
+ if (!query)
194
+ return true;
195
+ const name = command.name.toLowerCase();
196
+ const aliases = command.aliases?.map(alias => alias.toLowerCase()) ?? [];
197
+ return name.startsWith(query) || aliases.some(alias => alias.startsWith(query));
198
+ }
199
+ function commandPickerLabel(command) {
200
+ const hint = command.argumentHint ? ` ${command.argumentHint}` : '';
201
+ const aliases = command.aliases?.length ? ` (${command.aliases.join(', ')})` : '';
202
+ return `/${command.name}${hint}${aliases}`;
203
+ }
204
+ function createCommandPickerState(input) {
205
+ const query = commandPickerQuery(input.input);
206
+ const getCategoryLabel = input.categoryLabel ?? commandCategoryLabel;
207
+ const ranked = input.commands
208
+ .map((command, index) => ({
209
+ command,
210
+ index,
211
+ matchRank: commandMatchRank(command, query),
212
+ }))
213
+ .filter(item => commandMatchesQuery(item.command, query))
214
+ .sort((left, right) => {
215
+ const rankDelta = left.matchRank - right.matchRank;
216
+ return rankDelta !== 0 ? rankDelta : left.index - right.index;
217
+ });
218
+ const totalItems = ranked.length;
219
+ const visibleLimit = normalizeVisibleLimit(totalItems, input.maxVisibleItems ?? totalItems);
220
+ const maxOffset = maxPickerOffset(totalItems, visibleLimit);
221
+ const visibleStart = Math.max(0, Math.min(input.visibleStart ?? 0, maxOffset));
222
+ const visibleItems = ranked.slice(visibleStart, visibleStart + visibleLimit).map(item => {
223
+ const category = getCategoryLabel(item.command.category);
224
+ const aliases = item.command.aliases ?? [];
225
+ return {
226
+ command: item.command,
227
+ name: item.command.name,
228
+ value: item.command.name,
229
+ label: commandPickerLabel(item.command),
230
+ description: `${category} ${item.command.description}`,
231
+ categoryLabel: category,
232
+ aliases,
233
+ matchRank: item.matchRank,
234
+ };
235
+ });
236
+ return {
237
+ kind: 'command',
238
+ title: input.title ?? (query ? `Commands "${query}"` : 'Commands'),
239
+ totalItems,
240
+ visibleStart,
241
+ visibleLimit,
242
+ page: totalItems === 0 ? 1 : Math.floor(visibleStart / visibleLimit) + 1,
243
+ pageCount: totalItems === 0 ? 1 : Math.ceil(totalItems / visibleLimit),
244
+ visibleItems,
245
+ hasPreviousPage: visibleStart > 0,
246
+ hasNextPage: visibleStart < maxOffset,
247
+ };
248
+ }
249
+ function getFileMentionQuery(input) {
250
+ const match = input.match(/(^|\s)@([^\s]*)$/u);
251
+ if (!match || match.index === undefined)
252
+ return null;
253
+ const atIndex = match.index + match[1].length;
254
+ return {
255
+ base: input.slice(0, atIndex),
256
+ query: match[2] ?? '',
257
+ };
258
+ }
259
+ function filePickerValue(file) {
260
+ return file.isDirectory ? `${file.path}/` : file.path;
261
+ }
262
+ function filePickerLabel(file) {
263
+ return `${file.isDirectory ? 'dir' : 'file'} ${filePickerValue(file)}`;
264
+ }
265
+ function createFilePickerState(input) {
266
+ const query = getFileMentionQuery(input.input);
267
+ if (!query)
268
+ return null;
269
+ const totalItems = input.files.length;
270
+ const visibleLimit = normalizeVisibleLimit(totalItems, input.maxVisibleItems ?? totalItems);
271
+ const maxOffset = maxPickerOffset(totalItems, visibleLimit);
272
+ const visibleStart = Math.max(0, Math.min(input.visibleStart ?? 0, maxOffset));
273
+ return {
274
+ kind: 'file',
275
+ title: input.title ?? (query.query ? `Files "${query.query}"` : 'Files'),
276
+ totalItems,
277
+ visibleStart,
278
+ visibleLimit,
279
+ page: totalItems === 0 ? 1 : Math.floor(visibleStart / visibleLimit) + 1,
280
+ pageCount: totalItems === 0 ? 1 : Math.ceil(totalItems / visibleLimit),
281
+ visibleItems: input.files.slice(visibleStart, visibleStart + visibleLimit).map(file => ({
282
+ file,
283
+ path: file.path,
284
+ value: filePickerValue(file),
285
+ label: filePickerLabel(file),
286
+ description: file.isDirectory ? 'directory' : 'file',
287
+ isDirectory: file.isDirectory,
288
+ })),
289
+ hasPreviousPage: visibleStart > 0,
290
+ hasNextPage: visibleStart < maxOffset,
291
+ };
292
+ }
293
+ function previewText(value, maxLength) {
294
+ const singleLine = value.replace(/\s+/g, ' ').trim();
295
+ if (singleLine.length <= maxLength)
296
+ return singleLine;
297
+ return `${singleLine.slice(0, Math.max(0, maxLength - 3))}...`;
298
+ }
299
+ function createPermissionDecisionPickerState(request) {
300
+ const scope = permissionScopeDisplayValue(permissionScopeFromArgs(request.args));
301
+ const reason = request.reason?.trim();
302
+ const allowDescription = [scope === 'scope=unknown' ? '' : scope, reason].filter(Boolean).join(' ');
303
+ const items = [
304
+ {
305
+ value: 'allow',
306
+ label: `Allow ${request.name}`,
307
+ description: allowDescription,
308
+ approved: true,
309
+ },
310
+ {
311
+ value: 'deny',
312
+ label: `Deny ${request.name}`,
313
+ description: 'Do not run this tool call',
314
+ approved: false,
315
+ },
316
+ ];
317
+ return {
318
+ kind: 'permission',
319
+ title: 'Tool Permission',
320
+ totalItems: items.length,
321
+ visibleStart: 0,
322
+ visibleLimit: items.length,
323
+ page: 1,
324
+ pageCount: 1,
325
+ visibleItems: items,
326
+ hasPreviousPage: false,
327
+ hasNextPage: false,
328
+ };
329
+ }
330
+ function createEditPreviewPickerState(input) {
331
+ const { request } = input;
332
+ const totalItems = request.candidates.length;
333
+ const visibleLimit = normalizeVisibleLimit(totalItems, input.maxVisibleItems ?? totalItems);
334
+ const maxOffset = maxPickerOffset(totalItems, visibleLimit);
335
+ const visibleStart = Math.max(0, Math.min(input.visibleStart ?? 0, maxOffset));
336
+ const matchLimit = input.maxMatchLength ?? 50;
337
+ const replacementLimit = input.maxReplacementLength ?? 40;
338
+ const kindLabel = request.kind === 'fuzzy'
339
+ ? `fuzzy (${request.strategy ?? 'match'})`
340
+ : 'exact';
341
+ return {
342
+ kind: 'edit-preview',
343
+ title: `Edit Preview: ${request.path} (${kindLabel}, ${totalItems} candidate${totalItems === 1 ? '' : 's'})`,
344
+ totalItems,
345
+ visibleStart,
346
+ visibleLimit,
347
+ page: totalItems === 0 ? 1 : Math.floor(visibleStart / visibleLimit) + 1,
348
+ pageCount: totalItems === 0 ? 1 : Math.ceil(totalItems / visibleLimit),
349
+ visibleItems: request.candidates.slice(visibleStart, visibleStart + visibleLimit).map(candidate => {
350
+ const matchPreview = previewText(candidate.match, matchLimit);
351
+ const replacementPreview = previewText(request.newString, replacementLimit);
352
+ return {
353
+ candidate,
354
+ value: String(candidate.line),
355
+ label: `line ${candidate.line}: ${matchPreview}`,
356
+ description: `→ ${replacementPreview}`,
357
+ line: candidate.line,
358
+ matchPreview,
359
+ replacementPreview,
360
+ };
361
+ }),
362
+ hasPreviousPage: visibleStart > 0,
363
+ hasNextPage: visibleStart < maxOffset,
364
+ };
365
+ }
366
+ function normalizePickerModelId(value) {
367
+ return (value ?? '').trim().toLowerCase();
368
+ }
369
+ function createModelPickerState(input) {
370
+ const current = normalizePickerModelId(input.currentModel);
371
+ const totalItems = input.models.length;
372
+ const visibleLimit = normalizeVisibleLimit(totalItems, input.maxVisibleItems ?? totalItems);
373
+ const maxOffset = maxPickerOffset(totalItems, visibleLimit);
374
+ const visibleStart = Math.max(0, Math.min(input.visibleStart ?? 0, maxOffset));
375
+ return {
376
+ kind: 'model',
377
+ title: input.title ?? 'Available Models',
378
+ totalItems,
379
+ visibleStart,
380
+ visibleLimit,
381
+ page: totalItems === 0 ? 1 : Math.floor(visibleStart / visibleLimit) + 1,
382
+ pageCount: totalItems === 0 ? 1 : Math.ceil(totalItems / visibleLimit),
383
+ visibleItems: input.models.slice(visibleStart, visibleStart + visibleLimit).map(model => {
384
+ const name = model.name;
385
+ const alias = model.alias?.trim() || undefined;
386
+ const isCurrent = current !== ''
387
+ && (normalizePickerModelId(name) === current || normalizePickerModelId(alias) === current);
388
+ const aliasLabel = alias ? ` (${alias})` : '';
389
+ const description = [
390
+ model.provider,
391
+ typeof model.contextWindow === 'number' ? `${model.contextWindow} ctx` : undefined,
392
+ typeof model.maxOutputTokens === 'number' ? `${model.maxOutputTokens} output` : undefined,
393
+ model.source,
394
+ ].filter(Boolean).join(' ');
395
+ return {
396
+ model,
397
+ name,
398
+ value: name,
399
+ alias,
400
+ provider: model.provider,
401
+ contextWindow: model.contextWindow,
402
+ maxOutputTokens: model.maxOutputTokens,
403
+ source: model.source,
404
+ isCurrent,
405
+ label: `${name}${aliasLabel}`,
406
+ description,
407
+ };
408
+ }),
409
+ hasPreviousPage: visibleStart > 0,
410
+ hasNextPage: visibleStart < maxOffset,
411
+ };
412
+ }
413
+ function summarizePermissionValue(value) {
414
+ if (typeof value === 'string')
415
+ return value;
416
+ try {
417
+ const serialized = JSON.stringify(value);
418
+ return typeof serialized === 'string' ? serialized : String(value);
419
+ }
420
+ catch {
421
+ return String(value);
422
+ }
423
+ }
424
+ function summarizePermissionArgs(args) {
425
+ return Object.entries(args)
426
+ .slice(0, 4)
427
+ .map(([key, value]) => `${key}=${summarizePermissionValue(value)}`)
428
+ .join(' ');
429
+ }
430
+ function permissionScopeFromArgs(args) {
431
+ if (typeof args.command === 'string' && args.command.trim()) {
432
+ return { kind: 'command', value: `$ ${args.command}` };
433
+ }
434
+ if (typeof args.path === 'string' && args.path.trim()) {
435
+ return { kind: 'path', value: args.path };
436
+ }
437
+ if (Array.isArray(args.paths) && args.paths.length > 0) {
438
+ return { kind: 'paths', count: args.paths.length };
439
+ }
440
+ const detail = summarizePermissionArgs(args);
441
+ return detail ? { kind: 'args', value: detail } : { kind: 'unknown' };
442
+ }
443
+ function permissionScopeDisplayValue(scope) {
444
+ if (scope.kind === 'command')
445
+ return `cmd=${scope.value ?? ''}`;
446
+ if (scope.kind === 'path')
447
+ return `path=${scope.value ?? ''}`;
448
+ if (scope.kind === 'paths')
449
+ return `paths=${scope.count ?? 0}`;
450
+ if (scope.kind === 'args')
451
+ return `args=${scope.value ?? ''}`;
452
+ return 'scope=unknown';
453
+ }
454
+ function permissionRiskLevel(toolName, args = {}) {
455
+ if (toolName === 'exec_command' && typeof args.command === 'string') {
456
+ return (0, verification_profile_1.classifyCommandSafety)(args.command).risk;
457
+ }
458
+ const normalized = toolName.toLowerCase();
459
+ if (/(?:write|edit|delete|remove|patch|git_push|publish)/.test(normalized))
460
+ return 'high';
461
+ if (/(?:read|search|list|grep|find|stat|ls|cat)/.test(normalized))
462
+ return 'low';
463
+ return 'medium';
464
+ }
465
+ function permissionRiskDisplayValue(risk) {
466
+ const label = risk.level === 'high' ? 'HIGH' : risk.level === 'medium' ? 'med' : risk.level;
467
+ return `${label}: ${risk.reason}`;
468
+ }
469
+ function createPermissionPromptState(request, cwd) {
470
+ return {
471
+ requestId: request.id,
472
+ toolName: request.name,
473
+ scope: permissionScopeFromArgs(request.args),
474
+ cwd,
475
+ risk: {
476
+ level: permissionRiskLevel(request.name, request.args),
477
+ reason: request.reason?.trim() || 'approval required',
478
+ },
479
+ options: {
480
+ approve: 'y=yes',
481
+ deny: 'n=no',
482
+ },
483
+ };
484
+ }
485
+ function normalizeVisibleLimit(totalItems, requestedLimit) {
486
+ const safeRequested = Number.isFinite(requestedLimit) && requestedLimit && requestedLimit > 0
487
+ ? Math.floor(requestedLimit)
488
+ : 10;
489
+ return Math.max(1, Math.min(safeRequested, Math.max(1, totalItems)));
490
+ }
491
+ function maxPickerOffset(totalItems, visibleLimit) {
492
+ return Math.max(0, Math.ceil(totalItems / visibleLimit) - 1) * visibleLimit;
493
+ }
494
+ function createSessionPickerState(request, visibleStart = 0) {
495
+ const totalItems = request.sessions.length;
496
+ const visibleLimit = normalizeVisibleLimit(totalItems, request.maxVisibleItems);
497
+ const maxOffset = maxPickerOffset(totalItems, visibleLimit);
498
+ const clampedStart = Math.max(0, Math.min(visibleStart, maxOffset));
499
+ const visibleSessions = request.sessions.slice(clampedStart, clampedStart + visibleLimit);
500
+ return {
501
+ kind: 'session',
502
+ title: request.title,
503
+ totalItems,
504
+ visibleStart: clampedStart,
505
+ visibleLimit,
506
+ page: totalItems === 0 ? 1 : Math.floor(clampedStart / visibleLimit) + 1,
507
+ pageCount: totalItems === 0 ? 1 : Math.ceil(totalItems / visibleLimit),
508
+ visibleItems: visibleSessions.map((session, index) => ({
509
+ session,
510
+ globalIndex: clampedStart + index + 1,
511
+ sessionId: session.id,
512
+ shortId: session.id.slice(0, 8),
513
+ title: sessionPickerTitle(session),
514
+ messageCount: session.messageCount ?? 0,
515
+ historySizeBytes: session.historySizeBytes ?? 0,
516
+ model: session.model,
517
+ projectPath: session.projectPath,
518
+ showProject: Boolean(request.showProject),
519
+ })),
520
+ hasPreviousPage: clampedStart > 0,
521
+ hasNextPage: clampedStart < maxOffset,
522
+ };
523
+ }
524
+ function movePickerPageOffset(state, delta) {
525
+ const maxOffset = maxPickerOffset(state.totalItems, state.visibleLimit);
526
+ return Math.max(0, Math.min(state.visibleStart + delta * state.visibleLimit, maxOffset));
527
+ }
528
+ function createStatusSnapshot(input = {}) {
529
+ const rendererName = typeof input.renderer === 'string' && input.renderer.trim()
530
+ ? input.renderer
531
+ : 'terminal';
532
+ const capabilities = (0, ui_events_1.resolveUiRendererCapabilities)(input.capabilities, rendererName);
533
+ return {
534
+ model: input.model,
535
+ sessionId: input.sessionId,
536
+ tokens: input.tokens,
537
+ costUsd: input.costUsd,
538
+ runningState: input.runningState,
539
+ permissionMode: input.permissionMode,
540
+ loop: input.loop,
541
+ renderer: {
542
+ name: rendererName,
543
+ status: rendererStatus(rendererName),
544
+ capabilities,
545
+ capabilityLabels: rendererCapabilityLabels(capabilities),
546
+ },
547
+ };
548
+ }
549
+ function transcriptEntryToBlock(entry) {
550
+ const kind = entry.title === 'resume'
551
+ ? 'resume'
552
+ : entry.role;
553
+ return {
554
+ id: entry.id,
555
+ kind,
556
+ role: entry.role,
557
+ title: entry.title,
558
+ content: entry.content,
559
+ };
560
+ }
561
+ function toolActivityBatchLabel(activity) {
562
+ if (typeof activity.batchCount !== 'number'
563
+ || activity.batchCount <= 1
564
+ || typeof activity.batchIndex !== 'number'
565
+ || activity.batchIndex < 0
566
+ || activity.batchIndex >= activity.batchCount) {
567
+ return '';
568
+ }
569
+ return `Batch ${activity.batchIndex + 1}/${activity.batchCount} · `;
570
+ }
571
+ function toolActivityFromStarted(event, detail = '') {
572
+ const command = event.name === 'exec_command' && typeof event.args.command === 'string'
573
+ ? event.args.command
574
+ : undefined;
575
+ return {
576
+ callId: event.callId,
577
+ name: event.name,
578
+ state: 'running',
579
+ detail: command ? undefined : detail || undefined,
580
+ command,
581
+ batchCount: event.batchCount,
582
+ batchIndex: event.batchIndex,
583
+ };
584
+ }
585
+ function toolActivityFromFinished(event, detail = '') {
586
+ return {
587
+ callId: event.callId,
588
+ name: event.name,
589
+ state: event.skipped ? 'skipped' : event.success ? 'success' : 'error',
590
+ detail: detail || undefined,
591
+ command: event.name === 'exec_command' && typeof event.args.command === 'string'
592
+ ? event.args.command
593
+ : undefined,
594
+ summary: event.summary,
595
+ durationMs: event.duration,
596
+ error: event.error,
597
+ outputBytes: event.outputBytes,
598
+ artifactRef: event.artifactRef,
599
+ batchCount: event.batchCount,
600
+ batchIndex: event.batchIndex,
601
+ };
602
+ }
603
+ function formatToolActivityTranscript(activity) {
604
+ const prefix = toolActivityBatchLabel(activity);
605
+ const lines = [];
606
+ const detailLooksTruncated = Boolean(activity.detail?.includes('...'));
607
+ if (activity.state === 'queued') {
608
+ lines.push(`${prefix}Queued ${activity.name}${activity.detail ? ` ${activity.detail}` : ''}`);
609
+ }
610
+ else if (activity.state === 'running') {
611
+ lines.push(`${prefix}Running ${activity.name}${activity.detail ? ` ${activity.detail}` : ''}`);
612
+ }
613
+ else if (activity.state === 'requested') {
614
+ lines.push(`${prefix}Requested ${activity.name}${activity.detail ? ` ${activity.detail}` : ''}`);
615
+ }
616
+ else if (activity.state === 'skipped') {
617
+ lines.push(`${prefix}Skipped ${activity.name}${activity.detail ? ` ${activity.detail}` : ''}`);
618
+ }
619
+ else {
620
+ const symbol = activity.state === 'success' ? '✓' : '✗';
621
+ const suffix = activity.detail ? ` ${activity.detail}` : '';
622
+ const duration = typeof activity.durationMs === 'number' ? ` (${activity.durationMs}ms)` : '';
623
+ lines.push(`${prefix}${activity.summary || `${symbol} ${activity.name}${suffix}${duration}`}`);
624
+ }
625
+ if (activity.command) {
626
+ lines.push(` $ ${activity.command}`);
627
+ }
628
+ if (activity.artifactRef) {
629
+ lines.push(` Full output: /artifacts show ${activity.artifactRef.id} --full (${(0, format_1.formatBytes)(activity.artifactRef.outputBytes)})`);
630
+ }
631
+ else if (typeof activity.outputBytes === 'number') {
632
+ lines.push(` output ${(0, format_1.formatBytes)(activity.outputBytes)}`);
633
+ }
634
+ if (activity.error) {
635
+ lines.push(`Error: ${activity.error}`);
636
+ }
637
+ if (detailLooksTruncated || activity.artifactRef || activity.error) {
638
+ lines.push(' Details: /last-tool or /trace latest');
639
+ }
640
+ return lines.filter(Boolean).join('\n');
641
+ }
642
+ //# sourceMappingURL=ui-view-model.js.map