@deepseek-ai/dsh-api-session-controller 0.1.7-alpha.2 → 0.1.7-rc.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.
@@ -371,6 +371,8 @@ let _deepseek_ai_dsh_api_session_controller_session_fork_result$schema$value
371
371
  const _deepseek_ai_dsh_api_session_controller_session_fork_result$schema = () => (_deepseek_ai_dsh_api_session_controller_session_fork_result$schema$value ??= z.object({
372
372
  'sessionId': z.intersection(z.string(), z.unknown()).readonly(),
373
373
  }))
374
+ let _deepseek_ai_dsh_api_session_controller_session_initializeDefaultModel_result$schema$value
375
+ const _deepseek_ai_dsh_api_session_controller_session_initializeDefaultModel_result$schema = () => (_deepseek_ai_dsh_api_session_controller_session_initializeDefaultModel_result$schema$value ??= z.void())
374
376
  let _deepseek_ai_dsh_api_session_controller_session_list_parameter_0$schema$value
375
377
  const _deepseek_ai_dsh_api_session_controller_session_list_parameter_0$schema = () => (_deepseek_ai_dsh_api_session_controller_session_list_parameter_0$schema$value ??= z.object({
376
378
  'cursor': z.string().readonly().optional(),
@@ -831,7 +833,7 @@ export const TYPERT_REMOTE = {
831
833
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionAttachmentValue',
832
834
  create: _deepseek_ai_dsh_api_session_controller_session_attachment_result$schema,
833
835
  },
834
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":419,"column":3},
836
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":437,"column":3},
835
837
  },
836
838
  {
837
839
  id: '@deepseek-ai/dsh-api-session-controller#session/cancel',
@@ -856,7 +858,7 @@ export const TYPERT_REMOTE = {
856
858
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionCancelValue',
857
859
  create: _deepseek_ai_dsh_api_session_controller_session_cancel_result$schema,
858
860
  },
859
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":439,"column":3},
861
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":457,"column":3},
860
862
  },
861
863
  {
862
864
  id: '@deepseek-ai/dsh-api-session-controller#session/canOpenWorkspacePath',
@@ -871,7 +873,7 @@ export const TYPERT_REMOTE = {
871
873
  typeSymbol: '@deepseek-ai/dsh-api-session-controller#session/canOpenWorkspacePath:result',
872
874
  create: _deepseek_ai_dsh_api_session_controller_session_canOpenWorkspacePath_result$schema,
873
875
  },
874
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":301,"column":3},
876
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":319,"column":3},
875
877
  },
876
878
  {
877
879
  id: '@deepseek-ai/dsh-api-session-controller#session/control',
@@ -888,7 +890,7 @@ export const TYPERT_REMOTE = {
888
890
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionControlFrame',
889
891
  create: _deepseek_ai_dsh_api_session_controller_session_control_result$schema,
890
892
  },
891
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":502,"column":3},
893
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":520,"column":3},
892
894
  },
893
895
  {
894
896
  id: '@deepseek-ai/dsh-api-session-controller#session/create',
@@ -940,7 +942,7 @@ export const TYPERT_REMOTE = {
940
942
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionFollowFrame',
941
943
  create: _deepseek_ai_dsh_api_session_controller_session_follow_result$schema,
942
944
  },
943
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":462,"column":3},
945
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":480,"column":3},
944
946
  },
945
947
  {
946
948
  id: '@deepseek-ai/dsh-api-session-controller#session/fork',
@@ -965,7 +967,22 @@ export const TYPERT_REMOTE = {
965
967
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionForkValue',
966
968
  create: _deepseek_ai_dsh_api_session_controller_session_fork_result$schema,
967
969
  },
968
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":397,"column":3},
970
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":415,"column":3},
971
+ },
972
+ {
973
+ id: '@deepseek-ai/dsh-api-session-controller#session/initializeDefaultModel',
974
+ service: 'sessionController',
975
+ namespace: 'session',
976
+ method: 'initializeDefaultModel',
977
+ invocation: { kind: 'direct' },
978
+ parameters: [
979
+ ],
980
+ result: {
981
+ mode: 'strict',
982
+ typeSymbol: '@deepseek-ai/dsh-api-session-controller#session/initializeDefaultModel:result',
983
+ create: _deepseek_ai_dsh_api_session_controller_session_initializeDefaultModel_result$schema,
984
+ },
985
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":292,"column":9},
969
986
  },
970
987
  {
971
988
  id: '@deepseek-ai/dsh-api-session-controller#session/list',
@@ -1006,7 +1023,7 @@ export const TYPERT_REMOTE = {
1006
1023
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#ModelCatalog',
1007
1024
  create: _deepseek_ai_dsh_api_session_controller_session_modelCatalog_result$schema,
1008
1025
  },
1009
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":292,"column":3},
1026
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":310,"column":3},
1010
1027
  },
1011
1028
  {
1012
1029
  id: '@deepseek-ai/dsh-api-session-controller#session/openWorkspacePath',
@@ -1032,7 +1049,7 @@ export const TYPERT_REMOTE = {
1032
1049
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionOpenWorkspacePathValue',
1033
1050
  create: _deepseek_ai_dsh_api_session_controller_session_openWorkspacePath_result$schema,
1034
1051
  },
1035
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":322,"column":9},
1052
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":340,"column":9},
1036
1053
  },
1037
1054
  {
1038
1055
  id: '@deepseek-ai/dsh-api-session-controller#session/page',
@@ -1058,7 +1075,7 @@ export const TYPERT_REMOTE = {
1058
1075
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionPage',
1059
1076
  create: _deepseek_ai_dsh_api_session_controller_session_page_result$schema,
1060
1077
  },
1061
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":450,"column":3},
1078
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":468,"column":3},
1062
1079
  },
1063
1080
  {
1064
1081
  id: '@deepseek-ai/dsh-api-session-controller#session/projections',
@@ -1084,7 +1101,7 @@ export const TYPERT_REMOTE = {
1084
1101
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionProjectionsValue',
1085
1102
  create: _deepseek_ai_dsh_api_session_controller_session_projections_result$schema,
1086
1103
  },
1087
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":473,"column":9},
1104
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":491,"column":9},
1088
1105
  },
1089
1106
  {
1090
1107
  id: '@deepseek-ai/dsh-api-session-controller#session/prompt',
@@ -1110,7 +1127,7 @@ export const TYPERT_REMOTE = {
1110
1127
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionPromptValue',
1111
1128
  create: _deepseek_ai_dsh_api_session_controller_session_prompt_result$schema,
1112
1129
  },
1113
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":408,"column":3},
1130
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":426,"column":3},
1114
1131
  },
1115
1132
  {
1116
1133
  id: '@deepseek-ai/dsh-api-session-controller#session/rename',
@@ -1135,7 +1152,7 @@ export const TYPERT_REMOTE = {
1135
1152
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionRenameValue',
1136
1153
  create: _deepseek_ai_dsh_api_session_controller_session_rename_result$schema,
1137
1154
  },
1138
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":385,"column":3},
1155
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":403,"column":3},
1139
1156
  },
1140
1157
  {
1141
1158
  id: '@deepseek-ai/dsh-api-session-controller#session/search',
@@ -1211,7 +1228,7 @@ export const TYPERT_REMOTE = {
1211
1228
  typeSymbol: '@deepseek-ai/dsh-api-session-controller/types#SessionUpdateQueueValue',
1212
1229
  create: _deepseek_ai_dsh_api_session_controller_session_updateQueue_result$schema,
1213
1230
  },
1214
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":429,"column":3},
1231
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":447,"column":3},
1215
1232
  },
1216
1233
  {
1217
1234
  id: '@deepseek-ai/dsh-api-session-controller#session/workspacePathApplications',
@@ -1237,7 +1254,7 @@ export const TYPERT_REMOTE = {
1237
1254
  typeSymbol: '@deepseek-ai/dsh-api-session-controller#session/workspacePathApplications:result',
1238
1255
  create: _deepseek_ai_dsh_api_session_controller_session_workspacePathApplications_result$schema,
1239
1256
  },
1240
- sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":352,"column":9},
1257
+ sourceLocation: {"file":"packages/api/session-controller/src/index.ts","line":370,"column":9},
1241
1258
  },
1242
1259
  {
1243
1260
  id: '@deepseek-ai/dsh-api-session-controller#skills/list',
@@ -8,4 +8,17 @@ import type { ModelCatalog, ModelSelection } from './types.ts';
8
8
  * @returns successful non-empty provider groups and isolated provider failures.
9
9
  */
10
10
  export declare function buildModelCatalog(ctx: Context, defaultSelection?: ModelSelection): Promise<ModelCatalog>;
11
+ /**
12
+ * Check a GUI selection against the current available provider catalog.
13
+ * @param ctx - Host LLM registry.
14
+ * @param selection - stored or explicitly requested selection.
15
+ * @returns whether the exact model is currently advertised as available.
16
+ */
17
+ export declare function modelAvailable(ctx: Context, selection: ModelSelection): Promise<boolean>;
18
+ /**
19
+ * Check configured provider API-key references independently of model availability.
20
+ * @param ctx - Host registry, settings, and credential services.
21
+ * @returns whether any API-key provider has a configured credential.
22
+ */
23
+ export declare function hasProviderApiKey(ctx: Context): Promise<boolean>;
11
24
  //# sourceMappingURL=catalog.d.ts.map
@@ -1,4 +1,6 @@
1
1
  /** Shared projection of the live LLM registry into the browser model catalog. */
2
+ import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
3
+ import { credentialRef } from '@deepseek-ai/dsh-credentials';
2
4
  /**
3
5
  * Build the browser model catalog without requiring a Session.
4
6
  * @param ctx - Host context carrying the live LLM registry.
@@ -47,12 +49,59 @@ export async function buildModelCatalog(ctx, defaultSelection = ctx.agentDefault
47
49
  };
48
50
  }
49
51
  }));
52
+ const groups = catalog.flatMap(item => item.kind === 'group' ? [item.group] : [])
53
+ .filter(group => group.models.length > 0);
50
54
  return {
51
55
  default: { ...defaultSelection },
52
- routableProviders: providers.map(provider => provider.id),
53
- groups: catalog.flatMap(item => item.kind === 'group' ? [item.group] : [])
54
- .filter(group => group.models.length > 0),
56
+ routableProviders: groups.map(group => group.id),
57
+ groups,
55
58
  failures: catalog.flatMap(item => item.kind === 'failure' ? [item.failure] : []),
56
59
  };
57
60
  }
61
+ /**
62
+ * Check a GUI selection against the current available provider catalog.
63
+ * @param ctx - Host LLM registry.
64
+ * @param selection - stored or explicitly requested selection.
65
+ * @returns whether the exact model is currently advertised as available.
66
+ */
67
+ export async function modelAvailable(ctx, selection) {
68
+ if (!ctx.llm.listProviders().some(provider => provider.id === selection.provider))
69
+ return false;
70
+ let models;
71
+ try {
72
+ models = await ctx.llm.listModels(selection.provider);
73
+ }
74
+ catch (error) {
75
+ throw new RemoteError('session/model-unavailable', error instanceof Error ? error.message : String(error), { provider: selection.provider, model: selection.model });
76
+ }
77
+ return models.some(model => model.id === selection.model);
78
+ }
79
+ /**
80
+ * Check configured provider API-key references independently of model availability.
81
+ * @param ctx - Host registry, settings, and credential services.
82
+ * @returns whether any API-key provider has a configured credential.
83
+ */
84
+ export async function hasProviderApiKey(ctx) {
85
+ const settings = ctx.get('settings');
86
+ const credentials = ctx.get('credentials');
87
+ if (settings === undefined || credentials === undefined) {
88
+ throw new RemoteError('session/provider-credentials-unavailable', 'provider credentials are unavailable', {});
89
+ }
90
+ const namespaces = settings.describe({ redactSecrets: true });
91
+ for (const provider of ctx.llm.listConfigurableProviders()) {
92
+ if (provider.provider === 'deepseek-account')
93
+ continue;
94
+ let profile = namespaces.find(namespace => namespace.ns === provider.settingsNs)?.value;
95
+ for (const key of provider.settingsPath) {
96
+ profile = typeof profile === 'object' && profile !== null ? Reflect.get(profile, key) : undefined;
97
+ }
98
+ if (typeof profile !== 'object' || profile === null)
99
+ continue;
100
+ const ref = Reflect.get(profile, 'apiKeyEnv');
101
+ if (typeof ref === 'string' && ref.length > 0
102
+ && (await credentials.describe(credentialRef(ref))).configured)
103
+ return true;
104
+ }
105
+ return false;
106
+ }
58
107
  //# sourceMappingURL=catalog.js.map
@@ -6,6 +6,8 @@ import type { SessionAssistantSettlementEntry, SessionEventLikeEntry, SessionLiv
6
6
  export type ClientAssistantStreamResult = {
7
7
  readonly type: 'publish';
8
8
  readonly entry: SessionLiveEventEntry;
9
+ /** Retire the successful attempt's transient rows after publishing its Step end. */
10
+ readonly retireAttemptId?: LlmAttemptId;
9
11
  } | {
10
12
  readonly type: 'settlement';
11
13
  readonly attemptId: LlmAttemptId;
@@ -22,6 +24,7 @@ export type ClientAssistantStreamResult = {
22
24
  /** Keeps transient Assistant presentation behind one settlement-aware interface. */
23
25
  export declare class ClientAssistantStream {
24
26
  private activeAttempt;
27
+ private retainedAttempt;
25
28
  private readonly pending;
26
29
  private publishedSeqs;
27
30
  private durableCursor;
@@ -41,6 +44,8 @@ export declare class ClientAssistantStream {
41
44
  acceptDurable(entry: SessionLiveEventEntry): ClientAssistantStreamResult;
42
45
  /**
43
46
  * Fold one dense transient frame and release its named durable settlement.
47
+ * Successful messages retain their transient rows until the owning Step ends;
48
+ * interrupted messages, failed attempts, and abandonment retire them immediately.
44
49
  * @param frame - next Assistant stream frame received by the follow connection.
45
50
  * @returns a transient, publication, or rebaseline decision, or `undefined` when no entry becomes visible.
46
51
  */
@@ -3,6 +3,7 @@ import { expandAssistantStream } from '@deepseek-ai/dsh-llm/assistant-stream';
3
3
  /** Keeps transient Assistant presentation behind one settlement-aware interface. */
4
4
  export class ClientAssistantStream {
5
5
  activeAttempt;
6
+ retainedAttempt;
6
7
  pending = new Map();
7
8
  publishedSeqs = new Set();
8
9
  durableCursor = -1;
@@ -17,6 +18,7 @@ export class ClientAssistantStream {
17
18
  this.pending.clear();
18
19
  this.transientInGap = 0;
19
20
  this.activeAttempt = undefined;
21
+ this.retainedAttempt = undefined;
20
22
  const opening = baseline?.activeAttempt;
21
23
  if (opening !== undefined) {
22
24
  this.activeAttempt = {
@@ -73,13 +75,16 @@ export class ClientAssistantStream {
73
75
  }
74
76
  /**
75
77
  * Fold one dense transient frame and release its named durable settlement.
78
+ * Successful messages retain their transient rows until the owning Step ends;
79
+ * interrupted messages, failed attempts, and abandonment retire them immediately.
76
80
  * @param frame - next Assistant stream frame received by the follow connection.
77
81
  * @returns a transient, publication, or rebaseline decision, or `undefined` when no entry becomes visible.
78
82
  */
79
83
  acceptFrame(frame) {
80
84
  switch (frame.type) {
81
85
  case 'start':
82
- if (this.activeAttempt !== undefined || this.pending.size > 0)
86
+ if (this.activeAttempt !== undefined || this.retainedAttempt !== undefined
87
+ || this.pending.size > 0)
83
88
  return { type: 'rebaseline' };
84
89
  this.pending.clear();
85
90
  this.activeAttempt = {
@@ -140,6 +145,10 @@ export class ClientAssistantStream {
140
145
  return { type: 'rebaseline' };
141
146
  }
142
147
  this.pending.delete(frame.outcome.seq);
148
+ if (entry.event.type === 'assistant/message' && entry.event.data.interrupted !== true) {
149
+ this.retainedAttempt = { attemptId: attempt.attemptId, turn: attempt.turn, step: attempt.step };
150
+ return this.publish(entry);
151
+ }
143
152
  this.publishedSeqs.add(entry.event.seq);
144
153
  return { type: 'settlement', attemptId: attempt.attemptId, entry };
145
154
  }
@@ -157,6 +166,12 @@ export class ClientAssistantStream {
157
166
  }
158
167
  publish(entry) {
159
168
  this.publishedSeqs.add(entry.event.seq);
169
+ const retained = this.retainedAttempt;
170
+ if (retained !== undefined && entry.event.type === 'step/end'
171
+ && entry.event.data.turn === retained.turn && entry.event.data.step === retained.step) {
172
+ this.retainedAttempt = undefined;
173
+ return { type: 'publish', entry, retireAttemptId: retained.attemptId };
174
+ }
160
175
  return { type: 'publish', entry };
161
176
  }
162
177
  }
@@ -634,8 +634,12 @@ export class Session {
634
634
  this.notifier.markDirty();
635
635
  return;
636
636
  }
637
- if (result?.type === 'publish' && this.appendLive(result.entry)) {
638
- this.notifier.markDirty();
637
+ if (result?.type === 'publish') {
638
+ const changed = this.appendLive(result.entry);
639
+ if (result.retireAttemptId !== undefined)
640
+ this.eventSource.settleAssistant(result.retireAttemptId);
641
+ if (changed || result.retireAttemptId !== undefined)
642
+ this.notifier.markDirty();
639
643
  }
640
644
  else if (result?.type === 'transient') {
641
645
  this.eventSource.append(result.entry);
@@ -20,9 +20,9 @@ export declare class SessionCommandController {
20
20
  */
21
21
  create(request: SessionCreateRequest): Promise<SessionCreateValue>;
22
22
  /**
23
- * Validate and install one Session-local model selection.
23
+ * Validate and install one Session-local model selection; save the default in the background.
24
24
  * @param request - Session identity and requested model selection.
25
- * @returns the normalized selection installed for the Session.
25
+ * @returns the normalized selection installed for the Session, without waiting for default persistence.
26
26
  */
27
27
  selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>;
28
28
  /**
@@ -45,6 +45,7 @@ export declare class SessionCommandController {
45
45
  * @returns acknowledgement that the Agent accepted the prompt.
46
46
  */
47
47
  prompt(request: SessionPromptRequest): Promise<SessionPromptValue>;
48
+ private requireModel;
48
49
  /**
49
50
  * Read one durable image after proving the Session log references it.
50
51
  * @param request - Session and attachment identities used for authorization.
@@ -51,6 +51,7 @@ 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 { modelAvailable } from "./catalog.js";
54
55
  import { randomUUID } from 'node:crypto';
55
56
  import { brandString } from '@deepseek-ai/dsh-brand';
56
57
  import { AttachmentError } from '@deepseek-ai/dsh-attachment';
@@ -137,14 +138,15 @@ export class SessionCommandController {
137
138
  return { sessionId, ...(agentPreset === undefined ? {} : { agentPreset }) };
138
139
  }
139
140
  /**
140
- * Validate and install one Session-local model selection.
141
+ * Validate and install one Session-local model selection; save the default in the background.
141
142
  * @param request - Session identity and requested model selection.
142
- * @returns the normalized selection installed for the Session.
143
+ * @returns the normalized selection installed for the Session, without waiting for default persistence.
143
144
  */
144
145
  async selectModel(request) {
145
146
  const agent = await this.resolveAgent(request.sessionId);
146
147
  return this.agents.serializeImageAdmission(agent, async () => {
147
148
  try {
149
+ await this.requireModel(request);
148
150
  const resolved = await this.ctx.llm.resolveCallConfig({
149
151
  provider: request.provider,
150
152
  model: request.model,
@@ -160,12 +162,9 @@ export class SessionCommandController {
160
162
  : { reasoningEffort: resolved.reasoningEffort }),
161
163
  };
162
164
  this.agents.selectForNextRequest(agent, selected);
163
- try {
164
- await this.ctx.agentDefaultModel.saveSelection(selected);
165
- }
166
- catch (error) {
165
+ void this.ctx.agentDefaultModel.saveSelection(selected).catch((error) => {
167
166
  this.ctx.logger.warn(`session-controller: model selection changed for the Session but the default was not saved: ${String(error)}`);
168
- }
167
+ });
169
168
  return { selected: { ...selected } };
170
169
  }
171
170
  catch (error) {
@@ -301,10 +300,6 @@ export class SessionCommandController {
301
300
  const agent = await this.resolveAgent(request.sessionId);
302
301
  if (hasPromptRequest(agent, request.requestId))
303
302
  return { accepted: true };
304
- const selection = this.agents.selectionFor(agent).current;
305
- if (!routeServed(this.ctx, selection.provider)) {
306
- throw new RemoteError('session/model-unavailable', `no adapter serves provider "${selection.provider}"; select a model for this session`, { provider: selection.provider, model: selection.model });
307
- }
308
303
  const source = {
309
304
  kind: 'user',
310
305
  rpcId: request.requestId,
@@ -355,6 +350,11 @@ export class SessionCommandController {
355
350
  };
356
351
  return hasImage ? this.agents.serializeImageAdmission(agent, admit) : admit();
357
352
  }
353
+ async requireModel(selection) {
354
+ if (!await modelAvailable(this.ctx, selection)) {
355
+ throw new RemoteError('session/model-unavailable', 'Select an available model before sending a message.', { provider: selection.provider, model: selection.model });
356
+ }
357
+ }
358
358
  /**
359
359
  * Read one durable image after proving the Session log references it.
360
360
  * @param request - Session and attachment identities used for authorization.
@@ -628,7 +628,4 @@ function referencedImage(events, attachmentId) {
628
628
  }
629
629
  return undefined;
630
630
  }
631
- function routeServed(ctx, provider) {
632
- return ctx.llm.listProviders().some(entry => entry.id === provider);
633
- }
634
631
  //# sourceMappingURL=commands.js.map
@@ -92,11 +92,16 @@ export declare class SessionController extends TypertRemoteService {
92
92
  */
93
93
  create(request: SessionCreateRequest): Promise<SessionCreateValue>;
94
94
  /**
95
- * Select one Session-local model after explicitly resuming the Session.
95
+ * Select one Session-local model after explicitly resuming the Session; save the default in the background.
96
96
  * @param request - Session identity and requested model selection.
97
- * @returns the normalized selection installed for the Session.
97
+ * @returns the normalized selection installed for the Session, without waiting for default persistence.
98
98
  */
99
99
  selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>;
100
+ /**
101
+ * Select the first available account model after login when no provider API key is configured.
102
+ * @returns after saving the first available model or retaining the existing default.
103
+ */
104
+ initializeDefaultModel(): Promise<void>;
100
105
  /**
101
106
  * Describe every currently routable model for Host-generation selectors.
102
107
  * @returns provider-grouped models, the deployment default, and isolated provider failures.
@@ -88,7 +88,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
88
88
  import { hostname } from 'node:os';
89
89
  import { resolve } from 'node:path';
90
90
  import z from '@deepseek-ai/schemastery';
91
- import { errorChain } from '@deepseek-ai/dsh-llm';
91
+ import { errorChain, ReasoningEffortId } from '@deepseek-ai/dsh-llm';
92
92
  import { canOpenNativePath, nativeFileManager, nativeFileApplications, openNativeFileApplication, openNativeAssociatedPath, revealNativePath } from '@deepseek-ai/dsh-native-command';
93
93
  import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
94
94
  import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
@@ -98,7 +98,7 @@ import { SessionControlController } from "./control.js";
98
98
  import { SessionHistoryController } from "./history.js";
99
99
  import { SessionFileReferences } from "./file-references.js";
100
100
  import { ApiSessionList } from "./list.js";
101
- import { buildModelCatalog } from "./catalog.js";
101
+ import { buildModelCatalog, hasProviderApiKey } from "./catalog.js";
102
102
  import { installModelSelectionProjection } from "./model-selection-projection.js";
103
103
  import { SessionSkillCatalog } from "./skill-catalog.js";
104
104
  import { SessionMediaReferences } from "./media-references.js";
@@ -114,6 +114,7 @@ let SessionController = (() => {
114
114
  let _search_decorators;
115
115
  let _create_decorators;
116
116
  let _selectModel_decorators;
117
+ let _initializeDefaultModel_decorators;
117
118
  let _modelCatalog_decorators;
118
119
  let _canOpenWorkspacePath_decorators;
119
120
  let _openWorkspacePath_decorators;
@@ -135,6 +136,7 @@ let SessionController = (() => {
135
136
  _search_decorators = [Remote('search')];
136
137
  _create_decorators = [Remote('create')];
137
138
  _selectModel_decorators = [Remote('selectModel')];
139
+ _initializeDefaultModel_decorators = [Remote];
138
140
  _modelCatalog_decorators = [Remote('modelCatalog')];
139
141
  _canOpenWorkspacePath_decorators = [Remote];
140
142
  _openWorkspacePath_decorators = [Remote('openWorkspacePath')];
@@ -153,6 +155,7 @@ let SessionController = (() => {
153
155
  __esDecorate(this, null, _search_decorators, { kind: "method", name: "search", static: false, private: false, access: { has: obj => "search" in obj, get: obj => obj.search }, metadata: _metadata }, null, _instanceExtraInitializers);
154
156
  __esDecorate(this, null, _create_decorators, { kind: "method", name: "create", static: false, private: false, access: { has: obj => "create" in obj, get: obj => obj.create }, metadata: _metadata }, null, _instanceExtraInitializers);
155
157
  __esDecorate(this, null, _selectModel_decorators, { kind: "method", name: "selectModel", static: false, private: false, access: { has: obj => "selectModel" in obj, get: obj => obj.selectModel }, metadata: _metadata }, null, _instanceExtraInitializers);
158
+ __esDecorate(this, null, _initializeDefaultModel_decorators, { kind: "method", name: "initializeDefaultModel", static: false, private: false, access: { has: obj => "initializeDefaultModel" in obj, get: obj => obj.initializeDefaultModel }, metadata: _metadata }, null, _instanceExtraInitializers);
156
159
  __esDecorate(this, null, _modelCatalog_decorators, { kind: "method", name: "modelCatalog", static: false, private: false, access: { has: obj => "modelCatalog" in obj, get: obj => obj.modelCatalog }, metadata: _metadata }, null, _instanceExtraInitializers);
157
160
  __esDecorate(this, null, _canOpenWorkspacePath_decorators, { kind: "method", name: "canOpenWorkspacePath", static: false, private: false, access: { has: obj => "canOpenWorkspacePath" in obj, get: obj => obj.canOpenWorkspacePath }, metadata: _metadata }, null, _instanceExtraInitializers);
158
161
  __esDecorate(this, null, _openWorkspacePath_decorators, { kind: "method", name: "openWorkspacePath", static: false, private: false, access: { has: obj => "openWorkspacePath" in obj, get: obj => obj.openWorkspacePath }, metadata: _metadata }, null, _instanceExtraInitializers);
@@ -339,13 +342,30 @@ let SessionController = (() => {
339
342
  return this.commands.create(request);
340
343
  }
341
344
  /**
342
- * Select one Session-local model after explicitly resuming the Session.
345
+ * Select one Session-local model after explicitly resuming the Session; save the default in the background.
343
346
  * @param request - Session identity and requested model selection.
344
- * @returns the normalized selection installed for the Session.
347
+ * @returns the normalized selection installed for the Session, without waiting for default persistence.
345
348
  */
346
349
  selectModel(request) {
347
350
  return this.commands.selectModel(request);
348
351
  }
352
+ /**
353
+ * Select the first available account model after login when no provider API key is configured.
354
+ * @returns after saving the first available model or retaining the existing default.
355
+ */
356
+ async initializeDefaultModel() {
357
+ const provider = 'deepseek-account';
358
+ if (await hasProviderApiKey(this.ctx))
359
+ return;
360
+ const catalog = await buildModelCatalog(this.ctx);
361
+ const model = catalog.groups.find(group => group.id === provider)?.models[0];
362
+ if (model === undefined)
363
+ throw new RemoteError('session/provider-models-unavailable', `provider "${provider}" has no available models`, { provider });
364
+ const selection = { provider, model: model.id,
365
+ ...model.reasoning?.defaultEffort === undefined ? {} : { reasoningEffort: ReasoningEffortId(model.reasoning.defaultEffort) },
366
+ };
367
+ await this.ctx.agentDefaultModel.saveSelection(selection);
368
+ }
349
369
  /**
350
370
  * Describe every currently routable model for Host-generation selectors.
351
371
  * @returns provider-grouped models, the deployment default, and isolated provider failures.
@@ -140,7 +140,7 @@ export interface ModelCatalogFailure {
140
140
  /** Host-generation model catalog and the default used by unconfigured Sessions. */
141
141
  export interface ModelCatalog {
142
142
  readonly default: ModelSelection;
143
- /** Provider routes currently able to serve a request, including empty catalogs. */
143
+ /** Provider routes with at least one currently available catalog model. */
144
144
  readonly routableProviders: readonly string[];
145
145
  readonly groups: readonly ModelProviderGroup[];
146
146
  readonly failures: readonly ModelCatalogFailure[];
@@ -179,6 +179,10 @@ export declare const SESSION_SEARCH_RESULT_LIMIT = 20;
179
179
  export declare const SESSION_SEARCH_SNIPPET_MAX_CODE_POINTS = 240;
180
180
  declare module '@deepseek-ai/dsh-typert-protocol' {
181
181
  interface RemoteErrorDetailsMap {
182
+ 'session/provider-credentials-unavailable': Record<string, never>;
183
+ 'session/provider-models-unavailable': {
184
+ readonly provider: string;
185
+ };
182
186
  'session/model-unavailable': {
183
187
  readonly provider: string;
184
188
  readonly model: string;