@raisindb/client 0.1.5 → 0.1.17

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 (44) hide show
  1. package/README.md +281 -70
  2. package/dist/chunk-DJDP5CX6.mjs +866 -0
  3. package/dist/chunk-DJDP5CX6.mjs.map +1 -0
  4. package/dist/chunk-JRFBP4WK.mjs +170 -0
  5. package/dist/chunk-JRFBP4WK.mjs.map +1 -0
  6. package/dist/chunk-OLJOZIIU.mjs +1348 -0
  7. package/dist/chunk-OLJOZIIU.mjs.map +1 -0
  8. package/dist/chunk-T2ZSNYFG.mjs +248 -0
  9. package/dist/chunk-T2ZSNYFG.mjs.map +1 -0
  10. package/dist/chunk-VZX3ACSO.mjs +190 -0
  11. package/dist/chunk-VZX3ACSO.mjs.map +1 -0
  12. package/dist/chunk-WFBAWOVM.mjs +532 -0
  13. package/dist/chunk-WFBAWOVM.mjs.map +1 -0
  14. package/dist/conversation-list-store-CNcVagmw.d.mts +6120 -0
  15. package/dist/conversation-list-store-CNcVagmw.d.ts +6120 -0
  16. package/dist/index-DQjE-4Oj.d.ts +301 -0
  17. package/dist/index-FkFH4HHQ.d.mts +301 -0
  18. package/dist/index.d.mts +66 -6108
  19. package/dist/index.d.ts +66 -6108
  20. package/dist/index.js +859 -109
  21. package/dist/index.js.map +1 -0
  22. package/dist/index.mjs +639 -3108
  23. package/dist/index.mjs.map +1 -0
  24. package/dist/react.d.mts +4 -0
  25. package/dist/react.d.ts +4 -0
  26. package/dist/react.js +2500 -0
  27. package/dist/react.js.map +1 -0
  28. package/dist/react.mjs +9 -0
  29. package/dist/react.mjs.map +1 -0
  30. package/dist/svelte-sql-62EaYoeM.d.ts +52 -0
  31. package/dist/svelte-sql-CgT-Ij-P.d.mts +52 -0
  32. package/dist/svelte.d.mts +288 -0
  33. package/dist/svelte.d.ts +288 -0
  34. package/dist/svelte.js +2413 -0
  35. package/dist/svelte.js.map +1 -0
  36. package/dist/svelte.mjs +25 -0
  37. package/dist/svelte.mjs.map +1 -0
  38. package/dist/vue.d.mts +197 -0
  39. package/dist/vue.d.ts +197 -0
  40. package/dist/vue.js +1441 -0
  41. package/dist/vue.js.map +1 -0
  42. package/dist/vue.mjs +9 -0
  43. package/dist/vue.mjs.map +1 -0
  44. package/package.json +18 -3
