@paymanai/payman-typescript-ask-sdk 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -3,6 +3,17 @@ import React from 'react';
3
3
  type MessageRole = "user" | "assistant" | "system";
4
4
  type StreamProgress = "started" | "processing" | "completed" | "error";
5
5
  type WorkflowStage = "DEV" | "SANDBOX" | "PROD" | "ARCHIVED";
6
+ type UserActionRequest = {
7
+ userActionId: string;
8
+ message: string;
9
+ requestedSchema: Record<string, unknown>;
10
+ };
11
+ type UserActionResult = "approved" | "rejected";
12
+ type UserActionState = {
13
+ request: UserActionRequest | null;
14
+ result: UserActionResult | null;
15
+ clearOtpTrigger: number;
16
+ };
6
17
  type StreamingStep = {
7
18
  id: string;
8
19
  eventType: string;
@@ -36,6 +47,8 @@ type MessageDisplay = {
36
47
  steps?: StreamingStep[];
37
48
  isCancelled?: boolean;
38
49
  currentExecutingStepId?: string;
50
+ /** Result of user action (OTP approval/rejection) */
51
+ userActionResult?: UserActionResult;
39
52
  };
40
53
  type SessionParams = {
41
54
  id?: string;
@@ -125,9 +138,13 @@ type ChatCallbacks = {
125
138
  }) => void;
126
139
  /** Called when session ID changes */
127
140
  onSessionIdChange?: (sessionId: string) => void;
141
+ /** Called when a user action (e.g. OTP) is required */
142
+ onUserActionRequired?: (request: UserActionRequest) => void;
143
+ /** Called on user action events (SUCCESS, INVALID, EXPIRED, REJECTED, RESENT, FAILED) */
144
+ onUserActionEvent?: (eventType: string, message: string) => void;
128
145
  };
129
146
 
