@deepseek-ai/dsh-api-session-controller 0.2.1-alpha.1 → 0.2.1-alpha.3

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.
@@ -21,12 +21,38 @@ export declare class ApiSessionCwdConflict extends Error {
21
21
  readonly existingCwd: string | undefined;
22
22
  constructor(sessionId: SessionId, requestedCwd: string, existingCwd: string | undefined);
23
23
  }
24
- /** Explicit-id creation attempted to adopt a Session under another preset. */
24
+ /** Reuse of a Session named an Agent preset while the deployment composes no preset roster. */
25
25
  export declare class ApiSessionPresetConflict extends Error {
26
26
  readonly sessionId: SessionId;
27
27
  readonly requestedPreset: string;
28
- readonly existingPreset: string | undefined;
29
- constructor(sessionId: SessionId, requestedPreset: string, existingPreset: string | undefined);
28
+ constructor(sessionId: SessionId, requestedPreset: string);
29
+ }
30
+ /** Placement and Agent preset for one Session created for a new conversation. */
31
+ export interface ApiSessionCreateRequest {
32
+ /** Blank Session to reuse, or the identity of a new Session when none is stored under it. */
33
+ readonly sessionId?: SessionId;
34
+ /** Directory the Session owns. */
35
+ readonly cwd: string;
36
+ /** Preset the Session runs; the configured default when omitted. */
37
+ readonly agentPreset?: string;
38
+ /**
39
+ * Placement rule the caller owns, such as Workspace membership and archive state.
40
+ * @param sessionId - existing Session named by the request.
41
+ * @returns whether that Session may be reused.
42
+ */
43
+ readonly reusable: (sessionId: SessionId) => boolean;
44
+ }
45
+ /** The Session one creation request created or reused. */
46
+ export interface ApiSessionCreated {
47
+ /** Live Agent of the Session. */
48
+ readonly agent: Agent;
49
+ /** Preset the Session ran when the request finished, before a later request for the same identity; absent without a roster. */
50
+ readonly agentPreset: string | undefined;
51
+ }
52
+ /** Resolved preset and pre-publication Agent setup for one create or resume. */
53
+ interface ApiSessionComposition {
54
+ readonly agentPreset?: string;
55
+ readonly setup: AgentSetup;
30
56
  }
31
57
  /** Failures produced while resolving one ordinary Session identity to its live Agent. */
32
58
  export type ApiSessionAgentError = RemoteError<'session/not-found' | 'session/agent-busy' | 'session/writer-held' | 'gateway/internal'>;
