@opengeni/sdk 0.13.0 → 0.15.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.
- package/README.md +33 -11
- package/dist/index.d.ts +171 -59
- package/dist/index.js +360 -190
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +167 -53
- package/src/errors.ts +13 -0
- package/src/index.ts +26 -2
- package/src/stream.ts +3 -0
- package/src/types.ts +137 -39
- package/src/workspace-control-stream.ts +101 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Framework-agnostic TypeScript SDK for the OpenGeni API: typed client, session lifecycle, SSE event streaming with reconnect + replay-by-sequence, and proxy re-streaming helpers.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
package/src/client.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
-
import { OpenGeniApiError } from "./errors";
|
|
1
|
+
import { OpenGeniApiContractMismatchError, OpenGeniApiError } from "./errors";
|
|
2
2
|
import {
|
|
3
3
|
streamSessionEvents,
|
|
4
4
|
type SessionEventStreamTransport,
|
|
5
5
|
type StreamSessionEventsOptions,
|
|
6
6
|
} from "./stream";
|
|
7
|
+
import {
|
|
8
|
+
streamWorkspaceControlEvents,
|
|
9
|
+
type WorkspaceControlStreamTransport,
|
|
10
|
+
} from "./workspace-control-stream";
|
|
7
11
|
import type {
|
|
8
12
|
AccessContext,
|
|
9
13
|
AddWorkspaceMemberRequest,
|
|
@@ -90,8 +94,15 @@ import type {
|
|
|
90
94
|
SessionMcpCredentialUpdateInput,
|
|
91
95
|
SessionQueueSnapshot,
|
|
92
96
|
SessionQueueMutationResponse,
|
|
97
|
+
ComposerDraft,
|
|
98
|
+
DeleteSessionQueueItemRequest,
|
|
99
|
+
EditSessionQueueItemRequest,
|
|
100
|
+
MoveSessionQueueItemRequest,
|
|
101
|
+
SaveComposerDraftRequest,
|
|
102
|
+
SteerSessionQueueItemRequest,
|
|
93
103
|
SessionControlResponse,
|
|
94
104
|
WorkspaceInferenceControlResponse,
|
|
105
|
+
WorkspaceControlEvent,
|
|
95
106
|
SessionTurn,
|
|
96
107
|
// Stream surfacing (Phase 5): capability negotiation + viewer lifecycle + config.
|
|
97
108
|
SessionCapabilities,
|
|
@@ -161,6 +172,7 @@ import type {
|
|
|
161
172
|
OAuthStartRequest,
|
|
162
173
|
OAuthStartResponse,
|
|
163
174
|
} from "./types";
|
|
175
|
+
import { OPENGENI_API_CONTRACT_HEADER, OPENGENI_API_CONTRACT_REVISION } from "./types";
|
|
164
176
|
|
|
165
177
|
export type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
166
178
|
|
|
@@ -182,8 +194,8 @@ export type SendMessageInput = {
|
|
|
182
194
|
model?: string;
|
|
183
195
|
reasoningEffort?: ReasoningEffort;
|
|
184
196
|
clientEventId?: string;
|
|
185
|
-
|
|
186
|
-
|
|
197
|
+
controlEtag?: string;
|
|
198
|
+
expectedDraftRevision?: number;
|
|
187
199
|
mcpCredentialUpdates?: SessionMcpCredentialUpdateInput[];
|
|
188
200
|
};
|
|
189
201
|
|
|
@@ -276,7 +288,7 @@ export class OpenGeniClient {
|
|
|
276
288
|
search?: string;
|
|
277
289
|
} = {},
|
|
278
290
|
): Promise<SessionListResponse> {
|
|
279
|
-
|
|
291
|
+
return await this.requestJson<SessionListResponse>(
|
|
280
292
|
"GET",
|
|
281
293
|
`/v1/workspaces/${workspaceId}/sessions`,
|
|
282
294
|
undefined,
|
|
@@ -294,23 +306,6 @@ export class OpenGeniClient {
|
|
|
294
306
|
: {}),
|
|
295
307
|
},
|
|
296
308
|
);
|
|
297
|
-
if (Array.isArray(response)) {
|
|
298
|
-
// Rolling/same-major compatibility: an older API ignores `view=page` and
|
|
299
|
-
// returns the historical array. That is an honest one-page projection;
|
|
300
|
-
// never pretend it honored a cursor supplied directly by a caller.
|
|
301
|
-
if (options.cursor) {
|
|
302
|
-
throw new Error("The connected OpenGeni API does not support stable session-page cursors");
|
|
303
|
-
}
|
|
304
|
-
// Older APIs ignore unknown query parameters. Treating their unfiltered
|
|
305
|
-
// array as a successful search would be worse than an explicit rolling-
|
|
306
|
-
// upgrade error (and client-side filtering cannot recover matches beyond
|
|
307
|
-
// the old endpoint's bounded first page).
|
|
308
|
-
if (options.search?.trim()) {
|
|
309
|
-
throw new Error("The connected OpenGeni API does not support session search");
|
|
310
|
-
}
|
|
311
|
-
return { pinned: [], sessions: response, nextCursor: null };
|
|
312
|
-
}
|
|
313
|
-
return response;
|
|
314
309
|
}
|
|
315
310
|
|
|
316
311
|
/** Set this authenticated member's personal workspace pin for a session. */
|
|
@@ -551,14 +546,14 @@ export class OpenGeniClient {
|
|
|
551
546
|
async pauseSession(
|
|
552
547
|
workspaceId: string,
|
|
553
548
|
sessionId: string,
|
|
554
|
-
options: { reason?: string; clientEventId?: string } = {},
|
|
555
|
-
): Promise<
|
|
556
|
-
return (
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
})
|
|
561
|
-
)
|
|
549
|
+
options: { reason?: string; clientEventId?: string; expectedControlEtag?: string } = {},
|
|
550
|
+
): Promise<SessionControlResponse> {
|
|
551
|
+
return await this.controlSession(workspaceId, sessionId, {
|
|
552
|
+
action: "pause",
|
|
553
|
+
clientEventId: options.clientEventId ?? crypto.randomUUID(),
|
|
554
|
+
...(options.reason ? { reason: options.reason } : {}),
|
|
555
|
+
...(options.expectedControlEtag ? { expectedControlEtag: options.expectedControlEtag } : {}),
|
|
556
|
+
});
|
|
562
557
|
}
|
|
563
558
|
|
|
564
559
|
async sendApprovalDecision(
|
|
@@ -619,6 +614,7 @@ export class OpenGeniClient {
|
|
|
619
614
|
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
620
615
|
...(options.signal ? { signal: options.signal } : {}),
|
|
621
616
|
});
|
|
617
|
+
assertApiContractResponse(response);
|
|
622
618
|
if (!response.ok) {
|
|
623
619
|
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
624
620
|
}
|
|
@@ -637,15 +633,73 @@ export class OpenGeniClient {
|
|
|
637
633
|
);
|
|
638
634
|
}
|
|
639
635
|
|
|
640
|
-
async
|
|
636
|
+
async moveQueueItem(
|
|
637
|
+
workspaceId: string,
|
|
638
|
+
sessionId: string,
|
|
639
|
+
turnId: string,
|
|
640
|
+
request: MoveSessionQueueItemRequest,
|
|
641
|
+
): Promise<SessionQueueMutationResponse> {
|
|
642
|
+
return await this.requestJson<SessionQueueMutationResponse>(
|
|
643
|
+
"POST",
|
|
644
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/move`,
|
|
645
|
+
request,
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
async editQueueItem(
|
|
650
|
+
workspaceId: string,
|
|
651
|
+
sessionId: string,
|
|
652
|
+
turnId: string,
|
|
653
|
+
request: EditSessionQueueItemRequest,
|
|
654
|
+
): Promise<SessionQueueMutationResponse> {
|
|
655
|
+
return await this.requestJson<SessionQueueMutationResponse>(
|
|
656
|
+
"POST",
|
|
657
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/edit`,
|
|
658
|
+
request,
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
async steerQueueItem(
|
|
663
|
+
workspaceId: string,
|
|
664
|
+
sessionId: string,
|
|
665
|
+
turnId: string,
|
|
666
|
+
request: SteerSessionQueueItemRequest,
|
|
667
|
+
): Promise<SessionQueueMutationResponse> {
|
|
668
|
+
return await this.requestJson<SessionQueueMutationResponse>(
|
|
669
|
+
"POST",
|
|
670
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/steer`,
|
|
671
|
+
request,
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
async deleteQueueItem(
|
|
641
676
|
workspaceId: string,
|
|
642
677
|
sessionId: string,
|
|
643
678
|
turnId: string,
|
|
644
|
-
request:
|
|
679
|
+
request: DeleteSessionQueueItemRequest,
|
|
645
680
|
): Promise<SessionQueueMutationResponse> {
|
|
646
681
|
return await this.requestJson<SessionQueueMutationResponse>(
|
|
647
682
|
"POST",
|
|
648
|
-
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/
|
|
683
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/queue/${turnId}/delete`,
|
|
684
|
+
request,
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
async getComposerDraft(workspaceId: string, sessionId: string): Promise<ComposerDraft> {
|
|
689
|
+
return await this.requestJson<ComposerDraft>(
|
|
690
|
+
"GET",
|
|
691
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/composer-draft`,
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async saveComposerDraft(
|
|
696
|
+
workspaceId: string,
|
|
697
|
+
sessionId: string,
|
|
698
|
+
request: SaveComposerDraftRequest,
|
|
699
|
+
): Promise<ComposerDraft> {
|
|
700
|
+
return await this.requestJson<ComposerDraft>(
|
|
701
|
+
"PUT",
|
|
702
|
+
`/v1/workspaces/${workspaceId}/sessions/${sessionId}/composer-draft`,
|
|
649
703
|
request,
|
|
650
704
|
);
|
|
651
705
|
}
|
|
@@ -654,12 +708,10 @@ export class OpenGeniClient {
|
|
|
654
708
|
workspaceId: string,
|
|
655
709
|
sessionId: string,
|
|
656
710
|
request: {
|
|
657
|
-
|
|
711
|
+
action: "pause" | "resume";
|
|
658
712
|
reason?: string;
|
|
659
|
-
clientEventId
|
|
660
|
-
|
|
661
|
-
expectedControlGeneration?: number;
|
|
662
|
-
expectedWorkspaceInferenceGeneration?: number;
|
|
713
|
+
clientEventId: string;
|
|
714
|
+
expectedControlEtag?: string;
|
|
663
715
|
},
|
|
664
716
|
): Promise<SessionControlResponse> {
|
|
665
717
|
return await this.requestJson<SessionControlResponse>(
|
|
@@ -672,20 +724,23 @@ export class OpenGeniClient {
|
|
|
672
724
|
async resumeSession(
|
|
673
725
|
workspaceId: string,
|
|
674
726
|
sessionId: string,
|
|
675
|
-
options: { reason?: string; clientEventId?: string } = {},
|
|
727
|
+
options: { reason?: string; clientEventId?: string; expectedControlEtag?: string } = {},
|
|
676
728
|
): Promise<SessionControlResponse> {
|
|
677
|
-
return await this.controlSession(workspaceId, sessionId, {
|
|
729
|
+
return await this.controlSession(workspaceId, sessionId, {
|
|
730
|
+
action: "resume",
|
|
731
|
+
clientEventId: options.clientEventId ?? crypto.randomUUID(),
|
|
732
|
+
...(options.reason ? { reason: options.reason } : {}),
|
|
733
|
+
...(options.expectedControlEtag ? { expectedControlEtag: options.expectedControlEtag } : {}),
|
|
734
|
+
});
|
|
678
735
|
}
|
|
679
736
|
|
|
680
737
|
async setWorkspaceInferenceState(
|
|
681
738
|
workspaceId: string,
|
|
682
739
|
request: {
|
|
683
|
-
|
|
684
|
-
reason
|
|
740
|
+
action: "pause" | "resume";
|
|
741
|
+
reason?: string;
|
|
685
742
|
clientEventId: string;
|
|
686
|
-
|
|
687
|
-
expectedGeneration: number;
|
|
688
|
-
exceptSessionIds?: string[];
|
|
743
|
+
expectedRevision?: number;
|
|
689
744
|
},
|
|
690
745
|
): Promise<WorkspaceInferenceControlResponse> {
|
|
691
746
|
return await this.requestJson<WorkspaceInferenceControlResponse>(
|
|
@@ -695,18 +750,60 @@ export class OpenGeniClient {
|
|
|
695
750
|
);
|
|
696
751
|
}
|
|
697
752
|
|
|
698
|
-
|
|
699
|
-
async deleteQueuedTurn(
|
|
753
|
+
async listWorkspaceControlEvents(
|
|
700
754
|
workspaceId: string,
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
755
|
+
options: { after?: number; limit?: number } = {},
|
|
756
|
+
): Promise<WorkspaceControlEvent[]> {
|
|
757
|
+
return await this.requestJson<WorkspaceControlEvent[]>(
|
|
758
|
+
"GET",
|
|
759
|
+
`/v1/workspaces/${workspaceId}/control-events`,
|
|
760
|
+
undefined,
|
|
761
|
+
{
|
|
762
|
+
...(options.after !== undefined ? { after: String(options.after) } : {}),
|
|
763
|
+
...(options.limit !== undefined ? { limit: String(options.limit) } : {}),
|
|
764
|
+
},
|
|
707
765
|
);
|
|
708
766
|
}
|
|
709
767
|
|
|
768
|
+
streamWorkspaceControlEvents(
|
|
769
|
+
workspaceId: string,
|
|
770
|
+
options: StreamSessionEventsOptions = {},
|
|
771
|
+
): AsyncGenerator<WorkspaceControlEvent, void, void> {
|
|
772
|
+
return streamWorkspaceControlEvents(this.workspaceControlStreamTransport(workspaceId), options);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
workspaceControlStreamTransport(workspaceId: string): WorkspaceControlStreamTransport {
|
|
776
|
+
return {
|
|
777
|
+
openStream: async (after, signal) =>
|
|
778
|
+
await this.openWorkspaceControlEventStream(workspaceId, {
|
|
779
|
+
after,
|
|
780
|
+
...(signal ? { signal } : {}),
|
|
781
|
+
}),
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async openWorkspaceControlEventStream(
|
|
786
|
+
workspaceId: string,
|
|
787
|
+
options: { after?: number; signal?: AbortSignal } = {},
|
|
788
|
+
): Promise<ReadableStream<Uint8Array>> {
|
|
789
|
+
const response = await this.fetchImpl(
|
|
790
|
+
this.url(`/v1/workspaces/${workspaceId}/control-events/stream`, {
|
|
791
|
+
after: String(options.after ?? 0),
|
|
792
|
+
}),
|
|
793
|
+
{
|
|
794
|
+
method: "GET",
|
|
795
|
+
headers: { ...this.headers(), Accept: "text/event-stream" },
|
|
796
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
797
|
+
},
|
|
798
|
+
);
|
|
799
|
+
assertApiContractResponse(response);
|
|
800
|
+
if (!response.ok) throw new OpenGeniApiError(response.status, await safeText(response));
|
|
801
|
+
if (!response.body) {
|
|
802
|
+
throw new OpenGeniApiError(response.status, "SSE response did not include a readable body");
|
|
803
|
+
}
|
|
804
|
+
return response.body;
|
|
805
|
+
}
|
|
806
|
+
|
|
710
807
|
/**
|
|
711
808
|
* Steer: atomically put this prompt at the head and supersede the current
|
|
712
809
|
* inference. The client performs one request and renders server order.
|
|
@@ -1121,7 +1218,14 @@ export class OpenGeniClient {
|
|
|
1121
1218
|
* knowledge of the host setup; safe to call before any auth is established.
|
|
1122
1219
|
*/
|
|
1123
1220
|
async getClientConfig(): Promise<ClientConfig> {
|
|
1124
|
-
|
|
1221
|
+
const config = await this.requestJson<ClientConfig>("GET", "/v1/config/client");
|
|
1222
|
+
if (config.apiContractRevision !== OPENGENI_API_CONTRACT_REVISION) {
|
|
1223
|
+
throw new OpenGeniApiContractMismatchError(
|
|
1224
|
+
OPENGENI_API_CONTRACT_REVISION,
|
|
1225
|
+
String(config.apiContractRevision || "(missing)"),
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
return config;
|
|
1125
1229
|
}
|
|
1126
1230
|
|
|
1127
1231
|
/** The caller's access context: subject, account + workspace grants, defaults. */
|
|
@@ -2058,6 +2162,7 @@ export class OpenGeniClient {
|
|
|
2058
2162
|
return {
|
|
2059
2163
|
...(this.options.apiKey ? { Authorization: `Bearer ${this.options.apiKey}` } : {}),
|
|
2060
2164
|
...extra,
|
|
2165
|
+
[OPENGENI_API_CONTRACT_HEADER]: OPENGENI_API_CONTRACT_REVISION,
|
|
2061
2166
|
};
|
|
2062
2167
|
}
|
|
2063
2168
|
|
|
@@ -2208,6 +2313,7 @@ export class OpenGeniClient {
|
|
|
2208
2313
|
},
|
|
2209
2314
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
2210
2315
|
});
|
|
2316
|
+
assertApiContractResponse(response);
|
|
2211
2317
|
if (!response.ok) {
|
|
2212
2318
|
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
2213
2319
|
}
|
|
@@ -2225,12 +2331,20 @@ export class OpenGeniClient {
|
|
|
2225
2331
|
},
|
|
2226
2332
|
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
2227
2333
|
});
|
|
2334
|
+
assertApiContractResponse(response);
|
|
2228
2335
|
if (!response.ok) {
|
|
2229
2336
|
throw new OpenGeniApiError(response.status, await safeText(response));
|
|
2230
2337
|
}
|
|
2231
2338
|
}
|
|
2232
2339
|
}
|
|
2233
2340
|
|
|
2341
|
+
function assertApiContractResponse(response: Response): void {
|
|
2342
|
+
const actual = response.headers.get(OPENGENI_API_CONTRACT_HEADER);
|
|
2343
|
+
if (actual && actual !== OPENGENI_API_CONTRACT_REVISION) {
|
|
2344
|
+
throw new OpenGeniApiContractMismatchError(OPENGENI_API_CONTRACT_REVISION, actual);
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2234
2348
|
async function safeText(response: Response): Promise<string> {
|
|
2235
2349
|
try {
|
|
2236
2350
|
return await response.text();
|
package/src/errors.ts
CHANGED
|
@@ -11,6 +11,19 @@ export class OpenGeniApiError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/** The browser bundle and API disagree about their state-changing wire contract. */
|
|
15
|
+
export class OpenGeniApiContractMismatchError extends Error {
|
|
16
|
+
readonly expected: string;
|
|
17
|
+
readonly actual: string;
|
|
18
|
+
|
|
19
|
+
constructor(expected: string, actual: string) {
|
|
20
|
+
super(`OpenGeni API contract mismatch: client expects ${expected}, API serves ${actual}`);
|
|
21
|
+
this.name = "OpenGeniApiContractMismatchError";
|
|
22
|
+
this.expected = expected;
|
|
23
|
+
this.actual = actual;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
14
27
|
/** Error for an unrecoverable event-stream condition (not a transient drop). */
|
|
15
28
|
export class OpenGeniStreamError extends Error {
|
|
16
29
|
constructor(message: string) {
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,12 @@ export type {
|
|
|
5
5
|
SendMessageInput,
|
|
6
6
|
SteerMessageResult,
|
|
7
7
|
} from "./client";
|
|
8
|
-
export {
|
|
8
|
+
export {
|
|
9
|
+
OpenGeniApiContractMismatchError,
|
|
10
|
+
OpenGeniApiError,
|
|
11
|
+
OpenGeniStreamError,
|
|
12
|
+
isRetryableStreamError,
|
|
13
|
+
} from "./errors";
|
|
9
14
|
export {
|
|
10
15
|
formatSseEvent,
|
|
11
16
|
proxySessionEventStream,
|
|
@@ -43,7 +48,15 @@ export type {
|
|
|
43
48
|
StreamConnectionState,
|
|
44
49
|
StreamSessionEventsOptions,
|
|
45
50
|
} from "./stream";
|
|
46
|
-
export {
|
|
51
|
+
export { streamWorkspaceControlEvents } from "./workspace-control-stream";
|
|
52
|
+
export type { WorkspaceControlStreamTransport } from "./workspace-control-stream";
|
|
53
|
+
export {
|
|
54
|
+
KNOWN_PERMISSIONS,
|
|
55
|
+
KNOWN_USAGE_EVENT_TYPES,
|
|
56
|
+
OPENGENI_API_CONTRACT_HEADER,
|
|
57
|
+
OPENGENI_API_CONTRACT_REVISION,
|
|
58
|
+
SESSION_EVENT_TYPES,
|
|
59
|
+
} from "./types";
|
|
47
60
|
export type {
|
|
48
61
|
AccessContext,
|
|
49
62
|
AccessGrant,
|
|
@@ -191,6 +204,16 @@ export type {
|
|
|
191
204
|
SessionQueueMutationResponse,
|
|
192
205
|
SessionQueueSnapshot,
|
|
193
206
|
SessionControlResponse,
|
|
207
|
+
ComposerDraft,
|
|
208
|
+
DeleteSessionQueueItemRequest,
|
|
209
|
+
EditSessionQueueItemRequest,
|
|
210
|
+
EffectiveControlBlocker,
|
|
211
|
+
EffectiveControlResumeOption,
|
|
212
|
+
EffectiveSessionControl,
|
|
213
|
+
MoveSessionQueueItemRequest,
|
|
214
|
+
SaveComposerDraftRequest,
|
|
215
|
+
SessionCommandReceipt,
|
|
216
|
+
SteerSessionQueueItemRequest,
|
|
194
217
|
WorkspaceInferenceControlResponse,
|
|
195
218
|
SessionSystemUpdate,
|
|
196
219
|
SessionSystemUpdateKind,
|
|
@@ -306,6 +329,7 @@ export type {
|
|
|
306
329
|
UserApprovalDecisionEventInput,
|
|
307
330
|
UserMessageEventInput,
|
|
308
331
|
Workspace,
|
|
332
|
+
WorkspaceControlEvent,
|
|
309
333
|
VariableSet,
|
|
310
334
|
VariableSetVariableMetadata,
|
|
311
335
|
Rig,
|
package/src/stream.ts
CHANGED
|
@@ -34,6 +34,8 @@ export type StreamSessionEventsOptions = {
|
|
|
34
34
|
* reconnects = N+1 total open-stream calls). Defaults to unlimited.
|
|
35
35
|
*/
|
|
36
36
|
maxReconnectAttempts?: number;
|
|
37
|
+
/** Await authoritative client reconciliation before exposing `live`. */
|
|
38
|
+
beforeLive?: (() => void | Promise<void>) | undefined;
|
|
37
39
|
onStateChange?: (state: StreamConnectionState) => void;
|
|
38
40
|
};
|
|
39
41
|
|
|
@@ -75,6 +77,7 @@ export async function* streamSessionEvents(
|
|
|
75
77
|
everConnected = true;
|
|
76
78
|
failedAttempts = 0;
|
|
77
79
|
delayMs = baseDelayMs;
|
|
80
|
+
await options.beforeLive?.();
|
|
78
81
|
options.onStateChange?.("live");
|
|
79
82
|
for await (const message of parseSseStream(body)) {
|
|
80
83
|
// Re-check after every yield resumption: an abort from the consumer
|