@chatcode/chatcode-cli 3.0.4-rc.1 → 3.0.4-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.
Files changed (123) hide show
  1. package/lib/{command-errors-BkCHTQHj.js → command-errors-CScdshCl.js} +12 -12
  2. package/lib/{command-trees-5HFyC5sI.js → command-trees-Dc17MYfV.js} +2 -2
  3. package/lib/{component-identity-De91VHwW.js → component-identity-DFJD_d-P.js} +3 -3
  4. package/lib/{dist-CsDWUvQ4.js → dist-w_sIm4VD.js} +231 -231
  5. package/lib/{extension-events-CdYCRP0o.js → extension-events-qXmhzmHo.js} +26 -26
  6. package/lib/{frames-DOX-Jgk8.js → frames-B89znn_0.js} +4 -4
  7. package/lib/{host-access-Bk4xps5E.js → host-access-B0CRAwsX.js} +1 -1
  8. package/lib/{keymap-CJ1kvLR2.js → keymap-CIoVICBI.js} +2 -2
  9. package/lib/{plugin-XR7ncbnW.js → plugin-BKQsL2xn.js} +1638 -1363
  10. package/lib/{scenes-CAmWncZD.js → scenes-BRf-zMpF.js} +3 -3
  11. package/lib/{settings-sections-CakcvBv9.js → settings-sections-4gMhveN_.js} +3 -3
  12. package/lib/{statusBarSettings-BPPS_Usm.js → statusBarSettings-CnmdSHkO.js} +3 -3
  13. package/lib/{supports-color-DyChQwlI.js → supports-color-CcW-FGjr.js} +2 -2
  14. package/lib/{themes-OjcUpZzw.js → themes-Ce97Zsri.js} +12 -12
  15. package/lib/tui-compat-command-trees.js +1 -1
  16. package/lib/tui-compat-extensions.js +3 -3
  17. package/lib/tui-compat-plugin-host.js +5 -5
  18. package/lib/tui-compat-scenes.js +1 -1
  19. package/lib/tui-compat-settings-sections.js +1 -1
  20. package/lib/tui-compat-working-activity.js +4 -4
  21. package/lib/tui-compat-workspaces.js +1 -1
  22. package/lib/tui-compat.js +5 -5
  23. package/lib/{workspaces-XY7UdfXp.js → workspaces-1xrm-e3V.js} +2 -2
  24. package/package.json +29 -28
  25. package/runtime/manifest.json +2 -2
  26. package/runtime/node_modules/@deepseek-ai/dsh-agent-default-model/lib/index.js +118 -22
  27. package/runtime/node_modules/@deepseek-ai/dsh-agent-default-model/lib/types/index.d.ts +20 -3
  28. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/client.js +113 -39
  29. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/index.js +4 -0
  30. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/types/remote-events.d.ts +3 -0
  31. package/runtime/node_modules/@deepseek-ai/dsh-api-remotes/lib/types/remote-events.js +1 -0
  32. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/index.js +173 -34
  33. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/typert.host.js +48 -28
  34. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/typert.remote-client.js +24 -19
  35. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/agent.d.ts +9 -1
  36. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/agent.js +36 -6
  37. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/archived-session-gate.js +1 -1
  38. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/catalog.js +4 -1
  39. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/client/sessions/service.d.ts +1 -1
  40. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/commands.d.ts +2 -2
  41. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/commands.js +24 -8
  42. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/index.d.ts +4 -3
  43. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/index.js +31 -14
  44. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/list.d.ts +6 -0
  45. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/list.js +89 -4
  46. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/remote-events.d.ts +1 -1
  47. package/runtime/node_modules/@deepseek-ai/dsh-api-session-controller/lib/types/types.d.ts +18 -1
  48. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/client.js +30 -0
  49. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/index.js +83 -2
  50. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.host.js +104 -11
  51. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.remote-client.d.ts +5 -1
  52. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/typert.remote-client.js +78 -11
  53. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/model.d.ts +14 -1
  54. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/model.js +17 -0
  55. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/service.d.ts +15 -0
  56. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/client/service.js +12 -0
  57. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/commands.d.ts +13 -1
  58. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/commands.js +44 -2
  59. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/index.d.ts +13 -1
  60. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/index.js +22 -0
  61. package/runtime/node_modules/@deepseek-ai/dsh-api-workspace-controller/lib/types/types.d.ts +17 -0
  62. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/client.js +931 -7
  63. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/index.js +57 -19
  64. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/client/index.d.ts +5 -2
  65. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/config.d.ts +12 -0
  66. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/events.d.ts +4 -4
  67. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/lib/types/index.d.ts +3 -8
  68. package/runtime/node_modules/@deepseek-ai/dsh-client-hmr/package.json +6 -2
  69. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-commands/lib/client.js +10 -0
  70. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-deliverables/lib/types/client/present-open.d.ts +1 -1
  71. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-model-selection/lib/client.js +8 -4
  72. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-account/lib/client.js +2 -2
  73. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js +1 -1
  74. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js +3 -1
  75. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/client.js +1917 -206
  76. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/ArchivedSessions.d.ts +13 -0
  77. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/contract/slots.d.ts +4 -0
  78. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/locales.d.ts +40 -0
  79. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/recent.d.ts +11 -0
  80. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/rows/RecentList.d.ts +15 -0
  81. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/rows/Rows.d.ts +6 -1
  82. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/session-actions/DeleteSession.d.ts +24 -0
  83. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/stores.d.ts +2 -2
  84. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/lib/types/client/tree.d.ts +15 -13
  85. package/runtime/node_modules/@deepseek-ai/dsh-client-ui-workspace/package.json +3 -1
  86. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/index.js +16 -0
  87. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/typert.host.js +7 -3
  88. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/typert.remote-client.js +6 -2
  89. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/types/index.js +20 -0
  90. package/runtime/node_modules/@deepseek-ai/dsh-commands/lib/types/types.d.ts +5 -0
  91. package/runtime/node_modules/@deepseek-ai/dsh-cordis-client-runner/lib/client.js +5 -2
  92. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/index.js +4 -0
  93. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/typert.host.js +5 -5
  94. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/typert.remote-client.js +5 -5
  95. package/runtime/node_modules/@deepseek-ai/dsh-schedule/lib/types/index.js +5 -0
  96. package/runtime/node_modules/@deepseek-ai/dsh-session/lib/types/index.d.ts +21 -0
  97. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence/lib/index.js +12 -0
  98. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence/lib/types/index.d.ts +15 -0
  99. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence-jsonl/lib/index.js +95 -2
  100. package/runtime/node_modules/@deepseek-ai/dsh-session-persistence-jsonl/lib/types/index.d.ts +3 -1
  101. package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/lib/index.js +25 -1
  102. package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/lib/types/index.d.ts +1 -0
  103. package/runtime/node_modules/@deepseek-ai/dsh-session-projection-cache/package.json +2 -1
  104. package/runtime/node_modules/@deepseek-ai/dsh-session-query/lib/index.js +13 -0
  105. package/runtime/node_modules/@deepseek-ai/dsh-session-query/lib/types/observation.d.ts +5 -0
  106. package/runtime/node_modules/@deepseek-ai/dsh-session-query-sqlite/lib/index.js +26 -0
  107. package/runtime/node_modules/@deepseek-ai/dsh-session-query-sqlite/package.json +2 -1
  108. package/runtime/node_modules/@deepseek-ai/dsh-storage-domain/lib/index.js +5 -1
  109. package/runtime/node_modules/@deepseek-ai/dsh-storage-domain/lib/types/domain.d.ts +2 -2
  110. package/runtime/node_modules/@deepseek-ai/dsh-subagent/lib/types/control.d.ts +1 -1
  111. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/index.js +106 -56
  112. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/config.d.ts +2 -2
  113. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/index.d.ts +2 -1
  114. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/services/tianti/config.d.ts +4 -2
  115. package/runtime/node_modules/@deepseek-ai/dsh-tianti/lib/types/tools.d.ts +2 -2
  116. package/runtime/node_modules/@deepseek-ai/dsh-tianti/package.json +2 -0
  117. package/runtime/node_modules/@deepseek-ai/dsh-tool-cordis/lib/types/api-catalog.js +103 -12
  118. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/index.js +215 -4
  119. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/invariant.js +6 -1
  120. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/index.d.ts +31 -0
  121. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/index.js +219 -2
  122. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/spec.d.ts +4 -0
  123. package/runtime/node_modules/@deepseek-ai/dsh-workspace/lib/types/spec.js +4 -0
