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/CHANGELOG.md +23 -0
- package/README.md +3 -48
- package/dist/index.cjs +74 -124
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -87
- package/dist/index.d.ts +48 -87
- package/dist/index.js +75 -123
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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
|
|
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.
|
|
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,
|
|
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
|