aamp-openclaw-plugin 0.1.30 → 0.1.31

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
@@ -1833,6 +1833,7 @@ var AampClient = class _AampClient extends TinyEmitter {
1833
1833
  jmapClient;
1834
1834
  smtpSender;
1835
1835
  config;
1836
+ streamAppendQueues = /* @__PURE__ */ new Map();
1836
1837
  constructor(config) {
1837
1838
  super();
1838
1839
  this.config = config;
@@ -2162,7 +2163,15 @@ var AampClient = class _AampClient extends TinyEmitter {
2162
2163
  }
2163
2164
  return res.json();
2164
2165
  }
2165
- async appendStreamEvent(opts) {
2166
+ getStreamAppendQueue(streamId) {
2167
+ let queue = this.streamAppendQueues.get(streamId);
2168
+ if (!queue) {
2169
+ queue = { running: false, operations: [] };
2170
+ this.streamAppendQueues.set(streamId, queue);
2171
+ }
2172
+ return queue;
2173
+ }
2174
+ async dispatchStreamAppend(opts) {
2166
2175
  const stream = await this.resolveStreamCapability();
2167
2176
  const res = await _AampClient.callDiscoveredApi(this.config.baseUrl, {
2168
2177
  action: stream.appendAction ?? "aamp.stream.append",
@@ -2176,7 +2185,98 @@ var AampClient = class _AampClient extends TinyEmitter {
2176
2185
  }
2177
2186
  return res.json();
2178
2187
  }
2188
+ enqueueStreamAppend(streamId, operation) {
2189
+ const queue = this.getStreamAppendQueue(streamId);
2190
+ queue.operations.push(operation);
2191
+ void this.drainStreamAppendQueue(streamId);
2192
+ }
2193
+ async drainStreamAppendQueue(streamId) {
2194
+ const queue = this.streamAppendQueues.get(streamId);
2195
+ if (!queue || queue.running)
2196
+ return;
2197
+ queue.running = true;
2198
+ try {
2199
+ while (queue.operations.length) {
2200
+ const operation = queue.operations.shift();
2201
+ if (!operation)
2202
+ continue;
2203
+ if (operation.kind === "text-delta-batch") {
2204
+ try {
2205
+ const event = await this.dispatchStreamAppend({
2206
+ streamId,
2207
+ type: "text.delta",
2208
+ payload: {
2209
+ ...operation.payload,
2210
+ text: operation.text
2211
+ }
2212
+ });
2213
+ for (const resolve of operation.resolvers)
2214
+ resolve(event);
2215
+ } catch (error) {
2216
+ for (const reject of operation.rejecters)
2217
+ reject(error);
2218
+ }
2219
+ continue;
2220
+ }
2221
+ try {
2222
+ const event = await this.dispatchStreamAppend(operation.opts);
2223
+ operation.resolve(event);
2224
+ } catch (error) {
2225
+ operation.reject(error);
2226
+ }
2227
+ }
2228
+ } finally {
2229
+ queue.running = false;
2230
+ if (queue.operations.length === 0) {
2231
+ this.streamAppendQueues.delete(streamId);
2232
+ }
2233
+ }
2234
+ }
2235
+ async flushStreamAppendQueue(streamId) {
2236
+ while (true) {
2237
+ const queue = this.streamAppendQueues.get(streamId);
2238
+ if (!queue)
2239
+ return;
2240
+ if (!queue.running && queue.operations.length === 0) {
2241
+ this.streamAppendQueues.delete(streamId);
2242
+ return;
2243
+ }
2244
+ await new Promise((resolve) => setTimeout(resolve, 0));
2245
+ }
2246
+ }
2247
+ async appendStreamEvent(opts) {
2248
+ if (opts.type === "text.delta" && typeof opts.payload.text === "string") {
2249
+ return await new Promise((resolve, reject) => {
2250
+ const queue = this.getStreamAppendQueue(opts.streamId);
2251
+ const lastOperation = queue.operations.at(-1);
2252
+ if (lastOperation?.kind === "text-delta-batch") {
2253
+ lastOperation.text += String(opts.payload.text ?? "");
2254
+ lastOperation.resolvers.push(resolve);
2255
+ lastOperation.rejecters.push(reject);
2256
+ return;
2257
+ }
2258
+ this.enqueueStreamAppend(opts.streamId, {
2259
+ kind: "text-delta-batch",
2260
+ text: String(opts.payload.text ?? ""),
2261
+ payload: {
2262
+ ...opts.payload
2263
+ },
2264
+ resolvers: [resolve],
2265
+ rejecters: [reject]
2266
+ });
2267
+ });
2268
+ }
2269
+ return await new Promise((resolve, reject) => {
2270
+ this.enqueueStreamAppend(opts.streamId, {
2271
+ kind: "single-event",
2272
+ opts,
2273
+ resolve,
2274
+ reject
2275
+ });
2276
+ });
2277
+ }
2179
2278
  async closeStream(opts) {
2279
+ await this.flushStreamAppendQueue(opts.streamId);
2180
2280
  const stream = await this.resolveStreamCapability();
2181
2281
  const res = await _AampClient.callDiscoveredApi(this.config.baseUrl, {
2182
2282
  action: stream.closeAction ?? "aamp.stream.close",
@@ -2436,6 +2536,7 @@ function baseUrl(aampHost) {
2436
2536
  var pendingTasks = /* @__PURE__ */ new Map();
2437
2537
  var activeTaskStreams = /* @__PURE__ */ new Map();
2438
2538
  var terminalTaskIds = new Set(loadTaskState(defaultTaskStatePath()).terminalTaskIds ?? []);
2539
+ var AAMP_SESSION_PREFIX = "aamp:";
2439
2540
  var dispatchedSubtasks = /* @__PURE__ */ new Map();
2440
2541
  var waitingDispatches = /* @__PURE__ */ new Map();
2441
2542
  var aampClient = null;
@@ -2447,7 +2548,6 @@ var lastLoggedTransportMode = "disconnected";
2447
2548
  var reconcileTimer = null;
2448
2549
  var transportMonitorTimer = null;
2449
2550
  var historicalReconcileCompleted = false;
2450
- var currentSessionKey = "agent:main:main";
2451
2551
  var channelRuntime = null;
2452
2552
  var channelCfg = null;
2453
2553
  async function ensureTaskStream(task) {
@@ -2514,6 +2614,12 @@ function logTransportState(api, mode, email, previousMode) {
2514
2614
  function isSyntheticPendingKey(taskKey) {
2515
2615
  return taskKey.startsWith("result:") || taskKey.startsWith("help:");
2516
2616
  }
2617
+ function isAampSessionKey(sessionKey) {
2618
+ return typeof sessionKey === "string" && sessionKey.startsWith(AAMP_SESSION_PREFIX);
2619
+ }
2620
+ function buildAampWakeSessionKey(kind, id) {
2621
+ return `${AAMP_SESSION_PREFIX}wake:${kind}:${id}`;
2622
+ }
2517
2623
  function saveTerminalTaskIds() {
2518
2624
  saveTaskState({ terminalTaskIds: [...terminalTaskIds] }, defaultTaskStatePath());
2519
2625
  }
@@ -2749,10 +2855,11 @@ var src_default = {
2749
2855
  api.logger.info(`[AAMP] Directory profile synced${cardText ? " (card text registered)" : ""}`);
2750
2856
  }
2751
2857
  function wakeAgentForPendingTask(task) {
2752
- const fallback = () => triggerHeartbeatWake(currentSessionKey, `task ${task.taskId}`);
2858
+ const fallbackSessionKey = buildAampWakeSessionKey("task", task.taskId);
2859
+ const fallback = () => triggerHeartbeatWake(fallbackSessionKey, `task ${task.taskId}`);
2753
2860
  const dispatcher = channelRuntime?.reply?.dispatchReplyWithBufferedBlockDispatcher;
2754
2861
  api.logger.info(
2755
- `[AAMP] Wake requested for task ${task.taskId} \u2014 channelRuntime=${channelRuntime ? "yes" : "no"} channelCfg=${channelCfg ? "yes" : "no"} dispatcher=${typeof dispatcher === "function" ? "yes" : "no"} session=${currentSessionKey}`
2862
+ `[AAMP] Wake requested for task ${task.taskId} \u2014 channelRuntime=${channelRuntime ? "yes" : "no"} channelCfg=${channelCfg ? "yes" : "no"} dispatcher=${typeof dispatcher === "function" ? "yes" : "no"} fallbackSession=${fallbackSessionKey}`
2756
2863
  );
2757
2864
  if (!channelRuntime || !channelCfg || typeof dispatcher !== "function") {
2758
2865
  fallback();
@@ -2879,7 +2986,7 @@ var src_default = {
2879
2986
  } catch (err) {
2880
2987
  api.logger.error(`[AAMP] task.dispatch handler failed for ${task.taskId}: ${err.message}`);
2881
2988
  if (pendingTasks.has(task.taskId)) {
2882
- triggerHeartbeatWake(currentSessionKey, `task ${task.taskId}`);
2989
+ triggerHeartbeatWake(buildAampWakeSessionKey("task", task.taskId), `task ${task.taskId}`);
2883
2990
  }
2884
2991
  }
2885
2992
  })();
@@ -3014,7 +3121,7 @@ ${notifyBody?.bodyText ?? "Sub-task completed."}${actionSection}`;
3014
3121
  api.logger.error(`[AAMP] Channel dispatch failed: ${err.message}`);
3015
3122
  });
3016
3123
  } else {
3017
- const notifySessionKey = `agent:main:aamp-notify-${Date.now()}`;
3124
+ const notifySessionKey = buildAampWakeSessionKey("result", result.taskId);
3018
3125
  try {
3019
3126
  api.runtime.system.requestHeartbeatNow({ reason: "wake", sessionKey: notifySessionKey });
3020
3127
  api.logger.info(`[AAMP] Heartbeat for sub-task result ${result.taskId}`);
@@ -3090,7 +3197,7 @@ ${notifyBody?.bodyText ?? help.question}`;
3090
3197
  api.logger.error(`[AAMP] Channel dispatch failed for help: ${err.message}`);
3091
3198
  });
3092
3199
  } else {
3093
- const helpSessionKey = `agent:main:aamp-notify-${Date.now()}`;
3200
+ const helpSessionKey = buildAampWakeSessionKey("help", help.taskId);
3094
3201
  try {
3095
3202
  api.runtime.system.requestHeartbeatNow({ reason: "wake", sessionKey: helpSessionKey });
3096
3203
  api.logger.info(`[AAMP] Heartbeat fallback for sub-task help ${help.taskId}`);
@@ -3230,7 +3337,10 @@ ${notifyBody?.bodyText ?? help.question}`;
3230
3337
  return;
3231
3338
  api.logger.info(`[AAMP] gateway_start: re-triggering heartbeat for ${pendingTasks.size} pending task(s)`);
3232
3339
  try {
3233
- api.runtime.system.requestHeartbeatNow({ reason: "wake", sessionKey: currentSessionKey });
3340
+ api.runtime.system.requestHeartbeatNow({
3341
+ reason: "wake",
3342
+ sessionKey: buildAampWakeSessionKey("queue", "gateway-start")
3343
+ });
3234
3344
  } catch (err) {
3235
3345
  api.logger.warn(`[AAMP] gateway_start heartbeat failed: ${err.message}`);
3236
3346
  }
@@ -3238,8 +3348,8 @@ ${notifyBody?.bodyText ?? help.question}`;
3238
3348
  api.on(
3239
3349
  "before_prompt_build",
3240
3350
  (_event, ctx) => {
3241
- if (ctx?.sessionKey && !String(ctx.sessionKey).startsWith("aamp:")) {
3242
- currentSessionKey = ctx.sessionKey;
3351
+ if (!isAampSessionKey(ctx?.sessionKey)) {
3352
+ return {};
3243
3353
  }
3244
3354
  for (const [id, t] of pendingTasks) {
3245
3355
  if (hasExpired(t)) {
@@ -3525,7 +3635,10 @@ ${task.contextLinks.map((l) => ` - ${l}`).join("\n")}` : "",
3525
3635
  api.logger.info(`[AAMP] \u2192 task.result ${task.taskId} ${p.status}`);
3526
3636
  if (pendingTasks.size > 0) {
3527
3637
  try {
3528
- api.runtime.system.requestHeartbeatNow({ reason: "wake", sessionKey: currentSessionKey });
3638
+ api.runtime.system.requestHeartbeatNow({
3639
+ reason: "wake",
3640
+ sessionKey: buildAampWakeSessionKey("queue", "follow-up")
3641
+ });
3529
3642
  } catch {
3530
3643
  }
3531
3644
  }