@paymanai/payman-typescript-ask-sdk 2.0.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -136,7 +136,38 @@ async function streamWorkflowEvents(url, body, headers, options = {}) {
136
136
  });
137
137
  }
138
138
 
139
- // src/utils/eventProcessor.ts
139
+ // src/utils/stageLabels.ts
140
+ var STAGE_LABELS = {
141
+ sanitizer: "Checking your request",
142
+ analyzer: "Understanding what you're asking",
143
+ prefetcher: "Gathering context",
144
+ planner: "Planning how to handle this",
145
+ execution: "Working on it",
146
+ formatter: "Writing the response"
147
+ };
148
+ function stageLabel(stage) {
149
+ const label = STAGE_LABELS[stage];
150
+ if (label) return label;
151
+ const pretty = stage.replace(/[_-]/g, " ");
152
+ return `Running ${pretty}`;
153
+ }
154
+
155
+ // src/utils/v2EventProcessor.ts
156
+ function isBlandStatus(message) {
157
+ if (!message) return true;
158
+ const normalized = message.trim().toLowerCase().replace(/[…\.]+$/, "").trim();
159
+ return BLAND_STATUS_LABELS.has(normalized);
160
+ }
161
+ var BLAND_STATUS_LABELS = /* @__PURE__ */ new Set([
162
+ "executing",
163
+ "working on it",
164
+ "thinking",
165
+ "processing",
166
+ "reviewing your request",
167
+ "composing response",
168
+ "checking your request",
169
+ "polishing the response"
170
+ ]);
140
171
  function getEventMessage(event) {
141
172
  if (event.message?.trim()) {
142
173
  return event.message.trim();
@@ -146,54 +177,104 @@ function getEventMessage(event) {
146
177
  }
147
178
  const eventType = event.eventType;
148
179
  switch (eventType) {
149
- case "STARTED":
150
- case "WORKFLOW_STARTED":
151
- return "Starting workflow...";
152
- case "ORCHESTRATOR_THINKING":
153
- return "Planning execution strategy...";
154
- case "ORCHESTRATOR_COMPLETED":
155
- return "Planning complete";
156
- case "INTENT_STARTED":
157
- return event.workerName ? `${event.workerName} started` : "Processing intent...";
158
- case "INTENT_PROGRESS":
159
- return event.workerName ? `${event.workerName} in progress` : "Thinking...";
160
- case "INTENT_COMPLETED":
161
- return event.workerName ? `${event.workerName} completed` : "Intent completed";
162
- case "AGGREGATOR_THINKING":
163
- return "Combining results...";
164
- case "AGGREGATOR_COMPLETED":
165
- return "Results combined";
166
- case "COMPLETED":
167
- case "WORKFLOW_COMPLETED":
168
- return "Workflow completed successfully";
169
- case "ERROR":
170
- case "WORKFLOW_ERROR":
171
- case "INTENT_ERROR":
172
- return event.errorMessage || "An error occurred";
173
- case "USER_ACTION_REQUIRED":
174
- return "Waiting for verification...";
175
- case "USER_ACTION_SUCCESS":
176
- return "Verification approved";
177
- case "USER_ACTION_EXPIRED":
178
- return "Verification expired";
179
- case "USER_ACTION_INVALID":
180
- return "Invalid code. Please try again.";
181
- case "USER_ACTION_REJECTED":
182
- return "Verification cancelled";
183
- case "USER_ACTION_RESENT":
184
- return "Verification code resent";
185
- case "USER_ACTION_FAILED":
186
- return "Verification failed";
187
- case "INTENT_THINKING":
188
- return event.workerName ? `${event.workerName} thinking...` : "Thinking...";
189
- case "INTENT_THINKING_CONT":
190
- return event.message || "";
180
+ case "RUN_STARTED":
181
+ return "Starting agent run...";
182
+ case "TOOL_CALL_STARTED": {
183
+ const description = typeof event.description === "string" ? event.description.trim() : "";
184
+ if (description) return description;
185
+ const toolName = typeof event.toolName === "string" ? event.toolName : "";
186
+ return toolName ? `Calling ${toolName}...` : "Calling tool...";
187
+ }
188
+ case "TOOL_CALL_COMPLETED": {
189
+ const description = typeof event.description === "string" ? event.description.trim() : "";
190
+ if (description) return description;
191
+ const toolName = typeof event.toolName === "string" ? event.toolName : "";
192
+ return toolName ? `${toolName} completed` : "Tool call completed";
193
+ }
194
+ case "RUN_IN_PROGRESS":
195
+ return event.partialText || "Thinking...";
196
+ case "RUN_COMPLETED":
197
+ return "Agent run completed";
198
+ case "RUN_FAILED":
199
+ return event.errorMessage || "Agent run failed";
191
200
  case "KEEP_ALIVE":
192
- return "";
201
+ return event.description || "";
202
+ case "THINKING_DELTA":
203
+ return event.text || "";
193
204
  default:
194
205
  return eventType;
195
206
  }
196
207
  }
208
+ function extractResponseContent(response) {
209
+ if (typeof response === "string") {
210
+ return response;
211
+ }
212
+ if (typeof response === "object" && response !== null) {
213
+ const resp = response;
214
+ if ("text" in resp && typeof resp.text === "string") {
215
+ return resp.text;
216
+ }
217
+ if ("content" in resp && typeof resp.content === "string") {
218
+ return resp.content;
219
+ }
220
+ if ("message" in resp && typeof resp.message === "string") {
221
+ return resp.message;
222
+ }
223
+ if ("answer" in resp && typeof resp.answer === "string") {
224
+ return resp.answer;
225
+ }
226
+ return JSON.stringify(response);
227
+ }
228
+ return "";
229
+ }
230
+ function normalizeEvent(event) {
231
+ const type = event.eventType;
232
+ switch (type) {
233
+ case "RUN_STARTED":
234
+ return { ...event, eventType: "WORKFLOW_STARTED" };
235
+ case "TOOL_CALL_STARTED": {
236
+ const toolName = typeof event.toolName === "string" ? event.toolName : void 0;
237
+ const description = typeof event.description === "string" ? event.description.trim() : "";
238
+ return {
239
+ ...event,
240
+ eventType: "INTENT_STARTED",
241
+ workerName: toolName ?? event.workerName,
242
+ message: description || event.message
243
+ };
244
+ }
245
+ case "TOOL_CALL_COMPLETED": {
246
+ const toolName = typeof event.toolName === "string" ? event.toolName : void 0;
247
+ const description = typeof event.description === "string" ? event.description.trim() : "";
248
+ return {
249
+ ...event,
250
+ eventType: "INTENT_COMPLETED",
251
+ workerName: toolName ?? event.workerName,
252
+ message: description || event.message
253
+ };
254
+ }
255
+ case "RUN_IN_PROGRESS":
256
+ return {
257
+ ...event,
258
+ eventType: "INTENT_PROGRESS",
259
+ message: event.partialText ?? event.message
260
+ };
261
+ case "RUN_COMPLETED":
262
+ return {
263
+ ...event,
264
+ eventType: "WORKFLOW_COMPLETED",
265
+ // state machine reads event.response for the final content
266
+ response: event.response ?? event.message
267
+ };
268
+ case "RUN_FAILED":
269
+ return {
270
+ ...event,
271
+ eventType: "WORKFLOW_ERROR",
272
+ errorMessage: event.errorMessage ?? event.message
273
+ };
274
+ default:
275
+ return event;
276
+ }
277
+ }
197
278
  function isUserActionPrompt(text) {
198
279
  return /\benter\b.+\b(code|otp)\b/i.test(text) || /\b(authorization|verification)\s+code\b/i.test(text) || /\bsent\s+to\s+your\b/i.test(text);
199
280
  }
@@ -227,304 +308,6 @@ function workingPhaseDetailForDisplay(raw) {
227
308
  }
228
309
  return t;
229
310
  }
