@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/commands.js
CHANGED
|
@@ -55,6 +55,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
55
55
|
import { brandString } from '@deepseek-ai/dsh-brand';
|
|
56
56
|
import { AttachmentError } from '@deepseek-ai/dsh-attachment';
|
|
57
57
|
import { ReasoningEffortId, assistantStreamChunks, createUserMessage, freezeMessage, } from '@deepseek-ai/dsh-llm';
|
|
58
|
+
import { buildForkSeed } from '@deepseek-ai/dsh-session/fork';
|
|
58
59
|
import { SessionLogOffset, SessionSeq } from '@deepseek-ai/dsh-session';
|
|
59
60
|
import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
|
|
60
61
|
import { SessionTitleInvalidError } from '@deepseek-ai/dsh-session-title';
|
|
@@ -65,6 +66,23 @@ import { ApiSessionCwdConflict, ApiSessionNotFound, ApiSessionPresetConflict, Ap
|
|
|
65
66
|
function hasPromptContent(content) {
|
|
66
67
|
return content.some(part => part.type !== 'text' || part.text.trim().length > 0);
|
|
67
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Resolve the omitted-`atSeq` default to the latest completed-turn prefix,
|
|
71
|
+
* including standalone events before the next turn begins.
|
|
72
|
+
*/
|
|
73
|
+
function latestCompletedPrefixBoundary(events) {
|
|
74
|
+
const lastTurnEnd = events.findLast(event => event.type === 'turn/end');
|
|
75
|
+
if (lastTurnEnd === undefined)
|
|
76
|
+
return undefined;
|
|
77
|
+
let boundary = lastTurnEnd.seq;
|
|
78
|
+
for (const next of events.slice(boundary + 1)) {
|
|
79
|
+
if (next.type === 'turn/start' || (next.type === 'user/message' && next.surfaceOp === 'append')
|
|
80
|
+
|| next.type === 'agent/inbox/spliced')
|
|
81
|
+
break;
|
|
82
|
+
boundary = next.seq;
|
|
83
|
+
}
|
|
84
|
+
return boundary;
|
|
85
|
+
}
|
|
68
86
|
/** Implements Session business commands delegated by the Session Controller Remote service. */
|
|
69
87
|
export class SessionCommandController {
|
|
70
88
|
ctx;
|
|
@@ -180,8 +198,10 @@ export class SessionCommandController {
|
|
|
180
198
|
}
|
|
181
199
|
}
|
|
182
200
|
/**
|
|
183
|
-
* Create a new ordinary Session from
|
|
184
|
-
*
|
|
201
|
+
* Create a new ordinary Session from an exact event prefix. An explicit
|
|
202
|
+
* `atSeq` is the inclusive cut; an omitted value selects the latest
|
|
203
|
+
* completed-turn prefix. An open cut receives synthetic fork closers.
|
|
204
|
+
* @param request - source Session and optional exact event boundary.
|
|
185
205
|
* @returns the new Session identity.
|
|
186
206
|
*/
|
|
187
207
|
async fork(request) {
|
|
@@ -208,20 +228,13 @@ export class SessionCommandController {
|
|
|
208
228
|
throw new RemoteError('gateway/internal', `fork source unavailable for session "${request.sessionId}": ${String(error)}`, {});
|
|
209
229
|
}
|
|
210
230
|
const source = __addDisposableResource(env_1, observed, false);
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
?? (atSeq === undefined || atSeq > lastSeq
|
|
217
|
-
? source.events.findLast(event => event.type === 'turn/end')
|
|
218
|
-
: undefined);
|
|
219
|
-
if (boundary === undefined) {
|
|
220
|
-
throw new RemoteError('session/fork-unavailable', atSeq !== undefined && atSeq <= lastSeq
|
|
221
|
-
? `session "${request.sessionId}" has not completed the turn containing event ${String(atSeq)}`
|
|
222
|
-
: `session "${request.sessionId}" has no completed turn to fork from`, { sessionId: request.sessionId });
|
|
231
|
+
const boundary = atSeq ?? latestCompletedPrefixBoundary(source.events);
|
|
232
|
+
if (boundary === undefined || source.events[boundary]?.seq !== boundary) {
|
|
233
|
+
throw new RemoteError('session/fork-unavailable', request.atSeq === undefined
|
|
234
|
+
? `session "${request.sessionId}" has no completed turn to fork from`
|
|
235
|
+
: `event ${String(request.atSeq)} does not exist in session "${request.sessionId}" (last seq: ${String(source.events.at(-1)?.seq ?? 'none')})`, { sessionId: request.sessionId });
|
|
223
236
|
}
|
|
224
|
-
const
|
|
237
|
+
const seed = buildForkSeed(source.events, boundary);
|
|
225
238
|
let workspace;
|
|
226
239
|
try {
|
|
227
240
|
workspace = await this.forkWorkspace(source.header);
|
|
@@ -235,8 +248,8 @@ export class SessionCommandController {
|
|
|
235
248
|
const { provider, model } = this.ctx.agentDefaultModel.currentSelection();
|
|
236
249
|
await this.ctx.agents.create({
|
|
237
250
|
sessionId: childId,
|
|
238
|
-
seed
|
|
239
|
-
inheritedEventCount:
|
|
251
|
+
seed,
|
|
252
|
+
inheritedEventCount: SessionLogOffset(boundary + 1),
|
|
240
253
|
meta: {
|
|
241
254
|
...(source.header.cwd === undefined ? {} : { cwd: source.header.cwd }),
|
|
242
255
|
parentSession: source.header.id,
|
|
@@ -377,12 +390,13 @@ export class SessionCommandController {
|
|
|
377
390
|
}
|
|
378
391
|
}
|
|
379
392
|
/**
|
|
380
|
-
* Mutate one
|
|
393
|
+
* Mutate one pending Inbox occurrence, restoring an ordinary cold Agent when needed.
|
|
381
394
|
* @param request - Session, queue item, and requested mutation.
|
|
382
395
|
* @returns acknowledgement that the queue mutation was applied.
|
|
383
396
|
*/
|
|
384
|
-
updateQueue(request) {
|
|
397
|
+
async updateQueue(request) {
|
|
385
398
|
if (request.action.kind === 'edit') {
|
|
399
|
+
// oxlint-disable-next-line typescript/no-unnecessary-condition -- Remote callers can submit untyped JSON.
|
|
386
400
|
if (request.action.content.some(block => block.type !== 'text')) {
|
|
387
401
|
throw new RemoteError('session/attachment-invalid', 'queue edits accept text content only', { reason: 'QUEUE_EDIT_NON_TEXT' });
|
|
388
402
|
}
|
|
@@ -390,9 +404,15 @@ export class SessionCommandController {
|
|
|
390
404
|
throw new RemoteError('gateway/bad-request', 'queue edit content must include non-whitespace text', {});
|
|
391
405
|
}
|
|
392
406
|
}
|
|
393
|
-
|
|
407
|
+
let agent = this.ctx.agents.get(request.sessionId);
|
|
394
408
|
if (agent === undefined) {
|
|
395
|
-
|
|
409
|
+
const found = await this.agents.resolveAgent(request.sessionId);
|
|
410
|
+
if ('error' in found) {
|
|
411
|
+
if (found.error.code !== 'session/not-found')
|
|
412
|
+
throw found.error;
|
|
413
|
+
throw new RemoteError('session/queue-item-not-found', 'queued item is no longer pending', { itemId: request.itemId });
|
|
414
|
+
}
|
|
415
|
+
agent = found.agent;
|
|
396
416
|
}
|
|
397
417
|
if (hasApiSessionSubagentOwner(this.ctx, agent.session, agent)) {
|
|
398
418
|
const identity = this.ctx.sessionProjections
|
|
@@ -465,6 +485,9 @@ export class SessionCommandController {
|
|
|
465
485
|
rejectCreation(sessionId, error) {
|
|
466
486
|
if (remoteErrorOf(error) !== undefined)
|
|
467
487
|
throw error;
|
|
488
|
+
if (error instanceof Error && error.name === 'SessionAlreadyOwnedError') {
|
|
489
|
+
throw new RemoteError('session/writer-held', error.message, { sessionId });
|
|
490
|
+
}
|
|
468
491
|
if (error instanceof ApiSessionPresetConflict) {
|
|
469
492
|
throw new RemoteError('agent-preset/conflict', error.message, {
|
|
470
493
|
sessionId: error.sessionId,
|
|
@@ -548,33 +571,52 @@ function imageBlockIn(content, match) {
|
|
|
548
571
|
if (match(ref))
|
|
549
572
|
return ref;
|
|
550
573
|
}
|
|
551
|
-
if (block.type === 'tool-result') {
|
|
552
|
-
const nested = imageBlockIn(block.content, match);
|
|
553
|
-
if (nested !== undefined)
|
|
554
|
-
return nested;
|
|
555
|
-
}
|
|
556
574
|
}
|
|
557
575
|
return undefined;
|
|
558
576
|
}
|
|
577
|
+
/** Read only first-party declared content fields; unknown event payloads stay opaque. */
|
|
559
578
|
function imageInEvent(event, match) {
|
|
560
579
|
const data = event.data;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
580
|
+
// First-party event payloads can be present without their producer plugin mounted.
|
|
581
|
+
const type = event.type;
|
|
582
|
+
switch (type) {
|
|
583
|
+
case 'user/message':
|
|
584
|
+
case 'tool/ptc-dispatch':
|
|
585
|
+
return imageBlockIn(data.content, match);
|
|
586
|
+
case 'system/message':
|
|
587
|
+
case 'developer/message':
|
|
588
|
+
case 'tool/result':
|
|
589
|
+
case 'team/message/queued':
|
|
590
|
+
return imageBlockIn(data.message?.content, match);
|
|
591
|
+
case 'agent/inbox/spliced': {
|
|
592
|
+
const messages = data.inserted;
|
|
593
|
+
if (!Array.isArray(messages))
|
|
594
|
+
return undefined;
|
|
595
|
+
for (const message of messages) {
|
|
596
|
+
if (typeof message !== 'object' || message === null || Array.isArray(message))
|
|
597
|
+
continue;
|
|
598
|
+
const found = imageBlockIn(message.content, match);
|
|
599
|
+
if (found !== undefined)
|
|
600
|
+
return found;
|
|
601
|
+
}
|
|
602
|
+
return undefined;
|
|
603
|
+
}
|
|
604
|
+
case 'compaction/summary':
|
|
605
|
+
return imageBlockIn(data.summary, match) ?? imageBlockIn(data.rawOutput, match);
|
|
606
|
+
case 'assistant/message': {
|
|
607
|
+
const found = imageBlockIn(data.message?.content, match);
|
|
575
608
|
if (found !== undefined)
|
|
576
609
|
return found;
|
|
610
|
+
break;
|
|
577
611
|
}
|
|
612
|
+
case 'assistant/attempt': break;
|
|
613
|
+
default: return undefined;
|
|
614
|
+
}
|
|
615
|
+
const assistant = event;
|
|
616
|
+
for (const chunk of assistantStreamChunks(assistant.data.stream, 'block-end')) {
|
|
617
|
+
const found = imageBlockIn([chunk.block], match);
|
|
618
|
+
if (found !== undefined)
|
|
619
|
+
return found;
|
|
578
620
|
}
|
|
579
621
|
return undefined;
|
|
580
622
|
}
|
package/lib/types/control.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/** Live Session
|
|
1
|
+
/** Live Session projection state with reconnect baselines. */
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
3
|
import type { SessionControlFrame } from './types.ts';
|
|
4
4
|
/** Owns the Host-wide Session control stream. */
|
|
5
5
|
export declare class SessionControlController {
|
|
6
6
|
private readonly ctx;
|
|
7
7
|
private readonly streams;
|
|
8
|
-
/** @param ctx - Host context carrying live Agent
|
|
8
|
+
/** @param ctx - Host context carrying live Agent and projection services. */
|
|
9
9
|
constructor(ctx: Context);
|
|
10
10
|
/**
|
|
11
11
|
* Open one generation of Host-wide live control state.
|
|
@@ -15,8 +15,6 @@ export declare class SessionControlController {
|
|
|
15
15
|
control(signal: AbortSignal): AsyncIterable<SessionControlFrame>;
|
|
16
16
|
private baseline;
|
|
17
17
|
private projectionBaseline;
|
|
18
|
-
private onJobsChanged;
|
|
19
|
-
private jobsFor;
|
|
20
18
|
private broadcast;
|
|
21
19
|
}
|
|
22
20
|
//# sourceMappingURL=control.d.ts.map
|
package/lib/types/control.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/** Live Session
|
|
1
|
+
/** Live Session projection state with reconnect baselines. */
|
|
2
2
|
import { Deque } from '@deepseek-ai/dsh-deque';
|
|
3
3
|
/** Owns the Host-wide Session control stream. */
|
|
4
4
|
export class SessionControlController {
|
|
5
5
|
ctx;
|
|
6
6
|
streams = new Set();
|
|
7
|
-
/** @param ctx - Host context carrying live Agent
|
|
7
|
+
/** @param ctx - Host context carrying live Agent and projection services. */
|
|
8
8
|
constructor(ctx) {
|
|
9
9
|
this.ctx = ctx;
|
|
10
10
|
ctx.sessionProjections.onChanged((session, key, value, seq) => {
|
|
@@ -15,24 +15,6 @@ export class SessionControlController {
|
|
|
15
15
|
value: value,
|
|
16
16
|
seq,
|
|
17
17
|
});
|
|
18
|
-
if (key !== 'inbox')
|
|
19
|
-
return;
|
|
20
|
-
const agent = this.ctx.agents.get(session.id);
|
|
21
|
-
if (agent?.session !== session)
|
|
22
|
-
return;
|
|
23
|
-
this.broadcast({
|
|
24
|
-
type: 'queue',
|
|
25
|
-
sessionId: session.id,
|
|
26
|
-
items: queueItemsFromInbox(value),
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
ctx.inject(['jobs'], (jobsCtx) => {
|
|
30
|
-
jobsCtx.jobs.onJobsChanged((owner) => { this.onJobsChanged(owner); });
|
|
31
|
-
});
|
|
32
|
-
ctx.on('session/created', (session) => {
|
|
33
|
-
const jobs = this.jobsFor(this.ctx.agents.get(session.id));
|
|
34
|
-
if (jobs.length > 0)
|
|
35
|
-
this.broadcast({ type: 'jobs', sessionId: session.id, jobs });
|
|
36
18
|
});
|
|
37
19
|
ctx.effect(() => () => {
|
|
38
20
|
for (const stream of this.streams)
|
|
@@ -60,16 +42,7 @@ export class SessionControlController {
|
|
|
60
42
|
}
|
|
61
43
|
baseline() {
|
|
62
44
|
const sessions = this.ctx.sessions.list();
|
|
63
|
-
const queues = Object.create(null);
|
|
64
|
-
const jobs = Object.create(null);
|
|
65
|
-
for (const session of sessions) {
|
|
66
|
-
const agent = this.ctx.agents.get(session.id);
|
|
67
|
-
queues[session.id] = agent?.session === session ? queueItems(agent) : [];
|
|
68
|
-
jobs[session.id] = this.jobsFor(agent);
|
|
69
|
-
}
|
|
70
45
|
return {
|
|
71
|
-
queues,
|
|
72
|
-
jobs,
|
|
73
46
|
projections: this.projectionBaseline(sessions),
|
|
74
47
|
};
|
|
75
48
|
}
|
|
@@ -85,23 +58,6 @@ export class SessionControlController {
|
|
|
85
58
|
}
|
|
86
59
|
return blocks;
|
|
87
60
|
}
|
|
88
|
-
onJobsChanged(owner) {
|
|
89
|
-
if (owner !== undefined) {
|
|
90
|
-
this.broadcast({ type: 'jobs', sessionId: owner.id, jobs: this.jobsFor(owner) });
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
for (const session of this.ctx.sessions.list()) {
|
|
94
|
-
this.broadcast({
|
|
95
|
-
type: 'jobs',
|
|
96
|
-
sessionId: session.id,
|
|
97
|
-
jobs: this.jobsFor(this.ctx.agents.get(session.id)),
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
jobsFor(agent) {
|
|
102
|
-
const jobs = this.ctx.get('jobs');
|
|
103
|
-
return jobs === undefined ? [] : jobs.list(agent).map(jobView);
|
|
104
|
-
}
|
|
105
61
|
broadcast(frame) {
|
|
106
62
|
for (const stream of this.streams)
|
|
107
63
|
stream.push(frame);
|
|
@@ -148,42 +104,4 @@ class ControlQueue {
|
|
|
148
104
|
}
|
|
149
105
|
}
|
|
150
106
|
}
|
|
151
|
-
function queueItems(agent) {
|
|
152
|
-
return queueItemsFromInbox({
|
|
153
|
-
'next-turn': agent.inbox.nextTurn,
|
|
154
|
-
'next-step': agent.inbox.nextStep,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
function queueItemsFromInbox(inbox) {
|
|
158
|
-
return [
|
|
159
|
-
...inbox['next-turn'].map(message => ({
|
|
160
|
-
id: message.id,
|
|
161
|
-
placement: 'queued',
|
|
162
|
-
...promptRpcId(message),
|
|
163
|
-
message: { id: message.id, content: message.content },
|
|
164
|
-
})),
|
|
165
|
-
...inbox['next-step'].map(message => ({
|
|
166
|
-
id: message.id,
|
|
167
|
-
placement: message.source.kind === 'user' ? 'steering' : 'context',
|
|
168
|
-
...promptRpcId(message),
|
|
169
|
-
message: { id: message.id, content: message.content },
|
|
170
|
-
})),
|
|
171
|
-
];
|
|
172
|
-
}
|
|
173
|
-
/** Prompt-RPC identity carried by a browser-submitted message's user source. */
|
|
174
|
-
function promptRpcId(message) {
|
|
175
|
-
const source = message.source;
|
|
176
|
-
return source.kind === 'user' && 'rpcId' in source ? { rpcId: source.rpcId } : {};
|
|
177
|
-
}
|
|
178
|
-
function jobView(job) {
|
|
179
|
-
return {
|
|
180
|
-
id: job.id,
|
|
181
|
-
kind: job.kind,
|
|
182
|
-
label: job.label,
|
|
183
|
-
status: job.status,
|
|
184
|
-
...(job.detail === undefined ? {} : { detail: job.detail }),
|
|
185
|
-
startedAt: job.startedAt,
|
|
186
|
-
...(job.finishedAt === undefined ? {} : { finishedAt: job.finishedAt }),
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
107
|
//# sourceMappingURL=control.js.map
|
package/lib/types/history.js
CHANGED
|
@@ -370,7 +370,7 @@ function validateAddress(address, header, inheritedEventCount, projections) {
|
|
|
370
370
|
reason: 'unsupported',
|
|
371
371
|
});
|
|
372
372
|
}
|
|
373
|
-
if (identity.mode !== address.mode) {
|
|
373
|
+
if (address.mode !== 'unknown' && identity.mode !== address.mode) {
|
|
374
374
|
throw new RemoteError('subagent/unauthorized', 'subagent mode does not match the supplied address', {
|
|
375
375
|
childSessionId: address.childSessionId,
|
|
376
376
|
});
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/** Session Remote owner: cold reads, explicit Agent commands, and live control state. */
|
|
2
2
|
import { Context } from '@deepseek-ai/cordis';
|
|
3
3
|
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
import { nativeFileApplications, openNativeFileApplication } from '@deepseek-ai/dsh-native-command';
|
|
4
5
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
5
6
|
import type { SessionInspection } from '@deepseek-ai/dsh-session-persistence';
|
|
6
7
|
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
7
8
|
import { type ApiSessionAgentResult } from './agent.ts';
|
|
8
9
|
import { buildModelCatalog } from './catalog.ts';
|
|
9
|
-
import type { ModelCatalog, SessionAttachmentRequest, SessionAttachmentValue, SessionCancelRequest, SessionCancelValue, SessionControlFrame, SessionCreateRequest, SessionCreateValue, SessionFollowFrame, SessionFollowRequest, SessionForkRequest, SessionForkValue, SessionListRequest, SessionListValue, SessionOpenWorkspacePathRequest, SessionOpenWorkspacePathValue, SessionPage, SessionPageRequest, SessionPromptRequest, SessionPromptValue, SessionRenameRequest, SessionRenameValue, SessionSearchRequest, SessionSearchValue, SessionSelectModelRequest, SessionSelectModelValue, SessionUpdateQueueRequest, SessionUpdateQueueValue } from './types.ts';
|
|
10
|
+
import type { ModelCatalog, SessionWorkspacePathApplication, SessionAttachmentRequest, SessionAttachmentValue, SessionCancelRequest, SessionCancelValue, SessionControlFrame, SessionCreateRequest, SessionCreateValue, SessionFollowFrame, SessionFollowRequest, SessionForkRequest, SessionForkValue, SessionListRequest, SessionListValue, SessionOpenWorkspacePathRequest, SessionOpenWorkspacePathValue, SessionPage, SessionPageRequest, SessionPromptRequest, SessionPromptValue, SessionRenameRequest, SessionRenameValue, SessionSearchRequest, SessionSearchValue, SessionSelectModelRequest, SessionSelectModelValue, SessionProjectionsRequest, SessionProjectionsValue, SessionUpdateQueueRequest, SessionUpdateQueueValue } from './types.ts';
|
|
10
11
|
export type * from './types.ts';
|
|
11
12
|
export { ApiSessionNotFound } from './agent.ts';
|
|
12
13
|
export { SessionFileReferences } from './file-references.ts';
|
|
@@ -26,6 +27,10 @@ export interface Config {
|
|
|
26
27
|
export interface SessionControllerInternals {
|
|
27
28
|
/** Native default-application handoff. */
|
|
28
29
|
readonly openPath?: (path: string, signal: AbortSignal) => Promise<void>;
|
|
30
|
+
/** Native file-association query. */
|
|
31
|
+
readonly fileApplications?: typeof nativeFileApplications;
|
|
32
|
+
/** Explicit registered-application handoff. */
|
|
33
|
+
readonly openFileApplication?: typeof openNativeFileApplication;
|
|
29
34
|
/** Native file-manager handoff. */
|
|
30
35
|
readonly revealPath?: (path: string, signal: AbortSignal) => Promise<void>;
|
|
31
36
|
/** Native handoff availability probe. */
|
|
@@ -41,6 +46,8 @@ export declare class SessionController extends TypertRemoteService {
|
|
|
41
46
|
private readonly history;
|
|
42
47
|
private readonly listState;
|
|
43
48
|
private readonly openPath;
|
|
49
|
+
private readonly fileApplications;
|
|
50
|
+
private readonly openFileApplication;
|
|
44
51
|
private readonly revealPath;
|
|
45
52
|
private readonly canOpenPath;
|
|
46
53
|
private readonly promotions;
|
|
@@ -110,13 +117,24 @@ export declare class SessionController extends TypertRemoteService {
|
|
|
110
117
|
fileManager: 'finder' | 'explorer' | 'directory' | null;
|
|
111
118
|
};
|
|
112
119
|
/**
|
|
113
|
-
*
|
|
120
|
+
* Verify one path through the composed filesystem and open it on the Host desktop.
|
|
114
121
|
* @param request - path after best-effort Session workspace resolution.
|
|
115
122
|
* @param signal - caller lifetime; abort terminates the native command.
|
|
116
123
|
* @returns confirmation after the native opener accepts the path.
|
|
117
|
-
* @throws RemoteError when the request is invalid, cancelled, or the opener fails.
|
|
124
|
+
* @throws RemoteError when the request is invalid, has no verified Host mapping, is cancelled, or the opener fails.
|
|
118
125
|
*/
|
|
119
126
|
openWorkspacePath(request: SessionOpenWorkspacePathRequest, signal: AbortSignal): Promise<SessionOpenWorkspacePathValue>;
|
|
127
|
+
/**
|
|
128
|
+
* Query current file handlers on the serving desktop without activating an Agent.
|
|
129
|
+
* @param request - file path in Host filesystem syntax.
|
|
130
|
+
* @param signal - caller lifetime, propagated to filesystem and desktop queries.
|
|
131
|
+
* @returns OS application names, icons, and default selection; empty when desktop opening is unavailable.
|
|
132
|
+
* @throws RemoteError when the path is invalid, the query is cancelled, or native discovery fails.
|
|
133
|
+
*/
|
|
134
|
+
workspacePathApplications(request: {
|
|
135
|
+
readonly path: string;
|
|
136
|
+
}, signal: AbortSignal): Promise<readonly SessionWorkspacePathApplication[]>;
|
|
137
|
+
private verifyDesktopPath;
|
|
120
138
|
/**
|
|
121
139
|
* Rename one Session after explicitly resuming it.
|
|
122
140
|
* @param request - Session identity and proposed title.
|
|
@@ -124,8 +142,10 @@ export declare class SessionController extends TypertRemoteService {
|
|
|
124
142
|
*/
|
|
125
143
|
rename(request: SessionRenameRequest): Promise<SessionRenameValue>;
|
|
126
144
|
/**
|
|
127
|
-
* Fork one cold-readable
|
|
128
|
-
*
|
|
145
|
+
* Fork one cold-readable exact event prefix into a new Session. An omitted
|
|
146
|
+
* boundary selects the latest completed-turn prefix; an open cut receives
|
|
147
|
+
* synthetic fork closers.
|
|
148
|
+
* @param request - source Session and optional exact inclusive event boundary.
|
|
129
149
|
* @returns the new Session identity.
|
|
130
150
|
*/
|
|
131
151
|
fork(request: SessionForkRequest): Promise<SessionForkValue>;
|
|
@@ -143,11 +163,11 @@ export declare class SessionController extends TypertRemoteService {
|
|
|
143
163
|
*/
|
|
144
164
|
attachment(request: SessionAttachmentRequest): Promise<SessionAttachmentValue>;
|
|
145
165
|
/**
|
|
146
|
-
* Mutate one still-pending queue occurrence
|
|
166
|
+
* Mutate one still-pending queue occurrence, resuming a cold Agent first.
|
|
147
167
|
* @param request - Session, queue item, and requested mutation.
|
|
148
168
|
* @returns acknowledgement that the queue mutation was applied.
|
|
149
169
|
*/
|
|
150
|
-
updateQueue(request: SessionUpdateQueueRequest): SessionUpdateQueueValue
|
|
170
|
+
updateQueue(request: SessionUpdateQueueRequest): Promise<SessionUpdateQueueValue>;
|
|
151
171
|
/**
|
|
152
172
|
* Cancel one active Agent turn without dropping its pending inbox.
|
|
153
173
|
* @param request - Session whose active Agent turn is cancelled.
|
|
@@ -169,6 +189,13 @@ export declare class SessionController extends TypertRemoteService {
|
|
|
169
189
|
* frames and optional cursorless assistant-stream frames.
|
|
170
190
|
*/
|
|
171
191
|
follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>;
|
|
192
|
+
/**
|
|
193
|
+
* Read all registered projections without activating an Agent.
|
|
194
|
+
* @param request - Session whose current values are required.
|
|
195
|
+
* @param signal - cancellation for the Session observation.
|
|
196
|
+
* @returns complete baseline, or null when the Session does not exist.
|
|
197
|
+
*/
|
|
198
|
+
projections(request: SessionProjectionsRequest, signal: AbortSignal): Promise<SessionProjectionsValue>;
|
|
172
199
|
/**
|
|
173
200
|
* Stream a complete live-control baseline followed by replacement frames.
|
|
174
201
|
* @param signal - cancellation owned by the Remote stream carrier.
|