@deepseek-ai/dsh-api-session-controller 0.1.6-alpha.2 → 0.1.7-alpha.2
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 +34 -9
- package/README.zh.md +34 -9
- package/lib/client.js +774 -641
- package/lib/index.js +318 -143
- package/lib/typert.host.js +384 -186
- package/lib/typert.remote-client.d.ts +10 -5
- package/lib/typert.remote-client.js +225 -125
- package/lib/types/archived-session-gate.d.ts +30 -0
- package/lib/types/archived-session-gate.js +52 -0
- package/lib/types/client/contract/session.d.ts +11 -4
- package/lib/types/client/contract/sessions.d.ts +11 -16
- package/lib/types/client/contract/snapshot.d.ts +1 -1
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/sessions/lineage.d.ts +4 -1
- package/lib/types/client/sessions/lineage.js +2 -1
- package/lib/types/client/sessions/manager.d.ts +66 -59
- package/lib/types/client/sessions/manager.js +249 -277
- package/lib/types/client/sessions/projection-store.d.ts +38 -13
- package/lib/types/client/sessions/projection-store.js +54 -13
- package/lib/types/client/sessions/remotes.d.ts +1 -2
- package/lib/types/client/sessions/service.d.ts +23 -35
- package/lib/types/client/sessions/service.js +35 -47
- package/lib/types/client/sessions/session.d.ts +23 -19
- package/lib/types/client/sessions/session.js +158 -59
- package/lib/types/client/transport.js +5 -2
- package/lib/types/commands.d.ts +4 -2
- package/lib/types/commands.js +72 -36
- package/lib/types/control.d.ts +2 -4
- package/lib/types/control.js +2 -44
- package/lib/types/history.js +25 -10
- package/lib/types/index.d.ts +32 -5
- package/lib/types/index.js +117 -13
- package/lib/types/list.js +24 -15
- package/lib/types/skill-catalog.js +60 -50
- package/lib/types/types.d.ts +45 -25
- package/lib/types/types.js +0 -1
- package/package.json +71 -76
package/lib/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { hostname } from "node:os";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
2
3
|
import z from "@deepseek-ai/schemastery";
|
|
3
4
|
import { AssistantStreamAccumulator, ReasoningEffortId, assistantStreamChunks, createUserMessage, errorChain, freezeMessage } from "@deepseek-ai/dsh-llm";
|
|
4
|
-
import { canOpenNativePath, nativeFileManager,
|
|
5
|
+
import { canOpenNativePath, nativeFileApplications, nativeFileManager, openNativeAssociatedPath, openNativeFileApplication, revealNativePath } from "@deepseek-ai/dsh-native-command";
|
|
6
|
+
import { SessionQueryError } from "@deepseek-ai/dsh-session-query";
|
|
5
7
|
import { Remote, RemoteError, TypertRemoteService, remoteErrorOf } from "@deepseek-ai/dsh-typert-protocol";
|
|
6
8
|
import { mkdir } from "node:fs/promises";
|
|
7
9
|
import { installModelSelection } from "@deepseek-ai/dsh-agent";
|
|
8
|
-
import { SessionQueryError } from "@deepseek-ai/dsh-session-query";
|
|
9
10
|
import { randomUUID } from "node:crypto";
|
|
10
11
|
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
11
12
|
import { AttachmentError } from "@deepseek-ai/dsh-attachment";
|
|
13
|
+
import { buildForkSeed } from "@deepseek-ai/dsh-session/fork";
|
|
12
14
|
import { SessionLogOffset, SessionSeq, isAppendSurfaceEvent } from "@deepseek-ai/dsh-session";
|
|
13
15
|
import { SessionTitleInvalidError } from "@deepseek-ai/dsh-session-title";
|
|
14
16
|
import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
|
|
@@ -16,7 +18,6 @@ import { assertNever } from "@deepseek-ai/dsh-util-values";
|
|
|
16
18
|
import { Deque } from "@deepseek-ai/dsh-deque";
|
|
17
19
|
import { z as z$1 } from "zod";
|
|
18
20
|
import { isUserInvocable } from "@deepseek-ai/dsh-skill";
|
|
19
|
-
import { isAbsolute } from "node:path";
|
|
20
21
|
import { FsError } from "@deepseek-ai/dsh-fs";
|
|
21
22
|
import mime from "mime-types";
|
|
22
23
|
//#region lib/types/agent.js
|
|
@@ -550,6 +551,20 @@ var __disposeResources$3 = (function(SuppressedError) {
|
|
|
550
551
|
function hasPromptContent(content) {
|
|
551
552
|
return content.some((part) => part.type !== "text" || part.text.trim().length > 0);
|
|
552
553
|
}
|
|
554
|
+
/**
|
|
555
|
+
* Resolve the omitted-`atSeq` default to the latest completed-turn prefix,
|
|
556
|
+
* including standalone events before the next turn begins.
|
|
557
|
+
*/
|
|
558
|
+
function latestCompletedPrefixBoundary(events) {
|
|
559
|
+
const lastTurnEnd = events.findLast((event) => event.type === "turn/end");
|
|
560
|
+
if (lastTurnEnd === void 0) return void 0;
|
|
561
|
+
let boundary = lastTurnEnd.seq;
|
|
562
|
+
for (const next of events.slice(boundary + 1)) {
|
|
563
|
+
if (next.type === "turn/start" || next.type === "user/message" && next.surfaceOp === "append" || next.type === "agent/inbox/spliced") break;
|
|
564
|
+
boundary = next.seq;
|
|
565
|
+
}
|
|
566
|
+
return boundary;
|
|
567
|
+
}
|
|
553
568
|
/** Implements Session business commands delegated by the Session Controller Remote service. */
|
|
554
569
|
var SessionCommandController = class {
|
|
555
570
|
ctx;
|
|
@@ -655,8 +670,10 @@ var SessionCommandController = class {
|
|
|
655
670
|
}
|
|
656
671
|
}
|
|
657
672
|
/**
|
|
658
|
-
* Create a new ordinary Session from
|
|
659
|
-
*
|
|
673
|
+
* Create a new ordinary Session from an exact event prefix. An explicit
|
|
674
|
+
* `atSeq` is the inclusive cut; an omitted value selects the latest
|
|
675
|
+
* completed-turn prefix. An open cut receives synthetic fork closers.
|
|
676
|
+
* @param request - source Session and optional exact event boundary.
|
|
660
677
|
* @returns the new Session identity.
|
|
661
678
|
*/
|
|
662
679
|
async fork(request) {
|
|
@@ -680,10 +697,9 @@ var SessionCommandController = class {
|
|
|
680
697
|
throw new RemoteError("gateway/internal", `fork source unavailable for session "${request.sessionId}": ${String(error)}`, {});
|
|
681
698
|
}
|
|
682
699
|
const source = __addDisposableResource$3(env_1, observed, false);
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
const cut = SessionLogOffset(boundary.seq + 1);
|
|
700
|
+
const boundary = atSeq ?? latestCompletedPrefixBoundary(source.events);
|
|
701
|
+
if (boundary === void 0 || source.events[boundary]?.seq !== boundary) throw new RemoteError("session/fork-unavailable", request.atSeq === void 0 ? `session "${request.sessionId}" has no completed turn to fork from` : `event ${String(request.atSeq)} does not exist in session "${request.sessionId}" (last seq: ${String(source.events.at(-1)?.seq ?? "none")})`, { sessionId: request.sessionId });
|
|
702
|
+
const seed = buildForkSeed(source.events, boundary);
|
|
687
703
|
let workspace;
|
|
688
704
|
try {
|
|
689
705
|
workspace = await this.forkWorkspace(source.header);
|
|
@@ -696,8 +712,8 @@ var SessionCommandController = class {
|
|
|
696
712
|
const { provider, model } = this.ctx.agentDefaultModel.currentSelection();
|
|
697
713
|
await this.ctx.agents.create({
|
|
698
714
|
sessionId: childId,
|
|
699
|
-
seed
|
|
700
|
-
inheritedEventCount:
|
|
715
|
+
seed,
|
|
716
|
+
inheritedEventCount: SessionLogOffset(boundary + 1),
|
|
701
717
|
meta: {
|
|
702
718
|
...source.header.cwd === void 0 ? {} : { cwd: source.header.cwd },
|
|
703
719
|
parentSession: source.header.id,
|
|
@@ -891,6 +907,7 @@ var SessionCommandController = class {
|
|
|
891
907
|
}
|
|
892
908
|
rejectCreation(sessionId, error) {
|
|
893
909
|
if (remoteErrorOf(error) !== void 0) throw error;
|
|
910
|
+
if (error instanceof Error && error.name === "SessionAlreadyOwnedError") throw new RemoteError("session/writer-held", error.message, { sessionId });
|
|
894
911
|
if (error instanceof ApiSessionPresetConflict) throw new RemoteError("agent-preset/conflict", error.message, {
|
|
895
912
|
sessionId: error.sessionId,
|
|
896
913
|
requestedPreset: error.requestedPreset,
|
|
@@ -966,23 +983,39 @@ function imageBlockIn(content, match) {
|
|
|
966
983
|
const ref = block.attachment;
|
|
967
984
|
if (match(ref)) return ref;
|
|
968
985
|
}
|
|
969
|
-
if (block.type === "tool-result") {
|
|
970
|
-
const nested = imageBlockIn(block.content, match);
|
|
971
|
-
if (nested !== void 0) return nested;
|
|
972
|
-
}
|
|
973
986
|
}
|
|
974
987
|
}
|
|
988
|
+
/** Read only first-party declared content fields; unknown event payloads stay opaque. */
|
|
975
989
|
function imageInEvent(event, match) {
|
|
976
990
|
const data = event.data;
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
991
|
+
switch (event.type) {
|
|
992
|
+
case "user/message":
|
|
993
|
+
case "tool/ptc-dispatch": return imageBlockIn(data.content, match);
|
|
994
|
+
case "system/message":
|
|
995
|
+
case "developer/message":
|
|
996
|
+
case "tool/result":
|
|
997
|
+
case "team/message/queued": return imageBlockIn(data.message?.content, match);
|
|
998
|
+
case "agent/inbox/spliced": {
|
|
999
|
+
const messages = data.inserted;
|
|
1000
|
+
if (!Array.isArray(messages)) return void 0;
|
|
1001
|
+
for (const message of messages) {
|
|
1002
|
+
if (typeof message !== "object" || message === null || Array.isArray(message)) continue;
|
|
1003
|
+
const found = imageBlockIn(message.content, match);
|
|
1004
|
+
if (found !== void 0) return found;
|
|
1005
|
+
}
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
case "compaction/summary": return imageBlockIn(data.summary, match) ?? imageBlockIn(data.rawOutput, match);
|
|
1009
|
+
case "assistant/message": {
|
|
1010
|
+
const found = imageBlockIn(data.message?.content, match);
|
|
1011
|
+
if (found !== void 0) return found;
|
|
1012
|
+
break;
|
|
1013
|
+
}
|
|
1014
|
+
case "assistant/attempt": break;
|
|
1015
|
+
default: return;
|
|
984
1016
|
}
|
|
985
|
-
|
|
1017
|
+
const assistant = event;
|
|
1018
|
+
for (const chunk of assistantStreamChunks(assistant.data.stream, "block-end")) {
|
|
986
1019
|
const found = imageBlockIn([chunk.block], match);
|
|
987
1020
|
if (found !== void 0) return found;
|
|
988
1021
|
}
|
|
@@ -998,12 +1031,12 @@ function routeServed(ctx, provider) {
|
|
|
998
1031
|
}
|
|
999
1032
|
//#endregion
|
|
1000
1033
|
//#region lib/types/control.js
|
|
1001
|
-
/** Live Session
|
|
1034
|
+
/** Live Session projection state with reconnect baselines. */
|
|
1002
1035
|
/** Owns the Host-wide Session control stream. */
|
|
1003
1036
|
var SessionControlController = class {
|
|
1004
1037
|
ctx;
|
|
1005
1038
|
streams = /* @__PURE__ */ new Set();
|
|
1006
|
-
/** @param ctx - Host context carrying live Agent
|
|
1039
|
+
/** @param ctx - Host context carrying live Agent and projection services. */
|
|
1007
1040
|
constructor(ctx) {
|
|
1008
1041
|
this.ctx = ctx;
|
|
1009
1042
|
ctx.sessionProjections.onChanged((session, key, value, seq) => {
|
|
@@ -1015,19 +1048,6 @@ var SessionControlController = class {
|
|
|
1015
1048
|
seq
|
|
1016
1049
|
});
|
|
1017
1050
|
});
|
|
1018
|
-
ctx.inject(["jobs"], (jobsCtx) => {
|
|
1019
|
-
jobsCtx.jobs.onJobsChanged((owner) => {
|
|
1020
|
-
this.onJobsChanged(owner);
|
|
1021
|
-
});
|
|
1022
|
-
});
|
|
1023
|
-
ctx.on("session/created", (session) => {
|
|
1024
|
-
const jobs = this.jobsFor(this.ctx.agents.get(session.id));
|
|
1025
|
-
if (jobs.length > 0) this.broadcast({
|
|
1026
|
-
type: "jobs",
|
|
1027
|
-
sessionId: session.id,
|
|
1028
|
-
jobs
|
|
1029
|
-
});
|
|
1030
|
-
});
|
|
1031
1051
|
ctx.effect(() => () => {
|
|
1032
1052
|
for (const stream of this.streams) stream.end();
|
|
1033
1053
|
this.streams.clear();
|
|
@@ -1055,15 +1075,7 @@ var SessionControlController = class {
|
|
|
1055
1075
|
}
|
|
1056
1076
|
baseline() {
|
|
1057
1077
|
const sessions = this.ctx.sessions.list();
|
|
1058
|
-
|
|
1059
|
-
for (const session of sessions) {
|
|
1060
|
-
const agent = this.ctx.agents.get(session.id);
|
|
1061
|
-
jobs[session.id] = this.jobsFor(agent);
|
|
1062
|
-
}
|
|
1063
|
-
return {
|
|
1064
|
-
jobs,
|
|
1065
|
-
projections: this.projectionBaseline(sessions)
|
|
1066
|
-
};
|
|
1078
|
+
return { projections: this.projectionBaseline(sessions) };
|
|
1067
1079
|
}
|
|
1068
1080
|
projectionBaseline(sessions) {
|
|
1069
1081
|
const blocks = Object.create(null);
|
|
@@ -1076,25 +1088,6 @@ var SessionControlController = class {
|
|
|
1076
1088
|
}
|
|
1077
1089
|
return blocks;
|
|
1078
1090
|
}
|
|
1079
|
-
onJobsChanged(owner) {
|
|
1080
|
-
if (owner !== void 0) {
|
|
1081
|
-
this.broadcast({
|
|
1082
|
-
type: "jobs",
|
|
1083
|
-
sessionId: owner.id,
|
|
1084
|
-
jobs: this.jobsFor(owner)
|
|
1085
|
-
});
|
|
1086
|
-
return;
|
|
1087
|
-
}
|
|
1088
|
-
for (const session of this.ctx.sessions.list()) this.broadcast({
|
|
1089
|
-
type: "jobs",
|
|
1090
|
-
sessionId: session.id,
|
|
1091
|
-
jobs: this.jobsFor(this.ctx.agents.get(session.id))
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
jobsFor(agent) {
|
|
1095
|
-
const jobs = this.ctx.get("jobs");
|
|
1096
|
-
return jobs === void 0 ? [] : jobs.list(agent).map(jobView);
|
|
1097
|
-
}
|
|
1098
1091
|
broadcast(frame) {
|
|
1099
1092
|
for (const stream of this.streams) stream.push(frame);
|
|
1100
1093
|
}
|
|
@@ -1140,17 +1133,6 @@ var ControlQueue = class {
|
|
|
1140
1133
|
}
|
|
1141
1134
|
}
|
|
1142
1135
|
};
|
|
1143
|
-
function jobView(job) {
|
|
1144
|
-
return {
|
|
1145
|
-
id: job.id,
|
|
1146
|
-
kind: job.kind,
|
|
1147
|
-
label: job.label,
|
|
1148
|
-
status: job.status,
|
|
1149
|
-
...job.detail === void 0 ? {} : { detail: job.detail },
|
|
1150
|
-
startedAt: job.startedAt,
|
|
1151
|
-
...job.finishedAt === void 0 ? {} : { finishedAt: job.finishedAt }
|
|
1152
|
-
};
|
|
1153
|
-
}
|
|
1154
1136
|
//#endregion
|
|
1155
1137
|
//#region lib/types/assistant-stream.js
|
|
1156
1138
|
/** Process-local assistant state retained for reconnecting Web followers. */
|
|
@@ -1347,7 +1329,7 @@ var SessionHistoryController = class {
|
|
|
1347
1329
|
if (throughSeq > sourceCursor) throw new RemoteError("gateway/bad-request", `session page through seq ${String(throughSeq)} is past cursor ${String(sourceCursor)}`, {});
|
|
1348
1330
|
/* v8 ignore next -- Session and persistence validation guarantee a dense zero-based event prefix. */
|
|
1349
1331
|
if (throughSeq >= 0 && sourceLog[throughSeq]?.seq !== throughSeq) throw new RemoteError("gateway/internal", `session log does not contain through seq ${String(throughSeq)}`, {});
|
|
1350
|
-
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq);
|
|
1332
|
+
const page = paginate(sourceLog, beforeSeq, request.maxMessages ?? DEFAULT_MAX_MESSAGES, throughSeq, request.turnWindow);
|
|
1351
1333
|
return {
|
|
1352
1334
|
records: pageRecords(page.events),
|
|
1353
1335
|
hasMore: page.hasMore
|
|
@@ -1366,7 +1348,7 @@ var SessionHistoryController = class {
|
|
|
1366
1348
|
* @returns a complete opening snapshot followed by gap-free durable events and opted-in assistant frames.
|
|
1367
1349
|
*/
|
|
1368
1350
|
async *follow(request, signal) {
|
|
1369
|
-
|
|
1351
|
+
validateHistoryWindow(request);
|
|
1370
1352
|
const { address } = request;
|
|
1371
1353
|
const target = addressId(address);
|
|
1372
1354
|
const buffered = new Deque();
|
|
@@ -1426,7 +1408,7 @@ var SessionHistoryController = class {
|
|
|
1426
1408
|
signal.throwIfAborted();
|
|
1427
1409
|
const cursor = source.cursor;
|
|
1428
1410
|
snapshotCursor = cursor;
|
|
1429
|
-
const page = paginate(events, void 0, request.maxMessages ?? DEFAULT_MAX_MESSAGES);
|
|
1411
|
+
const page = paginate(events, void 0, request.maxMessages ?? DEFAULT_MAX_MESSAGES, cursor, request.turnWindow);
|
|
1430
1412
|
const assistantStream = request.assistantStream === true ? this.assistantStreams.get(target)?.snapshot() ?? { revision: 0 } : void 0;
|
|
1431
1413
|
const assistantStreamOrdinalCut = assistantStreamOrdinal;
|
|
1432
1414
|
yield {
|
|
@@ -1533,10 +1515,15 @@ function projectionBlock(snapshot) {
|
|
|
1533
1515
|
function validatePageRequest(request) {
|
|
1534
1516
|
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", {});
|
|
1535
1517
|
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", {});
|
|
1536
|
-
|
|
1518
|
+
validateHistoryWindow(request);
|
|
1537
1519
|
}
|
|
1538
|
-
function
|
|
1520
|
+
function validateHistoryWindow(request) {
|
|
1539
1521
|
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", {});
|
|
1522
|
+
const window = request.turnWindow;
|
|
1523
|
+
if (window !== void 0) {
|
|
1524
|
+
if (!Number.isSafeInteger(window.minMessages) || window.minMessages <= 0 || window.minMessages > (request.maxMessages ?? DEFAULT_MAX_MESSAGES)) throw new RemoteError("gateway/bad-request", "turnWindow.minMessages must be a positive safe integer no greater than maxMessages", {});
|
|
1525
|
+
if (!Number.isSafeInteger(window.minTurns) || window.minTurns <= 0) throw new RemoteError("gateway/bad-request", "turnWindow.minTurns must be a positive safe integer", {});
|
|
1526
|
+
}
|
|
1540
1527
|
}
|
|
1541
1528
|
function addressId(address) {
|
|
1542
1529
|
return address.kind === "session" ? address.sessionId : address.childSessionId;
|
|
@@ -1558,7 +1545,7 @@ function validateAddress(address, header, inheritedEventCount, projections) {
|
|
|
1558
1545
|
childSessionId: address.childSessionId,
|
|
1559
1546
|
reason: "unsupported"
|
|
1560
1547
|
});
|
|
1561
|
-
if (identity.mode !== address.mode) throw new RemoteError("subagent/unauthorized", "subagent mode does not match the supplied address", { childSessionId: address.childSessionId });
|
|
1548
|
+
if (address.mode !== "unknown" && identity.mode !== address.mode) throw new RemoteError("subagent/unauthorized", "subagent mode does not match the supplied address", { childSessionId: address.childSessionId });
|
|
1562
1549
|
}
|
|
1563
1550
|
function rejectNotFound(address) {
|
|
1564
1551
|
if (address.kind === "session") throw new RemoteError("session/not-found", `session "${address.sessionId}" not found`, { sessionId: address.sessionId });
|
|
@@ -1567,12 +1554,20 @@ function rejectNotFound(address) {
|
|
|
1567
1554
|
childSessionId: address.childSessionId
|
|
1568
1555
|
});
|
|
1569
1556
|
}
|
|
1570
|
-
function paginate(events, beforeSeq, maxMessages, throughSeq
|
|
1557
|
+
function paginate(events, beforeSeq, maxMessages, throughSeq, turnWindow) {
|
|
1571
1558
|
const end = SessionLogOffset(Math.min(throughSeq + 1, beforeSeq ?? throughSeq + 1));
|
|
1572
1559
|
let count = 0;
|
|
1560
|
+
let turns = 0;
|
|
1573
1561
|
let cut = SessionLogOffset(0);
|
|
1574
1562
|
for (let index = end - 1; index >= 0; index--) {
|
|
1575
1563
|
const event = events[index];
|
|
1564
|
+
if (turnWindow !== void 0 && event.type === "turn/start") {
|
|
1565
|
+
turns++;
|
|
1566
|
+
if (count >= turnWindow.minMessages && turns >= turnWindow.minTurns) {
|
|
1567
|
+
cut = SessionLogOffset(index);
|
|
1568
|
+
break;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1576
1571
|
if (!MESSAGE_TYPES$1.has(event.type) || !isAppendSurfaceEvent(event)) continue;
|
|
1577
1572
|
count++;
|
|
1578
1573
|
const sources = event.sourceEventSeqs;
|
|
@@ -1783,6 +1778,7 @@ var ApiSessionList = class {
|
|
|
1783
1778
|
return {
|
|
1784
1779
|
sessionId: session.id,
|
|
1785
1780
|
updatedAt: updatedAt(session.header, metadata),
|
|
1781
|
+
agentAvailable: this.ctx.agents.get(session.id)?.session === session,
|
|
1786
1782
|
running: this.ctx.agents.get(session.id)?.status === "running",
|
|
1787
1783
|
blank: metadata?.blank ?? session.seq === 0,
|
|
1788
1784
|
...listFields(session.header),
|
|
@@ -1819,6 +1815,7 @@ var ApiSessionList = class {
|
|
|
1819
1815
|
return {
|
|
1820
1816
|
sessionId: header.id,
|
|
1821
1817
|
updatedAt: updatedAt(header, metadata),
|
|
1818
|
+
agentAvailable: false,
|
|
1822
1819
|
running: false,
|
|
1823
1820
|
blank: metadata?.blank ?? false,
|
|
1824
1821
|
...listFields(header),
|
|
@@ -1915,18 +1912,29 @@ var ApiSessionList = class {
|
|
|
1915
1912
|
}
|
|
1916
1913
|
projectionsFor(header, session) {
|
|
1917
1914
|
try {
|
|
1915
|
+
if (session !== void 0) return hintsOf("sequenced", this.ctx.sessionProjections.cachedSnapshot(session));
|
|
1918
1916
|
const cache = this.ctx.get("sessionProjectionCache");
|
|
1919
|
-
|
|
1920
|
-
return block !== void 0 && Object.keys(block.values).length > 0 ? {
|
|
1921
|
-
asOfSeq: block.asOfSeq,
|
|
1922
|
-
values: block.values
|
|
1923
|
-
} : void 0;
|
|
1917
|
+
return hintsOf("cached", cache?.cachedSnapshot(header) ?? cache?.cachedPredecessorTitle(header));
|
|
1924
1918
|
} catch (error) {
|
|
1925
1919
|
this.ctx.logger.warn(`api-session.list: projection column for "${header.id}" failed; serving the row without it: ${String(error)}`);
|
|
1926
1920
|
return;
|
|
1927
1921
|
}
|
|
1928
1922
|
}
|
|
1929
1923
|
};
|
|
1924
|
+
/**
|
|
1925
|
+
* Wrap one projection block as Session-list hints of the named sequence space.
|
|
1926
|
+
* @param kind - which sequence space the block's watermark belongs to.
|
|
1927
|
+
* @param block - the block, or `undefined` when no source served one.
|
|
1928
|
+
* @returns the hints, or `undefined` when the block is absent or carries no value.
|
|
1929
|
+
*/
|
|
1930
|
+
function hintsOf(kind, block) {
|
|
1931
|
+
if (block === void 0 || Object.keys(block.values).length === 0) return void 0;
|
|
1932
|
+
return {
|
|
1933
|
+
kind,
|
|
1934
|
+
asOfSeq: block.asOfSeq,
|
|
1935
|
+
values: block.values
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1930
1938
|
function normalizeSearchQuery(query) {
|
|
1931
1939
|
const normalized = query.trim();
|
|
1932
1940
|
if (normalized.length === 0) throw new RemoteError("gateway/bad-request", "session search query must not be empty", {});
|
|
@@ -2211,59 +2219,71 @@ let SessionSkillCatalog = (() => {
|
|
|
2211
2219
|
* @throws RemoteError when the Session cannot be inspected or no registry can serve it.
|
|
2212
2220
|
*/
|
|
2213
2221
|
async list(request, signal) {
|
|
2214
|
-
const
|
|
2215
|
-
|
|
2216
|
-
|
|
2222
|
+
const env_1 = {
|
|
2223
|
+
stack: [],
|
|
2224
|
+
error: void 0,
|
|
2225
|
+
hasError: false
|
|
2226
|
+
};
|
|
2217
2227
|
try {
|
|
2218
|
-
const
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
hasError: false
|
|
2222
|
-
};
|
|
2228
|
+
const { sessionId } = request;
|
|
2229
|
+
let cwd;
|
|
2230
|
+
let agentPreset;
|
|
2223
2231
|
try {
|
|
2224
|
-
const
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2232
|
+
const env_2 = {
|
|
2233
|
+
stack: [],
|
|
2234
|
+
error: void 0,
|
|
2235
|
+
hasError: false
|
|
2236
|
+
};
|
|
2237
|
+
try {
|
|
2238
|
+
const observation = __addDisposableResource$1(env_2, await this.ctx.sessionQuery.observeSession(sessionId), false);
|
|
2239
|
+
if (observation.projections === void 0) throw new Error("skill catalog requires a projected Session observation");
|
|
2240
|
+
cwd = observation.header.cwd;
|
|
2241
|
+
agentPreset = observation.projections.values.agentPreset ?? void 0;
|
|
2242
|
+
} catch (e_1) {
|
|
2243
|
+
env_2.error = e_1;
|
|
2244
|
+
env_2.hasError = true;
|
|
2245
|
+
} finally {
|
|
2246
|
+
__disposeResources$1(env_2);
|
|
2247
|
+
}
|
|
2248
|
+
} catch (error) {
|
|
2249
|
+
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") throw new RemoteError("session/not-found", `session "${sessionId}" not found`, { sessionId });
|
|
2250
|
+
throw new RemoteError("gateway/internal", `session "${sessionId}" could not be inspected: ${String(error)}`, {});
|
|
2233
2251
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
}
|
|
2255
|
-
} catch (
|
|
2256
|
-
|
|
2252
|
+
if (cwd === void 0) throw new RemoteError("gateway/internal", `session "${sessionId}" has no project cwd`, {});
|
|
2253
|
+
const live = this.ctx.agents.get(sessionId);
|
|
2254
|
+
const presets = this.ctx.get("agentPresets");
|
|
2255
|
+
const skillRegistry = (live === void 0 ? void 0 : presets?.serviceFor(live, "skills")) ?? this.ctx.get("skills");
|
|
2256
|
+
if (skillRegistry === void 0) throw new RemoteError("gateway/internal", "skill registry is absent: neither this session's agent preset nor the host composition mounts @deepseek-ai/dsh-skill", {});
|
|
2257
|
+
const lease = __addDisposableResource$1(env_1, live === void 0 ? await this.scopeFor(agentPreset) : void 0, true);
|
|
2258
|
+
const scope = live ?? lease?.key;
|
|
2259
|
+
try {
|
|
2260
|
+
return { skills: (await skillRegistry.list({
|
|
2261
|
+
cwd,
|
|
2262
|
+
scope
|
|
2263
|
+
})).filter(isUserInvocable).map((skill) => ({
|
|
2264
|
+
name: skill.name,
|
|
2265
|
+
...skill.path === void 0 ? {} : { path: skill.path },
|
|
2266
|
+
description: skill.description,
|
|
2267
|
+
...skill.whenToUse === void 0 ? {} : { whenToUse: skill.whenToUse },
|
|
2268
|
+
modelInvocable: skill.invocation.modelInvocable
|
|
2269
|
+
})) };
|
|
2270
|
+
} catch (error) {
|
|
2271
|
+
throw new RemoteError("gateway/internal", `skill listing failed: ${String(error)}`, {});
|
|
2272
|
+
}
|
|
2273
|
+
} catch (e_2) {
|
|
2274
|
+
env_1.error = e_2;
|
|
2275
|
+
env_1.hasError = true;
|
|
2276
|
+
} finally {
|
|
2277
|
+
const result_1 = __disposeResources$1(env_1);
|
|
2278
|
+
if (result_1) await result_1;
|
|
2257
2279
|
}
|
|
2258
2280
|
}
|
|
2259
2281
|
/** Resolve a live or standing preset scope without creating an Agent. */
|
|
2260
|
-
async scopeFor(
|
|
2261
|
-
const live = this.ctx.agents.get(sessionId);
|
|
2262
|
-
if (live !== void 0) return live;
|
|
2282
|
+
async scopeFor(agentPreset) {
|
|
2263
2283
|
const presets = this.ctx.get("agentPresets");
|
|
2264
2284
|
if (presets === void 0) return void 0;
|
|
2265
2285
|
try {
|
|
2266
|
-
return await presets.
|
|
2286
|
+
return await presets.acquireScope(agentPreset);
|
|
2267
2287
|
} catch {
|
|
2268
2288
|
return;
|
|
2269
2289
|
}
|
|
@@ -2344,6 +2364,58 @@ const SessionMediaReferences = {
|
|
|
2344
2364
|
}
|
|
2345
2365
|
};
|
|
2346
2366
|
//#endregion
|
|
2367
|
+
//#region lib/types/archived-session-gate.js
|
|
2368
|
+
/**
|
|
2369
|
+
* The controller's admission gate for archived Sessions: an archived
|
|
2370
|
+
* Session, or a subagent descendant of one, must not run a model step until
|
|
2371
|
+
* it is restored. The work a Session still runs is reported and stopped by
|
|
2372
|
+
* its owners — the Agent registry (`turn`), the job registry seam (`job`),
|
|
2373
|
+
* the Subagent runtime (`subagent`), and the Schedule plugin (`schedule`) —
|
|
2374
|
+
* through the Workspace registry's archive-admission events.
|
|
2375
|
+
*/
|
|
2376
|
+
/**
|
|
2377
|
+
* The gate as a plugin for `ctx.plugin(...)`: it loads once the Agent
|
|
2378
|
+
* registry, Session store, and Workspace registry are available and unwinds
|
|
2379
|
+
* with the plugin's fiber. A late waking delivery to an archived Session — a
|
|
2380
|
+
* subagent settlement, a queued follow-up — proposes a step the gate rejects,
|
|
2381
|
+
* which the loop ends as `blocked` without a request; unarchiving lifts the
|
|
2382
|
+
* gate for the whole lineage.
|
|
2383
|
+
*/
|
|
2384
|
+
const ArchivedSessionGate = {
|
|
2385
|
+
name: "archived-session-gate",
|
|
2386
|
+
inject: [
|
|
2387
|
+
"agents",
|
|
2388
|
+
"sessions",
|
|
2389
|
+
"workspaceRegistry"
|
|
2390
|
+
],
|
|
2391
|
+
apply(ctx) {
|
|
2392
|
+
ctx.on("agent/pre-step", (payload, next) => underArchivedSession(ctx, payload.agent) ? Promise.resolve({ kind: "reject" }) : next());
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
/**
|
|
2396
|
+
* Whether the Agent's Session, or a Session above it in its subagent lineage,
|
|
2397
|
+
* is archived. Lineage follows the durable header fields through
|
|
2398
|
+
* subagent-origin Sessions only: a fork of an archived Session is an
|
|
2399
|
+
* independent conversation.
|
|
2400
|
+
* @param ctx - Host context.
|
|
2401
|
+
* @param agent - the Agent proposing a step.
|
|
2402
|
+
* @returns whether an archived Session owns the step.
|
|
2403
|
+
*/
|
|
2404
|
+
function underArchivedSession(ctx, agent) {
|
|
2405
|
+
const archived = ctx.workspaceRegistry.archivedSessionIds;
|
|
2406
|
+
let header = agent.session.header;
|
|
2407
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2408
|
+
while (!visited.has(header.id)) {
|
|
2409
|
+
if (archived.includes(header.id)) return true;
|
|
2410
|
+
visited.add(header.id);
|
|
2411
|
+
if (header.origin !== "subagent" || header.parentSession === void 0) return false;
|
|
2412
|
+
const parent = ctx.sessions.get(header.parentSession);
|
|
2413
|
+
if (parent === void 0) return archived.includes(header.parentSession);
|
|
2414
|
+
header = parent.header;
|
|
2415
|
+
}
|
|
2416
|
+
return false;
|
|
2417
|
+
}
|
|
2418
|
+
//#endregion
|
|
2347
2419
|
//#region lib/types/index.js
|
|
2348
2420
|
/** Session Remote owner: cold reads, explicit Agent commands, and live control state. */
|
|
2349
2421
|
var __runInitializers = function(thisArg, initializers, value) {
|
|
@@ -2453,6 +2525,7 @@ let SessionController = (() => {
|
|
|
2453
2525
|
let _modelCatalog_decorators;
|
|
2454
2526
|
let _canOpenWorkspacePath_decorators;
|
|
2455
2527
|
let _openWorkspacePath_decorators;
|
|
2528
|
+
let _workspacePathApplications_decorators;
|
|
2456
2529
|
let _rename_decorators;
|
|
2457
2530
|
let _fork_decorators;
|
|
2458
2531
|
let _prompt_decorators;
|
|
@@ -2461,6 +2534,7 @@ let SessionController = (() => {
|
|
|
2461
2534
|
let _cancel_decorators;
|
|
2462
2535
|
let _page_decorators;
|
|
2463
2536
|
let _follow_decorators;
|
|
2537
|
+
let _projections_decorators;
|
|
2464
2538
|
let _control_decorators;
|
|
2465
2539
|
return class SessionController extends _classSuper {
|
|
2466
2540
|
static {
|
|
@@ -2472,6 +2546,7 @@ let SessionController = (() => {
|
|
|
2472
2546
|
_modelCatalog_decorators = [Remote("modelCatalog")];
|
|
2473
2547
|
_canOpenWorkspacePath_decorators = [Remote];
|
|
2474
2548
|
_openWorkspacePath_decorators = [Remote("openWorkspacePath")];
|
|
2549
|
+
_workspacePathApplications_decorators = [Remote("workspacePathApplications")];
|
|
2475
2550
|
_rename_decorators = [Remote("rename")];
|
|
2476
2551
|
_fork_decorators = [Remote("fork")];
|
|
2477
2552
|
_prompt_decorators = [Remote("prompt")];
|
|
@@ -2480,6 +2555,7 @@ let SessionController = (() => {
|
|
|
2480
2555
|
_cancel_decorators = [Remote("cancel")];
|
|
2481
2556
|
_page_decorators = [Remote("page")];
|
|
2482
2557
|
_follow_decorators = [Remote({ mode: "stream" })];
|
|
2558
|
+
_projections_decorators = [Remote("projections")];
|
|
2483
2559
|
_control_decorators = [Remote({ mode: "stream" })];
|
|
2484
2560
|
__esDecorate(this, null, _list_decorators, {
|
|
2485
2561
|
kind: "method",
|
|
@@ -2558,6 +2634,17 @@ let SessionController = (() => {
|
|
|
2558
2634
|
},
|
|
2559
2635
|
metadata: _metadata
|
|
2560
2636
|
}, null, _instanceExtraInitializers);
|
|
2637
|
+
__esDecorate(this, null, _workspacePathApplications_decorators, {
|
|
2638
|
+
kind: "method",
|
|
2639
|
+
name: "workspacePathApplications",
|
|
2640
|
+
static: false,
|
|
2641
|
+
private: false,
|
|
2642
|
+
access: {
|
|
2643
|
+
has: (obj) => "workspacePathApplications" in obj,
|
|
2644
|
+
get: (obj) => obj.workspacePathApplications
|
|
2645
|
+
},
|
|
2646
|
+
metadata: _metadata
|
|
2647
|
+
}, null, _instanceExtraInitializers);
|
|
2561
2648
|
__esDecorate(this, null, _rename_decorators, {
|
|
2562
2649
|
kind: "method",
|
|
2563
2650
|
name: "rename",
|
|
@@ -2646,6 +2733,17 @@ let SessionController = (() => {
|
|
|
2646
2733
|
},
|
|
2647
2734
|
metadata: _metadata
|
|
2648
2735
|
}, null, _instanceExtraInitializers);
|
|
2736
|
+
__esDecorate(this, null, _projections_decorators, {
|
|
2737
|
+
kind: "method",
|
|
2738
|
+
name: "projections",
|
|
2739
|
+
static: false,
|
|
2740
|
+
private: false,
|
|
2741
|
+
access: {
|
|
2742
|
+
has: (obj) => "projections" in obj,
|
|
2743
|
+
get: (obj) => obj.projections
|
|
2744
|
+
},
|
|
2745
|
+
metadata: _metadata
|
|
2746
|
+
}, null, _instanceExtraInitializers);
|
|
2649
2747
|
__esDecorate(this, null, _control_decorators, {
|
|
2650
2748
|
kind: "method",
|
|
2651
2749
|
name: "control",
|
|
@@ -2669,6 +2767,7 @@ let SessionController = (() => {
|
|
|
2669
2767
|
"agents",
|
|
2670
2768
|
"attachments",
|
|
2671
2769
|
"fileUploads",
|
|
2770
|
+
"fs",
|
|
2672
2771
|
"llm",
|
|
2673
2772
|
"sessions",
|
|
2674
2773
|
"sessionProjections",
|
|
@@ -2683,6 +2782,8 @@ let SessionController = (() => {
|
|
|
2683
2782
|
history;
|
|
2684
2783
|
listState;
|
|
2685
2784
|
openPath;
|
|
2785
|
+
fileApplications;
|
|
2786
|
+
openFileApplication;
|
|
2686
2787
|
revealPath;
|
|
2687
2788
|
canOpenPath;
|
|
2688
2789
|
promotions = /* @__PURE__ */ new Set();
|
|
@@ -2709,18 +2810,26 @@ let SessionController = (() => {
|
|
|
2709
2810
|
this.promote(observation);
|
|
2710
2811
|
});
|
|
2711
2812
|
this.listState = new ApiSessionList(ctx);
|
|
2712
|
-
this.
|
|
2813
|
+
this.fileApplications = internals.fileApplications ?? nativeFileApplications;
|
|
2814
|
+
this.openFileApplication = internals.openFileApplication ?? openNativeFileApplication;
|
|
2815
|
+
this.openPath = internals.openPath ?? openNativeAssociatedPath;
|
|
2713
2816
|
this.revealPath = internals.revealPath ?? revealNativePath;
|
|
2714
2817
|
this.canOpenPath = internals.canOpenPath ?? (() => config.nativeOpen ?? (internals.openPath !== void 0 || canOpenNativePath()));
|
|
2715
2818
|
ctx.plugin(SessionFileReferences);
|
|
2716
2819
|
ctx.plugin(SessionMediaReferences);
|
|
2717
2820
|
ctx.plugin(SessionSkillCatalog);
|
|
2821
|
+
ctx.plugin(ArchivedSessionGate);
|
|
2718
2822
|
ctx.on("session/created", (session) => {
|
|
2719
2823
|
ctx.emit("api-session/added", this.listState.summaryFor(session));
|
|
2720
2824
|
});
|
|
2721
2825
|
ctx.on("session/disposed", (session) => {
|
|
2722
2826
|
ctx.emit("api-session/removed", session.id);
|
|
2723
2827
|
});
|
|
2828
|
+
const publishAgentAvailability = ({ agent }) => {
|
|
2829
|
+
if (ctx.sessions.get(agent.id) === agent.session) ctx.emit("api-session/added", this.listState.summaryFor(agent.session));
|
|
2830
|
+
};
|
|
2831
|
+
ctx.on("agent/created", publishAgentAvailability);
|
|
2832
|
+
ctx.on("agent/disposed", publishAgentAvailability);
|
|
2724
2833
|
ctx.on("agent/status", ({ agent, status }) => {
|
|
2725
2834
|
ctx.emit("api-session/status", agent.id, status === "running");
|
|
2726
2835
|
});
|
|
@@ -2846,24 +2955,53 @@ let SessionController = (() => {
|
|
|
2846
2955
|
};
|
|
2847
2956
|
}
|
|
2848
2957
|
/**
|
|
2849
|
-
*
|
|
2958
|
+
* Verify one path through the composed filesystem and open it on the Host desktop.
|
|
2850
2959
|
* @param request - path after best-effort Session workspace resolution.
|
|
2851
2960
|
* @param signal - caller lifetime; abort terminates the native command.
|
|
2852
2961
|
* @returns confirmation after the native opener accepts the path.
|
|
2853
|
-
* @throws RemoteError when the request is invalid, cancelled, or the opener fails.
|
|
2962
|
+
* @throws RemoteError when the request is invalid, has no verified Host mapping, is cancelled, or the opener fails.
|
|
2854
2963
|
*/
|
|
2855
2964
|
async openWorkspacePath(request, signal) {
|
|
2856
|
-
if (request.path.length === 0) throw new RemoteError("gateway/bad-request", "session.openWorkspacePath requires a non-empty path", {});
|
|
2857
|
-
signal.throwIfAborted();
|
|
2858
2965
|
try {
|
|
2859
|
-
|
|
2860
|
-
|
|
2966
|
+
const path = await this.verifyDesktopPath(request.path, signal);
|
|
2967
|
+
if (request.action === "reveal") await this.revealPath(path, signal);
|
|
2968
|
+
else if (request.application !== void 0) await this.openFileApplication(path, request.application, signal);
|
|
2969
|
+
else await this.openPath(path, signal);
|
|
2861
2970
|
return { opened: true };
|
|
2862
2971
|
} catch (error) {
|
|
2863
2972
|
if (signal.aborted) throw new RemoteError("gateway/cancelled", "path open was aborted", {});
|
|
2864
|
-
|
|
2973
|
+
if (error instanceof RemoteError) throw error;
|
|
2974
|
+
throw new RemoteError("gateway/internal", "path open failed", {}, { cause: error });
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* Query current file handlers on the serving desktop without activating an Agent.
|
|
2979
|
+
* @param request - file path in Host filesystem syntax.
|
|
2980
|
+
* @param signal - caller lifetime, propagated to filesystem and desktop queries.
|
|
2981
|
+
* @returns OS application names, icons, and default selection; empty when desktop opening is unavailable.
|
|
2982
|
+
* @throws RemoteError when the path is invalid, the query is cancelled, or native discovery fails.
|
|
2983
|
+
*/
|
|
2984
|
+
async workspacePathApplications(request, signal) {
|
|
2985
|
+
if (!this.canOpenPath()) return [];
|
|
2986
|
+
try {
|
|
2987
|
+
const path = await this.verifyDesktopPath(request.path, signal);
|
|
2988
|
+
return await this.fileApplications(path, signal);
|
|
2989
|
+
} catch (error) {
|
|
2990
|
+
if (signal.aborted) throw new RemoteError("gateway/cancelled", "application query was aborted", {});
|
|
2991
|
+
if (error instanceof RemoteError) throw error;
|
|
2992
|
+
throw new RemoteError("gateway/internal", "file application query failed", {}, { cause: error });
|
|
2865
2993
|
}
|
|
2866
2994
|
}
|
|
2995
|
+
async verifyDesktopPath(path, signal) {
|
|
2996
|
+
if (path.length === 0) throw new RemoteError("gateway/bad-request", "A non-empty file path is required", {});
|
|
2997
|
+
signal.throwIfAborted();
|
|
2998
|
+
const hostPath = resolve(path);
|
|
2999
|
+
const { fs } = this.ctx;
|
|
3000
|
+
const mapped = fs.processPathFromHostPath(hostPath);
|
|
3001
|
+
if (mapped === void 0 || fs.processPath(await fs.resolve(mapped, { signal })) !== hostPath) throw new RemoteError("gateway/bad-request", "Path has no verified Host path", {});
|
|
3002
|
+
signal.throwIfAborted();
|
|
3003
|
+
return hostPath;
|
|
3004
|
+
}
|
|
2867
3005
|
/**
|
|
2868
3006
|
* Rename one Session after explicitly resuming it.
|
|
2869
3007
|
* @param request - Session identity and proposed title.
|
|
@@ -2873,8 +3011,10 @@ let SessionController = (() => {
|
|
|
2873
3011
|
return this.commands.rename(request);
|
|
2874
3012
|
}
|
|
2875
3013
|
/**
|
|
2876
|
-
* Fork one cold-readable
|
|
2877
|
-
*
|
|
3014
|
+
* Fork one cold-readable exact event prefix into a new Session. An omitted
|
|
3015
|
+
* boundary selects the latest completed-turn prefix; an open cut receives
|
|
3016
|
+
* synthetic fork closers.
|
|
3017
|
+
* @param request - source Session and optional exact inclusive event boundary.
|
|
2878
3018
|
* @returns the new Session identity.
|
|
2879
3019
|
*/
|
|
2880
3020
|
fork(request) {
|
|
@@ -2934,6 +3074,41 @@ let SessionController = (() => {
|
|
|
2934
3074
|
return this.history.follow(request, signal);
|
|
2935
3075
|
}
|
|
2936
3076
|
/**
|
|
3077
|
+
* Read all registered projections without activating an Agent.
|
|
3078
|
+
* @param request - Session whose current values are required.
|
|
3079
|
+
* @param signal - cancellation for the Session observation.
|
|
3080
|
+
* @returns complete baseline, or null when the Session does not exist.
|
|
3081
|
+
*/
|
|
3082
|
+
async projections(request, signal) {
|
|
3083
|
+
const { sessionId } = request;
|
|
3084
|
+
if (sessionId.length === 0) throw new RemoteError("gateway/bad-request", "sessionId must not be empty", {});
|
|
3085
|
+
try {
|
|
3086
|
+
const env_2 = {
|
|
3087
|
+
stack: [],
|
|
3088
|
+
error: void 0,
|
|
3089
|
+
hasError: false
|
|
3090
|
+
};
|
|
3091
|
+
try {
|
|
3092
|
+
const projections = __addDisposableResource(env_2, await this.ctx.sessionQuery.observeSession(sessionId, { signal }), false).projections;
|
|
3093
|
+
if (projections === void 0) throw new RemoteError("session/projections-unavailable", "Session projections are unavailable", {});
|
|
3094
|
+
return {
|
|
3095
|
+
asOfSeq: projections.asOfSeq,
|
|
3096
|
+
values: projections.values
|
|
3097
|
+
};
|
|
3098
|
+
} catch (e_2) {
|
|
3099
|
+
env_2.error = e_2;
|
|
3100
|
+
env_2.hasError = true;
|
|
3101
|
+
} finally {
|
|
3102
|
+
__disposeResources(env_2);
|
|
3103
|
+
}
|
|
3104
|
+
} catch (error) {
|
|
3105
|
+
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") return null;
|
|
3106
|
+
if (signal.aborted || error instanceof SessionQueryError && error.code === "SESSION_QUERY_ABORTED") throw new RemoteError("gateway/cancelled", "Session projection read was cancelled", {}, { cause: error });
|
|
3107
|
+
if (error instanceof RemoteError) throw error;
|
|
3108
|
+
throw new RemoteError("gateway/internal", "Session projection read failed", {}, { cause: error });
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
/**
|
|
2937
3112
|
* Stream a complete live-control baseline followed by replacement frames.
|
|
2938
3113
|
* @param signal - cancellation owned by the Remote stream carrier.
|
|
2939
3114
|
* @returns one complete baseline followed by live replacement frames.
|