@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
@@ -4,9 +4,62 @@
4
4
  * domain data form.
5
5
  * @module @deepseek-ai/dsh-workspace
6
6
  */
7
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
8
+ if (value !== null && value !== void 0) {
9
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
10
+ var dispose, inner;
11
+ if (async) {
12
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
13
+ dispose = value[Symbol.asyncDispose];
14
+ }
15
+ if (dispose === void 0) {
16
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
17
+ dispose = value[Symbol.dispose];
18
+ if (async) inner = dispose;
19
+ }
20
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
21
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
22
+ env.stack.push({ value: value, dispose: dispose, async: async });
23
+ }
24
+ else if (async) {
25
+ env.stack.push({ async: true });
26
+ }
27
+ return value;
28
+ };
29
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
30
+ return function (env) {
31
+ function fail(e) {
32
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
33
+ env.hasError = true;
34
+ }
35
+ var r, s = 0;
36
+ function next() {
37
+ while (r = env.stack.pop()) {
38
+ try {
39
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
40
+ if (r.dispose) {
41
+ var result = r.dispose.call(r.value);
42
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
43
+ }
44
+ else s |= 1;
45
+ }
46
+ catch (e) {
47
+ fail(e);
48
+ }
49
+ }
50
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
51
+ if (env.hasError) throw env.error;
52
+ }
53
+ return next();
54
+ };
55
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
56
+ var e = new Error(message);
57
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
58
+ });
7
59
  import { randomUUID } from 'node:crypto';
8
60
  import { mkdir, stat } from 'node:fs/promises';
9
61
  import { Service } from '@deepseek-ai/cordis';
62
+ import { SessionAlreadyOwnedError } from '@deepseek-ai/dsh-session-persistence';
10
63
  import { WorkspaceEntity } from "./entity.js";
11
64
  export { WorkspaceMoveInvalidError } from "./entity.js";
12
65
  import { defaultWorkspaceTitle, fullyQualifiedWorkspacePath, realpathNormalize } from "./paths.js";
@@ -69,6 +122,21 @@ export class WorkspaceArchivedSessionPinError extends Error {
69
122
  this.name = 'WorkspaceArchivedSessionPinError';
70
123
  }
71
124
  }
125
+ /** Permanent deletion requires an archived root and no active Session ownership. */
126
+ export class WorkspaceSessionDeletionError extends Error {
127
+ sessionId;
128
+ reason;
129
+ /**
130
+ * @param sessionId - identity whose lifecycle prevents the requested operation.
131
+ * @param reason - stable refusal discriminant shared by the Web and TUI.
132
+ */
133
+ constructor(sessionId, reason) {
134
+ super(`cannot delete session '${sessionId}': ${reason}`);
135
+ this.sessionId = sessionId;
136
+ this.reason = reason;
137
+ this.name = 'WorkspaceSessionDeletionError';
138
+ }
139
+ }
72
140
  /** A workspace reorder named a source or anchor absent from the durable registry order. */