130
- declare function useChat(config: ChatConfig, callbacks?: ChatCallbacks): {
147
+ type UseChatReturn = {
131
148
  messages: MessageDisplay[];
132
149
  sendMessage: (userMessage: string) => Promise<void>;
133
150
  clearMessages: () => void;
@@ -137,7 +154,16 @@ declare function useChat(config: ChatConfig, callbacks?: ChatCallbacks): {
137
154
  getMessages: () => MessageDisplay[];
138
155
  isWaitingForResponse: boolean;
139
156
  sessionId: string | undefined;
157
+ /** User action (OTP) state — always present, inert when workflow has no user actions */
158
+ userActionState: UserActionState;
159
+ /** Submit OTP for approval */
160
+ approveUserAction: (otp: string) => Promise<void>;
161
+ /** Reject / cancel a user action */
162
+ rejectUserAction: () => Promise<void>;
163
+ /** Resend OTP code */
164
+ resendOtp: () => Promise<void>;
140
165
  };
166
+ declare function useChat(config: ChatConfig, callbacks?: ChatCallbacks): UseChatReturn;
141
167
 
142
168
  /**
143
169
  * Cross-platform UUID v4 generator
@@ -159,6 +185,11 @@ type StreamEvent = {
159
185
  inputTokens?: number;
160
186
  outputTokens?: number;
161
187
  elapsedMs?: number;
188
+ userActionRequest?: {
189
+ userActionId: string;
190
+ message: string;
191
+ requestedSchema: Record<string, unknown>;
192
+ };
162
193
  [key: string]: unknown;
163
194
  };
164
195
  type StreamOptions = {
@@ -172,4 +203,21 @@ type StreamOptions = {
172
203
  */
173
204
  declare function streamWorkflowEvents(url: string, body: Record<string, unknown>, headers: Record<string, string>, options?: StreamOptions): Promise<void>;
174
205
 
175
- export { type APIConfig, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type MessageDisplay, type MessageRole, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type WorkflowStage, generateId, streamWorkflowEvents, useChat };
206
+ type UserActionResponse = {
207
+ success: boolean;
208
+ message: string;
209
+ };
210
+ /**
211
+ * Submit a user action (e.g. OTP verification)
212
+ */
213
+ declare function submitUserAction(config: ChatConfig, userActionId: string, data?: Record<string, unknown>): Promise<UserActionResponse>;
214
+ /**
215
+ * Cancel / reject a user action
216
+ */
217
+ declare function cancelUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
218
+ /**
219
+ * Resend a user action (e.g. request a new OTP)
220
+ */
221
+ declare function resendUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
222
+
223
+ export { type APIConfig, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type MessageDisplay, type MessageRole, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type UseChatReturn, type UserActionRequest, type UserActionResult, type UserActionState, type WorkflowStage, cancelUserAction, generateId, resendUserAction, streamWorkflowEvents, submitUserAction, useChat };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,17 @@ import React from 'react';
3
3
  type MessageRole = "user" | "assistant" | "system";
4
4
  type StreamProgress = "started" | "processing" | "completed" | "error";
5
5
  type WorkflowStage = "DEV" | "SANDBOX" | "PROD" | "ARCHIVED";
6
+ type UserActionRequest = {
7
+ userActionId: string;
8
+ message: string;
9
+ requestedSchema: Record<string, unknown>;
10
+ };
11
+ type UserActionResult = "approved" | "rejected";
12
+ type UserActionState = {
13
+ request: UserActionRequest | null;
14
+ result: UserActionResult | null;
15
+ clearOtpTrigger: number;
16
+ };
6
17
  type StreamingStep = {
7
18
  id: string;
8
19
  eventType: string;
@@ -36,6 +47,8 @@ type MessageDisplay = {
36
47
  steps?: StreamingStep[];
37
48
  isCancelled?: boolean;
38
49
  currentExecutingStepId?: string;
50
+ /** Result of user action (OTP approval/rejection) */
51
+ userActionResult?: UserActionResult;
39
52
  };
40
53
  type SessionParams = {
41
54
  id?: string;
@@ -125,9 +138,13 @@ type ChatCallbacks = {
125
138
  }) => void;
126
139
  /** Called when session ID changes */
127
140
  onSessionIdChange?: (sessionId: string) => void;
141
+ /** Called when a user action (e.g. OTP) is required */
142
+ onUserActionRequired?: (request: UserActionRequest) => void;
143
+ /** Called on user action events (SUCCESS, INVALID, EXPIRED, REJECTED, RESENT, FAILED) */
144
+ onUserActionEvent?: (eventType: string, message: string) => void;
128
145
  };
129
146
 
130
- declare function useChat(config: ChatConfig, callbacks?: ChatCallbacks): {
147
+ type UseChatReturn = {
131
148
  messages: MessageDisplay[];
132
149
  sendMessage: (userMessage: string) => Promise<void>;
133
150
  clearMessages: () => void;
@@ -137,7 +154,16 @@ declare function useChat(config: ChatConfig, callbacks?: ChatCallbacks): {
137
154
  getMessages: () => MessageDisplay[];
138
155
  isWaitingForResponse: boolean;
139
156
  sessionId: string | undefined;
157
+ /** User action (OTP) state — always present, inert when workflow has no user actions */
158
+ userActionState: UserActionState;
159
+ /** Submit OTP for approval */
160
+ approveUserAction: (otp: string) => Promise<void>;
161
+ /** Reject / cancel a user action */
162
+ rejectUserAction: () => Promise<void>;
163
+ /** Resend OTP code */
164
+ resendOtp: () => Promise<void>;
140
165
  };
166
+ declare function useChat(config: ChatConfig, callbacks?: ChatCallbacks): UseChatReturn;
141
167
 
142
168
  /**
143
169
  * Cross-platform UUID v4 generator
@@ -159,6 +185,11 @@ type StreamEvent = {
159
185
  inputTokens?: number;
160
186
  outputTokens?: number;
161
187
  elapsedMs?: number;
188
+ userActionRequest?: {
189
+ userActionId: string;
190
+ message: string;
191
+ requestedSchema: Record<string, unknown>;
192
+ };
162
193
  [key: string]: unknown;
163
194
  };
164
195
  type StreamOptions = {
@@ -172,4 +203,21 @@ type StreamOptions = {
172
203
  */
173
204
  declare function streamWorkflowEvents(url: string, body: Record<string, unknown>, headers: Record<string, string>, options?: StreamOptions): Promise<void>;
174
205
 
175
- export { type APIConfig, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type MessageDisplay, type MessageRole, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type WorkflowStage, generateId, streamWorkflowEvents, useChat };
206
+ type UserActionResponse = {
207
+ success: boolean;
208
+ message: string;
209
+ };
210
+ /**
211
+ * Submit a user action (e.g. OTP verification)
212
+ */
213
+ declare function submitUserAction(config: ChatConfig, userActionId: string, data?: Record<string, unknown>): Promise<UserActionResponse>;
214
+ /**
215
+ * Cancel / reject a user action
216
+ */
217
+ declare function cancelUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
218
+ /**
219
+ * Resend a user action (e.g. request a new OTP)
220
+ */
221
+ declare function resendUserAction(config: ChatConfig, userActionId: string): Promise<UserActionResponse>;
222
+
223
+ export { type APIConfig, type ChatCallbacks, type ChatConfig, type ChunkDisplay, type MessageDisplay, type MessageRole, type SessionParams, type StreamEvent, type StreamOptions, type StreamProgress, type StreamingStep, type UseChatReturn, type UserActionRequest, type UserActionResult, type UserActionState, type WorkflowStage, cancelUserAction, generateId, resendUserAction, streamWorkflowEvents, submitUserAction, useChat };
package/dist/index.js CHANGED
@@ -113,10 +113,10 @@ async function streamWorkflowEvents(url, body, headers, options = {}) {
113
113
  // src/utils/eventProcessor.ts
114
114
  function getEventMessage(event) {
115
115
  if (event.message?.trim()) {
116
- return event.message;
116
+ return event.message.trim();
117
117
  }
118
118
  if (event.errorMessage?.trim()) {
119
- return event.errorMessage;
119
+ return event.errorMessage.trim();
120
120
  }
121
121
  const eventType = event.eventType;
122
122
  switch (eventType) {
@@ -144,6 +144,20 @@ function getEventMessage(event) {
144
144
  case "WORKFLOW_ERROR":
145
145
  case "INTENT_ERROR":
146
146
  return event.errorMessage || "An error occurred";
147
+ case "USER_ACTION_REQUIRED":
148
+ return "Waiting for verification...";
149
+ case "USER_ACTION_SUCCESS":
150
+ return "Verification successful";
151
+ case "USER_ACTION_EXPIRED":
152
+ return "Verification expired";
153
+ case "USER_ACTION_INVALID":
154
+ return "Invalid input, please try again";
155
+ case "USER_ACTION_REJECTED":
156
+ return "Verification rejected";
157
+ case "USER_ACTION_RESENT":
158
+ return "Verification code resent";
159
+ case "USER_ACTION_FAILED":
160
+ return "Verification failed";
147
161
  default:
148
162
  return eventType;
149
163
  }
@@ -170,6 +184,14 @@ function extractResponseContent(response) {
170
184
  }
171
185
  return "";
172
186
  }
187
+ function completeLastInProgressStep(steps) {
188
+ for (let i = steps.length - 1; i >= 0; i--) {
189
+ if (steps[i].status === "in_progress") {
190
+ steps[i].status = "completed";
191
+ return;
192
+ }
193
+ }
194
+ }
173
195
  function processStreamEvent(event, state) {
174
196
  const eventType = event.eventType;
175
197
  const message = getEventMessage(event);
@@ -269,6 +291,110 @@ function processStreamEvent(event, state) {
269
291
  });
270
292
  state.currentExecutingStepId = stepId;
271
293
  }
294
+ } else if (eventType === "USER_ACTION_REQUIRED") {
295
+ completeLastInProgressStep(state.steps);
296
+ if (event.userActionRequest) {
297
+ state.userActionRequest = {
298
+ userActionId: event.userActionRequest.userActionId,
299
+ message: event.userActionRequest.message,
300
+ requestedSchema: event.userActionRequest.requestedSchema
301
+ };
302
+ }
303
+ state.userActionPending = true;
304
+ const stepId = `step-${state.stepCounter++}`;
305
+ state.steps.push({
306
+ id: stepId,
307
+ eventType,
308
+ message,
309
+ status: "in_progress",
310
+ timestamp: Date.now(),
311
+ elapsedMs: event.elapsedMs
312
+ });
313
+ state.currentExecutingStepId = stepId;
314
+ } else if (eventType === "USER_ACTION_SUCCESS") {
315
+ completeLastInProgressStep(state.steps);
316
+ state.userActionRequest = void 0;
317
+ state.userActionPending = false;
318
+ state.userActionResult = "approved";
319
+ const stepId = `step-${state.stepCounter++}`;
320
+ state.steps.push({
321
+ id: stepId,
322
+ eventType,
323
+ message,
324
+ status: "completed",
325
+ timestamp: Date.now(),
326
+ elapsedMs: event.elapsedMs
327
+ });
328
+ } else if (eventType === "USER_ACTION_INVALID") {
329
+ completeLastInProgressStep(state.steps);
330
+ const errorStepId = `step-${state.stepCounter++}`;
331
+ state.steps.push({
332
+ id: errorStepId,
333
+ eventType,
334
+ message,
335
+ status: "error",
336
+ timestamp: Date.now(),
337
+ elapsedMs: event.elapsedMs
338
+ });
339
+ const retryStepId = `step-${state.stepCounter++}`;
340
+ state.steps.push({
341
+ id: retryStepId,
342
+ eventType: "USER_ACTION_REQUIRED",
343
+ message: "Waiting for verification...",
344
+ status: "in_progress",
345
+ timestamp: Date.now()
346
+ });
347
+ state.currentExecutingStepId = retryStepId;
348
+ } else if (eventType === "USER_ACTION_EXPIRED") {
349
+ completeLastInProgressStep(state.steps);
350
+ state.userActionRequest = void 0;
351
+ state.userActionPending = false;
352
+ const stepId = `step-${state.stepCounter++}`;
353
+ state.steps.push({
354
+ id: stepId,
355
+ eventType,
356
+ message,
357
+ status: "error",
358
+ timestamp: Date.now(),
359
+ elapsedMs: event.elapsedMs
360
+ });
361
+ } else if (eventType === "USER_ACTION_REJECTED") {
362
+ completeLastInProgressStep(state.steps);
363
+ state.userActionRequest = void 0;
364
+ state.userActionPending = false;
365
+ state.userActionResult = "rejected";
366
+ const stepId = `step-${state.stepCounter++}`;
367
+ state.steps.push({
368
+ id: stepId,
369
+ eventType,
370
+ message,
371
+ status: "completed",
372
+ timestamp: Date.now(),
373
+ elapsedMs: event.elapsedMs
374
+ });
375
+ } else if (eventType === "USER_ACTION_RESENT") {
376
+ const stepId = `step-${state.stepCounter++}`;
377
+ state.steps.push({
378
+ id: stepId,
379
+ eventType,
380
+ message,
381
+ status: "completed",
382
+ timestamp: Date.now(),
383
+ elapsedMs: event.elapsedMs
384
+ });
385
+ } else if (eventType === "USER_ACTION_FAILED") {
386
+ completeLastInProgressStep(state.steps);
387
+ state.userActionRequest = void 0;
388
+ state.userActionPending = false;
389
+ const stepId = `step-${state.stepCounter++}`;
390
+ state.steps.push({
391
+ id: stepId,
392
+ eventType,
393
+ message,
394
+ status: "error",
395
+ timestamp: Date.now(),
396
+ elapsedMs: event.elapsedMs
397
+ });
272
398
  }
273
399
  return state;
274
400
  }
@@ -290,9 +416,9 @@ ${state.errorMessage}` : "",
290
416
  executionId: state.executionId,
291
417
  sessionId: state.sessionId,
292
418
  steps: state.hasError ? [] : [...state.steps],
293
- // Don't show steps on error
294
419
  currentExecutingStepId: state.hasError ? void 0 : state.currentExecutingStepId,
295
- isCancelled: false
420
+ isCancelled: false,
421
+ userActionResult: state.userActionResult
296
422
  };
297
423
  }
298
424
  function createErrorMessageUpdate(error, state) {
@@ -333,9 +459,9 @@ ${state.errorMessage}` : state.accumulatedContent || "",
333
459
  executionId: state.executionId,
334
460
  tracingData: state.finalData,
335
461
  steps: state.hasError ? [] : [...state.steps],
336
- // Don't show steps on error
337
462
  isCancelled: false,
338
- currentExecutingStepId: void 0
463
+ currentExecutingStepId: void 0,
464
+ userActionResult: state.userActionResult
339
465
  };
340
466
  }
341
467
  function createCancelledMessageUpdate(steps, currentMessage) {
@@ -381,6 +507,14 @@ function buildStreamingUrl(config) {
381
507
  }
382
508
  return `${config.api.baseUrl}${endpoint}?${queryParams.toString()}`;
383
509
  }
510
+ function buildUserActionUrl(config, userActionId, action) {
511
+ const endpoint = config.api.streamEndpoint || "/api/workflows/ask/stream";
512
+ const [endpointPath] = endpoint.split("?");
513
+ const normalizedEndpointPath = endpointPath.replace(/\/+$/, "");
514
+ const basePath = normalizedEndpointPath.endsWith("/stream") ? normalizedEndpointPath.slice(0, -"/stream".length) : normalizedEndpointPath;
515
+ const encodedUserActionId = encodeURIComponent(userActionId);
516
+ return `${config.api.baseUrl}${basePath}/user-action/${encodedUserActionId}/${action}`;
517
+ }
384
518
  function buildRequestHeaders(config) {
385
519
  const headers = {
386
520
  ...config.api.headers
@@ -390,8 +524,39 @@ function buildRequestHeaders(config) {
390
524
  }
391
525
  return headers;
392
526
  }
527
+
528
+ // src/utils/userActionClient.ts
529
+ async function sendUserActionRequest(config, userActionId, action, data) {
530
+ const url = buildUserActionUrl(config, userActionId, action);
531
+ const baseHeaders = buildRequestHeaders(config);
532
+ const hasBody = data !== void 0;
533
+ const headers = hasBody ? { "Content-Type": "application/json", ...baseHeaders } : baseHeaders;
534
+ const response = await fetch(url, {
535
+ method: "POST",
536
+ headers,
537
+ body: hasBody ? JSON.stringify(data) : void 0
538
+ });
539
+ if (!response.ok) {
540
+ const errorText = await response.text();
541
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
542
+ }
543
+ return await response.json();
544
+ }
545
+ async function submitUserAction(config, userActionId, data) {
546
+ return sendUserActionRequest(config, userActionId, "submit", data);
547
+ }
548
+ async function cancelUserAction(config, userActionId) {
549
+ return sendUserActionRequest(config, userActionId, "cancel");
550
+ }
551
+ async function resendUserAction(config, userActionId) {
552
+ return sendUserActionRequest(config, userActionId, "resend");
553
+ }
393
554
  function useStreamManager(config, callbacks, setMessages, setIsWaitingForResponse) {
394
555
  const abortControllerRef = react.useRef(null);
556
+ const configRef = react.useRef(config);
557
+ configRef.current = config;
558
+ const callbacksRef = react.useRef(callbacks);
559
+ callbacksRef.current = callbacks;
395
560
  const startStream = react.useCallback(
396
561
  async (userMessage, streamingId, sessionId) => {
397
562
  abortControllerRef.current?.abort();
@@ -406,11 +571,15 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
406
571
  stepCounter: 0,
407
572
  currentExecutingStepId: void 0,
408
573
  hasError: false,
409
- errorMessage: ""
574
+ errorMessage: "",
575
+ userActionRequest: void 0,
576
+ userActionPending: false,
577
+ userActionResult: void 0
410
578
  };
411
- const requestBody = buildRequestBody(config, userMessage, sessionId);
412
- const url = buildStreamingUrl(config);
413
- const headers = buildRequestHeaders(config);
579
+ const currentConfig = configRef.current;
580
+ const requestBody = buildRequestBody(currentConfig, userMessage, sessionId);
581
+ const url = buildStreamingUrl(currentConfig);
582
+ const headers = buildRequestHeaders(currentConfig);
414
583
  try {
415
584
  await streamWorkflowEvents(url, requestBody, headers, {
416
585
  signal: abortController.signal,
@@ -421,7 +590,15 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
421
590
  if (event.executionId) state.executionId = event.executionId;
422
591
  if (event.sessionId) state.currentSessionId = event.sessionId;
423
592
  processStreamEvent(event, state);
424
- const currentMessage = event.message?.trim() || event.errorMessage?.trim();
593
+ const eventType = event.eventType;
594
+ if (eventType === "USER_ACTION_REQUIRED" && state.userActionRequest) {
595
+ callbacksRef.current.onUserActionRequired?.(state.userActionRequest);
596
+ } else if (eventType.startsWith("USER_ACTION_") && eventType !== "USER_ACTION_REQUIRED") {
597
+ const msg = event.message?.trim() || event.errorMessage?.trim() || getEventMessage(event);
598
+ callbacksRef.current.onUserActionEvent?.(eventType, msg);
599
+ }
600
+ const rawMessage = event.message?.trim() || event.errorMessage?.trim();
601
+ const currentMessage = rawMessage || (event.eventType?.startsWith("USER_ACTION_") ? getEventMessage(event) : void 0);
425
602
  setMessages(
426
603
  (prev) => prev.map(
427
604
  (msg) => msg.id === streamingId ? {
@@ -437,7 +614,7 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
437
614
  onError: (error) => {
438
615
  setIsWaitingForResponse(false);
439
616
  if (error.name !== "AbortError") {
440
- callbacks.onError?.(error);
617
+ callbacksRef.current.onError?.(error);
441
618
  }
442
619
  setMessages(
443
620
  (prev) => prev.map(
@@ -451,7 +628,7 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
451
628
  onComplete: () => {
452
629
  setIsWaitingForResponse(false);
453
630
  if (state.currentSessionId && state.currentSessionId !== sessionId) {
454
- callbacks.onSessionIdChange?.(state.currentSessionId);
631
+ callbacksRef.current.onSessionIdChange?.(state.currentSessionId);
455
632
  }
456
633
  const finalMessage = createFinalMessage(streamingId, {
457
634
  ...state,
@@ -462,14 +639,14 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
462
639
  (msg) => msg.id === streamingId ? finalMessage : msg
463
640
  )
464
641
  );
465
- callbacks.onStreamComplete?.(finalMessage);
642
+ callbacksRef.current.onStreamComplete?.(finalMessage);
466
643
  }
467
644
  });
468
645
  return state.currentSessionId;
469
646
  } catch (error) {
470
647
  setIsWaitingForResponse(false);
471
648
  if (error.name !== "AbortError") {
472
- callbacks.onError?.(error);
649
+ callbacksRef.current.onError?.(error);
473
650
  }
474
651
  setMessages(
475
652
  (prev) => prev.map(
@@ -482,7 +659,7 @@ function useStreamManager(config, callbacks, setMessages, setIsWaitingForRespons
482
659
  return state.currentSessionId;
483
660
  }
484
661
  },
485
- [config, callbacks, setMessages, setIsWaitingForResponse]
662
+ [setMessages, setIsWaitingForResponse]
486
663
  );
487
664
  const cancelStream = react.useCallback(() => {
488
665
  abortControllerRef.current?.abort();
@@ -499,18 +676,61 @@ function useChat(config, callbacks = {}) {
499
676
  const [messages, setMessages] = react.useState([]);
500
677
  const [isWaitingForResponse, setIsWaitingForResponse] = react.useState(false);
501
678
  const sessionIdRef = react.useRef(void 0);
679
+ const callbacksRef = react.useRef(callbacks);
680
+ callbacksRef.current = callbacks;
681
+ const configRef = react.useRef(config);
682
+ configRef.current = config;
683
+ const [userActionState, setUserActionState] = react.useState({
684
+ request: null,
685
+ result: null,
686
+ clearOtpTrigger: 0
687
+ });
688
+ const userActionStateRef = react.useRef(userActionState);
689
+ userActionStateRef.current = userActionState;
690
+ const wrappedCallbacks = react.useMemo(() => ({
691
+ ...callbacksRef.current,
692
+ onMessageSent: (message) => callbacksRef.current.onMessageSent?.(message),
693
+ onStreamStart: () => callbacksRef.current.onStreamStart?.(),
694
+ onStreamComplete: (message) => callbacksRef.current.onStreamComplete?.(message),
695
+ onError: (error) => callbacksRef.current.onError?.(error),
696
+ onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
697
+ onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
698
+ onUserActionRequired: (request) => {
699
+ setUserActionState((prev) => ({ ...prev, request, result: null }));
700
+ callbacksRef.current.onUserActionRequired?.(request);
701
+ },
702
+ onUserActionEvent: (eventType, message) => {
703
+ switch (eventType) {
704
+ case "USER_ACTION_SUCCESS":
705
+ setUserActionState((prev) => ({ ...prev, request: null, result: "approved" }));
706
+ break;
707
+ case "USER_ACTION_REJECTED":
708
+ setUserActionState((prev) => ({ ...prev, request: null, result: "rejected" }));
709
+ break;
710
+ case "USER_ACTION_EXPIRED":
711
+ case "USER_ACTION_FAILED":
712
+ setUserActionState((prev) => ({ ...prev, request: null }));
713
+ break;
714
+ case "USER_ACTION_INVALID":
715
+ setUserActionState((prev) => ({ ...prev, clearOtpTrigger: prev.clearOtpTrigger + 1 }));
716
+ break;
717
+ }
718
+ callbacksRef.current.onUserActionEvent?.(eventType, message);
719
+ }
720
+ // eslint-disable-next-line react-hooks/exhaustive-deps
721
+ }), []);
502
722
  const { startStream, cancelStream: cancelStreamManager, abortControllerRef } = useStreamManager(
503
723
  config,
504
- callbacks,
724
+ wrappedCallbacks,
505
725
  setMessages,
506
726
  setIsWaitingForResponse
507
727
  );
508
728
  const sendMessage = react.useCallback(
509
729
  async (userMessage) => {
510
730
  if (!userMessage.trim()) return;
511
- if (!sessionIdRef.current && config.autoGenerateSessionId !== false) {
731
+ if (!sessionIdRef.current && configRef.current.autoGenerateSessionId !== false) {
512
732
  sessionIdRef.current = generateId();
513
- callbacks.onSessionIdChange?.(sessionIdRef.current);
733
+ callbacksRef.current.onSessionIdChange?.(sessionIdRef.current);
514
734
  }
515
735
  const userMessageId = `user-${Date.now()}`;
516
736
  const userMsg = {
@@ -521,9 +741,9 @@ function useChat(config, callbacks = {}) {
521
741
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
522
742
  };
523
743
  setMessages((prev) => [...prev, userMsg]);
524
- callbacks.onMessageSent?.(userMessage);
744
+ callbacksRef.current.onMessageSent?.(userMessage);
525
745
  setIsWaitingForResponse(true);
526
- callbacks.onStreamStart?.();
746
+ callbacksRef.current.onStreamStart?.();
527
747
  const streamingId = `assistant-${Date.now()}`;
528
748
  const streamingMsg = {
529
749
  id: streamingId,
@@ -549,7 +769,7 @@ function useChat(config, callbacks = {}) {
549
769
  sessionIdRef.current = newSessionId;
550
770
  }
551
771
  },
552
- [config, callbacks, startStream, setMessages, setIsWaitingForResponse]
772
+ [startStream]
553
773
  );
554
774
  const clearMessages = react.useCallback(() => {
555
775
  setMessages([]);
@@ -557,6 +777,7 @@ function useChat(config, callbacks = {}) {
557
777
  const cancelStream = react.useCallback(() => {
558
778
  cancelStreamManager();
559
779
  setIsWaitingForResponse(false);
780
+ setUserActionState((prev) => ({ ...prev, request: null, result: null }));
560
781
  setMessages(
561
782
  (prev) => prev.map((msg) => {
562
783
  if (msg.isStreaming) {
@@ -577,6 +798,7 @@ function useChat(config, callbacks = {}) {
577
798
  sessionIdRef.current = void 0;
578
799
  abortControllerRef.current?.abort();
579
800
  setIsWaitingForResponse(false);
801
+ setUserActionState({ request: null, result: null, clearOtpTrigger: 0 });
580
802
  }, []);
581
803
  const getSessionId = react.useCallback(() => {
582
804
  return sessionIdRef.current;
@@ -584,6 +806,56 @@ function useChat(config, callbacks = {}) {
584
806
  const getMessages = react.useCallback(() => {
585
807
  return messages;
586
808
  }, [messages]);
809
+ const approveUserAction = react.useCallback(
810
+ async (otp) => {
811
+ const request = userActionStateRef.current.request;
812
+ if (!request) return;
813
+ try {
814
+ await submitUserAction(configRef.current, request.userActionId, { otp });
815
+ } catch (error) {
816
+ setUserActionState((prev) => ({
817
+ ...prev,
818
+ clearOtpTrigger: prev.clearOtpTrigger + 1
819
+ }));
820
+ callbacksRef.current.onError?.(error);
821
+ throw error;
822
+ }
823
+ },
824
+ []
825
+ );
826
+ const rejectUserAction = react.useCallback(async () => {
827
+ const request = userActionStateRef.current.request;
828
+ if (!request) return;
829
+ try {
830
+ setMessages((prev) => {
831
+ let lastStreamingIdx = -1;
832
+ for (let i = prev.length - 1; i >= 0; i--) {
833
+ if (prev[i].role === "assistant" && prev[i].isStreaming) {
834
+ lastStreamingIdx = i;
835
+ break;
836
+ }
837
+ }
838
+ if (lastStreamingIdx === -1) return prev;
839
+ return prev.map(
840
+ (msg, i) => i === lastStreamingIdx ? { ...msg, currentMessage: "Rejecting..." } : msg
841
+ );
842
+ });
843
+ await cancelUserAction(configRef.current, request.userActionId);
844
+ } catch (error) {
845
+ callbacksRef.current.onError?.(error);
846
+ throw error;
847
+ }
848
+ }, []);
849
+ const resendOtp = react.useCallback(async () => {
850
+ const request = userActionStateRef.current.request;
851
+ if (!request) return;
852
+ try {
853
+ await resendUserAction(configRef.current, request.userActionId);
854
+ } catch (error) {
855
+ callbacksRef.current.onError?.(error);
856
+ throw error;
857
+ }
858
+ }, []);
587
859
  return {
588
860
  messages,
589
861
  sendMessage,
@@ -593,12 +865,20 @@ function useChat(config, callbacks = {}) {
593
865
  getSessionId,
594
866
  getMessages,
595
867
  isWaitingForResponse,
596
- sessionId: sessionIdRef.current
868
+ sessionId: sessionIdRef.current,
869
+ // User action (OTP) state and methods
870
+ userActionState,
871
+ approveUserAction,
872
+ rejectUserAction,
873
+ resendOtp
597
874
  };
598
875
  }
599
876
 
877
+ exports.cancelUserAction = cancelUserAction;
600
878
  exports.generateId = generateId;
879
+ exports.resendUserAction = resendUserAction;
601
880
  exports.streamWorkflowEvents = streamWorkflowEvents;
881
+ exports.submitUserAction = submitUserAction;
602
882
  exports.useChat = useChat;
603
883
  //# sourceMappingURL=index.js.map
604
884
  //# sourceMappingURL=index.js.map