@okxweb3/a2a-node 0.2.18-beta-4dd056b897-261015113839 → 0.2.18

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 +708 -1849
  2. package/dist/index.js +704 -1745
  3. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -3788,10 +3788,6 @@ function isDaemonCommand(value) {
3788
3788
  if (parsed.type === "xmtp-send") {
3789
3789
  return typeof parsed.jobId === "string" && typeof parsed.message === "string";
3790
3790
  }
3791
- if (parsed.type === "xmtp-subscription-push") {
3792
- const command = parsed;
3793
- return typeof command.myAgentId === "string" && typeof command.content === "string" && Array.isArray(command.conversationIds) && command.conversationIds.every((item) => typeof item === "string");
3794
- }
3795
3791
  if (parsed.type === "xmtp-get-pending-list") {
3796
3792
  return true;
3797
3793
  }
@@ -3847,7 +3843,6 @@ var init_command_store = __esm({
3847
3843
  import_node_fs8 = require("node:fs");
3848
3844
  import_node_path11 = require("node:path");
3849
3845
  init_paths();
3850
- init_session_store();
3851
3846
  ({ DatabaseSync: DatabaseSync2 } = loadSqlite2());
3852
3847
  XMTP_DEBUG_RESULT_TTL_MS = 5 * 6e4;
3853
3848
  CommandStore = class {
@@ -3877,17 +3872,6 @@ var init_command_store = __esm({
3877
3872
  createdAt: Date.now()
3878
3873
  };
3879
3874
  }
3880
- createXmtpSubscriptionPushCommand(params) {
3881
- return {
3882
- id: (0, import_node_crypto2.randomUUID)(),
3883
- type: "xmtp-subscription-push",
3884
- myAgentId: params.myAgentId,
3885
- content: params.content,
3886
- conversationIds: params.conversationIds,
3887
- deliveryId: params.deliveryId,
3888
- createdAt: Date.now()
3889
- };
3890
- }
3891
3875
  createXmtpGetPendingListCommand() {
3892
3876
  return {
3893
3877
  id: (0, import_node_crypto2.randomUUID)(),
@@ -3981,65 +3965,6 @@ var init_command_store = __esm({
3981
3965
  );
3982
3966
  return this.commandPath(command.id);
3983
3967
  }
3984
- /**
3985
- * Atomic idempotent submit keyed on a business `idempotencyKey` (DEXMARKET-9035,
3986
- * M2). The FIRST caller for a given key inserts its command and gets
3987
- * `created: true`; every later caller with the SAME key — including after a
3988
- * daemon / CommandStore rebuild — gets the ALREADY-persisted command back with
3989
- * `created: false` and NO new row. Concurrency-safe: correctness rests on the
3990
- * partial UNIQUE index on `idempotency_key`, not a check-then-insert (which
3991
- * races), so a `INSERT ... ON CONFLICT DO NOTHING` collapses concurrent
3992
- * duplicates to exactly one row. Recoverable SQLITE_BUSY/LOCKED errors reuse
3993
- * the repo's bounded exponential backoff. This never changes the plain
3994
- * `submit` path used by `xmtp-send` and every other daemon command.
3995
- */
3996
- async submitIdempotent(command, idempotencyKey) {
3997
- if (!idempotencyKey) {
3998
- throw new Error("submitIdempotent requires a non-empty idempotencyKey");
3999
- }
4000
- return this.withBusyRetry(
4001
- () => this.transaction(() => {
4002
- const now = Date.now();
4003
- const insert = this.db.prepare(`
4004
- INSERT INTO command_queue (
4005
- id, type, status, command_json, result_json,
4006
- created_at_ms, updated_at_ms, processing_started_at_ms, completed_at_ms,
4007
- idempotency_key
4008
- )
4009
- VALUES (?, ?, 'pending', ?, NULL, ?, ?, NULL, NULL, ?)
4010
- ON CONFLICT(idempotency_key) WHERE idempotency_key IS NOT NULL DO NOTHING
4011
- `).run(
4012
- command.id,
4013
- command.type,
4014
- stringifyForStorage(command),
4015
- normalizeCreatedAt(command.createdAt, now),
4016
- now,
4017
- idempotencyKey
4018
- );
4019
- if (Number(insert.changes ?? 0) > 0) {
4020
- return { path: this.commandPath(command.id), commandId: command.id, created: true };
4021
- }
4022
- const existing = this.db.prepare(`
4023
- SELECT id FROM command_queue WHERE idempotency_key = ?
4024
- `).get(idempotencyKey);
4025
- const existingId = existing?.id ?? command.id;
4026
- return { path: this.commandPath(existingId), commandId: existingId, created: false };
4027
- })
4028
- );
4029
- }
4030
- async withBusyRetry(operation) {
4031
- const attempts = 6;
4032
- for (let attempt = 1; ; attempt += 1) {
4033
- try {
4034
- return operation();
4035
- } catch (err2) {
4036
- if (!isSqliteBusyError(err2) || attempt >= attempts) {
4037
- throw err2;
4038
- }
4039
- await new Promise((resolve14) => setTimeout(resolve14, Math.min(50 * 2 ** (attempt - 1), 1e3)));
4040
- }
4041
- }
4042
- }
4043
3968
  async listPending() {
4044
3969
  return (await this.listPendingEntries()).map((entry) => entry.command);
4045
3970
  }
@@ -4105,25 +4030,10 @@ var init_command_store = __esm({
4105
4030
  this.db.exec(`
4106
4031
  CREATE INDEX IF NOT EXISTS idx_command_queue_status_created
4107
4032
  ON command_queue(status, created_at_ms, id)
4108
- `);
4109
- this.ensureColumn("command_queue", "idempotency_key", "TEXT");
4110
- this.db.exec(`
4111
- CREATE UNIQUE INDEX IF NOT EXISTS idx_command_queue_idempotency_key
4112
- ON command_queue(idempotency_key)
4113
- WHERE idempotency_key IS NOT NULL
4114
4033
  `);
4115
4034
  this.purgeExpiredXmtpDebugResults();
4116
4035
  this.importLegacyFileCommands();
4117
4036
  }
4118
- ensureColumn(table, column, definition) {
4119
- this.transaction(() => {
4120
- const rows = this.db.prepare(`PRAGMA table_info(${table})`).all();
4121
- if (rows.some((row) => row.name === column)) {
4122
- return;
4123
- }
4124
- this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
4125
- });
4126
- }
4127
4037
  purgeExpiredXmtpDebugResults() {
4128
4038
  this.db.prepare(`
4129
4039
  DELETE FROM command_queue
@@ -4777,7 +4687,7 @@ var require_is = __commonJS({
4777
4687
  function isPrimitive(wat) {
4778
4688
  return wat === null || typeof wat !== "object" && typeof wat !== "function";
4779
4689
  }
4780
- function isPlainObject6(wat) {
4690
+ function isPlainObject4(wat) {
4781
4691
  return isBuiltin(wat, "Object");
4782
4692
  }
4783
4693
  function isEvent(wat) {
@@ -4793,7 +4703,7 @@ var require_is = __commonJS({
4793
4703
  return Boolean(wat && wat.then && typeof wat.then === "function");
4794
4704
  }
4795
4705
  function isSyntheticEvent(wat) {
4796
- return isPlainObject6(wat) && "nativeEvent" in wat && "preventDefault" in wat && "stopPropagation" in wat;
4706
+ return isPlainObject4(wat) && "nativeEvent" in wat && "preventDefault" in wat && "stopPropagation" in wat;
4797
4707
  }
4798
4708
  function isNaN2(wat) {
4799
4709
  return typeof wat === "number" && wat !== wat;
@@ -4816,7 +4726,7 @@ var require_is = __commonJS({
4816
4726
  exports2.isEvent = isEvent;
4817
4727
  exports2.isInstanceOf = isInstanceOf;
4818
4728
  exports2.isNaN = isNaN2;
4819
- exports2.isPlainObject = isPlainObject6;
4729
+ exports2.isPlainObject = isPlainObject4;
4820
4730
  exports2.isPrimitive = isPrimitive;
4821
4731
  exports2.isRegExp = isRegExp;
4822
4732
  exports2.isString = isString;
@@ -20079,23 +19989,6 @@ var init_events = __esm({
20079
19989
  XMTP_RATE_LIMIT_CREATE_SUPPRESSED: "XMTP rate limit create suppressed",
20080
19990
  XMTP_RATE_LIMIT_PROBE: "XMTP rate limit probe",
20081
19991
  XMTP_RATE_LIMIT_RECOVERED: "XMTP rate limit recovered",
20082
- // ── subscription observability info (DEXMARKET-9096) ───────────
20083
- // All of the following logger.info checkpoints MUST also be listed in
20084
- // SENTRY_INFO_ALLOWLIST (index.ts): info() returns early for an unlisted
20085
- // event, which ships a checkpoint that looks implemented but emits nothing.
20086
- SUBSCRIPTION_PUSH_STARTED: "Subscription push started",
20087
- SUBSCRIPTION_PUSH_TARGETS_RESOLVED: "Subscription push targets resolved",
20088
- SUBSCRIPTION_PUSH_EMPTY_TARGETS: "Subscription push empty targets",
20089
- SUBSCRIPTION_PUSH_SUMMARY: "Subscription push summary",
20090
- SUBSCRIPTION_INBOUND_RECEIVED: "Subscription inbound received",
20091
- SUBSCRIPTION_INBOUND_GATE_DROPPED: "Subscription inbound gate dropped",
20092
- SUBSCRIPTION_JOBS_QUERY_STARTED: "Subscription jobs query started",
20093
- SUBSCRIPTION_JOBS_QUERY_SUCCEEDED: "Subscription jobs query succeeded",
20094
- SUBSCRIPTION_JOBS_EMPTY: "Subscription jobs empty",
20095
- SUBSCRIPTION_INBOUND_OWNERSHIP_ROUTED: "Subscription inbound ownership routed",
20096
- SUBSCRIPTION_JOB_DISPATCHED: "Subscription job dispatched",
20097
- SUBSCRIPTION_JOB_DUPLICATE_IGNORED: "Subscription job duplicate ignored",
20098
- SUBSCRIPTION_FANOUT_SUMMARY: "Subscription fan-out summary",
20099
19992
  // ── error ─────────────────────────────────────────────────────
20100
19993
  XMTP_CONNECTION_FAILED: "XMTP connection failed",
20101
19994
  CONVERSATIONS_STREAM_FAILED: "conversations.stream failed",
@@ -20162,13 +20055,6 @@ var init_events = __esm({
20162
20055
  AGENT_INSTALLATION_LIMIT_NEAR: "Agent installation limit near",
20163
20056
  XMTP_HISTORY_SYNC_REQUEST_FAILED: "XMTP history sync request failed",
20164
20057
  XMTP_DEBUG_SYNC_FAILED: "XMTP debug sync failed",
20165
- // ── subscription observability error (DEXMARKET-9096) ──────────
20166
- // error-level: always emitted (no allowlist gate). Error objects are
20167
- // classified by buildErrorDiagnostics — never attach raw Error.message.
20168
- SUBSCRIPTION_PUSH_TARGET_FAILED: "Subscription push target failed",
20169
- SUBSCRIPTION_INBOUND_AGENTIC_ID_MISSING: "Subscription inbound agenticId missing",
20170
- SUBSCRIPTION_JOBS_FAIL_CLOSED: "Subscription jobs fail closed",
20171
- SUBSCRIPTION_JOB_FAILED: "Subscription job failed",
20172
20058
  // Underlying XMTP SDK client close failed during recycle/removal/shutdown
20173
20059
  // (DEXMARKET-9116). Error-level → NOT added to SENTRY_INFO_ALLOWLIST.
20174
20060
  XMTP_CLIENT_CLOSE_FAILED: "XMTP client close failed"
@@ -20844,13 +20730,6 @@ var init_sentry_logger = __esm({
20844
20730
  ]);
20845
20731
  SENTRY_TAG_KEYS = /* @__PURE__ */ new Set([
20846
20732
  "agentId",
20847
- // Subscription correlation agent ids (DEXMARKET-9096). Bounded-cardinality,
20848
- // same class as agentId/senderAgentId/onchainosAgentId — promoted to indexed
20849
- // tags for Sentry filtering. NOT added to SENTRY_FINGERPRINT_KEYS, so the
20850
- // global issue fingerprint is unchanged. High-cardinality ids
20851
- // (conversationId/messageId/deliveryId) are deliberately NOT here.
20852
- "aspAgentId",
20853
- "userAgentId",
20854
20733
  "agentPlatform",
20855
20734
  "aiProvider",
20856
20735
  "cacheName",
@@ -21018,22 +20897,6 @@ var init_sentry_logger = __esm({
21018
20897
  LogEvent.SESSION_KEY_DERIVED,
21019
20898
  LogEvent.SESSIONS_QUERIED,
21020
20899
  LogEvent.SYNC_TICK_COMPLETED,
21021
- // Subscription observability info checkpoints (DEXMARKET-9096). Each is a
21022
- // logger.info event; omitting any makes info() drop it silently so the whole
21023
- // subscription trace would look implemented and emit nothing in production.
21024
- LogEvent.SUBSCRIPTION_PUSH_STARTED,
21025
- LogEvent.SUBSCRIPTION_PUSH_TARGETS_RESOLVED,
21026
- LogEvent.SUBSCRIPTION_PUSH_EMPTY_TARGETS,
21027
- LogEvent.SUBSCRIPTION_PUSH_SUMMARY,
21028
- LogEvent.SUBSCRIPTION_INBOUND_RECEIVED,
21029
- LogEvent.SUBSCRIPTION_INBOUND_GATE_DROPPED,
21030
- LogEvent.SUBSCRIPTION_JOBS_QUERY_STARTED,
21031
- LogEvent.SUBSCRIPTION_JOBS_QUERY_SUCCEEDED,
21032
- LogEvent.SUBSCRIPTION_JOBS_EMPTY,
21033
- LogEvent.SUBSCRIPTION_INBOUND_OWNERSHIP_ROUTED,
21034
- LogEvent.SUBSCRIPTION_JOB_DISPATCHED,
21035
- LogEvent.SUBSCRIPTION_JOB_DUPLICATE_IGNORED,
21036
- LogEvent.SUBSCRIPTION_FANOUT_SUMMARY,
21037
20900
  // XMTP creation rate-limit cooldown checkpoints (DEXMARKET-9116). info() drops
21038
20901
  // any event not listed here, so these must stay registered.
21039
20902
  LogEvent.XMTP_RATE_LIMIT_COOLDOWN_STARTED,
@@ -25400,7 +25263,7 @@ var require_websocket2 = __commonJS({
25400
25263
  var http2 = require("http");
25401
25264
  var net = require("net");
25402
25265
  var tls = require("tls");
25403
- var { randomBytes: randomBytes4, createHash: createHash7 } = require("crypto");
25266
+ var { randomBytes: randomBytes4, createHash: createHash6 } = require("crypto");
25404
25267
  var { Duplex, Readable } = require("stream");
25405
25268
  var { URL: URL2 } = require("url");
25406
25269
  var PerMessageDeflate = require_permessage_deflate();
@@ -26060,7 +25923,7 @@ var require_websocket2 = __commonJS({
26060
25923
  abortHandshake(websocket, socket, "Invalid Upgrade header");
26061
25924
  return;
26062
25925
  }
26063
- const digest = createHash7("sha1").update(key + GUID).digest("base64");
25926
+ const digest = createHash6("sha1").update(key + GUID).digest("base64");
26064
25927
  if (res.headers["sec-websocket-accept"] !== digest) {
26065
25928
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
26066
25929
  return;
@@ -26427,7 +26290,7 @@ var require_websocket_server = __commonJS({
26427
26290
  var EventEmitter2 = require("events");
26428
26291
  var http2 = require("http");
26429
26292
  var { Duplex } = require("stream");
26430
- var { createHash: createHash7 } = require("crypto");
26293
+ var { createHash: createHash6 } = require("crypto");
26431
26294
  var extension = require_extension();
26432
26295
  var PerMessageDeflate = require_permessage_deflate();
26433
26296
  var subprotocol = require_subprotocol();
@@ -26728,7 +26591,7 @@ var require_websocket_server = __commonJS({
26728
26591
  );
26729
26592
  }
26730
26593
  if (this._state > RUNNING) return abortHandshake(socket, 503);
26731
- const digest = createHash7("sha1").update(key + GUID).digest("base64");
26594
+ const digest = createHash6("sha1").update(key + GUID).digest("base64");
26732
26595
  const headers = [
26733
26596
  "HTTP/1.1 101 Switching Protocols",
26734
26597
  "Upgrade: websocket",
@@ -27155,7 +27018,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
27155
27018
  client: {
27156
27019
  id: "gateway-client",
27157
27020
  displayName: "okx-a2a-node",
27158
- version: "0.2.18-beta-4dd056b897-261015113839",
27021
+ version: "0.2.18",
27159
27022
  platform: "node",
27160
27023
  mode: "backend",
27161
27024
  instanceId
@@ -27166,7 +27029,7 @@ function buildConnectParams(config, instanceId, protocolVersion) {
27166
27029
  commands: [],
27167
27030
  permissions: {},
27168
27031
  locale: Intl.DateTimeFormat().resolvedOptions().locale || "en-US",
27169
- userAgent: `okx-a2a-node/${"0.2.18-beta-4dd056b897-261015113839"}`,
27032
+ userAgent: `okx-a2a-node/${"0.2.18"}`,
27170
27033
  auth: {
27171
27034
  ...config.token ? { token: config.token } : {},
27172
27035
  ...config.password ? { password: config.password } : {}
@@ -29226,7 +29089,7 @@ var init_sentry_config = __esm({
29226
29089
  environment = process.env.SENTRY_ENV === "dev" ? "dev" : "prod";
29227
29090
  SENTRY_CONFIG = {
29228
29091
  projectName: "okx/openclaw-okx-a2a-extension",
29229
- release: "0.2.18-beta-4dd056b897-261015113839",
29092
+ release: "0.2.18",
29230
29093
  environment,
29231
29094
  runtimeContainer: normalizeRuntimeContainer(process.env.OKX_A2A_RUNTIME_CONTAINER)
29232
29095
  };
@@ -40396,7 +40259,7 @@ async function exportDiagnosticLogs(options) {
40396
40259
  node: process.version,
40397
40260
  platform: process.platform,
40398
40261
  arch: process.arch,
40399
- packageVersion: true ? "0.2.18-beta-4dd056b897-261015113839" : "unknown",
40262
+ packageVersion: true ? "0.2.18" : "unknown",
40400
40263
  sensitiveContentIncluded: options.includeSensitiveContent,
40401
40264
  listenerAndLlmContentIncluded: true,
40402
40265
  credentialsAlwaysRedacted: true,
@@ -43087,7 +42950,7 @@ function createAsyncStreamProxy(stream) {
43087
42950
  }
43088
42951
  });
43089
42952
  }
43090
- var import_node_bindings, import_node_bindings2, import_node_crypto9, import_types, import_node_path26, import_node_process, HistorySyncUrls, envMap, reverseEnvMap, envToString, createBackend, InboxReassignError, AccountAlreadyAssociatedError, MissingContentTypeError, SignerUnavailableError, ClientNotInitializedError, StreamFailedError, StreamInvalidRetryAttemptsError, generateInboxId, getInboxIdForIdentifier, isReaction, isReply, isText, isRemoteAttachment, isAttachment, isMultiRemoteAttachment, isTransactionReference, isGroupUpdated, isReadReceipt, isLeaveRequest, isWalletSendCalls, isIntent, isActions, isMarkdown, CodecRegistry, getContentFromDecodedMessageContent, getContentTypeFromDecodedMessageContent, DecodedMessage, AsyncStream, usableProperties, isUsableProperty, wait, DEFAULT_RETRY_DELAY, DEFAULT_RETRY_ATTEMPTS, createStream, Conversation, Dm, Group, Conversations, DebugInformation, Preferences, networkOptionKeys, hasBackend, resolveBackend$1, createClient, resolveBackend, createEphemeralIdentifier, toInboxUpdatesCountMap, Client;
42953
+ var import_node_bindings, import_node_bindings2, import_node_crypto9, import_types, import_node_path26, import_node_process, HistorySyncUrls, envMap, reverseEnvMap, envToString, createBackend, InboxReassignError, AccountAlreadyAssociatedError, MissingContentTypeError, SignerUnavailableError, ClientNotInitializedError, StreamFailedError, StreamInvalidRetryAttemptsError, generateInboxId, getInboxIdForIdentifier, isReaction, isReply, isText, isRemoteAttachment, isAttachment, isMultiRemoteAttachment, isTransactionReference, isGroupUpdated, isReadReceipt, isLeaveRequest, isWalletSendCalls, isIntent, isActions, isMarkdown, CodecRegistry, getContentFromDecodedMessageContent, getContentTypeFromDecodedMessageContent, DecodedMessage, AsyncStream, usableProperties, isUsableProperty, DEFAULT_RETRY_DELAY, DEFAULT_RETRY_ATTEMPTS, createStream, Conversation, Dm, Group, Conversations, DebugInformation, Preferences, networkOptionKeys, hasBackend, resolveBackend$1, createClient, resolveBackend, createEphemeralIdentifier, toInboxUpdatesCountMap, Client;
43091
42954
  var init_dist2 = __esm({
43092
42955
  "../../node_modules/@xmtp/node-sdk/dist/index.js"() {
43093
42956
  import_node_bindings = require("@xmtp/node-bindings");
@@ -43497,7 +43360,6 @@ var init_dist2 = __esm({
43497
43360
  isUsableProperty = (prop) => {
43498
43361
  return usableProperties.includes(prop);
43499
43362
  };
43500
- wait = (ms) => new Promise((resolve14) => setTimeout(resolve14, ms));
43501
43363
  DEFAULT_RETRY_DELAY = 6e4;
43502
43364
  DEFAULT_RETRY_ATTEMPTS = 10;
43503
43365
  createStream = async (streamFunction, streamValueMutator, options) => {
@@ -43506,82 +43368,161 @@ var init_dist2 = __esm({
43506
43368
  throw new StreamInvalidRetryAttemptsError();
43507
43369
  }
43508
43370
  const asyncStream = new AsyncStream();
43371
+ let stopped = false;
43372
+ let current;
43373
+ let recovering = false;
43374
+ let cancelDelay;
43375
+ const isStopped = () => stopped;
43376
+ const isCurrent = (native) => !stopped && current === native;
43377
+ asyncStream.onDone = () => {
43378
+ if (stopped) {
43379
+ return;
43380
+ }
43381
+ stopped = true;
43382
+ cancelDelay?.();
43383
+ const native = current;
43384
+ current = void 0;
43385
+ native?.closer?.end();
43386
+ };
43387
+ const delay = (ms) => new Promise((resolve14) => {
43388
+ const timer = setTimeout(() => {
43389
+ cancelDelay = void 0;
43390
+ resolve14();
43391
+ }, ms);
43392
+ cancelDelay = () => {
43393
+ clearTimeout(timer);
43394
+ cancelDelay = void 0;
43395
+ resolve14();
43396
+ };
43397
+ });
43398
+ const reportError = (error) => {
43399
+ if (!stopped) {
43400
+ onError?.(error);
43401
+ }
43402
+ };
43403
+ const deliver = (value) => {
43404
+ if (stopped || value === void 0) {
43405
+ return;
43406
+ }
43407
+ asyncStream.push(value);
43408
+ onValue?.(value);
43409
+ };
43509
43410
  const streamCallback = (error, value) => {
43411
+ if (stopped) {
43412
+ return;
43413
+ }
43510
43414
  if (error) {
43511
43415
  onError?.(error);
43512
43416
  return;
43513
43417
  }
43514
- if (value !== void 0) {
43515
- try {
43516
- if (streamValueMutator) {
43517
- const mutatedValue = streamValueMutator(value);
43518
- if ((0, import_types.isPromise)(mutatedValue)) {
43519
- void mutatedValue.then((mutatedValue2) => {
43520
- if (mutatedValue2 !== void 0) {
43521
- asyncStream.push(mutatedValue2);
43522
- onValue?.(mutatedValue2);
43523
- }
43524
- }).catch((error2) => {
43525
- onError?.(error2);
43526
- });
43527
- } else {
43528
- if (mutatedValue !== void 0) {
43529
- asyncStream.push(mutatedValue);
43530
- onValue?.(mutatedValue);
43531
- }
43532
- }
43418
+ if (value === void 0) {
43419
+ return;
43420
+ }
43421
+ try {
43422
+ if (streamValueMutator) {
43423
+ const mutatedValue = streamValueMutator(value);
43424
+ if ((0, import_types.isPromise)(mutatedValue)) {
43425
+ void mutatedValue.then(deliver).catch(reportError);
43533
43426
  } else {
43534
- asyncStream.push(value);
43535
- onValue?.(value);
43427
+ deliver(mutatedValue);
43536
43428
  }
43537
- } catch (error2) {
43538
- onError?.(error2);
43429
+ } else {
43430
+ deliver(value);
43539
43431
  }
43432
+ } catch (error2) {
43433
+ reportError(error2);
43540
43434
  }
43541
43435
  };
43542
- const retry2 = async (retries = retryAttempts) => {
43543
- if (retries === 0) {
43544
- void asyncStream.end();
43545
- onError?.(new StreamFailedError(retryAttempts));
43546
- return;
43436
+ const fail = (error) => {
43437
+ void asyncStream.end();
43438
+ onError?.(error);
43439
+ };
43440
+ const open2 = async () => {
43441
+ if (stopped) {
43442
+ return false;
43547
43443
  }
43548
- await wait(retryDelay);
43549
- onRetry?.(retryAttempts - retries + 1, retryAttempts);
43444
+ const native = { ready: false, closed: false };
43445
+ current = native;
43446
+ const closer = await streamFunction(streamCallback, () => {
43447
+ onNativeClose(native);
43448
+ });
43449
+ native.closer = closer;
43450
+ if (!isCurrent(native)) {
43451
+ closer.end();
43452
+ return false;
43453
+ }
43454
+ await closer.waitForReady();
43455
+ if (!isCurrent(native)) {
43456
+ closer.end();
43457
+ return false;
43458
+ }
43459
+ if (native.closed || closer.isClosed()) {
43460
+ closer.end();
43461
+ return false;
43462
+ }
43463
+ native.ready = true;
43464
+ return true;
43465
+ };
43466
+ const restart = async () => {
43550
43467
  try {
43551
- const streamCloser = await streamFunction(streamCallback, () => {
43552
- onFail?.();
43553
- void retry2();
43554
- });
43555
- await streamCloser.waitForReady();
43556
- asyncStream.onDone = () => {
43557
- streamCloser.end();
43558
- };
43559
- onRestart?.();
43560
- } catch (error) {
43561
- onError?.(error);
43562
- void retry2(retries - 1);
43468
+ for (let attempt = 1; attempt <= retryAttempts; attempt++) {
43469
+ await delay(retryDelay);
43470
+ if (isStopped()) {
43471
+ return;
43472
+ }
43473
+ onRetry?.(attempt, retryAttempts);
43474
+ let restarted = false;
43475
+ try {
43476
+ restarted = await open2();
43477
+ } catch (error) {
43478
+ reportError(error);
43479
+ }
43480
+ if (isStopped()) {
43481
+ return;
43482
+ }
43483
+ if (restarted) {
43484
+ try {
43485
+ onRestart?.();
43486
+ } catch (error) {
43487
+ reportError(error);
43488
+ }
43489
+ return;
43490
+ }
43491
+ }
43492
+ fail(new StreamFailedError(retryAttempts));
43493
+ } finally {
43494
+ recovering = false;
43563
43495
  }
43564
43496
  };
43565
- const startRetry = () => {
43566
- if (retryOnFail) {
43567
- void retry2();
43568
- } else {
43569
- void asyncStream.end();
43570
- onError?.(new StreamFailedError(0));
43497
+ const recover = () => {
43498
+ if (stopped || recovering) {
43499
+ return;
43500
+ }
43501
+ if (!retryOnFail) {
43502
+ fail(new StreamFailedError(0));
43503
+ return;
43571
43504
  }
43505
+ recovering = true;
43506
+ void restart();
43572
43507
  };
43508
+ function onNativeClose(native) {
43509
+ if (!isCurrent(native) || native.closed) {
43510
+ return;
43511
+ }
43512
+ native.closed = true;
43513
+ onFail?.();
43514
+ if (native.ready) {
43515
+ recover();
43516
+ }
43517
+ }
43518
+ let started = false;
43573
43519
  try {
43574
- const streamCloser = await streamFunction(streamCallback, () => {
43575
- onFail?.();
43576
- startRetry();
43577
- });
43578
- await streamCloser.waitForReady();
43579
- asyncStream.onDone = () => {
43580
- streamCloser.end();
43581
- };
43520
+ started = await open2();
43582
43521
  } catch (error) {
43583
- onError?.(error);
43584
- startRetry();
43522
+ reportError(error);
43523
+ }
43524
+ if (!started) {
43525
+ recover();
43585
43526
  }
43586
43527
  return createAsyncStreamProxy(asyncStream);
43587
43528
  };
@@ -45751,7 +45692,7 @@ var require_retry_promise = __commonJS({
45751
45692
  return true;
45752
45693
  }
45753
45694
  };
45754
- function wait3(ms) {
45695
+ function wait2(ms) {
45755
45696
  return __awaiter(this, void 0, void 0, function() {
45756
45697
  return __generator(this, function(_a) {
45757
45698
  return [2, new Promise(function(resolve14) {
@@ -45760,7 +45701,7 @@ var require_retry_promise = __commonJS({
45760
45701
  });
45761
45702
  });
45762
45703
  }
45763
- exports2.wait = wait3;
45704
+ exports2.wait = wait2;
45764
45705
  function retry2(f, config) {
45765
45706
  return __awaiter(this, void 0, void 0, function() {
45766
45707
  var effectiveConfig;
@@ -45850,7 +45791,7 @@ var require_retry_promise = __commonJS({
45850
45791
  return [3, 5];
45851
45792
  case 5:
45852
45793
  millisToWait = delay(i + 1, config.delay);
45853
- return [4, wait3(millisToWait > config.maxBackOff ? config.maxBackOff : millisToWait)];
45794
+ return [4, wait2(millisToWait > config.maxBackOff ? config.maxBackOff : millisToWait)];
45854
45795
  case 6:
45855
45796
  _a.sent();
45856
45797
  if (done()) {
@@ -53724,7 +53665,7 @@ var init_withResolvers = __esm({
53724
53665
  });
53725
53666
 
53726
53667
  // ../../node_modules/viem/_esm/utils/promise/createBatchScheduler.js
53727
- function createBatchScheduler({ fn, id, shouldSplitBatch, wait: wait3 = 0, sort }) {
53668
+ function createBatchScheduler({ fn, id, shouldSplitBatch, wait: wait2 = 0, sort }) {
53728
53669
  const exec2 = async () => {
53729
53670
  const scheduler = getScheduler();
53730
53671
  flush2();
@@ -53762,7 +53703,7 @@ function createBatchScheduler({ fn, id, shouldSplitBatch, wait: wait3 = 0, sort
53762
53703
  return promise;
53763
53704
  }
53764
53705
  setScheduler({ args, resolve: resolve14, reject });
53765
- setTimeout(exec2, wait3);
53706
+ setTimeout(exec2, wait2);
53766
53707
  return promise;
53767
53708
  }
53768
53709
  };
@@ -53828,7 +53769,7 @@ var init_observe = __esm({
53828
53769
  });
53829
53770
 
53830
53771
  // ../../node_modules/viem/_esm/utils/wait.js
53831
- async function wait2(time) {
53772
+ async function wait(time) {
53832
53773
  return new Promise((res) => setTimeout(res, time));
53833
53774
  }
53834
53775
  var init_wait = __esm({
@@ -53845,12 +53786,12 @@ function poll(fn, { emitOnBegin, initialWaitTime, interval }) {
53845
53786
  if (emitOnBegin)
53846
53787
  data = await fn({ unpoll: unwatch });
53847
53788
  const initialWait = await initialWaitTime?.(data) ?? interval;
53848
- await wait2(initialWait);
53789
+ await wait(initialWait);
53849
53790
  const poll2 = async () => {
53850
53791
  if (!active)
53851
53792
  return;
53852
53793
  await fn({ unpoll: unwatch });
53853
- await wait2(interval);
53794
+ await wait(interval);
53854
53795
  poll2();
53855
53796
  };
53856
53797
  poll2();
@@ -54214,7 +54155,7 @@ function withRetry(fn, { delay: delay_ = 100, retryCount = 2, shouldRetry: shoul
54214
54155
  const retry2 = async ({ error }) => {
54215
54156
  const delay = typeof delay_ === "function" ? delay_({ count, error }) : delay_;
54216
54157
  if (delay)
54217
- await wait2(delay);
54158
+ await wait(delay);
54218
54159
  attemptRetry({ count: count + 1 });
54219
54160
  };
54220
54161
  try {
@@ -56679,7 +56620,7 @@ var init_transport = __esm({
56679
56620
  function http(url, config = {}) {
56680
56621
  const { batch, fetchFn, fetchOptions, key = "http", methods, name = "HTTP JSON-RPC", onFetchRequest, onFetchResponse, retryDelay, raw } = config;
56681
56622
  return ({ chain, retryCount: retryCount_, timeout: timeout_ }) => {
56682
- const { batchSize = 1e3, wait: wait3 = 0 } = typeof batch === "object" ? batch : {};
56623
+ const { batchSize = 1e3, wait: wait2 = 0 } = typeof batch === "object" ? batch : {};
56683
56624
  const retryCount = config.retryCount ?? retryCount_;
56684
56625
  const timeout = timeout_ ?? config.timeout ?? 1e4;
56685
56626
  const url_ = url || chain?.rpcUrls.default.http[0];
@@ -56700,7 +56641,7 @@ function http(url, config = {}) {
56700
56641
  const body = { method, params };
56701
56642
  const { schedule } = createBatchScheduler({
56702
56643
  id: url_,
56703
- wait: wait3,
56644
+ wait: wait2,
56704
56645
  shouldSplitBatch(requests) {
56705
56646
  return requests.length > batchSize;
56706
56647
  },
@@ -57039,7 +56980,7 @@ var init_dist4 = __esm({
57039
56980
  init_accounts();
57040
56981
  init_chains();
57041
56982
  import_node_crypto10 = require("node:crypto");
57042
- version2 = "2.3.0";
56983
+ version2 = "2.3.1";
57043
56984
  fromSelf = (message, client) => {
57044
56985
  return message.senderInboxId === client.inboxId;
57045
56986
  };
@@ -58987,6 +58928,9 @@ function replayScanBackoffWithJitterMs(consecutiveFailures, randomValue = Math.r
58987
58928
  const jitteredMs = Math.round(baseMs * (0.9 + boundedRandom * 0.2));
58988
58929
  return Math.min(REPLAY_SCAN_MAX_BACKOFF_MS, jitteredMs);
58989
58930
  }
58931
+ function isListenHealthy(outcome) {
58932
+ return outcome === "started" || outcome === "retrying";
58933
+ }
58990
58934
  function isOfflineReplayForTrigger(trigger) {
58991
58935
  return trigger === "startup";
58992
58936
  }
@@ -59014,7 +58958,7 @@ function createOfflineReplayAddressSummary(address) {
59014
58958
  durationMs: 0
59015
58959
  };
59016
58960
  }
59017
- var import_node_fs24, import_node_path28, DEFAULT_DATA_DIR, XMTP_INSTALLATION_WARNING_THRESHOLD, XMTP_INSTALLATION_NEAR_LIMIT_THRESHOLD, REPLAY_SCAN_INITIAL_BACKOFF_MS, REPLAY_SCAN_MAX_BACKOFF_MS, SENSITIVE_WORDS_LAZY_RETRY_COOLDOWN_MS, InboundReplayGate, SYSTEM_CONFIG_DEFAULTS, STREAM_RECOVERY_ALERT_THRESHOLD_MS, STREAM_RECOVERY_STABILITY_WINDOW_MS, REPLAY_GATE_DRAIN_MAX_ATTEMPTS, REPLAY_GATE_DRAIN_RETRY_DELAY_MS, SEMVER_RE, isRecord5, isPositiveNumber, SYSTEM_CONFIG_VALIDATORS, XmtpService;
58961
+ var import_node_fs24, import_node_path28, DEFAULT_DATA_DIR, XMTP_INSTALLATION_WARNING_THRESHOLD, XMTP_INSTALLATION_NEAR_LIMIT_THRESHOLD, REPLAY_SCAN_INITIAL_BACKOFF_MS, REPLAY_SCAN_MAX_BACKOFF_MS, SENSITIVE_WORDS_LAZY_RETRY_COOLDOWN_MS, InboundReplayGate, SYSTEM_CONFIG_DEFAULTS, STREAM_RECOVERY_ALERT_THRESHOLD_MS, STREAM_RECOVERY_STABILITY_WINDOW_MS, REPLAY_GATE_DRAIN_MAX_ATTEMPTS, REPLAY_GATE_DRAIN_RETRY_DELAY_MS, SEMVER_RE, isRecord5, isPositiveNumber, SYSTEM_CONFIG_VALIDATORS, XmtpClientCreateBlockedError, XmtpService;
59018
58962
  var init_xmtp_sdk = __esm({
59019
58963
  "../core/src/xmtp-sdk/index.ts"() {
59020
58964
  "use strict";
@@ -59146,6 +59090,18 @@ var init_xmtp_sdk = __esm({
59146
59090
  minAvailablePluginVersion: (value) => typeof value === "string",
59147
59091
  extConfig: (value) => isRecord5(value) && (value.tips === void 0 || isRecord5(value.tips) && Object.values(value.tips).every((item) => typeof item === "string"))
59148
59092
  };
59093
+ XmtpClientCreateBlockedError = class extends Error {
59094
+ reason;
59095
+ address;
59096
+ agentId;
59097
+ constructor(reason, address, agentId) {
59098
+ super(`xmtp client create blocked (${reason})`);
59099
+ this.name = "XmtpClientCreateBlockedError";
59100
+ this.reason = reason;
59101
+ this.address = address;
59102
+ this.agentId = agentId;
59103
+ }
59104
+ };
59149
59105
  XmtpService = class _XmtpService {
59150
59106
  static instance = null;
59151
59107
  env = process.env.XMTP_ENV ?? "production";
@@ -59183,6 +59139,25 @@ var init_xmtp_sdk = __esm({
59183
59139
  streamRecoverySetTimeout = (callback, delayMs) => setTimeout(callback, delayMs);
59184
59140
  streamRecoveryClearTimeout = (timer) => clearTimeout(timer);
59185
59141
  streamStartSetTimeout = (callback, delayMs) => setTimeout(callback, delayMs);
59142
+ xmtpAgentFactory = createAgentWithInstallationPrune;
59143
+ // Old clients whose close() was not confirmed. Keyed by lower-cased address.
59144
+ // The reference is kept only for controlled cleanup; it is never served.
59145
+ pendingReleaseByAddress = /* @__PURE__ */ new Map();
59146
+ // Addresses whose pending release was confirmed late (after the caller gave
59147
+ // up waiting) and still need a replacement client on the next recycle.
59148
+ releaseConfirmedNeedsCreate = /* @__PURE__ */ new Set();
59149
+ // Lower-cased addresses owned by a create. The slot is held from before the
59150
+ // SDK factory runs until the new client is registered in `clients` (or the
59151
+ // create failed), so no flow can observe "no lock and no client".
59152
+ clientCreateInFlight = /* @__PURE__ */ new Set();
59153
+ // Clients whose close() was confirmed; a repeated release is a no-op.
59154
+ releasedClients = /* @__PURE__ */ new WeakSet();
59155
+ // One listener setup per client, shared by concurrent init/refresh callers.
59156
+ listenTaskByClient = /* @__PURE__ */ new WeakMap();
59157
+ inboundReplayGateOwners = /* @__PURE__ */ new WeakMap();
59158
+ releaseWaitTimeoutMs = 3e4;
59159
+ releaseWaitSetTimeout = (callback, delayMs) => setTimeout(callback, delayMs);
59160
+ releaseWaitClearTimeout = (timer) => clearTimeout(timer);
59186
59161
  nextClientGeneration = 1;
59187
59162
  clientGenerationByClient = /* @__PURE__ */ new WeakMap();
59188
59163
  resolveStartupReplayCompletion;
@@ -59331,6 +59306,56 @@ var init_xmtp_sdk = __esm({
59331
59306
  return { ok: remaining.length === 0, failures: remaining };
59332
59307
  }
59333
59308
  async createClientForAgent(agentInfo, options = {}) {
59309
+ const address = agentInfo.communicationAddress;
59310
+ const addressKey = address.toLowerCase();
59311
+ let blockedReason;
59312
+ if (this.pendingReleaseByAddress.has(addressKey)) {
59313
+ blockedReason = "release_pending";
59314
+ } else if (this.clientCreateInFlight.has(addressKey)) {
59315
+ blockedReason = "create_in_flight";
59316
+ } else if (this.getClientByAddress(address)) {
59317
+ blockedReason = "client_active";
59318
+ }
59319
+ if (blockedReason) {
59320
+ throw new XmtpClientCreateBlockedError(
59321
+ blockedReason,
59322
+ address,
59323
+ agentInfo.agentId
59324
+ );
59325
+ }
59326
+ this.clientCreateInFlight.add(addressKey);
59327
+ try {
59328
+ const created = await this.createClientForAgentGuarded(agentInfo, options);
59329
+ await this.registerCreatedClient(created, options.logPrefix ?? "[xmtp-sdk]");
59330
+ return created;
59331
+ } finally {
59332
+ this.clientCreateInFlight.delete(addressKey);
59333
+ }
59334
+ }
59335
+ // Ownership hand-off, still inside the create slot: the new client becomes
59336
+ // visible in `clients` before the slot is released. Never overwrites an
59337
+ // existing registration; a conflicting new object is released, not dropped.
59338
+ async registerCreatedClient(created, phase) {
59339
+ const { address, agent, agentInfo } = created;
59340
+ const addressKey = address.toLowerCase();
59341
+ const existing = this.getClientByAddress(address);
59342
+ if (existing && existing !== agent || this.pendingReleaseByAddress.has(addressKey)) {
59343
+ const reason = existing ? "client_active" : "release_pending";
59344
+ logWithTimestamp(
59345
+ `[xmtp-sdk] create registration conflict (${reason}): ${address} (agentId=${agentInfo.agentId}), releasing the new client`
59346
+ );
59347
+ await this.releaseClient(
59348
+ address,
59349
+ agent,
59350
+ agentInfo.agentId,
59351
+ `${phase}/register-conflict`
59352
+ );
59353
+ throw new XmtpClientCreateBlockedError(reason, address, agentInfo.agentId);
59354
+ }
59355
+ this.stoppedAddresses.delete(addressKey);
59356
+ this.clients.set(address, agent);
59357
+ }
59358
+ async createClientForAgentGuarded(agentInfo, options = {}) {
59334
59359
  const address = agentInfo.communicationAddress;
59335
59360
  const phase = options.logPrefix ?? "[xmtp-sdk]";
59336
59361
  const cooldown = getXmtpCreationCooldown();
@@ -59359,7 +59384,7 @@ var init_xmtp_sdk = __esm({
59359
59384
  try {
59360
59385
  const signer = this.tools.createSigner(agentInfo);
59361
59386
  const dbPath = (inboxId2) => `${this.dataDir}/${inboxId2}-${this.env}.db3`;
59362
- const agent = await createAgentWithInstallationPrune(signer, {
59387
+ const agent = await this.xmtpAgentFactory(signer, {
59363
59388
  dbPath,
59364
59389
  env: this.env
59365
59390
  }, {
@@ -59368,6 +59393,7 @@ var init_xmtp_sdk = __esm({
59368
59393
  logPrefix: options.logPrefix
59369
59394
  });
59370
59395
  cooldown.recordCreateSuccess(decision);
59396
+ this.releaseConfirmedNeedsCreate.delete(address.toLowerCase());
59371
59397
  if (decision.mode === "probe") {
59372
59398
  logger.info(LogEvent.XMTP_RATE_LIMIT_RECOVERED, {
59373
59399
  onchainosAgentId: agentInfo.agentId,
@@ -59405,6 +59431,128 @@ var init_xmtp_sdk = __esm({
59405
59431
  throw err2;
59406
59432
  }
59407
59433
  }
59434
+ logReleaseNotConfirmed(address, agentId, phase, reason, err2) {
59435
+ logger.error(
59436
+ LogEvent.XMTP_CLIENT_CLOSE_FAILED,
59437
+ err2 instanceof Error ? err2 : new Error(`xmtp client close not confirmed (${reason})`),
59438
+ { walletAddress: address, onchainosAgentId: agentId, phase, reason }
59439
+ );
59440
+ }
59441
+ // Single release entry (recycle / refresh remove / failed replacement).
59442
+ // Confirmation is ONLY a successful client.close(); a timeout, a missing
59443
+ // close() or a close failure keeps the old reference in
59444
+ // pendingReleaseByAddress and blocks new creates for that address.
59445
+ async releaseClient(address, client, agentId, phase) {
59446
+ const key = address.toLowerCase();
59447
+ if (this.releasedClients.has(client)) {
59448
+ this.unpublishClient(address, client, phase);
59449
+ return true;
59450
+ }
59451
+ const existing = this.pendingReleaseByAddress.get(key);
59452
+ if (existing?.inFlight) {
59453
+ this.unpublishClient(address, client, phase);
59454
+ return false;
59455
+ }
59456
+ const entry = existing ?? { client, agentId, phase };
59457
+ entry.client = client;
59458
+ entry.agentId = agentId;
59459
+ entry.phase = phase;
59460
+ this.pendingReleaseByAddress.set(key, entry);
59461
+ this.unpublishClient(address, client, phase);
59462
+ let timedOut = false;
59463
+ const task = stopAndCloseXmtpClient(client, {
59464
+ address,
59465
+ agentId,
59466
+ phase,
59467
+ onCloseError: (err2) => this.logReleaseNotConfirmed(address, agentId, phase, "close_threw", err2)
59468
+ }).then((result) => {
59469
+ if (!result.stopped) {
59470
+ logWithTimestamp(
59471
+ `[xmtp-sdk] release: stop failed but close proceeded: ${address} (agentId=${agentId}) phase=${phase}`
59472
+ );
59473
+ }
59474
+ entry.inFlight = void 0;
59475
+ if (result.closed) {
59476
+ this.releasedClients.add(client);
59477
+ }
59478
+ if (result.closed && this.pendingReleaseByAddress.get(key) === entry) {
59479
+ this.pendingReleaseByAddress.delete(key);
59480
+ if (timedOut) {
59481
+ this.releaseConfirmedNeedsCreate.add(key);
59482
+ }
59483
+ }
59484
+ return result;
59485
+ });
59486
+ entry.inFlight = task.then((result) => result.closed);
59487
+ const timeoutHandle = {};
59488
+ const timeoutPromise = new Promise((resolve14) => {
59489
+ timeoutHandle.timer = this.releaseWaitSetTimeout(
59490
+ () => resolve14(null),
59491
+ this.releaseWaitTimeoutMs
59492
+ );
59493
+ });
59494
+ const outcome = await Promise.race([task, timeoutPromise]);
59495
+ if (timeoutHandle.timer !== void 0) {
59496
+ this.releaseWaitClearTimeout(timeoutHandle.timer);
59497
+ }
59498
+ if (outcome === null) {
59499
+ timedOut = true;
59500
+ this.logReleaseNotConfirmed(address, agentId, phase, "close_timeout");
59501
+ return false;
59502
+ }
59503
+ if (outcome.closed) {
59504
+ if (this.pendingReleaseByAddress.get(key) === entry) {
59505
+ this.pendingReleaseByAddress.delete(key);
59506
+ }
59507
+ return true;
59508
+ }
59509
+ if (!outcome.closeFailed) {
59510
+ this.logReleaseNotConfirmed(address, agentId, phase, "close_unavailable");
59511
+ }
59512
+ return false;
59513
+ }
59514
+ // Stops serving `client` to new operations. Every removal is identity
59515
+ // checked, so a late caller can never remove a newer instance (or its replay
59516
+ // gate / recovery / retry state) registered for the same address.
59517
+ unpublishClient(address, client, phase) {
59518
+ const key = address.toLowerCase();
59519
+ let removed = false;
59520
+ for (const [registeredAddress, registered] of this.clients) {
59521
+ if (registered === client && registeredAddress.toLowerCase() === key) {
59522
+ this.clients.delete(registeredAddress);
59523
+ removed = true;
59524
+ }
59525
+ }
59526
+ const gate = this.inboundReplayGates.get(key);
59527
+ if (gate) {
59528
+ const owner = this.inboundReplayGateOwners.get(gate);
59529
+ if (owner === client || owner === void 0 && removed) {
59530
+ this.inboundReplayGates.delete(key);
59531
+ }
59532
+ }
59533
+ this.cancelStreamRecoveryForAddress(address, client);
59534
+ if (removed) {
59535
+ this.replayScanFailuresByAddress.delete(key);
59536
+ this.clearReplayScanRetry(
59537
+ address,
59538
+ phase.startsWith("recycle") ? "client_recycled" : "client_removed"
59539
+ );
59540
+ }
59541
+ if (!this.getClientByAddress(address)) {
59542
+ this.stoppedAddresses.add(key);
59543
+ }
59544
+ }
59545
+ async settlePendingRelease(address) {
59546
+ const entry = this.pendingReleaseByAddress.get(address.toLowerCase());
59547
+ if (!entry) {
59548
+ return true;
59549
+ }
59550
+ if (entry.inFlight) {
59551
+ return false;
59552
+ }
59553
+ const retryPhase = entry.phase.endsWith("/retry") ? entry.phase : `${entry.phase}/retry`;
59554
+ return this.releaseClient(address, entry.client, entry.agentId, retryPhase);
59555
+ }
59408
59556
  async recordInstallationHealth(params) {
59409
59557
  const { address, agent, agentInfo, phase, attempt } = params;
59410
59558
  try {
@@ -59525,8 +59673,6 @@ var init_xmtp_sdk = __esm({
59525
59673
  const r = results[i];
59526
59674
  if (r.status === "fulfilled") {
59527
59675
  const { address, agent, agentInfo } = r.value;
59528
- this.stoppedAddresses.delete(address.toLowerCase());
59529
- this.clients.set(address, agent);
59530
59676
  logWithTimestamp(
59531
59677
  `[xmtp-sdk] identity initialized: ${address} (agentId=${agentInfo.agentId})`
59532
59678
  );
@@ -59552,6 +59698,10 @@ var init_xmtp_sdk = __esm({
59552
59698
  phase: "init",
59553
59699
  attempt: "1"
59554
59700
  });
59701
+ } else if (r.reason instanceof XmtpClientCreateBlockedError) {
59702
+ logWithTimestamp(
59703
+ `[xmtp-sdk] identity init: create blocked (${r.reason.reason}): ${agents[i].communicationAddress} (agentId=${agents[i].agentId})`
59704
+ );
59555
59705
  } else {
59556
59706
  failedAgents.push(agents[i]);
59557
59707
  if (r.reason instanceof XmtpCreationCooldownSkipError) {
@@ -59588,8 +59738,6 @@ var init_xmtp_sdk = __esm({
59588
59738
  const r = retryResults[i];
59589
59739
  if (r.status === "fulfilled") {
59590
59740
  const { address, agent, agentInfo } = r.value;
59591
- this.stoppedAddresses.delete(address.toLowerCase());
59592
- this.clients.set(address, agent);
59593
59741
  logWithTimestamp(
59594
59742
  `[xmtp-sdk] retry succeeded: ${address} (agentId=${agentInfo.agentId})`
59595
59743
  );
@@ -59615,6 +59763,11 @@ var init_xmtp_sdk = __esm({
59615
59763
  phase: "init",
59616
59764
  attempt: "2"
59617
59765
  });
59766
+ } else if (r.reason instanceof XmtpClientCreateBlockedError) {
59767
+ const info = failedAgents[i];
59768
+ logWithTimestamp(
59769
+ `[xmtp-sdk] retry create blocked (${r.reason.reason}): ${info.communicationAddress} (agentId=${info.agentId})`
59770
+ );
59618
59771
  } else if (r.reason instanceof XmtpCreationCooldownSkipError) {
59619
59772
  const info = failedAgents[i];
59620
59773
  logWithTimestamp(
@@ -59697,36 +59850,38 @@ var init_xmtp_sdk = __esm({
59697
59850
  throw err2;
59698
59851
  }
59699
59852
  }
59853
+ for (const pendingKey of [...this.pendingReleaseByAddress.keys()]) {
59854
+ const settled = await this.settlePendingRelease(pendingKey);
59855
+ logWithTimestamp(
59856
+ `[xmtp-sdk] refresh: pending release ${pendingKey} ${settled ? "confirmed" : "still pending"}`
59857
+ );
59858
+ }
59700
59859
  const oldAddresses = new Set(
59701
- this.allAgents.map((a) => a.communicationAddress)
59860
+ this.allAgents.map((a) => a.communicationAddress.toLowerCase())
59861
+ );
59862
+ const newAddresses = new Set(
59863
+ newAgents.map((a) => a.communicationAddress.toLowerCase())
59702
59864
  );
59703
- const newAddresses = new Set(newAgents.map((a) => a.communicationAddress));
59704
59865
  const added = newAgents.filter(
59705
- (a) => !oldAddresses.has(a.communicationAddress) || !this.clients.has(a.communicationAddress)
59866
+ (a) => !oldAddresses.has(a.communicationAddress.toLowerCase()) || !this.getClientByAddress(a.communicationAddress)
59706
59867
  );
59707
59868
  const removed = this.allAgents.filter(
59708
- (a) => !newAddresses.has(a.communicationAddress)
59869
+ (a) => !newAddresses.has(a.communicationAddress.toLowerCase())
59709
59870
  );
59710
59871
  this.allAgents = newAgents;
59711
59872
  for (const agentInfo of removed) {
59712
59873
  const address = agentInfo.communicationAddress;
59713
- const client = this.clients.get(address);
59874
+ const client = this.getClientByAddress(address);
59714
59875
  if (client) {
59715
59876
  try {
59716
- this.cancelStreamRecoveryForAddress(address);
59717
- this.stoppedAddresses.add(address.toLowerCase());
59718
- await stopAndCloseXmtpClient(client, {
59877
+ const released = await this.releaseClient(
59719
59878
  address,
59720
- agentId: agentInfo.agentId,
59721
- phase: "refresh/remove",
59722
- onCloseError: (err2) => logger.error(
59723
- LogEvent.XMTP_CLIENT_CLOSE_FAILED,
59724
- err2 instanceof Error ? err2 : new Error(String(err2)),
59725
- { walletAddress: address, onchainosAgentId: agentInfo.agentId, phase: "refresh/remove" }
59726
- )
59727
- });
59879
+ client,
59880
+ agentInfo.agentId,
59881
+ "refresh/remove"
59882
+ );
59728
59883
  logWithTimestamp(
59729
- `[xmtp-sdk] refresh: stopped client: ${address} (agentId=${agentInfo.agentId})`
59884
+ `[xmtp-sdk] refresh: stopped client: ${address} (agentId=${agentInfo.agentId}) released=${released}`
59730
59885
  );
59731
59886
  } catch (err2) {
59732
59887
  logWithTimestamp(`[xmtp-sdk] refresh: failed to stop client: ${address}`, err2);
@@ -59736,10 +59891,6 @@ var init_xmtp_sdk = __esm({
59736
59891
  phase: "stop-client"
59737
59892
  });
59738
59893
  }
59739
- this.clients.delete(address);
59740
- this.inboundReplayGates.delete(address.toLowerCase());
59741
- this.replayScanFailuresByAddress.delete(address.toLowerCase());
59742
- this.clearReplayScanRetry(address, "client_removed");
59743
59894
  logger.info(LogEvent.AGENT_CLIENT_REMOVED, {
59744
59895
  ...agentExtras({
59745
59896
  walletAddress: address,
@@ -59781,8 +59932,6 @@ var init_xmtp_sdk = __esm({
59781
59932
  const r = addResults[i];
59782
59933
  if (r.status === "fulfilled") {
59783
59934
  const { address, agentInfo } = r.value;
59784
- this.stoppedAddresses.delete(address.toLowerCase());
59785
- this.clients.set(address, r.value.agent);
59786
59935
  logWithTimestamp(
59787
59936
  `[xmtp-sdk] refresh: identity initialized: ${address} (agentId=${agentInfo.agentId})`
59788
59937
  );
@@ -59809,6 +59958,10 @@ var init_xmtp_sdk = __esm({
59809
59958
  phase: "refresh",
59810
59959
  attempt: "1"
59811
59960
  });
59961
+ } else if (r.reason instanceof XmtpClientCreateBlockedError) {
59962
+ logWithTimestamp(
59963
+ `[xmtp-sdk] refresh: create blocked (${r.reason.reason}): ${added[i].communicationAddress} (agentId=${added[i].agentId})`
59964
+ );
59812
59965
  } else {
59813
59966
  failedAgents.push(added[i]);
59814
59967
  if (r.reason instanceof XmtpCreationCooldownSkipError) {
@@ -59848,8 +60001,6 @@ var init_xmtp_sdk = __esm({
59848
60001
  const r = retryResults[i];
59849
60002
  if (r.status === "fulfilled") {
59850
60003
  const { address, agentInfo } = r.value;
59851
- this.stoppedAddresses.delete(address.toLowerCase());
59852
- this.clients.set(address, r.value.agent);
59853
60004
  logWithTimestamp(
59854
60005
  `[xmtp-sdk] refresh: retry succeeded: ${address} (agentId=${agentInfo.agentId})`
59855
60006
  );
@@ -59876,6 +60027,11 @@ var init_xmtp_sdk = __esm({
59876
60027
  phase: "refresh",
59877
60028
  attempt: "2"
59878
60029
  });
60030
+ } else if (r.reason instanceof XmtpClientCreateBlockedError) {
60031
+ const info = failedAgents[i];
60032
+ logWithTimestamp(
60033
+ `[xmtp-sdk] refresh: retry create blocked (${r.reason.reason}): ${info.communicationAddress} (agentId=${info.agentId})`
60034
+ );
59879
60035
  } else if (r.reason instanceof XmtpCreationCooldownSkipError) {
59880
60036
  const info = failedAgents[i];
59881
60037
  logWithTimestamp(
@@ -59901,7 +60057,8 @@ var init_xmtp_sdk = __esm({
59901
60057
  }
59902
60058
  }
59903
60059
  await this.startListeningForAddresses(
59904
- added.map((a) => a.communicationAddress)
60060
+ added.map((a) => a.communicationAddress),
60061
+ "refresh/listen-failed"
59905
60062
  );
59906
60063
  for (const agentInfo of added) {
59907
60064
  const address = agentInfo.communicationAddress;
@@ -59966,30 +60123,42 @@ var init_xmtp_sdk = __esm({
59966
60123
  }
59967
60124
  for (const agentInfo of this.allAgents) {
59968
60125
  const address = agentInfo.communicationAddress;
59969
- const oldClient = this.clients.get(address);
59970
- if (!oldClient) {
60126
+ const addressKey = address.toLowerCase();
60127
+ const oldClient = this.getClientByAddress(address);
60128
+ const hasPendingRelease = this.pendingReleaseByAddress.has(addressKey) || this.releaseConfirmedNeedsCreate.has(addressKey);
60129
+ if (!oldClient && !hasPendingRelease) {
59971
60130
  continue;
59972
60131
  }
59973
- const addressKey = address.toLowerCase();
59974
- try {
59975
- this.cancelStreamRecoveryForAddress(address);
59976
- this.stoppedAddresses.add(addressKey);
59977
- await stopAndCloseXmtpClient(oldClient, {
59978
- address,
59979
- agentId: agentInfo.agentId,
59980
- phase: "recycle",
59981
- onCloseError: (err2) => logger.error(
59982
- LogEvent.XMTP_CLIENT_CLOSE_FAILED,
59983
- err2 instanceof Error ? err2 : new Error(String(err2)),
59984
- { walletAddress: address, onchainosAgentId: agentInfo.agentId, phase: "recycle" }
59985
- )
59986
- });
59987
- this.clients.delete(address);
59988
- this.inboundReplayGates.delete(addressKey);
59989
- this.replayScanFailuresByAddress.delete(addressKey);
59990
- this.clearReplayScanRetry(address, "client_recycled");
60132
+ if (!oldClient) {
60133
+ let settled = false;
60134
+ try {
60135
+ settled = await this.settlePendingRelease(address);
60136
+ } catch (err2) {
60137
+ logWithTimestamp(`[xmtp-sdk] recycle: pending release retry failed: ${address}`, err2);
60138
+ }
60139
+ if (!settled) {
60140
+ failed++;
60141
+ continue;
60142
+ }
60143
+ } else {
60144
+ let released = false;
60145
+ try {
60146
+ released = await this.releaseClient(
60147
+ address,
60148
+ oldClient,
60149
+ agentInfo.agentId,
60150
+ "recycle"
60151
+ );
60152
+ } catch (err2) {
60153
+ logWithTimestamp(`[xmtp-sdk] recycle: failed to stop client: ${address}`, err2);
60154
+ logger.error(LogEvent.AGENT_REFRESH_FAILED, err2 instanceof Error ? err2 : new Error(String(err2)), {
60155
+ walletAddress: address,
60156
+ onchainosAgentId: agentInfo.agentId,
60157
+ phase: "recycle/stop-client"
60158
+ });
60159
+ }
59991
60160
  logWithTimestamp(
59992
- `[xmtp-sdk] recycle: stopped client: ${address} (agentId=${agentInfo.agentId})`
60161
+ `[xmtp-sdk] recycle: stopped client: ${address} (agentId=${agentInfo.agentId}) released=${released}`
59993
60162
  );
59994
60163
  logger.info(LogEvent.AGENT_CLIENT_REMOVED, {
59995
60164
  ...agentExtras({
@@ -59999,23 +60168,18 @@ var init_xmtp_sdk = __esm({
59999
60168
  }),
60000
60169
  phase: "recycle"
60001
60170
  });
60002
- } catch (err2) {
60003
- failed++;
60004
- this.stoppedAddresses.delete(addressKey);
60005
- logWithTimestamp(`[xmtp-sdk] recycle: failed to stop client: ${address}`, err2);
60006
- logger.error(LogEvent.AGENT_REFRESH_FAILED, err2 instanceof Error ? err2 : new Error(String(err2)), {
60007
- walletAddress: address,
60008
- onchainosAgentId: agentInfo.agentId,
60009
- phase: "recycle/stop-client"
60010
- });
60011
- continue;
60171
+ if (!released) {
60172
+ failed++;
60173
+ continue;
60174
+ }
60012
60175
  }
60176
+ let newAgent;
60177
+ let newAgentReleased = false;
60013
60178
  try {
60014
60179
  const { agent } = await this.createClientForAgent(agentInfo, {
60015
60180
  logPrefix: "[xmtp-sdk:recycle]"
60016
60181
  });
60017
- this.stoppedAddresses.delete(addressKey);
60018
- this.clients.set(address, agent);
60182
+ newAgent = agent;
60019
60183
  logWithTimestamp(
60020
60184
  `[xmtp-sdk] recycle: identity initialized: ${address} (agentId=${agentInfo.agentId})`
60021
60185
  );
@@ -60039,7 +60203,13 @@ var init_xmtp_sdk = __esm({
60039
60203
  agentInfo,
60040
60204
  phase: "recycle"
60041
60205
  });
60042
- await this.startListeningForAddresses([address]);
60206
+ const listen = (await this.startListeningForAddresses([address], "recycle/listen-failed")).get(address);
60207
+ if (listen?.client !== agent || !isListenHealthy(listen.outcome)) {
60208
+ newAgentReleased = listen?.client === agent && listen.outcome === "failed";
60209
+ throw new Error(
60210
+ `xmtp listener not started (${listen?.client === agent ? listen.outcome : "client_replaced"})`
60211
+ );
60212
+ }
60043
60213
  const replayGate = this.inboundReplayGates.get(addressKey);
60044
60214
  let replaySummary;
60045
60215
  try {
@@ -60058,8 +60228,21 @@ var init_xmtp_sdk = __esm({
60058
60228
  recycled++;
60059
60229
  } catch (err2) {
60060
60230
  failed++;
60061
- this.cancelStreamRecoveryForAddress(address);
60062
- this.clients.delete(address);
60231
+ if (newAgent && !newAgentReleased) {
60232
+ try {
60233
+ await this.releaseClient(
60234
+ address,
60235
+ newAgent,
60236
+ agentInfo.agentId,
60237
+ "recycle/create-failed"
60238
+ );
60239
+ } catch (releaseErr) {
60240
+ logWithTimestamp(
60241
+ `[xmtp-sdk] recycle: failed to release new client: ${address}`,
60242
+ releaseErr
60243
+ );
60244
+ }
60245
+ }
60063
60246
  logWithTimestamp(`[xmtp-sdk] recycle: failed to recreate client: ${address}`, err2);
60064
60247
  logger.error(
60065
60248
  LogEvent.XMTP_CONNECTION_FAILED,
@@ -60463,7 +60646,7 @@ var init_xmtp_sdk = __esm({
60463
60646
  }
60464
60647
  }
60465
60648
  async startListening() {
60466
- await this.startListeningForAddresses([...this.clients.keys()]);
60649
+ await this.startListeningForAddresses([...this.clients.keys()], "listen-failed");
60467
60650
  }
60468
60651
  async drainInboundReplayGate(address, expectedGate) {
60469
60652
  if (!expectedGate) {
@@ -60528,11 +60711,22 @@ var init_xmtp_sdk = __esm({
60528
60711
  );
60529
60712
  return gate;
60530
60713
  }
60531
- cancelStreamRecoveryForAddress(address) {
60714
+ // With `client`, only that client's recovery is cancelled, so a late caller
60715
+ // cannot cancel the recovery of a newer instance on the same address.
60716
+ cancelStreamRecoveryForAddress(address, client) {
60532
60717
  const addressKey = address.toLowerCase();
60533
- const cleanup = this.streamRecoveryCleanupByAddress.get(addressKey);
60718
+ const entry = this.streamRecoveryCleanupByAddress.get(addressKey);
60719
+ if (!entry || client && entry.client !== client) {
60720
+ return;
60721
+ }
60534
60722
  this.streamRecoveryCleanupByAddress.delete(addressKey);
60535
- cleanup?.();
60723
+ entry.cleanup();
60724
+ }
60725
+ // True while `client` is the registered client of `address` (case-insensitive)
60726
+ // and the address is neither stopped nor being released.
60727
+ isCurrentListenOwner(address, client) {
60728
+ const key = address.toLowerCase();
60729
+ return this.getClientByAddress(address) === client && !this.stoppedAddresses.has(key) && !this.pendingReleaseByAddress.has(key);
60536
60730
  }
60537
60731
  getClientGeneration(client) {
60538
60732
  const existing = this.clientGenerationByClient.get(client);
@@ -60684,10 +60878,17 @@ var init_xmtp_sdk = __esm({
60684
60878
  this.notifyAgentsWakeupAfterReplay();
60685
60879
  return summary;
60686
60880
  }
60687
- async startListeningForAddresses(addresses) {
60881
+ // Starts (at most once per client) the listener of every registered address
60882
+ // and reports each outcome. A "failed" client, i.e. one with neither a live
60883
+ // listener nor an owned retry task, is released with `releasePhase` before
60884
+ // this returns, so it is never served as a healthy instance.
60885
+ async startListeningForAddresses(addresses, releasePhase = "listen-failed") {
60688
60886
  const valid = addresses.filter((addr) => this.clients.has(addr));
60689
- await parallelMap(valid, async (address) => {
60690
- const agent = this.clients.get(address);
60887
+ const targets = valid.map((address) => ({
60888
+ address,
60889
+ client: this.clients.get(address)
60890
+ }));
60891
+ const listenForClient = async (address, agent) => {
60691
60892
  const clientGeneration = this.getClientGeneration(agent);
60692
60893
  const addressKey = address.toLowerCase();
60693
60894
  const tag = `[xmtp-sdk:${address}]`;
@@ -60698,6 +60899,11 @@ var init_xmtp_sdk = __esm({
60698
60899
  onchainosAgentId: onchainosAgent?.agentId,
60699
60900
  role: onchainosAgent?.role
60700
60901
  };
60902
+ const ownsAddress = () => this.isCurrentListenOwner(address, agent);
60903
+ if (!ownsAddress()) {
60904
+ logWithTimestamp(`${tag} listener setup skipped: client no longer current`);
60905
+ return "skipped";
60906
+ }
60701
60907
  try {
60702
60908
  await agent.client.conversations.syncAll([
60703
60909
  import_node_bindings2.ConsentState.Allowed,
@@ -60705,13 +60911,21 @@ var init_xmtp_sdk = __esm({
60705
60911
  ]);
60706
60912
  logWithTimestamp(`${tag} conversations synced (non-Denied)`);
60707
60913
  } catch (err2) {
60708
- logWithTimestamp(`${tag} conversation sync failed, skipping this client:`, err2);
60914
+ if (!ownsAddress()) {
60915
+ logWithTimestamp(`${tag} stale conversation sync failed after replacement, ignored:`, err2);
60916
+ return "skipped";
60917
+ }
60918
+ logWithTimestamp(`${tag} conversation sync failed, listener not started:`, err2);
60709
60919
  logger.error(
60710
60920
  LogEvent.CONVERSATIONS_STREAM_FAILED,
60711
60921
  err2 instanceof Error ? err2 : void 0,
60712
60922
  agentExtras(identity)
60713
60923
  );
60714
- return;
60924
+ return "failed";
60925
+ }
60926
+ if (!ownsAddress()) {
60927
+ logWithTimestamp(`${tag} listener setup skipped after sync: client no longer current`);
60928
+ return "skipped";
60715
60929
  }
60716
60930
  agent.use(loggingMiddleware(identity));
60717
60931
  agent.use(selfFilterMiddleware());
@@ -60740,6 +60954,7 @@ var init_xmtp_sdk = __esm({
60740
60954
  });
60741
60955
  const gate = new InboundReplayGate(inbound);
60742
60956
  this.inboundReplayGates.set(address.toLowerCase(), gate);
60957
+ this.inboundReplayGateOwners.set(gate, agent);
60743
60958
  agent.use(gate.middleware);
60744
60959
  } else {
60745
60960
  logWithTimestamp(
@@ -60757,16 +60972,25 @@ var init_xmtp_sdk = __esm({
60757
60972
  const MAX_RETRIES = 5;
60758
60973
  const BASE_DELAY_MS = 1e3;
60759
60974
  const startWithRetry = async (attempt = 0) => {
60760
- if (this.stoppedAddresses.has(address.toLowerCase()) || !this.clients.has(address)) {
60975
+ if (!ownsAddress()) {
60761
60976
  logWithTimestamp(`${tag} start skipped: client stopped or removed`);
60762
- return;
60977
+ return "skipped";
60763
60978
  }
60764
60979
  try {
60765
60980
  const liveStreamOptions = {
60766
60981
  consentStates: [import_node_bindings2.ConsentState.Allowed, import_node_bindings2.ConsentState.Unknown]
60767
60982
  };
60768
60983
  await agent.start(liveStreamOptions);
60984
+ if (!ownsAddress()) {
60985
+ logWithTimestamp(`${tag} stale agent.start resolved after replacement, ignored`);
60986
+ return "skipped";
60987
+ }
60988
+ return "started";
60769
60989
  } catch (err2) {
60990
+ if (!ownsAddress()) {
60991
+ logWithTimestamp(`${tag} stale agent.start failed after replacement, ignored:`, err2);
60992
+ return "skipped";
60993
+ }
60770
60994
  logWithTimestamp(`${tag} agent.start error (attempt=${attempt}):`, err2);
60771
60995
  logger.error(
60772
60996
  LogEvent.AGENT_START_FAILED,
@@ -60776,17 +61000,27 @@ var init_xmtp_sdk = __esm({
60776
61000
  attempt: String(attempt)
60777
61001
  }
60778
61002
  );
60779
- scheduleRetry(attempt);
61003
+ return scheduleRetry(attempt);
60780
61004
  }
60781
61005
  };
60782
61006
  const scheduleRetry = (attempt) => {
60783
- if (this.stoppedAddresses.has(address.toLowerCase()) || !this.clients.has(address)) {
61007
+ if (!ownsAddress()) {
60784
61008
  logWithTimestamp(`${tag} reconnect skipped: client stopped or removed`);
60785
- return;
61009
+ return "skipped";
60786
61010
  }
60787
61011
  if (attempt >= MAX_RETRIES) {
60788
- logWithTimestamp(`${tag} reached max retries (${MAX_RETRIES}), giving up reconnect`);
60789
- return;
61012
+ logWithTimestamp(
61013
+ `${tag} reached max retries (${MAX_RETRIES}), giving up reconnect; releasing client`
61014
+ );
61015
+ void this.releaseClient(
61016
+ address,
61017
+ agent,
61018
+ identity.onchainosAgentId ?? "",
61019
+ "listen/retry-exhausted"
61020
+ ).catch((err2) => {
61021
+ logWithTimestamp(`${tag} release after retry exhaustion failed:`, err2);
61022
+ });
61023
+ return "failed";
60790
61024
  }
60791
61025
  const delay = BASE_DELAY_MS * 2 ** attempt;
60792
61026
  logWithTimestamp(`${tag} reconnecting in ${delay}ms (attempt=${attempt + 1})`);
@@ -60794,6 +61028,7 @@ var init_xmtp_sdk = __esm({
60794
61028
  () => void startWithRetry(attempt + 1),
60795
61029
  delay
60796
61030
  );
61031
+ return "retrying";
60797
61032
  };
60798
61033
  const streamRecovery = new StreamRecoveryTelemetry();
60799
61034
  let recoveryTimeout;
@@ -60813,10 +61048,10 @@ var init_xmtp_sdk = __esm({
60813
61048
  recoveryStabilityTimeout = void 0;
60814
61049
  }
60815
61050
  };
60816
- this.streamRecoveryCleanupByAddress.set(
60817
- addressKey,
60818
- cleanupStreamRecovery
60819
- );
61051
+ this.streamRecoveryCleanupByAddress.set(addressKey, {
61052
+ client: agent,
61053
+ cleanup: cleanupStreamRecovery
61054
+ });
60820
61055
  agent.on("unhandledError", (err2) => {
60821
61056
  if (recoveryDisposed || this.clients.get(address) !== agent || this.stoppedAddresses.has(addressKey)) {
60822
61057
  return;
@@ -60978,9 +61213,54 @@ var init_xmtp_sdk = __esm({
60978
61213
  `${tag} agent message event msg=${msg?.id ?? "(unknown)"} kind=${msg?.kind ?? "(unknown)"} conv=${conversation?.id ?? msg?.conversationId ?? "(unknown)"} sender=${msg?.senderInboxId ?? "(unknown)"} isText=${typeof ctx.isText === "function" ? String(ctx.isText()) : "unknown"} preview="${previewDebugContent(msg?.content)}"`
60979
61214
  );
60980
61215
  });
60981
- await startWithRetry();
60982
- logWithTimestamp(`${tag} message listener started`);
61216
+ const outcome = await startWithRetry();
61217
+ logWithTimestamp(`${tag} message listener ${outcome}`);
61218
+ return outcome;
61219
+ };
61220
+ const settled = await parallelMap(targets, async ({ address, client }) => {
61221
+ let task = this.listenTaskByClient.get(client);
61222
+ if (!task) {
61223
+ task = listenForClient(address, client);
61224
+ this.listenTaskByClient.set(client, task);
61225
+ }
61226
+ let outcome;
61227
+ try {
61228
+ outcome = await task;
61229
+ } catch (err2) {
61230
+ logWithTimestamp(`[xmtp-sdk:${address}] listener setup failed:`, err2);
61231
+ logger.error(
61232
+ LogEvent.AGENT_START_FAILED,
61233
+ err2 instanceof Error ? err2 : new Error(String(err2)),
61234
+ {
61235
+ ...agentExtras({
61236
+ walletAddress: address,
61237
+ inboxId: client.client?.inboxId,
61238
+ onchainosAgentId: this.getAgentByAddress(address)?.agentId
61239
+ }),
61240
+ phase: "listener-setup"
61241
+ }
61242
+ );
61243
+ outcome = "failed";
61244
+ }
61245
+ if (outcome === "failed") {
61246
+ await this.releaseClient(
61247
+ address,
61248
+ client,
61249
+ this.getAgentByAddress(address)?.agentId ?? "",
61250
+ releasePhase
61251
+ );
61252
+ }
61253
+ return outcome;
61254
+ });
61255
+ const results = /* @__PURE__ */ new Map();
61256
+ targets.forEach(({ address, client }, i) => {
61257
+ const r = settled[i];
61258
+ results.set(address, {
61259
+ client,
61260
+ outcome: r.status === "fulfilled" ? r.value : "failed"
61261
+ });
60983
61262
  });
61263
+ return results;
60984
61264
  }
60985
61265
  async replayOfflineMessagesForAddress(address, expectedClient, trigger = "periodic_repair", recoveryIdentity) {
60986
61266
  const addressReplayStartedAt = Date.now();
@@ -62127,247 +62407,6 @@ var init_onchainos = __esm({
62127
62407
  }
62128
62408
  });
62129
62409
 
62130
- // ../core/src/xmtp-sdk/onchainos/list-jobs-by-verified-sender.ts
62131
- function isPlainObject(value) {
62132
- return !!value && typeof value === "object" && !Array.isArray(value);
62133
- }
62134
- function isNonEmptyString(value) {
62135
- return typeof value === "string" && value.length > 0;
62136
- }
62137
- function redactLogValue(value, left = 6, right = 4) {
62138
- if (!value) {
62139
- return "(none)";
62140
- }
62141
- if (value.length <= left + right + 1) {
62142
- return "(redacted)";
62143
- }
62144
- return `${value.slice(0, left)}\u2026${value.slice(-right)}`;
62145
- }
62146
- function guardSessionExpired3(stdout, command) {
62147
- if (isSessionExpired(stdout)) {
62148
- reportSessionExpired(command);
62149
- }
62150
- }
62151
- function guardCatchSessionExpired3(err2, command) {
62152
- const stdout = String(err2?.stdout ?? "");
62153
- if (stdout && isSessionExpired(stdout)) {
62154
- reportSessionExpired(command);
62155
- }
62156
- }
62157
- function buildListJobsByVerifiedSenderArgs(params) {
62158
- return [
62159
- "agent",
62160
- "list-jobs-by-verified-sender",
62161
- "--group-id",
62162
- params.groupId,
62163
- "--sender-inbox-id",
62164
- params.senderInboxId,
62165
- "--agentic-id",
62166
- params.agenticId
62167
- ];
62168
- }
62169
- function extractErrorText(errorLike) {
62170
- if (typeof errorLike === "string") {
62171
- return errorLike;
62172
- }
62173
- if (typeof errorLike === "number") {
62174
- return String(errorLike);
62175
- }
62176
- if (errorLike && typeof errorLike === "object") {
62177
- const anyErr = errorLike;
62178
- return [anyErr.code, anyErr.message, anyErr.stdout, anyErr.stderr].filter((value) => typeof value === "string" || typeof value === "number").map(String).join(" ");
62179
- }
62180
- return errorLike == null ? "" : String(errorLike);
62181
- }
62182
- function classifyListJobsError(errorLike) {
62183
- const text = extractErrorText(errorLike);
62184
- for (const [code, reason] of Object.entries(KNOWN_ERROR_CODES2)) {
62185
- if (text.includes(code)) {
62186
- return { reason, errorCode: code };
62187
- }
62188
- }
62189
- if (/\b401\b/.test(text)) {
62190
- return { reason: "auth_error", errorCode: "401" };
62191
- }
62192
- if (/\b403\b/.test(text)) {
62193
- return { reason: "auth_error", errorCode: "403" };
62194
- }
62195
- return { reason: "invalid_response" };
62196
- }
62197
- function normalizeListJobsResponse(data) {
62198
- const rawJobs = isPlainObject(data) && Array.isArray(data.jobs) ? data.jobs : [];
62199
- const seen = /* @__PURE__ */ new Set();
62200
- const jobs = [];
62201
- let invalidCount = 0;
62202
- for (const item of rawJobs) {
62203
- if (!isPlainObject(item) || !isNonEmptyString(item.jobId) || !isNonEmptyString(item.userAgentId) || !isNonEmptyString(item.aspAgentId)) {
62204
- invalidCount += 1;
62205
- continue;
62206
- }
62207
- const key = `${item.jobId}|${item.userAgentId}|${item.aspAgentId}`;
62208
- if (seen.has(key)) {
62209
- continue;
62210
- }
62211
- seen.add(key);
62212
- jobs.push({ jobId: item.jobId, userAgentId: item.userAgentId, aspAgentId: item.aspAgentId });
62213
- }
62214
- return { jobs, invalidCount };
62215
- }
62216
- async function listJobsByVerifiedSender(params, execFn = exec) {
62217
- logWithTimestamp(
62218
- `[onchainos] listing jobs by verified sender: group=${params.groupId} sender=${redactLogValue(params.senderInboxId)} agentic=${params.agenticId}`
62219
- );
62220
- let stdout;
62221
- let stderr;
62222
- try {
62223
- ({ stdout, stderr } = await execFn(buildListJobsByVerifiedSenderArgs(params)));
62224
- } catch (err2) {
62225
- guardCatchSessionExpired3(err2, "list-jobs-by-verified-sender");
62226
- const classified = classifyListJobsError(err2);
62227
- const reason = classified.reason === "invalid_response" ? "network_error" : classified.reason;
62228
- warnWithTimestamp(
62229
- `[onchainos] list-jobs-by-verified-sender exec failed (group=${params.groupId} sender=${redactLogValue(params.senderInboxId)}); fail-closed reason=${reason}`
62230
- );
62231
- return { status: "fail_closed", jobs: [], invalidCount: 0, reason, errorCode: classified.errorCode };
62232
- }
62233
- if (stderr) {
62234
- logWithTimestamp(`[onchainos] stderr (list-jobs-by-verified-sender): ${stderr}`);
62235
- }
62236
- let res;
62237
- try {
62238
- res = parseCliJson(stdout, "list-jobs-by-verified-sender", {
62239
- groupId: params.groupId,
62240
- agenticId: params.agenticId
62241
- });
62242
- } catch {
62243
- return { status: "fail_closed", jobs: [], invalidCount: 0, reason: "invalid_response" };
62244
- }
62245
- if (!res.ok) {
62246
- guardSessionExpired3(stdout, "list-jobs-by-verified-sender");
62247
- const classified = classifyListJobsError(res.error);
62248
- warnWithTimestamp(
62249
- `[onchainos] list-jobs-by-verified-sender ok=false (group=${params.groupId}); fail-closed reason=${classified.reason}`
62250
- );
62251
- logger.error(
62252
- LogEvent.ONCHAINOS_CLI_ERROR,
62253
- new Error("list-jobs-by-verified-sender ok=false"),
62254
- cliOkFalseExtras(stdout, "list-jobs-by-verified-sender", {
62255
- groupId: params.groupId,
62256
- agenticId: params.agenticId,
62257
- reason: classified.reason
62258
- })
62259
- );
62260
- return { status: "fail_closed", jobs: [], invalidCount: 0, reason: classified.reason, errorCode: classified.errorCode };
62261
- }
62262
- const { jobs, invalidCount } = normalizeListJobsResponse(res.data);
62263
- if (jobs.length === 0) {
62264
- logWithTimestamp(
62265
- `[onchainos] list-jobs-by-verified-sender: empty (group=${params.groupId} invalidCount=${invalidCount})`
62266
- );
62267
- return { status: "empty", jobs, invalidCount };
62268
- }
62269
- logWithTimestamp(
62270
- `[onchainos] list-jobs-by-verified-sender: ok (group=${params.groupId} jobs=${jobs.length} invalidCount=${invalidCount})`
62271
- );
62272
- return { status: "ok", jobs, invalidCount };
62273
- }
62274
- function listJobsByVerifiedSenderHelpAdvertisesCommand(helpText) {
62275
- if (typeof helpText !== "string" || helpText.length === 0) {
62276
- return false;
62277
- }
62278
- const lower = helpText.toLowerCase();
62279
- if (/unknown command|unrecognized|not a valid command|no such (sub)?command|command not found/.test(lower)) {
62280
- return false;
62281
- }
62282
- return lower.includes(LIST_JOBS_BY_VERIFIED_SENDER_HELP_TOKEN);
62283
- }
62284
- function maybeRecommendListJobsUpgrade(capability) {
62285
- if (capability.supported || capabilityRecommendedPairs.has(CAPABILITY_UPGRADE_PAIR_KEY)) {
62286
- return;
62287
- }
62288
- warnWithTimestamp(
62289
- `[onchainos] list-jobs-by-verified-sender unsupported (reason=${capability.reason}); ${CAPABILITY_UPGRADE_REASON}`
62290
- );
62291
- const hook = XmtpService.getInstance().tools?.notifyUpgradeRecommendation;
62292
- if (!hook) {
62293
- return;
62294
- }
62295
- capabilityRecommendedPairs.add(CAPABILITY_UPGRADE_PAIR_KEY);
62296
- try {
62297
- void Promise.resolve(
62298
- hook({
62299
- component: "onchainos",
62300
- capability: LIST_JOBS_BY_VERIFIED_SENDER_HELP_TOKEN,
62301
- reason: CAPABILITY_UPGRADE_REASON
62302
- })
62303
- ).catch((err2) => {
62304
- warnWithTimestamp("[onchainos] list-jobs-by-verified-sender upgrade notice async delivery failed:", err2);
62305
- });
62306
- } catch (err2) {
62307
- warnWithTimestamp("[onchainos] list-jobs-by-verified-sender upgrade notice threw:", err2);
62308
- }
62309
- }
62310
- function detectListJobsByVerifiedSenderSupport(execFn = exec) {
62311
- if (!capabilityProbe2) {
62312
- capabilityProbe2 = (async () => {
62313
- try {
62314
- const { stdout, stderr } = await execFn([
62315
- "agent",
62316
- "list-jobs-by-verified-sender",
62317
- "--help"
62318
- ]);
62319
- const supported = listJobsByVerifiedSenderHelpAdvertisesCommand(`${stdout}
62320
- ${stderr}`);
62321
- logWithTimestamp(
62322
- `[onchainos] list-jobs-by-verified-sender capability: ${supported ? "supported" : "unsupported_cli_capability"}`
62323
- );
62324
- return supported ? { supported: true } : { supported: false, reason: "unsupported_cli_capability" };
62325
- } catch (err2) {
62326
- logWithTimestamp(
62327
- "[onchainos] list-jobs-by-verified-sender --help probe failed; treating as onchainos_upgrade_required:",
62328
- err2
62329
- );
62330
- return { supported: false, reason: "onchainos_upgrade_required" };
62331
- }
62332
- })().then((capability) => {
62333
- maybeRecommendListJobsUpgrade(capability);
62334
- return capability;
62335
- });
62336
- }
62337
- return capabilityProbe2;
62338
- }
62339
- async function listJobsByVerifiedSenderWithCapability(params, execFn = exec) {
62340
- const capability = await detectListJobsByVerifiedSenderSupport(execFn);
62341
- if (!capability.supported) {
62342
- warnWithTimestamp(
62343
- `[onchainos] list-jobs-by-verified-sender skipped: CLI capability unavailable (reason=${capability.reason}) group=${params.groupId} sender=${redactLogValue(params.senderInboxId)}`
62344
- );
62345
- return { status: "fail_closed", jobs: [], invalidCount: 0, reason: capability.reason };
62346
- }
62347
- return listJobsByVerifiedSender(params, execFn);
62348
- }
62349
- var KNOWN_ERROR_CODES2, LIST_JOBS_BY_VERIFIED_SENDER_HELP_TOKEN, CAPABILITY_UPGRADE_PAIR_KEY, CAPABILITY_UPGRADE_REASON, capabilityProbe2, capabilityRecommendedPairs;
62350
- var init_list_jobs_by_verified_sender = __esm({
62351
- "../core/src/xmtp-sdk/onchainos/list-jobs-by-verified-sender.ts"() {
62352
- "use strict";
62353
- init_log();
62354
- init_sentry_logger();
62355
- init_bin();
62356
- init_cli_response();
62357
- init_session_expired();
62358
- init_xmtp_sdk();
62359
- KNOWN_ERROR_CODES2 = {
62360
- "130100079": "sender_not_authorized",
62361
- "130100073": "sender_not_eligible"
62362
- };
62363
- LIST_JOBS_BY_VERIFIED_SENDER_HELP_TOKEN = "list-jobs-by-verified-sender";
62364
- CAPABILITY_UPGRADE_PAIR_KEY = "onchainos:list-jobs-by-verified-sender";
62365
- CAPABILITY_UPGRADE_REASON = "OnchainOS does not support `agent list-jobs-by-verified-sender`; the V2 subscription shared-group inbound path is fail-closed until OnchainOS is upgraded. Upgrade the OnchainOS CLI, then re-run.";
62366
- capabilityProbe2 = null;
62367
- capabilityRecommendedPairs = /* @__PURE__ */ new Set();
62368
- }
62369
- });
62370
-
62371
62410
  // ../core/src/xmtp-sdk/onchainos/signer.ts
62372
62411
  async function onchainosSign(agentId, keyUuid, walletAddress, message) {
62373
62412
  logWithTimestamp(
@@ -63037,7 +63076,7 @@ function buildXmtpMessageSentExtra(params) {
63037
63076
  function logXmtpMessageSent(params) {
63038
63077
  logger.info(LogEvent.MESSAGE_SENT, buildXmtpMessageSentExtra(params));
63039
63078
  }
63040
- function isPlainObject2(value) {
63079
+ function isPlainObject(value) {
63041
63080
  return !!value && typeof value === "object" && !Array.isArray(value);
63042
63081
  }
63043
63082
  function readString3(value) {
@@ -63223,8 +63262,8 @@ function buildDmReplyRaw(command, target, localAgentId) {
63223
63262
  });
63224
63263
  }
63225
63264
  function buildGroupReplyRaw(command, target, myXmtpAddress, localAgent) {
63226
- const payload = isPlainObject2(target.payload) ? target.payload : null;
63227
- const sender = isPlainObject2(payload?.sender) ? payload.sender : null;
63265
+ const payload = isPlainObject(target.payload) ? target.payload : null;
63266
+ const sender = isPlainObject(payload?.sender) ? payload.sender : null;
63228
63267
  return buildSendEnvelope({
63229
63268
  content: command.message,
63230
63269
  myXmtpAddress,
@@ -63821,8 +63860,8 @@ async function handleXmtpSendCommand(params) {
63821
63860
  payload: parseJson(rawText)
63822
63861
  };
63823
63862
  await store.append(command.jobId, stored, sessionAgentId);
63824
- const payloadObject = isPlainObject2(target.payload) ? target.payload : null;
63825
- const sender = isPlainObject2(payloadObject?.sender) ? payloadObject.sender : null;
63863
+ const payloadObject = isPlainObject(target.payload) ? target.payload : null;
63864
+ const sender = isPlainObject(payloadObject?.sender) ? payloadObject.sender : null;
63826
63865
  const remoteAgentId = readString3(sender?.agentId);
63827
63866
  await notifyAgentMessageToUserAttention({
63828
63867
  direction: "outbound" /* OUTBOUND */,
@@ -63875,438 +63914,6 @@ var init_xmtp_send = __esm({
63875
63914
  }
63876
63915
  });
63877
63916
 
63878
- // ../core/src/a2a/subscription-types.ts
63879
- var SUBSCRIPTION_MESSAGE_TYPE;
63880
- var init_subscription_types = __esm({
63881
- "../core/src/a2a/subscription-types.ts"() {
63882
- "use strict";
63883
- SUBSCRIPTION_MESSAGE_TYPE = "a2a-subscription-push";
63884
- }
63885
- });
63886
-
63887
- // ../core/src/a2a/subscription-envelope.ts
63888
- function isPlainObject3(value) {
63889
- return !!value && typeof value === "object" && !Array.isArray(value);
63890
- }
63891
- function buildSubscriptionEnvelope(params) {
63892
- const envelope = {
63893
- msgType: SUBSCRIPTION_MESSAGE_TYPE,
63894
- content: params.content,
63895
- senderAgentId: params.senderAgentId
63896
- };
63897
- if (params.deliveryId) {
63898
- envelope.deliveryId = params.deliveryId;
63899
- }
63900
- return JSON.stringify(envelope);
63901
- }
63902
- function isSubscriptionEnvelope(payload) {
63903
- return isPlainObject3(payload) && payload.msgType === SUBSCRIPTION_MESSAGE_TYPE;
63904
- }
63905
- function isNonEmptyString2(value) {
63906
- return typeof value === "string" && value.length > 0;
63907
- }
63908
- function readSubscriptionEnvelope(payload) {
63909
- if (!isSubscriptionEnvelope(payload)) {
63910
- return { ok: false, reason: "not_subscription" };
63911
- }
63912
- const record = payload;
63913
- if (!isNonEmptyString2(record.content)) {
63914
- return { ok: false, reason: "missing_content" };
63915
- }
63916
- const envelope = {
63917
- msgType: SUBSCRIPTION_MESSAGE_TYPE,
63918
- content: record.content,
63919
- senderAgentId: isNonEmptyString2(record.senderAgentId) ? record.senderAgentId : ""
63920
- };
63921
- if (isNonEmptyString2(record.deliveryId)) {
63922
- envelope.deliveryId = record.deliveryId;
63923
- }
63924
- return { ok: true, envelope };
63925
- }
63926
- var init_subscription_envelope = __esm({
63927
- "../core/src/a2a/subscription-envelope.ts"() {
63928
- "use strict";
63929
- init_subscription_types();
63930
- }
63931
- });
63932
-
63933
- // src/subscription-telemetry.ts
63934
- function definedExtras(input) {
63935
- return Object.entries(input).reduce((out, [key, value]) => {
63936
- if (typeof value === "string" && value.trim().length > 0) {
63937
- out[key] = value.trim();
63938
- }
63939
- return out;
63940
- }, {});
63941
- }
63942
- function countExtras(input) {
63943
- return Object.entries(input).reduce((out, [key, value]) => {
63944
- if (typeof value === "number" && Number.isFinite(value)) {
63945
- out[key] = String(value);
63946
- }
63947
- return out;
63948
- }, {});
63949
- }
63950
- function buildSubscriptionSendExtras(input) {
63951
- return {
63952
- stage: input.checkpoint,
63953
- ...logFieldExtras({
63954
- checkpoint: input.checkpoint,
63955
- outcome: input.outcome ?? null,
63956
- transport: SUBSCRIPTION_TRANSPORT,
63957
- eventFamily: "outbound",
63958
- deliveryId: input.deliveryId ?? null
63959
- }),
63960
- ...definedExtras({
63961
- aspAgentId: input.aspAgentId,
63962
- conversationId: input.conversationId,
63963
- reason: input.reason
63964
- }),
63965
- ...countExtras({
63966
- targetCount: input.targetCount,
63967
- sentCount: input.sentCount,
63968
- failedCount: input.failedCount,
63969
- notGroupCount: input.notGroupCount
63970
- })
63971
- };
63972
- }
63973
- function buildSubscriptionInboundExtras(input) {
63974
- return {
63975
- stage: input.checkpoint,
63976
- ...logFieldExtras({
63977
- checkpoint: input.checkpoint,
63978
- outcome: input.outcome ?? null,
63979
- transport: SUBSCRIPTION_TRANSPORT,
63980
- eventFamily: "inbound",
63981
- deliveryId: input.deliveryId ?? null
63982
- }),
63983
- ...definedExtras({
63984
- userAgentId: input.userAgentId,
63985
- conversationId: input.conversationId,
63986
- messageId: input.messageId,
63987
- senderInboxId: input.senderInboxId,
63988
- reason: input.reason,
63989
- errorCode: input.errorCode
63990
- }),
63991
- ...countExtras({
63992
- returnedCount: input.returnedCount,
63993
- invalidCount: input.invalidCount,
63994
- ownedCount: input.ownedCount,
63995
- ignoredCount: input.ignoredCount
63996
- })
63997
- };
63998
- }
63999
- function buildSubscriptionJobExtras(input) {
64000
- return {
64001
- stage: input.checkpoint,
64002
- ...logFieldExtras({
64003
- checkpoint: input.checkpoint,
64004
- outcome: input.outcome,
64005
- transport: SUBSCRIPTION_TRANSPORT,
64006
- eventFamily: "dispatch",
64007
- deliveryId: input.deliveryId ?? null
64008
- }),
64009
- ...definedExtras({
64010
- jobId: input.jobId,
64011
- userAgentId: input.userAgentId,
64012
- aspAgentId: input.aspAgentId,
64013
- conversationId: input.conversationId,
64014
- reason: input.reason
64015
- }),
64016
- ...countExtras({
64017
- total: input.total,
64018
- started: input.started,
64019
- failed: input.failed,
64020
- skippedDuplicate: input.skippedDuplicate
64021
- })
64022
- };
64023
- }
64024
- var SUBSCRIPTION_TRANSPORT;
64025
- var init_subscription_telemetry = __esm({
64026
- "src/subscription-telemetry.ts"() {
64027
- "use strict";
64028
- init_log_fields();
64029
- SUBSCRIPTION_TRANSPORT = "xmtp";
64030
- }
64031
- });
64032
-
64033
- // src/subscription-push.ts
64034
- function describeError(err2) {
64035
- if (err2 instanceof Error) {
64036
- return err2.message;
64037
- }
64038
- if (typeof err2 === "string" && err2.length > 0) {
64039
- return err2;
64040
- }
64041
- return "unknown error";
64042
- }
64043
- async function requireXmtpClient2(service, address) {
64044
- const existing = service.getClientByAddress(address);
64045
- if (existing) {
64046
- return existing;
64047
- }
64048
- await service.refreshAgents();
64049
- const refreshed = service.getClientByAddress(address);
64050
- if (!refreshed) {
64051
- throw new Error(`no XMTP client found for ${address}`);
64052
- }
64053
- return refreshed;
64054
- }
64055
- function resolveLocalAddress(service, myAgentId) {
64056
- const address = service.getAddressByAgentId(myAgentId);
64057
- if (!address) {
64058
- throw new Error(`No local communication address found for myAgentId=${myAgentId}`);
64059
- }
64060
- return address;
64061
- }
64062
- async function syncConversationsOnLocalMiss2(agent) {
64063
- const conversations = agent.client.conversations;
64064
- const existing = conversationSyncPromises2.get(conversations);
64065
- if (existing) {
64066
- return existing;
64067
- }
64068
- const pending = conversations.sync();
64069
- conversationSyncPromises2.set(conversations, pending);
64070
- try {
64071
- await pending;
64072
- } finally {
64073
- if (conversationSyncPromises2.get(conversations) === pending) {
64074
- conversationSyncPromises2.delete(conversations);
64075
- }
64076
- }
64077
- }
64078
- async function findGroupConversation2(agent, conversationId) {
64079
- let conversation = await agent.client.conversations.getConversationById(conversationId);
64080
- if (!(conversation instanceof Group)) {
64081
- await syncConversationsOnLocalMiss2(agent);
64082
- conversation = await agent.client.conversations.getConversationById(conversationId);
64083
- }
64084
- return conversation instanceof Group ? conversation : null;
64085
- }
64086
- async function sendSubscriptionToGroup(params) {
64087
- const { agent, conversationId, command, myXmtpAddress, senderRole } = params;
64088
- let conversation;
64089
- try {
64090
- conversation = await findGroupConversation2(agent, conversationId);
64091
- } catch (err2) {
64092
- return { conversationId, status: "not_group", error: describeError(err2) };
64093
- }
64094
- if (!conversation) {
64095
- return {
64096
- conversationId,
64097
- status: "not_group",
64098
- error: `conversationId=${conversationId} is not a Group (DM or unknown conversation)`
64099
- };
64100
- }
64101
- const envelope = buildSubscriptionEnvelope({
64102
- content: command.content,
64103
- senderAgentId: command.myAgentId,
64104
- deliveryId: command.deliveryId
64105
- });
64106
- const sendStartMs = Date.now();
64107
- try {
64108
- const xmtpMessageId = await conversation.sendText(envelope);
64109
- logXmtpMessageSent({
64110
- xmtpMessageId,
64111
- conversationId,
64112
- // V2 has no job dimension (FR-1) — deliveryId (or a fixed marker)
64113
- // stands in for the telemetry taxonomy's jobId slot.
64114
- jobId: command.deliveryId ?? "subscription-push",
64115
- chatType: "group",
64116
- component: "node_xmtp_subscription_push",
64117
- myXmtpAddress,
64118
- senderAgentId: command.myAgentId,
64119
- role: senderRole,
64120
- sendDurationMs: Date.now() - sendStartMs
64121
- });
64122
- const messageId = typeof xmtpMessageId === "string" && xmtpMessageId.length > 0 ? xmtpMessageId : String(xmtpMessageId);
64123
- logWithTimestamp(
64124
- `[okx-agent-task] subscription-push sent conversationId=${conversationId} deliveryId=${command.deliveryId ?? "(none)"}`
64125
- );
64126
- return { conversationId, status: "sent", messageId };
64127
- } catch (err2) {
64128
- logWithTimestamp(
64129
- `[okx-agent-task] subscription-push failed conversationId=${conversationId} deliveryId=${command.deliveryId ?? "(none)"}`
64130
- );
64131
- return { conversationId, status: "failed", error: describeError(err2) };
64132
- }
64133
- }
64134
- function summarizeSubscriptionPush(results) {
64135
- let sentCount = 0;
64136
- let failedCount = 0;
64137
- for (const result of results) {
64138
- if (result.status === "sent") {
64139
- sentCount++;
64140
- } else {
64141
- failedCount++;
64142
- }
64143
- }
64144
- return { conversationCount: results.length, sentCount, failedCount };
64145
- }
64146
- async function handleSubscriptionPushCommand(params) {
64147
- const { command, service } = params;
64148
- const aspAgentId = command.myAgentId;
64149
- const deliveryId = command.deliveryId;
64150
- const conversationIds = command.conversationIds ?? [];
64151
- logger.info(
64152
- LogEvent.SUBSCRIPTION_PUSH_STARTED,
64153
- buildSubscriptionSendExtras({
64154
- checkpoint: "outbound/subscription_push_started",
64155
- aspAgentId,
64156
- deliveryId,
64157
- targetCount: conversationIds.length
64158
- })
64159
- );
64160
- if (conversationIds.length === 0) {
64161
- logger.info(
64162
- LogEvent.SUBSCRIPTION_PUSH_EMPTY_TARGETS,
64163
- buildSubscriptionSendExtras({
64164
- checkpoint: "outbound/subscription_push_empty_targets",
64165
- outcome: "ignored",
64166
- aspAgentId,
64167
- deliveryId,
64168
- targetCount: 0,
64169
- reason: "empty_targets"
64170
- })
64171
- );
64172
- return { conversationCount: 0, sentCount: 0, failedCount: 0, results: [] };
64173
- }
64174
- const myXmtpAddress = resolveLocalAddress(service, command.myAgentId);
64175
- const agent = await requireXmtpClient2(service, myXmtpAddress);
64176
- const senderRole = service.getAgentByAddress?.(myXmtpAddress)?.role;
64177
- logger.info(
64178
- LogEvent.SUBSCRIPTION_PUSH_TARGETS_RESOLVED,
64179
- buildSubscriptionSendExtras({
64180
- checkpoint: "outbound/subscription_push_targets_resolved",
64181
- aspAgentId,
64182
- deliveryId,
64183
- targetCount: conversationIds.length
64184
- })
64185
- );
64186
- const settled = await parallelMap(
64187
- conversationIds,
64188
- (conversationId) => sendSubscriptionToGroup({
64189
- agent,
64190
- conversationId,
64191
- command,
64192
- myXmtpAddress,
64193
- senderRole
64194
- }),
64195
- DEFAULT_CONCURRENCY
64196
- );
64197
- const results = settled.map((outcome, index2) => {
64198
- if (outcome.status === "fulfilled") {
64199
- return outcome.value;
64200
- }
64201
- return {
64202
- conversationId: conversationIds[index2],
64203
- status: "failed",
64204
- error: describeError(outcome.reason)
64205
- };
64206
- });
64207
- for (const result of results) {
64208
- if (result.status === "sent") {
64209
- continue;
64210
- }
64211
- logger.error(
64212
- LogEvent.SUBSCRIPTION_PUSH_TARGET_FAILED,
64213
- void 0,
64214
- buildSubscriptionSendExtras({
64215
- checkpoint: "outbound/subscription_push_target_failed",
64216
- outcome: "failed",
64217
- aspAgentId,
64218
- deliveryId,
64219
- conversationId: result.conversationId,
64220
- reason: result.status
64221
- })
64222
- );
64223
- }
64224
- const summary = summarizeSubscriptionPush(results);
64225
- const summaryOutcome = summary.failedCount === 0 ? "success" : summary.sentCount > 0 ? "partial" : "failed";
64226
- logger.info(
64227
- LogEvent.SUBSCRIPTION_PUSH_SUMMARY,
64228
- buildSubscriptionSendExtras({
64229
- checkpoint: "outbound/subscription_push_summary",
64230
- outcome: summaryOutcome,
64231
- aspAgentId,
64232
- deliveryId,
64233
- targetCount: summary.conversationCount,
64234
- sentCount: summary.sentCount,
64235
- failedCount: summary.failedCount
64236
- })
64237
- );
64238
- return { ...summary, results };
64239
- }
64240
- var DEFAULT_CONCURRENCY, conversationSyncPromises2;
64241
- var init_subscription_push = __esm({
64242
- "src/subscription-push.ts"() {
64243
- "use strict";
64244
- init_log();
64245
- init_sentry_logger();
64246
- init_dist2();
64247
- init_subscription_envelope();
64248
- init_concurrency();
64249
- init_xmtp_send();
64250
- init_subscription_telemetry();
64251
- DEFAULT_CONCURRENCY = 20;
64252
- conversationSyncPromises2 = /* @__PURE__ */ new WeakMap();
64253
- }
64254
- });
64255
-
64256
- // src/ai-run-log-path.ts
64257
- function buildRunLogFileName(request) {
64258
- const source = request.source;
64259
- const sessionKey = request.sessionKey;
64260
- const messageId = request.messageId;
64261
- const jobId = request.jobId;
64262
- const safeMessageId = encodeURIComponent(messageId);
64263
- const prefix = source === "job-dispatch" ? "ai-" : "ai-session-";
64264
- const encodedContext = source === "job-dispatch" ? encodeURIComponent(jobId ?? "unknown") : encodeURIComponent(sessionKey);
64265
- const readable = `${prefix}${encodedContext}-${safeMessageId}.log`;
64266
- if (Buffer.byteLength(readable, "utf8") <= RUN_LOG_FILENAME_MAX_BYTES) {
64267
- return readable;
64268
- }
64269
- const readableWithoutExt = readable.slice(0, -".log".length);
64270
- const digest = (0, import_node_crypto13.createHash)("sha256").update(readableWithoutExt).digest("hex").slice(0, 24);
64271
- const suffix = `-${digest}.log`;
64272
- const budgetForContext = RUN_LOG_FILENAME_MAX_BYTES - Buffer.byteLength(prefix, "utf8") - Buffer.byteLength(suffix, "utf8");
64273
- const contextHead = truncateToUtf8ByteBudgetSafe(encodedContext, Math.max(budgetForContext, 0));
64274
- return `${prefix}${contextHead}${suffix}`;
64275
- }
64276
- function truncateToUtf8ByteBudgetSafe(encoded, maxBytes) {
64277
- if (maxBytes <= 0) {
64278
- return "";
64279
- }
64280
- if (Buffer.byteLength(encoded, "utf8") <= maxBytes) {
64281
- return encoded;
64282
- }
64283
- let candidateLength = Math.min(maxBytes, encoded.length);
64284
- while (candidateLength > 0) {
64285
- const candidate = encoded.slice(0, candidateLength);
64286
- if (!endsWithSplitEscape(candidate)) {
64287
- return candidate;
64288
- }
64289
- candidateLength -= 1;
64290
- }
64291
- return "";
64292
- }
64293
- function endsWithSplitEscape(value) {
64294
- const percentIndex = value.lastIndexOf("%");
64295
- if (percentIndex === -1) {
64296
- return false;
64297
- }
64298
- const tail = value.length - percentIndex - 1;
64299
- return tail < 2;
64300
- }
64301
- var import_node_crypto13, RUN_LOG_FILENAME_MAX_BYTES;
64302
- var init_ai_run_log_path = __esm({
64303
- "src/ai-run-log-path.ts"() {
64304
- "use strict";
64305
- import_node_crypto13 = require("node:crypto");
64306
- RUN_LOG_FILENAME_MAX_BYTES = 240;
64307
- }
64308
- });
64309
-
64310
63917
  // src/ai-adapter.ts
64311
63918
  function buildClaudeAllowedToolsArgs() {
64312
63919
  return ["--allowedTools", ...CLAUDE_ALLOWED_TOOLS];
@@ -66154,7 +65761,7 @@ function normalizeAttentionProvider(provider) {
66154
65761
  return provider === "unknown" ? null : provider;
66155
65762
  }
66156
65763
  function stableHash2(value) {
66157
- return (0, import_node_crypto14.createHash)("sha256").update(value).digest("hex").slice(0, 24);
65764
+ return (0, import_node_crypto13.createHash)("sha256").update(value).digest("hex").slice(0, 24);
66158
65765
  }
66159
65766
  function formatCommand(args) {
66160
65767
  return args.map(shellQuote).join(" ");
@@ -66178,18 +65785,17 @@ function shortenJobId2(jobId) {
66178
65785
  }
66179
65786
  return `${jobId.slice(0, 6)}\u2026${jobId.slice(-4)}`;
66180
65787
  }
66181
- var import_node_crypto14, import_node_fs26, import_promises12, import_node_os9, import_node_path31, AiRunner, CODEX_TOOL_FAILURE_MARKER;
65788
+ var import_node_crypto13, import_node_fs26, import_promises12, import_node_os9, import_node_path31, AiRunner, CODEX_TOOL_FAILURE_MARKER;
66182
65789
  var init_ai_runner = __esm({
66183
65790
  "src/ai-runner.ts"() {
66184
65791
  "use strict";
66185
65792
  init_log();
66186
- import_node_crypto14 = require("node:crypto");
65793
+ import_node_crypto13 = require("node:crypto");
66187
65794
  import_node_fs26 = require("node:fs");
66188
65795
  import_promises12 = require("node:fs/promises");
66189
65796
  import_node_os9 = require("node:os");
66190
65797
  import_node_path31 = require("node:path");
66191
65798
  init_ai_command();
66192
- init_ai_run_log_path();
66193
65799
  init_paths();
66194
65800
  init_file_store();
66195
65801
  init_ai_provider();
@@ -66817,15 +66423,13 @@ var init_ai_runner = __esm({
66817
66423
  );
66818
66424
  }
66819
66425
  buildRunLogPath(request) {
66820
- return (0, import_node_path31.join)(
66821
- this.logsDir,
66822
- buildRunLogFileName({
66823
- source: request.source,
66824
- sessionKey: request.sessionKey,
66825
- messageId: request.messageId,
66826
- jobId: request.jobId
66827
- })
66828
- );
66426
+ const safeMessageId = encodeURIComponent(request.messageId);
66427
+ if (request.source === "job-dispatch") {
66428
+ const safeJobId = encodeURIComponent(request.jobId ?? "unknown");
66429
+ return (0, import_node_path31.join)(this.logsDir, `ai-${safeJobId}-${safeMessageId}.log`);
66430
+ }
66431
+ const safeSessionKey = encodeURIComponent(request.sessionKey);
66432
+ return (0, import_node_path31.join)(this.logsDir, `ai-session-${safeSessionKey}-${safeMessageId}.log`);
66829
66433
  }
66830
66434
  readRunAiSessionId(provider, request, file, sessionMeta) {
66831
66435
  if (request.source === "job-dispatch" && request.jobId) {
@@ -67850,9 +67454,6 @@ function commandSentryExtra(command, extra = {}) {
67850
67454
  base.myAgentId = command.myAgentId ?? "";
67851
67455
  base.toAgentId = command.toAgentId ?? "";
67852
67456
  base.toXmtpAddress = command.toXmtpAddress ?? "";
67853
- } else if (command.type === "xmtp-subscription-push") {
67854
- base.myAgentId = command.myAgentId;
67855
- base.conversationCount = command.conversationIds.length;
67856
67457
  } else if (command.type === "xmtp-deny-pending-conversation") {
67857
67458
  base.groupId = command.groupId;
67858
67459
  base.jobId = command.jobId ?? "";
@@ -68208,10 +67809,6 @@ async function handleDaemonCommand(command, params) {
68208
67809
  });
68209
67810
  return { ok: true, messageId: result.messageId };
68210
67811
  }
68211
- if (command.type === "xmtp-subscription-push") {
68212
- const result = await handleSubscriptionPushCommand({ command, service: params.service });
68213
- return { ok: true, payload: result };
68214
- }
68215
67812
  if (command.type === "xmtp-get-pending-list") {
68216
67813
  return { ok: true, payload: await listPendingTaskRequests(params) };
68217
67814
  }
@@ -68294,12 +67891,12 @@ async function listPendingTaskRequests(params) {
68294
67891
  }
68295
67892
  async function denyPendingConversationsByJobId(jobId, params, content2) {
68296
67893
  const pending = await listPendingTaskRequests(params);
68297
- const matched = pending.filter((item) => isPlainObject4(item) && readString4(item.jobId) === jobId);
67894
+ const matched = pending.filter((item) => isPlainObject2(item) && readString4(item.jobId) === jobId);
68298
67895
  const clientAddresses = [...params.service.getClients().keys()];
68299
67896
  const targetsByKey = collectDenyTargetsFromPending(matched, clientAddresses);
68300
67897
  const groupIds = /* @__PURE__ */ new Set();
68301
67898
  for (const item of matched) {
68302
- if (!isPlainObject4(item)) {
67899
+ if (!isPlainObject2(item)) {
68303
67900
  continue;
68304
67901
  }
68305
67902
  const groupId = readString4(item.groupId);
@@ -68326,7 +67923,7 @@ async function denyPendingConversationsByJobId(jobId, params, content2) {
68326
67923
  function collectDenyTargetsFromPending(pending, fallbackAddresses) {
68327
67924
  const targetsByKey = /* @__PURE__ */ new Map();
68328
67925
  for (const item of pending) {
68329
- if (!isPlainObject4(item)) {
67926
+ if (!isPlainObject2(item)) {
68330
67927
  continue;
68331
67928
  }
68332
67929
  const groupId = readString4(item.groupId);
@@ -68351,7 +67948,7 @@ ${groupId}`, {
68351
67948
  async function resolvePendingTargetsByGroupId(groupId, addresses, params) {
68352
67949
  const addressSet = addresses ? new Set(addresses) : null;
68353
67950
  const pending = (await listPendingTaskRequests(params)).filter((item) => {
68354
- if (!isPlainObject4(item) || readString4(item.groupId) !== groupId) {
67951
+ if (!isPlainObject2(item) || readString4(item.groupId) !== groupId) {
68355
67952
  return false;
68356
67953
  }
68357
67954
  const address = readString4(item.myXmtpAddress);
@@ -68471,7 +68068,7 @@ function filterKnownPendingGroups(items, sessionStore) {
68471
68068
  return items;
68472
68069
  }
68473
68070
  return items.filter((item) => {
68474
- if (!isPlainObject4(item)) {
68071
+ if (!isPlainObject2(item)) {
68475
68072
  return true;
68476
68073
  }
68477
68074
  const jobId = readString4(item.jobId);
@@ -68482,7 +68079,7 @@ function filterKnownPendingGroups(items, sessionStore) {
68482
68079
  return sessionStore.querySessions({ jobId, toAgentId, limit: 1 }).length === 0;
68483
68080
  });
68484
68081
  }
68485
- function isPlainObject4(value) {
68082
+ function isPlainObject2(value) {
68486
68083
  return !!value && typeof value === "object" && !Array.isArray(value);
68487
68084
  }
68488
68085
  function readString4(value) {
@@ -68616,10 +68213,6 @@ function formatCommandCompleted(command, result) {
68616
68213
  if (command.type === "xmtp-send") {
68617
68214
  return `[okx-agent-task] xmtp-send command completed id=${command.id} job=${command.jobId} messageId=${result.messageId}`;
68618
68215
  }
68619
- if (command.type === "xmtp-subscription-push") {
68620
- const payload = isPlainObject4(result.payload) ? result.payload : {};
68621
- return `[okx-agent-task] xmtp-subscription-push command completed id=${command.id} deliveryId=${command.deliveryId ?? "(none)"} conversationCount=${payload.conversationCount ?? 0} sentCount=${payload.sentCount ?? 0} failedCount=${payload.failedCount ?? 0}`;
68622
- }
68623
68216
  if (command.type === "xmtp-get-pending-list") {
68624
68217
  return `[okx-agent-task] xmtp-get-pending-list command completed id=${command.id}`;
68625
68218
  }
@@ -68640,8 +68233,8 @@ function formatCommandCompleted(command, result) {
68640
68233
  return `[okx-agent-task] xmtp-refresh-agents command completed id=${command.id}`;
68641
68234
  }
68642
68235
  function formatXmtpDebugCompleted(command, payload) {
68643
- const record = isPlainObject4(payload) ? payload : {};
68644
- const summary = isPlainObject4(record.summary) ? record.summary : {};
68236
+ const record = isPlainObject2(payload) ? payload : {};
68237
+ const summary = isPlainObject2(record.summary) ? record.summary : {};
68645
68238
  const count = command.action === "group-list" ? record.groupCount ?? 0 : command.action === "group-inspect" ? record.conversationFound ? 1 : 0 : summary.matchedCount ?? 0;
68646
68239
  return `[okx-agent-task] xmtp-debug command completed id=${command.id} action=${command.action} rows=${String(count)}`;
68647
68240
  }
@@ -68802,7 +68395,6 @@ var init_command_processor = __esm({
68802
68395
  init_command_store();
68803
68396
  init_invalid_message_store();
68804
68397
  init_xmtp_send();
68805
- init_subscription_push();
68806
68398
  init_ai_runner();
68807
68399
  init_session_store();
68808
68400
  init_ai_provider();
@@ -68830,7 +68422,7 @@ async function enqueueAiJobMessageDispatch(params) {
68830
68422
  await commands.submit(command);
68831
68423
  return command;
68832
68424
  }
68833
- async function enqueueAiSessionMessageDispatchIdempotent(params) {
68425
+ async function enqueueAiSessionMessageDispatch(params) {
68834
68426
  const commands = params.commands ?? new CommandStore(params.homeDir);
68835
68427
  const messageId = params.event.messageId ?? `session-${Date.now()}`;
68836
68428
  const command = commands.createAiSessionMessageDispatchCommand({
@@ -68840,21 +68432,7 @@ async function enqueueAiSessionMessageDispatchIdempotent(params) {
68840
68432
  jobId: params.event.jobId,
68841
68433
  agentId: params.event.agentId
68842
68434
  });
68843
- const idempotencyKey = params.event.idempotencyKey?.trim();
68844
- if (idempotencyKey) {
68845
- const dbKey = `${command.sessionKey}
68846
- ${command.messageId}`;
68847
- const result = await commands.submitIdempotent(command, dbKey);
68848
- return {
68849
- command: result.created ? command : { ...command, id: result.commandId },
68850
- created: result.created
68851
- };
68852
- }
68853
68435
  await commands.submit(command);
68854
- return { command, created: true };
68855
- }
68856
- async function enqueueAiSessionMessageDispatch(params) {
68857
- const { command } = await enqueueAiSessionMessageDispatchIdempotent(params);
68858
68436
  return command;
68859
68437
  }
68860
68438
  var init_ai_dispatch_queue = __esm({
@@ -68864,156 +68442,6 @@ var init_ai_dispatch_queue = __esm({
68864
68442
  }
68865
68443
  });
68866
68444
 
68867
- // src/subscription-inbound.ts
68868
- function decideSubscriptionGate(input) {
68869
- if (!isSubscriptionEnvelope(input.payload)) {
68870
- return { kind: "not_subscription" };
68871
- }
68872
- if (input.chatType === "dm") {
68873
- return { kind: "drop", reason: "dm_subscription" };
68874
- }
68875
- if (input.chatType === "unknown") {
68876
- return { kind: "drop", reason: "unknown_conversation_subscription" };
68877
- }
68878
- const read = readSubscriptionEnvelope(input.payload);
68879
- if (!read.ok) {
68880
- return { kind: "drop", reason: "invalid_envelope", detail: read.reason };
68881
- }
68882
- if (!input.senderInboxId) {
68883
- return { kind: "drop", reason: "missing_sender_inbox_id" };
68884
- }
68885
- return { kind: "v2", envelope: read.envelope };
68886
- }
68887
- function partitionSubscriptionJobsByOwner(input) {
68888
- const owned = [];
68889
- for (const job of input.jobs) {
68890
- if (job.userAgentId === input.localAgentId) {
68891
- owned.push(job);
68892
- }
68893
- }
68894
- return {
68895
- owned,
68896
- returnedCount: input.jobs.length,
68897
- ownedCount: owned.length,
68898
- ignoredCount: input.jobs.length - owned.length
68899
- };
68900
- }
68901
- function normalizeSubscriptionJobs(input) {
68902
- const seen = /* @__PURE__ */ new Set();
68903
- const normalized = [];
68904
- for (const job of input.jobs) {
68905
- const dedupKey = `${job.jobId}|${job.userAgentId}|${job.aspAgentId}`;
68906
- if (seen.has(dedupKey)) {
68907
- continue;
68908
- }
68909
- seen.add(dedupKey);
68910
- normalized.push({
68911
- jobId: job.jobId,
68912
- userAgentId: job.userAgentId,
68913
- aspAgentId: job.aspAgentId,
68914
- msgType: SUBSCRIPTION_MESSAGE_TYPE,
68915
- content: input.envelope.content,
68916
- ...input.envelope.deliveryId ? { deliveryId: input.envelope.deliveryId } : {},
68917
- conversationId: input.conversationId,
68918
- senderInboxId: input.senderInboxId
68919
- });
68920
- }
68921
- return normalized;
68922
- }
68923
- function subscriptionIdempotencyKey(input) {
68924
- const messageDimension = input.deliveryId || input.messageId;
68925
- return `${input.myXmtpAddress}|${messageDimension}|${input.jobId}`;
68926
- }
68927
- function tryMarkSubscriptionTarget(key) {
68928
- if (subscriptionTargets.has(key)) {
68929
- return false;
68930
- }
68931
- subscriptionTargets.set(key, Date.now());
68932
- while (subscriptionTargets.size > SUBSCRIPTION_TARGETS_MAX) {
68933
- const oldest = subscriptionTargets.keys().next().value;
68934
- if (oldest === void 0) {
68935
- break;
68936
- }
68937
- subscriptionTargets.delete(oldest);
68938
- }
68939
- return true;
68940
- }
68941
- function redactSenderInboxId(senderInboxId, left = 8, right = 6) {
68942
- if (!senderInboxId) {
68943
- return "(none)";
68944
- }
68945
- if (senderInboxId.length <= left + right + 1) {
68946
- return senderInboxId;
68947
- }
68948
- return `${senderInboxId.slice(0, left)}\u2026${senderInboxId.slice(-right)}`;
68949
- }
68950
- async function fanOutSubscriptionJobs(input) {
68951
- const { jobs, myXmtpAddress, messageId, dispatch, concurrency } = input;
68952
- const total = jobs.length;
68953
- const capacityAnomaly = total > SUBSCRIPTION_FANOUT_MAX;
68954
- if (capacityAnomaly) {
68955
- logWithTimestamp(
68956
- `[okx-agent-task:${myXmtpAddress}] subscription fan-out capacity anomaly: jobs=${total} max=${SUBSCRIPTION_FANOUT_MAX} message=${messageId}`
68957
- );
68958
- }
68959
- const settled = await parallelMap(
68960
- jobs,
68961
- async (job) => {
68962
- const idempotencyKey = subscriptionIdempotencyKey({
68963
- myXmtpAddress,
68964
- deliveryId: job.deliveryId,
68965
- messageId,
68966
- jobId: job.jobId
68967
- });
68968
- if (subscriptionTargets.has(idempotencyKey)) {
68969
- return { key: idempotencyKey, outcome: "duplicate" };
68970
- }
68971
- const dispatchOutcome = await dispatch(job, idempotencyKey);
68972
- const isDurableDuplicate = !!dispatchOutcome && dispatchOutcome.created === false;
68973
- return { key: idempotencyKey, outcome: isDurableDuplicate ? "duplicate" : "started" };
68974
- },
68975
- concurrency ?? 20
68976
- );
68977
- let started = 0;
68978
- let failed = 0;
68979
- let skippedDuplicate = 0;
68980
- const results = [];
68981
- settled.forEach((result, idx) => {
68982
- const job = jobs[idx];
68983
- if (result.status === "rejected") {
68984
- failed += 1;
68985
- results.push({ jobId: job?.jobId ?? "(unknown)", outcome: "failed", error: result.reason });
68986
- logWithTimestamp(
68987
- `[okx-agent-task:${myXmtpAddress}] subscription dispatch failed message=${messageId} job=${job?.jobId ?? "(unknown)"} sender=${redactSenderInboxId(job?.senderInboxId)}:`,
68988
- result.reason
68989
- );
68990
- return;
68991
- }
68992
- tryMarkSubscriptionTarget(result.value.key);
68993
- if (result.value.outcome === "duplicate") {
68994
- skippedDuplicate += 1;
68995
- results.push({ jobId: job?.jobId ?? "(unknown)", outcome: "duplicate" });
68996
- } else {
68997
- started += 1;
68998
- results.push({ jobId: job?.jobId ?? "(unknown)", outcome: "started" });
68999
- }
69000
- });
69001
- return { total, started, failed, skippedDuplicate, capacityAnomaly, results };
69002
- }
69003
- var SUBSCRIPTION_TARGETS_MAX, subscriptionTargets, SUBSCRIPTION_FANOUT_MAX;
69004
- var init_subscription_inbound = __esm({
69005
- "src/subscription-inbound.ts"() {
69006
- "use strict";
69007
- init_log();
69008
- init_concurrency();
69009
- init_subscription_types();
69010
- init_subscription_envelope();
69011
- SUBSCRIPTION_TARGETS_MAX = 2e3;
69012
- subscriptionTargets = /* @__PURE__ */ new Map();
69013
- SUBSCRIPTION_FANOUT_MAX = 250;
69014
- }
69015
- });
69016
-
69017
68445
  // src/message-handler.ts
69018
68446
  function tryMarkProcessed(messageId) {
69019
68447
  if (processedMessageIds.has(messageId)) {
@@ -69029,7 +68457,7 @@ function tryMarkProcessed(messageId) {
69029
68457
  }
69030
68458
  return true;
69031
68459
  }
69032
- function isPlainObject5(value) {
68460
+ function isPlainObject3(value) {
69033
68461
  return !!value && typeof value === "object" && !Array.isArray(value);
69034
68462
  }
69035
68463
  function safeStringify(value) {
@@ -69089,9 +68517,9 @@ function readNumber(value) {
69089
68517
  }
69090
68518
  function resolveSessionAgentId(params) {
69091
68519
  const { chatType, payload, myXmtpAddress } = params;
69092
- const payloadObject = isPlainObject5(payload) ? payload : null;
69093
- const sender = isPlainObject5(payloadObject?.sender) ? payloadObject.sender : null;
69094
- const message = isPlainObject5(payloadObject?.message) ? payloadObject.message : null;
68520
+ const payloadObject = isPlainObject3(payload) ? payload : null;
68521
+ const sender = isPlainObject3(payloadObject?.sender) ? payloadObject.sender : null;
68522
+ const message = isPlainObject3(payloadObject?.message) ? payloadObject.message : null;
69095
68523
  const localAgentId = XmtpService.getInstance().getAgentByAddress(myXmtpAddress)?.agentId ?? null;
69096
68524
  if (chatType === "group") {
69097
68525
  return readString5(sender?.agentId) ?? readString5(payloadObject?.receiverAgentId) ?? localAgentId;
@@ -69102,7 +68530,7 @@ function resolveSessionAgentId(params) {
69102
68530
  return localAgentId;
69103
68531
  }
69104
68532
  function routeMessage(chatType, parsed, payload) {
69105
- if (!parsed || !isPlainObject5(payload)) {
68533
+ if (!parsed || !isPlainObject3(payload)) {
69106
68534
  return {
69107
68535
  jobId: BACKUP_JOB_ID,
69108
68536
  route: "backup",
@@ -69114,7 +68542,7 @@ function routeMessage(chatType, parsed, payload) {
69114
68542
  return jobId ? { jobId, route: "job", routeReason: "group-job-id" } : { jobId: BACKUP_JOB_ID, route: "backup", routeReason: "group-missing-job-id" };
69115
68543
  }
69116
68544
  if (chatType === "dm") {
69117
- const message = isPlainObject5(payload.message) ? payload.message : null;
68545
+ const message = isPlainObject3(payload.message) ? payload.message : null;
69118
68546
  const nestedJobId = readString5(message?.jobId);
69119
68547
  const directJobId = readString5(payload.jobId);
69120
68548
  const jobId = nestedJobId ?? directJobId;
@@ -69127,10 +68555,10 @@ function routeMessage(chatType, parsed, payload) {
69127
68555
  return { jobId: BACKUP_JOB_ID, route: "backup", routeReason: "unknown-conversation" };
69128
68556
  }
69129
68557
  function extractDirectToUserNotification(payload) {
69130
- if (!isPlainObject5(payload)) {
68558
+ if (!isPlainObject3(payload)) {
69131
68559
  return { kind: "none" };
69132
68560
  }
69133
- const message = isPlainObject5(payload.message) ? payload.message : null;
68561
+ const message = isPlainObject3(payload.message) ? payload.message : null;
69134
68562
  if (message?.isDirectToUser !== true) {
69135
68563
  return { kind: "none" };
69136
68564
  }
@@ -69154,10 +68582,10 @@ function extractDirectToUserNotification(payload) {
69154
68582
  };
69155
68583
  }
69156
68584
  function extractSystemNotification(payload) {
69157
- if (!isPlainObject5(payload)) {
68585
+ if (!isPlainObject3(payload)) {
69158
68586
  return null;
69159
68587
  }
69160
- const message = isPlainObject5(payload.message) ? payload.message : null;
68588
+ const message = isPlainObject3(payload.message) ? payload.message : null;
69161
68589
  const isDirectCommunication = message?.isDirectCommunication === true;
69162
68590
  if (message?.source !== "system" && !isDirectCommunication) {
69163
68591
  return null;
@@ -69383,7 +68811,7 @@ function inboundStageExtras(stage, outcome) {
69383
68811
  })
69384
68812
  };
69385
68813
  }
69386
- function definedExtras2(input) {
68814
+ function definedExtras(input) {
69387
68815
  return Object.entries(input).reduce((out, [key, value]) => {
69388
68816
  const text = readString5(value);
69389
68817
  if (text) {
@@ -69415,7 +68843,7 @@ function buildInboundTerminalExtras(input) {
69415
68843
  originPlatform: classifyOriginPlatform({ env: process.env }),
69416
68844
  deliveryId: resolveDeliveryId({ messageId, jobId, sessionKey })
69417
68845
  }),
69418
- ...definedExtras2({
68846
+ ...definedExtras({
69419
68847
  messageId,
69420
68848
  conversationId,
69421
68849
  chatType,
@@ -69449,7 +68877,7 @@ function maybeNotifyInboundAgentMessage(params) {
69449
68877
  return;
69450
68878
  }
69451
68879
  const content2 = payloadObject.content;
69452
- const sender = isPlainObject5(payloadObject.sender) ? payloadObject.sender : null;
68880
+ const sender = isPlainObject3(payloadObject.sender) ? payloadObject.sender : null;
69453
68881
  const localAgent = XmtpService.getInstance().getAgentByAddress(params.deps.myXmtpAddress);
69454
68882
  void notifyAgentMessageToUserAttention({
69455
68883
  direction: "inbound" /* INBOUND */,
@@ -69480,11 +68908,11 @@ function stringifySystemPayload(payload) {
69480
68908
  }
69481
68909
  }
69482
68910
  function summarizePayloadForLog(payload) {
69483
- if (!isPlainObject5(payload)) {
68911
+ if (!isPlainObject3(payload)) {
69484
68912
  return "payload=non-object";
69485
68913
  }
69486
- const message = isPlainObject5(payload.message) ? payload.message : null;
69487
- const sender = isPlainObject5(payload.sender) ? payload.sender : null;
68914
+ const message = isPlainObject3(payload.message) ? payload.message : null;
68915
+ const sender = isPlainObject3(payload.sender) ? payload.sender : null;
69488
68916
  const jobId = readString5(payload.jobId) ?? readString5(message?.jobId);
69489
68917
  const parts = [
69490
68918
  readString5(payload.msgType) ? `type=${readString5(payload.msgType)}` : null,
@@ -69753,7 +69181,7 @@ async function verifyInboundA2AGroupMessage(params) {
69753
69181
  messageId,
69754
69182
  groupId,
69755
69183
  jobId,
69756
- agentId: readString5(isPlainObject5(payload.sender) ? payload.sender.agentId : null),
69184
+ agentId: readString5(isPlainObject3(payload.sender) ? payload.sender.agentId : null),
69757
69185
  localAgentId: myAgent?.agentId ?? null,
69758
69186
  senderAddress,
69759
69187
  senderInboxId,
@@ -69779,7 +69207,7 @@ async function verifyInboundA2AGroupMessage(params) {
69779
69207
  });
69780
69208
  return false;
69781
69209
  }
69782
- const sender = isPlainObject5(payload.sender) ? payload.sender : null;
69210
+ const sender = isPlainObject3(payload.sender) ? payload.sender : null;
69783
69211
  const senderAgentId = readString5(sender?.agentId);
69784
69212
  const senderRole = readNumber(sender?.role);
69785
69213
  if (!senderAgentId || !myAgent) {
@@ -69948,302 +69376,9 @@ ${failureMessage}`,
69948
69376
  }
69949
69377
  return true;
69950
69378
  }
69951
- async function processSubscriptionInbound(ctx, deps, input) {
69952
- const { decision, conversationId, messageId } = input;
69953
- const senderInboxId = ctx.message.senderInboxId ?? "";
69954
- if (decision.kind === "drop") {
69955
- logger.info(
69956
- LogEvent.SUBSCRIPTION_INBOUND_GATE_DROPPED,
69957
- buildSubscriptionInboundExtras({
69958
- checkpoint: "inbound/subscription_gate_dropped",
69959
- outcome: "dropped",
69960
- conversationId,
69961
- messageId,
69962
- senderInboxId: shortenLogValue(senderInboxId),
69963
- reason: decision.reason
69964
- })
69965
- );
69966
- logWithTimestamp(
69967
- `[okx-agent-task:${deps.myXmtpAddress}] subscription message dropped reason=${decision.reason}${decision.detail ? ` detail=${decision.detail}` : ""} conv=${conversationId} message=${shortenLogValue(messageId)} sender=${shortenLogValue(senderInboxId)}`
69968
- );
69969
- return true;
69970
- }
69971
- if (decision.kind === "not_subscription") {
69972
- return true;
69973
- }
69974
- const { envelope } = decision;
69975
- const agenticId = XmtpService.getInstance().getAgentByAddress(deps.myXmtpAddress)?.agentId;
69976
- if (!agenticId) {
69977
- logWithTimestamp(
69978
- `[okx-agent-task:${deps.myXmtpAddress}] subscription fail-closed: no local agenticId for ${deps.myXmtpAddress}, conv=${conversationId} message=${shortenLogValue(messageId)}`
69979
- );
69980
- logger.error(
69981
- LogEvent.SUBSCRIPTION_INBOUND_AGENTIC_ID_MISSING,
69982
- new Error("Subscription inbound: local agenticId not resolved"),
69983
- buildSubscriptionInboundExtras({
69984
- checkpoint: "inbound/subscription_agentic_id",
69985
- outcome: "blocked",
69986
- conversationId,
69987
- messageId,
69988
- senderInboxId: shortenLogValue(senderInboxId),
69989
- reason: "missing_agentic_id"
69990
- })
69991
- );
69992
- return true;
69993
- }
69994
- logWithTimestamp(
69995
- `[okx-agent-task:${deps.myXmtpAddress}] subscription received msgType=${envelope.msgType} conv=${conversationId} message=${shortenLogValue(messageId)} sender=${shortenLogValue(senderInboxId)}`
69996
- );
69997
- logger.info(
69998
- LogEvent.SUBSCRIPTION_INBOUND_RECEIVED,
69999
- buildSubscriptionInboundExtras({
70000
- checkpoint: "inbound/subscription_received",
70001
- userAgentId: agenticId,
70002
- conversationId,
70003
- messageId,
70004
- deliveryId: envelope.deliveryId,
70005
- senderInboxId: shortenLogValue(senderInboxId)
70006
- })
70007
- );
70008
- const listJobs = deps.listJobsByVerifiedSenderImpl ?? listJobsByVerifiedSenderWithCapability;
70009
- logger.info(
70010
- LogEvent.SUBSCRIPTION_JOBS_QUERY_STARTED,
70011
- buildSubscriptionInboundExtras({
70012
- checkpoint: "inbound/subscription_jobs_query_started",
70013
- userAgentId: agenticId,
70014
- conversationId,
70015
- messageId
70016
- })
70017
- );
70018
- const listJobsResult = await listJobs({
70019
- groupId: conversationId,
70020
- senderInboxId,
70021
- agenticId
70022
- });
70023
- logWithTimestamp(
70024
- `[okx-agent-task:${deps.myXmtpAddress}] subscription queried status=${listJobsResult.status} jobs=${listJobsResult.jobs.length} invalid=${listJobsResult.invalidCount} conv=${conversationId} message=${shortenLogValue(messageId)}`
70025
- );
70026
- if (listJobsResult.status === "fail_closed") {
70027
- logger.error(
70028
- LogEvent.SUBSCRIPTION_JOBS_FAIL_CLOSED,
70029
- new Error("Subscription inbound: listJobsByVerifiedSender fail-closed"),
70030
- buildSubscriptionInboundExtras({
70031
- checkpoint: "inbound/subscription_jobs_fail_closed",
70032
- outcome: "blocked",
70033
- userAgentId: agenticId,
70034
- conversationId,
70035
- messageId,
70036
- senderInboxId: shortenLogValue(senderInboxId),
70037
- reason: listJobsResult.reason ?? "unknown",
70038
- errorCode: listJobsResult.errorCode
70039
- })
70040
- );
70041
- return true;
70042
- }
70043
- if (listJobsResult.status === "empty") {
70044
- logWithTimestamp(
70045
- `[okx-agent-task:${deps.myXmtpAddress}] subscription empty: no verified jobs for conv=${conversationId} message=${shortenLogValue(messageId)}`
70046
- );
70047
- logger.info(
70048
- LogEvent.SUBSCRIPTION_JOBS_EMPTY,
70049
- buildSubscriptionInboundExtras({
70050
- checkpoint: "inbound/subscription_jobs_empty",
70051
- outcome: "ignored",
70052
- userAgentId: agenticId,
70053
- conversationId,
70054
- messageId,
70055
- reason: "jobs_empty",
70056
- returnedCount: 0,
70057
- invalidCount: listJobsResult.invalidCount
70058
- })
70059
- );
70060
- return true;
70061
- }
70062
- logger.info(
70063
- LogEvent.SUBSCRIPTION_JOBS_QUERY_SUCCEEDED,
70064
- buildSubscriptionInboundExtras({
70065
- checkpoint: "inbound/subscription_jobs_query_succeeded",
70066
- userAgentId: agenticId,
70067
- conversationId,
70068
- messageId,
70069
- returnedCount: listJobsResult.jobs.length,
70070
- invalidCount: listJobsResult.invalidCount
70071
- })
70072
- );
70073
- const ownership = partitionSubscriptionJobsByOwner({
70074
- jobs: listJobsResult.jobs,
70075
- localAgentId: agenticId
70076
- });
70077
- logWithTimestamp(
70078
- `[okx-agent-task:${deps.myXmtpAddress}] subscription routed receiver=${agenticId} returned=${ownership.returnedCount} localOwned=${ownership.ownedCount} ignored=${ownership.ignoredCount} conv=${conversationId} message=${shortenLogValue(messageId)}`
70079
- );
70080
- logger.info(
70081
- LogEvent.SUBSCRIPTION_INBOUND_OWNERSHIP_ROUTED,
70082
- buildSubscriptionInboundExtras({
70083
- checkpoint: "inbound/subscription_ownership_routed",
70084
- outcome: ownership.ownedCount === 0 ? "ignored" : null,
70085
- userAgentId: agenticId,
70086
- conversationId,
70087
- messageId,
70088
- returnedCount: ownership.returnedCount,
70089
- ownedCount: ownership.ownedCount,
70090
- ignoredCount: ownership.ignoredCount
70091
- })
70092
- );
70093
- if (ownership.ownedCount === 0) {
70094
- return true;
70095
- }
70096
- const normalizedJobs = normalizeSubscriptionJobs({
70097
- jobs: ownership.owned,
70098
- envelope,
70099
- conversationId,
70100
- senderInboxId
70101
- });
70102
- logWithTimestamp(
70103
- `[okx-agent-task:${deps.myXmtpAddress}] subscription expanded jobs=${normalizedJobs.length} conv=${conversationId} message=${shortenLogValue(messageId)}`
70104
- );
70105
- let senderXmtpAddress = null;
70106
- try {
70107
- const resolved = await ctx.getSenderAddress();
70108
- senderXmtpAddress = resolved ? resolved : null;
70109
- } catch (err2) {
70110
- logWithTimestamp(
70111
- `[okx-agent-task:${deps.myXmtpAddress}] subscription sender address unresolved (degrading to null) conv=${conversationId} message=${shortenLogValue(messageId)}:`,
70112
- err2
70113
- );
70114
- }
70115
- const fanoutResult = await fanOutSubscriptionJobs({
70116
- jobs: normalizedJobs,
70117
- myXmtpAddress: deps.myXmtpAddress,
70118
- messageId,
70119
- dispatch: async (job, idempotencyKey) => {
70120
- const sessionKey = buildSessionKey({
70121
- jobId: job.jobId,
70122
- myAgentId: job.userAgentId,
70123
- toAgentId: job.aspAgentId
70124
- });
70125
- deps.sessionStore?.upsertSession({
70126
- sessionKey,
70127
- jobId: job.jobId,
70128
- myAgentId: job.userAgentId,
70129
- toAgentId: job.aspAgentId,
70130
- groupId: job.conversationId,
70131
- myAgentXmtpAddress: deps.myXmtpAddress,
70132
- toAgentXmtpAddress: senderXmtpAddress
70133
- });
70134
- return deps.onSessionMessage?.({
70135
- sessionKey,
70136
- content: JSON.stringify({
70137
- msgType: job.msgType,
70138
- content: job.content,
70139
- ...job.deliveryId ? { deliveryId: job.deliveryId } : {},
70140
- jobId: job.jobId,
70141
- userAgentId: job.userAgentId,
70142
- aspAgentId: job.aspAgentId,
70143
- conversationId: job.conversationId,
70144
- senderInboxId: job.senderInboxId,
70145
- // Raw XMTP messageId preserved for sender/receiver telemetry join;
70146
- // the durable dedup identity is the idempotencyKey passed as this
70147
- // session-message's own `messageId` field below (M1 fix).
70148
- xmtpMessageId: messageId
70149
- }),
70150
- messageId: idempotencyKey,
70151
- // Durable exactly-once enqueue key (M2): the wiring persists via
70152
- // CommandStore.submitIdempotent keyed on sessionKey+messageId so a
70153
- // re-processed / replayed message never creates a second command.
70154
- idempotencyKey,
70155
- jobId: job.jobId,
70156
- agentId: job.userAgentId
70157
- });
70158
- }
70159
- });
70160
- logWithTimestamp(
70161
- `[okx-agent-task:${deps.myXmtpAddress}] subscription fan-out succeeded=${fanoutResult.started} failed=${fanoutResult.failed} skippedDuplicate=${fanoutResult.skippedDuplicate} total=${fanoutResult.total} capacityAnomaly=${fanoutResult.capacityAnomaly} conv=${conversationId} message=${shortenLogValue(messageId)}`
70162
- );
70163
- const normalizedJobsById = new Map(normalizedJobs.map((job) => [job.jobId, job]));
70164
- for (const result of fanoutResult.results) {
70165
- const job = normalizedJobsById.get(result.jobId);
70166
- if (result.outcome === "started") {
70167
- logger.info(
70168
- LogEvent.SUBSCRIPTION_JOB_DISPATCHED,
70169
- buildSubscriptionJobExtras({
70170
- checkpoint: "dispatch/subscription_job_dispatched",
70171
- outcome: "success",
70172
- jobId: result.jobId,
70173
- userAgentId: agenticId,
70174
- aspAgentId: job?.aspAgentId,
70175
- conversationId,
70176
- deliveryId: envelope.deliveryId
70177
- })
70178
- );
70179
- } else if (result.outcome === "duplicate") {
70180
- logger.info(
70181
- LogEvent.SUBSCRIPTION_JOB_DUPLICATE_IGNORED,
70182
- buildSubscriptionJobExtras({
70183
- checkpoint: "dispatch/subscription_job_duplicate_ignored",
70184
- outcome: "ignored",
70185
- jobId: result.jobId,
70186
- userAgentId: agenticId,
70187
- aspAgentId: job?.aspAgentId,
70188
- conversationId,
70189
- deliveryId: envelope.deliveryId,
70190
- reason: "duplicate_delivery"
70191
- })
70192
- );
70193
- } else {
70194
- logger.error(
70195
- LogEvent.SUBSCRIPTION_JOB_FAILED,
70196
- toLoggableError(result.error),
70197
- buildSubscriptionJobExtras({
70198
- checkpoint: "dispatch/subscription_job_failed",
70199
- outcome: "failed",
70200
- jobId: result.jobId,
70201
- userAgentId: agenticId,
70202
- aspAgentId: job?.aspAgentId,
70203
- conversationId,
70204
- deliveryId: envelope.deliveryId,
70205
- reason: "session_dispatch_failed"
70206
- })
70207
- );
70208
- }
70209
- }
70210
- logger.info(
70211
- LogEvent.SUBSCRIPTION_FANOUT_SUMMARY,
70212
- buildSubscriptionJobExtras({
70213
- checkpoint: "dispatch/subscription_fanout_summary",
70214
- outcome: fanoutResult.failed === 0 ? "success" : fanoutResult.started > 0 || fanoutResult.skippedDuplicate > 0 ? "partial" : "failed",
70215
- conversationId,
70216
- userAgentId: agenticId,
70217
- total: fanoutResult.total,
70218
- started: fanoutResult.started,
70219
- failed: fanoutResult.failed,
70220
- skippedDuplicate: fanoutResult.skippedDuplicate
70221
- })
70222
- );
70223
- return true;
70224
- }
70225
69379
  async function processFileMessage(ctx, deps, options = {}) {
70226
69380
  const messageId = ctx.message.id ?? "";
70227
69381
  const timing = createInboundTimingTracker(extractXmtpSentAtMs2(ctx.message));
70228
- const subscriptionPrecheckRawText = extractRawText(ctx.message.content);
70229
- const subscriptionPrecheckParse = parseJson2(subscriptionPrecheckRawText);
70230
- if (subscriptionPrecheckParse.ok && isSubscriptionEnvelope(subscriptionPrecheckParse.data)) {
70231
- const { chatType: subscriptionChatType, conversationId: subscriptionConversationId } = getConversationInfo(ctx.conversation);
70232
- const gateDecision = decideSubscriptionGate({
70233
- // getConversationInfo's declared StoredChatType also lists "system",
70234
- // but that value is only ever produced for stored-message bookkeeping
70235
- // elsewhere in this file — the conversation-type branch above never
70236
- // returns it, so this narrowing is safe.
70237
- chatType: subscriptionChatType === "system" ? "unknown" : subscriptionChatType,
70238
- payload: subscriptionPrecheckParse.data,
70239
- senderInboxId: ctx.message.senderInboxId ?? ""
70240
- });
70241
- return processSubscriptionInbound(ctx, deps, {
70242
- decision: gateDecision,
70243
- conversationId: subscriptionConversationId,
70244
- messageId: messageId || `subscription-${(0, import_node_crypto15.randomUUID)()}`
70245
- });
70246
- }
70247
69382
  if (messageId && !tryMarkProcessed(messageId)) {
70248
69383
  logWithTimestamp(`[okx-agent-task:${deps.myXmtpAddress}] duplicate message ignored: ${messageId}`);
70249
69384
  logger.info(LogEvent.INBOUND_IGNORED_DUPLICATE, timing.extras({
@@ -70266,6 +69401,19 @@ async function processFileMessage(ctx, deps, options = {}) {
70266
69401
  chatType,
70267
69402
  ...extra
70268
69403
  });
69404
+ if (chatType === "dm") {
69405
+ const dmTrustStartedAt = Date.now();
69406
+ const trusted = await isTrustedDmSender({
69407
+ myXmtpAddress: deps.myXmtpAddress,
69408
+ senderInboxId: ctx.message.senderInboxId ?? "",
69409
+ messageId,
69410
+ conversationId
69411
+ });
69412
+ timing.mark("dmTrustCheck", dmTrustStartedAt);
69413
+ if (!trusted) {
69414
+ return true;
69415
+ }
69416
+ }
70269
69417
  const parseStartedAt = Date.now();
70270
69418
  const receivedRawText = extractRawText(ctx.message.content);
70271
69419
  const parseResult = parseJson2(receivedRawText);
@@ -70281,26 +69429,9 @@ async function processFileMessage(ctx, deps, options = {}) {
70281
69429
  }
70282
69430
  }
70283
69431
  timing.mark("payloadParse", parseStartedAt);
70284
- const parsedDmData = parseResult.ok ? parseResult.data : null;
70285
- const parsedDmMessage = isPlainObject5(parsedDmData) && isPlainObject5(parsedDmData.message) ? parsedDmData.message : null;
70286
- const dmCarriesSystemMarkers = parsedDmMessage?.isDirectToUser === true || parsedDmMessage?.source === "system" || parsedDmMessage?.isDirectCommunication === true;
70287
- const isA2ADm = chatType === "dm" && parseResult.ok && isA2AEnvelope(parseResult.data) && !dmCarriesSystemMarkers;
70288
- if (chatType === "dm" && !isA2ADm) {
70289
- const dmTrustStartedAt = Date.now();
70290
- const trusted = await isTrustedDmSender({
70291
- myXmtpAddress: deps.myXmtpAddress,
70292
- senderInboxId: ctx.message.senderInboxId ?? "",
70293
- messageId,
70294
- conversationId
70295
- });
70296
- timing.mark("dmTrustCheck", dmTrustStartedAt);
70297
- if (!trusted) {
70298
- return true;
70299
- }
70300
- }
70301
69432
  const directToUser = chatType === "dm" && parseResult.ok ? extractDirectToUserNotification(parseResult.data) : { kind: "none" };
70302
69433
  if (directToUser.kind === "invalid") {
70303
- const droppedMessageId = messageId || `direct-to-user-${(0, import_node_crypto15.randomUUID)()}`;
69434
+ const droppedMessageId = messageId || `direct-to-user-${(0, import_node_crypto14.randomUUID)()}`;
70304
69435
  logger.error(
70305
69436
  LogEvent.INBOUND_DROP_DM_INVALID_PAYLOAD,
70306
69437
  new Error("Invalid direct-to-user system DM"),
@@ -70324,7 +69455,7 @@ async function processFileMessage(ctx, deps, options = {}) {
70324
69455
  await dispatchDirectToUserNotification({
70325
69456
  deps,
70326
69457
  notification: directToUser.notification,
70327
- messageId: messageId || `direct-to-user-${(0, import_node_crypto15.randomUUID)()}`,
69458
+ messageId: messageId || `direct-to-user-${(0, import_node_crypto14.randomUUID)()}`,
70328
69459
  timing,
70329
69460
  source: options.source ?? "live"
70330
69461
  });
@@ -70332,7 +69463,7 @@ async function processFileMessage(ctx, deps, options = {}) {
70332
69463
  }
70333
69464
  const systemNotification = chatType === "dm" && parseResult.ok ? extractSystemNotification(parseResult.data) : null;
70334
69465
  if (systemNotification) {
70335
- const systemMessageId = messageId || `system-${(0, import_node_crypto15.randomUUID)()}`;
69466
+ const systemMessageId = messageId || `system-${(0, import_node_crypto14.randomUUID)()}`;
70336
69467
  logWithTimestamp(
70337
69468
  `[okx-agent-task:${deps.myXmtpAddress}] xmtp_system_message_received agentId=${systemNotification.agentId ?? ""} msg=${shortenLogValue(systemMessageId)} event=${systemNotification.event ?? ""} job=${shortenLogValue(systemNotification.jobId)} conv=${conversationId} source=${options.source ?? "live"} rawBytes=${Buffer.byteLength(rawText, "utf8")} rawPreview=${previewRawEnvelope(rawText)}`
70338
69469
  );
@@ -70493,7 +69624,7 @@ async function processFileMessage(ctx, deps, options = {}) {
70493
69624
  timing.mark("routeResolve", routeStartedAt);
70494
69625
  if (route.route === "job" && parseResult.ok && isA2AEnvelope(parseResult.data) && !isValidExternalJobId(route.jobId)) {
70495
69626
  const payloadObject = parseResult.data;
70496
- const sender = isPlainObject5(payloadObject.sender) ? payloadObject.sender : null;
69627
+ const sender = isPlainObject3(payloadObject.sender) ? payloadObject.sender : null;
70497
69628
  logWithTimestamp(
70498
69629
  `[okx-agent-task:${deps.myXmtpAddress}] inbound A2A message has invalid jobId, dropping: jobLength=${route.jobId.length} group=${conversationId}`
70499
69630
  );
@@ -70522,32 +69653,7 @@ async function processFileMessage(ctx, deps, options = {}) {
70522
69653
  });
70523
69654
  return true;
70524
69655
  }
70525
- if (isA2ADm && route.route !== "job") {
70526
- const payloadObject = parseResult.data;
70527
- const sender = isPlainObject5(payloadObject.sender) ? payloadObject.sender : null;
70528
- recordInvalidXmtpMessageBestEffort({
70529
- messageId,
70530
- groupId: conversationId,
70531
- jobId: null,
70532
- agentId: readString5(sender?.agentId),
70533
- localAgentId: XmtpService.getInstance().getAgentByAddress(deps.myXmtpAddress)?.agentId ?? null,
70534
- senderInboxId: ctx.message.senderInboxId ?? "",
70535
- reason: "dm_a2a_missing_job_id",
70536
- stage: "inbound/job_id_validation",
70537
- logTag: `[okx-agent-task:${deps.myXmtpAddress}]`
70538
- });
70539
- logger.info(LogEvent.INBOUND_DROP_INVALID_JOBID, {
70540
- ...agentExtras({ walletAddress: deps.myXmtpAddress }),
70541
- conversationId,
70542
- messageId,
70543
- chatType,
70544
- peerInboxId: ctx.message.senderInboxId ?? "",
70545
- ...inboundStageExtras("inbound/job_id_validation", "dropped"),
70546
- reason: "dm_a2a_missing_job_id"
70547
- });
70548
- return true;
70549
- }
70550
- if (route.route === "job" && chatType !== "group" && !isA2ADm) {
69656
+ if (route.route === "job" && chatType !== "group") {
70551
69657
  const providerBindStartedAt = Date.now();
70552
69658
  const providerGate = await bindMessageJobProviderToCurrentDefault(deps, route.jobId, "inbound-xmtp");
70553
69659
  timing.mark("providerBind", providerBindStartedAt);
@@ -70587,7 +69693,7 @@ async function processFileMessage(ctx, deps, options = {}) {
70587
69693
  );
70588
69694
  }
70589
69695
  if (route.route === "job" && chatType === "group") {
70590
- const payloadObject = isPlainObject5(parseResult.ok ? parseResult.data : null) ? parseResult.data : null;
69696
+ const payloadObject = isPlainObject3(parseResult.ok ? parseResult.data : null) ? parseResult.data : null;
70591
69697
  const service = XmtpService.getInstance();
70592
69698
  const trustedSystemSender = service.isTrustedSystemAccountSender({
70593
69699
  senderAddress,
@@ -70613,7 +69719,7 @@ async function processFileMessage(ctx, deps, options = {}) {
70613
69719
  });
70614
69720
  return true;
70615
69721
  }
70616
- const sender = isPlainObject5(payloadObject?.sender) ? payloadObject.sender : null;
69722
+ const sender = isPlainObject3(payloadObject?.sender) ? payloadObject.sender : null;
70617
69723
  const localAgent = service.getAgentByAddress(deps.myXmtpAddress);
70618
69724
  const myAgentId = localAgent?.agentId ?? readString5(payloadObject?.receiverAgentId) ?? sessionAgentId ?? null;
70619
69725
  const toAgentId = readString5(sender?.agentId);
@@ -70653,7 +69759,7 @@ async function processFileMessage(ctx, deps, options = {}) {
70653
69759
  }));
70654
69760
  return true;
70655
69761
  }
70656
- const routedMessageId = messageId || `group-${(0, import_node_crypto15.randomUUID)()}`;
69762
+ const routedMessageId = messageId || `group-${(0, import_node_crypto14.randomUUID)()}`;
70657
69763
  const sessionKey = ensureInboundGroupSession(deps, {
70658
69764
  jobId: route.jobId,
70659
69765
  myAgentId,
@@ -70718,102 +69824,8 @@ async function processFileMessage(ctx, deps, options = {}) {
70718
69824
  );
70719
69825
  return true;
70720
69826
  }
70721
- if (route.route === "job" && isA2ADm) {
70722
- const payloadObject = parseResult.data;
70723
- const accepted = await verifyInboundA2AGroupMessage({
70724
- payload: parseResult.data,
70725
- myXmtpAddress: deps.myXmtpAddress,
70726
- senderAddress,
70727
- senderInboxId: ctx.message.senderInboxId ?? "",
70728
- jobId: route.jobId,
70729
- groupId: conversationId,
70730
- messageId,
70731
- consentState: void 0,
70732
- trustedSystemSender: false,
70733
- isOfflineReplay: options.isOfflineReplay ?? false,
70734
- timing
70735
- });
70736
- if (!accepted) {
70737
- return true;
70738
- }
70739
- const providerBindStartedAt = Date.now();
70740
- const providerGate = await bindMessageJobProviderToCurrentDefault(deps, route.jobId, "inbound-xmtp");
70741
- timing.mark("providerBind", providerBindStartedAt);
70742
- if (!providerGate.allowed) {
70743
- logger.info(LogEvent.INBOUND_IGNORED_NON_BUSINESS, timing.extras({
70744
- ...buildInboundTerminalExtras({
70745
- terminalState: "provider_gate_denied",
70746
- myXmtpAddress: deps.myXmtpAddress,
70747
- messageId,
70748
- conversationId,
70749
- chatType,
70750
- jobId: route.jobId,
70751
- route: route.route,
70752
- reason: "job_provider_gate_denied"
70753
- }),
70754
- routeReason: route.routeReason,
70755
- providerBinding: providerGate.provider ?? ""
70756
- }));
70757
- return true;
70758
- }
70759
- const myAgentId = XmtpService.getInstance().getAgentByAddress(deps.myXmtpAddress)?.agentId ?? readString5(payloadObject.receiverAgentId) ?? sessionAgentId ?? null;
70760
- const sender = isPlainObject5(payloadObject.sender) ? payloadObject.sender : null;
70761
- const toAgentId = readString5(sender?.agentId);
70762
- const routedMessageId = messageId || `dm-${(0, import_node_crypto15.randomUUID)()}`;
70763
- const sessionKey = ensureInboundGroupSession(deps, {
70764
- jobId: route.jobId,
70765
- myAgentId,
70766
- toAgentId,
70767
- groupId: null,
70768
- toAgentXmtpAddress: senderAddress || readString5(payloadObject.fromXmtpAddress)
70769
- });
70770
- const dispatchStartedAt = Date.now();
70771
- void Promise.resolve(deps.onSessionMessage?.({
70772
- sessionKey,
70773
- content: rawText,
70774
- messageId: routedMessageId,
70775
- jobId: route.jobId,
70776
- agentId: myAgentId
70777
- })).then(() => {
70778
- timing.mark("sessionDispatch", dispatchStartedAt);
70779
- logger.info(LogEvent.INBOUND_DELIVERED, commonDeliveryExtras({
70780
- taskId: route.jobId,
70781
- agentId: myAgentId ?? "",
70782
- peerWalletAddress: senderAddress,
70783
- peerInboxId: ctx.message.senderInboxId ?? "",
70784
- peerAgentId: toAgentId ?? "",
70785
- sessionKey,
70786
- messageId: routedMessageId,
70787
- route: "dm",
70788
- ...inboundStageExtras("inbound/dm_session_dispatch", "success"),
70789
- dispatchTarget: deps.onSessionMessage ? "session" : "none"
70790
- }));
70791
- }).catch((err2) => {
70792
- logWithTimestamp(`[okx-agent-task:${deps.myXmtpAddress}] onSessionMessage failed:`, err2);
70793
- logger.error(LogEvent.INBOUND_DISPATCH_FAILED, toLoggableError(err2), timing.extras({
70794
- ...buildInboundTerminalExtras({
70795
- terminalState: "dispatch_failed",
70796
- myXmtpAddress: deps.myXmtpAddress,
70797
- messageId: routedMessageId,
70798
- conversationId,
70799
- chatType,
70800
- jobId: route.jobId,
70801
- sessionKey,
70802
- route: "dm",
70803
- reason: "session_dispatch_failed"
70804
- }),
70805
- checkpoint: "inbound/dm_session_dispatch_failed",
70806
- agentId: myAgentId ?? "",
70807
- peerAgentId: toAgentId ?? ""
70808
- }));
70809
- });
70810
- logWithTimestamp(
70811
- `[okx-agent-task:${deps.myXmtpAddress}] session dispatch queued route=dm session=${sessionKey} message=${shortenLogValue(routedMessageId)} ${summarizePayloadForLog(parseResult.data)}`
70812
- );
70813
- return true;
70814
- }
70815
69827
  const stored = {
70816
- id: messageId || `local-${(0, import_node_crypto15.randomUUID)()}`,
69828
+ id: messageId || `local-${(0, import_node_crypto14.randomUUID)()}`,
70817
69829
  direction: "inbound",
70818
69830
  route: route.route,
70819
69831
  routeReason: route.routeReason,
@@ -70831,8 +69843,8 @@ async function processFileMessage(ctx, deps, options = {}) {
70831
69843
  await deps.store.append(route.jobId, stored, sessionAgentId);
70832
69844
  timing.mark("storeAppend", storeAppendStartedAt);
70833
69845
  if ((route.route === "job" || parseResult.ok && !isA2AEnvelope(stored.payload)) && !options.skipNotify) {
70834
- const payloadObject = isPlainObject5(stored.payload) ? stored.payload : null;
70835
- const sender = isPlainObject5(payloadObject?.sender) ? payloadObject.sender : null;
69846
+ const payloadObject = isPlainObject3(stored.payload) ? stored.payload : null;
69847
+ const sender = isPlainObject3(payloadObject?.sender) ? payloadObject.sender : null;
70836
69848
  const localAgent = XmtpService.getInstance().getAgentByAddress(deps.myXmtpAddress);
70837
69849
  const sessionKey = route.route === "job" ? buildSessionKey({
70838
69850
  jobId: route.jobId,
@@ -70850,8 +69862,8 @@ async function processFileMessage(ctx, deps, options = {}) {
70850
69862
  });
70851
69863
  }
70852
69864
  if (route.route === "job" && chatType === "group" && conversationId && senderAddress) {
70853
- const payloadObject = isPlainObject5(stored.payload) ? stored.payload : null;
70854
- const sender = isPlainObject5(payloadObject?.sender) ? payloadObject.sender : null;
69865
+ const payloadObject = isPlainObject3(stored.payload) ? stored.payload : null;
69866
+ const sender = isPlainObject3(payloadObject?.sender) ? payloadObject.sender : null;
70855
69867
  await deps.store.updateXmtpSession(route.jobId, {
70856
69868
  xmtpGroupId: conversationId,
70857
69869
  myXmtpAddress: deps.myXmtpAddress,
@@ -70994,7 +70006,7 @@ function processOfflineFromCoreDeps(store, ctx, deps, options) {
70994
70006
  function buildSystemStoredMessage(params) {
70995
70007
  const now = Date.now();
70996
70008
  return {
70997
- id: params.id ?? `system-${now}-${(0, import_node_crypto15.randomUUID)()}`,
70009
+ id: params.id ?? `system-${now}-${(0, import_node_crypto14.randomUUID)()}`,
70998
70010
  direction: "system",
70999
70011
  route: "backup",
71000
70012
  routeReason: params.reason,
@@ -71010,14 +70022,14 @@ function buildSystemStoredMessage(params) {
71010
70022
  };
71011
70023
  }
71012
70024
  function isA2AEnvelope(payload) {
71013
- return isPlainObject5(payload) && payload.msgType === A2A_MESSAGE_TYPE;
70025
+ return isPlainObject3(payload) && payload.msgType === A2A_MESSAGE_TYPE;
71014
70026
  }
71015
- var import_node_crypto15, PROCESSED_IDS_MAX, processedMessageIds, EXTERNAL_JOB_ID_PATTERN, INBOUND_TRANSPORT, INBOUND_EVENT_FAMILY, INBOUND_TERMINAL_FIELDS;
70027
+ var import_node_crypto14, PROCESSED_IDS_MAX, processedMessageIds, EXTERNAL_JOB_ID_PATTERN, INBOUND_TRANSPORT, INBOUND_EVENT_FAMILY, INBOUND_TERMINAL_FIELDS;
71016
70028
  var init_message_handler = __esm({
71017
70029
  "src/message-handler.ts"() {
71018
70030
  "use strict";
71019
70031
  init_log();
71020
- import_node_crypto15 = require("node:crypto");
70032
+ import_node_crypto14 = require("node:crypto");
71021
70033
  init_dist2();
71022
70034
  init_a2a();
71023
70035
  init_envelope_shared();
@@ -71032,10 +70044,6 @@ var init_message_handler = __esm({
71032
70044
  init_session_store();
71033
70045
  init_agent_message_notice();
71034
70046
  init_ai_provider();
71035
- init_subscription_envelope();
71036
- init_subscription_inbound();
71037
- init_list_jobs_by_verified_sender();
71038
- init_subscription_telemetry();
71039
70047
  PROCESSED_IDS_MAX = 1e3;
71040
70048
  processedMessageIds = /* @__PURE__ */ new Map();
71041
70049
  EXTERNAL_JOB_ID_PATTERN = /^0x[0-9a-fA-F]{64}$/;
@@ -71089,7 +70097,7 @@ function isOriginalParentProcessAlive(options) {
71089
70097
  }
71090
70098
  function registerUserAttentionWatcher(options) {
71091
70099
  return options.store.registerUserAttentionWatcher({
71092
- id: options.id ?? (0, import_node_crypto16.randomUUID)(),
70100
+ id: options.id ?? (0, import_node_crypto15.randomUUID)(),
71093
70101
  provider: options.provider,
71094
70102
  jobId: options.jobId,
71095
70103
  nowMs: options.nowMs ?? Date.now(),
@@ -71168,11 +70176,11 @@ function userWatchEventDeliveredSentryExtra(event) {
71168
70176
  hasDecision: String(event.hasDecision)
71169
70177
  };
71170
70178
  }
71171
- var import_node_crypto16, USER_ATTENTION_WATCH_REPLACED_MESSAGE, USER_ATTENTION_WATCHER_SCAN_MS, USER_ATTENTION_WATCHER_TTL_MS, USER_ATTENTION_WATCH_PARENT_CHECK_MS;
70179
+ var import_node_crypto15, USER_ATTENTION_WATCH_REPLACED_MESSAGE, USER_ATTENTION_WATCHER_SCAN_MS, USER_ATTENTION_WATCHER_TTL_MS, USER_ATTENTION_WATCH_PARENT_CHECK_MS;
71172
70180
  var init_user_attention_watchers = __esm({
71173
70181
  "src/user-attention-watchers.ts"() {
71174
70182
  "use strict";
71175
- import_node_crypto16 = require("node:crypto");
70183
+ import_node_crypto15 = require("node:crypto");
71176
70184
  init_sentry_logger();
71177
70185
  USER_ATTENTION_WATCH_REPLACED_MESSAGE = "Another okx-a2a user watch with the same job scope was started, so this watcher has been closed. Do not run this watch command again automatically, because starting another watcher may interrupt a different session that is already monitoring task progress.";
71178
70186
  USER_ATTENTION_WATCHER_SCAN_MS = 500;
@@ -71194,7 +70202,6 @@ __export(listener_exports, {
71194
70202
  ListenerDeferredTaskScheduler: () => ListenerDeferredTaskScheduler,
71195
70203
  ListenerMaintenanceGate: () => ListenerMaintenanceGate,
71196
70204
  XMTP_CLIENT_RECYCLE_INTERVAL_ENV: () => XMTP_CLIENT_RECYCLE_INTERVAL_ENV,
71197
- buildUpgradeRecommendationNotice: () => buildUpgradeRecommendationNotice,
71198
70205
  getHermesGatewayPluginStatus: () => getHermesGatewayPluginStatus,
71199
70206
  hermesConfigEnablesOkxA2a: () => hermesConfigEnablesOkxA2a,
71200
70207
  isGatewayAvailableForHeartbeat: () => isGatewayAvailableForHeartbeat,
@@ -71211,34 +70218,6 @@ async function runHeartbeatRefreshCycle(options) {
71211
70218
  const refresh = options.refresh && refreshAllowed ? await timeSettled(options.refresh) : void 0;
71212
70219
  return { heartbeat, versionRefresh, refresh };
71213
70220
  }
71214
- function buildUpgradeRecommendationNotice(input) {
71215
- const capability = input.capability?.trim() ?? "";
71216
- if (capability === MESSAGE_ELIGIBLE_OFFLINE_REPLAY_HELP_TOKEN) {
71217
- return {
71218
- title: "OnchainOS upgrade recommended",
71219
- content: "The installed OnchainOS version does not support offline-replay preferences. Subscription messages will continue to replay normally. Upgrade OnchainOS to enable this capability.\n\nSuggested command: onchainos upgrade",
71220
- idempotencyKey: UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY
71221
- };
71222
- }
71223
- if (capability === LIST_JOBS_BY_VERIFIED_SENDER_HELP_TOKEN) {
71224
- return {
71225
- title: "OnchainOS upgrade required for Subscription Shared Group",
71226
- content: "The installed OnchainOS version does not support the verified-sender task query required by Subscription Shared Group. Until OnchainOS is upgraded, Subscription Shared Group messages fail closed and are NOT routed to an AI Session. Upgrade OnchainOS to enable this capability.\n\nSuggested command: onchainos upgrade",
71227
- idempotencyKey: LIST_JOBS_UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY
71228
- };
71229
- }
71230
- const capabilitySlug = capability ? capability.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") : "";
71231
- const idempotencyKey = capabilitySlug ? `system:onchainos-${capabilitySlug}-upgrade` : "system:onchainos-capability-upgrade";
71232
- const capabilityLabel = capability || "a required capability";
71233
- const reasonSuffix = input.reason?.trim() ? ` ${input.reason.trim()}` : "";
71234
- return {
71235
- title: "OnchainOS upgrade recommended",
71236
- content: `The installed OnchainOS version does not support ${capabilityLabel}.${reasonSuffix} Upgrade OnchainOS to enable this capability.
71237
-
71238
- Suggested command: onchainos upgrade`,
71239
- idempotencyKey
71240
- };
71241
- }
71242
70221
  function resolveOfflineReplayIntervalSec(systemConfig) {
71243
70222
  const interval = systemConfig.offlineReplayInterval;
71244
70223
  if (typeof interval !== "number" || !Number.isFinite(interval) || interval <= 0) {
@@ -71466,12 +70445,8 @@ async function runListenerWithLock(options, paths) {
71466
70445
  onJobMessageStored: async (event) => {
71467
70446
  await enqueueAiJobMessageDispatch({ commands, event });
71468
70447
  },
71469
- // Returns { created } so the V2 subscription fan-out can distinguish a
71470
- // durable DB duplicate (idempotent success) from a fresh enqueue. V1
71471
- // group/system callers ignore the return; their events carry no
71472
- // idempotencyKey, so the plain submit path (unchanged) is used.
71473
70448
  onSessionMessage: async (event) => {
71474
- return enqueueAiSessionMessageDispatchIdempotent({ commands, event });
70449
+ await enqueueAiSessionMessageDispatch({ commands, event });
71475
70450
  }
71476
70451
  }),
71477
70452
  processOfflineMessage: (ctx, deps, replayOptions) => processOfflineFromCoreDeps(store, ctx, deps, {
@@ -71481,7 +70456,7 @@ async function runListenerWithLock(options, paths) {
71481
70456
  await enqueueAiJobMessageDispatch({ commands, event });
71482
70457
  },
71483
70458
  onSessionMessage: async (event) => {
71484
- return enqueueAiSessionMessageDispatchIdempotent({ commands, event });
70459
+ await enqueueAiSessionMessageDispatch({ commands, event });
71485
70460
  }
71486
70461
  }),
71487
70462
  notifySessionExpired: (command) => {
@@ -71499,12 +70474,12 @@ async function runListenerWithLock(options, paths) {
71499
70474
  });
71500
70475
  },
71501
70476
  notifyUpgradeRecommendation: (input) => {
71502
- const notice = buildUpgradeRecommendationNotice(input);
70477
+ const content2 = "The installed OnchainOS version does not support offline-replay preferences. Subscription messages will continue to replay normally. Upgrade OnchainOS to enable this capability.\n\nSuggested command: onchainos upgrade";
71503
70478
  void notifySystemMessageToUser({
71504
- title: notice.title,
71505
- content: notice.content,
70479
+ title: "OnchainOS upgrade recommended",
70480
+ content: content2,
71506
70481
  store: sessionStore,
71507
- idempotencyKey: notice.idempotencyKey
70482
+ idempotencyKey: UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY
71508
70483
  }).catch((err2) => {
71509
70484
  logWithTimestamp(
71510
70485
  `[okx-agent-task] upgrade recommendation notify failed component=${input.component} capability=${input.capability}:`,
@@ -71513,12 +70488,12 @@ async function runListenerWithLock(options, paths) {
71513
70488
  });
71514
70489
  }
71515
70490
  });
71516
- service.setPluginVersion("0.2.18-beta-4dd056b897-261015113839");
70491
+ service.setPluginVersion("0.2.18");
71517
70492
  await service.init();
71518
70493
  const pluginVersionStatus = service.pluginVersionStatus;
71519
70494
  if (pluginVersionStatus.unavailable) {
71520
70495
  throw new Error(
71521
- `@okxweb3/a2a-node v${"0.2.18-beta-4dd056b897-261015113839"} is below the required minimum v${pluginVersionStatus.minVersion}`
70496
+ `@okxweb3/a2a-node v${"0.2.18"} is below the required minimum v${pluginVersionStatus.minVersion}`
71522
70497
  );
71523
70498
  }
71524
70499
  const systemConfig = service.getSystemConfig();
@@ -71546,7 +70521,7 @@ async function runListenerWithLock(options, paths) {
71546
70521
  onchainosAgentId: "*",
71547
70522
  reason: "system-config missing sentryDsn",
71548
70523
  pluginId: "@okxweb3/a2a-node",
71549
- pluginVersion: "0.2.18-beta-4dd056b897-261015113839"
70524
+ pluginVersion: "0.2.18"
71550
70525
  });
71551
70526
  }
71552
70527
  logWithTimestamp(
@@ -71896,7 +70871,7 @@ async function timeSettled(fn) {
71896
70871
  return { durationMs: Date.now() - startedAt, error };
71897
70872
  }
71898
70873
  }
71899
- var import_node_fs27, import_promises13, import_node_os10, import_node_path32, DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC, DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC, XMTP_CLIENT_RECYCLE_INTERVAL_ENV, HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS, AUTH_RECOVERY_PROBE_INTERVAL_MS, AUTH_RECOVERY_REFRESH_BACKOFF_MS, AgentRefreshAuthGate, AgentRefreshCoordinator, ListenerDeferredTaskScheduler, ListenerMaintenanceGate, UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY, LIST_JOBS_UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY, SHUTDOWN_FORCE_RESOLVE_MS;
70874
+ var import_node_fs27, import_promises13, import_node_os10, import_node_path32, DEFAULT_OFFLINE_REPLAY_INTERVAL_SEC, DEFAULT_XMTP_CLIENT_RECYCLE_INTERVAL_SEC, XMTP_CLIENT_RECYCLE_INTERVAL_ENV, HEARTBEAT_GATEWAY_CHECK_TIMEOUT_MS, AUTH_RECOVERY_PROBE_INTERVAL_MS, AUTH_RECOVERY_REFRESH_BACKOFF_MS, AgentRefreshAuthGate, AgentRefreshCoordinator, ListenerDeferredTaskScheduler, ListenerMaintenanceGate, UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY, SHUTDOWN_FORCE_RESOLVE_MS;
71900
70875
  var init_listener = __esm({
71901
70876
  "src/listener.ts"() {
71902
70877
  "use strict";
@@ -71909,7 +70884,6 @@ var init_listener = __esm({
71909
70884
  init_onchainos();
71910
70885
  init_system_config();
71911
70886
  init_offline_replay_capability();
71912
- init_list_jobs_by_verified_sender();
71913
70887
  init_signer();
71914
70888
  init_bin();
71915
70889
  init_xmtp_sdk();
@@ -72090,7 +71064,6 @@ var init_listener = __esm({
72090
71064
  }
72091
71065
  };
72092
71066
  UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY = "system:onchainos-offline-replay-upgrade";
72093
- LIST_JOBS_UPGRADE_RECOMMENDATION_IDEMPOTENCY_KEY = "system:onchainos-list-jobs-by-verified-sender-upgrade";
72094
71067
  SHUTDOWN_FORCE_RESOLVE_MS = 5e3;
72095
71068
  }
72096
71069
  });
@@ -90180,7 +89153,7 @@ var require_fetch_pb = __commonJS({
90180
89153
  }
90181
89154
  });
90182
89155
  }
90183
- function isPlainObject6(value) {
89156
+ function isPlainObject4(value) {
90184
89157
  const isObject = Object.prototype.toString.call(value).slice(8, -1) === "Object";
90185
89158
  const isObjLike = value !== null && isObject;
90186
89159
  if (!isObjLike || !isObject) {
@@ -90203,7 +89176,7 @@ var require_fetch_pb = __commonJS({
90203
89176
  const isNonEmptyPrimitiveArray = Array.isArray(value) && value.every((v) => isPrimitive(v)) && value.length > 0;
90204
89177
  const isNonZeroValuePrimitive = isPrimitive(value) && !isZeroValuePrimitive(value);
90205
89178
  let objectToMerge = {};
90206
- if (isPlainObject6(value)) {
89179
+ if (isPlainObject4(value)) {
90207
89180
  objectToMerge = flattenRequestPayload(value, newPath);
90208
89181
  } else if (isNonZeroValuePrimitive || isNonEmptyPrimitiveArray) {
90209
89182
  objectToMerge = { [newPath]: value };
@@ -115772,10 +114745,10 @@ var require_node4 = __commonJS({
115772
114745
 
115773
114746
  // ../../node_modules/@xmtp/content-type-remote-attachment/dist/index.js
115774
114747
  async function encrypt(plain, secret, additionalData) {
115775
- const salt = import_node_crypto17.webcrypto.getRandomValues(new Uint8Array(KDFSaltSize));
115776
- const nonce = import_node_crypto17.webcrypto.getRandomValues(new Uint8Array(AESGCMNonceSize));
114748
+ const salt = import_node_crypto16.webcrypto.getRandomValues(new Uint8Array(KDFSaltSize));
114749
+ const nonce = import_node_crypto16.webcrypto.getRandomValues(new Uint8Array(AESGCMNonceSize));
115777
114750
  const key = await hkdf(secret, salt);
115778
- const encrypted = await import_node_crypto17.webcrypto.subtle.encrypt(aesGcmParams(nonce), key, plain);
114751
+ const encrypted = await import_node_crypto16.webcrypto.subtle.encrypt(aesGcmParams(nonce), key, plain);
115779
114752
  return new Ciphertext({
115780
114753
  aes256GcmHkdfSha256: {
115781
114754
  payload: new Uint8Array(encrypted),
@@ -115789,7 +114762,7 @@ async function decrypt(encrypted, secret, additionalData) {
115789
114762
  throw new Error("invalid payload ciphertext");
115790
114763
  }
115791
114764
  const key = await hkdf(secret, encrypted.aes256GcmHkdfSha256.hkdfSalt);
115792
- const decrypted = await import_node_crypto17.webcrypto.subtle.decrypt(aesGcmParams(encrypted.aes256GcmHkdfSha256.gcmNonce), key, encrypted.aes256GcmHkdfSha256.payload);
114765
+ const decrypted = await import_node_crypto16.webcrypto.subtle.decrypt(aesGcmParams(encrypted.aes256GcmHkdfSha256.gcmNonce), key, encrypted.aes256GcmHkdfSha256.payload);
115793
114766
  return new Uint8Array(decrypted);
115794
114767
  }
115795
114768
  function aesGcmParams(nonce, additionalData) {
@@ -115800,18 +114773,18 @@ function aesGcmParams(nonce, additionalData) {
115800
114773
  return spec;
115801
114774
  }
115802
114775
  async function hkdf(secret, salt) {
115803
- const key = await import_node_crypto17.webcrypto.subtle.importKey("raw", secret, "HKDF", false, [
114776
+ const key = await import_node_crypto16.webcrypto.subtle.importKey("raw", secret, "HKDF", false, [
115804
114777
  "deriveKey"
115805
114778
  ]);
115806
- return import_node_crypto17.webcrypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt, info: hkdfNoInfo }, key, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
114779
+ return import_node_crypto16.webcrypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt, info: hkdfNoInfo }, key, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
115807
114780
  }
115808
- var import_proto, import_node_crypto17, ContentTypeAttachment, AttachmentCodec, KDFSaltSize, AESGCMNonceSize, AESGCMTagLength, Ciphertext, hkdfNoInfo, ContentTypeRemoteAttachment, RemoteAttachmentCodec;
114781
+ var import_proto, import_node_crypto16, ContentTypeAttachment, AttachmentCodec, KDFSaltSize, AESGCMNonceSize, AESGCMTagLength, Ciphertext, hkdfNoInfo, ContentTypeRemoteAttachment, RemoteAttachmentCodec;
115809
114782
  var init_dist6 = __esm({
115810
114783
  "../../node_modules/@xmtp/content-type-remote-attachment/dist/index.js"() {
115811
114784
  init_dist5();
115812
114785
  init_secp256k12();
115813
114786
  import_proto = __toESM(require_node4(), 1);
115814
- import_node_crypto17 = require("node:crypto");
114787
+ import_node_crypto16 = require("node:crypto");
115815
114788
  ContentTypeAttachment = new ContentTypeId({
115816
114789
  authorityId: "xmtp.org",
115817
114790
  typeId: "attachment",
@@ -115891,7 +114864,7 @@ var init_dist6 = __esm({
115891
114864
  if (payload.length === 0) {
115892
114865
  throw new Error(`no payload for remote attachment at ${remoteAttachment.url}`);
115893
114866
  }
115894
- const digestBytes = new Uint8Array(await import_node_crypto17.webcrypto.subtle.digest("SHA-256", payload));
114867
+ const digestBytes = new Uint8Array(await import_node_crypto16.webcrypto.subtle.digest("SHA-256", payload));
115895
114868
  const digest = etc.bytesToHex(digestBytes);
115896
114869
  if (digest !== remoteAttachment.contentDigest) {
115897
114870
  throw new Error("content digest does not match");
@@ -115915,7 +114888,7 @@ var init_dist6 = __esm({
115915
114888
  return codec.decode(encodedContent, codecRegistry);
115916
114889
  }
115917
114890
  static async encodeEncrypted(content$1, codec) {
115918
- const secret = import_node_crypto17.webcrypto.getRandomValues(new Uint8Array(32));
114891
+ const secret = import_node_crypto16.webcrypto.getRandomValues(new Uint8Array(32));
115919
114892
  const encodedContent = import_proto.content.EncodedContent.encode(codec.encode(content$1, {
115920
114893
  codecFor() {
115921
114894
  return void 0;
@@ -115928,7 +114901,7 @@ var init_dist6 = __esm({
115928
114901
  if (!salt || !nonce || !payload) {
115929
114902
  throw new Error("missing encryption key");
115930
114903
  }
115931
- const digestBytes = new Uint8Array(await import_node_crypto17.webcrypto.subtle.digest("SHA-256", payload));
114904
+ const digestBytes = new Uint8Array(await import_node_crypto16.webcrypto.subtle.digest("SHA-256", payload));
115932
114905
  const digest = etc.bytesToHex(digestBytes);
115933
114906
  return {
115934
114907
  digest,
@@ -116146,7 +115119,7 @@ async function uploadFile(params) {
116146
115119
  const attachment = { filename, mimeType, data: new Uint8Array(data) };
116147
115120
  const encrypted = await RemoteAttachmentCodec.encodeEncrypted(attachment, new AttachmentCodec());
116148
115121
  ensureFileDirs();
116149
- const encryptedPath = (0, import_node_path35.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto18.randomUUID)()}.enc`);
115122
+ const encryptedPath = (0, import_node_path35.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto17.randomUUID)()}.enc`);
116150
115123
  (0, import_node_fs29.writeFileSync)(encryptedPath, encrypted.payload);
116151
115124
  try {
116152
115125
  const stdout = runOnchainos([
@@ -116190,7 +115163,7 @@ async function uploadFile(params) {
116190
115163
  }
116191
115164
  async function downloadFile(params) {
116192
115165
  ensureFileDirs();
116193
- const encryptedPath = (0, import_node_path35.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto18.randomUUID)()}.enc`);
115166
+ const encryptedPath = (0, import_node_path35.resolve)(FILE_WORK_DIR, `${(0, import_node_crypto17.randomUUID)()}.enc`);
116194
115167
  try {
116195
115168
  const stdout = runOnchainos([
116196
115169
  "agent",
@@ -116215,7 +115188,7 @@ async function downloadFile(params) {
116215
115188
  throw new Error(`file download failed: ${stdout}`);
116216
115189
  }
116217
115190
  const payload = new Uint8Array((0, import_node_fs29.readFileSync)(encryptedPath));
116218
- const digestBytes = new Uint8Array(await import_node_crypto18.webcrypto.subtle.digest("SHA-256", payload));
115191
+ const digestBytes = new Uint8Array(await import_node_crypto17.webcrypto.subtle.digest("SHA-256", payload));
116219
115192
  const actualDigest = Array.from(digestBytes).map((byte) => byte.toString(16).padStart(2, "0")).join("");
116220
115193
  if (actualDigest !== params.digest) {
116221
115194
  throw new Error(`file download digest verification failed: expected=${params.digest}, actual=${actualDigest}`);
@@ -116223,7 +115196,7 @@ async function downloadFile(params) {
116223
115196
  const decryptedBytes = await decryptAttachmentPayload(payload, params);
116224
115197
  const encodedContent = import_proto2.content.EncodedContent.decode(decryptedBytes);
116225
115198
  const attachment = new AttachmentCodec().decode(encodedContent);
116226
- const outputFilename = params.filename || attachment.filename || `${(0, import_node_crypto18.randomUUID)()}.bin`;
115199
+ const outputFilename = params.filename || attachment.filename || `${(0, import_node_crypto17.randomUUID)()}.bin`;
116227
115200
  const outputDir = DOWNLOADS_DIR;
116228
115201
  ensureA2aTaskDir(outputDir);
116229
115202
  const outputPath = (0, import_node_path35.resolve)(outputDir, (0, import_node_path35.basename)(outputFilename));
@@ -116240,15 +115213,15 @@ async function decryptAttachmentPayload(payload, params) {
116240
115213
  const secretBytes = new Uint8Array(Buffer.from(params.secret, "base64"));
116241
115214
  const saltBytes = new Uint8Array(Buffer.from(params.salt, "base64"));
116242
115215
  const nonceBytes = new Uint8Array(Buffer.from(params.nonce, "base64"));
116243
- const hkdfKey = await import_node_crypto18.webcrypto.subtle.importKey("raw", secretBytes, "HKDF", false, ["deriveKey"]);
116244
- const aesKey = await import_node_crypto18.webcrypto.subtle.deriveKey(
115216
+ const hkdfKey = await import_node_crypto17.webcrypto.subtle.importKey("raw", secretBytes, "HKDF", false, ["deriveKey"]);
115217
+ const aesKey = await import_node_crypto17.webcrypto.subtle.deriveKey(
116245
115218
  { name: "HKDF", hash: "SHA-256", salt: saltBytes, info: new Uint8Array().buffer },
116246
115219
  hkdfKey,
116247
115220
  { name: "AES-GCM", length: 256 },
116248
115221
  false,
116249
115222
  ["decrypt"]
116250
115223
  );
116251
- return new Uint8Array(await import_node_crypto18.webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonceBytes }, aesKey, toArrayBuffer(payload)));
115224
+ return new Uint8Array(await import_node_crypto17.webcrypto.subtle.decrypt({ name: "AES-GCM", iv: nonceBytes }, aesKey, toArrayBuffer(payload)));
116252
115225
  }
116253
115226
  function toArrayBuffer(bytes) {
116254
115227
  return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
@@ -116388,12 +115361,12 @@ function readRequiredOption(args, name) {
116388
115361
  }
116389
115362
  return value;
116390
115363
  }
116391
- var import_node_crypto18, import_node_fs29, import_node_path35, import_proto2, OKX_A2A_PATHS, OKX_A2A_HOME_DIR, FILE_WORK_DIR, DOWNLOADS_DIR, SYSTEM_CONFIG_PATH;
115364
+ var import_node_crypto17, import_node_fs29, import_node_path35, import_proto2, OKX_A2A_PATHS, OKX_A2A_HOME_DIR, FILE_WORK_DIR, DOWNLOADS_DIR, SYSTEM_CONFIG_PATH;
116392
115365
  var init_file_cli = __esm({
116393
115366
  "src/file-cli.ts"() {
116394
115367
  "use strict";
116395
115368
  init_win_spawn();
116396
- import_node_crypto18 = require("node:crypto");
115369
+ import_node_crypto17 = require("node:crypto");
116397
115370
  import_node_fs29 = require("node:fs");
116398
115371
  import_node_path35 = require("node:path");
116399
115372
  init_dist6();
@@ -119891,7 +118864,7 @@ async function getCurrentNodeCliVersion() {
119891
118864
  return await getGlobalNpmPackageVersion(UPDATE_PACKAGES.node) ?? getBundledNodeCliVersion();
119892
118865
  }
119893
118866
  function getBundledNodeCliVersion() {
119894
- return true ? "0.2.18-beta-4dd056b897-261015113839" : null;
118867
+ return true ? "0.2.18" : null;
119895
118868
  }
119896
118869
  function readConfiguredAiProvider() {
119897
118870
  const explicit = process.env.OKX_A2A_AI_PROVIDER || process.env.OKX_AGENT_TASK_AI_CLI;
@@ -120101,7 +119074,7 @@ async function updateHermes(release, options) {
120101
119074
  }
120102
119075
  }
120103
119076
  async function installGatewayPluginForDoctor(target) {
120104
- const release = isPrereleaseVersion("0.2.18-beta-4dd056b897-261015113839") ? "beta" : "latest";
119077
+ const release = isPrereleaseVersion("0.2.18") ? "beta" : "latest";
120105
119078
  const insideTargetGateway = detectGatewayInvocation() === target;
120106
119079
  const options = {
120107
119080
  restart: !insideTargetGateway,
@@ -121154,7 +120127,7 @@ async function runDoctor(options = {}) {
121154
120127
  platform: options.platform ?? process.platform,
121155
120128
  env: options.env ?? process.env,
121156
120129
  target: options.target ?? resolveDoctorTarget(options.env ?? process.env),
121157
- cliVersion: options.cliVersion ?? (true ? "0.2.18-beta-4dd056b897-261015113839" : "0.0.0"),
120130
+ cliVersion: options.cliVersion ?? (true ? "0.2.18" : "0.0.0"),
121158
120131
  fixMode: options.fix === true,
121159
120132
  nonInteractive: options.nonInteractive === true,
121160
120133
  packageChanged: false,
@@ -122261,7 +121234,7 @@ init_sentry_config();
122261
121234
  init_runtime_metadata();
122262
121235
  var CURRENT_GATEWAY_SESSION_KEYS_ENV4 = "OKX_A2A_CURRENT_GATEWAY_SESSION_KEYS";
122263
121236
  function printUsage3() {
122264
- console.log(`okx-a2a ${"0.2.18-beta-4dd056b897-261015113839"}
121237
+ console.log(`okx-a2a ${"0.2.18"}
122265
121238
 
122266
121239
  Usage:
122267
121240
  okx-a2a <command> [options]
@@ -122291,8 +121264,6 @@ Commands:
122291
121264
  Alias for runtime switch-current
122292
121265
  job-provider Manage job-to-provider bindings
122293
121266
  xmtp-send Queue an XMTP message through the running daemon
122294
- subscription-push
122295
- Queue a V2 subscription push to one or more XMTP Group conversations
122296
121267
  trade-records
122297
121268
  Insert, query, and soft-delete local business trade records
122298
121269
  capabilities Print machine-readable A2A capability negotiation status (used by OnchainOS)
@@ -122305,7 +121276,7 @@ Run \`okx-a2a <command> -h\` for command-specific help.
122305
121276
  `);
122306
121277
  }
122307
121278
  function printVersion() {
122308
- console.log("0.2.18-beta-4dd056b897-261015113839");
121279
+ console.log("0.2.18");
122309
121280
  }
122310
121281
  function printDaemonUsage() {
122311
121282
  console.log(`Usage: okx-a2a daemon <start|restart|stop|status|autostart> [options]
@@ -122402,18 +121373,6 @@ Options:
122402
121373
  --json Print machine-readable output with ok boolean
122403
121374
  `);
122404
121375
  }
122405
- function printSubscriptionPushUsage() {
122406
- console.log(`Usage: okx-a2a subscription-push --my-agent-id <id> --message <text> (--conversation-id <id> ... | --conversation-ids <id,id,...>) [options]
122407
-
122408
- Options:
122409
- --my-agent-id <id> Local sender agent id used to resolve the XMTP client
122410
- --message <text> Subscription content
122411
- --conversation-id <id> Target Group conversationId (repeatable)
122412
- --conversation-ids <id,id> Comma-separated list of target Group conversationIds
122413
- --delivery-id <id> Optional deliveryId carried in the V2 envelope
122414
- --json Print machine-readable output with ok boolean
122415
- `);
122416
- }
122417
121376
  function printCommandUsage(command) {
122418
121377
  if (command === "daemon" || command === "start" || command === "restart" || command === "stop" || command === "status") {
122419
121378
  printDaemonUsage();
@@ -122483,10 +121442,6 @@ function printCommandUsage(command) {
122483
121442
  printXmtpSendUsage();
122484
121443
  return true;
122485
121444
  }
122486
- if (command === "subscription-push") {
122487
- printSubscriptionPushUsage();
122488
- return true;
122489
- }
122490
121445
  if (command === "trade-records") {
122491
121446
  printTradeRecordsUsageProxy();
122492
121447
  return true;
@@ -122814,79 +121769,6 @@ async function queueXmtpSend(args) {
122814
121769
  commands.close();
122815
121770
  }
122816
121771
  }
122817
- async function queueXmtpSubscriptionPush(args) {
122818
- if (hasHelpFlag5(args)) {
122819
- printSubscriptionPushUsage();
122820
- return;
122821
- }
122822
- assertOnlyKnownOptions(args, {
122823
- command: "subscription-push",
122824
- options: /* @__PURE__ */ new Set([
122825
- "--my-agent-id",
122826
- "--message",
122827
- "--conversation-id",
122828
- "--conversation-ids",
122829
- "--delivery-id"
122830
- ]),
122831
- flags: /* @__PURE__ */ new Set(["--json"])
122832
- });
122833
- const json = hasFlag2(args, "--json");
122834
- const myAgentId = readOption5(args, "--my-agent-id");
122835
- const content2 = readOption5(args, "--message");
122836
- const deliveryId = readOption5(args, "--delivery-id");
122837
- const conversationIds = readSubscriptionPushConversationIds(args);
122838
- if (!myAgentId || !content2) {
122839
- throw new Error("subscription-push requires --my-agent-id and --message");
122840
- }
122841
- await assertDaemonRunningForQueuedCommand2("subscription-push");
122842
- const commands = new CommandStore();
122843
- try {
122844
- const command = commands.createXmtpSubscriptionPushCommand({
122845
- myAgentId,
122846
- content: content2,
122847
- conversationIds,
122848
- deliveryId
122849
- });
122850
- await commands.submit(command);
122851
- const result = await commands.waitForResult(command.id, Number.POSITIVE_INFINITY);
122852
- if (!result) {
122853
- printSubscriptionPushCommandResult(json, {
122854
- id: command.id,
122855
- ok: false,
122856
- completedAt: Date.now(),
122857
- error: "subscription-push finished without result"
122858
- });
122859
- process.exitCode = 1;
122860
- return;
122861
- }
122862
- if (!result.ok) {
122863
- printSubscriptionPushCommandResult(json, result);
122864
- process.exitCode = 1;
122865
- return;
122866
- }
122867
- printSubscriptionPushCommandResult(json, result);
122868
- } finally {
122869
- commands.close();
122870
- }
122871
- }
122872
- function readSubscriptionPushConversationIds(args) {
122873
- const ids = /* @__PURE__ */ new Set();
122874
- for (let i = 0; i < args.length; i++) {
122875
- if (args[i] === "--conversation-id" && args[i + 1]) {
122876
- ids.add(args[i + 1]);
122877
- }
122878
- }
122879
- const commaList = readOption5(args, "--conversation-ids");
122880
- if (commaList) {
122881
- for (const item of commaList.split(",")) {
122882
- const trimmed = item.trim();
122883
- if (trimmed) {
122884
- ids.add(trimmed);
122885
- }
122886
- }
122887
- }
122888
- return [...ids];
122889
- }
122890
121772
  function parseXmtpSendPayloadOption(raw) {
122891
121773
  const trimmed = raw?.trim();
122892
121774
  if (!trimmed) {
@@ -123020,12 +121902,12 @@ function assertOnlyKnownOptions(args, input) {
123020
121902
  async function queueXmtpDaemonCommand(args, type) {
123021
121903
  const json = hasFlag2(args, "--json");
123022
121904
  const timeoutMsRaw = readOption5(args, "--timeout-ms");
123023
- const wait3 = type === "xmtp-get-pending-list" || type === "xmtp-deny-pending-conversation" || type === "xmtp-refresh-agents";
121905
+ const wait2 = type === "xmtp-get-pending-list" || type === "xmtp-deny-pending-conversation" || type === "xmtp-refresh-agents";
123024
121906
  await assertDaemonRunningForQueuedCommand2(type);
123025
121907
  const commands = new CommandStore();
123026
121908
  const command = type === "xmtp-get-pending-list" ? commands.createXmtpGetPendingListCommand() : type === "xmtp-deny-pending-conversation" ? commands.createXmtpDenyPendingConversationCommand(readDenyPendingConversationOptions(args)) : commands.createXmtpRefreshAgentsCommand();
123027
121909
  await commands.submit(command);
123028
- if (!wait3) {
121910
+ if (!wait2) {
123029
121911
  outputCommandResult(json, { id: command.id, ok: true, completedAt: Date.now(), payload: { queued: true } }, `${type} queued command=${command.id}`);
123030
121912
  return;
123031
121913
  }
@@ -123196,23 +122078,6 @@ function printXmtpSendCommandResult(json, result) {
123196
122078
  }
123197
122079
  console.log(`ok=false xmtp-send failed command=${result.id} error=${result.error ?? "unknown error"}`);
123198
122080
  }
123199
- function printSubscriptionPushCommandResult(json, result) {
123200
- const payload = result.payload;
123201
- if (json) {
123202
- console.log(JSON.stringify({
123203
- ok: result.ok,
123204
- commandId: result.id,
123205
- payload: result.payload ?? null,
123206
- error: result.error ?? null
123207
- }));
123208
- return;
123209
- }
123210
- if (result.ok) {
123211
- console.log(`ok=true subscription-push command=${result.id} conversationCount=${payload?.conversationCount ?? 0} sentCount=${payload?.sentCount ?? 0} failedCount=${payload?.failedCount ?? 0}`);
123212
- return;
123213
- }
123214
- console.log(`ok=false subscription-push failed command=${result.id} error=${result.error ?? "unknown error"}`);
123215
- }
123216
122081
  function formatDaemonCommandResult(type, result) {
123217
122082
  if (!result.ok) {
123218
122083
  return `ok=false ${type} command=${result.id} error=${result.error ?? "unknown error"}`;
@@ -123906,10 +122771,10 @@ async function main() {
123906
122771
  if (command === "xmtp-test") {
123907
122772
  const { handleXmtpTestCommand: handleXmtpTestCommand2 } = await Promise.resolve().then(() => (init_xmtp_test_cli(), xmtp_test_cli_exports));
123908
122773
  await handleXmtpTestCommand2(process.argv.slice(3), {
123909
- packageVersion: "0.2.18-beta-4dd056b897-261015113839",
123910
- agentSdkVersion: "2.3.0",
123911
- nodeSdkVersion: "6.1.0",
123912
- nodeBindingsVersion: "1.11.0"
122774
+ packageVersion: "0.2.18",
122775
+ agentSdkVersion: "2.3.1",
122776
+ nodeSdkVersion: "6.1.1",
122777
+ nodeBindingsVersion: "1.11.1"
123913
122778
  });
123914
122779
  return;
123915
122780
  }
@@ -123919,12 +122784,6 @@ async function main() {
123919
122784
  await flushDirectCliSentry();
123920
122785
  return;
123921
122786
  }
123922
- if (command === "subscription-push") {
123923
- await initDirectCliSentry();
123924
- await queueXmtpSubscriptionPush(process.argv.slice(3));
123925
- await flushDirectCliSentry();
123926
- return;
123927
- }
123928
122787
  if (command === "task") {
123929
122788
  await initDirectCliSentry();
123930
122789
  await handleTaskCommand(process.argv.slice(3));