@ricsam/r5d-worker 0.0.172 → 0.0.173

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.
@@ -1,6 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { OperationEnvelope, OwnershipFence, RUNTIME_PROTOCOL, RuntimeHello, RuntimeId } from "@ricsam/r5d-api/runtime-protocol";
3
3
  const EXECUTOR_CAPABILITY = "host-executor-v1";
4
+ const EXECUTOR_STREAM_CAPABILITY = "host-stream-v1";
5
+ const STREAM_CHUNK_BYTES = 64 * 1024;
4
6
  const MAX_FRAME_BYTES = 1024 * 1024;
5
7
  const text = z.string().max(256 * 1024).refine((s) => !s.includes("\0"), "NUL is not allowed");
6
8
  const absolutePath = z.string().min(1).max(4096).refine((s) => s.startsWith("/") && !s.includes("\0"), "Absolute POSIX path required");
@@ -54,7 +56,17 @@ const ExecutorCommand = z.discriminatedUnion("method", [
54
56
  cols: z.number().int().min(1).max(1e3),
55
57
  rows: z.number().int().min(1).max(1e3)
56
58
  }).strict(),
57
- z.object({ method: z.literal("cancel"), run: RunIdentity, actionId: RuntimeId }).strict()
59
+ z.object({ method: z.literal("cancel"), run: RunIdentity, actionId: RuntimeId }).strict(),
60
+ // Live terminal channel. Neither command has a durable action identity: a
61
+ // subscription is replayed from an offset, and live input is keystrokes the
62
+ // person can simply type again, exactly as over ssh.
63
+ z.object({ method: z.literal("subscribe"), run: RunIdentity, offset: z.number().int().nonnegative().default(0) }).strict(),
64
+ z.object({
65
+ method: z.literal("input"),
66
+ run: RunIdentity,
67
+ data: text.optional(),
68
+ resize: z.object({ cols: z.number().int().min(1).max(1e3), rows: z.number().int().min(1).max(1e3) }).strict().optional()
69
+ }).strict().refine((value) => value.data !== void 0 || value.resize !== void 0, "Input carries data or a size")
58
70
  ]);
