@ellipsis-dev/sdk 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,208 @@
1
+ // src/stream/index.ts
2
+ var SESSION_STREAM_PROTOCOL_VERSION = 3;
3
+ var WS_CLOSE_NORMAL = 1e3;
4
+ var WS_CLOSE_GOING_AWAY = 1001;
5
+ var WS_CLOSE_UNSUPPORTED_PROTOCOL_VERSION = 1002;
6
+ var WS_CLOSE_NO_PROTOCOL = 1003;
7
+ var WS_CLOSE_AUTH_FAILED = 1008;
8
+ var WS_CLOSE_SERVER_ERROR = 1011;
9
+ var WS_CLOSE_OVER_CAPACITY = 1013;
10
+ function sessionStatusWord(session) {
11
+ return session.surface?.status ?? session.status;
12
+ }
13
+ var StreamUnavailableError = class extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "StreamUnavailableError";
17
+ }
18
+ };
19
+ var StreamAuthError = class extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = "StreamAuthError";
23
+ }
24
+ };
25
+ function streamQuery(afterSeq) {
26
+ const base = `protocol=${SESSION_STREAM_PROTOCOL_VERSION}`;
27
+ return afterSeq > 0 ? `${base}&after_seq=${afterSeq}` : base;
28
+ }
29
+ var HEARTBEAT_TIMEOUT_MS = 45e3;
30
+ var DEFAULT_MAX_RECONNECTS = 5;
31
+ function classifyCloseCode(code) {
32
+ switch (code) {
33
+ case WS_CLOSE_NORMAL:
34
+ return "normal";
35
+ case WS_CLOSE_AUTH_FAILED:
36
+ case 4401:
37
+ // dashboard ticket door: bad/expired ticket
38
+ case 4403:
39
+ return "auth";
40
+ case WS_CLOSE_UNSUPPORTED_PROTOCOL_VERSION:
41
+ case WS_CLOSE_NO_PROTOCOL:
42
+ return "unsupported";
43
+ default:
44
+ return "retry";
45
+ }
46
+ }
47
+ function nextReconnectDelayMs(attempt) {
48
+ const base = 500;
49
+ const max = 8e3;
50
+ return Math.min(max, base * 2 ** Math.max(0, attempt - 1));
51
+ }
52
+ function decideReconnect(params) {
53
+ const { closeKind, everReceivedFrame, attempt, maxReconnects } = params;
54
+ if (closeKind === "auth") return { action: "fail-auth" };
55
+ if (closeKind === "unsupported") return { action: "fallback" };
56
+ const cap = everReceivedFrame ? maxReconnects : Math.min(2, maxReconnects);
57
+ if (attempt >= cap) return { action: "fallback" };
58
+ return { action: "reconnect", delayMs: nextReconnectDelayMs(attempt) };
59
+ }
60
+ function connectOnce(sock, emit, signal) {
61
+ return new Promise((resolve) => {
62
+ let settled = false;
63
+ let heartbeat;
64
+ const finish = (result) => {
65
+ if (settled) return;
66
+ settled = true;
67
+ if (heartbeat) clearTimeout(heartbeat);
68
+ if (signal) signal.removeEventListener("abort", onAbort);
69
+ sock.close();
70
+ resolve(result);
71
+ };
72
+ const onAbort = () => finish({ kind: "aborted" });
73
+ const bumpHeartbeat = () => {
74
+ if (heartbeat) clearTimeout(heartbeat);
75
+ heartbeat = setTimeout(
76
+ () => finish({ kind: "error", err: new Error("heartbeat timeout") }),
77
+ HEARTBEAT_TIMEOUT_MS
78
+ );
79
+ };
80
+ if (signal) {
81
+ if (signal.aborted) {
82
+ finish({ kind: "aborted" });
83
+ return;
84
+ }
85
+ signal.addEventListener("abort", onAbort);
86
+ }
87
+ sock.onOpen(() => bumpHeartbeat());
88
+ sock.onMessage((data) => {
89
+ bumpHeartbeat();
90
+ let frame;
91
+ try {
92
+ frame = JSON.parse(data);
93
+ } catch {
94
+ return;
95
+ }
96
+ emit(frame);
97
+ if (frame.type === "done") {
98
+ finish({ kind: "done" });
99
+ } else if (frame.type === "error") {
100
+ finish({
101
+ kind: "frameError",
102
+ message: frame.message ?? "stream error"
103
+ });
104
+ }
105
+ });
106
+ sock.onClose((code) => finish({ kind: "closed", code }));
107
+ sock.onError((err) => finish({ kind: "error", err }));
108
+ });
109
+ }
110
+ function sleep(ms, signal) {
111
+ return new Promise((resolve) => {
112
+ const timer = setTimeout(resolve, ms);
113
+ signal?.addEventListener(
114
+ "abort",
115
+ () => {
116
+ clearTimeout(timer);
117
+ resolve();
118
+ },
119
+ { once: true }
120
+ );
121
+ });
122
+ }
123
+ async function streamSession(opts) {
124
+ const maxReconnects = opts.maxReconnects ?? DEFAULT_MAX_RECONNECTS;
125
+ let afterSeq = opts.afterSeq ?? 0;
126
+ let everReceivedFrame = false;
127
+ let attempt = 0;
128
+ let lastStatusWord = "";
129
+ let lastExitStatus = null;
130
+ const emit = (frame) => {
131
+ everReceivedFrame = true;
132
+ attempt = 0;
133
+ if (frame.type === "records_append") {
134
+ const records = frame.records;
135
+ for (const record of records) {
136
+ if (typeof record.feed_seq === "number") {
137
+ afterSeq = Math.max(afterSeq, record.feed_seq);
138
+ }
139
+ }
140
+ } else if (frame.type === "snapshot" || frame.type === "session") {
141
+ const session = frame.session;
142
+ lastStatusWord = sessionStatusWord(session);
143
+ lastExitStatus = session.exit_status ?? null;
144
+ }
145
+ opts.onFrame(frame);
146
+ };
147
+ for (; ; ) {
148
+ if (opts.signal?.aborted) return { type: "aborted" };
149
+ let res;
150
+ try {
151
+ const sock = await opts.openSocket({
152
+ sessionId: opts.sessionId,
153
+ afterSeq,
154
+ query: streamQuery(afterSeq)
155
+ });
156
+ res = await connectOnce(sock, emit, opts.signal);
157
+ } catch (err) {
158
+ res = {
159
+ kind: "error",
160
+ err: err instanceof Error ? err : new Error(String(err))
161
+ };
162
+ }
163
+ if (res.kind === "done") {
164
+ return {
165
+ type: "done",
166
+ status: lastStatusWord,
167
+ exitStatus: lastExitStatus
168
+ };
169
+ }
170
+ if (res.kind === "frameError")
171
+ return { type: "error", message: res.message };
172
+ if (res.kind === "aborted") return { type: "aborted" };
173
+ attempt++;
174
+ const decision = decideReconnect({
175
+ closeKind: res.kind === "closed" ? classifyCloseCode(res.code) : void 0,
176
+ everReceivedFrame,
177
+ attempt,
178
+ maxReconnects
179
+ });
180
+ if (decision.action === "fail-auth") {
181
+ throw new StreamAuthError("not authorized to stream this session");
182
+ }
183
+ if (decision.action === "fallback") {
184
+ const why = res.kind === "error" ? res.err.message : `stream closed (code ${res.code})`;
185
+ throw new StreamUnavailableError(why);
186
+ }
187
+ await sleep(decision.delayMs ?? 0, opts.signal);
188
+ }
189
+ }
190
+
191
+ export {
192
+ SESSION_STREAM_PROTOCOL_VERSION,
193
+ WS_CLOSE_NORMAL,
194
+ WS_CLOSE_GOING_AWAY,
195
+ WS_CLOSE_UNSUPPORTED_PROTOCOL_VERSION,
196
+ WS_CLOSE_NO_PROTOCOL,
197
+ WS_CLOSE_AUTH_FAILED,
198
+ WS_CLOSE_SERVER_ERROR,
199
+ WS_CLOSE_OVER_CAPACITY,
200
+ sessionStatusWord,
201
+ StreamUnavailableError,
202
+ StreamAuthError,
203
+ streamQuery,
204
+ classifyCloseCode,
205
+ nextReconnectDelayMs,
206
+ decideReconnect,
207
+ streamSession
208
+ };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as components } from './types-DtVn9Yj3.js';
2
- export { A as AgentConfigSource, a as AttributionType, B as BudgetSource, C as ClaudeSessionRecord, b as CodexEvent, d as CodexItem, e as CodexSessionRecord, f as CreateReviewRequest, D as DeltaFrame, g as DoneFrame, E as ErrorFrame, F as Finding, G as GithubAccountSnippet, h as GithubAccountType, H as Harness, i as HeartbeatFrame, L as LifecycleSessionRecord, P as ParentKind, j as PromptBlockedReason, R as RecordsAppendFrame, k as ResolvedReviewScope, l as Review, m as ReviewConfiguration, n as ReviewCounters, o as ReviewFinding, p as ReviewRequester, q as ReviewScope, r as ReviewScopeKind, s as ReviewStage, t as ReviewedCommit, u as ReviewsListResponse, S as SdkAssistantRecord, v as SdkContentBlock, w as SdkRateLimitRecord, x as SdkRecord, y as SdkResultRecord, z as SdkSystemRecord, I as SdkUserRecord, J as SendSessionMessageRequest, K as Session, M as SessionExecution, N as SessionExecutionsListResponse, O as SessionExitStatus, Q as SessionFrame, T as SessionLiveness, U as SessionMessage, V as SessionMessageResponse, W as SessionMessageStatus, X as SessionPr, Y as SessionPrompting, Z as SessionRecord, _ as SessionRecordsListResponse, $ as SessionResponse, a0 as SessionSource, a1 as SessionState, a2 as SessionStatus, a3 as SessionStreamFrame, a4 as SessionSurface, a5 as SessionsListResponse, a6 as SnapshotFrame, a7 as StreamFrame, a8 as TokensInfo, a9 as paths } from './types-DtVn9Yj3.js';
1
+ import { c as components } from './types-DB1hOyQE.js';
2
+ export { A as AgentConfigSource, a as AttributionType, B as BudgetSource, C as ClaudeSessionRecord, b as CodexEvent, d as CodexItem, e as CodexSessionRecord, f as CreateReviewRequest, D as DeltaFrame, g as DoneFrame, E as ErrorFrame, F as Finding, G as GithubAccountSnippet, h as GithubAccountType, H as Harness, i as HeartbeatFrame, L as LifecycleSessionRecord, P as ParentKind, j as PromptBlockedReason, R as RecordsAppendFrame, k as ResolvedReviewScope, l as Review, m as ReviewConfiguration, n as ReviewCounters, o as ReviewFinding, p as ReviewRequester, q as ReviewScope, r as ReviewScopeKind, s as ReviewStage, t as ReviewedCommit, u as ReviewsListResponse, S as SdkAssistantRecord, v as SdkContentBlock, w as SdkRateLimitRecord, x as SdkRecord, y as SdkResultRecord, z as SdkSystemRecord, I as SdkUserRecord, J as SendSessionMessageRequest, K as Session, M as SessionExecution, N as SessionExecutionsListResponse, O as SessionExitStatus, Q as SessionFrame, T as SessionGit, U as SessionLiveness, V as SessionMessage, W as SessionMessageResponse, X as SessionMessageStatus, Y as SessionPrompting, Z as SessionRecord, _ as SessionRecordsListResponse, $ as SessionResponse, a0 as SessionSource, a1 as SessionState, a2 as SessionStatus, a3 as SessionStreamFrame, a4 as SessionSurface, a5 as SessionsListResponse, a6 as SnapshotFrame, a7 as StreamFrame, a8 as TokensInfo, a9 as paths } from './types-DB1hOyQE.js';
3
3
 
