@paymanai/payman-ask-sdk 4.0.15 → 4.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, forwardRef, useCallback, useRef, useState, useImperativeHandle, useEffect, useMemo, useLayoutEffect, useContext } from 'react';
1
+ import { createContext, forwardRef, useCallback, useRef, useState, useMemo, useEffect, useImperativeHandle, useLayoutEffect, useContext } from 'react';
2
2
  import { AnimatePresence, motion } from 'framer-motion';
3
3
  import { clsx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
@@ -4209,6 +4209,7 @@ function VerificationInline({
4209
4209
  const [code, setCode] = useState("");
4210
4210
  const [errored, setErrored] = useState(false);
4211
4211
  const [resendSec, setResendSec] = useState(0);
4212
+ const [hiddenAfterResend, setHiddenAfterResend] = useState(false);
4212
4213
  const lastSubmittedRef = useRef(null);
4213
4214
  const resendTimerRef = useRef(void 0);
4214
4215
  const status = prompt.status;
@@ -4222,6 +4223,9 @@ function VerificationInline({
4222
4223
  lastSubmittedRef.current = null;
4223
4224
  }
4224
4225
  }, [prompt.subAction, prompt.userActionId]);
4226
+ useEffect(() => {
4227
+ setHiddenAfterResend(false);
4228
+ }, [prompt.expirySeconds, prompt.message, prompt.subAction, prompt.userActionId]);
4225
4229
  useEffect(() => {
4226
4230
  if (code.length < codeLen) lastSubmittedRef.current = null;
4227
4231
  }, [code, codeLen]);
@@ -4272,11 +4276,13 @@ function VerificationInline({
4272
4276
  if (locked || resendSec > 0) return;
4273
4277
  setErrored(false);
4274
4278
  setCode("");
4279
+ setHiddenAfterResend(true);
4275
4280
  lastSubmittedRef.current = null;
4276
4281
  try {
4277
4282
  await onResend(prompt.userActionId);
4278
4283
  startResendCooldown();
4279
4284
  } catch {
4285
+ setHiddenAfterResend(false);
4280
4286
  }
4281
4287
  }, [locked, onResend, prompt.userActionId, resendSec, startResendCooldown]);
4282
4288
  const handleCancel = useCallback(() => {
@@ -4284,6 +4290,7 @@ function VerificationInline({
4284
4290
  void onCancel(prompt.userActionId);
4285
4291
  }, [busy, onCancel, prompt.userActionId]);
4286
4292
  const description = prompt.message?.trim() || (isNumeric ? `Enter the ${codeLen}-digit code to continue` : "Enter the verification code to continue");
4293
+ if (hiddenAfterResend) return null;
4287
4294
  return /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua", role: "group", "aria-label": "Verification required", children: [
4288
4295
  /* @__PURE__ */ jsxs("div", { className: "payman-v2-ua-head", children: [
4289
4296
  /* @__PURE__ */ jsx(ShieldCheck, { className: "payman-v2-ua-icon", size: 15, strokeWidth: 1.75, "aria-hidden": true }),
@@ -4379,6 +4386,9 @@ function SchemaFormInline({
4379
4386
  const busy = status === "submitting";
4380
4387
  const stale = status === "stale";
4381
4388
  const locked = busy || stale || expired;
4389
+ const isUserConfirmation = prompt.subAction === "UserConfirmation";
4390
+ const messageFormat = prompt.metadata?.["payman/messageFormat"];
4391
+ const renderMarkdown = messageFormat === "markdown";
4382
4392
  const setValue = (key, value) => {
4383
4393
  setValues((prev) => ({ ...prev, [key]: value }));
4384
4394
  setErrors((prev) => {
@@ -4404,8 +4414,8 @@ function SchemaFormInline({
4404
4414
  /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-title", children: "Action required" }),
4405
4415
  typeof secondsLeft === "number" && !stale && /* @__PURE__ */ jsx("span", { className: "payman-v2-ua-timer", children: expired ? "Expired" : `${secondsLeft}s` })
4406
4416
  ] }),
4407
- prompt.message?.trim() && /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message }),
4408
- stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: "This action has no inputs to fill." }) : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
4417
+ prompt.message?.trim() && (renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-markdown", children: /* @__PURE__ */ jsx(MarkdownRendererV2, { content: prompt.message }) }) : /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-desc", children: prompt.message })),
4418
+ stale ? /* @__PURE__ */ jsx("p", { className: "payman-v2-ua-stale", children: "This request is no longer available." }) : fields.length === 0 ? null : /* @__PURE__ */ jsx("div", { className: "payman-v2-ua-form", children: fields.map(([key, field]) => {
4409
4419
  const widget = classifyField(field);
4410
4420
  const label = field.title || key;
4411
4421
  const required = isRequired(schema, key);
@@ -4478,7 +4488,7 @@ function SchemaFormInline({
4478
4488
  className: "payman-v2-ua-btn payman-v2-ua-btn-primary",
4479
4489
  disabled: locked,
4480
4490
  onClick: handleSubmit,
4481
- children: busy ? "Submitting\u2026" : "Submit"
4491
+ children: busy ? "Submitting\u2026" : isUserConfirmation ? "Confirm" : "Submit"
4482
4492
  }
4483
4493
  ),
4484
4494
  /* @__PURE__ */ jsx(
@@ -4534,10 +4544,25 @@ function useExpiryCountdown(prompt) {
4534
4544
  const expired = initial !== void 0 && secondsLeft === 0;
4535
4545
  return [secondsLeft, expired];
4536
4546
  }
4537
- function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
4547
+ function UserActionInline({
4548
+ prompt,
4549
+ onSubmit,
4550
+ onCancel,
4551
+ onResend,
4552
+ onExpired
4553
+ }) {
4538
4554
  const [secondsLeft, expired] = useExpiryCountdown(prompt);
4555
+ useEffect(() => {
4556
+ if (expired && prompt.kind !== "notification") onExpired?.();
4557
+ }, [expired, onExpired, prompt.kind]);
4539
4558
  let body;
4540
- if (prompt.kind === "verification") {
4559
+ if (expired && prompt.kind !== "notification") {
4560
+ const note = {
4561
+ id: `${prompt.userActionId}-expired`,
4562
+ message: prompt.kind === "verification" ? "Verification Request Expired" : "User Form Expired"
4563
+ };
4564
+ body = /* @__PURE__ */ jsx(NotificationInline, { notification: note });
4565
+ } else if (prompt.kind === "verification") {
4541
4566
  body = /* @__PURE__ */ jsx(
4542
4567
  VerificationInline,
4543
4568
  {
@@ -4577,10 +4602,23 @@ function UserActionInline({ prompt, onSubmit, onCancel, onResend }) {
4577
4602
  }
4578
4603
  var SCROLL_THRESHOLD2 = 100;
4579
4604
  var USER_SCROLL_UP_EPSILON = 4;
4605
+ var PROMPT_KEY_SEPARATOR = "";
4606
+ function getPromptSlotKey(prompt) {
4607
+ return prompt.toolCallId || prompt.userActionId;
4608
+ }
4609
+ function getPromptViewKey(prompt) {
4610
+ return [
4611
+ getPromptSlotKey(prompt),
4612
+ prompt.userActionId,
4613
+ prompt.subAction ?? "",
4614
+ prompt.expirySeconds ?? "",
4615
+ prompt.message ?? ""
4616
+ ].join(PROMPT_KEY_SEPARATOR);
4617
+ }
4580
4618
  var MessageListV2 = forwardRef(
4581
4619
  function MessageListV22({
4582
4620
  messages,
4583
- isStreaming: _isStreaming = false,
4621
+ isStreaming = false,
4584
4622
  onEditUserMessage,
4585
4623
  onRetryUserMessage,
4586
4624
  onImageClick,
@@ -4602,6 +4640,7 @@ var MessageListV2 = forwardRef(
4602
4640
  const scrollInnerRef = useRef(null);
4603
4641
  const isNearBottomRef = useRef(true);
4604
4642
  const [showScrollBtn, setShowScrollBtn] = useState(false);
4643
+ const [expiredPromptViewState, setExpiredPromptViewState] = useState({});
4605
4644
  const prevCountRef = useRef(messages.length);
4606
4645
  const pauseStickUntilUserMessageRef = useRef(false);
4607
4646
  const followingBottomRef = useRef(true);
@@ -4612,6 +4651,81 @@ var MessageListV2 = forwardRef(
4612
4651
  if (!el) return 0;
4613
4652
  return el.scrollHeight - el.scrollTop - el.clientHeight;
4614
4653
  }, []);
4654
+ const messageActivityFingerprint = useMemo(() => {
4655
+ const last = messages[messages.length - 1];
4656
+ const promptFingerprint = (userActionPrompts ?? []).map((prompt) => `${getPromptViewKey(prompt)}:${prompt.status}`).join(PROMPT_KEY_SEPARATOR);
4657
+ const notificationFingerprint = (notifications ?? []).map((notification) => notification.id).join(PROMPT_KEY_SEPARATOR);
4658
+ if (!last) {
4659
+ return [
4660
+ 0,
4661
+ isStreaming ? "streaming" : "idle",
4662
+ promptFingerprint,
4663
+ notificationFingerprint
4664
+ ].join(PROMPT_KEY_SEPARATOR);
4665
+ }
4666
+ return [
4667
+ messages.length,
4668
+ isStreaming ? "streaming" : "idle",
4669
+ last.id,
4670
+ last.role,
4671
+ last.content ?? "",
4672
+ last.isStreaming ? "streaming" : "done",
4673
+ last.streamProgress ?? "",
4674
+ last.steps?.length ?? 0,
4675
+ last.errorDetails ?? "",
4676
+ promptFingerprint,
4677
+ notificationFingerprint
4678
+ ].join(PROMPT_KEY_SEPARATOR);
4679
+ }, [isStreaming, messages, notifications, userActionPrompts]);
4680
+ const handleUserActionExpired = useCallback(
4681
+ (promptKey) => {
4682
+ setExpiredPromptViewState((prev) => {
4683
+ if (prev[promptKey]) return prev;
4684
+ return {
4685
+ ...prev,
4686
+ [promptKey]: { baseline: messageActivityFingerprint, hidden: false }
4687
+ };
4688
+ });
4689
+ },
4690
+ [messageActivityFingerprint]
4691
+ );
4692
+ useEffect(() => {
4693
+ setExpiredPromptViewState((prev) => {
4694
+ let changed = false;
4695
+ const next = {};
4696
+ for (const [key, state] of Object.entries(prev)) {
4697
+ if (!state.hidden && state.baseline !== messageActivityFingerprint) {
4698
+ next[key] = { ...state, hidden: true };
4699
+ changed = true;
4700
+ } else {
4701
+ next[key] = state;
4702
+ }
4703
+ }
4704
+ return changed ? next : prev;
4705
+ });
4706
+ }, [messageActivityFingerprint]);
4707
+ useEffect(() => {
4708
+ const livePromptKeys = new Set((userActionPrompts ?? []).map(getPromptViewKey));
4709
+ setExpiredPromptViewState((prev) => {
4710
+ let changed = false;
4711
+ const next = {};
4712
+ for (const [key, state] of Object.entries(prev)) {
4713
+ if (livePromptKeys.has(key)) {
4714
+ next[key] = state;
4715
+ } else {
4716
+ changed = true;
4717
+ }
4718
+ }
4719
+ return changed ? next : prev;
4720
+ });
4721
+ }, [userActionPrompts]);
4722
+ const visibleUserActionPrompts = useMemo(
4723
+ () => userActionPrompts?.filter((prompt) => {
4724
+ const promptKey = getPromptViewKey(prompt);
4725
+ return !expiredPromptViewState[promptKey]?.hidden;
4726
+ }),
4727
+ [expiredPromptViewState, userActionPrompts]
4728
+ );
4615
4729
  const pinToBottom = useCallback((behavior = "instant") => {
4616
4730
  const el = scrollRef.current;
4617
4731
  if (!el) return;
@@ -4748,16 +4862,20 @@ var MessageListV2 = forwardRef(
4748
4862
  },
4749
4863
  note.id
4750
4864
  )),
4751
- userActionPrompts?.map((prompt) => /* @__PURE__ */ jsx(
4752
- UserActionInline,
4753
- {
4754
- prompt,
4755
- onSubmit: onSubmitUserAction ?? noop,
4756
- onCancel: onCancelUserAction ?? noop,
4757
- onResend: onResendUserAction ?? noop
4758
- },
4759
- prompt.toolCallId || prompt.userActionId
4760
- ))
4865
+ visibleUserActionPrompts?.map((prompt) => {
4866
+ const promptKey = getPromptViewKey(prompt);
4867
+ return /* @__PURE__ */ jsx(
4868
+ UserActionInline,
4869
+ {
4870
+ prompt,
4871
+ onSubmit: onSubmitUserAction ?? noop,
4872
+ onCancel: onCancelUserAction ?? noop,
4873
+ onResend: onResendUserAction ?? noop,
4874
+ onExpired: () => handleUserActionExpired(promptKey)
4875
+ },
4876
+ promptKey
4877
+ );
4878
+ })
4761
4879
  ]
4762
4880
  }
4763
4881
  )