230
- function extractResponseContent(response) {
231
- if (typeof response === "string") {
232
- return response;
233
- }
234
- if (typeof response === "object" && response !== null) {
235
- const resp = response;
236
- if ("text" in resp && typeof resp.text === "string") {
237
- return resp.text;
238
- }
239
- if ("content" in resp && typeof resp.content === "string") {
240
- return resp.content;
241
- }
242
- if ("message" in resp && typeof resp.message === "string") {
243
- return resp.message;
244
- }
245
- if ("answer" in resp && typeof resp.answer === "string") {
246
- return resp.answer;
247
- }
248
- return JSON.stringify(response);
249
- }
250
- return "";
251
- }
252
-
253
- // src/utils/messageStateManager.ts
254
- function buildFormattedThinking(steps, allThinkingText) {
255
- const parts = [];
256
- const safeSteps = steps ?? [];
257
- const cleanAll = allThinkingText.replace(/^\s+/, "");
258
- if (cleanAll) {
259
- const firstStepWithThinking = safeSteps.find(
260
- (s) => s.thinkingText && s.thinkingText.trim()
261
- );
262
- if (!firstStepWithThinking) {
263
- parts.push("**Preflight**");
264
- parts.push(cleanAll);
265
- } else {
266
- const stepText = firstStepWithThinking.thinkingText.trim();
267
- const idx = cleanAll.indexOf(stepText);
268
- if (idx > 0) {
269
- const orphaned = cleanAll.substring(0, idx).replace(/\s+$/, "");
270
- if (orphaned) {
271
- parts.push("**Preflight**");
272
- parts.push(orphaned);
273
- }
274
- }
275
- }
276
- }
277
- for (const step of safeSteps) {
278
- switch (step.eventType) {
279
- case "ORCHESTRATOR_THINKING":
280
- parts.push("**Planning**");
281
- if (step.message) parts.push(step.message);
282
- break;
283
- case "WORKING": {
284
- const detail = workingPhaseDetailForDisplay(step.message || "");
285
- parts.push("**Working**");
286
- if (detail) parts.push(detail);
287
- break;
288
- }
289
- case "INTENT_STARTED": {
290
- let label = step.message || "Processing";
291
- const started = label.match(/^(.+?)\s+started$/i);
292
- const progress = label.match(/^(.+?)\s+in progress$/i);
293
- if (started) label = started[1];
294
- else if (progress) label = progress[1];
295
- parts.push(`**${label}**`);
296
- if (step.thinkingText) parts.push(step.thinkingText);
297
- break;
298
- }
299
- case "INTENT_PROGRESS": {
300
- if (step.thinkingText) parts.push(step.thinkingText);
301
- else if (step.message) parts.push(step.message);
302
- break;
303
- }
304
- case "AGGREGATOR_THINKING":
305
- parts.push("**Finalizing**");
306
- if (step.message) parts.push(step.message);
307
- break;
308
- case "USER_ACTION_REQUIRED":
309
- parts.push("**Verification Required**");
310
- if (step.message) {
311
- parts.push(getUserActionDisplayMessage(step.eventType, step.message));
312
- }
313
- break;
314
- case "USER_ACTION_SUCCESS":
315
- parts.push(`\u2713 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
316
- break;
317
- case "USER_ACTION_REJECTED":
318
- parts.push(getUserActionDisplayMessage(step.eventType, step.message));
319
- break;
320
- case "USER_ACTION_EXPIRED":
321
- parts.push(`\u2717 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
322
- break;
323
- case "USER_ACTION_INVALID":
324
- parts.push(`\u2717 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
325
- break;
326
- case "USER_ACTION_RESENT":
327
- parts.push(getUserActionDisplayMessage(step.eventType, step.message));
328
- break;
329
- case "USER_ACTION_FAILED":
330
- parts.push(`\u2717 ${getUserActionDisplayMessage(step.eventType, step.message)}`);
331
- break;
332
- }
333
- }
334
- return parts.length > 0 ? parts.join("\n") : allThinkingText;
335
- }
336
- function createCancelledMessageUpdate(steps, currentMessage) {
337
- const updatedSteps = steps.map((step) => {
338
- if (step.status === "in_progress") {
339
- return { ...step, status: "pending" };
340
- }
341
- return step;
342
- });
343
- return {
344
- isStreaming: false,
345
- isCancelled: true,
346
- steps: updatedSteps,
347
- currentExecutingStepId: void 0,
348
- // Preserve currentMessage so UI can show it with X icon
349
- currentMessage: currentMessage || "Thinking..."
350
- };
351
- }
352
-
353
- // src/utils/requestBuilder.ts
354
- function buildRequestBody(config, userMessage, sessionId) {
355
- const owner = config.session?.owner;
356
- const sessionAttributes = owner?.attributes && Object.keys(owner.attributes).length > 0 ? owner.attributes : void 0;
357
- return {
358
- workflowName: config.workflow.name,
359
- userInput: userMessage,
360
- sessionId,
361
- sessionOwnerId: owner?.id || "",
362
- sessionOwnerLabel: owner?.name || "",
363
- sessionAttributes,
364
- options: {
365
- clientTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone
366
- }
367
- };
368
- }
369
- function getStageParamName(config) {
370
- return config.api.stageQueryParam ?? "stage";
371
- }
372
- function getStage(config) {
373
- return config.workflow.stage ?? "DEV";
374
- }
375
- function buildStreamingUrl(config) {
376
- const endpoint = config.api.streamEndpoint || "/api/workflows/ask/stream";
377
- const queryParams = new URLSearchParams({
378
- [getStageParamName(config)]: getStage(config)
379
- });
380
- if (config.workflow.version !== void 0) {
381
- queryParams.append("workflowVersion", String(config.workflow.version));
382
- }
383
- return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
384
- }
385
- function deriveBasePath(config) {
386
- const endpoint = config.api.streamEndpoint || "/api/workflows/ask/stream";
387
- const [endpointPath] = endpoint.split("?");
388
- const normalized = endpointPath.replace(/\/+$/, "");
389
- return normalized.endsWith("/stream") ? normalized.slice(0, -"/stream".length) : normalized;
390
- }
391
- function buildUserActionUrl(config, userActionId, action) {
392
- const basePath = deriveBasePath(config);
393
- const encodedUserActionId = encodeURIComponent(userActionId);
394
- return `${config.api.baseUrl}${basePath}/user-action/${encodedUserActionId}/${action}`;
395
- }
396
- function buildResolveImagesUrl(config) {
397
- if (config.api.resolveImagesEndpoint) {
398
- return `${config.api.baseUrl}${config.api.resolveImagesEndpoint}`;
399
- }
400
- return `${config.api.baseUrl}${deriveBasePath(config)}/resolve-image-urls`;
401
- }
402
- function buildRequestHeaders(config) {
403
- const headers = { ...config.api.headers };
404
- if (config.api.authToken) {
405
- headers.Authorization = `Bearer ${config.api.authToken}`;
406
- }
407
- return headers;
408
- }
409
- function buildScopeKey(config) {
410
- return [
411
- config.session?.userId ?? "",
412
- config.workflow.id ?? "",
413
- config.workflow.version ?? "",
414
- config.workflow.stage ?? ""
415
- ].join("|");
416
- }
417
-
418
- // src/utils/userActionClient.ts
419
- async function sendUserActionRequest(config, userActionId, action, data) {
420
- const url = buildUserActionUrl(config, userActionId, action);
421
- const baseHeaders = buildRequestHeaders(config);
422
- const hasBody = data !== void 0;
423
- const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
424
- const response = await fetch(url, {
425
- method: "POST",
426
- headers,
427
- body: hasBody ? JSON.stringify(data) : void 0
428
- });
429
- if (!response.ok) {
430
- const errorText = await response.text();
431
- throw new Error(`HTTP ${response.status}: ${errorText}`);
432
- }
433
- return await response.json();
434
- }
435
- async function submitUserAction(config, userActionId, data) {
436
- return sendUserActionRequest(config, userActionId, "submit", data);
437
- }
438
- async function cancelUserAction(config, userActionId) {
439
- return sendUserActionRequest(config, userActionId, "cancel");
440
- }
441
- async function resendUserAction(config, userActionId) {
442
- return sendUserActionRequest(config, userActionId, "resend");
443
- }
444
- var storage = reactNativeMmkv.createMMKV({ id: "payman-chat-store" });
445
- var chatStore = {
446
- get(key) {
447
- const raw = storage.getString(key);
448
- if (!raw) return [];
449
- try {
450
- return JSON.parse(raw);
451
- } catch {
452
- return [];
453
- }
454
- },
455
- set(key, messages) {
456
- storage.set(key, JSON.stringify(messages));
457
- },
458
- delete(key) {
459
- storage.delete(key);
460
- }
461
- };
462
-
463
- // src/utils/activeStreamStore.ts
464
- var streams = /* @__PURE__ */ new Map();
465
- var activeStreamStore = {
466
- has(key) {
467
- return streams.has(key);
468
- },
469
- get(key) {
470
- const entry = streams.get(key);
471
- if (!entry) return null;
472
- return { messages: entry.messages, isWaiting: entry.isWaiting };
473
- },
474
- // Called before startStream — registers the controller and initial messages
475
- start(key, abortController, initialMessages) {
476
- const existing = streams.get(key);
477
- streams.set(key, {
478
- messages: initialMessages,
479
- isWaiting: true,
480
- abortController,
481
- listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
482
- });
483
- },
484
- // Called by the stream on every event — applies the same updater pattern React uses
485
- applyMessages(key, updater) {
486
- const entry = streams.get(key);
487
- if (!entry) return;
488
- const next = typeof updater === "function" ? updater(entry.messages) : updater;
489
- entry.messages = next;
490
- entry.listeners.forEach((l) => l(next, entry.isWaiting));
491
- },
492
- setWaiting(key, waiting) {
493
- const entry = streams.get(key);
494
- if (!entry) return;
495
- entry.isWaiting = waiting;
496
- entry.listeners.forEach((l) => l(entry.messages, waiting));
497
- },
498
- // Called when stream completes — persists to chatStore and cleans up
499
- complete(key) {
500
- const entry = streams.get(key);
501
- if (!entry) return;
502
- entry.isWaiting = false;
503
- entry.listeners.forEach((l) => l(entry.messages, false));
504
- const toSave = entry.messages.filter((m) => !m.isStreaming);
505
- if (toSave.length > 0) chatStore.set(key, toSave);
506
- streams.delete(key);
507
- },
508
- // Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
509
- subscribe(key, listener) {
510
- const entry = streams.get(key);
511
- if (!entry) return () => {
512
- };
513
- entry.listeners.add(listener);
514
- return () => {
515
- streams.get(key)?.listeners.delete(listener);
516
- };
517
- },
518
- // Explicit user cancel — aborts the controller and removes the entry
519
- abort(key) {
520
- const entry = streams.get(key);
521
- if (!entry) return;
522
- entry.abortController.abort();
523
- streams.delete(key);
524
- }
525
- };
526
-
527
- // src/utils/v2EventProcessor.ts
528
311
  function getEventText(event, field) {
529
312
  const value = event[field];
530
313
  return typeof value === "string" ? value.trim() : "";
@@ -548,6 +331,16 @@ function addThinkingLine(state, header, detail) {
548
331
  function appendThinkingText(state, text) {
549
332
  state.formattedThinkingText += text;
550
333
  }
334
+ function updateExecutionStageMessage(state, msg) {
335
+ if (!msg) return;
336
+ for (let i = state.steps.length - 1; i >= 0; i--) {
337
+ const s = state.steps[i];
338
+ if (s.eventType === "STAGE_STARTED" && (s.stage === "executor" || s.stage === "execution") && s.status === "in_progress") {
339
+ s.message = msg;
340
+ return;
341
+ }
342
+ }
343
+ }
551
344
  function completeLastInProgressStep(steps) {
552
345
  for (let i = steps.length - 1; i >= 0; i--) {
553
346
  if (steps[i].status === "in_progress") {
@@ -575,11 +368,31 @@ function createInitialV2State() {
575
368
  currentExecutingStepId: void 0
576
369
  };
577
370
  }
578
- function processStreamEventV2(event, state) {
371
+ function processStreamEventV2(rawEvent, state) {
372
+ const event = normalizeEvent(rawEvent);
579
373
  const eventType = event.eventType;
580
374
  if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
581
375
  if (event.executionId) state.executionId = event.executionId;
582
376
  if (event.sessionId) state.sessionId = event.sessionId;
377
+ const description = typeof event.description === "string" ? event.description : "";
378
+ if (description) {
379
+ for (let i = state.steps.length - 1; i >= 0; i--) {
380
+ if (state.steps[i].status === "in_progress") {
381
+ state.steps[i].message = description;
382
+ break;
383
+ }
384
+ }
385
+ }
386
+ return state;
387
+ }
388
+ if (typeof eventType === "string" && eventType.toUpperCase() === "THINKING_DELTA") {
389
+ const text = typeof event.text === "string" ? event.text : "";
390
+ if (text) {
391
+ appendThinkingText(state, text);
392
+ }
393
+ if (event.executionId) state.executionId = event.executionId;
394
+ if (event.sessionId) state.sessionId = event.sessionId;
395
+ state.lastEventType = "THINKING_DELTA";
583
396
  return state;
584
397
  }
585
398
  if (event.executionId) state.executionId = event.executionId;
@@ -590,6 +403,16 @@ function processStreamEventV2(event, state) {
590
403
  case "STARTED":
591
404
  state.lastEventType = eventType;
592
405
  break;
406
+ case "INTENT_PROGRESS": {
407
+ const rawMessage = typeof event.message === "string" ? event.message : "";
408
+ const rawPartial = typeof event.partialText === "string" ? event.partialText : "";
409
+ const delta = rawMessage || rawPartial;
410
+ if (delta.length > 0) {
411
+ state.finalResponse += delta;
412
+ }
413
+ state.lastEventType = eventType;
414
+ break;
415
+ }
593
416
  case "INTENT_THINKING": {
594
417
  const worker = getEventText(event, "workerName") || "Worker";
595
418
  const msg = getEventText(event, "message") || "Thinking...";
@@ -691,6 +514,7 @@ function processStreamEventV2(event, state) {
691
514
  elapsedMs: event.elapsedMs
692
515
  });
693
516
  state.currentExecutingStepId = stepId;
517
+ updateExecutionStageMessage(state, message);
694
518
  state.lastEventType = eventType;
695
519
  break;
696
520
  }
@@ -733,6 +557,10 @@ function processStreamEventV2(event, state) {
733
557
  }
734
558
  case "WORKFLOW_COMPLETED":
735
559
  case "COMPLETED": {
560
+ const totalTime = Number(event.totalTimeMs);
561
+ if (Number.isFinite(totalTime) && totalTime > 0) {
562
+ state.totalElapsedMs = totalTime;
563
+ }
736
564
  let content = extractResponseContent(event.response);
737
565
  const trace = event.trace && typeof event.trace === "object" ? event.trace : null;
738
566
  if (!content && trace?.workflowMsg && typeof trace.workflowMsg === "string") {
@@ -745,7 +573,9 @@ function processStreamEventV2(event, state) {
745
573
  }
746
574
  if (content) {
747
575
  state.finalResponse = content;
748
- state.finalData = event.response ?? event.trace;
576
+ if (event.trace && typeof event.trace === "object") {
577
+ state.finalData = event.trace;
578
+ }
749
579
  state.hasError = false;
750
580
  state.errorMessage = "";
751
581
  } else {
@@ -927,12 +757,373 @@ function processStreamEventV2(event, state) {
927
757
  state.lastEventType = eventType;
928
758
  break;
929
759
  }
760
+ // ---- K2 pipeline stage lifecycle events ----
761
+ //
762
+ // The k2-server playground streaming API emits
763
+ // STAGE_STARTED / STAGE_COMPLETED / STAGE_FAILED /
764
+ // STAGE_SKIPPED around each pipeline stage so the UI can
765
+ // show real progress (sanitizer → analyzer → planner → …)
766
+ // instead of a static placeholder. Each STAGE_STARTED
767
+ // pushes an in-progress step with a user-facing label; the
768
+ // matching STAGE_COMPLETED/FAILED closes it WITHOUT
769
+ // rewriting the label — a terse "completed" flash between
770
+ // stages was more noise than signal. STAGE_SKIPPED removes
771
+ // the just-opened step entirely (skipping is expected on
772
+ // DIRECT_RESPONSE short-circuits).
773
+ //
774
+ // Finding the matching step uses `stepId` directly rather
775
+ // than comparing labels — the processor remembers which id
776
+ // it minted for the latest open STAGE_STARTED and the
777
+ // closing events look it up from state.currentExecutingStepId.
778
+ case "STAGE_STARTED": {
779
+ const stage = getEventText(event, "stage");
780
+ if (!stage) {
781
+ state.lastEventType = eventType;
782
+ break;
783
+ }
784
+ const serverMessage = getEventText(event, "message");
785
+ let initialMessage = serverMessage || stageLabel(stage);
786
+ if (stage === "executor" || stage === "execution") {
787
+ for (let i = state.steps.length - 1; i >= 0; i--) {
788
+ const s = state.steps[i];
789
+ if (s.eventType === "STAGE_STARTED" && s.stage === "analyzer" && s.status === "completed" && s.message) {
790
+ initialMessage = s.message;
791
+ break;
792
+ }
793
+ }
794
+ }
795
+ const stepId = `stage-${stage}-${state.stepCounter++}`;
796
+ state.steps.push({
797
+ id: stepId,
798
+ eventType,
799
+ message: initialMessage,
800
+ status: "in_progress",
801
+ timestamp: Date.now(),
802
+ stage
803
+ });
804
+ state.currentExecutingStepId = stepId;
805
+ state.lastEventType = eventType;
806
+ break;
807
+ }
808
+ case "STAGE_COMPLETED": {
809
+ const stage = getEventText(event, "stage");
810
+ if (!stage) {
811
+ state.lastEventType = eventType;
812
+ break;
813
+ }
814
+ const serverMessage = getEventText(event, "message");
815
+ for (let i = state.steps.length - 1; i >= 0; i--) {
816
+ const s = state.steps[i];
817
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
818
+ if (serverMessage) s.message = serverMessage;
819
+ s.status = "completed";
820
+ const durationMs = Number(event.durationMs);
821
+ if (Number.isFinite(durationMs)) s.elapsedMs = durationMs;
822
+ if (s.id === state.currentExecutingStepId) {
823
+ state.currentExecutingStepId = void 0;
824
+ }
825
+ break;
826
+ }
827
+ }
828
+ state.lastEventType = eventType;
829
+ break;
830
+ }
831
+ case "STAGE_FAILED": {
832
+ const stage = getEventText(event, "stage");
833
+ if (!stage) {
834
+ state.lastEventType = eventType;
835
+ break;
836
+ }
837
+ const serverMessage = getEventText(event, "message");
838
+ for (let i = state.steps.length - 1; i >= 0; i--) {
839
+ const s = state.steps[i];
840
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
841
+ if (serverMessage) s.message = serverMessage;
842
+ s.status = "error";
843
+ const durationMs = Number(event.durationMs);
844
+ if (Number.isFinite(durationMs)) s.elapsedMs = durationMs;
845
+ if (s.id === state.currentExecutingStepId) {
846
+ state.currentExecutingStepId = void 0;
847
+ }
848
+ break;
849
+ }
850
+ }
851
+ state.lastEventType = eventType;
852
+ break;
853
+ }
854
+ case "STAGE_SKIPPED": {
855
+ const stage = getEventText(event, "stage");
856
+ if (!stage) {
857
+ state.lastEventType = eventType;
858
+ break;
859
+ }
860
+ for (let i = state.steps.length - 1; i >= 0; i--) {
861
+ const s = state.steps[i];
862
+ if (s.eventType === "STAGE_STARTED" && s.stage === stage && s.status === "in_progress") {
863
+ if (s.id === state.currentExecutingStepId) {
864
+ state.currentExecutingStepId = void 0;
865
+ }
866
+ s.status = "skipped";
867
+ break;
868
+ }
869
+ }
870
+ state.lastEventType = eventType;
871
+ break;
872
+ }
930
873
  default:
931
874
  state.lastEventType = eventType;
932
875
  break;
933
876
  }
934
- return state;
877
+ return state;
878
+ }
879
+
880
+ // src/utils/messageStateManager.ts
881
+ function buildFormattedThinking(steps, allThinkingText) {
882
+ const parts = [];
883
+ const safeSteps = steps ?? [];
884
+ const cleanAll = allThinkingText.replace(/^\s+/, "");
885
+ if (cleanAll) {
886
+ const firstStepWithThinking = safeSteps.find(
887
+ (s) => s.thinkingText && s.thinkingText.trim()
888
+ );
889
+ if (!firstStepWithThinking) {
890
+ parts.push("**Preflight**");
891
+ parts.push(cleanAll);
892
+ } else {
893
+ const stepText = firstStepWithThinking.thinkingText.trim();
894
+ const idx = cleanAll.indexOf(stepText);
895
+ if (idx > 0) {
896
+ const orphaned = cleanAll.substring(0, idx).replace(/\s+$/, "");
897
+ if (orphaned) {
898
+ parts.push("**Preflight**");
899
+ parts.push(orphaned);
900
+ }
901
+ }
902
+ }
903
+ }
904
+ for (const step of safeSteps) {
905
+ switch (step.eventType) {
906
+ case "STAGE_STARTED": {
907
+ if (step.message) parts.push(`**${step.message}**`);
908
+ break;
909
+ }
910
+ case "ORCHESTRATOR_THINKING":
911
+ parts.push("**Planning**");
912
+ if (step.message) parts.push(step.message);
913
+ break;
914
+ case "INTENT_STARTED": {
915
+ let label = step.message || "Processing";
916
+ const started = label.match(/^(.+?)\s+started$/i);
917
+ const progress = label.match(/^(.+?)\s+in progress$/i);
918
+ if (started) label = started[1];
919
+ else if (progress) label = progress[1];
920
+ parts.push(`**${label}**`);
921
+ if (step.thinkingText) parts.push(step.thinkingText);
922
+ break;
923
+ }
924
+ case "INTENT_PROGRESS": {
925
+ if (step.thinkingText) parts.push(step.thinkingText);
926
+ else if (step.message) parts.push(step.message);
927
+ break;
928
+ }
929
+ case "AGGREGATOR_THINKING":
930
+ parts.push("**Finalizing**");
931
+ if (step.message) parts.push(step.message);
932
+ break;
933
+ case "USER_ACTION_REQUIRED":
934
+ parts.push("**Verification Required**");
935
+ if (step.message) parts.push(step.message);
936
+ break;
937
+ case "USER_ACTION_SUCCESS":
938
+ parts.push(`\u2713 ${step.message || "Verification successful"}`);
939
+ break;
940
+ case "USER_ACTION_REJECTED":
941
+ parts.push(`\u2717 ${step.message || "Verification rejected"}`);
942
+ break;
943
+ case "USER_ACTION_EXPIRED":
944
+ parts.push(`\u2717 ${step.message || "Verification expired"}`);
945
+ break;
946
+ case "USER_ACTION_FAILED":
947
+ parts.push(`\u2717 ${step.message || "Verification failed"}`);
948
+ break;
949
+ }
950
+ }
951
+ return parts.length > 0 ? parts.join("\n") : allThinkingText;
952
+ }
953
+ function createCancelledMessageUpdate(steps, currentMessage) {
954
+ const updatedSteps = steps.map((step) => {
955
+ if (step.status === "in_progress") {
956
+ return { ...step, status: "pending" };
957
+ }
958
+ return step;
959
+ });
960
+ return {
961
+ isStreaming: false,
962
+ isCancelled: true,
963
+ steps: updatedSteps,
964
+ currentExecutingStepId: void 0,
965
+ // Preserve currentMessage so UI can show it with X icon
966
+ currentMessage: currentMessage || "Thinking..."
967
+ };
968
+ }
969
+
970
+ // src/utils/requestBuilder.ts
971
+ var DEFAULT_STREAM_ENDPOINT = "/api/playground/ask/stream";
972
+ function buildRequestBody(config, userMessage, sessionId, options) {
973
+ const sessionOwner = config.sessionParams;
974
+ const sessionAttributes = sessionOwner?.attributes && Object.keys(sessionOwner.attributes).length > 0 ? sessionOwner.attributes : void 0;
975
+ return {
976
+ agentId: config.agentId,
977
+ userInput: userMessage,
978
+ sessionId,
979
+ sessionOwnerLabel: sessionOwner?.name || void 0,
980
+ sessionAttributes,
981
+ analysisMode: options?.analysisMode
982
+ };
983
+ }
984
+ function buildStreamingUrl(config) {
985
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
986
+ const stage = config.stage || "DEV";
987
+ const stageParamName = config.stageQueryParam ?? "stage";
988
+ const queryParams = new URLSearchParams({ [stageParamName]: stage });
989
+ return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
990
+ }
991
+ function buildUserActionUrl(config, userActionId, action) {
992
+ const endpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
993
+ const [endpointPath] = endpoint.split("?");
994
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
995
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
996
+ const encodedUserActionId = encodeURIComponent(userActionId);
997
+ return `${config.api.baseUrl}${basePath}/user-action/${encodedUserActionId}/${action}`;
998
+ }
999
+ function buildResolveImagesUrl(config) {
1000
+ if (config.api.resolveImagesEndpoint) {
1001
+ return `${config.api.baseUrl}${config.api.resolveImagesEndpoint}`;
1002
+ }
1003
+ const streamEndpoint = config.api.streamEndpoint || DEFAULT_STREAM_ENDPOINT;
1004
+ const [endpointPath] = streamEndpoint.split("?");
1005
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
1006
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
1007
+ return `${config.api.baseUrl}${basePath}/resolve-image-urls`;
1008
+ }
1009
+ function buildRequestHeaders(config) {
1010
+ const headers = {
1011
+ ...config.api.headers
1012
+ };
1013
+ if (config.api.authToken) {
1014
+ headers.Authorization = `Bearer ${config.api.authToken}`;
1015
+ }
1016
+ return headers;
1017
+ }
1018
+
1019
+ // src/utils/userActionClient.ts
1020
+ async function sendUserActionRequest(config, userActionId, action, data) {
1021
+ const url = buildUserActionUrl(config, userActionId, action);
1022
+ const baseHeaders = buildRequestHeaders(config);
1023
+ const hasBody = data !== void 0;
1024
+ const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
1025
+ const response = await fetch(url, {
1026
+ method: "POST",
1027
+ headers,
1028
+ body: hasBody ? JSON.stringify(data) : void 0
1029
+ });
1030
+ if (!response.ok) {
1031
+ const errorText = await response.text();
1032
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
1033
+ }
1034
+ return await response.json();
1035
+ }
1036
+ async function submitUserAction(config, userActionId, data) {
1037
+ return sendUserActionRequest(config, userActionId, "submit", data);
1038
+ }
1039
+ async function cancelUserAction(config, userActionId) {
1040
+ return sendUserActionRequest(config, userActionId, "cancel");
935
1041
  }
1042
+ async function resendUserAction(config, userActionId) {
1043
+ return sendUserActionRequest(config, userActionId, "resend");
1044
+ }
1045
+ var storage = reactNativeMmkv.createMMKV({ id: "payman-chat-store" });
1046
+ var chatStore = {
1047
+ get(key) {
1048
+ const raw = storage.getString(key);
1049
+ if (!raw) return [];
1050
+ try {
1051
+ return JSON.parse(raw);
1052
+ } catch {
1053
+ return [];
1054
+ }
1055
+ },
1056
+ set(key, messages) {
1057
+ storage.set(key, JSON.stringify(messages));
1058
+ },
1059
+ delete(key) {
1060
+ storage.delete(key);
1061
+ }
1062
+ };
1063
+
1064
+ // src/utils/activeStreamStore.ts
1065
+ var streams = /* @__PURE__ */ new Map();
1066
+ var activeStreamStore = {
1067
+ has(key) {
1068
+ return streams.has(key);
1069
+ },
1070
+ get(key) {
1071
+ const entry = streams.get(key);
1072
+ if (!entry) return null;
1073
+ return { messages: entry.messages, isWaiting: entry.isWaiting };
1074
+ },
1075
+ // Called before startStream — registers the controller and initial messages
1076
+ start(key, abortController, initialMessages) {
1077
+ const existing = streams.get(key);
1078
+ streams.set(key, {
1079
+ messages: initialMessages,
1080
+ isWaiting: true,
1081
+ abortController,
1082
+ listeners: existing?.listeners ?? /* @__PURE__ */ new Set()
1083
+ });
1084
+ },
1085
+ // Called by the stream on every event — applies the same updater pattern React uses
1086
+ applyMessages(key, updater) {
1087
+ const entry = streams.get(key);
1088
+ if (!entry) return;
1089
+ const next = typeof updater === "function" ? updater(entry.messages) : updater;
1090
+ entry.messages = next;
1091
+ entry.listeners.forEach((l) => l(next, entry.isWaiting));
1092
+ },
1093
+ setWaiting(key, waiting) {
1094
+ const entry = streams.get(key);
1095
+ if (!entry) return;
1096
+ entry.isWaiting = waiting;
1097
+ entry.listeners.forEach((l) => l(entry.messages, waiting));
1098
+ },
1099
+ // Called when stream completes — persists to chatStore and cleans up
1100
+ complete(key) {
1101
+ const entry = streams.get(key);
1102
+ if (!entry) return;
1103
+ entry.isWaiting = false;
1104
+ entry.listeners.forEach((l) => l(entry.messages, false));
1105
+ const toSave = entry.messages.filter((m) => !m.isStreaming);
1106
+ if (toSave.length > 0) chatStore.set(key, toSave);
1107
+ streams.delete(key);
1108
+ },
1109
+ // Subscribe — returns unsubscribe fn. Component calls this on mount, cleanup on unmount.
1110
+ subscribe(key, listener) {
1111
+ const entry = streams.get(key);
1112
+ if (!entry) return () => {
1113
+ };
1114
+ entry.listeners.add(listener);
1115
+ return () => {
1116
+ streams.get(key)?.listeners.delete(listener);
1117
+ };
1118
+ },
1119
+ // Explicit user cancel — aborts the controller and removes the entry
1120
+ abort(key) {
1121
+ const entry = streams.get(key);
1122
+ if (!entry) return;
1123
+ entry.abortController.abort();
1124
+ streams.delete(key);
1125
+ }
1126
+ };
936
1127
 
937
1128
  // src/utils/ragImageResolver.ts
938
1129
  var RAG_IMAGE_REGEX = /\/api\/rag\/chunks\/[^"'\s]+\/image/;
@@ -993,12 +1184,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
993
1184
  const callbacksRef = react.useRef(callbacks);
994
1185
  callbacksRef.current = callbacks;
995
1186
  const startStream = react.useCallback(
996
- async (userMessage, streamingId, sessionId, externalAbortController) => {
1187
+ async (userMessage, streamingId, sessionId, externalAbortController, options) => {
997
1188
  abortControllerRef.current?.abort();
998
1189
  const abortController = externalAbortController ?? new AbortController();
999
1190
  abortControllerRef.current = abortController;
1000
1191
  const state = createInitialV2State();
1001
- const streamStartedAt = Date.now();
1002
1192
  const updateMessage = (update) => {
1003
1193
  if (abortController.signal.aborted) return;
1004
1194
  setMessages(
@@ -1008,7 +1198,12 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1008
1198
  );
1009
1199
  };
1010
1200
  const currentConfig = configRef.current;
1011
- const requestBody = buildRequestBody(currentConfig, userMessage, sessionId);
1201
+ const requestBody = buildRequestBody(
1202
+ currentConfig,
1203
+ userMessage,
1204
+ sessionId,
1205
+ options
1206
+ );
1012
1207
  const url = buildStreamingUrl(currentConfig);
1013
1208
  const headers = buildRequestHeaders(currentConfig);
1014
1209
  try {
@@ -1016,16 +1211,11 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1016
1211
  signal: abortController.signal,
1017
1212
  onEvent: (event) => {
1018
1213
  if (abortController.signal.aborted) return;
1019
- if (typeof event.eventType === "string" && event.eventType.toUpperCase() === "KEEP_ALIVE") {
1020
- if (event.executionId) state.executionId = event.executionId;
1021
- if (event.sessionId) state.sessionId = event.sessionId;
1022
- return;
1023
- }
1024
1214
  processStreamEventV2(event, state);
1025
1215
  const eventType = event.eventType;
1026
1216
  if (eventType === "USER_ACTION_REQUIRED" && state.userActionRequest) {
1027
1217
  callbacksRef.current.onUserActionRequired?.(state.userActionRequest);
1028
- } else if (eventType.startsWith("USER_ACTION_") && eventType !== "USER_ACTION_REQUIRED") {
1218
+ } else if (typeof eventType === "string" && eventType.startsWith("USER_ACTION_") && eventType !== "USER_ACTION_REQUIRED") {
1029
1219
  const msg = getUserActionDisplayMessage(
1030
1220
  eventType,
1031
1221
  event.message?.trim() || event.errorMessage?.trim() || getEventMessage(event)
@@ -1034,7 +1224,18 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1034
1224
  }
1035
1225
  const activeStep = state.steps.find((s) => s.id === state.currentExecutingStepId);
1036
1226
  const lastInProgressStep = [...state.steps].reverse().find((s) => s.status === "in_progress");
1037
- const currentMessage = activeStep?.message || lastInProgressStep?.message || getEventMessage(event);
1227
+ const useful = (m) => m && !isBlandStatus(m) ? m : void 0;
1228
+ const latestUsefulStep = [...state.steps].reverse().find(
1229
+ (s) => s.message && !isBlandStatus(s.message)
1230
+ );
1231
+ const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
1232
+ // nothing useful seen yet). Render the bland label so the
1233
+ // bubble isn't blank.
1234
+ activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
1235
+ if (currentMessage) {
1236
+ callbacksRef.current.onStatusMessage?.(currentMessage);
1237
+ }
1238
+ callbacksRef.current.onStepsUpdate?.([...state.steps]);
1038
1239
  if (state.hasError) {
1039
1240
  updateMessage({
1040
1241
  streamingContent: FRIENDLY_ERROR_MESSAGE,
@@ -1050,7 +1251,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1050
1251
  });
1051
1252
  } else {
1052
1253
  updateMessage({
1053
- streamingContent: "",
1254
+ streamingContent: state.finalResponse,
1054
1255
  content: "",
1055
1256
  currentMessage,
1056
1257
  streamProgress: "processing",
@@ -1067,6 +1268,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1067
1268
  },
1068
1269
  onError: (error) => {
1069
1270
  setIsWaitingForResponse(false);
1271
+ callbacksRef.current.onStatusMessage?.(null);
1070
1272
  if (error.name !== "AbortError") {
1071
1273
  callbacksRef.current.onError?.(error);
1072
1274
  }
@@ -1104,6 +1306,8 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1104
1306
  },
1105
1307
  onComplete: () => {
1106
1308
  setIsWaitingForResponse(false);
1309
+ callbacksRef.current.onStatusMessage?.(null);
1310
+ callbacksRef.current.onStepsUpdate?.([]);
1107
1311
  if (state.userActionPending) {
1108
1312
  state.userActionPending = false;
1109
1313
  state.userActionRequest = void 0;
@@ -1127,14 +1331,18 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1127
1331
  isError: state.hasError,
1128
1332
  errorDetails: state.hasError ? state.errorMessage : void 0,
1129
1333
  executionId: state.executionId,
1130
- tracingData: state.finalData,
1334
+ // Defensive: tracingData must be an object (or undefined)
1335
+ // so the JSON viewer doesn't char-iterate a string. The
1336
+ // event processor already drops bare strings; this is
1337
+ // the last stop before the message leaves the SDK.
1338
+ tracingData: state.finalData != null && typeof state.finalData === "object" ? state.finalData : void 0,
1131
1339
  steps: state.hasError ? [] : [...state.steps],
1132
1340
  isCancelled: false,
1133
1341
  currentExecutingStepId: void 0,
1134
1342
  userActionResult: state.userActionResult,
1135
1343
  formattedThinkingText: state.hasError ? void 0 : state.formattedThinkingText || void 0,
1136
1344
  isResolvingImages: needsImageResolve,
1137
- thinkingDurationSec: state.hasError ? void 0 : Math.max(0, Math.round((Date.now() - streamStartedAt) / 1e3))
1345
+ totalElapsedMs: state.hasError ? void 0 : state.totalElapsedMs
1138
1346
  };
1139
1347
  setMessages(
1140
1348
  (prev) => prev.map(
@@ -1219,199 +1427,45 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
1219
1427
  };
1220
1428
  }
1221
1429
 
1222
- // src/utils/workflowUsersClient.ts
1223
- var DEFAULT_SESSION_PAGE_SIZE = 20;
1224
- var DEFAULT_CONVERSATION_PAGE_SIZE = 50;
1225
- function getStageParamName2(config) {
1226
- return config.api.stageQueryParam ?? "stage";
1227
- }
1228
- function requireOwnerId(config) {
1229
- const ownerId = config.session?.owner?.id;
1230
- if (!ownerId) {
1231
- throw new Error(
1232
- "workflowUsersClient: session.owner.id is required to call this endpoint."
1233
- );
1234
- }
1235
- return ownerId;
1236
- }
1237
- function requireWorkflowName(config) {
1238
- const workflowName = config.workflow.name;
1239
- if (!workflowName) {
1240
- throw new Error(
1241
- "workflowUsersClient: workflow.name is required to call this endpoint."
1242
- );
1243
- }
1244
- return workflowName;
1245
- }
1246
- function requireWorkflowId(config) {
1247
- const workflowId = config.workflow.id;
1248
- if (!workflowId) {
1249
- throw new Error(
1250
- "workflowUsersClient: workflow.id is required to call this endpoint."
1251
- );
1252
- }
1253
- return workflowId;
1254
- }
1255
- function isPlaygroundYaakAuth(config) {
1256
- if (config.api.authToken) return false;
1257
- const headers = config.api.headers;
1258
- if (!headers) return false;
1259
- for (const key of Object.keys(headers)) {
1260
- const lower = key.toLowerCase();
1261
- if (lower === "yaak-api-key" || lower === "x-yaak-api-key") return true;
1262
- }
1263
- return false;
1264
- }
1265
- async function fetchJson(url, headers, signal) {
1266
- const response = await fetch(url, {
1267
- method: "GET",
1268
- headers,
1269
- signal
1270
- });
1271
- if (!response.ok) {
1272
- const errorText = await response.text();
1273
- throw new Error(`HTTP ${response.status}: ${errorText}`);
1274
- }
1275
- return await response.json();
1276
- }
1277
- async function listSessions(config, opts = {}) {
1278
- const ownerId = requireOwnerId(config);
1279
- const useYaakEndpoint = isPlaygroundYaakAuth(config);
1280
- const params = new URLSearchParams({
1281
- page: String(opts.page ?? 0),
1282
- size: String(opts.size ?? DEFAULT_SESSION_PAGE_SIZE)
1283
- });
1284
- if (useYaakEndpoint) {
1285
- params.set("workflowUserId", ownerId);
1286
- params.set("workflowName", requireWorkflowName(config));
1287
- } else {
1288
- params.set("workflowId", requireWorkflowId(config));
1289
- }
1290
- if (config.workflow.stage) {
1291
- params.set(getStageParamName2(config), config.workflow.stage);
1292
- }
1293
- const url = useYaakEndpoint ? `${config.api.baseUrl}/api/workflows/ask/sessions?${params.toString()}` : `${config.api.baseUrl}/api/workflow-users/${encodeURIComponent(
1294
- ownerId
1295
- )}/sessions?${params.toString()}`;
1296
- return fetchJson(
1297
- url,
1298
- buildRequestHeaders(config),
1299
- opts.signal
1300
- );
1301
- }
1302
- async function listConversations(config, opts) {
1303
- const ownerId = requireOwnerId(config);
1304
- const useYaakEndpoint = isPlaygroundYaakAuth(config);
1305
- const params = new URLSearchParams({
1306
- sessionId: opts.sessionId,
1307
- page: String(opts.page ?? 0),
1308
- size: String(opts.size ?? DEFAULT_CONVERSATION_PAGE_SIZE)
1309
- });
1310
- if (useYaakEndpoint) {
1311
- params.set("workflowUserId", ownerId);
1312
- params.set("workflowName", requireWorkflowName(config));
1313
- }
1314
- if (config.workflow.stage) {
1315
- params.set(getStageParamName2(config), config.workflow.stage);
1316
- }
1317
- const url = useYaakEndpoint ? `${config.api.baseUrl}/api/workflows/ask/conversations?${params.toString()}` : `${config.api.baseUrl}/api/workflow-users/${encodeURIComponent(
1318
- ownerId
1319
- )}/conversations?${params.toString()}`;
1320
- return fetchJson(
1321
- url,
1322
- buildRequestHeaders(config),
1323
- opts.signal
1324
- );
1325
- }
1326
-
1327
1430
  // src/hooks/useChatV2.ts
1328
- function conversationEntryToMessages(entry, sessionId) {
1329
- return [
1330
- {
1331
- id: `history-user-${entry.executionId}`,
1332
- sessionId,
1333
- role: "user",
1334
- content: entry.query,
1335
- timestamp: entry.createdAt,
1336
- isHistorical: true
1337
- },
1338
- {
1339
- id: `history-assistant-${entry.executionId}`,
1340
- sessionId,
1341
- role: "assistant",
1342
- content: entry.response,
1343
- timestamp: entry.createdAt,
1344
- executionId: entry.executionId,
1345
- isHistorical: true
1346
- }
1347
- ];
1348
- }
1349
- function streamKeyFor(scopeKey, sessionId) {
1350
- return `${scopeKey}|sid:${sessionId ?? ""}`;
1351
- }
1352
1431
  function useChatV2(config, callbacks = {}) {
1353
- const scopeKey = react.useMemo(() => buildScopeKey(config), [
1354
- config.session?.userId,
1355
- config.workflow?.id,
1356
- config.workflow?.version,
1357
- config.workflow?.stage
1358
- ]);
1359
- const initialSessionId = chatStore.get(scopeKey).find((m) => m.sessionId)?.sessionId ?? config.session?.initialId ?? void 0;
1360
1432
  const [messages, setMessages] = react.useState(() => {
1361
- const stored = chatStore.get(scopeKey);
1362
- if (stored.length > 0) return stored;
1363
- return config.session?.initialMessages ?? [];
1433
+ if (config.userId) return chatStore.get(config.userId);
1434
+ return config.initialMessages ?? [];
1364
1435
  });
1365
1436
  const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
1366
- const [loadingSessionId, setLoadingSessionId] = react.useState(void 0);
1367
- const [currentSessionId, setCurrentSessionId] = react.useState(initialSessionId);
1368
- const sessionIdRef = react.useRef(initialSessionId);
1369
- const prevScopeKeyRef = react.useRef(scopeKey);
1370
- const activeStreamSessionRef = react.useRef(void 0);
1437
+ const sessionIdRef = react.useRef(
1438
+ config.userId ? chatStore.get(config.userId).find((m) => m.sessionId)?.sessionId ?? config.initialSessionId ?? void 0 : config.initialSessionId ?? void 0
1439
+ );
1440
+ const prevUserIdRef = react.useRef(config.userId);
1371
1441
  const callbacksRef = react.useRef(callbacks);
1372
1442
  callbacksRef.current = callbacks;
1373
1443
  const configRef = react.useRef(config);
1374
1444
  configRef.current = config;
1375
- const scopeKeyRef = react.useRef(scopeKey);
1376
- scopeKeyRef.current = scopeKey;
1377
1445
  const messagesRef = react.useRef(messages);
1378
1446
  messagesRef.current = messages;
1379
1447
  const storeAwareSetMessages = react.useCallback(
1380
1448
  (updater) => {
1381
- const scope = scopeKeyRef.current;
1382
- const streamSid = activeStreamSessionRef.current;
1383
- if (streamSid !== void 0) {
1384
- const streamKey = streamKeyFor(scope, streamSid);
1385
- if (activeStreamStore.has(streamKey)) {
1386
- activeStreamStore.applyMessages(
1387
- streamKey,
1388
- updater
1389
- );
1390
- }
1391
- if (sessionIdRef.current === streamSid) {
1392
- setMessages(updater);
1393
- }
1394
- return;
1449
+ const { userId } = configRef.current;
1450
+ if (userId && activeStreamStore.has(userId)) {
1451
+ activeStreamStore.applyMessages(userId, updater);
1395
1452
  }
1396
1453
  setMessages(updater);
1397
1454
  },
1455
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1398
1456
  []
1399
1457
  );
1400
- const storeAwareSetIsWaiting = react.useCallback((waiting) => {
1401
- const scope = scopeKeyRef.current;
1402
- const streamSid = activeStreamSessionRef.current;
1403
- if (streamSid !== void 0) {
1404
- const streamKey = streamKeyFor(scope, streamSid);
1405
- if (activeStreamStore.has(streamKey)) {
1406
- activeStreamStore.setWaiting(streamKey, waiting);
1458
+ const storeAwareSetIsWaiting = react.useCallback(
1459
+ (waiting) => {
1460
+ const { userId } = configRef.current;
1461
+ if (userId && activeStreamStore.has(userId)) {
1462
+ activeStreamStore.setWaiting(userId, waiting);
1407
1463
  }
1408
- if (sessionIdRef.current === streamSid) {
1409
- setIsWaitingForResponse(waiting);
1410
- }
1411
- return;
1412
- }
1413
- setIsWaitingForResponse(waiting);
1414
- }, []);
1464
+ setIsWaitingForResponse(waiting);
1465
+ },
1466
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1467
+ []
1468
+ );
1415
1469
  const [userActionState, setUserActionState] = react.useState({
1416
1470
  request: null,
1417
1471
  result: null,
@@ -1419,43 +1473,38 @@ function useChatV2(config, callbacks = {}) {
1419
1473
  });
1420
1474
  const userActionStateRef = react.useRef(userActionState);
1421
1475
  userActionStateRef.current = userActionState;
1422
- const wrappedCallbacks = react.useMemo(
1423
- () => ({
1424
- ...callbacksRef.current,
1425
- onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
1426
- onStreamStart: () => callbacksRef.current.onStreamStart?.(),
1427
- onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
1428
- onError: (error) => callbacksRef.current.onError?.(error),
1429
- onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1430
- onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1431
- onUserActionRequired: (request) => {
1432
- setUserActionState((prev) => ({ ...prev, request, result: null }));
1433
- callbacksRef.current.onUserActionRequired?.(request);
1434
- },
1435
- onUserActionEvent: (eventType, message) => {
1436
- switch (eventType) {
1437
- case "USER_ACTION_SUCCESS":
1438
- setUserActionState((prev) => ({ ...prev, request: null, result: "approved" }));
1439
- break;
1440
- case "USER_ACTION_REJECTED":
1441
- setUserActionState((prev) => ({ ...prev, request: null, result: "rejected" }));
1442
- break;
1443
- case "USER_ACTION_EXPIRED":
1444
- case "USER_ACTION_FAILED":
1445
- setUserActionState((prev) => ({ ...prev, request: null }));
1446
- break;
1447
- case "USER_ACTION_INVALID":
1448
- setUserActionState((prev) => ({
1449
- ...prev,
1450
- clearOtpTrigger: prev.clearOtpTrigger + 1
1451
- }));
1452
- break;
1453
- }
1454
- callbacksRef.current.onUserActionEvent?.(eventType, message);
1476
+ const wrappedCallbacks = react.useMemo(() => ({
1477
+ ...callbacksRef.current,
1478
+ onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
1479
+ onStreamStart: () => callbacksRef.current.onStreamStart?.(),
1480
+ onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
1481
+ onError: (error) => callbacksRef.current.onError?.(error),
1482
+ onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1483
+ onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1484
+ onUserActionRequired: (request) => {
1485
+ setUserActionState((prev) => ({ ...prev, request, result: null }));
1486
+ callbacksRef.current.onUserActionRequired?.(request);
1487
+ },
1488
+ onUserActionEvent: (eventType, message) => {
1489
+ switch (eventType) {
1490
+ case "USER_ACTION_SUCCESS":
1491
+ setUserActionState((prev) => ({ ...prev, request: null, result: "approved" }));
1492
+ break;
1493
+ case "USER_ACTION_REJECTED":
1494
+ setUserActionState((prev) => ({ ...prev, request: null, result: "rejected" }));
1495
+ break;
1496
+ case "USER_ACTION_EXPIRED":
1497
+ case "USER_ACTION_FAILED":
1498
+ setUserActionState((prev) => ({ ...prev, request: null }));
1499
+ break;
1500
+ case "USER_ACTION_INVALID":
1501
+ setUserActionState((prev) => ({ ...prev, clearOtpTrigger: prev.clearOtpTrigger + 1 }));
1502
+ break;
1455
1503
  }
1456
- }),
1457
- []
1458
- );
1504
+ callbacksRef.current.onUserActionEvent?.(eventType, message);
1505
+ }
1506
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1507
+ }), []);
1459
1508
  const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManagerV2(
1460
1509
  config,
1461
1510
  wrappedCallbacks,
@@ -1463,12 +1512,10 @@ function useChatV2(config, callbacks = {}) {
1463
1512
  storeAwareSetIsWaiting
1464
1513
  );
1465
1514
  const sendMessage = react.useCallback(
1466
- async (userMessage) => {
1515
+ async (userMessage, options) => {
1467
1516
  if (!userMessage.trim()) return;
1468
- const autoGen = configRef.current.session?.autoGenerateId;
1469
- if (!sessionIdRef.current && autoGen !== false) {
1517
+ if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
1470
1518
  sessionIdRef.current = generateId();
1471
- setCurrentSessionId(sessionIdRef.current);
1472
1519
  callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
1473
1520
  }
1474
1521
  const userMessageId = `user-${Date.now()}`;
@@ -1500,40 +1547,39 @@ function useChatV2(config, callbacks = {}) {
1500
1547
  };
1501
1548
  setMessages((prev) => [...prev, streamingMsg]);
1502
1549
  const abortController = new AbortController();
1503
- const scope = scopeKeyRef.current;
1504
- const streamSessionId = sessionIdRef.current;
1505
- const streamKey = streamKeyFor(scope, streamSessionId);
1506
- activeStreamSessionRef.current = streamSessionId;
1507
- const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1508
- activeStreamStore.start(streamKey, abortController, initialMessages);
1550
+ const { userId } = configRef.current;
1551
+ if (userId) {
1552
+ const initialMessages = [...messagesRef.current, userMsg, streamingMsg];
1553
+ activeStreamStore.start(userId, abortController, initialMessages);
1554
+ }
1509
1555
  const newSessionId = await startStream(
1510
1556
  userMessage,
1511
1557
  streamingId,
1512
- streamSessionId,
1513
- abortController
1558
+ sessionIdRef.current,
1559
+ abortController,
1560
+ options
1514
1561
  );
1515
- activeStreamStore.complete(streamKey);
1516
- activeStreamSessionRef.current = void 0;
1517
- if (!abortController.signal.aborted && newSessionId && newSessionId !== streamSessionId && sessionIdRef.current === streamSessionId) {
1562
+ if (userId) {
1563
+ activeStreamStore.complete(userId);
1564
+ }
1565
+ if (!abortController.signal.aborted && newSessionId && newSessionId !== sessionIdRef.current) {
1518
1566
  sessionIdRef.current = newSessionId;
1519
- setCurrentSessionId(newSessionId);
1520
1567
  }
1521
1568
  },
1522
1569
  [startStream]
1523
1570
  );
1524
1571
  const clearMessages = react.useCallback(() => {
1525
- chatStore.delete(scopeKeyRef.current);
1572
+ if (configRef.current.userId) {
1573
+ chatStore.delete(configRef.current.userId);
1574
+ }
1526
1575
  setMessages([]);
1527
1576
  }, []);
1528
1577
  const prependMessages = react.useCallback((msgs) => {
1529
1578
  setMessages((prev) => [...msgs, ...prev]);
1530
1579
  }, []);
1531
1580
  const cancelStream = react.useCallback(() => {
1532
- const scope = scopeKeyRef.current;
1533
- const viewSid = sessionIdRef.current;
1534
- const viewStreamKey = streamKeyFor(scope, viewSid);
1535
- if (activeStreamStore.has(viewStreamKey)) {
1536
- activeStreamStore.abort(viewStreamKey);
1581
+ if (configRef.current.userId) {
1582
+ activeStreamStore.abort(configRef.current.userId);
1537
1583
  }
1538
1584
  cancelStreamManager();
1539
1585
  setIsWaitingForResponse(false);
@@ -1543,7 +1589,10 @@ function useChatV2(config, callbacks = {}) {
1543
1589
  if (msg.isStreaming) {
1544
1590
  return {
1545
1591
  ...msg,
1546
- ...createCancelledMessageUpdate(msg.steps || [], msg.currentMessage)
1592
+ ...createCancelledMessageUpdate(
1593
+ msg.steps || [],
1594
+ msg.currentMessage
1595
+ )
1547
1596
  };
1548
1597
  }
1549
1598
  return msg;
@@ -1551,37 +1600,39 @@ function useChatV2(config, callbacks = {}) {
1551
1600
  );
1552
1601
  }, [cancelStreamManager]);
1553
1602
  const resetSession = react.useCallback(() => {
1554
- const scope = scopeKeyRef.current;
1555
- const viewSid = sessionIdRef.current;
1556
- const viewStreamKey = streamKeyFor(scope, viewSid);
1557
- if (activeStreamStore.has(viewStreamKey)) {
1558
- activeStreamStore.abort(viewStreamKey);
1603
+ if (configRef.current.userId) {
1604
+ activeStreamStore.abort(configRef.current.userId);
1605
+ chatStore.delete(configRef.current.userId);
1559
1606
  }
1560
- chatStore.delete(scope);
1561
1607
  setMessages([]);
1562
1608
  sessionIdRef.current = void 0;
1563
- setCurrentSessionId(void 0);
1564
- activeStreamSessionRef.current = void 0;
1565
1609
  abortControllerRef.current?.abort();
1566
1610
  setIsWaitingForResponse(false);
1567
1611
  setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1568
1612
  }, []);
1569
- const getSessionId = react.useCallback(() => sessionIdRef.current, []);
1570
- const getMessages = react.useCallback(() => messages, [messages]);
1571
- const approveUserAction = react.useCallback(async (otp) => {
1572
- const request = userActionStateRef.current.request;
1573
- if (!request) return;
1574
- try {
1575
- await submitUserAction(configRef.current, request.userActionId, { otp });
1576
- } catch (error) {
1577
- setUserActionState((prev) => ({
1578
- ...prev,
1579
- clearOtpTrigger: prev.clearOtpTrigger + 1
1580
- }));
1581
- callbacksRef.current.onError?.(error);
1582
- throw error;
1583
- }
1613
+ const getSessionId = react.useCallback(() => {
1614
+ return sessionIdRef.current;
1584
1615
  }, []);
1616
+ const getMessages = react.useCallback(() => {
1617
+ return messages;
1618
+ }, [messages]);
1619
+ const approveUserAction = react.useCallback(
1620
+ async (otp) => {
1621
+ const request = userActionStateRef.current.request;
1622
+ if (!request) return;
1623
+ try {
1624
+ await submitUserAction(configRef.current, request.userActionId, { otp });
1625
+ } catch (error) {
1626
+ setUserActionState((prev) => ({
1627
+ ...prev,
1628
+ clearOtpTrigger: prev.clearOtpTrigger + 1
1629
+ }));
1630
+ callbacksRef.current.onError?.(error);
1631
+ throw error;
1632
+ }
1633
+ },
1634
+ []
1635
+ );
1585
1636
  const rejectUserAction = react.useCallback(async () => {
1586
1637
  const request = userActionStateRef.current.request;
1587
1638
  if (!request) return;
@@ -1615,91 +1666,43 @@ function useChatV2(config, callbacks = {}) {
1615
1666
  throw error;
1616
1667
  }
1617
1668
  }, []);
1618
- const inFlightLoadRef = react.useRef(null);
1619
- const loadingSessionIdRef = react.useRef(void 0);
1620
- loadingSessionIdRef.current = loadingSessionId;
1621
- const loadSession = react.useCallback(async (sessionId) => {
1622
- const inFlight = inFlightLoadRef.current;
1623
- if (inFlight && inFlight.sessionId === sessionId) {
1624
- return inFlight.promise;
1625
- }
1626
- if (sessionIdRef.current === sessionId && loadingSessionIdRef.current !== sessionId) {
1627
- return;
1628
- }
1629
- const run = async () => {
1630
- const scope = scopeKeyRef.current;
1631
- setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1632
- sessionIdRef.current = sessionId;
1633
- setCurrentSessionId(sessionId);
1634
- callbacksRef.current.onSessionIdChange?.(sessionId);
1635
- const streamKey = streamKeyFor(scope, sessionId);
1636
- const active = activeStreamStore.get(streamKey);
1637
- if (active) {
1638
- setMessages(active.messages);
1639
- setIsWaitingForResponse(active.isWaiting);
1640
- setLoadingSessionId(void 0);
1641
- return;
1642
- }
1643
- setIsWaitingForResponse(false);
1644
- setMessages([]);
1645
- chatStore.delete(scope);
1646
- setLoadingSessionId(sessionId);
1647
- try {
1648
- const response = await listConversations(configRef.current, { sessionId });
1649
- const entries = response.data ?? [];
1650
- const historical = entries.flatMap((entry) => conversationEntryToMessages(entry, sessionId));
1651
- if (sessionIdRef.current === sessionId) {
1652
- setMessages(historical);
1653
- }
1654
- if (historical.length > 0) {
1655
- chatStore.set(scope, historical);
1656
- }
1657
- } catch (error) {
1658
- callbacksRef.current.onError?.(error);
1659
- throw error;
1660
- } finally {
1661
- setLoadingSessionId((current) => current === sessionId ? void 0 : current);
1662
- }
1663
- };
1664
- const promise = run().finally(() => {
1665
- if (inFlightLoadRef.current?.sessionId === sessionId) {
1666
- inFlightLoadRef.current = null;
1667
- }
1668
- });
1669
- inFlightLoadRef.current = { sessionId, promise };
1670
- return promise;
1671
- }, []);
1672
1669
  react.useEffect(() => {
1673
- const key = streamKeyFor(scopeKey, currentSessionId);
1674
- const unsubscribe = activeStreamStore.subscribe(key, (msgs, isWaiting) => {
1670
+ const { userId } = config;
1671
+ if (!userId) return;
1672
+ const unsubscribe = activeStreamStore.subscribe(userId, (msgs, isWaiting) => {
1675
1673
  setMessages(msgs);
1676
1674
  setIsWaitingForResponse(isWaiting);
1677
1675
  });
1678
- const active = activeStreamStore.get(key);
1676
+ const active = activeStreamStore.get(userId);
1679
1677
  if (active) {
1680
1678
  setMessages(active.messages);
1681
1679
  setIsWaitingForResponse(active.isWaiting);
1682
1680
  }
1683
1681
  return unsubscribe;
1684
- }, [scopeKey, currentSessionId]);
1682
+ }, []);
1685
1683
  react.useEffect(() => {
1684
+ if (!config.userId) return;
1686
1685
  const toSave = messages.filter((m) => !m.isStreaming);
1687
1686
  if (toSave.length > 0) {
1688
- chatStore.set(scopeKey, toSave);
1687
+ chatStore.set(config.userId, toSave);
1689
1688
  }
1690
- }, [messages, scopeKey]);
1689
+ }, [messages, config.userId]);
1691
1690
  react.useEffect(() => {
1692
- const prevKey = prevScopeKeyRef.current;
1693
- prevScopeKeyRef.current = scopeKey;
1694
- if (prevKey === scopeKey) return;
1695
- const stored = chatStore.get(scopeKey);
1696
- setMessages(stored);
1697
- const restoredSessionId = stored.find((m) => m.sessionId)?.sessionId ?? configRef.current.session?.initialId ?? void 0;
1698
- sessionIdRef.current = restoredSessionId;
1699
- setCurrentSessionId(restoredSessionId);
1700
- setIsWaitingForResponse(false);
1701
- setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1702
- }, [scopeKey]);
1691
+ const prevUserId = prevUserIdRef.current;
1692
+ prevUserIdRef.current = config.userId;
1693
+ if (prevUserId === config.userId) return;
1694
+ if (prevUserId && !config.userId) {
1695
+ chatStore.delete(prevUserId);
1696
+ setMessages([]);
1697
+ sessionIdRef.current = void 0;
1698
+ setIsWaitingForResponse(false);
1699
+ setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
1700
+ } else if (config.userId) {
1701
+ const stored = chatStore.get(config.userId);
1702
+ setMessages(stored);
1703
+ sessionIdRef.current = stored.find((m) => m.sessionId)?.sessionId;
1704
+ }
1705
+ }, [config.userId]);
1703
1706
  return {
1704
1707
  messages,
1705
1708
  sendMessage,
@@ -1710,13 +1713,11 @@ function useChatV2(config, callbacks = {}) {
1710
1713
  getSessionId,
1711
1714
  getMessages,
1712
1715
  isWaitingForResponse,
1713
- sessionId: currentSessionId,
1716
+ sessionId: sessionIdRef.current,
1714
1717
  userActionState,
1715
1718
  approveUserAction,
1716
1719
  rejectUserAction,
1717
- resendOtp,
1718
- loadSession,
1719
- loadingSessionId
1720
+ resendOtp
1720
1721
  };
1721
1722
  }
1722
1723
  function useVoice() {
@@ -1836,18 +1837,14 @@ function useVoice() {
1836
1837
  }
1837
1838
 
1838
1839
  exports.buildFormattedThinking = buildFormattedThinking;
1839
- exports.buildScopeKey = buildScopeKey;
1840
1840
  exports.cancelUserAction = cancelUserAction;
1841
1841
  exports.createInitialV2State = createInitialV2State;
1842
1842
  exports.generateId = generateId;
1843
- exports.listConversations = listConversations;
1844
- exports.listSessions = listSessions;
1845
1843
  exports.processStreamEventV2 = processStreamEventV2;
1846
1844
  exports.resendUserAction = resendUserAction;
1847
1845
  exports.streamWorkflowEvents = streamWorkflowEvents;
1848
1846
  exports.submitUserAction = submitUserAction;
1849
1847
  exports.useChatV2 = useChatV2;
1850
1848
  exports.useVoice = useVoice;
1851
- exports.workingPhaseDetailForDisplay = workingPhaseDetailForDisplay;
1852
1849
  //# sourceMappingURL=index.native.js.map
1853
1850
  //# sourceMappingURL=index.native.js.map