@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.
Files changed (44) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +42 -7
  3. package/README.zh.md +42 -7
  4. package/lib/client.js +1037 -992
  5. package/lib/index.js +314 -183
  6. package/lib/typert.host.js +539 -331
  7. package/lib/typert.remote-client.d.ts +10 -5
  8. package/lib/typert.remote-client.js +374 -264
  9. package/lib/types/agent.d.ts +1 -1
  10. package/lib/types/agent.js +8 -1
  11. package/lib/types/archived-session-gate.d.ts +30 -0
  12. package/lib/types/archived-session-gate.js +52 -0
  13. package/lib/types/client/contract/session.d.ts +2 -0
  14. package/lib/types/client/contract/sessions.d.ts +65 -35
  15. package/lib/types/client/contract/snapshot.d.ts +0 -14
  16. package/lib/types/client/index.d.ts +12 -3
  17. package/lib/types/client/index.js +16 -5
  18. package/lib/types/client/scope.d.ts +13 -3
  19. package/lib/types/client/scope.js +15 -22
  20. package/lib/types/client/sessions/lineage.d.ts +5 -5
  21. package/lib/types/client/sessions/lineage.js +3 -4
  22. package/lib/types/client/sessions/manager.d.ts +91 -105
  23. package/lib/types/client/sessions/manager.js +307 -434
  24. package/lib/types/client/sessions/projection-store.d.ts +34 -21
  25. package/lib/types/client/sessions/projection-store.js +48 -24
  26. package/lib/types/client/sessions/remotes.d.ts +1 -2
  27. package/lib/types/client/sessions/service.d.ts +53 -150
  28. package/lib/types/client/sessions/service.js +267 -264
  29. package/lib/types/client/sessions/session.d.ts +24 -35
  30. package/lib/types/client/sessions/session.js +131 -73
  31. package/lib/types/commands.d.ts +6 -4
  32. package/lib/types/commands.js +82 -40
  33. package/lib/types/control.d.ts +2 -4
  34. package/lib/types/control.js +2 -84
  35. package/lib/types/history.js +1 -1
  36. package/lib/types/index.d.ts +34 -7
  37. package/lib/types/index.js +118 -14
  38. package/lib/types/list.js +24 -15
  39. package/lib/types/skill-catalog.js +60 -50
  40. package/lib/types/types.d.ts +40 -40
  41. package/lib/types/types.js +0 -1
  42. package/package.json +71 -76
  43. package/lib/types/client/sessions/queue-mirror.d.ts +0 -26
  44. package/lib/types/client/sessions/queue-mirror.js +0 -61
@@ -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 one completed-turn prefix.
184
- * @param request - source Session and optional event anchor.
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 lastSeq = source.events.at(-1)?.seq ?? -1;
212
- const anchoredBoundary = atSeq === undefined
213
- ? undefined
214
- : source.events.find(event => event.type === 'turn/end' && event.seq >= atSeq);
215
- const boundary = anchoredBoundary
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 cut = SessionLogOffset(boundary.seq + 1);
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: source.events.slice(0, cut),
239
- inheritedEventCount: cut,
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 still-pending queue occurrence without resuming a cold Agent.
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
- const agent = this.ctx.agents.get(request.sessionId);
407
+ let agent = this.ctx.agents.get(request.sessionId);
394
408
  if (agent === undefined) {
395
- throw new RemoteError('session/queue-item-not-found', 'queued item is no longer pending', { itemId: request.itemId });
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
- const direct = imageBlockIn(data.content, match);
562
- if (direct !== undefined)
563
- return direct;
564
- const message = imageBlockIn(data.message?.content, match);
565
- if (message !== undefined)
566
- return message;
567
- for (const inserted of data.inserted ?? []) {
568
- const found = imageBlockIn(inserted.content, match);
569
- if (found !== undefined)
570
- return found;
571
- }
572
- if (event.type === 'assistant/message' || event.type === 'assistant/attempt') {
573
- for (const chunk of assistantStreamChunks(event.data.stream, 'block-end')) {
574
- const found = imageBlockIn([chunk.block], match);
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
  }
@@ -1,11 +1,11 @@
1
- /** Live Session queue, jobs, and projection state with reconnect baselines. */
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, projection, and jobs services. */
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
@@ -1,10 +1,10 @@
1
- /** Live Session queue, jobs, and projection state with reconnect baselines. */
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, projection, and jobs services. */
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
@@ -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
  });
@@ -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
- * Open one path prepared by a Session-aware caller on the Host desktop.
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 completed-turn prefix into a new Session.
128
- * @param request - source Session and optional event anchor.
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 on a live Agent.
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.