@actana/sdk 0.2.2

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 (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/dist/core-client.d.ts +616 -0
  4. package/dist/core-client.d.ts.map +1 -0
  5. package/dist/core-client.js +1036 -0
  6. package/dist/core-client.js.map +1 -0
  7. package/dist/core-link-cursor-storage.d.ts +32 -0
  8. package/dist/core-link-cursor-storage.d.ts.map +1 -0
  9. package/dist/core-link-cursor-storage.js +66 -0
  10. package/dist/core-link-cursor-storage.js.map +1 -0
  11. package/dist/core-link-frames.d.ts +1123 -0
  12. package/dist/core-link-frames.d.ts.map +1 -0
  13. package/dist/core-link-frames.js +349 -0
  14. package/dist/core-link-frames.js.map +1 -0
  15. package/dist/core-link-socket.d.ts +54 -0
  16. package/dist/core-link-socket.d.ts.map +1 -0
  17. package/dist/core-link-socket.js +74 -0
  18. package/dist/core-link-socket.js.map +1 -0
  19. package/dist/core-link-transport.d.ts +177 -0
  20. package/dist/core-link-transport.d.ts.map +1 -0
  21. package/dist/core-link-transport.js +432 -0
  22. package/dist/core-link-transport.js.map +1 -0
  23. package/dist/core-registration-blob.d.ts +52 -0
  24. package/dist/core-registration-blob.d.ts.map +1 -0
  25. package/dist/core-registration-blob.js +61 -0
  26. package/dist/core-registration-blob.js.map +1 -0
  27. package/dist/core-session.d.ts +321 -0
  28. package/dist/core-session.d.ts.map +1 -0
  29. package/dist/core-session.js +660 -0
  30. package/dist/core-session.js.map +1 -0
  31. package/dist/durable-core-client.d.ts +172 -0
  32. package/dist/durable-core-client.d.ts.map +1 -0
  33. package/dist/durable-core-client.js +264 -0
  34. package/dist/durable-core-client.js.map +1 -0
  35. package/dist/terminal-screen.d.ts +139 -0
  36. package/dist/terminal-screen.d.ts.map +1 -0
  37. package/dist/terminal-screen.js +807 -0
  38. package/dist/terminal-screen.js.map +1 -0
  39. package/package.json +51 -0
@@ -0,0 +1,52 @@
1
+ import type { CoreLinkTlsMaterial } from "./core-link-socket.ts";
2
+ /**
3
+ * A decoded registration blob. `endpoint` is the Core's `wss://host:port` core
4
+ * link; `label` is the machine's own suggestion for an alias and is not used by
5
+ * anything here.
6
+ */
7
+ export type CoreRegistrationBlob = {
8
+ endpoint: string;
9
+ label?: string;
10
+ caCert: string;
11
+ clientCert: string;
12
+ clientKey: string;
13
+ bearer: string;
14
+ };
15
+ /**
16
+ * Everything a blob says about how to reach one Core, unpacked into the parts a
17
+ * client dials with — and **exposed rather than merely used**.
18
+ *
19
+ * `httpsBaseUrl` is the reason this type exists instead of a private helper.
20
+ * A Core's file surface will mount on the same mTLS server the WebSocket
21
+ * already listens on (#129 F2), and the same `tls` material authenticates both,
22
+ * so the connection a client holds is "a Core connection" and not "a socket"
23
+ * (#129's phase-3 guardrail 1). Naming the base URL now costs nothing and keeps
24
+ * that from being a breaking change to a published surface later. Nothing in
25
+ * this phase reads it.
26
+ */
27
+ export type CoreConnection = {
28
+ /** The `wss://` (or `ws://`) core-link URL to dial. */
29
+ url: string;
30
+ /**
31
+ * The `https://` origin of the same Core, derived by swapping the scheme —
32
+ * the server that terminates the WebSocket is the server that will answer
33
+ * `/v1/…`. No path, no trailing slash.
34
+ */
35
+ httpsBaseUrl: string;
36
+ /** The PEM material for the mTLS handshake, or null for a plain `ws://` dial. */
37
+ tls: CoreLinkTlsMaterial | null;
38
+ /** The signed bearer presented in the `auth` frame right after the handshake. */
39
+ bearer: string;
40
+ };
41
+ /**
42
+ * Unpack a registration blob into a {@link CoreConnection}.
43
+ *
44
+ * `ws://` is accepted here even though a blob's endpoint must be `wss://` for a
45
+ * real Core (ADR 0002, and the Panel's decoder refuses anything else): this
46
+ * function converts a shape, it does not police one, and a loopback endpoint is
47
+ * how the Core's own tests and a local rig dial. A `ws://` endpoint yields
48
+ * `tls: null` — there is no handshake to pin — and the bearer is still
49
+ * presented, because the `auth` frame is the app-layer session either way.
50
+ */
51
+ export declare function coreConnectionFromBlob(blob: CoreRegistrationBlob): CoreConnection;
52
+ //# sourceMappingURL=core-registration-blob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-registration-blob.d.ts","sourceRoot":"","sources":["../src/core-registration-blob.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEjE;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,uDAAuD;IACvD,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,iFAAiF;IACjF,GAAG,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAChC,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,cAAc,CAWjF"}
@@ -0,0 +1,61 @@
1
+ // The registration blob, as a Core client reads it.
2
+ //
3
+ // `actana setup` emits one base64 artifact per machine — `{endpoint, caCert,
4
+ // clientCert, clientKey, bearer}` (CONTEXT.md "Registration blob"). It is the
5
+ // whole of what a Core client needs to reach a Core: the endpoint to dial, the
6
+ // PEM material for the mTLS handshake, and the bearer for the `auth` frame
7
+ // (#129 D1).
8
+ //
9
+ // Two deliberate boundaries.
10
+ //
11
+ // **The SDK takes a blob object, never a file** (#129 D9). Where the blob is
12
+ // kept — `~/.config/actana/cores/<name>.txt`, an environment variable, a
13
+ // Panel's encrypted store — is the caller's business, and the CLI's config
14
+ // resolution is its own ticket. Nothing here reads a path or base64-decodes a
15
+ // paste.
16
+ //
17
+ // **The shape is declared here, not imported from `@actana/shared`.**
18
+ // `packages/shared` is private and stays private ([ADR 0025][adr] D4), so an
19
+ // SDK that imported `RegistrationBlob` from it would be a published package
20
+ // with a dependency nobody outside this repository can resolve — the exact
21
+ // alternative that ADR rejected. The two declarations are structurally
22
+ // identical, so a Panel holding a `RegistrationBlob` passes it here with no
23
+ // conversion, and the field names are the wire's rather than either side's.
24
+ //
25
+ // [adr]: ../../docs/adr/0025-the-protocol-ships-with-the-client.md
26
+ /**
27
+ * Unpack a registration blob into a {@link CoreConnection}.
28
+ *
29
+ * `ws://` is accepted here even though a blob's endpoint must be `wss://` for a
30
+ * real Core (ADR 0002, and the Panel's decoder refuses anything else): this
31
+ * function converts a shape, it does not police one, and a loopback endpoint is
32
+ * how the Core's own tests and a local rig dial. A `ws://` endpoint yields
33
+ * `tls: null` — there is no handshake to pin — and the bearer is still
34
+ * presented, because the `auth` frame is the app-layer session either way.
35
+ */
36
+ export function coreConnectionFromBlob(blob) {
37
+ const url = blob.endpoint.trim();
38
+ const secure = url.startsWith("wss://");
39
+ return {
40
+ url,
41
+ httpsBaseUrl: httpsBaseUrlFor(url),
42
+ tls: secure
43
+ ? { ca: blob.caCert, cert: blob.clientCert, key: blob.clientKey }
44
+ : null,
45
+ bearer: blob.bearer,
46
+ };
47
+ }
48
+ /**
49
+ * `wss://host:port` → `https://host:port`, `ws://…` → `http://…`. Anything
50
+ * else is returned unchanged rather than guessed at — a caller that handed this
51
+ * a URL with no WebSocket scheme knows something about its Core that this
52
+ * function does not.
53
+ */
54
+ function httpsBaseUrlFor(url) {
55
+ if (url.startsWith("wss://"))
56
+ return `https://${url.slice("wss://".length)}`.replace(/\/+$/, "");
57
+ if (url.startsWith("ws://"))
58
+ return `http://${url.slice("ws://".length)}`.replace(/\/+$/, "");
59
+ return url;
60
+ }
61
+ //# sourceMappingURL=core-registration-blob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-registration-blob.js","sourceRoot":"","sources":["../src/core-registration-blob.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,+EAA+E;AAC/E,2EAA2E;AAC3E,aAAa;AACb,EAAE;AACF,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,yEAAyE;AACzE,2EAA2E;AAC3E,8EAA8E;AAC9E,SAAS;AACT,EAAE;AACF,sEAAsE;AACtE,6EAA6E;AAC7E,4EAA4E;AAC5E,2EAA2E;AAC3E,uEAAuE;AACvE,4EAA4E;AAC5E,4EAA4E;AAC5E,EAAE;AACF,mEAAmE;AA6CnE;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAA0B;IAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACxC,OAAO;QACL,GAAG;QACH,YAAY,EAAE,eAAe,CAAC,GAAG,CAAC;QAClC,GAAG,EAAE,MAAM;YACT,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE;YACjE,CAAC,CAAC,IAAI;QACR,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,GAAW;IAClC,IAAI,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,WAAW,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACjG,IAAI,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,UAAU,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9F,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,321 @@
1
+ import type { CoreLinkPtySpawnHarness } from "./core-link-frames.ts";
2
+ import type { CoreClient } from "./core-client.ts";
3
+ type Unsubscribe = () => void;
4
+ /**
5
+ * The command a fresh Session starts with, per harness, when the caller names
6
+ * none.
7
+ *
8
+ * The first token has to be that harness's canonical binary or the Core refuses
9
+ * the spawn — that is the allow-list, and it is enforced there, not here. What
10
+ * this table adds beyond the binary is the one flag a harness needs for the
11
+ * Core to hear about it at all: `codex` reports its lifecycle through hooks only
12
+ * when started with `--enable hooks`, and a Session that never reports is a
13
+ * Session {@link CoreSession.waitForIdle} waits on forever.
14
+ *
15
+ * A caller wanting anything else — a model, a resumed session id — passes
16
+ * `command` and takes the Core's answer on it.
17
+ */
18
+ export declare const HARNESS_LAUNCH_COMMANDS: Readonly<Record<CoreLinkPtySpawnHarness, string>>;
19
+ /**
20
+ * The statuses that mean the harness has stopped and is waiting on a human.
21
+ *
22
+ * `finished` is a completed turn, `needs-input` a permission prompt or a
23
+ * question, `interrupted` an escape, `terminated` a dead process, and
24
+ * `disconnected` a Core that restarted underneath the Session. Every one of them
25
+ * is a state that does not leave on its own, which is the property
26
+ * {@link CoreSession.waitForIdle} is waiting for — not "finished", which would
27
+ * hang on the question a caller could have answered.
28
+ */
29
+ export declare const SETTLED_SESSION_STATUSES: ReadonlySet<string>;
30
+ /**
31
+ * How a failed status read is re-asked: this many further attempts, this long
32
+ * apart.
33
+ *
34
+ * A retry of a *read*, and only of a read. It re-asks the Core a question whose
35
+ * answer the Core already settled on — it does not retry a prompt, a keystroke
36
+ * or a spawn, and it cannot make a Session look idle sooner than the Core says
37
+ * it is. The reason it has to exist: `needs-input`, `interrupted` and
38
+ * `terminated` reach this layer only as `task:updated`, and that event is
39
+ * appended once. Swallowing the read that failed on it leaves
40
+ * {@link CoreSession.waitForIdle} waiting for a report that will not be made
41
+ * again, and by design there is no deadline to end that wait.
42
+ *
43
+ * Bounded rather than indefinite: a link that is still down after three tries a
44
+ * quarter-second apart is not going to be talked round by a fourth, and a
45
+ * Session that polls forever is the busy-loop version of the timer #191 deleted.
46
+ */
47
+ export declare const STATUS_READ_RETRIES = 3;
48
+ export declare const STATUS_READ_RETRY_MS = 250;
49
+ export type CoreSessionStartOptions = {
50
+ /**
51
+ * The Project to start this Session in. Either this or {@link taskId}: with a
52
+ * `projectId` a Task row is created on the Core first, because a Session's
53
+ * status lives on that row and a spawn naming a row that does not exist
54
+ * reports nothing back.
55
+ */
56
+ projectId?: string;
57
+ /** An existing Task to start a Session for. Either this or {@link projectId}. */
58
+ taskId?: string;
59
+ /** Title for the Task created from {@link projectId}. Ignored with a `taskId`. */
60
+ title?: string;
61
+ /**
62
+ * The working directory on the **Core's** machine.
63
+ *
64
+ * A machine path, validatable only there: the Core resolves it through
65
+ * `realpath` and refuses it unless it lands inside a registered Project root.
66
+ * Nothing here checks it — this process may not even be on that machine — so a
67
+ * bad path comes back as a rejected {@link start}, which is the design.
68
+ */
69
+ cwd: string;
70
+ /** Which harness to run. */
71
+ harness: CoreLinkPtySpawnHarness;
72
+ /**
73
+ * The starting prompt, handed to the Core as `initialInput`.
74
+ *
75
+ * Text, and no timing with it. The Core waits for the harness's TUI to settle,
76
+ * answers whatever dialog it opened, writes this, and sends the carriage
77
+ * return separately (ADR 0026). Omit it to start a Session with no prompt and
78
+ * drive it with {@link CoreSession.send}.
79
+ */
80
+ prompt?: string;
81
+ /**
82
+ * Override the launch command. Defaults to {@link HARNESS_LAUNCH_COMMANDS}.
83
+ * The Core allow-lists the binary and every flag; a command it does not accept
84
+ * rejects the spawn rather than being trimmed here.
85
+ */
86
+ command?: string;
87
+ /** PTY width. The screen is built at the same size, or the two disagree about wrapping. */
88
+ cols?: number;
89
+ /** PTY height. Same. */
90
+ rows?: number;
91
+ /** How many scrolled-off lines {@link CoreSession.screen} keeps. */
92
+ scrollback?: number;
93
+ /** Start the harness with permission prompts disabled. See {@link HARNESS_SKIP_PERMISSION_FLAGS}. */
94
+ dangerouslySkipPermissions?: boolean;
95
+ /** The Core's theme hint for the harness, so its output matches a Panel's. */
96
+ theme?: "dark" | "light";
97
+ /**
98
+ * Subscribe this client to the Core's event log if nothing has yet, so
99
+ * {@link CoreSession.waitForIdle} and {@link CoreSession.onStatus} have
100
+ * something to work from. Default true.
101
+ *
102
+ * Set it false when the events are already arriving by another route and the
103
+ * replay tail is not wanted — a {@link DurableCoreClient} subscribes itself,
104
+ * and this checks before sending anything, so the flag is for the case where a
105
+ * caller is doing something the check cannot see.
106
+ */
107
+ subscribeToEvents?: boolean;
108
+ };
109
+ /** What a Session settled on. */
110
+ export type CoreSessionIdle = {
111
+ /** The Core's status for this Session — one of {@link SETTLED_SESSION_STATUSES}. */
112
+ status: string;
113
+ /** True when the harness's process exited rather than settling on a status. */
114
+ exited: boolean;
115
+ /** The process's exit code, when it exited. */
116
+ exitCode?: number;
117
+ };
118
+ export type CoreSessionWaitOptions = {
119
+ /**
120
+ * Give up after this long, in ms. **No deadline by default**, and that is not
121
+ * an oversight: a turn takes as long as the work takes, and a default that
122
+ * fires would report a healthy Session as broken — the same lie the flat
123
+ * timer #191 deleted used to tell. A caller that needs a deadline knows what
124
+ * its own is.
125
+ */
126
+ timeoutMs?: number;
127
+ };
128
+ /** The Core refused to start this Session. Carries the Core's own reason. */
129
+ export declare class CoreSessionStartError extends Error {
130
+ constructor(message: string, options?: {
131
+ cause?: unknown;
132
+ });
133
+ }
134
+ /**
135
+ * One Session on one Core, driven programmatically.
136
+ *
137
+ * Built by {@link start}. Holds a screen fed from the Session's PTY, the Core's
138
+ * last reported status for it, and the listeners that keep both current — so
139
+ * it must be released with {@link dispose} (or {@link kill}, which disposes)
140
+ * when the caller is done, or those listeners outlive it on the client.
141
+ */
142
+ export declare class CoreSession {
143
+ /** The Task this Session belongs to. Its status is the Session's status. */
144
+ readonly taskId: string;
145
+ /** The Core's id for this Session's PTY. */
146
+ readonly ptyId: string;
147
+ /** The harness running in it. */
148
+ readonly harness: CoreLinkPtySpawnHarness;
149
+ /** The command the Core was asked to start, after defaulting. */
150
+ readonly command: string;
151
+ private readonly client;
152
+ private readonly terminal;
153
+ private readonly unsubscribes;
154
+ private readonly dataListeners;
155
+ private readonly exitListeners;
156
+ private readonly statusListeners;
157
+ private readonly idleWaiters;
158
+ /**
159
+ * The Core's last reported status, or null before one has been *observed*.
160
+ *
161
+ * Null rather than the status the Task carried when this Session started, and
162
+ * the distinction is what makes {@link waitForIdle} correct: a caller starting
163
+ * a Session on a Task that was already `finished` is waiting for the next turn
164
+ * to end, not being told about the last one. Only a status learned from an
165
+ * event after {@link start} lands here.
166
+ */
167
+ private lastStatus;
168
+ private exit;
169
+ private disposed;
170
+ /** A status read is in flight; another event arrived while it was. */
171
+ private statusReadInFlight;
172
+ private statusReadAgain;
173
+ /** Re-asks left for the read that failed, and the timer carrying the next. */
174
+ private statusReadRetriesLeft;
175
+ private statusRetryTimer;
176
+ private constructor();
177
+ /**
178
+ * Start a Session and return once the Core has one running.
179
+ *
180
+ * What happens, in order: a Task row is created when the caller named a
181
+ * Project rather than a Task; the client is subscribed to the Core's event log
182
+ * if nothing has done that yet; the PTY's byte stream is wired up *before* the
183
+ * spawn goes out; and the spawn carries the prompt as `initialInput` for the
184
+ * Core to deliver.
185
+ *
186
+ * The stream is wired first on purpose. A harness starts printing its banner
187
+ * immediately, and on a Core that fans output out by subscription the
188
+ * connection that spawned a PTY is subscribed to it before the answer is even
189
+ * written — so the first bytes can be on the wire before this side knows the
190
+ * PTY's id. They are held and replayed into the screen once it is known, which
191
+ * is the difference between a transcript that starts at the beginning and one
192
+ * that starts wherever the round trip happened to end.
193
+ *
194
+ * Rejects with the Core's own message when the Core refuses: a working
195
+ * directory outside every registered Project root, a command whose binary is
196
+ * not that harness's, a flag that is not allow-listed, a harness that is not
197
+ * installed on that machine.
198
+ */
199
+ static start(client: CoreClient, opts: CoreSessionStartOptions): Promise<CoreSession>;
200
+ /**
201
+ * Write to the Session, exactly these bytes and nothing else.
202
+ *
203
+ * The equivalent of typing, not of prompting. Nothing is appended: no carriage
204
+ * return, no delay, no waiting for the harness to look ready. That restraint
205
+ * is the rule rather than a gap — a client that decided when to press Enter
206
+ * would be doing prompt delivery, which is the Core's (ADR 0026), and would do
207
+ * it differently from every other client. A *starting* prompt goes through
208
+ * {@link CoreSessionStartOptions.prompt}, where the Core owns the schedule.
209
+ *
210
+ * What this is for is everything after: answering the numbered option of a
211
+ * question the harness asked (`send("2")` then `send("\r")`), an escape
212
+ * (`send("\u001B")`), a follow-up typed into a harness already at its prompt.
213
+ *
214
+ * Resolves false when the Core did not accept the write — a PTY that has
215
+ * exited. Rejects when another Core client holds this Session's lock.
216
+ */
217
+ send(text: string): Promise<boolean>;
218
+ /**
219
+ * Every chunk of this Session's output, as it arrives.
220
+ *
221
+ * Raw PTY bytes, escape sequences included — the same stream the screen is
222
+ * built from. A caller wanting the rendered text wants {@link screen}; this is
223
+ * for one that is streaming somewhere else.
224
+ */
225
+ onData(cb: (chunk: string) => void): Unsubscribe;
226
+ /** The harness's process exited. Fires once. */
227
+ onExit(cb: (exit: {
228
+ exitCode: number;
229
+ signal?: number;
230
+ }) => void): Unsubscribe;
231
+ /** The Core reported a new status for this Session. */
232
+ onStatus(cb: (status: string) => void): Unsubscribe;
233
+ /**
234
+ * What a terminal would be showing for this Session, **including the lines
235
+ * that have scrolled off the top of it**.
236
+ *
237
+ * The scrolled-off part is not a bonus: a harness's conversation left the
238
+ * screen long ago, so the transcript is the scrollback and a caller reading
239
+ * only the visible rows reads a status bar. See `terminal-screen.ts` for what
240
+ * is emulated and what an erase costs.
241
+ *
242
+ * **Read it while the Session is alive.** A harness that runs full-screen
243
+ * leaves the alternate screen when it quits, and what a terminal shows after
244
+ * that is the main buffer — which is where nothing was ever printed. Reading
245
+ * before {@link kill}, not after, is the difference between the transcript and
246
+ * an empty string. (Claude Code 2.1.228 runs full-screen, so this is the
247
+ * ordinary case rather than an exotic one.)
248
+ */
249
+ screen(): string;
250
+ /** Only the rows on screen right now — for reading a dialog rather than a transcript. */
251
+ viewport(): string;
252
+ /** The screen as an array of lines, scrollback first. */
253
+ lines(): string[];
254
+ /** The Core's last reported status, or null before one has been observed. */
255
+ status(): string | null;
256
+ /** How the harness's process ended, or null while it is running. */
257
+ exitStatus(): {
258
+ exitCode: number;
259
+ signal?: number;
260
+ } | null;
261
+ /**
262
+ * Wait until the Core reports this Session settled — the harness finished its
263
+ * turn, asked a question, was interrupted, or died.
264
+ *
265
+ * **The Core's report, not a guess from the byte stream.** The harness's own
266
+ * lifecycle hooks move the Session's status on the Core, the change lands in
267
+ * the event log, and this is watching for it. Nothing here inspects output for
268
+ * quietness: that is a timing decision, it belongs to the Core, and the flat
269
+ * timer that used to make it here is what #191 deleted.
270
+ *
271
+ * Only statuses observed *after* this Session started count, so starting one
272
+ * on a Task that was already `finished` waits for this turn rather than
273
+ * returning last turn's answer. An exit resolves it too — a harness that died
274
+ * is not going to report anything else.
275
+ *
276
+ * Resolves as soon as it can: if the Session has already settled by the time
277
+ * this is called, it answers from what it saw.
278
+ *
279
+ * With no {@link CoreSessionWaitOptions.timeoutMs} this waits indefinitely, on
280
+ * purpose. A status read that fails is re-asked, so a link that blinks does
281
+ * not cost the report; a link that stays down does, and nothing here invents a
282
+ * status the Core never sent. A caller that must not hang on a broken Core
283
+ * passes a deadline it chose itself.
284
+ */
285
+ waitForIdle(opts?: CoreSessionWaitOptions): Promise<CoreSessionIdle>;
286
+ /** Resize the PTY and the screen together, so both agree about wrapping. */
287
+ resize(cols: number, rows: number): Promise<boolean>;
288
+ /** Kill the harness's process, then release this Session's listeners. */
289
+ kill(): Promise<boolean>;
290
+ /**
291
+ * Release the listeners this Session holds on the client, leaving the harness
292
+ * running. The screen stops advancing; the Core carries on.
293
+ */
294
+ dispose(): void;
295
+ private ingest;
296
+ private ingestExit;
297
+ /** @internal — called by {@link start} for events held during the spawn. */
298
+ private onCoreEvent;
299
+ /**
300
+ * Read this Session's status back off the Core.
301
+ *
302
+ * The `task:updated` event says a row moved, not what it moved to, and the
303
+ * Core owns the answer — so it is asked. Coalesced, because a turn's worth of
304
+ * hook events arrives in a burst and each one would otherwise be its own round
305
+ * trip: a read already in flight is re-run once at the end rather than queued
306
+ * behind itself.
307
+ *
308
+ * A read that fails is re-asked ({@link STATUS_READ_RETRIES}), because on
309
+ * `needs-input`, `interrupted` and `terminated` there is no second event to
310
+ * carry the news.
311
+ */
312
+ private readStatus;
313
+ /** The next re-ask of a read that failed. Cleared by {@link dispose}. */
314
+ private scheduleStatusRetry;
315
+ private noteStatus;
316
+ /** What {@link waitForIdle} would answer right now, or null if it must wait. */
317
+ private settledNow;
318
+ private releaseWaiters;
319
+ }
320
+ export {};
321
+ //# sourceMappingURL=core-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-session.d.ts","sourceRoot":"","sources":["../src/core-session.ts"],"names":[],"mappings":"AAkDA,OAAO,KAAK,EAEV,uBAAuB,EAExB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,KAAK,WAAW,GAAG,MAAM,IAAI,CAAC;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAKrF,CAAC;AAkBF;;;;;;;;;GASG;AACH,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAMvD,CAAC;AAgBH;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,4BAA4B;IAC5B,OAAO,EAAE,uBAAuB,CAAC;IACjC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qGAAqG;IACrG,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACzB;;;;;;;;;OASG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,iCAAiC;AACjC,MAAM,MAAM,eAAe,GAAG;IAC5B,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,6EAA6E;AAC7E,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED;;;;;;;GAOG;AACH,qBAAa,WAAW;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAElD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAsC;IACpE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoE;IAClG,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuC;IACvE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA8C;IAE1E;;;;;;;;OAQG;IACH,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,IAAI,CAAsD;IAClE,OAAO,CAAC,QAAQ,CAAS;IACzB,sEAAsE;IACtE,OAAO,CAAC,kBAAkB,CAAS;IACnC,OAAO,CAAC,eAAe,CAAS;IAChC,8EAA8E;IAC9E,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,gBAAgB,CAA8C;IAEtE,OAAO;IAgBP;;;;;;;;;;;;;;;;;;;;;OAqBG;WACU,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC;IAiH3F;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIpC;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,WAAW;IAKhD,gDAAgD;IAChD,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,WAAW;IAK9E,uDAAuD;IACvD,QAAQ,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,WAAW;IAKnD;;;;;;;;;;;;;;;OAeG;IACH,MAAM,IAAI,MAAM;IAIhB,yFAAyF;IACzF,QAAQ,IAAI,MAAM;IAIlB,yDAAyD;IACzD,KAAK,IAAI,MAAM,EAAE;IAIjB,6EAA6E;IAC7E,MAAM,IAAI,MAAM,GAAG,IAAI;IAIvB,oEAAoE;IACpE,UAAU,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAM1D;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,WAAW,CAAC,IAAI,GAAE,sBAA2B,GAAG,OAAO,CAAC,eAAe,CAAC;IA0BxE,4EAA4E;IACtE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM1D,yEAAyE;IACnE,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAQ9B;;;OAGG;IACH,OAAO,IAAI,IAAI;IAwBf,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,UAAU;IAgBlB,4EAA4E;IAC5E,OAAO,CAAC,WAAW;IAYnB;;;;;;;;;;;;OAYG;YACW,UAAU;IAiCxB,yEAAyE;IACzE,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,UAAU;IAalB,gFAAgF;IAChF,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,cAAc;CAKvB"}