@okxweb3/a2a-node 0.1.4-beta-520175e226-260703171232 → 0.1.4

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 +257 -56
  2. package/dist/index.js +53 -10
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -154,6 +154,20 @@ function buildSessionKey(input) {
154
154
  encodeURIComponent(input.toAgentId || "unknown")
155
155
  ].join(":");
156
156
  }
157
+ function isSqliteBusyError(err2) {
158
+ if (!(err2 instanceof Error)) {
159
+ return false;
160
+ }
161
+ const code2 = err2.errcode;
162
+ if (typeof code2 === "number" && ((code2 & 255) === 5 || (code2 & 255) === 6)) {
163
+ return true;
164
+ }
165
+ return /database is locked|database table is locked/i.test(err2.message);
166
+ }
167
+ function sleepSyncMs(ms) {
168
+ const buf = new Int32Array(new SharedArrayBuffer(4));
169
+ Atomics.wait(buf, 0, 0, ms);
170
+ }
157
171
  function loadSqlite() {
158
172
  const originalEmitWarning = process.emitWarning;
159
173
  process.emitWarning = ((warning, ...args) => {
@@ -606,7 +620,28 @@ var init_session_store = __esm({
606
620
  this.db = new DatabaseSync(this.dbPath);
607
621
  this.ensureReady();
608
622
  }
623
+ // Opening the store races other processes writing the same DB (daemon
624
+ // coordinator scans, other CLI watches). Two busy flavors both crash an
625
+ // unprotected open: schema DDL starving past busy_timeout, and the WAL
626
+ // journal-mode conversion which returns SQLITE_BUSY IMMEDIATELY under any
627
+ // concurrent writer (busy_timeout does not apply to it). Retry bounded with
628
+ // exponential backoff so the cumulative window (~7.75s) rides out real
629
+ // contention bursts instead of crashing on arrival.
609
630
  ensureReady() {
631
+ const attempts = 6;
632
+ for (let attempt = 1; ; attempt += 1) {
633
+ try {
634
+ this.ensureReadyOnce();
635
+ return;
636
+ } catch (err2) {
637
+ if (!isSqliteBusyError(err2) || attempt >= attempts) {
638
+ throw err2;
639
+ }
640
+ sleepSyncMs(Math.min(250 * 2 ** (attempt - 1), 4e3));
641
+ }
642
+ }
643
+ }
644
+ ensureReadyOnce() {
610
645
  this.db.exec("PRAGMA busy_timeout=5000");
611
646
  this.db.exec("PRAGMA journal_mode=WAL");
612
647
  this.db.exec("PRAGMA foreign_keys=ON");
@@ -1281,13 +1316,19 @@ var init_session_store = __esm({
1281
1316
  };
1282
1317
  });
1283
1318
  }
1319
+ // Returns null when the watcher row no longer exists (e.g. TTL-expired after
1320
+ // heartbeats starved on a busy database) so the caller can re-register
1321
+ // instead of polling a row that will never receive events again.
1284
1322
  heartbeatUserAttentionWatcher(watcher, nowMs) {
1285
1323
  assertNonEmpty(watcher.id, "id");
1286
- this.db.prepare(`
1324
+ const result = this.db.prepare(`
1287
1325
  UPDATE user_attention_watchers
1288
1326
  SET updated_at_ms = ?
1289
1327
  WHERE id = ?
1290
1328
  `).run(nowMs, watcher.id);
1329
+ if (Number(result.changes) === 0) {
1330
+ return null;
1331
+ }
1291
1332
  return {
1292
1333
  ...watcher,
1293
1334
  updatedAt: nowMs
@@ -8237,7 +8278,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
8237
8278
  client: {
8238
8279
  id: "gateway-client",
8239
8280
  displayName: "okx-a2a-node",
8240
- version: "0.1.4-beta-520175e226-260703171232",
8281
+ version: "0.1.4",
8241
8282
  platform: "node",
8242
8283
  mode: "backend",
8243
8284
  instanceId
@@ -8248,7 +8289,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
8248
8289
  commands: [],
8249
8290
  permissions: {},
8250
8291
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
8251
- userAgent: `okx-a2a-node/${"0.1.4-beta-520175e226-260703171232"}`,
8292
+ userAgent: `okx-a2a-node/${"0.1.4"}`,
8252
8293
  auth: {
8253
8294
  ...config.token ? { token: config.token } : {},
8254
8295
  ...config.password ? { password: config.password } : {}
@@ -25504,7 +25545,7 @@ var init_sentry_config = __esm({
25504
25545
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
25505
25546
  SENTRY_CONFIG = {
25506
25547
  projectName: "okx/openclaw-okx-a2a-extension",
25507
- release: "0.1.4-beta-520175e226-260703171232",
25548
+ release: "0.1.4",
25508
25549
  environment
25509
25550
  };
25510
25551
  }
@@ -94014,12 +94055,12 @@ async function runListenerWithLock(options, paths) {
94014
94055
  }));
94015
94056
  }
94016
94057
  });
94017
- service.setPluginVersion("0.1.4-beta-520175e226-260703171232");
94058
+ service.setPluginVersion("0.1.4");
94018
94059
  await service.init();
94019
94060
  const pluginVersionStatus = service.pluginVersionStatus;
94020
94061
  if (pluginVersionStatus.unavailable) {
94021
94062
  throw new Error(
94022
- `@okxweb3/a2a-node v${"0.1.4-beta-520175e226-260703171232"} is below the required minimum v${pluginVersionStatus.minVersion}`
94063
+ `@okxweb3/a2a-node v${"0.1.4"} is below the required minimum v${pluginVersionStatus.minVersion}`
94023
94064
  );
94024
94065
  }
94025
94066
  const systemConfig = service.getSystemConfig();
@@ -94037,7 +94078,7 @@ async function runListenerWithLock(options, paths) {
94037
94078
  onchainosAgentId: "*",
94038
94079
  reason: "system-config missing sentryDsn",
94039
94080
  pluginId: "@okxweb3/a2a-node",
94040
- pluginVersion: "0.1.4-beta-520175e226-260703171232"
94081
+ pluginVersion: "0.1.4"
94041
94082
  });
94042
94083
  }
94043
94084
  logWithTimestamp(
@@ -95147,13 +95188,20 @@ async function watchUserAttentionLoop(store, parsed, json, options = {}) {
95147
95188
  if (!parentMonitor.isAlive()) {
95148
95189
  return;
95149
95190
  }
95150
- const batch = store.consumeUnseenUserAttentionForWatch(
95151
- options.jobId || options.provider ? {
95152
- ...options.provider ? { provider: options.provider } : {},
95153
- ...options.jobId ? { jobId: options.jobId } : {}
95154
- } : void 0
95155
- );
95156
- if (batch.items.length > 0) {
95191
+ let batch = null;
95192
+ try {
95193
+ batch = store.consumeUnseenUserAttentionForWatch(
95194
+ options.jobId || options.provider ? {
95195
+ ...options.provider ? { provider: options.provider } : {},
95196
+ ...options.jobId ? { jobId: options.jobId } : {}
95197
+ } : void 0
95198
+ );
95199
+ } catch (err2) {
95200
+ if (!isSqliteBusyError(err2)) {
95201
+ throw err2;
95202
+ }
95203
+ }
95204
+ if (batch && batch.items.length > 0) {
95157
95205
  outputAttentionItems(json, batch.items, "User attention needed");
95158
95206
  return;
95159
95207
  }
@@ -95177,7 +95225,7 @@ async function watchUserAttentionViaDaemon(store, parsed, json, options) {
95177
95225
  const timeoutMs = (readNumberOption(parsed, "timeout") ?? 0) * 1e3;
95178
95226
  const pollMs = readNumberOption(parsed, "poll-ms") ?? 500;
95179
95227
  const deadline = timeoutMs > 0 ? Date.now() + timeoutMs : null;
95180
- const registration = registerUserAttentionWatcher({
95228
+ const registration = await registerWatcherWithBusyRetry({
95181
95229
  store,
95182
95230
  provider: options.provider,
95183
95231
  jobId: options.jobId
@@ -95205,7 +95253,14 @@ async function watchUserAttentionViaDaemon(store, parsed, json, options) {
95205
95253
  if (!parentMonitor.isAlive()) {
95206
95254
  return;
95207
95255
  }
95208
- const event = readUserAttentionWatcherEvent(store, watcher.id);
95256
+ let event = null;
95257
+ try {
95258
+ event = readUserAttentionWatcherEvent(store, watcher.id);
95259
+ } catch (err2) {
95260
+ if (!isSqliteBusyError(err2)) {
95261
+ throw err2;
95262
+ }
95263
+ }
95209
95264
  if (event) {
95210
95265
  outputWatcherEvent(json, event);
95211
95266
  return;
@@ -95220,7 +95275,23 @@ async function watchUserAttentionViaDaemon(store, parsed, json, options) {
95220
95275
  return;
95221
95276
  }
95222
95277
  if (Date.now() >= nextHeartbeatAt) {
95223
- watcher = heartbeatUserAttentionWatcher(store, watcher);
95278
+ try {
95279
+ const next = heartbeatUserAttentionWatcher(store, watcher);
95280
+ if (next) {
95281
+ watcher = next;
95282
+ } else {
95283
+ watcher = registerUserAttentionWatcher({
95284
+ store,
95285
+ provider: options.provider,
95286
+ jobId: options.jobId,
95287
+ id: watcher.id
95288
+ }).watcher;
95289
+ }
95290
+ } catch (err2) {
95291
+ if (!isSqliteBusyError(err2)) {
95292
+ throw err2;
95293
+ }
95294
+ }
95224
95295
  nextHeartbeatAt = Date.now() + Math.min(1e3, Math.max(100, pollMs));
95225
95296
  }
95226
95297
  const waitMs = deadline ? Math.max(0, Math.min(pollMs, USER_ATTENTION_WATCH_PARENT_CHECK_MS, deadline - Date.now())) : Math.min(pollMs, USER_ATTENTION_WATCH_PARENT_CHECK_MS);
@@ -95231,6 +95302,23 @@ async function watchUserAttentionViaDaemon(store, parsed, json, options) {
95231
95302
  watcherCleanup.dispose();
95232
95303
  }
95233
95304
  }
95305
+ async function registerWatcherWithBusyRetry(options) {
95306
+ const attempts = 3;
95307
+ for (let attempt = 1; ; attempt += 1) {
95308
+ try {
95309
+ return registerUserAttentionWatcher({
95310
+ store: options.store,
95311
+ provider: options.provider,
95312
+ jobId: options.jobId
95313
+ });
95314
+ } catch (err2) {
95315
+ if (!isSqliteBusyError(err2) || attempt >= attempts) {
95316
+ throw err2;
95317
+ }
95318
+ await sleep3(250 * attempt);
95319
+ }
95320
+ }
95321
+ }
95234
95322
  function installWatcherProcessCleanup(store, getWatcher) {
95235
95323
  let cleaned = false;
95236
95324
  const cleanup = () => {
@@ -95240,7 +95328,10 @@ function installWatcherProcessCleanup(store, getWatcher) {
95240
95328
  cleaned = true;
95241
95329
  const watcher = getWatcher();
95242
95330
  if (watcher) {
95243
- removeUserAttentionWatcher(store, watcher.id);
95331
+ try {
95332
+ removeUserAttentionWatcher(store, watcher.id);
95333
+ } catch {
95334
+ }
95244
95335
  }
95245
95336
  };
95246
95337
  const onExit = () => {
@@ -96411,8 +96502,8 @@ function bindHermesSessionRouteIfAvailable(store, session) {
96411
96502
  if (!jobId || process.env.OKX_A2A_GATEWAY_PROVIDER !== "hermes" && !hasHermesRuntimeMarker()) {
96412
96503
  return;
96413
96504
  }
96414
- const route = currentHermesRouteFromEnv2();
96415
- if (!route) {
96505
+ const routes = currentHermesRoutesFromEnv2();
96506
+ if (routes.length === 0) {
96416
96507
  logger.info(LogEvent.HERMES_SESSION_ROUTE_BINDING, sessionCliSentryExtra("hermes_session_route_binding", {
96417
96508
  status: "skipped",
96418
96509
  reason: "missing_route",
@@ -96430,26 +96521,53 @@ function bindHermesSessionRouteIfAvailable(store, session) {
96430
96521
  const sessionKeys = /* @__PURE__ */ new Set();
96431
96522
  sessionKeys.add(session.sessionKey);
96432
96523
  sessionKeys.add(buildBackupJobSessionKey(jobId));
96433
- persistHermesRouteBinding2(store, { jobId, route });
96524
+ for (const route of routes) {
96525
+ persistHermesRouteBinding2(store, { jobId, route, sessionAliases: [...sessionKeys] });
96526
+ }
96434
96527
  logger.info(LogEvent.HERMES_SESSION_ROUTE_BINDING, sessionCliSentryExtra("hermes_session_route_binding", {
96435
96528
  status: "bound",
96436
96529
  sessionKey: session.sessionKey,
96437
96530
  backupSessionKey: buildBackupJobSessionKey(jobId),
96438
96531
  jobId,
96439
- gatewaySessionKey: route.gateway_session_key ?? route.session_key,
96440
- gatewayPlatform: route.platform,
96441
- gatewayChatId: route.chat_id,
96442
- gatewayThreadId: route.thread_id,
96443
- sessionKeyCount: String(sessionKeys.size)
96532
+ gatewaySessionKey: routes.map((route) => route.gateway_session_key ?? route.session_key).join(","),
96533
+ gatewayPlatform: routes.map((route) => route.platform).join(","),
96534
+ gatewayChatId: routes.map((route) => route.chat_id).join(","),
96535
+ gatewayThreadId: routes.map((route) => route.thread_id).join(","),
96536
+ sessionKeyCount: String(sessionKeys.size),
96537
+ routeCount: String(routes.length)
96444
96538
  }));
96445
96539
  }
96446
- function currentHermesRouteFromEnv2() {
96447
- const platform = normalizeOptionalText4(process.env.OKX_A2A_CURRENT_GATEWAY_PLATFORM);
96448
- const chatId = normalizeOptionalText4(process.env.OKX_A2A_CURRENT_GATEWAY_CHAT_ID);
96540
+ function currentHermesRoutesFromEnv2(env = process.env) {
96541
+ const routes = [];
96542
+ const append = (route) => {
96543
+ if (!route) {
96544
+ return;
96545
+ }
96546
+ const key = `${route.platform}
96547
+ ${route.chat_id}
96548
+ ${route.thread_id}`;
96549
+ if (routes.some((existing) => `${existing.platform}
96550
+ ${existing.chat_id}
96551
+ ${existing.thread_id}` === key)) {
96552
+ return;
96553
+ }
96554
+ routes.push(route);
96555
+ };
96556
+ for (const sessionKey of readGatewaySessionKeysFromEnv2(env)) {
96557
+ append(routeFromHermesSessionKey2(sessionKey));
96558
+ }
96559
+ append(currentHermesRouteFromEnv2(env));
96560
+ append(routeFromHermesSessionEnv(env));
96561
+ append(routeFromHermesSessionKey2(env.HERMES_SESSION_KEY));
96562
+ return routes;
96563
+ }
96564
+ function currentHermesRouteFromEnv2(env = process.env) {
96565
+ const platform = normalizeOptionalText4(env.OKX_A2A_CURRENT_GATEWAY_PLATFORM);
96566
+ const chatId = normalizeOptionalText4(env.OKX_A2A_CURRENT_GATEWAY_CHAT_ID);
96449
96567
  if (platform && chatId) {
96450
- const sessionKey = normalizeOptionalText4(process.env.OKX_A2A_CURRENT_GATEWAY_SESSION_KEY);
96568
+ const sessionKey = normalizeOptionalText4(env.OKX_A2A_CURRENT_GATEWAY_SESSION_KEY);
96451
96569
  const parsedGatewaySession = parseHermesGatewaySessionKey3(sessionKey);
96452
- const threadId = normalizeOptionalText4(process.env.OKX_A2A_CURRENT_GATEWAY_THREAD_ID) ?? matchingParsedThreadId3(parsedGatewaySession, { platform, chatId });
96570
+ const threadId = normalizeOptionalText4(env.OKX_A2A_CURRENT_GATEWAY_THREAD_ID) ?? matchingParsedThreadId3(parsedGatewaySession, { platform, chatId });
96453
96571
  return buildHermesRouteEntry({
96454
96572
  platform,
96455
96573
  chatId,
@@ -96457,11 +96575,15 @@ function currentHermesRouteFromEnv2() {
96457
96575
  sessionKey
96458
96576
  });
96459
96577
  }
96460
- return routeFromHermesSessionEnv() ?? routeFromHermesSessionKey2(process.env.HERMES_SESSION_KEY);
96578
+ return null;
96461
96579
  }
96462
96580
  function buildHermesRouteEntry(input) {
96463
96581
  const chatType = input.threadId ? "thread" : "dm";
96464
- const gatewaySessionKey = input.sessionKey ?? (input.threadId ? `agent:main:${input.platform}:thread:${encodeURIComponent(input.chatId)}:${encodeURIComponent(input.threadId)}` : `agent:main:${input.platform}:dm:${encodeURIComponent(input.chatId)}`);
96582
+ const gatewaySessionKey = input.sessionKey ?? hermesGatewaySessionKey2({
96583
+ platform: input.platform,
96584
+ chatId: input.chatId,
96585
+ threadId: input.threadId
96586
+ });
96465
96587
  return {
96466
96588
  platform: input.platform,
96467
96589
  chat_id: input.chatId,
@@ -96474,19 +96596,19 @@ function buildHermesRouteEntry(input) {
96474
96596
  gateway_session_key: gatewaySessionKey
96475
96597
  };
96476
96598
  }
96477
- function routeFromHermesSessionEnv() {
96478
- const platform = normalizeOptionalText4(process.env.HERMES_SESSION_PLATFORM);
96479
- const chatId = normalizeOptionalText4(process.env.HERMES_SESSION_CHAT_ID);
96599
+ function routeFromHermesSessionEnv(env = process.env) {
96600
+ const platform = normalizeOptionalText4(env.HERMES_SESSION_PLATFORM);
96601
+ const chatId = normalizeOptionalText4(env.HERMES_SESSION_CHAT_ID);
96480
96602
  if (!platform || !chatId) {
96481
96603
  return null;
96482
96604
  }
96483
96605
  return buildHermesRouteEntry({
96484
96606
  platform,
96485
96607
  chatId,
96486
- sessionKey: normalizeOptionalText4(process.env.HERMES_SESSION_KEY),
96487
- chatName: normalizeOptionalText4(process.env.HERMES_SESSION_CHAT_NAME),
96488
- userId: normalizeOptionalText4(process.env.HERMES_SESSION_USER_ID),
96489
- userName: normalizeOptionalText4(process.env.HERMES_SESSION_USER_NAME)
96608
+ sessionKey: normalizeOptionalText4(env.HERMES_SESSION_KEY),
96609
+ chatName: normalizeOptionalText4(env.HERMES_SESSION_CHAT_NAME),
96610
+ userId: normalizeOptionalText4(env.HERMES_SESSION_USER_ID),
96611
+ userName: normalizeOptionalText4(env.HERMES_SESSION_USER_NAME)
96490
96612
  });
96491
96613
  }
96492
96614
  function routeFromHermesSessionKey2(value) {
@@ -96498,7 +96620,49 @@ function routeFromHermesSessionKey2(value) {
96498
96620
  if (!parsed) {
96499
96621
  return null;
96500
96622
  }
96501
- return buildHermesRouteEntry({ ...parsed, sessionKey });
96623
+ return buildHermesRouteEntry({
96624
+ ...parsed,
96625
+ sessionKey: hermesGatewaySessionKey2({
96626
+ platform: parsed.platform,
96627
+ chatId: parsed.chatId,
96628
+ threadId: parsed.threadId ?? ""
96629
+ })
96630
+ });
96631
+ }
96632
+ function readGatewaySessionKeysFromEnv2(env) {
96633
+ const keys = /* @__PURE__ */ new Set();
96634
+ const list = normalizeOptionalText4(env[CURRENT_GATEWAY_SESSION_KEYS_ENV3]);
96635
+ if (list) {
96636
+ try {
96637
+ const parsed = JSON.parse(list);
96638
+ if (Array.isArray(parsed)) {
96639
+ for (const item of parsed) {
96640
+ if (typeof item === "string" && item.trim()) {
96641
+ keys.add(item.trim());
96642
+ }
96643
+ }
96644
+ }
96645
+ } catch {
96646
+ for (const item of list.split(",")) {
96647
+ if (item.trim()) {
96648
+ keys.add(item.trim());
96649
+ }
96650
+ }
96651
+ }
96652
+ }
96653
+ const single = normalizeOptionalText4(env.OKX_A2A_CURRENT_GATEWAY_SESSION_KEY);
96654
+ if (single) {
96655
+ keys.add(single);
96656
+ }
96657
+ return [...keys];
96658
+ }
96659
+ function hermesGatewaySessionKey2(input) {
96660
+ const chatId = encodeURIComponent(input.chatId);
96661
+ const threadId = input.threadId ? encodeURIComponent(input.threadId) : "";
96662
+ if (input.platform === "telegram") {
96663
+ return threadId ? `agent:main:telegram:dm:${chatId}:${threadId}` : `agent:main:telegram:dm:${chatId}`;
96664
+ }
96665
+ return threadId ? `agent:main:${input.platform}:thread:${chatId}:${threadId}` : `agent:main:${input.platform}:dm:${chatId}`;
96502
96666
  }
96503
96667
  function parseHermesGatewaySessionKey3(sessionKey) {
96504
96668
  const normalized = normalizeOptionalText4(sessionKey);
@@ -96506,18 +96670,25 @@ function parseHermesGatewaySessionKey3(sessionKey) {
96506
96670
  return null;
96507
96671
  }
96508
96672
  const parts = normalized.startsWith("agent:") ? normalized.split(":").slice(2) : normalized.split(":");
96509
- if (parts.length < 3) {
96673
+ if (parts.length < 2) {
96510
96674
  return null;
96511
96675
  }
96512
96676
  const [platform, scope, ...rest] = parts;
96513
96677
  if (!platform || platform === "okx-a2a" || platform === "backup" || platform === "job") {
96514
96678
  return null;
96515
96679
  }
96516
- if (scope === "dm" && rest[0]) {
96680
+ if (!isHermesGatewayScope2(scope)) {
96681
+ return {
96682
+ platform,
96683
+ chatId: safeDecodeURIComponent5(scope),
96684
+ ...rest.length > 0 ? { threadId: safeDecodeURIComponent5(rest.join(":")) } : {}
96685
+ };
96686
+ }
96687
+ if ((scope === "dm" || scope === "direct" || scope === "channel") && rest[0]) {
96517
96688
  return {
96518
96689
  platform,
96519
96690
  chatId: safeDecodeURIComponent5(rest[0]),
96520
- ...platform === "telegram" && rest[1] ? { threadId: safeDecodeURIComponent5(rest.slice(1).join(":")) } : {}
96691
+ ...platform === "telegram" && rest[1] ? { threadId: safeDecodeURIComponent5(parseTelegramTopicSuffix4(rest.slice(1))) } : {}
96521
96692
  };
96522
96693
  }
96523
96694
  if (scope === "thread" && rest[0] && rest[1]) {
@@ -96527,8 +96698,31 @@ function parseHermesGatewaySessionKey3(sessionKey) {
96527
96698
  threadId: safeDecodeURIComponent5(rest.slice(1).join(":"))
96528
96699
  };
96529
96700
  }
96701
+ if (platform === "telegram" && scope === "group" && rest[0]) {
96702
+ const threadId = parseTelegramTopicSuffix4(rest.slice(1));
96703
+ return {
96704
+ platform,
96705
+ chatId: safeDecodeURIComponent5(rest[0]),
96706
+ ...threadId ? { threadId: safeDecodeURIComponent5(threadId) } : {}
96707
+ };
96708
+ }
96530
96709
  return null;
96531
96710
  }
96711
+ function isHermesGatewayScope2(value) {
96712
+ return value === "dm" || value === "direct" || value === "channel" || value === "thread" || value === "group";
96713
+ }
96714
+ function parseTelegramTopicSuffix4(parts) {
96715
+ if (parts.length === 0) {
96716
+ return "";
96717
+ }
96718
+ if (parts[0] === "topic") {
96719
+ return parts[1] ?? "";
96720
+ }
96721
+ if (parts[0] === "thread") {
96722
+ return parts.length >= 3 ? parts.slice(2).join(":") : parts[1] ?? "";
96723
+ }
96724
+ return parts.join(":");
96725
+ }
96532
96726
  function matchingParsedThreadId3(parsed, expected) {
96533
96727
  if (!parsed || parsed.platform !== expected.platform || parsed.chatId !== expected.chatId) {
96534
96728
  return null;
@@ -96556,10 +96750,10 @@ function persistHermesRouteBinding2(store, input) {
96556
96750
  store.upsertJobGatewayRoute({
96557
96751
  jobId: input.jobId,
96558
96752
  provider: "hermes",
96559
- route: hermesRouteEntryToGatewayRoute2(input.route)
96753
+ route: hermesRouteEntryToGatewayRoute2(input.route, input.sessionAliases)
96560
96754
  });
96561
96755
  }
96562
- function hermesRouteEntryToGatewayRoute2(route) {
96756
+ function hermesRouteEntryToGatewayRoute2(route, sessionAliases = []) {
96563
96757
  return {
96564
96758
  platform: route.platform,
96565
96759
  chatId: route.chat_id,
@@ -96569,7 +96763,12 @@ function hermesRouteEntryToGatewayRoute2(route) {
96569
96763
  userId: route.user_id,
96570
96764
  userName: route.user_name,
96571
96765
  sessionKey: route.session_key,
96572
- gatewaySessionKey: route.gateway_session_key
96766
+ gatewaySessionKey: route.gateway_session_key,
96767
+ sessionAliases: [
96768
+ ...new Set(
96769
+ [...route.session_aliases ?? [], ...sessionAliases].map((value) => value.trim()).filter(Boolean)
96770
+ )
96771
+ ]
96573
96772
  };
96574
96773
  }
96575
96774
  function hasHermesRuntimeMarker(env = process.env) {
@@ -96772,6 +96971,7 @@ function readNumberOption2(parsed, name2) {
96772
96971
  }
96773
96972
  return value;
96774
96973
  }
96974
+ var CURRENT_GATEWAY_SESSION_KEYS_ENV3;
96775
96975
  var init_session_cli = __esm({
96776
96976
  "src/session-cli.ts"() {
96777
96977
  "use strict";
@@ -96787,6 +96987,7 @@ var init_session_cli = __esm({
96787
96987
  init_openclaw_gateway();
96788
96988
  init_sentry_logger();
96789
96989
  init_openclaw_gateway_config();
96990
+ CURRENT_GATEWAY_SESSION_KEYS_ENV3 = "OKX_A2A_CURRENT_GATEWAY_SESSION_KEYS";
96790
96991
  }
96791
96992
  });
96792
96993
 
@@ -97672,7 +97873,7 @@ async function getCurrentNodeCliVersion() {
97672
97873
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
97673
97874
  }
97674
97875
  function getBundledNodeCliVersion() {
97675
- return true ? "0.1.4-beta-520175e226-260703171232" : null;
97876
+ return true ? "0.1.4" : null;
97676
97877
  }
97677
97878
  function readConfiguredAiProvider() {
97678
97879
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -97863,7 +98064,7 @@ async function updateHermes(release, options) {
97863
98064
  }
97864
98065
  }
97865
98066
  async function installGatewayPluginForDoctor(target) {
97866
- const release = isPrereleaseVersion("0.1.4-beta-520175e226-260703171232") ? "beta" : "latest";
98067
+ const release = isPrereleaseVersion("0.1.4") ? "beta" : "latest";
97867
98068
  const insideTargetGateway = detectGatewayInvocation() === target;
97868
98069
  const options = {
97869
98070
  restart: !insideTargetGateway,
@@ -98936,7 +99137,7 @@ async function runDoctor(options = {}) {
98936
99137
  platform: options.platform ?? process.platform,
98937
99138
  env: options.env ?? process.env,
98938
99139
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
98939
- cliVersion: options.cliVersion ?? (true ? "0.1.4-beta-520175e226-260703171232" : "0.0.0"),
99140
+ cliVersion: options.cliVersion ?? (true ? "0.1.4" : "0.0.0"),
98940
99141
  fixMode: options.fix === true,
98941
99142
  packageChanged: false
98942
99143
  };
@@ -100006,9 +100207,9 @@ async function readFully(handle, buffer2, position) {
100006
100207
  init_win_spawn();
100007
100208
  init_sentry_logger();
100008
100209
  init_sentry_config();
100009
- var CURRENT_GATEWAY_SESSION_KEYS_ENV3 = "OKX_A2A_CURRENT_GATEWAY_SESSION_KEYS";
100210
+ var CURRENT_GATEWAY_SESSION_KEYS_ENV4 = "OKX_A2A_CURRENT_GATEWAY_SESSION_KEYS";
100010
100211
  function printUsage2() {
100011
- console.log(`okx-a2a ${"0.1.4-beta-520175e226-260703171232"}
100212
+ console.log(`okx-a2a ${"0.1.4"}
100012
100213
 
100013
100214
  Usage:
100014
100215
  okx-a2a <command> [options]
@@ -100046,7 +100247,7 @@ Run \`okx-a2a <command> -h\` for command-specific help.
100046
100247
  `);
100047
100248
  }
100048
100249
  function printVersion() {
100049
- console.log("0.1.4-beta-520175e226-260703171232");
100250
+ console.log("0.1.4");
100050
100251
  }
100051
100252
  function printDaemonUsage() {
100052
100253
  console.log(`Usage: okx-a2a daemon <start|restart|stop|status|autostart> [options]
@@ -100454,7 +100655,7 @@ async function queueXmtpSend(args) {
100454
100655
  myAgentId: target.myAgentId,
100455
100656
  toAgentId: target.toAgentId,
100456
100657
  toXmtpAddress: toXmtpAddress ?? target.toXmtpAddress,
100457
- gatewaySessionKeys: readGatewaySessionKeysFromEnv2(),
100658
+ gatewaySessionKeys: readGatewaySessionKeysFromEnv3(),
100458
100659
  payload: payloadOverride
100459
100660
  });
100460
100661
  await commands.submit(command);
@@ -100489,9 +100690,9 @@ function parseXmtpSendPayloadOption(raw) {
100489
100690
  return void 0;
100490
100691
  }
100491
100692
  }
100492
- function readGatewaySessionKeysFromEnv2(env = process.env) {
100693
+ function readGatewaySessionKeysFromEnv3(env = process.env) {
100493
100694
  const keys = /* @__PURE__ */ new Set();
100494
- const list = env[CURRENT_GATEWAY_SESSION_KEYS_ENV3]?.trim();
100695
+ const list = env[CURRENT_GATEWAY_SESSION_KEYS_ENV4]?.trim();
100495
100696
  if (list) {
100496
100697
  try {
100497
100698
  const parsed = JSON.parse(list);
package/dist/index.js CHANGED
@@ -154,6 +154,20 @@ function buildSessionKey(input) {
154
154
  encodeURIComponent(input.toAgentId || "unknown")
155
155
  ].join(":");
156
156
  }
157
+ function isSqliteBusyError(err2) {
158
+ if (!(err2 instanceof Error)) {
159
+ return false;
160
+ }
161
+ const code2 = err2.errcode;
162
+ if (typeof code2 === "number" && ((code2 & 255) === 5 || (code2 & 255) === 6)) {
163
+ return true;
164
+ }
165
+ return /database is locked|database table is locked/i.test(err2.message);
166
+ }
167
+ function sleepSyncMs(ms) {
168
+ const buf = new Int32Array(new SharedArrayBuffer(4));
169
+ Atomics.wait(buf, 0, 0, ms);
170
+ }
157
171
  function loadSqlite() {
158
172
  const originalEmitWarning = process.emitWarning;
159
173
  process.emitWarning = ((warning, ...args) => {
@@ -606,7 +620,28 @@ var init_session_store = __esm({
606
620
  this.db = new DatabaseSync(this.dbPath);
607
621
  this.ensureReady();
608
622
  }
623
+ // Opening the store races other processes writing the same DB (daemon
624
+ // coordinator scans, other CLI watches). Two busy flavors both crash an
625
+ // unprotected open: schema DDL starving past busy_timeout, and the WAL
626
+ // journal-mode conversion which returns SQLITE_BUSY IMMEDIATELY under any
627
+ // concurrent writer (busy_timeout does not apply to it). Retry bounded with
628
+ // exponential backoff so the cumulative window (~7.75s) rides out real
629
+ // contention bursts instead of crashing on arrival.
609
630
  ensureReady() {
631
+ const attempts = 6;
632
+ for (let attempt = 1; ; attempt += 1) {
633
+ try {
634
+ this.ensureReadyOnce();
635
+ return;
636
+ } catch (err2) {
637
+ if (!isSqliteBusyError(err2) || attempt >= attempts) {
638
+ throw err2;
639
+ }
640
+ sleepSyncMs(Math.min(250 * 2 ** (attempt - 1), 4e3));
641
+ }
642
+ }
643
+ }
644
+ ensureReadyOnce() {
610
645
  this.db.exec("PRAGMA busy_timeout=5000");
611
646
  this.db.exec("PRAGMA journal_mode=WAL");
612
647
  this.db.exec("PRAGMA foreign_keys=ON");
@@ -1281,13 +1316,19 @@ var init_session_store = __esm({
1281
1316
  };
1282
1317
  });
1283
1318
  }
1319
+ // Returns null when the watcher row no longer exists (e.g. TTL-expired after
1320
+ // heartbeats starved on a busy database) so the caller can re-register
1321
+ // instead of polling a row that will never receive events again.
1284
1322
  heartbeatUserAttentionWatcher(watcher, nowMs) {
1285
1323
  assertNonEmpty(watcher.id, "id");
1286
- this.db.prepare(`
1324
+ const result = this.db.prepare(`
1287
1325
  UPDATE user_attention_watchers
1288
1326
  SET updated_at_ms = ?
1289
1327
  WHERE id = ?
1290
1328
  `).run(nowMs, watcher.id);
1329
+ if (Number(result.changes) === 0) {
1330
+ return null;
1331
+ }
1291
1332
  return {
1292
1333
  ...watcher,
1293
1334
  updatedAt: nowMs
@@ -71569,7 +71610,7 @@ async function getCurrentNodeCliVersion() {
71569
71610
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
71570
71611
  }
71571
71612
  function getBundledNodeCliVersion() {
71572
- return true ? "0.1.4-beta-520175e226-260703171232" : null;
71613
+ return true ? "0.1.4" : null;
71573
71614
  }
71574
71615
  function readConfiguredAiProvider() {
71575
71616
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -71760,7 +71801,7 @@ async function updateHermes(release, options) {
71760
71801
  }
71761
71802
  }
71762
71803
  async function installGatewayPluginForDoctor(target) {
71763
- const release = isPrereleaseVersion("0.1.4-beta-520175e226-260703171232") ? "beta" : "latest";
71804
+ const release = isPrereleaseVersion("0.1.4") ? "beta" : "latest";
71764
71805
  const insideTargetGateway = detectGatewayInvocation() === target;
71765
71806
  const options = {
71766
71807
  restart: !insideTargetGateway,
@@ -72776,6 +72817,7 @@ __export(index_exports, {
72776
72817
  isOriginalParentProcessAlive: () => isOriginalParentProcessAlive,
72777
72818
  isPrereleaseVersion: () => isPrereleaseVersion,
72778
72819
  isRetryableOpenClawGatewayError: () => isRetryableOpenClawGatewayError,
72820
+ isSqliteBusyError: () => isSqliteBusyError,
72779
72821
  isWindowsElevated: () => isWindowsElevated,
72780
72822
  killProcessTree: () => killProcessTree,
72781
72823
  logWinCompat: () => logWinCompat,
@@ -89263,7 +89305,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
89263
89305
  client: {
89264
89306
  id: "gateway-client",
89265
89307
  displayName: "okx-a2a-node",
89266
- version: "0.1.4-beta-520175e226-260703171232",
89308
+ version: "0.1.4",
89267
89309
  platform: "node",
89268
89310
  mode: "backend",
89269
89311
  instanceId
@@ -89274,7 +89316,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
89274
89316
  commands: [],
89275
89317
  permissions: {},
89276
89318
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
89277
- userAgent: `okx-a2a-node/${"0.1.4-beta-520175e226-260703171232"}`,
89319
+ userAgent: `okx-a2a-node/${"0.1.4"}`,
89278
89320
  auth: {
89279
89321
  ...config.token ? { token: config.token } : {},
89280
89322
  ...config.password ? { password: config.password } : {}
@@ -93744,7 +93786,7 @@ init_ai_provider();
93744
93786
  var environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
93745
93787
  var SENTRY_CONFIG = {
93746
93788
  projectName: "okx/openclaw-okx-a2a-extension",
93747
- release: "0.1.4-beta-520175e226-260703171232",
93789
+ release: "0.1.4",
93748
93790
  environment
93749
93791
  };
93750
93792
 
@@ -93971,12 +94013,12 @@ async function runListenerWithLock(options, paths) {
93971
94013
  }));
93972
94014
  }
93973
94015
  });
93974
- service.setPluginVersion("0.1.4-beta-520175e226-260703171232");
94016
+ service.setPluginVersion("0.1.4");
93975
94017
  await service.init();
93976
94018
  const pluginVersionStatus = service.pluginVersionStatus;
93977
94019
  if (pluginVersionStatus.unavailable) {
93978
94020
  throw new Error(
93979
- `@okxweb3/a2a-node v${"0.1.4-beta-520175e226-260703171232"} is below the required minimum v${pluginVersionStatus.minVersion}`
94021
+ `@okxweb3/a2a-node v${"0.1.4"} is below the required minimum v${pluginVersionStatus.minVersion}`
93980
94022
  );
93981
94023
  }
93982
94024
  const systemConfig = service.getSystemConfig();
@@ -93994,7 +94036,7 @@ async function runListenerWithLock(options, paths) {
93994
94036
  onchainosAgentId: "*",
93995
94037
  reason: "system-config missing sentryDsn",
93996
94038
  pluginId: "@okxweb3/a2a-node",
93997
- pluginVersion: "0.1.4-beta-520175e226-260703171232"
94039
+ pluginVersion: "0.1.4"
93998
94040
  });
93999
94041
  }
94000
94042
  logWithTimestamp(
@@ -95717,7 +95759,7 @@ async function runDoctor(options = {}) {
95717
95759
  platform: options.platform ?? process.platform,
95718
95760
  env: options.env ?? process.env,
95719
95761
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
95720
- cliVersion: options.cliVersion ?? (true ? "0.1.4-beta-520175e226-260703171232" : "0.0.0"),
95762
+ cliVersion: options.cliVersion ?? (true ? "0.1.4" : "0.0.0"),
95721
95763
  fixMode: options.fix === true,
95722
95764
  packageChanged: false
95723
95765
  };
@@ -96013,6 +96055,7 @@ init_win_native_launcher();
96013
96055
  isOriginalParentProcessAlive,
96014
96056
  isPrereleaseVersion,
96015
96057
  isRetryableOpenClawGatewayError,
96058
+ isSqliteBusyError,
96016
96059
  isWindowsElevated,
96017
96060
  killProcessTree,
96018
96061
  logWinCompat,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okxweb3/a2a-node",
3
- "version": "0.1.4-beta-520175e226-260703171232",
3
+ "version": "0.1.4",
4
4
  "description": "Host-agnostic Node CLI for E2E encrypted agent-to-agent communication via XMTP",
5
5
  "main": "dist/index.js",
6
6
  "bin": {