@aexhq/sdk 0.46.3-canary → 0.46.5-canary
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/_contracts/event-stream-client.js +78 -11
- package/dist/cli.mjs +36 -7
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -45,8 +45,19 @@ export function hasRunTerminalType(event) {
|
|
|
45
45
|
* the coordinator's pair (platform `packages/shared/src/event-stream-client.ts`).
|
|
46
46
|
*/
|
|
47
47
|
const COORDINATOR_PING = "aex:ping";
|
|
48
|
-
/**
|
|
49
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Pull request for everything from `nextSequence` onward.
|
|
50
|
+
*
|
|
51
|
+
* Sent on open ($connect cannot safely PostToConnection before the handshake
|
|
52
|
+
* completes) and again whenever a frame declares a predecessor this client has
|
|
53
|
+
* not reached. Catch-up is PULL-based because the connection row holds only the
|
|
54
|
+
* server's BELIEF about what reached the socket: a frame that was posted but
|
|
55
|
+
* never arrived leaves the client behind that belief with no way for the server
|
|
56
|
+
* to notice. Naming the cursor makes the client authoritative about what it
|
|
57
|
+
* actually holds. An older coordinator ignores the extra field and falls back to
|
|
58
|
+
* its own high-water, so sending it is safe against any deployed server.
|
|
59
|
+
*/
|
|
60
|
+
const replayFrom = (nextSequence) => JSON.stringify({ action: "replay", from: nextSequence });
|
|
50
61
|
/** Default half-open watchdog window — 3× the ping cadence, so 2 pongs can be lost. */
|
|
51
62
|
const DEFAULT_IDLE_TIMEOUT_MS = 45_000;
|
|
52
63
|
/** Default client keep-alive ping cadence. */
|
|
@@ -98,6 +109,7 @@ export async function* streamCoordinatorEvents(opts) {
|
|
|
98
109
|
const ws = makeWs(url.toString());
|
|
99
110
|
const pending = [];
|
|
100
111
|
const seenSequences = new Set();
|
|
112
|
+
const catchUp = createCatchUp(ws, cursor);
|
|
101
113
|
let closed = false;
|
|
102
114
|
let disconnectReason = "";
|
|
103
115
|
let resolveNext = null;
|
|
@@ -199,15 +211,7 @@ export async function* streamCoordinatorEvents(opts) {
|
|
|
199
211
|
};
|
|
200
212
|
ws.addEventListener("open", () => {
|
|
201
213
|
armIdle();
|
|
202
|
-
|
|
203
|
-
try {
|
|
204
|
-
ws.send(COORDINATOR_REPLAY);
|
|
205
|
-
}
|
|
206
|
-
catch {
|
|
207
|
-
// socket not open / send unsupported — the DDB-stream replay kicker and
|
|
208
|
-
// quiet-reconnect path still cover replay.
|
|
209
|
-
}
|
|
210
|
-
}
|
|
214
|
+
catchUp.request();
|
|
211
215
|
if (pingIntervalMs > 0 && typeof ws.send === "function") {
|
|
212
216
|
pingTimer = timerPort.setInterval(() => {
|
|
213
217
|
try {
|
|
@@ -232,6 +236,11 @@ export async function* streamCoordinatorEvents(opts) {
|
|
|
232
236
|
if (isReplayableEvent(evt)) {
|
|
233
237
|
if (evt.sequence <= cursor || seenSequences.has(evt.sequence))
|
|
234
238
|
return;
|
|
239
|
+
// A frame the client cannot chain is refused, leaving both the cursor
|
|
240
|
+
// and `seenSequences` untouched so the pull's copy of it is not later
|
|
241
|
+
// suppressed as a duplicate.
|
|
242
|
+
if (!catchUp.chains(evt))
|
|
243
|
+
return;
|
|
235
244
|
seenSequences.add(evt.sequence);
|
|
236
245
|
}
|
|
237
246
|
else if (!rememberLiveId(evt.id)) {
|
|
@@ -325,6 +334,64 @@ export async function* streamCoordinatorEvents(opts) {
|
|
|
325
334
|
await sleep(reconnectDelayMs, timerPort, opts.signal);
|
|
326
335
|
}
|
|
327
336
|
}
|
|
337
|
+
/**
|
|
338
|
+
* The exclusive lower bound the coordinator declared for a durable frame: the
|
|
339
|
+
* sequence of the public event immediately preceding it in journal projection
|
|
340
|
+
* order (`-1` when the frame opens the session).
|
|
341
|
+
*
|
|
342
|
+
* Transport-only — it describes this frame's position in the stream, not the
|
|
343
|
+
* event, so it is never part of the durable {@link AexEvent} the archive returns.
|
|
344
|
+
* Absent means the producer made no claim (an older coordinator, or a path that
|
|
345
|
+
* cannot prove what came before it), and an unproven link is not a gap: the
|
|
346
|
+
* frame is accepted exactly as it was before this check existed.
|
|
347
|
+
*/
|
|
348
|
+
function declaredPredecessor(frame) {
|
|
349
|
+
const value = frame.prevSequence;
|
|
350
|
+
return typeof value === "number" && Number.isSafeInteger(value) ? value : undefined;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Per-connection catch-up bookkeeping.
|
|
354
|
+
*
|
|
355
|
+
* The high-water is RECEIPT-side — the highest durable sequence admitted on this
|
|
356
|
+
* connection, including frames still queued for the consumer. The gap check has
|
|
357
|
+
* to run against that and not against the yield-side cursor, which lags while
|
|
358
|
+
* the consumer drains: a contiguous burst landing in one turn would otherwise
|
|
359
|
+
* report every frame after the first as a hole.
|
|
360
|
+
*
|
|
361
|
+
* One pull is outstanding per high-water position, so a run of unchainable
|
|
362
|
+
* frames costs one request rather than one per frame; it re-arms by
|
|
363
|
+
* construction, since the next gap at a moved position names a different one.
|
|
364
|
+
*/
|
|
365
|
+
function createCatchUp(ws, cursor) {
|
|
366
|
+
let accepted = cursor;
|
|
367
|
+
let pulledFrom = null;
|
|
368
|
+
const request = () => {
|
|
369
|
+
const from = accepted + 1;
|
|
370
|
+
if (pulledFrom === from || typeof ws.send !== "function")
|
|
371
|
+
return;
|
|
372
|
+
pulledFrom = from;
|
|
373
|
+
try {
|
|
374
|
+
ws.send(replayFrom(from));
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
// socket not open / send unsupported — the idle + quiet watchdogs still
|
|
378
|
+
// reconnect, and a reconnect resumes from the cursor anyway.
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
return {
|
|
382
|
+
request,
|
|
383
|
+
chains: (frame) => {
|
|
384
|
+
const predecessor = declaredPredecessor(frame);
|
|
385
|
+
if (predecessor !== undefined && predecessor > accepted) {
|
|
386
|
+
request();
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
389
|
+
if (frame.sequence > accepted)
|
|
390
|
+
accepted = frame.sequence;
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
}
|
|
328
395
|
function isCoordinatorStreamEvent(value) {
|
|
329
396
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
330
397
|
return false;
|
package/dist/cli.mjs
CHANGED
|
@@ -3501,7 +3501,7 @@ function hasRunTerminalType(event) {
|
|
|
3501
3501
|
return event.type === "RUN_FINISHED" || event.type === "RUN_ERROR";
|
|
3502
3502
|
}
|
|
3503
3503
|
var COORDINATOR_PING = "aex:ping";
|
|
3504
|
-
var
|
|
3504
|
+
var replayFrom = (nextSequence) => JSON.stringify({ action: "replay", from: nextSequence });
|
|
3505
3505
|
var DEFAULT_IDLE_TIMEOUT_MS = 45e3;
|
|
3506
3506
|
var DEFAULT_PING_INTERVAL_MS = 15e3;
|
|
3507
3507
|
var DEFAULT_EVENT_QUIET_RECHECK_MS = 9e4;
|
|
@@ -3546,6 +3546,7 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
3546
3546
|
const ws = makeWs(url.toString());
|
|
3547
3547
|
const pending = [];
|
|
3548
3548
|
const seenSequences = /* @__PURE__ */ new Set();
|
|
3549
|
+
const catchUp = createCatchUp(ws, cursor);
|
|
3549
3550
|
let closed = false;
|
|
3550
3551
|
let disconnectReason = "";
|
|
3551
3552
|
let resolveNext = null;
|
|
@@ -3639,12 +3640,7 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
3639
3640
|
};
|
|
3640
3641
|
ws.addEventListener("open", () => {
|
|
3641
3642
|
armIdle();
|
|
3642
|
-
|
|
3643
|
-
try {
|
|
3644
|
-
ws.send(COORDINATOR_REPLAY);
|
|
3645
|
-
} catch {
|
|
3646
|
-
}
|
|
3647
|
-
}
|
|
3643
|
+
catchUp.request();
|
|
3648
3644
|
if (pingIntervalMs > 0 && typeof ws.send === "function") {
|
|
3649
3645
|
pingTimer = timerPort.setInterval(() => {
|
|
3650
3646
|
try {
|
|
@@ -3667,6 +3663,8 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
3667
3663
|
if (isReplayableEvent(evt)) {
|
|
3668
3664
|
if (evt.sequence <= cursor || seenSequences.has(evt.sequence))
|
|
3669
3665
|
return;
|
|
3666
|
+
if (!catchUp.chains(evt))
|
|
3667
|
+
return;
|
|
3670
3668
|
seenSequences.add(evt.sequence);
|
|
3671
3669
|
} else if (!rememberLiveId(evt.id)) {
|
|
3672
3670
|
return;
|
|
@@ -3742,6 +3740,37 @@ async function* streamCoordinatorEvents(opts) {
|
|
|
3742
3740
|
await sleep(reconnectDelayMs, timerPort, opts.signal);
|
|
3743
3741
|
}
|
|
3744
3742
|
}
|
|
3743
|
+
function declaredPredecessor(frame) {
|
|
3744
|
+
const value = frame.prevSequence;
|
|
3745
|
+
return typeof value === "number" && Number.isSafeInteger(value) ? value : void 0;
|
|
3746
|
+
}
|
|
3747
|
+
function createCatchUp(ws, cursor) {
|
|
3748
|
+
let accepted = cursor;
|
|
3749
|
+
let pulledFrom = null;
|
|
3750
|
+
const request = () => {
|
|
3751
|
+
const from = accepted + 1;
|
|
3752
|
+
if (pulledFrom === from || typeof ws.send !== "function")
|
|
3753
|
+
return;
|
|
3754
|
+
pulledFrom = from;
|
|
3755
|
+
try {
|
|
3756
|
+
ws.send(replayFrom(from));
|
|
3757
|
+
} catch {
|
|
3758
|
+
}
|
|
3759
|
+
};
|
|
3760
|
+
return {
|
|
3761
|
+
request,
|
|
3762
|
+
chains: (frame) => {
|
|
3763
|
+
const predecessor = declaredPredecessor(frame);
|
|
3764
|
+
if (predecessor !== void 0 && predecessor > accepted) {
|
|
3765
|
+
request();
|
|
3766
|
+
return false;
|
|
3767
|
+
}
|
|
3768
|
+
if (frame.sequence > accepted)
|
|
3769
|
+
accepted = frame.sequence;
|
|
3770
|
+
return true;
|
|
3771
|
+
}
|
|
3772
|
+
};
|
|
3773
|
+
}
|
|
3745
3774
|
function isCoordinatorStreamEvent(value) {
|
|
3746
3775
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
3747
3776
|
return false;
|
package/dist/cli.mjs.sha256
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
a57352fbb791f3941e71e1c75b56cbe77a1c7616be763c021fe5b37e316a9928 cli.mjs
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aexhq/sdk",
|
|
3
|
-
"version": "0.46.
|
|
3
|
+
"version": "0.46.5-canary",
|
|
4
4
|
"description": "TypeScript SDK for autonomous agent sessions with managed Vercel AI Gateway model access.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -55,6 +55,6 @@
|
|
|
55
55
|
"zod": "4.4.3"
|
|
56
56
|
},
|
|
57
57
|
"aexRelease": {
|
|
58
|
-
"sourceSha": "
|
|
58
|
+
"sourceSha": "b2bf9ebe5c7e86a89e37f48c9c93ef359252202e"
|
|
59
59
|
}
|
|
60
60
|
}
|