@okxweb3/a2a-node 0.2.0 → 0.2.1-beta-8ae7a0d300-260807161635

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 +231 -47
  2. package/dist/index.js +227 -43
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9433,7 +9433,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
9433
9433
  client: {
9434
9434
  id: "gateway-client",
9435
9435
  displayName: "okx-a2a-node",
9436
- version: "0.2.0",
9436
+ version: "0.2.1-beta-8ae7a0d300-260807161635",
9437
9437
  platform: "node",
9438
9438
  mode: "backend",
9439
9439
  instanceId
@@ -9444,7 +9444,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
9444
9444
  commands: [],
9445
9445
  permissions: {},
9446
9446
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
9447
- userAgent: `okx-a2a-node/${"0.2.0"}`,
9447
+ userAgent: `okx-a2a-node/${"0.2.1-beta-8ae7a0d300-260807161635"}`,
9448
9448
  auth: {
9449
9449
  ...config.token ? { token: config.token } : {},
9450
9450
  ...config.password ? { password: config.password } : {}
@@ -25471,6 +25471,7 @@ var init_events = __esm({
25471
25471
  INBOUND_DELIVERED: "Inbound delivered to session",
25472
25472
  DM_DELIVERED: "DM delivered to session",
25473
25473
  OUTBOUND_ELIGIBILITY_CHECKED: "Outbound eligibility checked",
25474
+ MESSAGE_ELIGIBILITY_CHECKED: "Message eligibility checked",
25474
25475
  AGENT_CLIENT_CREATED: "Agent client created",
25475
25476
  AGENT_INSTALLATION_SUMMARY: "Agent installation summary",
25476
25477
  XMTP_HISTORY_SYNC_REQUESTED: "XMTP history sync requested",
@@ -26057,6 +26058,7 @@ var init_sentry_logger = __esm({
26057
26058
  LogEvent.HERMES_SESSION_ROUTE_BINDING,
26058
26059
  LogEvent.INBOUND_BLOCKED_ADDRESS_MISMATCH,
26059
26060
  LogEvent.INBOUND_BLOCKED_INELIGIBLE,
26061
+ LogEvent.MESSAGE_ELIGIBILITY_CHECKED,
26060
26062
  LogEvent.INBOUND_BLOCKED_SENSITIVE,
26061
26063
  LogEvent.INBOUND_DROP_DM_INVALID_PAYLOAD,
26062
26064
  LogEvent.INBOUND_DROP_DM_NON_SYSTEM,
@@ -27414,7 +27416,7 @@ var init_sentry_config = __esm({
27414
27416
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
27415
27417
  SENTRY_CONFIG = {
27416
27418
  projectName: "okx/openclaw-okx-a2a-extension",
27417
- release: "0.2.0",
27419
+ release: "0.2.1-beta-8ae7a0d300-260807161635",
27418
27420
  environment,
27419
27421
  runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
27420
27422
  };
@@ -38584,7 +38586,7 @@ async function exportDiagnosticLogs(options) {
38584
38586
  node: process.version,
38585
38587
  platform: process.platform,
38586
38588
  arch: process.arch,
38587
- packageVersion: true ? "0.2.0" : "unknown",
38589
+ packageVersion: true ? "0.2.1-beta-8ae7a0d300-260807161635" : "unknown",
38588
38590
  sensitiveContentIncluded: options.includeSensitiveContent,
38589
38591
  listenerAndLlmContentIncluded: true,
38590
38592
  credentialsAlwaysRedacted: true,
@@ -39466,6 +39468,47 @@ var init_logs_export = __esm({
39466
39468
  }
39467
39469
  });
39468
39470
 
39471
+ // src/capabilities-cli.ts
39472
+ var capabilities_cli_exports = {};
39473
+ __export(capabilities_cli_exports, {
39474
+ getA2ACapabilities: () => getA2ACapabilities,
39475
+ handleCapabilitiesCommand: () => handleCapabilitiesCommand
39476
+ });
39477
+ function getA2ACapabilities() {
39478
+ return {
39479
+ messageEligibleOfflineReplay: {
39480
+ ok: true,
39481
+ fixCommands: [],
39482
+ message: ""
39483
+ }
39484
+ };
39485
+ }
39486
+ function handleCapabilitiesCommand(args) {
39487
+ if (args.some((arg) => arg === "-h" || arg === "--help")) {
39488
+ process.stdout.write(CAPABILITIES_USAGE);
39489
+ return;
39490
+ }
39491
+ const unknown = args.filter((arg) => arg !== "--json");
39492
+ if (unknown.length > 0) {
39493
+ throw new Error(`Unknown capabilities option: ${unknown[0]}`);
39494
+ }
39495
+ const capabilities = getA2ACapabilities();
39496
+ if (args.includes("--json")) {
39497
+ process.stdout.write(`${JSON.stringify(capabilities)}
39498
+ `);
39499
+ return;
39500
+ }
39501
+ process.stdout.write(`${JSON.stringify(capabilities, null, 2)}
39502
+ `);
39503
+ }
39504
+ var CAPABILITIES_USAGE;
39505
+ var init_capabilities_cli = __esm({
39506
+ "src/capabilities-cli.ts"() {
39507
+ "use strict";
39508
+ CAPABILITIES_USAGE = "Usage: okx-a2a capabilities [--json]\n\nPrint this package's machine-readable A2A capability negotiation status.\nOnchainOS runs `okx-a2a capabilities --json` to detect supported capabilities.\n";
39509
+ }
39510
+ });
39511
+
39469
39512
  // ../core/src/xmtp-sdk/onchainos/bin.ts
39470
39513
  async function resolve9() {
39471
39514
  if (resolvedBin) {
@@ -55216,6 +55259,9 @@ function parseGroupPayload(content2) {
55216
55259
  return null;
55217
55260
  }
55218
55261
  }
55262
+ function isOfflineReplayForTrigger(trigger) {
55263
+ return trigger === "startup";
55264
+ }
55219
55265
  function createOfflineReplayAddressSummary(address) {
55220
55266
  return {
55221
55267
  address,
@@ -55972,7 +56018,7 @@ var init_xmtp_sdk = __esm({
55972
56018
  logWithTimestamp(
55973
56019
  `[xmtp-sdk] refresh: ${address} has a prior sync timestamp, triggering offline replay`
55974
56020
  );
55975
- void this.replayOfflineMessagesForAddress(address).catch((err2) => {
56021
+ void this.replayOfflineMessagesForAddress(address, "agent_refresh").catch((err2) => {
55976
56022
  logWithTimestamp(`[xmtp-sdk] refresh: ${address} offline replay failed:`, err2);
55977
56023
  logger.error(
55978
56024
  LogEvent.OFFLINE_REPLAY_FAILED,
@@ -56072,7 +56118,7 @@ var init_xmtp_sdk = __esm({
56072
56118
  await this.startListeningForAddresses([address]);
56073
56119
  let replaySummary;
56074
56120
  try {
56075
- replaySummary = await this.replayOfflineMessagesForAddress(address);
56121
+ replaySummary = await this.replayOfflineMessagesForAddress(address, "client_recycle");
56076
56122
  } finally {
56077
56123
  await this.drainInboundReplayGate(address);
56078
56124
  }
@@ -56525,7 +56571,7 @@ var init_xmtp_sdk = __esm({
56525
56571
  * cannot overtake the stored backlog.
56526
56572
  */
56527
56573
  async completeStartupReplay() {
56528
- const summary = await this.replayOfflineMessages();
56574
+ const summary = await this.replayOfflineMessages("startup");
56529
56575
  for (const address of this.clients.keys()) {
56530
56576
  await this.drainInboundReplayGate(address);
56531
56577
  }
@@ -56663,9 +56709,10 @@ var init_xmtp_sdk = __esm({
56663
56709
  logWithTimestamp(`${tag} message listener started`);
56664
56710
  });
56665
56711
  }
56666
- async replayOfflineMessagesForAddress(address) {
56712
+ async replayOfflineMessagesForAddress(address, trigger = "periodic_repair") {
56667
56713
  const addressReplayStartedAt = Date.now();
56668
56714
  const summary = createOfflineReplayAddressSummary(address);
56715
+ const isOfflineReplay = isOfflineReplayForTrigger(trigger);
56669
56716
  const agent = this.clients.get(address);
56670
56717
  if (!agent) {
56671
56718
  summary.durationMs = Date.now() - addressReplayStartedAt;
@@ -56789,7 +56836,8 @@ var init_xmtp_sdk = __esm({
56789
56836
  };
56790
56837
  const handleStartedAt = Date.now();
56791
56838
  const handled = await processOffline(fakeCtx, handlerDeps, {
56792
- skipNotify: true
56839
+ skipNotify: true,
56840
+ isOfflineReplay
56793
56841
  });
56794
56842
  const handleMs = Date.now() - handleStartedAt;
56795
56843
  summary.handleMs += handleMs;
@@ -56871,14 +56919,14 @@ var init_xmtp_sdk = __esm({
56871
56919
  );
56872
56920
  return summary;
56873
56921
  }
56874
- async replayOfflineMessages() {
56922
+ async replayOfflineMessages(trigger = "periodic_repair") {
56875
56923
  await this._backupReady;
56876
56924
  const replayStartedAt = Date.now();
56877
56925
  let totalReplayed = 0;
56878
56926
  let totalSkipped = 0;
56879
56927
  const addresses = [];
56880
56928
  for (const address of this.clients.keys()) {
56881
- const summary2 = await this.replayOfflineMessagesForAddress(address);
56929
+ const summary2 = await this.replayOfflineMessagesForAddress(address, trigger);
56882
56930
  addresses.push(summary2);
56883
56931
  const { replayed, skipped: skipped2 } = summary2;
56884
56932
  totalReplayed += replayed;
@@ -56987,6 +57035,111 @@ var init_session_expired = __esm({
56987
57035
  }
56988
57036
  });
56989
57037
 
57038
+ // ../core/src/xmtp-sdk/onchainos/offline-replay-capability.ts
57039
+ function messageEligibleHelpAdvertisesOfflineReplay(helpText) {
57040
+ if (typeof helpText !== "string" || helpText.length === 0) {
57041
+ return false;
57042
+ }
57043
+ return helpText.toLowerCase().includes(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN);
57044
+ }
57045
+ function buildMessageEligibleArgs(params, opts) {
57046
+ const args = [
57047
+ "agent",
57048
+ "message-eligible",
57049
+ "--agent-id",
57050
+ params.agentId,
57051
+ "--client-agent-id",
57052
+ params.clientAgentId,
57053
+ "--provider-agent-id",
57054
+ params.providerAgentId,
57055
+ "--client-communication-address",
57056
+ params.clientCommunicationAddress,
57057
+ "--provider-communication-address",
57058
+ params.providerCommunicationAddress,
57059
+ "--job-id",
57060
+ params.jobId,
57061
+ "--group-id",
57062
+ params.groupId,
57063
+ "--direction",
57064
+ params.direction,
57065
+ "--provider-security-rate",
57066
+ String(params.providerSecurityRate)
57067
+ ];
57068
+ if (opts.offlineReplaySupported) {
57069
+ args.push(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG, String(params.isOfflineReplay));
57070
+ }
57071
+ return args;
57072
+ }
57073
+ function resolveOfflineReplayCapabilityMarker(supported) {
57074
+ return supported ? "supported" : "legacy_downgrade";
57075
+ }
57076
+ function maybeRecommendOnchainosUpgrade(supported) {
57077
+ if (supported || recommendedPairs.has(UPGRADE_RECOMMENDATION_PAIR_KEY)) {
57078
+ return;
57079
+ }
57080
+ const hook = XmtpService.getInstance().tools.notifyUpgradeRecommendation;
57081
+ if (!hook) {
57082
+ logWithTimestamp(
57083
+ "[onchainos] tools.notifyUpgradeRecommendation not injected \u2014 deferring offline-replay upgrade notice"
57084
+ );
57085
+ return;
57086
+ }
57087
+ recommendedPairs.add(UPGRADE_RECOMMENDATION_PAIR_KEY);
57088
+ try {
57089
+ void Promise.resolve(
57090
+ hook({
57091
+ component: "onchainos",
57092
+ capability: MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
57093
+ reason: UPGRADE_RECOMMENDATION_REASON
57094
+ })
57095
+ ).catch((err2) => {
57096
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation async delivery failed:", err2);
57097
+ });
57098
+ } catch (err2) {
57099
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation threw:", err2);
57100
+ }
57101
+ }
57102
+ function detectMessageEligibleOfflineReplaySupport(execFn = exec) {
57103
+ if (!capabilityProbe) {
57104
+ capabilityProbe = (async () => {
57105
+ try {
57106
+ const { stdout, stderr } = await execFn(["agent", "message-eligible", "--help"]);
57107
+ const supported = messageEligibleHelpAdvertisesOfflineReplay(`${stdout}
57108
+ ${stderr}`);
57109
+ logWithTimestamp(
57110
+ `[onchainos] message-eligible offline-replay capability: ${supported ? "supported" : "legacy_downgrade"}`
57111
+ );
57112
+ return supported;
57113
+ } catch (err2) {
57114
+ logWithTimestamp(
57115
+ "[onchainos] message-eligible --help probe failed; treating offline-replay as unsupported (legacy):",
57116
+ err2
57117
+ );
57118
+ return false;
57119
+ }
57120
+ })().then((supported) => {
57121
+ maybeRecommendOnchainosUpgrade(supported);
57122
+ return supported;
57123
+ });
57124
+ }
57125
+ return capabilityProbe;
57126
+ }
57127
+ var MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG, MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN, UPGRADE_RECOMMENDATION_PAIR_KEY, UPGRADE_RECOMMENDATION_REASON, capabilityProbe, recommendedPairs;
57128
+ var init_offline_replay_capability = __esm({
57129
+ "../core/src/xmtp-sdk/onchainos/offline-replay-capability.ts"() {
57130
+ "use strict";
57131
+ init_log();
57132
+ init_bin();
57133
+ init_xmtp_sdk();
57134
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG = "--is-offline-replay";
57135
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN = "is-offline-replay";
57136
+ UPGRADE_RECOMMENDATION_PAIR_KEY = "onchainos:message-eligible-offline-replay";
57137
+ UPGRADE_RECOMMENDATION_REASON = "OnchainOS does not support the offline-replay eligibility argument; using the legacy message-eligible contract.";
57138
+ capabilityProbe = null;
57139
+ recommendedPairs = /* @__PURE__ */ new Set();
57140
+ }
57141
+ });
57142
+
56990
57143
  // ../core/src/xmtp-sdk/onchainos/system-config.ts
56991
57144
  function notifyExpired(command, options) {
56992
57145
  if (options?.notifySessionExpired) {
@@ -57385,35 +57538,32 @@ async function sendHeartbeat(chainIndex) {
57385
57538
  }
57386
57539
  async function checkMessageEligible(params) {
57387
57540
  logWithTimestamp(
57388
- `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate}`
57541
+ `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate} isOfflineReplay=${params.isOfflineReplay}`
57389
57542
  );
57543
+ const offlineReplaySupported = await detectMessageEligibleOfflineReplaySupport();
57544
+ const offlineReplayCapability = resolveOfflineReplayCapabilityMarker(offlineReplaySupported);
57545
+ const emitEligibilityChecked = (outcome, eligible) => {
57546
+ logger.info(LogEvent.MESSAGE_ELIGIBILITY_CHECKED, {
57547
+ component: "onchainos_cli",
57548
+ source: "onchainos",
57549
+ stage: "eligibility_check",
57550
+ direction: params.direction,
57551
+ jobId: params.jobId,
57552
+ isOfflineReplay: String(params.isOfflineReplay),
57553
+ offlineReplayCapability,
57554
+ outcome,
57555
+ eligible
57556
+ });
57557
+ };
57390
57558
  let stdout;
57391
57559
  let stderr;
57392
57560
  try {
57393
- ({ stdout, stderr } = await exec([
57394
- "agent",
57395
- "message-eligible",
57396
- "--agent-id",
57397
- params.agentId,
57398
- "--client-agent-id",
57399
- params.clientAgentId,
57400
- "--provider-agent-id",
57401
- params.providerAgentId,
57402
- "--client-communication-address",
57403
- params.clientCommunicationAddress,
57404
- "--provider-communication-address",
57405
- params.providerCommunicationAddress,
57406
- "--job-id",
57407
- params.jobId,
57408
- "--group-id",
57409
- params.groupId,
57410
- "--direction",
57411
- params.direction,
57412
- "--provider-security-rate",
57413
- String(params.providerSecurityRate)
57414
- ]));
57561
+ ({ stdout, stderr } = await exec(
57562
+ buildMessageEligibleArgs(params, { offlineReplaySupported })
57563
+ ));
57415
57564
  } catch (err2) {
57416
57565
  guardCatchSessionExpired2(err2, "message-eligible");
57566
+ emitEligibilityChecked("failed", "unknown");
57417
57567
  throw messageEligibleUnavailableError({
57418
57568
  reason: "message-eligible-cli-error",
57419
57569
  stdout: typeof err2?.stdout === "string" ? err2.stdout : "",
@@ -57438,6 +57588,7 @@ async function checkMessageEligible(params) {
57438
57588
  providerSecurityRate: String(params.providerSecurityRate)
57439
57589
  });
57440
57590
  } catch (err2) {
57591
+ emitEligibilityChecked("failed", "unknown");
57441
57592
  throw messageEligibleUnavailableError({
57442
57593
  reason: "message-eligible-json-parse-failed",
57443
57594
  stdout,
@@ -57467,6 +57618,7 @@ async function checkMessageEligible(params) {
57467
57618
  reason: "ok=false"
57468
57619
  })
57469
57620
  );
57621
+ emitEligibilityChecked("failed", "unknown");
57470
57622
  throw messageEligibleUnavailableError({
57471
57623
  reason: "message-eligible-ok-false",
57472
57624
  stdout,
@@ -57476,6 +57628,7 @@ async function checkMessageEligible(params) {
57476
57628
  logWithTimestamp(
57477
57629
  `[onchainos] message-eligible result: eligible=${res.data.eligible}`
57478
57630
  );
57631
+ emitEligibilityChecked("success", String(res.data.eligible));
57479
57632
  return res.data;
57480
57633
  }
57481
57634
  async function fetchSensitiveWords() {
@@ -57529,6 +57682,7 @@ var init_onchainos = __esm({
57529
57682
  init_xmtp_sdk();
57530
57683
  init_cli_response();
57531
57684
  init_session_expired();
57685
+ init_offline_replay_capability();
57532
57686
  init_cli_response();
57533
57687
  init_session_expired();
57534
57688
  init_system_config();
@@ -58394,7 +58548,9 @@ async function assertOutboundEligible(params) {
58394
58548
  jobId,
58395
58549
  groupId,
58396
58550
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
58397
- providerSecurityRate
58551
+ providerSecurityRate,
58552
+ // Outbound messages are never startup backlog recovery.
58553
+ isOfflineReplay: false
58398
58554
  });
58399
58555
  logWithTimestamp(
58400
58556
  `[okx-agent-task] outbound message-eligible: senderAgentId=${senderAgent.agentId} receiverAgentId=${receiverAgent.agentId} job=${jobId} group=${groupId} result=${JSON.stringify(result)}`
@@ -63996,6 +64152,7 @@ async function verifyInboundA2AGroupMessage(params) {
63996
64152
  messageId,
63997
64153
  consentState,
63998
64154
  trustedSystemSender = false,
64155
+ isOfflineReplay = false,
63999
64156
  timing
64000
64157
  } = params;
64001
64158
  if (!isA2AEnvelope(payload)) {
@@ -64099,7 +64256,8 @@ async function verifyInboundA2AGroupMessage(params) {
64099
64256
  jobId,
64100
64257
  groupId,
64101
64258
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
64102
- providerSecurityRate
64259
+ providerSecurityRate,
64260
+ isOfflineReplay
64103
64261
  });
64104
64262
  timing?.mark("eligibilityCheck", eligibilityStartedAt);
64105
64263
  logWithTimestamp(
@@ -64548,6 +64706,7 @@ async function processFileMessage(ctx, deps, options = {}) {
64548
64706
  messageId,
64549
64707
  consentState: ctx.conversation instanceof Group ? ctx.conversation.consentState() : void 0,
64550
64708
  trustedSystemSender,
64709
+ isOfflineReplay: options.isOfflineReplay ?? false,
64551
64710
  timing
64552
64711
  });
64553
64712
  if (!accepted) {
@@ -65272,14 +65431,28 @@ async function runListenerWithLock(options, paths) {
65272
65431
  reason: "append_backup_failed"
65273
65432
  });
65274
65433
  });
65434
+ },
65435
+ notifyUpgradeRecommendation: (input) => {
65436
+ const content2 = "The installed OnchainOS version does not support offline-replay preferences. Subscription messages will continue to replay normally. Upgrade OnchainOS to enable this capability.\n\nSuggested command: onchainos upgrade";
65437
+ void notifySystemMessageToUser({
65438
+ title: "OnchainOS upgrade recommended",
65439
+ content: content2,
65440
+ store: sessionStore,
65441
+ idempotencyKey: UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY
65442
+ }).catch((err2) => {
65443
+ logWithTimestamp(
65444
+ `[okx-agent-task] upgrade recommendation notify failed component=${input.component} capability=${input.capability}:`,
65445
+ err2
65446
+ );
65447
+ });
65275
65448
  }
65276
65449
  });
65277
- service.setPluginVersion("0.2.0");
65450
+ service.setPluginVersion("0.2.1-beta-8ae7a0d300-260807161635");
65278
65451
  await service.init();
65279
65452
  const pluginVersionStatus = service.pluginVersionStatus;
65280
65453
  if (pluginVersionStatus.unavailable) {
65281
65454
  throw new Error(
65282
- `@okxweb3/a2a-node v${"0.2.0"} is below the required minimum v${pluginVersionStatus.minVersion}`
65455
+ `@okxweb3/a2a-node v${"0.2.1-beta-8ae7a0d300-260807161635"} is below the required minimum v${pluginVersionStatus.minVersion}`
65283
65456
  );
65284
65457
  }
65285
65458
  const systemConfig = service.getSystemConfig();
@@ -65297,12 +65470,14 @@ async function runListenerWithLock(options, paths) {
65297
65470
  onchainosAgentId: "*",
65298
65471
  reason: "system-config missing sentryDsn",
65299
65472
  pluginId: "@okxweb3/a2a-node",
65300
- pluginVersion: "0.2.0"
65473
+ pluginVersion: "0.2.1-beta-8ae7a0d300-260807161635"
65301
65474
  });
65302
65475
  }
65303
65476
  logWithTimestamp(
65304
65477
  `[okx-agent-task] listener initialized, clients=${service.getClients().size}, home=${store.homeDir}`
65305
65478
  );
65479
+ void detectMessageEligibleOfflineReplaySupport().catch(() => {
65480
+ });
65306
65481
  const startupReplaySummary = await service.completeStartupReplay();
65307
65482
  logWithTimestamp(
65308
65483
  `[okx-agent-task] startup replay complete clients=${startupReplaySummary.clients} replayed=${startupReplaySummary.replayed} skipped=${startupReplaySummary.skipped} duration=${startupReplaySummary.durationMs}ms`
@@ -65440,7 +65615,7 @@ async function runListenerWithLock(options, paths) {
65440
65615
  );
65441
65616
  return;
65442
65617
  }
65443
- const replayResult = await timeSettled(() => service.replayOfflineMessages());
65618
+ const replayResult = await timeSettled(() => service.replayOfflineMessages("periodic_repair"));
65444
65619
  const replayMs = replayResult.durationMs;
65445
65620
  if (replayResult.error) {
65446
65621
  logger.error(LogEvent.OFFLINE_REPLAY_FAILED, replayResult.error instanceof Error ? replayResult.error : new Error(String(replayResult.error)), {
@@ -65671,7 +65846,7 @@ async function timeSettled(fn) {
65671
65846
  return { durationMs: Date.now() - startedAt, error };
65672
65847
  }
65673
65848
  }
65674
- var import_node_fs25, import_promises13, import_node_os11, import_node_path30, DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC, DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC, XMTP_CLIENT_RECYCLE_INTERVAL_ENV, HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS, SHUTDOWN_FORCE_RESOLVE_MS;
65849
+ var import_node_fs25, import_promises13, import_node_os11, import_node_path30, DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC, DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC, XMTP_CLIENT_RECYCLE_INTERVAL_ENV, HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS, UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY, SHUTDOWN_FORCE_RESOLVE_MS;
65675
65850
  var init_listener = __esm({
65676
65851
  "src/listener.ts"() {
65677
65852
  "use strict";
@@ -65682,6 +65857,7 @@ var init_listener = __esm({
65682
65857
  import_node_path30 = require("node:path");
65683
65858
  init_onchainos();
65684
65859
  init_system_config();
65860
+ init_offline_replay_capability();
65685
65861
  init_signer();
65686
65862
  init_xmtp_sdk();
65687
65863
  init_file_store();
@@ -65690,6 +65866,7 @@ var init_listener = __esm({
65690
65866
  init_command_store();
65691
65867
  init_ai_dispatch_queue();
65692
65868
  init_message_handler();
65869
+ init_agent_message_notice();
65693
65870
  init_paths();
65694
65871
  init_user_attention_ipc();
65695
65872
  init_user_attention_watchers();
@@ -65702,6 +65879,7 @@ var init_listener = __esm({
65702
65879
  DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC = 6 * 60 * 60;
65703
65880
  XMTP_CLIENT_RECYCLE_INTERVAL_ENV = "OKX_A2A_XMTP_CLIENT_RECYCLE_INTERVAL_SEC";
65704
65881
  HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS = 2e3;
65882
+ UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY = "system:onchainos-offline-replay-upgrade";
65705
65883
  SHUTDOWN_FORCE_RESOLVE_MS = 5e3;
65706
65884
  }
65707
65885
  });
@@ -113372,7 +113550,7 @@ async function getCurrentNodeCliVersion() {
113372
113550
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
113373
113551
  }
113374
113552
  function getBundledNodeCliVersion() {
113375
- return true ? "0.2.0" : null;
113553
+ return true ? "0.2.1-beta-8ae7a0d300-260807161635" : null;
113376
113554
  }
113377
113555
  function readConfiguredAiProvider() {
113378
113556
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -113582,7 +113760,7 @@ async function updateHermes(release, options) {
113582
113760
  }
113583
113761
  }
113584
113762
  async function installGatewayPluginForDoctor(target) {
113585
- const release = isPrereleaseVersion("0.2.0") ? "beta" : "latest";
113763
+ const release = isPrereleaseVersion("0.2.1-beta-8ae7a0d300-260807161635") ? "beta" : "latest";
113586
113764
  const insideTargetGateway = detectGatewayInvocation() === target;
113587
113765
  const options = {
113588
113766
  restart: !insideTargetGateway,
@@ -114570,7 +114748,7 @@ async function runDoctor(options = {}) {
114570
114748
  platform: options.platform ?? process.platform,
114571
114749
  env: options.env ?? process.env,
114572
114750
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
114573
- cliVersion: options.cliVersion ?? (true ? "0.2.0" : "0.0.0"),
114751
+ cliVersion: options.cliVersion ?? (true ? "0.2.1-beta-8ae7a0d300-260807161635" : "0.0.0"),
114574
114752
  fixMode: options.fix === true,
114575
114753
  nonInteractive: options.nonInteractive === true,
114576
114754
  packageChanged: false,
@@ -115538,7 +115716,7 @@ init_sentry_logger();
115538
115716
  init_sentry_config();
115539
115717
  var CURRENT_GATEWAY_SESSION_KEYS_ENV4 = "OKX_A2A_CURRENT_GATEWAY_SESSION_KEYS";
115540
115718
  function printUsage3() {
115541
- console.log(`okx-a2a ${"0.2.0"}
115719
+ console.log(`okx-a2a ${"0.2.1-beta-8ae7a0d300-260807161635"}
115542
115720
 
115543
115721
  Usage:
115544
115722
  okx-a2a <command> [options]
@@ -115568,6 +115746,7 @@ Commands:
115568
115746
  Alias for runtime switch-current
115569
115747
  job-provider Manage job-to-provider bindings
115570
115748
  xmtp-send Queue an XMTP message through the running daemon
115749
+ capabilities Print machine-readable A2A capability negotiation status (used by OnchainOS)
115571
115750
 
115572
115751
  Global options:
115573
115752
  -h, --help Show help
@@ -115577,7 +115756,7 @@ Run \`okx-a2a <command> -h\` for command-specific help.
115577
115756
  `);
115578
115757
  }
115579
115758
  function printVersion() {
115580
- console.log("0.2.0");
115759
+ console.log("0.2.1-beta-8ae7a0d300-260807161635");
115581
115760
  }
115582
115761
  function printDaemonUsage() {
115583
115762
  console.log(`Usage: okx-a2a daemon <start|restart|stop|status|autostart> [options]
@@ -116924,6 +117103,11 @@ async function main() {
116924
117103
  if (hasHelpFlag5(args) && !shouldDeferHelpToNestedHandler(command, args) && printCommandUsage(command)) {
116925
117104
  return;
116926
117105
  }
117106
+ if (command === "capabilities") {
117107
+ const { handleCapabilitiesCommand: handleCapabilitiesCommand2 } = await Promise.resolve().then(() => (init_capabilities_cli(), capabilities_cli_exports));
117108
+ handleCapabilitiesCommand2(process.argv.slice(3));
117109
+ return;
117110
+ }
116927
117111
  if (command === "daemon") {
116928
117112
  if (hasHelpFlag5(args)) {
116929
117113
  printDaemonUsage();
@@ -116983,7 +117167,7 @@ async function main() {
116983
117167
  if (command === "xmtp-test") {
116984
117168
  const { handleXmtpTestCommand: handleXmtpTestCommand2 } = await Promise.resolve().then(() => (init_xmtp_test_cli(), xmtp_test_cli_exports));
116985
117169
  await handleXmtpTestCommand2(process.argv.slice(3), {
116986
- packageVersion: "0.2.0",
117170
+ packageVersion: "0.2.1-beta-8ae7a0d300-260807161635",
116987
117171
  agentSdkVersion: "2.3.0",
116988
117172
  nodeSdkVersion: "6.1.0",
116989
117173
  nodeBindingsVersion: "1.11.0"
package/dist/index.js CHANGED
@@ -19597,6 +19597,7 @@ var init_events = __esm({
19597
19597
  INBOUND_DELIVERED: "Inbound delivered to session",
19598
19598
  DM_DELIVERED: "DM delivered to session",
19599
19599
  OUTBOUND_ELIGIBILITY_CHECKED: "Outbound eligibility checked",
19600
+ MESSAGE_ELIGIBILITY_CHECKED: "Message eligibility checked",
19600
19601
  AGENT_CLIENT_CREATED: "Agent client created",
19601
19602
  AGENT_INSTALLATION_SUMMARY: "Agent installation summary",
19602
19603
  XMTP_HISTORY_SYNC_REQUESTED: "XMTP history sync requested",
@@ -20183,6 +20184,7 @@ var init_sentry_logger = __esm({
20183
20184
  LogEvent.HERMES_SESSION_ROUTE_BINDING,
20184
20185
  LogEvent.INBOUND_BLOCKED_ADDRESS_MISMATCH,
20185
20186
  LogEvent.INBOUND_BLOCKED_INELIGIBLE,
20187
+ LogEvent.MESSAGE_ELIGIBILITY_CHECKED,
20186
20188
  LogEvent.INBOUND_BLOCKED_SENSITIVE,
20187
20189
  LogEvent.INBOUND_DROP_DM_INVALID_PAYLOAD,
20188
20190
  LogEvent.INBOUND_DROP_DM_NON_SYSTEM,
@@ -30930,7 +30932,7 @@ var init_sentry_config = __esm({
30930
30932
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
30931
30933
  SENTRY_CONFIG = {
30932
30934
  projectName: "okx/openclaw-okx-a2a-extension",
30933
- release: "0.2.0",
30935
+ release: "0.2.1-beta-8ae7a0d300-260807161635",
30934
30936
  environment,
30935
30937
  runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
30936
30938
  };
@@ -31860,7 +31862,7 @@ async function getCurrentNodeCliVersion() {
31860
31862
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
31861
31863
  }
31862
31864
  function getBundledNodeCliVersion() {
31863
- return true ? "0.2.0" : null;
31865
+ return true ? "0.2.1-beta-8ae7a0d300-260807161635" : null;
31864
31866
  }
31865
31867
  function readConfiguredAiProvider() {
31866
31868
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -32070,7 +32072,7 @@ async function updateHermes(release, options) {
32070
32072
  }
32071
32073
  }
32072
32074
  async function installGatewayPluginForDoctor(target) {
32073
- const release = isPrereleaseVersion("0.2.0") ? "beta" : "latest";
32075
+ const release = isPrereleaseVersion("0.2.1-beta-8ae7a0d300-260807161635") ? "beta" : "latest";
32074
32076
  const insideTargetGateway = detectGatewayInvocation() === target;
32075
32077
  const options = {
32076
32078
  restart: !insideTargetGateway,
@@ -42740,6 +42742,8 @@ __export(index_exports, {
42740
42742
  HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS: () => HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
42741
42743
  InboundReplayGate: () => InboundReplayGate,
42742
42744
  InvalidXmtpMessageStore: () => InvalidXmtpMessageStore,
42745
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
42746
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
42743
42747
  NATIVE_LAUNCHER_EXE_NAME: () => NATIVE_LAUNCHER_EXE_NAME,
42744
42748
  OPENCLAW_GATEWAY_ROUTE_GROUP_ID: () => OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
42745
42749
  SYSTEM_NOTIFICATION_SESSION_KEY: () => SYSTEM_NOTIFICATION_SESSION_KEY,
@@ -42757,6 +42761,7 @@ __export(index_exports, {
42757
42761
  XMTP_CLIENT_RECYCLE_INTERVAL_ENV: () => XMTP_CLIENT_RECYCLE_INTERVAL_ENV,
42758
42762
  XmtpDm: () => Dm,
42759
42763
  XmtpGroup: () => Group,
42764
+ XmtpService: () => XmtpService,
42760
42765
  acquireDaemonLock: () => acquireDaemonLock,
42761
42766
  activeDaemonPointerPath: () => activeDaemonPointerPath,
42762
42767
  aiRunSentryExtra: () => aiRunSentryExtra,
@@ -42777,6 +42782,7 @@ __export(index_exports, {
42777
42782
  buildInboundTerminalExtras: () => buildInboundTerminalExtras,
42778
42783
  buildLaunchdPlist: () => buildLaunchdPlist,
42779
42784
  buildLauncherEntryJs: () => buildLauncherEntryJs,
42785
+ buildMessageEligibleArgs: () => buildMessageEligibleArgs,
42780
42786
  buildPendingConversationBackupDispatch: () => buildPendingConversationBackupDispatch,
42781
42787
  buildSchtasksCreateArgs: () => buildSchtasksCreateArgs,
42782
42788
  buildSchtasksDeleteArgs: () => buildSchtasksDeleteArgs,
@@ -42808,6 +42814,7 @@ __export(index_exports, {
42808
42814
  detectAiProviders: () => detectAiProviders,
42809
42815
  detectCurrentAiProvider: () => detectCurrentAiProvider,
42810
42816
  detectGatewayInvocation: () => detectGatewayInvocation,
42817
+ detectMessageEligibleOfflineReplaySupport: () => detectMessageEligibleOfflineReplaySupport,
42811
42818
  detectRuntime: () => detectRuntime,
42812
42819
  ensureDaemonReady: () => ensureDaemonReady,
42813
42820
  ensureDefaultAiProvider: () => ensureDefaultAiProvider,
@@ -42821,9 +42828,11 @@ __export(index_exports, {
42821
42828
  findWindowsNativeOkxA2aExe: () => findWindowsNativeOkxA2aExe,
42822
42829
  followFile: () => followFile,
42823
42830
  formatDoctorReportForHumans: () => formatDoctorReportForHumans,
42831
+ getA2ACapabilities: () => getA2ACapabilities,
42824
42832
  getDaemonStatus: () => getDaemonStatus,
42825
42833
  getHermesGatewayPluginStatus: () => getHermesGatewayPluginStatus,
42826
42834
  getSessionBusyTracker: () => getSessionBusyTracker,
42835
+ handleCapabilitiesCommand: () => handleCapabilitiesCommand,
42827
42836
  handleDoctorCommand: () => handleDoctorCommand,
42828
42837
  handleXmtpDebugCommand: () => handleXmtpDebugCommand,
42829
42838
  handleXmtpSendCommand: () => handleXmtpSendCommand,
@@ -42837,6 +42846,7 @@ __export(index_exports, {
42837
42846
  isDefaultTaskHome: () => isDefaultTaskHome,
42838
42847
  isGatewayAvailableForHeartbeat: () => isGatewayAvailableForHeartbeat,
42839
42848
  isHermesGatewayPluginEnabled: () => isHermesGatewayPluginEnabled,
42849
+ isOfflineReplayForTrigger: () => isOfflineReplayForTrigger,
42840
42850
  isOpenClawA2aPluginAvailable: () => isOpenClawA2aPluginAvailable,
42841
42851
  isOpenClawGatewayAvailable: () => isOpenClawGatewayAvailable,
42842
42852
  isOriginalParentProcessAlive: () => isOriginalParentProcessAlive,
@@ -42848,6 +42858,8 @@ __export(index_exports, {
42848
42858
  launchdPlistHasCurrentRestartPolicy: () => launchdPlistHasCurrentRestartPolicy,
42849
42859
  logWinCompat: () => logWinCompat,
42850
42860
  logXmtpMessageSent: () => logXmtpMessageSent,
42861
+ maybeRecommendOnchainosUpgrade: () => maybeRecommendOnchainosUpgrade,
42862
+ messageEligibleHelpAdvertisesOfflineReplay: () => messageEligibleHelpAdvertisesOfflineReplay,
42851
42863
  nodeVersionSatisfies: () => nodeVersionSatisfies,
42852
42864
  normalizeAiProvider: () => normalizeAiProvider,
42853
42865
  normalizeHermesOkxA2aPluginConfig: () => normalizeHermesOkxA2aPluginConfig,
@@ -42873,6 +42885,7 @@ __export(index_exports, {
42873
42885
  removeUserAttentionWatcher: () => removeUserAttentionWatcher,
42874
42886
  reportDoctorRunToSentry: () => reportDoctorRunToSentry,
42875
42887
  reportUserNotificationFailure: () => reportUserNotificationFailure,
42888
+ resetMessageEligibleOfflineReplayCapabilityForTests: () => resetMessageEligibleOfflineReplayCapabilityForTests,
42876
42889
  resetResolvedOnchainosBinForTests: () => resetResolvedOnchainosBinForTests,
42877
42890
  resolveAiPermissionPreset: () => resolveAiPermissionPreset,
42878
42891
  resolveAiProviderCommand: () => resolveAiProviderCommand,
@@ -42884,6 +42897,7 @@ __export(index_exports, {
42884
42897
  resolveDoctorTarget: () => resolveDoctorTarget,
42885
42898
  resolveEnvelopePayload: () => resolveEnvelopePayload,
42886
42899
  resolveNpmCmdShimScript: () => resolveNpmCmdShimScript,
42900
+ resolveOfflineReplayCapabilityMarker: () => resolveOfflineReplayCapabilityMarker,
42887
42901
  resolveOfflineReplayIntervalSec: () => resolveOfflineReplayIntervalSec,
42888
42902
  resolveOpenClawGatewayConfig: () => resolveOpenClawGatewayConfig,
42889
42903
  resolveOpenClawGatewayRoute: () => resolveOpenClawGatewayRoute,
@@ -54808,6 +54822,9 @@ function parseGroupPayload(content) {
54808
54822
  return null;
54809
54823
  }
54810
54824
  }
54825
+ function isOfflineReplayForTrigger(trigger) {
54826
+ return trigger === "startup";
54827
+ }
54811
54828
  var XmtpService = class _XmtpService {
54812
54829
  static instance = null;
54813
54830
  env = process.env.XMTP_ENV ?? "production";
@@ -55461,7 +55478,7 @@ var XmtpService = class _XmtpService {
55461
55478
  logWithTimestamp(
55462
55479
  `[xmtp-sdk] refresh: ${address} has a prior sync timestamp, triggering offline replay`
55463
55480
  );
55464
- void this.replayOfflineMessagesForAddress(address).catch((err) => {
55481
+ void this.replayOfflineMessagesForAddress(address, "agent_refresh").catch((err) => {
55465
55482
  logWithTimestamp(`[xmtp-sdk] refresh: ${address} offline replay failed:`, err);
55466
55483
  logger.error(
55467
55484
  LogEvent.OFFLINE_REPLAY_FAILED,
@@ -55561,7 +55578,7 @@ var XmtpService = class _XmtpService {
55561
55578
  await this.startListeningForAddresses([address]);
55562
55579
  let replaySummary;
55563
55580
  try {
55564
- replaySummary = await this.replayOfflineMessagesForAddress(address);
55581
+ replaySummary = await this.replayOfflineMessagesForAddress(address, "client_recycle");
55565
55582
  } finally {
55566
55583
  await this.drainInboundReplayGate(address);
55567
55584
  }
@@ -56014,7 +56031,7 @@ var XmtpService = class _XmtpService {
56014
56031
  * cannot overtake the stored backlog.
56015
56032
  */
56016
56033
  async completeStartupReplay() {
56017
- const summary = await this.replayOfflineMessages();
56034
+ const summary = await this.replayOfflineMessages("startup");
56018
56035
  for (const address of this.clients.keys()) {
56019
56036
  await this.drainInboundReplayGate(address);
56020
56037
  }
@@ -56152,9 +56169,10 @@ var XmtpService = class _XmtpService {
56152
56169
  logWithTimestamp(`${tag} message listener started`);
56153
56170
  });
56154
56171
  }
56155
- async replayOfflineMessagesForAddress(address) {
56172
+ async replayOfflineMessagesForAddress(address, trigger = "periodic_repair") {
56156
56173
  const addressReplayStartedAt = Date.now();
56157
56174
  const summary = createOfflineReplayAddressSummary(address);
56175
+ const isOfflineReplay = isOfflineReplayForTrigger(trigger);
56158
56176
  const agent = this.clients.get(address);
56159
56177
  if (!agent) {
56160
56178
  summary.durationMs = Date.now() - addressReplayStartedAt;
@@ -56278,7 +56296,8 @@ var XmtpService = class _XmtpService {
56278
56296
  };
56279
56297
  const handleStartedAt = Date.now();
56280
56298
  const handled = await processOffline(fakeCtx, handlerDeps, {
56281
- skipNotify: true
56299
+ skipNotify: true,
56300
+ isOfflineReplay
56282
56301
  });
56283
56302
  const handleMs = Date.now() - handleStartedAt;
56284
56303
  summary.handleMs += handleMs;
@@ -56360,14 +56379,14 @@ var XmtpService = class _XmtpService {
56360
56379
  );
56361
56380
  return summary;
56362
56381
  }
56363
- async replayOfflineMessages() {
56382
+ async replayOfflineMessages(trigger = "periodic_repair") {
56364
56383
  await this._backupReady;
56365
56384
  const replayStartedAt = Date.now();
56366
56385
  let totalReplayed = 0;
56367
56386
  let totalSkipped = 0;
56368
56387
  const addresses = [];
56369
56388
  for (const address of this.clients.keys()) {
56370
- const summary2 = await this.replayOfflineMessagesForAddress(address);
56389
+ const summary2 = await this.replayOfflineMessagesForAddress(address, trigger);
56371
56390
  addresses.push(summary2);
56372
56391
  const { replayed, skipped: skipped2 } = summary2;
56373
56392
  totalReplayed += replayed;
@@ -56808,6 +56827,107 @@ function reportSessionExpired(command) {
56808
56827
  );
56809
56828
  }
56810
56829
 
56830
+ // ../core/src/xmtp-sdk/onchainos/offline-replay-capability.ts
56831
+ init_log();
56832
+ var MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG = "--is-offline-replay";
56833
+ var MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN = "is-offline-replay";
56834
+ var UPGRADE_RECOMMENDATION_PAIR_KEY = "onchainos:message-eligible-offline-replay";
56835
+ var UPGRADE_RECOMMENDATION_REASON = "OnchainOS does not support the offline-replay eligibility argument; using the legacy message-eligible contract.";
56836
+ function messageEligibleHelpAdvertisesOfflineReplay(helpText) {
56837
+ if (typeof helpText !== "string" || helpText.length === 0) {
56838
+ return false;
56839
+ }
56840
+ return helpText.toLowerCase().includes(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN);
56841
+ }
56842
+ function buildMessageEligibleArgs(params, opts) {
56843
+ const args = [
56844
+ "agent",
56845
+ "message-eligible",
56846
+ "--agent-id",
56847
+ params.agentId,
56848
+ "--client-agent-id",
56849
+ params.clientAgentId,
56850
+ "--provider-agent-id",
56851
+ params.providerAgentId,
56852
+ "--client-communication-address",
56853
+ params.clientCommunicationAddress,
56854
+ "--provider-communication-address",
56855
+ params.providerCommunicationAddress,
56856
+ "--job-id",
56857
+ params.jobId,
56858
+ "--group-id",
56859
+ params.groupId,
56860
+ "--direction",
56861
+ params.direction,
56862
+ "--provider-security-rate",
56863
+ String(params.providerSecurityRate)
56864
+ ];
56865
+ if (opts.offlineReplaySupported) {
56866
+ args.push(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG, String(params.isOfflineReplay));
56867
+ }
56868
+ return args;
56869
+ }
56870
+ function resolveOfflineReplayCapabilityMarker(supported) {
56871
+ return supported ? "supported" : "legacy_downgrade";
56872
+ }
56873
+ var capabilityProbe = null;
56874
+ var recommendedPairs = /* @__PURE__ */ new Set();
56875
+ function resetMessageEligibleOfflineReplayCapabilityForTests() {
56876
+ capabilityProbe = null;
56877
+ recommendedPairs.clear();
56878
+ }
56879
+ function maybeRecommendOnchainosUpgrade(supported) {
56880
+ if (supported || recommendedPairs.has(UPGRADE_RECOMMENDATION_PAIR_KEY)) {
56881
+ return;
56882
+ }
56883
+ const hook = XmtpService.getInstance().tools.notifyUpgradeRecommendation;
56884
+ if (!hook) {
56885
+ logWithTimestamp(
56886
+ "[onchainos] tools.notifyUpgradeRecommendation not injected \u2014 deferring offline-replay upgrade notice"
56887
+ );
56888
+ return;
56889
+ }
56890
+ recommendedPairs.add(UPGRADE_RECOMMENDATION_PAIR_KEY);
56891
+ try {
56892
+ void Promise.resolve(
56893
+ hook({
56894
+ component: "onchainos",
56895
+ capability: MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
56896
+ reason: UPGRADE_RECOMMENDATION_REASON
56897
+ })
56898
+ ).catch((err) => {
56899
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation async delivery failed:", err);
56900
+ });
56901
+ } catch (err) {
56902
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation threw:", err);
56903
+ }
56904
+ }
56905
+ function detectMessageEligibleOfflineReplaySupport(execFn = exec) {
56906
+ if (!capabilityProbe) {
56907
+ capabilityProbe = (async () => {
56908
+ try {
56909
+ const { stdout, stderr } = await execFn(["agent", "message-eligible", "--help"]);
56910
+ const supported = messageEligibleHelpAdvertisesOfflineReplay(`${stdout}
56911
+ ${stderr}`);
56912
+ logWithTimestamp(
56913
+ `[onchainos] message-eligible offline-replay capability: ${supported ? "supported" : "legacy_downgrade"}`
56914
+ );
56915
+ return supported;
56916
+ } catch (err) {
56917
+ logWithTimestamp(
56918
+ "[onchainos] message-eligible --help probe failed; treating offline-replay as unsupported (legacy):",
56919
+ err
56920
+ );
56921
+ return false;
56922
+ }
56923
+ })().then((supported) => {
56924
+ maybeRecommendOnchainosUpgrade(supported);
56925
+ return supported;
56926
+ });
56927
+ }
56928
+ return capabilityProbe;
56929
+ }
56930
+
56811
56931
  // ../core/src/xmtp-sdk/onchainos/system-config.ts
56812
56932
  init_log();
56813
56933
  init_sentry_logger();
@@ -57204,35 +57324,32 @@ async function sendHeartbeat(chainIndex) {
57204
57324
  }
57205
57325
  async function checkMessageEligible(params) {
57206
57326
  logWithTimestamp(
57207
- `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate}`
57327
+ `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate} isOfflineReplay=${params.isOfflineReplay}`
57208
57328
  );
57329
+ const offlineReplaySupported = await detectMessageEligibleOfflineReplaySupport();
57330
+ const offlineReplayCapability = resolveOfflineReplayCapabilityMarker(offlineReplaySupported);
57331
+ const emitEligibilityChecked = (outcome, eligible) => {
57332
+ logger.info(LogEvent.MESSAGE_ELIGIBILITY_CHECKED, {
57333
+ component: "onchainos_cli",
57334
+ source: "onchainos",
57335
+ stage: "eligibility_check",
57336
+ direction: params.direction,
57337
+ jobId: params.jobId,
57338
+ isOfflineReplay: String(params.isOfflineReplay),
57339
+ offlineReplayCapability,
57340
+ outcome,
57341
+ eligible
57342
+ });
57343
+ };
57209
57344
  let stdout;
57210
57345
  let stderr;
57211
57346
  try {
57212
- ({ stdout, stderr } = await exec([
57213
- "agent",
57214
- "message-eligible",
57215
- "--agent-id",
57216
- params.agentId,
57217
- "--client-agent-id",
57218
- params.clientAgentId,
57219
- "--provider-agent-id",
57220
- params.providerAgentId,
57221
- "--client-communication-address",
57222
- params.clientCommunicationAddress,
57223
- "--provider-communication-address",
57224
- params.providerCommunicationAddress,
57225
- "--job-id",
57226
- params.jobId,
57227
- "--group-id",
57228
- params.groupId,
57229
- "--direction",
57230
- params.direction,
57231
- "--provider-security-rate",
57232
- String(params.providerSecurityRate)
57233
- ]));
57347
+ ({ stdout, stderr } = await exec(
57348
+ buildMessageEligibleArgs(params, { offlineReplaySupported })
57349
+ ));
57234
57350
  } catch (err) {
57235
57351
  guardCatchSessionExpired2(err, "message-eligible");
57352
+ emitEligibilityChecked("failed", "unknown");
57236
57353
  throw messageEligibleUnavailableError({
57237
57354
  reason: "message-eligible-cli-error",
57238
57355
  stdout: typeof err?.stdout === "string" ? err.stdout : "",
@@ -57257,6 +57374,7 @@ async function checkMessageEligible(params) {
57257
57374
  providerSecurityRate: String(params.providerSecurityRate)
57258
57375
  });
57259
57376
  } catch (err) {
57377
+ emitEligibilityChecked("failed", "unknown");
57260
57378
  throw messageEligibleUnavailableError({
57261
57379
  reason: "message-eligible-json-parse-failed",
57262
57380
  stdout,
@@ -57286,6 +57404,7 @@ async function checkMessageEligible(params) {
57286
57404
  reason: "ok=false"
57287
57405
  })
57288
57406
  );
57407
+ emitEligibilityChecked("failed", "unknown");
57289
57408
  throw messageEligibleUnavailableError({
57290
57409
  reason: "message-eligible-ok-false",
57291
57410
  stdout,
@@ -57295,6 +57414,7 @@ async function checkMessageEligible(params) {
57295
57414
  logWithTimestamp(
57296
57415
  `[onchainos] message-eligible result: eligible=${res.data.eligible}`
57297
57416
  );
57417
+ emitEligibilityChecked("success", String(res.data.eligible));
57298
57418
  return res.data;
57299
57419
  }
57300
57420
  async function fetchSensitiveWords() {
@@ -58117,7 +58237,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
58117
58237
  client: {
58118
58238
  id: "gateway-client",
58119
58239
  displayName: "okx-a2a-node",
58120
- version: "0.2.0",
58240
+ version: "0.2.1-beta-8ae7a0d300-260807161635",
58121
58241
  platform: "node",
58122
58242
  mode: "backend",
58123
58243
  instanceId
@@ -58128,7 +58248,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
58128
58248
  commands: [],
58129
58249
  permissions: {},
58130
58250
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
58131
- userAgent: `okx-a2a-node/${"0.2.0"}`,
58251
+ userAgent: `okx-a2a-node/${"0.2.1-beta-8ae7a0d300-260807161635"}`,
58132
58252
  auth: {
58133
58253
  ...config.token ? { token: config.token } : {},
58134
58254
  ...config.password ? { password: config.password } : {}
@@ -59890,7 +60010,9 @@ async function assertOutboundEligible(params) {
59890
60010
  jobId,
59891
60011
  groupId,
59892
60012
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
59893
- providerSecurityRate
60013
+ providerSecurityRate,
60014
+ // Outbound messages are never startup backlog recovery.
60015
+ isOfflineReplay: false
59894
60016
  });
59895
60017
  logWithTimestamp(
59896
60018
  `[okx-agent-task] outbound message-eligible: senderAgentId=${senderAgent.agentId} receiverAgentId=${receiverAgent.agentId} job=${jobId} group=${groupId} result=${JSON.stringify(result)}`
@@ -62729,6 +62851,7 @@ async function verifyInboundA2AGroupMessage(params) {
62729
62851
  messageId,
62730
62852
  consentState,
62731
62853
  trustedSystemSender = false,
62854
+ isOfflineReplay = false,
62732
62855
  timing
62733
62856
  } = params;
62734
62857
  if (!isA2AEnvelope(payload)) {
@@ -62832,7 +62955,8 @@ async function verifyInboundA2AGroupMessage(params) {
62832
62955
  jobId,
62833
62956
  groupId,
62834
62957
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
62835
- providerSecurityRate
62958
+ providerSecurityRate,
62959
+ isOfflineReplay
62836
62960
  });
62837
62961
  timing?.mark("eligibilityCheck", eligibilityStartedAt);
62838
62962
  logWithTimestamp(
@@ -63281,6 +63405,7 @@ async function processFileMessage(ctx, deps, options = {}) {
63281
63405
  messageId,
63282
63406
  consentState: ctx.conversation instanceof Group ? ctx.conversation.consentState() : void 0,
63283
63407
  trustedSystemSender,
63408
+ isOfflineReplay: options.isOfflineReplay ?? false,
63284
63409
  timing
63285
63410
  });
63286
63411
  if (!accepted) {
@@ -63864,6 +63989,7 @@ var DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC = 300;
63864
63989
  var DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC = 6 * 60 * 60;
63865
63990
  var XMTP_CLIENT_RECYCLE_INTERVAL_ENV = "OKX_A2A_XMTP_CLIENT_RECYCLE_INTERVAL_SEC";
63866
63991
  var HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS = 2e3;
63992
+ var UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY = "system:onchainos-offline-replay-upgrade";
63867
63993
  function resolveOfflineReplayIntervalSec(systemConfig) {
63868
63994
  const interval = systemConfig.offlineReplayInterval;
63869
63995
  if (typeof interval !== "number" || !Number.isFinite(interval) || interval <= 0) {
@@ -64104,14 +64230,28 @@ async function runListenerWithLock(options, paths) {
64104
64230
  reason: "append_backup_failed"
64105
64231
  });
64106
64232
  });
64233
+ },
64234
+ notifyUpgradeRecommendation: (input) => {
64235
+ const content = "The installed OnchainOS version does not support offline-replay preferences. Subscription messages will continue to replay normally. Upgrade OnchainOS to enable this capability.\n\nSuggested command: onchainos upgrade";
64236
+ void notifySystemMessageToUser({
64237
+ title: "OnchainOS upgrade recommended",
64238
+ content,
64239
+ store: sessionStore,
64240
+ idempotencyKey: UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY
64241
+ }).catch((err) => {
64242
+ logWithTimestamp(
64243
+ `[okx-agent-task] upgrade recommendation notify failed component=${input.component} capability=${input.capability}:`,
64244
+ err
64245
+ );
64246
+ });
64107
64247
  }
64108
64248
  });
64109
- service.setPluginVersion("0.2.0");
64249
+ service.setPluginVersion("0.2.1-beta-8ae7a0d300-260807161635");
64110
64250
  await service.init();
64111
64251
  const pluginVersionStatus = service.pluginVersionStatus;
64112
64252
  if (pluginVersionStatus.unavailable) {
64113
64253
  throw new Error(
64114
- `@okxweb3/a2a-node v${"0.2.0"} is below the required minimum v${pluginVersionStatus.minVersion}`
64254
+ `@okxweb3/a2a-node v${"0.2.1-beta-8ae7a0d300-260807161635"} is below the required minimum v${pluginVersionStatus.minVersion}`
64115
64255
  );
64116
64256
  }
64117
64257
  const systemConfig = service.getSystemConfig();
@@ -64129,12 +64269,14 @@ async function runListenerWithLock(options, paths) {
64129
64269
  onchainosAgentId: "*",
64130
64270
  reason: "system-config missing sentryDsn",
64131
64271
  pluginId: "@okxweb3/a2a-node",
64132
- pluginVersion: "0.2.0"
64272
+ pluginVersion: "0.2.1-beta-8ae7a0d300-260807161635"
64133
64273
  });
64134
64274
  }
64135
64275
  logWithTimestamp(
64136
64276
  `[okx-agent-task] listener initialized, clients=${service.getClients().size}, home=${store.homeDir}`
64137
64277
  );
64278
+ void detectMessageEligibleOfflineReplaySupport().catch(() => {
64279
+ });
64138
64280
  const startupReplaySummary = await service.completeStartupReplay();
64139
64281
  logWithTimestamp(
64140
64282
  `[okx-agent-task] startup replay complete clients=${startupReplaySummary.clients} replayed=${startupReplaySummary.replayed} skipped=${startupReplaySummary.skipped} duration=${startupReplaySummary.durationMs}ms`
@@ -64272,7 +64414,7 @@ async function runListenerWithLock(options, paths) {
64272
64414
  );
64273
64415
  return;
64274
64416
  }
64275
- const replayResult = await timeSettled(() => service.replayOfflineMessages());
64417
+ const replayResult = await timeSettled(() => service.replayOfflineMessages("periodic_repair"));
64276
64418
  const replayMs = replayResult.durationMs;
64277
64419
  if (replayResult.error) {
64278
64420
  logger.error(LogEvent.OFFLINE_REPLAY_FAILED, replayResult.error instanceof Error ? replayResult.error : new Error(String(replayResult.error)), {
@@ -65378,6 +65520,36 @@ init_ai_command();
65378
65520
  init_update_cli();
65379
65521
  init_win_spawn();
65380
65522
 
65523
+ // src/capabilities-cli.ts
65524
+ function getA2ACapabilities() {
65525
+ return {
65526
+ messageEligibleOfflineReplay: {
65527
+ ok: true,
65528
+ fixCommands: [],
65529
+ message: ""
65530
+ }
65531
+ };
65532
+ }
65533
+ var CAPABILITIES_USAGE = "Usage: okx-a2a capabilities [--json]\n\nPrint this package's machine-readable A2A capability negotiation status.\nOnchainOS runs `okx-a2a capabilities --json` to detect supported capabilities.\n";
65534
+ function handleCapabilitiesCommand(args) {
65535
+ if (args.some((arg) => arg === "-h" || arg === "--help")) {
65536
+ process.stdout.write(CAPABILITIES_USAGE);
65537
+ return;
65538
+ }
65539
+ const unknown = args.filter((arg) => arg !== "--json");
65540
+ if (unknown.length > 0) {
65541
+ throw new Error(`Unknown capabilities option: ${unknown[0]}`);
65542
+ }
65543
+ const capabilities = getA2ACapabilities();
65544
+ if (args.includes("--json")) {
65545
+ process.stdout.write(`${JSON.stringify(capabilities)}
65546
+ `);
65547
+ return;
65548
+ }
65549
+ process.stdout.write(`${JSON.stringify(capabilities, null, 2)}
65550
+ `);
65551
+ }
65552
+
65381
65553
  // src/logs-export.ts
65382
65554
  var import_node_child_process10 = require("node:child_process");
65383
65555
  var import_node_fs27 = require("node:fs");
@@ -66698,7 +66870,7 @@ async function exportDiagnosticLogs(options) {
66698
66870
  node: process.version,
66699
66871
  platform: process.platform,
66700
66872
  arch: process.arch,
66701
- packageVersion: true ? "0.2.0" : "unknown",
66873
+ packageVersion: true ? "0.2.1-beta-8ae7a0d300-260807161635" : "unknown",
66702
66874
  sensitiveContentIncluded: options.includeSensitiveContent,
66703
66875
  listenerAndLlmContentIncluded: true,
66704
66876
  credentialsAlwaysRedacted: true,
@@ -68423,7 +68595,7 @@ async function runDoctor(options = {}) {
68423
68595
  platform: options.platform ?? process.platform,
68424
68596
  env: options.env ?? process.env,
68425
68597
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
68426
- cliVersion: options.cliVersion ?? (true ? "0.2.0" : "0.0.0"),
68598
+ cliVersion: options.cliVersion ?? (true ? "0.2.1-beta-8ae7a0d300-260807161635" : "0.0.0"),
68427
68599
  fixMode: options.fix === true,
68428
68600
  nonInteractive: options.nonInteractive === true,
68429
68601
  packageChanged: false,
@@ -68657,6 +68829,8 @@ init_autostart_windows();
68657
68829
  HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
68658
68830
  InboundReplayGate,
68659
68831
  InvalidXmtpMessageStore,
68832
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
68833
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
68660
68834
  NATIVE_LAUNCHER_EXE_NAME,
68661
68835
  OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
68662
68836
  SYSTEM_NOTIFICATION_SESSION_KEY,
@@ -68674,6 +68848,7 @@ init_autostart_windows();
68674
68848
  XMTP_CLIENT_RECYCLE_INTERVAL_ENV,
68675
68849
  XmtpDm,
68676
68850
  XmtpGroup,
68851
+ XmtpService,
68677
68852
  acquireDaemonLock,
68678
68853
  activeDaemonPointerPath,
68679
68854
  aiRunSentryExtra,
@@ -68694,6 +68869,7 @@ init_autostart_windows();
68694
68869
  buildInboundTerminalExtras,
68695
68870
  buildLaunchdPlist,
68696
68871
  buildLauncherEntryJs,
68872
+ buildMessageEligibleArgs,
68697
68873
  buildPendingConversationBackupDispatch,
68698
68874
  buildSchtasksCreateArgs,
68699
68875
  buildSchtasksDeleteArgs,
@@ -68725,6 +68901,7 @@ init_autostart_windows();
68725
68901
  detectAiProviders,
68726
68902
  detectCurrentAiProvider,
68727
68903
  detectGatewayInvocation,
68904
+ detectMessageEligibleOfflineReplaySupport,
68728
68905
  detectRuntime,
68729
68906
  ensureDaemonReady,
68730
68907
  ensureDefaultAiProvider,
@@ -68738,9 +68915,11 @@ init_autostart_windows();
68738
68915
  findWindowsNativeOkxA2aExe,
68739
68916
  followFile,
68740
68917
  formatDoctorReportForHumans,
68918
+ getA2ACapabilities,
68741
68919
  getDaemonStatus,
68742
68920
  getHermesGatewayPluginStatus,
68743
68921
  getSessionBusyTracker,
68922
+ handleCapabilitiesCommand,
68744
68923
  handleDoctorCommand,
68745
68924
  handleXmtpDebugCommand,
68746
68925
  handleXmtpSendCommand,
@@ -68754,6 +68933,7 @@ init_autostart_windows();
68754
68933
  isDefaultTaskHome,
68755
68934
  isGatewayAvailableForHeartbeat,
68756
68935
  isHermesGatewayPluginEnabled,
68936
+ isOfflineReplayForTrigger,
68757
68937
  isOpenClawA2aPluginAvailable,
68758
68938
  isOpenClawGatewayAvailable,
68759
68939
  isOriginalParentProcessAlive,
@@ -68765,6 +68945,8 @@ init_autostart_windows();
68765
68945
  launchdPlistHasCurrentRestartPolicy,
68766
68946
  logWinCompat,
68767
68947
  logXmtpMessageSent,
68948
+ maybeRecommendOnchainosUpgrade,
68949
+ messageEligibleHelpAdvertisesOfflineReplay,
68768
68950
  nodeVersionSatisfies,
68769
68951
  normalizeAiProvider,
68770
68952
  normalizeHermesOkxA2aPluginConfig,
@@ -68790,6 +68972,7 @@ init_autostart_windows();
68790
68972
  removeUserAttentionWatcher,
68791
68973
  reportDoctorRunToSentry,
68792
68974
  reportUserNotificationFailure,
68975
+ resetMessageEligibleOfflineReplayCapabilityForTests,
68793
68976
  resetResolvedOnchainosBinForTests,
68794
68977
  resolveAiPermissionPreset,
68795
68978
  resolveAiProviderCommand,
@@ -68801,6 +68984,7 @@ init_autostart_windows();
68801
68984
  resolveDoctorTarget,
68802
68985
  resolveEnvelopePayload,
68803
68986
  resolveNpmCmdShimScript,
68987
+ resolveOfflineReplayCapabilityMarker,
68804
68988
  resolveOfflineReplayIntervalSec,
68805
68989
  resolveOpenClawGatewayConfig,
68806
68990
  resolveOpenClawGatewayRoute,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okxweb3/a2a-node",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-beta-8ae7a0d300-260807161635",
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": {