praxis-agent 0.35.0 → 0.37.0

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 (111) hide show
  1. package/README.md +20 -2
  2. package/dist/application/background-agent-manager.d.ts +16 -6
  3. package/dist/application/background-agent-manager.js +139 -97
  4. package/dist/application/context-engine.d.ts +56 -0
  5. package/dist/application/context-engine.js +129 -0
  6. package/dist/application/durable-follow-up.d.ts +14 -0
  7. package/dist/application/durable-follow-up.js +52 -0
  8. package/dist/application/native-session-transcript.d.ts +72 -0
  9. package/dist/application/native-session-transcript.js +351 -0
  10. package/dist/application/native-sidechain-transcript.d.ts +34 -0
  11. package/dist/application/native-sidechain-transcript.js +156 -0
  12. package/dist/application/session-service.d.ts +23 -4
  13. package/dist/application/session-service.js +1130 -494
  14. package/dist/application/subagent-service.d.ts +5 -1
  15. package/dist/application/subagent-service.js +749 -320
  16. package/dist/application/team-agent-runtime.d.ts +29 -0
  17. package/dist/application/team-agent-runtime.js +169 -0
  18. package/dist/application/team-capability.d.ts +27 -0
  19. package/dist/application/team-capability.js +41 -0
  20. package/dist/application/team-lead-operations.d.ts +38 -0
  21. package/dist/application/team-lead-operations.js +155 -0
  22. package/dist/application/team-mailbox.d.ts +54 -0
  23. package/dist/application/team-mailbox.js +196 -0
  24. package/dist/application/team-manager.d.ts +113 -0
  25. package/dist/application/team-manager.js +681 -0
  26. package/dist/application/team-workspace.d.ts +29 -0
  27. package/dist/application/team-workspace.js +197 -0
  28. package/dist/application/top-level-agent-manager.d.ts +1 -0
  29. package/dist/application/top-level-agent-manager.js +1025 -403
  30. package/dist/application/transcript-projection.d.ts +35 -0
  31. package/dist/application/transcript-projection.js +139 -0
  32. package/dist/application/turn-lifecycle.d.ts +2 -0
  33. package/dist/application/turn-memory-coordinator.d.ts +43 -0
  34. package/dist/application/turn-memory-coordinator.js +98 -0
  35. package/dist/cli/agents-dashboard.js +10 -5
  36. package/dist/cli/interactive.d.ts +7 -0
  37. package/dist/cli/interactive.js +645 -334
  38. package/dist/cli/tui/claude-style.d.ts +8 -25
  39. package/dist/cli/tui/claude-style.js +299 -277
  40. package/dist/cli/tui/decision-surface-model.d.ts +71 -0
  41. package/dist/cli/tui/decision-surface-model.js +131 -0
  42. package/dist/cli/tui/decision-surface.d.ts +7 -0
  43. package/dist/cli/tui/decision-surface.js +41 -0
  44. package/dist/cli/tui/help-surface-model.d.ts +68 -0
  45. package/dist/cli/tui/help-surface-model.js +131 -0
  46. package/dist/cli/tui/mcp-panel.js +1 -2
  47. package/dist/cli/tui/permission-surface-model.d.ts +195 -0
  48. package/dist/cli/tui/permission-surface-model.js +360 -0
  49. package/dist/cli/tui/permission-surface.d.ts +7 -0
  50. package/dist/cli/tui/permission-surface.js +69 -0
  51. package/dist/cli/tui/presentation-environment.d.ts +32 -0
  52. package/dist/cli/tui/presentation-environment.js +149 -0
  53. package/dist/cli/tui/task-panel.js +10 -1
  54. package/dist/cli/tui/theme.d.ts +46 -3
  55. package/dist/cli/tui/theme.js +414 -56
  56. package/dist/cli/tui/tool-permission.js +16 -8
  57. package/dist/cli/tui/transcript-presentation.d.ts +19 -1
  58. package/dist/cli/tui/transcript-presentation.js +26 -6
  59. package/dist/cli/tui/transcript-viewport.d.ts +18 -0
  60. package/dist/cli/tui/transcript-viewport.js +517 -111
  61. package/dist/cli/tui/transcript-window-model.d.ts +108 -0
  62. package/dist/cli/tui/transcript-window-model.js +772 -0
  63. package/dist/cli/tui/tui-interaction-router.d.ts +78 -0
  64. package/dist/cli/tui/tui-interaction-router.js +127 -0
  65. package/dist/cli/tui/tui-screen-model.d.ts +114 -0
  66. package/dist/cli/tui/tui-screen-model.js +210 -0
  67. package/dist/cli/tui/tui-view-model.d.ts +2 -49
  68. package/dist/cli/tui/tui-view-model.js +1 -68
  69. package/dist/cli-runtime.d.ts +4 -2
  70. package/dist/cli-runtime.js +397 -32
  71. package/dist/compatibility/claude/transcript-codec.js +6 -0
  72. package/dist/core/agent-orchestration.d.ts +31 -0
  73. package/dist/core/agent-orchestration.js +235 -0
  74. package/dist/core/permission-policy.d.ts +4 -0
  75. package/dist/core/permission-policy.js +21 -0
  76. package/dist/core/runtime.d.ts +6 -0
  77. package/dist/core/runtime.js +20 -2
  78. package/dist/core/team-mailbox.d.ts +54 -0
  79. package/dist/core/team-mailbox.js +212 -0
  80. package/dist/core/team-ownership.d.ts +84 -0
  81. package/dist/core/team-ownership.js +505 -0
  82. package/dist/core/tool-execution-scheduler.js +6 -1
  83. package/dist/core/transcript-event.d.ts +5 -1
  84. package/dist/core/transcript-event.js +12 -0
  85. package/dist/persistence/claude-job-store.d.ts +58 -0
  86. package/dist/persistence/claude-job-store.js +483 -1
  87. package/dist/persistence/claude-transcript-store.d.ts +2 -1
  88. package/dist/persistence/claude-transcript-store.js +2 -10
  89. package/dist/persistence/native-team-mailbox.d.ts +68 -0
  90. package/dist/persistence/native-team-mailbox.js +542 -0
  91. package/dist/persistence/native-team-store.d.ts +34 -0
  92. package/dist/persistence/native-team-store.js +257 -0
  93. package/dist/persistence/native-transcript-reader.d.ts +53 -0
  94. package/dist/persistence/native-transcript-reader.js +283 -0
  95. package/dist/persistence/native-transcript-store.d.ts +69 -0
  96. package/dist/persistence/native-transcript-store.js +321 -0
  97. package/dist/persistence/subagent-lifecycle-store.d.ts +41 -8
  98. package/dist/persistence/subagent-lifecycle-store.js +373 -146
  99. package/dist/persistence/transcript-file-append.d.ts +2 -0
  100. package/dist/persistence/transcript-file-append.js +10 -0
  101. package/dist/platform/exclusive-file-lease.d.ts +3 -0
  102. package/dist/platform/exclusive-file-lease.js +3 -0
  103. package/dist/tools/claude-capabilities.d.ts +4 -0
  104. package/dist/tools/claude-capabilities.js +18 -2
  105. package/dist/tools/team-lead-tools.d.ts +19 -0
  106. package/dist/tools/team-lead-tools.js +479 -0
  107. package/dist/tools/team-member-tools.d.ts +21 -0
  108. package/dist/tools/team-member-tools.js +259 -0
  109. package/package.json +1 -1
  110. package/dist/cli/tui/fullscreen-renderer.d.ts +0 -14
  111. package/dist/cli/tui/fullscreen-renderer.js +0 -13
