@okxweb3/a2a-node 0.2.3 → 0.2.4-beta-e7a52faf6f-260812104443

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 +384 -109
  2. package/dist/index.js +370 -98
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -32104,7 +32104,7 @@ var init_sentry_config = __esm({
32104
32104
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
32105
32105
  SENTRY_CONFIG = {
32106
32106
  projectName: "okx/openclaw-okx-a2a-extension",
32107
- release: "0.2.3",
32107
+ release: "0.2.4-beta-e7a52faf6f-260812104443",
32108
32108
  environment,
32109
32109
  runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
32110
32110
  };
@@ -33045,7 +33045,7 @@ async function getCurrentNodeCliVersion() {
33045
33045
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
33046
33046
  }
33047
33047
  function getBundledNodeCliVersion() {
33048
- return true ? "0.2.3" : null;
33048
+ return true ? "0.2.4-beta-e7a52faf6f-260812104443" : null;
33049
33049
  }
33050
33050
  function readConfiguredAiProvider() {
33051
33051
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -33255,7 +33255,7 @@ async function updateHermes(release, options) {
33255
33255
  }
33256
33256
  }
33257
33257
  async function installGatewayPluginForDoctor(target) {
33258
- const release = isPrereleaseVersion("0.2.3") ? "beta" : "latest";
33258
+ const release = isPrereleaseVersion("0.2.4-beta-e7a52faf6f-260812104443") ? "beta" : "latest";
33259
33259
  const insideTargetGateway = detectGatewayInvocation() === target;
33260
33260
  const options = {
33261
33261
  restart: !insideTargetGateway,
@@ -43946,6 +43946,8 @@ var index_exports = {};
43946
43946
  __export(index_exports, {
43947
43947
  AI_PROVIDERS: () => AI_PROVIDERS,
43948
43948
  AgentMessageDirection: () => AgentMessageDirection,
43949
+ AgentRefreshAuthGate: () => AgentRefreshAuthGate,
43950
+ AgentRefreshCoordinator: () => AgentRefreshCoordinator,
43949
43951
  AiRunner: () => AiRunner,
43950
43952
  BACKUP_JOB_ID: () => BACKUP_JOB_ID,
43951
43953
  CODEX_APP_DIRS_ENV: () => CODEX_APP_DIRS_ENV,
@@ -43965,6 +43967,8 @@ __export(index_exports, {
43965
43967
  InboundReplayGate: () => InboundReplayGate,
43966
43968
  InvalidXmtpMessageStore: () => InvalidXmtpMessageStore,
43967
43969
  JOB_PROVIDER_BINDING_SOURCES: () => JOB_PROVIDER_BINDING_SOURCES,
43970
+ ListenerDeferredTaskScheduler: () => ListenerDeferredTaskScheduler,
43971
+ ListenerMaintenanceGate: () => ListenerMaintenanceGate,
43968
43972
  LogEvent: () => LogEvent,
43969
43973
  MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
43970
43974
  MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN: () => MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
@@ -44109,6 +44113,7 @@ __export(index_exports, {
44109
44113
  notifyUserAttentionChanged: () => notifyUserAttentionChanged,
44110
44114
  parseLogsExportArgs: () => parseLogsExportArgs,
44111
44115
  parsePluginYamlVersion: () => parsePluginYamlVersion,
44116
+ parseWalletLoginStatus: () => parseWalletLoginStatus,
44112
44117
  parseWindowsParentProcessJson: () => parseWindowsParentProcessJson,
44113
44118
  performRuntimeSwitch: () => performRuntimeSwitch,
44114
44119
  persistCurrentRuntimeSelectionForNewJob: () => persistCurrentRuntimeSelectionForNewJob,
@@ -44161,6 +44166,7 @@ __export(index_exports, {
44161
44166
  resolveXmtpClientRecycleIntervalSec: () => resolveXmtpClientRecycleIntervalSec,
44162
44167
  runAiAdapter: () => runAiAdapter,
44163
44168
  runDoctor: () => runDoctor,
44169
+ runHeartbeatRefreshCycle: () => runHeartbeatRefreshCycle,
44164
44170
  runListener: () => runListener,
44165
44171
  scanUserAttentionWatchers: () => scanUserAttentionWatchers,
44166
44172
  setOpenClawWebSocketFactoryForTests: () => setOpenClawWebSocketFactoryForTests,
@@ -55865,6 +55871,63 @@ function compareTimestamp(a, b) {
55865
55871
  return 0;
55866
55872
  }
55867
55873
 
55874
+ // ../core/src/xmtp-sdk/onchainos/session-expired.ts
55875
+ init_sentry_logger();
55876
+ var SESSION_EXPIRED_RE = /session expired/i;
55877
+ var AUTH_FAILURE_PATTERNS = [
55878
+ ["session_expired", /session expired/i],
55879
+ ["not_logged_in", /not logged in/i],
55880
+ ["login_required", /login required/i],
55881
+ ["authentication_required", /authentication required/i]
55882
+ ];
55883
+ function isSessionExpired(stdout) {
55884
+ try {
55885
+ const res = JSON.parse(stdout);
55886
+ return res.ok === false && SESSION_EXPIRED_RE.test(String(res.error ?? ""));
55887
+ } catch {
55888
+ return SESSION_EXPIRED_RE.test(stdout);
55889
+ }
55890
+ }
55891
+ function classifyOnchainosAuthError(err) {
55892
+ if (!err) {
55893
+ return null;
55894
+ }
55895
+ const values = [String(err)];
55896
+ if (typeof err === "object") {
55897
+ const anyErr = err;
55898
+ values.push(anyErr.stdout, anyErr.stderr, anyErr.message);
55899
+ }
55900
+ for (const value of values) {
55901
+ if (typeof value !== "string") {
55902
+ continue;
55903
+ }
55904
+ for (const [reason, pattern] of AUTH_FAILURE_PATTERNS) {
55905
+ if (pattern.test(value)) {
55906
+ return reason;
55907
+ }
55908
+ }
55909
+ }
55910
+ return null;
55911
+ }
55912
+ function isOnchainosAuthError(err) {
55913
+ return classifyOnchainosAuthError(err) !== null;
55914
+ }
55915
+ function reportSessionExpired(command) {
55916
+ logger.error(
55917
+ LogEvent.ONCHAINOS_SESSION_EXPIRED,
55918
+ new Error("onchainos session expired"),
55919
+ {
55920
+ component: "onchainos_cli",
55921
+ source: "onchainos",
55922
+ stage: "session_expired",
55923
+ operation: command,
55924
+ command,
55925
+ communicationClass: "onchainos_auth_issue",
55926
+ reason: "session_expired"
55927
+ }
55928
+ );
55929
+ }
55930
+
55868
55931
  // ../core/src/xmtp-sdk/index.ts
55869
55932
  var DEFAULT_DATA_DIR = resolveA2aTaskPaths().xmtpDir;
55870
55933
  var XMTP_INSTALLATION_WARNING_THRESHOLD = 2;
@@ -55898,22 +55961,6 @@ function ensureCacheDir(dataDir) {
55898
55961
  ensureA2aTaskDir(dataDir);
55899
55962
  }
55900
55963
  var SENSITIVE_WORDS_LAZY_RETRY_COOLDOWN_MS = 10 * 60 * 1e3;
55901
- var SESSION_EXPIRED_RE = /session expired/i;
55902
- function isSessionExpiredError(err) {
55903
- if (!err) {
55904
- return false;
55905
- }
55906
- if (typeof err === "object") {
55907
- const anyErr = err;
55908
- if (typeof anyErr.stdout === "string" && SESSION_EXPIRED_RE.test(anyErr.stdout)) {
55909
- return true;
55910
- }
55911
- if (typeof anyErr.message === "string" && SESSION_EXPIRED_RE.test(anyErr.message)) {
55912
- return true;
55913
- }
55914
- }
55915
- return SESSION_EXPIRED_RE.test(String(err));
55916
- }
55917
55964
  function loadSensitiveWordsFromCache(dataDir) {
55918
55965
  try {
55919
55966
  const path2 = cachePath(dataDir, "sensitive-words.json");
@@ -56832,12 +56879,14 @@ var XmtpService = class _XmtpService {
56832
56879
  newAgents = agentList.agents;
56833
56880
  changed = agentList.changed;
56834
56881
  } catch (err) {
56835
- if (isSessionExpiredError(err)) {
56882
+ const authFailureReason = classifyOnchainosAuthError(err);
56883
+ if (authFailureReason) {
56836
56884
  logWithTimestamp(
56837
- "[xmtp-sdk] refresh: onchainos session expired, taking all local clients offline"
56885
+ `[xmtp-sdk] refresh: onchainos auth unavailable (${authFailureReason}), taking all local clients offline`
56838
56886
  );
56839
56887
  logger.info(LogEvent.AGENT_REFRESH_SESSION_EXPIRED, {
56840
56888
  onchainosAgentId: "*",
56889
+ authFailureReason,
56841
56890
  previousClientCount: String(this.clients.size),
56842
56891
  previousAgentCount: String(this.allAgents.length),
56843
56892
  previousAgentIds: this.allAgents.map((a) => a.agentId).join(","),
@@ -58744,33 +58793,6 @@ function parseCliJson(stdout, command, extras = {}) {
58744
58793
  }
58745
58794
  }
58746
58795
 
58747
- // ../core/src/xmtp-sdk/onchainos/session-expired.ts
58748
- init_sentry_logger();
58749
- var SESSION_EXPIRED_RE2 = /session expired/i;
58750
- function isSessionExpired(stdout) {
58751
- try {
58752
- const res = JSON.parse(stdout);
58753
- return res.ok === false && SESSION_EXPIRED_RE2.test(String(res.error ?? ""));
58754
- } catch {
58755
- return SESSION_EXPIRED_RE2.test(stdout);
58756
- }
58757
- }
58758
- function reportSessionExpired(command) {
58759
- logger.error(
58760
- LogEvent.ONCHAINOS_SESSION_EXPIRED,
58761
- new Error("onchainos session expired"),
58762
- {
58763
- component: "onchainos_cli",
58764
- source: "onchainos",
58765
- stage: "session_expired",
58766
- operation: command,
58767
- command,
58768
- communicationClass: "onchainos_auth_issue",
58769
- reason: "session_expired"
58770
- }
58771
- );
58772
- }
58773
-
58774
58796
  // ../core/src/xmtp-sdk/onchainos/offline-replay-capability.ts
58775
58797
  init_log();
58776
58798
  var MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG = "--is-offline-replay";
@@ -58926,6 +58948,42 @@ async function fetchSystemConfig(options) {
58926
58948
  return res.data;
58927
58949
  }
58928
58950
 
58951
+ // ../core/src/xmtp-sdk/onchainos/wallet-status.ts
58952
+ init_log();
58953
+ init_sentry_logger();
58954
+ function parseWalletLoginStatus(stdout) {
58955
+ const response = parseCliJson(stdout, "wallet-status");
58956
+ if (!response.ok) {
58957
+ const error = new Error("onchainos wallet status failed");
58958
+ logger.error(
58959
+ LogEvent.ONCHAINOS_CLI_ERROR,
58960
+ error,
58961
+ cliOkFalseExtras(stdout, "wallet-status")
58962
+ );
58963
+ throw error;
58964
+ }
58965
+ if (typeof response.data?.loggedIn !== "boolean") {
58966
+ const error = new Error("onchainos wallet status returned invalid loggedIn state");
58967
+ logger.error(LogEvent.ONCHAINOS_CLI_ERROR, error, {
58968
+ component: "onchainos_cli",
58969
+ source: "onchainos",
58970
+ stage: "response_validation",
58971
+ communicationClass: "onchainos_cli_or_api_issue",
58972
+ operation: "wallet-status",
58973
+ reason: "invalid_logged_in_state"
58974
+ });
58975
+ throw error;
58976
+ }
58977
+ return response.data.loggedIn;
58978
+ }
58979
+ async function queryWalletLoginStatus() {
58980
+ logWithTimestamp("[onchainos] checking wallet login status");
58981
+ const { stdout } = await exec(["wallet", "status"]);
58982
+ const loggedIn = parseWalletLoginStatus(stdout);
58983
+ logWithTimestamp(`[onchainos] wallet login status: ${loggedIn ? "logged in" : "logged out"}`);
58984
+ return loggedIn;
58985
+ }
58986
+
58929
58987
  // ../core/src/xmtp-sdk/onchainos/index.ts
58930
58988
  var MESSAGE_ELIGIBILITY_SERVICE_UNAVAILABLE_AI_HINT = "Message eligibility service is temporarily unavailable. Please try again later.";
58931
58989
  function messageEligibleUnavailableError(input) {
@@ -59056,7 +59114,10 @@ async function fetchAgentPage(page) {
59056
59114
  );
59057
59115
  if (!res.ok) {
59058
59116
  guardSessionExpired2(stdout, "list-agents");
59059
- const parseErr = new Error("onchainos agent get failed");
59117
+ const detail = typeof res.error === "string" ? res.error.trim() : "";
59118
+ const parseErr = new Error(
59119
+ detail ? `onchainos agent get failed: ${detail}` : "onchainos agent get failed"
59120
+ );
59060
59121
  logger.error(
59061
59122
  LogEvent.ONCHAINOS_CLI_ERROR,
59062
59123
  parseErr,
@@ -60181,7 +60242,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
60181
60242
  client: {
60182
60243
  id: "gateway-client",
60183
60244
  displayName: "okx-a2a-node",
60184
- version: "0.2.3",
60245
+ version: "0.2.4-beta-e7a52faf6f-260812104443",
60185
60246
  platform: "node",
60186
60247
  mode: "backend",
60187
60248
  instanceId
@@ -60192,7 +60253,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
60192
60253
  commands: [],
60193
60254
  permissions: {},
60194
60255
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
60195
- userAgent: `okx-a2a-node/${"0.2.3"}`,
60256
+ userAgent: `okx-a2a-node/${"0.2.4-beta-e7a52faf6f-260812104443"}`,
60196
60257
  auth: {
60197
60258
  ...config.token ? { token: config.token } : {},
60198
60259
  ...config.password ? { password: config.password } : {}
@@ -63621,7 +63682,7 @@ async function handleDaemonCommand(command, params) {
63621
63682
  const payload = await handleXmtpDebugCommand(command, params.service);
63622
63683
  return { ok: true, payload };
63623
63684
  }
63624
- const refresh = await params.service.refreshAgents();
63685
+ const refresh = await (params.refreshAgents?.() ?? params.service.refreshAgents());
63625
63686
  return {
63626
63687
  ok: true,
63627
63688
  payload: {
@@ -65974,6 +66035,144 @@ var DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC = 300;
65974
66035
  var DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC = 6 * 60 * 60;
65975
66036
  var XMTP_CLIENT_RECYCLE_INTERVAL_ENV = "OKX_A2A_XMTP_CLIENT_RECYCLE_INTERVAL_SEC";
65976
66037
  var HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS = 2e3;
66038
+ var AUTH_RECOVERY_PROBE_INTERVAL_MS = 6e4;
66039
+ async function runHeartbeatRefreshCycle(options) {
66040
+ const heartbeat = await timeSettled(options.heartbeat);
66041
+ const refreshAllowed = heartbeat.value === "performed" || heartbeat.value === "refresh_safe";
66042
+ const refresh = options.refresh && refreshAllowed ? await timeSettled(options.refresh) : void 0;
66043
+ return { heartbeat, refresh };
66044
+ }
66045
+ var AgentRefreshAuthGate = class {
66046
+ authBlocked = false;
66047
+ get blocked() {
66048
+ return this.authBlocked;
66049
+ }
66050
+ recordSuccess() {
66051
+ this.authBlocked = false;
66052
+ }
66053
+ recordFailure(err) {
66054
+ if (!isOnchainosAuthError(err)) {
66055
+ return false;
66056
+ }
66057
+ this.authBlocked = true;
66058
+ return true;
66059
+ }
66060
+ };
66061
+ var AgentRefreshCoordinator = class {
66062
+ constructor(options) {
66063
+ this.options = options;
66064
+ }
66065
+ options;
66066
+ authGate = new AgentRefreshAuthGate();
66067
+ refreshInFlight = null;
66068
+ recoveryProbeInFlight = null;
66069
+ get blocked() {
66070
+ return this.authGate.blocked;
66071
+ }
66072
+ async lookup(previousFingerprint) {
66073
+ const wasBlocked = this.authGate.blocked;
66074
+ try {
66075
+ const result = await this.options.lookup(previousFingerprint);
66076
+ this.authGate.recordSuccess();
66077
+ if (wasBlocked) {
66078
+ this.options.onAuthRecovered?.();
66079
+ }
66080
+ return result;
66081
+ } catch (err) {
66082
+ if (this.authGate.recordFailure(err) && !wasBlocked) {
66083
+ this.options.onAuthBlocked?.();
66084
+ }
66085
+ throw err;
66086
+ }
66087
+ }
66088
+ async runScheduled() {
66089
+ if (this.options.isStopping() || this.authGate.blocked) {
66090
+ return null;
66091
+ }
66092
+ return this.runRefresh();
66093
+ }
66094
+ async runManual() {
66095
+ return this.runRefresh();
66096
+ }
66097
+ runRecoveryProbe() {
66098
+ if (this.options.isStopping() || !this.authGate.blocked || !this.options.checkLoggedIn) {
66099
+ return Promise.resolve(null);
66100
+ }
66101
+ if (this.recoveryProbeInFlight) {
66102
+ return this.recoveryProbeInFlight;
66103
+ }
66104
+ this.recoveryProbeInFlight = (async () => {
66105
+ const loggedIn = await this.options.checkLoggedIn?.();
66106
+ if (this.options.isStopping() || !this.authGate.blocked || !loggedIn) {
66107
+ return null;
66108
+ }
66109
+ const refresh = await this.runRefresh();
66110
+ return this.authGate.blocked ? null : refresh;
66111
+ })().finally(() => {
66112
+ this.recoveryProbeInFlight = null;
66113
+ });
66114
+ return this.recoveryProbeInFlight;
66115
+ }
66116
+ runRefresh() {
66117
+ if (this.refreshInFlight) {
66118
+ return this.refreshInFlight;
66119
+ }
66120
+ this.refreshInFlight = this.options.refresh().finally(() => {
66121
+ this.refreshInFlight = null;
66122
+ });
66123
+ return this.refreshInFlight;
66124
+ }
66125
+ };
66126
+ var ListenerDeferredTaskScheduler = class {
66127
+ timers = /* @__PURE__ */ new Set();
66128
+ schedule(task) {
66129
+ const timer = setTimeout(() => {
66130
+ this.timers.delete(timer);
66131
+ task();
66132
+ }, 0);
66133
+ this.timers.add(timer);
66134
+ }
66135
+ cancelAll() {
66136
+ for (const timer of this.timers) {
66137
+ clearTimeout(timer);
66138
+ }
66139
+ this.timers.clear();
66140
+ }
66141
+ };
66142
+ var ListenerMaintenanceGate = class {
66143
+ inFlight = null;
66144
+ offlineReplayPending = false;
66145
+ xmtpRecyclePending = false;
66146
+ tryEnter(task) {
66147
+ if (task !== "xmtp_recycle" && this.xmtpRecyclePending || this.inFlight) {
66148
+ if (task === "offline_replay") {
66149
+ this.offlineReplayPending = true;
66150
+ } else if (task === "xmtp_recycle") {
66151
+ this.xmtpRecyclePending = true;
66152
+ }
66153
+ return false;
66154
+ }
66155
+ if (task === "xmtp_recycle") {
66156
+ this.xmtpRecyclePending = false;
66157
+ }
66158
+ this.inFlight = task;
66159
+ return true;
66160
+ }
66161
+ leave(task) {
66162
+ if (this.inFlight !== task) {
66163
+ return null;
66164
+ }
66165
+ this.inFlight = null;
66166
+ if (this.xmtpRecyclePending) {
66167
+ return "xmtp_recycle";
66168
+ }
66169
+ if (this.offlineReplayPending) {
66170
+ this.offlineReplayPending = false;
66171
+ return "offline_replay";
66172
+ }
66173
+ return null;
66174
+ }
66175
+ };
65977
66176
  var UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY = "system:onchainos-offline-replay-upgrade";
65978
66177
  function resolveOfflineReplayIntervalSec(systemConfig) {
65979
66178
  const interval = systemConfig.offlineReplayInterval;
@@ -66173,10 +66372,25 @@ async function runListenerWithLock(options, paths) {
66173
66372
  }
66174
66373
  });
66175
66374
  logWithTimestamp("[okx-agent-task] user_attention watcher coordinator scanning SQLite watcher state");
66375
+ let stopping = false;
66176
66376
  const service = XmtpService.getInstance();
66377
+ const agentRefreshCoordinator = new AgentRefreshCoordinator({
66378
+ lookup: (previousFingerprint) => listAllAgentsWithMetadata(previousFingerprint),
66379
+ refresh: () => service.refreshAgents(),
66380
+ checkLoggedIn: () => queryWalletLoginStatus(),
66381
+ isStopping: () => stopping,
66382
+ onAuthBlocked: () => {
66383
+ logWithTimestamp(
66384
+ "[okx-agent-task] agent refresh auth blocked; periodic agent get paused. Wallet login recovery will be checked every 60s; run `okx-a2a agent refresh` to recover immediately"
66385
+ );
66386
+ },
66387
+ onAuthRecovered: () => {
66388
+ logWithTimestamp("[okx-agent-task] agent refresh auth recovered; periodic refresh resumed");
66389
+ }
66390
+ });
66177
66391
  service.setDataDir(paths.xmtpDir);
66178
66392
  service.setTools({
66179
- listAllAgentsWithMetadata: async (previousFingerprint) => listAllAgentsWithMetadata(previousFingerprint),
66393
+ listAllAgentsWithMetadata: (previousFingerprint) => agentRefreshCoordinator.lookup(previousFingerprint),
66180
66394
  fetchSensitiveWords: async () => fetchSensitiveWords(),
66181
66395
  fetchSystemConfig: async () => fetchSystemConfig({
66182
66396
  notifySessionExpired: (command) => service.tools.notifySessionExpired?.(command)
@@ -66203,7 +66417,7 @@ async function runListenerWithLock(options, paths) {
66203
66417
  }
66204
66418
  }),
66205
66419
  notifySessionExpired: (command) => {
66206
- const rawText = `The onchainos login session has expired while running ${command}. Run \`onchainos wallet login\` and restart \`okx-a2a\`.`;
66420
+ const rawText = `The onchainos login session has expired while running ${command}. Run \`onchainos wallet login\`; A2A will recover within 60 seconds, or run \`okx-a2a agent refresh\` to recover immediately.`;
66207
66421
  void store.appendBackup(buildSystemStoredMessage({
66208
66422
  reason: "onchainos-session-expired",
66209
66423
  rawText,
@@ -66231,12 +66445,12 @@ async function runListenerWithLock(options, paths) {
66231
66445
  });
66232
66446
  }
66233
66447
  });
66234
- service.setPluginVersion("0.2.3");
66448
+ service.setPluginVersion("0.2.4-beta-e7a52faf6f-260812104443");
66235
66449
  await service.init();
66236
66450
  const pluginVersionStatus = service.pluginVersionStatus;
66237
66451
  if (pluginVersionStatus.unavailable) {
66238
66452
  throw new Error(
66239
- `@okxweb3/a2a-node v${"0.2.3"} is below the required minimum v${pluginVersionStatus.minVersion}`
66453
+ `@okxweb3/a2a-node v${"0.2.4-beta-e7a52faf6f-260812104443"} is below the required minimum v${pluginVersionStatus.minVersion}`
66240
66454
  );
66241
66455
  }
66242
66456
  const systemConfig = service.getSystemConfig();
@@ -66254,7 +66468,7 @@ async function runListenerWithLock(options, paths) {
66254
66468
  onchainosAgentId: "*",
66255
66469
  reason: "system-config missing sentryDsn",
66256
66470
  pluginId: "@okxweb3/a2a-node",
66257
- pluginVersion: "0.2.3"
66471
+ pluginVersion: "0.2.4-beta-e7a52faf6f-260812104443"
66258
66472
  });
66259
66473
  }
66260
66474
  logWithTimestamp(
@@ -66266,37 +66480,56 @@ async function runListenerWithLock(options, paths) {
66266
66480
  logWithTimestamp(
66267
66481
  `[okx-agent-task] startup replay complete clients=${startupReplaySummary.clients} replayed=${startupReplaySummary.replayed} skipped=${startupReplaySummary.skipped} duration=${startupReplaySummary.durationMs}ms`
66268
66482
  );
66269
- let maintenanceInFlight = null;
66270
66483
  let lastRecycleReplayAtMs = 0;
66271
- let xmtpRecyclePending = false;
66272
- const tryEnterMaintenanceTask = (task) => {
66273
- if (task !== "xmtp_recycle" && xmtpRecyclePending) {
66274
- return false;
66275
- }
66276
- if (maintenanceInFlight) {
66277
- return false;
66484
+ const maintenanceGate = new ListenerMaintenanceGate();
66485
+ const deferredMaintenanceTasks = new ListenerDeferredTaskScheduler();
66486
+ const leaveMaintenanceTask = (task) => {
66487
+ const pendingTask = maintenanceGate.leave(task);
66488
+ if (stopping) {
66489
+ return;
66278
66490
  }
66279
- if (task === "xmtp_recycle") {
66280
- xmtpRecyclePending = false;
66491
+ if (pendingTask === "xmtp_recycle") {
66492
+ deferredMaintenanceTasks.schedule(() => void xmtpRecycleTick());
66493
+ } else if (pendingTask === "offline_replay") {
66494
+ deferredMaintenanceTasks.schedule(() => void offlineReplayTick());
66281
66495
  }
66282
- maintenanceInFlight = task;
66283
- return true;
66284
66496
  };
66285
- const leaveMaintenanceTask = (task) => {
66286
- if (maintenanceInFlight === task) {
66287
- maintenanceInFlight = null;
66288
- if (xmtpRecyclePending && !stopping) {
66289
- setTimeout(() => void xmtpRecycleTick(), 0);
66497
+ const syncTick = async () => {
66498
+ if (stopping || agentRefreshCoordinator.blocked) {
66499
+ return;
66500
+ }
66501
+ if (!maintenanceGate.tryEnter("sync")) {
66502
+ return;
66503
+ }
66504
+ const tickStartedAt = Date.now();
66505
+ try {
66506
+ const refresh = await agentRefreshCoordinator.runScheduled();
66507
+ if (!refresh) {
66508
+ return;
66290
66509
  }
66510
+ logWithTimestamp(
66511
+ `[okx-agent-task] sync tick agents=${refresh.agentCount} changed=${refresh.changed ? "yes" : "no"} added=${refresh.added.length} removed=${refresh.removed.length}`
66512
+ );
66513
+ } catch (err) {
66514
+ logger.error(LogEvent.DAEMON_TICK_FAILED, err instanceof Error ? err : new Error(String(err)), {
66515
+ component: "node_listener",
66516
+ stage: "sync_tick",
66517
+ durationMs: String(Date.now() - tickStartedAt),
66518
+ reason: "sync_tick_exception"
66519
+ });
66520
+ logWithTimestamp(`[okx-agent-task] sync tick failed total=${Date.now() - tickStartedAt}ms:`, err);
66521
+ } finally {
66522
+ leaveMaintenanceTask("sync");
66291
66523
  }
66292
66524
  };
66293
- const syncTick = async () => {
66294
- if (!tryEnterMaintenanceTask("sync")) {
66295
- return;
66525
+ let heartbeatInFlight = false;
66526
+ const heartbeatTick = async () => {
66527
+ if (heartbeatInFlight || stopping) {
66528
+ return "blocked";
66296
66529
  }
66530
+ heartbeatInFlight = true;
66297
66531
  const tickStartedAt = Date.now();
66298
66532
  try {
66299
- const refresh = await service.refreshAgents();
66300
66533
  const hasActiveAgents = service.getClients().size > 0;
66301
66534
  let shouldHeartbeat = hasActiveAgents;
66302
66535
  const heartbeatProvider = resolveConfiguredAiProvider({ store: sessionStore }) ?? detectGatewayInvocation();
@@ -66311,7 +66544,7 @@ async function runListenerWithLock(options, paths) {
66311
66544
  new Error(`heartbeat gateway check failed: provider=hermes reason=${hermesStatus.reason}`),
66312
66545
  {
66313
66546
  component: "node_listener",
66314
- stage: "sync_tick",
66547
+ stage: "heartbeat_tick",
66315
66548
  reason: hermesStatus.reason ?? "unknown",
66316
66549
  provider: "hermes",
66317
66550
  hermesHome: hermesStatus.hermesHome,
@@ -66335,7 +66568,7 @@ async function runListenerWithLock(options, paths) {
66335
66568
  new Error(`heartbeat skipped: provider=${heartbeatProvider ?? "(not set)"} gateway unavailable`),
66336
66569
  {
66337
66570
  component: "node_listener",
66338
- stage: "sync_tick",
66571
+ stage: "heartbeat_tick",
66339
66572
  reason: "gateway_unavailable",
66340
66573
  provider: heartbeatProvider ?? "(not set)",
66341
66574
  chainIndex: ONCHAINOS_CHAIN_INDEX,
@@ -66351,7 +66584,7 @@ async function runListenerWithLock(options, paths) {
66351
66584
  new Error("heartbeat skipped: no active agents"),
66352
66585
  {
66353
66586
  component: "node_listener",
66354
- stage: "sync_tick",
66587
+ stage: "heartbeat_tick",
66355
66588
  reason: "no_active_agents",
66356
66589
  provider: heartbeatProvider ?? "(not set)",
66357
66590
  chainIndex: ONCHAINOS_CHAIN_INDEX
@@ -66364,7 +66597,7 @@ async function runListenerWithLock(options, paths) {
66364
66597
  if (shouldHeartbeat && heartbeatResult.error) {
66365
66598
  logger.error(LogEvent.HEARTBEAT_FAILED, heartbeatResult.error instanceof Error ? heartbeatResult.error : new Error(String(heartbeatResult.error)), {
66366
66599
  component: "node_listener",
66367
- stage: "sync_tick",
66600
+ stage: "heartbeat_tick",
66368
66601
  reason: heartbeatResult.error instanceof Error ? heartbeatResult.error.name : "unknown_error",
66369
66602
  durationMs: String(heartbeatMs),
66370
66603
  chainIndex: ONCHAINOS_CHAIN_INDEX,
@@ -66372,23 +66605,27 @@ async function runListenerWithLock(options, paths) {
66372
66605
  });
66373
66606
  logWithTimestamp(`[okx-agent-task] heartbeat failed duration=${heartbeatMs}ms:`, heartbeatResult.error);
66374
66607
  }
66375
- logWithTimestamp(
66376
- `[okx-agent-task] sync tick agents=${refresh.agentCount} changed=${refresh.changed ? "yes" : "no"} added=${refresh.added.length} removed=${refresh.removed.length}`
66377
- );
66608
+ return shouldHeartbeat ? "performed" : "refresh_safe";
66378
66609
  } catch (err) {
66379
- logger.error(LogEvent.DAEMON_TICK_FAILED, err instanceof Error ? err : new Error(String(err)), {
66610
+ logger.error(LogEvent.HEARTBEAT_FAILED, err instanceof Error ? err : new Error(String(err)), {
66380
66611
  component: "node_listener",
66381
- stage: "sync_tick",
66612
+ stage: "heartbeat_tick",
66382
66613
  durationMs: String(Date.now() - tickStartedAt),
66383
- reason: "sync_tick_exception"
66614
+ reason: "heartbeat_tick_exception",
66615
+ chainIndex: ONCHAINOS_CHAIN_INDEX,
66616
+ communicationClass: "onchainos_or_network"
66384
66617
  });
66385
- logWithTimestamp(`[okx-agent-task] sync tick failed total=${Date.now() - tickStartedAt}ms:`, err);
66618
+ logWithTimestamp(`[okx-agent-task] heartbeat tick failed total=${Date.now() - tickStartedAt}ms:`, err);
66619
+ return "blocked";
66386
66620
  } finally {
66387
- leaveMaintenanceTask("sync");
66621
+ heartbeatInFlight = false;
66388
66622
  }
66389
66623
  };
66390
66624
  const offlineReplayTick = async () => {
66391
- if (!tryEnterMaintenanceTask("offline_replay")) {
66625
+ if (stopping) {
66626
+ return;
66627
+ }
66628
+ if (!maintenanceGate.tryEnter("offline_replay")) {
66392
66629
  return;
66393
66630
  }
66394
66631
  const tickStartedAt = Date.now();
@@ -66430,8 +66667,10 @@ async function runListenerWithLock(options, paths) {
66430
66667
  }
66431
66668
  };
66432
66669
  const xmtpRecycleTick = async () => {
66433
- if (!tryEnterMaintenanceTask("xmtp_recycle")) {
66434
- xmtpRecyclePending = true;
66670
+ if (stopping) {
66671
+ return;
66672
+ }
66673
+ if (!maintenanceGate.tryEnter("xmtp_recycle")) {
66435
66674
  return;
66436
66675
  }
66437
66676
  const tickStartedAt = Date.now();
@@ -66475,7 +66714,6 @@ async function runListenerWithLock(options, paths) {
66475
66714
  let timer;
66476
66715
  let offlineReplayTimer;
66477
66716
  let xmtpClientRecycleTimer;
66478
- let stopping = false;
66479
66717
  const scheduleSyncTimer = (nextIntervalSec) => {
66480
66718
  if (stopping) {
66481
66719
  return;
@@ -66486,7 +66724,10 @@ async function runListenerWithLock(options, paths) {
66486
66724
  intervalSec = Math.max(10, nextIntervalSec);
66487
66725
  logWithTimestamp(`[okx-agent-task] scheduled sync interval ${intervalSec}s`);
66488
66726
  timer = setInterval(() => {
66489
- void syncTick();
66727
+ void runHeartbeatRefreshCycle({
66728
+ heartbeat: heartbeatTick,
66729
+ refresh: syncTick
66730
+ });
66490
66731
  }, intervalSec * 1e3);
66491
66732
  };
66492
66733
  const scheduleOfflineReplayTimer = (nextIntervalSec) => {
@@ -66568,9 +66809,32 @@ async function runListenerWithLock(options, paths) {
66568
66809
  const systemConfigRefreshTimer = setInterval(() => {
66569
66810
  void refreshSystemConfigTick();
66570
66811
  }, 60 * 60 * 1e3);
66571
- void syncTick();
66812
+ const authRecoveryProbeTimer = setInterval(() => {
66813
+ if (stopping || !agentRefreshCoordinator.blocked) {
66814
+ return;
66815
+ }
66816
+ void agentRefreshCoordinator.runRecoveryProbe().then((refresh) => {
66817
+ if (!refresh) {
66818
+ return;
66819
+ }
66820
+ logWithTimestamp(
66821
+ `[okx-agent-task] auth recovery refresh agents=${refresh.agentCount} added=${refresh.added.length} removed=${refresh.removed.length}`
66822
+ );
66823
+ }).catch((err) => {
66824
+ logger.error(LogEvent.DAEMON_TICK_FAILED, err instanceof Error ? err : new Error(String(err)), {
66825
+ component: "node_listener",
66826
+ stage: "auth_recovery_probe",
66827
+ reason: "auth_recovery_probe_failed"
66828
+ });
66829
+ logWithTimestamp("[okx-agent-task] auth recovery probe failed:", err);
66830
+ });
66831
+ }, AUTH_RECOVERY_PROBE_INTERVAL_MS);
66832
+ void runHeartbeatRefreshCycle({
66833
+ heartbeat: heartbeatTick
66834
+ });
66572
66835
  const commandProcessor = startCommandProcessor({
66573
66836
  service,
66837
+ refreshAgents: () => agentRefreshCoordinator.runManual(),
66574
66838
  store,
66575
66839
  sessionStore,
66576
66840
  homeDir: options.homeDir
@@ -66589,6 +66853,7 @@ async function runListenerWithLock(options, paths) {
66589
66853
  resolve11();
66590
66854
  }, SHUTDOWN_FORCE_RESOLVE_MS);
66591
66855
  void (async () => {
66856
+ deferredMaintenanceTasks.cancelAll();
66592
66857
  if (timer) {
66593
66858
  clearInterval(timer);
66594
66859
  }
@@ -66599,6 +66864,7 @@ async function runListenerWithLock(options, paths) {
66599
66864
  clearInterval(xmtpClientRecycleTimer);
66600
66865
  }
66601
66866
  clearInterval(systemConfigRefreshTimer);
66867
+ clearInterval(authRecoveryProbeTimer);
66602
66868
  commandProcessor.stop();
66603
66869
  await watcherCoordinator.stop();
66604
66870
  try {
@@ -68900,7 +69166,7 @@ async function exportDiagnosticLogs(options) {
68900
69166
  node: process.version,
68901
69167
  platform: process.platform,
68902
69168
  arch: process.arch,
68903
- packageVersion: true ? "0.2.3" : "unknown",
69169
+ packageVersion: true ? "0.2.4-beta-e7a52faf6f-260812104443" : "unknown",
68904
69170
  sensitiveContentIncluded: options.includeSensitiveContent,
68905
69171
  listenerAndLlmContentIncluded: true,
68906
69172
  credentialsAlwaysRedacted: true,
@@ -70736,7 +71002,7 @@ async function runDoctor(options = {}) {
70736
71002
  platform: options.platform ?? process.platform,
70737
71003
  env: options.env ?? process.env,
70738
71004
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
70739
- cliVersion: options.cliVersion ?? (true ? "0.2.3" : "0.0.0"),
71005
+ cliVersion: options.cliVersion ?? (true ? "0.2.4-beta-e7a52faf6f-260812104443" : "0.0.0"),
70740
71006
  fixMode: options.fix === true,
70741
71007
  nonInteractive: options.nonInteractive === true,
70742
71008
  packageChanged: false,
@@ -70959,6 +71225,8 @@ init_autostart_windows();
70959
71225
  0 && (module.exports = {
70960
71226
  AI_PROVIDERS,
70961
71227
  AgentMessageDirection,
71228
+ AgentRefreshAuthGate,
71229
+ AgentRefreshCoordinator,
70962
71230
  AiRunner,
70963
71231
  BACKUP_JOB_ID,
70964
71232
  CODEX_APP_DIRS_ENV,
@@ -70978,6 +71246,8 @@ init_autostart_windows();
70978
71246
  InboundReplayGate,
70979
71247
  InvalidXmtpMessageStore,
70980
71248
  JOB_PROVIDER_BINDING_SOURCES,
71249
+ ListenerDeferredTaskScheduler,
71250
+ ListenerMaintenanceGate,
70981
71251
  LogEvent,
70982
71252
  MESSAGE_ELIGIBLE_OFFLINE_REPLAY_FLAG,
70983
71253
  MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN,
@@ -71122,6 +71392,7 @@ init_autostart_windows();
71122
71392
  notifyUserAttentionChanged,
71123
71393
  parseLogsExportArgs,
71124
71394
  parsePluginYamlVersion,
71395
+ parseWalletLoginStatus,
71125
71396
  parseWindowsParentProcessJson,
71126
71397
  performRuntimeSwitch,
71127
71398
  persistCurrentRuntimeSelectionForNewJob,
@@ -71174,6 +71445,7 @@ init_autostart_windows();
71174
71445
  resolveXmtpClientRecycleIntervalSec,
71175
71446
  runAiAdapter,
71176
71447
  runDoctor,
71448
+ runHeartbeatRefreshCycle,
71177
71449
  runListener,
71178
71450
  scanUserAttentionWatchers,
71179
71451
  setOpenClawWebSocketFactoryForTests,