@deepseek-ai/dsh-api-session-controller 0.2.0-rc.2 → 0.2.1-alpha.2

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/lib/types/list.js CHANGED
@@ -1,4 +1,6 @@
1
1
  /** Cold-safe Session list and search projection. */
2
+ import { performance } from 'node:perf_hooks';
3
+ import { scheduler } from 'node:timers/promises';
2
4
  import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
3
5
  import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
4
6
  import { z } from 'zod';
@@ -53,9 +55,14 @@ export function truncateUnicodeCodePoints(value, maximum) {
53
55
  /** Owns list projection registration, bounded cold summaries, and authorized search. */
54
56
  export class ApiSessionList {
55
57
  ctx;
56
- /** @param ctx - Host context carrying Session, query, persistence, and projection services. */
57
- constructor(ctx) {
58
+ workSliceMs;
59
+ /**
60
+ * @param ctx - Host context carrying Session, query, persistence, and projection services.
61
+ * @param workSliceMs - Resolved positive integral list-work budget in milliseconds.
62
+ */
63
+ constructor(ctx, workSliceMs) {
58
64
  this.ctx = ctx;
65
+ this.workSliceMs = workSliceMs;
59
66
  ctx.sessionProjections.register({
60
67
  key: 'sessionListMetadata',
61
68
  stateSchema: sessionListMetadataSchema,
@@ -90,6 +97,7 @@ export class ApiSessionList {
90
97
  sessionId: session.id,
91
98
  updatedAt: updatedAt(session.header, metadata),
92
99
  agentAvailable: this.ctx.agents.get(session.id)?.session === session,
100
+ formatStatus: 'current',
93
101
  running: this.ctx.agents.get(session.id)?.status === 'running',
94
102
  blank: metadata?.blank ?? session.seq === 0,
95
103
  ...listFields(session.header),
@@ -98,7 +106,7 @@ export class ApiSessionList {
98
106
  }
99
107
  /**
100
108
  * Read every visible attached and persisted Session without activating an Agent.
101
- * @param signal - optional cancellation for persistence reads.
109
+ * @param signal - optional cancellation for persistence reads and summary generation.
102
110
  * @returns visible Session summaries ordered by activity.
103
111
  */
104
112
  async list(signal) {
@@ -107,28 +115,43 @@ export class ApiSessionList {
107
115
  signal?.throwIfAborted();
108
116
  const items = [];
109
117
  const cold = [];
118
+ let yieldDeadline = performance.now() + this.workSliceMs;
110
119
  for (const record of records) {
120
+ signal?.throwIfAborted();
111
121
  const live = this.ctx.sessions.get(record.header.id);
112
122
  if (live !== undefined) {
113
123
  items.push(this.summaryFor(live));
114
- continue;
115
124
  }
116
- if (record.header.cwd === undefined)
117
- continue;
118
- cold.push(record.header);
125
+ else if (record.header.cwd !== undefined) {
126
+ cold.push(record);
127
+ }
128
+ if (performance.now() >= yieldDeadline) {
129
+ await scheduler.yield();
130
+ signal?.throwIfAborted();
131
+ yieldDeadline = performance.now() + this.workSliceMs;
132
+ }
119
133
  }
120
- for (const header of cold)
121
- items.push(this.summarizeCold(header));
134
+ for (const record of cold) {
135
+ signal?.throwIfAborted();
136
+ items.push(this.summarizeCold(record));
137
+ if (performance.now() >= yieldDeadline) {
138
+ await scheduler.yield();
139
+ signal?.throwIfAborted();
140
+ yieldDeadline = performance.now() + this.workSliceMs;
141
+ }
142
+ }
143
+ signal?.throwIfAborted();
122
144
  items.sort((left, right) => right.updatedAt - left.updatedAt);
123
145
  return items;
124
146
  }
125
- summarizeCold(header) {
147
+ summarizeCold({ header, formatStatus }) {
126
148
  const projections = this.projectionsFor(header, undefined);
127
149
  const metadata = projections?.values.sessionListMetadata;
128
150
  return {
129
151
  sessionId: header.id,
130
152
  updatedAt: updatedAt(header, metadata),
131
153
  agentAvailable: false,
154
+ ...(formatStatus === undefined ? {} : { formatStatus }),
132
155
  running: false,
133
156
  // A large, metadata-less, or inaccessible cache miss remains unknown and visible.
134
157
  blank: metadata?.blank ?? false,
@@ -100,7 +100,7 @@ let SessionSkillCatalog = (() => {
100
100
  __esDecorate(this, null, _list_decorators, { kind: "method", name: "list", static: false, private: false, access: { has: obj => "list" in obj, get: obj => obj.list }, metadata: _metadata }, null, _instanceExtraInitializers);
101
101
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
102
102
  }
103
- static inject = ['agents', 'sessionQuery', 'typert'];
103
+ static inject = ['agents', 'sessionQuery', 'typert', 'workingDirectory'];
104
104
  /** @param ctx - Host context carrying Session reads and optional skill/preset services. */
105
105
  constructor(ctx) {
106
106
  super(ctx, 'sessionSkillCatalog', { namespace: 'skills' });
@@ -116,7 +116,6 @@ let SessionSkillCatalog = (() => {
116
116
  async list(request, signal) {
117
117
  const env_1 = { stack: [], error: void 0, hasError: false };
118
118
  try {
119
- void signal;
120
119
  const { sessionId } = request;
121
120
  let cwd;
122
121
  let agentPreset;
@@ -127,8 +126,13 @@ let SessionSkillCatalog = (() => {
127
126
  if (observation.projections === undefined) {
128
127
  throw new Error('skill catalog requires a projected Session observation');
129
128
  }
130
- cwd = observation.header.cwd;
129
+ cwd = observation.projections.values.workingDirectory
130
+ ?? observation.header.cwd
131
+ ?? this.ctx.workingDirectory.defaultDirectory;
131
132
  agentPreset = observation.projections.values.agentPreset ?? undefined;
133
+ const live = this.ctx.agents.get(sessionId);
134
+ if (live !== undefined)
135
+ cwd = await this.ctx.workingDirectory.ensure(live, signal);
132
136
  }
133
137
  catch (e_1) {
134
138
  env_2.error = e_1;
@@ -145,9 +149,6 @@ let SessionSkillCatalog = (() => {
145
149
  }
146
150
  throw new RemoteError('gateway/internal', `session "${sessionId}" could not be inspected: ${String(error)}`, {});
147
151
  }
148
- if (cwd === undefined) {
149
- throw new RemoteError('gateway/internal', `session "${sessionId}" has no project cwd`, {});
150
- }
151
152
  const live = this.ctx.agents.get(sessionId);
152
153
  const presets = this.ctx.get('agentPresets');
153
154
  const scoped = live === undefined ? undefined : presets?.serviceFor(live, 'skills');
@@ -159,6 +159,8 @@ export type QueueAction = {
159
159
  export interface SessionSummary {
160
160
  /** Whether this Session currently owns a live Agent. */
161
161
  readonly agentAvailable: boolean;
162
+ /** Current for attached Sessions; otherwise the persistence format status when reported. */
163
+ readonly formatStatus?: 'current' | 'migration-required';
162
164
  readonly sessionId: SessionId;
163
165
  readonly updatedAt: number;
164
166
  readonly running: boolean;
@@ -226,6 +228,9 @@ declare module '@deepseek-ai/dsh-typert-protocol' {
226
228
  'session/fork-unavailable': {
227
229
  readonly sessionId: SessionId;
228
230
  };
231
+ 'session/migration-required': {
232
+ readonly sessionId: SessionId;
233
+ };
229
234
  'subagent/not-found': {
230
235
  readonly parentSessionId: SessionId;
231
236
  readonly childSessionId: SessionId;
@@ -310,6 +315,8 @@ export interface SessionForkRequest {
310
315
  readonly sessionId: SessionId;
311
316
  /** Exact inclusive source event seq; omission selects the latest completed-turn prefix. */
312
317
  readonly atSeq?: number;
318
+ /** False asks the Host to check for required migration before forking. Defaults to true. */
319
+ readonly allowMigration?: boolean;
313
320
  }
314
321
  /** Identity of a newly forked Session. */
315
322
  export interface SessionForkValue {
@@ -396,8 +403,13 @@ export type SessionAddress = {
396
403
  export interface SessionProjectionsRequest {
397
404
  readonly sessionId: SessionId;
398
405
  }
399
- /** Complete Session projection baseline; null when the Session does not exist. */
400
- export type SessionProjectionsValue = SessionProjectionBaseline | null;
406
+ /** Exact projections carry their event cut; required migration returns sequence-free cache hints. Null means absent. */
407
+ export type SessionProjectionsValue = (SessionProjectionBaseline & {
408
+ readonly kind: 'sequenced';
409
+ }) | {
410
+ readonly kind: 'migration-required';
411
+ readonly values: SessionProjectionValues;
412
+ } | null;
401
413
  /** One raw Session event in the Remote journal. */
402
414
  export interface SessionEventEntry {
403
415
  readonly type: 'event';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-api-session-controller",
3
3
  "description": "Session Remote commands, cold reads, and live control transport",
4
- "version": "0.2.0-rc.2",
4
+ "version": "0.2.1-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -65,43 +65,44 @@
65
65
  ],
66
66
  "license": "MIT",
67
67
  "dependencies": {
68
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.2",
69
+ "@deepseek-ai/dsh-deque": "0.2.1-alpha.2",
70
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.1",
68
71
  "mime-types": "^3.0.2",
69
- "zod": "^4.4.3",
70
- "@deepseek-ai/dsh-brand": "0.2.0-rc.2",
71
- "@deepseek-ai/dsh-deque": "0.2.0-rc.2",
72
- "@deepseek-ai/schemastery": "~3.18.4"
72
+ "zod": "^4.4.3"
73
73
  },
74
74
  "peerDependencies": {
75
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
76
- "@deepseek-ai/dsh-agent-default-model": "0.2.0-rc.2",
77
- "@deepseek-ai/cordis": "~4.0.4",
78
- "@deepseek-ai/dsh-agent-preset-registry": "0.2.0-rc.2",
79
- "@deepseek-ai/dsh-api-gateway": "0.2.0-rc.2",
80
- "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
81
- "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
82
- "@deepseek-ai/dsh-client-file-upload": "0.2.0-rc.2",
83
- "@deepseek-ai/dsh-commands": "0.2.0-rc.2",
84
- "@deepseek-ai/dsh-fs": "0.2.0-rc.2",
85
- "@deepseek-ai/dsh-native-command": "0.2.0-rc.2",
86
- "@deepseek-ai/dsh-scope": "0.2.0-rc.2",
87
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
88
- "@deepseek-ai/dsh-session-persistence": "0.2.0-rc.2",
89
- "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
90
- "@deepseek-ai/dsh-session-projection-cache": "0.2.0-rc.2",
91
- "@deepseek-ai/dsh-session-title": "0.2.0-rc.2",
92
- "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
93
- "@deepseek-ai/dsh-session-query": "0.2.0-rc.2",
94
- "@deepseek-ai/dsh-skill": "0.2.0-rc.2",
95
- "@deepseek-ai/dsh-subagent": "0.2.0-rc.2",
96
- "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
97
- "@deepseek-ai/dsh-typert-registry": "0.2.0-rc.2",
98
- "@deepseek-ai/dsh-util-time": "0.2.0-rc.2",
99
- "@deepseek-ai/dsh-util-values": "0.2.0-rc.2",
100
- "@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
101
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
102
- "@deepseek-ai/dsh-file-reference": "0.2.0-rc.2",
103
- "@deepseek-ai/dsh-workspace": "0.2.0-rc.2",
104
- "@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2"
75
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
76
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
77
+ "@deepseek-ai/dsh-agent-default-model": "0.2.1-alpha.2",
78
+ "@deepseek-ai/dsh-agent-preset-registry": "0.2.1-alpha.2",
79
+ "@deepseek-ai/dsh-api-gateway": "0.2.1-alpha.2",
80
+ "@deepseek-ai/dsh-attachment": "0.2.1-alpha.2",
81
+ "@deepseek-ai/dsh-client-connection": "0.2.1-alpha.2",
82
+ "@deepseek-ai/dsh-client-file-upload": "0.2.1-alpha.2",
83
+ "@deepseek-ai/dsh-commands": "0.2.1-alpha.2",
84
+ "@deepseek-ai/dsh-credentials": "0.2.1-alpha.2",
85
+ "@deepseek-ai/dsh-file-reference": "0.2.1-alpha.2",
86
+ "@deepseek-ai/dsh-fs": "0.2.1-alpha.2",
87
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
88
+ "@deepseek-ai/dsh-native-command": "0.2.1-alpha.2",
89
+ "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
90
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.2",
91
+ "@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.2",
92
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
93
+ "@deepseek-ai/dsh-session-projection-cache": "0.2.1-alpha.2",
94
+ "@deepseek-ai/dsh-session-query": "0.2.1-alpha.2",
95
+ "@deepseek-ai/dsh-session-title": "0.2.1-alpha.2",
96
+ "@deepseek-ai/dsh-settings": "0.2.1-alpha.2",
97
+ "@deepseek-ai/dsh-skill": "0.2.1-alpha.2",
98
+ "@deepseek-ai/dsh-subagent": "0.2.1-alpha.2",
99
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2",
100
+ "@deepseek-ai/dsh-typert-registry": "0.2.1-alpha.2",
101
+ "@deepseek-ai/dsh-util-time": "0.2.1-alpha.2",
102
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.2",
103
+ "@deepseek-ai/dsh-util-workspace-path": "0.2.1-alpha.2",
104
+ "@deepseek-ai/dsh-workspace": "0.2.1-alpha.2",
105
+ "@deepseek-ai/dsh-working-directory": "0.2.1-alpha.2"
105
106
  },
106
107
  "peerDependenciesMeta": {
107
108
  "@deepseek-ai/dsh-session-persistence": {
@@ -115,48 +116,50 @@
115
116
  }
116
117
  },
117
118
  "devDependencies": {
119
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
120
+ "@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
121
+ "@deepseek-ai/dsh-agent-default-model": "0.2.1-alpha.2",
122
+ "@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.2",
123
+ "@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.2",
124
+ "@deepseek-ai/dsh-agent-preset-registry": "0.2.1-alpha.2",
125
+ "@deepseek-ai/dsh-api-gateway": "0.2.1-alpha.2",
126
+ "@deepseek-ai/dsh-attachment": "0.2.1-alpha.2",
127
+ "@deepseek-ai/dsh-client-connection": "0.2.1-alpha.2",
128
+ "@deepseek-ai/dsh-client-file-upload": "0.2.1-alpha.2",
129
+ "@deepseek-ai/dsh-client-store": "0.2.1-alpha.2",
130
+ "@deepseek-ai/dsh-client-test-runtime": "0.2.1-alpha.2",
131
+ "@deepseek-ai/dsh-commands": "0.2.1-alpha.2",
132
+ "@deepseek-ai/dsh-credentials": "0.2.1-alpha.2",
133
+ "@deepseek-ai/dsh-file-reference": "0.2.1-alpha.2",
134
+ "@deepseek-ai/dsh-fs": "0.2.1-alpha.2",
135
+ "@deepseek-ai/dsh-fs-local": "0.2.1-alpha.2",
136
+ "@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
137
+ "@deepseek-ai/dsh-native-command": "0.2.1-alpha.2",
138
+ "@deepseek-ai/dsh-permission-presets": "0.2.1-alpha.2",
139
+ "@deepseek-ai/dsh-remote-mock": "0.2.1-alpha.2",
140
+ "@deepseek-ai/dsh-scope": "0.2.1-alpha.2",
141
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.2",
142
+ "@deepseek-ai/dsh-session-persistence": "0.2.1-alpha.2",
143
+ "@deepseek-ai/dsh-session-persistence-jsonl": "0.2.1-alpha.2",
144
+ "@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
145
+ "@deepseek-ai/dsh-session-projection-cache": "0.2.1-alpha.2",
146
+ "@deepseek-ai/dsh-session-query": "0.2.1-alpha.2",
147
+ "@deepseek-ai/dsh-session-title": "0.2.1-alpha.2",
148
+ "@deepseek-ai/dsh-settings": "0.2.1-alpha.2",
149
+ "@deepseek-ai/dsh-skill": "0.2.1-alpha.2",
150
+ "@deepseek-ai/dsh-storage": "0.2.1-alpha.2",
151
+ "@deepseek-ai/dsh-storage-domain": "0.2.1-alpha.2",
152
+ "@deepseek-ai/dsh-storage-json": "0.2.1-alpha.2",
153
+ "@deepseek-ai/dsh-subagent": "0.2.1-alpha.2",
154
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.2",
155
+ "@deepseek-ai/dsh-typert-registry": "0.2.1-alpha.2",
156
+ "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.2",
157
+ "@deepseek-ai/dsh-util-time": "0.2.1-alpha.2",
158
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.2",
159
+ "@deepseek-ai/dsh-util-workspace-path": "0.2.1-alpha.2",
160
+ "@deepseek-ai/dsh-workspace": "0.2.1-alpha.2",
118
161
  "@types/mime-types": "^3.0.1",
119
- "@deepseek-ai/cordis": "~4.0.4",
120
- "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
121
- "@deepseek-ai/dsh-agent-default-model": "0.2.0-rc.2",
122
- "@deepseek-ai/dsh-agent-loop": "0.2.0-rc.2",
123
- "@deepseek-ai/dsh-agent-loop-testkit": "0.2.0-rc.2",
124
- "@deepseek-ai/dsh-api-gateway": "0.2.0-rc.2",
125
- "@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
126
- "@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
127
- "@deepseek-ai/dsh-client-file-upload": "0.2.0-rc.2",
128
- "@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
129
- "@deepseek-ai/dsh-client-test-runtime": "0.2.0-rc.2",
130
- "@deepseek-ai/dsh-commands": "0.2.0-rc.2",
131
- "@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
132
- "@deepseek-ai/dsh-file-reference": "0.2.0-rc.2",
133
- "@deepseek-ai/dsh-fs": "0.2.0-rc.2",
134
- "@deepseek-ai/dsh-fs-local": "0.2.0-rc.2",
135
- "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
136
- "@deepseek-ai/dsh-native-command": "0.2.0-rc.2",
137
- "@deepseek-ai/dsh-permission-presets": "0.2.0-rc.2",
138
- "@deepseek-ai/dsh-remote-mock": "0.2.0-rc.2",
139
- "@deepseek-ai/dsh-scope": "0.2.0-rc.2",
140
- "@deepseek-ai/dsh-session": "0.2.0-rc.2",
141
- "@deepseek-ai/dsh-session-persistence": "0.2.0-rc.2",
142
- "@deepseek-ai/dsh-session-projection": "0.2.0-rc.2",
143
- "@deepseek-ai/dsh-session-projection-cache": "0.2.0-rc.2",
144
- "@deepseek-ai/dsh-session-query": "0.2.0-rc.2",
145
- "@deepseek-ai/dsh-session-title": "0.2.0-rc.2",
146
- "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
147
- "@deepseek-ai/dsh-skill": "0.2.0-rc.2",
148
- "@deepseek-ai/dsh-agent-preset-registry": "0.2.0-rc.2",
149
- "@deepseek-ai/dsh-storage-domain": "0.2.0-rc.2",
150
- "@deepseek-ai/dsh-storage-json": "0.2.0-rc.2",
151
- "@deepseek-ai/dsh-subagent": "0.2.0-rc.2",
152
- "@deepseek-ai/dsh-typert-protocol": "0.2.0-rc.2",
153
- "@deepseek-ai/dsh-typert-registry": "0.2.0-rc.2",
154
- "@deepseek-ai/dsh-util-crypto": "0.2.0-rc.2",
155
- "@deepseek-ai/dsh-util-time": "0.2.0-rc.2",
156
- "@deepseek-ai/dsh-util-values": "0.2.0-rc.2",
157
- "@deepseek-ai/dsh-util-workspace-path": "0.2.0-rc.2",
158
- "@deepseek-ai/dsh-storage": "0.2.0-rc.2",
159
- "@deepseek-ai/dsh-workspace": "0.2.0-rc.2"
162
+ "@deepseek-ai/dsh-working-directory": "0.2.1-alpha.2"
160
163
  },
161
164
  "scripts": {
162
165
  "bundle": "tsdown",