@okxweb3/a2a-node 0.1.11 → 0.1.12-beta-61603b42d4-260803155630

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 +230 -46
  2. package/dist/index.js +227 -43
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -9366,7 +9366,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
9366
9366
  client: {
9367
9367
  id: "gateway-client",
9368
9368
  displayName: "okx-a2a-node",
9369
- version: "0.1.11",
9369
+ version: "0.1.12-beta-61603b42d4-260803155630",
9370
9370
  platform: "node",
9371
9371
  mode: "backend",
9372
9372
  instanceId
@@ -9377,7 +9377,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
9377
9377
  commands: [],
9378
9378
  permissions: {},
9379
9379
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
9380
- userAgent: `okx-a2a-node/${"0.1.11"}`,
9380
+ userAgent: `okx-a2a-node/${"0.1.12-beta-61603b42d4-260803155630"}`,
9381
9381
  auth: {
9382
9382
  ...config.token ? { token: config.token } : {},
9383
9383
  ...config.password ? { password: config.password } : {}
@@ -25401,6 +25401,7 @@ var init_events = __esm({
25401
25401
  INBOUND_DELIVERED: "Inbound delivered to session",
25402
25402
  DM_DELIVERED: "DM delivered to session",
25403
25403
  OUTBOUND_ELIGIBILITY_CHECKED: "Outbound eligibility checked",
25404
+ MESSAGE_ELIGIBILITY_CHECKED: "Message eligibility checked",
25404
25405
  AGENT_CLIENT_CREATED: "Agent client created",
25405
25406
  AGENT_CLIENT_REMOVED: "Agent client removed",
25406
25407
  ADDRESS_SYNC_COMPLETED: "Address sync completed",
@@ -25911,6 +25912,7 @@ var init_sentry_logger = __esm({
25911
25912
  LogEvent.HERMES_SESSION_ROUTE_BINDING,
25912
25913
  LogEvent.INBOUND_BLOCKED_ADDRESS_MISMATCH,
25913
25914
  LogEvent.INBOUND_BLOCKED_INELIGIBLE,
25915
+ LogEvent.MESSAGE_ELIGIBILITY_CHECKED,
25914
25916
  LogEvent.INBOUND_BLOCKED_SENSITIVE,
25915
25917
  LogEvent.INBOUND_DROP_DM_INVALID_PAYLOAD,
25916
25918
  LogEvent.INBOUND_DROP_DM_NON_SYSTEM,
@@ -27064,7 +27066,7 @@ var init_sentry_config = __esm({
27064
27066
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
27065
27067
  SENTRY_CONFIG = {
27066
27068
  projectName: "okx/openclaw-okx-a2a-extension",
27067
- release: "0.1.11",
27069
+ release: "0.1.12-beta-61603b42d4-260803155630",
27068
27070
  environment,
27069
27071
  runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
27070
27072
  };
@@ -38234,7 +38236,7 @@ async function exportDiagnosticLogs(options) {
38234
38236
  node: process.version,
38235
38237
  platform: process.platform,
38236
38238
  arch: process.arch,
38237
- packageVersion: true ? "0.1.11" : "unknown",
38239
+ packageVersion: true ? "0.1.12-beta-61603b42d4-260803155630" : "unknown",
38238
38240
  sensitiveContentIncluded: options.includeSensitiveContent,
38239
38241
  listenerAndLlmContentIncluded: true,
38240
38242
  credentialsAlwaysRedacted: true,
@@ -39116,6 +39118,47 @@ var init_logs_export = __esm({
39116
39118
  }
39117
39119
  });
39118
39120
 
39121
+ // src/capabilities-cli.ts
39122
+ var capabilities_cli_exports = {};
39123
+ __export(capabilities_cli_exports, {
39124
+ getA2ACapabilities: () => getA2ACapabilities,
39125
+ handleCapabilitiesCommand: () => handleCapabilitiesCommand
39126
+ });
39127
+ function getA2ACapabilities() {
39128
+ return {
39129
+ messageEligibleOfflineReplay: {
39130
+ ok: true,
39131
+ fixCommands: [],
39132
+ message: ""
39133
+ }
39134
+ };
39135
+ }
39136
+ function handleCapabilitiesCommand(args) {
39137
+ if (args.some((arg) => arg === "-h" || arg === "--help")) {
39138
+ process.stdout.write(CAPABILITIES_USAGE);
39139
+ return;
39140
+ }
39141
+ const unknown = args.filter((arg) => arg !== "--json");
39142
+ if (unknown.length > 0) {
39143
+ throw new Error(`Unknown capabilities option: ${unknown[0]}`);
39144
+ }
39145
+ const capabilities = getA2ACapabilities();
39146
+ if (args.includes("--json")) {
39147
+ process.stdout.write(`${JSON.stringify(capabilities)}
39148
+ `);
39149
+ return;
39150
+ }
39151
+ process.stdout.write(`${JSON.stringify(capabilities, null, 2)}
39152
+ `);
39153
+ }
39154
+ var CAPABILITIES_USAGE;
39155
+ var init_capabilities_cli = __esm({
39156
+ "src/capabilities-cli.ts"() {
39157
+ "use strict";
39158
+ 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";
39159
+ }
39160
+ });
39161
+
39119
39162
  // ../core/src/xmtp-sdk/onchainos/bin.ts
39120
39163
  async function resolve8() {
39121
39164
  if (resolvedBin) {
@@ -98535,6 +98578,9 @@ function parseGroupPayload(content3) {
98535
98578
  return null;
98536
98579
  }
98537
98580
  }
98581
+ function isOfflineReplayForTrigger(trigger) {
98582
+ return trigger === "startup";
98583
+ }
98538
98584
  function createOfflineReplayAddressSummary(address) {
98539
98585
  return {
98540
98586
  address,
@@ -99148,7 +99194,7 @@ var init_xmtp_sdk = __esm({
99148
99194
  logWithTimestamp(
99149
99195
  `[xmtp-sdk] refresh: ${address} has a prior sync timestamp, triggering offline replay`
99150
99196
  );
99151
- void this.replayOfflineMessagesForAddress(address).catch((err2) => {
99197
+ void this.replayOfflineMessagesForAddress(address, "agent_refresh").catch((err2) => {
99152
99198
  logWithTimestamp(`[xmtp-sdk] refresh: ${address} offline replay failed:`, err2);
99153
99199
  logger.error(
99154
99200
  LogEvent.OFFLINE_REPLAY_FAILED,
@@ -99236,7 +99282,7 @@ var init_xmtp_sdk = __esm({
99236
99282
  await this.startListeningForAddresses([address]);
99237
99283
  let replaySummary;
99238
99284
  try {
99239
- replaySummary = await this.replayOfflineMessagesForAddress(address);
99285
+ replaySummary = await this.replayOfflineMessagesForAddress(address, "client_recycle");
99240
99286
  } finally {
99241
99287
  await this.drainInboundReplayGate(address);
99242
99288
  }
@@ -99689,7 +99735,7 @@ var init_xmtp_sdk = __esm({
99689
99735
  * cannot overtake the stored backlog.
99690
99736
  */
99691
99737
  async completeStartupReplay() {
99692
- const summary = await this.replayOfflineMessages();
99738
+ const summary = await this.replayOfflineMessages("startup");
99693
99739
  for (const address of this.clients.keys()) {
99694
99740
  await this.drainInboundReplayGate(address);
99695
99741
  }
@@ -99800,9 +99846,10 @@ var init_xmtp_sdk = __esm({
99800
99846
  logWithTimestamp(`${tag} message listener started`);
99801
99847
  });
99802
99848
  }
99803
- async replayOfflineMessagesForAddress(address) {
99849
+ async replayOfflineMessagesForAddress(address, trigger = "periodic_repair") {
99804
99850
  const addressReplayStartedAt = Date.now();
99805
99851
  const summary = createOfflineReplayAddressSummary(address);
99852
+ const isOfflineReplay = isOfflineReplayForTrigger(trigger);
99806
99853
  const agent = this.clients.get(address);
99807
99854
  if (!agent) {
99808
99855
  summary.durationMs = Date.now() - addressReplayStartedAt;
@@ -99925,7 +99972,8 @@ var init_xmtp_sdk = __esm({
99925
99972
  };
99926
99973
  const handleStartedAt = Date.now();
99927
99974
  const handled = await processOffline(fakeCtx, handlerDeps, {
99928
- skipNotify: true
99975
+ skipNotify: true,
99976
+ isOfflineReplay
99929
99977
  });
99930
99978
  const handleMs = Date.now() - handleStartedAt;
99931
99979
  summary.handleMs += handleMs;
@@ -100003,14 +100051,14 @@ var init_xmtp_sdk = __esm({
100003
100051
  summary.durationMs = Date.now() - addressReplayStartedAt;
100004
100052
  return summary;
100005
100053
  }
100006
- async replayOfflineMessages() {
100054
+ async replayOfflineMessages(trigger = "periodic_repair") {
100007
100055
  await this._backupReady;
100008
100056
  const replayStartedAt = Date.now();
100009
100057
  let totalReplayed = 0;
100010
100058
  let totalSkipped = 0;
100011
100059
  const addresses = [];
100012
100060
  for (const address of this.clients.keys()) {
100013
- const summary2 = await this.replayOfflineMessagesForAddress(address);
100061
+ const summary2 = await this.replayOfflineMessagesForAddress(address, trigger);
100014
100062
  addresses.push(summary2);
100015
100063
  const { replayed, skipped: skipped2 } = summary2;
100016
100064
  totalReplayed += replayed;
@@ -100119,6 +100167,111 @@ var init_session_expired = __esm({
100119
100167
  }
100120
100168
  });
100121
100169
 
100170
+ // ../core/src/xmtp-sdk/onchainos/offline-replay-capability.ts
100171
+ function messageEligibleHelpAdvertisesOfflineReplay(helpText) {
100172
+ if (typeof helpText !== "string" || helpText.length === 0) {
100173
+ return false;
100174
+ }
100175
+ return helpText.toLowerCase().includes(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN);
100176
+ }
100177
+ function buildMessageEligibleArgs(params, opts) {
100178
+ const args = [
100179
+ "agent",
100180
+ "message-eligible",
100181
+ "--agent-id",
100182
+ params.agentId,
100183
+ "--client-agent-id",
100184
+ params.clientAgentId,
100185
+ "--provider-agent-id",
100186
+ params.providerAgentId,
100187
+ "--client-communication-address",
100188
+ params.clientCommunicationAddress,
100189
+ "--provider-communication-address",
100190
+ params.providerCommunicationAddress,
100191
+ "--job-id",
100192
+ params.jobId,
100193
+ "--group-id",
100194
+ params.groupId,
100195
+ "--direction",
100196
+ params.direction,
100197
+ "--provider-security-rate",
100198
+ String(params.providerSecurityRate)
100199
+ ];
100200
+ if (opts.offlineReplaySupported) {
100201
+ args.push(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG, String(params.isOfflineReplay));
100202
+ }
100203
+ return args;
100204
+ }
100205
+ function resolveOfflineReplayCapabilityMarker(supported) {
100206
+ return supported ? "supported" : "legacy_downgrade";
100207
+ }
100208
+ function maybeRecommendOnchainosUpgrade(supported) {
100209
+ if (supported || recommendedPairs.has(UPGRADE_RECOMMENDATION_PAIR_KEY)) {
100210
+ return;
100211
+ }
100212
+ const hook = XmtpService.getInstance().tools.notifyUpgradeRecommendation;
100213
+ if (!hook) {
100214
+ logWithTimestamp(
100215
+ "[onchainos] tools.notifyUpgradeRecommendation not injected \u2014 deferring offline-replay upgrade notice"
100216
+ );
100217
+ return;
100218
+ }
100219
+ recommendedPairs.add(UPGRADE_RECOMMENDATION_PAIR_KEY);
100220
+ try {
100221
+ void Promise.resolve(
100222
+ hook({
100223
+ component: "onchainos",
100224
+ capability: MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
100225
+ reason: UPGRADE_RECOMMENDATION_REASON
100226
+ })
100227
+ ).catch((err2) => {
100228
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation async delivery failed:", err2);
100229
+ });
100230
+ } catch (err2) {
100231
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation threw:", err2);
100232
+ }
100233
+ }
100234
+ function detectMessageEligibleOfflineReplaySupport(execFn = exec) {
100235
+ if (!capabilityProbe) {
100236
+ capabilityProbe = (async () => {
100237
+ try {
100238
+ const { stdout, stderr } = await execFn(["agent", "message-eligible", "--help"]);
100239
+ const supported = messageEligibleHelpAdvertisesOfflineReplay(`${stdout}
100240
+ ${stderr}`);
100241
+ logWithTimestamp(
100242
+ `[onchainos] message-eligible offline-replay capability: ${supported ? "supported" : "legacy_downgrade"}`
100243
+ );
100244
+ return supported;
100245
+ } catch (err2) {
100246
+ logWithTimestamp(
100247
+ "[onchainos] message-eligible --help probe failed; treating offline-replay as unsupported (legacy):",
100248
+ err2
100249
+ );
100250
+ return false;
100251
+ }
100252
+ })().then((supported) => {
100253
+ maybeRecommendOnchainosUpgrade(supported);
100254
+ return supported;
100255
+ });
100256
+ }
100257
+ return capabilityProbe;
100258
+ }
100259
+ var MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG, MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN, UPGRADE_RECOMMENDATION_PAIR_KEY, UPGRADE_RECOMMENDATION_REASON, capabilityProbe, recommendedPairs;
100260
+ var init_offline_replay_capability = __esm({
100261
+ "../core/src/xmtp-sdk/onchainos/offline-replay-capability.ts"() {
100262
+ "use strict";
100263
+ init_log();
100264
+ init_bin();
100265
+ init_xmtp_sdk();
100266
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG = "--is-offline-replay";
100267
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN = "is-offline-replay";
100268
+ UPGRADE_RECOMMENDATION_PAIR_KEY = "onchainos:message-eligible-offline-replay";
100269
+ UPGRADE_RECOMMENDATION_REASON = "OnchainOS does not support the offline-replay eligibility argument; using the legacy message-eligible contract.";
100270
+ capabilityProbe = null;
100271
+ recommendedPairs = /* @__PURE__ */ new Set();
100272
+ }
100273
+ });
100274
+
100122
100275
  // ../core/src/xmtp-sdk/onchainos/system-config.ts
100123
100276
  function notifyExpired(command, options) {
100124
100277
  if (options?.notifySessionExpired) {
@@ -100510,35 +100663,32 @@ async function sendHeartbeat(chainIndex) {
100510
100663
  }
100511
100664
  async function checkMessageEligible(params) {
100512
100665
  logWithTimestamp(
100513
- `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate}`
100666
+ `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate} isOfflineReplay=${params.isOfflineReplay}`
100514
100667
  );
100668
+ const offlineReplaySupported = await detectMessageEligibleOfflineReplaySupport();
100669
+ const offlineReplayCapability = resolveOfflineReplayCapabilityMarker(offlineReplaySupported);
100670
+ const emitEligibilityChecked = (outcome, eligible) => {
100671
+ logger.info(LogEvent.MESSAGE_ELIGIBILITY_CHECKED, {
100672
+ component: "onchainos_cli",
100673
+ source: "onchainos",
100674
+ stage: "eligibility_check",
100675
+ direction: params.direction,
100676
+ jobId: params.jobId,
100677
+ isOfflineReplay: String(params.isOfflineReplay),
100678
+ offlineReplayCapability,
100679
+ outcome,
100680
+ eligible
100681
+ });
100682
+ };
100515
100683
  let stdout;
100516
100684
  let stderr;
100517
100685
  try {
100518
- ({ stdout, stderr } = await exec([
100519
- "agent",
100520
- "message-eligible",
100521
- "--agent-id",
100522
- params.agentId,
100523
- "--client-agent-id",
100524
- params.clientAgentId,
100525
- "--provider-agent-id",
100526
- params.providerAgentId,
100527
- "--client-communication-address",
100528
- params.clientCommunicationAddress,
100529
- "--provider-communication-address",
100530
- params.providerCommunicationAddress,
100531
- "--job-id",
100532
- params.jobId,
100533
- "--group-id",
100534
- params.groupId,
100535
- "--direction",
100536
- params.direction,
100537
- "--provider-security-rate",
100538
- String(params.providerSecurityRate)
100539
- ]));
100686
+ ({ stdout, stderr } = await exec(
100687
+ buildMessageEligibleArgs(params, { offlineReplaySupported })
100688
+ ));
100540
100689
  } catch (err2) {
100541
100690
  guardCatchSessionExpired2(err2, "message-eligible");
100691
+ emitEligibilityChecked("failed", "unknown");
100542
100692
  throw messageEligibleUnavailableError({
100543
100693
  reason: "message-eligible-cli-error",
100544
100694
  stdout: typeof err2?.stdout === "string" ? err2.stdout : "",
@@ -100563,6 +100713,7 @@ async function checkMessageEligible(params) {
100563
100713
  providerSecurityRate: String(params.providerSecurityRate)
100564
100714
  });
100565
100715
  } catch (err2) {
100716
+ emitEligibilityChecked("failed", "unknown");
100566
100717
  throw messageEligibleUnavailableError({
100567
100718
  reason: "message-eligible-json-parse-failed",
100568
100719
  stdout,
@@ -100592,6 +100743,7 @@ async function checkMessageEligible(params) {
100592
100743
  reason: "ok=false"
100593
100744
  })
100594
100745
  );
100746
+ emitEligibilityChecked("failed", "unknown");
100595
100747
  throw messageEligibleUnavailableError({
100596
100748
  reason: "message-eligible-ok-false",
100597
100749
  stdout,
@@ -100601,6 +100753,7 @@ async function checkMessageEligible(params) {
100601
100753
  logWithTimestamp(
100602
100754
  `[onchainos] message-eligible result: eligible=${res.data.eligible}`
100603
100755
  );
100756
+ emitEligibilityChecked("success", String(res.data.eligible));
100604
100757
  return res.data;
100605
100758
  }
100606
100759
  async function fetchSensitiveWords() {
@@ -100654,6 +100807,7 @@ var init_onchainos = __esm({
100654
100807
  init_xmtp_sdk();
100655
100808
  init_cli_response();
100656
100809
  init_session_expired();
100810
+ init_offline_replay_capability();
100657
100811
  init_cli_response();
100658
100812
  init_session_expired();
100659
100813
  init_system_config();
@@ -101502,7 +101656,9 @@ async function assertOutboundEligible(params) {
101502
101656
  jobId,
101503
101657
  groupId,
101504
101658
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
101505
- providerSecurityRate
101659
+ providerSecurityRate,
101660
+ // Outbound messages are never startup backlog recovery.
101661
+ isOfflineReplay: false
101506
101662
  });
101507
101663
  logWithTimestamp(
101508
101664
  `[okx-agent-task] outbound message-eligible: senderAgentId=${senderAgent.agentId} receiverAgentId=${receiverAgent.agentId} job=${jobId} group=${groupId} result=${JSON.stringify(result)}`
@@ -106443,6 +106599,7 @@ async function verifyInboundA2AGroupMessage(params) {
106443
106599
  messageId,
106444
106600
  consentState,
106445
106601
  trustedSystemSender = false,
106602
+ isOfflineReplay = false,
106446
106603
  timing
106447
106604
  } = params;
106448
106605
  if (!isA2AEnvelope(payload)) {
@@ -106546,7 +106703,8 @@ async function verifyInboundA2AGroupMessage(params) {
106546
106703
  jobId,
106547
106704
  groupId,
106548
106705
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
106549
- providerSecurityRate
106706
+ providerSecurityRate,
106707
+ isOfflineReplay
106550
106708
  });
106551
106709
  timing?.mark("eligibilityCheck", eligibilityStartedAt);
106552
106710
  logWithTimestamp(
@@ -106992,6 +107150,7 @@ async function processFileMessage(ctx, deps, options = {}) {
106992
107150
  messageId,
106993
107151
  consentState: ctx.conversation instanceof Group ? ctx.conversation.consentState : void 0,
106994
107152
  trustedSystemSender,
107153
+ isOfflineReplay: options.isOfflineReplay ?? false,
106995
107154
  timing
106996
107155
  });
106997
107156
  if (!accepted) {
@@ -107716,14 +107875,28 @@ async function runListenerWithLock(options, paths) {
107716
107875
  reason: "append_backup_failed"
107717
107876
  });
107718
107877
  });
107878
+ },
107879
+ notifyUpgradeRecommendation: (input) => {
107880
+ const content3 = "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";
107881
+ void notifySystemMessageToUser({
107882
+ title: "OnchainOS upgrade recommended",
107883
+ content: content3,
107884
+ store: sessionStore,
107885
+ idempotencyKey: UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY
107886
+ }).catch((err2) => {
107887
+ logWithTimestamp(
107888
+ `[okx-agent-task] upgrade recommendation notify failed component=${input.component} capability=${input.capability}:`,
107889
+ err2
107890
+ );
107891
+ });
107719
107892
  }
107720
107893
  });
107721
- service.setPluginVersion("0.1.11");
107894
+ service.setPluginVersion("0.1.12-beta-61603b42d4-260803155630");
107722
107895
  await service.init();
107723
107896
  const pluginVersionStatus = service.pluginVersionStatus;
107724
107897
  if (pluginVersionStatus.unavailable) {
107725
107898
  throw new Error(
107726
- `@okxweb3/a2a-node v${"0.1.11"} is below the required minimum v${pluginVersionStatus.minVersion}`
107899
+ `@okxweb3/a2a-node v${"0.1.12-beta-61603b42d4-260803155630"} is below the required minimum v${pluginVersionStatus.minVersion}`
107727
107900
  );
107728
107901
  }
107729
107902
  const systemConfig = service.getSystemConfig();
@@ -107741,12 +107914,14 @@ async function runListenerWithLock(options, paths) {
107741
107914
  onchainosAgentId: "*",
107742
107915
  reason: "system-config missing sentryDsn",
107743
107916
  pluginId: "@okxweb3/a2a-node",
107744
- pluginVersion: "0.1.11"
107917
+ pluginVersion: "0.1.12-beta-61603b42d4-260803155630"
107745
107918
  });
107746
107919
  }
107747
107920
  logWithTimestamp(
107748
107921
  `[okx-agent-task] listener initialized, clients=${service.getClients().size}, home=${store.homeDir}`
107749
107922
  );
107923
+ void detectMessageEligibleOfflineReplaySupport().catch(() => {
107924
+ });
107750
107925
  const startupReplaySummary = await service.completeStartupReplay();
107751
107926
  logWithTimestamp(
107752
107927
  `[okx-agent-task] startup replay complete clients=${startupReplaySummary.clients} replayed=${startupReplaySummary.replayed} skipped=${startupReplaySummary.skipped} duration=${startupReplaySummary.durationMs}ms`
@@ -107884,7 +108059,7 @@ async function runListenerWithLock(options, paths) {
107884
108059
  );
107885
108060
  return;
107886
108061
  }
107887
- const replayResult = await timeSettled(() => service.replayOfflineMessages());
108062
+ const replayResult = await timeSettled(() => service.replayOfflineMessages("periodic_repair"));
107888
108063
  const replayMs = replayResult.durationMs;
107889
108064
  if (replayResult.error) {
107890
108065
  logger.error(LogEvent.OFFLINE_REPLAY_FAILED, replayResult.error instanceof Error ? replayResult.error : new Error(String(replayResult.error)), {
@@ -108115,7 +108290,7 @@ async function timeSettled(fn) {
108115
108290
  return { durationMs: Date.now() - startedAt, error };
108116
108291
  }
108117
108292
  }
108118
- var import_node_fs23, import_promises13, import_node_os11, import_node_path28, 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;
108293
+ var import_node_fs23, import_promises13, import_node_os11, import_node_path28, 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;
108119
108294
  var init_listener = __esm({
108120
108295
  "src/listener.ts"() {
108121
108296
  "use strict";
@@ -108126,6 +108301,7 @@ var init_listener = __esm({
108126
108301
  import_node_path28 = require("node:path");
108127
108302
  init_onchainos();
108128
108303
  init_system_config();
108304
+ init_offline_replay_capability();
108129
108305
  init_signer();
108130
108306
  init_xmtp_sdk();
108131
108307
  init_file_store();
@@ -108134,6 +108310,7 @@ var init_listener = __esm({
108134
108310
  init_command_store();
108135
108311
  init_ai_dispatch_queue();
108136
108312
  init_message_handler();
108313
+ init_agent_message_notice();
108137
108314
  init_paths();
108138
108315
  init_user_attention_ipc();
108139
108316
  init_user_attention_watchers();
@@ -108146,6 +108323,7 @@ var init_listener = __esm({
108146
108323
  DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC = 6 * 60 * 60;
108147
108324
  XMTP_CLIENT_RECYCLE_INTERVAL_ENV = "OKX_A2A_XMTP_CLIENT_RECYCLE_INTERVAL_SEC";
108148
108325
  HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS = 2e3;
108326
+ UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY = "system:onchainos-offline-replay-upgrade";
108149
108327
  SHUTDOWN_FORCE_RESOLVE_MS = 5e3;
108150
108328
  }
108151
108329
  });
@@ -111763,7 +111941,7 @@ async function getCurrentNodeCliVersion() {
111763
111941
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
111764
111942
  }
111765
111943
  function getBundledNodeCliVersion() {
111766
- return true ? "0.1.11" : null;
111944
+ return true ? "0.1.12-beta-61603b42d4-260803155630" : null;
111767
111945
  }
111768
111946
  function readConfiguredAiProvider() {
111769
111947
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -111973,7 +112151,7 @@ async function updateHermes(release, options) {
111973
112151
  }
111974
112152
  }
111975
112153
  async function installGatewayPluginForDoctor(target) {
111976
- const release = isPrereleaseVersion("0.1.11") ? "beta" : "latest";
112154
+ const release = isPrereleaseVersion("0.1.12-beta-61603b42d4-260803155630") ? "beta" : "latest";
111977
112155
  const insideTargetGateway = detectGatewayInvocation() === target;
111978
112156
  const options = {
111979
112157
  restart: !insideTargetGateway,
@@ -112961,7 +113139,7 @@ async function runDoctor(options = {}) {
112961
113139
  platform: options.platform ?? process.platform,
112962
113140
  env: options.env ?? process.env,
112963
113141
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
112964
- cliVersion: options.cliVersion ?? (true ? "0.1.11" : "0.0.0"),
113142
+ cliVersion: options.cliVersion ?? (true ? "0.1.12-beta-61603b42d4-260803155630" : "0.0.0"),
112965
113143
  fixMode: options.fix === true,
112966
113144
  nonInteractive: options.nonInteractive === true,
112967
113145
  packageChanged: false,
@@ -114116,7 +114294,7 @@ init_sentry_logger();
114116
114294
  init_sentry_config();
114117
114295
  var CURRENT_GATEWAY_SESSION_KEYS_ENV4 = "OKX_A2A_CURRENT_GATEWAY_SESSION_KEYS";
114118
114296
  function printUsage2() {
114119
- console.log(`okx-a2a ${"0.1.11"}
114297
+ console.log(`okx-a2a ${"0.1.12-beta-61603b42d4-260803155630"}
114120
114298
 
114121
114299
  Usage:
114122
114300
  okx-a2a <command> [options]
@@ -114145,6 +114323,7 @@ Commands:
114145
114323
  Alias for runtime switch-current
114146
114324
  job-provider Manage job-to-provider bindings
114147
114325
  xmtp-send Queue an XMTP message through the running daemon
114326
+ capabilities Print machine-readable A2A capability negotiation status (used by OnchainOS)
114148
114327
 
114149
114328
  Global options:
114150
114329
  -h, --help Show help
@@ -114154,7 +114333,7 @@ Run \`okx-a2a <command> -h\` for command-specific help.
114154
114333
  `);
114155
114334
  }
114156
114335
  function printVersion() {
114157
- console.log("0.1.11");
114336
+ console.log("0.1.12-beta-61603b42d4-260803155630");
114158
114337
  }
114159
114338
  function printDaemonUsage() {
114160
114339
  console.log(`Usage: okx-a2a daemon <start|restart|stop|status|autostart> [options]
@@ -115495,6 +115674,11 @@ async function main() {
115495
115674
  if (hasHelpFlag5(args) && !shouldDeferHelpToNestedHandler(command, args) && printCommandUsage(command)) {
115496
115675
  return;
115497
115676
  }
115677
+ if (command === "capabilities") {
115678
+ const { handleCapabilitiesCommand: handleCapabilitiesCommand2 } = await Promise.resolve().then(() => (init_capabilities_cli(), capabilities_cli_exports));
115679
+ handleCapabilitiesCommand2(process.argv.slice(3));
115680
+ return;
115681
+ }
115498
115682
  if (command === "daemon") {
115499
115683
  if (hasHelpFlag5(args)) {
115500
115684
  printDaemonUsage();
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_CLIENT_REMOVED: "Agent client removed",
19602
19603
  ADDRESS_SYNC_COMPLETED: "Address sync completed",
@@ -20107,6 +20108,7 @@ var init_sentry_logger = __esm({
20107
20108
  LogEvent.HERMES_SESSION_ROUTE_BINDING,
20108
20109
  LogEvent.INBOUND_BLOCKED_ADDRESS_MISMATCH,
20109
20110
  LogEvent.INBOUND_BLOCKED_INELIGIBLE,
20111
+ LogEvent.MESSAGE_ELIGIBILITY_CHECKED,
20110
20112
  LogEvent.INBOUND_BLOCKED_SENSITIVE,
20111
20113
  LogEvent.INBOUND_DROP_DM_INVALID_PAYLOAD,
20112
20114
  LogEvent.INBOUND_DROP_DM_NON_SYSTEM,
@@ -73047,7 +73049,7 @@ var init_sentry_config = __esm({
73047
73049
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
73048
73050
  SENTRY_CONFIG = {
73049
73051
  projectName: "okx/openclaw-okx-a2a-extension",
73050
- release: "0.1.11",
73052
+ release: "0.1.12-beta-61603b42d4-260803155630",
73051
73053
  environment,
73052
73054
  runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
73053
73055
  };
@@ -73977,7 +73979,7 @@ async function getCurrentNodeCliVersion() {
73977
73979
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
73978
73980
  }
73979
73981
  function getBundledNodeCliVersion() {
73980
- return true ? "0.1.11" : null;
73982
+ return true ? "0.1.12-beta-61603b42d4-260803155630" : null;
73981
73983
  }
73982
73984
  function readConfiguredAiProvider() {
73983
73985
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -74187,7 +74189,7 @@ async function updateHermes(release, options) {
74187
74189
  }
74188
74190
  }
74189
74191
  async function installGatewayPluginForDoctor(target) {
74190
- const release = isPrereleaseVersion("0.1.11") ? "beta" : "latest";
74192
+ const release = isPrereleaseVersion("0.1.12-beta-61603b42d4-260803155630") ? "beta" : "latest";
74191
74193
  const insideTargetGateway = detectGatewayInvocation() === target;
74192
74194
  const options = {
74193
74195
  restart: !insideTargetGateway,
@@ -84857,6 +84859,8 @@ __export(index_exports, {
84857
84859
  HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS: () => HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
84858
84860
  InboundReplayGate: () => InboundReplayGate,
84859
84861
  InvalidXmtpMessageStore: () => InvalidXmtpMessageStore,
84862
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
84863
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
84860
84864
  NATIVE_LAUNCHER_EXE_NAME: () => NATIVE_LAUNCHER_EXE_NAME,
84861
84865
  OPENCLAW_GATEWAY_ROUTE_GROUP_ID: () => OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
84862
84866
  SYSTEM_NOTIFICATION_SESSION_KEY: () => SYSTEM_NOTIFICATION_SESSION_KEY,
@@ -84874,6 +84878,7 @@ __export(index_exports, {
84874
84878
  XMTP_CLIENT_RECYCLE_INTERVAL_ENV: () => XMTP_CLIENT_RECYCLE_INTERVAL_ENV,
84875
84879
  XmtpDm: () => Dm,
84876
84880
  XmtpGroup: () => Group,
84881
+ XmtpService: () => XmtpService,
84877
84882
  acquireDaemonLock: () => acquireDaemonLock,
84878
84883
  activeDaemonPointerPath: () => activeDaemonPointerPath,
84879
84884
  aiRunSentryExtra: () => aiRunSentryExtra,
@@ -84894,6 +84899,7 @@ __export(index_exports, {
84894
84899
  buildInboundTerminalExtras: () => buildInboundTerminalExtras,
84895
84900
  buildLaunchdPlist: () => buildLaunchdPlist,
84896
84901
  buildLauncherEntryJs: () => buildLauncherEntryJs,
84902
+ buildMessageEligibleArgs: () => buildMessageEligibleArgs,
84897
84903
  buildPendingConversationBackupDispatch: () => buildPendingConversationBackupDispatch,
84898
84904
  buildSchtasksCreateArgs: () => buildSchtasksCreateArgs,
84899
84905
  buildSchtasksDeleteArgs: () => buildSchtasksDeleteArgs,
@@ -84925,6 +84931,7 @@ __export(index_exports, {
84925
84931
  detectAiProviders: () => detectAiProviders,
84926
84932
  detectCurrentAiProvider: () => detectCurrentAiProvider,
84927
84933
  detectGatewayInvocation: () => detectGatewayInvocation,
84934
+ detectMessageEligibleOfflineReplaySupport: () => detectMessageEligibleOfflineReplaySupport,
84928
84935
  detectRuntime: () => detectRuntime,
84929
84936
  ensureDaemonReady: () => ensureDaemonReady,
84930
84937
  ensureDefaultAiProvider: () => ensureDefaultAiProvider,
@@ -84938,9 +84945,11 @@ __export(index_exports, {
84938
84945
  findWindowsNativeOkxA2aExe: () => findWindowsNativeOkxA2aExe,
84939
84946
  followFile: () => followFile,
84940
84947
  formatDoctorReportForHumans: () => formatDoctorReportForHumans,
84948
+ getA2ACapabilities: () => getA2ACapabilities,
84941
84949
  getDaemonStatus: () => getDaemonStatus,
84942
84950
  getHermesGatewayPluginStatus: () => getHermesGatewayPluginStatus,
84943
84951
  getSessionBusyTracker: () => getSessionBusyTracker,
84952
+ handleCapabilitiesCommand: () => handleCapabilitiesCommand,
84944
84953
  handleDoctorCommand: () => handleDoctorCommand,
84945
84954
  handleXmtpSendCommand: () => handleXmtpSendCommand,
84946
84955
  hasAiRuntimeMarker: () => hasAiRuntimeMarker,
@@ -84952,6 +84961,7 @@ __export(index_exports, {
84952
84961
  isDefaultTaskHome: () => isDefaultTaskHome,
84953
84962
  isGatewayAvailableForHeartbeat: () => isGatewayAvailableForHeartbeat,
84954
84963
  isHermesGatewayPluginEnabled: () => isHermesGatewayPluginEnabled,
84964
+ isOfflineReplayForTrigger: () => isOfflineReplayForTrigger,
84955
84965
  isOpenClawA2aPluginAvailable: () => isOpenClawA2aPluginAvailable,
84956
84966
  isOpenClawGatewayAvailable: () => isOpenClawGatewayAvailable,
84957
84967
  isOriginalParentProcessAlive: () => isOriginalParentProcessAlive,
@@ -84962,6 +84972,8 @@ __export(index_exports, {
84962
84972
  killProcessTree: () => killProcessTree,
84963
84973
  launchdPlistHasCurrentRestartPolicy: () => launchdPlistHasCurrentRestartPolicy,
84964
84974
  logWinCompat: () => logWinCompat,
84975
+ maybeRecommendOnchainosUpgrade: () => maybeRecommendOnchainosUpgrade,
84976
+ messageEligibleHelpAdvertisesOfflineReplay: () => messageEligibleHelpAdvertisesOfflineReplay,
84965
84977
  nodeVersionSatisfies: () => nodeVersionSatisfies,
84966
84978
  normalizeAiProvider: () => normalizeAiProvider,
84967
84979
  normalizeHermesOkxA2aPluginConfig: () => normalizeHermesOkxA2aPluginConfig,
@@ -84986,6 +84998,7 @@ __export(index_exports, {
84986
84998
  removeUserAttentionWatcher: () => removeUserAttentionWatcher,
84987
84999
  reportDoctorRunToSentry: () => reportDoctorRunToSentry,
84988
85000
  reportUserNotificationFailure: () => reportUserNotificationFailure,
85001
+ resetMessageEligibleOfflineReplayCapabilityForTests: () => resetMessageEligibleOfflineReplayCapabilityForTests,
84989
85002
  resetResolvedOnchainosBinForTests: () => resetResolvedOnchainosBinForTests,
84990
85003
  resolveAiPermissionPreset: () => resolveAiPermissionPreset,
84991
85004
  resolveAiProviderCommand: () => resolveAiProviderCommand,
@@ -84997,6 +85010,7 @@ __export(index_exports, {
84997
85010
  resolveDoctorTarget: () => resolveDoctorTarget,
84998
85011
  resolveEnvelopePayload: () => resolveEnvelopePayload,
84999
85012
  resolveNpmCmdShimScript: () => resolveNpmCmdShimScript,
85013
+ resolveOfflineReplayCapabilityMarker: () => resolveOfflineReplayCapabilityMarker,
85000
85014
  resolveOfflineReplayIntervalSec: () => resolveOfflineReplayIntervalSec,
85001
85015
  resolveOpenClawGatewayConfig: () => resolveOpenClawGatewayConfig,
85002
85016
  resolveOpenClawGatewayRoute: () => resolveOpenClawGatewayRoute,
@@ -97878,6 +97892,9 @@ function parseGroupPayload(content3) {
97878
97892
  return null;
97879
97893
  }
97880
97894
  }
97895
+ function isOfflineReplayForTrigger(trigger) {
97896
+ return trigger === "startup";
97897
+ }
97881
97898
  var XmtpService = class _XmtpService {
97882
97899
  static instance = null;
97883
97900
  env = process.env.XMTP_ENV ?? "production";
@@ -98391,7 +98408,7 @@ var XmtpService = class _XmtpService {
98391
98408
  logWithTimestamp(
98392
98409
  `[xmtp-sdk] refresh: ${address} has a prior sync timestamp, triggering offline replay`
98393
98410
  );
98394
- void this.replayOfflineMessagesForAddress(address).catch((err2) => {
98411
+ void this.replayOfflineMessagesForAddress(address, "agent_refresh").catch((err2) => {
98395
98412
  logWithTimestamp(`[xmtp-sdk] refresh: ${address} offline replay failed:`, err2);
98396
98413
  logger.error(
98397
98414
  LogEvent.OFFLINE_REPLAY_FAILED,
@@ -98479,7 +98496,7 @@ var XmtpService = class _XmtpService {
98479
98496
  await this.startListeningForAddresses([address]);
98480
98497
  let replaySummary;
98481
98498
  try {
98482
- replaySummary = await this.replayOfflineMessagesForAddress(address);
98499
+ replaySummary = await this.replayOfflineMessagesForAddress(address, "client_recycle");
98483
98500
  } finally {
98484
98501
  await this.drainInboundReplayGate(address);
98485
98502
  }
@@ -98932,7 +98949,7 @@ var XmtpService = class _XmtpService {
98932
98949
  * cannot overtake the stored backlog.
98933
98950
  */
98934
98951
  async completeStartupReplay() {
98935
- const summary = await this.replayOfflineMessages();
98952
+ const summary = await this.replayOfflineMessages("startup");
98936
98953
  for (const address of this.clients.keys()) {
98937
98954
  await this.drainInboundReplayGate(address);
98938
98955
  }
@@ -99043,9 +99060,10 @@ var XmtpService = class _XmtpService {
99043
99060
  logWithTimestamp(`${tag} message listener started`);
99044
99061
  });
99045
99062
  }
99046
- async replayOfflineMessagesForAddress(address) {
99063
+ async replayOfflineMessagesForAddress(address, trigger = "periodic_repair") {
99047
99064
  const addressReplayStartedAt = Date.now();
99048
99065
  const summary = createOfflineReplayAddressSummary(address);
99066
+ const isOfflineReplay = isOfflineReplayForTrigger(trigger);
99049
99067
  const agent = this.clients.get(address);
99050
99068
  if (!agent) {
99051
99069
  summary.durationMs = Date.now() - addressReplayStartedAt;
@@ -99168,7 +99186,8 @@ var XmtpService = class _XmtpService {
99168
99186
  };
99169
99187
  const handleStartedAt = Date.now();
99170
99188
  const handled = await processOffline(fakeCtx, handlerDeps, {
99171
- skipNotify: true
99189
+ skipNotify: true,
99190
+ isOfflineReplay
99172
99191
  });
99173
99192
  const handleMs = Date.now() - handleStartedAt;
99174
99193
  summary.handleMs += handleMs;
@@ -99246,14 +99265,14 @@ var XmtpService = class _XmtpService {
99246
99265
  summary.durationMs = Date.now() - addressReplayStartedAt;
99247
99266
  return summary;
99248
99267
  }
99249
- async replayOfflineMessages() {
99268
+ async replayOfflineMessages(trigger = "periodic_repair") {
99250
99269
  await this._backupReady;
99251
99270
  const replayStartedAt = Date.now();
99252
99271
  let totalReplayed = 0;
99253
99272
  let totalSkipped = 0;
99254
99273
  const addresses = [];
99255
99274
  for (const address of this.clients.keys()) {
99256
- const summary2 = await this.replayOfflineMessagesForAddress(address);
99275
+ const summary2 = await this.replayOfflineMessagesForAddress(address, trigger);
99257
99276
  addresses.push(summary2);
99258
99277
  const { replayed, skipped: skipped2 } = summary2;
99259
99278
  totalReplayed += replayed;
@@ -99694,6 +99713,107 @@ function reportSessionExpired(command) {
99694
99713
  );
99695
99714
  }
99696
99715
 
99716
+ // ../core/src/xmtp-sdk/onchainos/offline-replay-capability.ts
99717
+ init_log();
99718
+ var MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG = "--is-offline-replay";
99719
+ var MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN = "is-offline-replay";
99720
+ var UPGRADE_RECOMMENDATION_PAIR_KEY = "onchainos:message-eligible-offline-replay";
99721
+ var UPGRADE_RECOMMENDATION_REASON = "OnchainOS does not support the offline-replay eligibility argument; using the legacy message-eligible contract.";
99722
+ function messageEligibleHelpAdvertisesOfflineReplay(helpText) {
99723
+ if (typeof helpText !== "string" || helpText.length === 0) {
99724
+ return false;
99725
+ }
99726
+ return helpText.toLowerCase().includes(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN);
99727
+ }
99728
+ function buildMessageEligibleArgs(params, opts) {
99729
+ const args = [
99730
+ "agent",
99731
+ "message-eligible",
99732
+ "--agent-id",
99733
+ params.agentId,
99734
+ "--client-agent-id",
99735
+ params.clientAgentId,
99736
+ "--provider-agent-id",
99737
+ params.providerAgentId,
99738
+ "--client-communication-address",
99739
+ params.clientCommunicationAddress,
99740
+ "--provider-communication-address",
99741
+ params.providerCommunicationAddress,
99742
+ "--job-id",
99743
+ params.jobId,
99744
+ "--group-id",
99745
+ params.groupId,
99746
+ "--direction",
99747
+ params.direction,
99748
+ "--provider-security-rate",
99749
+ String(params.providerSecurityRate)
99750
+ ];
99751
+ if (opts.offlineReplaySupported) {
99752
+ args.push(MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG, String(params.isOfflineReplay));
99753
+ }
99754
+ return args;
99755
+ }
99756
+ function resolveOfflineReplayCapabilityMarker(supported) {
99757
+ return supported ? "supported" : "legacy_downgrade";
99758
+ }
99759
+ var capabilityProbe = null;
99760
+ var recommendedPairs = /* @__PURE__ */ new Set();
99761
+ function resetMessageEligibleOfflineReplayCapabilityForTests() {
99762
+ capabilityProbe = null;
99763
+ recommendedPairs.clear();
99764
+ }
99765
+ function maybeRecommendOnchainosUpgrade(supported) {
99766
+ if (supported || recommendedPairs.has(UPGRADE_RECOMMENDATION_PAIR_KEY)) {
99767
+ return;
99768
+ }
99769
+ const hook = XmtpService.getInstance().tools.notifyUpgradeRecommendation;
99770
+ if (!hook) {
99771
+ logWithTimestamp(
99772
+ "[onchainos] tools.notifyUpgradeRecommendation not injected \u2014 deferring offline-replay upgrade notice"
99773
+ );
99774
+ return;
99775
+ }
99776
+ recommendedPairs.add(UPGRADE_RECOMMENDATION_PAIR_KEY);
99777
+ try {
99778
+ void Promise.resolve(
99779
+ hook({
99780
+ component: "onchainos",
99781
+ capability: MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
99782
+ reason: UPGRADE_RECOMMENDATION_REASON
99783
+ })
99784
+ ).catch((err2) => {
99785
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation async delivery failed:", err2);
99786
+ });
99787
+ } catch (err2) {
99788
+ logWithTimestamp("[onchainos] tools.notifyUpgradeRecommendation threw:", err2);
99789
+ }
99790
+ }
99791
+ function detectMessageEligibleOfflineReplaySupport(execFn = exec) {
99792
+ if (!capabilityProbe) {
99793
+ capabilityProbe = (async () => {
99794
+ try {
99795
+ const { stdout, stderr } = await execFn(["agent", "message-eligible", "--help"]);
99796
+ const supported = messageEligibleHelpAdvertisesOfflineReplay(`${stdout}
99797
+ ${stderr}`);
99798
+ logWithTimestamp(
99799
+ `[onchainos] message-eligible offline-replay capability: ${supported ? "supported" : "legacy_downgrade"}`
99800
+ );
99801
+ return supported;
99802
+ } catch (err2) {
99803
+ logWithTimestamp(
99804
+ "[onchainos] message-eligible --help probe failed; treating offline-replay as unsupported (legacy):",
99805
+ err2
99806
+ );
99807
+ return false;
99808
+ }
99809
+ })().then((supported) => {
99810
+ maybeRecommendOnchainosUpgrade(supported);
99811
+ return supported;
99812
+ });
99813
+ }
99814
+ return capabilityProbe;
99815
+ }
99816
+
99697
99817
  // ../core/src/xmtp-sdk/onchainos/system-config.ts
99698
99818
  init_log();
99699
99819
  init_sentry_logger();
@@ -100083,35 +100203,32 @@ async function sendHeartbeat(chainIndex) {
100083
100203
  }
100084
100204
  async function checkMessageEligible(params) {
100085
100205
  logWithTimestamp(
100086
- `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate}`
100206
+ `[onchainos] checking message-eligible: job=${params.jobId} direction=${params.direction} providerSecurityRate=${params.providerSecurityRate} isOfflineReplay=${params.isOfflineReplay}`
100087
100207
  );
100208
+ const offlineReplaySupported = await detectMessageEligibleOfflineReplaySupport();
100209
+ const offlineReplayCapability = resolveOfflineReplayCapabilityMarker(offlineReplaySupported);
100210
+ const emitEligibilityChecked = (outcome, eligible) => {
100211
+ logger.info(LogEvent.MESSAGE_ELIGIBILITY_CHECKED, {
100212
+ component: "onchainos_cli",
100213
+ source: "onchainos",
100214
+ stage: "eligibility_check",
100215
+ direction: params.direction,
100216
+ jobId: params.jobId,
100217
+ isOfflineReplay: String(params.isOfflineReplay),
100218
+ offlineReplayCapability,
100219
+ outcome,
100220
+ eligible
100221
+ });
100222
+ };
100088
100223
  let stdout;
100089
100224
  let stderr;
100090
100225
  try {
100091
- ({ stdout, stderr } = await exec([
100092
- "agent",
100093
- "message-eligible",
100094
- "--agent-id",
100095
- params.agentId,
100096
- "--client-agent-id",
100097
- params.clientAgentId,
100098
- "--provider-agent-id",
100099
- params.providerAgentId,
100100
- "--client-communication-address",
100101
- params.clientCommunicationAddress,
100102
- "--provider-communication-address",
100103
- params.providerCommunicationAddress,
100104
- "--job-id",
100105
- params.jobId,
100106
- "--group-id",
100107
- params.groupId,
100108
- "--direction",
100109
- params.direction,
100110
- "--provider-security-rate",
100111
- String(params.providerSecurityRate)
100112
- ]));
100226
+ ({ stdout, stderr } = await exec(
100227
+ buildMessageEligibleArgs(params, { offlineReplaySupported })
100228
+ ));
100113
100229
  } catch (err2) {
100114
100230
  guardCatchSessionExpired2(err2, "message-eligible");
100231
+ emitEligibilityChecked("failed", "unknown");
100115
100232
  throw messageEligibleUnavailableError({
100116
100233
  reason: "message-eligible-cli-error",
100117
100234
  stdout: typeof err2?.stdout === "string" ? err2.stdout : "",
@@ -100136,6 +100253,7 @@ async function checkMessageEligible(params) {
100136
100253
  providerSecurityRate: String(params.providerSecurityRate)
100137
100254
  });
100138
100255
  } catch (err2) {
100256
+ emitEligibilityChecked("failed", "unknown");
100139
100257
  throw messageEligibleUnavailableError({
100140
100258
  reason: "message-eligible-json-parse-failed",
100141
100259
  stdout,
@@ -100165,6 +100283,7 @@ async function checkMessageEligible(params) {
100165
100283
  reason: "ok=false"
100166
100284
  })
100167
100285
  );
100286
+ emitEligibilityChecked("failed", "unknown");
100168
100287
  throw messageEligibleUnavailableError({
100169
100288
  reason: "message-eligible-ok-false",
100170
100289
  stdout,
@@ -100174,6 +100293,7 @@ async function checkMessageEligible(params) {
100174
100293
  logWithTimestamp(
100175
100294
  `[onchainos] message-eligible result: eligible=${res.data.eligible}`
100176
100295
  );
100296
+ emitEligibilityChecked("success", String(res.data.eligible));
100177
100297
  return res.data;
100178
100298
  }
100179
100299
  async function fetchSensitiveWords() {
@@ -100996,7 +101116,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
100996
101116
  client: {
100997
101117
  id: "gateway-client",
100998
101118
  displayName: "okx-a2a-node",
100999
- version: "0.1.11",
101119
+ version: "0.1.12-beta-61603b42d4-260803155630",
101000
101120
  platform: "node",
101001
101121
  mode: "backend",
101002
101122
  instanceId
@@ -101007,7 +101127,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
101007
101127
  commands: [],
101008
101128
  permissions: {},
101009
101129
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
101010
- userAgent: `okx-a2a-node/${"0.1.11"}`,
101130
+ userAgent: `okx-a2a-node/${"0.1.12-beta-61603b42d4-260803155630"}`,
101011
101131
  auth: {
101012
101132
  ...config.token ? { token: config.token } : {},
101013
101133
  ...config.password ? { password: config.password } : {}
@@ -102749,7 +102869,9 @@ async function assertOutboundEligible(params) {
102749
102869
  jobId,
102750
102870
  groupId,
102751
102871
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
102752
- providerSecurityRate
102872
+ providerSecurityRate,
102873
+ // Outbound messages are never startup backlog recovery.
102874
+ isOfflineReplay: false
102753
102875
  });
102754
102876
  logWithTimestamp(
102755
102877
  `[okx-agent-task] outbound message-eligible: senderAgentId=${senderAgent.agentId} receiverAgentId=${receiverAgent.agentId} job=${jobId} group=${groupId} result=${JSON.stringify(result)}`
@@ -104933,6 +105055,7 @@ async function verifyInboundA2AGroupMessage(params) {
104933
105055
  messageId,
104934
105056
  consentState,
104935
105057
  trustedSystemSender = false,
105058
+ isOfflineReplay = false,
104936
105059
  timing
104937
105060
  } = params;
104938
105061
  if (!isA2AEnvelope(payload)) {
@@ -105036,7 +105159,8 @@ async function verifyInboundA2AGroupMessage(params) {
105036
105159
  jobId,
105037
105160
  groupId,
105038
105161
  direction: isSenderClient ? "client_to_provider" : "provider_to_client",
105039
- providerSecurityRate
105162
+ providerSecurityRate,
105163
+ isOfflineReplay
105040
105164
  });
105041
105165
  timing?.mark("eligibilityCheck", eligibilityStartedAt);
105042
105166
  logWithTimestamp(
@@ -105482,6 +105606,7 @@ async function processFileMessage(ctx, deps, options = {}) {
105482
105606
  messageId,
105483
105607
  consentState: ctx.conversation instanceof Group ? ctx.conversation.consentState : void 0,
105484
105608
  trustedSystemSender,
105609
+ isOfflineReplay: options.isOfflineReplay ?? false,
105485
105610
  timing
105486
105611
  });
105487
105612
  if (!accepted) {
@@ -106065,6 +106190,7 @@ var DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC = 300;
106065
106190
  var DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC = 6 * 60 * 60;
106066
106191
  var XMTP_CLIENT_RECYCLE_INTERVAL_ENV = "OKX_A2A_XMTP_CLIENT_RECYCLE_INTERVAL_SEC";
106067
106192
  var HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS = 2e3;
106193
+ var UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY = "system:onchainos-offline-replay-upgrade";
106068
106194
  function resolveOfflineReplayIntervalSec(systemConfig) {
106069
106195
  const interval = systemConfig.offlineReplayInterval;
106070
106196
  if (typeof interval !== "number" || !Number.isFinite(interval) || interval <= 0) {
@@ -106305,14 +106431,28 @@ async function runListenerWithLock(options, paths) {
106305
106431
  reason: "append_backup_failed"
106306
106432
  });
106307
106433
  });
106434
+ },
106435
+ notifyUpgradeRecommendation: (input) => {
106436
+ const content3 = "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";
106437
+ void notifySystemMessageToUser({
106438
+ title: "OnchainOS upgrade recommended",
106439
+ content: content3,
106440
+ store: sessionStore,
106441
+ idempotencyKey: UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY
106442
+ }).catch((err2) => {
106443
+ logWithTimestamp(
106444
+ `[okx-agent-task] upgrade recommendation notify failed component=${input.component} capability=${input.capability}:`,
106445
+ err2
106446
+ );
106447
+ });
106308
106448
  }
106309
106449
  });
106310
- service.setPluginVersion("0.1.11");
106450
+ service.setPluginVersion("0.1.12-beta-61603b42d4-260803155630");
106311
106451
  await service.init();
106312
106452
  const pluginVersionStatus = service.pluginVersionStatus;
106313
106453
  if (pluginVersionStatus.unavailable) {
106314
106454
  throw new Error(
106315
- `@okxweb3/a2a-node v${"0.1.11"} is below the required minimum v${pluginVersionStatus.minVersion}`
106455
+ `@okxweb3/a2a-node v${"0.1.12-beta-61603b42d4-260803155630"} is below the required minimum v${pluginVersionStatus.minVersion}`
106316
106456
  );
106317
106457
  }
106318
106458
  const systemConfig = service.getSystemConfig();
@@ -106330,12 +106470,14 @@ async function runListenerWithLock(options, paths) {
106330
106470
  onchainosAgentId: "*",
106331
106471
  reason: "system-config missing sentryDsn",
106332
106472
  pluginId: "@okxweb3/a2a-node",
106333
- pluginVersion: "0.1.11"
106473
+ pluginVersion: "0.1.12-beta-61603b42d4-260803155630"
106334
106474
  });
106335
106475
  }
106336
106476
  logWithTimestamp(
106337
106477
  `[okx-agent-task] listener initialized, clients=${service.getClients().size}, home=${store.homeDir}`
106338
106478
  );
106479
+ void detectMessageEligibleOfflineReplaySupport().catch(() => {
106480
+ });
106339
106481
  const startupReplaySummary = await service.completeStartupReplay();
106340
106482
  logWithTimestamp(
106341
106483
  `[okx-agent-task] startup replay complete clients=${startupReplaySummary.clients} replayed=${startupReplaySummary.replayed} skipped=${startupReplaySummary.skipped} duration=${startupReplaySummary.durationMs}ms`
@@ -106473,7 +106615,7 @@ async function runListenerWithLock(options, paths) {
106473
106615
  );
106474
106616
  return;
106475
106617
  }
106476
- const replayResult = await timeSettled(() => service.replayOfflineMessages());
106618
+ const replayResult = await timeSettled(() => service.replayOfflineMessages("periodic_repair"));
106477
106619
  const replayMs = replayResult.durationMs;
106478
106620
  if (replayResult.error) {
106479
106621
  logger.error(LogEvent.OFFLINE_REPLAY_FAILED, replayResult.error instanceof Error ? replayResult.error : new Error(String(replayResult.error)), {
@@ -106713,6 +106855,36 @@ init_ai_command();
106713
106855
  init_update_cli();
106714
106856
  init_win_spawn();
106715
106857
 
106858
+ // src/capabilities-cli.ts
106859
+ function getA2ACapabilities() {
106860
+ return {
106861
+ messageEligibleOfflineReplay: {
106862
+ ok: true,
106863
+ fixCommands: [],
106864
+ message: ""
106865
+ }
106866
+ };
106867
+ }
106868
+ 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";
106869
+ function handleCapabilitiesCommand(args) {
106870
+ if (args.some((arg) => arg === "-h" || arg === "--help")) {
106871
+ process.stdout.write(CAPABILITIES_USAGE);
106872
+ return;
106873
+ }
106874
+ const unknown = args.filter((arg) => arg !== "--json");
106875
+ if (unknown.length > 0) {
106876
+ throw new Error(`Unknown capabilities option: ${unknown[0]}`);
106877
+ }
106878
+ const capabilities = getA2ACapabilities();
106879
+ if (args.includes("--json")) {
106880
+ process.stdout.write(`${JSON.stringify(capabilities)}
106881
+ `);
106882
+ return;
106883
+ }
106884
+ process.stdout.write(`${JSON.stringify(capabilities, null, 2)}
106885
+ `);
106886
+ }
106887
+
106716
106888
  // src/log-tail.ts
106717
106889
  var import_node_fs20 = require("node:fs");
106718
106890
  var import_promises10 = require("node:fs/promises");
@@ -108219,7 +108391,7 @@ async function exportDiagnosticLogs(options) {
108219
108391
  node: process.version,
108220
108392
  platform: process.platform,
108221
108393
  arch: process.arch,
108222
- packageVersion: true ? "0.1.11" : "unknown",
108394
+ packageVersion: true ? "0.1.12-beta-61603b42d4-260803155630" : "unknown",
108223
108395
  sensitiveContentIncluded: options.includeSensitiveContent,
108224
108396
  listenerAndLlmContentIncluded: true,
108225
108397
  credentialsAlwaysRedacted: true,
@@ -109944,7 +110116,7 @@ async function runDoctor(options = {}) {
109944
110116
  platform: options.platform ?? process.platform,
109945
110117
  env: options.env ?? process.env,
109946
110118
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
109947
- cliVersion: options.cliVersion ?? (true ? "0.1.11" : "0.0.0"),
110119
+ cliVersion: options.cliVersion ?? (true ? "0.1.12-beta-61603b42d4-260803155630" : "0.0.0"),
109948
110120
  fixMode: options.fix === true,
109949
110121
  nonInteractive: options.nonInteractive === true,
109950
110122
  packageChanged: false,
@@ -110178,6 +110350,8 @@ init_autostart_windows();
110178
110350
  HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS,
110179
110351
  InboundReplayGate,
110180
110352
  InvalidXmtpMessageStore,
110353
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
110354
+ MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
110181
110355
  NATIVE_LAUNCHER_EXE_NAME,
110182
110356
  OPENCLAW_GATEWAY_ROUTE_GROUP_ID,
110183
110357
  SYSTEM_NOTIFICATION_SESSION_KEY,
@@ -110195,6 +110369,7 @@ init_autostart_windows();
110195
110369
  XMTP_CLIENT_RECYCLE_INTERVAL_ENV,
110196
110370
  XmtpDm,
110197
110371
  XmtpGroup,
110372
+ XmtpService,
110198
110373
  acquireDaemonLock,
110199
110374
  activeDaemonPointerPath,
110200
110375
  aiRunSentryExtra,
@@ -110215,6 +110390,7 @@ init_autostart_windows();
110215
110390
  buildInboundTerminalExtras,
110216
110391
  buildLaunchdPlist,
110217
110392
  buildLauncherEntryJs,
110393
+ buildMessageEligibleArgs,
110218
110394
  buildPendingConversationBackupDispatch,
110219
110395
  buildSchtasksCreateArgs,
110220
110396
  buildSchtasksDeleteArgs,
@@ -110246,6 +110422,7 @@ init_autostart_windows();
110246
110422
  detectAiProviders,
110247
110423
  detectCurrentAiProvider,
110248
110424
  detectGatewayInvocation,
110425
+ detectMessageEligibleOfflineReplaySupport,
110249
110426
  detectRuntime,
110250
110427
  ensureDaemonReady,
110251
110428
  ensureDefaultAiProvider,
@@ -110259,9 +110436,11 @@ init_autostart_windows();
110259
110436
  findWindowsNativeOkxA2aExe,
110260
110437
  followFile,
110261
110438
  formatDoctorReportForHumans,
110439
+ getA2ACapabilities,
110262
110440
  getDaemonStatus,
110263
110441
  getHermesGatewayPluginStatus,
110264
110442
  getSessionBusyTracker,
110443
+ handleCapabilitiesCommand,
110265
110444
  handleDoctorCommand,
110266
110445
  handleXmtpSendCommand,
110267
110446
  hasAiRuntimeMarker,
@@ -110273,6 +110452,7 @@ init_autostart_windows();
110273
110452
  isDefaultTaskHome,
110274
110453
  isGatewayAvailableForHeartbeat,
110275
110454
  isHermesGatewayPluginEnabled,
110455
+ isOfflineReplayForTrigger,
110276
110456
  isOpenClawA2aPluginAvailable,
110277
110457
  isOpenClawGatewayAvailable,
110278
110458
  isOriginalParentProcessAlive,
@@ -110283,6 +110463,8 @@ init_autostart_windows();
110283
110463
  killProcessTree,
110284
110464
  launchdPlistHasCurrentRestartPolicy,
110285
110465
  logWinCompat,
110466
+ maybeRecommendOnchainosUpgrade,
110467
+ messageEligibleHelpAdvertisesOfflineReplay,
110286
110468
  nodeVersionSatisfies,
110287
110469
  normalizeAiProvider,
110288
110470
  normalizeHermesOkxA2aPluginConfig,
@@ -110307,6 +110489,7 @@ init_autostart_windows();
110307
110489
  removeUserAttentionWatcher,
110308
110490
  reportDoctorRunToSentry,
110309
110491
  reportUserNotificationFailure,
110492
+ resetMessageEligibleOfflineReplayCapabilityForTests,
110310
110493
  resetResolvedOnchainosBinForTests,
110311
110494
  resolveAiPermissionPreset,
110312
110495
  resolveAiProviderCommand,
@@ -110318,6 +110501,7 @@ init_autostart_windows();
110318
110501
  resolveDoctorTarget,
110319
110502
  resolveEnvelopePayload,
110320
110503
  resolveNpmCmdShimScript,
110504
+ resolveOfflineReplayCapabilityMarker,
110321
110505
  resolveOfflineReplayIntervalSec,
110322
110506
  resolveOpenClawGatewayConfig,
110323
110507
  resolveOpenClawGatewayRoute,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okxweb3/a2a-node",
3
- "version": "0.1.11",
3
+ "version": "0.1.12-beta-61603b42d4-260803155630",
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": {