@flowy-agent-store/client 0.1.0-beta.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.
package/dist/index.cjs ADDED
@@ -0,0 +1,1216 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _flowy_agent_store_protocol = require("@flowy-agent-store/protocol");
3
+ //#region src/transport.ts
4
+ /**
5
+ * JSON-RPC 2.0 transport over WebSocket for the App Server protocol.
6
+ *
7
+ * Works in browsers and in Node 22+/Bun (global `WebSocket`). The browser
8
+ * WebSocket API cannot set custom headers, so an optional bearer token is
9
+ * appended as a query parameter (`?token=...`) when provided; HTTP calls in
10
+ * the SDK use the `Authorization` header instead.
11
+ */
12
+ /**
13
+ * True for loopback-only URLs (`127.0.0.0/8`, `::1`, `localhost`).
14
+ * SDK spawn contract (docs/agent-store/12-sdk-packaging.md P0-4): an SDK
15
+ * must only speak the local-trusted mode over loopback; anything else is
16
+ * rejected before connecting.
17
+ */
18
+ function isLoopbackUrl(raw) {
19
+ try {
20
+ const host = new URL(raw).hostname.toLowerCase();
21
+ if (host === "localhost" || host === "::1") return true;
22
+ if (host.startsWith("[") && host.endsWith("]")) return host === "[::1]";
23
+ const parts = host.split(".").map(Number);
24
+ return parts.length === 4 && parts[0] === 127 && parts.every((n) => Number.isInteger(n) && n >= 0 && n <= 255);
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+ var WebSocketTransport = class {
30
+ socket = null;
31
+ nextId = 1;
32
+ pending = /* @__PURE__ */ new Map();
33
+ listeners = /* @__PURE__ */ new Set();
34
+ connectResolvers = [];
35
+ connectRejecters = [];
36
+ closedByCaller = false;
37
+ requestTimeoutMs;
38
+ url;
39
+ token;
40
+ constructor(url, options = {}) {
41
+ this.url = url;
42
+ this.token = options.token;
43
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
44
+ }
45
+ get connected() {
46
+ return this.socket?.readyState === WebSocket.OPEN;
47
+ }
48
+ connect() {
49
+ if (this.connected) return Promise.resolve();
50
+ this.closedByCaller = false;
51
+ const target = this.token ? `${this.url}${this.url.includes("?") ? "&" : "?"}token=${encodeURIComponent(this.token)}` : this.url;
52
+ const socket = new WebSocket(target);
53
+ this.socket = socket;
54
+ socket.onopen = () => {
55
+ for (const resolve of this.connectResolvers.splice(0)) resolve();
56
+ };
57
+ socket.onmessage = (event) => {
58
+ this.handleMessage(event.data);
59
+ };
60
+ socket.onclose = (event) => {
61
+ this.failAllPending(new _flowy_agent_store_protocol.TransportError("close", `app-server connection closed (code ${event.code})`, { retryable: !this.closedByCaller }));
62
+ this.socket = null;
63
+ for (const reject of this.connectRejecters.splice(0)) reject(new _flowy_agent_store_protocol.TransportError("connect", "app-server connection closed before opening"));
64
+ };
65
+ socket.onerror = () => {
66
+ this.socket?.close();
67
+ };
68
+ return new Promise((resolve, reject) => {
69
+ this.connectResolvers.push(resolve);
70
+ this.connectRejecters.push(reject);
71
+ });
72
+ }
73
+ request(method, params) {
74
+ const socket = this.requireSocket();
75
+ const id = this.nextId++;
76
+ const payload = {
77
+ jsonrpc: "2.0",
78
+ id,
79
+ method,
80
+ params
81
+ };
82
+ return new Promise((resolve, reject) => {
83
+ const timer = setTimeout(() => {
84
+ this.pending.delete(id);
85
+ reject(new _flowy_agent_store_protocol.RequestTimeoutError(method, this.requestTimeoutMs));
86
+ }, this.requestTimeoutMs);
87
+ this.pending.set(id, {
88
+ resolve,
89
+ reject,
90
+ timer,
91
+ method
92
+ });
93
+ try {
94
+ socket.send(JSON.stringify(payload));
95
+ } catch (error) {
96
+ clearTimeout(timer);
97
+ this.pending.delete(id);
98
+ reject(new _flowy_agent_store_protocol.TransportError("send", `failed to send ${method}`, { cause: error }));
99
+ }
100
+ });
101
+ }
102
+ notify(method, params) {
103
+ const socket = this.requireSocket();
104
+ const payload = {
105
+ jsonrpc: "2.0",
106
+ method,
107
+ params
108
+ };
109
+ try {
110
+ socket.send(JSON.stringify(payload));
111
+ } catch (error) {
112
+ throw new _flowy_agent_store_protocol.TransportError("send", `failed to send notification ${method}`, { cause: error });
113
+ }
114
+ }
115
+ onNotification(listener) {
116
+ this.listeners.add(listener);
117
+ return () => {
118
+ this.listeners.delete(listener);
119
+ };
120
+ }
121
+ close() {
122
+ this.closedByCaller = true;
123
+ this.socket?.close();
124
+ this.socket = null;
125
+ for (const [, pending] of this.pending) {
126
+ clearTimeout(pending.timer);
127
+ pending.reject(new _flowy_agent_store_protocol.TransportError("close", "app-server transport closed by caller"));
128
+ }
129
+ this.pending.clear();
130
+ }
131
+ requireSocket() {
132
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) throw new _flowy_agent_store_protocol.TransportError("send", "app-server transport is not connected", { retryable: true });
133
+ return this.socket;
134
+ }
135
+ handleMessage(raw) {
136
+ let message;
137
+ try {
138
+ message = JSON.parse(String(raw));
139
+ } catch {
140
+ return;
141
+ }
142
+ if ("id" in message && message.id !== void 0) {
143
+ const response = message;
144
+ const pending = response.id === null ? void 0 : this.pending.get(Number(response.id));
145
+ if (!pending) return;
146
+ clearTimeout(pending.timer);
147
+ this.pending.delete(Number(response.id));
148
+ if (response.error) pending.reject(new _flowy_agent_store_protocol.AppServerError(response.error));
149
+ else pending.resolve(response.result);
150
+ return;
151
+ }
152
+ if ("method" in message) {
153
+ const notification = message;
154
+ for (const listener of [...this.listeners]) try {
155
+ listener(notification);
156
+ } catch {}
157
+ return;
158
+ }
159
+ }
160
+ failAllPending(error) {
161
+ for (const [, pending] of this.pending) {
162
+ clearTimeout(pending.timer);
163
+ pending.reject(error);
164
+ }
165
+ this.pending.clear();
166
+ }
167
+ };
168
+ //#endregion
169
+ //#region src/agents.ts
170
+ var AgentClient = class {
171
+ transport;
172
+ constructor(transport) {
173
+ this.transport = transport;
174
+ }
175
+ /** List Agent Store AgentDefinitions (public summaries only). */
176
+ list() {
177
+ return this.transport.request("agent/list", {});
178
+ }
179
+ /** Fetch one AgentDefinition; raw prompts never cross the wire. */
180
+ get(agentId) {
181
+ return this.transport.request("agent/get", { agent_id: agentId });
182
+ }
183
+ };
184
+ //#endregion
185
+ //#region src/conversations.ts
186
+ var ConversationClient = class {
187
+ transport;
188
+ constructor(transport) {
189
+ this.transport = transport;
190
+ }
191
+ create(input) {
192
+ const model = input.model ? {
193
+ provider_id: input.model.provider_id,
194
+ model: input.model.model
195
+ } : void 0;
196
+ return this.transport.request("conversation/create", {
197
+ name: input.name,
198
+ model,
199
+ workspace: input.workspaceId ? { id: input.workspaceId } : void 0,
200
+ reasoning_effort: input.reasoningEffort || void 0
201
+ });
202
+ }
203
+ update(conversationId, input) {
204
+ return this.transport.request("conversation/update", {
205
+ conversation_id: conversationId,
206
+ name: input.name,
207
+ model: input.model ? {
208
+ provider_id: input.model.provider_id,
209
+ model: input.model.model
210
+ } : void 0,
211
+ reasoning_effort: input.reasoningEffort || void 0
212
+ });
213
+ }
214
+ modelOptions() {
215
+ return this.transport.request("conversation/model-options", {});
216
+ }
217
+ list(limit = 100) {
218
+ return this.transport.request("conversation/list", { limit });
219
+ }
220
+ get(conversationId) {
221
+ return this.transport.request("conversation/get", { conversation_id: conversationId });
222
+ }
223
+ messages(query) {
224
+ return this.transport.request("conversation/messages", {
225
+ conversation_id: query.conversationId,
226
+ page: query.page,
227
+ page_size: query.pageSize,
228
+ cursor: query.cursor
229
+ });
230
+ }
231
+ send(conversationId, content, idempotencyKey) {
232
+ return this.transport.request("conversation/send", {
233
+ conversation_id: conversationId,
234
+ content,
235
+ idempotency_key: idempotencyKey
236
+ });
237
+ }
238
+ cancel(conversationId) {
239
+ return this.transport.request("conversation/cancel", { conversation_id: conversationId });
240
+ }
241
+ delete(conversationId) {
242
+ return this.transport.request("conversation/delete", { conversation_id: conversationId });
243
+ }
244
+ async follow(conversationId) {
245
+ await this.transport.request("conversation/subscribe", { conversation_id: conversationId });
246
+ return new ConversationSubscription(this.transport, conversationId);
247
+ }
248
+ };
249
+ var ConversationSubscription = class {
250
+ transport;
251
+ conversationId;
252
+ events = /* @__PURE__ */ new Set();
253
+ resyncs = /* @__PURE__ */ new Set();
254
+ closed = false;
255
+ lastSeenSequence = 0;
256
+ removeListener;
257
+ constructor(transport, conversationId) {
258
+ this.transport = transport;
259
+ this.conversationId = conversationId;
260
+ this.removeListener = transport.onNotification((notification) => this.dispatch(notification));
261
+ }
262
+ get lastSequence() {
263
+ return this.lastSeenSequence;
264
+ }
265
+ onEvent(listener) {
266
+ this.events.add(listener);
267
+ return () => this.events.delete(listener);
268
+ }
269
+ onResync(listener) {
270
+ this.resyncs.add(listener);
271
+ return () => this.resyncs.delete(listener);
272
+ }
273
+ async close() {
274
+ if (this.closed) return;
275
+ this.closed = true;
276
+ this.removeListener?.();
277
+ this.removeListener = null;
278
+ this.events.clear();
279
+ this.resyncs.clear();
280
+ try {
281
+ await this.transport.request("conversation/unsubscribe", { conversation_id: this.conversationId });
282
+ } catch {}
283
+ }
284
+ dispatch(notification) {
285
+ if (notification.method === "conversation/event") {
286
+ const event = notification.params;
287
+ if (event.conversation_id !== this.conversationId || event.sequence <= this.lastSeenSequence) return;
288
+ this.lastSeenSequence = event.sequence;
289
+ for (const listener of [...this.events]) try {
290
+ listener(event);
291
+ } catch {}
292
+ return;
293
+ }
294
+ if (notification.method === "conversation/resync-required") {
295
+ if (!notification.params.conversation_ids.includes(this.conversationId)) return;
296
+ for (const listener of [...this.resyncs]) try {
297
+ listener(notification.params.reason);
298
+ } catch {}
299
+ }
300
+ }
301
+ };
302
+ //#endregion
303
+ //#region src/connectors.ts
304
+ var ConnectorClient = class {
305
+ transport;
306
+ constructor(transport) {
307
+ this.transport = transport;
308
+ }
309
+ /** List the Agent Store Connector catalog (public summaries only). */
310
+ list() {
311
+ return this.transport.request("connector/list", {});
312
+ }
313
+ /** Fetch one Connector detail: namespaced tools + auth state. */
314
+ get(connectorId) {
315
+ return this.transport.request("connector/get", { connector_id: connectorId });
316
+ }
317
+ /** Combined auth × probe status. `connected` only when auth is ready AND the last probe succeeded. */
318
+ status(connectorId) {
319
+ return this.transport.request("connector/status", { connector_id: connectorId });
320
+ }
321
+ /** Run a connection probe; the server persists the result. */
322
+ test(connectorId) {
323
+ return this.transport.request("connector/test", { connector_id: connectorId });
324
+ }
325
+ /** Check the current OAuth state for a connector. */
326
+ authStatus(connectorId) {
327
+ return this.transport.request("connector/auth/status", { connector_id: connectorId });
328
+ }
329
+ /**
330
+ * Kick off the OAuth browser flow on the trusted host. Returns the start
331
+ * acknowledgement immediately; poll `authStatus` until `authenticated`.
332
+ */
333
+ authStart(connectorId) {
334
+ return this.transport.request("connector/auth/start", { connector_id: connectorId });
335
+ }
336
+ /** Revoke the OAuth token for a connector. */
337
+ logout(connectorId) {
338
+ return this.transport.request("connector/auth/logout", { connector_id: connectorId }).then(() => void 0);
339
+ }
340
+ };
341
+ //#endregion
342
+ //#region src/runs.ts
343
+ /**
344
+ * Run client: agent/run, run/get, run/result, run/events, run/cancel plus the
345
+ * realtime `follow` subscription over the WebSocket transport.
346
+ */
347
+ var RunClient = class {
348
+ transport;
349
+ constructor(transport) {
350
+ this.transport = transport;
351
+ }
352
+ /** Start an Agent run and return its asynchronous receipt. */
353
+ agent(input) {
354
+ return this.transport.request("agent/run", toWire(input));
355
+ }
356
+ /** Read the authoritative run state. */
357
+ get(runId) {
358
+ return this.transport.request("run/get", { run_id: runId });
359
+ }
360
+ /** Read the terminal result (fails until the run reaches a terminal state). */
361
+ result(runId) {
362
+ return this.transport.request("run/result", { run_id: runId });
363
+ }
364
+ /** Replay persisted events after a cursor. */
365
+ events(query) {
366
+ return this.transport.request("run/events", {
367
+ run_id: query.runId,
368
+ after_sequence: query.afterSequence,
369
+ limit: query.limit
370
+ });
371
+ }
372
+ /** Request cancellation; the terminal state is confirmed by events/run/get. */
373
+ cancel(input) {
374
+ return this.transport.request("run/cancel", {
375
+ run_id: input.runId,
376
+ expected_version: input.expectedVersion,
377
+ command_id: input.commandId,
378
+ idempotency_key: input.idempotencyKey
379
+ });
380
+ }
381
+ /**
382
+ * Inject steering text into the currently active step of a running Agent
383
+ * (runtime durable conversation effect; server resolves the opaque step).
384
+ */
385
+ steer(input) {
386
+ return this.transport.request("run/steer", {
387
+ run_id: input.runId,
388
+ text: input.text,
389
+ expected_version: input.expectedVersion,
390
+ command_id: input.commandId,
391
+ idempotency_key: input.idempotencyKey
392
+ });
393
+ }
394
+ /** Subscribe to best-effort realtime events for one run. */
395
+ async follow(runId, options) {
396
+ const params = { run_id: runId };
397
+ await this.transport.request("run/subscribe", params);
398
+ const subscription = new EventSubscription(this.transport, runId, (afterSequence) => this.events({
399
+ runId,
400
+ afterSequence
401
+ }), options);
402
+ if (options?.catchUp !== false) await subscription.catchUp().catch(() => void 0);
403
+ return subscription;
404
+ }
405
+ };
406
+ /** Upper bound for the silent catch-up page (cursor baseline, not delivery). */
407
+ const CATCH_UP_LIMIT = 500;
408
+ /** Prune the dedup set past this size (sequences are per-run monotonic). */
409
+ const SEEN_SET_PRUNE_AT = 2e3;
410
+ const SEEN_SET_KEEP_BELOW_MAX = 500;
411
+ var EventSubscription = class {
412
+ transport;
413
+ runId;
414
+ fetchAfter;
415
+ eventListeners = /* @__PURE__ */ new Set();
416
+ resyncListeners = /* @__PURE__ */ new Set();
417
+ errorListeners = /* @__PURE__ */ new Set();
418
+ closed = false;
419
+ unsubscribeNotification = null;
420
+ lastSeenSequence = 0;
421
+ seenSequences = /* @__PURE__ */ new Set();
422
+ resyncInFlight = null;
423
+ autoResync;
424
+ constructor(transport, runId, fetchAfter, options) {
425
+ this.transport = transport;
426
+ this.runId = runId;
427
+ this.fetchAfter = fetchAfter;
428
+ this.autoResync = options?.autoResync !== false;
429
+ this.unsubscribeNotification = transport.onNotification((notification) => {
430
+ this.dispatch(notification);
431
+ });
432
+ }
433
+ onEvent(listener) {
434
+ this.eventListeners.add(listener);
435
+ return () => {
436
+ this.eventListeners.delete(listener);
437
+ };
438
+ }
439
+ onResync(listener) {
440
+ this.resyncListeners.add(listener);
441
+ return () => {
442
+ this.resyncListeners.delete(listener);
443
+ };
444
+ }
445
+ onError(listener) {
446
+ this.errorListeners.add(listener);
447
+ return () => {
448
+ this.errorListeners.delete(listener);
449
+ };
450
+ }
451
+ get lastSequence() {
452
+ return this.lastSeenSequence;
453
+ }
454
+ async close() {
455
+ if (this.closed) return;
456
+ this.closed = true;
457
+ this.unsubscribeNotification?.();
458
+ this.eventListeners.clear();
459
+ this.resyncListeners.clear();
460
+ this.errorListeners.clear();
461
+ try {
462
+ await this.transport.request("run/unsubscribe", { run_id: this.runId });
463
+ } catch {}
464
+ }
465
+ dispatch(notification) {
466
+ if (this.closed) return;
467
+ if (notification.method === "event") {
468
+ const event = notification.params;
469
+ if (event.run_id !== this.runId) return;
470
+ if (!Number.isFinite(event.sequence) || this.seenSequences.has(event.sequence)) return;
471
+ this.markSeen(event.sequence);
472
+ this.emitEvent(event);
473
+ return;
474
+ }
475
+ if (notification.method === "run/resync-required") {
476
+ const params = notification.params;
477
+ if (params.run_ids.length > 0 && !params.run_ids.includes(this.runId)) return;
478
+ for (const listener of [...this.resyncListeners]) try {
479
+ listener(params);
480
+ } catch {}
481
+ if (this.autoResync && this.fetchAfter) this.resync().catch((error) => this.emitError(error));
482
+ return;
483
+ }
484
+ }
485
+ /**
486
+ * Silent cursor baseline: mark already-persisted events seen WITHOUT
487
+ * dispatching, so the first resync never replays history as "new".
488
+ * For explicit history use `RunClient.events({ afterSequence: 0 })`.
489
+ */
490
+ async catchUp() {
491
+ if (this.closed || !this.fetchAfter) return;
492
+ const history = await this.fetchAfter(0);
493
+ const page = Array.isArray(history) ? history.slice(0, CATCH_UP_LIMIT) : [];
494
+ for (const event of page) if (event.run_id === this.runId && Number.isFinite(event.sequence)) this.markSeen(event.sequence);
495
+ }
496
+ /**
497
+ *追平 missed events via `run/events` after the cursor. Returned (and
498
+ * dispatched) in sequence order, deduplicated against live delivery.
499
+ * Concurrent calls share one flight.
500
+ */
501
+ async resync() {
502
+ if (this.closed) return [];
503
+ if (this.resyncInFlight) return this.resyncInFlight;
504
+ if (!this.fetchAfter) return [];
505
+ const fetchAfter = this.fetchAfter;
506
+ const task = (async () => {
507
+ const missed = await fetchAfter(this.lastSeenSequence);
508
+ const fresh = (Array.isArray(missed) ? [...missed] : []).filter((event) => event.run_id === this.runId && Number.isFinite(event.sequence)).sort((left, right) => left.sequence - right.sequence).filter((event) => {
509
+ if (this.seenSequences.has(event.sequence)) return false;
510
+ this.markSeen(event.sequence);
511
+ return true;
512
+ });
513
+ for (const event of fresh) this.emitEvent(event);
514
+ return fresh;
515
+ })();
516
+ this.resyncInFlight = task;
517
+ try {
518
+ return await task;
519
+ } finally {
520
+ if (this.resyncInFlight === task) this.resyncInFlight = null;
521
+ }
522
+ }
523
+ markSeen(sequence) {
524
+ this.seenSequences.add(sequence);
525
+ if (sequence > this.lastSeenSequence) this.lastSeenSequence = sequence;
526
+ if (this.seenSequences.size > SEEN_SET_PRUNE_AT) {
527
+ const floor = this.lastSeenSequence - SEEN_SET_KEEP_BELOW_MAX;
528
+ for (const seen of this.seenSequences) if (seen < floor) this.seenSequences.delete(seen);
529
+ }
530
+ }
531
+ emitEvent(event) {
532
+ for (const listener of [...this.eventListeners]) try {
533
+ listener(event);
534
+ } catch {}
535
+ }
536
+ emitError(error) {
537
+ for (const listener of [...this.errorListeners]) try {
538
+ listener(error);
539
+ } catch {}
540
+ }
541
+ /** Forward transport-level errors into subscription error listeners. */
542
+ deliverError(error) {
543
+ if (error instanceof _flowy_agent_store_protocol.TransportError) for (const listener of [...this.errorListeners]) try {
544
+ listener(error);
545
+ } catch {}
546
+ }
547
+ };
548
+ function toWire(input) {
549
+ return {
550
+ agent_id: input.agentId,
551
+ agent_version: input.agentVersion,
552
+ goal: input.goal,
553
+ input: input.input,
554
+ workspace: input.workspaceId ? { id: input.workspaceId } : void 0,
555
+ steps: input.steps,
556
+ command_id: input.commandId,
557
+ idempotency_key: input.idempotencyKey,
558
+ mentions: input.mentions
559
+ };
560
+ }
561
+ //#endregion
562
+ //#region src/skills.ts
563
+ var SkillClient = class {
564
+ transport;
565
+ constructor(transport) {
566
+ this.transport = transport;
567
+ }
568
+ /** List the Agent Store Skill catalog (public summaries only). */
569
+ list() {
570
+ return this.transport.request("skill/list", {});
571
+ }
572
+ /** Fetch one Skill detail; instructions are a bounded public summary. */
573
+ get(skillId) {
574
+ return this.transport.request("skill/get", { skill_id: skillId });
575
+ }
576
+ };
577
+ //#endregion
578
+ //#region src/teams.ts
579
+ var TeamClient = class {
580
+ transport;
581
+ constructor(transport) {
582
+ this.transport = transport;
583
+ }
584
+ /** List AgentTeamDefinitions (fixed-member rosters). */
585
+ list() {
586
+ return this.transport.request("team/list", {});
587
+ }
588
+ /** Fetch one TeamDefinition; planning-context bodies are never returned. */
589
+ get(teamId) {
590
+ return this.transport.request("team/get", { team_id: teamId });
591
+ }
592
+ };
593
+ //#endregion
594
+ //#region src/models.ts
595
+ var ModelClient = class {
596
+ transport;
597
+ constructor(transport) {
598
+ this.transport = transport;
599
+ }
600
+ /** Enabled providers' models; `is_default` marks the `agent/run` fallback. */
601
+ async list() {
602
+ return (await this.transport.request("models/list", {})).items;
603
+ }
604
+ };
605
+ //#endregion
606
+ //#region src/workspaces.ts
607
+ var WorkspaceClient = class {
608
+ transport;
609
+ constructor(transport) {
610
+ this.transport = transport;
611
+ }
612
+ list() {
613
+ return this.transport.request("workspace/list", {});
614
+ }
615
+ /**
616
+ * Validate + register (idempotently per canonical root) an absolute local
617
+ * directory chosen by the owner. The server canonicalizes the path and
618
+ * rejects links/reparse points; the returned view only carries the
619
+ * canonical path back to this same authenticated connection.
620
+ */
621
+ create(path) {
622
+ return this.transport.request("workspace/create", { path });
623
+ }
624
+ /**
625
+ * Revoke (soft-delete) an owner's active workspace. Its conversations are
626
+ * kept and their `workspace_id` reference is preserved, so re-registering
627
+ * the same path re-activates the same workspace and those conversations
628
+ * become visible again.
629
+ */
630
+ revoke(workspaceId) {
631
+ return this.transport.request("workspace/revoke", { workspace_id: workspaceId });
632
+ }
633
+ };
634
+ //#endregion
635
+ //#region src/client.ts
636
+ /**
637
+ * Typed App Server client (`@flowy-agent-store/client`).
638
+ *
639
+ * Transport-agnostic: all business methods go through the injected
640
+ * `Transport` (07 §2.3). Web-only helpers (asset URLs, `/api/fs/browse`,
641
+ * one-shot HTTP registration) live in the host app, not here.
642
+ *
643
+ * Connection lifecycle: transport connect → `initialize` → protocol version
644
+ * check → `initialized` notification → ready. Business methods require a
645
+ * ready client; the server rejects them with `not_initialized` otherwise.
646
+ */
647
+ var AppServerClient = class {
648
+ transport;
649
+ /** Legacy preset/execution workflow client. */
650
+ runs;
651
+ /** Persistent presetless Nomi chat client. */
652
+ conversations;
653
+ /** Owner-scoped workspace registry client (user-chosen paths). */
654
+ workspaces;
655
+ /** Agent Store Skill catalog client (`skill/list`, `skill/get`). */
656
+ skills;
657
+ /** Agent Store Connector catalog/status/probe/OAuth client. */
658
+ connectors;
659
+ /** Agent Store AgentDefinition catalog client (`agent/list`, `agent/get`). */
660
+ agents;
661
+ /** Agent Store Team catalog client (`team/list`, `team/get`). */
662
+ teams;
663
+ /** Public model directory (`models/list`, REQ-PAR-05b). */
664
+ models;
665
+ clientInfo;
666
+ capabilities;
667
+ initializeResult = null;
668
+ notificationListeners = /* @__PURE__ */ new Set();
669
+ constructor(options) {
670
+ this.clientInfo = options.client;
671
+ this.capabilities = options.capabilities;
672
+ this.transport = options.transport;
673
+ this.runs = new RunClient(this.transport);
674
+ this.conversations = new ConversationClient(this.transport);
675
+ this.workspaces = new WorkspaceClient(this.transport);
676
+ this.skills = new SkillClient(this.transport);
677
+ this.connectors = new ConnectorClient(this.transport);
678
+ this.agents = new AgentClient(this.transport);
679
+ this.teams = new TeamClient(this.transport);
680
+ this.models = new ModelClient(this.transport);
681
+ this.transport.onNotification((notification) => {
682
+ for (const listener of [...this.notificationListeners]) try {
683
+ listener(notification);
684
+ } catch {}
685
+ });
686
+ }
687
+ get ready() {
688
+ return this.initializeResult !== null;
689
+ }
690
+ get initializeInfo() {
691
+ return this.initializeResult;
692
+ }
693
+ /** Connect and perform the initialize/initialized handshake. */
694
+ async connect() {
695
+ await this.transport.connect();
696
+ const request = {
697
+ protocol_version: _flowy_agent_store_protocol.APP_SERVER_PROTOCOL_VERSION,
698
+ client: this.clientInfo,
699
+ capabilities: this.capabilities
700
+ };
701
+ const result = await this.transport.request("initialize", request);
702
+ if (result.protocol_version !== _flowy_agent_store_protocol.APP_SERVER_PROTOCOL_VERSION) throw new _flowy_agent_store_protocol.ProtocolError("version_mismatch", `server protocol version ${result.protocol_version} is not supported (client ${_flowy_agent_store_protocol.APP_SERVER_PROTOCOL_VERSION})`);
703
+ this.transport.notify("initialized", {});
704
+ this.initializeResult = result;
705
+ return result;
706
+ }
707
+ onNotification(listener) {
708
+ this.notificationListeners.add(listener);
709
+ return () => {
710
+ this.notificationListeners.delete(listener);
711
+ };
712
+ }
713
+ /** Close the transport; the server revokes the connection immediately. */
714
+ close() {
715
+ this.transport.close();
716
+ this.initializeResult = null;
717
+ this.notificationListeners.clear();
718
+ }
719
+ /** Import a local CodeBuddy/WorkBuddy source directory (roadmap Phase 1). */
720
+ async runImport(input) {
721
+ return this.transport.request("import/run", input);
722
+ }
723
+ async listImports() {
724
+ return this.transport.request("import/list", {});
725
+ }
726
+ /** One immutable snapshot with its standardized components. */
727
+ async getImport(snapshotId) {
728
+ return this.transport.request("import/get", { snapshot_id: snapshotId });
729
+ }
730
+ /** Install a snapshot into the runtime (roadmap Phase 2). */
731
+ async runInstall(input) {
732
+ return this.transport.request("install/run", input);
733
+ }
734
+ /** Per-component installation state of a snapshot. */
735
+ async getInstallStatus(snapshotId) {
736
+ return this.transport.request("install/status", { snapshot_id: snapshotId });
737
+ }
738
+ /** Disable installed components (runtime artifacts stay). */
739
+ async disableInstall(snapshotId, componentIds) {
740
+ return this.transport.request("install/disable", {
741
+ snapshot_id: snapshotId,
742
+ component_ids: componentIds
743
+ });
744
+ }
745
+ /** Re-enable previously disabled components. */
746
+ async enableInstall(snapshotId, componentIds) {
747
+ return this.transport.request("install/enable", {
748
+ snapshot_id: snapshotId,
749
+ component_ids: componentIds
750
+ });
751
+ }
752
+ /** Uninstall: remove runtime artifacts and clear the state (snapshot kept). */
753
+ async uninstallInstall(snapshotId, componentIds) {
754
+ return this.transport.request("install/uninstall", {
755
+ snapshot_id: snapshotId,
756
+ component_ids: componentIds
757
+ });
758
+ }
759
+ /** Register a marketplace source on the trusted host. */
760
+ async addMarketplace(input) {
761
+ return this.transport.request("market/add", input);
762
+ }
763
+ /** All active marketplaces. */
764
+ async listMarketplaces() {
765
+ return this.transport.request("market/list", {});
766
+ }
767
+ /** One marketplace with its discovered entries. */
768
+ async getMarketplace(marketplaceId) {
769
+ return this.transport.request("market/get", { marketplace_id: marketplaceId });
770
+ }
771
+ /** Remove a marketplace (`cascade` uninstalls snapshots installed from it). */
772
+ async removeMarketplace(marketplaceId, cascade) {
773
+ return this.transport.request("market/remove", {
774
+ marketplace_id: marketplaceId,
775
+ cascade
776
+ });
777
+ }
778
+ /** Toggle auto-update for a marketplace. */
779
+ async setMarketplaceAutoUpdate(marketplaceId, enabled) {
780
+ return this.transport.request("market/auto-update", {
781
+ marketplace_id: marketplaceId,
782
+ enabled
783
+ });
784
+ }
785
+ /** Re-fetch a marketplace source and rebuild entries when the revision changed. */
786
+ async refreshMarketplace(marketplaceId) {
787
+ return this.transport.request("market/refresh", { marketplace_id: marketplaceId });
788
+ }
789
+ /** Import one discovered marketplace entry (provenance linked). */
790
+ async importMarketplaceEntry(marketplaceId, entryName) {
791
+ return this.transport.request("market/entry-import", {
792
+ marketplace_id: marketplaceId,
793
+ entry_name: entryName
794
+ });
795
+ }
796
+ /** All store items across enabled marketplaces (display + install state). */
797
+ async listStore() {
798
+ return this.transport.request("store/list", {});
799
+ }
800
+ /** One-click install: import (when missing) + register in one call. */
801
+ async installStoreEntry(marketplaceId, entryName) {
802
+ return this.transport.request("store/install-entry", {
803
+ marketplace_id: marketplaceId,
804
+ entry_name: entryName
805
+ });
806
+ }
807
+ };
808
+ //#endregion
809
+ //#region src/conversation-handle.ts
810
+ /** How long `send` waits for a terminal event before returning a still-open turn. */
811
+ const TURN_TIMEOUT_MS = 6e5;
812
+ function stringValue(value) {
813
+ return typeof value === "string" ? value : void 0;
814
+ }
815
+ /** Aggregate assistant text from `message.delta` events (fallback path). */
816
+ function aggregateAssistantText(events) {
817
+ const byId = /* @__PURE__ */ new Map();
818
+ for (const event of events) {
819
+ if (event.event_type !== "message.delta") continue;
820
+ if (typeof event.payload["content"] !== "string") continue;
821
+ const id = stringValue(event.payload["message_id"]) ?? "";
822
+ if (!id) continue;
823
+ byId.set(id, (byId.get(id) ?? "") + event.payload["content"]);
824
+ }
825
+ let last = null;
826
+ for (const value of byId.values()) if (value) last = value;
827
+ return last;
828
+ }
829
+ function lastUsage(events) {
830
+ for (let index = events.length - 1; index >= 0; index -= 1) if (events[index].event_type === "context.usage") {
831
+ const value = events[index].payload["context_usage"];
832
+ return value && typeof value === "object" ? value : null;
833
+ }
834
+ }
835
+ var ConversationHandle = class ConversationHandle {
836
+ conversationId;
837
+ conversations;
838
+ subscription;
839
+ constructor(conversations, conversationId, subscription) {
840
+ this.conversations = conversations;
841
+ this.conversationId = conversationId;
842
+ this.subscription = subscription;
843
+ }
844
+ /** Create a new conversation and subscribe to its live event stream. */
845
+ static async open(conversations, input) {
846
+ const view = await conversations.create(input ?? {});
847
+ const subscription = await conversations.follow(view.conversation_id);
848
+ return new ConversationHandle(conversations, view.conversation_id, subscription);
849
+ }
850
+ /** Attach to an existing conversation (by id) and subscribe. */
851
+ static async attach(conversations, conversationId) {
852
+ const subscription = await conversations.follow(conversationId);
853
+ return new ConversationHandle(conversations, conversationId, subscription);
854
+ }
855
+ /** Register a listener for every conversation event (returns an unsubscribe). */
856
+ onEvent(listener) {
857
+ return this.subscription.onEvent(listener);
858
+ }
859
+ /**
860
+ * Send a message (a "turn") and await its terminal state. The event collector
861
+ * is installed before the send so streaming deltas / usage are captured even
862
+ * when they arrive before the send acknowledgment is parsed.
863
+ */
864
+ async send(content, options = {}) {
865
+ const collected = [];
866
+ let terminalResolve;
867
+ let terminal = false;
868
+ const terminalPromise = new Promise((resolve) => {
869
+ terminalResolve = resolve;
870
+ });
871
+ const markTerminal = () => {
872
+ if (terminal) return;
873
+ terminal = true;
874
+ terminalResolve();
875
+ };
876
+ const off = this.subscription.onEvent((event) => {
877
+ collected.push(event);
878
+ if (event.event_type === "turn.status" && stringValue(event.payload["status"]) === "completed") markTerminal();
879
+ else if (event.event_type === "message.error") markTerminal();
880
+ });
881
+ let receipt;
882
+ try {
883
+ receipt = await this.conversations.send(this.conversationId, content, options.idempotencyKey ?? crypto.randomUUID());
884
+ if (!receipt.completed && !terminal) await Promise.race([terminalPromise, new Promise((resolve) => setTimeout(resolve, TURN_TIMEOUT_MS))]);
885
+ } finally {
886
+ off();
887
+ }
888
+ return {
889
+ conversation_id: this.conversationId,
890
+ content,
891
+ message_id: receipt.message_id,
892
+ turn_id: receipt.turn_id,
893
+ accepted: receipt.accepted,
894
+ completed: receipt.completed || terminal,
895
+ events: collected,
896
+ assistant_text: receipt.result_text ?? aggregateAssistantText(collected),
897
+ isError: Boolean(receipt.result_error) || collected.some((event) => event.event_type === "message.error"),
898
+ result_error: receipt.result_error ?? null,
899
+ result_error_code: receipt.result_error_code ?? null,
900
+ usage: lastUsage(collected)
901
+ };
902
+ }
903
+ /** Cancel the in-flight turn (idempotent; erases the current transcript row). */
904
+ cancel() {
905
+ return this.conversations.cancel(this.conversationId).then(() => void 0);
906
+ }
907
+ /** Fetch the transcript (paginated). */
908
+ messages(query) {
909
+ return this.conversations.messages({
910
+ conversationId: this.conversationId,
911
+ ...query
912
+ });
913
+ }
914
+ /** Unsubscribe and release the handle. */
915
+ close() {
916
+ return this.subscription.close();
917
+ }
918
+ };
919
+ /** Convenience: simple transcript snapshot (all roles, newest last). */
920
+ function conversationMessages(page) {
921
+ return page.items;
922
+ }
923
+ //#endregion
924
+ //#region src/turn-result.ts
925
+ const KIND_BY_EVENT = {
926
+ "run.plan_changed": "plan",
927
+ "task.updated": "task",
928
+ "attempt.updated": "attempt",
929
+ "run.status_changed": "status",
930
+ "approval.requested": "approval",
931
+ "approval.responded": "approval"
932
+ };
933
+ function asRecord(payload) {
934
+ return payload && typeof payload === "object" ? payload : {};
935
+ }
936
+ function pickString(source, key) {
937
+ const value = source[key];
938
+ return typeof value === "string" ? value : void 0;
939
+ }
940
+ function toItem(event) {
941
+ const payload = asRecord(event.payload);
942
+ return {
943
+ sequence: event.sequence,
944
+ event_type: event.event_type,
945
+ kind: KIND_BY_EVENT[event.event_type] ?? "other",
946
+ ref_id: pickString(payload, "attempt_id") ?? pickString(payload, "step_id"),
947
+ status: pickString(payload, "status"),
948
+ reason: pickString(payload, "reason") ?? pickString(payload, "change")
949
+ };
950
+ }
951
+ function extractUsage(events) {
952
+ for (const event of events) {
953
+ const payload = asRecord(event.payload);
954
+ const candidate = asRecord(payload["usage"] ?? payload["context_usage"]);
955
+ const input = candidate["input_tokens"] ?? candidate["inputTokens"];
956
+ const output = candidate["output_tokens"] ?? candidate["outputTokens"];
957
+ const total = candidate["total_tokens"] ?? candidate["totalTokens"];
958
+ if (typeof input === "number" || typeof output === "number" || typeof total === "number") return {
959
+ ...typeof input === "number" ? { input_tokens: input } : {},
960
+ ...typeof output === "number" ? { output_tokens: output } : {},
961
+ ...typeof total === "number" ? { total_tokens: total } : {}
962
+ };
963
+ }
964
+ }
965
+ /** Build the terminal turn result from the authoritative view + event stream. */
966
+ function aggregateTurnResult(view, events) {
967
+ const ordered = [...events].sort((left, right) => left.sequence - right.sequence);
968
+ const usage = extractUsage(ordered);
969
+ return {
970
+ run_id: view.run_id,
971
+ status: view.status,
972
+ version: view.version,
973
+ final_response: view.summary ?? null,
974
+ output_files: view.output_files ?? [],
975
+ preset_revision: view.preset_revision ?? null,
976
+ content_digest: view.content_digest ?? null,
977
+ events: ordered,
978
+ items: ordered.map(toItem),
979
+ ...usage ? { usage } : {}
980
+ };
981
+ }
982
+ //#endregion
983
+ //#region src/run-handle.ts
984
+ /** Poll cadence while waiting for a terminal state (authoritative backstop). */
985
+ const POLL_INTERVAL_MS = 3e3;
986
+ /** Absolute cap so a wedged run cannot hang `finished` forever. */
987
+ const FINISHED_TIMEOUT_MS = 18e5;
988
+ /** Live events kept for the terminal aggregation (bounded). */
989
+ const MAX_BUFFERED_EVENTS = 2e3;
990
+ const TERMINAL = [
991
+ "completed",
992
+ "failed",
993
+ "cancelled"
994
+ ];
995
+ /**
996
+ * A launched Agent run. Async-iterable over live events; await `.finished`
997
+ * for the blocking Codex `thread.run()` shape.
998
+ */
999
+ var AgentRunHandle = class {
1000
+ runId;
1001
+ receipt;
1002
+ runClient;
1003
+ poll;
1004
+ subscription;
1005
+ terminalView = null;
1006
+ terminalResult = null;
1007
+ finishWaiters = [];
1008
+ iteratorQueue = [];
1009
+ iteratorWaiters = [];
1010
+ iteratorClosed = false;
1011
+ bufferedEvents = [];
1012
+ bufferedSequences = /* @__PURE__ */ new Set();
1013
+ constructor(runClient, receipt, subscription, poll) {
1014
+ this.runId = receipt.run_id;
1015
+ this.receipt = receipt;
1016
+ this.runClient = runClient;
1017
+ this.subscription = subscription;
1018
+ this.poll = poll;
1019
+ subscription?.onEvent((event) => this.pushEvent(event));
1020
+ subscription?.onError(() => void 0);
1021
+ }
1022
+ /**
1023
+ * Resolves with the aggregated terminal turn result (≈ `thread.run()`):
1024
+ * authoritative status/version plus `final_response`, output files, the
1025
+ * persisted event stream and derived items.
1026
+ */
1027
+ get finished() {
1028
+ if (this.terminalResult) return Promise.resolve(this.terminalResult);
1029
+ return new Promise((resolve, reject) => {
1030
+ this.finishWaiters.push((error, result) => {
1031
+ if (error) {
1032
+ reject(error);
1033
+ return;
1034
+ }
1035
+ resolve(result);
1036
+ });
1037
+ this.ensureTerminalWatch();
1038
+ });
1039
+ }
1040
+ /** Latest live cursor (proxied from the subscription when attached). */
1041
+ get lastSequence() {
1042
+ return this.subscription?.lastSequence ?? 0;
1043
+ }
1044
+ /** Ask the server to cancel; terminal confirmation still arrives via events/get. */
1045
+ async cancel() {
1046
+ await this.runClient.cancel({
1047
+ runId: this.runId,
1048
+ expectedVersion: this.receipt.version
1049
+ });
1050
+ }
1051
+ /**
1052
+ * Inject steering text into the currently active step (≈ Codex turn steer).
1053
+ * Throws `conflict` when the run already changed or has no active step —
1054
+ * refresh `receipt`-level state via `runClient.get()` and retry.
1055
+ */
1056
+ async steer(text) {
1057
+ return this.runClient.steer({
1058
+ runId: this.runId,
1059
+ text,
1060
+ expectedVersion: this.receipt.version
1061
+ });
1062
+ }
1063
+ /** Detach the live subscription (events may still be polled via run/events). */
1064
+ async close() {
1065
+ this.iteratorClosed = true;
1066
+ const waiters = this.iteratorWaiters;
1067
+ this.iteratorWaiters = [];
1068
+ for (const waiter of waiters) waiter({
1069
+ value: void 0,
1070
+ done: true
1071
+ });
1072
+ const subscription = this.subscription;
1073
+ this.subscription = null;
1074
+ await subscription?.close();
1075
+ }
1076
+ /** Async iterator over live events (≈ `thread.runStreamed()`). */
1077
+ [Symbol.asyncIterator]() {
1078
+ return { next: () => {
1079
+ if (this.iteratorQueue.length > 0) {
1080
+ const event = this.iteratorQueue.shift();
1081
+ return Promise.resolve({
1082
+ value: event,
1083
+ done: false
1084
+ });
1085
+ }
1086
+ if (this.iteratorClosed) return Promise.resolve({
1087
+ value: void 0,
1088
+ done: true
1089
+ });
1090
+ return new Promise((resolve) => {
1091
+ this.iteratorWaiters.push(resolve);
1092
+ });
1093
+ } };
1094
+ }
1095
+ pushEvent(event) {
1096
+ if (!this.bufferedSequences.has(event.sequence)) {
1097
+ this.bufferedSequences.add(event.sequence);
1098
+ this.bufferedEvents.push(event);
1099
+ if (this.bufferedEvents.length > MAX_BUFFERED_EVENTS) {
1100
+ const dropped = this.bufferedEvents.shift();
1101
+ if (dropped) this.bufferedSequences.delete(dropped.sequence);
1102
+ }
1103
+ }
1104
+ const waiter = this.iteratorWaiters.shift();
1105
+ if (waiter) {
1106
+ waiter({
1107
+ value: event,
1108
+ done: false
1109
+ });
1110
+ return;
1111
+ }
1112
+ this.iteratorQueue.push(event);
1113
+ }
1114
+ /**
1115
+ * Authoritative backfill for `finished`: `run/result` (terminal summary)
1116
+ * plus the persisted `run/events` stream, falling back to the live buffer
1117
+ * when the query fails.
1118
+ */
1119
+ async aggregate(view) {
1120
+ const result = await this.runClient.result(this.runId).catch(() => null);
1121
+ const events = await this.runClient.events({
1122
+ runId: this.runId,
1123
+ afterSequence: 0,
1124
+ limit: MAX_BUFFERED_EVENTS
1125
+ }).catch(() => this.bufferedEvents);
1126
+ const source = events.length > 0 ? events : this.bufferedEvents;
1127
+ return aggregateTurnResult(result ?? view, source);
1128
+ }
1129
+ /** Poll run/get until terminal, then resolve all `finished` waiters once. */
1130
+ ensureTerminalWatch() {
1131
+ (async () => {
1132
+ const deadline = Date.now() + FINISHED_TIMEOUT_MS;
1133
+ try {
1134
+ while (!this.terminalView && Date.now() < deadline) {
1135
+ const view = await this.poll(this.runId);
1136
+ if (TERMINAL.includes(view.status)) this.terminalView = view;
1137
+ else await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
1138
+ }
1139
+ const waiters = this.finishWaiters;
1140
+ this.finishWaiters = [];
1141
+ if (!this.terminalView) {
1142
+ const timeout = /* @__PURE__ */ new Error(`run ${this.runId} did not reach a terminal state in time`);
1143
+ for (const waiter of waiters) waiter(timeout);
1144
+ return;
1145
+ }
1146
+ this.terminalResult = await this.aggregate(this.terminalView);
1147
+ for (const waiter of waiters) waiter(void 0, this.terminalResult);
1148
+ } catch (error) {
1149
+ const waiters = this.finishWaiters;
1150
+ this.finishWaiters = [];
1151
+ for (const waiter of waiters) waiter(error);
1152
+ }
1153
+ })();
1154
+ }
1155
+ };
1156
+ /** Codex-parity launch: start the run and return a live handle immediately. */
1157
+ async function launchRun(runClient, input, options = {}) {
1158
+ const receipt = await runClient.agent(input);
1159
+ return new AgentRunHandle(runClient, receipt, options.follow === false ? null : await runClient.follow(receipt.run_id), (runId) => runClient.get(runId));
1160
+ }
1161
+ //#endregion
1162
+ //#region src/retry.ts
1163
+ /**
1164
+ * Retry helper (REQ-PAR-05e, ≈ Codex `retry_on_overload`): exponential backoff
1165
+ * with jitter, driven by the protocol's stable `retryable` hint.
1166
+ *
1167
+ * Only errors the server/transport marks retryable are retried; protocol
1168
+ * errors that carry `retryable: false` (validation, not_found, ...) surface
1169
+ * immediately. `command_id` / `idempotency_key` callers stay safe because the
1170
+ * App Server deduplicates replayed mutations.
1171
+ */
1172
+ /** Run `operation`, retrying retryable failures with exponential backoff. */
1173
+ async function withRetry(operation, options = {}) {
1174
+ const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
1175
+ const baseDelayMs = Math.max(0, options.baseDelayMs ?? 500);
1176
+ const maxDelayMs = Math.max(baseDelayMs, options.maxDelayMs ?? 8e3);
1177
+ const jitter = Math.min(1, Math.max(0, options.jitter ?? .25));
1178
+ const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1179
+ const shouldRetry = options.shouldRetry ?? ((error) => (0, _flowy_agent_store_protocol.isRetryableError)(error));
1180
+ let lastError;
1181
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) try {
1182
+ return await operation();
1183
+ } catch (error) {
1184
+ lastError = error;
1185
+ if (attempt >= maxAttempts || !shouldRetry(error, attempt)) throw error;
1186
+ const ceiling = Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1));
1187
+ const delayMs = Math.round(ceiling * (1 - jitter) + Math.random() * ceiling * jitter);
1188
+ options.onRetry?.({
1189
+ attempt,
1190
+ delayMs,
1191
+ error
1192
+ });
1193
+ await sleep(delayMs);
1194
+ }
1195
+ throw lastError;
1196
+ }
1197
+ //#endregion
1198
+ exports.AgentClient = AgentClient;
1199
+ exports.AgentRunHandle = AgentRunHandle;
1200
+ exports.AppServerClient = AppServerClient;
1201
+ exports.ConnectorClient = ConnectorClient;
1202
+ exports.ConversationClient = ConversationClient;
1203
+ exports.ConversationHandle = ConversationHandle;
1204
+ exports.ConversationSubscription = ConversationSubscription;
1205
+ exports.EventSubscription = EventSubscription;
1206
+ exports.ModelClient = ModelClient;
1207
+ exports.RunClient = RunClient;
1208
+ exports.SkillClient = SkillClient;
1209
+ exports.TeamClient = TeamClient;
1210
+ exports.WebSocketTransport = WebSocketTransport;
1211
+ exports.WorkspaceClient = WorkspaceClient;
1212
+ exports.aggregateTurnResult = aggregateTurnResult;
1213
+ exports.conversationMessages = conversationMessages;
1214
+ exports.isLoopbackUrl = isLoopbackUrl;
1215
+ exports.launchRun = launchRun;
1216
+ exports.withRetry = withRetry;