@okxweb3/a2a-node 0.2.8 → 0.2.9-beta-912b775358-260824171011

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.
Files changed (3) hide show
  1. package/dist/cli.js +760 -112
  2. package/dist/index.js +725 -91
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3992,6 +3992,34 @@ var init_command_store = __esm({
3992
3992
  }
3993
3993
  return null;
3994
3994
  }
3995
+ /**
3996
+ * Read-only lookup of a command's current lifecycle state by id. Returns null
3997
+ * when no row exists. Used by the WakeupNotify recovery reconcile to decide
3998
+ * whether an in-flight wakeup's command completed, failed, or was orphaned by a
3999
+ * crash — it does not mutate the queue or alter recovery behavior.
4000
+ */
4001
+ getCommandState(commandId) {
4002
+ const row = this.db.prepare(`
4003
+ SELECT status, result_json, processing_started_at_ms FROM command_queue
4004
+ WHERE id = ?
4005
+ `).get(commandId);
4006
+ if (!row?.status) {
4007
+ return null;
4008
+ }
4009
+ let ok = null;
4010
+ if (row.result_json) {
4011
+ try {
4012
+ ok = Boolean(JSON.parse(row.result_json).ok);
4013
+ } catch {
4014
+ ok = null;
4015
+ }
4016
+ }
4017
+ return {
4018
+ status: row.status,
4019
+ ok,
4020
+ processingStartedAtMs: row.processing_started_at_ms ?? null
4021
+ };
4022
+ }
3995
4023
  close() {
3996
4024
  this.db.close();
3997
4025
  }
