aamp-openclaw-plugin 0.1.32 → 0.1.34

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.js CHANGED
@@ -2535,6 +2535,8 @@ function baseUrl(aampHost) {
2535
2535
  }
2536
2536
  var pendingTasks = /* @__PURE__ */ new Map();
2537
2537
  var activeTaskStreams = /* @__PURE__ */ new Map();
2538
+ var sessionTaskBindings = /* @__PURE__ */ new Map();
2539
+ var taskVisibleStreamState = /* @__PURE__ */ new Map();
2538
2540
  var terminalTaskIds = new Set(loadTaskState(defaultTaskStatePath()).terminalTaskIds ?? []);
2539
2541
  var AAMP_SESSION_PREFIX = "aamp:";
2540
2542
  var DEFAULT_OPENCLAW_AGENT_ID = "main";
@@ -2554,6 +2556,7 @@ var lastLoggedTransportMode = "disconnected";
2554
2556
  var reconcileTimer = null;
2555
2557
  var transportMonitorTimer = null;
2556
2558
  var historicalReconcileCompleted = false;
2559
+ var stopAgentEventSubscription = null;
2557
2560
  var channelRuntime = null;
2558
2561
  var channelCfg = null;
2559
2562
  async function ensureTaskStream(task) {
@@ -2620,6 +2623,12 @@ function logTransportState(api, mode, email, previousMode) {
2620
2623
  function isSyntheticPendingKey(taskKey) {
2621
2624
  return taskKey.startsWith("result:") || taskKey.startsWith("help:");
2622
2625
  }
2626
+ function isTaskAwaitingHelpReply(task) {
2627
+ return task.awaitingHelpReply === true;
2628
+ }
2629
+ function isActionablePendingTask(taskKey, task) {
2630
+ return !isSyntheticPendingKey(taskKey) && !isTaskAwaitingHelpReply(task);
2631
+ }
2623
2632
  function normalizeOpenClawAgentId(value) {
2624
2633
  const trimmed = typeof value === "string" ? value.trim() : "";
2625
2634
  if (!trimmed)
@@ -2664,6 +2673,112 @@ function buildAampTaskSessionKey(taskId, config) {
2664
2673
  function buildAampWakeSessionKey(kind, id) {
2665
2674
  return `${AAMP_SESSION_PREFIX}wake:${kind}:${id}`;
2666
2675
  }
2676
+ function resolvePendingKeyFromSessionKey(sessionKey) {
2677
+ if (typeof sessionKey !== "string")
2678
+ return void 0;
2679
+ const normalized = stripOpenClawAgentScope(sessionKey).trim();
2680
+ if (!normalized)
2681
+ return void 0;
2682
+ const parts = normalized.split(":");
2683
+ if (parts[0]?.toLowerCase() !== "aamp")
2684
+ return void 0;
2685
+ if (parts[1]?.toLowerCase() === "wake") {
2686
+ const kind = parts[2]?.toLowerCase();
2687
+ const id = parts.slice(3).join(":").trim();
2688
+ if (!id)
2689
+ return void 0;
2690
+ if (kind === "task")
2691
+ return id;
2692
+ if (kind === "result" || kind === "help")
2693
+ return `${kind}:${id}`;
2694
+ return void 0;
2695
+ }
2696
+ if (parts[1]?.toLowerCase() === "default") {
2697
+ const kind = parts[2]?.toLowerCase();
2698
+ const id = parts.slice(3).join(":").trim();
2699
+ if (!id)
2700
+ return void 0;
2701
+ if (kind === "task")
2702
+ return id;
2703
+ if (kind === "result" || kind === "help")
2704
+ return `${kind}:${id}`;
2705
+ }
2706
+ return void 0;
2707
+ }
2708
+ function normalizeBoundSessionKey(sessionKey) {
2709
+ if (typeof sessionKey !== "string")
2710
+ return void 0;
2711
+ const normalized = stripOpenClawAgentScope(sessionKey).trim();
2712
+ return normalized || void 0;
2713
+ }
2714
+ function bindSessionToTask(sessionKey, taskId) {
2715
+ const normalized = normalizeBoundSessionKey(sessionKey);
2716
+ if (!normalized)
2717
+ return;
2718
+ if (!taskId) {
2719
+ sessionTaskBindings.delete(normalized);
2720
+ return;
2721
+ }
2722
+ sessionTaskBindings.set(normalized, taskId);
2723
+ }
2724
+ function resolveStreamTaskId(sessionKey) {
2725
+ const pendingKey = resolvePendingKeyFromSessionKey(sessionKey);
2726
+ if (pendingKey && !isSyntheticPendingKey(pendingKey))
2727
+ return pendingKey;
2728
+ const normalized = normalizeBoundSessionKey(sessionKey);
2729
+ if (!normalized)
2730
+ return void 0;
2731
+ const taskId = sessionTaskBindings.get(normalized);
2732
+ if (!taskId)
2733
+ return void 0;
2734
+ if (!pendingTasks.has(taskId) && !activeTaskStreams.has(taskId)) {
2735
+ sessionTaskBindings.delete(normalized);
2736
+ return void 0;
2737
+ }
2738
+ return taskId;
2739
+ }
2740
+ function forgetTaskStreamContext(taskId) {
2741
+ taskVisibleStreamState.delete(taskId);
2742
+ for (const [sessionKey, boundTaskId] of sessionTaskBindings) {
2743
+ if (boundTaskId === taskId) {
2744
+ sessionTaskBindings.delete(sessionKey);
2745
+ }
2746
+ }
2747
+ }
2748
+ async function handleAgentVisibleTextEvent(event) {
2749
+ if (event.stream !== "assistant")
2750
+ return;
2751
+ const taskId = resolveStreamTaskId(event.sessionKey);
2752
+ if (!taskId || !activeTaskStreams.has(taskId))
2753
+ return;
2754
+ const payload = event.data && typeof event.data === "object" ? event.data : {};
2755
+ const fullText = typeof payload.text === "string" ? payload.text : void 0;
2756
+ const deltaText = typeof payload.delta === "string" ? payload.delta : void 0;
2757
+ if (!fullText && !deltaText)
2758
+ return;
2759
+ const runId = typeof event.runId === "string" && event.runId.trim() ? event.runId : "__default__";
2760
+ const previousState = taskVisibleStreamState.get(taskId);
2761
+ const previousText = previousState?.runId === runId ? previousState.text : "";
2762
+ let nextText = previousText;
2763
+ let nextDelta = "";
2764
+ if (typeof fullText === "string") {
2765
+ nextText = fullText;
2766
+ if (fullText.startsWith(previousText)) {
2767
+ nextDelta = fullText.slice(previousText.length);
2768
+ } else if (!previousText) {
2769
+ nextDelta = fullText;
2770
+ } else if (deltaText) {
2771
+ nextDelta = deltaText;
2772
+ }
2773
+ } else if (deltaText) {
2774
+ nextText = `${previousText}${deltaText}`;
2775
+ nextDelta = deltaText;
2776
+ }
2777
+ taskVisibleStreamState.set(taskId, { runId, text: nextText });
2778
+ if (!nextDelta)
2779
+ return;
2780
+ await appendTaskStream(taskId, "text.delta", { text: nextDelta });
2781
+ }
2667
2782
  function saveTerminalTaskIds() {
2668
2783
  saveTaskState({ terminalTaskIds: [...terminalTaskIds] }, defaultTaskStatePath());
2669
2784
  }
@@ -2706,7 +2821,7 @@ function nextPendingEntry() {
2706
2821
  if (notifications.length > 0) {
2707
2822
  return notifications.sort((a, b) => new Date(a[1].receivedAt).getTime() - new Date(b[1].receivedAt).getTime())[0];
2708
2823
  }
2709
- return entries.filter(([key]) => !key.startsWith("result:") && !key.startsWith("help:")).sort((a, b) => {
2824
+ return entries.filter(([key, task]) => isActionablePendingTask(key, task)).sort((a, b) => {
2710
2825
  const rankDiff = priorityRank(a[1].priority) - priorityRank(b[1].priority);
2711
2826
  if (rankDiff !== 0)
2712
2827
  return rankDiff;
@@ -2875,6 +2990,22 @@ var src_default = {
2875
2990
  api.logger.warn(`[AAMP] Could not trigger heartbeat for ${label}: ${err.message}`);
2876
2991
  }
2877
2992
  }
2993
+ if (stopAgentEventSubscription) {
2994
+ try {
2995
+ stopAgentEventSubscription();
2996
+ } catch {
2997
+ }
2998
+ stopAgentEventSubscription = null;
2999
+ }
3000
+ const onAgentEvent = api.runtime?.events?.onAgentEvent;
3001
+ if (typeof onAgentEvent === "function") {
3002
+ const unsubscribe = onAgentEvent((event) => {
3003
+ void handleAgentVisibleTextEvent(event).catch((err) => {
3004
+ api.logger.warn(`[AAMP] Failed to mirror assistant stream: ${err.message}`);
3005
+ });
3006
+ });
3007
+ stopAgentEventSubscription = typeof unsubscribe === "function" ? unsubscribe : null;
3008
+ }
2878
3009
  function getConfiguredCardText() {
2879
3010
  const inline = cfg.cardText?.trim();
2880
3011
  if (inline)
@@ -3043,6 +3174,7 @@ var src_default = {
3043
3174
  pendingTasks.delete(`help:${cancel.taskId}`);
3044
3175
  dispatchedSubtasks.delete(cancel.taskId);
3045
3176
  waitingDispatches.delete(cancel.taskId);
3177
+ forgetTaskStreamContext(cancel.taskId);
3046
3178
  rememberTerminalTask(cancel.taskId);
3047
3179
  void closeTaskStream(cancel.taskId, { reason: "task.cancel" }).catch(() => {
3048
3180
  });
@@ -3375,6 +3507,13 @@ ${notifyBody?.bodyText ?? help.question}`;
3375
3507
  } catch {
3376
3508
  }
3377
3509
  }
3510
+ if (stopAgentEventSubscription) {
3511
+ try {
3512
+ stopAgentEventSubscription();
3513
+ } catch {
3514
+ }
3515
+ stopAgentEventSubscription = null;
3516
+ }
3378
3517
  }
3379
3518
  });
3380
3519
  api.on("gateway_start", () => {
@@ -3421,7 +3560,17 @@ ${notifyBody?.bodyText ?? help.question}`;
3421
3560
  }
3422
3561
  if (pendingTasks.size === 0)
3423
3562
  return {};
3424
- const nextEntry = nextPendingEntry();
3563
+ const targetedPendingKey = resolvePendingKeyFromSessionKey(ctx?.sessionKey);
3564
+ const targetedEntry = targetedPendingKey ? (() => {
3565
+ const targetedTask = pendingTasks.get(targetedPendingKey);
3566
+ if (!targetedTask)
3567
+ return void 0;
3568
+ if (!isSyntheticPendingKey(targetedPendingKey) && isTaskAwaitingHelpReply(targetedTask)) {
3569
+ return void 0;
3570
+ }
3571
+ return [targetedPendingKey, targetedTask];
3572
+ })() : void 0;
3573
+ const nextEntry = targetedPendingKey ? targetedEntry : nextPendingEntry();
3425
3574
  if (!nextEntry)
3426
3575
  return {};
3427
3576
  const [taskKey, task] = nextEntry;
@@ -3429,12 +3578,17 @@ ${notifyBody?.bodyText ?? help.question}`;
3429
3578
  if (isNotification && taskKey) {
3430
3579
  pendingTasks.delete(taskKey);
3431
3580
  }
3432
- const actionableTasks = [...pendingTasks.entries()].filter(([key]) => !key.startsWith("result:") && !key.startsWith("help:")).map(([, t]) => t).sort((a, b) => {
3581
+ const actionableTasks = [...pendingTasks.entries()].filter(([key, pendingTask]) => isActionablePendingTask(key, pendingTask)).map(([, t]) => t).sort((a, b) => {
3433
3582
  const rankDiff = priorityRank(a.priority) - priorityRank(b.priority);
3434
3583
  if (rankDiff !== 0)
3435
3584
  return rankDiff;
3436
3585
  return new Date(a.receivedAt).getTime() - new Date(b.receivedAt).getTime();
3437
3586
  });
3587
+ bindSessionToTask(
3588
+ ctx?.sessionKey,
3589
+ isNotification ? actionableTasks.length === 1 ? actionableTasks[0]?.taskId : void 0 : task.taskId
3590
+ );
3591
+ const otherActionableTasks = actionableTasks.filter((pendingTask) => pendingTask.taskId !== task.taskId);
3438
3592
  const hasAttachmentInfo = isNotification && (task.bodyText?.includes("aamp_download_attachment") ?? false);
3439
3593
  const actionRequiredSection = isNotification && actionableTasks.length > 0 ? [
3440
3594
  ``,
@@ -3468,8 +3622,8 @@ ${notifyBody?.bodyText ?? help.question}`;
3468
3622
  task.bodyText ? `
3469
3623
  ${task.bodyText}` : "",
3470
3624
  actionRequiredSection,
3471
- pendingTasks.size > 1 ? `
3472
- (+${pendingTasks.size - 1} more items queued)` : ""
3625
+ otherActionableTasks.length > 0 ? `
3626
+ (+${otherActionableTasks.length} more tasks queued)` : ""
3473
3627
  ] : [
3474
3628
  `## Pending AAMP Task (action required)`,
3475
3629
  ``,
@@ -3509,8 +3663,8 @@ ${task.bodyText}` : "",
3509
3663
  ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
3510
3664
  task.expiresAt ? `Expires: ${task.expiresAt}` : `Expires: none`,
3511
3665
  `Received: ${task.receivedAt}`,
3512
- pendingTasks.size > 1 ? `
3513
- (+${pendingTasks.size - 1} more tasks queued)` : ""
3666
+ otherActionableTasks.length > 0 ? `
3667
+ (+${otherActionableTasks.length} more tasks queued)` : ""
3514
3668
  ].filter(Boolean).join("\n");
3515
3669
  return { prependContext: lines };
3516
3670
  },
@@ -3657,14 +3811,17 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
3657
3811
  state: "completing",
3658
3812
  label: `Sending ${p.status} result`
3659
3813
  });
3660
- if (p.output) {
3661
- await appendTaskStream(task.taskId, "text.delta", { text: p.output });
3814
+ const streamedText = taskVisibleStreamState.get(task.taskId)?.text ?? "";
3815
+ const finalOutputDelta = streamedText && p.output.startsWith(streamedText) ? p.output.slice(streamedText.length) : p.output;
3816
+ if (finalOutputDelta) {
3817
+ await appendTaskStream(task.taskId, "text.delta", { text: finalOutputDelta });
3662
3818
  }
3663
3819
  await closeTaskStream(task.taskId, {
3664
3820
  reason: "task.result",
3665
3821
  status: p.status,
3666
3822
  ...p.errorMsg ? { error: p.errorMsg } : {}
3667
3823
  });
3824
+ forgetTaskStreamContext(task.taskId);
3668
3825
  await aampClient.sendResult({
3669
3826
  to: task.from,
3670
3827
  taskId: task.taskId,
@@ -3741,6 +3898,7 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
3741
3898
  await closeTaskStream(task.taskId, {
3742
3899
  reason: "task.help_needed"
3743
3900
  });
3901
+ forgetTaskStreamContext(task.taskId);
3744
3902
  await aampClient.sendHelp({
3745
3903
  to: task.from,
3746
3904
  taskId: task.taskId,
@@ -3749,12 +3907,16 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
3749
3907
  suggestedOptions: p.suggestedOptions ?? [],
3750
3908
  inReplyTo: task.messageId || void 0
3751
3909
  });
3910
+ pendingTasks.set(task.taskId, {
3911
+ ...task,
3912
+ awaitingHelpReply: true
3913
+ });
3752
3914
  api.logger.info(`[AAMP] \u2192 task.help_needed ${task.taskId}`);
3753
3915
  return {
3754
3916
  content: [
3755
3917
  {
3756
3918
  type: "text",
3757
- text: `Help request sent for task ${task.taskId}. The task remains pending until the dispatcher replies.`
3919
+ text: `Help request sent for task ${task.taskId}. The task is now suspended until the dispatcher replies.`
3758
3920
  }
3759
3921
  ]
3760
3922
  };
@@ -3774,7 +3936,7 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
3774
3936
  return rankDiff;
3775
3937
  return new Date(a.receivedAt).getTime() - new Date(b.receivedAt).getTime();
3776
3938
  }).map(
3777
- (t, i) => `${i + 1}. [${t.priority}] [${t.taskId}] "${t.title}"${t.bodyText ? `
3939
+ (t, i) => `${i + 1}. [${t.priority}] [${t.taskId}] "${t.title}"${isTaskAwaitingHelpReply(t) ? " (waiting for dispatcher reply)" : ""}${t.bodyText ? `
3778
3940
  Description: ${t.bodyText}` : ""} \u2014 from ${t.from} (received ${t.receivedAt})`
3779
3941
  );
3780
3942
  return {