@deepseek-ai/dsh-api-session-controller 0.1.2-alpha.3 → 0.1.2-alpha.5
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) {
|
|
@@ -658,7 +659,12 @@ var SessionCommandController = class {
|
|
|
658
659
|
hasError: false
|
|
659
660
|
};
|
|
660
661
|
try {
|
|
661
|
-
|
|
662
|
+
let atSeq;
|
|
663
|
+
try {
|
|
664
|
+
atSeq = request.atSeq === void 0 ? void 0 : SessionSeq(request.atSeq);
|
|
665
|
+
} catch {
|
|
666
|
+
throw new RemoteError("gateway/bad-request", "atSeq must be a non-negative safe integer", {});
|
|
667
|
+
}
|
|
662
668
|
let observed;
|
|
663
669
|
try {
|
|
664
670
|
observed = await this.ctx.sessionQuery.observeSession(request.sessionId);
|
|
@@ -668,11 +674,10 @@ var SessionCommandController = class {
|
|
|
668
674
|
}
|
|
669
675
|
const source = __addDisposableResource$4(env_1, observed, false);
|
|
670
676
|
const lastSeq = source.events.at(-1)?.seq ?? -1;
|
|
671
|
-
const atSeq = request.atSeq;
|
|
672
677
|
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);
|
|
673
678
|
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 });
|
|
674
|
-
let cut = boundary.seq + 1;
|
|
675
|
-
while (cut < source.events.length && source.events[cut]?.type !== "turn/start") cut
|
|
679
|
+
let cut = SessionLogOffset(boundary.seq + 1);
|
|
680
|
+
while (cut < source.events.length && source.events[cut]?.type !== "turn/start") cut = SessionLogOffset(cut + 1);
|
|
676
681
|
let workspace;
|
|
677
682
|
try {
|
|
678
683
|
workspace = await this.forkWorkspace(source.header);
|
|
@@ -686,10 +691,11 @@ var SessionCommandController = class {
|
|
|
686
691
|
await this.ctx.agents.create({
|
|
687
692
|
sessionId: childId,
|
|
688
693
|
seed: source.events.slice(0, cut),
|
|
694
|
+
inheritedEventCount: cut,
|
|
689
695
|
meta: {
|
|
690
696
|
...source.header.cwd === void 0 ? {} : { cwd: source.header.cwd },
|
|
691
697
|
parentSession: source.header.id,
|
|
692
|
-
|
|
698
|
+
isSeeded: true,
|
|
693
699
|
...composition.agentPreset === void 0 ? {} : { agentPreset: composition.agentPreset }
|
|
694
700
|
},
|
|
695
701
|
agentOptions: {
|
|
@@ -854,7 +860,7 @@ var SessionCommandController = class {
|
|
|
854
860
|
if (attached !== void 0) return {
|
|
855
861
|
id: attached.id,
|
|
856
862
|
header: attached.header,
|
|
857
|
-
events:
|
|
863
|
+
events: attached.snapshotEvents()
|
|
858
864
|
};
|
|
859
865
|
const inspected = await inspectApiSession(this.ctx, sessionId);
|
|
860
866
|
return {
|
|
@@ -1203,14 +1209,16 @@ var SessionHistoryController = class {
|
|
|
1203
1209
|
};
|
|
1204
1210
|
try {
|
|
1205
1211
|
validatePageRequest(request);
|
|
1212
|
+
const throughSeq = request.throughSeq === -1 ? -1 : SessionSeq(request.throughSeq);
|
|
1213
|
+
const beforeSeq = request.beforeSeq === void 0 ? void 0 : SessionLogOffset(request.beforeSeq);
|
|
1206
1214
|
const source = __addDisposableResource$3(env_1, await this.sourceFor(request.address, signal, false), false);
|
|
1207
1215
|
signal.throwIfAborted();
|
|
1208
1216
|
const sourceLog = source.events;
|
|
1209
1217
|
const sourceCursor = sourceLog.at(-1)?.seq ?? -1;
|
|
1210
|
-
if (
|
|
1218
|
+
if (throughSeq > sourceCursor) throw new RemoteError("gateway/bad-request", `session page through seq ${String(throughSeq)} is past cursor ${String(sourceCursor)}`, {});
|
|
1211
1219
|
/* v8 ignore next -- Session and persistence validation guarantee a dense zero-based event prefix. */
|
|
1212
|
-
if (
|
|
1213
|
-
const page = paginate(sourceLog,
|
|
1220
|
+
if (throughSeq >= 0 && sourceLog[throughSeq]?.seq !== throughSeq) throw new RemoteError("gateway/internal", `session log does not contain through seq ${String(throughSeq)}`, {});
|
|
1221
|
+
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq);
|
|
1214
1222
|
return {
|
|
1215
1223
|
records: pageRecords(page.events),
|
|
1216
1224
|
hasMore: page.hasMore
|
|
@@ -1253,7 +1261,7 @@ var SessionHistoryController = class {
|
|
|
1253
1261
|
}, { global: true });
|
|
1254
1262
|
const disposeCreated = this.ctx.on("session/created", (session) => {
|
|
1255
1263
|
if (session.id !== target) return;
|
|
1256
|
-
const suffix = session.
|
|
1264
|
+
const suffix = session.snapshotEvents(snapshotCursor === void 0 ? session.firstLiveSeq : SessionLogOffset(snapshotCursor + 1));
|
|
1257
1265
|
for (let index = suffix.length - 1; index >= 0; index -= 1) buffered.pushFront(suffix[index]);
|
|
1258
1266
|
notify();
|
|
1259
1267
|
}, { global: true });
|
|
@@ -1276,7 +1284,7 @@ var SessionHistoryController = class {
|
|
|
1276
1284
|
const page = paginate(events, void 0, request.maxMessages ?? DEFAULT_MAX_MESSAGES);
|
|
1277
1285
|
yield {
|
|
1278
1286
|
type: "snapshot",
|
|
1279
|
-
header: source.header,
|
|
1287
|
+
header: wireHeader(source.header, source.inheritedEventCount),
|
|
1280
1288
|
cursor,
|
|
1281
1289
|
records: pageRecords(page.events),
|
|
1282
1290
|
hasMore: page.hasMore,
|
|
@@ -1294,7 +1302,7 @@ var SessionHistoryController = class {
|
|
|
1294
1302
|
throw error;
|
|
1295
1303
|
}
|
|
1296
1304
|
}
|
|
1297
|
-
let
|
|
1305
|
+
let nextOffset = SessionLogOffset(cursor + 1);
|
|
1298
1306
|
while (!follower.closed && !signal.aborted) {
|
|
1299
1307
|
const item = buffered.popFront();
|
|
1300
1308
|
if (item === void 0) {
|
|
@@ -1303,9 +1311,10 @@ var SessionHistoryController = class {
|
|
|
1303
1311
|
});
|
|
1304
1312
|
continue;
|
|
1305
1313
|
}
|
|
1306
|
-
|
|
1307
|
-
if (item.seq
|
|
1308
|
-
|
|
1314
|
+
const expectedSeq = SessionSeq(nextOffset);
|
|
1315
|
+
if (item.seq < expectedSeq) continue;
|
|
1316
|
+
if (item.seq !== expectedSeq) throw new RemoteError("gateway/internal", `session event stream skipped seq ${String(expectedSeq)}`, {});
|
|
1317
|
+
nextOffset = SessionLogOffset(nextOffset + 1);
|
|
1309
1318
|
yield entryFor(item);
|
|
1310
1319
|
}
|
|
1311
1320
|
} catch (e_2) {
|
|
@@ -1333,7 +1342,7 @@ var SessionHistoryController = class {
|
|
|
1333
1342
|
rejectNotFound(address);
|
|
1334
1343
|
}
|
|
1335
1344
|
try {
|
|
1336
|
-
validateAddress(address, observation.header, observation.projections);
|
|
1345
|
+
validateAddress(address, observation.header, observation.inheritedEventCount, observation.projections);
|
|
1337
1346
|
} catch (error) {
|
|
1338
1347
|
observation[Symbol.dispose]();
|
|
1339
1348
|
throw error;
|
|
@@ -1352,8 +1361,8 @@ function projectionBlock(snapshot) {
|
|
|
1352
1361
|
};
|
|
1353
1362
|
}
|
|
1354
1363
|
function validatePageRequest(request) {
|
|
1355
|
-
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", {});
|
|
1356
|
-
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", {});
|
|
1364
|
+
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", {});
|
|
1365
|
+
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", {});
|
|
1357
1366
|
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", {});
|
|
1358
1367
|
}
|
|
1359
1368
|
function validateFollowRequest(request) {
|
|
@@ -1362,7 +1371,7 @@ function validateFollowRequest(request) {
|
|
|
1362
1371
|
function addressId(address) {
|
|
1363
1372
|
return address.kind === "session" ? address.sessionId : address.childSessionId;
|
|
1364
1373
|
}
|
|
1365
|
-
function validateAddress(address, header, projections) {
|
|
1374
|
+
function validateAddress(address, header, inheritedEventCount, projections) {
|
|
1366
1375
|
if (address.kind === "session") {
|
|
1367
1376
|
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" });
|
|
1368
1377
|
return;
|
|
@@ -1374,7 +1383,7 @@ function validateAddress(address, header, projections) {
|
|
|
1374
1383
|
childSessionId: address.childSessionId,
|
|
1375
1384
|
reason: "corrupt"
|
|
1376
1385
|
});
|
|
1377
|
-
if (identity === void 0 || identity.seq <
|
|
1386
|
+
if (identity === void 0 || identity.seq < inheritedEventCount) throw new RemoteError("subagent/catalog-diagnostic", "subagent descriptor is unavailable", {
|
|
1378
1387
|
parentSessionId: address.parentSessionId,
|
|
1379
1388
|
childSessionId: address.childSessionId,
|
|
1380
1389
|
reason: "unsupported"
|
|
@@ -1389,18 +1398,20 @@ function rejectNotFound(address) {
|
|
|
1389
1398
|
});
|
|
1390
1399
|
}
|
|
1391
1400
|
function paginate(events, beforeSeq, maxMessages, throughSeq = events.at(-1)?.seq ?? -1) {
|
|
1392
|
-
const end = Math.min(throughSeq + 1, beforeSeq ?? throughSeq + 1);
|
|
1401
|
+
const end = SessionLogOffset(Math.min(throughSeq + 1, beforeSeq ?? throughSeq + 1));
|
|
1393
1402
|
let count = 0;
|
|
1394
|
-
let cut = 0;
|
|
1403
|
+
let cut = SessionLogOffset(0);
|
|
1395
1404
|
for (let index = end - 1; index >= 0; index--) {
|
|
1396
1405
|
const event = events[index];
|
|
1397
1406
|
if (!MESSAGE_TYPES$1.has(event.type) || !isAppendSurfaceEvent(event)) continue;
|
|
1398
1407
|
count++;
|
|
1399
1408
|
const sources = event.sourceEventSeqs;
|
|
1400
1409
|
let groupStart = event.seq;
|
|
1401
|
-
if (sources !== void 0)
|
|
1410
|
+
if (sources !== void 0) {
|
|
1411
|
+
for (const source of sources) if (source < groupStart) groupStart = source;
|
|
1412
|
+
}
|
|
1402
1413
|
if (count >= maxMessages) {
|
|
1403
|
-
cut = groupStart;
|
|
1414
|
+
cut = SessionLogOffset(groupStart);
|
|
1404
1415
|
break;
|
|
1405
1416
|
}
|
|
1406
1417
|
}
|
|
@@ -1409,6 +1420,14 @@ function paginate(events, beforeSeq, maxMessages, throughSeq = events.at(-1)?.se
|
|
|
1409
1420
|
hasMore: cut > 0
|
|
1410
1421
|
};
|
|
1411
1422
|
}
|
|
1423
|
+
/** Translate logical Session metadata to the unchanged v0 browser wire. */
|
|
1424
|
+
function wireHeader(header, inheritedEventCount) {
|
|
1425
|
+
const { isSeeded, ...wire } = header;
|
|
1426
|
+
return {
|
|
1427
|
+
...wire,
|
|
1428
|
+
...isSeeded ? { seedLength: inheritedEventCount } : {}
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1412
1431
|
function entryFor(event) {
|
|
1413
1432
|
return {
|
|
1414
1433
|
type: "event",
|
|
@@ -1874,7 +1893,7 @@ var ApiSessionList = class {
|
|
|
1874
1893
|
}
|
|
1875
1894
|
projectionsFor(header, session) {
|
|
1876
1895
|
try {
|
|
1877
|
-
const block = session === void 0 ? this.ctx.get("sessionProjectionCache")?.cachedSnapshot(header) : this.ctx.sessionProjections.cachedSnapshot(session);
|
|
1896
|
+
const block = session === void 0 ? header.isSeeded ? void 0 : this.ctx.get("sessionProjectionCache")?.cachedSnapshot(header, SessionLogOffset(0)) : this.ctx.sessionProjections.cachedSnapshot(session);
|
|
1878
1897
|
return block !== void 0 && Object.keys(block.values).length > 0 ? {
|
|
1879
1898
|
asOfSeq: block.asOfSeq,
|
|
1880
1899
|
values: block.values
|
|
@@ -2657,7 +2676,8 @@ let SessionController = (() => {
|
|
|
2657
2676
|
const attached = this.ctx.sessions.get(sessionId);
|
|
2658
2677
|
if (attached !== void 0) return Promise.resolve({
|
|
2659
2678
|
meta: attached.header,
|
|
2660
|
-
|
|
2679
|
+
inheritedEventCount: attached.inheritedEventCount,
|
|
2680
|
+
events: attached.snapshotEvents()
|
|
2661
2681
|
});
|
|
2662
2682
|
return inspectApiSession(this.ctx, sessionId, signal);
|
|
2663
2683
|
}
|