@@ -0,0 +1,772 @@
1
+ import { projectTranscriptPresentation, transcriptPresentationEntryKey, transcriptReadSummaryKey, } from './transcript-presentation.js';
2
+ import { createTranscriptEntryViewportIndex, estimateTranscriptEntryLines, projectTranscriptEntryRange, } from './transcript-viewport.js';
3
+ const historyChangeLineage = Symbol('tui-history-change-lineage');
4
+ function retainedHistoryChange(revision, changedFrom, history, previousHistory) {
5
+ const change = { revision, changedFrom };
6
+ Object.defineProperty(change, historyChangeLineage, {
7
+ value: Object.freeze({
8
+ history,
9
+ ...(previousHistory ? { previousHistory } : {}),
10
+ }),
11
+ enumerable: false,
12
+ });
13
+ return Object.freeze(change);
14
+ }
15
+ /**
16
+ * Creates a caller-owned immutable mutation fact with non-persisted provenance.
17
+ * Provenance lets the retained model verify an append in O(1); unbranded or
18
+ * inconsistent facts remain valid inputs but deliberately take the cold path.
19
+ */
20
+ export function createTuiHistoryChange(revision, changedFrom, history, previousHistory) {
21
+ if (previousHistory) {
22
+ if (changedFrom > previousHistory.length || changedFrom > history.length)
23
+ return Object.freeze({ revision, changedFrom });
24
+ for (let index = 0; index < changedFrom; index += 1) {
25
+ if (history[index] !== previousHistory[index])
26
+ return Object.freeze({ revision, changedFrom });
27
+ }
28
+ }
29
+ return retainedHistoryChange(revision, changedFrom, history, previousHistory);
30
+ }
31
+ /** Constructs an append and its lineage proof without scanning the old prefix. */
32
+ export function appendTuiHistory(revision, previousHistory, suffix) {
33
+ const history = [...previousHistory, ...suffix];
34
+ return {
35
+ history,
36
+ change: retainedHistoryChange(revision, previousHistory.length, history, previousHistory),
37
+ };
38
+ }
39
+ /** @internal Brands an append already constructed by the React history owner. */
40
+ export function createTuiAppendHistoryChange(revision, previousHistory, history) {
41
+ if (history.length <= previousHistory.length)
42
+ return Object.freeze({
43
+ revision,
44
+ changedFrom: previousHistory.length,
45
+ });
46
+ return retainedHistoryChange(revision, previousHistory.length, history, previousHistory);
47
+ }
48
+ const LEAF_SIZE = 64;
49
+ const VIEWPORT_INDEX_THRESHOLD = 64;
50
+ function leaf(entries, rows, viewportIndexes = []) {
51
+ return {
52
+ kind: 'leaf',
53
+ entries,
54
+ rows,
55
+ viewportIndexes,
56
+ entryCount: entries.length,
57
+ totalRows: rows.reduce((total, count) => total + count, 0),
58
+ };
59
+ }
60
+ function nodeHeight(node) {
61
+ return node.kind === 'branch' ? node.height : 1;
62
+ }
63
+ function branch(left, right) {
64
+ return {
65
+ kind: 'branch',
66
+ left,
67
+ right,
68
+ entryCount: left.entryCount + right.entryCount,
69
+ totalRows: left.totalRows + right.totalRows,
70
+ height: Math.max(nodeHeight(left), nodeHeight(right)) + 1,
71
+ };
72
+ }
73
+ function rotateRowLeft(node) {
74
+ if (node.right.kind !== 'branch')
75
+ return node;
76
+ return branch(branch(node.left, node.right.left), node.right.right);
77
+ }
78
+ function rotateRowRight(node) {
79
+ if (node.left.kind !== 'branch')
80
+ return node;
81
+ return branch(node.left.left, branch(node.left.right, node.right));
82
+ }
83
+ function balanceRow(node) {
84
+ const factor = nodeHeight(node.left) - nodeHeight(node.right);
85
+ if (factor > 1 && node.left.kind === 'branch') {
86
+ const left = nodeHeight(node.left.left) < nodeHeight(node.left.right)
87
+ ? rotateRowLeft(node.left)
88
+ : node.left;
89
+ return rotateRowRight(branch(left, node.right));
90
+ }
91
+ if (factor < -1 && node.right.kind === 'branch') {
92
+ const right = nodeHeight(node.right.right) < nodeHeight(node.right.left)
93
+ ? rotateRowRight(node.right)
94
+ : node.right;
95
+ return rotateRowLeft(branch(node.left, right));
96
+ }
97
+ return node;
98
+ }
99
+ function buildBalancedRows(leaves) {
100
+ if (leaves.length === 0)
101
+ return leaf([], []);
102
+ const build = (start, end) => {
103
+ if (end - start === 1)
104
+ return leaves[start] ?? leaf([], []);
105
+ const middle = start + Math.floor((end - start) / 2);
106
+ return branch(build(start, middle), build(middle, end));
107
+ };
108
+ return build(0, leaves.length);
109
+ }
110
+ function buildTree(entries, rows, viewportIndexes) {
111
+ const leaves = [];
112
+ for (let start = 0; start < entries.length; start += LEAF_SIZE) {
113
+ leaves.push(leaf(entries.slice(start, start + LEAF_SIZE), rows.slice(start, start + LEAF_SIZE), viewportIndexes.slice(start, start + LEAF_SIZE)));
114
+ }
115
+ return buildBalancedRows(leaves);
116
+ }
117
+ function appendTree(node, entry, rows, viewportIndex) {
118
+ if (node.kind === 'leaf') {
119
+ if (node.entries.length < LEAF_SIZE)
120
+ return leaf([...node.entries, entry], [...node.rows, rows], [...node.viewportIndexes, viewportIndex]);
121
+ return branch(node, leaf([entry], [rows], [viewportIndex]));
122
+ }
123
+ return balanceRow(branch(node.left, appendTree(node.right, entry, rows, viewportIndex)));
124
+ }
125
+ function updateTree(node, index, entry, rows, viewportIndex) {
126
+ if (node.kind === 'leaf') {
127
+ if (index < 0 || index >= node.entryCount)
128
+ return node;
129
+ const entries = [...node.entries];
130
+ const counts = [...node.rows];
131
+ const viewportIndexes = [...node.viewportIndexes];
132
+ entries[index] = entry;
133
+ counts[index] = rows;
134
+ viewportIndexes[index] = viewportIndex;
135
+ return leaf(entries, counts, viewportIndexes);
136
+ }
137
+ return index < node.left.entryCount
138
+ ? branch(updateTree(node.left, index, entry, rows, viewportIndex), node.right)
139
+ : branch(node.left, updateTree(node.right, index - node.left.entryCount, entry, rows, viewportIndex));
140
+ }
141
+ function joinRows(left, right) {
142
+ if (left.entryCount === 0)
143
+ return right;
144
+ if (right.entryCount === 0)
145
+ return left;
146
+ if (nodeHeight(left) > nodeHeight(right) + 1 && left.kind === 'branch')
147
+ return balanceRow(branch(left.left, joinRows(left.right, right)));
148
+ if (nodeHeight(right) > nodeHeight(left) + 1 && right.kind === 'branch')
149
+ return balanceRow(branch(joinRows(left, right.left), right.right));
150
+ return branch(left, right);
151
+ }
152
+ function takeTree(node, count) {
153
+ if (count <= 0)
154
+ return leaf([], []);
155
+ if (count >= node.entryCount)
156
+ return node;
157
+ if (node.kind === 'leaf')
158
+ return leaf(node.entries.slice(0, count), node.rows.slice(0, count), node.viewportIndexes.slice(0, count));
159
+ if (count <= node.left.entryCount)
160
+ return takeTree(node.left, count);
161
+ return joinRows(node.left, takeTree(node.right, count - node.left.entryCount));
162
+ }
163
+ function treeAt(node, index) {
164
+ if (index < 0 || index >= node.entryCount)
165
+ return undefined;
166
+ if (node.kind === 'leaf')
167
+ return node.entries[index];
168
+ return index < node.left.entryCount
169
+ ? treeAt(node.left, index)
170
+ : treeAt(node.right, index - node.left.entryCount);
171
+ }
172
+ function treeEntries(node, output = []) {
173
+ if (node.kind === 'leaf')
174
+ output.push(...node.entries);
175
+ else {
176
+ treeEntries(node.left, output);
177
+ treeEntries(node.right, output);
178
+ }
179
+ return output;
180
+ }
181
+ function collectRange(node, start, end, base = 0, output = []) {
182
+ if (end <= base || start >= base + node.totalRows)
183
+ return output;
184
+ if (node.kind === 'leaf') {
185
+ let cursor = base;
186
+ for (let index = 0; index < node.entryCount; index += 1) {
187
+ const count = node.rows[index] ?? 0;
188
+ const entry = node.entries[index];
189
+ const viewportIndex = node.viewportIndexes[index];
190
+ if (entry && cursor + count > start && cursor < end)
191
+ output.push({
192
+ entry,
193
+ start: cursor,
194
+ rows: count,
195
+ ...(viewportIndex ? { viewportIndex } : {}),
196
+ });
197
+ cursor += count;
198
+ }
199
+ return output;
200
+ }
201
+ collectRange(node.left, start, end, base, output);
202
+ collectRange(node.right, start, end, base + node.left.totalRows, output);
203
+ return output;
204
+ }
205
+ function pendingHeight(node) {
206
+ return node?.height ?? 0;
207
+ }
208
+ function pendingNode(key, queue, left, right) {
209
+ return {
210
+ key,
211
+ queue,
212
+ ...(left ? { left } : {}),
213
+ ...(right ? { right } : {}),
214
+ height: Math.max(pendingHeight(left), pendingHeight(right)) + 1,
215
+ };
216
+ }
217
+ function rotatePendingLeft(node) {
218
+ const right = node.right;
219
+ if (!right)
220
+ return node;
221
+ return pendingNode(right.key, right.queue, pendingNode(node.key, node.queue, node.left, right.left), right.right);
222
+ }
223
+ function rotatePendingRight(node) {
224
+ const left = node.left;
225
+ if (!left)
226
+ return node;
227
+ return pendingNode(left.key, left.queue, left.left, pendingNode(node.key, node.queue, left.right, node.right));
228
+ }
229
+ function balancePending(node) {
230
+ const factor = pendingHeight(node.left) - pendingHeight(node.right);
231
+ if (factor > 1 && node.left) {
232
+ const left = pendingHeight(node.left.left) < pendingHeight(node.left.right)
233
+ ? rotatePendingLeft(node.left)
234
+ : node.left;
235
+ return rotatePendingRight(pendingNode(node.key, node.queue, left, node.right));
236
+ }
237
+ if (factor < -1 && node.right) {
238
+ const right = pendingHeight(node.right.right) < pendingHeight(node.right.left)
239
+ ? rotatePendingRight(node.right)
240
+ : node.right;
241
+ return rotatePendingLeft(pendingNode(node.key, node.queue, node.left, right));
242
+ }
243
+ return node;
244
+ }
245
+ function pendingGet(node, key) {
246
+ let cursor = node;
247
+ while (cursor) {
248
+ if (key === cursor.key)
249
+ return cursor.queue;
250
+ cursor = key < cursor.key ? cursor.left : cursor.right;
251
+ }
252
+ return undefined;
253
+ }
254
+ function pendingSet(node, key, queue) {
255
+ if (!node)
256
+ return pendingNode(key, queue);
257
+ if (key === node.key)
258
+ return pendingNode(key, queue, node.left, node.right);
259
+ return key < node.key
260
+ ? balancePending(pendingNode(node.key, node.queue, pendingSet(node.left, key, queue), node.right))
261
+ : balancePending(pendingNode(node.key, node.queue, node.left, pendingSet(node.right, key, queue)));
262
+ }
263
+ function pendingMinimum(node) {
264
+ let cursor = node;
265
+ while (cursor.left)
266
+ cursor = cursor.left;
267
+ return cursor;
268
+ }
269
+ function pendingDelete(node, key) {
270
+ if (!node)
271
+ return undefined;
272
+ if (key < node.key) {
273
+ const left = pendingDelete(node.left, key);
274
+ return balancePending(pendingNode(node.key, node.queue, left, node.right));
275
+ }
276
+ if (key > node.key) {
277
+ const right = pendingDelete(node.right, key);
278
+ return balancePending(pendingNode(node.key, node.queue, node.left, right));
279
+ }
280
+ if (!node.left)
281
+ return node.right;
282
+ if (!node.right)
283
+ return node.left;
284
+ const successor = pendingMinimum(node.right);
285
+ return balancePending(pendingNode(successor.key, successor.queue, node.left, pendingDelete(node.right, successor.key)));
286
+ }
287
+ function pendingPush(root, key, call) {
288
+ const queue = pendingGet(root, key) ?? { length: 0 };
289
+ return pendingSet(root, key, {
290
+ ...queue,
291
+ back: { value: call, ...(queue.back ? { next: queue.back } : {}) },
292
+ length: queue.length + 1,
293
+ });
294
+ }
295
+ function reversePendingList(list) {
296
+ let cursor = list;
297
+ let reversed;
298
+ while (cursor) {
299
+ reversed = {
300
+ value: cursor.value,
301
+ ...(reversed ? { next: reversed } : {}),
302
+ };
303
+ cursor = cursor.next;
304
+ }
305
+ return reversed;
306
+ }
307
+ function pendingFirst(queue) {
308
+ if (queue?.front)
309
+ return queue.front.value;
310
+ let cursor = queue?.back;
311
+ while (cursor?.next)
312
+ cursor = cursor.next;
313
+ return cursor?.value;
314
+ }
315
+ function pendingPop(root, key) {
316
+ const queue = pendingGet(root, key);
317
+ if (!queue || queue.length === 0)
318
+ return [undefined, root];
319
+ const front = queue.front ?? reversePendingList(queue.back);
320
+ const first = front?.value;
321
+ if (!first)
322
+ return [undefined, root];
323
+ const remaining = {
324
+ ...(front.next ? { front: front.next } : {}),
325
+ ...(queue.front && queue.back ? { back: queue.back } : {}),
326
+ length: queue.length - 1,
327
+ };
328
+ return [
329
+ first,
330
+ remaining.length > 0
331
+ ? pendingSet(root, key, remaining)
332
+ : pendingDelete(root, key),
333
+ ];
334
+ }
335
+ function coldPairing(history, entries) {
336
+ const entryIndexes = new Map(entries.map((entry, index) => [entry.key, index]));
337
+ const tools = new Map();
338
+ const shells = new Map();
339
+ const pairedToolCallByResult = new Map();
340
+ const toolResultByCall = new Map();
341
+ for (let sourceIndex = 0; sourceIndex < history.length; sourceIndex += 1) {
342
+ const item = history[sourceIndex];
343
+ if (!item)
344
+ continue;
345
+ if (item.kind === 'tool') {
346
+ const pending = tools.get(item.call.id) ?? { calls: [], next: 0 };
347
+ pending.calls.push({
348
+ sourceIndex,
349
+ presentationIndex: entryIndexes.get(transcriptPresentationEntryKey(item, sourceIndex)) ??
350
+ -1,
351
+ item,
352
+ });
353
+ tools.set(item.call.id, pending);
354
+ continue;
355
+ }
356
+ if (item.kind === 'tool-result') {
357
+ const pending = tools.get(item.callId);
358
+ const call = pending?.calls[pending.next];
359
+ if (pending && call) {
360
+ pending.next += 1;
361
+ pairedToolCallByResult.set(sourceIndex, call);
362
+ toolResultByCall.set(call.sourceIndex, item);
363
+ if (pending.next === pending.calls.length)
364
+ tools.delete(item.callId);
365
+ }
366
+ continue;
367
+ }
368
+ if (item.kind === 'shell') {
369
+ const pending = shells.get(item.callId) ?? { calls: [], next: 0 };
370
+ pending.calls.push({
371
+ sourceIndex,
372
+ presentationIndex: entryIndexes.get(transcriptPresentationEntryKey(item, sourceIndex)) ??
373
+ -1,
374
+ item,
375
+ });
376
+ shells.set(item.callId, pending);
377
+ continue;
378
+ }
379
+ if (item.kind === 'shell-result') {
380
+ const pending = shells.get(item.callId);
381
+ if (pending?.calls[pending.next])
382
+ pending.next += 1;
383
+ if (pending && pending.next === pending.calls.length)
384
+ shells.delete(item.callId);
385
+ }
386
+ }
387
+ let pendingTools;
388
+ for (const [key, pending] of tools) {
389
+ for (const call of pending.calls.slice(pending.next))
390
+ pendingTools = pendingPush(pendingTools, key, call);
391
+ }
392
+ let pendingShells;
393
+ for (const [key, pending] of shells) {
394
+ for (const call of pending.calls.slice(pending.next))
395
+ pendingShells = pendingPush(pendingShells, key, call);
396
+ }
397
+ return {
398
+ ...(pendingTools ? { pendingTools } : {}),
399
+ ...(pendingShells ? { pendingShells } : {}),
400
+ entryIndexes,
401
+ pairedToolCallByResult,
402
+ toolResultByCall,
403
+ };
404
+ }
405
+ function coldTailReadRun(history, pairing) {
406
+ let start = history.length;
407
+ while (start > 0) {
408
+ const item = history[start - 1];
409
+ if (item?.kind === 'tool' && item.call.name === 'Read') {
410
+ start -= 1;
411
+ continue;
412
+ }
413
+ if (item?.kind === 'tool-result' && !item.isError) {
414
+ const call = pairing.pairedToolCallByResult.get(start - 1);
415
+ if (call?.item.kind === 'tool' && call.item.call.name === 'Read') {
416
+ start -= 1;
417
+ continue;
418
+ }
419
+ }
420
+ break;
421
+ }
422
+ if (start >= history.length)
423
+ return undefined;
424
+ const readCalls = [];
425
+ for (let index = start; index < history.length; index += 1) {
426
+ const item = history[index];
427
+ if (item?.kind === 'tool' && item.call.name === 'Read') {
428
+ const result = pairing.toolResultByCall.get(index);
429
+ readCalls.push({
430
+ sourceIndex: index,
431
+ entry: {
432
+ kind: 'tool',
433
+ key: transcriptPresentationEntryKey(item, index),
434
+ item,
435
+ ...(result ? { result } : {}),
436
+ },
437
+ });
438
+ continue;
439
+ }
440
+ if (item?.kind === 'tool-result') {
441
+ const call = pairing.pairedToolCallByResult.get(index);
442
+ if (!call || call.sourceIndex < start || item.isError)
443
+ return undefined;
444
+ continue;
445
+ }
446
+ return undefined;
447
+ }
448
+ if (readCalls.length === 0)
449
+ return undefined;
450
+ const summarized = readCalls.every((call) => call.entry.result !== undefined);
451
+ const firstCall = readCalls[0];
452
+ if (!firstCall)
453
+ return undefined;
454
+ const targetKey = summarized
455
+ ? transcriptReadSummaryKey(start)
456
+ : firstCall.entry.key;
457
+ const startPresentationIndex = pairing.entryIndexes.get(targetKey);
458
+ if (startPresentationIndex === undefined)
459
+ return undefined;
460
+ return {
461
+ startSourceIndex: start,
462
+ startPresentationIndex,
463
+ nextSourceIndex: history.length,
464
+ calls: readCalls,
465
+ summarized,
466
+ };
467
+ }
468
+ function canReuseEntry(previous, next) {
469
+ if (previous.kind !== next.kind || previous.key !== next.key)
470
+ return false;
471
+ if (previous.kind === 'read-summary' && next.kind === 'read-summary')
472
+ return previous.count === next.count;
473
+ if (previous.kind === 'item' && next.kind === 'item')
474
+ return previous.item === next.item;
475
+ if (previous.kind === 'tool' && next.kind === 'tool')
476
+ return previous.item === next.item && previous.result === next.result;
477
+ if (previous.kind === 'shell' && next.kind === 'shell')
478
+ return previous.item === next.item && previous.result === next.result;
479
+ if (previous.kind === 'orphan-tool-result' &&
480
+ next.kind === 'orphan-tool-result')
481
+ return previous.item === next.item;
482
+ if (previous.kind === 'orphan-shell-result' &&
483
+ next.kind === 'orphan-shell-result')
484
+ return previous.item === next.item;
485
+ return false;
486
+ }
487
+ function reuseColdEntries(previous, entries) {
488
+ if (!previous)
489
+ return entries;
490
+ const byKey = new Map(treeEntries(previous.tree).map((entry) => [entry.key, entry]));
491
+ return entries.map((entry) => {
492
+ const prior = byKey.get(entry.key);
493
+ return prior && canReuseEntry(prior, entry) ? prior : entry;
494
+ });
495
+ }
496
+ function coldState(input, previous) {
497
+ const projected = projectTranscriptPresentation(input.history, input.mode);
498
+ const entries = reuseColdEntries(previous, projected);
499
+ const rows = entries.map((entry) => estimateTranscriptEntryLines(entry, input.width, input.mode));
500
+ const viewportIndexes = entries.map((entry, index) => {
501
+ const rowCount = rows[index] ?? 0;
502
+ if (rowCount <= VIEWPORT_INDEX_THRESHOLD)
503
+ return undefined;
504
+ const viewportIndex = createTranscriptEntryViewportIndex(entry, input.width, input.mode);
505
+ return viewportIndex?.rows.length === rowCount ? viewportIndex : undefined;
506
+ });
507
+ const pairing = coldPairing(input.history, entries);
508
+ const tailReadRun = input.mode === 'normal'
509
+ ? coldTailReadRun(input.history, pairing)
510
+ : undefined;
511
+ return {
512
+ history: input.history,
513
+ mode: input.mode,
514
+ width: input.width,
515
+ tree: buildTree(entries, rows, viewportIndexes),
516
+ ...(pairing.pendingTools ? { pendingTools: pairing.pendingTools } : {}),
517
+ ...(pairing.pendingShells ? { pendingShells: pairing.pendingShells } : {}),
518
+ ...(tailReadRun ? { tailReadRun } : {}),
519
+ revision: input.revision,
520
+ };
521
+ }
522
+ function entryForItem(item, sourceIndex) {
523
+ const key = transcriptPresentationEntryKey(item, sourceIndex);
524
+ if (item.kind === 'tool')
525
+ return { kind: 'tool', key, item };
526
+ if (item.kind === 'tool-result')
527
+ return { kind: 'orphan-tool-result', key, item };
528
+ if (item.kind === 'shell')
529
+ return { kind: 'shell', key, item };
530
+ if (item.kind === 'shell-result')
531
+ return { kind: 'orphan-shell-result', key, item };
532
+ return { kind: 'item', key, item };
533
+ }
534
+ function appendEntry(tree, entry, width, mode) {
535
+ const rowCount = estimateTranscriptEntryLines(entry, width, mode);
536
+ const viewportIndex = rowCount > VIEWPORT_INDEX_THRESHOLD
537
+ ? createTranscriptEntryViewportIndex(entry, width, mode)
538
+ : undefined;
539
+ return appendTree(tree, entry, rowCount, viewportIndex?.rows.length === rowCount ? viewportIndex : undefined);
540
+ }
541
+ function updateEntry(tree, index, entry, width, mode) {
542
+ const rowCount = estimateTranscriptEntryLines(entry, width, mode);
543
+ const viewportIndex = rowCount > VIEWPORT_INDEX_THRESHOLD
544
+ ? createTranscriptEntryViewportIndex(entry, width, mode)
545
+ : undefined;
546
+ return updateTree(tree, index, entry, rowCount, viewportIndex?.rows.length === rowCount ? viewportIndex : undefined);
547
+ }
548
+ function appendState(input, previous, change) {
549
+ const changedFrom = change.changedFrom;
550
+ const lineage = change[historyChangeLineage];
551
+ const previousLength = previous.history.length;
552
+ if (lineage?.history !== input.history ||
553
+ lineage.previousHistory !== previous.history ||
554
+ input.mode !== previous.mode ||
555
+ input.width !== previous.width ||
556
+ input.revision !== previous.revision + 1 ||
557
+ changedFrom !== previousLength ||
558
+ input.history.length <= previousLength ||
559
+ (previousLength > 0 &&
560
+ input.history[previousLength - 1] !==
561
+ previous.history[previousLength - 1]))
562
+ return undefined;
563
+ const suffix = input.history.slice(previousLength);
564
+ let tree = previous.tree;
565
+ let pendingTools = previous.pendingTools;
566
+ let pendingShells = previous.pendingShells;
567
+ let tailReadRun = previous.tailReadRun;
568
+ for (let offset = 0; offset < suffix.length; offset += 1) {
569
+ const sourceIndex = previousLength + offset;
570
+ const item = suffix[offset];
571
+ if (!item)
572
+ continue;
573
+ const pendingTool = item.kind === 'tool-result'
574
+ ? pendingFirst(pendingGet(pendingTools, item.callId))
575
+ : undefined;
576
+ const continuesReadRun = input.mode === 'normal' &&
577
+ tailReadRun?.nextSourceIndex === sourceIndex &&
578
+ ((item.kind === 'tool' && item.call.name === 'Read') ||
579
+ (item.kind === 'tool-result' &&
580
+ pendingTool?.item.kind === 'tool' &&
581
+ pendingTool.item.call.name === 'Read' &&
582
+ tailReadRun.calls.some((call) => call.sourceIndex === pendingTool.sourceIndex)));
583
+ if (!continuesReadRun)
584
+ tailReadRun = undefined;
585
+ if (item.kind === 'tool') {
586
+ if (item.call.name === 'Read' && input.mode === 'normal') {
587
+ if (tailReadRun?.summarized) {
588
+ tree = takeTree(tree, tailReadRun.startPresentationIndex);
589
+ for (const call of tailReadRun.calls)
590
+ tree = appendEntry(tree, call.entry, input.width, input.mode);
591
+ }
592
+ const entry = entryForItem(item, sourceIndex);
593
+ if (entry.kind !== 'tool')
594
+ return undefined;
595
+ const presentationIndex = tree.entryCount;
596
+ tree = appendEntry(tree, entry, input.width, input.mode);
597
+ pendingTools = pendingPush(pendingTools, item.call.id, {
598
+ sourceIndex,
599
+ presentationIndex,
600
+ item,
601
+ });
602
+ tailReadRun = tailReadRun
603
+ ? {
604
+ ...tailReadRun,
605
+ nextSourceIndex: sourceIndex + 1,
606
+ calls: [...tailReadRun.calls, { sourceIndex, entry }],
607
+ summarized: false,
608
+ }
609
+ : {
610
+ startSourceIndex: sourceIndex,
611
+ startPresentationIndex: presentationIndex,
612
+ nextSourceIndex: sourceIndex + 1,
613
+ calls: [{ sourceIndex, entry }],
614
+ summarized: false,
615
+ };
616
+ continue;
617
+ }
618
+ const entry = entryForItem(item, sourceIndex);
619
+ const presentationIndex = tree.entryCount;
620
+ tree = appendEntry(tree, entry, input.width, input.mode);
621
+ pendingTools = pendingPush(pendingTools, item.call.id, {
622
+ sourceIndex,
623
+ presentationIndex,
624
+ item,
625
+ });
626
+ continue;
627
+ }
628
+ if (item.kind === 'tool-result') {
629
+ const [pending, nextPendingTools] = pendingPop(pendingTools, item.callId);
630
+ pendingTools = nextPendingTools;
631
+ if (!pending) {
632
+ tree = appendEntry(tree, entryForItem(item, sourceIndex), input.width, input.mode);
633
+ continue;
634
+ }
635
+ const prior = treeAt(tree, pending.presentationIndex);
636
+ if (prior?.kind !== 'tool')
637
+ return undefined;
638
+ const next = { ...prior, result: item };
639
+ tree = updateEntry(tree, pending.presentationIndex, next, input.width, input.mode);
640
+ if (continuesReadRun && tailReadRun) {
641
+ const calls = tailReadRun.calls.map((call) => call.sourceIndex === pending.sourceIndex
642
+ ? { ...call, entry: next }
643
+ : call);
644
+ if (item.isError) {
645
+ tailReadRun = undefined;
646
+ }
647
+ else if (calls.every((call) => call.entry.result !== undefined)) {
648
+ tree = takeTree(tree, tailReadRun.startPresentationIndex);
649
+ tree = appendEntry(tree, {
650
+ kind: 'read-summary',
651
+ key: transcriptReadSummaryKey(tailReadRun.startSourceIndex),
652
+ count: calls.length,
653
+ }, input.width, input.mode);
654
+ tailReadRun = {
655
+ ...tailReadRun,
656
+ nextSourceIndex: sourceIndex + 1,
657
+ calls,
658
+ summarized: true,
659
+ };
660
+ }
661
+ else {
662
+ tailReadRun = {
663
+ ...tailReadRun,
664
+ nextSourceIndex: sourceIndex + 1,
665
+ calls,
666
+ summarized: false,
667
+ };
668
+ }
669
+ }
670
+ continue;
671
+ }
672
+ if (item.kind === 'shell') {
673
+ const entry = entryForItem(item, sourceIndex);
674
+ const presentationIndex = tree.entryCount;
675
+ tree = appendEntry(tree, entry, input.width, input.mode);
676
+ pendingShells = pendingPush(pendingShells, item.callId, {
677
+ sourceIndex,
678
+ presentationIndex,
679
+ item,
680
+ });
681
+ continue;
682
+ }
683
+ if (item.kind === 'shell-result') {
684
+ const [pending, nextPendingShells] = pendingPop(pendingShells, item.callId);
685
+ pendingShells = nextPendingShells;
686
+ if (!pending) {
687
+ tree = appendEntry(tree, entryForItem(item, sourceIndex), input.width, input.mode);
688
+ continue;
689
+ }
690
+ const prior = treeAt(tree, pending.presentationIndex);
691
+ if (prior?.kind !== 'shell')
692
+ return undefined;
693
+ const next = { ...prior, result: item };
694
+ tree = updateEntry(tree, pending.presentationIndex, next, input.width, input.mode);
695
+ continue;
696
+ }
697
+ tree = appendEntry(tree, entryForItem(item, sourceIndex), input.width, input.mode);
698
+ }
699
+ return {
700
+ history: input.history,
701
+ mode: input.mode,
702
+ width: input.width,
703
+ tree,
704
+ ...(pendingTools ? { pendingTools } : {}),
705
+ ...(pendingShells ? { pendingShells } : {}),
706
+ ...(tailReadRun ? { tailReadRun } : {}),
707
+ revision: input.revision,
708
+ };
709
+ }
710
+ function select(state, pageRows, offset, width, mode) {
711
+ const max = Math.max(0, state.tree.totalRows - pageRows);
712
+ const clamped = Math.min(Math.max(0, offset), max);
713
+ const end = state.tree.totalRows - clamped;
714
+ const start = Math.max(0, end - pageRows);
715
+ const candidates = collectRange(state.tree, start, end);
716
+ const selected = [];
717
+ for (const candidate of candidates) {
718
+ const candidateEnd = candidate.start + candidate.rows;
719
+ const overlapStart = Math.max(start, candidate.start);
720
+ const overlapEnd = Math.min(end, candidateEnd);
721
+ if (overlapStart >= overlapEnd)
722
+ continue;
723
+ const localStart = overlapStart - candidate.start;
724
+ const overlapRows = overlapEnd - overlapStart;
725
+ if (localStart === 0 && overlapRows === candidate.rows) {
726
+ selected.push(candidate.entry);
727
+ continue;
728
+ }
729
+ if (clamped === 0 && candidateEnd < end)
730
+ continue;
731
+ const projected = projectTranscriptEntryRange(candidate.entry, localStart, overlapRows, width, mode, candidate.viewportIndex);
732
+ const projectedRows = projected.viewportSlice?.rows ?? candidate.rows;
733
+ if (projectedRows <= overlapRows)
734
+ selected.push(projected);
735
+ }
736
+ return selected;
737
+ }
738
+ export function projectTranscriptWindow(input, previous, change) {
739
+ const validFacts = change !== undefined &&
740
+ change.revision === input.revision &&
741
+ Number.isInteger(input.revision) &&
742
+ input.revision >= 0 &&
743
+ Number.isInteger(change.changedFrom) &&
744
+ change.changedFrom >= 0 &&
745
+ change.changedFrom <= input.history.length;
746
+ const advanced = previous && validFacts && change
747
+ ? appendState(input, previous, change)
748
+ : undefined;
749
+ const same = !advanced &&
750
+ validFacts &&
751
+ previous !== undefined &&
752
+ input.revision === previous.revision &&
753
+ input.history === previous.history &&
754
+ input.mode === previous.mode &&
755
+ input.width === previous.width;
756
+ const state = advanced ?? (same ? previous : coldState(input, previous));
757
+ const transition = advanced
758
+ ? 'append'
759
+ : same
760
+ ? 'same'
761
+ : 'cold';
762
+ const totalRows = state.tree.totalRows;
763
+ const maxOffset = Math.max(0, totalRows - input.pageRows);
764
+ const allEntries = input.bounded ? [] : treeEntries(state.tree);
765
+ const entries = input.bounded
766
+ ? input.pageRows >= totalRows
767
+ ? treeEntries(state.tree)
768
+ : select(state, input.pageRows, input.scrollOffset, input.width, input.mode)
769
+ : allEntries;
770
+ return { state, allEntries, entries, totalRows, maxOffset, transition };
771
+ }
772
+ //# sourceMappingURL=transcript-window-model.js.map