@deepseek-ai/dsh-api-session-controller 0.2.0-rc.2 → 0.2.1-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 +8 -8
- package/README.md +7 -3
- package/README.zh.md +7 -3
- package/lib/client.js +666 -20
- package/lib/index.js +76 -23
- package/lib/typert.host.js +376 -90
- package/lib/typert.remote-client.js +311 -69
- package/lib/types/client/contract/sessions.d.ts +4 -2
- package/lib/types/client/sessions/lineage.d.ts +2 -0
- package/lib/types/client/sessions/manager.d.ts +7 -5
- package/lib/types/client/sessions/manager.js +36 -11
- package/lib/types/client/sessions/projection-store.d.ts +3 -3
- package/lib/types/client/sessions/projection-store.js +1 -1
- package/lib/types/client/sessions/service.d.ts +8 -4
- package/lib/types/client/sessions/service.js +7 -3
- package/lib/types/index.d.ts +14 -7
- package/lib/types/index.js +45 -13
- package/lib/types/list.d.ts +7 -3
- package/lib/types/list.js +33 -10
- package/lib/types/skill-catalog.js +7 -6
- package/lib/types/types.d.ts +14 -2
- package/package.json +79 -76
package/lib/index.js
CHANGED
|
@@ -17,6 +17,8 @@ import { SessionTitleInvalidError } from "@deepseek-ai/dsh-session-title";
|
|
|
17
17
|
import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
|
|
18
18
|
import { assertNever } from "@deepseek-ai/dsh-util-values";
|
|
19
19
|
import { Deque } from "@deepseek-ai/dsh-deque";
|
|
20
|
+
import { performance } from "node:perf_hooks";
|
|
21
|
+
import { scheduler } from "node:timers/promises";
|
|
20
22
|
import { z as z$1 } from "zod";
|
|
21
23
|
import { isUserInvocable } from "@deepseek-ai/dsh-skill";
|
|
22
24
|
import { FsError } from "@deepseek-ai/dsh-fs";
|
|
@@ -1831,9 +1833,14 @@ function truncateUnicodeCodePoints(value, maximum) {
|
|
|
1831
1833
|
/** Owns list projection registration, bounded cold summaries, and authorized search. */
|
|
1832
1834
|
var ApiSessionList = class {
|
|
1833
1835
|
ctx;
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
+
workSliceMs;
|
|
1837
|
+
/**
|
|
1838
|
+
* @param ctx - Host context carrying Session, query, persistence, and projection services.
|
|
1839
|
+
* @param workSliceMs - Resolved positive integral list-work budget in milliseconds.
|
|
1840
|
+
*/
|
|
1841
|
+
constructor(ctx, workSliceMs) {
|
|
1836
1842
|
this.ctx = ctx;
|
|
1843
|
+
this.workSliceMs = workSliceMs;
|
|
1837
1844
|
ctx.sessionProjections.register({
|
|
1838
1845
|
key: "sessionListMetadata",
|
|
1839
1846
|
stateSchema: sessionListMetadataSchema,
|
|
@@ -1874,6 +1881,7 @@ var ApiSessionList = class {
|
|
|
1874
1881
|
sessionId: session.id,
|
|
1875
1882
|
updatedAt: updatedAt(session.header, metadata),
|
|
1876
1883
|
agentAvailable: this.ctx.agents.get(session.id)?.session === session,
|
|
1884
|
+
formatStatus: "current",
|
|
1877
1885
|
running: this.ctx.agents.get(session.id)?.status === "running",
|
|
1878
1886
|
blank: metadata?.blank ?? session.seq === 0,
|
|
1879
1887
|
...listFields(session.header),
|
|
@@ -1882,7 +1890,7 @@ var ApiSessionList = class {
|
|
|
1882
1890
|
}
|
|
1883
1891
|
/**
|
|
1884
1892
|
* Read every visible attached and persisted Session without activating an Agent.
|
|
1885
|
-
* @param signal - optional cancellation for persistence reads.
|
|
1893
|
+
* @param signal - optional cancellation for persistence reads and summary generation.
|
|
1886
1894
|
* @returns visible Session summaries ordered by activity.
|
|
1887
1895
|
*/
|
|
1888
1896
|
async list(signal) {
|
|
@@ -1891,26 +1899,39 @@ var ApiSessionList = class {
|
|
|
1891
1899
|
signal?.throwIfAborted();
|
|
1892
1900
|
const items = [];
|
|
1893
1901
|
const cold = [];
|
|
1902
|
+
let yieldDeadline = performance.now() + this.workSliceMs;
|
|
1894
1903
|
for (const record of records) {
|
|
1904
|
+
signal?.throwIfAborted();
|
|
1895
1905
|
const live = this.ctx.sessions.get(record.header.id);
|
|
1896
|
-
if (live !== void 0)
|
|
1897
|
-
|
|
1898
|
-
|
|
1906
|
+
if (live !== void 0) items.push(this.summaryFor(live));
|
|
1907
|
+
else if (record.header.cwd !== void 0) cold.push(record);
|
|
1908
|
+
if (performance.now() >= yieldDeadline) {
|
|
1909
|
+
await scheduler.yield();
|
|
1910
|
+
signal?.throwIfAborted();
|
|
1911
|
+
yieldDeadline = performance.now() + this.workSliceMs;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
for (const record of cold) {
|
|
1915
|
+
signal?.throwIfAborted();
|
|
1916
|
+
items.push(this.summarizeCold(record));
|
|
1917
|
+
if (performance.now() >= yieldDeadline) {
|
|
1918
|
+
await scheduler.yield();
|
|
1919
|
+
signal?.throwIfAborted();
|
|
1920
|
+
yieldDeadline = performance.now() + this.workSliceMs;
|
|
1899
1921
|
}
|
|
1900
|
-
if (record.header.cwd === void 0) continue;
|
|
1901
|
-
cold.push(record.header);
|
|
1902
1922
|
}
|
|
1903
|
-
|
|
1923
|
+
signal?.throwIfAborted();
|
|
1904
1924
|
items.sort((left, right) => right.updatedAt - left.updatedAt);
|
|
1905
1925
|
return items;
|
|
1906
1926
|
}
|
|
1907
|
-
summarizeCold(header) {
|
|
1927
|
+
summarizeCold({ header, formatStatus }) {
|
|
1908
1928
|
const projections = this.projectionsFor(header, void 0);
|
|
1909
1929
|
const metadata = projections?.values.sessionListMetadata;
|
|
1910
1930
|
return {
|
|
1911
1931
|
sessionId: header.id,
|
|
1912
1932
|
updatedAt: updatedAt(header, metadata),
|
|
1913
1933
|
agentAvailable: false,
|
|
1934
|
+
...formatStatus === void 0 ? {} : { formatStatus },
|
|
1914
1935
|
running: false,
|
|
1915
1936
|
blank: metadata?.blank ?? false,
|
|
1916
1937
|
...listFields(header),
|
|
@@ -2242,7 +2263,8 @@ let SessionSkillCatalog = (() => {
|
|
|
2242
2263
|
static inject = [
|
|
2243
2264
|
"agents",
|
|
2244
2265
|
"sessionQuery",
|
|
2245
|
-
"typert"
|
|
2266
|
+
"typert",
|
|
2267
|
+
"workingDirectory"
|
|
2246
2268
|
];
|
|
2247
2269
|
/** @param ctx - Host context carrying Session reads and optional skill/preset services. */
|
|
2248
2270
|
constructor(ctx) {
|
|
@@ -2275,8 +2297,10 @@ let SessionSkillCatalog = (() => {
|
|
|
2275
2297
|
try {
|
|
2276
2298
|
const observation = __addDisposableResource$1(env_2, await this.ctx.sessionQuery.observeSession(sessionId), false);
|
|
2277
2299
|
if (observation.projections === void 0) throw new Error("skill catalog requires a projected Session observation");
|
|
2278
|
-
cwd = observation.header.cwd;
|
|
2300
|
+
cwd = observation.projections.values.workingDirectory ?? observation.header.cwd ?? this.ctx.workingDirectory.defaultDirectory;
|
|
2279
2301
|
agentPreset = observation.projections.values.agentPreset ?? void 0;
|
|
2302
|
+
const live = this.ctx.agents.get(sessionId);
|
|
2303
|
+
if (live !== void 0) cwd = await this.ctx.workingDirectory.ensure(live, signal);
|
|
2280
2304
|
} catch (e_1) {
|
|
2281
2305
|
env_2.error = e_1;
|
|
2282
2306
|
env_2.hasError = true;
|
|
@@ -2287,7 +2311,6 @@ let SessionSkillCatalog = (() => {
|
|
|
2287
2311
|
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") throw new RemoteError("session/not-found", `session "${sessionId}" not found`, { sessionId });
|
|
2288
2312
|
throw new RemoteError("gateway/internal", `session "${sessionId}" could not be inspected: ${String(error)}`, {});
|
|
2289
2313
|
}
|
|
2290
|
-
if (cwd === void 0) throw new RemoteError("gateway/internal", `session "${sessionId}" has no project cwd`, {});
|
|
2291
2314
|
const live = this.ctx.agents.get(sessionId);
|
|
2292
2315
|
const presets = this.ctx.get("agentPresets");
|
|
2293
2316
|
const skillRegistry = (live === void 0 ? void 0 : presets?.serviceFor(live, "skills")) ?? this.ctx.get("skills");
|
|
@@ -2826,7 +2849,10 @@ let SessionController = (() => {
|
|
|
2826
2849
|
"typert",
|
|
2827
2850
|
"workspaceRegistry"
|
|
2828
2851
|
];
|
|
2829
|
-
static Config = z.object({
|
|
2852
|
+
static Config = z.object({
|
|
2853
|
+
nativeOpen: z.boolean(),
|
|
2854
|
+
listWorkSliceMs: z.natural().min(1).default(16)
|
|
2855
|
+
});
|
|
2830
2856
|
agents = __runInitializers(this, _instanceExtraInitializers);
|
|
2831
2857
|
commands;
|
|
2832
2858
|
controlState;
|
|
@@ -2840,11 +2866,12 @@ let SessionController = (() => {
|
|
|
2840
2866
|
promotions = /* @__PURE__ */ new Set();
|
|
2841
2867
|
/**
|
|
2842
2868
|
* @param ctx - Host context containing the Session capability assembly.
|
|
2843
|
-
* @param config - native-opener deployment policy.
|
|
2869
|
+
* @param config - native-opener and list-scheduling deployment policy.
|
|
2844
2870
|
* @param internals - host integrations replaceable by direct unit tests.
|
|
2845
2871
|
*/
|
|
2846
2872
|
constructor(ctx, config, internals = {}) {
|
|
2847
2873
|
super(ctx, "sessionController", { namespace: "session" });
|
|
2874
|
+
const resolved = SessionController.Config(config);
|
|
2848
2875
|
installModelSelectionProjection(ctx);
|
|
2849
2876
|
this.agents = new ApiSessionAgentController(ctx);
|
|
2850
2877
|
this.commands = new SessionCommandController(ctx, this.agents, process.cwd());
|
|
@@ -2860,7 +2887,7 @@ let SessionController = (() => {
|
|
|
2860
2887
|
this.history = new SessionHistoryController(ctx, (observation) => {
|
|
2861
2888
|
this.promote(observation);
|
|
2862
2889
|
});
|
|
2863
|
-
this.listState = new ApiSessionList(ctx);
|
|
2890
|
+
this.listState = new ApiSessionList(ctx, resolved.listWorkSliceMs);
|
|
2864
2891
|
this.fileApplications = internals.fileApplications ?? nativeFileApplications;
|
|
2865
2892
|
this.openFileApplication = internals.openFileApplication ?? openNativeFileApplication;
|
|
2866
2893
|
this.openPath = internals.openPath ?? openNativeAssociatedPath;
|
|
@@ -2948,7 +2975,7 @@ let SessionController = (() => {
|
|
|
2948
2975
|
/**
|
|
2949
2976
|
* Read all visible Session rows without resuming an Agent.
|
|
2950
2977
|
* @param _request - reserved empty list request.
|
|
2951
|
-
* @param signal - cancellation for persistence reads.
|
|
2978
|
+
* @param signal - cancellation for persistence reads and summary generation.
|
|
2952
2979
|
* @returns visible Session summaries ordered by activity.
|
|
2953
2980
|
*/
|
|
2954
2981
|
async list(_request, signal) {
|
|
@@ -3081,10 +3108,13 @@ let SessionController = (() => {
|
|
|
3081
3108
|
* Fork one cold-readable exact event prefix into a new Session. An omitted
|
|
3082
3109
|
* boundary selects the latest completed-turn prefix; an open cut receives
|
|
3083
3110
|
* synthetic fork closers.
|
|
3084
|
-
* @param request - source Session
|
|
3111
|
+
* @param request - source Session, optional exact inclusive event boundary, and migration preflight choice.
|
|
3085
3112
|
* @returns the new Session identity.
|
|
3086
3113
|
*/
|
|
3087
|
-
fork(request) {
|
|
3114
|
+
async fork(request) {
|
|
3115
|
+
if (request.allowMigration === false && this.ctx.sessions.get(request.sessionId) === void 0) {
|
|
3116
|
+
if ((await this.ctx.get("sessionPersistence")?.stat(request.sessionId))?.formatStatus === "migration-required" && this.ctx.sessions.get(request.sessionId) === void 0) throw new RemoteError("session/migration-required", "Open the source Session to complete migration before forking", { sessionId: request.sessionId });
|
|
3117
|
+
}
|
|
3088
3118
|
return this.commands.fork(request);
|
|
3089
3119
|
}
|
|
3090
3120
|
/**
|
|
@@ -3141,10 +3171,10 @@ let SessionController = (() => {
|
|
|
3141
3171
|
return this.history.follow(request, signal);
|
|
3142
3172
|
}
|
|
3143
3173
|
/**
|
|
3144
|
-
* Read
|
|
3174
|
+
* Read exact projections, returning cached hints only when migration is required.
|
|
3145
3175
|
* @param request - Session whose current values are required.
|
|
3146
|
-
* @param signal - cancellation for the Session
|
|
3147
|
-
* @returns
|
|
3176
|
+
* @param signal - cancellation for the Session read.
|
|
3177
|
+
* @returns a sequenced baseline, cached hints when migration is deferred, or null when absent.
|
|
3148
3178
|
*/
|
|
3149
3179
|
async projections(request, signal) {
|
|
3150
3180
|
const { sessionId } = request;
|
|
@@ -3156,9 +3186,22 @@ let SessionController = (() => {
|
|
|
3156
3186
|
hasError: false
|
|
3157
3187
|
};
|
|
3158
3188
|
try {
|
|
3189
|
+
signal.throwIfAborted();
|
|
3190
|
+
const live = this.liveProjections(sessionId);
|
|
3191
|
+
if (live !== void 0) return live;
|
|
3192
|
+
const stored = await this.ctx.get("sessionPersistence")?.stat(sessionId, { signal });
|
|
3193
|
+
signal.throwIfAborted();
|
|
3194
|
+
const attached = this.liveProjections(sessionId);
|
|
3195
|
+
if (attached !== void 0) return attached;
|
|
3196
|
+
if (stored === void 0) return null;
|
|
3197
|
+
if (stored.formatStatus === "migration-required") return {
|
|
3198
|
+
kind: "migration-required",
|
|
3199
|
+
values: { ...(this.ctx.get("sessionProjectionCache")?.cachedSnapshot(stored.header))?.values }
|
|
3200
|
+
};
|
|
3159
3201
|
const projections = __addDisposableResource(env_2, await this.ctx.sessionQuery.observeSession(sessionId, { signal }), false).projections;
|
|
3160
3202
|
if (projections === void 0) throw new RemoteError("session/projections-unavailable", "Session projections are unavailable", {});
|
|
3161
3203
|
return {
|
|
3204
|
+
kind: "sequenced",
|
|
3162
3205
|
asOfSeq: projections.asOfSeq,
|
|
3163
3206
|
values: projections.values
|
|
3164
3207
|
};
|
|
@@ -3169,12 +3212,22 @@ let SessionController = (() => {
|
|
|
3169
3212
|
__disposeResources(env_2);
|
|
3170
3213
|
}
|
|
3171
3214
|
} catch (error) {
|
|
3172
|
-
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") return null;
|
|
3215
|
+
if (error instanceof SessionQueryError && error.code === "SESSION_QUERY_SESSION_NOT_FOUND") return this.liveProjections(sessionId) ?? null;
|
|
3173
3216
|
if (signal.aborted || error instanceof SessionQueryError && error.code === "SESSION_QUERY_ABORTED") throw new RemoteError("gateway/cancelled", "Session projection read was cancelled", {}, { cause: error });
|
|
3174
3217
|
if (error instanceof RemoteError) throw error;
|
|
3175
3218
|
throw new RemoteError("gateway/internal", "Session projection read failed", {}, { cause: error });
|
|
3176
3219
|
}
|
|
3177
3220
|
}
|
|
3221
|
+
liveProjections(sessionId) {
|
|
3222
|
+
const session = this.ctx.sessions.get(sessionId);
|
|
3223
|
+
if (session === void 0) return void 0;
|
|
3224
|
+
const snapshot = this.ctx.sessionProjections.snapshot(session);
|
|
3225
|
+
return {
|
|
3226
|
+
kind: "sequenced",
|
|
3227
|
+
asOfSeq: snapshot.asOfSeq,
|
|
3228
|
+
values: snapshot.values
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3178
3231
|
/**
|
|
3179
3232
|
* Stream a complete live-control baseline followed by replacement frames.
|
|
3180
3233
|
* @param signal - cancellation owned by the Remote stream carrier.
|