@@ -163,6 +163,7 @@ export async function inspectApiSession(ctx, sessionId, signal) {
163
163
  /** Owns every operation that may create, resume, or configure a Web Agent. */
164
164
  export class ApiSessionAgentController {
165
165
  ctx;
166
+ owned = new Map();
166
167
  resumes = new Map();
167
168
  creations = new Map();
168
169
  selections = new WeakMap();
@@ -170,6 +171,20 @@ export class ApiSessionAgentController {
170
171
  /** @param ctx - Host context carrying Agent, model, persistence, and Typert services. */
171
172
  constructor(ctx) {
172
173
  this.ctx = ctx;
174
+ ctx.on('session/delete-release', async ({ sessionIds }) => {
175
+ for (const id of sessionIds) {
176
+ const handle = this.owned.get(id);
177
+ if (handle?.agent.status === 'idle')
178
+ await handle.dispose();
179
+ }
180
+ });
181
+ ctx.on('agent/disposed', ({ agent }) => {
182
+ if (this.owned.get(agent.session.id)?.agent === agent)
183
+ this.owned.delete(agent.session.id);
184
+ });
185
+ ctx.effect(() => async () => {
186
+ await Promise.all([...this.owned.values()].map(handle => handle.dispose()));
187
+ }, 'api-session.ownedAgents');
173
188
  ctx.typert.lookups.configure('agent', async (sessionId) => {
174
189
  const found = await this.resolveAgent(sessionId);
175
190
  if ('error' in found)
@@ -307,6 +322,8 @@ export class ApiSessionAgentController {
307
322
  const defaultModel = this.ctx.agentDefaultModel;
308
323
  const selection = {
309
324
  get current() {
325
+ if (defaultModel.usesSharedPreference())
326
+ return defaultModel.currentSelection();
310
327
  if (picked !== undefined)
311
328
  return picked;
312
329
  const loggedHeader = agent.session.requestHeader();
@@ -445,11 +462,11 @@ export class ApiSessionAgentController {
445
462
  if (published !== undefined && hasApiSessionSubagentOwner(this.ctx, published, live)) {
446
463
  throw new ApiSessionSubagentOwnership(sessionId);
447
464
  }
448
- return (await this.ctx.agents.resume({
465
+ return (await this.remember(this.ctx.agents.resume({
449
466
  resumeSessionId: sessionId,
450
467
  agentOptions: this.agentOptions(),
451
468
  setup: composition.setup,
452
- })).agent;
469
+ }))).agent;
453
470
  }
454
471
  async createOrAdopt(sessionId, cwd, checkPersistedIdentity, presetId) {
455
472
  const attached = this.ctx.sessions.get(sessionId);
@@ -473,11 +490,11 @@ export class ApiSessionAgentController {
473
490
  const storedPreset = this.presetForObservation(observation);
474
491
  this.assertPresetUnchanged(sessionId, presetId, storedPreset);
475
492
  const composition = await this.composeAgent(storedPreset);
476
- return (await this.ctx.agents.resume({
493
+ return (await this.remember(this.ctx.agents.resume({
477
494
  resumeSessionId: sessionId,
478
495
  agentOptions: this.agentOptions(),
479
496
  setup: composition.setup,
480
- })).agent;
497
+ }))).agent;
481
498
  }
482
499
  catch (e_3) {
483
500
  env_3.error = e_3;
@@ -500,7 +517,7 @@ export class ApiSessionAgentController {
500
517
  throw new Error(`failed to ensure project directory "${cwd}": ${String(error)}`, { cause: error });
501
518
  }
502
519
  const composition = await this.composeAgent(presetId);
503
- return (await this.ctx.agents.create({
520
+ return (await this.remember(this.ctx.agents.create({
504
521
  sessionId,
505
522
  agentOptions: this.agentOptions(),
506
523
  meta: {
@@ -508,7 +525,20 @@ export class ApiSessionAgentController {
508
525
  ...(composition.agentPreset === undefined ? {} : { agentPreset: composition.agentPreset }),
509
526
  },
510
527
  setup: composition.setup,
511
- })).agent;
528
+ }))).agent;
529
+ }
530
+ /**
531
+ * Create a Web-owned Agent whose idle handle can be released before permanent deletion.
532
+ * @param options - Agent creation input shared by new Sessions and ordinary forks.
533
+ * @returns the owned Agent handle.
534
+ */
535
+ createOwned(options) {
536
+ return this.remember(this.ctx.agents.create(options));
537
+ }
538
+ async remember(pending) {
539
+ const handle = await pending;
540
+ this.owned.set(handle.agent.session.id, handle);
541
+ return handle;
512
542
  }
513
543
  agentOptions() {
514
544
  const { provider, model } = this.ctx.agentDefaultModel.currentSelection();
@@ -33,7 +33,7 @@ export const ArchivedSessionGate = {
33
33
  * @returns whether an archived Session owns the step.
34
34
  */
35
35
  export function underArchivedSession(ctx, agent) {
36
- const archived = ctx.workspaceRegistry.archivedSessionIds;
36
+ const archived = [...ctx.workspaceRegistry.archivedSessionIds, ...ctx.workspaceRegistry.deletedSessionIds];
37
37
  let header = agent.session.header;
38
38
  const visited = new Set();
39
39
  while (!visited.has(header.id)) {
@@ -57,8 +57,11 @@ export async function buildModelCatalog(ctx, defaultSelection = ctx.agentDefault
57
57
  const groups = catalog.flatMap(item => item.kind === 'group' ? [item.group] : [])
58
58
  .filter(group => group.models.length > 0);
59
59
  const savedDisplayName = ctx.agentDefaultModel.sharedDisplayName();
60
+ const processSelection = ctx.agentDefaultModel.usesSharedPreference();
61
+ const selected = processSelection ? await ctx.agentDefaultModel.resolveSelection() : defaultSelection;
60
62
  return {
61
- default: { ...defaultSelection },
63
+ default: { ...selected },
64
+ ...processSelection ? { selectionScope: 'process' } : {},
62
65
  ...savedDisplayName === undefined ? {} : { savedDisplayName },
63
66
  routableProviders: groups.map(group => group.id),
64
67
  groups,
@@ -32,7 +32,7 @@ export interface SessionSummary {
32
32
  * summary, `sessionListMetadata`, and client acceptance/running observations.
33
33
  * New Session reuses a blank one targeting the same workspace. Filtering
34
34
  * stays with the consumer: the store carries every row, while the Workspace
35
- * browser shows only the selected blank entry.
35
+ * browser separately hides Sessions without real user input.
36
36
  */
37
37
  blank: boolean;
38
38
  updatedAt: number;
@@ -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; save the default in the background.
23
+ * Validate, persist the default, and install one model selection before acknowledging success.
24
24
  * @param request - Session identity and requested model selection.
25
- * @returns the normalized selection installed for the Session, without waiting for default persistence.
25
+ * @returns the normalized selection after the shared save and Session selection succeed.
26
26
  */
27
27
  selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>;
28
28
  /**
@@ -138,9 +138,9 @@ export class SessionCommandController {
138
138
  return { sessionId, ...(agentPreset === undefined ? {} : { agentPreset }) };
139
139
  }
140
140
  /**
141
- * Validate and install one Session-local model selection; save the default in the background.
141
+ * Validate, persist the default, and install one model selection before acknowledging success.
142
142
  * @param request - Session identity and requested model selection.
143
- * @returns the normalized selection installed for the Session, without waiting for default persistence.
143
+ * @returns the normalized selection after the shared save and Session selection succeed.
144
144
  */
145
145
  async selectModel(request) {
146
146
  const agent = await this.resolveAgent(request.sessionId);
@@ -161,10 +161,20 @@ export class SessionCommandController {
161
161
  ? {}
162
162
  : { reasoningEffort: resolved.reasoningEffort }),
163
163
  };
164
- this.agents.selectForNextRequest(agent, selected);
165
- void this.ctx.agentDefaultModel.saveSelection(selected).catch((error) => {
166
- this.ctx.logger.warn(`session-controller: model selection changed for the Session but the default was not saved: ${String(error)}`);
167
- });
164
+ const info = await this.ctx.llm.resolveModelInfo(selected.provider, selected.model);
165
+ try {
166
+ await this.ctx.agentDefaultModel.saveSelection(selected, info.name);
167
+ }
168
+ catch (error) {
169
+ throw new RemoteError('session/model-save-failed', `model selection was not changed because the default could not be saved: ${String(error)}`, { provider: selected.provider, model: selected.model });
170
+ }
171
+ this.ctx.emit('api-session/model-updated');
172
+ try {
173
+ this.agents.selectForNextRequest(agent, selected);
174
+ }
175
+ catch (error) {
176
+ throw new RemoteError('session/model-install-failed', `the default model was saved, but the Session selection could not be recorded: ${String(error)}`, { provider: selected.provider, model: selected.model });
177
+ }
168
178
  return { selected: { ...selected } };
169
179
  }
170
180
  catch (error) {
@@ -245,7 +255,7 @@ export class SessionCommandController {
245
255
  const composition = await this.agents.composeAgent(this.agents.presetForObservation(source));
246
256
  try {
247
257
  const { provider, model } = this.ctx.agentDefaultModel.currentSelection();
248
- await this.ctx.agents.create({
258
+ await this.agents.createOwned({
249
259
  sessionId: childId,
250
260
  seed,
251
261
  inheritedEventCount: SessionLogOffset(boundary + 1),
@@ -310,6 +320,11 @@ export class SessionCommandController {
310
320
  try {
311
321
  const env_2 = { stack: [], error: void 0, hasError: false };
312
322
  try {
323
+ if (this.ctx.agentDefaultModel.usesSharedPreference()) {
324
+ const route = await this.ctx.agentDefaultModel.resolveSelection();
325
+ if (route.provider === '' || route.model === '')
326
+ throw new RemoteError('session/model-unavailable', 'the selected model is unavailable; select a model before sending a message', { provider: route.provider, model: route.model });
327
+ }
313
328
  if (hasImage) {
314
329
  const current = this.agents.selectionFor(agent).current;
315
330
  const model = await this.ctx.llm.resolveModelInfo(current.provider, current.model);
@@ -348,7 +363,8 @@ export class SessionCommandController {
348
363
  }
349
364
  return { accepted: true };
350
365
  };
351
- return hasImage ? this.agents.serializeImageAdmission(agent, admit) : admit();
366
+ return hasImage || this.ctx.agentDefaultModel.usesSharedPreference()
367
+ ? this.agents.serializeImageAdmission(agent, admit) : admit();
352
368
  }
353
369
  async requireModel(selection) {
354
370
  if (!await modelAvailable(this.ctx, selection)) {
@@ -92,13 +92,13 @@ 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; save the default in the background.
95
+ * Select a model after explicitly resuming the Session and awaiting default persistence.
96
96
  * @param request - Session identity and requested model selection.
97
- * @returns the normalized selection installed for the Session, without waiting for default persistence.
97
+ * @returns the normalized selection after the shared save and Session selection succeed.
98
98
  */
99
99
  selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>;
100
100
  /**
101
- * Select the first available account model after login when no provider API key is configured.
101
+ * Initialize legacy account deployments without changing a product process's cached selection.
102
102
  * @returns after saving the first available model or retaining the existing default.
103
103
  */
104
104
  initializeDefaultModel(): Promise<void>;
@@ -194,6 +194,7 @@ export declare class SessionController extends TypertRemoteService {
194
194
  * frames and optional cursorless assistant-stream frames.
195
195
  */
196
196
  follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>;
197
+ private assertNotDeleted;
197
198
  /**
198
199
  * Read all registered projections without activating an Agent.
199
200
  * @param request - Session whose current values are required.
@@ -236,6 +236,10 @@ let SessionController = (() => {
236
236
  // until it is restored; what it still runs is stopped by the owners that
237
237
  // answer the Workspace registry's archive-admission events.
238
238
  ctx.plugin(ArchivedSessionGate);
239
+ ctx.on('session/deleted', (ids) => {
240
+ for (const id of ids)
241
+ ctx.emit('api-session/removed', id);
242
+ });
239
243
  ctx.on('session/created', (session) => {
240
244
  ctx.emit('api-session/added', this.listState.summaryFor(session));
241
245
  });
@@ -338,22 +342,26 @@ let SessionController = (() => {
338
342
  * @param request - requested identity, location, and Agent preset.
339
343
  * @returns the Session identity and resolved preset when configured.
340
344
  */
341
- create(request) {
345
+ async create(request) {
346
+ if (request.sessionId !== undefined)
347
+ await this.assertNotDeleted(request.sessionId);
342
348
  return this.commands.create(request);
343
349
  }
344
350
  /**
345
- * Select one Session-local model after explicitly resuming the Session; save the default in the background.
351
+ * Select a model after explicitly resuming the Session and awaiting default persistence.
346
352
  * @param request - Session identity and requested model selection.
347
- * @returns the normalized selection installed for the Session, without waiting for default persistence.
353
+ * @returns the normalized selection after the shared save and Session selection succeed.
348
354
  */
349
355
  selectModel(request) {
350
- return this.commands.selectModel(request);
356
+ return this.assertNotDeleted(request.sessionId).then(() => this.commands.selectModel(request));
351
357
  }
352
358
  /**
353
- * Select the first available account model after login when no provider API key is configured.
359
+ * Initialize legacy account deployments without changing a product process's cached selection.
354
360
  * @returns after saving the first available model or retaining the existing default.
355
361
  */
356
362
  async initializeDefaultModel() {
363
+ if (this.ctx.agentDefaultModel.usesSharedPreference())
364
+ return;
357
365
  const provider = 'deepseek-account';
358
366
  if (await hasProviderApiKey(this.ctx))
359
367
  return;
@@ -364,7 +372,8 @@ let SessionController = (() => {
364
372
  const selection = { provider, model: model.id,
365
373
  ...model.reasoning?.defaultEffort === undefined ? {} : { reasoningEffort: ReasoningEffortId(model.reasoning.defaultEffort) },
366
374
  };
367
- await this.ctx.agentDefaultModel.saveSelection(selection);
375
+ await this.ctx.agentDefaultModel.saveSelection(selection, model.name);
376
+ this.ctx.emit('api-session/model-updated');
368
377
  }
369
378
  /**
370
379
  * Describe every currently routable model for Host-generation selectors.
@@ -455,7 +464,7 @@ let SessionController = (() => {
455
464
  * @returns the accepted title and durable event sequence.
456
465
  */
457
466
  rename(request) {
458
- return this.commands.rename(request);
467
+ return this.assertNotDeleted(request.sessionId).then(() => this.commands.rename(request));
459
468
  }
460
469
  /**
461
470
  * Fork one cold-readable exact event prefix into a new Session. An omitted
@@ -465,7 +474,7 @@ let SessionController = (() => {
465
474
  * @returns the new Session identity.
466
475
  */
467
476
  fork(request) {
468
- return this.commands.fork(request);
477
+ return this.assertNotDeleted(request.sessionId).then(() => this.commands.fork(request));
469
478
  }
470
479
  /**
471
480
  * Admit one prompt after explicitly resuming its Session.
@@ -475,7 +484,7 @@ let SessionController = (() => {
475
484
  */
476
485
  prompt(request, signal) {
477
486
  signal.throwIfAborted();
478
- return this.commands.prompt(request);
487
+ return this.assertNotDeleted(request.sessionId).then(() => this.commands.prompt(request));
479
488
  }
480
489
  /**
481
490
  * Read one image proven reachable from the addressed Session log.
@@ -483,7 +492,7 @@ let SessionController = (() => {
483
492
  * @returns the durable attachment reference and base64-encoded bytes.
484
493
  */
485
494
  attachment(request) {
486
- return this.commands.attachment(request);
495
+ return this.assertNotDeleted(request.sessionId).then(() => this.commands.attachment(request));
487
496
  }
488
497
  /**
489
498
  * Mutate one still-pending queue occurrence, resuming a cold Agent first.
@@ -491,7 +500,7 @@ let SessionController = (() => {
491
500
  * @returns acknowledgement that the queue mutation was applied.
492
501
  */
493
502
  updateQueue(request) {
494
- return this.commands.updateQueue(request);
503
+ return this.assertNotDeleted(request.sessionId).then(() => this.commands.updateQueue(request));
495
504
  }
496
505
  /**
497
506
  * Cancel one active Agent turn without dropping its pending inbox.
@@ -508,7 +517,7 @@ let SessionController = (() => {
508
517
  * @returns one chronological page.
509
518
  */
510
519
  page(request, signal) {
511
- return this.history.page(request, signal);
520
+ return this.assertNotDeleted(request.address.kind === 'session' ? request.address.sessionId : request.address.childSessionId).then(() => this.history.page(request, signal));
512
521
  }
513
522
  /**
514
523
  * Follow one Session log from its opening or resume cursor.
@@ -517,8 +526,15 @@ let SessionController = (() => {
517
526
  * @returns a complete opening snapshot followed by gap-free durable event
518
527
  * frames and optional cursorless assistant-stream frames.
519
528
  */
520
- follow(request, signal) {
521
- return this.history.follow(request, signal);
529
+ async *follow(request, signal) {
530
+ await this.assertNotDeleted(request.address.kind === 'session' ? request.address.sessionId : request.address.childSessionId);
531
+ yield* this.history.follow(request, signal);
532
+ }
533
+ async assertNotDeleted(sessionId) {
534
+ await this.ctx.workspaceRegistry.refresh();
535
+ if (this.ctx.workspaceRegistry.deletedSessionIds.includes(sessionId)) {
536
+ throw new RemoteError('session/not-found', 'Session has been permanently deleted', { sessionId });
537
+ }
522
538
  }
523
539
  /**
524
540
  * Read all registered projections without activating an Agent.
@@ -531,6 +547,7 @@ let SessionController = (() => {
531
547
  if (sessionId.length === 0) {
532
548
  throw new RemoteError('gateway/bad-request', 'sessionId must not be empty', {});
533
549
  }
550
+ await this.assertNotDeleted(sessionId);
534
551
  try {
535
552
  const env_2 = { stack: [], error: void 0, hasError: false };
536
553
  try {
@@ -19,6 +19,10 @@ export declare function truncateUnicodeCodePoints(value: string, maximum: number
19
19
  /** Owns list projection registration, bounded cold summaries, and authorized search. */
20
20
  export declare class ApiSessionList {
21
21
  private readonly ctx;
22
+ private readonly backfillAbort;
23
+ private readonly backfills;
24
+ private readonly checkedRevisions;
25
+ private backfill;
22
26
  /** @param ctx - Host context carrying Session, query, persistence, and projection services. */
23
27
  constructor(ctx: Context);
24
28
  /**
@@ -34,6 +38,8 @@ export declare class ApiSessionList {
34
38
  */
35
39
  list(signal?: AbortSignal): Promise<SessionSummary[]>;
36
40
  private summarizeCold;
41
+ /** Serialize missing summaries outside list latency; unreadable history stays unknown. */
42
+ private backfillCold;
37
43
  /**
38
44
  * Search current visible message content without activating any matching Session.
39
45
  * @param query - literal message-content query.
@@ -1,4 +1,5 @@
1
1
  /** Cold-safe Session list and search projection. */
2
+ import { SessionLogOffset } from '@deepseek-ai/dsh-session';
2
3
  import { SessionQueryError } from '@deepseek-ai/dsh-session-query';
3
4
  import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
4
5
  import { z } from 'zod';
@@ -7,6 +8,7 @@ const SEARCH_PROVIDER_CALL_LIMIT = 100;
7
8
  const SESSION_SEARCH_QUERY_MAX_CHARS = 500;
8
9
  const MESSAGE_TYPES = new Set(['user/message', 'assistant/message']);
9
10
  const sessionListMetadataSchema = z.object({
11
+ hasUserPrompt: z.boolean().default(false),
10
12
  blank: z.boolean(),
11
13
  lastPromptAt: z.number().nullable(),
12
14
  });
@@ -26,12 +28,20 @@ const imageLimitsSchema = z.object({
26
28
  */
27
29
  export function applySessionListMetadata(state, event) {
28
30
  const blank = state.blank && event.type !== 'turn/start';
31
+ const hasUserPrompt = state.hasUserPrompt === true || hasHumanInput(event);
29
32
  const lastPromptAt = event.type === 'user/message' && event.data.source.kind === 'user'
30
33
  ? event.time
31
34
  : state.lastPromptAt;
32
- return blank === state.blank && lastPromptAt === state.lastPromptAt
35
+ return blank === state.blank && lastPromptAt === state.lastPromptAt && hasUserPrompt === state.hasUserPrompt
33
36
  ? state
34
- : { blank, lastPromptAt };
37
+ : { blank, lastPromptAt, hasUserPrompt };
38
+ }
39
+ /** User-origin text or media is conversation input; injected context is not. */
40
+ function hasHumanInput(event) {
41
+ const messages = event.type === 'user/message' ? [{ ...event.data, role: 'user' }]
42
+ : event.type === 'agent/inbox/spliced' ? event.data.inserted : [];
43
+ return messages.some(message => message.role === 'user' && message.source.kind === 'user'
44
+ && message.content.some(block => block.type !== 'text' || block.text.trim() !== ''));
35
45
  }
36
46
  /**
37
47
  * Return the longest prefix containing at most `maximum` Unicode code points.
@@ -53,16 +63,28 @@ export function truncateUnicodeCodePoints(value, maximum) {
53
63
  /** Owns list projection registration, bounded cold summaries, and authorized search. */
54
64
  export class ApiSessionList {
55
65
  ctx;
66
+ backfillAbort = new AbortController();
67
+ backfills = new Set();
68
+ checkedRevisions = new Map();
69
+ backfill = Promise.resolve();
56
70
  /** @param ctx - Host context carrying Session, query, persistence, and projection services. */
57
71
  constructor(ctx) {
58
72
  this.ctx = ctx;
59
73
  ctx.sessionProjections.register({
60
74
  key: 'sessionListMetadata',
61
75
  stateSchema: sessionListMetadataSchema,
62
- init: () => ({ blank: true, lastPromptAt: null }),
76
+ init: () => ({ blank: true, lastPromptAt: null, hasUserPrompt: false }),
63
77
  apply: applySessionListMetadata,
64
78
  wire: { viewSchema: sessionListMetadataSchema, view: state => state },
65
- stateVersion: 1,
79
+ stateVersion: 2,
80
+ });
81
+ ctx.effect(() => async () => {
82
+ this.backfillAbort.abort();
83
+ await this.backfill;
84
+ }, 'session-list: stop summary backfill');
85
+ ctx.on('session/event', (session, event) => {
86
+ if (hasHumanInput(event))
87
+ ctx.emit('api-session/added', this.summaryFor(session));
66
88
  });
67
89
  ctx.inject(['attachments'], (attachmentCtx) => {
68
90
  ctx.sessionProjections.register({
@@ -104,6 +126,11 @@ export class ApiSessionList {
104
126
  async list(signal) {
105
127
  signal?.throwIfAborted();
106
128
  const records = await this.ctx.sessionQuery.listSessions(signal);
129
+ const listedIds = new Set(records.map(record => record.header.id));
130
+ for (const id of this.checkedRevisions.keys()) {
131
+ if (!listedIds.has(id))
132
+ this.checkedRevisions.delete(id);
133
+ }
107
134
  signal?.throwIfAborted();
108
135
  const items = [];
109
136
  const cold = [];
@@ -128,6 +155,8 @@ export class ApiSessionList {
128
155
  async summarizeCold(header) {
129
156
  const projections = await this.projectionsFor(header, undefined);
130
157
  const metadata = projections?.values.sessionListMetadata;
158
+ if (metadata?.hasUserPrompt !== true)
159
+ this.backfillCold(header.id);
131
160
  return {
132
161
  sessionId: header.id,
133
162
  updatedAt: updatedAt(header, metadata),
@@ -139,6 +168,62 @@ export class ApiSessionList {
139
168
  ...(projections === undefined ? {} : { projections }),
140
169
  };
141
170
  }
171
+ /** Serialize missing summaries outside list latency; unreadable history stays unknown. */
172
+ backfillCold(sessionId) {
173
+ if (this.backfillAbort.signal.aborted || this.backfills.has(sessionId))
174
+ return;
175
+ const persistence = this.ctx.get('sessionPersistence');
176
+ const cache = this.ctx.get('sessionProjectionCache');
177
+ if (persistence === undefined || cache === undefined)
178
+ return;
179
+ this.backfills.add(sessionId);
180
+ this.backfill = this.backfill.then(async () => {
181
+ const signal = this.backfillAbort.signal;
182
+ signal.throwIfAborted();
183
+ const live = this.ctx.sessions.get(sessionId);
184
+ if (live !== undefined) {
185
+ this.ctx.emit('api-session/added', this.summaryFor(live));
186
+ return;
187
+ }
188
+ const before = await persistence.stat(sessionId, { signal });
189
+ if (before === undefined || before.revision === this.checkedRevisions.get(sessionId))
190
+ return;
191
+ const handle = await persistence.open(sessionId, 'read', { signal });
192
+ let snapshot;
193
+ try {
194
+ const read = await handle.read(SessionLogOffset(0), undefined, { signal });
195
+ signal.throwIfAborted();
196
+ const after = await persistence.stat(sessionId, { signal });
197
+ if (after?.revision !== before.revision || after.header.createdAt !== handle.header.createdAt)
198
+ return;
199
+ snapshot = await cache.coldSnapshot(handle.header, handle.inheritedEventCount, read.events);
200
+ this.checkedRevisions.set(sessionId, before.revision);
201
+ }
202
+ finally {
203
+ await handle.close();
204
+ }
205
+ signal.throwIfAborted();
206
+ const attached = this.ctx.sessions.get(sessionId);
207
+ if (attached !== undefined) {
208
+ this.ctx.emit('api-session/added', this.summaryFor(attached));
209
+ return;
210
+ }
211
+ const stat = await persistence.stat(sessionId, { signal });
212
+ if (stat?.revision !== before.revision)
213
+ return;
214
+ const projections = hintsOf('cached', snapshot);
215
+ this.ctx.emit('api-session/added', {
216
+ sessionId, updatedAt: updatedAt(handle.header, snapshot.values.sessionListMetadata),
217
+ agentAvailable: false, running: false,
218
+ blank: snapshot.values.sessionListMetadata?.blank ?? false,
219
+ ...listFields(handle.header), ...projections === undefined ? {} : { projections },
220
+ });
221
+ }).catch((error) => {
222
+ if (!this.backfillAbort.signal.aborted) {
223
+ this.ctx.logger.warn(`api-session.list: summary backfill for "${sessionId}" failed: ${String(error)}`);
224
+ }
225
+ }).finally(() => { this.backfills.delete(sessionId); });
226
+ }
142
227
  /**
143
228
  * Search current visible message content without activating any matching Session.
144
229
  * @param query - literal message-content query.
@@ -1,5 +1,5 @@
1
1
  /** Session Controller events available to a Remote Event assembly. */
2
- type SessionControllerRemoteEvent = 'api-session/activity' | 'api-session/added' | 'api-session/error' | 'api-session/removed' | 'api-session/status';
2
+ type SessionControllerRemoteEvent = 'api-session/activity' | 'api-session/added' | 'api-session/error' | 'api-session/model-updated' | 'api-session/removed' | 'api-session/status';
3
3
  declare module '@deepseek-ai/dsh-typert-protocol' {
4
4
  interface TypertRemoteEventSelection extends Record<SessionControllerRemoteEvent, true> {
5
5
  }
@@ -37,6 +37,8 @@ declare module '@deepseek-ai/dsh-session/types' {
37
37
  }
38
38
  /** Persisted hints used to summarize a cold Session. */
39
39
  export interface SessionListMetadata {
40
+ /** Real user input exists in this prefix; absent in older cached views. */
41
+ readonly hasUserPrompt?: boolean;
40
42
  /** Whether the folded prefix contains no turn. */
41
43
  readonly blank: boolean;
42
44
  /** Latest human-authored prompt time in the folded prefix. */
@@ -144,7 +146,9 @@ export interface ModelCatalogFailure {
144
146
  /** Host-generation model catalog and the default used by unconfigured Sessions. */
145
147
  export interface ModelCatalog {
146
148
  readonly default: ModelSelection;
147
- /** Latest shared display name; presentation may use it only for the exact provider/model pair. */
149
+ /** Product profiles use the Host process cache rather than a Session's historical route. */
150
+ readonly selectionScope?: 'process';
151
+ /** Process-cached display name; presentation may use it only for the exact provider/model pair. */
148
152
  readonly savedDisplayName?: {
149
153
  readonly provider: string;
150
154
  readonly model: string;
@@ -197,6 +201,14 @@ declare module '@deepseek-ai/dsh-typert-protocol' {
197
201
  readonly provider: string;
198
202
  readonly model: string;
199
203
  };
204
+ 'session/model-save-failed': {
205
+ readonly provider: string;
206
+ readonly model: string;
207
+ };
208
+ 'session/model-install-failed': {
209
+ readonly provider: string;
210
+ readonly model: string;
211
+ };
200
212
  'session/conflict': {
201
213
  readonly sessionId: SessionId;
202
214
  readonly requestedCwd: string;
@@ -555,6 +567,11 @@ export type SessionControlFrame = {
555
567
  } & SessionProjectionUpdate);
556
568
  declare module '@deepseek-ai/cordis' {
557
569
  interface Events {
570
+ /**
571
+ * The Host process committed a user-selected default; browser catalogs must reload from that process.
572
+ * @mode emit
573
+ */
574
+ 'api-session/model-updated'(): void;
558
575
  /**
559
576
  * A Session became visible or its Agent was created or disposed.
560
577
  * Consumers upsert the summary and replace its current running and availability state.
@@ -153,6 +153,26 @@ window.__ModuleLoader__.load({
153
153
  return result;
154
154
  }
155
155
  /**
156
+ * Request the Host's deletion scope for an archived root.
157
+ * @param sessionId - archived root to preview.
158
+ * @returns the Host deletion preview.
159
+ */
160
+ sessionDeletionScope(sessionId) {
161
+ return this.remote.sessionDeletionScope({ sessionId });
162
+ }
163
+ /**
164
+ * Request permanent deletion of exactly the confirmed Session scope.
165
+ * @param sessionId - archived root.
166
+ * @param expectedSessionIds - exact scope confirmed by the user.
167
+ * @returns the Host deletion result.
168
+ */
169
+ deleteSession(sessionId, expectedSessionIds) {
170
+ return this.remote.deleteSession({
171
+ sessionId,
172
+ expectedSessionIds
173
+ });
174
+ }
175
+ /**
156
176
  * Pin one Session and install the returned complete pin set.
157
177
  * A reply superseded by a later pin request or a pushed set installs nothing.
158
178
  * @param sessionId - Session to pin.
@@ -421,6 +441,16 @@ window.__ModuleLoader__.load({
421
441
  const result = await this.model.unarchiveSession(sessionId);
422
442
  if (!result.ok) throw commandError("session unarchive", result.error);
423
443
  }
444
+ async sessionDeletionScope(sessionId) {
445
+ const result = await this.model.sessionDeletionScope(sessionId);
446
+ if (!result.ok) throw new WorkspaceArchiveError(result.error);
447
+ return result.value.sessionIds;
448
+ }
449
+ async deleteSession(sessionId, expectedSessionIds) {
450
+ const result = await this.model.deleteSession(sessionId, expectedSessionIds);
451
+ if (!result.ok) throw new WorkspaceArchiveError(result.error);
452
+ return result.value.sessionIds;
453
+ }
424
454
  async pinSession(sessionId) {
425
455
  const result = await this.model.pinSession(sessionId);
426
456
  if (!result.ok) throw commandError("session pin", result.error);