@crazx/dsh-api-session-controller 0.1.2-alpha.3.zw.1 → 0.1.2-alpha.5.zw.1
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/README.i18n.yaml +2 -2
- package/README.md +2 -0
- package/README.zh.md +2 -0
- package/lib/client.js +67 -14
- package/lib/index.js +47 -27
- package/lib/typert.host.js +112 -72
- package/lib/typert.remote-client.js +31 -31
- package/lib/types/agent.d.ts +3 -5
- package/lib/types/agent.js +5 -1
- package/lib/types/client/contract/session.d.ts +3 -3
- package/lib/types/client/sessions/manager.d.ts +2 -2
- package/lib/types/client/sessions/manager.js +10 -5
- package/lib/types/client/sessions/projection-store.d.ts +4 -3
- package/lib/types/client/sessions/service.d.ts +1 -1
- package/lib/types/client/sessions/service.js +2 -1
- package/lib/types/client/sessions/session.d.ts +3 -3
- package/lib/types/client/sessions/session.js +18 -9
- package/lib/types/commands.js +14 -9
- package/lib/types/history.js +45 -24
- package/lib/types/index.d.ts +3 -5
- package/lib/types/index.js +5 -1
- package/lib/types/list.js +4 -1
- package/lib/types/types.d.ts +22 -3
- package/package.json +57 -64
- package/lib/invariant.js +0 -13
- package/lib/types/invariant.d.ts +0 -9
- package/lib/types/invariant.js +0 -12
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/api/session-controller/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: b3d22a340fff6a49270de520a043a1c8dfdbf096
|
|
6
|
+
README.zh.md: 45590a53db17b32cc59f6107957c284b53ee8302
|
package/README.md
CHANGED
package/README.zh.md
CHANGED
package/lib/client.js
CHANGED
|
@@ -176,6 +176,36 @@ window.__ModuleLoader__.load({
|
|
|
176
176
|
}
|
|
177
177
|
};
|
|
178
178
|
//#endregion
|
|
179
|
+
//#region ../../util/brand/lib/index.js
|
|
180
|
+
/**
|
|
181
|
+
* Apply a compile-time number brand without changing the value.
|
|
182
|
+
* @param value - number admitted by the domain that owns the target brand.
|
|
183
|
+
* @returns the same number with the requested compile-time brand.
|
|
184
|
+
*/
|
|
185
|
+
function brandNumber(value) {
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region ../../core/session/lib/types/types.js
|
|
190
|
+
/**
|
|
191
|
+
* Admit a numeric value as an existing Session event position.
|
|
192
|
+
* @param value - non-negative safe integer admitted by the owning log operation.
|
|
193
|
+
* @returns the same number with the Session-sequence brand.
|
|
194
|
+
*/
|
|
195
|
+
function SessionSeq(value) {
|
|
196
|
+
if (!Number.isSafeInteger(value) || value < 0 || Object.is(value, -0)) throw new TypeError(`SessionSeq must be a non-negative safe integer, got ${String(value)}`);
|
|
197
|
+
return brandNumber(value);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Admit a numeric value as a Session log offset.
|
|
201
|
+
* @param value - non-negative safe integer used as a gap or prefix length.
|
|
202
|
+
* @returns the same number with the Session-log-offset brand.
|
|
203
|
+
*/
|
|
204
|
+
function SessionLogOffset(value) {
|
|
205
|
+
if (!Number.isSafeInteger(value) || value < 0 || Object.is(value, -0)) throw new TypeError(`SessionLogOffset must be a non-negative safe integer, got ${String(value)}`);
|
|
206
|
+
return brandNumber(value);
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
179
209
|
//#region ../../util/workspace-path/lib/index.js
|
|
180
210
|
/**
|
|
181
211
|
* Read the final non-empty segment of a Workspace path for display.
|
|
@@ -721,6 +751,14 @@ window.__ModuleLoader__.load({
|
|
|
721
751
|
return true;
|
|
722
752
|
}
|
|
723
753
|
};
|
|
754
|
+
//#endregion
|
|
755
|
+
//#region lib/types/client/sessions/session.js
|
|
756
|
+
function projectionsBaseline(value) {
|
|
757
|
+
return {
|
|
758
|
+
...value,
|
|
759
|
+
asOfSeq: value.asOfSeq === -1 ? -1 : SessionSeq(value.asOfSeq)
|
|
760
|
+
};
|
|
761
|
+
}
|
|
724
762
|
/**
|
|
725
763
|
* Owns a session's event window, lifecycle state, and observable
|
|
726
764
|
* snapshot. React bindings remain outside this data layer. Features see only
|
|
@@ -731,7 +769,7 @@ window.__ModuleLoader__.load({
|
|
|
731
769
|
sessionId;
|
|
732
770
|
remote;
|
|
733
771
|
options;
|
|
734
|
-
baseSeq = 0;
|
|
772
|
+
baseSeq = SessionLogOffset(0);
|
|
735
773
|
hasMore = false;
|
|
736
774
|
openState = "cold";
|
|
737
775
|
openError = null;
|
|
@@ -972,8 +1010,16 @@ window.__ModuleLoader__.load({
|
|
|
972
1010
|
sessionId: this.sessionId,
|
|
973
1011
|
title
|
|
974
1012
|
});
|
|
975
|
-
if (result.ok)
|
|
976
|
-
|
|
1013
|
+
if (!result.ok) return result;
|
|
1014
|
+
const seq = SessionSeq(result.value.seq);
|
|
1015
|
+
this.projections.apply("title", result.value.title, seq);
|
|
1016
|
+
return {
|
|
1017
|
+
ok: true,
|
|
1018
|
+
value: {
|
|
1019
|
+
title: result.value.title,
|
|
1020
|
+
seq
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
977
1023
|
}
|
|
978
1024
|
/**
|
|
979
1025
|
* Execute one slash-command line against this session's agent — pure
|
|
@@ -1023,7 +1069,7 @@ window.__ModuleLoader__.load({
|
|
|
1023
1069
|
loadThrough(seq) {
|
|
1024
1070
|
if (this.openState !== "open" || !this.hasMore || this.baseSeq <= seq) return Promise.resolve();
|
|
1025
1071
|
if (this.jumpPromise !== null) {
|
|
1026
|
-
this.jumpTargetSeq = Math.min(this.jumpTargetSeq ?? seq, seq);
|
|
1072
|
+
this.jumpTargetSeq = SessionSeq(Math.min(this.jumpTargetSeq ?? seq, seq));
|
|
1027
1073
|
return this.jumpPromise;
|
|
1028
1074
|
}
|
|
1029
1075
|
if (this.loadingOlder) return Promise.resolve();
|
|
@@ -1067,7 +1113,7 @@ window.__ModuleLoader__.load({
|
|
|
1067
1113
|
this.openPromise = null;
|
|
1068
1114
|
this.openState = "cold";
|
|
1069
1115
|
this.openError = null;
|
|
1070
|
-
this.baseSeq = 0;
|
|
1116
|
+
this.baseSeq = SessionLogOffset(0);
|
|
1071
1117
|
this.notifier.markDirty();
|
|
1072
1118
|
await this.open();
|
|
1073
1119
|
}
|
|
@@ -1211,7 +1257,7 @@ window.__ModuleLoader__.load({
|
|
|
1211
1257
|
acceptEventChange(change) {
|
|
1212
1258
|
switch (change.type) {
|
|
1213
1259
|
case "replace":
|
|
1214
|
-
this.installWindow(change.entries, change.hasMore, change.page.projections);
|
|
1260
|
+
this.installWindow(change.entries, change.hasMore, change.page.projections === void 0 ? void 0 : projectionsBaseline(change.page.projections));
|
|
1215
1261
|
return;
|
|
1216
1262
|
case "prepend":
|
|
1217
1263
|
this.prependWindow(change.entries, change.hasMore);
|
|
@@ -1221,7 +1267,7 @@ window.__ModuleLoader__.load({
|
|
|
1221
1267
|
}
|
|
1222
1268
|
/** Replace the complete contiguous window and apply page-owned projection metadata. */
|
|
1223
1269
|
installWindow(entries, hasMore, projections) {
|
|
1224
|
-
this.baseSeq = entries[0]?.event.seq ?? 0;
|
|
1270
|
+
this.baseSeq = SessionLogOffset(entries[0]?.event.seq ?? 0);
|
|
1225
1271
|
this.hasMore = hasMore;
|
|
1226
1272
|
if (entries.some((entry) => entry.event.type === "turn/start")) this.firstPromptPendingTurn = false;
|
|
1227
1273
|
if (projections !== void 0) this.projections.seed(projections);
|
|
@@ -1231,7 +1277,7 @@ window.__ModuleLoader__.load({
|
|
|
1231
1277
|
}
|
|
1232
1278
|
/** Prepend one stream-validated history page. */
|
|
1233
1279
|
prependWindow(entries, hasMore) {
|
|
1234
|
-
this.baseSeq = entries[0]
|
|
1280
|
+
this.baseSeq = entries[0] === void 0 ? this.baseSeq : SessionLogOffset(entries[0].event.seq);
|
|
1235
1281
|
this.hasMore = hasMore;
|
|
1236
1282
|
this.eventSource.prepend(entries, hasMore);
|
|
1237
1283
|
}
|
|
@@ -1356,6 +1402,9 @@ window.__ModuleLoader__.load({
|
|
|
1356
1402
|
}
|
|
1357
1403
|
//#endregion
|
|
1358
1404
|
//#region lib/types/client/sessions/manager.js
|
|
1405
|
+
function sessionSeqCursor(value) {
|
|
1406
|
+
return value === -1 ? -1 : SessionSeq(value);
|
|
1407
|
+
}
|
|
1359
1408
|
function catalogAvailability(parentAvailable) {
|
|
1360
1409
|
return parentAvailable === void 0 ? {} : { parentAvailable };
|
|
1361
1410
|
}
|
|
@@ -1689,7 +1738,7 @@ window.__ModuleLoader__.load({
|
|
|
1689
1738
|
if (block === void 0) continue;
|
|
1690
1739
|
const store = this.projectionStore(s.sessionId);
|
|
1691
1740
|
const values = block.values;
|
|
1692
|
-
for (const key of Object.keys(values)) store.apply(key, values[key], block.asOfSeq);
|
|
1741
|
+
for (const key of Object.keys(values)) store.apply(key, values[key], sessionSeqCursor(block.asOfSeq));
|
|
1693
1742
|
}
|
|
1694
1743
|
} else {
|
|
1695
1744
|
this.listState = "error";
|
|
@@ -1840,7 +1889,7 @@ window.__ModuleLoader__.load({
|
|
|
1840
1889
|
return;
|
|
1841
1890
|
}
|
|
1842
1891
|
if (frame.type === "projection") {
|
|
1843
|
-
this.projectionStore(frame.sessionId).apply(frame.key, frame.value, frame.seq);
|
|
1892
|
+
this.projectionStore(frame.sessionId).apply(frame.key, frame.value, SessionSeq(frame.seq));
|
|
1844
1893
|
this.notifier.markDirty();
|
|
1845
1894
|
return;
|
|
1846
1895
|
}
|
|
@@ -1860,8 +1909,12 @@ window.__ModuleLoader__.load({
|
|
|
1860
1909
|
for (const [sessionId, jobs] of Object.entries(baseline.jobs)) if (jobs.length > 0) this.jobsBySession.set(sessionId, jobs);
|
|
1861
1910
|
for (const [sessionId, block] of Object.entries(baseline.projections)) {
|
|
1862
1911
|
const store = this.projectionStore(sessionId);
|
|
1863
|
-
|
|
1864
|
-
store.
|
|
1912
|
+
const asOfSeq = sessionSeqCursor(block.asOfSeq);
|
|
1913
|
+
store.truncate(asOfSeq);
|
|
1914
|
+
store.seed({
|
|
1915
|
+
...block,
|
|
1916
|
+
asOfSeq
|
|
1917
|
+
});
|
|
1865
1918
|
}
|
|
1866
1919
|
for (const [sessionId, session] of this.sessions) session.replaceControl(this.queues.get(sessionId) ?? []);
|
|
1867
1920
|
this.notifier.markDirty();
|
|
@@ -1876,7 +1929,7 @@ window.__ModuleLoader__.load({
|
|
|
1876
1929
|
const projections = summary.projections;
|
|
1877
1930
|
if (projections !== void 0) {
|
|
1878
1931
|
const store = this.projectionStore(summary.sessionId);
|
|
1879
|
-
for (const [key, value] of Object.entries(projections.values)) store.apply(key, value, projections.asOfSeq);
|
|
1932
|
+
for (const [key, value] of Object.entries(projections.values)) store.apply(key, value, sessionSeqCursor(projections.asOfSeq));
|
|
1880
1933
|
}
|
|
1881
1934
|
if (summary.origin === "subagent" && summary.parentSessionId !== void 0) this.markCatalogParentExpandable(summary.parentSessionId);
|
|
1882
1935
|
if (summary.parentSessionId !== void 0 && (this.selected === summary.parentSessionId || this.openCatalogs.has(summary.parentSessionId))) this.scheduleCatalogRefresh(summary.parentSessionId);
|
|
@@ -2400,7 +2453,7 @@ window.__ModuleLoader__.load({
|
|
|
2400
2453
|
const sourceTitle = opts.increaseTitle ? this.list.getSnapshot().byId[opts.sessionId]?.title : void 0;
|
|
2401
2454
|
const result = await this.manager.fork({
|
|
2402
2455
|
sessionId: opts.sessionId,
|
|
2403
|
-
...opts.atSeq === void 0 ? {} : { atSeq: Math.floor(opts.atSeq) }
|
|
2456
|
+
...opts.atSeq === void 0 ? {} : { atSeq: SessionSeq(Math.floor(opts.atSeq)) }
|
|
2404
2457
|
});
|
|
2405
2458
|
if (!result.ok) throw new SessionForkError(result.error, opts.sessionId);
|
|
2406
2459
|
this.projectList();
|
package/lib/index.js
CHANGED
|
@@ -8,10 +8,10 @@ import { SessionQueryError } from "@deepseek-ai/dsh-session-query";
|
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
10
10
|
import { AttachmentError, admitPromptContent } from "@deepseek-ai/dsh-attachment";
|
|
11
|
+
import { SessionLogOffset, SessionSeq, isAppendSurfaceEvent } from "@deepseek-ai/dsh-session";
|
|
11
12
|
import { SessionTitleInvalidError } from "@deepseek-ai/dsh-session-title";
|
|
12
13
|
import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
|
|
13
14
|
import { Deque } from "@deepseek-ai/dsh-deque";
|
|
14
|
-
import { isAppendSurfaceEvent } from "@deepseek-ai/dsh-session";
|
|
15
15
|
import { isChunkRow, packChunkRuns } from "@deepseek-ai/dsh-session/chunk-rows";
|
|
16
16
|
import { z as z$1 } from "zod";
|
|
17
17
|
import { isUserInvocable } from "@deepseek-ai/dsh-skill";
|
|
@@ -154,6 +154,7 @@ async function inspectApiSession(ctx, sessionId, signal) {
|
|
|
154
154
|
if (observation.header.cwd === void 0) throw new ApiSessionNotFound(`session "${sessionId}" not found`);
|
|
155
155
|
return {
|
|
156
156
|
meta: observation.header,
|
|
157
|
+
inheritedEventCount: observation.inheritedEventCount,
|
|
157
158
|
events: [...observation.events]
|
|
158
159
|
};
|
|
159
160
|
} catch (e_1) {
|
|
@@ -675,7 +676,12 @@ var SessionCommandController = class {
|
|
|
675
676
|
hasError: false
|
|
676
677
|
};
|
|
677
678
|
try {
|
|
678
|
-
|
|
679
|
+
let atSeq;
|
|
680
|
+
try {
|
|
681
|
+
atSeq = request.atSeq === void 0 ? void 0 : SessionSeq(request.atSeq);
|
|
682
|
+
} catch {
|
|
683
|
+
throw new RemoteError("gateway/bad-request", "atSeq must be a non-negative safe integer", {});
|
|
684
|
+
}
|
|
679
685
|
let observed;
|
|
680
686
|
try {
|
|
681
687
|
observed = await this.ctx.sessionQuery.observeSession(request.sessionId);
|
|
@@ -685,11 +691,10 @@ var SessionCommandController = class {
|
|
|
685
691
|
}
|
|
686
692
|
const source = __addDisposableResource$4(env_1, observed, false);
|
|
687
693
|
const lastSeq = source.events.at(-1)?.seq ?? -1;
|
|
688
|
-
const atSeq = request.atSeq;
|
|
689
694
|
const boundary = (atSeq === void 0 ? void 0 : source.events.find((event) => event.type === "turn/end" && event.seq >= atSeq)) ?? (atSeq === void 0 || atSeq > lastSeq ? source.events.findLast((event) => event.type === "turn/end") : void 0);
|
|
690
695
|
if (boundary === void 0) throw new RemoteError("session/fork-unavailable", atSeq !== void 0 && atSeq <= lastSeq ? `session "${request.sessionId}" has not completed the turn containing event ${String(atSeq)}` : `session "${request.sessionId}" has no completed turn to fork from`, { sessionId: request.sessionId });
|
|
691
|
-
let cut = boundary.seq + 1;
|
|
692
|
-
while (cut < source.events.length && source.events[cut]?.type !== "turn/start") cut
|
|
696
|
+
let cut = SessionLogOffset(boundary.seq + 1);
|
|
697
|
+
while (cut < source.events.length && source.events[cut]?.type !== "turn/start") cut = SessionLogOffset(cut + 1);
|
|
693
698
|
let workspace;
|
|
694
699
|
try {
|
|
695
700
|
workspace = await this.forkWorkspace(source.header);
|
|
@@ -703,10 +708,11 @@ var SessionCommandController = class {
|
|
|
703
708
|
await this.ctx.agents.create({
|
|
704
709
|
sessionId: childId,
|
|
705
710
|
seed: source.events.slice(0, cut),
|
|
711
|
+
inheritedEventCount: cut,
|
|
706
712
|
meta: {
|
|
707
713
|
...source.header.cwd === void 0 ? {} : { cwd: source.header.cwd },
|
|
708
714
|
parentSession: source.header.id,
|
|
709
|
-
|
|
715
|
+
isSeeded: true,
|
|
710
716
|
...composition.agentPreset === void 0 ? {} : { agentPreset: composition.agentPreset }
|
|
711
717
|
},
|
|
712
718
|
agentOptions: {
|
|
@@ -871,7 +877,7 @@ var SessionCommandController = class {
|
|
|
871
877
|
if (attached !== void 0) return {
|
|
872
878
|
id: attached.id,
|
|
873
879
|
header: attached.header,
|
|
874
|
-
events:
|
|
880
|
+
events: attached.snapshotEvents()
|
|
875
881
|
};
|
|
876
882
|
const inspected = await inspectApiSession(this.ctx, sessionId);
|
|
877
883
|
return {
|
|
@@ -1220,14 +1226,16 @@ var SessionHistoryController = class {
|
|
|
1220
1226
|
};
|
|
1221
1227
|
try {
|
|
1222
1228
|
validatePageRequest(request);
|
|
1229
|
+
const throughSeq = request.throughSeq === -1 ? -1 : SessionSeq(request.throughSeq);
|
|
1230
|
+
const beforeSeq = request.beforeSeq === void 0 ? void 0 : SessionLogOffset(request.beforeSeq);
|
|
1223
1231
|
const source = __addDisposableResource$3(env_1, await this.sourceFor(request.address, signal, false), false);
|
|
1224
1232
|
signal.throwIfAborted();
|
|
1225
1233
|
const sourceLog = source.events;
|
|
1226
1234
|
const sourceCursor = sourceLog.at(-1)?.seq ?? -1;
|
|
1227
|
-
if (
|
|
1235
|
+
if (throughSeq > sourceCursor) throw new RemoteError("gateway/bad-request", `session page through seq ${String(throughSeq)} is past cursor ${String(sourceCursor)}`, {});
|
|
1228
1236
|
/* v8 ignore next -- Session and persistence validation guarantee a dense zero-based event prefix. */
|
|
1229
|
-
if (
|
|
1230
|
-
const page = paginate(sourceLog,
|
|
1237
|
+
if (throughSeq >= 0 && sourceLog[throughSeq]?.seq !== throughSeq) throw new RemoteError("gateway/internal", `session log does not contain through seq ${String(throughSeq)}`, {});
|
|
1238
|
+
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq);
|
|
1231
1239
|
return {
|
|
1232
1240
|
records: pageRecords(page.events),
|
|
1233
1241
|
hasMore: page.hasMore
|
|
@@ -1270,7 +1278,7 @@ var SessionHistoryController = class {
|
|
|
1270
1278
|
}, { global: true });
|
|
1271
1279
|
const disposeCreated = this.ctx.on("session/created", (session) => {
|
|
1272
1280
|
if (session.id !== target) return;
|
|
1273
|
-
const suffix = session.
|
|
1281
|
+
const suffix = session.snapshotEvents(snapshotCursor === void 0 ? session.firstLiveSeq : SessionLogOffset(snapshotCursor + 1));
|
|
1274
1282
|
for (let index = suffix.length - 1; index >= 0; index -= 1) buffered.pushFront(suffix[index]);
|
|
1275
1283
|
notify();
|
|
1276
1284
|
}, { global: true });
|
|
@@ -1293,7 +1301,7 @@ var SessionHistoryController = class {
|
|
|
1293
1301
|
const page = paginate(events, void 0, request.maxMessages ?? DEFAULT_MAX_MESSAGES);
|
|
1294
1302
|
yield {
|
|
1295
1303
|
type: "snapshot",
|
|
1296
|
-
header: source.header,
|
|
1304
|
+
header: wireHeader(source.header, source.inheritedEventCount),
|
|
1297
1305
|
cursor,
|
|
1298
1306
|
records: pageRecords(page.events),
|
|
1299
1307
|
hasMore: page.hasMore,
|
|
@@ -1311,7 +1319,7 @@ var SessionHistoryController = class {
|
|
|
1311
1319
|
throw error;
|
|
1312
1320
|
}
|
|
1313
1321
|
}
|
|
1314
|
-
let
|
|
1322
|
+
let nextOffset = SessionLogOffset(cursor + 1);
|
|
1315
1323
|
while (!follower.closed && !signal.aborted) {
|
|
1316
1324
|
const item = buffered.popFront();
|
|
1317
1325
|
if (item === void 0) {
|
|
@@ -1320,9 +1328,10 @@ var SessionHistoryController = class {
|
|
|
1320
1328
|
});
|
|
1321
1329
|
continue;
|
|
1322
1330
|
}
|
|
1323
|
-
|
|
1324
|
-
if (item.seq
|
|
1325
|
-
|
|
1331
|
+
const expectedSeq = SessionSeq(nextOffset);
|
|
1332
|
+
if (item.seq < expectedSeq) continue;
|
|
1333
|
+
if (item.seq !== expectedSeq) throw new RemoteError("gateway/internal", `session event stream skipped seq ${String(expectedSeq)}`, {});
|
|
1334
|
+
nextOffset = SessionLogOffset(nextOffset + 1);
|
|
1326
1335
|
yield entryFor(item);
|
|
1327
1336
|
}
|
|
1328
1337
|
} catch (e_2) {
|
|
@@ -1350,7 +1359,7 @@ var SessionHistoryController = class {
|
|
|
1350
1359
|
rejectNotFound(address);
|
|
1351
1360
|
}
|
|
1352
1361
|
try {
|
|
1353
|
-
validateAddress(address, observation.header, observation.projections);
|
|
1362
|
+
validateAddress(address, observation.header, observation.inheritedEventCount, observation.projections);
|
|
1354
1363
|
} catch (error) {
|
|
1355
1364
|
observation[Symbol.dispose]();
|
|
1356
1365
|
throw error;
|
|
@@ -1369,8 +1378,8 @@ function projectionBlock(snapshot) {
|
|
|
1369
1378
|
};
|
|
1370
1379
|
}
|
|
1371
1380
|
function validatePageRequest(request) {
|
|
1372
|
-
if (!Number.isSafeInteger(request.throughSeq) || request.throughSeq < -1) throw new RemoteError("gateway/bad-request", "throughSeq must be an integer greater than or equal to -1", {});
|
|
1373
|
-
if (request.beforeSeq !== void 0 && (!Number.isSafeInteger(request.beforeSeq) || request.beforeSeq < 0)) throw new RemoteError("gateway/bad-request", "beforeSeq must be a non-negative safe integer", {});
|
|
1381
|
+
if (!Number.isSafeInteger(request.throughSeq) || request.throughSeq < -1 || Object.is(request.throughSeq, -0)) throw new RemoteError("gateway/bad-request", "throughSeq must be an integer greater than or equal to -1", {});
|
|
1382
|
+
if (request.beforeSeq !== void 0 && (!Number.isSafeInteger(request.beforeSeq) || request.beforeSeq < 0 || Object.is(request.beforeSeq, -0))) throw new RemoteError("gateway/bad-request", "beforeSeq must be a non-negative safe integer", {});
|
|
1374
1383
|
if (request.maxMessages !== void 0 && (!Number.isSafeInteger(request.maxMessages) || request.maxMessages <= 0)) throw new RemoteError("gateway/bad-request", "maxMessages must be a positive safe integer", {});
|
|
1375
1384
|
}
|
|
1376
1385
|
function validateFollowRequest(request) {
|
|
@@ -1379,7 +1388,7 @@ function validateFollowRequest(request) {
|
|
|
1379
1388
|
function addressId(address) {
|
|
1380
1389
|
return address.kind === "session" ? address.sessionId : address.childSessionId;
|
|
1381
1390
|
}
|
|
1382
|
-
function validateAddress(address, header, projections) {
|
|
1391
|
+
function validateAddress(address, header, inheritedEventCount, projections) {
|
|
1383
1392
|
if (address.kind === "session") {
|
|
1384
1393
|
if (header.origin === "subagent") throw new RemoteError("session/agent-busy", "subagent Sessions require their durable parent address", { reason: "use subagent delivery for this child session" });
|
|
1385
1394
|
return;
|
|
@@ -1391,7 +1400,7 @@ function validateAddress(address, header, projections) {
|
|
|
1391
1400
|
childSessionId: address.childSessionId,
|
|
1392
1401
|
reason: "corrupt"
|
|
1393
1402
|
});
|
|
1394
|
-
if (identity === void 0 || identity.seq <
|
|
1403
|
+
if (identity === void 0 || identity.seq < inheritedEventCount) throw new RemoteError("subagent/catalog-diagnostic", "subagent descriptor is unavailable", {
|
|
1395
1404
|
parentSessionId: address.parentSessionId,
|
|
1396
1405
|
childSessionId: address.childSessionId,
|
|
1397
1406
|
reason: "unsupported"
|
|
@@ -1406,18 +1415,20 @@ function rejectNotFound(address) {
|
|
|
1406
1415
|
});
|
|
1407
1416
|
}
|
|
1408
1417
|
function paginate(events, beforeSeq, maxMessages, throughSeq = events.at(-1)?.seq ?? -1) {
|
|
1409
|
-
const end = Math.min(throughSeq + 1, beforeSeq ?? throughSeq + 1);
|
|
1418
|
+
const end = SessionLogOffset(Math.min(throughSeq + 1, beforeSeq ?? throughSeq + 1));
|
|
1410
1419
|
let count = 0;
|
|
1411
|
-
let cut = 0;
|
|
1420
|
+
let cut = SessionLogOffset(0);
|
|
1412
1421
|
for (let index = end - 1; index >= 0; index--) {
|
|
1413
1422
|
const event = events[index];
|
|
1414
1423
|
if (!MESSAGE_TYPES$1.has(event.type) || !isAppendSurfaceEvent(event)) continue;
|
|
1415
1424
|
count++;
|
|
1416
1425
|
const sources = event.sourceEventSeqs;
|
|
1417
1426
|
let groupStart = event.seq;
|
|
1418
|
-
if (sources !== void 0)
|
|
1427
|
+
if (sources !== void 0) {
|
|
1428
|
+
for (const source of sources) if (source < groupStart) groupStart = source;
|
|
1429
|
+
}
|
|
1419
1430
|
if (count >= maxMessages) {
|
|
1420
|
-
cut = groupStart;
|
|
1431
|
+
cut = SessionLogOffset(groupStart);
|
|
1421
1432
|
break;
|
|
1422
1433
|
}
|
|
1423
1434
|
}
|
|
@@ -1426,6 +1437,14 @@ function paginate(events, beforeSeq, maxMessages, throughSeq = events.at(-1)?.se
|
|
|
1426
1437
|
hasMore: cut > 0
|
|
1427
1438
|
};
|
|
1428
1439
|
}
|
|
1440
|
+
/** Translate logical Session metadata to the unchanged v0 browser wire. */
|
|
1441
|
+
function wireHeader(header, inheritedEventCount) {
|
|
1442
|
+
const { isSeeded, ...wire } = header;
|
|
1443
|
+
return {
|
|
1444
|
+
...wire,
|
|
1445
|
+
...isSeeded ? { seedLength: inheritedEventCount } : {}
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1429
1448
|
function entryFor(event) {
|
|
1430
1449
|
return {
|
|
1431
1450
|
type: "event",
|
|
@@ -1891,7 +1910,7 @@ var ApiSessionList = class {
|
|
|
1891
1910
|
}
|
|
1892
1911
|
projectionsFor(header, session) {
|
|
1893
1912
|
try {
|
|
1894
|
-
const block = session === void 0 ? this.ctx.get("sessionProjectionCache")?.cachedSnapshot(header) : this.ctx.sessionProjections.cachedSnapshot(session);
|
|
1913
|
+
const block = session === void 0 ? header.isSeeded ? void 0 : this.ctx.get("sessionProjectionCache")?.cachedSnapshot(header, SessionLogOffset(0)) : this.ctx.sessionProjections.cachedSnapshot(session);
|
|
1895
1914
|
return block !== void 0 && Object.keys(block.values).length > 0 ? {
|
|
1896
1915
|
asOfSeq: block.asOfSeq,
|
|
1897
1916
|
values: block.values
|
|
@@ -2674,7 +2693,8 @@ let SessionController = (() => {
|
|
|
2674
2693
|
const attached = this.ctx.sessions.get(sessionId);
|
|
2675
2694
|
if (attached !== void 0) return Promise.resolve({
|
|
2676
2695
|
meta: attached.header,
|
|
2677
|
-
|
|
2696
|
+
inheritedEventCount: attached.inheritedEventCount,
|
|
2697
|
+
events: attached.snapshotEvents()
|
|
2678
2698
|
});
|
|
2679
2699
|
return inspectApiSession(this.ctx, sessionId, signal);
|
|
2680
2700
|
}
|