4
4
  interface CursorResponse {
5
5
  has_more: boolean;
@@ -1,4 +1,4 @@
1
- import { Z as SessionRecord, aa as CodexFileChangeItem, b as CodexEvent, ab as CodexMcpToolCallItem, x as SdkRecord, K as Session, U as SessionMessage, a7 as StreamFrame } from '../types-DtVn9Yj3.js';
1
+ import { Z as SessionRecord, aa as CodexFileChangeItem, b as CodexEvent, ab as CodexMcpToolCallItem, x as SdkRecord, K as Session, V as SessionMessage, a7 as StreamFrame } from '../types-DB1hOyQE.js';
2
2
 
3
3
  interface ChatToolNode {
4
4
  key: string;
@@ -42,9 +42,46 @@ interface ChatTurn {
42
42
  costUsd: number | null;
43
43
  tokens: number | null;
44
44
  resumed: boolean;
45
+ isError: boolean;
45
46
  }
46
47
  declare function groupRecordsToChatTurns(records: readonly SessionRecord[]): ChatTurn[];
47
48
 
49
+ type RecordSlice = {
50
+ feed_seq: number;
51
+ source: string;
52
+ record_type: string;
53
+ payload: Record<string, unknown>;
54
+ session_message_id?: string | null;
55
+ };
56
+ declare function recordSlice(records: readonly SessionRecord[]): readonly RecordSlice[];
57
+ declare function humanDuration(seconds: number): string;
58
+ declare function sessionLogText(recordType: string, payload: Record<string, unknown>): string | null;
59
+ declare function awaitingAgentPhase(records: readonly RecordSlice[]): 'boot' | 'turn' | null;
60
+ declare function deliveredUnechoedSends(records: readonly RecordSlice[]): {
61
+ id: string;
62
+ body: string;
63
+ cancelled: boolean;
64
+ }[];
65
+ type SandboxLogKind = 'step' | 'output' | 'done' | 'failed';
66
+ type SandboxLogLine = {
67
+ key: string;
68
+ kind: SandboxLogKind;
69
+ text: string;
70
+ };
71
+ type SandboxState = {
72
+ headline: string;
73
+ done: boolean;
74
+ readySeconds: number | null;
75
+ sandboxDone: boolean;
76
+ configName: string | null;
77
+ configCommitSha: string | null;
78
+ log: SandboxLogLine[];
79
+ };
80
+ declare function hookPhrase(step: string): string;
81
+ declare function deriveSandboxState(records: readonly RecordSlice[], minFeedSeq: number): SandboxState | null;
82
+ declare function sandboxSummary(sandbox: SandboxState | null): string;
83
+ declare function lastLines(log: readonly SandboxLogLine[], max: number): SandboxLogLine[];
84
+
48
85
  declare function sandboxOutputStep(payload: Record<string, unknown>): string;
49
86
  declare function sandboxOutputLines(payload: Record<string, unknown>): string[];
50
87
  declare function sandboxOutputLine(payload: Record<string, unknown>): string | null;
@@ -104,6 +141,12 @@ interface SessionTranscriptSnapshot {
104
141
  lastEventAt: number | null;
105
142
  conversationOver: boolean;
106
143
  }
144
+ declare function seedTranscriptStore(store: SessionTranscriptStore, seed: {
145
+ session: Session;
146
+ records: readonly SessionRecord[];
147
+ messages?: readonly SessionMessage[] | null;
148
+ earliestFeedSeq?: number | null;
149
+ }): void;
107
150
  declare function emptySessionTranscriptSnapshot(): SessionTranscriptSnapshot;
108
151
  declare class SessionTranscriptStore {
109
152
  private snapshot;
@@ -122,4 +165,4 @@ declare class SessionTranscriptStore {
122
165
  ingest: (rawFrame: StreamFrame) => void;
123
166
  }
124
167
 
125
- export { type ChatNode, type ChatToolNode, type ChatTurn, type EventToItemsOptions, type ItemKind, type SessionTranscriptSnapshot, SessionTranscriptStore, StreamFrame, type TranscriptItem, cacheTierLabel, clampLines, codexChangedPaths, codexEventToItems, codexMcpToolName, collapseToolRuns, emptySessionTranscriptSnapshot, eventToItems, foldCosts, formatDuration, groupRecordsToChatTurns, isConnectVisibleRecord, isConversationOver, lifecycleText, oneLine, pendingToolCalls, recordToItems, resultCostUsd, sandboxOutputLine, sandboxOutputLines, sandboxOutputStep, sandboxPhaseLabel, statusActivityText, summarizeToolInput, toolResultText };
168
+ export { type ChatNode, type ChatToolNode, type ChatTurn, type EventToItemsOptions, type ItemKind, type RecordSlice, type SandboxLogKind, type SandboxLogLine, type SandboxState, type SessionTranscriptSnapshot, SessionTranscriptStore, StreamFrame, type TranscriptItem, awaitingAgentPhase, cacheTierLabel, clampLines, codexChangedPaths, codexEventToItems, codexMcpToolName, collapseToolRuns, deliveredUnechoedSends, deriveSandboxState, emptySessionTranscriptSnapshot, eventToItems, foldCosts, formatDuration, groupRecordsToChatTurns, hookPhrase, humanDuration, isConnectVisibleRecord, isConversationOver, lastLines, lifecycleText, oneLine, pendingToolCalls, recordSlice, recordToItems, resultCostUsd, sandboxOutputLine, sandboxOutputLines, sandboxOutputStep, sandboxPhaseLabel, sandboxSummary, seedTranscriptStore, sessionLogText, statusActivityText, summarizeToolInput, toolResultText };