package/dist/index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,19 +15,11 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
21
+ var src_exports = {};
22
+ __export(src_exports, {
33
23
  AllNodeEventTypes: () => AllNodeEventTypes,
34
24
  Archetypes: () => Archetypes,
35
25
  AuthManager: () => AuthManager,
@@ -49,6 +39,7 @@ __export(index_exports, {
49
39
  DEFAULT_RETRY_BASE_DELAY: () => DEFAULT_RETRY_BASE_DELAY,
50
40
  DEFAULT_RETRY_MAX_DELAY: () => DEFAULT_RETRY_MAX_DELAY,
51
41
  DEFAULT_RETRY_OPTIONS: () => DEFAULT_RETRY_OPTIONS,
42
+ DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS: () => DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS,
52
43
  Database: () => Database,
53
44
  ElementTypes: () => ElementTypes,
54
45
  EventHandler: () => EventHandler,
@@ -59,6 +50,7 @@ __export(index_exports, {
59
50
  HttpDatabase: () => HttpDatabase,
60
51
  HttpFunctionsApi: () => HttpFunctionsApi,
61
52
  HttpWorkspaceClient: () => HttpWorkspaceClient,
53
+ InboxApi: () => InboxApi,
62
54
  LocalStorageTokenStorage: () => LocalStorageTokenStorage,
63
55
  LogLevel: () => LogLevel,
64
56
  MemoryTokenStorage: () => MemoryTokenStorage,
@@ -69,6 +61,7 @@ __export(index_exports, {
69
61
  NodeOperations: () => NodeOperations,
70
62
  NodeTypes: () => NodeTypes,
71
63
  RAISIN_CONTEXT_KEY: () => RAISIN_CONTEXT_KEY,
64
+ RETRY_STRATEGIES: () => RETRY_STRATEGIES,
72
65
  RaisinAbortError: () => RaisinAbortError,
73
66
  RaisinAuthError: () => RaisinAuthError,
74
67
  RaisinClient: () => RaisinClient,
@@ -104,6 +97,7 @@ __export(index_exports, {
104
97
  createFlowAdapter: () => createFlowAdapter,
105
98
  createLoader: () => createLoader,
106
99
  createRaisinReact: () => createRaisinReact,
100
+ createRaisinVue: () => createRaisinVue,
107
101
  createRequestScopedClient: () => createRequestScopedClient,
108
102
  createSSRClient: () => createSSRClient,
109
103
  createSqlAdapter: () => createSqlAdapter,
@@ -116,13 +110,18 @@ __export(index_exports, {
116
110
  getErrorMessage: () => getErrorMessage,
117
111
  getLogLevel: () => getLogLevel,
118
112
  getLoggerConfig: () => getLoggerConfig,
113
+ getRefDisplayName: () => getRefDisplayName,
114
+ getRefPath: () => getRefPath,
119
115
  isEventMessage: () => isEventMessage,
116
+ isFlowDefinitionContainer: () => isFlowDefinitionContainer,
117
+ isFlowDefinitionStep: () => isFlowDefinitionStep,
120
118
  isHttpClient: () => isHttpClient,
121
119
  isResponseEnvelope: () => isResponseEnvelope,
122
120
  isRetryableError: () => isRetryableError,
123
121
  isTerminalEvent: () => isTerminalEvent,
124
122
  isWebSocketClient: () => isWebSocketClient,
125
123
  logger: () => logger,
124
+ normalizeHomePath: () => normalizeHomePath,
126
125
  projectPlansFromMessages: () => projectPlansFromMessages,
127
126
  rowsToObjects: () => rowsToObjects,
128
127
  setLogLevel: () => setLogLevel,
@@ -132,7 +131,7 @@ __export(index_exports, {
132
131
  useFlow: () => useFlow,
133
132
  withRetry: () => withRetry
134
133
  });
135
- module.exports = __toCommonJS(index_exports);
134
+ module.exports = __toCommonJS(src_exports);
136
135
 
137
136
  // src/client.ts
138
137
  var import_events4 = require("events");
@@ -326,7 +325,8 @@ var Connection = class extends import_events.EventEmitter {
326
325
  autoReconnect: options.autoReconnect ?? true,
327
326
  heartbeatInterval: options.heartbeatInterval ?? 3e4,
328
327
  heartbeatTimeout: options.heartbeatTimeout ?? 5e3,
329
- protocols: options.protocols
328
+ protocols: options.protocols,
329
+ headers: options.headers
330
330
  };
331
331
  this.reconnectManager = new ReconnectManager(options.reconnectOptions);
332
332
  this.WebSocketImpl = getWebSocketImpl();
@@ -342,10 +342,10 @@ var Connection = class extends import_events.EventEmitter {
342
342
  this.setState("connecting" /* Connecting */);
343
343
  return new Promise((resolve, reject) => {
344
344
  try {
345
- this.ws = new this.WebSocketImpl(
346
- this.url,
347
- this.options.protocols
348
- );
345
+ const isBrowserWebSocket = typeof WebSocket !== "undefined" && this.WebSocketImpl === WebSocket;
346
+ this.ws = isBrowserWebSocket || !this.options.headers ? new this.WebSocketImpl(this.url, this.options.protocols) : new this.WebSocketImpl(this.url, this.options.protocols, {
347
+ headers: this.options.headers
348
+ });
349
349
  this.ws.binaryType = "arraybuffer";
350
350
  this.ws.onopen = () => {
351
351
  this.setState("connected" /* Connected */);
@@ -434,6 +434,16 @@ var Connection = class extends import_events.EventEmitter {
434
434
  isConnected() {
435
435
  return this.state === "connected" /* Connected */;
436
436
  }
437
+ /**
438
+ * Whether auto-reconnect is enabled for this connection.
439
+ *
440
+ * Used by the client to decide whether requests issued while the
441
+ * connection is down can be queued (the connection will come back) or
442
+ * should fail immediately.
443
+ */
444
+ isAutoReconnectEnabled() {
445
+ return this.options.autoReconnect;
446
+ }
437
447
  /**
438
448
  * Reconnect to the server
439
449
  */
@@ -1010,6 +1020,14 @@ var EventHandler = class {
1010
1020
  * If a subscription with identical filters already exists, the callback
1011
1021
  * is added to the existing subscription instead of creating a new one.
1012
1022
  *
1023
+ * Path filter semantics (matched server-side):
1024
+ * - A plain path matches **only that exact node** — there is no implicit
1025
+ * prefix matching.
1026
+ * - `*` matches exactly one path segment (e.g. `/inbox/*` matches
1027
+ * `/inbox/a` but not `/inbox/a/b`).
1028
+ * - `**` matches recursively (e.g. `/inbox/**` matches all descendants
1029
+ * of `/inbox`, but not `/inbox` itself).
1030
+ *
1013
1031
  * @param filters - Event filters
1014
1032
  * @param callback - Callback function for events
1015
1033
  * @returns Subscription object
@@ -1200,14 +1218,29 @@ var EventHandler = class {
1200
1218
  * Restore subscriptions after reconnect
1201
1219
  *
1202
1220
  * Re-subscribes to the server with the original filters for all active
1203
- * subscription entries.
1221
+ * subscription entries. Failed entries are kept (with their callbacks)
1222
+ * and marked pending so a retry call can re-attempt them without
1223
+ * duplicating already-restored subscriptions.
1224
+ *
1225
+ * @param options.retry - When true, only re-attempts entries that failed
1226
+ * in a previous call (instead of re-subscribing everything)
1227
+ * @throws Error if any subscription could not be restored
1204
1228
  */
1205
- async restoreSubscriptions() {
1206
- const entriesToRestore = Array.from(this.filterSubscriptions.entries());
1207
- this.subscriptions.clear();
1208
- this.filterSubscriptions.clear();
1229
+ async restoreSubscriptions(options) {
1230
+ if (!options?.retry) {
1231
+ for (const entry of this.filterSubscriptions.values()) {
1232
+ this.subscriptions.delete(entry.serverId);
1233
+ entry.restorePending = true;
1234
+ }
1235
+ }
1236
+ const entriesToRestore = Array.from(this.filterSubscriptions.entries()).filter(([, entry]) => entry.restorePending);
1237
+ let failureCount = 0;
1238
+ let lastError;
1209
1239
  for (const [hash, entry] of entriesToRestore) {
1210
- if (entry.callbacks.size === 0) continue;
1240
+ if (entry.callbacks.size === 0) {
1241
+ entry.restorePending = false;
1242
+ continue;
1243
+ }
1211
1244
  try {
1212
1245
  const payload = { filters: entry.filters };
1213
1246
  const response = await this.sendRequest(
@@ -1215,22 +1248,24 @@ var EventHandler = class {
1215
1248
  "subscribe" /* Subscribe */
1216
1249
  );
1217
1250
  const newServerId = response.subscription_id;
1218
- this.filterSubscriptions.set(hash, {
1219
- serverId: newServerId,
1220
- filters: entry.filters,
1221
- callbacks: entry.callbacks
1222
- });
1251
+ entry.serverId = newServerId;
1252
+ entry.restorePending = false;
1223
1253
  this.subscriptions.set(newServerId, hash);
1224
1254
  for (const callback of entry.callbacks) {
1225
1255
  this.callbackToHash.set(callback, hash);
1226
1256
  }
1227
1257
  } catch (error) {
1228
1258
  logger.error("Failed to restore subscription:", error);
1229
- for (const callback of entry.callbacks) {
1230
- this.callbackToHash.delete(callback);
1231
- }
1259
+ failureCount++;
1260
+ lastError = error;
1232
1261
  }
1233
1262
  }
1263
+ if (failureCount > 0) {
1264
+ const message = lastError instanceof Error ? lastError.message : String(lastError);
1265
+ throw new Error(
1266
+ `Failed to restore ${failureCount} subscription(s) after reconnect: ${message}`
1267
+ );
1268
+ }
1234
1269
  }
1235
1270
  };
1236
1271
  var EventSubscriptions = class {
@@ -1265,7 +1300,15 @@ var EventSubscriptions = class {
1265
1300
  /**
1266
1301
  * Subscribe to events for a specific path
1267
1302
  *
1268
- * @param path - Path pattern (supports wildcards like "/folder/*")
1303
+ * Path pattern semantics (matched server-side):
1304
+ * - A plain path matches **only that exact node** — there is no implicit
1305
+ * prefix matching, so `/folder` does NOT match `/folder/child`.
1306
+ * - `*` matches exactly one path segment: `/folder/*` matches
1307
+ * `/folder/child` but not `/folder/child/grandchild`.
1308
+ * - `**` matches recursively: `/folder/**` matches every descendant of
1309
+ * `/folder` (but not `/folder` itself).
1310
+ *
1311
+ * @param path - Path pattern (e.g. "/folder", "/folder/*", "/folder/**")
1269
1312
  * @param callback - Callback function for events
1270
1313
  * @param options - Optional subscription options
1271
1314
  * @param options.includeNode - Include full node data in event payload (default: false)
@@ -1897,7 +1940,17 @@ var NodeFileSource = class {
1897
1940
  return;
1898
1941
  }
1899
1942
  if (!this._fsPromises) {
1900
- this._fsPromises = await import("fs/promises");
1943
+ if (!isNodeEnvironment()) {
1944
+ throw new Error(
1945
+ "NodeFileSource requires Node.js (fs/promises is not available in browsers). In the browser, use a File or Blob object instead."
1946
+ );
1947
+ }
1948
+ const specifier = "fs/promises";
1949
+ this._fsPromises = await import(
1950
+ /* @vite-ignore */
1951
+ /* webpackIgnore: true */
1952
+ specifier
1953
+ );
1901
1954
  }
1902
1955
  const stats = await this._fsPromises.stat(this._filePath);
1903
1956
  this._size = stats.size;
@@ -3241,6 +3294,7 @@ var SSEClient = class {
3241
3294
  this._lastEventId = options.lastEventId;
3242
3295
  this.autoReconnect = options.reconnect?.enabled ?? true;
3243
3296
  this.externalSignal = options.signal;
3297
+ this.inactivityTimeoutMs = options.inactivityTimeoutMs;
3244
3298
  this.eventTypeSet = options.eventTypes?.length ? new Set(options.eventTypes) : null;
3245
3299
  this.reconnectManager = new ReconnectManager({
3246
3300
  initialDelay: options.reconnect?.initialDelay ?? 1e3,
@@ -3384,6 +3438,23 @@ var SSEClient = class {
3384
3438
  if (err.name === "AbortError" || err instanceof RaisinAbortError) {
3385
3439
  return;
3386
3440
  }
3441
+ if (err instanceof RaisinTimeoutError && err.code === "SSE_INACTIVITY_TIMEOUT") {
3442
+ logger.warn("SSE inactivity timeout:", err.message);
3443
+ if (!this.autoReconnect) {
3444
+ this.setState("disconnected");
3445
+ onError?.(err);
3446
+ throw err;
3447
+ }
3448
+ this.setState("reconnecting");
3449
+ const scheduled2 = await this.waitForReconnect();
3450
+ if (!scheduled2) {
3451
+ logger.warn("SSE max reconnection attempts reached");
3452
+ this.setState("disconnected");
3453
+ onError?.(err);
3454
+ throw err;
3455
+ }
3456
+ continue;
3457
+ }
3387
3458
  logger.error("SSE connection error:", err.message);
3388
3459
  onError?.(err);
3389
3460
  if (!this.autoReconnect) {
@@ -3432,7 +3503,9 @@ var SSEClient = class {
3432
3503
  fetchOptions.headers["Content-Type"] = "application/json";
3433
3504
  fetchOptions.body = JSON.stringify(this.options.body);
3434
3505
  }
3435
- const response = await this.fetchImpl(this.url, fetchOptions);
3506
+ const response = await this.withInactivityTimeout(
3507
+ this.fetchImpl(this.url, fetchOptions)
3508
+ );
3436
3509
  if (!response.ok) {
3437
3510
  const text = await response.text().catch(() => "");
3438
3511
  const msg = `SSE connection failed: ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`;
@@ -3455,7 +3528,7 @@ var SSEClient = class {
3455
3528
  let buffer = "";
3456
3529
  try {
3457
3530
  while (true) {
3458
- const { done, value } = await reader.read();
3531
+ const { done, value } = await this.withInactivityTimeout(reader.read());
3459
3532
  if (done) break;
3460
3533
  buffer += decoder.decode(value, { stream: true });
3461
3534
  const blocks = buffer.split("\n\n");
@@ -3486,9 +3559,48 @@ var SSEClient = class {
3486
3559
  }
3487
3560
  }
3488
3561
  } finally {
3489
- reader.releaseLock();
3562
+ try {
3563
+ reader.releaseLock();
3564
+ } catch {
3565
+ }
3490
3566
  }
3491
3567
  }
3568
+ /**
3569
+ * Race a stream operation against the inactivity timeout.
3570
+ *
3571
+ * If no bytes (or response) arrive within `inactivityTimeoutMs`, the
3572
+ * internal connection is aborted and the operation rejects with a
3573
+ * `RaisinTimeoutError` (code `SSE_INACTIVITY_TIMEOUT`). The timer is
3574
+ * effectively reset on every chunk because each `reader.read()` is
3575
+ * wrapped individually.
3576
+ */
3577
+ withInactivityTimeout(promise) {
3578
+ const timeoutMs = this.inactivityTimeoutMs;
3579
+ if (!timeoutMs || timeoutMs <= 0) return promise;
3580
+ return new Promise((resolve, reject) => {
3581
+ const timer = setTimeout(() => {
3582
+ promise.catch(() => void 0);
3583
+ this.abortController?.abort();
3584
+ reject(
3585
+ new RaisinTimeoutError(
3586
+ `SSE stream inactive for ${timeoutMs}ms`,
3587
+ "SSE_INACTIVITY_TIMEOUT",
3588
+ timeoutMs
3589
+ )
3590
+ );
3591
+ }, timeoutMs);
3592
+ promise.then(
3593
+ (value) => {
3594
+ clearTimeout(timer);
3595
+ resolve(value);
3596
+ },
3597
+ (error) => {
3598
+ clearTimeout(timer);
3599
+ reject(error);
3600
+ }
3601
+ );
3602
+ });
3603
+ }
3492
3604
  /**
3493
3605
  * Wait for the reconnect manager to schedule a reconnection attempt.
3494
3606
  * Returns true if reconnection was scheduled, false if max attempts reached.
@@ -3513,7 +3625,24 @@ function isRecoveredDoneEvent(event) {
3513
3625
  return typeof done.content !== "string" || done.content.trim().length === 0;
3514
3626
  }
3515
3627
 
3628
+ // src/utils/home-path.ts
3629
+ var ACCESS_CONTROL_PREFIX = "/raisin:access_control";
3630
+ function normalizeHomePath(home) {
3631
+ if (home === null || home === void 0) return null;
3632
+ let path = home.trim();
3633
+ if (path.length === 0) return null;
3634
+ if (path === ACCESS_CONTROL_PREFIX) return "/";
3635
+ if (path.startsWith(`${ACCESS_CONTROL_PREFIX}/`)) {
3636
+ path = path.slice(ACCESS_CONTROL_PREFIX.length);
3637
+ }
3638
+ while (path.length > 1 && path.endsWith("/")) {
3639
+ path = path.slice(0, -1);
3640
+ }
3641
+ return path;
3642
+ }
3643
+
3516
3644
  // src/conversations.ts
3645
+ var DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS = 12e4;
3517
3646
  var ConversationManager = class {
3518
3647
  constructor(baseUrl, repository, authManager, options = {}, executeSql) {
3519
3648
  this.baseUrl = baseUrl.replace(/\/$/, "");
@@ -3535,10 +3664,11 @@ var ConversationManager = class {
3535
3664
  );
3536
3665
  if (!result?.rows?.[0]) throw new Error("User not authenticated");
3537
3666
  const row = result.rows[0];
3538
- this.cachedUserHome = row.home;
3539
- this.cachedUserId = row.user_id || this.cachedUserHome;
3540
- if (!this.cachedUserHome) throw new Error("User home path not found");
3541
- return { userId: this.cachedUserId, userHome: this.cachedUserHome };
3667
+ const userHome = normalizeHomePath(row.home);
3668
+ if (!userHome) throw new Error("User home path not found");
3669
+ this.cachedUserHome = userHome;
3670
+ this.cachedUserId = row.user_id || userHome;
3671
+ return { userId: this.cachedUserId, userHome };
3542
3672
  }
3543
3673
  // ==========================================================================
3544
3674
  // Conversation listing
@@ -3733,13 +3863,15 @@ var ConversationManager = class {
3733
3863
  fetch: this.fetchImpl,
3734
3864
  signal: options?.signal,
3735
3865
  method: "POST",
3736
- body: { channel: streamTarget.channel, path: streamTarget.path }
3866
+ body: { channel: streamTarget.channel, path: streamTarget.path },
3867
+ inactivityTimeoutMs: options?.inactivityTimeoutMs ?? DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS
3737
3868
  });
3738
3869
  const iterator = sse[Symbol.asyncIterator]();
3739
3870
  let pendingFirst = iterator.next();
3740
3871
  try {
3741
3872
  await sse.waitUntilConnected();
3742
3873
  } catch {
3874
+ pendingFirst?.catch(() => void 0);
3743
3875
  pendingFirst = null;
3744
3876
  sse.close();
3745
3877
  logger.debug("SSE connection failed, falling back to create-only");
@@ -3776,6 +3908,15 @@ var ConversationManager = class {
3776
3908
  return;
3777
3909
  }
3778
3910
  }
3911
+ } catch (error) {
3912
+ if (error instanceof RaisinTimeoutError && error.code === "SSE_INACTIVITY_TIMEOUT") {
3913
+ logger.warn(
3914
+ `[ConversationManager] SSE stream inactive for ${error.timeoutMs}ms, ending turn with synthetic waiting event`
3915
+ );
3916
+ yield { type: "waiting", timestamp: (/* @__PURE__ */ new Date()).toISOString() };
3917
+ return;
3918
+ }
3919
+ throw error;
3779
3920
  } finally {
3780
3921
  sse.close();
3781
3922
  }
@@ -4538,31 +4679,29 @@ var FlowClient = class _FlowClient {
4538
4679
  * Respond to a human task within a flow.
4539
4680
  *
4540
4681
  * When a flow is waiting on a human_task step (approval, input, review),
4541
- * use this method to submit the response and resume the flow.
4682
+ * use this method to submit the response. Completion is validated
4683
+ * (caller must be the task's assignee or an admin) and resumes the flow.
4542
4684
  *
4543
- * @param instanceId - Flow instance ID
4544
- * @param taskId - The human task node ID
4545
- * @param response - The human's response data
4685
+ * Note: prefer `InboxApi` for full inbox functionality (listing tasks,
4686
+ * task details); this is a convenience passthrough to the same endpoint.
4687
+ *
4688
+ * @param instanceId - Flow instance ID (unused; kept for API stability)
4689
+ * @param taskId - The inbox task node ID (or path)
4690
+ * @param response - The response data
4546
4691
  *
4547
4692
  * @example
4548
4693
  * ```typescript
4549
4694
  * // Approve a task
4550
- * await flows.respondToHumanTask('instance-123', 'step-5', {
4695
+ * await flows.respondToHumanTask('instance-123', 'task-abc', {
4551
4696
  * action: 'approve',
4552
4697
  * comment: 'Looks good!',
4553
4698
  * });
4554
- *
4555
- * // Provide input for an input task
4556
- * await flows.respondToHumanTask('instance-123', 'step-3', {
4557
- * name: 'John Doe',
4558
- * email: 'john@example.com',
4559
- * });
4560
4699
  * ```
4561
4700
  */
4562
- async respondToHumanTask(instanceId, taskId, response, options) {
4701
+ async respondToHumanTask(_instanceId, taskId, response, options) {
4563
4702
  await this.request({
4564
4703
  method: "POST",
4565
- path: `/api/flows/${this.repository}/instances/${instanceId}/tasks/${taskId}/respond`,
4704
+ path: `/api/inbox/${this.repository}/tasks/${encodeURIComponent(taskId)}/complete`,
4566
4705
  body: { response },
4567
4706
  signal: options?.signal
4568
4707
  });
@@ -4701,6 +4840,134 @@ var FlowClient = class _FlowClient {
4701
4840
  }
4702
4841
  };
4703
4842
 
4843
+ // src/inbox.ts
4844
+ var InboxApi = class {
4845
+ constructor(baseUrl, repository, authManager, options = {}) {
4846
+ this.baseUrl = baseUrl.replace(/\/$/, "");
4847
+ this.repository = repository;
4848
+ this.authManager = authManager;
4849
+ this.fetchImpl = options.fetch ?? fetch;
4850
+ this.requestTimeout = options.requestTimeout ?? 3e4;
4851
+ }
4852
+ /**
4853
+ * List the caller's inbox tasks.
4854
+ *
4855
+ * Tasks are sorted pending-first, then by priority (highest first) and
4856
+ * due time (earliest first).
4857
+ */
4858
+ async listTasks(options = {}) {
4859
+ const params = new URLSearchParams();
4860
+ if (options.status) params.set("status", options.status);
4861
+ if (options.assignee) params.set("assignee", options.assignee);
4862
+ const query = params.size > 0 ? `?${params.toString()}` : "";
4863
+ return this.request({
4864
+ method: "GET",
4865
+ path: `/api/inbox/${this.repository}${query}`,
4866
+ signal: options.signal
4867
+ });
4868
+ }
4869
+ /**
4870
+ * Get a single inbox task by ID (or path).
4871
+ */
4872
+ async getTask(taskId, options) {
4873
+ return this.request({
4874
+ method: "GET",
4875
+ path: `/api/inbox/${this.repository}/tasks/${encodeURIComponent(taskId)}`,
4876
+ signal: options?.signal
4877
+ });
4878
+ }
4879
+ /**
4880
+ * Complete an inbox task.
4881
+ *
4882
+ * The caller must be the task's assignee (or an admin). Completion marks
4883
+ * the task done, records who decided, and resumes the owning flow.
4884
+ *
4885
+ * @param taskId - Task node ID (or path)
4886
+ * @param response - The response payload:
4887
+ * - approval tasks: `{ action: '<option value>', comment?: string }`
4888
+ * - input tasks: the input value(s) matching the task's input_schema
4889
+ * - review/action tasks: any acknowledgement payload
4890
+ *
4891
+ * @example
4892
+ * ```typescript
4893
+ * await inbox.completeTask('task-123', { action: 'approve' });
4894
+ * ```
4895
+ */
4896
+ async completeTask(taskId, response, options) {
4897
+ return this.request({
4898
+ method: "POST",
4899
+ path: `/api/inbox/${this.repository}/tasks/${encodeURIComponent(taskId)}/complete`,
4900
+ body: { response },
4901
+ signal: options?.signal
4902
+ });
4903
+ }
4904
+ // ==========================================================================
4905
+ // Internal HTTP request helper
4906
+ // ==========================================================================
4907
+ async request(options) {
4908
+ const url = `${this.baseUrl}${options.path}`;
4909
+ const headers = {
4910
+ "Content-Type": "application/json"
4911
+ };
4912
+ const token = this.authManager.getAccessToken();
4913
+ if (token) {
4914
+ headers["Authorization"] = `Bearer ${token}`;
4915
+ }
4916
+ const controller = new AbortController();
4917
+ const timeoutId = setTimeout(() => controller.abort(), this.requestTimeout);
4918
+ if (options.signal) {
4919
+ if (options.signal.aborted) {
4920
+ clearTimeout(timeoutId);
4921
+ throw new RaisinAbortError();
4922
+ }
4923
+ options.signal.addEventListener("abort", () => controller.abort(), {
4924
+ once: true
4925
+ });
4926
+ }
4927
+ try {
4928
+ const response = await this.fetchImpl(url, {
4929
+ method: options.method,
4930
+ headers,
4931
+ body: options.body ? JSON.stringify(options.body) : void 0,
4932
+ signal: controller.signal
4933
+ });
4934
+ clearTimeout(timeoutId);
4935
+ if (!response.ok) {
4936
+ const errorText = await response.text();
4937
+ let errorMessage;
4938
+ try {
4939
+ const errorJson = JSON.parse(errorText);
4940
+ errorMessage = errorJson.message || errorJson.error || errorText;
4941
+ } catch {
4942
+ errorMessage = errorText || `HTTP ${response.status}: ${response.statusText}`;
4943
+ }
4944
+ throw classifyHttpError(response.status, errorMessage);
4945
+ }
4946
+ const contentType = response.headers.get("content-type");
4947
+ if (contentType?.includes("application/json")) {
4948
+ return await response.json();
4949
+ }
4950
+ return void 0;
4951
+ } catch (error) {
4952
+ clearTimeout(timeoutId);
4953
+ if (error instanceof RaisinAbortError || error instanceof RaisinTimeoutError) {
4954
+ throw error;
4955
+ }
4956
+ if (error instanceof Error && error.name === "AbortError") {
4957
+ if (options.signal?.aborted) {
4958
+ throw new RaisinAbortError();
4959
+ }
4960
+ throw new RaisinTimeoutError(
4961
+ `Request timeout after ${this.requestTimeout}ms`,
4962
+ "REQUEST_TIMEOUT",
4963
+ this.requestTimeout
4964
+ );
4965
+ }
4966
+ throw error;
4967
+ }
4968
+ }
4969
+ };
4970
+
4704
4971
  // src/database.ts
4705
4972
  var Database = class _Database {
4706
4973
  constructor(repository, context, sendRequest, eventHandler, branch, revision, getUploadManager, getSignAssetUrl, httpOptions) {
@@ -4977,8 +5244,44 @@ var Database = class _Database {
4977
5244
  }
4978
5245
  return this._flowClient;
4979
5246
  }
5247
+ /**
5248
+ * Get a pre-configured InboxApi for human-in-the-loop tasks.
5249
+ *
5250
+ * Requires the Database to have been created via `RaisinClient.database()`
5251
+ * (which automatically provides HTTP context).
5252
+ *
5253
+ * @example
5254
+ * ```typescript
5255
+ * const db = client.database('my-repo');
5256
+ * const { tasks } = await db.inbox.listTasks({ status: 'pending' });
5257
+ * await db.inbox.completeTask(tasks[0].id, { action: 'approve' });
5258
+ * ```
5259
+ */
5260
+ get inbox() {
5261
+ if (!this._inboxApi) {
5262
+ if (!this.httpOptions?.httpBaseUrl || !this.httpOptions?.authManager) {
5263
+ throw new Error(
5264
+ "db.inbox requires HTTP context. Use client.database() to get a Database with inbox support."
5265
+ );
5266
+ }
5267
+ this._inboxApi = new InboxApi(
5268
+ this.httpOptions.httpBaseUrl,
5269
+ this.repository,
5270
+ this.httpOptions.authManager
5271
+ );
5272
+ }
5273
+ return this._inboxApi;
5274
+ }
4980
5275
  /**
4981
5276
  * Get EventHandler for subscribing to real-time events
5277
+ *
5278
+ * Path filter semantics for subscriptions (matched server-side):
5279
+ * - A plain path matches **only that exact node** — there is no implicit
5280
+ * prefix matching.
5281
+ * - `*` matches exactly one path segment (`/inbox/*` matches `/inbox/a`,
5282
+ * not `/inbox/a/b`).
5283
+ * - `**` matches recursively (`/inbox/**` matches all descendants of
5284
+ * `/inbox`, but not `/inbox` itself).
4982
5285
  */
4983
5286
  events() {
4984
5287
  return this.eventHandler;
@@ -6306,9 +6609,13 @@ var RaisinHttpClient = class extends import_events3.EventEmitter {
6306
6609
  body: payload,
6307
6610
  skipAuth: true
6308
6611
  });
6309
- this.authManager.setTokens(response.data);
6612
+ if (response.data.token) {
6613
+ this.authManager.storage.setAccessToken(response.data.token);
6614
+ } else {
6615
+ this.authManager.setTokens(response.data);
6616
+ }
6310
6617
  this._currentUser = {
6311
- userId: "admin",
6618
+ userId: response.data.user_id ?? "admin",
6312
6619
  anonymous: false
6313
6620
  };
6314
6621
  this.emit("authenticated", response.data);
@@ -7196,7 +7503,7 @@ var HttpWorkspaceClient = class {
7196
7503
  };
7197
7504
 
7198
7505
  // src/client.ts
7199
- var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7506
+ var _RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7200
7507
  constructor(url, options = {}) {
7201
7508
  super();
7202
7509
  this._currentUser = null;
@@ -7210,23 +7517,41 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7210
7517
  // Reconnection tracking
7211
7518
  this._previousConnectionState = "disconnected" /* Disconnected */;
7212
7519
  this._reconnectedListeners = /* @__PURE__ */ new Set();
7520
+ this._everConnected = false;
7521
+ // Requests queued while the connection is re-establishing.
7522
+ // Flushed after reconnect + re-auth; bounded by MAX_QUEUED_REQUESTS.
7523
+ this._queuedRequests = [];
7524
+ /**
7525
+ * Backoff delays (ms) between subscription-restore retries after reconnect.
7526
+ * @internal - overridable in tests
7527
+ */
7528
+ this._restoreRetryDelaysMs = [1e3, 3e3, 9e3];
7213
7529
  // Upload manager (uses HTTP under the hood)
7214
7530
  this._uploadManager = null;
7215
7531
  if (options.logLevel !== void 0) {
7216
7532
  setLogLevel(options.logLevel);
7217
7533
  }
7534
+ const connectionUrl = this.buildConnectionUrl(url, options.repository);
7218
7535
  this.options = {
7219
7536
  requestTimeout: options.requestTimeout ?? 3e4,
7220
- tenantId: options.tenantId ?? this.extractTenantFromUrl(url),
7537
+ tenantId: options.tenantId ?? this.extractTenantFromUrl(connectionUrl),
7221
7538
  defaultBranch: options.defaultBranch ?? "main"
7222
7539
  };
7223
- this._httpBaseUrl = options.httpBaseUrl ?? this.deriveHttpUrl(url);
7224
- this._repository = this.extractRepositoryFromUrl(url);
7540
+ this._httpBaseUrl = options.httpBaseUrl ?? this.deriveHttpUrl(connectionUrl);
7541
+ this._repository = options.repository ?? this.extractRepositoryFromUrl(connectionUrl);
7225
7542
  this._context = {
7226
7543
  tenant_id: this.options.tenantId,
7227
7544
  branch: this.options.defaultBranch
7228
7545
  };
7229
- this.connection = new Connection(url, options.connection);
7546
+ const explicitTenant = options.tenantId ?? (this.extractTenantFromUrl(url) !== "default" ? this.options.tenantId : void 0);
7547
+ const connectionOptions = {
7548
+ ...options.connection,
7549
+ headers: {
7550
+ ...explicitTenant ? { "x-tenant-id": explicitTenant } : {},
7551
+ ...options.connection?.headers
7552
+ }
7553
+ };
7554
+ this.connection = this.createConnection(connectionUrl, connectionOptions);
7230
7555
  this.requestTracker = new RequestTracker({
7231
7556
  defaultTimeout: this.options.requestTimeout
7232
7557
  });
@@ -7236,6 +7561,14 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7236
7561
  );
7237
7562
  this.setupConnectionHandlers();
7238
7563
  }
7564
+ /**
7565
+ * Create the underlying WebSocket connection.
7566
+ * Seam for tests to inject a fake connection.
7567
+ * @internal
7568
+ */
7569
+ createConnection(url, options) {
7570
+ return new Connection(url, options);
7571
+ }
7239
7572
  /**
7240
7573
  * Derive HTTP URL from WebSocket URL
7241
7574
  * ws://host:port/path -> http://host:port
@@ -7251,15 +7584,45 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7251
7584
  return "http://localhost:8081";
7252
7585
  }
7253
7586
  }
7587
+ /**
7588
+ * Build the WebSocket connection URL.
7589
+ *
7590
+ * When a repository option is provided and the URL carries no path
7591
+ * (e.g. `ws://host:8081`), build the tenant-less `/ws/{repository}`
7592
+ * route internally. URLs that already carry a path are used as-is.
7593
+ */
7594
+ buildConnectionUrl(url, repository) {
7595
+ if (!repository) {
7596
+ return url;
7597
+ }
7598
+ try {
7599
+ const wsUrl = url.replace(/^raisin:\/\//, "ws://").replace(/^raisins:\/\//, "wss://");
7600
+ const parsed = new URL(wsUrl);
7601
+ const hasPath = parsed.pathname.split("/").some((p) => p.length > 0);
7602
+ if (hasPath) {
7603
+ return url;
7604
+ }
7605
+ return `${url.replace(/\/+$/, "")}/ws/${repository}`;
7606
+ } catch {
7607
+ return url;
7608
+ }
7609
+ }
7254
7610
  /**
7255
7611
  * Extract repository from URL
7256
- * URL format: raisin://host:port/tenant/repository or ws://host:port/tenant/repository
7612
+ * URL forms: /ws/{repo} (tenant-less, preferred), /sys/{tenant}/{repo}
7613
+ * (operator/multi-tenant), /{tenant}/{repo} (legacy).
7257
7614
  */
7258
7615
  extractRepositoryFromUrl(url) {
7259
7616
  try {
7260
7617
  const wsUrl = url.replace(/^raisin:\/\//, "ws://").replace(/^raisins:\/\//, "wss://");
7261
7618
  const parsed = new URL(wsUrl);
7262
7619
  const parts = parsed.pathname.split("/").filter((p) => p.length > 0);
7620
+ if (parts[0] === "ws") {
7621
+ return parts.length > 1 ? parts[1] : "";
7622
+ }
7623
+ if (parts[0] === "sys") {
7624
+ return parts.length > 2 ? parts[2] : "";
7625
+ }
7263
7626
  return parts.length > 1 ? parts[1] : "";
7264
7627
  } catch {
7265
7628
  return "";
@@ -7267,14 +7630,24 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7267
7630
  }
7268
7631
  /**
7269
7632
  * Extract tenant ID from URL
7270
- * URL format: raisin://host:port/tenant/repository or ws://host:port/tenant/repository
7633
+ * URL forms: /ws/{repo} (tenant-less — no tenant in URL),
7634
+ * /sys/{tenant}/{repo} (canonical), /{tenant}/{repo} (legacy).
7635
+ * When the URL doesn't carry a tenant, fall back to 'default' — the
7636
+ * server's tenant middleware resolves it anyway (production derives the
7637
+ * tenant from the request itself), so an explicit tenantId is optional.
7271
7638
  */
7272
7639
  extractTenantFromUrl(url) {
7273
7640
  try {
7274
7641
  const wsUrl = url.replace(/^raisin:\/\//, "ws://").replace(/^raisins:\/\//, "wss://");
7275
7642
  const parsed = new URL(wsUrl);
7276
7643
  const parts = parsed.pathname.split("/").filter((p) => p.length > 0);
7277
- return parts.length > 0 ? parts[0] : "default";
7644
+ if (parts[0] === "ws") {
7645
+ return "default";
7646
+ }
7647
+ if (parts[0] === "sys") {
7648
+ return parts.length > 1 ? parts[1] : "default";
7649
+ }
7650
+ return parts.length >= 2 ? parts[0] : "default";
7278
7651
  } catch (error) {
7279
7652
  return "default";
7280
7653
  }
@@ -7288,7 +7661,11 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7288
7661
  });
7289
7662
  this.connection.on("stateChange", (state) => {
7290
7663
  const wasDisconnected = this._previousConnectionState === "disconnected" /* Disconnected */ || this._previousConnectionState === "reconnecting" /* Reconnecting */;
7664
+ if (state === "connected" /* Connected */) {
7665
+ this._everConnected = true;
7666
+ }
7291
7667
  if (state === "disconnected" /* Disconnected */ || state === "closed" /* Closed */) {
7668
+ this._queuedRequests = [];
7292
7669
  this.requestTracker.cancelAll();
7293
7670
  }
7294
7671
  if (state === "connected" /* Connected */ && wasDisconnected && this._previousConnectionState !== "disconnected" /* Disconnected */) {
@@ -7353,19 +7730,36 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7353
7730
  };
7354
7731
  logger.debug(`Anonymous user info stored: ${message.user_id}`);
7355
7732
  this._updateReadyState();
7733
+ this._flushQueuedRequests();
7356
7734
  if (hasActiveSubscriptions) {
7357
7735
  this._restoreSubscriptionsAndNotify();
7358
7736
  }
7359
7737
  } else {
7360
7738
  logger.info(`[handleConnectedMessage] Stored token found, auto-authenticating...`);
7361
- this.autoReauthenticate().then(async () => {
7739
+ this.autoReauthenticate().then(async (authenticated) => {
7740
+ if (authenticated) {
7741
+ this._flushQueuedRequests();
7742
+ } else {
7743
+ this._rejectQueuedRequests(
7744
+ new RaisinAuthError(
7745
+ "Re-authentication failed after reconnect",
7746
+ "AUTH_TOKEN_EXPIRED",
7747
+ 401
7748
+ )
7749
+ );
7750
+ }
7362
7751
  if (hasActiveSubscriptions) {
7363
7752
  await this._restoreSubscriptionsAndNotify();
7364
7753
  }
7365
7754
  }).catch((err) => {
7366
7755
  logger.error(`[handleConnectedMessage] Auto-authenticate failed:`, err);
7756
+ this._rejectQueuedRequests(
7757
+ err instanceof Error ? err : new Error("Re-authentication failed after reconnect")
7758
+ );
7367
7759
  });
7368
7760
  }
7761
+ } else {
7762
+ this._flushQueuedRequests();
7369
7763
  }
7370
7764
  this.emit("connected", {
7371
7765
  connectionId: message.connection_id,
@@ -7374,18 +7768,41 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7374
7768
  });
7375
7769
  }
7376
7770
  /**
7377
- * Restore subscriptions after reconnection and notify listeners
7771
+ * Restore subscriptions after reconnection and notify listeners.
7772
+ *
7773
+ * Retries with backoff (see `_restoreRetryDelaysMs`); on final failure a
7774
+ * `subscription_restore_failed` event is emitted so applications can
7775
+ * resync their state (e.g. reload lists and re-subscribe manually).
7378
7776
  * @internal
7379
7777
  */
7380
7778
  async _restoreSubscriptionsAndNotify() {
7381
- try {
7382
- logger.info("[_restoreSubscriptionsAndNotify] Restoring subscriptions...");
7383
- await this.eventHandler.restoreSubscriptions();
7384
- logger.info("[_restoreSubscriptionsAndNotify] Subscriptions restored successfully");
7385
- this._emitReconnected();
7386
- } catch (error) {
7387
- logger.error("[_restoreSubscriptionsAndNotify] Failed to restore subscriptions:", error);
7779
+ const delays = this._restoreRetryDelaysMs;
7780
+ let lastError;
7781
+ for (let attempt = 0; attempt <= delays.length; attempt++) {
7782
+ try {
7783
+ logger.info(
7784
+ `[_restoreSubscriptionsAndNotify] Restoring subscriptions (attempt ${attempt + 1}/${delays.length + 1})...`
7785
+ );
7786
+ await this.eventHandler.restoreSubscriptions({ retry: attempt > 0 });
7787
+ logger.info("[_restoreSubscriptionsAndNotify] Subscriptions restored successfully");
7788
+ this._emitReconnected();
7789
+ return;
7790
+ } catch (error) {
7791
+ lastError = error;
7792
+ logger.error(
7793
+ `[_restoreSubscriptionsAndNotify] Restore attempt ${attempt + 1} failed:`,
7794
+ error
7795
+ );
7796
+ if (attempt < delays.length) {
7797
+ await new Promise((resolve) => setTimeout(resolve, delays[attempt]));
7798
+ }
7799
+ }
7388
7800
  }
7801
+ logger.error("[_restoreSubscriptionsAndNotify] Giving up after retries; events may be stale");
7802
+ this.emit(
7803
+ "subscription_restore_failed",
7804
+ lastError instanceof Error ? lastError : new Error(String(lastError))
7805
+ );
7389
7806
  }
7390
7807
  /**
7391
7808
  * Emit reconnected event to all listeners
@@ -7404,13 +7821,15 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7404
7821
  }
7405
7822
  /**
7406
7823
  * Auto-reauthenticate with stored JWT on reconnection
7824
+ *
7825
+ * @returns true if the session was re-authenticated, false otherwise
7407
7826
  * @internal
7408
7827
  */
7409
7828
  async autoReauthenticate() {
7410
7829
  const token = this.authManager.storage.getAccessToken();
7411
7830
  if (!token) {
7412
7831
  logger.warn("[autoReauthenticate] No stored token");
7413
- return;
7832
+ return false;
7414
7833
  }
7415
7834
  const payload = AuthManager.parseToken(token);
7416
7835
  if (payload?.exp && typeof payload.exp === "number") {
@@ -7422,18 +7841,20 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7422
7841
  this.authManager.clear();
7423
7842
  this._currentUser = null;
7424
7843
  this._emitAuthEvent("SESSION_EXPIRED");
7425
- return;
7844
+ return false;
7426
7845
  }
7427
7846
  const newToken = this.authManager.storage.getAccessToken();
7428
7847
  if (newToken) {
7429
7848
  await this.authenticate({ type: "jwt", token: newToken });
7849
+ return true;
7430
7850
  }
7431
- return;
7851
+ return false;
7432
7852
  }
7433
7853
  }
7434
7854
  try {
7435
7855
  await this.authenticate({ type: "jwt", token });
7436
7856
  logger.info("[autoReauthenticate] Successfully re-authenticated");
7857
+ return true;
7437
7858
  } catch (err) {
7438
7859
  logger.error("[autoReauthenticate] Failed to authenticate:", err);
7439
7860
  const refreshed = await this.refreshToken();
@@ -7441,11 +7862,14 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7441
7862
  const newToken = this.authManager.storage.getAccessToken();
7442
7863
  if (newToken) {
7443
7864
  await this.authenticate({ type: "jwt", token: newToken });
7865
+ return true;
7444
7866
  }
7867
+ return false;
7445
7868
  } else {
7446
7869
  this.authManager.clear();
7447
7870
  this._currentUser = null;
7448
7871
  this._emitAuthEvent("SESSION_EXPIRED");
7872
+ return false;
7449
7873
  }
7450
7874
  }
7451
7875
  }
@@ -7561,18 +7985,18 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7561
7985
  }
7562
7986
  const url = `${this._httpBaseUrl}/auth/${repository}/login`;
7563
7987
  logger.debug("[loginWithEmail] POST", url);
7564
- const response = await fetch(url, {
7988
+ const response = await this._fetchWithTimeout(url, {
7565
7989
  method: "POST",
7566
7990
  headers: { "Content-Type": "application/json" },
7567
7991
  body: JSON.stringify({ email, password })
7568
7992
  });
7569
7993
  if (!response.ok) {
7570
7994
  const error = await response.json().catch(() => ({ message: response.statusText }));
7571
- const authError = {
7572
- code: error.code || "LOGIN_FAILED",
7573
- message: error.message || "Login failed"
7574
- };
7575
- throw authError;
7995
+ throw new RaisinAuthError(
7996
+ error.message || "Login failed",
7997
+ error.code || "LOGIN_FAILED",
7998
+ response.status
7999
+ );
7576
8000
  }
7577
8001
  const tokens = await response.json();
7578
8002
  logger.info("[loginWithEmail] HTTP login successful, user_id:", tokens.identity.id);
@@ -7605,7 +8029,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7605
8029
  anonymous: false,
7606
8030
  node: userNode ?? void 0
7607
8031
  };
7608
- const expiresInMs = tokens.expires_at * 1e3 - Date.now() - 5 * 60 * 1e3;
8032
+ const expiresInMs = _RaisinClient.expiresAtToMs(tokens.expires_at) - Date.now() - 5 * 60 * 1e3;
7609
8033
  if (expiresInMs > 0) {
7610
8034
  this.scheduleAutoRefresh(expiresInMs);
7611
8035
  }
@@ -7629,18 +8053,18 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7629
8053
  async registerWithEmail(email, password, repository, displayName) {
7630
8054
  logger.info("[registerWithEmail] Starting registration...");
7631
8055
  const url = `${this._httpBaseUrl}/auth/${repository}/register`;
7632
- const response = await fetch(url, {
8056
+ const response = await this._fetchWithTimeout(url, {
7633
8057
  method: "POST",
7634
8058
  headers: { "Content-Type": "application/json" },
7635
8059
  body: JSON.stringify({ email, password, display_name: displayName })
7636
8060
  });
7637
8061
  if (!response.ok) {
7638
8062
  const error = await response.json().catch(() => ({ message: response.statusText }));
7639
- const authError = {
7640
- code: error.code || "REGISTRATION_FAILED",
7641
- message: error.message || "Registration failed"
7642
- };
7643
- throw authError;
8063
+ throw new RaisinAuthError(
8064
+ error.message || "Registration failed",
8065
+ error.code || "REGISTRATION_FAILED",
8066
+ response.status
8067
+ );
7644
8068
  }
7645
8069
  const tokens = await response.json();
7646
8070
  logger.info("[registerWithEmail] Registration successful, user_id:", tokens.identity.id);
@@ -7671,7 +8095,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7671
8095
  anonymous: false,
7672
8096
  node: userNode ?? void 0
7673
8097
  };
7674
- const expiresInMs = tokens.expires_at * 1e3 - Date.now() - 5 * 60 * 1e3;
8098
+ const expiresInMs = _RaisinClient.expiresAtToMs(tokens.expires_at) - Date.now() - 5 * 60 * 1e3;
7675
8099
  if (expiresInMs > 0) {
7676
8100
  this.scheduleAutoRefresh(expiresInMs);
7677
8101
  }
@@ -7767,7 +8191,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7767
8191
  if (refreshToken) {
7768
8192
  try {
7769
8193
  const url = this._repository ? `${this._httpBaseUrl}/auth/${this._repository}/refresh` : `${this._httpBaseUrl}/auth/refresh`;
7770
- const response = await fetch(url, {
8194
+ const response = await this._fetchWithTimeout(url, {
7771
8195
  method: "POST",
7772
8196
  headers: { "Content-Type": "application/json" },
7773
8197
  body: JSON.stringify({ refresh_token: refreshToken })
@@ -7776,7 +8200,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7776
8200
  const tokens = await response.json();
7777
8201
  this.authManager.storage.setAccessToken(tokens.access_token);
7778
8202
  this.authManager.storage.setRefreshToken(tokens.refresh_token);
7779
- const expiresInMs = tokens.expires_at * 1e3 - Date.now() - 5 * 60 * 1e3;
8203
+ const expiresInMs = _RaisinClient.expiresAtToMs(tokens.expires_at) - Date.now() - 5 * 60 * 1e3;
7780
8204
  if (expiresInMs > 0) {
7781
8205
  this.scheduleAutoRefresh(expiresInMs);
7782
8206
  }
@@ -7893,7 +8317,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7893
8317
  try {
7894
8318
  logger.info("[refreshToken] Refreshing access token...");
7895
8319
  const url = this._repository ? `${this._httpBaseUrl}/auth/${this._repository}/refresh` : `${this._httpBaseUrl}/auth/refresh`;
7896
- const response = await fetch(url, {
8320
+ const response = await this._fetchWithTimeout(url, {
7897
8321
  method: "POST",
7898
8322
  headers: { "Content-Type": "application/json" },
7899
8323
  body: JSON.stringify({ refresh_token: refreshToken })
@@ -7923,7 +8347,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7923
8347
  if (this.authManager.storage instanceof LocalStorageTokenStorage) {
7924
8348
  this.authManager.storage.setUser(user);
7925
8349
  }
7926
- const expiresInMs = tokens.expires_at * 1e3 - Date.now() - 5 * 60 * 1e3;
8350
+ const expiresInMs = _RaisinClient.expiresAtToMs(tokens.expires_at) - Date.now() - 5 * 60 * 1e3;
7927
8351
  if (expiresInMs > 0) {
7928
8352
  this.scheduleAutoRefresh(expiresInMs);
7929
8353
  }
@@ -7936,6 +8360,53 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7936
8360
  return null;
7937
8361
  }
7938
8362
  }
8363
+ /**
8364
+ * Fetch with a timeout derived from `requestTimeout`.
8365
+ *
8366
+ * Aborts the request after `requestTimeout` ms and rejects with a
8367
+ * `RaisinTimeoutError`. If the caller provides its own AbortSignal in
8368
+ * `init.signal`, it is respected: an external abort rejects with the
8369
+ * original AbortError instead of a timeout error.
8370
+ * @internal
8371
+ */
8372
+ async _fetchWithTimeout(url, init = {}) {
8373
+ const timeoutMs = this.options.requestTimeout;
8374
+ const controller = new AbortController();
8375
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
8376
+ const externalSignal = init.signal ?? void 0;
8377
+ if (externalSignal) {
8378
+ if (externalSignal.aborted) {
8379
+ controller.abort();
8380
+ } else {
8381
+ externalSignal.addEventListener("abort", () => controller.abort(), { once: true });
8382
+ }
8383
+ }
8384
+ try {
8385
+ return await fetch(url, { ...init, signal: controller.signal });
8386
+ } catch (error) {
8387
+ if (error instanceof Error && error.name === "AbortError" && !externalSignal?.aborted) {
8388
+ throw new RaisinTimeoutError(
8389
+ `Request to ${url} timed out after ${timeoutMs}ms`,
8390
+ "REQUEST_TIMEOUT",
8391
+ timeoutMs
8392
+ );
8393
+ }
8394
+ throw error;
8395
+ } finally {
8396
+ clearTimeout(timer);
8397
+ }
8398
+ }
8399
+ /**
8400
+ * Normalize a token `expires_at` value to epoch milliseconds.
8401
+ *
8402
+ * Server endpoints are inconsistent: some return seconds, others
8403
+ * milliseconds. Anything above 1e11 can't be a seconds-epoch (year 5138),
8404
+ * so treat it as milliseconds.
8405
+ * @internal
8406
+ */
8407
+ static expiresAtToMs(expiresAt) {
8408
+ return expiresAt > 1e11 ? expiresAt : expiresAt * 1e3;
8409
+ }
7939
8410
  /**
7940
8411
  * Schedule automatic token refresh
7941
8412
  * @internal
@@ -7944,11 +8415,12 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7944
8415
  if (this._refreshTimer) {
7945
8416
  clearTimeout(this._refreshTimer);
7946
8417
  }
7947
- logger.debug(`[scheduleAutoRefresh] Scheduling refresh in ${Math.round(delayMs / 1e3 / 60)} minutes`);
8418
+ const clamped = Math.min(Math.max(delayMs, 3e4), 2147483647);
8419
+ logger.debug(`[scheduleAutoRefresh] Scheduling refresh in ${Math.round(clamped / 1e3 / 60)} minutes`);
7948
8420
  this._refreshTimer = setTimeout(async () => {
7949
8421
  logger.info("[autoRefresh] Auto-refreshing token...");
7950
8422
  await this.refreshToken();
7951
- }, delayMs);
8423
+ }, clamped);
7952
8424
  }
7953
8425
  /**
7954
8426
  * Get a database/repository interface
@@ -7972,10 +8444,26 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7972
8444
  }
7973
8445
  /**
7974
8446
  * Send a request to the server
8447
+ *
8448
+ * If the connection is temporarily down (connecting/reconnecting, or
8449
+ * disconnected with auto-reconnect enabled), the request is queued and
8450
+ * flushed once the connection is re-established and re-authenticated.
8451
+ * Queued requests are still subject to their own request timeout, and the
8452
+ * queue is bounded (overflow rejects immediately).
7975
8453
  */
7976
8454
  async sendRequestInternal(payload, requestType, contextOverride, requestOptions) {
7977
- if (!this.connection.isConnected()) {
7978
- throw new Error("Not connected to server");
8455
+ const connected = this.connection.isConnected();
8456
+ if (!connected) {
8457
+ const state = this.connection.getState();
8458
+ const willReconnect = state === "connecting" /* Connecting */ || state === "reconnecting" /* Reconnecting */ || state === "disconnected" /* Disconnected */ && this._everConnected && this.connection.isAutoReconnectEnabled();
8459
+ if (!willReconnect) {
8460
+ throw new Error("Not connected to server");
8461
+ }
8462
+ if (this._queuedRequests.length >= _RaisinClient.MAX_QUEUED_REQUESTS) {
8463
+ throw new Error(
8464
+ `Request queue is full (${_RaisinClient.MAX_QUEUED_REQUESTS} requests pending while reconnecting)`
8465
+ );
8466
+ }
7979
8467
  }
7980
8468
  const requestId = this.requestTracker.generateRequestId();
7981
8469
  const context = contextOverride || this._context;
@@ -7994,8 +8482,15 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
7994
8482
  try {
7995
8483
  const encoded = encodeMessage(request);
7996
8484
  logger.debug(`Encoded request - ID: ${requestId}, type: ${requestType}, size: ${encoded.length} bytes`);
7997
- this.connection.send(encoded);
7998
- logger.debug(`Request sent - ID: ${requestId}`);
8485
+ if (connected) {
8486
+ this.connection.send(encoded);
8487
+ logger.debug(`Request sent - ID: ${requestId}`);
8488
+ } else {
8489
+ this._queuedRequests.push({ requestId, encoded });
8490
+ logger.debug(
8491
+ `Request queued while reconnecting - ID: ${requestId}, queue size: ${this._queuedRequests.length}`
8492
+ );
8493
+ }
7999
8494
  } catch (error) {
8000
8495
  logger.error(`Failed to send request - ID: ${requestId}:`, error);
8001
8496
  this.requestTracker.rejectRequest(
@@ -8006,6 +8501,42 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
8006
8501
  }
8007
8502
  return responsePromise;
8008
8503
  }
8504
+ /**
8505
+ * Flush requests queued while the connection was re-establishing.
8506
+ * Called after the connection is connected and (re-)authenticated.
8507
+ * @internal
8508
+ */
8509
+ _flushQueuedRequests() {
8510
+ if (this._queuedRequests.length === 0) return;
8511
+ const queued = this._queuedRequests;
8512
+ this._queuedRequests = [];
8513
+ logger.info(`[_flushQueuedRequests] Flushing ${queued.length} queued request(s)`);
8514
+ for (const { requestId, encoded } of queued) {
8515
+ if (!this.requestTracker.hasPendingRequest(requestId)) continue;
8516
+ try {
8517
+ this.connection.send(encoded);
8518
+ } catch (error) {
8519
+ this.requestTracker.rejectRequest(
8520
+ requestId,
8521
+ error instanceof Error ? error : new Error("Failed to send queued request")
8522
+ );
8523
+ }
8524
+ }
8525
+ }
8526
+ /**
8527
+ * Reject all requests queued while the connection was re-establishing
8528
+ * (e.g. re-authentication failed after reconnect).
8529
+ * @internal
8530
+ */
8531
+ _rejectQueuedRequests(error) {
8532
+ if (this._queuedRequests.length === 0) return;
8533
+ const queued = this._queuedRequests;
8534
+ this._queuedRequests = [];
8535
+ logger.warn(`[_rejectQueuedRequests] Rejecting ${queued.length} queued request(s): ${error.message}`);
8536
+ for (const { requestId } of queued) {
8537
+ this.requestTracker.rejectRequest(requestId, error);
8538
+ }
8539
+ }
8009
8540
  /**
8010
8541
  * Get the HTTP base URL derived from the WebSocket URL
8011
8542
  */
@@ -8331,6 +8862,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
8331
8862
  */
8332
8863
  async _subscribeToUserHome(repository, userHome) {
8333
8864
  await this._unsubscribeFromUserHome();
8865
+ userHome = normalizeHomePath(userHome) ?? userHome;
8334
8866
  try {
8335
8867
  const db = this.database(repository);
8336
8868
  const pathParts = userHome.split("/").filter((p) => p.length > 0);
@@ -8630,7 +9162,7 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
8630
9162
  const branch = options.branch ?? this._context.branch ?? "main";
8631
9163
  const pathWithProperty = options.propertyPath ? `${options.path}@${options.propertyPath}` : options.path;
8632
9164
  const endpoint = `/api/repository/${options.repository}/${branch}/head/${options.workspace}${pathWithProperty}/raisin:sign`;
8633
- const response = await fetch(`${this._httpBaseUrl}${endpoint}`, {
9165
+ const response = await this._fetchWithTimeout(`${this._httpBaseUrl}${endpoint}`, {
8634
9166
  method: "POST",
8635
9167
  headers: {
8636
9168
  "Content-Type": "application/json",
@@ -8685,6 +9217,9 @@ var RaisinClient = class _RaisinClient extends import_events4.EventEmitter {
8685
9217
  return _RaisinClient.forSSR(baseUrl, options);
8686
9218
  }
8687
9219
  };
9220
+ /** Hard cap on requests held while reconnecting; overflow rejects immediately. */
9221
+ _RaisinClient.MAX_QUEUED_REQUESTS = 100;
9222
+ var RaisinClient = _RaisinClient;
8688
9223
 
8689
9224
  // src/node-builder.ts
8690
9225
  var NodeBuilder = class _NodeBuilder {
@@ -9019,6 +9554,29 @@ function isTerminalEvent(event) {
9019
9554
  return event.type === "flow_completed" || event.type === "flow_failed";
9020
9555
  }
9021
9556
 
9557
+ // src/types/flow-definition.ts
9558
+ function getRefDisplayName(ref) {
9559
+ const path = ref["raisin:path"] || ref["raisin:ref"];
9560
+ const segments = path.split("/").filter(Boolean);
9561
+ return segments[segments.length - 1] || path;
9562
+ }
9563
+ function getRefPath(ref) {
9564
+ return ref["raisin:path"] || ref["raisin:ref"];
9565
+ }
9566
+ var RETRY_STRATEGIES = {
9567
+ none: { max_retries: 0, base_delay_ms: 0, max_delay_ms: 0 },
9568
+ quick: { max_retries: 3, base_delay_ms: 1e3, max_delay_ms: 1e4 },
9569
+ standard: { max_retries: 5, base_delay_ms: 2e3, max_delay_ms: 6e4 },
9570
+ aggressive: { max_retries: 10, base_delay_ms: 5e3, max_delay_ms: 12e4 },
9571
+ llm: { max_retries: 5, base_delay_ms: 1e4, max_delay_ms: 12e4 }
9572
+ };
9573
+ function isFlowDefinitionStep(node) {
9574
+ return node.node_type === "raisin:FlowStep";
9575
+ }
9576
+ function isFlowDefinitionContainer(node) {
9577
+ return node.node_type === "raisin:FlowContainer";
9578
+ }
9579
+
9022
9580
  // src/utils/plan-projection.ts
9023
9581
  function asRecord(value) {
9024
9582
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -9357,10 +9915,12 @@ var ConversationStore = class {
9357
9915
  }
9358
9916
  switch (event.type) {
9359
9917
  case "text_chunk":
9918
+ this.markStreamingActivity();
9360
9919
  this._streamingText += event.text;
9361
9920
  this.notify();
9362
9921
  break;
9363
9922
  case "thought_chunk":
9923
+ this.markStreamingActivity();
9364
9924
  this.notify();
9365
9925
  break;
9366
9926
  case "assistant_message":
@@ -9369,6 +9929,7 @@ var ConversationStore = class {
9369
9929
  this.notify();
9370
9930
  break;
9371
9931
  case "tool_call_started": {
9932
+ this.markStreamingActivity();
9372
9933
  logger.debug("[ConversationStore] Tool started:", event.functionName);
9373
9934
  const info = {
9374
9935
  id: event.toolCallId,
@@ -9381,6 +9942,7 @@ var ConversationStore = class {
9381
9942
  break;
9382
9943
  }
9383
9944
  case "tool_call_completed": {
9945
+ this.markStreamingActivity();
9384
9946
  logger.debug("[ConversationStore] Tool completed:", event.toolCallId);
9385
9947
  const idx = this._activeToolCalls.findIndex((tc) => tc.id === event.toolCallId);
9386
9948
  if (idx >= 0) {
@@ -9455,6 +10017,7 @@ var ConversationStore = class {
9455
10017
  logger.error("[ConversationStore] Turn failed", event.error);
9456
10018
  this._error = event.error;
9457
10019
  this._isStreaming = false;
10020
+ this._isWaiting = false;
9458
10021
  this._streamingText = "";
9459
10022
  this._activeToolCalls = [];
9460
10023
  this.clearStreamingTimer();
@@ -9492,6 +10055,26 @@ var ConversationStore = class {
9492
10055
  }
9493
10056
  }
9494
10057
  }
10058
+ /**
10059
+ * Agent activity observed while the store thought the turn was over.
10060
+ *
10061
+ * This happens when execution continues WITHOUT a user message — plan
10062
+ * approval in approve_then_auto / step_by_step (approvePlan() returns an
10063
+ * enqueue receipt and the agent starts executing), or agent-initiated
10064
+ * follow-ups. The events arrive over the persistent live subscription
10065
+ * (no history replay), so activity is always "now": re-enter streaming
10066
+ * state so UIs show progress and the end-of-turn handling (done/waiting
10067
+ * → deferred reload) runs again for this continuation.
10068
+ */
10069
+ markStreamingActivity() {
10070
+ if (this._isStreaming) return;
10071
+ logger.debug("[ConversationStore] Agent activity while idle \u2014 re-entering streaming state");
10072
+ this._isStreaming = true;
10073
+ this._isWaiting = false;
10074
+ this._streamingText = "";
10075
+ this.resetStreamingTimer();
10076
+ this.startWatchdog();
10077
+ }
9495
10078
  /**
9496
10079
  * If accumulated streaming text hasn't been captured in an assistant_message,
9497
10080
  * synthesize one.
@@ -9725,6 +10308,14 @@ var ConversationStore = class {
9725
10308
  };
9726
10309
 
9727
10310
  // src/stores/conversation-list-store.ts
10311
+ function buildChatsSubscriptionFilters(userHome) {
10312
+ const home = normalizeHomePath(userHome) ?? userHome;
10313
+ return {
10314
+ workspace: "raisin:access_control",
10315
+ path: `${home}/inbox/chats/**`,
10316
+ event_types: ["node:created", "node:updated"]
10317
+ };
10318
+ }
9728
10319
  var ConversationListStore = class {
9729
10320
  constructor(options) {
9730
10321
  this.subscribers = /* @__PURE__ */ new Set();
@@ -9870,13 +10461,8 @@ var ConversationListStore = class {
9870
10461
  const rows = result.rows;
9871
10462
  const userHome = rows?.[0]?.home;
9872
10463
  if (!userHome) return;
9873
- const chatsPath = `${userHome}/inbox/chats`;
9874
10464
  this._realtimeSubscription = await events.subscribe(
9875
- {
9876
- workspace: "raisin:access_control",
9877
- path: chatsPath,
9878
- event_types: ["node:created", "node:updated"]
9879
- },
10465
+ buildChatsSubscriptionFilters(userHome),
9880
10466
  async (event) => {
9881
10467
  const eventData = event;
9882
10468
  const nodeId = eventData.payload?.node_id;
@@ -9982,7 +10568,7 @@ function useConversation(react, options) {
9982
10568
  var INITIAL_LIST_SNAPSHOT = {
9983
10569
  conversations: [],
9984
10570
  totalUnreadCount: 0,
9985
- isLoading: false,
10571
+ isLoading: true,
9986
10572
  error: null
9987
10573
  };
9988
10574
  function useConversationList(react, options) {
@@ -10860,6 +11446,160 @@ function createRaisinReact(react) {
10860
11446
  useFlow: useFlow2
10861
11447
  };
10862
11448
  }
11449
+
11450
+ // src/integrations/vue/vue-auth.ts
11451
+ function createUseAuth2(vue) {
11452
+ return function useAuth(client) {
11453
+ const adapter = createAuthAdapter(client);
11454
+ const snapshot = vue.ref(adapter.getSnapshot());
11455
+ const unsubscribe = adapter.subscribe((s) => {
11456
+ snapshot.value = s;
11457
+ });
11458
+ vue.onUnmounted(() => {
11459
+ unsubscribe();
11460
+ adapter.destroy();
11461
+ });
11462
+ return {
11463
+ user: vue.computed(() => snapshot.value.user),
11464
+ isAuthenticated: vue.computed(() => snapshot.value.isAuthenticated),
11465
+ isLoading: vue.computed(() => snapshot.value.isLoading),
11466
+ login: adapter.login,
11467
+ register: adapter.register,
11468
+ logout: adapter.logout,
11469
+ initSession: adapter.initSession
11470
+ };
11471
+ };
11472
+ }
11473
+
11474
+ // src/integrations/vue/vue-connection.ts
11475
+ function createUseConnection2(vue) {
11476
+ return function useConnection(client) {
11477
+ const adapter = createConnectionAdapter(client);
11478
+ const snapshot = vue.ref(adapter.getSnapshot());
11479
+ const unsubscribe = adapter.subscribe((s) => {
11480
+ snapshot.value = s;
11481
+ });
11482
+ vue.onUnmounted(() => {
11483
+ unsubscribe();
11484
+ adapter.destroy();
11485
+ });
11486
+ return {
11487
+ state: vue.computed(() => snapshot.value.state),
11488
+ isConnected: vue.computed(() => snapshot.value.isConnected),
11489
+ isReady: vue.computed(() => snapshot.value.isReady),
11490
+ connect: adapter.connect,
11491
+ disconnect: adapter.disconnect
11492
+ };
11493
+ };
11494
+ }
11495
+
11496
+ // src/integrations/vue/vue-sql.ts
11497
+ function createUseSql2(vue) {
11498
+ return function useSql(database, sql, params, options) {
11499
+ const adapter = createSqlAdapter(database, sql, params, options);
11500
+ const snapshot = vue.ref(adapter.getSnapshot());
11501
+ const unsubscribe = adapter.subscribe((s) => {
11502
+ snapshot.value = s;
11503
+ });
11504
+ vue.onUnmounted(() => {
11505
+ unsubscribe();
11506
+ adapter.destroy();
11507
+ });
11508
+ return {
11509
+ data: vue.computed(() => snapshot.value.data),
11510
+ isLoading: vue.computed(() => snapshot.value.isLoading),
11511
+ error: vue.computed(() => snapshot.value.error),
11512
+ refetch: adapter.refetch
11513
+ };
11514
+ };
11515
+ }
11516
+
11517
+ // src/integrations/vue/vue-subscription.ts
11518
+ function createUseSubscription2(vue) {
11519
+ return function useSubscription(database, filters, callback) {
11520
+ const adapter = createSubscriptionAdapter(database, filters, callback);
11521
+ vue.onUnmounted(() => adapter.destroy());
11522
+ return {
11523
+ unsubscribe: () => adapter.destroy()
11524
+ };
11525
+ };
11526
+ }
11527
+
11528
+ // src/integrations/vue/vue-conversation.ts
11529
+ function createUseConversation(vue) {
11530
+ return function useConversation2(options) {
11531
+ const store = new ConversationStore(options);
11532
+ const snapshot = vue.ref(store.getSnapshot());
11533
+ const unsubscribe = store.subscribe((s) => {
11534
+ snapshot.value = s;
11535
+ });
11536
+ vue.onUnmounted(() => {
11537
+ unsubscribe();
11538
+ store.destroy();
11539
+ });
11540
+ return {
11541
+ conversation: vue.computed(() => snapshot.value.conversation),
11542
+ messages: vue.computed(() => snapshot.value.messages),
11543
+ isStreaming: vue.computed(() => snapshot.value.isStreaming),
11544
+ isWaiting: vue.computed(() => snapshot.value.isWaiting),
11545
+ streamingText: vue.computed(() => snapshot.value.streamingText),
11546
+ error: vue.computed(() => snapshot.value.error),
11547
+ activeToolCalls: vue.computed(() => snapshot.value.activeToolCalls),
11548
+ plans: vue.computed(() => snapshot.value.plans),
11549
+ isLoading: vue.computed(() => snapshot.value.isLoading),
11550
+ conversationPath: vue.computed(() => snapshot.value.conversationPath),
11551
+ sendMessage: (content) => store.sendMessage(content),
11552
+ approvePlan: async (planPath) => {
11553
+ await store.approvePlan(planPath);
11554
+ },
11555
+ rejectPlan: async (planPath, feedback) => {
11556
+ await store.rejectPlan(planPath, feedback);
11557
+ },
11558
+ stop: () => store.stop(),
11559
+ loadMessages: () => store.loadMessages(),
11560
+ store
11561
+ };
11562
+ };
11563
+ }
11564
+ function createUseConversationList(vue) {
11565
+ return function useConversationList2(options) {
11566
+ const store = new ConversationListStore(options);
11567
+ const snapshot = vue.ref(store.getSnapshot());
11568
+ const unsubscribe = store.subscribe((s) => {
11569
+ snapshot.value = s;
11570
+ });
11571
+ store.load().catch((err) => {
11572
+ logger.error("[useConversationList] Initial load failed:", err);
11573
+ });
11574
+ vue.onUnmounted(() => {
11575
+ unsubscribe();
11576
+ store.destroy();
11577
+ });
11578
+ return {
11579
+ conversations: vue.computed(() => snapshot.value.conversations),
11580
+ totalUnreadCount: vue.computed(() => snapshot.value.totalUnreadCount),
11581
+ isLoading: vue.computed(() => snapshot.value.isLoading),
11582
+ error: vue.computed(() => snapshot.value.error),
11583
+ createConversation: (opts) => store.createConversation(opts),
11584
+ deleteConversation: (conversationPath) => store.deleteConversation(conversationPath),
11585
+ markAsRead: (conversationPath) => store.markAsRead(conversationPath),
11586
+ refresh: () => store.load(),
11587
+ store
11588
+ };
11589
+ };
11590
+ }
11591
+
11592
+ // src/integrations/vue/index.ts
11593
+ function createRaisinVue(vue) {
11594
+ return {
11595
+ useAuth: createUseAuth2(vue),
11596
+ useConnection: createUseConnection2(vue),
11597
+ useSql: createUseSql2(vue),
11598
+ useSubscription: createUseSubscription2(vue),
11599
+ useConversation: createUseConversation(vue),
11600
+ useConversationList: createUseConversationList(vue)
11601
+ };
11602
+ }
10863
11603
  // Annotate the CommonJS export names for ESM import in node:
10864
11604
  0 && (module.exports = {
10865
11605
  AllNodeEventTypes,
@@ -10881,6 +11621,7 @@ function createRaisinReact(react) {
10881
11621
  DEFAULT_RETRY_BASE_DELAY,
10882
11622
  DEFAULT_RETRY_MAX_DELAY,
10883
11623
  DEFAULT_RETRY_OPTIONS,
11624
+ DEFAULT_SEND_MESSAGE_INACTIVITY_TIMEOUT_MS,
10884
11625
  Database,
10885
11626
  ElementTypes,
10886
11627
  EventHandler,
@@ -10891,6 +11632,7 @@ function createRaisinReact(react) {
10891
11632
  HttpDatabase,
10892
11633
  HttpFunctionsApi,
10893
11634
  HttpWorkspaceClient,
11635
+ InboxApi,
10894
11636
  LocalStorageTokenStorage,
10895
11637
  LogLevel,
10896
11638
  MemoryTokenStorage,
@@ -10901,6 +11643,7 @@ function createRaisinReact(react) {
10901
11643
  NodeOperations,
10902
11644
  NodeTypes,
10903
11645
  RAISIN_CONTEXT_KEY,
11646
+ RETRY_STRATEGIES,
10904
11647
  RaisinAbortError,
10905
11648
  RaisinAuthError,
10906
11649
  RaisinClient,
@@ -10936,6 +11679,7 @@ function createRaisinReact(react) {
10936
11679
  createFlowAdapter,
10937
11680
  createLoader,
10938
11681
  createRaisinReact,
11682
+ createRaisinVue,
10939
11683
  createRequestScopedClient,
10940
11684
  createSSRClient,
10941
11685
  createSqlAdapter,
@@ -10948,13 +11692,18 @@ function createRaisinReact(react) {
10948
11692
  getErrorMessage,
10949
11693
  getLogLevel,
10950
11694
  getLoggerConfig,
11695
+ getRefDisplayName,
11696
+ getRefPath,
10951
11697
  isEventMessage,
11698
+ isFlowDefinitionContainer,
11699
+ isFlowDefinitionStep,
10952
11700
  isHttpClient,
10953
11701
  isResponseEnvelope,
10954
11702
  isRetryableError,
10955
11703
  isTerminalEvent,
10956
11704
  isWebSocketClient,
10957
11705
  logger,
11706
+ normalizeHomePath,
10958
11707
  projectPlansFromMessages,
10959
11708
  rowsToObjects,
10960
11709
  setLogLevel,
@@ -10964,3 +11713,4 @@ function createRaisinReact(react) {
10964
11713
  useFlow,
10965
11714
  withRetry
10966
11715
  });
11716
+ //# sourceMappingURL=index.js.map