mixdog 0.9.53 → 0.9.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/scripts/agent-model-liveness-test.mjs +68 -0
- package/scripts/anthropic-transport-policy-test.mjs +260 -0
- package/scripts/desktop-session-bridge-test.mjs +47 -0
- package/scripts/gemini-provider-test.mjs +396 -1
- package/scripts/grok-oauth-refresh-race-test.mjs +76 -1
- package/scripts/interrupted-turn-history-test.mjs +28 -0
- package/scripts/openai-oauth-ws-1006-retry-test.mjs +81 -1
- package/scripts/pending-completion-drop-test.mjs +160 -4
- package/scripts/pending-messages-lock-nonblocking-test.mjs +62 -0
- package/scripts/process-lifecycle-test.mjs +18 -4
- package/scripts/prompt-input-parity-test.mjs +145 -0
- package/scripts/provider-admission-scheduler-test.mjs +4 -5
- package/scripts/provider-contract-test.mjs +91 -33
- package/scripts/provider-toolcall-test.mjs +97 -17
- package/scripts/streaming-tail-window-test.mjs +146 -0
- package/scripts/tui-runtime-load-bench-entry.jsx +608 -0
- package/scripts/tui-runtime-load-bench.mjs +45 -0
- package/scripts/tui-store-frame-batch-test.mjs +99 -0
- package/scripts/tui-transcript-perf-test.mjs +367 -2
- package/scripts/write-backpressure-test.mjs +147 -0
- package/src/cli.mjs +5 -5
- package/src/lib/keychain-cjs.cjs +114 -25
- package/src/repl.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +62 -25
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +50 -23
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +15 -4
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +136 -27
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +104 -20
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +96 -75
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +40 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +35 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +49 -9
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +14 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +53 -13
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +145 -23
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +20 -4
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +316 -63
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +23 -1
- package/src/runtime/agent/orchestrator/session/store.mjs +46 -1
- package/src/runtime/agent/orchestrator/stall-policy.mjs +6 -13
- package/src/runtime/memory/lib/trace-store.mjs +66 -4
- package/src/runtime/shared/buffered-appender.mjs +83 -4
- package/src/runtime/shared/memory-snapshot.mjs +45 -36
- package/src/runtime/shared/process-lifecycle.mjs +166 -45
- package/src/runtime/shared/process-shutdown.mjs +2 -1
- package/src/session-runtime/model-route-api.mjs +4 -1
- package/src/session-runtime/native-search.mjs +4 -2
- package/src/session-runtime/provider-auth-api.mjs +8 -1
- package/src/session-runtime/provider-models.mjs +8 -3
- package/src/session-runtime/resource-api.mjs +2 -1
- package/src/session-runtime/runtime-core.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/warmup-schedulers.mjs +5 -1
- package/src/standalone/agent-tool.mjs +19 -1
- package/src/tui/App.jsx +10 -4
- package/src/tui/app/text-layout.mjs +9 -1
- package/src/tui/app/transcript-window.mjs +146 -60
- package/src/tui/app/use-mouse-input.mjs +16 -8
- package/src/tui/app/use-transcript-scroll.mjs +71 -19
- package/src/tui/app/use-transcript-window.mjs +21 -10
- package/src/tui/components/PromptInput.jsx +13 -6
- package/src/tui/dist/index.mjs +1094 -232
- package/src/tui/engine/context-state.mjs +31 -31
- package/src/tui/engine/frame-batched-store.mjs +75 -0
- package/src/tui/engine/session-api-ext.mjs +6 -1
- package/src/tui/engine/session-api.mjs +9 -3
- package/src/tui/engine/session-flow.mjs +54 -27
- package/src/tui/engine/turn.mjs +44 -3
- package/src/tui/engine.mjs +515 -36
- package/src/tui/input-editing.mjs +33 -13
- package/src/tui/markdown/measure-rendered-rows.mjs +117 -5
- package/vendor/ink/build/ink.js +8 -16
|
@@ -228,19 +228,39 @@ export function offsetAtCell(text, row, col, width) {
|
|
|
228
228
|
|
|
229
229
|
function boundaryPositions(text, width) {
|
|
230
230
|
const value = String(text || '');
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
231
|
+
const w = safeWidth(width);
|
|
232
|
+
const units = graphemeUnits(value);
|
|
233
|
+
const positions = [{ offset: 0, row: 0, col: 0 }];
|
|
234
|
+
let row = 0;
|
|
235
|
+
let col = 0;
|
|
236
|
+
|
|
237
|
+
// Build every grapheme-boundary position in one pass. This is the same state
|
|
238
|
+
// transition as caretPosition(), but avoids re-segmenting and re-scanning the
|
|
239
|
+
// entire prefix for every candidate during mouse hit-testing.
|
|
240
|
+
for (let i = 0; i < units.length; i += 1) {
|
|
241
|
+
const { end, segment } = units[i];
|
|
242
|
+
if (segment === '\n') {
|
|
243
|
+
row += 1;
|
|
244
|
+
col = 0;
|
|
245
|
+
} else {
|
|
246
|
+
const segmentWidth = displayWidth(segment);
|
|
247
|
+
if (segmentWidth > 0) {
|
|
248
|
+
if (col > 0 && col + segmentWidth > w) {
|
|
249
|
+
row += 1;
|
|
250
|
+
col = 0;
|
|
251
|
+
}
|
|
252
|
+
col += segmentWidth;
|
|
253
|
+
// Decide the flush-boundary advance from the FULL text so a soft-wrap
|
|
254
|
+
// offset reports {row N+1, col 0} (matching ink), exactly as
|
|
255
|
+
// caretPosition(value, end, w) does when more content follows.
|
|
256
|
+
if (col === w && i < units.length - 1) {
|
|
257
|
+
row += 1;
|
|
258
|
+
col = 0;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
positions.push({ offset: end, row, col });
|
|
263
|
+
}
|
|
244
264
|
return positions;
|
|
245
265
|
}
|
|
246
266
|
|
|
@@ -7,6 +7,23 @@ import { assistantBodyWidth, measureMarkdownTableRows } from './table-layout.mjs
|
|
|
7
7
|
import { renderTokenAnsiSegments } from './render-ansi.mjs';
|
|
8
8
|
import { resolveStreamingMarkdownParts } from './streaming-markdown.mjs';
|
|
9
9
|
|
|
10
|
+
// One latest measurement per live stream. The small LRU bound mirrors the
|
|
11
|
+
// streaming-markdown split caches: completed/abandoned stream ids cannot make
|
|
12
|
+
// this module retain an unbounded response history.
|
|
13
|
+
const streamingRowsByKey = new Map();
|
|
14
|
+
const STREAMING_ROWS_LRU_MAX = 32;
|
|
15
|
+
|
|
16
|
+
function cacheStreamingRows(key, entry) {
|
|
17
|
+
if (!key) return;
|
|
18
|
+
if (streamingRowsByKey.has(key)) streamingRowsByKey.delete(key);
|
|
19
|
+
streamingRowsByKey.set(key, entry);
|
|
20
|
+
while (streamingRowsByKey.size > STREAMING_ROWS_LRU_MAX) {
|
|
21
|
+
const oldest = streamingRowsByKey.keys().next().value;
|
|
22
|
+
if (oldest === undefined) break;
|
|
23
|
+
streamingRowsByKey.delete(oldest);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
10
27
|
function wrappedLineRows(line, width) {
|
|
11
28
|
const text = String(line);
|
|
12
29
|
const full = displayWidth(text);
|
|
@@ -62,17 +79,104 @@ export function measureMarkdownRenderedRows(text, columns, { trimPartialFences =
|
|
|
62
79
|
return Math.max(1, rows);
|
|
63
80
|
}
|
|
64
81
|
|
|
65
|
-
|
|
82
|
+
function measureStreamingPartsUncached(parts, columns) {
|
|
83
|
+
if (parts.plain) {
|
|
84
|
+
return estimateWrappedRowsFallback(parts.unstableForRender, columns, 3);
|
|
85
|
+
}
|
|
86
|
+
let rows = 0;
|
|
87
|
+
let childCount = 0;
|
|
88
|
+
if (parts.stablePrefix) {
|
|
89
|
+
rows += measureMarkdownRenderedRows(parts.stablePrefix, columns, { trimPartialFences: false });
|
|
90
|
+
childCount += 1;
|
|
91
|
+
}
|
|
92
|
+
if (parts.unstableSuffix) {
|
|
93
|
+
rows += measureMarkdownRenderedRows(parts.unstableForRender, columns, { trimPartialFences: true });
|
|
94
|
+
childCount += 1;
|
|
95
|
+
}
|
|
96
|
+
if (childCount === 2) rows += 1;
|
|
97
|
+
return childCount === 0 ? 1 : Math.max(1, rows);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Test/reference path: resolve the same renderer split, but deliberately bypass
|
|
101
|
+
// streamingRowsByKey and remeasure every rendered child from scratch.
|
|
102
|
+
export function measureStreamingMarkdownRenderedRowsUncached(text, columns, streamKey) {
|
|
66
103
|
const value = String(text ?? '');
|
|
67
104
|
if (!value) return 1;
|
|
105
|
+
return measureStreamingPartsUncached(resolveStreamingMarkdownParts(value, streamKey), columns);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function measureStreamingMarkdownRenderedRows(text, columns, streamKey) {
|
|
109
|
+
const value = String(text ?? '');
|
|
110
|
+
const key = streamKey == null || streamKey === '' ? null : String(streamKey);
|
|
111
|
+
const cached = key ? streamingRowsByKey.get(key) : null;
|
|
112
|
+
// Check the exact render inputs before even resolving markdown parts. App
|
|
113
|
+
// renders caused by typing or overlays therefore do no wrapping/token work
|
|
114
|
+
// when the live tail itself did not change.
|
|
115
|
+
if (cached && cached.text === value && cached.columns === columns) {
|
|
116
|
+
cacheStreamingRows(key, cached);
|
|
117
|
+
return cached.rows;
|
|
118
|
+
}
|
|
119
|
+
if (!value) {
|
|
120
|
+
cacheStreamingRows(key, {
|
|
121
|
+
text: value,
|
|
122
|
+
columns,
|
|
123
|
+
mode: 'empty',
|
|
124
|
+
rows: 1,
|
|
125
|
+
});
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
68
128
|
const parts = resolveStreamingMarkdownParts(value, streamKey);
|
|
69
129
|
if (parts.plain) {
|
|
70
|
-
|
|
130
|
+
const plain = parts.unstableForRender;
|
|
131
|
+
const width = Math.max(8, Number(columns || 80) - 3);
|
|
132
|
+
const lastBreak = plain.lastIndexOf('\n');
|
|
133
|
+
const stablePrefix = lastBreak >= 0 ? plain.substring(0, lastBreak + 1) : '';
|
|
134
|
+
let stableRows = 0;
|
|
135
|
+
if (cached
|
|
136
|
+
&& cached.mode === 'plain'
|
|
137
|
+
&& cached.columns === columns
|
|
138
|
+
&& stablePrefix.startsWith(cached.stablePrefix)) {
|
|
139
|
+
// Lines preceding the currently-growing final line wrap independently.
|
|
140
|
+
// Measure only complete lines added since the previous split.
|
|
141
|
+
const addedComplete = plain.substring(cached.stablePrefix.length, Math.max(cached.stablePrefix.length, lastBreak));
|
|
142
|
+
stableRows = cached.stableRows;
|
|
143
|
+
if (addedComplete) {
|
|
144
|
+
stableRows += addedComplete
|
|
145
|
+
.split('\n')
|
|
146
|
+
.reduce((sum, line) => sum + wrappedLineRows(line, width), 0);
|
|
147
|
+
}
|
|
148
|
+
} else if (lastBreak >= 0) {
|
|
149
|
+
stableRows = plain
|
|
150
|
+
.substring(0, lastBreak)
|
|
151
|
+
.split('\n')
|
|
152
|
+
.reduce((sum, line) => sum + wrappedLineRows(line, width), 0);
|
|
153
|
+
}
|
|
154
|
+
const finalLine = lastBreak >= 0 ? plain.substring(lastBreak + 1) : plain;
|
|
155
|
+
const rows = Math.max(1, stableRows + wrappedLineRows(finalLine, width));
|
|
156
|
+
cacheStreamingRows(key, {
|
|
157
|
+
text: value,
|
|
158
|
+
columns,
|
|
159
|
+
mode: 'plain',
|
|
160
|
+
stablePrefix,
|
|
161
|
+
stableRows,
|
|
162
|
+
rows,
|
|
163
|
+
});
|
|
164
|
+
return rows;
|
|
71
165
|
}
|
|
72
166
|
let rows = 0;
|
|
73
167
|
let childCount = 0;
|
|
168
|
+
let stableRows = 0;
|
|
74
169
|
if (parts.stablePrefix) {
|
|
75
|
-
|
|
170
|
+
// The renderer mounts stablePrefix and unstableSuffix as separate Markdown
|
|
171
|
+
// children. An unchanged stable child is therefore safe to reuse exactly;
|
|
172
|
+
// only the independently-rendered live suffix needs measuring on append.
|
|
173
|
+
stableRows = cached
|
|
174
|
+
&& cached.mode === 'markdown'
|
|
175
|
+
&& cached.columns === columns
|
|
176
|
+
&& cached.stablePrefix === parts.stablePrefix
|
|
177
|
+
? cached.stableRows
|
|
178
|
+
: measureMarkdownRenderedRows(parts.stablePrefix, columns, { trimPartialFences: false });
|
|
179
|
+
rows += stableRows;
|
|
76
180
|
childCount += 1;
|
|
77
181
|
}
|
|
78
182
|
if (parts.unstableSuffix) {
|
|
@@ -80,6 +184,14 @@ export function measureStreamingMarkdownRenderedRows(text, columns, streamKey) {
|
|
|
80
184
|
childCount += 1;
|
|
81
185
|
}
|
|
82
186
|
if (childCount === 2) rows += 1;
|
|
83
|
-
|
|
84
|
-
|
|
187
|
+
const measuredRows = childCount === 0 ? 1 : Math.max(1, rows);
|
|
188
|
+
cacheStreamingRows(key, {
|
|
189
|
+
text: value,
|
|
190
|
+
columns,
|
|
191
|
+
mode: 'markdown',
|
|
192
|
+
stablePrefix: parts.stablePrefix,
|
|
193
|
+
stableRows,
|
|
194
|
+
rows: measuredRows,
|
|
195
|
+
});
|
|
196
|
+
return measuredRows;
|
|
85
197
|
}
|
package/vendor/ink/build/ink.js
CHANGED
|
@@ -403,7 +403,7 @@ export default class Ink {
|
|
|
403
403
|
// [mixdog fork] Update the mouse drag-selection rectangle and repaint so the
|
|
404
404
|
// inverse highlight tracks the drag. Called by the App's mouse handler.
|
|
405
405
|
// A no-op-equal update is skipped to avoid redundant frames during motion.
|
|
406
|
-
setSelection = (rect
|
|
406
|
+
setSelection = (rect) => {
|
|
407
407
|
const a = this.selectionRect;
|
|
408
408
|
const same = a === rect ||
|
|
409
409
|
(a && rect &&
|
|
@@ -417,21 +417,10 @@ export default class Ink {
|
|
|
417
417
|
a.captureText === rect.captureText &&
|
|
418
418
|
a.selectionForeground === rect.selectionForeground &&
|
|
419
419
|
a.selectionBackground === rect.selectionBackground);
|
|
420
|
-
if (same)
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
this.selectionRect = rect ?? null;
|
|
427
|
-
}
|
|
420
|
+
if (same)
|
|
421
|
+
return;
|
|
422
|
+
this.selectionRect = rect ?? null;
|
|
428
423
|
if (!this.isUnmounted) {
|
|
429
|
-
if (options.immediate) {
|
|
430
|
-
this.selectionRepaintEpoch++;
|
|
431
|
-
this.selectionRepaintFlushPending = false;
|
|
432
|
-
this.rootNode.onImmediateRender();
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
424
|
this.scheduleSelectionRepaint();
|
|
436
425
|
}
|
|
437
426
|
};
|
|
@@ -456,7 +445,10 @@ export default class Ink {
|
|
|
456
445
|
if (this.hasPendingThrottledRender) {
|
|
457
446
|
return;
|
|
458
447
|
}
|
|
459
|
-
|
|
448
|
+
// Selection motion must share Ink's normal maxFps throttle; the old
|
|
449
|
+
// unthrottled callback regenerated a whole frame for every accepted
|
|
450
|
+
// drag update even though selection updates are coalesced.
|
|
451
|
+
this.rootNode.onRender();
|
|
460
452
|
});
|
|
461
453
|
};
|
|
462
454
|
// [mixdog fork] Given a 0-based cell (x, y), return the inclusive rect of the
|