@@ -66,7 +92,7 @@ export declare function inspectApiSession(ctx: Context, sessionId: SessionId, si
66
92
  export declare class ApiSessionAgentController {
67
93
  private readonly ctx;
68
94
  private readonly resumes;
69
- private readonly creations;
95
+ private readonly claims;
70
96
  private readonly selections;
71
97
  private readonly imageAdmissionChains;
72
98
  /** @param ctx - Host context carrying Agent, model, persistence, and Typert services. */
@@ -85,14 +111,23 @@ export declare class ApiSessionAgentController {
85
111
  resolveObservedAgent(observation: SessionObservation): Promise<ApiSessionAgentResult>;
86
112
  private resolve;
87
113
  /**
88
- * Resolve one requested identity, creating or resuming it once.
89
- * @param sessionId - requested Session identity.
90
- * @param cwd - directory the Session must own.
91
- * @param checkPersistedIdentity - whether to inspect a cold identity before creation.
92
- * @param presetId - optional Agent preset the Session must own.
93
- * @returns the matching live ordinary Agent.
114
+ * Create a Session for a new conversation, reusing the named blank Session
115
+ * while it is still reusable. A reused Session runs the requested preset, or
116
+ * the configured default when the request names none; the preset it recorded
117
+ * is not resolved, and a change is logged as `agent-preset/selected` before
118
+ * the Agent is published. A named Session that has started a turn, fails the
119
+ * caller's placement rule, or is written by another owner stays unchanged,
120
+ * and a new Session is created instead. Requests naming one identity run in
121
+ * arrival order, including their preset resolution, and check the named
122
+ * Session's directory and ownership before resolving the preset.
123
+ * @param request - optional Session to reuse, directory, preset, and placement rule.
124
+ * @returns the reused or created Session and the preset it ran when this request finished.
125
+ * @throws ApiSessionCwdConflict when the named Session belongs to another directory.
126
+ * @throws ApiSessionSubagentOwnership when subagent routing owns the named Session.
127
+ * @throws RemoteError `agent-preset/not-found` or `agent-preset/invalid` for the requested or default preset.
128
+ * @throws ApiSessionPresetConflict when no roster is composed and the request names a preset for a reused Session.
94
129
  */
95
- ensureSession(sessionId: SessionId, cwd: string, checkPersistedIdentity: boolean, presetId?: string): Promise<Agent>;
130
+ createSession(request: ApiSessionCreateRequest): Promise<ApiSessionCreated>;
96
131
  /**
97
132
  * Install or return the Session-local model selection used by prompt assembly.
98
133
  * @param agent - live Agent that owns the selection.
@@ -132,14 +167,24 @@ export declare class ApiSessionAgentController {
132
167
  * @param presetId - requested preset or the configured default when omitted.
133
168
  * @returns the resolved preset identity and Agent setup callback.
134
169
  */
135
- composeAgent(presetId: string | undefined): Promise<{
136
- readonly agentPreset?: string;
137
- readonly setup: AgentSetup;
138
- }>;
170
+ composeAgent(presetId: string | undefined): Promise<ApiSessionComposition>;
139
171
  private liveAgent;
140
172
  private resume;
141
173
  private resumeObserved;
142
- private createOrAdopt;
174
+ private inOrder;
175
+ /** @returns the reused Session, a new Session under `sessionId` when none is stored, or a new Session under a minted identity. */
176
+ private claim;
177
+ /** @returns the live blank Session under the requested preset, or undefined when it cannot be reused. */
178
+ private reuseLive;
179
+ /** Setup that re-checks blankness under the acquired writer, then records a preset change before publication. */
180
+ private reuseSetup;
181
+ /** Whether no turn has started, by the predicate the preset registry's `select` enforces. */
182
+ private isBlank;
183
+ /** Without a roster, reuse cannot apply a named preset. */
184
+ private assertPresetApplicable;
185
+ private created;
186
+ private createMinted;
187
+ private createAgent;
143
188
  private agentOptions;
144
189
  private installSelection;
145
190
  /**
@@ -148,7 +193,6 @@ export declare class ApiSessionAgentController {
148
193
  * @returns the current preset, or undefined when the capability is absent.
149
194
  */
150
195
  presetForObservation(observation: SessionObservation): string | undefined;
151
- private assertPresetUnchanged;
152
196
  }
153
197
  export {};
154
198
  //# sourceMappingURL=agent.d.ts.map
@@ -51,11 +51,13 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
51
51
  var e = new Error(message);
52
52
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
53
53
  });
54
+ import { randomUUID } from 'node:crypto';
54
55
  import { mkdir } from 'node:fs/promises';
55
56
  import { installModelSelection } from '@deepseek-ai/dsh-agent';
57
+ import { brandString } from '@deepseek-ai/dsh-brand';
56
58
  import { ReasoningEffortId } from '@deepseek-ai/dsh-llm';
57
59
  import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
58
- import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
60
+ import { RemoteError, remoteErrorOf } from '@deepseek-ai/dsh-typert-protocol';
59
61
  /** Cold Session identity absent from persistence. */
60
62
  export class ApiSessionNotFound extends Error {
61
63
  }
@@ -82,20 +84,19 @@ export class ApiSessionCwdConflict extends Error {
82
84
  this.existingCwd = existingCwd;
83
85
  }
84
86
  }
