@deepseek-ai/dsh-api-session-controller 0.1.2-alpha.5 → 0.1.3-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 +6 -6
- package/README.zh.md +6 -6
- package/lib/client.js +592 -28
- package/lib/index.js +269 -214
- package/lib/typert.host.js +183 -141
- package/lib/typert.remote-client.js +79 -97
- package/lib/types/assistant-stream.d.ts +26 -0
- package/lib/types/assistant-stream.js +83 -0
- package/lib/types/client/contract/events.d.ts +38 -7
- package/lib/types/client/contract/events.js +21 -0
- package/lib/types/client/contract/session.d.ts +7 -7
- package/lib/types/client/contract/snapshot.d.ts +15 -2
- package/lib/types/client/index.d.ts +2 -2
- package/lib/types/client/index.js +2 -0
- package/lib/types/client/sessions/assistant-stream.d.ts +51 -0
- package/lib/types/client/sessions/assistant-stream.js +168 -0
- package/lib/types/client/sessions/history-records.d.ts +2 -2
- package/lib/types/client/sessions/history-records.js +3 -8
- package/lib/types/client/sessions/queue-mirror.js +3 -3
- package/lib/types/client/sessions/remotes.d.ts +2 -2
- package/lib/types/client/sessions/session.d.ts +3 -1
- package/lib/types/client/sessions/session.js +63 -15
- package/lib/types/client/transport.d.ts +7 -3
- package/lib/types/client/transport.js +18 -3
- package/lib/types/commands.js +98 -28
- package/lib/types/history.d.ts +2 -1
- package/lib/types/history.js +66 -37
- package/lib/types/index.d.ts +4 -4
- package/lib/types/index.js +13 -5
- package/lib/types/list.d.ts +2 -9
- package/lib/types/list.js +10 -124
- package/lib/types/types.d.ts +71 -29
- package/package.json +63 -57
package/lib/index.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
-
import { ReasoningEffortId, createUserMessage, errorChain, freezeMessage } from "@deepseek-ai/dsh-llm";
|
|
2
|
+
import { AssistantStreamAccumulator, ReasoningEffortId, assistantStreamChunks, createUserMessage, errorChain, freezeMessage } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { canOpenNativePath, openNativePath } from "@deepseek-ai/dsh-native-command";
|
|
4
4
|
import { Remote, RemoteError, TypertRemoteService, remoteErrorOf } from "@deepseek-ai/dsh-typert-protocol";
|
|
5
|
-
import { mkdir
|
|
5
|
+
import { mkdir } from "node:fs/promises";
|
|
6
6
|
import { installModelSelection } from "@deepseek-ai/dsh-agent";
|
|
7
7
|
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
|
-
import { AttachmentError
|
|
10
|
+
import { AttachmentError } from "@deepseek-ai/dsh-attachment";
|
|
11
11
|
import { SessionLogOffset, SessionSeq, isAppendSurfaceEvent } from "@deepseek-ai/dsh-session";
|
|
12
12
|
import { SessionTitleInvalidError } from "@deepseek-ai/dsh-session-title";
|
|
13
13
|
import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
|
|
14
|
+
import { assertNever } from "@deepseek-ai/dsh-util-values";
|
|
14
15
|
import { Deque } from "@deepseek-ai/dsh-deque";
|
|
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";
|
|
18
18
|
//#region lib/types/agent.js
|
|
19
19
|
/** Agent activation, composition, and model-selection policy owned by API Session. */
|
|
20
|
-
var __addDisposableResource$
|
|
20
|
+
var __addDisposableResource$4 = function(env, value, async) {
|
|
21
21
|
if (value !== null && value !== void 0) {
|
|
22
22
|
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
23
23
|
var dispose, inner;
|
|
@@ -46,7 +46,7 @@ var __addDisposableResource$5 = function(env, value, async) {
|
|
|
46
46
|
} else if (async) env.stack.push({ async: true });
|
|
47
47
|
return value;
|
|
48
48
|
};
|
|
49
|
-
var __disposeResources$
|
|
49
|
+
var __disposeResources$4 = (function(SuppressedError) {
|
|
50
50
|
return function(env) {
|
|
51
51
|
function fail(e) {
|
|
52
52
|
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
@@ -147,7 +147,7 @@ async function inspectApiSession(ctx, sessionId, signal) {
|
|
|
147
147
|
hasError: false
|
|
148
148
|
};
|
|
149
149
|
try {
|
|
150
|
-
const observation = __addDisposableResource$
|
|
150
|
+
const observation = __addDisposableResource$4(env_1, await ctx.sessionQuery.observeSession(sessionId, {
|
|
151
151
|
...signal === void 0 ? {} : { signal },
|
|
152
152
|
projectionMode: "none"
|
|
153
153
|
}), false);
|
|
@@ -161,7 +161,7 @@ async function inspectApiSession(ctx, sessionId, signal) {
|
|
|
161
161
|
env_1.error = e_1;
|
|
162
162
|
env_1.hasError = true;
|
|
163
163
|
} finally {
|
|
164
|
-
__disposeResources$
|
|
164
|
+
__disposeResources$4(env_1);
|
|
165
165
|
}
|
|
166
166
|
} catch (error) {
|
|
167
167
|
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") throw new ApiSessionNotFound(`session "${sessionId}" not found`);
|
|
@@ -375,13 +375,13 @@ var ApiSessionAgentController = class {
|
|
|
375
375
|
hasError: false
|
|
376
376
|
};
|
|
377
377
|
try {
|
|
378
|
-
const observation = __addDisposableResource$
|
|
378
|
+
const observation = __addDisposableResource$4(env_2, await this.ctx.sessionQuery.observeSession(sessionId), false);
|
|
379
379
|
return await this.resumeObserved(sessionId, observation);
|
|
380
380
|
} catch (e_2) {
|
|
381
381
|
env_2.error = e_2;
|
|
382
382
|
env_2.hasError = true;
|
|
383
383
|
} finally {
|
|
384
|
-
__disposeResources$
|
|
384
|
+
__disposeResources$4(env_2);
|
|
385
385
|
}
|
|
386
386
|
} catch (error) {
|
|
387
387
|
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") throw new ApiSessionNotFound(`session "${sessionId}" not found`);
|
|
@@ -413,7 +413,7 @@ var ApiSessionAgentController = class {
|
|
|
413
413
|
hasError: false
|
|
414
414
|
};
|
|
415
415
|
try {
|
|
416
|
-
const observation = __addDisposableResource$
|
|
416
|
+
const observation = __addDisposableResource$4(env_3, await this.ctx.sessionQuery.observeSession(sessionId), false);
|
|
417
417
|
if (hasApiSessionSubagentOwner(this.ctx, { header: observation.header }, void 0)) throw new ApiSessionSubagentOwnership(sessionId);
|
|
418
418
|
if (observation.header.cwd !== cwd) throw new ApiSessionCwdConflict(sessionId, cwd, observation.header.cwd);
|
|
419
419
|
const storedPreset = this.presetForObservation(observation);
|
|
@@ -428,7 +428,7 @@ var ApiSessionAgentController = class {
|
|
|
428
428
|
env_3.error = e_3;
|
|
429
429
|
env_3.hasError = true;
|
|
430
430
|
} finally {
|
|
431
|
-
__disposeResources$
|
|
431
|
+
__disposeResources$4(env_3);
|
|
432
432
|
}
|
|
433
433
|
} catch (error) {
|
|
434
434
|
if (!(error instanceof SessionQueryError) || error.code !== "SESSION_QUERY_SESSION_NOT_FOUND") throw error;
|
|
@@ -485,7 +485,7 @@ function agentModelSelection(selection) {
|
|
|
485
485
|
//#endregion
|
|
486
486
|
//#region lib/types/commands.js
|
|
487
487
|
/** Session commands whose activation policy is explicit at each Remote method. */
|
|
488
|
-
var __addDisposableResource$
|
|
488
|
+
var __addDisposableResource$3 = function(env, value, async) {
|
|
489
489
|
if (value !== null && value !== void 0) {
|
|
490
490
|
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
491
491
|
var dispose, inner;
|
|
@@ -514,7 +514,7 @@ var __addDisposableResource$4 = function(env, value, async) {
|
|
|
514
514
|
} else if (async) env.stack.push({ async: true });
|
|
515
515
|
return value;
|
|
516
516
|
};
|
|
517
|
-
var __disposeResources$
|
|
517
|
+
var __disposeResources$3 = (function(SuppressedError) {
|
|
518
518
|
return function(env) {
|
|
519
519
|
function fail(e) {
|
|
520
520
|
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
@@ -672,7 +672,7 @@ var SessionCommandController = class {
|
|
|
672
672
|
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") throw new RemoteError("session/not-found", `session "${request.sessionId}" not found`, { sessionId: request.sessionId });
|
|
673
673
|
throw new RemoteError("gateway/internal", `fork source unavailable for session "${request.sessionId}": ${String(error)}`, {});
|
|
674
674
|
}
|
|
675
|
-
const source = __addDisposableResource$
|
|
675
|
+
const source = __addDisposableResource$3(env_1, observed, false);
|
|
676
676
|
const lastSeq = source.events.at(-1)?.seq ?? -1;
|
|
677
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);
|
|
678
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 });
|
|
@@ -720,7 +720,7 @@ var SessionCommandController = class {
|
|
|
720
720
|
env_1.error = e_1;
|
|
721
721
|
env_1.hasError = true;
|
|
722
722
|
} finally {
|
|
723
|
-
__disposeResources$
|
|
723
|
+
__disposeResources$3(env_1);
|
|
724
724
|
}
|
|
725
725
|
}
|
|
726
726
|
/**
|
|
@@ -732,6 +732,7 @@ var SessionCommandController = class {
|
|
|
732
732
|
const clientTimeZone = request.clientTimeZone === void 0 ? void 0 : canonicalClientTimeZone(request.clientTimeZone);
|
|
733
733
|
if (request.clientTimeZone !== void 0 && clientTimeZone === void 0) throw new RemoteError("session/invalid-time-zone", "clientTimeZone must be UTC or a valid IANA Area/Location name", { value: request.clientTimeZone });
|
|
734
734
|
const agent = await this.resolveAgent(request.sessionId);
|
|
735
|
+
if (hasPromptRequest(agent, request.requestId)) return { accepted: true };
|
|
735
736
|
const selection = this.agents.selectionFor(agent).current;
|
|
736
737
|
if (!routeServed(this.ctx, selection.provider)) throw new RemoteError("session/model-unavailable", `no adapter serves provider "${selection.provider}"; select a model for this session`, {
|
|
737
738
|
provider: selection.provider,
|
|
@@ -745,17 +746,33 @@ var SessionCommandController = class {
|
|
|
745
746
|
const hasImage = request.content.some((part) => part.type === "image");
|
|
746
747
|
const admit = async () => {
|
|
747
748
|
try {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
749
|
+
const env_2 = {
|
|
750
|
+
stack: [],
|
|
751
|
+
error: void 0,
|
|
752
|
+
hasError: false
|
|
753
|
+
};
|
|
754
|
+
try {
|
|
755
|
+
if (hasImage) {
|
|
756
|
+
const current = this.agents.selectionFor(agent).current;
|
|
757
|
+
const model = await this.ctx.llm.resolveModelInfo(current.provider, current.model);
|
|
758
|
+
if (model.inputModalities !== void 0 && !model.inputModalities.includes("image")) throw new RemoteError("session/attachment-invalid", `Model "${current.model}" does not support image input.`, { reason: "MODEL_DOES_NOT_SUPPORT_IMAGES" });
|
|
759
|
+
}
|
|
760
|
+
const admission = resolvePromptFileReceipts(request.content, (receiptId) => this.ctx.fileUploads.resolve(agent, receiptId));
|
|
761
|
+
const message = createUserMessage({
|
|
762
|
+
content: await this.ctx.attachments.admitPromptContent(admission.content),
|
|
763
|
+
source
|
|
764
|
+
});
|
|
765
|
+
if (this.ctx.agents.get(agent.id) !== agent) throw new RemoteError("session/not-found", `session "${agent.id}" was disposed during prompt admission`, { sessionId: agent.id });
|
|
766
|
+
const binding = __addDisposableResource$3(env_2, this.ctx.fileUploads.bindPrompt(agent, admission.receiptIds, request.requestId), false);
|
|
767
|
+
if (request.mode === "steer") agent.steer(message);
|
|
768
|
+
else agent.followup(message);
|
|
769
|
+
binding.commit();
|
|
770
|
+
} catch (e_2) {
|
|
771
|
+
env_2.error = e_2;
|
|
772
|
+
env_2.hasError = true;
|
|
773
|
+
} finally {
|
|
774
|
+
__disposeResources$3(env_2);
|
|
752
775
|
}
|
|
753
|
-
const message = createUserMessage({
|
|
754
|
-
content: await admitPromptContent(this.ctx.attachments, request.content),
|
|
755
|
-
source
|
|
756
|
-
});
|
|
757
|
-
if (request.mode === "steer") agent.steer(message);
|
|
758
|
-
else agent.followup(message);
|
|
759
776
|
} catch (error) {
|
|
760
777
|
if (remoteErrorOf(error) !== void 0) throw error;
|
|
761
778
|
if (error instanceof AttachmentError) throw new RemoteError("session/attachment-invalid", error.message, { reason: error.code });
|
|
@@ -799,8 +816,11 @@ var SessionCommandController = class {
|
|
|
799
816
|
updateQueue(request) {
|
|
800
817
|
if (request.action.kind === "edit" && request.action.content.some((block) => block.type !== "text")) throw new RemoteError("session/attachment-invalid", "queue edits accept text content only", { reason: "QUEUE_EDIT_NON_TEXT" });
|
|
801
818
|
const agent = this.ctx.agents.get(request.sessionId);
|
|
802
|
-
if (agent !== void 0 && hasApiSessionSubagentOwner(this.ctx, agent.session, agent)) throw apiSessionSubagentOwnershipError(request.sessionId);
|
|
803
819
|
if (agent === void 0) throw new RemoteError("session/queue-item-not-found", "queued item is no longer pending", { itemId: request.itemId });
|
|
820
|
+
if (hasApiSessionSubagentOwner(this.ctx, agent.session, agent)) {
|
|
821
|
+
const identity = this.ctx.sessionProjections.snapshot(agent.session, ["subagent"]).values.subagent;
|
|
822
|
+
if (identity?.mode !== "continuable" || !agent.session.isOwnSeq(identity.seq)) throw apiSessionSubagentOwnershipError(request.sessionId);
|
|
823
|
+
}
|
|
804
824
|
const nextTurn = agent.inbox.nextTurn.find((message) => message.id === request.itemId);
|
|
805
825
|
const nextStep = agent.inbox.nextStep.find((message) => message.id === request.itemId);
|
|
806
826
|
const located = nextTurn === void 0 ? nextStep === void 0 ? void 0 : {
|
|
@@ -813,13 +833,25 @@ var SessionCommandController = class {
|
|
|
813
833
|
if (located === void 0) throw new RemoteError("session/queue-item-not-found", "queued item is no longer pending", { itemId: request.itemId });
|
|
814
834
|
const { target, message } = located;
|
|
815
835
|
if (request.action.kind === "steer" && (target !== "next-turn" || agent.status !== "running")) throw new RemoteError("session/steer-unavailable", "current turn no longer accepts steering", { itemId: request.itemId });
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
836
|
+
switch (request.action.kind) {
|
|
837
|
+
case "edit":
|
|
838
|
+
agent.inbox.replace(request.itemId, freezeMessage({
|
|
839
|
+
...message,
|
|
840
|
+
content: [...request.action.content]
|
|
841
|
+
}));
|
|
842
|
+
break;
|
|
843
|
+
case "remove": {
|
|
844
|
+
agent.inbox.remove(request.itemId);
|
|
845
|
+
const source = message.source;
|
|
846
|
+
if (source.kind === "user" && "rpcId" in source) this.ctx.fileUploads.retirePrompt(agent, source.rpcId);
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
849
|
+
case "steer":
|
|
850
|
+
agent.inbox.remove(request.itemId);
|
|
851
|
+
agent.steer(message);
|
|
852
|
+
break;
|
|
853
|
+
/* v8 ignore next 2 -- closed-union exhaustiveness guard */
|
|
854
|
+
default: assertNever(request.action, "queue action");
|
|
823
855
|
}
|
|
824
856
|
return { accepted: true };
|
|
825
857
|
}
|
|
@@ -880,6 +912,34 @@ var SessionCommandController = class {
|
|
|
880
912
|
}
|
|
881
913
|
}
|
|
882
914
|
};
|
|
915
|
+
function resolvePromptFileReceipts(content, stagedFile) {
|
|
916
|
+
const receiptIds = /* @__PURE__ */ new Set();
|
|
917
|
+
return {
|
|
918
|
+
content: content.map((part) => {
|
|
919
|
+
if (part.type !== "file") return part;
|
|
920
|
+
const attachment = stagedFile(part.receiptId);
|
|
921
|
+
if (attachment === void 0) throw new RemoteError("session/attachment-invalid", "File was not uploaded for this session.", { reason: "FILE_NOT_STAGED" });
|
|
922
|
+
receiptIds.add(part.receiptId);
|
|
923
|
+
return {
|
|
924
|
+
type: "file",
|
|
925
|
+
attachment
|
|
926
|
+
};
|
|
927
|
+
}),
|
|
928
|
+
receiptIds: [...receiptIds]
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
function hasPromptRequest(agent, requestId) {
|
|
932
|
+
const matches = (message) => {
|
|
933
|
+
const source = message.source;
|
|
934
|
+
return source.kind === "user" && "rpcId" in source && source.rpcId === requestId;
|
|
935
|
+
};
|
|
936
|
+
if (agent.inbox.nextTurn.some(matches) || agent.inbox.nextStep.some(matches)) return true;
|
|
937
|
+
return agent.session.snapshotEvents().some((event) => {
|
|
938
|
+
if (event.type !== "user/message") return false;
|
|
939
|
+
const source = event.data.source;
|
|
940
|
+
return source.kind === "user" && "rpcId" in source && source.rpcId === requestId;
|
|
941
|
+
});
|
|
942
|
+
}
|
|
883
943
|
function imageBlockIn(content, match) {
|
|
884
944
|
if (!Array.isArray(content)) return void 0;
|
|
885
945
|
for (const value of content) {
|
|
@@ -905,7 +965,10 @@ function imageInEvent(event, match) {
|
|
|
905
965
|
const found = imageBlockIn(inserted.content, match);
|
|
906
966
|
if (found !== void 0) return found;
|
|
907
967
|
}
|
|
908
|
-
|
|
968
|
+
if (event.type === "assistant/message" || event.type === "assistant/attempt") for (const chunk of assistantStreamChunks(event.data.stream, "block-end")) {
|
|
969
|
+
const found = imageBlockIn([chunk.block], match);
|
|
970
|
+
if (found !== void 0) return found;
|
|
971
|
+
}
|
|
909
972
|
}
|
|
910
973
|
function referencedImage(events, attachmentId) {
|
|
911
974
|
for (const event of events) {
|
|
@@ -1116,9 +1179,90 @@ function jobView(job) {
|
|
|
1116
1179
|
};
|
|
1117
1180
|
}
|
|
1118
1181
|
//#endregion
|
|
1182
|
+
//#region lib/types/assistant-stream.js
|
|
1183
|
+
/** Process-local assistant state retained for reconnecting Web followers. */
|
|
1184
|
+
const EMPTY_BASELINE = { revision: 0 };
|
|
1185
|
+
/**
|
|
1186
|
+
* Folds dense Agent frames and materializes one shared immutable reconnect
|
|
1187
|
+
* baseline per accepted revision.
|
|
1188
|
+
*/
|
|
1189
|
+
var SessionAssistantStreamAccumulator = class {
|
|
1190
|
+
activeAttempt;
|
|
1191
|
+
revision = 0;
|
|
1192
|
+
snapshotValue = EMPTY_BASELINE;
|
|
1193
|
+
dirty = false;
|
|
1194
|
+
/**
|
|
1195
|
+
* Fold one trusted frame from the current attached Agent lifecycle.
|
|
1196
|
+
* @param frame - next dense process-local Assistant frame.
|
|
1197
|
+
* @param durableCursor - last committed Session seq when this frame was observed.
|
|
1198
|
+
*/
|
|
1199
|
+
accept(frame, durableCursor) {
|
|
1200
|
+
if (frame.type === "start" && frame.revision === 1 && this.revision !== 0) {
|
|
1201
|
+
this.activeAttempt = void 0;
|
|
1202
|
+
this.revision = 0;
|
|
1203
|
+
}
|
|
1204
|
+
if (frame.revision !== this.revision + 1) {
|
|
1205
|
+
this.activeAttempt = void 0;
|
|
1206
|
+
this.revision = frame.revision;
|
|
1207
|
+
this.dirty = true;
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
this.revision = frame.revision;
|
|
1211
|
+
switch (frame.type) {
|
|
1212
|
+
case "start":
|
|
1213
|
+
this.activeAttempt = {
|
|
1214
|
+
attemptId: frame.attemptId,
|
|
1215
|
+
startedAfterSeq: durableCursor,
|
|
1216
|
+
turn: frame.turn,
|
|
1217
|
+
step: frame.step,
|
|
1218
|
+
stream: new AssistantStreamAccumulator(),
|
|
1219
|
+
nextIndex: 0
|
|
1220
|
+
};
|
|
1221
|
+
break;
|
|
1222
|
+
case "chunk": {
|
|
1223
|
+
const attempt = this.activeAttempt;
|
|
1224
|
+
if (attempt === void 0 || attempt.attemptId !== frame.attemptId || frame.index !== attempt.nextIndex) {
|
|
1225
|
+
this.activeAttempt = void 0;
|
|
1226
|
+
break;
|
|
1227
|
+
}
|
|
1228
|
+
attempt.stream.push({
|
|
1229
|
+
time: frame.time,
|
|
1230
|
+
chunk: frame.chunk
|
|
1231
|
+
});
|
|
1232
|
+
attempt.nextIndex += 1;
|
|
1233
|
+
break;
|
|
1234
|
+
}
|
|
1235
|
+
case "end":
|
|
1236
|
+
this.activeAttempt = void 0;
|
|
1237
|
+
break;
|
|
1238
|
+
}
|
|
1239
|
+
this.dirty = true;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Read the cached reconnect baseline, materializing it after a state change.
|
|
1243
|
+
* @returns the identity-stable baseline for the latest accepted revision.
|
|
1244
|
+
*/
|
|
1245
|
+
snapshot() {
|
|
1246
|
+
if (!this.dirty) return this.snapshotValue;
|
|
1247
|
+
this.snapshotValue = {
|
|
1248
|
+
revision: this.revision,
|
|
1249
|
+
...this.activeAttempt === void 0 ? {} : { activeAttempt: {
|
|
1250
|
+
attemptId: this.activeAttempt.attemptId,
|
|
1251
|
+
startedAfterSeq: this.activeAttempt.startedAfterSeq,
|
|
1252
|
+
turn: this.activeAttempt.turn,
|
|
1253
|
+
step: this.activeAttempt.step,
|
|
1254
|
+
nextIndex: this.activeAttempt.nextIndex,
|
|
1255
|
+
stream: this.activeAttempt.stream.snapshot()
|
|
1256
|
+
} }
|
|
1257
|
+
};
|
|
1258
|
+
this.dirty = false;
|
|
1259
|
+
return this.snapshotValue;
|
|
1260
|
+
}
|
|
1261
|
+
};
|
|
1262
|
+
//#endregion
|
|
1119
1263
|
//#region lib/types/history.js
|
|
1120
1264
|
/** Cold Session history pagination and live-event source. */
|
|
1121
|
-
var __addDisposableResource$
|
|
1265
|
+
var __addDisposableResource$2 = function(env, value, async) {
|
|
1122
1266
|
if (value !== null && value !== void 0) {
|
|
1123
1267
|
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
1124
1268
|
var dispose, inner;
|
|
@@ -1147,7 +1291,7 @@ var __addDisposableResource$3 = function(env, value, async) {
|
|
|
1147
1291
|
} else if (async) env.stack.push({ async: true });
|
|
1148
1292
|
return value;
|
|
1149
1293
|
};
|
|
1150
|
-
var __disposeResources$
|
|
1294
|
+
var __disposeResources$2 = (function(SuppressedError) {
|
|
1151
1295
|
return function(env) {
|
|
1152
1296
|
function fail(e) {
|
|
1153
1297
|
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
@@ -1183,6 +1327,7 @@ var SessionHistoryController = class {
|
|
|
1183
1327
|
ctx;
|
|
1184
1328
|
promote;
|
|
1185
1329
|
closeFollowers = /* @__PURE__ */ new Set();
|
|
1330
|
+
assistantStreams = /* @__PURE__ */ new Map();
|
|
1186
1331
|
/**
|
|
1187
1332
|
* @param ctx - Host context carrying Session query and projection services.
|
|
1188
1333
|
* @param promote - starts ordinary Session activation after snapshot delivery.
|
|
@@ -1190,6 +1335,17 @@ var SessionHistoryController = class {
|
|
|
1190
1335
|
constructor(ctx, promote) {
|
|
1191
1336
|
this.ctx = ctx;
|
|
1192
1337
|
this.promote = promote;
|
|
1338
|
+
ctx.on("agent/assistant-stream", ({ agent, frame }) => {
|
|
1339
|
+
let stream = this.assistantStreams.get(agent.session.id);
|
|
1340
|
+
if (stream === void 0) {
|
|
1341
|
+
stream = new SessionAssistantStreamAccumulator();
|
|
1342
|
+
this.assistantStreams.set(agent.session.id, stream);
|
|
1343
|
+
}
|
|
1344
|
+
stream.accept(frame, cursorBeforeNext(agent.session.seq));
|
|
1345
|
+
}, { global: true });
|
|
1346
|
+
ctx.on("agent/disposed", ({ agent }) => {
|
|
1347
|
+
this.assistantStreams.delete(agent.session.id);
|
|
1348
|
+
}, { global: true });
|
|
1193
1349
|
ctx.effect(() => () => {
|
|
1194
1350
|
for (const close of this.closeFollowers) close();
|
|
1195
1351
|
this.closeFollowers.clear();
|
|
@@ -1211,7 +1367,7 @@ var SessionHistoryController = class {
|
|
|
1211
1367
|
validatePageRequest(request);
|
|
1212
1368
|
const throughSeq = request.throughSeq === -1 ? -1 : SessionSeq(request.throughSeq);
|
|
1213
1369
|
const beforeSeq = request.beforeSeq === void 0 ? void 0 : SessionLogOffset(request.beforeSeq);
|
|
1214
|
-
const source = __addDisposableResource$
|
|
1370
|
+
const source = __addDisposableResource$2(env_1, await this.sourceFor(request.address, signal, false), false);
|
|
1215
1371
|
signal.throwIfAborted();
|
|
1216
1372
|
const sourceLog = source.events;
|
|
1217
1373
|
const sourceCursor = sourceLog.at(-1)?.seq ?? -1;
|
|
@@ -1227,14 +1383,14 @@ var SessionHistoryController = class {
|
|
|
1227
1383
|
env_1.error = e_1;
|
|
1228
1384
|
env_1.hasError = true;
|
|
1229
1385
|
} finally {
|
|
1230
|
-
__disposeResources$
|
|
1386
|
+
__disposeResources$2(env_1);
|
|
1231
1387
|
}
|
|
1232
1388
|
}
|
|
1233
1389
|
/**
|
|
1234
1390
|
* Follow events appended after an initial cursor on one durable address.
|
|
1235
1391
|
* @param request - durable address and last committed sequence already held by the caller.
|
|
1236
1392
|
* @param signal - stream cancellation owned by the Remote carrier.
|
|
1237
|
-
* @returns a complete opening snapshot followed by gap-free
|
|
1393
|
+
* @returns a complete opening snapshot followed by gap-free durable events and opted-in assistant frames.
|
|
1238
1394
|
*/
|
|
1239
1395
|
async *follow(request, signal) {
|
|
1240
1396
|
validateFollowRequest(request);
|
|
@@ -1242,6 +1398,7 @@ var SessionHistoryController = class {
|
|
|
1242
1398
|
const target = addressId(address);
|
|
1243
1399
|
const buffered = new Deque();
|
|
1244
1400
|
let snapshotCursor;
|
|
1401
|
+
let assistantStreamOrdinal = 0;
|
|
1245
1402
|
let wake;
|
|
1246
1403
|
const notify = () => {
|
|
1247
1404
|
const resume = wake;
|
|
@@ -1256,13 +1413,28 @@ var SessionHistoryController = class {
|
|
|
1256
1413
|
this.closeFollowers.add(close);
|
|
1257
1414
|
const disposeEvent = this.ctx.on("session/event", (session, event) => {
|
|
1258
1415
|
if (session.id !== target) return;
|
|
1259
|
-
buffered.pushBack(
|
|
1416
|
+
buffered.pushBack({
|
|
1417
|
+
type: "event",
|
|
1418
|
+
event
|
|
1419
|
+
});
|
|
1260
1420
|
notify();
|
|
1261
1421
|
}, { global: true });
|
|
1262
1422
|
const disposeCreated = this.ctx.on("session/created", (session) => {
|
|
1263
1423
|
if (session.id !== target) return;
|
|
1264
1424
|
const suffix = session.snapshotEvents(snapshotCursor === void 0 ? session.firstLiveSeq : SessionLogOffset(snapshotCursor + 1));
|
|
1265
|
-
for (let index = suffix.length - 1; index >= 0; index -= 1) buffered.pushFront(
|
|
1425
|
+
for (let index = suffix.length - 1; index >= 0; index -= 1) buffered.pushFront({
|
|
1426
|
+
type: "event",
|
|
1427
|
+
event: suffix[index]
|
|
1428
|
+
});
|
|
1429
|
+
notify();
|
|
1430
|
+
}, { global: true });
|
|
1431
|
+
const disposeAssistantStream = request.assistantStream !== true ? void 0 : this.ctx.on("agent/assistant-stream", ({ agent, frame }) => {
|
|
1432
|
+
if (agent.session.id !== target) return;
|
|
1433
|
+
buffered.pushBack({
|
|
1434
|
+
type: "assistant-stream",
|
|
1435
|
+
frame: wireAssistantStreamFrame(frame, cursorBeforeNext(agent.session.seq)),
|
|
1436
|
+
ordinal: ++assistantStreamOrdinal
|
|
1437
|
+
});
|
|
1266
1438
|
notify();
|
|
1267
1439
|
}, { global: true });
|
|
1268
1440
|
const onAbort = () => {
|
|
@@ -1276,22 +1448,25 @@ var SessionHistoryController = class {
|
|
|
1276
1448
|
hasError: false
|
|
1277
1449
|
};
|
|
1278
1450
|
try {
|
|
1279
|
-
const source = __addDisposableResource$
|
|
1451
|
+
const source = __addDisposableResource$2(env_2, await this.sourceFor(address, signal, true), false);
|
|
1280
1452
|
const events = source.events;
|
|
1281
1453
|
signal.throwIfAborted();
|
|
1282
1454
|
const cursor = source.cursor;
|
|
1283
1455
|
snapshotCursor = cursor;
|
|
1284
1456
|
const page = paginate(events, void 0, request.maxMessages ?? DEFAULT_MAX_MESSAGES);
|
|
1457
|
+
const assistantStream = request.assistantStream === true ? this.assistantStreams.get(target)?.snapshot() ?? { revision: 0 } : void 0;
|
|
1458
|
+
const assistantStreamOrdinalCut = assistantStreamOrdinal;
|
|
1285
1459
|
yield {
|
|
1286
1460
|
type: "snapshot",
|
|
1287
|
-
header: wireHeader(source.header
|
|
1461
|
+
header: wireHeader(source.header),
|
|
1288
1462
|
cursor,
|
|
1289
1463
|
records: pageRecords(page.events),
|
|
1290
1464
|
hasMore: page.hasMore,
|
|
1291
1465
|
projections: source.projections === void 0 ? {
|
|
1292
1466
|
asOfSeq: cursor,
|
|
1293
1467
|
values: {}
|
|
1294
|
-
} : projectionBlock(source.projections)
|
|
1468
|
+
} : projectionBlock(source.projections),
|
|
1469
|
+
...assistantStream === void 0 ? {} : { assistantStream }
|
|
1295
1470
|
};
|
|
1296
1471
|
if (address.kind === "session" && source.source === "prepared") {
|
|
1297
1472
|
const promotion = source.retain();
|
|
@@ -1311,23 +1486,31 @@ var SessionHistoryController = class {
|
|
|
1311
1486
|
});
|
|
1312
1487
|
continue;
|
|
1313
1488
|
}
|
|
1489
|
+
if (item.type === "assistant-stream") {
|
|
1490
|
+
if (item.ordinal > assistantStreamOrdinalCut) yield {
|
|
1491
|
+
type: "assistant-stream",
|
|
1492
|
+
frame: item.frame
|
|
1493
|
+
};
|
|
1494
|
+
continue;
|
|
1495
|
+
}
|
|
1314
1496
|
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)}`, {});
|
|
1497
|
+
if (item.event.seq < expectedSeq) continue;
|
|
1498
|
+
if (item.event.seq !== expectedSeq) throw new RemoteError("gateway/internal", `session event stream skipped seq ${String(expectedSeq)}`, {});
|
|
1317
1499
|
nextOffset = SessionLogOffset(nextOffset + 1);
|
|
1318
|
-
yield entryFor(item);
|
|
1500
|
+
yield entryFor(item.event);
|
|
1319
1501
|
}
|
|
1320
1502
|
} catch (e_2) {
|
|
1321
1503
|
env_2.error = e_2;
|
|
1322
1504
|
env_2.hasError = true;
|
|
1323
1505
|
} finally {
|
|
1324
|
-
__disposeResources$
|
|
1506
|
+
__disposeResources$2(env_2);
|
|
1325
1507
|
}
|
|
1326
1508
|
} finally {
|
|
1327
1509
|
this.closeFollowers.delete(close);
|
|
1328
1510
|
signal.removeEventListener("abort", onAbort);
|
|
1329
1511
|
disposeCreated();
|
|
1330
1512
|
disposeEvent();
|
|
1513
|
+
disposeAssistantStream?.();
|
|
1331
1514
|
}
|
|
1332
1515
|
}
|
|
1333
1516
|
async sourceFor(address, signal, withProjections) {
|
|
@@ -1354,6 +1537,20 @@ var SessionHistoryController = class {
|
|
|
1354
1537
|
}
|
|
1355
1538
|
}
|
|
1356
1539
|
};
|
|
1540
|
+
function cursorBeforeNext(nextSeq) {
|
|
1541
|
+
return nextSeq === 0 ? -1 : SessionSeq(nextSeq - 1);
|
|
1542
|
+
}
|
|
1543
|
+
function wireAssistantStreamFrame(frame, durableCursor) {
|
|
1544
|
+
if (frame.type === "start") return {
|
|
1545
|
+
...frame,
|
|
1546
|
+
startedAfterSeq: durableCursor
|
|
1547
|
+
};
|
|
1548
|
+
if (frame.type === "end") return frame;
|
|
1549
|
+
return {
|
|
1550
|
+
...frame,
|
|
1551
|
+
chunk: frame.chunk
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1357
1554
|
function projectionBlock(snapshot) {
|
|
1358
1555
|
return {
|
|
1359
1556
|
asOfSeq: snapshot.asOfSeq,
|
|
@@ -1420,13 +1617,9 @@ function paginate(events, beforeSeq, maxMessages, throughSeq = events.at(-1)?.se
|
|
|
1420
1617
|
hasMore: cut > 0
|
|
1421
1618
|
};
|
|
1422
1619
|
}
|
|
1423
|
-
/** Translate logical Session metadata to the
|
|
1424
|
-
function wireHeader(header
|
|
1425
|
-
|
|
1426
|
-
return {
|
|
1427
|
-
...wire,
|
|
1428
|
-
...isSeeded ? { seedLength: inheritedEventCount } : {}
|
|
1429
|
-
};
|
|
1620
|
+
/** Translate current logical Session metadata to the browser wire. */
|
|
1621
|
+
function wireHeader(header) {
|
|
1622
|
+
return { ...header };
|
|
1430
1623
|
}
|
|
1431
1624
|
function entryFor(event) {
|
|
1432
1625
|
return {
|
|
@@ -1434,40 +1627,9 @@ function entryFor(event) {
|
|
|
1434
1627
|
event
|
|
1435
1628
|
};
|
|
1436
1629
|
}
|
|
1437
|
-
function chunkEntryFor(row) {
|
|
1438
|
-
switch (row.type) {
|
|
1439
|
-
case "text-chunks": return {
|
|
1440
|
-
type: "chunks",
|
|
1441
|
-
event: {
|
|
1442
|
-
type: "chunkrow/text-chunks",
|
|
1443
|
-
seq: row.seq0,
|
|
1444
|
-
time: row.time0,
|
|
1445
|
-
data: row.data
|
|
1446
|
-
}
|
|
1447
|
-
};
|
|
1448
|
-
case "reasoning-chunks": return {
|
|
1449
|
-
type: "chunks",
|
|
1450
|
-
event: {
|
|
1451
|
-
type: "chunkrow/reasoning-chunks",
|
|
1452
|
-
seq: row.seq0,
|
|
1453
|
-
time: row.time0,
|
|
1454
|
-
data: row.data
|
|
1455
|
-
}
|
|
1456
|
-
};
|
|
1457
|
-
case "tool-call-chunks": return {
|
|
1458
|
-
type: "chunks",
|
|
1459
|
-
event: {
|
|
1460
|
-
type: "chunkrow/tool-call-chunks",
|
|
1461
|
-
seq: row.seq0,
|
|
1462
|
-
time: row.time0,
|
|
1463
|
-
data: row.data
|
|
1464
|
-
}
|
|
1465
|
-
};
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
1630
|
/** Encode one bounded logical page without changing its pagination cut. */
|
|
1469
1631
|
function pageRecords(events) {
|
|
1470
|
-
return
|
|
1632
|
+
return events.map(entryFor);
|
|
1471
1633
|
}
|
|
1472
1634
|
//#endregion
|
|
1473
1635
|
//#region lib/types/file-references.js
|
|
@@ -1558,67 +1720,6 @@ let SessionFileReferences = (() => {
|
|
|
1558
1720
|
//#endregion
|
|
1559
1721
|
//#region lib/types/list.js
|
|
1560
1722
|
/** Cold-safe Session list and search projection. */
|
|
1561
|
-
var __addDisposableResource$2 = function(env, value, async) {
|
|
1562
|
-
if (value !== null && value !== void 0) {
|
|
1563
|
-
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
1564
|
-
var dispose, inner;
|
|
1565
|
-
if (async) {
|
|
1566
|
-
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
1567
|
-
dispose = value[Symbol.asyncDispose];
|
|
1568
|
-
}
|
|
1569
|
-
if (dispose === void 0) {
|
|
1570
|
-
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
1571
|
-
dispose = value[Symbol.dispose];
|
|
1572
|
-
if (async) inner = dispose;
|
|
1573
|
-
}
|
|
1574
|
-
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
1575
|
-
if (inner) dispose = function() {
|
|
1576
|
-
try {
|
|
1577
|
-
inner.call(this);
|
|
1578
|
-
} catch (e) {
|
|
1579
|
-
return Promise.reject(e);
|
|
1580
|
-
}
|
|
1581
|
-
};
|
|
1582
|
-
env.stack.push({
|
|
1583
|
-
value,
|
|
1584
|
-
dispose,
|
|
1585
|
-
async
|
|
1586
|
-
});
|
|
1587
|
-
} else if (async) env.stack.push({ async: true });
|
|
1588
|
-
return value;
|
|
1589
|
-
};
|
|
1590
|
-
var __disposeResources$2 = (function(SuppressedError) {
|
|
1591
|
-
return function(env) {
|
|
1592
|
-
function fail(e) {
|
|
1593
|
-
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
1594
|
-
env.hasError = true;
|
|
1595
|
-
}
|
|
1596
|
-
var r, s = 0;
|
|
1597
|
-
function next() {
|
|
1598
|
-
while (r = env.stack.pop()) try {
|
|
1599
|
-
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
1600
|
-
if (r.dispose) {
|
|
1601
|
-
var result = r.dispose.call(r.value);
|
|
1602
|
-
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
|
|
1603
|
-
fail(e);
|
|
1604
|
-
return next();
|
|
1605
|
-
});
|
|
1606
|
-
} else s |= 1;
|
|
1607
|
-
} catch (e) {
|
|
1608
|
-
fail(e);
|
|
1609
|
-
}
|
|
1610
|
-
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
1611
|
-
if (env.hasError) throw env.error;
|
|
1612
|
-
}
|
|
1613
|
-
return next();
|
|
1614
|
-
};
|
|
1615
|
-
})(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
1616
|
-
var e = new Error(message);
|
|
1617
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
1618
|
-
});
|
|
1619
|
-
/** Default maximum artifact size eligible for one cold projection observation. */
|
|
1620
|
-
const DEFAULT_COLD_BLANK_PROBE_MAX_BYTES = 1024;
|
|
1621
|
-
const COLD_SUMMARY_BATCH_SIZE = 16;
|
|
1622
1723
|
const SEARCH_PROVIDER_CALL_LIMIT = 100;
|
|
1623
1724
|
const SESSION_SEARCH_QUERY_MAX_CHARS = 500;
|
|
1624
1725
|
const MESSAGE_TYPES = new Set(["user/message", "assistant/message"]);
|
|
@@ -1667,14 +1768,9 @@ function truncateUnicodeCodePoints(value, maximum) {
|
|
|
1667
1768
|
/** Owns list projection registration, bounded cold summaries, and authorized search. */
|
|
1668
1769
|
var ApiSessionList = class {
|
|
1669
1770
|
ctx;
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
* @param ctx - Host context carrying Session, query, persistence, and projection services.
|
|
1673
|
-
* @param coldBlankProbeMaxBytes - maximum physical artifact size eligible for a full observation.
|
|
1674
|
-
*/
|
|
1675
|
-
constructor(ctx, coldBlankProbeMaxBytes) {
|
|
1771
|
+
/** @param ctx - Host context carrying Session, query, persistence, and projection services. */
|
|
1772
|
+
constructor(ctx) {
|
|
1676
1773
|
this.ctx = ctx;
|
|
1677
|
-
this.coldBlankProbeMaxBytes = coldBlankProbeMaxBytes;
|
|
1678
1774
|
ctx.sessionProjections.register({
|
|
1679
1775
|
key: "sessionListMetadata",
|
|
1680
1776
|
stateSchema: sessionListMetadataSchema,
|
|
@@ -1740,21 +1836,12 @@ var ApiSessionList = class {
|
|
|
1740
1836
|
if (record.header.cwd === void 0) continue;
|
|
1741
1837
|
cold.push(record.header);
|
|
1742
1838
|
}
|
|
1743
|
-
for (
|
|
1744
|
-
const settled = await Promise.allSettled(cold.slice(offset, offset + COLD_SUMMARY_BATCH_SIZE).map((header) => this.summarizeCold(header, signal)));
|
|
1745
|
-
for (const result of settled) {
|
|
1746
|
-
if (result.status === "rejected") throw result.reason;
|
|
1747
|
-
items.push(result.value);
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1839
|
+
for (const header of cold) items.push(this.summarizeCold(header));
|
|
1750
1840
|
items.sort((left, right) => right.updatedAt - left.updatedAt);
|
|
1751
1841
|
return items;
|
|
1752
1842
|
}
|
|
1753
|
-
|
|
1754
|
-
const
|
|
1755
|
-
const projections = cached?.values.sessionListMetadata?.blank === false ? cached : await this.probeSmallCold(header, signal) ?? cached;
|
|
1756
|
-
const raced = this.ctx.sessions.get(header.id);
|
|
1757
|
-
if (raced !== void 0) return this.summaryFor(raced);
|
|
1843
|
+
summarizeCold(header) {
|
|
1844
|
+
const projections = this.projectionsFor(header, void 0);
|
|
1758
1845
|
const metadata = projections?.values.sessionListMetadata;
|
|
1759
1846
|
return {
|
|
1760
1847
|
sessionId: header.id,
|
|
@@ -1765,44 +1852,6 @@ var ApiSessionList = class {
|
|
|
1765
1852
|
...projections === void 0 ? {} : { projections }
|
|
1766
1853
|
};
|
|
1767
1854
|
}
|
|
1768
|
-
async probeSmallCold(header, signal) {
|
|
1769
|
-
if (this.coldBlankProbeMaxBytes === 0) return void 0;
|
|
1770
|
-
const location = this.ctx.get("sessionPersistence")?.locate(header);
|
|
1771
|
-
if (location === void 0) return void 0;
|
|
1772
|
-
signal?.throwIfAborted();
|
|
1773
|
-
try {
|
|
1774
|
-
if ((await stat(location.path)).size > this.coldBlankProbeMaxBytes) return void 0;
|
|
1775
|
-
} catch {
|
|
1776
|
-
signal?.throwIfAborted();
|
|
1777
|
-
return;
|
|
1778
|
-
}
|
|
1779
|
-
try {
|
|
1780
|
-
const env_1 = {
|
|
1781
|
-
stack: [],
|
|
1782
|
-
error: void 0,
|
|
1783
|
-
hasError: false
|
|
1784
|
-
};
|
|
1785
|
-
try {
|
|
1786
|
-
const block = __addDisposableResource$2(env_1, await this.ctx.sessionQuery.observeSession(header.id, {
|
|
1787
|
-
...signal === void 0 ? {} : { signal },
|
|
1788
|
-
projectionMode: "all"
|
|
1789
|
-
}), false).projections;
|
|
1790
|
-
return block === void 0 ? void 0 : {
|
|
1791
|
-
asOfSeq: block.asOfSeq,
|
|
1792
|
-
values: block.values
|
|
1793
|
-
};
|
|
1794
|
-
} catch (e_1) {
|
|
1795
|
-
env_1.error = e_1;
|
|
1796
|
-
env_1.hasError = true;
|
|
1797
|
-
} finally {
|
|
1798
|
-
__disposeResources$2(env_1);
|
|
1799
|
-
}
|
|
1800
|
-
} catch (error) {
|
|
1801
|
-
signal?.throwIfAborted();
|
|
1802
|
-
this.ctx.logger.warn(`api-session.list: small cold observation for "${header.id}" failed; serving it as visible: ${String(error)}`);
|
|
1803
|
-
return;
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1806
1855
|
/**
|
|
1807
1856
|
* Search current visible message content without activating any matching Session.
|
|
1808
1857
|
* @param query - literal message-content query.
|
|
@@ -1893,7 +1942,8 @@ var ApiSessionList = class {
|
|
|
1893
1942
|
}
|
|
1894
1943
|
projectionsFor(header, session) {
|
|
1895
1944
|
try {
|
|
1896
|
-
const
|
|
1945
|
+
const cache = this.ctx.get("sessionProjectionCache");
|
|
1946
|
+
const block = session === void 0 ? header.isSeeded ? void 0 : cache?.cachedSnapshot(header, SessionLogOffset(0)) ?? cache?.cachedPredecessorTitle(header, SessionLogOffset(0)) : this.ctx.sessionProjections.cachedSnapshot(session);
|
|
1897
1947
|
return block !== void 0 && Object.keys(block.values).length > 0 ? {
|
|
1898
1948
|
asOfSeq: block.asOfSeq,
|
|
1899
1949
|
values: block.values
|
|
@@ -2571,6 +2621,7 @@ let SessionController = (() => {
|
|
|
2571
2621
|
"agentDefaultModel",
|
|
2572
2622
|
"agents",
|
|
2573
2623
|
"attachments",
|
|
2624
|
+
"fileUploads",
|
|
2574
2625
|
"llm",
|
|
2575
2626
|
"sessions",
|
|
2576
2627
|
"sessionProjections",
|
|
@@ -2578,10 +2629,7 @@ let SessionController = (() => {
|
|
|
2578
2629
|
"typert",
|
|
2579
2630
|
"workspaceRegistry"
|
|
2580
2631
|
];
|
|
2581
|
-
static Config = z.object({
|
|
2582
|
-
coldBlankProbeMaxBytes: z.natural().default(DEFAULT_COLD_BLANK_PROBE_MAX_BYTES),
|
|
2583
|
-
nativeOpen: z.boolean()
|
|
2584
|
-
});
|
|
2632
|
+
static Config = z.object({ nativeOpen: z.boolean() });
|
|
2585
2633
|
agents = __runInitializers(this, _instanceExtraInitializers);
|
|
2586
2634
|
commands;
|
|
2587
2635
|
controlState;
|
|
@@ -2592,13 +2640,19 @@ let SessionController = (() => {
|
|
|
2592
2640
|
promotions = /* @__PURE__ */ new Set();
|
|
2593
2641
|
/**
|
|
2594
2642
|
* @param ctx - Host context containing the Session capability assembly.
|
|
2595
|
-
* @param config -
|
|
2643
|
+
* @param config - native-opener deployment policy.
|
|
2644
|
+
* @param internals - host integrations replaceable by direct unit tests.
|
|
2596
2645
|
*/
|
|
2597
2646
|
constructor(ctx, config, internals = {}) {
|
|
2598
2647
|
super(ctx, "sessionController", { namespace: "session" });
|
|
2599
2648
|
installModelSelectionProjection(ctx);
|
|
2600
2649
|
this.agents = new ApiSessionAgentController(ctx);
|
|
2601
2650
|
this.commands = new SessionCommandController(ctx, this.agents, process.cwd());
|
|
2651
|
+
ctx.effect(() => ctx.fileUploads.registerAgentResolver(async (sessionId) => {
|
|
2652
|
+
const result = await this.agents.resolveAgent(sessionId);
|
|
2653
|
+
if ("error" in result) throw result.error;
|
|
2654
|
+
return result.agent;
|
|
2655
|
+
}), "session-controller: file-upload Agent resolver");
|
|
2602
2656
|
this.controlState = new SessionControlController(ctx);
|
|
2603
2657
|
ctx.effect(() => async () => {
|
|
2604
2658
|
await Promise.allSettled([...this.promotions]);
|
|
@@ -2606,7 +2660,7 @@ let SessionController = (() => {
|
|
|
2606
2660
|
this.history = new SessionHistoryController(ctx, (observation) => {
|
|
2607
2661
|
this.promote(observation);
|
|
2608
2662
|
});
|
|
2609
|
-
this.listState = new ApiSessionList(ctx
|
|
2663
|
+
this.listState = new ApiSessionList(ctx);
|
|
2610
2664
|
this.openPath = internals.openPath ?? openNativePath;
|
|
2611
2665
|
this.canOpenPath = internals.canOpenPath ?? (() => config.nativeOpen ?? (internals.openPath !== void 0 || canOpenNativePath()));
|
|
2612
2666
|
ctx.plugin(SessionFileReferences);
|
|
@@ -2810,7 +2864,8 @@ let SessionController = (() => {
|
|
|
2810
2864
|
* Follow one Session log from its opening or resume cursor.
|
|
2811
2865
|
* @param request - durable address and last committed sequence already held by the caller.
|
|
2812
2866
|
* @param signal - cancellation owned by the Remote stream carrier.
|
|
2813
|
-
* @returns a complete opening snapshot followed by gap-free event
|
|
2867
|
+
* @returns a complete opening snapshot followed by gap-free durable event
|
|
2868
|
+
* frames and optional cursorless assistant-stream frames.
|
|
2814
2869
|
*/
|
|
2815
2870
|
follow(request, signal) {
|
|
2816
2871
|
return this.history.follow(request, signal);
|