@@ -4677,7 +4705,7 @@ var require_is = __commonJS({
4677
4705
  function isPrimitive(wat) {
4678
4706
  return wat === null || typeof wat !== "object" && typeof wat !== "function";
4679
4707
  }
4680
- function isPlainObject4(wat) {
4708
+ function isPlainObject6(wat) {
4681
4709
  return isBuiltin(wat, "Object");
4682
4710
  }
4683
4711
  function isEvent(wat) {
@@ -4693,7 +4721,7 @@ var require_is = __commonJS({
4693
4721
  return Boolean(wat && wat.then && typeof wat.then === "function");
4694
4722
  }
4695
4723
  function isSyntheticEvent(wat) {
4696
- return isPlainObject4(wat) && "nativeEvent" in wat && "preventDefault" in wat && "stopPropagation" in wat;
4724
+ return isPlainObject6(wat) && "nativeEvent" in wat && "preventDefault" in wat && "stopPropagation" in wat;
4697
4725
  }
4698
4726
  function isNaN2(wat) {
4699
4727
  return typeof wat === "number" && wat !== wat;
@@ -4716,7 +4744,7 @@ var require_is = __commonJS({
4716
4744
  exports2.isEvent = isEvent;
4717
4745
  exports2.isInstanceOf = isInstanceOf;
4718
4746
  exports2.isNaN = isNaN2;
4719
- exports2.isPlainObject = isPlainObject4;
4747
+ exports2.isPlainObject = isPlainObject6;
4720
4748
  exports2.isPrimitive = isPrimitive;
4721
4749
  exports2.isRegExp = isRegExp;
4722
4750
  exports2.isString = isString;
@@ -19969,6 +19997,17 @@ var init_events = __esm({
19969
19997
  SYSTEM_NOTIFICATION_ROUTED: "System notification routed",
19970
19998
  HERMES_SESSION_ROUTE_BINDING: "Hermes session route binding",
19971
19999
  DOCTOR_REPORT: "Doctor report",
20000
+ // Paginated WakeupNotify recovery (BL-011) + bounded AI concurrency telemetry.
20001
+ // All info-level events below must ALSO be listed in SENTRY_INFO_ALLOWLIST
20002
+ // (index.ts) or info() drops them silently.
20003
+ WAKEUP_PULL_STARTED: "WakeupNotify pull started",
20004
+ WAKEUP_PULL_PAGE_FETCHED: "WakeupNotify pull page fetched",
20005
+ WAKEUP_PULL_PAGE_RETRY: "WakeupNotify pull page retry",
20006
+ WAKEUP_PULL_COMPLETED: "WakeupNotify pull completed",
20007
+ WAKEUP_NOTIFICATION_REJECTED: "WakeupNotify notification rejected",
20008
+ WAKEUP_CHUNK_DISPATCHED: "WakeupNotify chunk dispatched",
20009
+ WAKEUP_IN_MEMORY_WORK_DROPPED: "WakeupNotify in-memory work dropped",
20010
+ AI_CONCURRENCY_SNAPSHOT: "AI concurrency snapshot",
19972
20011
  // ── error ─────────────────────────────────────────────────────
19973
20012
  XMTP_CONNECTION_FAILED: "XMTP connection failed",
19974
20013
  CONVERSATIONS_STREAM_FAILED: "conversations.stream failed",
@@ -20023,6 +20062,7 @@ var init_events = __esm({
20023
20062
  AI_RUN_TOOL_FAILED: "AI run tool failed",
20024
20063
  IPC_FAILED: "IPC failed",
20025
20064
  NOTIFY_AGENTS_WAKEUP_FAILED: "Notify agents wakeup failed",
20065
+ WAKEUP_PULL_FAILED: "WakeupNotify pull failed",
20026
20066
  OUTBOUND_AGENT_LOOKUP_FAILED: "Outbound agent lookup failed",
20027
20067
  OUTBOUND_ELIGIBILITY_CHECK_FAILED: "Outbound eligibility check failed",
20028
20068
  OUTBOUND_ELIGIBILITY_BYPASSED: "Outbound eligibility bypassed",
@@ -20824,6 +20864,15 @@ var init_sentry_logger = __esm({
20824
20864
  LogEvent.USER_CHANNEL_MESSAGE_DELIVERED,
20825
20865
  LogEvent.SYSTEM_NOTIFICATION_RECEIVED,
20826
20866
  LogEvent.SYSTEM_NOTIFICATION_ROUTED,
20867
+ // Paginated WakeupNotify recovery (BL-011) + bounded AI concurrency telemetry.
20868
+ LogEvent.WAKEUP_PULL_STARTED,
20869
+ LogEvent.WAKEUP_PULL_PAGE_FETCHED,
20870
+ LogEvent.WAKEUP_PULL_PAGE_RETRY,
20871
+ LogEvent.WAKEUP_PULL_COMPLETED,
20872
+ LogEvent.WAKEUP_NOTIFICATION_REJECTED,
20873
+ LogEvent.WAKEUP_CHUNK_DISPATCHED,
20874
+ LogEvent.WAKEUP_IN_MEMORY_WORK_DROPPED,
20875
+ LogEvent.AI_CONCURRENCY_SNAPSHOT,
20827
20876
  LogEvent.HERMES_SESSION_ROUTE_BINDING,
20828
20877
  LogEvent.INBOUND_BLOCKED_ADDRESS_MISMATCH,
20829
20878
  LogEvent.INBOUND_BLOCKED_INELIGIBLE,
@@ -25239,7 +25288,7 @@ var require_websocket2 = __commonJS({
25239
25288
  var http2 = require("http");
25240
25289
  var net = require("net");
25241
25290
  var tls = require("tls");
25242
- var { randomBytes: randomBytes4, createHash: createHash6 } = require("crypto");
25291
+ var { randomBytes: randomBytes4, createHash: createHash7 } = require("crypto");
25243
25292
  var { Duplex, Readable } = require("stream");
25244
25293
  var { URL: URL2 } = require("url");
25245
25294
  var PerMessageDeflate = require_permessage_deflate();
@@ -25899,7 +25948,7 @@ var require_websocket2 = __commonJS({
25899
25948
  abortHandshake(websocket, socket, "Invalid Upgrade header");
25900
25949
  return;
25901
25950
  }
25902
- const digest = createHash6("sha1").update(key + GUID).digest("base64");
25951
+ const digest = createHash7("sha1").update(key + GUID).digest("base64");
25903
25952
  if (res.headers["sec-websocket-accept"] !== digest) {
25904
25953
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
25905
25954
  return;
@@ -26266,7 +26315,7 @@ var require_websocket_server = __commonJS({
26266
26315
  var EventEmitter2 = require("events");
26267
26316
  var http2 = require("http");
26268
26317
  var { Duplex } = require("stream");
26269
- var { createHash: createHash6 } = require("crypto");
26318
+ var { createHash: createHash7 } = require("crypto");
26270
26319
  var extension = require_extension();
26271
26320
  var PerMessageDeflate = require_permessage_deflate();
26272
26321
  var subprotocol = require_subprotocol();
@@ -26567,7 +26616,7 @@ var require_websocket_server = __commonJS({
26567
26616
  );
26568
26617
  }
26569
26618
  if (this._state > RUNNING) return abortHandshake(socket, 503);
26570
- const digest = createHash6("sha1").update(key + GUID).digest("base64");
26619
+ const digest = createHash7("sha1").update(key + GUID).digest("base64");
26571
26620
  const headers = [
26572
26621
  "HTTP/1.1 101 Switching Protocols",
26573
26622
  "Upgrade: websocket",
@@ -26932,7 +26981,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
26932
26981
  client: {
26933
26982
  id: "gateway-client",
26934
26983
  displayName: "okx-a2a-node",
26935
- version: "0.2.8",
26984
+ version: "0.2.9-beta-912b775358-260824171011",
26936
26985
  platform: "node",
26937
26986
  mode: "backend",
26938
26987
  instanceId
@@ -26943,7 +26992,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
26943
26992
  commands: [],
26944
26993
  permissions: {},
26945
26994
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
26946
- userAgent: `okx-a2a-node/${"0.2.8"}`,
26995
+ userAgent: `okx-a2a-node/${"0.2.9-beta-912b775358-260824171011"}`,
26947
26996
  auth: {
26948
26997
  ...config.token ? { token: config.token } : {},
26949
26998
  ...config.password ? { password: config.password } : {}
@@ -28910,7 +28959,7 @@ var init_sentry_config = __esm({
28910
28959
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
28911
28960
  SENTRY_CONFIG = {
28912
28961
  projectName: "okx/openclaw-okx-a2a-extension",
28913
- release: "0.2.8",
28962
+ release: "0.2.9-beta-912b775358-260824171011",
28914
28963
  environment,
28915
28964
  runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
28916
28965
  };
@@ -40080,7 +40129,7 @@ async function exportDiagnosticLogs(options) {
40080
40129
  node: process.version,
40081
40130
  platform: process.platform,
40082
40131
  arch: process.arch,
40083
- packageVersion: true ? "0.2.8" : "unknown",
40132
+ packageVersion: true ? "0.2.9-beta-912b775358-260824171011" : "unknown",
40084
40133
  sensitiveContentIncluded: options.includeSensitiveContent,
40085
40134
  listenerAndLlmContentIncluded: true,
40086
40135
  credentialsAlwaysRedacted: true,
@@ -58618,36 +58667,15 @@ var init_xmtp_sdk = __esm({
58618
58667
  timer
58619
58668
  });
58620
58669
  }
58621
- notifyAgentsWakeupAfterReplay() {
58622
- const notifyAgentsWakeup2 = this.tools.notifyAgentsWakeup;
58623
- const agentIds = this.allAgents.map((agent) => agent.agentId);
58624
- if (!notifyAgentsWakeup2 || agentIds.length === 0) {
58625
- return;
58626
- }
58627
- void notifyAgentsWakeup2(agentIds).then(() => {
58628
- logger.info(LogEvent.AGENTS_WAKEUP_NOTIFIED, {
58629
- count: String(agentIds.length),
58630
- onchainosAgentIds: agentIds.join(","),
58631
- stage: "after-startup-replay"
58632
- });
58633
- }).catch((err2) => {
58634
- logWithTimestamp("[xmtp-sdk] notifyAgentsWakeup unexpected error:", err2);
58635
- logger.error(
58636
- LogEvent.NOTIFY_AGENTS_WAKEUP_FAILED,
58637
- err2 instanceof Error ? err2 : new Error(String(err2)),
58638
- {
58639
- count: String(agentIds.length),
58640
- stage: "after-startup-replay"
58641
- }
58642
- );
58643
- });
58644
- }
58645
58670
  /**
58646
58671
  * Completes the startup handoff from stored history to the live stream.
58647
58672
  * Listeners are already attached, but their host handlers stay buffered
58648
- * until replay finishes and the buffer is drained. Only then do we tell
58649
- * the backend that the agents are awake, so wakeup-triggered messages
58650
- * cannot overtake the stored backlog.
58673
+ * until replay finishes and the buffer is drained.
58674
+ *
58675
+ * Telling the backend the agents are awake is deliberately NOT done here.
58676
+ * The daemon (listener.ts) runs the paginated task-wakeup pull AFTER the bounded
58677
+ * command processor is ready, so wakeup-triggered work cannot be dispatched
58678
+ * before the processor exists.
58651
58679
  */
58652
58680
  async completeStartupReplay() {
58653
58681
  const startupGates = new Map(
@@ -58662,7 +58690,6 @@ var init_xmtp_sdk = __esm({
58662
58690
  }
58663
58691
  this.resolveStartupReplayCompletion?.();
58664
58692
  this.resolveStartupReplayCompletion = void 0;
58665
- this.notifyAgentsWakeupAfterReplay();
58666
58693
  return summary;
58667
58694
  }
58668
58695
  async startListeningForAddresses(addresses) {
@@ -59901,39 +59928,6 @@ function fingerprintAgents(agents) {
59901
59928
  function filterNoisyAgentIdentityLog(stderr) {
59902
59929
  return stderr.split(/\r?\n/).filter((line) => !line.includes("[agent-identity] get response")).join("\n").trim();
59903
59930
  }
59904
- function previewLogText(value, max = 300) {
59905
- const normalized = value.replace(/\s+/g, " ").trim();
59906
- return normalized.length > max ? `${normalized.slice(0, max)}...` : normalized;
59907
- }
59908
- async function notifyAgentsWakeup(agentIds) {
59909
- if (agentIds.length === 0) {
59910
- logWithTimestamp("[onchainos] wakeup-notify: agentIds empty, skipping");
59911
- return;
59912
- }
59913
- const ids = agentIds.join(",");
59914
- logWithTimestamp(`[onchainos] sending wakeup-notify (agent-ids=${ids})`);
59915
- try {
59916
- const { stdout, stderr } = await exec([
59917
- "agent",
59918
- "wakeup-notify",
59919
- "--agent-ids",
59920
- ids
59921
- ]);
59922
- if (stderr) {
59923
- logWithTimestamp(`[onchainos] stderr (wakeup-notify): ${stderr}`);
59924
- }
59925
- logWithTimestamp(
59926
- `[onchainos] wakeup-notify result agentIds=${ids} stdoutBytes=${Buffer.byteLength(stdout ?? "", "utf8")} stdoutPreview=${previewLogText(stdout ?? "")}`
59927
- );
59928
- logWithTimestamp(`[onchainos] wakeup-notify succeeded (agent-ids=${ids})`);
59929
- } catch (err2) {
59930
- guardCatchSessionExpired2(err2, "wakeup-notify");
59931
- logWithTimestamp(
59932
- `[onchainos] wakeup-notify failed (agent-ids=${ids}):`,
59933
- err2?.message ?? err2
59934
- );
59935
- }
59936
- }
59937
59931
  async function sendHeartbeat(chainIndex) {
59938
59932
  logWithTimestamp(`[onchainos] sending heartbeat (chain-index=${chainIndex})`);
59939
59933
  try {
@@ -65195,6 +65189,29 @@ var init_xmtp_debug = __esm({
65195
65189
  }
65196
65190
  });
65197
65191
 
65192
+ // src/ai-concurrency-config.ts
65193
+ function resolveMaxActiveAiExecutions(env = process.env) {
65194
+ const raw = env.OKX_A2A_MAX_ACTIVE_AI_EXECUTIONS ?? env.OKX_AGENT_TASK_MAX_ACTIVE_AI_EXECUTIONS;
65195
+ if (raw === void 0 || raw.trim() === "") {
65196
+ return { limit: DEFAULT_MAX_ACTIVE_AI_EXECUTIONS, anomaly: null };
65197
+ }
65198
+ const parsed = Number(raw);
65199
+ if (Number.isFinite(parsed) && Number.isInteger(parsed) && parsed > 0) {
65200
+ return { limit: parsed, anomaly: null };
65201
+ }
65202
+ return {
65203
+ limit: DEFAULT_MAX_ACTIVE_AI_EXECUTIONS,
65204
+ anomaly: `invalid value "${raw}", using default ${DEFAULT_MAX_ACTIVE_AI_EXECUTIONS}`
65205
+ };
65206
+ }
65207
+ var DEFAULT_MAX_ACTIVE_AI_EXECUTIONS;
65208
+ var init_ai_concurrency_config = __esm({
65209
+ "src/ai-concurrency-config.ts"() {
65210
+ "use strict";
65211
+ DEFAULT_MAX_ACTIVE_AI_EXECUTIONS = 4;
65212
+ }
65213
+ });
65214
+
65198
65215
  // src/command-processor.ts
65199
65216
  function commandSentryExtra(command, extra = {}) {
65200
65217
  const base = {
@@ -65244,6 +65261,17 @@ function startCommandProcessor(params) {
65244
65261
  const executionTimeoutMs = readPositiveEnv("OKX_A2A_COMMAND_EXECUTION_TIMEOUT_MS", DEFAULT_COMMAND_EXECUTION_TIMEOUT_MS);
65245
65262
  const staleTimeoutMs = readPositiveEnv("OKX_A2A_COMMAND_STALE_TIMEOUT_MS", DEFAULT_COMMAND_STALE_TIMEOUT_MS);
65246
65263
  const aiStaleTimeoutMs = readPositiveEnv("OKX_A2A_AI_COMMAND_STALE_TIMEOUT_MS", DEFAULT_AI_COMMAND_STALE_TIMEOUT_MS);
65264
+ const { limit: maxActiveAiExecutions, anomaly: aiLimitAnomaly } = resolveMaxActiveAiExecutions(process.env);
65265
+ if (aiLimitAnomaly) {
65266
+ logWithTimestamp(`[okx-agent-task] AI concurrency limit config anomaly: ${aiLimitAnomaly}`);
65267
+ logger.info(LogEvent.AI_CONCURRENCY_SNAPSHOT, {
65268
+ component: "node_command_processor",
65269
+ stage: "config",
65270
+ reason: "invalid_config_using_default",
65271
+ configuredLimit: String(maxActiveAiExecutions),
65272
+ anomaly: aiLimitAnomaly
65273
+ });
65274
+ }
65247
65275
  let inFlight = false;
65248
65276
  let aiScanInFlight = false;
65249
65277
  const activeAiQueueKeys = /* @__PURE__ */ new Set();
@@ -65333,6 +65361,9 @@ function startCommandProcessor(params) {
65333
65361
  inFlight = false;
65334
65362
  }
65335
65363
  };
65364
+ const AI_SNAPSHOT_MIN_INTERVAL_MS = 15e3;
65365
+ let lastAiSnapshotSignature = "";
65366
+ let lastAiSnapshotAtMs = 0;
65336
65367
  const aiTick = async () => {
65337
65368
  if (aiScanInFlight) {
65338
65369
  return;
@@ -65345,15 +65376,33 @@ function startCommandProcessor(params) {
65345
65376
  skipCommandIds: activeAiCommandIds
65346
65377
  });
65347
65378
  const pending = await commands.listPendingEntries();
65379
+ const pendingAi = pending.filter((entry) => isAiDispatchCommand(entry.command));
65380
+ if (activeAiCommandIds.size > 0 || pendingAi.length > 0) {
65381
+ const snapshotNow = Date.now();
65382
+ const oldestQueuedAgeMs = pendingAi.reduce((oldest, entry) => {
65383
+ const createdAt = Number.isFinite(entry.command.createdAt) ? entry.command.createdAt : entry.mtimeMs;
65384
+ return Math.max(oldest, snapshotNow - createdAt);
65385
+ }, 0);
65386
+ const snapshotSignature = `${maxActiveAiExecutions}|${activeAiCommandIds.size}|${pendingAi.length}`;
65387
+ if (snapshotSignature !== lastAiSnapshotSignature || snapshotNow - lastAiSnapshotAtMs >= AI_SNAPSHOT_MIN_INTERVAL_MS) {
65388
+ lastAiSnapshotSignature = snapshotSignature;
65389
+ lastAiSnapshotAtMs = snapshotNow;
65390
+ logger.info(LogEvent.AI_CONCURRENCY_SNAPSHOT, {
65391
+ component: "node_command_processor",
65392
+ configuredLimit: String(maxActiveAiExecutions),
65393
+ activeCount: String(activeAiCommandIds.size),
65394
+ queueDepth: String(pendingAi.length),
65395
+ oldestQueuedAgeMs: String(oldestQueuedAgeMs)
65396
+ });
65397
+ }
65398
+ } else {
65399
+ lastAiSnapshotSignature = "";
65400
+ }
65348
65401
  for (const entry of pending) {
65349
65402
  const command = entry.command;
65350
65403
  if (!isAiDispatchCommand(command)) {
65351
65404
  continue;
65352
65405
  }
65353
- const queueKey = buildAiCommandQueueKey(command);
65354
- if (activeAiQueueKeys.has(queueKey)) {
65355
- continue;
65356
- }
65357
65406
  if (isStalePending(entry, aiStaleTimeoutMs)) {
65358
65407
  const message = `${command.type}/${command.kind} command expired after ${formatDuration2(Date.now() - command.createdAt)} in pending queue`;
65359
65408
  await commands.completePending(command, {
@@ -65364,13 +65413,20 @@ function startCommandProcessor(params) {
65364
65413
  stage: "pending",
65365
65414
  ageMs: Date.now() - command.createdAt,
65366
65415
  timeoutMs: aiStaleTimeoutMs,
65367
- reason: "pending_expired"
65416
+ reason: "queue_expired"
65368
65417
  }));
65369
65418
  logWithTimestamp(
65370
65419
  `[okx-agent-task] ${command.type}/${command.kind} command expired id=${command.id}: ${message}`
65371
65420
  );
65372
65421
  continue;
65373
65422
  }
65423
+ if (activeAiCommandIds.size >= maxActiveAiExecutions) {
65424
+ continue;
65425
+ }
65426
+ const queueKey = buildAiCommandQueueKey(command);
65427
+ if (activeAiQueueKeys.has(queueKey)) {
65428
+ continue;
65429
+ }
65374
65430
  const processingPath = await commands.take(command);
65375
65431
  if (!processingPath) {
65376
65432
  continue;
@@ -65943,6 +65999,9 @@ function isStalePending(entry, staleTimeoutMs) {
65943
65999
  return Date.now() - createdAt > staleTimeoutMs;
65944
66000
  }
65945
66001
  function buildAiCommandQueueKey(command) {
66002
+ if (command.jobId) {
66003
+ return `job:${command.jobId}`;
66004
+ }
65946
66005
  if (command.kind === "job-message") {
65947
66006
  return buildDispatchSessionKey(command.jobId, command.sessionAgentId);
65948
66007
  }
@@ -66050,6 +66109,7 @@ var init_command_processor = __esm({
66050
66109
  init_user_attention_ipc();
66051
66110
  init_sentry_logger();
66052
66111
  init_xmtp_debug();
66112
+ init_ai_concurrency_config();
66053
66113
  DEFAULT_COMMAND_EXECUTION_TIMEOUT_MS = 5e4;
66054
66114
  DEFAULT_COMMAND_STALE_TIMEOUT_MS = 12e4;
66055
66115
  DEFAULT_AI_COMMAND_STALE_TIMEOUT_MS = 30 * 6e4;
@@ -67650,6 +67710,575 @@ var init_message_handler = __esm({
67650
67710
  }
67651
67711
  });
67652
67712
 
67713
+ // src/wakeup-notify-client.ts
67714
+ function isTransientTransportError(err2) {
67715
+ const message = err2 instanceof Error ? err2.message : String(err2);
67716
+ return TRANSIENT_TRANSPORT_PATTERN.test(message);
67717
+ }
67718
+ function isPlainObject4(value) {
67719
+ return !!value && typeof value === "object" && !Array.isArray(value);
67720
+ }
67721
+ function isValidWakeupItem(raw) {
67722
+ if (!isPlainObject4(raw)) {
67723
+ return { ok: false, reason: "malformed_item" };
67724
+ }
67725
+ if (typeof raw.agentId !== "string" || raw.agentId.length === 0) {
67726
+ return { ok: false, reason: "missing_agent_id" };
67727
+ }
67728
+ const message = raw.message;
67729
+ if (!isPlainObject4(message) || message.source !== "system") {
67730
+ return { ok: false, reason: "not_system_notification" };
67731
+ }
67732
+ if (message.event !== "wakeup_notify") {
67733
+ return { ok: false, reason: "not_wakeup_notify" };
67734
+ }
67735
+ return {
67736
+ ok: true,
67737
+ item: { agentId: raw.agentId, message }
67738
+ };
67739
+ }
67740
+ function canonicalize(value) {
67741
+ if (Array.isArray(value)) {
67742
+ return value.map(canonicalize);
67743
+ }
67744
+ if (value && typeof value === "object") {
67745
+ const source = value;
67746
+ return Object.keys(source).sort().reduce((acc, key) => {
67747
+ acc[key] = canonicalize(source[key]);
67748
+ return acc;
67749
+ }, {});
67750
+ }
67751
+ return value;
67752
+ }
67753
+ function deriveWakeupDedupeKey(item) {
67754
+ const canonicalMessage = JSON.stringify(canonicalize(item.message ?? {}));
67755
+ const identity = `${item.agentId}
67756
+ ${canonicalMessage}`;
67757
+ return `wakeup:${(0, import_node_crypto15.createHash)("sha256").update(identity).digest("hex").slice(0, 24)}`;
67758
+ }
67759
+ async function fetchAllWakeupNotifications(opts) {
67760
+ const { agentIds, fetchPage, onPage } = opts;
67761
+ const maxPages = opts.maxPages;
67762
+ const maxPageAttempts = opts.maxPageAttempts ?? DEFAULT_MAX_PAGE_ATTEMPTS;
67763
+ const retryDelayMs = opts.retryDelayMs ?? DEFAULT_PAGE_RETRY_DELAY_MS;
67764
+ const sleep5 = opts.sleep ?? ((ms) => new Promise((resolve14) => setTimeout(resolve14, ms)));
67765
+ const isRetryableError = opts.isRetryableError ?? isTransientTransportError;
67766
+ const onRetry = opts.onRetry;
67767
+ const fetchPageWithRetry = async (pageNum) => {
67768
+ let attempt = 0;
67769
+ while (true) {
67770
+ attempt++;
67771
+ try {
67772
+ return await fetchPage({ agentIds, page: pageNum });
67773
+ } catch (err2) {
67774
+ if (!isRetryableError(err2) || attempt >= maxPageAttempts) {
67775
+ throw err2;
67776
+ }
67777
+ onRetry?.({ page: pageNum, attempt, reason: "transient_transport" });
67778
+ await sleep5(retryDelayMs * attempt + Math.floor(Math.random() * (retryDelayMs + 1)));
67779
+ }
67780
+ }
67781
+ };
67782
+ const items = [];
67783
+ const rejects = [];
67784
+ const visited = /* @__PURE__ */ new Set();
67785
+ let pagesFetched = 0;
67786
+ let page = 1;
67787
+ let terminated = "empty";
67788
+ try {
67789
+ while (true) {
67790
+ const resp = await fetchPageWithRetry(page);
67791
+ pagesFetched++;
67792
+ if (resp.code !== 0) {
67793
+ terminated = "business_error";
67794
+ break;
67795
+ }
67796
+ const rawList = resp.data?.list;
67797
+ if (!Array.isArray(rawList)) {
67798
+ rejects.push({ reason: "invalid_response_list" });
67799
+ terminated = "invalid_response";
67800
+ break;
67801
+ }
67802
+ const list = rawList;
67803
+ let itemCount = 0;
67804
+ let rejectedCount = 0;
67805
+ for (const element of list) {
67806
+ const check = isValidWakeupItem(element);
67807
+ if (check.ok) {
67808
+ items.push(check.item);
67809
+ itemCount++;
67810
+ } else {
67811
+ rejects.push({ reason: check.reason });
67812
+ rejectedCount++;
67813
+ }
67814
+ }
67815
+ const nextPage = resp.data?.nextPage;
67816
+ onPage?.({ page, itemCount, rejectedCount, hasNextPage: nextPage != null });
67817
+ if (nextPage === null) {
67818
+ terminated = items.length || rejects.length ? "next_page_null" : "empty";
67819
+ break;
67820
+ }
67821
+ if (typeof nextPage !== "number" || !Number.isInteger(nextPage) || nextPage <= 0) {
67822
+ rejects.push({ reason: "invalid_next_page" });
67823
+ terminated = "invalid_response";
67824
+ break;
67825
+ }
67826
+ visited.add(page);
67827
+ page = nextPage;
67828
+ if (visited.has(page) || maxPages !== void 0 && pagesFetched >= maxPages) {
67829
+ terminated = "max_pages";
67830
+ break;
67831
+ }
67832
+ }
67833
+ } catch {
67834
+ terminated = "transport_error";
67835
+ }
67836
+ const summary = {
67837
+ pagesFetched,
67838
+ received: items.length + rejects.length,
67839
+ persisted: 0,
67840
+ partial: 0,
67841
+ duplicates: 0,
67842
+ rejected: rejects.length,
67843
+ terminated
67844
+ };
67845
+ return { items, summary, rejects };
67846
+ }
67847
+ var import_node_crypto15, DEFAULT_MAX_PAGE_ATTEMPTS, DEFAULT_PAGE_RETRY_DELAY_MS, TRANSIENT_TRANSPORT_PATTERN;
67848
+ var init_wakeup_notify_client = __esm({
67849
+ "src/wakeup-notify-client.ts"() {
67850
+ "use strict";
67851
+ import_node_crypto15 = require("node:crypto");
67852
+ DEFAULT_MAX_PAGE_ATTEMPTS = 3;
67853
+ DEFAULT_PAGE_RETRY_DELAY_MS = 500;
67854
+ TRANSIENT_TRANSPORT_PATTERN = /timed out|timeout|ETIMEDOUT|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|socket hang up|network/i;
67855
+ }
67856
+ });
67857
+
67858
+ // src/wakeup-notify-ingest.ts
67859
+ async function ingestWakeupNotification(item, deps) {
67860
+ const messageId = deriveWakeupDedupeKey(item);
67861
+ const itemJobId = item.message.jobId ?? null;
67862
+ const rawText = JSON.stringify({ agentId: item.agentId, message: item.message });
67863
+ const notif = extractSystemNotification(JSON.parse(rawText));
67864
+ if (!notif?.agentId) {
67865
+ return {
67866
+ status: "rejected",
67867
+ reason: "invalid_system_notification",
67868
+ dedupeKey: messageId,
67869
+ jobId: itemJobId,
67870
+ targetCount: 0
67871
+ };
67872
+ }
67873
+ const targets = resolveSystemNotificationTargets(
67874
+ { sessionStore: deps.sessionStore, myXmtpAddress: deps.myXmtpAddress ?? "", store: void 0 },
67875
+ notif
67876
+ );
67877
+ let succeeded = 0;
67878
+ let failed = 0;
67879
+ for (const target of targets) {
67880
+ let enqueued = false;
67881
+ for (let attempt = 1; attempt <= MAX_TARGET_ENQUEUE_ATTEMPTS && !enqueued; attempt++) {
67882
+ try {
67883
+ await enqueueAiSessionMessageDispatch({
67884
+ commands: deps.commands,
67885
+ event: {
67886
+ sessionKey: target.sessionKey,
67887
+ content: rawText,
67888
+ messageId,
67889
+ jobId: target.jobId ?? null,
67890
+ agentId: target.agentId ?? null
67891
+ }
67892
+ });
67893
+ enqueued = true;
67894
+ } catch {
67895
+ }
67896
+ }
67897
+ if (enqueued) {
67898
+ succeeded += 1;
67899
+ } else {
67900
+ failed += 1;
67901
+ }
67902
+ }
67903
+ if (targets.length > 0 && succeeded === 0) {
67904
+ return {
67905
+ status: "rejected",
67906
+ reason: "enqueue_failed",
67907
+ dedupeKey: messageId,
67908
+ jobId: notif.jobId ?? null,
67909
+ targetCount: 0,
67910
+ failedTargetCount: failed
67911
+ };
67912
+ }
67913
+ if (failed > 0) {
67914
+ return {
67915
+ status: "partial",
67916
+ reason: "partial_enqueue",
67917
+ dedupeKey: messageId,
67918
+ jobId: notif.jobId ?? null,
67919
+ targetCount: succeeded,
67920
+ failedTargetCount: failed
67921
+ };
67922
+ }
67923
+ return {
67924
+ status: "persisted",
67925
+ dedupeKey: messageId,
67926
+ jobId: notif.jobId ?? null,
67927
+ targetCount: succeeded
67928
+ };
67929
+ }
67930
+ var MAX_TARGET_ENQUEUE_ATTEMPTS;
67931
+ var init_wakeup_notify_ingest = __esm({
67932
+ "src/wakeup-notify-ingest.ts"() {
67933
+ "use strict";
67934
+ init_ai_dispatch_queue();
67935
+ init_message_handler();
67936
+ init_wakeup_notify_client();
67937
+ MAX_TARGET_ENQUEUE_ATTEMPTS = 3;
67938
+ }
67939
+ });
67940
+
67941
+ // src/wakeup-notify-transport.ts
67942
+ function withTimeout3(promise, timeoutMs, label) {
67943
+ return new Promise((resolve14, reject) => {
67944
+ const timer = setTimeout(() => {
67945
+ reject(new Error(`${label} timed out after ${timeoutMs}ms`));
67946
+ }, timeoutMs);
67947
+ promise.then(
67948
+ (value) => {
67949
+ clearTimeout(timer);
67950
+ resolve14(value);
67951
+ },
67952
+ (err2) => {
67953
+ clearTimeout(timer);
67954
+ reject(err2);
67955
+ }
67956
+ );
67957
+ });
67958
+ }
67959
+ function resolveCliTimeoutMs(override) {
67960
+ if (typeof override === "number" && Number.isFinite(override) && override > 0) {
67961
+ return override;
67962
+ }
67963
+ const raw = Number(process.env.OKX_A2A_WAKEUP_CLI_TIMEOUT_MS);
67964
+ return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_CLI_TIMEOUT_MS;
67965
+ }
67966
+ function isPlainObject5(value) {
67967
+ return !!value && typeof value === "object" && !Array.isArray(value);
67968
+ }
67969
+ function mapCliStdoutToWakeupResponse(parsed) {
67970
+ const root = isPlainObject5(parsed) ? parsed : {};
67971
+ const rootData = isPlainObject5(root.data) ? root.data : {};
67972
+ const nestedData = isPlainObject5(rootData.data) ? rootData.data : {};
67973
+ const candidates = [root, rootData, nestedData];
67974
+ let code;
67975
+ let msg;
67976
+ let list = [];
67977
+ let listFound = false;
67978
+ let nextPage = null;
67979
+ let nextPageFound = false;
67980
+ for (const candidate of candidates) {
67981
+ if (code === void 0 && typeof candidate.code === "number" && Number.isFinite(candidate.code)) {
67982
+ code = candidate.code;
67983
+ }
67984
+ if (msg === void 0 && typeof candidate.msg === "string") {
67985
+ msg = candidate.msg;
67986
+ }
67987
+ if (!listFound && Array.isArray(candidate.list)) {
67988
+ list = candidate.list;
67989
+ listFound = true;
67990
+ }
67991
+ if (!nextPageFound && "nextPage" in candidate) {
67992
+ const raw = candidate.nextPage;
67993
+ if (typeof raw === "number" && Number.isFinite(raw)) {
67994
+ nextPage = raw;
67995
+ nextPageFound = true;
67996
+ } else if (raw === null) {
67997
+ nextPage = null;
67998
+ nextPageFound = true;
67999
+ }
68000
+ }
68001
+ }
68002
+ if (code === void 0) {
68003
+ code = root.ok === true ? 0 : 1;
68004
+ }
68005
+ return {
68006
+ code,
68007
+ msg,
68008
+ data: { list: listFound ? list : void 0, nextPage: nextPageFound ? nextPage : void 0 }
68009
+ };
68010
+ }
68011
+ function createOnchainosWakeupFetchPage(deps = {}) {
68012
+ const timeoutMs = resolveCliTimeoutMs(deps.timeoutMs);
68013
+ const exec2 = deps.exec ?? ((args) => exec(args, { timeoutMs }));
68014
+ return async ({ agentIds, page }) => {
68015
+ const { stdout } = await withTimeout3(
68016
+ exec2([
68017
+ "agent",
68018
+ TASK_WAKEUP_CLI_COMMAND,
68019
+ "--agent-ids",
68020
+ agentIds.join(","),
68021
+ "--page",
68022
+ String(page)
68023
+ ]),
68024
+ timeoutMs,
68025
+ `onchainos ${TASK_WAKEUP_CLI_COMMAND}`
68026
+ );
68027
+ const parsed = parseCliJson(stdout, TASK_WAKEUP_CLI_COMMAND, {
68028
+ page: String(page)
68029
+ });
68030
+ return mapCliStdoutToWakeupResponse(parsed);
68031
+ };
68032
+ }
68033
+ var TASK_WAKEUP_CLI_COMMAND, DEFAULT_CLI_TIMEOUT_MS;
68034
+ var init_wakeup_notify_transport = __esm({
68035
+ "src/wakeup-notify-transport.ts"() {
68036
+ "use strict";
68037
+ init_bin();
68038
+ init_cli_response();
68039
+ TASK_WAKEUP_CLI_COMMAND = "task-wakeup";
68040
+ DEFAULT_CLI_TIMEOUT_MS = 3e4;
68041
+ }
68042
+ });
68043
+
68044
+ // src/wakeup-chunk-config.ts
68045
+ function resolveWakeupChunkSize(env = process.env) {
68046
+ const fallback = resolveMaxActiveAiExecutions(env).limit;
68047
+ const raw = env.OKX_A2A_WAKEUP_CHUNK_SIZE;
68048
+ if (raw === void 0 || raw.trim() === "") {
68049
+ return { chunkSize: fallback, anomaly: null };
68050
+ }
68051
+ const parsed = Number(raw);
68052
+ if (Number.isFinite(parsed) && Number.isInteger(parsed) && parsed > 0) {
68053
+ return { chunkSize: parsed, anomaly: null };
68054
+ }
68055
+ return {
68056
+ chunkSize: fallback,
68057
+ anomaly: `invalid value "${raw}", using default ${fallback}`
68058
+ };
68059
+ }
68060
+ var init_wakeup_chunk_config = __esm({
68061
+ "src/wakeup-chunk-config.ts"() {
68062
+ "use strict";
68063
+ init_ai_concurrency_config();
68064
+ }
68065
+ });
68066
+
68067
+ // src/wakeup-pull.ts
68068
+ function splitIntoChunks(items, size3) {
68069
+ const chunkSize = Math.max(1, size3);
68070
+ const chunks = [];
68071
+ for (let i = 0; i < items.length; i += chunkSize) {
68072
+ chunks.push(items.slice(i, i + chunkSize));
68073
+ }
68074
+ return chunks;
68075
+ }
68076
+ function buildDroppedWorkTelemetry(snapshot) {
68077
+ return {
68078
+ sentry: {
68079
+ component: "wakeup_pull",
68080
+ reason: "daemon_shutdown",
68081
+ remainingChunks: String(snapshot.remainingChunks),
68082
+ droppedMessages: String(snapshot.droppedMessages),
68083
+ affectedJobCount: String(snapshot.pendingJobIds.length),
68084
+ inFlightChunk: String(snapshot.inFlightChunk)
68085
+ },
68086
+ local: {
68087
+ droppedMessages: snapshot.droppedMessages,
68088
+ pendingJobIds: snapshot.pendingJobIds.slice(0, MAX_LOGGED_PENDING_JOB_IDS),
68089
+ jobIdsTruncated: snapshot.pendingJobIds.length > MAX_LOGGED_PENDING_JOB_IDS,
68090
+ inFlightChunk: snapshot.inFlightChunk
68091
+ }
68092
+ };
68093
+ }
68094
+ function logDroppedWakeupWorkOnShutdown(dispatcher) {
68095
+ if (!dispatcher) {
68096
+ return;
68097
+ }
68098
+ const snapshot = dispatcher.pendingSnapshot();
68099
+ if (snapshot.droppedMessages === 0 && !snapshot.inFlightChunk) {
68100
+ return;
68101
+ }
68102
+ const telemetry = buildDroppedWorkTelemetry(snapshot);
68103
+ logger.info(LogEvent.WAKEUP_IN_MEMORY_WORK_DROPPED, telemetry.sentry);
68104
+ logWithTimestamp("[okx-agent-task] WakeupNotify messages dropped on shutdown", telemetry.local);
68105
+ }
68106
+ async function startWakeupNotifyPull(opts) {
68107
+ const enabled = opts.enabled ?? true;
68108
+ const fetchPage = opts.fetchPage ?? (enabled ? createOnchainosWakeupFetchPage() : void 0);
68109
+ if (!enabled || !fetchPage) {
68110
+ logWithTimestamp("[okx-agent-task] WakeupNotify pull skipped (disabled)");
68111
+ return null;
68112
+ }
68113
+ const agentIds = [
68114
+ ...new Set(
68115
+ [...opts.service.getClients().keys()].map((addr) => opts.service.getAgentByAddress(addr)?.agentId).filter((id) => typeof id === "string" && id.length > 0)
68116
+ )
68117
+ ];
68118
+ if (agentIds.length === 0) {
68119
+ logWithTimestamp("[okx-agent-task] WakeupNotify pull skipped (no active agents)");
68120
+ return null;
68121
+ }
68122
+ logger.info(LogEvent.WAKEUP_PULL_STARTED, {
68123
+ component: "wakeup_pull",
68124
+ agentCount: String(agentIds.length)
68125
+ });
68126
+ const { items, summary, rejects } = await fetchAllWakeupNotifications({
68127
+ agentIds,
68128
+ fetchPage,
68129
+ onPage: (p) => logger.info(LogEvent.WAKEUP_PULL_PAGE_FETCHED, {
68130
+ component: "wakeup_pull",
68131
+ page: String(p.page),
68132
+ itemCount: String(p.itemCount),
68133
+ rejectedCount: String(p.rejectedCount),
68134
+ hasNextPage: String(p.hasNextPage)
68135
+ }),
68136
+ onRetry: (r) => logger.info(LogEvent.WAKEUP_PULL_PAGE_RETRY, {
68137
+ component: "wakeup_pull",
68138
+ page: String(r.page),
68139
+ attempt: String(r.attempt),
68140
+ reason: r.reason
68141
+ })
68142
+ });
68143
+ for (const reject of rejects) {
68144
+ logger.info(LogEvent.WAKEUP_NOTIFICATION_REJECTED, {
68145
+ component: "wakeup_pull",
68146
+ reason: reject.reason
68147
+ });
68148
+ }
68149
+ if (summary.terminated === "transport_error" || summary.terminated === "business_error" || summary.terminated === "invalid_response") {
68150
+ logger.error(LogEvent.WAKEUP_PULL_FAILED, void 0, {
68151
+ component: "wakeup_pull",
68152
+ terminated: summary.terminated
68153
+ });
68154
+ }
68155
+ const resolved = opts.chunkSize != null ? { chunkSize: opts.chunkSize, anomaly: null } : resolveWakeupChunkSize(opts.env);
68156
+ if (resolved.anomaly) {
68157
+ logWithTimestamp(`[okx-agent-task] WakeupNotify chunk size ${resolved.anomaly}`);
68158
+ }
68159
+ const chunks = splitIntoChunks(items, resolved.chunkSize);
68160
+ logger.info(LogEvent.WAKEUP_PULL_COMPLETED, {
68161
+ component: "wakeup_pull",
68162
+ pagesFetched: String(summary.pagesFetched),
68163
+ received: String(summary.received),
68164
+ rejected: String(summary.rejected),
68165
+ chunkCount: String(chunks.length),
68166
+ size: String(resolved.chunkSize),
68167
+ terminated: summary.terminated
68168
+ });
68169
+ const deps = {
68170
+ commands: opts.commands,
68171
+ sessionStore: opts.sessionStore,
68172
+ myXmtpAddress: void 0
68173
+ };
68174
+ const dispatcher = new HeartbeatChunkDispatcher(chunks, deps, opts.shouldContinue);
68175
+ await dispatcher.dispatchNextChunk();
68176
+ return dispatcher.hasPendingChunks() ? dispatcher : null;
68177
+ }
68178
+ var HeartbeatChunkDispatcher, MAX_LOGGED_PENDING_JOB_IDS;
68179
+ var init_wakeup_pull = __esm({
68180
+ "src/wakeup-pull.ts"() {
68181
+ "use strict";
68182
+ init_log();
68183
+ init_sentry_logger();
68184
+ init_wakeup_notify_ingest();
68185
+ init_wakeup_notify_client();
68186
+ init_wakeup_notify_transport();
68187
+ init_wakeup_chunk_config();
68188
+ HeartbeatChunkDispatcher = class {
68189
+ constructor(chunks, deps, shouldContinue) {
68190
+ this.chunks = chunks;
68191
+ this.deps = deps;
68192
+ this.shouldContinue = shouldContinue;
68193
+ }
68194
+ chunks;
68195
+ deps;
68196
+ shouldContinue;
68197
+ index = 0;
68198
+ /** True while a chunk is mid-dispatch (between taking it and its enqueues settling). */
68199
+ dispatching = false;
68200
+ hasPendingChunks() {
68201
+ return this.index < this.chunks.length;
68202
+ }
68203
+ remainingChunks() {
68204
+ return Math.max(0, this.chunks.length - this.index);
68205
+ }
68206
+ /**
68207
+ * Read-only view of the not-yet-started chunks (for shutdown telemetry). `index` has
68208
+ * already advanced past any in-flight chunk, so `chunks.slice(index)` is exactly the work
68209
+ * that has not begun dispatching — the in-flight chunk is excluded from the dropped counts
68210
+ * and reflected only by `inFlightChunk`.
68211
+ */
68212
+ pendingSnapshot() {
68213
+ const notStarted = this.chunks.slice(this.index);
68214
+ let droppedMessages = 0;
68215
+ const jobIds = /* @__PURE__ */ new Set();
68216
+ for (const chunk of notStarted) {
68217
+ for (const item of chunk) {
68218
+ droppedMessages += 1;
68219
+ const jobId = item.message.jobId;
68220
+ if (typeof jobId === "string" && jobId.length > 0) {
68221
+ jobIds.add(jobId);
68222
+ }
68223
+ }
68224
+ }
68225
+ return {
68226
+ remainingChunks: notStarted.length,
68227
+ droppedMessages,
68228
+ pendingJobIds: [...jobIds],
68229
+ inFlightChunk: this.dispatching
68230
+ };
68231
+ }
68232
+ /** Dispatch the next chunk: enqueue every message in it in parallel. No-op when drained. */
68233
+ async dispatchNextChunk() {
68234
+ if (this.shouldContinue && !this.shouldContinue()) {
68235
+ return;
68236
+ }
68237
+ if (this.index >= this.chunks.length) {
68238
+ return;
68239
+ }
68240
+ const chunk = this.chunks[this.index];
68241
+ this.index += 1;
68242
+ const chunkNumber = this.index;
68243
+ this.dispatching = true;
68244
+ let persisted = 0;
68245
+ let partial = 0;
68246
+ let rejected = 0;
68247
+ try {
68248
+ const outcomes = await Promise.allSettled(
68249
+ chunk.map((item) => ingestWakeupNotification(item, this.deps))
68250
+ );
68251
+ for (const outcome of outcomes) {
68252
+ if (outcome.status === "fulfilled") {
68253
+ if (outcome.value.status === "persisted") {
68254
+ persisted += 1;
68255
+ } else if (outcome.value.status === "partial") {
68256
+ partial += 1;
68257
+ } else {
68258
+ rejected += 1;
68259
+ }
68260
+ } else {
68261
+ rejected += 1;
68262
+ }
68263
+ }
68264
+ } finally {
68265
+ this.dispatching = false;
68266
+ }
68267
+ logger.info(LogEvent.WAKEUP_CHUNK_DISPATCHED, {
68268
+ component: "wakeup_pull",
68269
+ chunk: String(chunkNumber),
68270
+ chunkCount: String(this.chunks.length),
68271
+ size: String(chunk.length),
68272
+ persisted: String(persisted),
68273
+ partial: String(partial),
68274
+ rejected: String(rejected)
68275
+ });
68276
+ }
68277
+ };
68278
+ MAX_LOGGED_PENDING_JOB_IDS = 100;
68279
+ }
68280
+ });
68281
+
67653
68282
  // src/user-attention-watchers.ts
67654
68283
  function createUserAttentionWatchParentMonitor(options = {}) {
67655
68284
  const parentPid = options.parentPid ?? process.ppid;
@@ -67688,7 +68317,7 @@ function isOriginalParentProcessAlive(options) {
67688
68317
  }
67689
68318
  function registerUserAttentionWatcher(options) {
67690
68319
  return options.store.registerUserAttentionWatcher({
67691
- id: options.id ?? (0, import_node_crypto15.randomUUID)(),
68320
+ id: options.id ?? (0, import_node_crypto16.randomUUID)(),
67692
68321
  provider: options.provider,
67693
68322
  jobId: options.jobId,
67694
68323
  nowMs: options.nowMs ?? Date.now(),
@@ -67767,11 +68396,11 @@ function userWatchEventDeliveredSentryExtra(event) {
67767
68396
  hasDecision: String(event.hasDecision)
67768
68397
  };
67769
68398
  }
67770
- var import_node_crypto15, USER_ATTENTION_WATCH_REPLACED_MESSAGE, USER_ATTENTION_WATCHER_SCAN_MS, USER_ATTENTION_WATCHER_TTL_MS, USER_ATTENTION_WATCH_PARENT_CHECK_MS;
68399
+ var import_node_crypto16, USER_ATTENTION_WATCH_REPLACED_MESSAGE, USER_ATTENTION_WATCHER_SCAN_MS, USER_ATTENTION_WATCHER_TTL_MS, USER_ATTENTION_WATCH_PARENT_CHECK_MS;
67771
68400
  var init_user_attention_watchers = __esm({
67772
68401
  "src/user-attention-watchers.ts"() {
67773
68402
  "use strict";
67774
- import_node_crypto15 = require("node:crypto");
68403
+ import_node_crypto16 = require("node:crypto");
67775
68404
  init_sentry_logger();
67776
68405
  USER_ATTENTION_WATCH_REPLACED_MESSAGE = "Another okx-a2a user watch with the same job scope was started, so this watcher has been closed. Do not run this watch command again automatically, because starting another watcher may interrupt a different session that is already monitoring task progress.";
67777
68406
  USER_ATTENTION_WATCHER_SCAN_MS = 500;
@@ -68029,7 +68658,6 @@ async function runListenerWithLock(options, paths) {
68029
68658
  fetchSystemConfig: async () => fetchSystemConfig({
68030
68659
  notifySessionExpired: (command) => service.tools.notifySessionExpired?.(command)
68031
68660
  }),
68032
- notifyAgentsWakeup: async (agentIds) => notifyAgentsWakeup(agentIds),
68033
68661
  createSigner: (agent) => createOnchainosSigner(agent),
68034
68662
  createInboundMiddleware: (deps) => createMiddlewareFromCoreDeps(store, deps, {
68035
68663
  sessionStore,
@@ -68079,12 +68707,12 @@ async function runListenerWithLock(options, paths) {
68079
68707
  });
68080
68708
  }
68081
68709
  });
68082
- service.setPluginVersion("0.2.8");
68710
+ service.setPluginVersion("0.2.9-beta-912b775358-260824171011");
68083
68711
  await service.init();
68084
68712
  const pluginVersionStatus = service.pluginVersionStatus;
68085
68713
  if (pluginVersionStatus.unavailable) {
68086
68714
  throw new Error(
68087
- `@okxweb3/a2a-node v${"0.2.8"} is below the required minimum v${pluginVersionStatus.minVersion}`
68715
+ `@okxweb3/a2a-node v${"0.2.9-beta-912b775358-260824171011"} is below the required minimum v${pluginVersionStatus.minVersion}`
68088
68716
  );
68089
68717
  }
68090
68718
  const systemConfig = service.getSystemConfig();
@@ -68112,7 +68740,7 @@ async function runListenerWithLock(options, paths) {
68112
68740
  onchainosAgentId: "*",
68113
68741
  reason: "system-config missing sentryDsn",
68114
68742
  pluginId: "@okxweb3/a2a-node",
68115
- pluginVersion: "0.2.8"
68743
+ pluginVersion: "0.2.9-beta-912b775358-260824171011"
68116
68744
  });
68117
68745
  }
68118
68746
  logWithTimestamp(
@@ -68355,6 +68983,7 @@ async function runListenerWithLock(options, paths) {
68355
68983
  let intervalSec = Math.max(10, service.getSystemConfig().heartbeatInterval);
68356
68984
  let offlineReplayIntervalSec = resolveOfflineReplayIntervalSec(service.getSystemConfig());
68357
68985
  let xmtpClientRecycleIntervalSec = resolveXmtpClientRecycleIntervalSec(service.getSystemConfig());
68986
+ let wakeupDispatcher = null;
68358
68987
  let timer;
68359
68988
  let offlineReplayTimer;
68360
68989
  let xmtpClientRecycleTimer;
@@ -68373,6 +69002,7 @@ async function runListenerWithLock(options, paths) {
68373
69002
  versionRefresh: refreshOnchainosVersionMetadata,
68374
69003
  refresh: syncTick
68375
69004
  });
69005
+ void wakeupDispatcher?.dispatchNextChunk();
68376
69006
  }, intervalSec * 1e3);
68377
69007
  };
68378
69008
  const scheduleOfflineReplayTimer = (nextIntervalSec) => {
@@ -68487,6 +69117,22 @@ async function runListenerWithLock(options, paths) {
68487
69117
  });
68488
69118
  await markDaemonReady(paths.daemonLockPath);
68489
69119
  logWithTimestamp(`[okx-agent-task] daemon ready lock=${paths.daemonLockPath}`);
69120
+ void startWakeupNotifyPull({
69121
+ service,
69122
+ commands,
69123
+ sessionStore,
69124
+ // Stop enqueuing if a shutdown begins mid-delivery (fire-and-forget pull).
69125
+ shouldContinue: () => !stopping
69126
+ }).then((dispatcher) => {
69127
+ wakeupDispatcher = dispatcher;
69128
+ }).catch((err2) => {
69129
+ logger.error(LogEvent.WAKEUP_PULL_FAILED, err2 instanceof Error ? err2 : new Error(String(err2)), {
69130
+ component: "node_listener",
69131
+ stage: "wakeup_pull_start",
69132
+ reason: "wakeup_pull_unhandled_error"
69133
+ });
69134
+ logWithTimestamp("[okx-agent-task] WakeupNotify pull failed to start:", err2);
69135
+ });
68490
69136
  await new Promise((resolve14) => {
68491
69137
  const shutdown2 = (signal) => {
68492
69138
  if (stopping) {
@@ -68499,6 +69145,7 @@ async function runListenerWithLock(options, paths) {
68499
69145
  resolve14();
68500
69146
  }, SHUTDOWN_FORCE_RESOLVE_MS);
68501
69147
  void (async () => {
69148
+ logDroppedWakeupWorkOnShutdown(wakeupDispatcher);
68502
69149
  deferredMaintenanceTasks.cancelAll();
68503
69150
  if (timer) {
68504
69151
  clearInterval(timer);
@@ -68560,6 +69207,7 @@ var init_listener = __esm({
68560
69207
  init_file_store();
68561
69208
  init_session_store();
68562
69209
  init_command_processor();
69210
+ init_wakeup_pull();
68563
69211
  init_command_store();
68564
69212
  init_ai_dispatch_queue();
68565
69213
  init_message_handler();
@@ -86823,7 +87471,7 @@ var require_fetch_pb = __commonJS({
86823
87471
  }
86824
87472
  });
86825
87473
  }
86826
- function isPlainObject4(value) {
87474
+ function isPlainObject6(value) {
86827
87475
  const isObject = Object.prototype.toString.call(value).slice(8, -1) === "Object";
86828
87476
  const isObjLike = value !== null && isObject;
86829
87477
  if (!isObjLike || !isObject) {
@@ -86846,7 +87494,7 @@ var require_fetch_pb = __commonJS({
86846
87494
  const isNonEmptyPrimitiveArray = Array.isArray(value) && value.every((v) => isPrimitive(v)) && value.length > 0;
86847
87495
  const isNonZeroValuePrimitive = isPrimitive(value) && !isZeroValuePrimitive(value);
86848
87496
  let objectToMerge = {};
86849
- if (isPlainObject4(value)) {
87497
+ if (isPlainObject6(value)) {
86850
87498
  objectToMerge = flattenRequestPayload(value, newPath);
86851
87499
  } else if (isNonZeroValuePrimitive || isNonEmptyPrimitiveArray) {
86852
87500
  objectToMerge = { [newPath]: value };
@@ -112415,10 +113063,10 @@ var require_node4 = __commonJS({
112415
113063
 
112416
113064
  // ../../node_modules/@xmtp/content-type-remote-attachment/dist/index.js
112417
113065
  async function encrypt(plain, secret, additionalData) {
112418
- const salt = import_node_crypto16.webcrypto.getRandomValues(new Uint8Array(KDFSaltSize));
112419
- const nonce = import_node_crypto16.webcrypto.getRandomValues(new Uint8Array(AESGCMNonceSize));
113066
+ const salt = import_node_crypto17.webcrypto.getRandomValues(new Uint8Array(KDFSaltSize));
113067
+ const nonce = import_node_crypto17.webcrypto.getRandomValues(new Uint8Array(AESGCMNonceSize));
112420
113068
  const key = await hkdf(secret, salt);
112421
- const encrypted = await import_node_crypto16.webcrypto.subtle.encrypt(aesGcmParams(nonce), key, plain);
113069
+ const encrypted = await import_node_crypto17.webcrypto.subtle.encrypt(aesGcmParams(nonce), key, plain);
112422
113070
  return new Ciphertext({
112423
113071
  aes256GcmHkdfSha256: {
112424
113072
  payload: new Uint8Array(encrypted),
@@ -112432,7 +113080,7 @@ async function decrypt(encrypted, secret, additionalData) {
112432
113080
  throw new Error("invalid payload ciphertext");
112433
113081
  }
112434
113082
  const key = await hkdf(secret, encrypted.aes256GcmHkdfSha256.hkdfSalt);
112435
- const decrypted = await import_node_crypto16.webcrypto.subtle.decrypt(aesGcmParams(encrypted.aes256GcmHkdfSha256.gcmNonce), key, encrypted.aes256GcmHkdfSha256.payload);
113083
+ const decrypted = await import_node_crypto17.webcrypto.subtle.decrypt(aesGcmParams(encrypted.aes256GcmHkdfSha256.gcmNonce), key, encrypted.aes256GcmHkdfSha256.payload);
112436
113084
  return new Uint8Array(decrypted);
112437
113085
  }
112438
113086
  function aesGcmParams(nonce, additionalData) {
@@ -112443,18 +113091,18 @@ function aesGcmParams(nonce, additionalData) {
112443
113091
  return spec;
112444
113092
  }
112445
113093
  async function hkdf(secret, salt) {
112446
- const key = await import_node_crypto16.webcrypto.subtle.importKey("raw", secret, "HKDF", false, [
113094
+ const key = await import_node_crypto17.webcrypto.subtle.importKey("raw", secret, "HKDF", false, [
112447
113095
  "deriveKey"
112448
113096
  ]);
112449
- return import_node_crypto16.webcrypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt, info: hkdfNoInfo }, key, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
113097
+ return import_node_crypto17.webcrypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt, info: hkdfNoInfo }, key, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
112450
113098
  }
112451
- var import_proto, import_node_crypto16, ContentTypeAttachment, AttachmentCodec, KDFSaltSize, AESGCMNonceSize, AESGCMTagLength, Ciphertext, hkdfNoInfo, ContentTypeRemoteAttachment, RemoteAttachmentCodec;
113099
+ var import_proto, import_node_crypto17, ContentTypeAttachment, AttachmentCodec, KDFSaltSize, AESGCMNonceSize, AESGCMTagLength, Ciphertext, hkdfNoInfo, ContentTypeRemoteAttachment, RemoteAttachmentCodec;
112452
113100
  var init_dist6 = __esm({
112453
113101
  "../../node_modules/@xmtp/content-type-remote-attachment/dist/index.js"() {
112454
113102
  init_dist5();
112455
113103
  init_secp256k12();
112456
113104
  import_proto = __toESM(require_node4(), 1);
112457
- import_node_crypto16 = require("node:crypto");
113105
+ import_node_crypto17 = require("node:crypto");
112458
113106
  ContentTypeAttachment = new ContentTypeId({
112459
113107
  authorityId: "xmtp.org",
112460
113108
  typeId: "attachment",
@@ -112534,7 +113182,7 @@ var init_dist6 = __esm({
112534
113182
  if (payload.length === 0) {
112535
113183
  throw new Error(`no payload for remote attachment at ${remoteAttachment.url}`);
112536
113184
  }
112537
- const digestBytes = new Uint8Array(await import_node_crypto16.webcrypto.subtle.digest("SHA-256", payload));
113185
+ const digestBytes = new Uint8Array(await import_node_crypto17.webcrypto.subtle.digest("SHA-256", payload));
112538
113186
  const digest = etc.bytesToHex(digestBytes);
112539
113187
  if (digest !== remoteAttachment.contentDigest) {
112540
113188
  throw new Error("content digest does not match");
@@ -112558,7 +113206,7 @@ var init_dist6 = __esm({
112558
113206
  return codec.decode(encodedContent, codecRegistry);
112559
113207
  }
112560
113208
  static async encodeEncrypted(content$1, codec) {
112561
- const secret = import_node_crypto16.webcrypto.getRandomValues(new Uint8Array(32));
113209
+ const secret = import_node_crypto17.webcrypto.getRandomValues(new Uint8Array(32));
112562
113210
  const encodedContent = import_proto.content.EncodedContent.encode(codec.encode(content$1, {
112563
113211
  codecFor() {
112564
113212
  return void 0;
@@ -112571,7 +113219,7 @@ var init_dist6 = __esm({
112571
113219
  if (!salt || !nonce || !payload) {
112572
113220
  throw new Error("missing encryption key");
112573
113221
  }
112574
- const digestBytes = new Uint8Array(await import_node_crypto16.webcrypto.subtle.digest("SHA-256", payload));
113222
+ const digestBytes = new Uint8Array(await import_node_crypto17.webcrypto.subtle.digest("SHA-256", payload));
112575
113223
  const digest = etc.bytesToHex(digestBytes);
112576
113224
  return {
112577
113225
  digest,
@@ -112789,7 +113437,7 @@ async function uploadFile(params) {
112789
113437
  const attachment = { filename, mimeType, data: new Uint8Array(data) };
112790
113438
  const encrypted = await RemoteAttachmentCodec.encodeEncrypted(attachment, new AttachmentCodec());
112791
113439
  ensureFileDirs();
112792
- const encryptedPath = (0, import_node_path33.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto17.randomUUID)()}.enc`);
113440
+ const encryptedPath = (0, import_node_path33.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto18.randomUUID)()}.enc`);
112793
113441
  (0, import_node_fs27.writeFileSync)(encryptedPath, encrypted.payload);
112794
113442
  try {
112795
113443
  const stdout = runOnchainos([
@@ -112833,7 +113481,7 @@ async function uploadFile(params) {
112833
113481
  }
112834
113482
  async function downloadFile(params) {
112835
113483
  ensureFileDirs();
112836
- const encryptedPath = (0, import_node_path33.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto17.randomUUID)()}.enc`);
113484
+ const encryptedPath = (0, import_node_path33.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto18.randomUUID)()}.enc`);
112837
113485
  try {
112838
113486
  const stdout = runOnchainos([
112839
113487
  "agent",
@@ -112858,7 +113506,7 @@ async function downloadFile(params) {
112858
113506
  throw new Error(`file download failed: ${stdout}`);
112859
113507
  }
112860
113508
  const payload = new Uint8Array((0, import_node_fs27.readFileSync)(encryptedPath));
112861
- const digestBytes = new Uint8Array(await import_node_crypto17.webcrypto.subtle.digest("SHA-256", payload));
113509
+ const digestBytes = new Uint8Array(await import_node_crypto18.webcrypto.subtle.digest("SHA-256", payload));
112862
113510
  const actualDigest = Array.from(digestBytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
112863
113511
  if (actualDigest !== params.digest) {
112864
113512
  throw new Error(`file download digest verification failed: expected=${params.digest}, actual=${actualDigest}`);
@@ -112866,7 +113514,7 @@ async function downloadFile(params) {
112866
113514
  const decryptedBytes = await decryptAttachmentPayload(payload, params);
112867
113515
  const encodedContent = import_proto2.content.EncodedContent.decode(decryptedBytes);
112868
113516
  const attachment = new AttachmentCodec().decode(encodedContent);
112869
- const outputFilename = params.filename || attachment.filename || `${(0, import_node_crypto17.randomUUID)()}.bin`;
113517
+ const outputFilename = params.filename || attachment.filename || `${(0, import_node_crypto18.randomUUID)()}.bin`;
112870
113518
  const outputDir = DOWNLOADS_DIR;
112871
113519
  ensureA2aTaskDir(outputDir);
112872
113520
  const outputPath = (0, import_node_path33.resolve)(outputDir, (0, import_node_path33.basename)(outputFilename));
@@ -112883,15 +113531,15 @@ async function decryptAttachmentPayload(payload, params) {
112883
113531
  const secretBytes = new Uint8Array(Buffer.from(params.secret, "base64"));
112884
113532
  const saltBytes = new Uint8Array(Buffer.from(params.salt, "base64"));
112885
113533
  const nonceBytes = new Uint8Array(Buffer.from(params.nonce, "base64"));
112886
- const hkdfKey = await import_node_crypto17.webcrypto.subtle.importKey("raw", secretBytes, "HKDF", false, ["deriveKey"]);
112887
- const aesKey = await import_node_crypto17.webcrypto.subtle.deriveKey(
113534
+ const hkdfKey = await import_node_crypto18.webcrypto.subtle.importKey("raw", secretBytes, "HKDF", false, ["deriveKey"]);
113535
+ const aesKey = await import_node_crypto18.webcrypto.subtle.deriveKey(
112888
113536
  { name: "HKDF", hash: "SHA-256", salt: saltBytes, info: new Uint8Array().buffer },
112889
113537
  hkdfKey,
112890
113538
  { name: "AES-GCM", length: 256 },
112891
113539
  false,
112892
113540
  ["decrypt"]
112893
113541
  );
112894
- return new Uint8Array(await import_node_crypto17.webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonceBytes }, aesKey, toArrayBuffer(payload)));
113542
+ return new Uint8Array(await import_node_crypto18.webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonceBytes }, aesKey, toArrayBuffer(payload)));
112895
113543
  }
112896
113544
  function toArrayBuffer(bytes) {
112897
113545
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
@@ -113031,12 +113679,12 @@ function readRequiredOption(args, name) {
113031
113679
  }
113032
113680
  return value;
113033
113681
  }
113034
- var import_node_crypto17, import_node_fs27, import_node_path33, import_proto2, OKX_A2A_PATHS, OKX_A2A_HOME_DIR, FILE_WORK_DIR, DOWNLOADS_DIR, SYSTEM_CONFIG_PATH;
113682
+ var import_node_crypto18, import_node_fs27, import_node_path33, import_proto2, OKX_A2A_PATHS, OKX_A2A_HOME_DIR, FILE_WORK_DIR, DOWNLOADS_DIR, SYSTEM_CONFIG_PATH;
113035
113683
  var init_file_cli = __esm({
113036
113684
  "src/file-cli.ts"() {
113037
113685
  "use strict";
113038
113686
  init_win_spawn();
113039
- import_node_crypto17 = require("node:crypto");
113687
+ import_node_crypto18 = require("node:crypto");
113040
113688
  import_node_fs27 = require("node:fs");
113041
113689
  import_node_path33 = require("node:path");
113042
113690
  init_dist6();
@@ -116519,7 +117167,7 @@ async function getCurrentNodeCliVersion() {
116519
117167
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
116520
117168
  }
116521
117169
  function getBundledNodeCliVersion() {
116522
- return true ? "0.2.8" : null;
117170
+ return true ? "0.2.9-beta-912b775358-260824171011" : null;
116523
117171
  }
116524
117172
  function readConfiguredAiProvider() {
116525
117173
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -116729,7 +117377,7 @@ async function updateHermes(release, options) {
116729
117377
  }
116730
117378
  }
116731
117379
  async function installGatewayPluginForDoctor(target) {
116732
- const release = isPrereleaseVersion("0.2.8") ? "beta" : "latest";
117380
+ const release = isPrereleaseVersion("0.2.9-beta-912b775358-260824171011") ? "beta" : "latest";
116733
117381
  const insideTargetGateway = detectGatewayInvocation() === target;
116734
117382
  const options = {
116735
117383
  restart: !insideTargetGateway,
@@ -117782,7 +118430,7 @@ async function runDoctor(options = {}) {
117782
118430
  platform: options.platform ?? process.platform,
117783
118431
  env: options.env ?? process.env,
117784
118432
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
117785
- cliVersion: options.cliVersion ?? (true ? "0.2.8" : "0.0.0"),
118433
+ cliVersion: options.cliVersion ?? (true ? "0.2.9-beta-912b775358-260824171011" : "0.0.0"),
117786
118434
  fixMode: options.fix === true,
117787
118435
  nonInteractive: options.nonInteractive === true,
117788
118436
  packageChanged: false,
@@ -118810,7 +119458,7 @@ init_sentry_config();
118810
119458
  init_runtime_metadata();
118811
119459
  var CURRENT_GATEWAY_SESSION_KEYS_ENV4 = "OKX_A2A_CURRENT_GATEWAY_SESSION_KEYS";
118812
119460
  function printUsage3() {
118813
- console.log(`okx-a2a ${"0.2.8"}
119461
+ console.log(`okx-a2a ${"0.2.9-beta-912b775358-260824171011"}
118814
119462
 
118815
119463
  Usage:
118816
119464
  okx-a2a <command> [options]
@@ -118850,7 +119498,7 @@ Run \`okx-a2a <command> -h\` for command-specific help.
118850
119498
  `);
118851
119499
  }
118852
119500
  function printVersion() {
118853
- console.log("0.2.8");
119501
+ console.log("0.2.9-beta-912b775358-260824171011");
118854
119502
  }
118855
119503
  function printDaemonUsage() {
118856
119504
  console.log(`Usage: okx-a2a daemon <start|restart|stop|status|autostart> [options]
@@ -120331,7 +120979,7 @@ async function main() {
120331
120979
  if (command === "xmtp-test") {
120332
120980
  const { handleXmtpTestCommand: handleXmtpTestCommand2 } = await Promise.resolve().then(() => (init_xmtp_test_cli(), xmtp_test_cli_exports));
120333
120981
  await handleXmtpTestCommand2(process.argv.slice(3), {
120334
- packageVersion: "0.2.8",
120982
+ packageVersion: "0.2.9-beta-912b775358-260824171011",
120335
120983
  agentSdkVersion: "2.3.0",
120336
120984
  nodeSdkVersion: "6.1.0",
120337
120985
  nodeBindingsVersion: "1.11.0"