85
- /** Explicit-id creation attempted to adopt a Session under another preset. */
87
+ /** Reuse of a Session named an Agent preset while the deployment composes no preset roster. */
86
88
  export class ApiSessionPresetConflict extends Error {
87
89
  sessionId;
88
90
  requestedPreset;
89
- existingPreset;
90
- constructor(sessionId, requestedPreset, existingPreset) {
91
- super(existingPreset === undefined
92
- ? `session "${sessionId}" records no agent preset and cannot be adopted under "${requestedPreset}"`
93
- : `session "${sessionId}" runs agent preset "${existingPreset}", not "${requestedPreset}"`);
91
+ constructor(sessionId, requestedPreset) {
92
+ super(`the deployment composes no agent preset roster, so session "${sessionId}" cannot run preset "${requestedPreset}"`);
94
93
  this.sessionId = sessionId;
95
94
  this.requestedPreset = requestedPreset;
96
- this.existingPreset = existingPreset;
97
95
  }
98
96
  }
97
+ /** A blank Session chosen for reuse started a turn before this Host acquired its writer. */
98
+ class ApiSessionStartedSinceObserved extends Error {
99
+ }
99
100
  /**
100
101
  * Test whether generic Session routing must leave an identity to subagent routing.
101
102
  * @param ctx - Host context carrying the Agent ownership registry.
@@ -164,7 +165,7 @@ export async function inspectApiSession(ctx, sessionId, signal) {
164
165
  export class ApiSessionAgentController {
165
166
  ctx;
166
167
  resumes = new Map();
167
- creations = new Map();
168
+ claims = new Map();
168
169
  selections = new WeakMap();
169
170
  imageAdmissionChains = new WeakMap();
170
171
  /** @param ctx - Host context carrying Agent, model, persistence, and Typert services. */
@@ -248,45 +249,27 @@ export class ApiSessionAgentController {
248
249
  }
249
250
  }
250
251
  /**
251
- * Resolve one requested identity, creating or resuming it once.
252
- * @param sessionId - requested Session identity.
253
- * @param cwd - directory the Session must own.
254
- * @param checkPersistedIdentity - whether to inspect a cold identity before creation.
255
- * @param presetId - optional Agent preset the Session must own.
256
- * @returns the matching live ordinary Agent.
252
+ * Create a Session for a new conversation, reusing the named blank Session
253
+ * while it is still reusable. A reused Session runs the requested preset, or
254
+ * the configured default when the request names none; the preset it recorded
255
+ * is not resolved, and a change is logged as `agent-preset/selected` before
256
+ * the Agent is published. A named Session that has started a turn, fails the
257
+ * caller's placement rule, or is written by another owner stays unchanged,
258
+ * and a new Session is created instead. Requests naming one identity run in
259
+ * arrival order, including their preset resolution, and check the named
260
+ * Session's directory and ownership before resolving the preset.
261
+ * @param request - optional Session to reuse, directory, preset, and placement rule.
262
+ * @returns the reused or created Session and the preset it ran when this request finished.
263
+ * @throws ApiSessionCwdConflict when the named Session belongs to another directory.
264
+ * @throws ApiSessionSubagentOwnership when subagent routing owns the named Session.
265
+ * @throws RemoteError `agent-preset/not-found` or `agent-preset/invalid` for the requested or default preset.
266
+ * @throws ApiSessionPresetConflict when no roster is composed and the request names a preset for a reused Session.
257
267
  */
