@deepseek-ai/dsh-api-session-controller 0.1.6-alpha.1 → 0.1.7-alpha.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/README.i18n.yaml +2 -2
- package/README.md +42 -7
- package/README.zh.md +42 -7
- package/lib/client.js +1037 -992
- package/lib/index.js +314 -183
- package/lib/typert.host.js +539 -331
- package/lib/typert.remote-client.d.ts +10 -5
- package/lib/typert.remote-client.js +374 -264
- package/lib/types/agent.d.ts +1 -1
- package/lib/types/agent.js +8 -1
- 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 +2 -0
- package/lib/types/client/contract/sessions.d.ts +65 -35
- package/lib/types/client/contract/snapshot.d.ts +0 -14
- package/lib/types/client/index.d.ts +12 -3
- package/lib/types/client/index.js +16 -5
- package/lib/types/client/scope.d.ts +13 -3
- package/lib/types/client/scope.js +15 -22
- package/lib/types/client/sessions/lineage.d.ts +5 -5
- package/lib/types/client/sessions/lineage.js +3 -4
- package/lib/types/client/sessions/manager.d.ts +91 -105
- package/lib/types/client/sessions/manager.js +307 -434
- package/lib/types/client/sessions/projection-store.d.ts +34 -21
- package/lib/types/client/sessions/projection-store.js +48 -24
- package/lib/types/client/sessions/remotes.d.ts +1 -2
- package/lib/types/client/sessions/service.d.ts +53 -150
- package/lib/types/client/sessions/service.js +267 -264
- package/lib/types/client/sessions/session.d.ts +24 -35
- package/lib/types/client/sessions/session.js +131 -73
- package/lib/types/commands.d.ts +6 -4
- package/lib/types/commands.js +82 -40
- package/lib/types/control.d.ts +2 -4
- package/lib/types/control.js +2 -84
- package/lib/types/history.js +1 -1
- package/lib/types/index.d.ts +34 -7
- package/lib/types/index.js +118 -14
- package/lib/types/list.js +24 -15
- package/lib/types/skill-catalog.js +60 -50
- package/lib/types/types.d.ts +40 -40
- package/lib/types/types.js +0 -1
- package/package.json +71 -76
- package/lib/types/client/sessions/queue-mirror.d.ts +0 -26
- package/lib/types/client/sessions/queue-mirror.js +0 -61
package/lib/types/index.js
CHANGED
|
@@ -86,9 +86,11 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
86
86
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
87
87
|
});
|
|
88
88
|
import { hostname } from 'node:os';
|
|
89
|
+
import { resolve } from 'node:path';
|
|
89
90
|
import z from '@deepseek-ai/schemastery';
|
|
90
91
|
import { errorChain } from '@deepseek-ai/dsh-llm';
|
|
91
|
-
import { canOpenNativePath, nativeFileManager,
|
|
92
|
+
import { canOpenNativePath, nativeFileManager, nativeFileApplications, openNativeFileApplication, openNativeAssociatedPath, revealNativePath } from '@deepseek-ai/dsh-native-command';
|
|
93
|
+
import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
|
|
92
94
|
import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
93
95
|
import { ApiSessionAgentController, inspectApiSession, } from "./agent.js";
|
|
94
96
|
import { SessionCommandController } from "./commands.js";
|
|
@@ -100,6 +102,7 @@ import { buildModelCatalog } from "./catalog.js";
|
|
|
100
102
|
import { installModelSelectionProjection } from "./model-selection-projection.js";
|
|
101
103
|
import { SessionSkillCatalog } from "./skill-catalog.js";
|
|
102
104
|
import { SessionMediaReferences } from "./media-references.js";
|
|
105
|
+
import { ArchivedSessionGate } from "./archived-session-gate.js";
|
|
103
106
|
export { ApiSessionNotFound } from "./agent.js";
|
|
104
107
|
export { SessionFileReferences } from "./file-references.js";
|
|
105
108
|
export { SessionSkillCatalog } from "./skill-catalog.js";
|
|
@@ -114,6 +117,7 @@ let SessionController = (() => {
|
|
|
114
117
|
let _modelCatalog_decorators;
|
|
115
118
|
let _canOpenWorkspacePath_decorators;
|
|
116
119
|
let _openWorkspacePath_decorators;
|
|
120
|
+
let _workspacePathApplications_decorators;
|
|
117
121
|
let _rename_decorators;
|
|
118
122
|
let _fork_decorators;
|
|
119
123
|
let _prompt_decorators;
|
|
@@ -122,6 +126,7 @@ let SessionController = (() => {
|
|
|
122
126
|
let _cancel_decorators;
|
|
123
127
|
let _page_decorators;
|
|
124
128
|
let _follow_decorators;
|
|
129
|
+
let _projections_decorators;
|
|
125
130
|
let _control_decorators;
|
|
126
131
|
return class SessionController extends _classSuper {
|
|
127
132
|
static {
|
|
@@ -133,6 +138,7 @@ let SessionController = (() => {
|
|
|
133
138
|
_modelCatalog_decorators = [Remote('modelCatalog')];
|
|
134
139
|
_canOpenWorkspacePath_decorators = [Remote];
|
|
135
140
|
_openWorkspacePath_decorators = [Remote('openWorkspacePath')];
|
|
141
|
+
_workspacePathApplications_decorators = [Remote('workspacePathApplications')];
|
|
136
142
|
_rename_decorators = [Remote('rename')];
|
|
137
143
|
_fork_decorators = [Remote('fork')];
|
|
138
144
|
_prompt_decorators = [Remote('prompt')];
|
|
@@ -141,6 +147,7 @@ let SessionController = (() => {
|
|
|
141
147
|
_cancel_decorators = [Remote('cancel')];
|
|
142
148
|
_page_decorators = [Remote('page')];
|
|
143
149
|
_follow_decorators = [Remote({ mode: 'stream' })];
|
|
150
|
+
_projections_decorators = [Remote('projections')];
|
|
144
151
|
_control_decorators = [Remote({ mode: 'stream' })];
|
|
145
152
|
__esDecorate(this, null, _list_decorators, { kind: "method", name: "list", static: false, private: false, access: { has: obj => "list" in obj, get: obj => obj.list }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
146
153
|
__esDecorate(this, null, _search_decorators, { kind: "method", name: "search", static: false, private: false, access: { has: obj => "search" in obj, get: obj => obj.search }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -149,6 +156,7 @@ let SessionController = (() => {
|
|
|
149
156
|
__esDecorate(this, null, _modelCatalog_decorators, { kind: "method", name: "modelCatalog", static: false, private: false, access: { has: obj => "modelCatalog" in obj, get: obj => obj.modelCatalog }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
150
157
|
__esDecorate(this, null, _canOpenWorkspacePath_decorators, { kind: "method", name: "canOpenWorkspacePath", static: false, private: false, access: { has: obj => "canOpenWorkspacePath" in obj, get: obj => obj.canOpenWorkspacePath }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
151
158
|
__esDecorate(this, null, _openWorkspacePath_decorators, { kind: "method", name: "openWorkspacePath", static: false, private: false, access: { has: obj => "openWorkspacePath" in obj, get: obj => obj.openWorkspacePath }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
159
|
+
__esDecorate(this, null, _workspacePathApplications_decorators, { kind: "method", name: "workspacePathApplications", static: false, private: false, access: { has: obj => "workspacePathApplications" in obj, get: obj => obj.workspacePathApplications }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
152
160
|
__esDecorate(this, null, _rename_decorators, { kind: "method", name: "rename", static: false, private: false, access: { has: obj => "rename" in obj, get: obj => obj.rename }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
153
161
|
__esDecorate(this, null, _fork_decorators, { kind: "method", name: "fork", static: false, private: false, access: { has: obj => "fork" in obj, get: obj => obj.fork }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
154
162
|
__esDecorate(this, null, _prompt_decorators, { kind: "method", name: "prompt", static: false, private: false, access: { has: obj => "prompt" in obj, get: obj => obj.prompt }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
@@ -157,6 +165,7 @@ let SessionController = (() => {
|
|
|
157
165
|
__esDecorate(this, null, _cancel_decorators, { kind: "method", name: "cancel", static: false, private: false, access: { has: obj => "cancel" in obj, get: obj => obj.cancel }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
158
166
|
__esDecorate(this, null, _page_decorators, { kind: "method", name: "page", static: false, private: false, access: { has: obj => "page" in obj, get: obj => obj.page }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
159
167
|
__esDecorate(this, null, _follow_decorators, { kind: "method", name: "follow", static: false, private: false, access: { has: obj => "follow" in obj, get: obj => obj.follow }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
168
|
+
__esDecorate(this, null, _projections_decorators, { kind: "method", name: "projections", static: false, private: false, access: { has: obj => "projections" in obj, get: obj => obj.projections }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
160
169
|
__esDecorate(this, null, _control_decorators, { kind: "method", name: "control", static: false, private: false, access: { has: obj => "control" in obj, get: obj => obj.control }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
161
170
|
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
162
171
|
}
|
|
@@ -165,6 +174,7 @@ let SessionController = (() => {
|
|
|
165
174
|
'agents',
|
|
166
175
|
'attachments',
|
|
167
176
|
'fileUploads',
|
|
177
|
+
'fs',
|
|
168
178
|
'llm',
|
|
169
179
|
'sessions',
|
|
170
180
|
'sessionProjections',
|
|
@@ -181,6 +191,8 @@ let SessionController = (() => {
|
|
|
181
191
|
history;
|
|
182
192
|
listState;
|
|
183
193
|
openPath;
|
|
194
|
+
fileApplications;
|
|
195
|
+
openFileApplication;
|
|
184
196
|
revealPath;
|
|
185
197
|
canOpenPath;
|
|
186
198
|
promotions = new Set();
|
|
@@ -208,19 +220,32 @@ let SessionController = (() => {
|
|
|
208
220
|
}, 'session-controller.promotions');
|
|
209
221
|
this.history = new SessionHistoryController(ctx, (observation) => { this.promote(observation); });
|
|
210
222
|
this.listState = new ApiSessionList(ctx);
|
|
211
|
-
this.
|
|
223
|
+
this.fileApplications = internals.fileApplications ?? nativeFileApplications;
|
|
224
|
+
this.openFileApplication = internals.openFileApplication ?? openNativeFileApplication;
|
|
225
|
+
this.openPath = internals.openPath ?? openNativeAssociatedPath;
|
|
212
226
|
this.revealPath = internals.revealPath ?? revealNativePath;
|
|
213
227
|
this.canOpenPath = internals.canOpenPath
|
|
214
228
|
?? (() => config.nativeOpen ?? (internals.openPath !== undefined || canOpenNativePath()));
|
|
215
229
|
ctx.plugin(SessionFileReferences);
|
|
216
230
|
ctx.plugin(SessionMediaReferences);
|
|
217
231
|
ctx.plugin(SessionSkillCatalog);
|
|
232
|
+
// An archived Session, or a subagent descendant of one, runs no model step
|
|
233
|
+
// until it is restored; what it still runs is stopped by the owners that
|
|
234
|
+
// answer the Workspace registry's archive-admission events.
|
|
235
|
+
ctx.plugin(ArchivedSessionGate);
|
|
218
236
|
ctx.on('session/created', (session) => {
|
|
219
237
|
ctx.emit('api-session/added', this.listState.summaryFor(session));
|
|
220
238
|
});
|
|
221
239
|
ctx.on('session/disposed', (session) => {
|
|
222
240
|
ctx.emit('api-session/removed', session.id);
|
|
223
241
|
});
|
|
242
|
+
const publishAgentAvailability = ({ agent }) => {
|
|
243
|
+
if (ctx.sessions.get(agent.id) === agent.session) {
|
|
244
|
+
ctx.emit('api-session/added', this.listState.summaryFor(agent.session));
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
ctx.on('agent/created', publishAgentAvailability);
|
|
248
|
+
ctx.on('agent/disposed', publishAgentAvailability);
|
|
224
249
|
ctx.on('agent/status', ({ agent, status }) => {
|
|
225
250
|
ctx.emit('api-session/status', agent.id, status === 'running');
|
|
226
251
|
});
|
|
@@ -344,29 +369,65 @@ let SessionController = (() => {
|
|
|
344
369
|
return { name: hostname(), available: fileManager !== null && this.canOpenPath(), fileManager };
|
|
345
370
|
}
|
|
346
371
|
/**
|
|
347
|
-
*
|
|
372
|
+
* Verify one path through the composed filesystem and open it on the Host desktop.
|
|
348
373
|
* @param request - path after best-effort Session workspace resolution.
|
|
349
374
|
* @param signal - caller lifetime; abort terminates the native command.
|
|
350
375
|
* @returns confirmation after the native opener accepts the path.
|
|
351
|
-
* @throws RemoteError when the request is invalid, cancelled, or the opener fails.
|
|
376
|
+
* @throws RemoteError when the request is invalid, has no verified Host mapping, is cancelled, or the opener fails.
|
|
352
377
|
*/
|
|
353
378
|
async openWorkspacePath(request, signal) {
|
|
354
|
-
if (request.path.length === 0) {
|
|
355
|
-
throw new RemoteError('gateway/bad-request', 'session.openWorkspacePath requires a non-empty path', {});
|
|
356
|
-
}
|
|
357
|
-
signal.throwIfAborted();
|
|
358
379
|
try {
|
|
380
|
+
const path = await this.verifyDesktopPath(request.path, signal);
|
|
359
381
|
if (request.action === 'reveal')
|
|
360
|
-
await this.revealPath(
|
|
382
|
+
await this.revealPath(path, signal);
|
|
383
|
+
else if (request.application !== undefined)
|
|
384
|
+
await this.openFileApplication(path, request.application, signal);
|
|
361
385
|
else
|
|
362
|
-
await this.openPath(
|
|
386
|
+
await this.openPath(path, signal);
|
|
363
387
|
return { opened: true };
|
|
364
388
|
}
|
|
365
389
|
catch (error) {
|
|
366
390
|
if (signal.aborted)
|
|
367
391
|
throw new RemoteError('gateway/cancelled', 'path open was aborted', {});
|
|
368
|
-
|
|
392
|
+
if (error instanceof RemoteError)
|
|
393
|
+
throw error;
|
|
394
|
+
throw new RemoteError('gateway/internal', 'path open failed', {}, { cause: error });
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Query current file handlers on the serving desktop without activating an Agent.
|
|
399
|
+
* @param request - file path in Host filesystem syntax.
|
|
400
|
+
* @param signal - caller lifetime, propagated to filesystem and desktop queries.
|
|
401
|
+
* @returns OS application names, icons, and default selection; empty when desktop opening is unavailable.
|
|
402
|
+
* @throws RemoteError when the path is invalid, the query is cancelled, or native discovery fails.
|
|
403
|
+
*/
|
|
404
|
+
async workspacePathApplications(request, signal) {
|
|
405
|
+
if (!this.canOpenPath())
|
|
406
|
+
return [];
|
|
407
|
+
try {
|
|
408
|
+
const path = await this.verifyDesktopPath(request.path, signal);
|
|
409
|
+
return await this.fileApplications(path, signal);
|
|
369
410
|
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
if (signal.aborted)
|
|
413
|
+
throw new RemoteError('gateway/cancelled', 'application query was aborted', {});
|
|
414
|
+
if (error instanceof RemoteError)
|
|
415
|
+
throw error;
|
|
416
|
+
throw new RemoteError('gateway/internal', 'file application query failed', {}, { cause: error });
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async verifyDesktopPath(path, signal) {
|
|
420
|
+
if (path.length === 0)
|
|
421
|
+
throw new RemoteError('gateway/bad-request', 'A non-empty file path is required', {});
|
|
422
|
+
signal.throwIfAborted();
|
|
423
|
+
const hostPath = resolve(path);
|
|
424
|
+
const { fs } = this.ctx;
|
|
425
|
+
const mapped = fs.processPathFromHostPath(hostPath);
|
|
426
|
+
if (mapped === undefined || fs.processPath(await fs.resolve(mapped, { signal })) !== hostPath) {
|
|
427
|
+
throw new RemoteError('gateway/bad-request', 'Path has no verified Host path', {});
|
|
428
|
+
}
|
|
429
|
+
signal.throwIfAborted();
|
|
430
|
+
return hostPath;
|
|
370
431
|
}
|
|
371
432
|
/**
|
|
372
433
|
* Rename one Session after explicitly resuming it.
|
|
@@ -377,8 +438,10 @@ let SessionController = (() => {
|
|
|
377
438
|
return this.commands.rename(request);
|
|
378
439
|
}
|
|
379
440
|
/**
|
|
380
|
-
* Fork one cold-readable
|
|
381
|
-
*
|
|
441
|
+
* Fork one cold-readable exact event prefix into a new Session. An omitted
|
|
442
|
+
* boundary selects the latest completed-turn prefix; an open cut receives
|
|
443
|
+
* synthetic fork closers.
|
|
444
|
+
* @param request - source Session and optional exact inclusive event boundary.
|
|
382
445
|
* @returns the new Session identity.
|
|
383
446
|
*/
|
|
384
447
|
fork(request) {
|
|
@@ -403,7 +466,7 @@ let SessionController = (() => {
|
|
|
403
466
|
return this.commands.attachment(request);
|
|
404
467
|
}
|
|
405
468
|
/**
|
|
406
|
-
* Mutate one still-pending queue occurrence
|
|
469
|
+
* Mutate one still-pending queue occurrence, resuming a cold Agent first.
|
|
407
470
|
* @param request - Session, queue item, and requested mutation.
|
|
408
471
|
* @returns acknowledgement that the queue mutation was applied.
|
|
409
472
|
*/
|
|
@@ -437,6 +500,47 @@ let SessionController = (() => {
|
|
|
437
500
|
follow(request, signal) {
|
|
438
501
|
return this.history.follow(request, signal);
|
|
439
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* Read all registered projections without activating an Agent.
|
|
505
|
+
* @param request - Session whose current values are required.
|
|
506
|
+
* @param signal - cancellation for the Session observation.
|
|
507
|
+
* @returns complete baseline, or null when the Session does not exist.
|
|
508
|
+
*/
|
|
509
|
+
async projections(request, signal) {
|
|
510
|
+
const { sessionId } = request;
|
|
511
|
+
if (sessionId.length === 0) {
|
|
512
|
+
throw new RemoteError('gateway/bad-request', 'sessionId must not be empty', {});
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
516
|
+
try {
|
|
517
|
+
const observation = __addDisposableResource(env_2, await this.ctx.sessionQuery.observeSession(sessionId, { signal }), false);
|
|
518
|
+
const projections = observation.projections;
|
|
519
|
+
if (projections === undefined) {
|
|
520
|
+
throw new RemoteError('session/projections-unavailable', 'Session projections are unavailable', {});
|
|
521
|
+
}
|
|
522
|
+
return { asOfSeq: projections.asOfSeq, values: projections.values };
|
|
523
|
+
}
|
|
524
|
+
catch (e_2) {
|
|
525
|
+
env_2.error = e_2;
|
|
526
|
+
env_2.hasError = true;
|
|
527
|
+
}
|
|
528
|
+
finally {
|
|
529
|
+
__disposeResources(env_2);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
catch (error) {
|
|
533
|
+
if (error instanceof SessionQueryError && error.code === 'SESSION_QUERY_SESSION_NOT_FOUND')
|
|
534
|
+
return null;
|
|
535
|
+
if (signal.aborted
|
|
536
|
+
|| (error instanceof SessionQueryError && error.code === 'SESSION_QUERY_ABORTED')) {
|
|
537
|
+
throw new RemoteError('gateway/cancelled', 'Session projection read was cancelled', {}, { cause: error });
|
|
538
|
+
}
|
|
539
|
+
if (error instanceof RemoteError)
|
|
540
|
+
throw error;
|
|
541
|
+
throw new RemoteError('gateway/internal', 'Session projection read failed', {}, { cause: error });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
440
544
|
/**
|
|
441
545
|
* Stream a complete live-control baseline followed by replacement frames.
|
|
442
546
|
* @param signal - cancellation owned by the Remote stream carrier.
|
package/lib/types/list.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/** Cold-safe Session list and search projection. */
|
|
2
|
-
import { SessionLogOffset } from '@deepseek-ai/dsh-session';
|
|
3
2
|
import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
|
|
4
3
|
import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
|
|
5
4
|
import { z } from 'zod';
|
|
@@ -90,6 +89,7 @@ export class ApiSessionList {
|
|
|
90
89
|
return {
|
|
91
90
|
sessionId: session.id,
|
|
92
91
|
updatedAt: updatedAt(session.header, metadata),
|
|
92
|
+
agentAvailable: this.ctx.agents.get(session.id)?.session === session,
|
|
93
93
|
running: this.ctx.agents.get(session.id)?.status === 'running',
|
|
94
94
|
blank: metadata?.blank ?? session.seq === 0,
|
|
95
95
|
...listFields(session.header),
|
|
@@ -128,6 +128,7 @@ export class ApiSessionList {
|
|
|
128
128
|
return {
|
|
129
129
|
sessionId: header.id,
|
|
130
130
|
updatedAt: updatedAt(header, metadata),
|
|
131
|
+
agentAvailable: false,
|
|
131
132
|
running: false,
|
|
132
133
|
// A large, metadata-less, or inaccessible cache miss remains unknown and visible.
|
|
133
134
|
blank: metadata?.blank ?? false,
|
|
@@ -246,21 +247,16 @@ export class ApiSessionList {
|
|
|
246
247
|
}
|
|
247
248
|
projectionsFor(header, session) {
|
|
248
249
|
try {
|
|
250
|
+
if (session !== undefined) {
|
|
251
|
+
// The live registry computed the block for this Session: its watermark
|
|
252
|
+
// shares the sequence space of the Session's baselines and frames.
|
|
253
|
+
return hintsOf('sequenced', this.ctx.sessionProjections.cachedSnapshot(session));
|
|
254
|
+
}
|
|
255
|
+
// A cold row reads the persisted cache by header alone; the cache serves
|
|
256
|
+
// seeded and unseeded lifecycles alike because a listing never seeds a
|
|
257
|
+
// fold. The watermark is the stored record's own.
|
|
249
258
|
const cache = this.ctx.get('sessionProjectionCache');
|
|
250
|
-
|
|
251
|
-
? header.isSeeded
|
|
252
|
-
? undefined
|
|
253
|
-
: cache?.cachedSnapshot(header, SessionLogOffset(0))
|
|
254
|
-
?? cache?.cachedPredecessorTitle(header, SessionLogOffset(0))
|
|
255
|
-
: this.ctx.sessionProjections.cachedSnapshot(session);
|
|
256
|
-
return block !== undefined && Object.keys(block.values).length > 0
|
|
257
|
-
? {
|
|
258
|
-
asOfSeq: block.asOfSeq,
|
|
259
|
-
// Listing hints contain every currently cached wire value but remain
|
|
260
|
-
// partial: missing cells and cache rows are never materialized here.
|
|
261
|
-
values: block.values,
|
|
262
|
-
}
|
|
263
|
-
: undefined;
|
|
259
|
+
return hintsOf('cached', cache?.cachedSnapshot(header) ?? cache?.cachedPredecessorTitle(header));
|
|
264
260
|
}
|
|
265
261
|
catch (error) {
|
|
266
262
|
this.ctx.logger.warn(`api-session.list: projection column for "${header.id}" failed; serving the row without it: ${String(error)}`);
|
|
@@ -268,6 +264,19 @@ export class ApiSessionList {
|
|
|
268
264
|
}
|
|
269
265
|
}
|
|
270
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Wrap one projection block as Session-list hints of the named sequence space.
|
|
269
|
+
* @param kind - which sequence space the block's watermark belongs to.
|
|
270
|
+
* @param block - the block, or `undefined` when no source served one.
|
|
271
|
+
* @returns the hints, or `undefined` when the block is absent or carries no value.
|
|
272
|
+
*/
|
|
273
|
+
function hintsOf(kind, block) {
|
|
274
|
+
if (block === undefined || Object.keys(block.values).length === 0)
|
|
275
|
+
return undefined;
|
|
276
|
+
// Listing hints contain every wire value the source currently holds but
|
|
277
|
+
// remain partial: missing cells and cache rows are never materialized here.
|
|
278
|
+
return { kind, asOfSeq: block.asOfSeq, values: block.values };
|
|
279
|
+
}
|
|
271
280
|
function normalizeSearchQuery(query) {
|
|
272
281
|
const normalized = query.trim();
|
|
273
282
|
if (normalized.length === 0) {
|
|
@@ -114,72 +114,82 @@ let SessionSkillCatalog = (() => {
|
|
|
114
114
|
* @throws RemoteError when the Session cannot be inspected or no registry can serve it.
|
|
115
115
|
*/
|
|
116
116
|
async list(request, signal) {
|
|
117
|
-
void
|
|
118
|
-
const { sessionId } = request;
|
|
119
|
-
let cwd;
|
|
120
|
-
let agentPreset;
|
|
117
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
121
118
|
try {
|
|
122
|
-
|
|
119
|
+
void signal;
|
|
120
|
+
const { sessionId } = request;
|
|
121
|
+
let cwd;
|
|
122
|
+
let agentPreset;
|
|
123
123
|
try {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
125
|
+
try {
|
|
126
|
+
const observation = __addDisposableResource(env_2, await this.ctx.sessionQuery.observeSession(sessionId), false);
|
|
127
|
+
if (observation.projections === undefined) {
|
|
128
|
+
throw new Error('skill catalog requires a projected Session observation');
|
|
129
|
+
}
|
|
130
|
+
cwd = observation.header.cwd;
|
|
131
|
+
agentPreset = observation.projections.values.agentPreset ?? undefined;
|
|
132
|
+
}
|
|
133
|
+
catch (e_1) {
|
|
134
|
+
env_2.error = e_1;
|
|
135
|
+
env_2.hasError = true;
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
__disposeResources(env_2);
|
|
127
139
|
}
|
|
128
|
-
cwd = observation.header.cwd;
|
|
129
|
-
agentPreset = observation.projections.values.agentPreset ?? undefined;
|
|
130
140
|
}
|
|
131
|
-
catch (
|
|
132
|
-
|
|
133
|
-
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (error instanceof SessionQueryError
|
|
143
|
+
&& error.code === 'SESSION_QUERY_SESSION_NOT_FOUND') {
|
|
144
|
+
throw new RemoteError('session/not-found', `session "${sessionId}" not found`, { sessionId });
|
|
145
|
+
}
|
|
146
|
+
throw new RemoteError('gateway/internal', `session "${sessionId}" could not be inspected: ${String(error)}`, {});
|
|
134
147
|
}
|
|
135
|
-
|
|
136
|
-
|
|
148
|
+
if (cwd === undefined) {
|
|
149
|
+
throw new RemoteError('gateway/internal', `session "${sessionId}" has no project cwd`, {});
|
|
137
150
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
const live = this.ctx.agents.get(sessionId);
|
|
152
|
+
const presets = this.ctx.get('agentPresets');
|
|
153
|
+
const scoped = live === undefined ? undefined : presets?.serviceFor(live, 'skills');
|
|
154
|
+
const skillRegistry = scoped ?? this.ctx.get('skills');
|
|
155
|
+
if (skillRegistry === undefined) {
|
|
156
|
+
throw new RemoteError('gateway/internal', 'skill registry is absent: neither this session\'s agent preset nor the host composition mounts @deepseek-ai/dsh-skill', {});
|
|
157
|
+
}
|
|
158
|
+
const lease = __addDisposableResource(env_1, live === undefined ? await this.scopeFor(agentPreset) : undefined, true);
|
|
159
|
+
const scope = live ?? lease?.key;
|
|
160
|
+
try {
|
|
161
|
+
const skills = (await skillRegistry.list({ cwd, scope })).filter(isUserInvocable);
|
|
162
|
+
return {
|
|
163
|
+
skills: skills.map(skill => ({
|
|
164
|
+
name: skill.name,
|
|
165
|
+
...skill.path === undefined ? {} : { path: skill.path },
|
|
166
|
+
description: skill.description,
|
|
167
|
+
...skill.whenToUse === undefined ? {} : { whenToUse: skill.whenToUse },
|
|
168
|
+
modelInvocable: skill.invocation.modelInvocable,
|
|
169
|
+
})),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
throw new RemoteError('gateway/internal', `skill listing failed: ${String(error)}`, {});
|
|
143
174
|
}
|
|
144
|
-
throw new RemoteError('gateway/internal', `session "${sessionId}" could not be inspected: ${String(error)}`, {});
|
|
145
|
-
}
|
|
146
|
-
if (cwd === undefined) {
|
|
147
|
-
throw new RemoteError('gateway/internal', `session "${sessionId}" has no project cwd`, {});
|
|
148
|
-
}
|
|
149
|
-
const live = this.ctx.agents.get(sessionId);
|
|
150
|
-
const presets = this.ctx.get('agentPresets');
|
|
151
|
-
const scoped = live === undefined ? undefined : presets?.serviceFor(live, 'skills');
|
|
152
|
-
const skillRegistry = scoped ?? this.ctx.get('skills');
|
|
153
|
-
if (skillRegistry === undefined) {
|
|
154
|
-
throw new RemoteError('gateway/internal', 'skill registry is absent: neither this session\'s agent preset nor the host composition mounts @deepseek-ai/dsh-skill', {});
|
|
155
175
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
skills: skills.map(skill => ({
|
|
161
|
-
name: skill.name,
|
|
162
|
-
...skill.path === undefined ? {} : { path: skill.path },
|
|
163
|
-
description: skill.description,
|
|
164
|
-
...skill.whenToUse === undefined ? {} : { whenToUse: skill.whenToUse },
|
|
165
|
-
modelInvocable: skill.invocation.modelInvocable,
|
|
166
|
-
})),
|
|
167
|
-
};
|
|
176
|
+
catch (e_2) {
|
|
177
|
+
env_1.error = e_2;
|
|
178
|
+
env_1.hasError = true;
|
|
168
179
|
}
|
|
169
|
-
|
|
170
|
-
|
|
180
|
+
finally {
|
|
181
|
+
const result_1 = __disposeResources(env_1);
|
|
182
|
+
if (result_1)
|
|
183
|
+
await result_1;
|
|
171
184
|
}
|
|
172
185
|
}
|
|
173
186
|
/** Resolve a live or standing preset scope without creating an Agent. */
|
|
174
|
-
async scopeFor(
|
|
175
|
-
const live = this.ctx.agents.get(sessionId);
|
|
176
|
-
if (live !== undefined)
|
|
177
|
-
return live;
|
|
187
|
+
async scopeFor(agentPreset) {
|
|
178
188
|
const presets = this.ctx.get('agentPresets');
|
|
179
189
|
if (presets === undefined)
|
|
180
190
|
return undefined;
|
|
181
191
|
try {
|
|
182
|
-
return await presets.
|
|
192
|
+
return await presets.acquireScope(agentPreset);
|
|
183
193
|
}
|
|
184
194
|
catch {
|
|
185
195
|
// An unknown or unusable recorded preset falls back to the global registry.
|
package/lib/types/types.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/** Browser-safe request, result, and lifecycle vocabulary for the Session Remote service. */
|
|
2
|
+
import type { NativeFileApplication } from '@deepseek-ai/dsh-native-command/types';
|
|
2
3
|
import type { AttachmentIdType, ImageAttachmentLimits, ImageAttachmentRef, ImageMediaType } from '@deepseek-ai/dsh-attachment';
|
|
3
4
|
import type { Branded } from '@deepseek-ai/dsh-brand';
|
|
4
5
|
import type { LlmAttemptId, MessageId } from '@deepseek-ai/dsh-llm/brand';
|
|
5
|
-
import type {
|
|
6
|
+
import type { TextBlock } from '@deepseek-ai/dsh-llm';
|
|
6
7
|
import type { SessionId, SessionSeqCursor } from '@deepseek-ai/dsh-session/types';
|
|
7
8
|
import type { SessionProjectionMap } from '@deepseek-ai/dsh-session-projection/types';
|
|
8
|
-
import type { JobId } from '@deepseek-ai/dsh-jobs/brand';
|
|
9
9
|
import type { JsonValue } from '@deepseek-ai/dsh-util-values';
|
|
10
10
|
import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types';
|
|
11
11
|
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
@@ -42,10 +42,24 @@ export interface SessionListMetadata {
|
|
|
42
42
|
/** Latest human-authored prompt time in the folded prefix. */
|
|
43
43
|
readonly lastPromptAt: number | null;
|
|
44
44
|
}
|
|
45
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Every available wire value a Session-list row carries as partial, possibly
|
|
47
|
+
* stale hints. `kind` and `asOfSeq` are independent facts: `kind` says which
|
|
48
|
+
* sequence space `asOfSeq` belongs to, and therefore how a client may merge
|
|
49
|
+
* the block; `asOfSeq` is the producer's watermark in that space.
|
|
50
|
+
*/
|
|
46
51
|
export interface SessionProjectionHints {
|
|
52
|
+
/**
|
|
53
|
+
* `sequenced`: the Host's live registry produced the block for an attached
|
|
54
|
+
* Session, so `asOfSeq` is comparable with baselines and frames of the same
|
|
55
|
+
* connection. `cached`: a header-only listing viewed the block from the
|
|
56
|
+
* persisted projection cache, so `asOfSeq` is the stored record's own
|
|
57
|
+
* watermark and must not be compared with the connected Session's values.
|
|
58
|
+
*/
|
|
59
|
+
readonly kind: 'cached' | 'sequenced';
|
|
60
|
+
/** Watermark of the block in the sequence space named by `kind`. */
|
|
47
61
|
readonly asOfSeq: number;
|
|
48
|
-
/** Provider-validated values present in the
|
|
62
|
+
/** Provider-validated values present in the block; omitted keys remain unknown. */
|
|
49
63
|
readonly values: SessionProjectionValues;
|
|
50
64
|
}
|
|
51
65
|
/** Complete projection values at an exact Session event cursor. */
|
|
@@ -135,7 +149,7 @@ export interface ModelCatalog {
|
|
|
135
149
|
export type QueueAction = {
|
|
136
150
|
readonly kind: 'edit';
|
|
137
151
|
/** Non-empty text-only replacement content. */
|
|
138
|
-
readonly content: readonly
|
|
152
|
+
readonly content: readonly TextBlock[];
|
|
139
153
|
} | {
|
|
140
154
|
readonly kind: 'remove';
|
|
141
155
|
} | {
|
|
@@ -143,6 +157,8 @@ export type QueueAction = {
|
|
|
143
157
|
};
|
|
144
158
|
/** One Session list entry. */
|
|
145
159
|
export interface SessionSummary {
|
|
160
|
+
/** Whether this Session currently owns a live Agent. */
|
|
161
|
+
readonly agentAvailable: boolean;
|
|
146
162
|
readonly sessionId: SessionId;
|
|
147
163
|
readonly updatedAt: number;
|
|
148
164
|
readonly running: boolean;
|
|
@@ -172,6 +188,10 @@ declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
|
172
188
|
readonly requestedCwd: string;
|
|
173
189
|
readonly existingCwd?: string;
|
|
174
190
|
};
|
|
191
|
+
'session/projections-unavailable': Record<string, never>;
|
|
192
|
+
'session/writer-held': {
|
|
193
|
+
readonly sessionId: SessionId;
|
|
194
|
+
};
|
|
175
195
|
'session/agent-busy': {
|
|
176
196
|
readonly reason: string;
|
|
177
197
|
};
|
|
@@ -284,6 +304,7 @@ export interface SessionRenameValue {
|
|
|
284
304
|
/** Session fork request. */
|
|
285
305
|
export interface SessionForkRequest {
|
|
286
306
|
readonly sessionId: SessionId;
|
|
307
|
+
/** Exact inclusive source event seq; omission selects the latest completed-turn prefix. */
|
|
287
308
|
readonly atSeq?: number;
|
|
288
309
|
}
|
|
289
310
|
/** Identity of a newly forked Session. */
|
|
@@ -336,6 +357,8 @@ export interface SessionCancelValue {
|
|
|
336
357
|
export interface SessionOpenWorkspacePathRequest {
|
|
337
358
|
/** File-manager navigation when requested; omission uses the default application. */
|
|
338
359
|
readonly action?: 'reveal';
|
|
360
|
+
/** Registered application identifier; ignored for reveal. Omission preserves the operating system default. */
|
|
361
|
+
readonly application?: string;
|
|
339
362
|
/** Path after best-effort Session workspace resolution, in Host filesystem syntax. */
|
|
340
363
|
readonly path: string;
|
|
341
364
|
}
|
|
@@ -363,8 +386,14 @@ export type SessionAddress = {
|
|
|
363
386
|
readonly kind: 'subagent';
|
|
364
387
|
readonly parentSessionId: SessionId;
|
|
365
388
|
readonly childSessionId: SessionId;
|
|
366
|
-
readonly mode: 'one-shot' | 'continuable';
|
|
389
|
+
readonly mode: 'one-shot' | 'continuable' | 'unknown';
|
|
367
390
|
};
|
|
391
|
+
/** One non-activating Session projection read. */
|
|
392
|
+
export interface SessionProjectionsRequest {
|
|
393
|
+
readonly sessionId: SessionId;
|
|
394
|
+
}
|
|
395
|
+
/** Complete Session projection baseline; null when the Session does not exist. */
|
|
396
|
+
export type SessionProjectionsValue = SessionProjectionBaseline | null;
|
|
368
397
|
/** One raw Session event in the Remote journal. */
|
|
369
398
|
export interface SessionEventEntry {
|
|
370
399
|
readonly type: 'event';
|
|
@@ -486,32 +515,8 @@ export type SessionFollowFrame = {
|
|
|
486
515
|
readonly type: 'assistant-stream';
|
|
487
516
|
readonly frame: SessionAssistantStreamFrame;
|
|
488
517
|
};
|
|
489
|
-
/** One pending inbox occurrence in the authoritative queue snapshot. */
|
|
490
|
-
export interface SessionQueuedItem {
|
|
491
|
-
readonly id: MessageId;
|
|
492
|
-
readonly placement: 'queued' | 'steering' | 'context';
|
|
493
|
-
/** Prompt-RPC identity from the queued message's user source; clients retire the matching local submission echo on it. */
|
|
494
|
-
readonly rpcId?: SessionRequestId;
|
|
495
|
-
/** JSON-safe message fields consumed by pending-queue presentation. */
|
|
496
|
-
readonly message: {
|
|
497
|
-
readonly id: MessageId;
|
|
498
|
-
readonly content: readonly JsonValue[];
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
/** Browser-safe background-job row. */
|
|
502
|
-
export interface SessionJob {
|
|
503
|
-
readonly id: JobId;
|
|
504
|
-
readonly kind: string;
|
|
505
|
-
readonly label: string;
|
|
506
|
-
readonly status: 'running' | 'stopping' | 'completed' | 'killed' | 'failed';
|
|
507
|
-
readonly detail?: string;
|
|
508
|
-
readonly startedAt: number;
|
|
509
|
-
readonly finishedAt?: number;
|
|
510
|
-
}
|
|
511
518
|
/** Complete live control baseline emitted once per control stream generation. */
|
|
512
519
|
export interface SessionControlBaseline {
|
|
513
|
-
readonly queues: Readonly<Record<SessionId, readonly SessionQueuedItem[]>>;
|
|
514
|
-
readonly jobs: Readonly<Record<SessionId, readonly SessionJob[]>>;
|
|
515
520
|
readonly projections: Readonly<Record<SessionId, SessionProjectionBaseline>>;
|
|
516
521
|
}
|
|
517
522
|
/** One finished projection value and its durable watermark. */
|
|
@@ -525,23 +530,16 @@ export interface SessionProjectionUpdate {
|
|
|
525
530
|
export type SessionControlFrame = {
|
|
526
531
|
readonly type: 'baseline';
|
|
527
532
|
readonly value: SessionControlBaseline;
|
|
528
|
-
} | {
|
|
529
|
-
readonly type: 'queue';
|
|
530
|
-
readonly sessionId: SessionId;
|
|
531
|
-
readonly items: readonly SessionQueuedItem[];
|
|
532
|
-
} | {
|
|
533
|
-
readonly type: 'jobs';
|
|
534
|
-
readonly sessionId: SessionId;
|
|
535
|
-
readonly jobs: readonly SessionJob[];
|
|
536
533
|
} | ({
|
|
537
534
|
readonly type: 'projection';
|
|
538
535
|
} & SessionProjectionUpdate);
|
|
539
536
|
declare module '@deepseek-ai/cordis' {
|
|
540
537
|
interface Events {
|
|
541
538
|
/**
|
|
542
|
-
* A Session became visible
|
|
539
|
+
* A Session became visible or its Agent was created or disposed.
|
|
540
|
+
* Consumers upsert the summary and replace its current running and availability state.
|
|
543
541
|
* @mode emit
|
|
544
|
-
* @param summary -
|
|
542
|
+
* @param summary - current list row for the Session.
|
|
545
543
|
*/
|
|
546
544
|
'api-session/added'(summary: SessionSummary): void;
|
|
547
545
|
/**
|
|
@@ -575,4 +573,6 @@ declare module '@deepseek-ai/cordis' {
|
|
|
575
573
|
}
|
|
576
574
|
/** JSON-compatible projection value accepted by list consumers. */
|
|
577
575
|
export type SessionProjectionValue = JsonValue;
|
|
576
|
+
/** Application metadata returned by the serving desktop for one file. */
|
|
577
|
+
export type SessionWorkspacePathApplication = NativeFileApplication;
|
|
578
578
|
//# sourceMappingURL=types.d.ts.map
|
package/lib/types/types.js
CHANGED