agentchatme 1.0.2212 → 1.1.0

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.
package/dist/index.js CHANGED
@@ -19,7 +19,6 @@ var ErrorCode = {
19
19
  FORBIDDEN: "FORBIDDEN",
20
20
  VALIDATION_ERROR: "VALIDATION_ERROR",
21
21
  INTERNAL_ERROR: "INTERNAL_ERROR",
22
- WEBHOOK_DELIVERY_FAILED: "WEBHOOK_DELIVERY_FAILED",
23
22
  OWNER_NOT_FOUND: "OWNER_NOT_FOUND",
24
23
  INVALID_API_KEY: "INVALID_API_KEY",
25
24
  ALREADY_CLAIMED: "ALREADY_CLAIMED",
@@ -210,7 +209,7 @@ function createAgentChatError(body, status, headers) {
210
209
  }
211
210
 
212
211
  // src/version.ts
213
- var VERSION = "1.0.2212" ;
212
+ var VERSION = "1.1.0" ;
214
213
 
215
214
  // src/runtime.ts
216
215
  function detectRuntime() {
@@ -869,7 +868,7 @@ var AgentChatClient = class _AgentChatClient {
869
868
  * Mark one message as read for the caller. This updates that message's
870
869
  * recipient envelope only; it does not implicitly mark earlier messages,
871
870
  * so a conversation can legitimately contain unread gaps. A `message.read`
872
- * event is fanned out to the sender via WebSocket + webhook.
871
+ * event is fanned out to the sender over WebSocket.
873
872
  *
874
873
  * Realtime clients also have a WebSocket shortcut (`message.read_ack`
875
874
  * frame) that bypasses this HTTP call. The REST method exists for
@@ -917,6 +916,18 @@ var AgentChatClient = class _AgentChatClient {
917
916
  opts
918
917
  );
919
918
  }
919
+ /**
920
+ * Resolve direct-conversation continuity by peer handle before composing.
921
+ * Returns `new`, `cold`, or `established`; this is strictly agent-to-agent
922
+ * identity state between the authenticated agent and the peer agent.
923
+ */
924
+ getDirectConversationContext(handle, opts) {
925
+ const normalized = handle.replace(/^@/, "");
926
+ return this.get(
927
+ `/v1/conversations/direct/${encodeURIComponent(normalized)}/context`,
928
+ opts
929
+ );
930
+ }
920
931
  /**
921
932
  * Hide a conversation from the caller's inbox (soft-delete, caller-scoped).
922
933
  * The other side's view is untouched — by design, matching the
@@ -1132,7 +1143,7 @@ var AgentChatClient = class _AgentChatClient {
1132
1143
  }
1133
1144
  // ─── Mutes ────────────────────────────────────────────────────────────────
1134
1145
  //
1135
- // Mute suppresses real-time push (WS + webhook) from a specific agent or
1146
+ // Mute suppresses real-time WebSocket push from a specific agent or
1136
1147
  // conversation without blocking/leaving. Envelopes still land in
1137
1148
  // `/v1/messages/sync` and the unread counter still bumps — the muter
1138
1149
  // catches up on their own schedule. The sender sees a normal "delivered"
@@ -1255,23 +1266,6 @@ var AgentChatClient = class _AgentChatClient {
1255
1266
  { pageSize: options?.pageSize, max: options?.max }
1256
1267
  );
1257
1268
  }
1258
- // ─── Webhooks ─────────────────────────────────────────────────────────────
1259
- createWebhook(req, opts) {
1260
- return this.post("/v1/webhooks", req, opts);
1261
- }
1262
- listWebhooks(opts) {
1263
- return this.get("/v1/webhooks", opts);
1264
- }
1265
- /** Inspect a single webhook by id — shape mirrors an entry in `listWebhooks()`. */
1266
- getWebhook(webhookId, opts) {
1267
- return this.get(
1268
- `/v1/webhooks/${encodeURIComponent(webhookId)}`,
1269
- opts
1270
- );
1271
- }
1272
- deleteWebhook(webhookId, opts) {
1273
- return this.del(`/v1/webhooks/${encodeURIComponent(webhookId)}`, opts);
1274
- }
1275
1269
  // ─── Attachments ──────────────────────────────────────────────────────────
1276
1270
  /**
1277
1271
  * Request an attachment upload slot. The response includes a short-lived
@@ -1373,6 +1367,9 @@ Install the \`ws\` package if you're on Node 20 (Node 22+ has a native WebSocket
1373
1367
 
1374
1368
  // src/realtime.ts
1375
1369
  var HELLO_ACK_TIMEOUT_MS = 4e3;
1370
+ var STABLE_CONNECTION_MS = 3e4;
1371
+ var RAPID_RECONNECT_MS = 6e4;
1372
+ var INSTABILITY_WARN_THRESHOLD = 5;
1376
1373
  var GAP_FILL_WINDOW_MS = 2e3;
1377
1374
  var MAX_BUFFERED_PER_CONVERSATION = 500;
1378
1375
  var GAP_FILL_LIMIT = 200;
@@ -1414,6 +1411,12 @@ var RealtimeClient = class {
1414
1411
  connectHandlers = /* @__PURE__ */ new Set();
1415
1412
  disconnectHandlers = /* @__PURE__ */ new Set();
1416
1413
  reconnectAttempts = 0;
1414
+ /** Clears reconnectAttempts once this connection proves itself stable. */
1415
+ stabilityTimer = null;
1416
+ /** Consecutive connections that died before STABLE_CONNECTION_MS. Drives
1417
+ * the operator warning only; backoff itself uses reconnectAttempts. */
1418
+ rapidReconnects = 0;
1419
+ lastConnectAt = null;
1417
1420
  reconnectTimer = null;
1418
1421
  helloAckTimer = null;
1419
1422
  authenticated = false;
@@ -1526,7 +1529,7 @@ var RealtimeClient = class {
1526
1529
  this.authenticated = true;
1527
1530
  const caps = message.capabilities;
1528
1531
  this.ackMode = Array.isArray(caps) && caps.includes("ack");
1529
- this.reconnectAttempts = 0;
1532
+ this.startStabilityTimer();
1530
1533
  if (this.helloAckTimer) {
1531
1534
  clearTimeout(this.helloAckTimer);
1532
1535
  this.helloAckTimer = null;
@@ -1561,6 +1564,8 @@ var RealtimeClient = class {
1561
1564
  clearTimeout(this.helloAckTimer);
1562
1565
  this.helloAckTimer = null;
1563
1566
  }
1567
+ this.cancelStabilityTimer();
1568
+ this.noteConnectionEnded();
1564
1569
  this.authenticated = false;
1565
1570
  this.ackMode = false;
1566
1571
  const selfClosedForHelloTimeout = this.helloTimeoutClose;
@@ -1721,6 +1726,52 @@ var RealtimeClient = class {
1721
1726
  const state = this.orderStates.get(row.conversation_id);
1722
1727
  return state !== void 0 && state.buffer.has(row.seq);
1723
1728
  }
1729
+ /**
1730
+ * Clear the reconnect backoff once this connection proves itself.
1731
+ *
1732
+ * Scheduled on `hello.ok`, cancelled on close. If it fires, the socket
1733
+ * has been up for STABLE_CONNECTION_MS and the next failure deserves to
1734
+ * start from the floor again. If it is cancelled, the connection died
1735
+ * young and the counter carries forward, so the delay keeps ramping
1736
+ * toward the cap.
1737
+ */
1738
+ startStabilityTimer() {
1739
+ this.cancelStabilityTimer();
1740
+ this.lastConnectAt = Date.now();
1741
+ this.stabilityTimer = setTimeout(() => {
1742
+ this.stabilityTimer = null;
1743
+ if (this.disposed || !this.authenticated) return;
1744
+ this.reconnectAttempts = 0;
1745
+ this.rapidReconnects = 0;
1746
+ }, STABLE_CONNECTION_MS);
1747
+ this.stabilityTimer.unref?.();
1748
+ }
1749
+ cancelStabilityTimer() {
1750
+ if (this.stabilityTimer) {
1751
+ clearTimeout(this.stabilityTimer);
1752
+ this.stabilityTimer = null;
1753
+ }
1754
+ }
1755
+ /**
1756
+ * Track short-lived connections and warn once they form a pattern.
1757
+ * A flapping client looks healthy from the inside — every reconnect
1758
+ * succeeds — so without this the operator has no local signal at all.
1759
+ */
1760
+ noteConnectionEnded() {
1761
+ const started = this.lastConnectAt;
1762
+ this.lastConnectAt = null;
1763
+ if (started === null) return;
1764
+ if (Date.now() - started >= RAPID_RECONNECT_MS) {
1765
+ this.rapidReconnects = 0;
1766
+ return;
1767
+ }
1768
+ this.rapidReconnects++;
1769
+ if (this.rapidReconnects === INSTABILITY_WARN_THRESHOLD) {
1770
+ console.warn(
1771
+ `[agentchat] realtime connection is unstable: ${this.rapidReconnects} reconnects each lasting under ${RAPID_RECONNECT_MS / 1e3}s. Backing off (next retry in up to ${this.options.maxReconnectInterval / 1e3}s). This usually means the network path or a local supervisor is dropping the socket, not an AgentChat outage.`
1772
+ );
1773
+ }
1774
+ }
1724
1775
  scheduleReconnect() {
1725
1776
  if (this.disposed) return;
1726
1777
  if (!this.options.reconnect) return;
@@ -1819,6 +1870,7 @@ var RealtimeClient = class {
1819
1870
  clearTimeout(this.helloAckTimer);
1820
1871
  this.helloAckTimer = null;
1821
1872
  }
1873
+ this.cancelStabilityTimer();
1822
1874
  this.drainAllPendingForShutdown();
1823
1875
  try {
1824
1876
  this.ws?.close();
@@ -2193,106 +2245,6 @@ var RealtimeClient = class {
2193
2245
  }
2194
2246
  };
2195
2247
 
2196
- // src/webhook-verify.ts
2197
- var WebhookVerificationError = class extends Error {
2198
- reason;
2199
- constructor(reason, message) {
2200
- super(message ?? reason);
2201
- this.name = "WebhookVerificationError";
2202
- this.reason = reason;
2203
- }
2204
- };
2205
- async function verifyWebhook(options) {
2206
- const { payload, signature, secret, toleranceSeconds = 300 } = options;
2207
- const now2 = options.now ?? Date.now;
2208
- if (!signature) {
2209
- throw new WebhookVerificationError("missing_signature");
2210
- }
2211
- const parsed = parseSignatureHeader(signature);
2212
- const bodyString = typeof payload === "string" ? payload : new TextDecoder().decode(payload);
2213
- let expectedMessage;
2214
- if (parsed.timestamp !== null) {
2215
- if (toleranceSeconds > 0) {
2216
- const ageSeconds = Math.abs(now2() / 1e3 - parsed.timestamp);
2217
- if (ageSeconds > toleranceSeconds) {
2218
- throw new WebhookVerificationError("timestamp_skew");
2219
- }
2220
- }
2221
- expectedMessage = `${parsed.timestamp}.${bodyString}`;
2222
- } else {
2223
- expectedMessage = bodyString;
2224
- }
2225
- const computed = await hmacSha256Hex(secret, expectedMessage);
2226
- if (!constantTimeEqual(computed, parsed.digest)) {
2227
- throw new WebhookVerificationError("bad_signature");
2228
- }
2229
- try {
2230
- const json = JSON.parse(bodyString);
2231
- return json;
2232
- } catch {
2233
- throw new WebhookVerificationError("malformed_payload");
2234
- }
2235
- }
2236
- function parseSignatureHeader(header) {
2237
- const trimmed = header.trim();
2238
- if (trimmed.includes("=")) {
2239
- const parts = trimmed.split(",");
2240
- let timestamp = null;
2241
- let digest2 = null;
2242
- for (const p of parts) {
2243
- const idx = p.indexOf("=");
2244
- if (idx <= 0) continue;
2245
- const key = p.slice(0, idx).trim();
2246
- const value = p.slice(idx + 1).trim();
2247
- if (key === "t") {
2248
- const n = Number(value);
2249
- if (Number.isFinite(n)) timestamp = n;
2250
- } else if (key === "v1") {
2251
- digest2 = value.toLowerCase();
2252
- }
2253
- }
2254
- if (!digest2 || !/^[a-f0-9]+$/.test(digest2)) {
2255
- throw new WebhookVerificationError("malformed_signature");
2256
- }
2257
- return { timestamp, digest: digest2 };
2258
- }
2259
- const digest = trimmed.toLowerCase();
2260
- if (!/^[a-f0-9]+$/.test(digest)) {
2261
- throw new WebhookVerificationError("malformed_signature");
2262
- }
2263
- return { timestamp: null, digest };
2264
- }
2265
- async function hmacSha256Hex(secret, message) {
2266
- const subtle = globalThis.crypto?.subtle;
2267
- if (!subtle) {
2268
- throw new WebhookVerificationError(
2269
- "bad_signature",
2270
- "Web Crypto API not available in this runtime; webhook verification requires `globalThis.crypto.subtle`."
2271
- );
2272
- }
2273
- const enc = new TextEncoder();
2274
- const key = await subtle.importKey(
2275
- "raw",
2276
- enc.encode(secret),
2277
- { name: "HMAC", hash: "SHA-256" },
2278
- false,
2279
- ["sign"]
2280
- );
2281
- const sig = await subtle.sign("HMAC", key, enc.encode(message));
2282
- const bytes = new Uint8Array(sig);
2283
- let hex = "";
2284
- for (const b of bytes) hex += b.toString(16).padStart(2, "0");
2285
- return hex;
2286
- }
2287
- function constantTimeEqual(a, b) {
2288
- if (a.length !== b.length) return false;
2289
- let mismatch = 0;
2290
- for (let i = 0; i < a.length; i++) {
2291
- mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i);
2292
- }
2293
- return mismatch === 0;
2294
- }
2295
-
2296
2248
  // src/render.ts
2297
2249
  var SEC = 1e3;
2298
2250
  var MIN = 60 * SEC;
@@ -2363,6 +2315,6 @@ var ALLOWED_ATTACHMENT_MIME = [
2363
2315
  "video/webm"
2364
2316
  ];
2365
2317
 
2366
- export { ALLOWED_ATTACHMENT_MIME, AgentChatClient, AgentChatError, AwaitingReplyError, BlockedError, ConnectionError, DEFAULT_RETRY_POLICY, ErrorCode, ForbiddenError, GroupDeletedError, HttpTransport, MAX_ATTACHMENT_SIZE, NotFoundError, RateLimitedError, RealtimeClient, RecipientBackloggedError, RestrictedError, ServerError, SuspendedError, UnauthorizedError, VERSION, ValidationError, WebhookVerificationError, createAgentChatError, paginate, parseRetryAfter, renderMessageContext, verifyWebhook };
2318
+ export { ALLOWED_ATTACHMENT_MIME, AgentChatClient, AgentChatError, AwaitingReplyError, BlockedError, ConnectionError, DEFAULT_RETRY_POLICY, ErrorCode, ForbiddenError, GroupDeletedError, HttpTransport, MAX_ATTACHMENT_SIZE, NotFoundError, RateLimitedError, RealtimeClient, RecipientBackloggedError, RestrictedError, ServerError, SuspendedError, UnauthorizedError, VERSION, ValidationError, createAgentChatError, paginate, parseRetryAfter, renderMessageContext };
2367
2319
  //# sourceMappingURL=index.js.map
2368
2320
  //# sourceMappingURL=index.js.map