tmux-ide 2.9.11 → 2.9.12
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/bin/cli.js +4158 -2545
- package/package.json +1 -1
- package/packages/contracts/src/index.ts +4 -0
- package/packages/contracts/src/pane-stream.ts +60 -0
- package/packages/contracts/src/terminal-delivery-binary-chunk.ts +55 -0
- package/packages/contracts/src/terminal-delivery.ts +17 -2
- package/packages/contracts/src/terminal-graphics.ts +68 -0
- package/packages/contracts/src/terminal-projection.ts +153 -0
- package/packages/core/src/fixtures/terminal-projection-vectors.json +585 -0
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/terminal-delivery-binary.ts +160 -0
- package/packages/core/src/terminal-delivery.ts +371 -16
- package/packages/core/src/terminal-projection-vectors.mjs +82 -0
- package/packages/core/src/terminal-projection.ts +476 -0
- package/packages/core/src/terminal-replica.ts +1 -1
- package/packages/daemon/dist/lib/daemon-embed.js +39 -3
- package/packages/daemon/dist/lib/embedded-tmux-server-owners.js +1 -0
- package/packages/daemon/dist/lib/tmux-server-owner.js +1 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Info.plist +2 -2
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/MacOS/tmux-ide-notifier +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/Resources/Assets.car +0 -0
- package/packages/daemon/dist/native/TmuxIdeNotifier.app/Contents/_CodeSignature/CodeResources +2 -2
- package/packages/daemon/dist/terminal/graphics/local-image-owner.js +330 -0
- package/packages/daemon/dist/terminal/graphics/local-image-store.js +255 -0
- package/packages/daemon/dist/terminal/pane-stream/pane-stream-websocket.js +176 -16
- package/packages/daemon/dist/terminal/pane-stream/runtime.js +1 -0
- package/packages/daemon/dist/terminal/session-runtime/registry.js +2 -0
- package/packages/daemon/dist/terminal/session-runtime/terminal-delivery-hub.js +146 -16
- package/packages/daemon/dist/terminal/session-runtime/terminal-projection-delivery.js +32 -0
- package/packages/daemon/dist/terminal/session-runtime/terminal-replica-owner.js +3 -4
- package/packages/daemon/dist/tui/mirror/open-tui-workspace-runtime-port.js +4 -0
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-interaction-controller.js +28 -3
- package/packages/daemon/dist/tui/mirror/runtime/application-terminal-workspace.jsx +13 -1
- package/packages/daemon/dist/tui/mirror/runtime/renderer-probe.js +195 -0
- package/packages/daemon/dist/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.js +6 -0
- package/packages/daemon/dist/tui/mirror/semantic-pane-render-source.js +4 -0
- package/packages/daemon/src/lib/daemon-embed.ts +38 -2
- package/packages/daemon/src/lib/embedded-tmux-server-owners.ts +3 -0
- package/packages/daemon/src/lib/tmux-server-owner.ts +3 -0
- package/packages/daemon/src/terminal/graphics/local-image-owner.ts +346 -0
- package/packages/daemon/src/terminal/graphics/local-image-store.ts +286 -0
- package/packages/daemon/src/terminal/pane-stream/pane-stream-websocket.ts +219 -15
- package/packages/daemon/src/terminal/pane-stream/runtime.ts +3 -0
- package/packages/daemon/src/terminal/session-runtime/registry.ts +2 -0
- package/packages/daemon/src/terminal/session-runtime/runtime-observability.ts +1 -1
- package/packages/daemon/src/terminal/session-runtime/terminal-delivery-hub.ts +216 -22
- package/packages/daemon/src/terminal/session-runtime/terminal-delivery-observation-identity.ts +7 -4
- package/packages/daemon/src/terminal/session-runtime/terminal-projection-delivery.ts +59 -0
- package/packages/daemon/src/terminal/session-runtime/terminal-replica-owner.ts +3 -4
- package/packages/daemon/src/tui/mirror/open-tui-workspace-runtime-port.ts +4 -0
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-interaction-controller.ts +26 -4
- package/packages/daemon/src/tui/mirror/runtime/application-terminal-workspace.tsx +20 -1
- package/packages/daemon/src/tui/mirror/runtime/renderer-probe.ts +257 -0
- package/packages/daemon/src/tui/mirror/runtime/terminal-fast-lane-renderer-adapter.ts +15 -0
- package/packages/daemon/src/tui/mirror/semantic-pane-render-source.ts +4 -0
- package/packages/daemon-client/src/pane-stream-client.ts +75 -1
|
@@ -1,5 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TERMINAL_PROJECTION_CAPABILITY_VERSION,
|
|
3
|
+
TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION,
|
|
4
|
+
type TerminalProjectionReceipt,
|
|
5
|
+
type TerminalProjectionMaterialize,
|
|
6
|
+
} from "@tmux-ide/contracts";
|
|
7
|
+
import type { LocalImageOwner } from "../graphics/local-image-owner.ts";
|
|
1
8
|
import {
|
|
2
9
|
PANE_STREAM_INPUT_LIFETIME_CAPABILITY_VERSION,
|
|
10
|
+
PANE_STREAM_INPUT_BYTES_CAPABILITY_VERSION,
|
|
3
11
|
type PaneStreamInputLifetime,
|
|
4
12
|
} from "@tmux-ide/contracts";
|
|
5
13
|
import { inflateSync } from "node:zlib";
|
|
@@ -15,6 +23,12 @@ import {
|
|
|
15
23
|
PANE_STREAM_REDEEM_PATH,
|
|
16
24
|
PANE_STREAM_WEBSOCKET_SUBPROTOCOL,
|
|
17
25
|
TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES,
|
|
26
|
+
TERMINAL_DELIVERY_BINARY_CAPABILITY_VERSION,
|
|
27
|
+
TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION,
|
|
28
|
+
TERMINAL_GRAPHICS_BINARY_CAPABILITY_VERSION,
|
|
29
|
+
TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION,
|
|
30
|
+
encodeTerminalGraphicsBinary,
|
|
31
|
+
encodeTerminalDeliveryBinaryChunk,
|
|
18
32
|
TERMINAL_GRAPHICS_CELL_PIXELS_CAPABILITY_VERSION,
|
|
19
33
|
TERMINAL_GRAPHICS_PRECISE_PIXELS_CAPABILITY_VERSION,
|
|
20
34
|
TERMINAL_GRAPHICS_LEGACY_MAX_IMAGE_BYTES,
|
|
@@ -245,6 +259,7 @@ export interface PaneStreamAdmissionSnapshot {
|
|
|
245
259
|
}
|
|
246
260
|
|
|
247
261
|
export interface PaneStreamAdmissionCoordinatorOptions {
|
|
262
|
+
readonly localImages?: LocalImageOwner;
|
|
248
263
|
readonly daemonInstanceId: string;
|
|
249
264
|
readonly webSocketUrl: string;
|
|
250
265
|
readonly leaseManager: PaneStreamLeaseAuthority;
|
|
@@ -326,6 +341,8 @@ export interface SessionRuntimeTerminalDeliveryConnection {
|
|
|
326
341
|
readonly closed?: Promise<"closed" | "sink-failed">;
|
|
327
342
|
readonly negotiation: TerminalDeliveryNegotiationResult;
|
|
328
343
|
ack(ack: TerminalDeliveryAck): void;
|
|
344
|
+
projectionReceipt?(receipt: TerminalProjectionReceipt): void;
|
|
345
|
+
materialize?(request: TerminalProjectionMaterialize): void;
|
|
329
346
|
nack(nack: TerminalDeliveryNack): void;
|
|
330
347
|
setVisibility(visibility: TerminalDeliveryVisibility): void;
|
|
331
348
|
close(): Promise<void>;
|
|
@@ -388,6 +405,7 @@ function errorFrameCode(error: unknown): PaneStreamErrorFrameCode {
|
|
|
388
405
|
* ticket digests; a daemon restart makes every prior descriptor inert.
|
|
389
406
|
*/
|
|
390
407
|
export class PaneStreamAdmissionCoordinator {
|
|
408
|
+
readonly #localImages: LocalImageOwner | undefined;
|
|
391
409
|
readonly #instanceId: string;
|
|
392
410
|
readonly #webSocketUrl: string;
|
|
393
411
|
readonly #leaseManager: PaneStreamLeaseAuthority;
|
|
@@ -421,6 +439,7 @@ export class PaneStreamAdmissionCoordinator {
|
|
|
421
439
|
#shutdownPromise: Promise<void> | null = null;
|
|
422
440
|
|
|
423
441
|
constructor(options: PaneStreamAdmissionCoordinatorOptions) {
|
|
442
|
+
this.#localImages = options.localImages;
|
|
424
443
|
this.#instanceId = BindingIdSchemaZ.parse(options.daemonInstanceId);
|
|
425
444
|
this.#webSocketUrl = PaneStreamLoopbackWebSocketUrlSchemaZ.parse(options.webSocketUrl);
|
|
426
445
|
this.#leaseManager = options.leaseManager;
|
|
@@ -792,6 +811,7 @@ export class PaneStreamAdmissionCoordinator {
|
|
|
792
811
|
let live: PaneStreamLiveConnection;
|
|
793
812
|
try {
|
|
794
813
|
live = new PaneStreamLiveConnection({
|
|
814
|
+
localImages: this.#localImages,
|
|
795
815
|
clientId: `psc-${this.#clientCounter}`,
|
|
796
816
|
socket,
|
|
797
817
|
descriptor: structuredClone(descriptor),
|
|
@@ -1010,6 +1030,7 @@ class PreAuthAdmission implements PaneStreamPreAuthAdmission {
|
|
|
1010
1030
|
}
|
|
1011
1031
|
|
|
1012
1032
|
interface LiveConnectionOptions {
|
|
1033
|
+
readonly localImages?: LocalImageOwner;
|
|
1013
1034
|
readonly clientId: string;
|
|
1014
1035
|
readonly socket: DirectTerminalSocket;
|
|
1015
1036
|
readonly descriptor: PaneStreamLeaseDescriptor;
|
|
@@ -1066,6 +1087,10 @@ interface PaneChannel {
|
|
|
1066
1087
|
graphicsServed: Map<number, string>;
|
|
1067
1088
|
graphicsState: TerminalGraphicsState | null;
|
|
1068
1089
|
graphicsZlib?: boolean;
|
|
1090
|
+
graphicsBinary?: boolean;
|
|
1091
|
+
binaryCells?: boolean;
|
|
1092
|
+
graphicsLocal?: boolean;
|
|
1093
|
+
graphicsInline?: Set<string>;
|
|
1069
1094
|
}
|
|
1070
1095
|
|
|
1071
1096
|
interface QueuedSend {
|
|
@@ -1097,6 +1122,8 @@ export function rawGraphicsState(state: TerminalGraphicsState): TerminalGraphics
|
|
|
1097
1122
|
const GRAPHICS_SEND_WINDOW_BYTES = 2 * 1024 * 1024;
|
|
1098
1123
|
|
|
1099
1124
|
export class PaneStreamLiveConnection {
|
|
1125
|
+
readonly #localImages: LocalImageOwner | undefined;
|
|
1126
|
+
readonly #localLeases = new Map<string, { pane: string; expiresAt: number }>();
|
|
1100
1127
|
readonly #clientId: string;
|
|
1101
1128
|
readonly #socket: DirectTerminalSocket;
|
|
1102
1129
|
readonly #descriptor: PaneStreamLeaseDescriptor;
|
|
@@ -1221,6 +1248,7 @@ export class PaneStreamLiveConnection {
|
|
|
1221
1248
|
}
|
|
1222
1249
|
|
|
1223
1250
|
constructor(options: LiveConnectionOptions) {
|
|
1251
|
+
this.#localImages = options.localImages;
|
|
1224
1252
|
this.#clientId = options.clientId;
|
|
1225
1253
|
this.#socket = options.socket;
|
|
1226
1254
|
this.#descriptor = options.descriptor;
|
|
@@ -1294,6 +1322,12 @@ export class PaneStreamLiveConnection {
|
|
|
1294
1322
|
)
|
|
1295
1323
|
? { inputLifetime: true as const }
|
|
1296
1324
|
: {}),
|
|
1325
|
+
...(this.#sessionRuntimeBinding &&
|
|
1326
|
+
this.#descriptor.terminalDelivery?.protocolVersions.includes(
|
|
1327
|
+
PANE_STREAM_INPUT_BYTES_CAPABILITY_VERSION,
|
|
1328
|
+
)
|
|
1329
|
+
? { inputBytes: true as const }
|
|
1330
|
+
: {}),
|
|
1297
1331
|
protocolVersion: PANE_STREAM_PROTOCOL_VERSION,
|
|
1298
1332
|
daemonInstanceId: this.#binding.daemonInstanceId,
|
|
1299
1333
|
requestId: this.#binding.requestId,
|
|
@@ -1336,6 +1370,14 @@ export class PaneStreamLiveConnection {
|
|
|
1336
1370
|
this.#stopAuthoritySnapshots?.();
|
|
1337
1371
|
this.#stopAuthoritySnapshots = null;
|
|
1338
1372
|
const closures: Promise<unknown>[] = [];
|
|
1373
|
+
this.#localLeases.clear();
|
|
1374
|
+
if (this.#localImages)
|
|
1375
|
+
closures.push(
|
|
1376
|
+
this.#localImages.releaseClient({
|
|
1377
|
+
generation: this.#localImages.identity.generation,
|
|
1378
|
+
client: this.#descriptor.leaseId,
|
|
1379
|
+
}),
|
|
1380
|
+
);
|
|
1339
1381
|
const layoutSubscription = this.#layoutSubscription;
|
|
1340
1382
|
this.#layoutSubscription = null;
|
|
1341
1383
|
if (layoutSubscription) closures.push(layoutSubscription.close().catch(() => undefined));
|
|
@@ -1564,7 +1606,8 @@ export class PaneStreamLiveConnection {
|
|
|
1564
1606
|
if (negotiated.generation !== this.#binding.daemonInstanceId) return true;
|
|
1565
1607
|
let incarnation: string | null = null;
|
|
1566
1608
|
for (const message of pending) {
|
|
1567
|
-
if (message.type !== "terminal.delivery")
|
|
1609
|
+
if (message.type !== "terminal.delivery" && message.type !== "terminal.projection")
|
|
1610
|
+
continue;
|
|
1568
1611
|
if (
|
|
1569
1612
|
message.workspaceName !== this.#descriptor.workspaceName ||
|
|
1570
1613
|
message.semanticPaneId !== channel.semanticPaneId ||
|
|
@@ -1686,13 +1729,19 @@ export class PaneStreamLiveConnection {
|
|
|
1686
1729
|
}
|
|
1687
1730
|
|
|
1688
1731
|
async #sendTerminalDelivery(pane: string, message: TerminalDeliveryServerMessage): Promise<void> {
|
|
1689
|
-
if (message.type === "terminal.
|
|
1732
|
+
if (message.type === "terminal.projection") {
|
|
1733
|
+
const channel = this.#panes.get(pane);
|
|
1734
|
+
if (channel?.deliveryAddress) channel.deliveryAddress.incarnation = message.incarnation;
|
|
1735
|
+
if (channel) channel.binaryCells = true;
|
|
1736
|
+
this.#sendFrame(pane, { type: "terminal-projection-envelope", pane, envelope: message });
|
|
1737
|
+
} else if (message.type === "terminal.delivery") {
|
|
1690
1738
|
if (!this.#diagnosticFirstSeedObserved) {
|
|
1691
1739
|
this.#diagnosticFirstSeedObserved = true;
|
|
1692
1740
|
this.#recordDiagnosticLifecycle("pane-stream-first-seed");
|
|
1693
1741
|
}
|
|
1694
1742
|
const channel = this.#panes.get(pane);
|
|
1695
1743
|
if (channel?.deliveryAddress) channel.deliveryAddress.incarnation = message.incarnation;
|
|
1744
|
+
if (channel) channel.binaryCells = message.encoding === "semantic-binary-v1";
|
|
1696
1745
|
// The trusted lease address was fixed at delivery admission; preserve the
|
|
1697
1746
|
// exact envelope and its observation identity through transmission.
|
|
1698
1747
|
const detailedDeliveryObservation = this.#observability?.enabled === true;
|
|
@@ -1775,13 +1824,26 @@ export class PaneStreamLiveConnection {
|
|
|
1775
1824
|
}
|
|
1776
1825
|
}
|
|
1777
1826
|
} else if (message.type === "terminal.delivery.chunk") {
|
|
1778
|
-
this.#
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1827
|
+
if (this.#panes.get(pane)?.binaryCells) {
|
|
1828
|
+
this.#sendEncoded(
|
|
1829
|
+
pane,
|
|
1830
|
+
Buffer.from(
|
|
1831
|
+
encodeTerminalDeliveryBinaryChunk(
|
|
1832
|
+
{ pane, transactionId: message.transactionId, index: message.index },
|
|
1833
|
+
message.bytes,
|
|
1834
|
+
),
|
|
1835
|
+
),
|
|
1836
|
+
true,
|
|
1837
|
+
);
|
|
1838
|
+
} else {
|
|
1839
|
+
this.#sendFrame(pane, {
|
|
1840
|
+
type: "terminal-delivery-chunk",
|
|
1841
|
+
pane,
|
|
1842
|
+
transactionId: message.transactionId,
|
|
1843
|
+
index: message.index,
|
|
1844
|
+
data: Buffer.from(message.bytes).toString("base64"),
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1785
1847
|
} else {
|
|
1786
1848
|
this.#sendFrame(pane, { type: "terminal-delivery-fault", pane, fault: message });
|
|
1787
1849
|
// Lifecycle/control truth must never queue behind bulk terminal bytes.
|
|
@@ -1803,6 +1865,25 @@ export class PaneStreamLiveConnection {
|
|
|
1803
1865
|
TERMINAL_GRAPHICS_ZLIB_CAPABILITY_VERSION,
|
|
1804
1866
|
) === true;
|
|
1805
1867
|
channel.graphicsZlib = zlib;
|
|
1868
|
+
channel.graphicsLocal =
|
|
1869
|
+
!!this.#localImages &&
|
|
1870
|
+
this.#descriptor.terminalDelivery?.protocolVersions.some(
|
|
1871
|
+
(version) =>
|
|
1872
|
+
version === TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION ||
|
|
1873
|
+
version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
|
|
1874
|
+
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION,
|
|
1875
|
+
) === true;
|
|
1876
|
+
channel.graphicsInline = new Set();
|
|
1877
|
+
channel.graphicsBinary =
|
|
1878
|
+
this.#descriptor.terminalDelivery?.protocolVersions.some(
|
|
1879
|
+
(version) =>
|
|
1880
|
+
version === TERMINAL_GRAPHICS_BINARY_CAPABILITY_VERSION ||
|
|
1881
|
+
version === TERMINAL_GRAPHICS_LOCAL_CAPABILITY_VERSION ||
|
|
1882
|
+
version === TERMINAL_DELIVERY_BINARY_CAPABILITY_VERSION ||
|
|
1883
|
+
version === TERMINAL_PROJECTION_CAPABILITY_VERSION ||
|
|
1884
|
+
version === TERMINAL_DELIVERY_BINARY_LOCAL_CAPABILITY_VERSION ||
|
|
1885
|
+
version === TERMINAL_PROJECTION_LOCAL_CAPABILITY_VERSION,
|
|
1886
|
+
) === true;
|
|
1806
1887
|
// Renderers that report cell pixels learn up front that this daemon
|
|
1807
1888
|
// accepts them, before any image exists.
|
|
1808
1889
|
const cellPixels =
|
|
@@ -1834,6 +1915,8 @@ export class PaneStreamLiveConnection {
|
|
|
1834
1915
|
...(zlib ? state : rawGraphicsState(state)),
|
|
1835
1916
|
...(cellPixels ? { cellPixels: true as const } : {}),
|
|
1836
1917
|
...(preciseCellPixels ? { preciseCellPixels: true as const } : {}),
|
|
1918
|
+
...(channel.graphicsBinary ? { binaryBlobs: true as const } : {}),
|
|
1919
|
+
...(channel.graphicsLocal ? { localFiles: true as const } : {}),
|
|
1837
1920
|
},
|
|
1838
1921
|
});
|
|
1839
1922
|
if (pushed.length > 0) this.#queueGraphics(channel, pushed);
|
|
@@ -1849,15 +1932,23 @@ export class PaneStreamLiveConnection {
|
|
|
1849
1932
|
placements: [],
|
|
1850
1933
|
cellPixels: true,
|
|
1851
1934
|
...(preciseCellPixels ? { preciseCellPixels: true as const } : {}),
|
|
1935
|
+
...(channel.graphicsBinary ? { binaryBlobs: true as const } : {}),
|
|
1936
|
+
...(channel.graphicsLocal ? { localFiles: true as const } : {}),
|
|
1852
1937
|
},
|
|
1853
1938
|
});
|
|
1854
1939
|
}
|
|
1855
1940
|
}
|
|
1856
1941
|
|
|
1857
|
-
#requestGraphics(pane: string, digests: readonly string[]): void {
|
|
1942
|
+
#requestGraphics(pane: string, digests: readonly string[], preferInline = false): void {
|
|
1858
1943
|
const channel = this.#panes.get(pane);
|
|
1859
1944
|
// Requests are only meaningful after an announcement; ignore stray ones.
|
|
1860
1945
|
if (!channel || channel.closed || !channel.graphicsAnnounced) return;
|
|
1946
|
+
if (preferInline && channel.graphicsLocal) {
|
|
1947
|
+
for (const digest of digests) {
|
|
1948
|
+
if ((channel.graphicsInline?.size ?? 0) < GRAPHICS_MAX_QUEUED_DIGESTS)
|
|
1949
|
+
channel.graphicsInline?.add(digest);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1861
1952
|
this.#queueGraphics(channel, digests);
|
|
1862
1953
|
}
|
|
1863
1954
|
|
|
@@ -1881,6 +1972,7 @@ export class PaneStreamLiveConnection {
|
|
|
1881
1972
|
try {
|
|
1882
1973
|
while (!this.#closed && !channel.closed && channel.graphicsQueue.length > 0) {
|
|
1883
1974
|
const digest = channel.graphicsQueue.shift()!;
|
|
1975
|
+
const inline = channel.graphicsInline?.delete(digest) ?? false;
|
|
1884
1976
|
const graphics = channel.delivery?.graphics;
|
|
1885
1977
|
const image = graphics?.image(digest);
|
|
1886
1978
|
if (!graphics || !image) continue;
|
|
@@ -1902,6 +1994,47 @@ export class PaneStreamLiveConnection {
|
|
|
1902
1994
|
continue;
|
|
1903
1995
|
const format = raw ? (image.format === "rgba-zlib" ? "rgba" : "rgb") : image.format;
|
|
1904
1996
|
const total = data.byteLength;
|
|
1997
|
+
if (!inline && channel.graphicsLocal && !raw && this.#localImages) {
|
|
1998
|
+
for (const [id, lease] of this.#localLeases)
|
|
1999
|
+
if (lease.expiresAt <= Date.now()) this.#localLeases.delete(id);
|
|
2000
|
+
if (this.#localLeases.size < 64) {
|
|
2001
|
+
try {
|
|
2002
|
+
const lease = await this.#localImages.acquire({
|
|
2003
|
+
generation: this.#localImages.identity.generation,
|
|
2004
|
+
client: this.#descriptor.leaseId,
|
|
2005
|
+
digest,
|
|
2006
|
+
bytes: data,
|
|
2007
|
+
});
|
|
2008
|
+
if (this.#closed || channel.closed || graphics.image(digest) !== image) {
|
|
2009
|
+
await this.#localImages.release(lease);
|
|
2010
|
+
continue;
|
|
2011
|
+
}
|
|
2012
|
+
this.#localLeases.set(lease.id, {
|
|
2013
|
+
pane: channel.semanticPaneId,
|
|
2014
|
+
expiresAt: lease.expiresAt,
|
|
2015
|
+
});
|
|
2016
|
+
this.#sendFrame(null, {
|
|
2017
|
+
type: "terminal-graphics-local",
|
|
2018
|
+
pane: channel.semanticPaneId,
|
|
2019
|
+
generation: lease.generation,
|
|
2020
|
+
leaseId: lease.id,
|
|
2021
|
+
path: lease.path,
|
|
2022
|
+
expiresAt: lease.expiresAt,
|
|
2023
|
+
blob: {
|
|
2024
|
+
digest,
|
|
2025
|
+
format,
|
|
2026
|
+
width: image.width,
|
|
2027
|
+
height: image.height,
|
|
2028
|
+
total,
|
|
2029
|
+
offset: 0,
|
|
2030
|
+
},
|
|
2031
|
+
});
|
|
2032
|
+
continue;
|
|
2033
|
+
} catch {
|
|
2034
|
+
/* Optional transport: retain the negotiated inline fallback. */
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
1905
2038
|
for (let offset = 0; offset < total; offset += TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES) {
|
|
1906
2039
|
while ((this.#socket.bufferedAmount ?? 0) > GRAPHICS_SEND_WINDOW_BYTES) {
|
|
1907
2040
|
if (this.#closed || channel.closed) return;
|
|
@@ -1912,6 +2045,29 @@ export class PaneStreamLiveConnection {
|
|
|
1912
2045
|
// the renderer drops the partial image and shows the newer one.
|
|
1913
2046
|
if (offset > 0 && graphics.image(digest) !== image) break;
|
|
1914
2047
|
const end = Math.min(total, offset + TERMINAL_GRAPHICS_BLOB_CHUNK_BYTES);
|
|
2048
|
+
if (channel.graphicsBinary) {
|
|
2049
|
+
this.#sendEncoded(
|
|
2050
|
+
null,
|
|
2051
|
+
Buffer.from(
|
|
2052
|
+
encodeTerminalGraphicsBinary(
|
|
2053
|
+
{
|
|
2054
|
+
pane: channel.semanticPaneId,
|
|
2055
|
+
blob: {
|
|
2056
|
+
digest,
|
|
2057
|
+
format,
|
|
2058
|
+
width: image.width,
|
|
2059
|
+
height: image.height,
|
|
2060
|
+
total,
|
|
2061
|
+
offset,
|
|
2062
|
+
},
|
|
2063
|
+
},
|
|
2064
|
+
data.subarray(offset, end),
|
|
2065
|
+
),
|
|
2066
|
+
),
|
|
2067
|
+
true,
|
|
2068
|
+
);
|
|
2069
|
+
continue;
|
|
2070
|
+
}
|
|
1915
2071
|
this.#sendFrame(null, {
|
|
1916
2072
|
type: "terminal-graphics-blob",
|
|
1917
2073
|
pane: channel.semanticPaneId,
|
|
@@ -2245,7 +2401,12 @@ export class PaneStreamLiveConnection {
|
|
|
2245
2401
|
#sendFrame(pane: string | null, frame: Record<string, unknown>): void {
|
|
2246
2402
|
if (this.#closed || this.#socket.readyState !== WS_OPEN) return;
|
|
2247
2403
|
const encoded = JSON.stringify(frame);
|
|
2248
|
-
|
|
2404
|
+
this.#sendEncoded(pane, encoded, false);
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
#sendEncoded(pane: string | null, encoded: string | Buffer, binary: boolean): void {
|
|
2408
|
+
if (this.#closed || this.#socket.readyState !== WS_OPEN) return;
|
|
2409
|
+
const bytes = Buffer.byteLength(encoded);
|
|
2249
2410
|
if (pane !== null) {
|
|
2250
2411
|
this.#ledger.take(this.#clientId, pane, "ws-send-buffer", bytes);
|
|
2251
2412
|
if (this.#deliveryAcks) this.#ledger.take(this.#clientId, pane, "renderer-backlog", 1);
|
|
@@ -2253,7 +2414,7 @@ export class PaneStreamLiveConnection {
|
|
|
2253
2414
|
this.#sendQueue.push({ pane, remaining: bytes });
|
|
2254
2415
|
this.#sentBytesTotal += bytes;
|
|
2255
2416
|
try {
|
|
2256
|
-
this.#socket.send(encoded, { binary
|
|
2417
|
+
this.#socket.send(encoded, { binary });
|
|
2257
2418
|
} catch {
|
|
2258
2419
|
this.close(1011, "stream-unavailable");
|
|
2259
2420
|
return;
|
|
@@ -2475,6 +2636,20 @@ export class PaneStreamLiveConnection {
|
|
|
2475
2636
|
});
|
|
2476
2637
|
return;
|
|
2477
2638
|
}
|
|
2639
|
+
if (frame.type === "terminal-projection-receipt") {
|
|
2640
|
+
const channel = this.#deliveryChannel(frame.receipt);
|
|
2641
|
+
if (!channel) return;
|
|
2642
|
+
if (!channel.delivery?.projectionReceipt) return this.#failProtocol("protocol-error");
|
|
2643
|
+
channel.delivery.projectionReceipt(frame.receipt);
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2646
|
+
if (frame.type === "terminal-projection-materialize") {
|
|
2647
|
+
const channel = this.#deliveryChannel(frame.request);
|
|
2648
|
+
if (!channel) return;
|
|
2649
|
+
if (!channel.delivery?.materialize) return this.#failProtocol("protocol-error");
|
|
2650
|
+
channel.delivery.materialize(frame.request);
|
|
2651
|
+
return;
|
|
2652
|
+
}
|
|
2478
2653
|
if (frame.type === "terminal-delivery-ack") {
|
|
2479
2654
|
const channel = this.#deliveryChannel(frame.ack);
|
|
2480
2655
|
if (!channel) return;
|
|
@@ -2504,7 +2679,25 @@ export class PaneStreamLiveConnection {
|
|
|
2504
2679
|
return;
|
|
2505
2680
|
}
|
|
2506
2681
|
if (frame.type === "terminal-graphics-request") {
|
|
2507
|
-
this.#requestGraphics(frame.pane, frame.digests);
|
|
2682
|
+
this.#requestGraphics(frame.pane, frame.digests, frame.preferInline === true);
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
if (frame.type === "terminal-graphics-local-release") {
|
|
2686
|
+
const lease = this.#localLeases.get(frame.leaseId);
|
|
2687
|
+
if (
|
|
2688
|
+
lease &&
|
|
2689
|
+
lease.pane === frame.pane &&
|
|
2690
|
+
frame.generation === this.#localImages?.identity.generation
|
|
2691
|
+
) {
|
|
2692
|
+
this.#localLeases.delete(frame.leaseId);
|
|
2693
|
+
void this.#localImages
|
|
2694
|
+
?.release({
|
|
2695
|
+
generation: frame.generation,
|
|
2696
|
+
client: this.#descriptor.leaseId,
|
|
2697
|
+
id: frame.leaseId,
|
|
2698
|
+
})
|
|
2699
|
+
.catch(() => {});
|
|
2700
|
+
}
|
|
2508
2701
|
return;
|
|
2509
2702
|
}
|
|
2510
2703
|
if (frame.type === "terminal-cell-pixels") {
|
|
@@ -2720,7 +2913,7 @@ export class PaneStreamLiveConnection {
|
|
|
2720
2913
|
#acceptInput(
|
|
2721
2914
|
pane: string,
|
|
2722
2915
|
seq: number,
|
|
2723
|
-
kind: "text" | "key",
|
|
2916
|
+
kind: "text" | "key" | "bytes",
|
|
2724
2917
|
data: string,
|
|
2725
2918
|
frameBytes: number,
|
|
2726
2919
|
performanceTraceId?: string,
|
|
@@ -2730,6 +2923,16 @@ export class PaneStreamLiveConnection {
|
|
|
2730
2923
|
ingressAtSharedMicros: number | null = null,
|
|
2731
2924
|
expectedLifetime?: PaneStreamInputLifetime,
|
|
2732
2925
|
): void {
|
|
2926
|
+
if (
|
|
2927
|
+
kind === "bytes" &&
|
|
2928
|
+
(!this.#sessionRuntimeBinding ||
|
|
2929
|
+
!this.#descriptor.terminalDelivery?.protocolVersions.includes(
|
|
2930
|
+
PANE_STREAM_INPUT_BYTES_CAPABILITY_VERSION,
|
|
2931
|
+
))
|
|
2932
|
+
) {
|
|
2933
|
+
this.#failProtocol("input-rejected");
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2733
2936
|
if (
|
|
2734
2937
|
expectedLifetime &&
|
|
2735
2938
|
(!this.#sessionRuntimeBinding ||
|
|
@@ -2862,7 +3065,8 @@ export class PaneStreamLiveConnection {
|
|
|
2862
3065
|
expectedLifetime,
|
|
2863
3066
|
);
|
|
2864
3067
|
else if (kind === "text") channel.sub!.sendText(data);
|
|
2865
|
-
else channel.sub!.sendKey(data);
|
|
3068
|
+
else if (kind === "key") channel.sub!.sendKey(data);
|
|
3069
|
+
else throw new Error("Byte input requires semantic runtime");
|
|
2866
3070
|
let ackStartedAtMicros: number | null = null;
|
|
2867
3071
|
let ackStartedAtSharedMicros: number | null = null;
|
|
2868
3072
|
if (trace) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LocalImageOwner } from "../graphics/local-image-owner.ts";
|
|
1
2
|
import type { SemanticPaneCatalog } from "../attachments/semantic-pane-catalog.ts";
|
|
2
3
|
import type { SessionRuntimeRegistry } from "../session-runtime/registry.ts";
|
|
3
4
|
import { SessionRuntimeTransportBinder } from "../session-runtime/transport-binding.ts";
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
} from "./pane-stream-websocket.ts";
|
|
10
11
|
|
|
11
12
|
export interface PaneStreamRuntimeOptions {
|
|
13
|
+
readonly localImages?: LocalImageOwner;
|
|
12
14
|
readonly daemonInstanceId: string;
|
|
13
15
|
readonly webSocketUrl: string;
|
|
14
16
|
/** Canonical daemon-generation session/control owner. */
|
|
@@ -38,6 +40,7 @@ export class PaneStreamRuntime {
|
|
|
38
40
|
const transportBinder = new SessionRuntimeTransportBinder(options.sessionRuntimeRegistry);
|
|
39
41
|
this.coordinator = new PaneStreamAdmissionCoordinator({
|
|
40
42
|
...options.admission,
|
|
43
|
+
localImages: options.localImages,
|
|
41
44
|
daemonInstanceId: options.daemonInstanceId,
|
|
42
45
|
webSocketUrl: options.webSocketUrl,
|
|
43
46
|
leaseManager,
|
|
@@ -2472,6 +2472,8 @@ class SessionRuntimeConsumerImpl implements SessionRuntimeConsumer {
|
|
|
2472
2472
|
negotiation: upstream.negotiation,
|
|
2473
2473
|
closed: upstream.closed,
|
|
2474
2474
|
ack: (ack: TerminalDeliveryAck) => upstream.ack(ack),
|
|
2475
|
+
...(upstream.projectionReceipt ? { projectionReceipt: upstream.projectionReceipt } : {}),
|
|
2476
|
+
...(upstream.materialize ? { materialize: upstream.materialize } : {}),
|
|
2475
2477
|
nack: (nack: TerminalDeliveryNack) => upstream.nack(nack),
|
|
2476
2478
|
setVisibility: (visibility: TerminalDeliveryVisibility) => upstream.setVisibility(visibility),
|
|
2477
2479
|
close: async () => {
|
|
@@ -81,7 +81,7 @@ export interface SessionRuntimeStageSpan {
|
|
|
81
81
|
readonly attemptedLegacySeedSizeCapped?: boolean;
|
|
82
82
|
readonly attemptedCompactPatchBytes?: number | null;
|
|
83
83
|
readonly attemptedCompactSeedBytes?: number | null;
|
|
84
|
-
readonly selectedEncoding?: "semantic-v1" | "semantic-compact-v1";
|
|
84
|
+
readonly selectedEncoding?: "semantic-v1" | "semantic-compact-v1" | "semantic-binary-v1";
|
|
85
85
|
readonly selectionStatus?:
|
|
86
86
|
| "patch-preferred"
|
|
87
87
|
| "seed-preferred"
|