59
71
  const ExecutorRequest = z.object({
60
72
  protocol: z.literal(RUNTIME_PROTOCOL),
@@ -124,6 +136,7 @@ export {
124
136
  AdapterConfig,
125
137
  AdapterGrant,
126
138
  EXECUTOR_CAPABILITY,
139
+ EXECUTOR_STREAM_CAPABILITY,
127
140
  ExecutorCommand,
128
141
  ExecutorConfig,
129
142
  ExecutorCredential,
@@ -133,6 +146,7 @@ export {
133
146
  MAX_FRAME_BYTES,
134
147
  Principal,
135
148
  RunIdentity,
149
+ STREAM_CHUNK_BYTES,
136
150
  ShellPayload,
137
151
  isDefiniteStartRejection,
138
152
  isStartNonAdmissionCode
@@ -50,6 +50,7 @@ class WorkspaceAuthority {
50
50
  closed = false;
51
51
  closeTask;
52
52
  actions = /* @__PURE__ */ new Set();
53
+ subscriptions = /* @__PURE__ */ new Set();
53
54
  repositoryInitializations = /* @__PURE__ */ new Map();
54
55
  outerRepository;
55
56
  pending = 0;
@@ -1895,19 +1896,59 @@ class WorkspaceAuthority {
1895
1896
  return this.action(async () => {
1896
1897
  const { b, route } = await this.runRoute(identity, run);
1897
1898
  const result = await route.client.poll({ ...run, fence: route.workerFence }, stdoutOffset, stderrOffset, maxBytes);
1898
- await this.serial(
1899
- b,
1900
- async () => {
1901
- b.state.runs[run.operationId].state = result.receipt.state;
1902
- b.state.runs[run.operationId].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1903
- if (["completed", "cancelled"].includes(result.receipt.state)) b.state.runs[run.operationId].completedAt ??= (/* @__PURE__ */ new Date()).toISOString();
1904
- await this.save(b);
1905
- },
1906
- true
1907
- );
1899
+ await this.recordRunState(b, run.operationId, result.receipt.state);
1908
1900
  return result;
1909
1901
  });
1910
1902
  }
1903
+ /** Run liveness is process state observed from the executor, not a workspace
1904
+ * mutation: it is recorded on its own short queue, never behind the checkout's
1905
+ * hydration, commit or publication work. A crash between two writers loses
1906
+ * nothing lasting — `refresh` re-reads every open run from the executor. */
1907
+ recordRunState(b, operationId, state) {
1908
+ return this.serialKey(
1909
+ `runs:${b.config.id}`,
1910
+ async () => {
1911
+ const run = b.state.runs[operationId];
1912
+ if (!run) return;
1913
+ run.state = state;
1914
+ run.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1915
+ if (["completed", "cancelled"].includes(state)) run.completedAt ??= (/* @__PURE__ */ new Date()).toISOString();
1916
+ await this.save(b);
1917
+ },
1918
+ true
1919
+ );
1920
+ }
1921
+ /** Live terminal channel. The subscription is not an admitted action: it
1922
+ * lives as long as its transport, outlives the request budget, and is torn
1923
+ * down on close. Its exit frame is the only durable-state effect. */
1924
+ async subscribe(identity, run, offset, handlers) {
1925
+ if (this.closing) throw new WorkspaceError("authority_closed", "Workspace authority is closing");
1926
+ const { b, route } = await this.runRoute(identity, run);
1927
+ if (!route.client.subscribe) throw new WorkspaceError("stream_unavailable", "Executor does not advertise terminal streaming", true);
1928
+ const holder = {};
1929
+ holder.subscription = await route.client.subscribe({ ...run, fence: route.workerFence }, offset, {
1930
+ frame: (frame) => {
1931
+ if (frame.event === "exit") void this.recordRunState(b, run.operationId, frame.receipt.state).catch(() => {
1932
+ });
1933
+ handlers.frame(frame);
1934
+ },
1935
+ close: (error) => {
1936
+ if (holder.subscription) this.subscriptions.delete(holder.subscription);
1937
+ handlers.close(error);
1938
+ }
1939
+ });
1940
+ this.subscriptions.add(holder.subscription);
1941
+ return holder.subscription;
1942
+ }
1943
+ /** Live PTY input: fenced and owned like a write, but with no action receipt
1944
+ * and no queue in front of it. */
1945
+ async streamInput(identity, run, data, resize) {
1946
+ return this.action(async () => {
1947
+ const { route } = await this.runRoute(identity, run);
1948
+ if (!route.client.input) throw new WorkspaceError("stream_unavailable", "Executor does not advertise terminal streaming", true);
1949
+ return route.client.input({ ...run, fence: route.workerFence }, data, resize);
1950
+ });
1951
+ }
1911
1952
  async actionOutcome(b, actionId, invoke) {
1912
1953
  try {
1913
1954
  const result = await invoke();
@@ -1963,6 +2004,7 @@ class WorkspaceAuthority {
1963
2004
  return task;
1964
2005
  }
1965
2006
  async finishClose() {
2007
+ for (const subscription of this.subscriptions) subscription.close();
1966
2008
  await Promise.allSettled([...this.actions]);
1967
2009
  await Promise.allSettled([...this.queues.values()]);
1968
2010
  for (const b of this.benches.values()) await this.refresh(b);
@@ -38,6 +38,19 @@ export declare const PersonalWorkerGrant: z.ZodObject<{
38
38
  }, z.core.$strict>;
39
39
  leaseExpiresAt: z.ZodCoercedNumber<unknown>;
40
40
  }, z.core.$loose>;
41
+ declare const Workbench: z.ZodObject<{
42
+ id: z.ZodString;
43
+ repositoryId: z.ZodString;
44
+ branch: z.ZodString;
45
+ sessionId: z.ZodString;
46
+ rootProfile: z.ZodDefault<z.ZodEnum<{
47
+ account: "account";
48
+ project: "project";
49
+ }>>;
50
+ namespace: z.ZodOptional<z.ZodString>;
51
+ projectName: z.ZodOptional<z.ZodString>;
52
+ baseCommitHash: z.ZodOptional<z.ZodString>;
53
+ }, z.core.$strict>;
41
54
  export declare const PersonalWorkspaceRequest: z.ZodObject<{
42
55
  protocol: z.ZodLiteral<1>;
43
56
  userId: z.ZodString;
@@ -151,5 +164,21 @@ export declare function openPersonalWorkerRuntime(options: {
151
164
  unchanged?: boolean;
152
165
  error?: string;
153
166
  }[]>;
167
+ subscribeTerminal: (input: {
168
+ sessionId: string;
169
+ workbench: z.infer<typeof Workbench>;
170
+ operationId: string;
171
+ offset: number;
172
+ }, handlers: Parameters<WorkspaceAuthority["subscribe"]>[3]) => Promise<import("../runtime/client").ExecutorSubscription>;
173
+ terminalInput: (input: {
174
+ sessionId: string;
175
+ operationId: string;
176
+ data?: string;
177
+ resize?: {
178
+ cols: number;
179
+ rows: number;
180
+ };
181
+ }) => Promise<import("../runtime/protocol").InputResult>;
154
182
  close(): Promise<void>;
155
183
  }>;
184
+ export {};
@@ -0,0 +1,132 @@
1
+ import { z } from "zod";
2
+ /** Live terminal channel between a personal worker and the web runtime, and
3
+ * between the web runtime and a browser. Frames are JSON text messages.
4
+ *
5
+ * Nothing on this channel is durable: a lost frame is retyped by the person
6
+ * at the keyboard or re-read from the durable offset by the browser. That is
7
+ * what keeps a keystroke off the relay ledger and out of every workspace queue. */
8
+ export declare const TERMINAL_STREAM_CAPABILITY = "terminal-stream-v1";
9
+ export declare const STREAM_INPUT_MAX_CHARS = 100000;
10
+ export declare const STREAM_HEARTBEAT_MS = 25000;
11
+ export declare const StreamWorkbench: z.ZodObject<{
12
+ id: z.ZodString;
13
+ repositoryId: z.ZodString;
14
+ branch: z.ZodString;
15
+ sessionId: z.ZodString;
16
+ rootProfile: z.ZodDefault<z.ZodEnum<{
17
+ account: "account";
18
+ project: "project";
19
+ }>>;
20
+ namespace: z.ZodOptional<z.ZodString>;
21
+ projectName: z.ZodOptional<z.ZodString>;
22
+ baseCommitHash: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strict>;
24
+ export type StreamWorkbench = z.output<typeof StreamWorkbench>;
25
+ /** Web runtime → worker. */
26
+ export declare const ServerToWorkerFrame: z.ZodDiscriminatedUnion<[z.ZodObject<{
27
+ type: z.ZodLiteral<"attach">;
28
+ streamId: z.ZodString;
29
+ sessionId: z.ZodString;
30
+ operationId: z.ZodString;
31
+ offset: z.ZodNumber;
32
+ workbench: z.ZodObject<{
33
+ id: z.ZodString;
34
+ repositoryId: z.ZodString;
35
+ branch: z.ZodString;
36
+ sessionId: z.ZodString;
37
+ rootProfile: z.ZodDefault<z.ZodEnum<{
38
+ account: "account";
39
+ project: "project";
40
+ }>>;
41
+ namespace: z.ZodOptional<z.ZodString>;
42
+ projectName: z.ZodOptional<z.ZodString>;
43
+ baseCommitHash: z.ZodOptional<z.ZodString>;
44
+ }, z.core.$strict>;
45
+ }, z.core.$strict>, z.ZodObject<{
46
+ type: z.ZodLiteral<"input">;
47
+ streamId: z.ZodString;
48
+ data: z.ZodOptional<z.ZodString>;
49
+ resize: z.ZodOptional<z.ZodObject<{
50
+ cols: z.ZodNumber;
51
+ rows: z.ZodNumber;
52
+ }, z.core.$strict>>;
53
+ }, z.core.$strict>, z.ZodObject<{
54
+ type: z.ZodLiteral<"detach">;
55
+ streamId: z.ZodString;
56
+ }, z.core.$strict>, z.ZodObject<{
57
+ type: z.ZodLiteral<"ping">;
58
+ }, z.core.$strict>], "type">;
59
+ export type ServerToWorkerFrame = z.infer<typeof ServerToWorkerFrame>;
60
+ /** Worker → web runtime. */
61
+ export declare const WorkerToServerFrame: z.ZodDiscriminatedUnion<[z.ZodObject<{
62
+ type: z.ZodLiteral<"hello">;
63
+ version: z.ZodString;
64
+ capabilities: z.ZodArray<z.ZodString>;
65
+ }, z.core.$strict>, z.ZodObject<{
66
+ type: z.ZodLiteral<"attached">;
67
+ streamId: z.ZodString;
68
+ offset: z.ZodNumber;
69
+ }, z.core.$strict>, z.ZodObject<{
70
+ type: z.ZodLiteral<"output">;
71
+ streamId: z.ZodString;
72
+ offset: z.ZodNumber;
73
+ base64: z.ZodString;
74
+ }, z.core.$strict>, z.ZodObject<{
75
+ type: z.ZodLiteral<"exit">;
76
+ streamId: z.ZodString;
77
+ exitCode: z.ZodNullable<z.ZodNumber>;
78
+ signal: z.ZodNullable<z.ZodString>;
79
+ }, z.core.$strict>, z.ZodObject<{
80
+ type: z.ZodLiteral<"error">;
81
+ streamId: z.ZodString;
82
+ code: z.ZodString;
83
+ }, z.core.$strict>, z.ZodObject<{
84
+ type: z.ZodLiteral<"detached">;
85
+ streamId: z.ZodString;
86
+ code: z.ZodOptional<z.ZodString>;
87
+ }, z.core.$strict>, z.ZodObject<{
88
+ type: z.ZodLiteral<"pong">;
89
+ }, z.core.$strict>], "type">;
90
+ export type WorkerToServerFrame = z.infer<typeof WorkerToServerFrame>;
91
+ /** Browser → web runtime. The PTY, its offset and the worker are fixed by the
92
+ * socket's URL, so the browser only ever sends what a keyboard produces. */
93
+ export declare const BrowserToServerFrame: z.ZodDiscriminatedUnion<[z.ZodObject<{
94
+ type: z.ZodLiteral<"input">;
95
+ data: z.ZodString;
96
+ }, z.core.$strict>, z.ZodObject<{
97
+ type: z.ZodLiteral<"resize">;
98
+ cols: z.ZodNumber;
99
+ rows: z.ZodNumber;
100
+ }, z.core.$strict>, z.ZodObject<{
101
+ type: z.ZodLiteral<"ping">;
102
+ }, z.core.$strict>, z.ZodObject<{
103
+ type: z.ZodLiteral<"pong">;
104
+ }, z.core.$strict>], "type">;
105
+ export type BrowserToServerFrame = z.infer<typeof BrowserToServerFrame>;
106
+ /** Web runtime → browser. `offset` is the durable stdout offset after the
107
+ * frame's bytes, the same number the polling transport calls its cursor. */
108
+ export type ServerToBrowserFrame = {
109
+ type: "attached";
110
+ offset: number;
111
+ } | {
112
+ type: "output";
113
+ offset: number;
114
+ base64: string;
115
+ } | {
116
+ type: "exit";
117
+ exitCode: number | null;
118
+ signal: string | null;
119
+ } | {
120
+ type: "error";
121
+ code: string;
122
+ } | {
123
+ type: "unavailable";
124
+ reason?: string;
125
+ } | {
126
+ type: "ping";
127
+ } | {
128
+ type: "pong";
129
+ };
130
+ /** Number of leading bytes that end on a UTF-8 sequence boundary. A split
131
+ * suffix is carried into the next frame so offsets never cut a character. */
132
+ export declare function completeUtf8Prefix(bytes: Uint8Array): number;
@@ -0,0 +1,31 @@
1
+ import type { PersonalWorkerRuntime } from "./runtime";
2
+ /** The subset of a WebSocket this client drives; Bun's global client fits it
3
+ * and tests substitute a scripted one. */
4
+ export type StreamSocket = {
5
+ onopen: ((event?: unknown) => void) | null;
6
+ onmessage: ((event: {
7
+ data: unknown;
8
+ }) => void) | null;
9
+ onclose: ((event?: unknown) => void) | null;
10
+ onerror: ((event?: unknown) => void) | null;
11
+ send(data: string): void;
12
+ close(code?: number, reason?: string): void;
13
+ };
14
+ export type TerminalStreamRuntime = Pick<PersonalWorkerRuntime, "subscribeTerminal" | "terminalInput">;
15
+ export type TerminalStreamOptions = {
16
+ /** ws(s) URL of this worker's stream endpoint, instance included. */
17
+ url: string;
18
+ credential: string;
19
+ version: string;
20
+ runtime: TerminalStreamRuntime;
21
+ connect?: (url: string, headers: Record<string, string>) => StreamSocket;
22
+ log?: (line: string) => void;
23
+ /** Reconnect delays; the last one repeats. */
24
+ backoffMs?: number[];
25
+ };
26
+ /** One long-lived socket per worker carries every attached shell. The socket
27
+ * is the transport only: PTYs, their receipts and their bytes stay in the
28
+ * executor, so a reconnect re-attaches at the browser's durable offset. */
29
+ export declare function startTerminalStream(options: TerminalStreamOptions): {
30
+ close(): Promise<void>;
31
+ };
@@ -1,9 +1,21 @@
1
1
  import type { OperationEnvelope, OwnershipFence } from "@ricsam/r5d-api/runtime-protocol";
2
- import { AdapterConfig, type ActionReceipt, type AdapterGrant, type AdapterRegistration, type ExecutorCommand, type PollResult, type RunIdentity, type RunReceipt } from "./protocol";
2
+ import { AdapterConfig, ExecutorError, type ActionReceipt, type AdapterGrant, type AdapterRegistration, type ExecutorCommand, type InputResult, type PollResult, type RunIdentity, type RunReceipt, type StreamFrame, type SubscribeResult } from "./protocol";
3
+ export type ExecutorSubscription = SubscribeResult & {
4
+ close(): void;
5
+ };
6
+ export type ExecutorStreamHandlers = {
7
+ frame(frame: StreamFrame): void;
8
+ /** Called once, after the last frame, with the transport's failure if any. */
9
+ close(error?: ExecutorError): void;
10
+ };
3
11
  /** Stateless one-request connections: disconnect/timeout never implies the operation did not happen. */
4
12
  export declare class HostExecutorClient {
5
13
  readonly config: ReturnType<typeof AdapterConfig.parse>;
6
14
  constructor(config: AdapterConfig);
15
+ private frame;
16
+ /** Keeps its socket open: the admission reply resolves the promise, then every
17
+ * newline-delimited frame reaches `handlers.frame` until either side closes. */
18
+ subscribe(run: RunIdentity, offset: number, handlers: ExecutorStreamHandlers): Promise<ExecutorSubscription>;
7
19
  request<T = unknown>(command: ExecutorCommand): Promise<T>;
8
20
  status(): Promise<unknown>;
9
21
  registerAdapter(grant: AdapterGrant): Promise<AdapterRegistration>;
@@ -14,4 +26,9 @@ export declare class HostExecutorClient {
14
26
  write(run: RunIdentity, actionId: string, data: string, eof?: boolean): Promise<ActionReceipt>;
15
27
  resize(run: RunIdentity, actionId: string, cols: number, rows: number): Promise<ActionReceipt>;
16
28
  cancel(run: RunIdentity, actionId: string): Promise<ActionReceipt>;
29
+ /** Live PTY input without a durable action: nothing to replay, nothing to poison. */
30
+ input(run: RunIdentity, data?: string, resize?: {
31
+ cols: number;
32
+ rows: number;
33
+ }): Promise<InputResult>;
17
34
  }
@@ -1,5 +1,5 @@
1
1
  import { type RuntimeHello } from "@ricsam/r5d-api/runtime-protocol";
2
- import { ExecutorConfig, type ResolvedConfig } from "./protocol";
2
+ import { ExecutorConfig, type ResolvedConfig, type StreamSink } from "./protocol";
3
3
  import { ExecutorStore } from "./storage";
4
4
  type PtyModule = typeof import("node-pty");
5
5
  export declare const tokenHash: (token: string) => string;
@@ -11,6 +11,7 @@ export declare class HostExecutor {
11
11
  private readonly adapters;
12
12
  readonly hello: RuntimeHello;
13
13
  private readonly active;
14
+ private readonly subscribers;
14
15
  private readonly leaseTimer;
15
16
  private closing;
16
17
  private poisoned;
@@ -19,8 +20,14 @@ export declare class HostExecutor {
19
20
  /** Authority is synchronous and authenticated; never queue lease renewal behind a blocked OS write.
20
21
  * Resource commands retain one ordered admission/action boundary. Commands already admitted may
21
22
  * complete after promotion; queued commands and yielded admission hashes must recheck the fence.
23
+ * The live terminal channel (subscribe, input) carries no durable action and
24
+ * never waits behind that boundary: a keystroke is not queued behind a spawn.
22
25
  */
23
- handle(input: unknown): Promise<unknown>;
26
+ handle(input: unknown, sink?: StreamSink): Promise<unknown>;
27
+ /** Drops a subscription whose transport closed. Idempotent. */
28
+ unsubscribe(sink: StreamSink): void;
29
+ private notify;
30
+ private pump;
24
31
  private authenticate;
25
32
  private authority;
26
33
  private workerFence;
@@ -1,5 +1,10 @@
1
1
  import { z } from "zod";
2
2
  export declare const EXECUTOR_CAPABILITY = "host-executor-v1";
3
+ /** Live terminal channel: a kept-open subscription socket plus fenced,
4
+ * receipt-free input. Advertised by executors that stream. */
5
+ export declare const EXECUTOR_STREAM_CAPABILITY = "host-stream-v1";
6
+ /** Longest single output frame on a subscription socket. */
7
+ export declare const STREAM_CHUNK_BYTES: number;
3
8
  export declare const MAX_FRAME_BYTES: number;
4
9
  export declare const Lane: z.ZodEnum<{
5
10
  general: "general";
@@ -248,6 +253,40 @@ export declare const ExecutorCommand: z.ZodDiscriminatedUnion<[z.ZodObject<{
248
253
  }, z.core.$strict>;
249
254
  }, z.core.$strict>;
250
255
  actionId: z.ZodString;
256
+ }, z.core.$strict>, z.ZodObject<{
257
+ method: z.ZodLiteral<"subscribe">;
258
+ run: z.ZodObject<{
259
+ operationId: z.ZodString;
260
+ userId: z.ZodString;
261
+ sessionId: z.ZodOptional<z.ZodString>;
262
+ fence: z.ZodObject<{
263
+ installationId: z.ZodString;
264
+ resourceType: z.ZodString;
265
+ resourceId: z.ZodString;
266
+ ownerId: z.ZodString;
267
+ epoch: z.ZodNumber;
268
+ }, z.core.$strict>;
269
+ }, z.core.$strict>;
270
+ offset: z.ZodDefault<z.ZodNumber>;
271
+ }, z.core.$strict>, z.ZodObject<{
272
+ method: z.ZodLiteral<"input">;
273
+ run: z.ZodObject<{
274
+ operationId: z.ZodString;
275
+ userId: z.ZodString;
276
+ sessionId: z.ZodOptional<z.ZodString>;
277
+ fence: z.ZodObject<{
278
+ installationId: z.ZodString;
279
+ resourceType: z.ZodString;
280
+ resourceId: z.ZodString;
281
+ ownerId: z.ZodString;
282
+ epoch: z.ZodNumber;
283
+ }, z.core.$strict>;
284
+ }, z.core.$strict>;
285
+ data: z.ZodOptional<z.ZodString>;
286
+ resize: z.ZodOptional<z.ZodObject<{
287
+ cols: z.ZodNumber;
288
+ rows: z.ZodNumber;
289
+ }, z.core.$strict>>;
251
290
  }, z.core.$strict>], "method">;
252
291
  export type ExecutorCommand = z.input<typeof ExecutorCommand>;
253
292
  export declare const ExecutorRequest: z.ZodObject<{
@@ -419,6 +458,40 @@ export declare const ExecutorRequest: z.ZodObject<{
419
458
  }, z.core.$strict>;
420
459
  }, z.core.$strict>;
421
460
  actionId: z.ZodString;
461
+ }, z.core.$strict>, z.ZodObject<{
462
+ method: z.ZodLiteral<"subscribe">;
463
+ run: z.ZodObject<{
464
+ operationId: z.ZodString;
465
+ userId: z.ZodString;
466
+ sessionId: z.ZodOptional<z.ZodString>;
467
+ fence: z.ZodObject<{
468
+ installationId: z.ZodString;
469
+ resourceType: z.ZodString;
470
+ resourceId: z.ZodString;
471
+ ownerId: z.ZodString;
472
+ epoch: z.ZodNumber;
473
+ }, z.core.$strict>;
474
+ }, z.core.$strict>;
475
+ offset: z.ZodDefault<z.ZodNumber>;
476
+ }, z.core.$strict>, z.ZodObject<{
477
+ method: z.ZodLiteral<"input">;
478
+ run: z.ZodObject<{
479
+ operationId: z.ZodString;
480
+ userId: z.ZodString;
481
+ sessionId: z.ZodOptional<z.ZodString>;
482
+ fence: z.ZodObject<{
483
+ installationId: z.ZodString;
484
+ resourceType: z.ZodString;
485
+ resourceId: z.ZodString;
486
+ ownerId: z.ZodString;
487
+ epoch: z.ZodNumber;
488
+ }, z.core.$strict>;
489
+ }, z.core.$strict>;
490
+ data: z.ZodOptional<z.ZodString>;
491
+ resize: z.ZodOptional<z.ZodObject<{
492
+ cols: z.ZodNumber;
493
+ rows: z.ZodNumber;
494
+ }, z.core.$strict>>;
422
495
  }, z.core.$strict>], "method">;
423
496
  }, z.core.$strict>;
424
497
  export declare const ExecutorCredential: z.ZodObject<{
@@ -521,6 +594,31 @@ export type ActionReceipt = {
521
594
  actionId: string;
522
595
  state: "accepted" | "completed" | "unknown";
523
596
  };
597
+ /** Frames written after a subscription's admission reply, one per line. `offset`
598
+ * is the durable stdout offset after the frame's bytes. */
599
+ export type StreamFrame = {
600
+ event: "output";
601
+ offset: number;
602
+ base64: string;
603
+ } | {
604
+ event: "exit";
605
+ receipt: RunReceipt;
606
+ };
607
+ export type SubscribeResult = {
608
+ receipt: RunReceipt;
609
+ offset: number;
610
+ };
611
+ export type InputResult = {
612
+ ok: true;
613
+ };
614
+ /** Transport a subscription pushes into; the daemon binds it to one socket. */
615
+ export type StreamSink = {
616
+ /** Resolves once the admission reply has been written; frames wait for it. */
617
+ ready: Promise<void>;
618
+ /** Resolves once the frame is flushed to the transport (backpressure). */
619
+ send(frame: StreamFrame): Promise<void>;
620
+ end(): void;
621
+ };
524
622
  export type PollResult = {
525
623
  receipt: RunReceipt;
526
624
  stdout: {