sentinelayer-cli 0.28.2 → 0.28.4
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/package.json +1 -1
- package/src/commands/session.js +89 -31
- package/src/session/listener.js +1 -0
- package/src/session/sync.js +207 -36
package/package.json
CHANGED
package/src/commands/session.js
CHANGED
|
@@ -145,6 +145,7 @@ const SESSION_SAY_LOCAL_ONLY_REASONS = new Set([
|
|
|
145
145
|
"not_authenticated",
|
|
146
146
|
"remote_sync_disabled_env",
|
|
147
147
|
]);
|
|
148
|
+
const SESSION_READ_AUTO_VIEW_TIMEOUT_MS = 500;
|
|
148
149
|
|
|
149
150
|
function isLocalOnlySessionSayReason(reason) {
|
|
150
151
|
return SESSION_SAY_LOCAL_ONLY_REASONS.has(normalizeString(reason));
|
|
@@ -625,11 +626,76 @@ function sessionReadViewTarget(event = {}) {
|
|
|
625
626
|
};
|
|
626
627
|
}
|
|
627
628
|
|
|
628
|
-
|
|
629
|
+
function normalizeAutoViewTimeoutMs(value) {
|
|
630
|
+
const parsed = Number(value);
|
|
631
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
632
|
+
return SESSION_READ_AUTO_VIEW_TIMEOUT_MS;
|
|
633
|
+
}
|
|
634
|
+
return Math.max(50, Math.min(5_000, Math.floor(parsed)));
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
async function writeSessionReadViews(sessionId, writeTargets = [], {
|
|
638
|
+
targetPath,
|
|
639
|
+
agentId,
|
|
640
|
+
timeoutMs = SESSION_READ_AUTO_VIEW_TIMEOUT_MS,
|
|
641
|
+
} = {}) {
|
|
642
|
+
if (!Array.isArray(writeTargets) || writeTargets.length === 0) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
const totalTimeoutMs = normalizeAutoViewTimeoutMs(timeoutMs);
|
|
646
|
+
const controller = new AbortController();
|
|
647
|
+
const startedAt = Date.now();
|
|
648
|
+
const timeoutHandle = setTimeout(() => {
|
|
649
|
+
try {
|
|
650
|
+
controller.abort();
|
|
651
|
+
} catch {
|
|
652
|
+
// ignore
|
|
653
|
+
}
|
|
654
|
+
}, totalTimeoutMs);
|
|
655
|
+
if (typeof timeoutHandle.unref === "function") {
|
|
656
|
+
timeoutHandle.unref();
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
try {
|
|
660
|
+
for (const target of writeTargets) {
|
|
661
|
+
if (controller.signal.aborted) {
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
const elapsedMs = Math.max(0, Date.now() - startedAt);
|
|
665
|
+
const remainingMs = Math.max(1, totalTimeoutMs - elapsedMs);
|
|
666
|
+
const result = await createSessionMessageAction(sessionId, {
|
|
667
|
+
actionType: "view",
|
|
668
|
+
targetPath,
|
|
669
|
+
targetSequenceId: target.targetSequenceId,
|
|
670
|
+
targetCursor: target.targetCursor,
|
|
671
|
+
metadata: {
|
|
672
|
+
source: "cli_read",
|
|
673
|
+
agentId,
|
|
674
|
+
},
|
|
675
|
+
idempotencyKey: defaultActionIdempotencyKey({
|
|
676
|
+
actionType: "view",
|
|
677
|
+
targetSequenceId: target.targetSequenceId,
|
|
678
|
+
targetCursor: target.targetCursor,
|
|
679
|
+
agentId,
|
|
680
|
+
}),
|
|
681
|
+
timeoutMs: remainingMs,
|
|
682
|
+
signal: controller.signal,
|
|
683
|
+
});
|
|
684
|
+
if (!result?.ok) {
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
} finally {
|
|
689
|
+
clearTimeout(timeoutHandle);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function recordSessionReadViews(sessionId, events = [], {
|
|
629
694
|
targetPath,
|
|
630
695
|
agentId,
|
|
631
696
|
enabled = false,
|
|
632
697
|
maxTargets = 50,
|
|
698
|
+
timeoutMs = SESSION_READ_AUTO_VIEW_TIMEOUT_MS,
|
|
633
699
|
} = {}) {
|
|
634
700
|
const maxAutoViewTargets = Math.max(0, Math.min(200, Number(maxTargets) || 0));
|
|
635
701
|
const summary = {
|
|
@@ -641,6 +707,8 @@ async function recordSessionReadViews(sessionId, events = [], {
|
|
|
641
707
|
duplicates: 0,
|
|
642
708
|
failed: 0,
|
|
643
709
|
skipped: 0,
|
|
710
|
+
queued: 0,
|
|
711
|
+
background: false,
|
|
644
712
|
reason: "",
|
|
645
713
|
};
|
|
646
714
|
if (!summary.enabled) {
|
|
@@ -662,42 +730,31 @@ async function recordSessionReadViews(sessionId, events = [], {
|
|
|
662
730
|
|
|
663
731
|
const writeTargets = maxAutoViewTargets > 0 ? targets.slice(-maxAutoViewTargets) : [];
|
|
664
732
|
summary.skipped = Math.max(0, targets.length - writeTargets.length);
|
|
733
|
+
summary.queued = writeTargets.length;
|
|
665
734
|
if (summary.skipped > 0) {
|
|
666
735
|
summary.reason = "target_cap_reached";
|
|
667
736
|
}
|
|
668
737
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
targetPath,
|
|
673
|
-
targetSequenceId: target.targetSequenceId,
|
|
674
|
-
targetCursor: target.targetCursor,
|
|
675
|
-
metadata: {
|
|
676
|
-
source: "cli_read",
|
|
677
|
-
agentId: summary.agentId,
|
|
678
|
-
},
|
|
679
|
-
idempotencyKey: defaultActionIdempotencyKey({
|
|
680
|
-
actionType: "view",
|
|
681
|
-
targetSequenceId: target.targetSequenceId,
|
|
682
|
-
targetCursor: target.targetCursor,
|
|
683
|
-
agentId: summary.agentId,
|
|
684
|
-
}),
|
|
685
|
-
timeoutMs: 15_000,
|
|
686
|
-
});
|
|
687
|
-
summary.attempted += 1;
|
|
688
|
-
if (result?.ok && result.action) {
|
|
689
|
-
summary.recorded += 1;
|
|
690
|
-
if (result.duplicate) {
|
|
691
|
-
summary.duplicates += 1;
|
|
692
|
-
}
|
|
693
|
-
continue;
|
|
738
|
+
if (writeTargets.length === 0) {
|
|
739
|
+
if (!summary.reason) {
|
|
740
|
+
summary.reason = "no_targets";
|
|
694
741
|
}
|
|
695
|
-
summary
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
742
|
+
return summary;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
summary.background = true;
|
|
746
|
+
if (!summary.reason) {
|
|
747
|
+
summary.reason = "queued_best_effort";
|
|
699
748
|
}
|
|
700
749
|
|
|
750
|
+
void writeSessionReadViews(sessionId, writeTargets, {
|
|
751
|
+
targetPath,
|
|
752
|
+
agentId: summary.agentId,
|
|
753
|
+
timeoutMs,
|
|
754
|
+
}).catch(() => {
|
|
755
|
+
// Best-effort view receipts must never affect transcript rendering.
|
|
756
|
+
});
|
|
757
|
+
|
|
701
758
|
return summary;
|
|
702
759
|
}
|
|
703
760
|
|
|
@@ -4652,11 +4709,12 @@ export function registerSessionCommand(program) {
|
|
|
4652
4709
|
: displayEvents.filter((event) => !isSessionControlEvent(event));
|
|
4653
4710
|
const hiddenControlEventCount = displayEvents.length - transcriptEvents.length;
|
|
4654
4711
|
const events = mergeSessionActionEvents(transcriptEvents, actionEvents).slice(-tail);
|
|
4655
|
-
const autoView =
|
|
4712
|
+
const autoView = recordSessionReadViews(normalizedSessionId, events, {
|
|
4656
4713
|
targetPath,
|
|
4657
4714
|
agentId: readAgentId,
|
|
4658
4715
|
enabled: Boolean(options.remote && options.view !== false),
|
|
4659
4716
|
maxTargets: process.env.SENTINELAYER_SESSION_READ_VIEW_MAX_TARGETS || 50,
|
|
4717
|
+
timeoutMs: process.env.SENTINELAYER_SESSION_READ_VIEW_TIMEOUT_MS || SESSION_READ_AUTO_VIEW_TIMEOUT_MS,
|
|
4660
4718
|
});
|
|
4661
4719
|
const remoteVerified = Boolean(
|
|
4662
4720
|
options.remote &&
|
package/src/session/listener.js
CHANGED
|
@@ -311,6 +311,7 @@ export async function listenSessionEvents({
|
|
|
311
311
|
const latest = await _pollLatest(normalizedSessionId, {
|
|
312
312
|
targetPath,
|
|
313
313
|
limit: 1,
|
|
314
|
+
forceCircuitProbe: true,
|
|
314
315
|
});
|
|
315
316
|
if (!latest?.ok) {
|
|
316
317
|
throw new Error(`Unable to start listener from the latest event (${latest?.reason || "unknown"}).`);
|
package/src/session/sync.js
CHANGED
|
@@ -172,17 +172,6 @@ function resolveGrantRole(rawRole) {
|
|
|
172
172
|
return AUTO_GRANT_DEFAULT_ROLE;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
async function readResponseJsonSafely(response) {
|
|
176
|
-
if (!response || typeof response.json !== "function") {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
try {
|
|
180
|
-
return await response.json();
|
|
181
|
-
} catch {
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
175
|
function isIdentityForgeryBody(body) {
|
|
187
176
|
if (!body || typeof body !== "object") return false;
|
|
188
177
|
const error = body.error;
|
|
@@ -412,6 +401,184 @@ function enforceRollingLimit(windowByKey, key, {
|
|
|
412
401
|
};
|
|
413
402
|
}
|
|
414
403
|
|
|
404
|
+
function makeSyncTimeoutError(reason) {
|
|
405
|
+
const error = new Error(normalizeString(reason) || "request_timeout");
|
|
406
|
+
error.name = "AbortError";
|
|
407
|
+
error.code = "SYNC_REQUEST_TIMEOUT";
|
|
408
|
+
return error;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function combineAbortSignals(signals = []) {
|
|
412
|
+
const activeSignals = signals.filter((signal) => signal && typeof signal === "object");
|
|
413
|
+
if (activeSignals.length === 0) return undefined;
|
|
414
|
+
if (activeSignals.length === 1) return activeSignals[0];
|
|
415
|
+
if (typeof AbortSignal !== "undefined" && typeof AbortSignal.any === "function") {
|
|
416
|
+
return AbortSignal.any(activeSignals);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const controller = new AbortController();
|
|
420
|
+
const abort = () => {
|
|
421
|
+
try {
|
|
422
|
+
controller.abort();
|
|
423
|
+
} catch {
|
|
424
|
+
// ignore
|
|
425
|
+
}
|
|
426
|
+
for (const signal of activeSignals) {
|
|
427
|
+
try {
|
|
428
|
+
signal.removeEventListener("abort", abort);
|
|
429
|
+
} catch {
|
|
430
|
+
// ignore
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
for (const signal of activeSignals) {
|
|
435
|
+
if (signal.aborted) {
|
|
436
|
+
abort();
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
441
|
+
} catch {
|
|
442
|
+
// ignore
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return controller.signal;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function abortRequestController(controller, reason) {
|
|
449
|
+
if (!controller || controller.signal?.aborted) return;
|
|
450
|
+
try {
|
|
451
|
+
controller.abort(makeSyncTimeoutError(reason));
|
|
452
|
+
} catch {
|
|
453
|
+
try {
|
|
454
|
+
controller.abort();
|
|
455
|
+
} catch {
|
|
456
|
+
// ignore
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function cancelResponseBody(response) {
|
|
462
|
+
try {
|
|
463
|
+
if (response?.body && typeof response.body.cancel === "function") {
|
|
464
|
+
void response.body.cancel();
|
|
465
|
+
}
|
|
466
|
+
} catch {
|
|
467
|
+
// Best-effort socket/body cleanup only.
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function isSyncTimeoutError(error) {
|
|
472
|
+
return error?.code === "SYNC_REQUEST_TIMEOUT";
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function withTimeout(promise, timeoutMs, { reason = "request_timeout", onTimeout = null } = {}) {
|
|
476
|
+
const resolvedTimeoutMs = normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS);
|
|
477
|
+
return new Promise((resolve, reject) => {
|
|
478
|
+
let settled = false;
|
|
479
|
+
const timeoutHandle = setTimeout(() => {
|
|
480
|
+
if (settled) return;
|
|
481
|
+
settled = true;
|
|
482
|
+
try {
|
|
483
|
+
onTimeout?.();
|
|
484
|
+
} catch {
|
|
485
|
+
// ignore
|
|
486
|
+
}
|
|
487
|
+
reject(makeSyncTimeoutError(reason));
|
|
488
|
+
}, resolvedTimeoutMs);
|
|
489
|
+
const finish = (fn, value) => {
|
|
490
|
+
if (settled) return;
|
|
491
|
+
settled = true;
|
|
492
|
+
clearTimeout(timeoutHandle);
|
|
493
|
+
fn(value);
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
Promise.resolve(promise).then(
|
|
497
|
+
(value) => finish(resolve, value),
|
|
498
|
+
(error) => finish(reject, error),
|
|
499
|
+
);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
async function readResponseJsonWithTimeout(
|
|
504
|
+
response,
|
|
505
|
+
timeoutMs,
|
|
506
|
+
{
|
|
507
|
+
timeoutReason = "response_body_timeout",
|
|
508
|
+
onTimeout = null,
|
|
509
|
+
fallback = undefined,
|
|
510
|
+
} = {},
|
|
511
|
+
) {
|
|
512
|
+
if (!response || typeof response.json !== "function") {
|
|
513
|
+
return fallback;
|
|
514
|
+
}
|
|
515
|
+
try {
|
|
516
|
+
return await withTimeout(response.json(), timeoutMs, {
|
|
517
|
+
reason: timeoutReason,
|
|
518
|
+
onTimeout: () => {
|
|
519
|
+
try {
|
|
520
|
+
onTimeout?.();
|
|
521
|
+
} finally {
|
|
522
|
+
cancelResponseBody(response);
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
} catch (error) {
|
|
527
|
+
if (isSyncTimeoutError(error)) {
|
|
528
|
+
throw error;
|
|
529
|
+
}
|
|
530
|
+
if (fallback !== undefined) {
|
|
531
|
+
return fallback;
|
|
532
|
+
}
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
async function readResponseJsonSafely(response, timeoutMs = DEFAULT_SYNC_TIMEOUT_MS) {
|
|
538
|
+
try {
|
|
539
|
+
return await readResponseJsonWithTimeout(response, timeoutMs, { fallback: null });
|
|
540
|
+
} catch {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
async function fetchJsonWithFullTimeout(
|
|
546
|
+
url,
|
|
547
|
+
options,
|
|
548
|
+
timeoutMs,
|
|
549
|
+
fetchImpl = fetchWithTimeout,
|
|
550
|
+
{ readErrorBody = false } = {},
|
|
551
|
+
) {
|
|
552
|
+
const resolvedTimeoutMs = normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS);
|
|
553
|
+
const controller = new AbortController();
|
|
554
|
+
const startedAtMs = Date.now();
|
|
555
|
+
let response = null;
|
|
556
|
+
const requestOptions = {
|
|
557
|
+
...options,
|
|
558
|
+
signal: combineAbortSignals([options?.signal, controller.signal]),
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
response = await withTimeout(
|
|
562
|
+
fetchImpl(url, requestOptions, resolvedTimeoutMs),
|
|
563
|
+
resolvedTimeoutMs,
|
|
564
|
+
{
|
|
565
|
+
reason: "request_timeout",
|
|
566
|
+
onTimeout: () => abortRequestController(controller, "request_timeout"),
|
|
567
|
+
},
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
let payload = {};
|
|
571
|
+
if (response?.ok || readErrorBody) {
|
|
572
|
+
const elapsedMs = Math.max(0, Date.now() - startedAtMs);
|
|
573
|
+
const remainingMs = Math.max(1, resolvedTimeoutMs - elapsedMs);
|
|
574
|
+
payload = await readResponseJsonWithTimeout(response, remainingMs, {
|
|
575
|
+
fallback: {},
|
|
576
|
+
onTimeout: () => abortRequestController(controller, "response_body_timeout"),
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
return { response, payload };
|
|
580
|
+
}
|
|
581
|
+
|
|
415
582
|
async function fetchWithTimeout(url, options, timeoutMs) {
|
|
416
583
|
const controller = new AbortController();
|
|
417
584
|
const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -421,7 +588,7 @@ async function fetchWithTimeout(url, options, timeoutMs) {
|
|
|
421
588
|
try {
|
|
422
589
|
return await fetch(url, {
|
|
423
590
|
...options,
|
|
424
|
-
signal: controller.signal,
|
|
591
|
+
signal: combineAbortSignals([options?.signal, controller.signal]),
|
|
425
592
|
});
|
|
426
593
|
} finally {
|
|
427
594
|
clearTimeout(timeoutHandle);
|
|
@@ -1008,7 +1175,7 @@ export async function pollHumanMessages(
|
|
|
1008
1175
|
const endpoint = `${apiBaseUrl}/api/v1/sessions/${encodeURIComponent(normalizedSessionId)}/human-messages?${query.toString()}`;
|
|
1009
1176
|
|
|
1010
1177
|
try {
|
|
1011
|
-
const response = await
|
|
1178
|
+
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
1012
1179
|
endpoint,
|
|
1013
1180
|
{
|
|
1014
1181
|
method: "GET",
|
|
@@ -1016,7 +1183,8 @@ export async function pollHumanMessages(
|
|
|
1016
1183
|
Authorization: `Bearer ${session.token}`,
|
|
1017
1184
|
},
|
|
1018
1185
|
},
|
|
1019
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS)
|
|
1186
|
+
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1187
|
+
fetchImpl,
|
|
1020
1188
|
);
|
|
1021
1189
|
if (!response || !response.ok) {
|
|
1022
1190
|
recordCircuitFailure(inboundCircuit, normalizedNowMs);
|
|
@@ -1027,7 +1195,6 @@ export async function pollHumanMessages(
|
|
|
1027
1195
|
cursor: normalizedSince || null,
|
|
1028
1196
|
};
|
|
1029
1197
|
}
|
|
1030
|
-
const payload = await response.json().catch(() => ({}));
|
|
1031
1198
|
recordCircuitSuccess(inboundCircuit);
|
|
1032
1199
|
|
|
1033
1200
|
const items = normalizeHumanMessageItems(payload);
|
|
@@ -1171,13 +1338,14 @@ export async function pollSessionEvents(
|
|
|
1171
1338
|
const endpoint = `${apiBaseUrl}/api/v1/sessions/${encodeURIComponent(normalizedSessionId)}/events?${query.toString()}`;
|
|
1172
1339
|
|
|
1173
1340
|
try {
|
|
1174
|
-
const response = await
|
|
1341
|
+
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
1175
1342
|
endpoint,
|
|
1176
1343
|
{
|
|
1177
1344
|
method: "GET",
|
|
1178
1345
|
headers: { Authorization: `Bearer ${session.token}` },
|
|
1179
1346
|
},
|
|
1180
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS)
|
|
1347
|
+
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1348
|
+
fetchImpl,
|
|
1181
1349
|
);
|
|
1182
1350
|
if (!response || !response.ok) {
|
|
1183
1351
|
recordCircuitFailure(inboundCircuit, normalizedNowMs);
|
|
@@ -1188,7 +1356,6 @@ export async function pollSessionEvents(
|
|
|
1188
1356
|
cursor: normalizedSince || null,
|
|
1189
1357
|
};
|
|
1190
1358
|
}
|
|
1191
|
-
const payload = await response.json().catch(() => ({}));
|
|
1192
1359
|
recordCircuitSuccess(inboundCircuit);
|
|
1193
1360
|
|
|
1194
1361
|
const items = Array.isArray(payload?.events) ? payload.events : [];
|
|
@@ -1491,13 +1658,14 @@ export async function pollSessionEventsBefore(
|
|
|
1491
1658
|
const endpoint = `${apiBaseUrl}/api/v1/sessions/${encodeURIComponent(normalizedSessionId)}/events/before?${query.toString()}`;
|
|
1492
1659
|
|
|
1493
1660
|
try {
|
|
1494
|
-
const response = await
|
|
1661
|
+
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
1495
1662
|
endpoint,
|
|
1496
1663
|
{
|
|
1497
1664
|
method: "GET",
|
|
1498
1665
|
headers: { Authorization: `Bearer ${session.token}` },
|
|
1499
1666
|
},
|
|
1500
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS)
|
|
1667
|
+
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1668
|
+
fetchImpl,
|
|
1501
1669
|
);
|
|
1502
1670
|
if (!response || !response.ok) {
|
|
1503
1671
|
recordCircuitFailure(inboundCircuit, normalizedNowMs);
|
|
@@ -1509,7 +1677,6 @@ export async function pollSessionEventsBefore(
|
|
|
1509
1677
|
beforeSequence: Number.isFinite(normalizedBeforeSequence) ? normalizedBeforeSequence : null,
|
|
1510
1678
|
};
|
|
1511
1679
|
}
|
|
1512
|
-
const payload = await response.json().catch(() => ({}));
|
|
1513
1680
|
recordCircuitSuccess(inboundCircuit);
|
|
1514
1681
|
|
|
1515
1682
|
const events = chronologicalSessionEvents(payload?.events || []);
|
|
@@ -1600,13 +1767,14 @@ export async function listSessionMessageActions(
|
|
|
1600
1767
|
const endpoint = `${apiBaseUrl}/api/v1/sessions/${encodeURIComponent(normalizedSessionId)}/actions?${query.toString()}`;
|
|
1601
1768
|
|
|
1602
1769
|
try {
|
|
1603
|
-
const response = await
|
|
1770
|
+
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
1604
1771
|
endpoint,
|
|
1605
1772
|
{
|
|
1606
1773
|
method: "GET",
|
|
1607
1774
|
headers: { Authorization: `Bearer ${session.token}` },
|
|
1608
1775
|
},
|
|
1609
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS)
|
|
1776
|
+
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1777
|
+
fetchImpl,
|
|
1610
1778
|
);
|
|
1611
1779
|
if (!response || !response.ok) {
|
|
1612
1780
|
recordCircuitFailure(inboundCircuit, normalizedNowMs);
|
|
@@ -1618,7 +1786,6 @@ export async function listSessionMessageActions(
|
|
|
1618
1786
|
projection: null,
|
|
1619
1787
|
};
|
|
1620
1788
|
}
|
|
1621
|
-
const payload = await response.json().catch(() => ({}));
|
|
1622
1789
|
recordCircuitSuccess(inboundCircuit);
|
|
1623
1790
|
const actions = Array.isArray(payload?.actions) ? payload.actions : [];
|
|
1624
1791
|
return {
|
|
@@ -1772,6 +1939,7 @@ export async function createSessionMessageAction(
|
|
|
1772
1939
|
metadata = {},
|
|
1773
1940
|
idempotencyKey = "",
|
|
1774
1941
|
timeoutMs = DEFAULT_SYNC_TIMEOUT_MS,
|
|
1942
|
+
signal = undefined,
|
|
1775
1943
|
resolveAuthSession = resolveActiveAuthSession,
|
|
1776
1944
|
fetchImpl = fetchWithTimeout,
|
|
1777
1945
|
nowMs = Date.now,
|
|
@@ -1833,7 +2001,7 @@ export async function createSessionMessageAction(
|
|
|
1833
2001
|
}
|
|
1834
2002
|
|
|
1835
2003
|
try {
|
|
1836
|
-
const response = await
|
|
2004
|
+
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
1837
2005
|
endpoint,
|
|
1838
2006
|
{
|
|
1839
2007
|
method: "POST",
|
|
@@ -1842,8 +2010,10 @@ export async function createSessionMessageAction(
|
|
|
1842
2010
|
Authorization: `Bearer ${session.token}`,
|
|
1843
2011
|
},
|
|
1844
2012
|
body: JSON.stringify(body),
|
|
2013
|
+
signal,
|
|
1845
2014
|
},
|
|
1846
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS)
|
|
2015
|
+
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
2016
|
+
fetchImpl,
|
|
1847
2017
|
);
|
|
1848
2018
|
if (!response || !response.ok) {
|
|
1849
2019
|
recordCircuitFailure(outboundCircuit, normalizedNowMs);
|
|
@@ -1853,7 +2023,6 @@ export async function createSessionMessageAction(
|
|
|
1853
2023
|
action: null,
|
|
1854
2024
|
};
|
|
1855
2025
|
}
|
|
1856
|
-
const payload = await response.json().catch(() => ({}));
|
|
1857
2026
|
recordCircuitSuccess(outboundCircuit);
|
|
1858
2027
|
return {
|
|
1859
2028
|
ok: Boolean(payload?.ok ?? true),
|
|
@@ -1955,13 +2124,14 @@ export async function searchSessionEvents(
|
|
|
1955
2124
|
const endpoint = `${apiBaseUrl}/api/v1/sessions/${encodeURIComponent(normalizedSessionId)}/events/search?${queryParams.toString()}`;
|
|
1956
2125
|
|
|
1957
2126
|
try {
|
|
1958
|
-
const response = await
|
|
2127
|
+
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
1959
2128
|
endpoint,
|
|
1960
2129
|
{
|
|
1961
2130
|
method: "GET",
|
|
1962
2131
|
headers: { Authorization: `Bearer ${session.token}` },
|
|
1963
2132
|
},
|
|
1964
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS)
|
|
2133
|
+
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
2134
|
+
fetchImpl,
|
|
1965
2135
|
);
|
|
1966
2136
|
if (!response || !response.ok) {
|
|
1967
2137
|
recordCircuitFailure(inboundCircuit, normalizedNowMs);
|
|
@@ -1975,7 +2145,6 @@ export async function searchSessionEvents(
|
|
|
1975
2145
|
nextBeforeSequence: null,
|
|
1976
2146
|
};
|
|
1977
2147
|
}
|
|
1978
|
-
const payload = await response.json().catch(() => ({}));
|
|
1979
2148
|
recordCircuitSuccess(inboundCircuit);
|
|
1980
2149
|
const results = Array.isArray(payload?.results) ? payload.results : [];
|
|
1981
2150
|
return {
|
|
@@ -2081,15 +2250,17 @@ export async function listSessionsFromApi({
|
|
|
2081
2250
|
const endpoint = `${apiBaseUrl}/api/v1/sessions?${query.toString()}`;
|
|
2082
2251
|
|
|
2083
2252
|
let response;
|
|
2253
|
+
let payload;
|
|
2084
2254
|
try {
|
|
2085
|
-
response = await
|
|
2255
|
+
({ response, payload } = await fetchJsonWithFullTimeout(
|
|
2086
2256
|
endpoint,
|
|
2087
2257
|
{
|
|
2088
2258
|
method: "GET",
|
|
2089
2259
|
headers: { Authorization: `Bearer ${session.token}` },
|
|
2090
2260
|
},
|
|
2091
2261
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
2092
|
-
|
|
2262
|
+
fetchImpl,
|
|
2263
|
+
));
|
|
2093
2264
|
} catch (err) {
|
|
2094
2265
|
return {
|
|
2095
2266
|
ok: false,
|
|
@@ -2114,7 +2285,6 @@ export async function listSessionsFromApi({
|
|
|
2114
2285
|
warnings: [],
|
|
2115
2286
|
};
|
|
2116
2287
|
}
|
|
2117
|
-
const payload = await response.json().catch(() => ({}));
|
|
2118
2288
|
const pageSessions = Array.isArray(payload?.sessions) ? payload.sessions : [];
|
|
2119
2289
|
for (const item of pageSessions) {
|
|
2120
2290
|
const sessionId = normalizeString(item?.sessionId || item?.id);
|
|
@@ -2200,15 +2370,17 @@ export async function fetchSessionFromApi(
|
|
|
2200
2370
|
const endpoint = `${apiBaseUrl}/api/v1/sessions/${encodeURIComponent(normalizedSessionId)}`;
|
|
2201
2371
|
|
|
2202
2372
|
let response;
|
|
2373
|
+
let body;
|
|
2203
2374
|
try {
|
|
2204
|
-
response = await
|
|
2375
|
+
({ response, payload: body } = await fetchJsonWithFullTimeout(
|
|
2205
2376
|
endpoint,
|
|
2206
2377
|
{
|
|
2207
2378
|
method: "GET",
|
|
2208
2379
|
headers: { Authorization: `Bearer ${session.token}` },
|
|
2209
2380
|
},
|
|
2210
2381
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
2211
|
-
|
|
2382
|
+
fetchImpl,
|
|
2383
|
+
));
|
|
2212
2384
|
} catch (err) {
|
|
2213
2385
|
return {
|
|
2214
2386
|
ok: false,
|
|
@@ -2220,7 +2392,6 @@ export async function fetchSessionFromApi(
|
|
|
2220
2392
|
return { ok: false, reason: "no_response", session: null };
|
|
2221
2393
|
}
|
|
2222
2394
|
if (response.ok) {
|
|
2223
|
-
const body = await response.json().catch(() => ({}));
|
|
2224
2395
|
const sessionPayload = body && body.session && typeof body.session === "object"
|
|
2225
2396
|
? body.session
|
|
2226
2397
|
: body && typeof body === "object"
|