73
141
  export class WorkspaceOrderInvalidError extends Error {
74
142
  workspaceId;
@@ -134,6 +202,7 @@ export class WorkspaceRegistry extends Service {
134
202
  await domain.refresh();
135
203
  this.state = domain.global.get();
136
204
  await this.recoverPendingMutation();
205
+ await this.recoverSessionDeletion();
137
206
  this.validateStoredState(this.requireState());
138
207
  if (!this.requireState().initialized) {
139
208
  const headers = await this.listStoredHeaders();
@@ -296,6 +365,144 @@ export class WorkspaceRegistry extends Service {
296
365
  get archivedSessionIds() {
297
366
  return this.requireState().archivedSessionIds;
298
367
  }
368
+ /** Permanently deleted identities retained for cross-process invalidation and stale-operation refusal. */
369
+ get deletedSessionIds() {
370
+ return this.requireState().deletedSessionIds ?? [];
371
+ }
372
+ /**
373
+ * Permanently delete an archived, inactive root and its owned subagent descendants.
374
+ * Independent forks are preserved. Reserve every writer before recording a recovery
375
+ * marker; a failure after that marker is retried on refresh or restart.
376
+ * @param sessionId - archived root identity; repeated deletion is idempotent.
377
+ * @param expectedIds - identities confirmed by the user; a changed scope refuses deletion.
378
+ * @returns deleted root and descendant identities after durable cleanup.
379
+ */
380
+ deleteSession(sessionId, expectedIds) {
381
+ return this.enqueueOperation(async () => {
382
+ const env_1 = { stack: [], error: void 0, hasError: false };
383
+ try {
384
+ if (this.deletedSessionIds.includes(sessionId))
385
+ return [sessionId];
386
+ if (!this.archivedSessionIds.includes(sessionId))
387
+ throw new WorkspaceSessionDeletionError(sessionId, 'not-archived');
388
+ const ids = new Set(await this.collectSessionDeletionScope(sessionId));
389
+ if (expectedIds !== undefined && (expectedIds.length !== ids.size
390
+ || new Set(expectedIds).size !== ids.size || expectedIds.some(id => !ids.has(id)))) {
391
+ throw new WorkspaceSessionDeletionError(sessionId, 'scope-changed');
392
+ }
393
+ for (const id of ids) {
394
+ const activity = await this.ctx.waterfall('workspace/session-activity', { sessionId: id }, () => Promise.resolve([]));
395
+ if (activity.length > 0)
396
+ throw new WorkspaceActiveSessionError(id, activity);
397
+ }
398
+ const targets = [...ids];
399
+ await this.ctx.parallel('session/delete-release', { sessionIds: targets });
400
+ for (const id of ids) {
401
+ if (this.ctx.get('sessions')?.get(id) !== undefined)
402
+ throw new WorkspaceSessionDeletionError(id, 'in-use');
403
+ }
404
+ const reservation = __addDisposableResource(env_1, await this.ctx.sessionPersistence.prepareDeletion(targets).catch((error) => {
405
+ if (error instanceof SessionAlreadyOwnedError)
406
+ throw new WorkspaceSessionDeletionError(sessionId, 'in-use');
407
+ throw error;
408
+ }), true);
409
+ await this.setState({ ...this.requireState(), pendingSessionDeletion: targets });
410
+ await reservation.commit();
411
+ await this.finishSessionDeletion(targets);
412
+ return targets;
413
+ }
414
+ catch (e_1) {
415
+ env_1.error = e_1;
416
+ env_1.hasError = true;
417
+ }
418
+ finally {
419
+ const result_1 = __disposeResources(env_1);
420
+ if (result_1)
421
+ await result_1;
422
+ }
423
+ });
424
+ }
425
+ /**
426
+ * Preview the root and owned descendants without opening their histories.
427
+ * @param sessionId - archived root to preview.
428
+ * @returns identities to show in the deletion confirmation.
429
+ */
430
+ sessionDeletionScope(sessionId) {
431
+ return this.enqueueOperation(async () => {
432
+ if (!this.archivedSessionIds.includes(sessionId))
433
+ throw new WorkspaceSessionDeletionError(sessionId, 'not-archived');
434
+ return this.collectSessionDeletionScope(sessionId);
435
+ });
436
+ }
437
+ async collectSessionDeletionScope(sessionId) {
438
+ const headers = await this.listStoredHeaders();
439
+ const ids = new Set([sessionId]);
440
+ for (let changed = true; changed;) {
441
+ changed = false;
442
+ for (const header of headers) {
443
+ if (header.origin === 'subagent' && header.parentSession !== undefined
444
+ && ids.has(header.parentSession) && !ids.has(header.id)) {
445
+ ids.add(header.id);
446
+ changed = true;
447
+ }
448
+ }
449
+ }
450
+ return [...ids];
451
+ }
452
+ async recoverSessionDeletion() {
453
+ const env_2 = { stack: [], error: void 0, hasError: false };
454
+ try {
455
+ const ids = this.requireState().pendingSessionDeletion;
456
+ if (ids === undefined)
457
+ return;
458
+ const reservation = __addDisposableResource(env_2, await this.ctx.sessionPersistence.prepareDeletion(ids), true);
459
+ await reservation.commit();
460
+ await this.finishSessionDeletion(ids);
461
+ }
462
+ catch (e_2) {
463
+ env_2.error = e_2;
464
+ env_2.hasError = true;
465
+ }
466
+ finally {
467
+ const result_2 = __disposeResources(env_2);
468
+ if (result_2)
469
+ await result_2;
470
+ }
471
+ }
472
+ notifyDeleted(ids) {
473
+ try {
474
+ this.ctx.emit('session/deleted', ids);
475
+ }
476
+ catch (error) {
477
+ this.ctx.logger.warn(`workspace: deletion listener failed: ${String(error)}`);
478
+ }
479
+ }
480
+ async finishSessionDeletion(ids) {
481
+ await this.ctx.parallel('session/deleting', { sessionIds: ids });
482
+ const removed = new Set(ids);
483
+ for (const [id, record] of this.requireTable().entries()) {
484
+ if (record.sessionIds.some(member => removed.has(member))) {
485
+ await this.requireTable().update(id, current => ({
486
+ ...current, sessionIds: current.sessionIds.filter(member => !removed.has(member)), updatedAt: new Date().toISOString(),
487
+ }));
488
+ }
489
+ }
490
+ const state = this.requireState();
491
+ await this.setState({
492
+ ...state,
493
+ archivedSessionIds: state.archivedSessionIds.filter(id => !removed.has(id)),
494
+ pinnedSessionIds: state.pinnedSessionIds.filter(id => !removed.has(id)),
495
+ deletedSessionIds: [...new Set([...(state.deletedSessionIds ?? []), ...ids])],
496
+ pendingSessionDeletion: undefined,
497
+ });
498
+ for (const id of ids) {
499
+ this.headers.delete(id);
500
+ this.sessionPaths.delete(id);
501
+ this.invalidSessionPaths.delete(id);
502
+ }
503
+ this.rebuildEntities();
504
+ this.notifyDeleted(ids);
505
+ }
299
506
  /**
300
507
  * Archive one session durably. The session must exist (live or in session
301
508
  * persistence); its workspace accounting — or lack of one — is irrelevant.
@@ -349,6 +556,8 @@ export class WorkspaceRegistry extends Service {
349
556
  */
350
557
  unarchiveSession(sessionId) {
351
558
  return this.enqueueOperation(async () => {
559
+ if (this.deletedSessionIds.includes(sessionId))
560
+ throw new WorkspaceSessionDeletionError(sessionId, 'deleted');
352
561
  // The chain slot serializes against every other registry write, so this
353
562
  // check-then-write pair cannot interleave with a concurrent archive.
354
563
  const state = this.requireState();
@@ -422,6 +631,8 @@ export class WorkspaceRegistry extends Service {
422
631
  * masquerade as an unknown session.
423
632
  */
424
633
  async sessionKnown(id) {
634
+ if (this.deletedSessionIds.includes(id))
635
+ return false;
425
636
  if (this.ctx.get('sessions')?.get(id) !== undefined)
426
637
  return true;
427
638
  if (this.headers.has(id))
@@ -766,13 +977,14 @@ export class WorkspaceRegistry extends Service {
766
977
  /** Every stored session's header, projected from the persistence snapshot listing. */
767
978
  async listStoredHeaders() {
768
979
  const snapshots = await this.ctx.sessionPersistence.list();
769
- return snapshots.map(snapshot => snapshot.header);
980
+ const deleted = new Set(this.deletedSessionIds);
981
+ return snapshots.map(snapshot => snapshot.header).filter(header => !deleted.has(header.id));
770
982
  }
771
983
  async indexLiveSessions() {
772
984
  const sessions = this.ctx.get('sessions');
773
985
  if (sessions === undefined)
774
986
  return;
775
- await this.indexHeaders(sessions.list().map(session => session.header));
987
+ await this.indexHeaders(sessions.list().filter(session => !this.deletedSessionIds.includes(session.id)).map(session => session.header));
776
988
  }
777
989
  reportFilteredCandidates() {
778
990
  for (const entity of this.entities.values()) {
@@ -829,6 +1041,7 @@ export class WorkspaceRegistry extends Service {
829
1041
  throw new Error('workspace registry is not started yet');
830
1042
  return await domain.exclusive(async () => {
831
1043
  const before = JSON.stringify([...this.entities.values()].map(entity => [entity.id, entity.sessionIds]));
1044
+ const previousDeleted = new Set(this.deletedSessionIds);
832
1045
  await domain.refresh();
833
1046
  const changed = this.snapshotIdentity() !== this.refreshedState;
834
1047
  this.state = domain.global.get();
@@ -839,6 +1052,10 @@ export class WorkspaceRegistry extends Service {
839
1052
  this.rebuildEntities();
840
1053
  }
841
1054
  await this.recoverPendingMutation();
1055
+ await this.recoverSessionDeletion();
1056
+ const newlyDeleted = this.deletedSessionIds.filter(id => !previousDeleted.has(id));
1057
+ if (newlyDeleted.length > 0)
1058
+ this.notifyDeleted(newlyDeleted);
842
1059
  this.validateStoredState(this.requireState());
843
1060
  const after = JSON.stringify([...this.entities.values()].map(entity => [entity.id, entity.sessionIds]));
844
1061
  if (changed || before !== after) {
@@ -46,6 +46,8 @@ export declare const workspaceDomainState: z.ZodObject<{
46
46
  operation: z.ZodLiteral<"delete">;
47
47
  workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
48
48
  }, z.core.$strip>], "operation">>;
49
+ deletedSessionIds: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
50
+ pendingSessionDeletion: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
49
51
  }, z.core.$strip>;
50
52
  /** Durable registry state inferred from {@link workspaceDomainState}. */
51
53
  export type WorkspaceDomainState = z.infer<typeof workspaceDomainState>;
@@ -72,6 +74,8 @@ export declare const workspaceDomainSpec: {
72
74
  operation: z.ZodLiteral<"delete">;
73
75
  workspaceId: z.ZodPipe<z.ZodString, z.ZodTransform<WorkspaceId, string>>;
74
76
  }, z.core.$strip>], "operation">>;
77
+ deletedSessionIds: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
78
+ pendingSessionDeletion: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<SessionId, string>>>>;
75
79
  }, z.core.$strip>;
76
80
  initial: {
77
81
  initialized: boolean;
@@ -51,6 +51,10 @@ export const workspaceDomainState = z.object({
51
51
  archivedSessionIds: z.array(sessionId).default([]),
52
52
  pinnedSessionIds: z.array(sessionId).default([]),
53
53
  pendingMutation: workspacePendingMutation.optional(),
54
+ /** Payload deletion committed identities; old processes must never restore these Sessions. */
55
+ deletedSessionIds: z.array(sessionId).optional(),
56
+ /** Complete deletion scope retained until payloads and secondary owners finish cleanup. */
57
+ pendingSessionDeletion: z.array(sessionId).optional(),
54
58
  });
55
59
  /**
56
60
  * The workspace domain spec: one `workspaces` table keyed by