@crazx/dsh-api-session-controller 0.1.2-alpha.3.zw.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +74 -0
- package/README.zh.md +74 -0
- package/lib/client.js +2724 -0
- package/lib/index.js +2826 -0
- package/lib/invariant.js +13 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +2574 -0
- package/lib/typert.remote-client.d.ts +67 -0
- package/lib/typert.remote-client.js +1119 -0
- package/lib/types/agent.d.ts +156 -0
- package/lib/types/agent.js +537 -0
- package/lib/types/catalog.d.ts +11 -0
- package/lib/types/catalog.js +58 -0
- package/lib/types/client/contract/events.d.ts +71 -0
- package/lib/types/client/contract/events.js +91 -0
- package/lib/types/client/contract/session.d.ts +150 -0
- package/lib/types/client/contract/session.js +2 -0
- package/lib/types/client/contract/sessions.d.ts +125 -0
- package/lib/types/client/contract/sessions.js +2 -0
- package/lib/types/client/contract/snapshot.d.ts +82 -0
- package/lib/types/client/contract/snapshot.js +2 -0
- package/lib/types/client/index.d.ts +30 -0
- package/lib/types/client/index.js +48 -0
- package/lib/types/client/ordered-baseline.d.ts +12 -0
- package/lib/types/client/ordered-baseline.js +41 -0
- package/lib/types/client/scope.d.ts +36 -0
- package/lib/types/client/scope.js +54 -0
- package/lib/types/client/sessions/history-records.d.ts +22 -0
- package/lib/types/client/sessions/history-records.js +31 -0
- package/lib/types/client/sessions/lineage.d.ts +38 -0
- package/lib/types/client/sessions/lineage.js +56 -0
- package/lib/types/client/sessions/manager.d.ts +280 -0
- package/lib/types/client/sessions/manager.js +894 -0
- package/lib/types/client/sessions/notifier.d.ts +39 -0
- package/lib/types/client/sessions/notifier.js +98 -0
- package/lib/types/client/sessions/projection-store.d.ts +108 -0
- package/lib/types/client/sessions/projection-store.js +129 -0
- package/lib/types/client/sessions/queue-mirror.d.ts +26 -0
- package/lib/types/client/sessions/queue-mirror.js +61 -0
- package/lib/types/client/sessions/remotes.d.ts +30 -0
- package/lib/types/client/sessions/remotes.js +8 -0
- package/lib/types/client/sessions/service.d.ts +349 -0
- package/lib/types/client/sessions/service.js +574 -0
- package/lib/types/client/sessions/session.d.ts +294 -0
- package/lib/types/client/sessions/session.js +711 -0
- package/lib/types/client/time-zone.d.ts +8 -0
- package/lib/types/client/time-zone.js +14 -0
- package/lib/types/client/transport.d.ts +73 -0
- package/lib/types/client/transport.js +106 -0
- package/lib/types/commands.d.ts +69 -0
- package/lib/types/commands.js +544 -0
- package/lib/types/control.d.ts +23 -0
- package/lib/types/control.js +192 -0
- package/lib/types/file-references.d.ts +27 -0
- package/lib/types/file-references.js +69 -0
- package/lib/types/history.d.ts +31 -0
- package/lib/types/history.js +376 -0
- package/lib/types/index.d.ts +171 -0
- package/lib/types/index.js +424 -0
- package/lib/types/invariant.d.ts +9 -0
- package/lib/types/invariant.js +12 -0
- package/lib/types/list.d.ts +53 -0
- package/lib/types/list.js +405 -0
- package/lib/types/model-selection-projection.d.ts +8 -0
- package/lib/types/model-selection-projection.js +66 -0
- package/lib/types/remote-events.d.ts +8 -0
- package/lib/types/remote-events.js +2 -0
- package/lib/types/skill-catalog.d.ts +28 -0
- package/lib/types/skill-catalog.js +192 -0
- package/lib/types/types.d.ts +505 -0
- package/lib/types/types.js +6 -0
- package/package.json +154 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/** Cold-safe Session list and search projection. */
|
|
2
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
3
|
+
if (value !== null && value !== void 0) {
|
|
4
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
5
|
+
var dispose, inner;
|
|
6
|
+
if (async) {
|
|
7
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
8
|
+
dispose = value[Symbol.asyncDispose];
|
|
9
|
+
}
|
|
10
|
+
if (dispose === void 0) {
|
|
11
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
12
|
+
dispose = value[Symbol.dispose];
|
|
13
|
+
if (async) inner = dispose;
|
|
14
|
+
}
|
|
15
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
16
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
17
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
18
|
+
}
|
|
19
|
+
else if (async) {
|
|
20
|
+
env.stack.push({ async: true });
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
};
|
|
24
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
25
|
+
return function (env) {
|
|
26
|
+
function fail(e) {
|
|
27
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
28
|
+
env.hasError = true;
|
|
29
|
+
}
|
|
30
|
+
var r, s = 0;
|
|
31
|
+
function next() {
|
|
32
|
+
while (r = env.stack.pop()) {
|
|
33
|
+
try {
|
|
34
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
35
|
+
if (r.dispose) {
|
|
36
|
+
var result = r.dispose.call(r.value);
|
|
37
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
38
|
+
}
|
|
39
|
+
else s |= 1;
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
fail(e);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
46
|
+
if (env.hasError) throw env.error;
|
|
47
|
+
}
|
|
48
|
+
return next();
|
|
49
|
+
};
|
|
50
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
51
|
+
var e = new Error(message);
|
|
52
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
53
|
+
});
|
|
54
|
+
import { stat } from 'node:fs/promises';
|
|
55
|
+
import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
|
|
56
|
+
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
|
|
57
|
+
import { z } from 'zod';
|
|
58
|
+
import { SESSION_SEARCH_RESULT_LIMIT, SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS, } from "./types.js";
|
|
59
|
+
/** Default maximum artifact size eligible for one cold projection observation. */
|
|
60
|
+
export const DEFAULT_COLD_BLANK_PROBE_MAX_BYTES = 1024;
|
|
61
|
+
const COLD_SUMMARY_BATCH_SIZE = 16;
|
|
62
|
+
const SEARCH_PROVIDER_CALL_LIMIT = 100;
|
|
63
|
+
const SESSION_SEARCH_QUERY_MAX_CHARS = 500;
|
|
64
|
+
const MESSAGE_TYPES = new Set(['user/message', 'assistant/message']);
|
|
65
|
+
const sessionListMetadataSchema = z.object({
|
|
66
|
+
blank: z.boolean(),
|
|
67
|
+
lastPromptAt: z.number().nullable(),
|
|
68
|
+
});
|
|
69
|
+
const imageLimitsSchema = z.object({
|
|
70
|
+
maxImageBytes: z.number().int().positive(),
|
|
71
|
+
maxImagesPerMessage: z.number().int().positive(),
|
|
72
|
+
maxMessageImageBytes: z.number().int().positive(),
|
|
73
|
+
maxImagePixels: z.number().int().positive(),
|
|
74
|
+
maxImageDimension: z.number().int().positive(),
|
|
75
|
+
mediaTypes: z.array(z.string()),
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* Advance the Session-list metadata projection by one committed event.
|
|
79
|
+
* @param state - metadata before the event.
|
|
80
|
+
* @param event - next committed Session event.
|
|
81
|
+
* @returns the original or advanced metadata value.
|
|
82
|
+
*/
|
|
83
|
+
export function applySessionListMetadata(state, event) {
|
|
84
|
+
const blank = state.blank && event.type !== 'turn/start';
|
|
85
|
+
const lastPromptAt = event.type === 'user/message' && event.data.source.kind === 'user'
|
|
86
|
+
? event.time
|
|
87
|
+
: state.lastPromptAt;
|
|
88
|
+
return blank === state.blank && lastPromptAt === state.lastPromptAt
|
|
89
|
+
? state
|
|
90
|
+
: { blank, lastPromptAt };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Return the longest prefix containing at most `maximum` Unicode code points.
|
|
94
|
+
* @param value - source text.
|
|
95
|
+
* @param maximum - maximum number of Unicode code points.
|
|
96
|
+
* @returns the source text or its longest allowed prefix.
|
|
97
|
+
*/
|
|
98
|
+
export function truncateUnicodeCodePoints(value, maximum) {
|
|
99
|
+
let count = 0;
|
|
100
|
+
let end = 0;
|
|
101
|
+
for (const codePoint of value) {
|
|
102
|
+
if (count === maximum)
|
|
103
|
+
return value.slice(0, end);
|
|
104
|
+
count++;
|
|
105
|
+
end += codePoint.length;
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
/** Owns list projection registration, bounded cold summaries, and authorized search. */
|
|
110
|
+
export class ApiSessionList {
|
|
111
|
+
ctx;
|
|
112
|
+
coldBlankProbeMaxBytes;
|
|
113
|
+
/**
|
|
114
|
+
* @param ctx - Host context carrying Session, query, persistence, and projection services.
|
|
115
|
+
* @param coldBlankProbeMaxBytes - maximum physical artifact size eligible for a full observation.
|
|
116
|
+
*/
|
|
117
|
+
constructor(ctx, coldBlankProbeMaxBytes) {
|
|
118
|
+
this.ctx = ctx;
|
|
119
|
+
this.coldBlankProbeMaxBytes = coldBlankProbeMaxBytes;
|
|
120
|
+
ctx.sessionProjections.register({
|
|
121
|
+
key: 'sessionListMetadata',
|
|
122
|
+
stateSchema: sessionListMetadataSchema,
|
|
123
|
+
init: () => ({ blank: true, lastPromptAt: null }),
|
|
124
|
+
apply: applySessionListMetadata,
|
|
125
|
+
wire: { viewSchema: sessionListMetadataSchema, view: state => state },
|
|
126
|
+
stateVersion: 1,
|
|
127
|
+
});
|
|
128
|
+
ctx.inject(['attachments'], (attachmentCtx) => {
|
|
129
|
+
ctx.sessionProjections.register({
|
|
130
|
+
key: 'imageLimits',
|
|
131
|
+
stateSchema: z.null(),
|
|
132
|
+
init: () => null,
|
|
133
|
+
apply: state => state,
|
|
134
|
+
wire: {
|
|
135
|
+
viewSchema: imageLimitsSchema,
|
|
136
|
+
view: () => attachmentCtx.attachments.imageLimits,
|
|
137
|
+
},
|
|
138
|
+
stateVersion: 1,
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Build one current attached-Session summary.
|
|
144
|
+
* @param session - attached Session to summarize.
|
|
145
|
+
* @returns current list metadata and available projections.
|
|
146
|
+
*/
|
|
147
|
+
summaryFor(session) {
|
|
148
|
+
const projections = this.projectionsFor(session.header, session);
|
|
149
|
+
const metadata = projections?.values.sessionListMetadata;
|
|
150
|
+
return {
|
|
151
|
+
sessionId: session.id,
|
|
152
|
+
updatedAt: updatedAt(session.header, metadata),
|
|
153
|
+
running: this.ctx.agents.get(session.id)?.status === 'running',
|
|
154
|
+
blank: metadata?.blank ?? session.seq === 0,
|
|
155
|
+
...listFields(session.header),
|
|
156
|
+
...(projections === undefined ? {} : { projections }),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Read every visible attached and persisted Session without activating an Agent.
|
|
161
|
+
* @param signal - optional cancellation for persistence reads.
|
|
162
|
+
* @returns visible Session summaries ordered by activity.
|
|
163
|
+
*/
|
|
164
|
+
async list(signal) {
|
|
165
|
+
signal?.throwIfAborted();
|
|
166
|
+
const records = await this.ctx.sessionQuery.listSessions(signal);
|
|
167
|
+
signal?.throwIfAborted();
|
|
168
|
+
const items = [];
|
|
169
|
+
const cold = [];
|
|
170
|
+
for (const record of records) {
|
|
171
|
+
const live = this.ctx.sessions.get(record.header.id);
|
|
172
|
+
if (live !== undefined) {
|
|
173
|
+
items.push(this.summaryFor(live));
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (record.header.cwd === undefined)
|
|
177
|
+
continue;
|
|
178
|
+
cold.push(record.header);
|
|
179
|
+
}
|
|
180
|
+
for (let offset = 0; offset < cold.length; offset += COLD_SUMMARY_BATCH_SIZE) {
|
|
181
|
+
const settled = await Promise.allSettled(cold.slice(offset, offset + COLD_SUMMARY_BATCH_SIZE)
|
|
182
|
+
.map(header => this.summarizeCold(header, signal)));
|
|
183
|
+
for (const result of settled) {
|
|
184
|
+
if (result.status === 'rejected')
|
|
185
|
+
throw result.reason;
|
|
186
|
+
items.push(result.value);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
items.sort((left, right) => right.updatedAt - left.updatedAt);
|
|
190
|
+
return items;
|
|
191
|
+
}
|
|
192
|
+
async summarizeCold(header, signal) {
|
|
193
|
+
const cached = this.projectionsFor(header, undefined);
|
|
194
|
+
const projections = cached?.values.sessionListMetadata?.blank === false
|
|
195
|
+
? cached
|
|
196
|
+
: await this.probeSmallCold(header, signal) ?? cached;
|
|
197
|
+
const raced = this.ctx.sessions.get(header.id);
|
|
198
|
+
if (raced !== undefined)
|
|
199
|
+
return this.summaryFor(raced);
|
|
200
|
+
const metadata = projections?.values.sessionListMetadata;
|
|
201
|
+
return {
|
|
202
|
+
sessionId: header.id,
|
|
203
|
+
updatedAt: updatedAt(header, metadata),
|
|
204
|
+
running: false,
|
|
205
|
+
// A large or inaccessible cache miss remains unknown and visible.
|
|
206
|
+
blank: metadata?.blank ?? false,
|
|
207
|
+
...listFields(header),
|
|
208
|
+
...(projections === undefined ? {} : { projections }),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async probeSmallCold(header, signal) {
|
|
212
|
+
if (this.coldBlankProbeMaxBytes === 0)
|
|
213
|
+
return undefined;
|
|
214
|
+
const persistence = this.ctx.get('sessionPersistence');
|
|
215
|
+
const location = persistence?.locate(header);
|
|
216
|
+
if (location === undefined)
|
|
217
|
+
return undefined;
|
|
218
|
+
signal?.throwIfAborted();
|
|
219
|
+
try {
|
|
220
|
+
if ((await stat(location.path)).size > this.coldBlankProbeMaxBytes)
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
signal?.throwIfAborted();
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
229
|
+
try {
|
|
230
|
+
const observation = __addDisposableResource(env_1, await this.ctx.sessionQuery.observeSession(header.id, {
|
|
231
|
+
...(signal === undefined ? {} : { signal }),
|
|
232
|
+
projectionMode: 'all',
|
|
233
|
+
}), false);
|
|
234
|
+
const block = observation.projections;
|
|
235
|
+
return block === undefined
|
|
236
|
+
? undefined
|
|
237
|
+
: { asOfSeq: block.asOfSeq, values: block.values };
|
|
238
|
+
}
|
|
239
|
+
catch (e_1) {
|
|
240
|
+
env_1.error = e_1;
|
|
241
|
+
env_1.hasError = true;
|
|
242
|
+
}
|
|
243
|
+
finally {
|
|
244
|
+
__disposeResources(env_1);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
signal?.throwIfAborted();
|
|
249
|
+
this.ctx.logger.warn(`api-session.list: small cold observation for "${header.id}" failed; serving it as visible: ${String(error)}`);
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Search current visible message content without activating any matching Session.
|
|
255
|
+
* @param query - literal message-content query.
|
|
256
|
+
* @param signal - cancellation for list and search reads.
|
|
257
|
+
* @returns authorized bounded Session search results.
|
|
258
|
+
*/
|
|
259
|
+
async search(query, signal) {
|
|
260
|
+
const normalizedQuery = normalizeSearchQuery(query);
|
|
261
|
+
signal.throwIfAborted();
|
|
262
|
+
const provider = this.ctx.get('sessionQuery');
|
|
263
|
+
if (provider === undefined) {
|
|
264
|
+
throw new RemoteError('gateway/internal', 'session search is unavailable: this deployment does not mount @deepseek-ai/dsh-session-query', {});
|
|
265
|
+
}
|
|
266
|
+
try {
|
|
267
|
+
const visible = await provider.listSessions(signal);
|
|
268
|
+
signal.throwIfAborted();
|
|
269
|
+
const visibleIds = new Set(visible
|
|
270
|
+
.filter(record => record.header.cwd !== undefined)
|
|
271
|
+
.map(record => record.header.id));
|
|
272
|
+
if (visibleIds.size === 0)
|
|
273
|
+
return { items: [], hasMore: false };
|
|
274
|
+
const authorized = [];
|
|
275
|
+
const acceptedIds = new Set();
|
|
276
|
+
const seenCursors = new Set();
|
|
277
|
+
let cursor;
|
|
278
|
+
let providerCalls = 0;
|
|
279
|
+
let pageLimit = SESSION_SEARCH_RESULT_LIMIT;
|
|
280
|
+
while (authorized.length <= SESSION_SEARCH_RESULT_LIMIT) {
|
|
281
|
+
signal.throwIfAborted();
|
|
282
|
+
if (providerCalls >= SEARCH_PROVIDER_CALL_LIMIT) {
|
|
283
|
+
throw new Error(`session search provider exceeded the ${SEARCH_PROVIDER_CALL_LIMIT}-call work budget`);
|
|
284
|
+
}
|
|
285
|
+
providerCalls++;
|
|
286
|
+
const requestedCursor = cursor;
|
|
287
|
+
const requestedLimit = pageLimit;
|
|
288
|
+
let page;
|
|
289
|
+
try {
|
|
290
|
+
page = await provider.searchSessions({
|
|
291
|
+
query: normalizedQuery,
|
|
292
|
+
eventFilters: [
|
|
293
|
+
{ kind: 'type', values: ['user/message', 'assistant/message'] },
|
|
294
|
+
{ kind: 'surface', values: ['current'] },
|
|
295
|
+
],
|
|
296
|
+
limit: requestedLimit,
|
|
297
|
+
...(requestedCursor === undefined ? {} : { cursor: requestedCursor }),
|
|
298
|
+
}, { signal });
|
|
299
|
+
signal.throwIfAborted();
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
signal.throwIfAborted();
|
|
303
|
+
if (requestedCursor === undefined
|
|
304
|
+
&& error instanceof SessionQueryError
|
|
305
|
+
&& error.code === 'SESSION_QUERY_INVALID_LIMIT'
|
|
306
|
+
&& requestedLimit > 1) {
|
|
307
|
+
pageLimit = Math.max(1, Math.floor(requestedLimit / 2));
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (requestedCursor !== undefined
|
|
311
|
+
&& error instanceof SessionQueryError
|
|
312
|
+
&& error.code === 'SESSION_QUERY_STALE_CURSOR') {
|
|
313
|
+
authorized.length = 0;
|
|
314
|
+
acceptedIds.clear();
|
|
315
|
+
seenCursors.clear();
|
|
316
|
+
cursor = undefined;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
if (page.items.length > requestedLimit) {
|
|
322
|
+
throw new Error(`session search provider returned ${String(page.items.length)} items; maximum is ${String(requestedLimit)}`);
|
|
323
|
+
}
|
|
324
|
+
for (const hit of page.items) {
|
|
325
|
+
if (authorized.length > SESSION_SEARCH_RESULT_LIMIT)
|
|
326
|
+
continue;
|
|
327
|
+
if (!visibleIds.has(hit.header.id)
|
|
328
|
+
|| hit.bestMatch.sessionId !== hit.header.id
|
|
329
|
+
|| hit.bestMatch.surface !== 'current'
|
|
330
|
+
|| !MESSAGE_TYPES.has(hit.bestMatch.type)
|
|
331
|
+
|| acceptedIds.has(hit.header.id))
|
|
332
|
+
continue;
|
|
333
|
+
acceptedIds.add(hit.header.id);
|
|
334
|
+
authorized.push({
|
|
335
|
+
sessionId: hit.header.id,
|
|
336
|
+
snippet: truncateUnicodeCodePoints(hit.bestMatch.snippet, SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS),
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
if (page.nextCursor !== undefined) {
|
|
340
|
+
if (seenCursors.has(page.nextCursor)) {
|
|
341
|
+
throw new Error('session search provider repeated a continuation cursor');
|
|
342
|
+
}
|
|
343
|
+
seenCursors.add(page.nextCursor);
|
|
344
|
+
}
|
|
345
|
+
if (authorized.length > SESSION_SEARCH_RESULT_LIMIT || page.nextCursor === undefined)
|
|
346
|
+
break;
|
|
347
|
+
cursor = page.nextCursor;
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
items: authorized.slice(0, SESSION_SEARCH_RESULT_LIMIT),
|
|
351
|
+
hasMore: authorized.length > SESSION_SEARCH_RESULT_LIMIT,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
catch (error) {
|
|
355
|
+
signal.throwIfAborted();
|
|
356
|
+
if (error instanceof SessionQueryError && error.code === 'SESSION_QUERY_ABORTED') {
|
|
357
|
+
throw new RemoteError('gateway/cancelled', 'session search was aborted', {});
|
|
358
|
+
}
|
|
359
|
+
throw new RemoteError('gateway/internal', `session search failed: ${String(error)}`, {});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
projectionsFor(header, session) {
|
|
363
|
+
try {
|
|
364
|
+
const block = session === undefined
|
|
365
|
+
? this.ctx.get('sessionProjectionCache')?.cachedSnapshot(header)
|
|
366
|
+
: this.ctx.sessionProjections.cachedSnapshot(session);
|
|
367
|
+
return block !== undefined && Object.keys(block.values).length > 0
|
|
368
|
+
? {
|
|
369
|
+
asOfSeq: block.asOfSeq,
|
|
370
|
+
// Listing hints contain every currently cached wire value but remain
|
|
371
|
+
// partial: missing cells and cache rows are never materialized here.
|
|
372
|
+
values: block.values,
|
|
373
|
+
}
|
|
374
|
+
: undefined;
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
this.ctx.logger.warn(`api-session.list: projection column for "${header.id}" failed; serving the row without it: ${String(error)}`);
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function normalizeSearchQuery(query) {
|
|
383
|
+
const normalized = query.trim();
|
|
384
|
+
if (normalized.length === 0) {
|
|
385
|
+
throw new RemoteError('gateway/bad-request', 'session search query must not be empty', {});
|
|
386
|
+
}
|
|
387
|
+
if (normalized.length > SESSION_SEARCH_QUERY_MAX_CHARS) {
|
|
388
|
+
throw new RemoteError('gateway/bad-request', `session search query must contain at most ${SESSION_SEARCH_QUERY_MAX_CHARS} UTF-16 code units`, {});
|
|
389
|
+
}
|
|
390
|
+
if (normalized.includes('\0')) {
|
|
391
|
+
throw new RemoteError('gateway/bad-request', 'session search query must not contain NUL', {});
|
|
392
|
+
}
|
|
393
|
+
return normalized;
|
|
394
|
+
}
|
|
395
|
+
function updatedAt(header, metadata) {
|
|
396
|
+
return Math.max(header.createdAt, metadata?.lastPromptAt ?? 0);
|
|
397
|
+
}
|
|
398
|
+
function listFields(header) {
|
|
399
|
+
return {
|
|
400
|
+
...(header.parentSession === undefined ? {} : { parentSessionId: header.parentSession }),
|
|
401
|
+
...(header.origin === undefined ? {} : { origin: header.origin }),
|
|
402
|
+
...(header.cwd === undefined ? {} : { cwd: header.cwd }),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Durable model-selection intent and request-use projection. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
/**
|
|
4
|
+
* Register the durable model-selection projection when the registry is present.
|
|
5
|
+
* @param ctx - Session Controller context.
|
|
6
|
+
*/
|
|
7
|
+
export declare function installModelSelectionProjection(ctx: Context): void;
|
|
8
|
+
//# sourceMappingURL=model-selection-projection.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Durable model-selection intent and request-use projection. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const modelSelectionSchema = z.object({
|
|
4
|
+
provider: z.string().min(1),
|
|
5
|
+
model: z.string().min(1),
|
|
6
|
+
reasoningEffort: z.string().min(1).optional(),
|
|
7
|
+
});
|
|
8
|
+
const modelSelectionProjectionStateSchema = z.object({
|
|
9
|
+
lastUsed: modelSelectionSchema.nullable(),
|
|
10
|
+
pending: modelSelectionSchema.nullable(),
|
|
11
|
+
});
|
|
12
|
+
const modelSelectionProjectionSchema = z.object({
|
|
13
|
+
lastUsed: modelSelectionSchema.nullable(),
|
|
14
|
+
next: modelSelectionSchema.nullable(),
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Advance durable model-selection state by one Session event.
|
|
18
|
+
* @param state - selection state before the event.
|
|
19
|
+
* @param event - next committed Session event.
|
|
20
|
+
* @returns the original or advanced selection state.
|
|
21
|
+
*/
|
|
22
|
+
function applyModelSelectionProjection(state, event) {
|
|
23
|
+
if (event.type === 'model/selection') {
|
|
24
|
+
return sameSelection(state.pending, event.data)
|
|
25
|
+
? state
|
|
26
|
+
: { lastUsed: state.lastUsed, pending: event.data };
|
|
27
|
+
}
|
|
28
|
+
if (event.type !== 'request/header')
|
|
29
|
+
return state;
|
|
30
|
+
const lastUsed = {
|
|
31
|
+
provider: event.data.header.config.provider,
|
|
32
|
+
model: event.data.header.config.model,
|
|
33
|
+
...(event.data.header.config.reasoningEffort === undefined
|
|
34
|
+
? {}
|
|
35
|
+
: { reasoningEffort: String(event.data.header.config.reasoningEffort) }),
|
|
36
|
+
};
|
|
37
|
+
const pending = sameSelection(state.pending, lastUsed) ? null : state.pending;
|
|
38
|
+
return sameSelection(state.lastUsed, lastUsed) && pending === state.pending
|
|
39
|
+
? state
|
|
40
|
+
: { lastUsed, pending };
|
|
41
|
+
}
|
|
42
|
+
const modelSelectionProjection = {
|
|
43
|
+
key: 'modelSelection',
|
|
44
|
+
stateSchema: modelSelectionProjectionStateSchema,
|
|
45
|
+
init: () => ({ lastUsed: null, pending: null }),
|
|
46
|
+
apply: applyModelSelectionProjection,
|
|
47
|
+
wire: {
|
|
48
|
+
viewSchema: modelSelectionProjectionSchema,
|
|
49
|
+
view: state => ({ lastUsed: state.lastUsed, next: state.pending ?? state.lastUsed }),
|
|
50
|
+
},
|
|
51
|
+
stateVersion: 2,
|
|
52
|
+
};
|
|
53
|
+
function sameSelection(left, right) {
|
|
54
|
+
return left === right || (left !== null && right !== null
|
|
55
|
+
&& left.provider === right.provider
|
|
56
|
+
&& left.model === right.model
|
|
57
|
+
&& left.reasoningEffort === right.reasoningEffort);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Register the durable model-selection projection when the registry is present.
|
|
61
|
+
* @param ctx - Session Controller context.
|
|
62
|
+
*/
|
|
63
|
+
export function installModelSelectionProjection(ctx) {
|
|
64
|
+
ctx.sessionProjections.register(modelSelectionProjection);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=model-selection-projection.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Session Controller events available to a Remote Event assembly. */
|
|
2
|
+
type SessionControllerRemoteEvent = 'api-session/activity' | 'api-session/added' | 'api-session/error' | 'api-session/removed' | 'api-session/status';
|
|
3
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
4
|
+
interface TypertRemoteEventSelection extends Record<SessionControllerRemoteEvent, true> {
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=remote-events.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Session-addressed, cold-readable skill catalog Remote. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
4
|
+
import type { SkillListRequest, SkillListValue } from './types.ts';
|
|
5
|
+
declare module '@deepseek-ai/cordis' {
|
|
6
|
+
interface Context {
|
|
7
|
+
/** Host owner of the Session-addressed `skills` Remote namespace. */
|
|
8
|
+
sessionSkillCatalog: SessionSkillCatalog;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Host service backing `ctx.remote.skills` without activating a cold Agent. */
|
|
12
|
+
export declare class SessionSkillCatalog extends TypertRemoteService {
|
|
13
|
+
static inject: string[];
|
|
14
|
+
/** @param ctx - Host context carrying Session reads and optional skill/preset services. */
|
|
15
|
+
constructor(ctx: Context);
|
|
16
|
+
/**
|
|
17
|
+
* List the user-invocable skills visible to one Session composition.
|
|
18
|
+
* @param request - Session identity whose cwd and preset select the catalog view.
|
|
19
|
+
* @param signal - caller lifetime carried by the Remote transport; admitted catalog reads retain their existing completion semantics.
|
|
20
|
+
* @returns user-invocable skill metadata without loading skill bodies.
|
|
21
|
+
* @throws RemoteError when the Session cannot be inspected or no registry can serve it.
|
|
22
|
+
*/
|
|
23
|
+
list(request: SkillListRequest, signal: AbortSignal): Promise<SkillListValue>;
|
|
24
|
+
/** Resolve a live or standing preset scope without creating an Agent. */
|
|
25
|
+
private scopeFor;
|
|
26
|
+
}
|
|
27
|
+
export default SessionSkillCatalog;
|
|
28
|
+
//# sourceMappingURL=skill-catalog.d.ts.map
|