@paymanai/payman-typescript-ask-sdk 4.0.25 → 4.0.27

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
@@ -201,6 +201,14 @@ type MessageDisplay = {
201
201
  isStreaming?: boolean;
202
202
  streamingContent?: string;
203
203
  currentMessage?: string;
204
+ /**
205
+ * True while work events (tool calls, LLM-call boundaries, keep-alive
206
+ * heartbeats) are arriving AFTER answer text already started streaming —
207
+ * e.g. the agent-builder streams an instant acknowledgment and then does
208
+ * its real work. Tells the UI to keep a live status line below the
209
+ * already-rendered text instead of showing just a frozen cursor.
210
+ */
211
+ hasTrailingActivity?: boolean;
204
212
  streamProgress?: StreamProgress;
205
213
  steps?: StreamingStep[];
206
214
  isCancelled?: boolean;
@@ -558,6 +566,16 @@ type V2EventProcessorState = {
558
566
  currentExecutingStepId?: string;
559
567
  /** Server-computed wall-clock duration captured from RUN_COMPLETED's `totalTimeMs`. */
560
568
  totalElapsedMs?: number;
569
+ /**
570
+ * True once work events (tool calls, LLM-call boundaries, keep-alive
571
+ * heartbeats) arrive AFTER answer text has started streaming. The
572
+ * agent-builder streams an instant acknowledgment first and then does
573
+ * its real work — this tells the UI to keep a live status line below
574
+ * the already-rendered text. Plain answer streaming (formatter deltas
575
+ * with no trailing work) never sets it. Sticky until the run ends so
576
+ * the status line doesn't flicker during think-gaps between tools.
577
+ */
578
+ trailingActivity: boolean;
561
579
  };
562
580
  declare function createInitialV2State(): V2EventProcessorState;
563
581
  declare function processStreamEventV2(rawEvent: StreamEvent, state: V2EventProcessorState): V2EventProcessorState;
package/dist/index.d.ts CHANGED
@@ -201,6 +201,14 @@ type MessageDisplay = {
201
201
  isStreaming?: boolean;
202
202
  streamingContent?: string;
203
203
  currentMessage?: string;
204
+ /**
205
+ * True while work events (tool calls, LLM-call boundaries, keep-alive
206
+ * heartbeats) are arriving AFTER answer text already started streaming —
207
+ * e.g. the agent-builder streams an instant acknowledgment and then does
208
+ * its real work. Tells the UI to keep a live status line below the
209
+ * already-rendered text instead of showing just a frozen cursor.
210
+ */
211
+ hasTrailingActivity?: boolean;
204
212
  streamProgress?: StreamProgress;
205
213
  steps?: StreamingStep[];
206
214
  isCancelled?: boolean;
@@ -558,6 +566,16 @@ type V2EventProcessorState = {
558
566
  currentExecutingStepId?: string;
559
567
  /** Server-computed wall-clock duration captured from RUN_COMPLETED's `totalTimeMs`. */
560
568
  totalElapsedMs?: number;
569
+ /**
570
+ * True once work events (tool calls, LLM-call boundaries, keep-alive
571
+ * heartbeats) arrive AFTER answer text has started streaming. The
572
+ * agent-builder streams an instant acknowledgment first and then does
573
+ * its real work — this tells the UI to keep a live status line below
574
+ * the already-rendered text. Plain answer streaming (formatter deltas
575
+ * with no trailing work) never sets it. Sticky until the run ends so
576
+ * the status line doesn't flicker during think-gaps between tools.
577
+ */
578
+ trailingActivity: boolean;
561
579
  };
562
580
  declare function createInitialV2State(): V2EventProcessorState;
563
581
  declare function processStreamEventV2(rawEvent: StreamEvent, state: V2EventProcessorState): V2EventProcessorState;
package/dist/index.js CHANGED
@@ -276,6 +276,8 @@ function getEventMessage(event) {
276
276
  }
277
277
  case "RUN_IN_PROGRESS":
278
278
  return event.partialText || "Thinking...";
279
+ case "LLM_CALL_STARTED":
280
+ return event.description || "";
279
281
  case "RUN_COMPLETED":
280
282
  return "Agent run completed";
281
283
  case "RUN_FAILED":
@@ -413,7 +415,8 @@ function createInitialV2State() {
413
415
  finalData: void 0,
414
416
  steps: [],
415
417
  stepCounter: 0,
416
- currentExecutingStepId: void 0
418
+ currentExecutingStepId: void 0,
419
+ trailingActivity: false
417
420
  };
418
421
  }
419
422
  function upsertUserAction(state, req) {
@@ -435,6 +438,7 @@ function processStreamEventV2(rawEvent, state) {
435
438
  if (typeof eventType === "string" && eventType.toUpperCase() === "KEEP_ALIVE") {
436
439
  if (event.executionId) state.executionId = event.executionId;
437
440
  if (event.sessionId) state.sessionId = event.sessionId;
441
+ if (state.finalResponse) state.trailingActivity = true;
438
442
  const description = typeof event.description === "string" ? event.description : "";
439
443
  if (description) {
440
444
  for (let i = state.steps.length - 1; i >= 0; i--) {
@@ -469,7 +473,12 @@ function processStreamEventV2(rawEvent, state) {
469
473
  state.lastEventType = eventType;
470
474
  break;
471
475
  }
476
+ case "LLM_CALL_STARTED":
477
+ if (state.finalResponse) state.trailingActivity = true;
478
+ state.lastEventType = eventType;
479
+ break;
472
480
  case "INTENT_STARTED": {
481
+ if (state.finalResponse) state.trailingActivity = true;
473
482
  const stepId = `step-${state.stepCounter++}`;
474
483
  state.steps.push({
475
484
  id: stepId,
@@ -485,6 +494,7 @@ function processStreamEventV2(rawEvent, state) {
485
494
  break;
486
495
  }
487
496
  case "INTENT_COMPLETED": {
497
+ if (state.finalResponse) state.trailingActivity = true;
488
498
  const intentStep = state.steps.find((s) => s.eventType === "INTENT_STARTED" && s.status === "in_progress");
489
499
  if (intentStep) {
490
500
  intentStep.status = "completed";
@@ -516,6 +526,7 @@ function processStreamEventV2(rawEvent, state) {
516
526
  step.status = "completed";
517
527
  }
518
528
  });
529
+ state.trailingActivity = false;
519
530
  state.lastEventType = eventType;
520
531
  break;
521
532
  }
@@ -587,6 +598,7 @@ function processStreamEventV2(rawEvent, state) {
587
598
  case "WORKFLOW_ERROR":
588
599
  state.hasError = true;
589
600
  state.errorMessage = event.errorMessage || event.message || "Workflow error";
601
+ state.trailingActivity = false;
590
602
  state.lastEventType = eventType;
591
603
  break;
592
604
  // ---- K2 pipeline stage lifecycle events ----
@@ -881,7 +893,9 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
881
893
  const latestUsefulStep = [...state.steps].reverse().find(
882
894
  (s) => s.message && !isBlandStatus(s.message)
883
895
  );
884
- const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
896
+ const eventTypeUpper = typeof event.eventType === "string" ? event.eventType.toUpperCase() : "";
897
+ const liveEventLabel = eventTypeUpper === "KEEP_ALIVE" || eventTypeUpper === "LLM_CALL_STARTED" ? useful(getEventMessage(event)) : void 0;
898
+ const currentMessage = useful(activeStep?.message) ?? useful(lastInProgressStep?.message) ?? liveEventLabel ?? latestUsefulStep?.message ?? useful(getEventMessage(event)) ?? // Last-resort: every candidate is bland (very first event,
885
899
  // nothing useful seen yet). Render the bland label so the
886
900
  // bubble isn't blank.
887
901
  activeStep?.message ?? lastInProgressStep?.message ?? getEventMessage(event);
@@ -906,6 +920,7 @@ function useStreamManagerV2(config, callbacks, setMessages, setIsWaitingForRespo
906
920
  streamingContent: state.finalResponse,
907
921
  content: "",
908
922
  currentMessage,
923
+ hasTrailingActivity: state.trailingActivity,
909
924
  streamProgress: "processing",
910
925
  isError: false,
911
926
  steps: [...state.steps],
@@ -1133,6 +1148,9 @@ function isUserActionExpired(prompt) {
1133
1148
 
1134
1149
  // src/hooks/useChatV2.ts
1135
1150
  var EMPTY_USER_ACTION_STATE2 = { prompts: [], notifications: [] };
1151
+ function promptSlotKey(p) {
1152
+ return p.toolCallId || p.userActionId;
1153
+ }
1136
1154
  function upsertPrompt(prompts, req) {
1137
1155
  const idx = prompts.findIndex(
1138
1156
  (p) => req.toolCallId ? p.toolCallId === req.toolCallId : p.userActionId === req.userActionId
@@ -1179,6 +1197,7 @@ function useChatV2(config, callbacks = {}) {
1179
1197
  configRef.current = config;
1180
1198
  const messagesRef = react.useRef(messages);
1181
1199
  messagesRef.current = messages;
1200
+ const pendingSubmissionsRef = react.useRef(/* @__PURE__ */ new Map());
1182
1201
  const storeAwareSetMessages = react.useCallback(
1183
1202
  (updater) => {
1184
1203
  const streamUserId = streamUserIdRef.current;
@@ -1213,6 +1232,8 @@ function useChatV2(config, callbacks = {}) {
1213
1232
  if (!config.userId) return EMPTY_USER_ACTION_STATE2;
1214
1233
  return activeStreamStore.get(config.userId)?.userActionState ?? EMPTY_USER_ACTION_STATE2;
1215
1234
  });
1235
+ const userActionStateRef = react.useRef(userActionState);
1236
+ userActionStateRef.current = userActionState;
1216
1237
  const storeAwareSetUserActionState = react.useCallback(
1217
1238
  (updater) => {
1218
1239
  const streamUserId = streamUserIdRef.current;
@@ -1240,6 +1261,12 @@ function useChatV2(config, callbacks = {}) {
1240
1261
  onExecutionTraceClick: (data) => callbacksRef.current.onExecutionTraceClick?.(data),
1241
1262
  onSessionIdChange: (sessionId) => callbacksRef.current.onSessionIdChange?.(sessionId),
1242
1263
  onUserActionRequired: (request) => {
1264
+ const requestSlotKey = promptSlotKey(request);
1265
+ for (const [pendingId, slotKey] of pendingSubmissionsRef.current) {
1266
+ if (slotKey === requestSlotKey) {
1267
+ pendingSubmissionsRef.current.delete(pendingId);
1268
+ }
1269
+ }
1243
1270
  storeAwareSetUserActionState((prev) => ({
1244
1271
  ...prev,
1245
1272
  prompts: upsertPrompt(prev.prompts, request)
@@ -1251,6 +1278,28 @@ function useChatV2(config, callbacks = {}) {
1251
1278
  (prev) => prev.notifications.some((n) => n.id === notification.id) ? prev : { ...prev, notifications: [...prev.notifications, notification] }
1252
1279
  );
1253
1280
  callbacksRef.current.onUserNotification?.(notification);
1281
+ },
1282
+ // A submitted-but-unconfirmed prompt (see `submitUserAction`) is only
1283
+ // safe to drop once the stream has demonstrably moved on. `onEvent`
1284
+ // in useStreamManagerV2 calls `onStepsUpdate` for *every* processed
1285
+ // stream event, unconditionally — so the first time this fires
1286
+ // after a submission, the event in hand is either the rejection
1287
+ // retry (a fresh `USER_ACTION_REQUIRED`, which the handler above
1288
+ // already deleted this pending entry for, earlier in the very same
1289
+ // event) or genuine forward progress (RUN_IN_PROGRESS, a tool call,
1290
+ // content deltas, RUN_COMPLETED, ...). Anything still pending by
1291
+ // the time we get here therefore means the run resumed normally —
1292
+ // clear it.
1293
+ onStepsUpdate: (steps) => {
1294
+ if (pendingSubmissionsRef.current.size > 0) {
1295
+ const resolved = [...pendingSubmissionsRef.current.keys()];
1296
+ pendingSubmissionsRef.current.clear();
1297
+ storeAwareSetUserActionState((prev) => ({
1298
+ ...prev,
1299
+ prompts: prev.prompts.filter((p) => !resolved.includes(p.userActionId))
1300
+ }));
1301
+ }
1302
+ callbacksRef.current.onStepsUpdate?.(steps);
1254
1303
  }
1255
1304
  // eslint-disable-next-line react-hooks/exhaustive-deps
1256
1305
  }), []);
@@ -1386,6 +1435,7 @@ function useChatV2(config, callbacks = {}) {
1386
1435
  [storeAwareSetUserActionState]
1387
1436
  );
1388
1437
  const removePrompt = react.useCallback((userActionId) => {
1438
+ pendingSubmissionsRef.current.delete(userActionId);
1389
1439
  storeAwareSetUserActionState((prev) => ({
1390
1440
  ...prev,
1391
1441
  prompts: prev.prompts.filter((p) => p.userActionId !== userActionId)
@@ -1396,7 +1446,10 @@ function useChatV2(config, callbacks = {}) {
1396
1446
  setPromptStatus(userActionId, "submitting");
1397
1447
  try {
1398
1448
  await submitUserAction(configRef.current, userActionId, content);
1399
- removePrompt(userActionId);
1449
+ const prompt = userActionStateRef.current.prompts.find(
1450
+ (p) => p.userActionId === userActionId
1451
+ );
1452
+ pendingSubmissionsRef.current.set(userActionId, prompt ? promptSlotKey(prompt) : userActionId);
1400
1453
  } catch (error) {
1401
1454
  if (error instanceof UserActionStaleError) {
1402
1455
  setPromptStatus(userActionId, "stale");
@@ -1407,7 +1460,7 @@ function useChatV2(config, callbacks = {}) {
1407
1460
  throw error;
1408
1461
  }
1409
1462
  },
1410
- [removePrompt, setPromptStatus]
1463
+ [setPromptStatus]
1411
1464
  );
1412
1465
  const cancelUserAction2 = react.useCallback(
1413
1466
  async (userActionId) => {