@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
@@ -1,10 +1,10 @@
1
1
  /** Model-facing Tianti queries and review proposals; human commands execute writes. */
2
2
  import type { Context } from '@deepseek-ai/cordis';
3
3
  import { TiantiRuntime } from './runtime.ts';
4
- /** Install reversible model tools with invocation-owned cancellation.
4
+ /** Install model tools for one enabled Tianti activation; its owner unregisters them on off.
5
5
  * @param ctx Harness tool registry.
6
6
  * @param runtime Plugin-owned endpoint and switch state.
7
- * @param signal Plugin lifetime cancellation.
7
+ * @param signal Cancellation for this activation, aborted on off or plugin disposal.
8
8
  * @param track Track active work until plugin disposal settles.
9
9
  * @returns Registration disposers, removed before active work is drained.
10
10
  */
@@ -35,6 +35,8 @@
35
35
  "@deepseek-ai/dsh-home-paths": "workspace:*"
36
36
  },
37
37
  "devDependencies": {
38
+ "@deepseek-ai/dsh-agent-loop": "workspace:*",
39
+ "@deepseek-ai/dsh-session-projection": "workspace:*",
38
40
  "@deepseek-ai/dsh-bash-local": "workspace:*",
39
41
  "@deepseek-ai/dsh-subprocess-local": "workspace:*",
40
42
  "@deepseek-ai/cordis": "workspace:~",
@@ -20,20 +20,32 @@ export const SERVICE_API = [
20
20
  methods: [
21
21
  {
22
22
  signature: 'currentSelection(): ModelSelection',
23
- description: 'Read the current default model selection.',
23
+ description: 'Read the current default model selection. A first launch without a valid preference adopts the first complete Config default when it arrives; that process-local choice then stays cached.',
24
24
  parameters: [],
25
25
  returns: 'a detached provider, model, and optional reasoning selection.',
26
26
  },
27
+ {
28
+ signature: 'usesSharedPreference(): boolean',
29
+ description: 'Whether this product uses a process-local selection initialized from Home at startup.',
30
+ parameters: [],
31
+ returns: 'true for the integrated ChatCode CLI and Web profiles.',
32
+ },
33
+ {
34
+ signature: 'async resolveSelection(): Promise<ModelSelection>',
35
+ description: 'Resolve the cached route against the live catalog without reading or writing preferences. A first launch without a valid preference may adopt an advertised fallback until its first available default is resolved. Saved or initialized choices never switch implicitly. Catalog errors preserve the candidate for retry.',
36
+ parameters: [],
37
+ returns: 'the exact available selection, an unverified candidate, or an empty provider/model pair.',
38
+ },
27
39
  {
28
40
  signature: 'sharedDisplayName(): { provider: string; model: string; name: string } | undefined',
29
- description: 'Read the latest shared display name without changing this process\'s model selection.',
41
+ description: 'Read this process\'s cached display name without observing another process\'s saves.',
30
42
  parameters: [],
31
43
  returns: 'The saved provider/model/name tuple, or undefined for missing or invalid name metadata.',
32
44
  },
33
45
  {
34
- signature: 'async saveSelection(next: ModelSelection): Promise<void>',
35
- description: 'Save the complete default model selection to the shared ChatCode Home file, or the owning profile entry in other deployments. Saves commit in submission order; a failed save rejects its caller without blocking later saves. A shared save also retains an available catalog name; metadata failures do not reject the route save.',
36
- parameters: [{ name: 'next', description: 'resolved selection accepted by an entry point.' }],
46
+ signature: 'async saveSelection(next: ModelSelection, name?: string): Promise<void>',
47
+ description: 'Save the complete default model selection to the shared ChatCode Home file, or the owning profile entry in other deployments. Saves commit in submission order; a failed save rejects its caller without blocking later saves. A shared save uses the caller\'s already resolved catalog name without an additional lookup.',
48
+ parameters: [{ name: 'next', description: 'resolved selection accepted by an entry point.' }, { name: 'name', description: 'optional display name from the entry point\'s catalog.' }],
37
49
  returns: 'fulfillment after the preference or optional profile write settles.',
38
50
  },
39
51
  ],
@@ -1843,20 +1855,20 @@ export const SERVICE_API = [
1843
1855
  returns: 'authorized bounded Session search results.',
1844
1856
  },
1845
1857
  {
1846
- signature: '@Remote(\'create\') create(request: SessionCreateRequest): Promise<SessionCreateValue>',
1858
+ signature: '@Remote(\'create\') async create(request: SessionCreateRequest): Promise<SessionCreateValue>',
1847
1859
  description: 'Create or idempotently adopt one ordinary Session.',
1848
1860
  parameters: [{ name: 'request', description: 'requested identity, location, and Agent preset.' }],
1849
1861
  returns: 'the Session identity and resolved preset when configured.',
1850
1862
  },
1851
1863
  {
1852
1864
  signature: '@Remote(\'selectModel\') selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>',
1853
- description: 'Select one Session-local model after explicitly resuming the Session; save the default in the background.',
1865
+ description: 'Select a model after explicitly resuming the Session and awaiting default persistence.',
1854
1866
  parameters: [{ name: 'request', description: 'Session identity and requested model selection.' }],
1855
- returns: 'the normalized selection installed for the Session, without waiting for default persistence.',
1867
+ returns: 'the normalized selection after the shared save and Session selection succeed.',
1856
1868
  },
1857
1869
  {
1858
1870
  signature: '@Remote async initializeDefaultModel(): Promise<void>',
1859
- description: 'Select the first available account model after login when no provider API key is configured.',
1871
+ description: 'Initialize legacy account deployments without changing a product process\'s cached selection.',
1860
1872
  parameters: [],
1861
1873
  returns: 'after saving the first available model or retaining the existing default.',
1862
1874
  },
@@ -1935,7 +1947,7 @@ export const SERVICE_API = [
1935
1947
  returns: 'one chronological page.',
1936
1948
  },
1937
1949
  {
1938
- signature: '@Remote({ mode: \'stream\' }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>',
1950
+ signature: '@Remote({ mode: \'stream\' }) async *follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>',
1939
1951
  description: 'Follow one Session log from its opening or resume cursor.',
1940
1952
  parameters: [{ name: 'request', description: 'durable address and last committed sequence already held by the caller.' }, { name: 'signal', description: 'cancellation owned by the Remote stream carrier.' }],
1941
1953
  returns: 'a complete opening snapshot followed by gap-free durable event frames and optional cursorless assistant-stream frames.',
@@ -2023,6 +2035,13 @@ export const SERVICE_API = [
2023
2035
  parameters: [{ name: 'options', description: 'optional cancellation.' }],
2024
2036
  returns: 'one snapshot per stored session.',
2025
2037
  },
2038
+ {
2039
+ signature: 'prepareDeletion(sessionIds: readonly SessionId[]): Promise<SessionDeletion>',
2040
+ description: 'Reserve every target\'s write ownership before any deletion is committed. The caller persists its recovery journal while this reservation is held, calls commit, and always disposes it to release ownership. Backends must retain deletion identities so old generations cannot reappear or be recreated.',
2041
+ parameters: [{ name: 'sessionIds', description: 'complete deletion scope, including owned descendants.' }],
2042
+ returns: 'an exclusive deletion reservation; missing targets are idempotent.',
2043
+ throws: ['when a writer holds any target, or the backend does not support deletion.'],
2044
+ },
2026
2045
  ],
2027
2046
  },
2028
2047
  {
@@ -3526,6 +3545,18 @@ export const SERVICE_API = [
3526
3545
  parameters: [{ name: 'request', description: 'Session identity to unarchive.' }],
3527
3546
  returns: 'the complete resulting archive set.',
3528
3547
  },
3548
+ {
3549
+ signature: '@Remote(\'sessionDeletionScope\') sessionDeletionScope(request: WorkspaceSessionDeletionPreviewRequest): Promise<WorkspaceSessionDeletionValue>',
3550
+ description: 'Preview the archived root and owned subagent Sessions covered by deletion.',
3551
+ parameters: [{ name: 'request', description: 'archived root to preview.' }],
3552
+ returns: 'complete deletion scope for confirmation.',
3553
+ },
3554
+ {
3555
+ signature: '@Remote(\'deleteSession\') deleteSession(request: WorkspaceSessionDeletionRequest): Promise<WorkspaceSessionDeletionValue>',
3556
+ description: 'Permanently remove an archived Session and its confirmed owned subagents.',
3557
+ parameters: [{ name: 'request', description: 'archived root and confirmed scope.' }],
3558
+ returns: 'identities permanently removed.',
3559
+ },
3529
3560
  {
3530
3561
  signature: '@Remote(\'pinSession\') pinSession(request: WorkspacePinSessionRequest): Promise<WorkspacePinValue>',
3531
3562
  description: 'Surface one known unarchived Session ahead of unpinned Sessions.',
@@ -3630,6 +3661,18 @@ export const SERVICE_API = [
3630
3661
  parameters: [{ name: 'id', description: 'Workspace to move.' }, { name: 'beforeId', description: 'Workspace anchor; omitted appends.' }],
3631
3662
  returns: 'the complete committed workspace order.',
3632
3663
  },
3664
+ {
3665
+ signature: 'deleteSession(sessionId: SessionId, expectedIds?: readonly SessionId[]): Promise<readonly SessionId[]>',
3666
+ description: 'Permanently delete an archived, inactive root and its owned subagent descendants. Independent forks are preserved. Reserve every writer before recording a recovery marker; a failure after that marker is retried on refresh or restart.',
3667
+ parameters: [{ name: 'sessionId', description: 'archived root identity; repeated deletion is idempotent.' }, { name: 'expectedIds', description: 'identities confirmed by the user; a changed scope refuses deletion.' }],
3668
+ returns: 'deleted root and descendant identities after durable cleanup.',
3669
+ },
3670
+ {
3671
+ signature: 'sessionDeletionScope(sessionId: SessionId): Promise<readonly SessionId[]>',
3672
+ description: 'Preview the root and owned descendants without opening their histories.',
3673
+ parameters: [{ name: 'sessionId', description: 'archived root to preview.' }],
3674
+ returns: 'identities to show in the deletion confirmation.',
3675
+ },
3633
3676
  {
3634
3677
  signature: 'archiveSession(sessionId: SessionId, options: ArchiveSessionOptions = {}): Promise<void>',
3635
3678
  description: 'Archive one session durably. The session must exist (live or in session persistence); its workspace accounting — or lack of one — is irrelevant. Without `stopActivity` the session must also be inactive: the `workspace/session-activity` waterfall is asked once, and any reported activity rejects with WorkspaceActiveSessionError before anything is written. With `stopActivity` the archive is written without an activity check, and the `workspace/session-stop` providers are then asked to stop the session\'s work: the durable archive set is what a provider\'s `agent/pre-step` gate reads, so every wake the stops induce is already blocked. Archiving drops the session\'s pin in the same durable write (pinning and archival are mutually exclusive). An already archived id resolves without writing, asking, or stopping.',
@@ -3801,6 +3844,14 @@ export const EVENT_API = [
3801
3844
  description: 'One Agent failed outside a durable turn position.',
3802
3845
  parameters: [{ name: 'sessionId', description: 'Agent and Session identity.' }, { name: 'message', description: 'user-safe failure chain.' }],
3803
3846
  },
3847
+ {
3848
+ name: 'api-session/model-updated',
3849
+ mode: 'emit',
3850
+ signature: '\'api-session/model-updated\'(): void',
3851
+ summary: 'The Host process committed a user-selected default; browser catalogs must reload from that process.',
3852
+ description: 'The Host process committed a user-selected default; browser catalogs must reload from that process.',
3853
+ parameters: [],
3854
+ },
3804
3855
  {
3805
3856
  name: 'api-session/removed',
3806
3857
  mode: 'emit',
@@ -4097,6 +4148,30 @@ export const EVENT_API = [
4097
4148
  description: 'Creation announcement during session publication. A synchronous throw vetoes and rolls back with a paired disposal; detach requested during dispatch is deferred. A returned-promise rejection is logged but cannot retroactively veto this synchronous boundary. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only sessions entered through that agent\'s context.',
4098
4149
  parameters: [{ name: 'session', description: 'the session just entered and announced.' }],
4099
4150
  },
4151
+ {
4152
+ name: 'session/delete-release',
4153
+ mode: 'parallel',
4154
+ signature: '\'session/delete-release\'(request: { readonly sessionIds: readonly SessionId[] }): Promise<void> | void',
4155
+ summary: 'Ask capability owners to release idle, archived Sessions before writer reservation.',
4156
+ description: 'Ask capability owners to release idle, archived Sessions before writer reservation. Owners release only handles they own; foreign or running Sessions remain protected.',
4157
+ parameters: [{ name: 'request', description: 'confirmed root and owned subagent identities.' }],
4158
+ },
4159
+ {
4160
+ name: 'session/deleted',
4161
+ mode: 'emit',
4162
+ signature: '\'session/deleted\'(sessionIds: readonly SessionId[]): void',
4163
+ summary: 'Permanent deletion observed locally or from shared storage.',
4164
+ description: 'Permanent deletion observed locally or from shared storage.',
4165
+ parameters: [{ name: 'sessionIds', description: 'removed identities, including owned descendants.' }],
4166
+ },
4167
+ {
4168
+ name: 'session/deleting',
4169
+ mode: 'parallel',
4170
+ signature: '\'session/deleting\'(request: { readonly sessionIds: readonly SessionId[] }): Promise<void> | void',
4171
+ summary: 'Permanently remove secondary Session records after stored payload deletion.',
4172
+ description: 'Permanently remove secondary Session records after stored payload deletion. Listeners are idempotent; failures leave the deletion journal pending for recovery.',
4173
+ parameters: [{ name: 'request', description: 'confirmed root and owned subagent identities.' }],
4174
+ },
4100
4175
  {
4101
4176
  name: 'session/disposed',
4102
4177
  mode: 'emit',
@@ -5714,7 +5789,7 @@ export const TYPE_API = [
5714
5789
  },
5715
5790
  {
5716
5791
  name: 'ModelCatalog',
5717
- declaration: 'export interface ModelCatalog {\n readonly default: ModelSelection;\n readonly savedDisplayName?: {\n readonly provider: string;\n readonly model: string;\n readonly name: string;\n };\n readonly routableProviders: readonly string[];\n readonly groups: readonly ModelProviderGroup[];\n readonly failures: readonly ModelCatalogFailure[];\n}',
5792
+ declaration: 'export interface ModelCatalog {\n readonly default: ModelSelection;\n readonly selectionScope?: \'process\';\n readonly savedDisplayName?: {\n readonly provider: string;\n readonly model: string;\n readonly name: string;\n };\n readonly routableProviders: readonly string[];\n readonly groups: readonly ModelProviderGroup[];\n readonly failures: readonly ModelCatalogFailure[];\n}',
5718
5793
  },
5719
5794
  {
5720
5795
  name: 'ModelCatalogFailure',
@@ -6066,7 +6141,7 @@ export const TYPE_API = [
6066
6141
  },
6067
6142
  {
6068
6143
  name: 'RecordTable',
6069
- declaration: 'export type RecordTable<K extends string, V> = Pick<KvTable<K, V>, \'read\' | \'put\'>;',
6144
+ declaration: 'export type RecordTable<K extends string, V> = Pick<KvTable<K, V>, \'read\' | \'put\' | \'delete\'>;',
6070
6145
  },
6071
6146
  {
6072
6147
  name: 'RecurringScheduleRecord',
@@ -6408,6 +6483,10 @@ export const TYPE_API = [
6408
6483
  name: 'SessionCreateValue',
6409
6484
  declaration: 'export interface SessionCreateValue {\n readonly sessionId: SessionId;\n readonly agentPreset?: string;\n}',
6410
6485
  },
6486
+ {
6487
+ name: 'SessionDeletion',
6488
+ declaration: 'export interface SessionDeletion extends AsyncDisposable {\n commit(): Promise<void>;\n}',
6489
+ },
6411
6490
  {
6412
6491
  name: 'SessionEvent',
6413
6492
  declaration: 'export type SessionEvent<T extends SessionEventType = SessionEventType> = {\n [K in SessionEventType]: {\n type: K;\n seq: SessionSeq;\n time: number;\n data: SessionEventMap[K];\n ignorable?: true;\n } & (K extends SurfaceEventType ? SurfaceIntent<K> : {\n surfaceOp?: never;\n sourceEventSeqs?: never;\n });\n}[T];',
@@ -8028,6 +8107,18 @@ export const TYPE_API = [
8028
8107
  name: 'WorkspaceRenameRequest',
8029
8108
  declaration: 'export interface WorkspaceRenameRequest {\n readonly workspaceId: WorkspaceId;\n readonly title: string;\n}',
8030
8109
  },
8110
+ {
8111
+ name: 'WorkspaceSessionDeletionPreviewRequest',
8112
+ declaration: 'export interface WorkspaceSessionDeletionPreviewRequest {\n readonly sessionId: SessionId;\n}',
8113
+ },
8114
+ {
8115
+ name: 'WorkspaceSessionDeletionRequest',
8116
+ declaration: 'export interface WorkspaceSessionDeletionRequest extends WorkspaceSessionDeletionPreviewRequest {\n readonly expectedSessionIds: readonly SessionId[];\n}',
8117
+ },
8118
+ {
8119
+ name: 'WorkspaceSessionDeletionValue',
8120
+ declaration: 'export interface WorkspaceSessionDeletionValue {\n readonly sessionIds: readonly SessionId[];\n}',
8121
+ },
8031
8122
  {
8032
8123
  name: 'WorkspaceUnarchiveSessionRequest',
8033
8124
  declaration: 'export interface WorkspaceUnarchiveSessionRequest {\n readonly sessionId: SessionId;\n}',
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { mkdir, realpath, stat } from "node:fs/promises";
3
3
  import { Service } from "@deepseek-ai/cordis";
4
+ import { SessionAlreadyOwnedError } from "@deepseek-ai/dsh-session-persistence";
4
5
  import { posix, win32 } from "node:path";
5
6
  import { z } from "zod";
6
7
  import { brandString } from "@deepseek-ai/dsh-brand";
@@ -252,7 +253,11 @@ const workspaceDomainState = z.object({
252
253
  workspaceIds: z.array(workspaceId),
253
254
  archivedSessionIds: z.array(sessionId).default([]),
254
255
  pinnedSessionIds: z.array(sessionId).default([]),
255
- pendingMutation: workspacePendingMutation.optional()
256
+ pendingMutation: workspacePendingMutation.optional(),
257
+ /** Payload deletion committed identities; old processes must never restore these Sessions. */
258
+ deletedSessionIds: z.array(sessionId).optional(),
259
+ /** Complete deletion scope retained until payloads and secondary owners finish cleanup. */
260
+ pendingSessionDeletion: z.array(sessionId).optional()
256
261
  });
257
262
  /**
258
263
  * The workspace domain spec: one `workspaces` table keyed by
@@ -282,6 +287,64 @@ const workspaceDomainSpec = defineDomain({
282
287
  * domain data form.
283
288
  * @module @deepseek-ai/dsh-workspace
284
289
  */
290
+ var __addDisposableResource = function(env, value, async) {
291
+ if (value !== null && value !== void 0) {
292
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
293
+ var dispose, inner;
294
+ if (async) {
295
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
296
+ dispose = value[Symbol.asyncDispose];
297
+ }
298
+ if (dispose === void 0) {
299
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
300
+ dispose = value[Symbol.dispose];
301
+ if (async) inner = dispose;
302
+ }
303
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
304
+ if (inner) dispose = function() {
305
+ try {
306
+ inner.call(this);
307
+ } catch (e) {
308
+ return Promise.reject(e);
309
+ }
310
+ };
311
+ env.stack.push({
312
+ value,
313
+ dispose,
314
+ async
315
+ });
316
+ } else if (async) env.stack.push({ async: true });
317
+ return value;
318
+ };
319
+ var __disposeResources = (function(SuppressedError) {
320
+ return function(env) {
321
+ function fail(e) {
322
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
323
+ env.hasError = true;
324
+ }
325
+ var r, s = 0;
326
+ function next() {
327
+ while (r = env.stack.pop()) try {
328
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
329
+ if (r.dispose) {
330
+ var result = r.dispose.call(r.value);
331
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) {
332
+ fail(e);
333
+ return next();
334
+ });
335
+ } else s |= 1;
336
+ } catch (e) {
337
+ fail(e);
338
+ }
339
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
340
+ if (env.hasError) throw env.error;
341
+ }
342
+ return next();
343
+ };
344
+ })(typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
345
+ var e = new Error(message);
346
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
347
+ });
285
348
  /**
286
349
  * Brand a string as a {@link WorkspaceId}.
287
350
  * @param id - Raw workspace id string.
@@ -338,6 +401,21 @@ var WorkspaceArchivedSessionPinError = class extends Error {
338
401
  this.name = "WorkspaceArchivedSessionPinError";
339
402
  }
340
403
  };
404
+ /** Permanent deletion requires an archived root and no active Session ownership. */
405
+ var WorkspaceSessionDeletionError = class extends Error {
406
+ sessionId;
407
+ reason;
408
+ /**
409
+ * @param sessionId - identity whose lifecycle prevents the requested operation.
410
+ * @param reason - stable refusal discriminant shared by the Web and TUI.
411
+ */
412
+ constructor(sessionId, reason) {
413
+ super(`cannot delete session '${sessionId}': ${reason}`);
414
+ this.sessionId = sessionId;
415
+ this.reason = reason;
416
+ this.name = "WorkspaceSessionDeletionError";
417
+ }
418
+ };
341
419
  /** A workspace reorder named a source or anchor absent from the durable registry order. */
342
420
  var WorkspaceOrderInvalidError = class extends Error {
343
421
  workspaceId;
@@ -403,6 +481,7 @@ var WorkspaceRegistry = class extends Service {
403
481
  await domain.refresh();
404
482
  this.state = domain.global.get();
405
483
  await this.recoverPendingMutation();
484
+ await this.recoverSessionDeletion();
406
485
  this.validateStoredState(this.requireState());
407
486
  if (!this.requireState().initialized) {
408
487
  const headers = await this.listStoredHeaders();
@@ -550,6 +629,130 @@ var WorkspaceRegistry = class extends Service {
550
629
  get archivedSessionIds() {
551
630
  return this.requireState().archivedSessionIds;
552
631
  }
632
+ /** Permanently deleted identities retained for cross-process invalidation and stale-operation refusal. */
633
+ get deletedSessionIds() {
634
+ return this.requireState().deletedSessionIds ?? [];
635
+ }
636
+ /**
637
+ * Permanently delete an archived, inactive root and its owned subagent descendants.
638
+ * Independent forks are preserved. Reserve every writer before recording a recovery
639
+ * marker; a failure after that marker is retried on refresh or restart.
640
+ * @param sessionId - archived root identity; repeated deletion is idempotent.
641
+ * @param expectedIds - identities confirmed by the user; a changed scope refuses deletion.
642
+ * @returns deleted root and descendant identities after durable cleanup.
643
+ */
644
+ deleteSession(sessionId, expectedIds) {
645
+ return this.enqueueOperation(async () => {
646
+ const env_1 = {
647
+ stack: [],
648
+ error: void 0,
649
+ hasError: false
650
+ };
651
+ try {
652
+ if (this.deletedSessionIds.includes(sessionId)) return [sessionId];
653
+ if (!this.archivedSessionIds.includes(sessionId)) throw new WorkspaceSessionDeletionError(sessionId, "not-archived");
654
+ const ids = new Set(await this.collectSessionDeletionScope(sessionId));
655
+ if (expectedIds !== void 0 && (expectedIds.length !== ids.size || new Set(expectedIds).size !== ids.size || expectedIds.some((id) => !ids.has(id)))) throw new WorkspaceSessionDeletionError(sessionId, "scope-changed");
656
+ for (const id of ids) {
657
+ const activity = await this.ctx.waterfall("workspace/session-activity", { sessionId: id }, () => Promise.resolve([]));
658
+ if (activity.length > 0) throw new WorkspaceActiveSessionError(id, activity);
659
+ }
660
+ const targets = [...ids];
661
+ await this.ctx.parallel("session/delete-release", { sessionIds: targets });
662
+ for (const id of ids) if (this.ctx.get("sessions")?.get(id) !== void 0) throw new WorkspaceSessionDeletionError(id, "in-use");
663
+ const reservation = __addDisposableResource(env_1, await this.ctx.sessionPersistence.prepareDeletion(targets).catch((error) => {
664
+ if (error instanceof SessionAlreadyOwnedError) throw new WorkspaceSessionDeletionError(sessionId, "in-use");
665
+ throw error;
666
+ }), true);
667
+ await this.setState({
668
+ ...this.requireState(),
669
+ pendingSessionDeletion: targets
670
+ });
671
+ await reservation.commit();
672
+ await this.finishSessionDeletion(targets);
673
+ return targets;
674
+ } catch (e_1) {
675
+ env_1.error = e_1;
676
+ env_1.hasError = true;
677
+ } finally {
678
+ const result_1 = __disposeResources(env_1);
679
+ if (result_1) await result_1;
680
+ }
681
+ });
682
+ }
683
+ /**
684
+ * Preview the root and owned descendants without opening their histories.
685
+ * @param sessionId - archived root to preview.
686
+ * @returns identities to show in the deletion confirmation.
687
+ */
688
+ sessionDeletionScope(sessionId) {
689
+ return this.enqueueOperation(async () => {
690
+ if (!this.archivedSessionIds.includes(sessionId)) throw new WorkspaceSessionDeletionError(sessionId, "not-archived");
691
+ return this.collectSessionDeletionScope(sessionId);
692
+ });
693
+ }
694
+ async collectSessionDeletionScope(sessionId) {
695
+ const headers = await this.listStoredHeaders();
696
+ const ids = new Set([sessionId]);
697
+ for (let changed = true; changed;) {
698
+ changed = false;
699
+ for (const header of headers) if (header.origin === "subagent" && header.parentSession !== void 0 && ids.has(header.parentSession) && !ids.has(header.id)) {
700
+ ids.add(header.id);
701
+ changed = true;
702
+ }
703
+ }
704
+ return [...ids];
705
+ }
706
+ async recoverSessionDeletion() {
707
+ const env_2 = {
708
+ stack: [],
709
+ error: void 0,
710
+ hasError: false
711
+ };
712
+ try {
713
+ const ids = this.requireState().pendingSessionDeletion;
714
+ if (ids === void 0) return;
715
+ await __addDisposableResource(env_2, await this.ctx.sessionPersistence.prepareDeletion(ids), true).commit();
716
+ await this.finishSessionDeletion(ids);
717
+ } catch (e_2) {
718
+ env_2.error = e_2;
719
+ env_2.hasError = true;
720
+ } finally {
721
+ const result_2 = __disposeResources(env_2);
722
+ if (result_2) await result_2;
723
+ }
724
+ }
725
+ notifyDeleted(ids) {
726
+ try {
727
+ this.ctx.emit("session/deleted", ids);
728
+ } catch (error) {
729
+ this.ctx.logger.warn(`workspace: deletion listener failed: ${String(error)}`);
730
+ }
731
+ }
732
+ async finishSessionDeletion(ids) {
733
+ await this.ctx.parallel("session/deleting", { sessionIds: ids });
734
+ const removed = new Set(ids);
735
+ for (const [id, record] of this.requireTable().entries()) if (record.sessionIds.some((member) => removed.has(member))) await this.requireTable().update(id, (current) => ({
736
+ ...current,
737
+ sessionIds: current.sessionIds.filter((member) => !removed.has(member)),
738
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
739
+ }));
740
+ const state = this.requireState();
741
+ await this.setState({
742
+ ...state,
743
+ archivedSessionIds: state.archivedSessionIds.filter((id) => !removed.has(id)),
744
+ pinnedSessionIds: state.pinnedSessionIds.filter((id) => !removed.has(id)),
745
+ deletedSessionIds: [...new Set([...state.deletedSessionIds ?? [], ...ids])],
746
+ pendingSessionDeletion: void 0
747
+ });
748
+ for (const id of ids) {
749
+ this.headers.delete(id);
750
+ this.sessionPaths.delete(id);
751
+ this.invalidSessionPaths.delete(id);
752
+ }
753
+ this.rebuildEntities();
754
+ this.notifyDeleted(ids);
755
+ }
553
756
  /**
554
757
  * Archive one session durably. The session must exist (live or in session
555
758
  * persistence); its workspace accounting — or lack of one — is irrelevant.
@@ -596,6 +799,7 @@ var WorkspaceRegistry = class extends Service {
596
799
  */
597
800
  unarchiveSession(sessionId) {
598
801
  return this.enqueueOperation(async () => {
802
+ if (this.deletedSessionIds.includes(sessionId)) throw new WorkspaceSessionDeletionError(sessionId, "deleted");
599
803
  const state = this.requireState();
600
804
  if (!state.archivedSessionIds.includes(sessionId)) return;
601
805
  await this.setState({
@@ -656,6 +860,7 @@ var WorkspaceRegistry = class extends Service {
656
860
  * masquerade as an unknown session.
657
861
  */
658
862
  async sessionKnown(id) {
863
+ if (this.deletedSessionIds.includes(id)) return false;
659
864
  if (this.ctx.get("sessions")?.get(id) !== void 0) return true;
660
865
  if (this.headers.has(id)) return true;
661
866
  await this.indexHeaders(await this.listStoredHeaders());
@@ -939,12 +1144,14 @@ var WorkspaceRegistry = class extends Service {
939
1144
  }
940
1145
  /** Every stored session's header, projected from the persistence snapshot listing. */
941
1146
  async listStoredHeaders() {
942
- return (await this.ctx.sessionPersistence.list()).map((snapshot) => snapshot.header);
1147
+ const snapshots = await this.ctx.sessionPersistence.list();
1148
+ const deleted = new Set(this.deletedSessionIds);
1149
+ return snapshots.map((snapshot) => snapshot.header).filter((header) => !deleted.has(header.id));
943
1150
  }
944
1151
  async indexLiveSessions() {
945
1152
  const sessions = this.ctx.get("sessions");
946
1153
  if (sessions === void 0) return;
947
- await this.indexHeaders(sessions.list().map((session) => session.header));
1154
+ await this.indexHeaders(sessions.list().filter((session) => !this.deletedSessionIds.includes(session.id)).map((session) => session.header));
948
1155
  }
949
1156
  reportFilteredCandidates() {
950
1157
  for (const entity of this.entities.values()) {
@@ -990,6 +1197,7 @@ var WorkspaceRegistry = class extends Service {
990
1197
  if (domain === void 0) throw new Error("workspace registry is not started yet");
991
1198
  return await domain.exclusive(async () => {
992
1199
  const before = JSON.stringify([...this.entities.values()].map((entity) => [entity.id, entity.sessionIds]));
1200
+ const previousDeleted = new Set(this.deletedSessionIds);
993
1201
  await domain.refresh();
994
1202
  const changed = this.snapshotIdentity() !== this.refreshedState;
995
1203
  this.state = domain.global.get();
@@ -1000,6 +1208,9 @@ var WorkspaceRegistry = class extends Service {
1000
1208
  this.rebuildEntities();
1001
1209
  }
1002
1210
  await this.recoverPendingMutation();
1211
+ await this.recoverSessionDeletion();
1212
+ const newlyDeleted = this.deletedSessionIds.filter((id) => !previousDeleted.has(id));
1213
+ if (newlyDeleted.length > 0) this.notifyDeleted(newlyDeleted);
1003
1214
  this.validateStoredState(this.requireState());
1004
1215
  const after = JSON.stringify([...this.entities.values()].map((entity) => [entity.id, entity.sessionIds]));
1005
1216
  if (changed || before !== after) try {
@@ -1022,4 +1233,4 @@ var WorkspaceRegistry = class extends Service {
1022
1233
  };
1023
1234
  const sameSessionIds = (left, right) => left.length === right.length && left.every((id, index) => id === right[index]);
1024
1235
  //#endregion
1025
- export { WorkspaceActiveSessionError, WorkspaceArchivedSessionPinError, WorkspaceId, WorkspaceMoveInvalidError, WorkspaceOrderInvalidError, WorkspaceRegistry, WorkspaceRegistry as default, WorkspaceUnknownSessionError, realpathNormalize, workspaceDomainSpec, workspaceDomainState, workspaceRecord };
1236
+ export { WorkspaceActiveSessionError, WorkspaceArchivedSessionPinError, WorkspaceId, WorkspaceMoveInvalidError, WorkspaceOrderInvalidError, WorkspaceRegistry, WorkspaceRegistry as default, WorkspaceSessionDeletionError, WorkspaceUnknownSessionError, realpathNormalize, workspaceDomainSpec, workspaceDomainState, workspaceRecord };
@@ -1,6 +1,7 @@
1
1
  import "node:crypto";
2
2
  import "node:fs/promises";
3
3
  import { Service } from "@deepseek-ai/cordis";
4
+ import "@deepseek-ai/dsh-session-persistence";
4
5
  import "node:path";
5
6
  import { z } from "zod";
6
7
  import { brandString } from "@deepseek-ai/dsh-brand";
@@ -51,7 +52,11 @@ defineDomain({
51
52
  workspaceIds: z.array(workspaceId),
52
53
  archivedSessionIds: z.array(sessionId).default([]),
53
54
  pinnedSessionIds: z.array(sessionId).default([]),
54
- pendingMutation: workspacePendingMutation.optional()
55
+ pendingMutation: workspacePendingMutation.optional(),
56
+ /** Payload deletion committed identities; old processes must never restore these Sessions. */
57
+ deletedSessionIds: z.array(sessionId).optional(),
58
+ /** Complete deletion scope retained until payloads and secondary owners finish cleanup. */
59
+ pendingSessionDeletion: z.array(sessionId).optional()
55
60
  }),
56
61
  initial: {
57
62
  initialized: false,
@@ -55,6 +55,16 @@ export declare class WorkspaceArchivedSessionPinError extends Error {
55
55
  */
56
56
  constructor(sessionId: SessionId);
57
57
  }
58
+ /** Permanent deletion requires an archived root and no active Session ownership. */
59
+ export declare class WorkspaceSessionDeletionError extends Error {
60
+ readonly sessionId: SessionId;
61
+ readonly reason: 'not-archived' | 'in-use' | 'deleted' | 'scope-changed';
62
+ /**
63
+ * @param sessionId - identity whose lifecycle prevents the requested operation.
64
+ * @param reason - stable refusal discriminant shared by the Web and TUI.
65
+ */
66
+ constructor(sessionId: SessionId, reason: 'not-archived' | 'in-use' | 'deleted' | 'scope-changed');
67
+ }
58
68
  /** A workspace reorder named a source or anchor absent from the durable registry order. */
59
69
  export declare class WorkspaceOrderInvalidError extends Error {
60
70
  readonly workspaceId: WorkspaceId;
@@ -202,6 +212,27 @@ export declare class WorkspaceRegistry extends Service {
202
212
  * @returns the archived session ids in archive order.
203
213
  */
204
214
  get archivedSessionIds(): readonly SessionId[];
215
+ /** Permanently deleted identities retained for cross-process invalidation and stale-operation refusal. */
216
+ get deletedSessionIds(): readonly SessionId[];
217
+ /**
218
+ * Permanently delete an archived, inactive root and its owned subagent descendants.
219
+ * Independent forks are preserved. Reserve every writer before recording a recovery
220
+ * marker; a failure after that marker is retried on refresh or restart.
221
+ * @param sessionId - archived root identity; repeated deletion is idempotent.
222
+ * @param expectedIds - identities confirmed by the user; a changed scope refuses deletion.
223
+ * @returns deleted root and descendant identities after durable cleanup.
224
+ */
225
+ deleteSession(sessionId: SessionId, expectedIds?: readonly SessionId[]): Promise<readonly SessionId[]>;
226
+ /**
227
+ * Preview the root and owned descendants without opening their histories.
228
+ * @param sessionId - archived root to preview.
229
+ * @returns identities to show in the deletion confirmation.
230
+ */
231
+ sessionDeletionScope(sessionId: SessionId): Promise<readonly SessionId[]>;
232
+ private collectSessionDeletionScope;
233
+ private recoverSessionDeletion;
234
+ private notifyDeleted;
235
+ private finishSessionDeletion;
205
236
  /**
206
237
  * Archive one session durably. The session must exist (live or in session
207
238
  * persistence); its workspace accounting — or lack of one — is irrelevant.