258
- async ensureSession(sessionId, cwd, checkPersistedIdentity, presetId) {
259
- let creation = this.creations.get(sessionId);
260
- if (creation === undefined) {
261
- creation = this.createOrAdopt(sessionId, cwd, checkPersistedIdentity, presetId)
262
- .catch((error) => {
263
- const live = this.ctx.agents.get(sessionId);
264
- if (live !== undefined) {
265
- if (hasApiSessionSubagentOwner(this.ctx, live.session, live)) {
266
- throw new ApiSessionSubagentOwnership(sessionId);
267
- }
268
- return live;
269
- }
270
- const attached = this.ctx.sessions.get(sessionId);
271
- if (attached !== undefined && hasApiSessionSubagentOwner(this.ctx, attached, undefined)) {
272
- throw new ApiSessionSubagentOwnership(sessionId);
273
- }
274
- throw error;
275
- })
276
- .finally(() => { this.creations.delete(sessionId); });
277
- this.creations.set(sessionId, creation);
278
- }
279
- const agent = await creation;
280
- if (hasApiSessionSubagentOwner(this.ctx, agent.session, agent)) {
281
- throw new ApiSessionSubagentOwnership(sessionId);
282
- }
283
- if (presetId !== undefined) {
284
- this.assertPresetUnchanged(sessionId, presetId, this.presetForSession(agent.session));
285
- }
286
- if (agent.session.header.cwd !== cwd) {
287
- throw new ApiSessionCwdConflict(sessionId, cwd, agent.session.header.cwd);
288
- }
289
- return agent;
268
+ async createSession(request) {
269
+ const named = request.sessionId;
270
+ if (named === undefined)
271
+ return this.created(await this.createMinted(request.cwd, await this.composeAgent(request.agentPreset)));
272
+ return this.inOrder(named, async () => this.created(await this.claim(named, request)));
290
273
  }
291
274
  /**
292
275
  * Install or return the Session-local model selection used by prompt assembly.
@@ -451,55 +434,141 @@ export class ApiSessionAgentController {
451
434
  setup: composition.setup,
452
435
  })).agent;
453
436
  }
454
- async createOrAdopt(sessionId, cwd, checkPersistedIdentity, presetId) {
437
+ async inOrder(sessionId, operation) {
438
+ const result = (this.claims.get(sessionId) ?? Promise.resolve()).then(operation);
439
+ const settled = result.then(() => undefined, () => undefined);
440
+ this.claims.set(sessionId, settled);
441
+ try {
442
+ return await result;
443
+ }
444
+ finally {
445
+ if (this.claims.get(sessionId) === settled)
446
+ this.claims.delete(sessionId);
447
+ }
448
+ }
449
+ /** @returns the reused Session, a new Session under `sessionId` when none is stored, or a new Session under a minted identity. */
450
+ async claim(sessionId, request) {
455
451
  const attached = this.ctx.sessions.get(sessionId);
456
452
  const live = this.ctx.agents.get(sessionId);
457
453
  if (attached !== undefined && hasApiSessionSubagentOwner(this.ctx, attached, live)) {
458
454
  throw new ApiSessionSubagentOwnership(sessionId);
459
455
  }
460
- if (live !== undefined)
461
- return live;
462
- if (checkPersistedIdentity) {
456
+ if (live !== undefined) {
457
+ if (live.session.header.cwd !== request.cwd) {
458
+ throw new ApiSessionCwdConflict(live.id, request.cwd, live.session.header.cwd);
459
+ }
460
+ const composition = await this.composeAgent(request.agentPreset);
461
+ return await this.reuseLive(live, request, composition) ?? this.createMinted(request.cwd, composition);
462
+ }
463
+ let observation;
464
+ try {
465
+ observation = await this.ctx.sessionQuery.observeSession(sessionId);
466
+ }
467
+ catch (error) {
468
+ if (!(error instanceof SessionQueryError) || error.code !== 'SESSION_QUERY_SESSION_NOT_FOUND')
469
+ throw error;
470
+ return this.createAgent(sessionId, request.cwd, await this.composeAgent(request.agentPreset));
471
+ }
472
+ let reusable;
473
+ {
474
+ const env_3 = { stack: [], error: void 0, hasError: false };
463
475
  try {
464
- const env_3 = { stack: [], error: void 0, hasError: false };
465
- try {
466
- const observation = __addDisposableResource(env_3, await this.ctx.sessionQuery.observeSession(sessionId), false);
467
- if (hasApiSessionSubagentOwner(this.ctx, { header: observation.header }, undefined)) {
468
- throw new ApiSessionSubagentOwnership(sessionId);
469
- }
470
- if (observation.header.cwd !== cwd) {
471
- throw new ApiSessionCwdConflict(sessionId, cwd, observation.header.cwd);
472
- }
473
- const storedPreset = this.presetForObservation(observation);
474
- this.assertPresetUnchanged(sessionId, presetId, storedPreset);
475
- const composition = await this.composeAgent(storedPreset);
476
- return (await this.ctx.agents.resume({
477
- resumeSessionId: sessionId,
478
- agentOptions: this.agentOptions(),
479
- setup: composition.setup,
480
- })).agent;
481
- }
482
- catch (e_3) {
483
- env_3.error = e_3;
484
- env_3.hasError = true;
476
+ const observed = __addDisposableResource(env_3, observation, false);
477
+ if (hasApiSessionSubagentOwner(this.ctx, { header: observed.header }, undefined)) {
478
+ throw new ApiSessionSubagentOwnership(sessionId);
485
479
  }
486
- finally {
487
- __disposeResources(env_3);
480
+ if (observed.header.cwd !== request.cwd) {
481
+ throw new ApiSessionCwdConflict(sessionId, request.cwd, observed.header.cwd);
488
482
  }
483
+ reusable = request.reusable(sessionId) && !observed.events.some(event => event.type === 'turn/start');
484
+ }
485
+ catch (e_3) {
486
+ env_3.error = e_3;
487
+ env_3.hasError = true;
489
488
  }
490
- catch (error) {
491
- if (!(error instanceof SessionQueryError)
492
- || error.code !== 'SESSION_QUERY_SESSION_NOT_FOUND')
493
- throw error;
489
+ finally {
490
+ __disposeResources(env_3);
494
491
  }
495
492
  }
493
+ const composition = await this.composeAgent(request.agentPreset);
494
+ if (!reusable)
495
+ return this.createMinted(request.cwd, composition);
496
+ this.assertPresetApplicable(sessionId, request.agentPreset);
497
+ try {
498
+ return (await this.ctx.agents.resume({
499
+ resumeSessionId: sessionId,
500
+ agentOptions: this.agentOptions(),
501
+ setup: this.reuseSetup(composition),
502
+ })).agent;
503
+ }
504
+ catch (error) {
505
+ if (error instanceof ApiSessionStartedSinceObserved)
506
+ return this.createMinted(request.cwd, composition);
507
+ if (!(error instanceof Error) || error.name !== 'SessionAlreadyOwnedError')
508
+ throw error;
509
+ // Another resume in this process may hold the writer for the Agent it published.
510
+ return this.ctx.agents.get(sessionId) === undefined
511
+ ? this.createMinted(request.cwd, composition)
512
+ : this.claim(sessionId, request);
513
+ }
514
+ }
515
+ /** @returns the live blank Session under the requested preset, or undefined when it cannot be reused. */
516
+ async reuseLive(live, request, composition) {
517
+ if (!request.reusable(live.id) || !this.isBlank(live.session))
518
+ return undefined;
519
+ this.assertPresetApplicable(live.id, request.agentPreset);
520
+ const current = this.presetForSession(live.session);
521
+ const presets = this.ctx.get('agentPresets');
522
+ const target = composition.agentPreset;
523
+ if (presets === undefined || target === undefined || target === current)
524
+ return live;
525
+ try {
526
+ await presets.select(live, target);
527
+ }
528
+ catch (error) {
529
+ if (remoteErrorOf(error)?.code !== 'agent-preset/locked')
530
+ throw error;
531
+ return undefined;
532
+ }
533
+ return live;
534
+ }
535
+ /** Setup that re-checks blankness under the acquired writer, then records a preset change before publication. */
536
+ reuseSetup(composition) {
537
+ return async (agentCtx, agent) => {
538
+ if (!this.isBlank(agent.session))
539
+ throw new ApiSessionStartedSinceObserved();
540
+ const commit = await composition.setup(agentCtx, agent);
541
+ const target = composition.agentPreset;
542
+ if (target !== undefined && this.presetForSession(agent.session) !== target) {
543
+ agent.session.append('agent-preset/selected', { agentPreset: target });
544
+ }
545
+ return commit;
546
+ };
547
+ }
548
+ /** Whether no turn has started, by the predicate the preset registry's `select` enforces. */
549
+ isBlank(session) {
550
+ const boundary = this.ctx.sessionProjections.stateOf(session, 'turnBoundary');
551
+ return boundary === undefined || (boundary.openTurnStartSeq === null && boundary.lastTurn === 0);
552
+ }
553
+ /** Without a roster, reuse cannot apply a named preset. */
554
+ assertPresetApplicable(sessionId, requested) {
555
+ if (requested === undefined || this.ctx.get('agentPresets') !== undefined)
556
+ return;
557
+ throw new ApiSessionPresetConflict(sessionId, requested);
558
+ }
559
+ created(agent) {
560
+ return { agent, agentPreset: this.presetForSession(agent.session) };
561
+ }
562
+ createMinted(cwd, composition) {
563
+ return this.createAgent(brandString(`session-${randomUUID()}`), cwd, composition);
564
+ }
565
+ async createAgent(sessionId, cwd, composition) {
496
566
  try {
497
567
  await mkdir(cwd, { recursive: true });
498
568
  }
499
569
  catch (error) {
500
570
  throw new Error(`failed to ensure project directory "${cwd}": ${String(error)}`, { cause: error });
501
571
  }
502
- const composition = await this.composeAgent(presetId);
503
572
  return (await this.ctx.agents.create({
504
573
  sessionId,
505
574
  agentOptions: this.agentOptions(),
@@ -528,11 +597,6 @@ export class ApiSessionAgentController {
528
597
  }
529
598
  return observation.projections.values.agentPreset ?? undefined;
530
599
  }
531
- assertPresetUnchanged(sessionId, requested, existing) {
532
- if (requested === undefined || requested === existing)
533
- return;
534
- throw new ApiSessionPresetConflict(sessionId, requested, existing);
535
- }
536
600
  }
537
601
  function agentModelSelection(selection) {
538
602
  return {
@@ -73,23 +73,26 @@ export interface ISessions {
73
73
  */
74
74
  readonly searchResultLimit: number;
75
75
  /**
76
- * Create or adopt a Session on the Host.
77
- * @param opts - target workspace, directory, and optional preallocated identity.
78
- * @returns the catalogued identity; retain it before borrowing its binding.
76
+ * Create a Session for a new conversation on the Host. The Host reuses the
77
+ * named blank Session while it is reusable and otherwise creates another; the
78
+ * Session runs the named preset, or the Host default when none is named.
79
+ * @param opts - target workspace or directory, an optional blank Session to reuse, and an optional Agent preset.
80
+ * @returns the catalogued identity the Host created or reused; retain it before borrowing its binding.
79
81
  */
80
82
  create(opts?: {
81
83
  workspaceId?: WorkspaceId;
82
84
  cwd?: string;
83
85
  sessionId?: SessionId;
86
+ agentPreset?: string;
84
87
  }): Promise<SessionId>;
85
88
  /**
86
89
  * Resolve an already discovered direct-parent address without opening it.
87
90
  * @param id - possible addressed child id.
88
- * @returns a retained or loaded-catalog address, without retaining a new selection or scope.
91
+ * @returns a retained, catalog-derived, or unknown-mode listed address, without retaining a new selection or scope.
89
92
  */
90
93
  subagentAddress(id: SessionId): SubagentAddress | undefined;
91
94
  /**
92
- * Load all Session projections once per connection; retry an unsuccessful initial read.
95
+ * Read projections without starting migration; retry failed or migration-deferred reads.
93
96
  * @param sessionId - Session to inspect without opening its conversation.
94
97
  * @returns completion of the current or newly started refresh.
95
98
  */
@@ -126,6 +129,8 @@ export interface ISessions {
126
129
  atSeq?: number;
127
130
  increaseTitle?: boolean;
128
131
  onCreated?: (childId: SessionId) => void;
132
+ /** False refuses source migration; omission preserves the Host default. */
133
+ allowMigration?: boolean;
129
134
  }): Promise<SessionId>;
130
135
  /**
131
136
  * Borrow an already-retained Agent-scoped Context without extending its lifetime.
@@ -21,6 +21,8 @@ export interface SessionListEntry {
21
21
  parentSessionId?: SessionId;
22
22
  /** Coarse durable origin for navigation filtering; not a continuation capability. */
23
23
  origin?: 'subagent';
24
+ /** Format classification supplied by the Host list. */
25
+ formatStatus?: SessionSummary['formatStatus'];
24
26
  cwd?: string;
25
27
  /** Current host-computed projection values for list consumers. */
26
28
  projectionValues?: Readonly<Partial<SessionProjectionMap>>;
@@ -35,7 +35,7 @@ export interface SessionListSnapshot {
35
35
  /** Shared projection values and the lifecycle of their explicit baseline read. */
36
36
  export interface SessionProjectionSnapshot {
37
37
  readonly values: Readonly<Partial<SessionProjectionMap>>;
38
- readonly state: 'idle' | 'loading' | 'ready' | 'error';
38
+ readonly state: 'idle' | 'loading' | 'ready' | 'migration-required' | 'error';
39
39
  readonly error: RemoteFailure | null;
40
40
  }
41
41
  /** Instance cluster + frame entry + the session list. */
@@ -86,8 +86,9 @@ export declare class SessionManager {
86
86
  resolveTarget(target: SessionTarget): SessionId;
87
87
  /**
88
88
  * Resolve an address for breadcrumb navigation without retaining transport authority.
89
- * @param sessionId - possible child id in an already-loaded catalog.
90
- * @returns A retained or catalog-derived direct-parent address.
89
+ * @param sessionId - possible child id in received Session metadata.
90
+ * @returns A retained address, then a catalog address, then an unknown-mode address
91
+ * from a listed subagent header; undefined without that evidence.
91
92
  */
92
93
  subagentAddress(sessionId: SessionId): SubagentAddress | undefined;
93
94
  /**
@@ -130,7 +131,7 @@ export declare class SessionManager {
130
131
  /** Resident per-session projection store (create-on-demand; outlives instantiation). */
131
132
  private projectionStore;
132
133
  /**
133
- * Load a complete projection baseline once per connection; retry unsuccessful reads.
134
+ * Read projections without starting migration; retry failed or migration-deferred reads.
134
135
  * @param sessionId - Session to inspect without opening its conversation.
135
136
  * @returns completion of the current or newly started read.
136
137
  */
@@ -154,13 +155,14 @@ export declare class SessionManager {
154
155
  * Contract session.create; on success merge into summaries immediately (no
155
156
  * wait for the next refresh). A created session is blank by definition
156
157
  * (entity birth precedes the first message).
157
- * @param opts - target workspace or working directory, plus an optional caller-owned id.
158
- * @returns the create result.
158
+ * @param opts - target workspace or working directory, an optional blank Session to reuse, and an optional Agent preset.
159
+ * @returns the create result, naming the Session the Host created or reused.
159
160
  */
160
161
  create(opts?: {
161
162
  workspaceId?: WorkspaceId;
162
163
  cwd?: string;
163
164
  sessionId?: SessionId;
165
+ agentPreset?: string;
164
166
  }): Promise<RemoteResult<{
165
167
  sessionId: SessionId;
166
168
  }>>;
@@ -171,12 +173,13 @@ export declare class SessionManager {
171
173
  * preserve it or lower it after an exact cut before the first `turn/start`;
172
174
  * lineage rides parentSessionId. A child published before Workspace
173
175
  * attachment fails is also reconciled into the list.
174
- * @param opts - source session and the optional exact inclusive boundary seq.
176
+ * @param opts - source session, optional exact inclusive boundary seq, and permission to start migration.
175
177
  * @returns the fork result (the child session id).
176
178
  */
177
179
  fork(opts: {
178
180
  sessionId: SessionId;
179
181
  atSeq?: SessionSeq;
182
+ allowMigration?: boolean;
180
183
  }): Promise<RemoteResult<{
181
184
  sessionId: SessionId;
182
185
  }>>;