@deepseek-ai/dsh-api-session-controller 0.1.6-alpha.2 → 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 (36) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +31 -8
  3. package/README.zh.md +31 -8
  4. package/lib/client.js +603 -515
  5. package/lib/index.js +299 -137
  6. package/lib/typert.host.js +373 -183
  7. package/lib/typert.remote-client.d.ts +10 -5
  8. package/lib/typert.remote-client.js +216 -124
  9. package/lib/types/archived-session-gate.d.ts +30 -0
  10. package/lib/types/archived-session-gate.js +52 -0
  11. package/lib/types/client/contract/session.d.ts +2 -0
  12. package/lib/types/client/contract/sessions.d.ts +11 -16
  13. package/lib/types/client/index.d.ts +1 -1
  14. package/lib/types/client/sessions/lineage.d.ts +4 -1
  15. package/lib/types/client/sessions/lineage.js +2 -1
  16. package/lib/types/client/sessions/manager.d.ts +66 -59
  17. package/lib/types/client/sessions/manager.js +249 -277
  18. package/lib/types/client/sessions/projection-store.d.ts +32 -13
  19. package/lib/types/client/sessions/projection-store.js +45 -13
  20. package/lib/types/client/sessions/remotes.d.ts +1 -2
  21. package/lib/types/client/sessions/service.d.ts +23 -35
  22. package/lib/types/client/sessions/service.js +35 -47
  23. package/lib/types/client/sessions/session.d.ts +18 -16
  24. package/lib/types/client/sessions/session.js +112 -50
  25. package/lib/types/commands.d.ts +4 -2
  26. package/lib/types/commands.js +72 -36
  27. package/lib/types/control.d.ts +2 -4
  28. package/lib/types/control.js +2 -44
  29. package/lib/types/history.js +1 -1
  30. package/lib/types/index.d.ts +32 -5
  31. package/lib/types/index.js +117 -13
  32. package/lib/types/list.js +24 -15
  33. package/lib/types/skill-catalog.js +60 -50
  34. package/lib/types/types.d.ts +37 -23
  35. package/lib/types/types.js +0 -1
  36. package/package.json +71 -76
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The controller's admission gate for archived Sessions: an archived
3
+ * Session, or a subagent descendant of one, must not run a model step until
4
+ * it is restored. The work a Session still runs is reported and stopped by
5
+ * its owners — the Agent registry (`turn`), the job registry seam (`job`),
6
+ * the Subagent runtime (`subagent`), and the Schedule plugin (`schedule`) —
7
+ * through the Workspace registry's archive-admission events.
8
+ */
9
+ /**
10
+ * The gate as a plugin for `ctx.plugin(...)`: it loads once the Agent
11
+ * registry, Session store, and Workspace registry are available and unwinds
12
+ * with the plugin's fiber. A late waking delivery to an archived Session — a
13
+ * subagent settlement, a queued follow-up — proposes a step the gate rejects,
14
+ * which the loop ends as `blocked` without a request; unarchiving lifts the
15
+ * gate for the whole lineage.
16
+ */
17
+ export const ArchivedSessionGate = {
18
+ name: 'archived-session-gate',
19
+ inject: ['agents', 'sessions', 'workspaceRegistry'],
20
+ apply(ctx) {
21
+ ctx.on('agent/pre-step', (payload, next) => underArchivedSession(ctx, payload.agent)
22
+ ? Promise.resolve({ kind: 'reject' })
23
+ : next());
24
+ },
25
+ };
26
+ /**
27
+ * Whether the Agent's Session, or a Session above it in its subagent lineage,
28
+ * is archived. Lineage follows the durable header fields through
29
+ * subagent-origin Sessions only: a fork of an archived Session is an
30
+ * independent conversation.
31
+ * @param ctx - Host context.
32
+ * @param agent - the Agent proposing a step.
33
+ * @returns whether an archived Session owns the step.
34
+ */
35
+ export function underArchivedSession(ctx, agent) {
36
+ const archived = ctx.workspaceRegistry.archivedSessionIds;
37
+ let header = agent.session.header;
38
+ const visited = new Set();
39
+ while (!visited.has(header.id)) {
40
+ if (archived.includes(header.id))
41
+ return true;
42
+ visited.add(header.id);
43
+ if (header.origin !== 'subagent' || header.parentSession === undefined)
44
+ return false;
45
+ const parent = ctx.sessions.get(header.parentSession);
46
+ if (parent === undefined)
47
+ return archived.includes(header.parentSession);
48
+ header = parent.header;
49
+ }
50
+ return false;
51
+ }
52
+ //# sourceMappingURL=archived-session-gate.js.map
@@ -127,6 +127,8 @@ export interface ISession {
127
127
  * turn-jump loader. Repeated calls while a jump is paging lower its shared
128
128
  * target and return the in-flight completion; `snapshot.loadingOlder` is
129
129
  * the busy signal for the whole jump.
130
+ * Older pages publish together at completion, including successful pages
131
+ * before a later failure; live events remain independently visible.
130
132
  * @param seq - durable event seq the window must reach (a turn's `turn/start` seq).
131
133
  * @returns completion once covered, exhausted, superseded, or failed soft.
132
134
  */
@@ -85,21 +85,15 @@ export interface ISessions {
85
85
  /**
86
86
  * Resolve an already discovered direct-parent address without opening it.
87
87
  * @param id - possible addressed child id.
88
- * @returns the retained address, when present.
88
+ * @returns a retained or loaded-catalog address, without retaining a new selection or scope.
89
89
  */
90
90
  subagentAddress(id: SessionId): SubagentAddress | undefined;
91
91
  /**
92
- * Mark whether a catalog menu is consuming live membership updates.
93
- * @param parentSessionId - catalog owner.
94
- * @param open - current menu state.
95
- */
96
- setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void;
97
- /**
98
- * Refresh one direct-child catalog.
99
- * @param parentSessionId - catalog owner.
92
+ * Load all Session projections once per connection; retry an unsuccessful initial read.
93
+ * @param sessionId - Session to inspect without opening its conversation.
100
94
  * @returns completion of the current or newly started refresh.
101
95
  */
102
- refreshSubagents(parentSessionId: SessionId): Promise<void>;
96
+ refreshProjections(sessionId: SessionId): Promise<void>;
103
97
  /**
104
98
  * Refresh the Host-authoritative Session list.
105
99
  * @returns completion of the current or newly started Session-list refresh.
@@ -117,12 +111,13 @@ export interface ISessions {
117
111
  hasMore: boolean;
118
112
  }>>;
119
113
  /**
120
- * Fork a session from a completed-turn prefix of the source; on resolution
121
- * the child is in the catalog and may be explicitly retained.
122
- * @param opts - source session id, the optional event seq anchoring the
123
- * cut (the boundary is the first turn/end at or after it; an in-log
124
- * anchor in an open turn is unavailable rather than clipped backward),
125
- * and whether to increment an inherited durable title before resolving.
114
+ * Fork a session from an exact inclusive prefix of the source; on
115
+ * resolution the child is catalogued and can be explicitly retained.
116
+ * @param opts - source session id, the optional exact inclusive boundary
117
+ * seq (a real event seq the caller already knows; a cut inside an open
118
+ * turn is balanced Host-side with synthetic closers, and omission selects
119
+ * the latest completed-turn prefix), and whether to increment an
120
+ * inherited durable title before resolving.
126
121
  * @returns the child session id.
127
122
  * @throws when the fork fails, or when a requested child-title rename fails after creation.
128
123
  */
@@ -6,7 +6,7 @@ export { createScope, scopeOf } from './scope.ts';
6
6
  export type { AgentContext, AgentScopeHandle } from './scope.ts';
7
7
  export { SessionCreateError, SessionForkError } from './sessions/service.ts';
8
8
  export type { SessionBinding, SessionListState, SessionSummary } from './sessions/service.ts';
9
- export type { SessionListPhase, SessionListSnapshot, SessionSearchResultItem, SubagentCatalogSnapshot, } from './sessions/manager.ts';
9
+ export type { SessionListPhase, SessionListSnapshot, SessionSearchResultItem, SessionProjectionSnapshot, } from './sessions/manager.ts';
10
10
  export type { Session } from './sessions/session.ts';
11
11
  export type { ProjectionsBaseline, ProjectionValueStore, SessionProjectionMap, UseProjection, } from './sessions/projection-store.ts';
12
12
  export type { BeginSubmissionInput, ISession, PendingSubmissionRetirement, ProjectionsFace, SessionFace, SubmissionHandle, } from './contract/session.ts';
@@ -13,7 +13,10 @@ export interface SessionListEntry {
13
13
  title?: string;
14
14
  updatedAt: number;
15
15
  running: boolean;
16
- /** Empty-log bit mirrored from the summary; lists hide blank sessions (filtering stays with the consumer). */
16
+ /**
17
+ * New Session presentation and reuse eligibility, reconciled with
18
+ * `sessionListMetadata`; lists hide blank sessions (filtering stays with the consumer).
19
+ */
17
20
  blank: boolean;
18
21
  parentSessionId?: SessionId;
19
22
  /** Coarse durable origin for navigation filtering; not a continuation capability. */
@@ -32,8 +32,9 @@ export function flattenLineage(summaries) {
32
32
  return;
33
33
  }
34
34
  visited.add(s.sessionId);
35
+ const { agentAvailable: _agentAvailable, ...row } = s;
35
36
  out.push({
36
- ...s,
37
+ ...row,
37
38
  depth,
38
39
  });
39
40
  const kids = children.get(s.sessionId);
@@ -1,9 +1,9 @@
1
1
  /** Host catalog, durable projection caches, and explicitly retained Client instances. */
2
- import type { SubagentAddress, SubagentCatalog } from '@deepseek-ai/dsh-subagent/client';
2
+ import type { SubagentAddress } from '@deepseek-ai/dsh-subagent/client';
3
3
  import { SessionSeq, type SessionId } from '@deepseek-ai/dsh-session/types';
4
4
  import type { SessionProjectionMap } from '@deepseek-ai/dsh-session-projection/types';
5
5
  import type { WorkspaceId } from '@deepseek-ai/dsh-workspace/types';
6
- import type { SessionControlFrame, SessionSummary, SessionJob as JobView } from '../../types.ts';
6
+ import type { SessionControlFrame, SessionRenameValue, SessionSummary } from '../../types.ts';
7
7
  import type { RemoteFailure, RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
8
8
  import type { SessionListEntry } from './lineage.ts';
9
9
  import { Session } from './session.ts';
@@ -30,23 +30,29 @@ export interface SessionListSnapshot {
30
30
  /** Arrival lifecycle (see {@link SessionListPhase}); `state` stays the pull-activity axis. */
31
31
  phase: SessionListPhase;
32
32
  error: RemoteFailure | null;
33
- subagentsByParent: Readonly<Record<SessionId, SubagentCatalogSnapshot>>;
34
- /** Background jobs per session; an absent key is an empty set. */
35
- jobsBySession: Readonly<Record<SessionId, readonly JobView[]>>;
33
+ projectionsBySession: Readonly<Record<SessionId, SessionProjectionSnapshot>>;
34
+ }
35
+ /** Shared projection values and the lifecycle of their explicit baseline read. */
36
+ export interface SessionProjectionSnapshot {
37
+ readonly values: Readonly<Partial<SessionProjectionMap>>;
38
+ readonly state: 'idle' | 'loading' | 'ready' | 'error';
39
+ readonly error: RemoteFailure | null;
36
40
  }
37
- /** One parent-addressed durable catalog projected through the sessions snapshot. */
38
- export type SubagentCatalogSnapshot = Omit<SubagentCatalog, 'parentAvailable'> & {
39
- /** Absent until the first successful catalog read. */
40
- readonly parentAvailable?: boolean;
41
- state: 'loading' | 'ready' | 'error';
42
- error: RemoteFailure | null;
43
- };
44
41
  /** Instance cluster + frame entry + the session list. */
45
42
  export declare class SessionManager {
46
43
  private readonly remote;
47
44
  private readonly sessions;
48
45
  /** In-flight Session disposals remain here after instances leave `sessions`, so manager disposal can await quiescence. */
49
46
  private readonly sessionDisposals;
47
+ /**
48
+ * Accepted/running presentation must survive a later empty-history list
49
+ * response. Host-asserted running is recorded even before a row, instance, or
50
+ * address holds the identity — the listing that would hold it may not have
51
+ * landed yet — while the client-local acceptance callback requires a current
52
+ * holder, because it can arrive from a replaced or already-dropped Session.
53
+ */
54
+ private readonly engagedSessions;
55
+ private disposed;
50
56
  /** Per-session projection value stores, retained independently of instance arrival (the
51
57
  * title-snapshot precedent, generalized): push frames land here whether or not the Session
52
58
  * is instantiated (list rows read the 'title' key), and an instantiated Session adopts the
@@ -61,18 +67,8 @@ export declare class SessionManager {
61
67
  /** Active list request's mutation log; its identity also fences completion after reconnect. */
62
68
  private listMutations;
63
69
  private readonly addresses;
64
- private readonly catalogs;
65
- private readonly catalogInflight;
66
- /** Catalog owners whose membership changed while a pull was in flight: one trailing refresh after it settles. */
67
- private readonly catalogStale;
68
- private readonly openCatalogs;
69
- private readonly catalogDebounce;
70
- /**
71
- * Background jobs per session, last-wins from Session Controller's control
72
- * stream. An empty set is stored as an absent key, so absence and `[]` are
73
- * one representation.
74
- */
75
- private readonly jobsBySession;
70
+ private readonly projectionLoads;
71
+ private readonly projectionInflight;
76
72
  private listSnapshotCache;
77
73
  /** Entry-identity cache (reference stability): list rebuilds reuse the previous entry
78
74
  * object when every field matches — wire refreshes mint all-new summary objects, so identity
@@ -88,18 +84,12 @@ export declare class SessionManager {
88
84
  * @returns the resolved identity with its explicit or catalog-derived history route installed.
89
85
  */
90
86
  resolveTarget(target: SessionTarget): SessionId;
91
- /**
92
- * Return the durable catalog address retained for one child.
93
- * @param sessionId - possible addressed child id.
94
- * @returns The direct-parent address, when navigation discovered one.
95
- */
96
- subagentAddress(sessionId: SessionId): SubagentAddress | undefined;
97
87
  /**
98
88
  * Resolve an address for breadcrumb navigation without retaining transport authority.
99
89
  * @param sessionId - possible child id in an already-loaded catalog.
100
90
  * @returns A retained or catalog-derived direct-parent address.
101
91
  */
102
- navigationAddress(sessionId: SessionId): SubagentAddress | undefined;
92
+ subagentAddress(sessionId: SessionId): SubagentAddress | undefined;
103
93
  /**
104
94
  * Withdraw an exact Client instance before running its teardown callbacks.
105
95
  * @param sessionId - identity to withdraw.
@@ -117,24 +107,36 @@ export declare class SessionManager {
117
107
  /**
118
108
  * Lazy build: return the existing instance or construct one (no auto-open —
119
109
  * the reference allocator opens history after binding the scope).
110
+ * New instances reconcile retained metadata before returning.
120
111
  * @param sessionId - the session to get.
121
112
  * @returns the resident instance.
122
113
  */
123
114
  get(sessionId: SessionId): Session;
124
115
  private createSession;
125
- /** Resident per-session projection store (create-on-demand; outlives instantiation). */
126
- private projectionStore;
116
+ private effectiveBlank;
127
117
  /**
128
- * Refresh one direct-child catalog, reusing its in-flight request.
129
- * @param parentSessionId - catalog owner.
118
+ * Identities an engagement may still belong to: the given list rows, resident
119
+ * Session instances, and retained child addresses.
120
+ * @param summaries - list rows of the caller's snapshot.
121
+ * @returns the retained identity set.
130
122
  */
131
- refreshSubagents(parentSessionId: SessionId): Promise<void>;
123
+ private retainedIds;
124
+ /**
125
+ * Forget one engagement that no retained identity holds.
126
+ * @param sessionId - identity whose engagement may be dropped.
127
+ * @param retained - identities from {@link retainedIds} for the caller's snapshot.
128
+ */
129
+ private pruneEngagement;
130
+ /** Resident per-session projection store (create-on-demand; outlives instantiation). */
131
+ private projectionStore;
132
132
  /**
133
- * Mark whether a catalog menu is consuming live membership updates.
134
- * @param parentSessionId - catalog owner.
135
- * @param open - current menu state.
133
+ * Load a complete projection baseline once per connection; retry unsuccessful reads.
134
+ * @param sessionId - Session to inspect without opening its conversation.
135
+ * @returns completion of the current or newly started read.
136
136
  */
137
- setSubagentCatalogOpen(parentSessionId: SessionId, open: boolean): void;
137
+ refreshProjections(sessionId: SessionId): Promise<void>;
138
+ private agentAvailable;
139
+ private updateParentAvailability;
138
140
  /** Full refresh via session.list (single-flight within one Host generation). */
139
141
  refreshList(): Promise<void>;
140
142
  /**
@@ -164,11 +166,12 @@ export declare class SessionManager {
164
166
  }>>;
165
167
  /**
166
168
  * Contract session.fork; on success merge the child into summaries
167
- * immediately (same synchronous-addressability guarantee as create). The
168
- * child carries the source's history, so it is never blank; lineage rides
169
- * parentSessionId so the list nests it under its source. A child published
170
- * before Workspace attachment fails is also reconciled into the list.
171
- * @param opts - source session and the optional seq anchoring the cut.
169
+ * immediately (same synchronous-addressability guarantee as create).
170
+ * Blankness starts provisionally true so the authoritative Host summary can
171
+ * preserve it or lower it after an exact cut before the first `turn/start`;
172
+ * lineage rides parentSessionId. A child published before Workspace
173
+ * attachment fails is also reconciled into the list.
174
+ * @param opts - source session and the optional exact inclusive boundary seq.
172
175
  * @returns the fork result (the child session id).
173
176
  */
174
177
  fork(opts: {
@@ -178,10 +181,15 @@ export declare class SessionManager {
178
181
  sessionId: SessionId;
179
182
  }>>;
180
183
  /**
181
- * Insert-or-enrich a locally synthesized summary: a new id prepends; an
182
- * existing entry only gains fields it lacks (the session-added frame and the
183
- * create() echo race — whichever lands second must fill the placeholder's
184
- * missing cwd/parentSessionId, never overwrite list-refresh data).
184
+ * Rename a Session and update its title projection without opening its history.
185
+ * @param sessionId - Session to rename.
186
+ * @param title - raw title text for Host normalization.
187
+ * @returns the accepted title and event position, or the Remote failure.
188
+ */
189
+ rename(sessionId: SessionId, title: string): Promise<RemoteResult<SessionRenameValue>>;
190
+ /**
191
+ * Merge a Host summary, replacing live state and filling missing metadata.
192
+ * Local create/fork placeholders only fill metadata on an existing row.
185
193
  */
186
194
  private mergeSummary;
187
195
  /** Apply immediately and retain for replay when a list response is in flight. */
@@ -214,6 +222,15 @@ export declare class SessionManager {
214
222
  * @param summary - current Host summary for the added Session.
215
223
  */
216
224
  handleSessionAdded(summary: SessionSummary): void;
225
+ /**
226
+ * Merge one list-surface projection block by the sequence space it declares.
227
+ * A `sequenced` block came from the Host's live registry for an attached
228
+ * Session, so each key lands under higher-seq-wins against that Session's
229
+ * baselines and frames. A `cached` block was viewed from the persisted
230
+ * checkpoint by a header-only listing: its watermark is not comparable with
231
+ * this connection's seqs, so it only fills keys no sequenced row holds.
232
+ */
233
+ private applyListBlock;
217
234
  /**
218
235
  * Apply one Session removal forwarded through `ctx.remote.$on`.
219
236
  * @param sessionId - removed Session identity.
@@ -244,16 +261,6 @@ export declare class SessionManager {
244
261
  * Opened Session follow streams resume independently through API Gateway.
245
262
  */
246
263
  handleConnected(): void;
247
- /** Debounce membership refetches for an explicitly consumed catalog. */
248
- private scheduleCatalogRefresh;
249
- /** Apply one Agent-driver transition to loaded and in-flight catalogs. */
250
- private updateCatalogActivity;
251
- /** Preserve and project a positive expandability hint after one direct subagent publishes. */
252
- private markCatalogParentExpandable;
253
- /** Apply one positive expandability hint to every loaded catalog containing that unique row id. */
254
- private applyCatalogParentExpandable;
255
- /** Fold request-local row mutations into one catalog result before publication. */
256
- private withCatalogMutations;
257
264
  private buildListSnapshot;
258
265
  }
259
266
  //# sourceMappingURL=manager.d.ts.map