@deepseek-harness-tui/dsh-tui 0.10.2 → 0.11.0

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 (277) hide show
  1. package/README.md +171 -124
  2. package/bin/dsh-tui.js +748 -22
  3. package/cordis.patch.yml +38 -10
  4. package/cordis.yml +2 -0
  5. package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
  6. package/lib/types/adapter/channel/session-projection.js +2 -0
  7. package/lib/types/adapter/channel/ui-policy.d.ts +4 -1
  8. package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
  9. package/lib/types/adapter/channel/ui-policy.js +5 -1
  10. package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
  11. package/lib/types/adapter/kernel/host-facade.js +1 -0
  12. package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
  13. package/lib/types/adapter/kernel/replay.js +3 -0
  14. package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
  15. package/lib/types/adapter/kernel/runtime.js +2 -0
  16. package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -1
  17. package/lib/types/adapter/kernel/slices/workspace.js +1 -0
  18. package/lib/types/adapter/ports/channel-ui.d.ts +22 -2
  19. package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
  20. package/lib/types/adapter/ports/channel-view.d.ts +66 -0
  21. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  22. package/lib/types/adapter/ports/channel-workspace.d.ts +23 -0
  23. package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -1
  24. package/lib/types/adapter/ports/workspace.d.ts +2 -0
  25. package/lib/types/adapter/ports/workspace.d.ts.map +1 -1
  26. package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -1
  27. package/lib/types/adapter/upstream/workspace-driver.js +10 -2
  28. package/lib/types/commands.d.ts.map +1 -1
  29. package/lib/types/commands.js +1 -0
  30. package/lib/types/components/ContextBarView.d.ts +15 -7
  31. package/lib/types/components/ContextBarView.d.ts.map +1 -1
  32. package/lib/types/components/ContextBarView.js +26 -22
  33. package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -1
  34. package/lib/types/components/ImagePreviewOverlay.js +11 -5
  35. package/lib/types/components/Markdown.d.ts +11 -1
  36. package/lib/types/components/Markdown.d.ts.map +1 -1
  37. package/lib/types/components/Markdown.js +20 -2
  38. package/lib/types/components/MermaidDiagram.d.ts +13 -0
  39. package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
  40. package/lib/types/components/MermaidDiagram.js +61 -0
  41. package/lib/types/components/MessageList.d.ts +26 -1
  42. package/lib/types/components/MessageList.d.ts.map +1 -1
  43. package/lib/types/components/MessageList.js +72 -7
  44. package/lib/types/components/PageMargin.d.ts +1 -1
  45. package/lib/types/components/PromptInput.d.ts +37 -1
  46. package/lib/types/components/PromptInput.d.ts.map +1 -1
  47. package/lib/types/components/PromptInput.js +243 -28
  48. package/lib/types/components/ScrollbarGutter.d.ts +20 -5
  49. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
  50. package/lib/types/components/ScrollbarGutter.js +88 -10
  51. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  52. package/lib/types/components/StreamingMarkdown.js +30 -28
  53. package/lib/types/components/Tooltip.d.ts.map +1 -1
  54. package/lib/types/components/Tooltip.js +45 -7
  55. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
  56. package/lib/types/components/messages/UserPromptMessage.js +5 -7
  57. package/lib/types/components/messages/useImageInspection.d.ts.map +1 -1
  58. package/lib/types/components/messages/useImageInspection.js +19 -9
  59. package/lib/types/components/promptDraftCache.d.ts +102 -0
  60. package/lib/types/components/promptDraftCache.d.ts.map +1 -0
  61. package/lib/types/components/promptDraftCache.js +76 -0
  62. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +17 -1
  63. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  64. package/lib/types/components/questions/AskUserQuestionPanel.js +40 -7
  65. package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
  66. package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
  67. package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
  68. package/lib/types/components/sessions/SessionListRow.d.ts +23 -1
  69. package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
  70. package/lib/types/components/sessions/SessionListRow.js +52 -5
  71. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
  72. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
  73. package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
  74. package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
  75. package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
  76. package/lib/types/dsh-adapter/bundled-presets.js +80 -0
  77. package/lib/types/dsh-adapter/channel/action-readiness.d.ts +1 -1
  78. package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
  79. package/lib/types/dsh-adapter/channel/action-readiness.js +3 -0
  80. package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +1 -1
  81. package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -1
  82. package/lib/types/dsh-adapter/channel/agent-view-projection.js +19 -2
  83. package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
  84. package/lib/types/dsh-adapter/channel/background-action.js +52 -29
  85. package/lib/types/dsh-adapter/channel/binding.d.ts +19 -2
  86. package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -1
  87. package/lib/types/dsh-adapter/channel/binding.js +78 -18
  88. package/lib/types/dsh-adapter/channel/composer-images.d.ts +5 -0
  89. package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
  90. package/lib/types/dsh-adapter/channel/composer-images.js +105 -6
  91. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +8 -0
  92. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
  93. package/lib/types/dsh-adapter/channel/context-bookkeeping.js +3 -2
  94. package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
  95. package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
  96. package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
  97. package/lib/types/dsh-adapter/channel/input-delivery.d.ts +6 -1
  98. package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
  99. package/lib/types/dsh-adapter/channel/input-delivery.js +9 -1
  100. package/lib/types/dsh-adapter/channel/local-actions.d.ts +3 -19
  101. package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
  102. package/lib/types/dsh-adapter/channel/local-actions.js +2 -1
  103. package/lib/types/dsh-adapter/channel/model-actions.d.ts +4 -1
  104. package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
  105. package/lib/types/dsh-adapter/channel/model-actions.js +82 -6
  106. package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
  107. package/lib/types/dsh-adapter/channel/model-switch.js +49 -31
  108. package/lib/types/dsh-adapter/channel/projection.d.ts +4 -0
  109. package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
  110. package/lib/types/dsh-adapter/channel/projection.js +29 -19
  111. package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
  112. package/lib/types/dsh-adapter/channel/reports.js +4 -12
  113. package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
  114. package/lib/types/dsh-adapter/channel/session-fork.js +26 -2
  115. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +3 -0
  116. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
  117. package/lib/types/dsh-adapter/channel/session-live-adoption.js +1 -0
  118. package/lib/types/dsh-adapter/channel/session-metadata.d.ts +10 -0
  119. package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
  120. package/lib/types/dsh-adapter/channel/session-metadata.js +2 -2
  121. package/lib/types/dsh-adapter/channel/session-resume.d.ts +11 -2
  122. package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
  123. package/lib/types/dsh-adapter/channel/session-resume.js +291 -94
  124. package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
  125. package/lib/types/dsh-adapter/channel/session-rewind.js +34 -16
  126. package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
  127. package/lib/types/dsh-adapter/channel/session-tree-actions.js +19 -3
  128. package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
  129. package/lib/types/dsh-adapter/channel/settings-host.js +4 -3
  130. package/lib/types/dsh-adapter/channel/state.d.ts +1 -1
  131. package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
  132. package/lib/types/dsh-adapter/channel/state.js +1 -1
  133. package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
  134. package/lib/types/dsh-adapter/channel/transcript.js +6 -12
  135. package/lib/types/dsh-adapter/channel/types.d.ts +6 -1
  136. package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
  137. package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +4 -1
  138. package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
  139. package/lib/types/dsh-adapter/channel/workspace-actions.js +41 -1
  140. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  141. package/lib/types/dsh-adapter/channel.js +67 -6
  142. package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
  143. package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
  144. package/lib/types/dsh-adapter/compat/messages.js +15 -0
  145. package/lib/types/dsh-adapter/compat/settings.d.ts +33 -0
  146. package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
  147. package/lib/types/dsh-adapter/compat/settings.js +43 -0
  148. package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
  149. package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
  150. package/lib/types/dsh-adapter/compat/shell.js +11 -0
  151. package/lib/types/dsh-adapter/contract.d.ts +4 -4
  152. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  153. package/lib/types/dsh-adapter/contract.js +15 -6
  154. package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
  155. package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
  156. package/lib/types/dsh-adapter/ide-channel.js +556 -0
  157. package/lib/types/dsh-adapter/index.d.ts +18 -3
  158. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  159. package/lib/types/dsh-adapter/index.js +32 -5
  160. package/lib/types/dsh-adapter/plugin.d.ts +5 -2
  161. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  162. package/lib/types/dsh-adapter/plugin.js +173 -42
  163. package/lib/types/dsh-adapter/preset-resolution.d.ts +8 -0
  164. package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
  165. package/lib/types/dsh-adapter/preset-resolution.js +10 -0
  166. package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
  167. package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
  168. package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
  169. package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
  170. package/lib/types/dsh-adapter/sessionTree.js +3 -2
  171. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  172. package/lib/types/dsh-adapter/subagents.js +6 -5
  173. package/lib/types/dsh-adapter/workspaces.d.ts +46 -3
  174. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  175. package/lib/types/dsh-adapter/workspaces.js +116 -0
  176. package/lib/types/effortPrefs.d.ts +15 -3
  177. package/lib/types/effortPrefs.d.ts.map +1 -1
  178. package/lib/types/effortPrefs.js +37 -3
  179. package/lib/types/homePrefs.d.ts +15 -0
  180. package/lib/types/homePrefs.d.ts.map +1 -0
  181. package/lib/types/homePrefs.js +53 -0
  182. package/lib/types/i18n.d.ts +257 -296
  183. package/lib/types/i18n.d.ts.map +1 -1
  184. package/lib/types/i18n.js +78 -84
  185. package/lib/types/ink/colorize.d.ts +10 -0
  186. package/lib/types/ink/colorize.d.ts.map +1 -1
  187. package/lib/types/ink/colorize.js +21 -0
  188. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  189. package/lib/types/ink/components/ScrollBox.js +12 -1
  190. package/lib/types/ink/dom.d.ts +1 -0
  191. package/lib/types/ink/dom.d.ts.map +1 -1
  192. package/lib/types/ink/hooks/use-copy-on-select.d.ts +5 -1
  193. package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
  194. package/lib/types/ink/hooks/use-copy-on-select.js +15 -1
  195. package/lib/types/ink/ink.d.ts.map +1 -1
  196. package/lib/types/ink/ink.js +36 -1
  197. package/lib/types/ink/output.d.ts +10 -1
  198. package/lib/types/ink/output.d.ts.map +1 -1
  199. package/lib/types/ink/output.js +109 -17
  200. package/lib/types/ink/render-border.d.ts +6 -1
  201. package/lib/types/ink/render-border.d.ts.map +1 -1
  202. package/lib/types/ink/render-border.js +25 -9
  203. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  204. package/lib/types/ink/render-node-to-output.js +48 -6
  205. package/lib/types/ink/renderer.d.ts +6 -0
  206. package/lib/types/ink/renderer.d.ts.map +1 -1
  207. package/lib/types/ink/renderer.js +2 -1
  208. package/lib/types/ink/selection.d.ts +60 -0
  209. package/lib/types/ink/selection.d.ts.map +1 -1
  210. package/lib/types/ink/selection.js +129 -0
  211. package/lib/types/ink/sixel-graphics.d.ts +19 -1
  212. package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
  213. package/lib/types/ink/sixel-graphics.js +124 -12
  214. package/lib/types/ink/terminal-image.d.ts +18 -1
  215. package/lib/types/ink/terminal-image.d.ts.map +1 -1
  216. package/lib/types/ink/terminal.d.ts +17 -0
  217. package/lib/types/ink/terminal.d.ts.map +1 -1
  218. package/lib/types/ink/terminal.js +20 -1
  219. package/lib/types/screens/Chat.d.ts +17 -1
  220. package/lib/types/screens/Chat.d.ts.map +1 -1
  221. package/lib/types/screens/Chat.js +284 -72
  222. package/lib/types/screens/SessionSupervisor.d.ts +84 -0
  223. package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
  224. package/lib/types/screens/SessionSupervisor.js +350 -0
  225. package/lib/types/screens/SessionTree.js +2 -2
  226. package/lib/types/screens/Settings.d.ts.map +1 -1
  227. package/lib/types/screens/Settings.js +32 -9
  228. package/lib/types/screens/StatusLine.d.ts +13 -0
  229. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  230. package/lib/types/screens/StatusLine.js +36 -17
  231. package/lib/types/screens/StatusMetrics.d.ts +79 -22
  232. package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
  233. package/lib/types/screens/StatusMetrics.js +153 -88
  234. package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
  235. package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
  236. package/lib/types/screens/sessionSupervisor/model.js +69 -0
  237. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
  238. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
  239. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +593 -0
  240. package/lib/types/sessionMounts.d.ts +272 -0
  241. package/lib/types/sessionMounts.d.ts.map +1 -0
  242. package/lib/types/sessionMounts.js +706 -0
  243. package/lib/types/sessions/format.d.ts.map +1 -1
  244. package/lib/types/sessions/format.js +4 -1
  245. package/lib/types/sessions/resumeFailure.d.ts +38 -0
  246. package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
  247. package/lib/types/sessions/resumeFailure.js +58 -0
  248. package/lib/types/terminal-utils/mermaid.d.ts +44 -0
  249. package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
  250. package/lib/types/terminal-utils/mermaid.js +83 -0
  251. package/lib/types/tips.d.ts.map +1 -1
  252. package/lib/types/tips.js +175 -19
  253. package/lib/types/tuiDisplayPrefs.d.ts +7 -9
  254. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
  255. package/lib/types/tuiDisplayPrefs.js +31 -28
  256. package/lib/types/update.d.ts +61 -0
  257. package/lib/types/update.d.ts.map +1 -1
  258. package/lib/types/update.js +194 -0
  259. package/lib/types/utils/imageResize.d.ts +90 -0
  260. package/lib/types/utils/imageResize.d.ts.map +1 -0
  261. package/lib/types/utils/imageResize.js +272 -0
  262. package/lib/types/utils/keymap.d.ts +5 -1
  263. package/lib/types/utils/keymap.d.ts.map +1 -1
  264. package/lib/types/utils/keymap.js +23 -0
  265. package/lib/types/workspaces.d.ts +2 -1
  266. package/lib/types/workspaces.d.ts.map +1 -1
  267. package/lib/types/workspaces.js +1 -1
  268. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +10 -10
  269. package/package.json +120 -103
  270. package/presets/liangshen/.dsh-tui-managed.json +1 -1
  271. package/presets/liangshen/agent.cordis.yml +15 -0
  272. package/lib/types/screens/AgentView.d.ts +0 -23
  273. package/lib/types/screens/AgentView.d.ts.map +0 -1
  274. package/lib/types/screens/AgentView.js +0 -546
  275. package/lib/types/screens/SessionBrowser.d.ts +0 -42
  276. package/lib/types/screens/SessionBrowser.d.ts.map +0 -1
  277. package/lib/types/screens/SessionBrowser.js +0 -911
@@ -0,0 +1,80 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { join } from 'node:path';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { parse } from 'yaml';
6
+ import { packagedPresetRoot } from './packaged-presets.js';
7
+ function declarativePresets(ctx) {
8
+ const service = ctx.get('agentPresets');
9
+ return service !== null && typeof service === 'object'
10
+ && 'register' in service && typeof service.register === 'function'
11
+ ? service
12
+ : undefined;
13
+ }
14
+ /** Preserve the Loader's expressions; only the owning plugin may evaluate them. */
15
+ function readPresetPatch(path) {
16
+ const patches = parse(readFileSync(path, 'utf8'), {
17
+ customTags: [{ tag: 'tag:yaml.org,2002:js', resolve: (value) => ({ __jsExpr: value }) }],
18
+ });
19
+ if (Array.isArray(patches) && patches.length === 1) {
20
+ const row = patches[0]?.insert?.[0];
21
+ if (row?.name === '@deepseek-ai/dsh-agent-preset'
22
+ && typeof row.config?.id === 'string' && Array.isArray(row.config.plugins)) {
23
+ return row.config;
24
+ }
25
+ }
26
+ throw new Error(`dsh-tui: invalid upstream preset declaration: ${path}`);
27
+ }
28
+ /**
29
+ * 0.1.7 removed directory discovery. Consume the official bundle definitions
30
+ * through its registry, without copying or reimplementing their tool sets.
31
+ * Web/profile declarations own their seats even while still activating.
32
+ * Returns false on the legacy directory-backed roster or while absent. An
33
+ * absent service is watched through Cordis so a late registry is not missed.
34
+ */
35
+ export async function registerBundledPresets(ctx) {
36
+ if (ctx.get('agentPresets') === undefined) {
37
+ ctx.inject(['agentPresets'], async (ready) => {
38
+ await registerBundledPresets(ready);
39
+ });
40
+ return false;
41
+ }
42
+ if (declarativePresets(ctx) === undefined)
43
+ return false;
44
+ const declared = new Set();
45
+ const loader = ctx.get('loader');
46
+ for (const entry of loader?.entries() ?? []) {
47
+ if (entry.disabled || entry.options.name !== '@deepseek-ai/dsh-agent-preset')
48
+ continue;
49
+ const config = entry.options.config;
50
+ if (config !== null && typeof config === 'object' && 'id' in config && typeof config.id === 'string') {
51
+ declared.add(config.id);
52
+ }
53
+ }
54
+ const require = createRequire(ctx.baseUrl ?? import.meta.url);
55
+ for (const id of ['standard', 'ptc', 'minimal', 'cordis']) {
56
+ if (declared.has(id))
57
+ continue;
58
+ const path = require.resolve(`@deepseek-ai/dsh-web-app/presets/${id}.patch.yml`);
59
+ const owner = ctx.extend({ baseUrl: pathToFileURL(path).href });
60
+ const dispose = await declarativePresets(owner).register(readPresetPatch(path));
61
+ ctx.effect(() => dispose);
62
+ }
63
+ if (!declared.has('liangshen')) {
64
+ const root = join(packagedPresetRoot(), 'liangshen');
65
+ const metadata = parse(readFileSync(join(root, 'preset.yml'), 'utf8'));
66
+ const dispose = await declarativePresets(ctx).register({
67
+ id: 'liangshen',
68
+ name: metadata.name,
69
+ description: metadata.description,
70
+ order: metadata.order,
71
+ plugins: [{
72
+ id: 'liangshen-plugins',
73
+ name: '@deepseek-ai/cordis-plugin-include',
74
+ config: { path: pathToFileURL(join(root, 'agent.cordis.yml')).href },
75
+ }],
76
+ });
77
+ ctx.effect(() => dispose);
78
+ }
79
+ return true;
80
+ }
@@ -4,7 +4,7 @@ import type { ChannelState } from './types.js';
4
4
  * specialist has been constructed. Construction-time callers fail closed
5
5
  * rather than receiving a plausible no-op while the Channel is incomplete.
6
6
  */
7
- export type ChannelActionDelegates = Pick<ChannelState, 'commandCompletions' | 'loadOlder' | 'rewindTo' | 'rewindToNode' | 'forkSession' | 'resumeTo' | 'newSession' | 'listWorkspaces' | 'resolveWorkspace' | 'switchWorkspace' | 'renameWorkspace' | 'workspaceCommands' | 'runWorkspaceCommand' | 'switchModel' | 'listEfforts' | 'setEffort' | 'setDefaultEffort' | 'cycleMode' | 'runPermissionPreset' | 'clear' | 'setActivityFrames' | 'listPresets' | 'switchPreset' | 'listModels' | 'listProviders' | 'invalidateModelCompletion' | 'listSkills' | 'describeCredential' | 'balanceInfo' | 'sideQuestion' | 'listFileCandidates' | 'listFiles' | 'listSessions' | 'previewSession' | 'bindApprovalStore' | 'agentViewRows' | 'subscribeAgentView' | 'dispatchBackgroundAgent' | 'stopBackgroundAgent' | 'attachToAgent' | 'peekAgentSession' | 'replyToAgent' | 'backgroundCurrent' | 'setResumeTarget' | 'renameSession' | 'setSessionColor' | 'recapRecent' | 'deleteSession' | 'renameSessionTo' | 'compact' | 'runExternalCommand' | 'runExternalCommandOutcome' | 'pushLocal' | 'mcpStatus' | 'exportSession' | 'initWorkspace' | 'doctorInfo' | 'pluginsInfo' | 'listSubagents'> & {
7
+ export type ChannelActionDelegates = Pick<ChannelState, 'commandCompletions' | 'loadOlder' | 'rewindTo' | 'rewindToNode' | 'forkSession' | 'resumeTo' | 'newSession' | 'listWorkspaces' | 'listWorkspaceRegistry' | 'removeWorkspace' | 'renameWorkspaceAt' | 'resolveWorkspace' | 'switchWorkspace' | 'renameWorkspace' | 'workspaceCommands' | 'runWorkspaceCommand' | 'switchModel' | 'listEfforts' | 'setEffort' | 'setDefaultEffort' | 'cycleMode' | 'runPermissionPreset' | 'clear' | 'setActivityFrames' | 'listPresets' | 'switchPreset' | 'listModels' | 'listProviders' | 'invalidateModelCompletion' | 'listSkills' | 'describeCredential' | 'balanceInfo' | 'sideQuestion' | 'listFileCandidates' | 'listFiles' | 'listSessions' | 'previewSession' | 'bindApprovalStore' | 'agentViewRows' | 'subscribeAgentView' | 'dispatchBackgroundAgent' | 'stopBackgroundAgent' | 'attachToAgent' | 'peekAgentSession' | 'replyToAgent' | 'backgroundCurrent' | 'setResumeTarget' | 'renameSession' | 'setSessionColor' | 'recapRecent' | 'deleteSession' | 'renameSessionTo' | 'compact' | 'runExternalCommand' | 'runExternalCommandOutcome' | 'pushLocal' | 'mcpStatus' | 'exportSession' | 'initWorkspace' | 'doctorInfo' | 'pluginsInfo' | 'listSubagents'> & {
8
8
  runLocalCommand(command: string, includeInContext: boolean): Promise<void>;
9
9
  };
10
10
  type ChannelActionMethodName = Exclude<keyof ChannelActionDelegates, 'runLocalCommand'>;
@@ -1 +1 @@
1
- {"version":3,"file":"action-readiness.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/action-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,YAAY,EAClD,oBAAoB,GACpB,WAAW,GACX,UAAU,GACV,cAAc,GACd,aAAa,GACb,UAAU,GACV,YAAY,GACZ,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,mBAAmB,GACnB,qBAAqB,GACrB,aAAa,GACb,aAAa,GACb,WAAW,GACX,kBAAkB,GAClB,WAAW,GACX,qBAAqB,GACrB,OAAO,GACP,mBAAmB,GACnB,aAAa,GACb,cAAc,GACd,YAAY,GACZ,eAAe,GACf,2BAA2B,GAC3B,YAAY,GACZ,oBAAoB,GACpB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,cAAc,GACd,gBAAgB,GAChB,mBAAmB,GACnB,eAAe,GACf,oBAAoB,GACpB,yBAAyB,GACzB,qBAAqB,GACrB,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,mBAAmB,GACnB,iBAAiB,GACjB,eAAe,GACf,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,iBAAiB,GACjB,SAAS,GACT,oBAAoB,GACpB,2BAA2B,GAC3B,WAAW,GACX,WAAW,GACX,eAAe,GACf,eAAe,GACf,YAAY,GACZ,aAAa,GACb,eAAe,CAClB,GAAG;IACF,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3E,CAAA;AAED,KAAK,uBAAuB,GAAG,OAAO,CAAC,MAAM,sBAAsB,EAAE,iBAAiB,CAAC,CAAA;AACvF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAA;AAE9E;4EAC4E;AAC5E,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,MAAM,sBAAsB,GAC5C,oBAAoB,CAiEtB;AAED,wBAAgB,4BAA4B;kBAG1B,sBAAsB,GAAG,IAAI;uBAIxB,sBAAsB;EAK5C"}
1
+ {"version":3,"file":"action-readiness.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/action-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,IAAI,CAAC,YAAY,EAClD,oBAAoB,GACpB,WAAW,GACX,UAAU,GACV,cAAc,GACd,aAAa,GACb,UAAU,GACV,YAAY,GACZ,gBAAgB,GAChB,uBAAuB,GACvB,iBAAiB,GACjB,mBAAmB,GACnB,kBAAkB,GAClB,iBAAiB,GACjB,iBAAiB,GACjB,mBAAmB,GACnB,qBAAqB,GACrB,aAAa,GACb,aAAa,GACb,WAAW,GACX,kBAAkB,GAClB,WAAW,GACX,qBAAqB,GACrB,OAAO,GACP,mBAAmB,GACnB,aAAa,GACb,cAAc,GACd,YAAY,GACZ,eAAe,GACf,2BAA2B,GAC3B,YAAY,GACZ,oBAAoB,GACpB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,cAAc,GACd,gBAAgB,GAChB,mBAAmB,GACnB,eAAe,GACf,oBAAoB,GACpB,yBAAyB,GACzB,qBAAqB,GACrB,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,mBAAmB,GACnB,iBAAiB,GACjB,eAAe,GACf,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,iBAAiB,GACjB,SAAS,GACT,oBAAoB,GACpB,2BAA2B,GAC3B,WAAW,GACX,WAAW,GACX,eAAe,GACf,eAAe,GACf,YAAY,GACZ,aAAa,GACb,eAAe,CAClB,GAAG;IACF,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3E,CAAA;AAED,KAAK,uBAAuB,GAAG,OAAO,CAAC,MAAM,sBAAsB,EAAE,iBAAiB,CAAC,CAAA;AACvF,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,CAAA;AAE9E;4EAC4E;AAC5E,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,MAAM,sBAAsB,GAC5C,oBAAoB,CAoEtB;AAED,wBAAgB,4BAA4B;kBAG1B,sBAAsB,GAAG,IAAI;uBAIxB,sBAAsB;EAK5C"}
@@ -10,6 +10,9 @@ export function createChannelActionMethods(getReadyActions) {
10
10
  resumeTo: sessionId => getReadyActions().resumeTo(sessionId),
11
11
  newSession: () => getReadyActions().newSession(),
12
12
  listWorkspaces: () => getReadyActions().listWorkspaces(),
13
+ listWorkspaceRegistry: () => getReadyActions().listWorkspaceRegistry(),
14
+ removeWorkspace: path => getReadyActions().removeWorkspace(path),
15
+ renameWorkspaceAt: (path, title) => getReadyActions().renameWorkspaceAt(path, title),
13
16
  resolveWorkspace: uri => getReadyActions().resolveWorkspace(uri),
14
17
  // Preserve ChannelUi's promise rejection contract after release: a caller
15
18
  // which already received this async method must observe a rejected promise,
@@ -18,7 +18,7 @@ type ApprovalStore = {
18
18
  /** Owns detached background handles and the agent-view projection. */
19
19
  export declare function createAgentViewProjection(ctx: Context, deps: {
20
20
  owner: Pick<ChannelOwner, 'current' | 'assertActive' | 'own'>;
21
- binding: Pick<Binding, 'agent' | 'capture' | 'isCurrent' | 'prepare' | 'abandon'>;
21
+ binding: Pick<Binding, 'agent' | 'capture' | 'isCurrent' | 'prepare' | 'abandon' | 'waitForDisposal'>;
22
22
  cwd(): string;
23
23
  configuredPreset?: string;
24
24
  configuredProvider?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-view-projection.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/agent-view-projection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent-view-projection.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/agent-view-projection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AACpF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAqBlD,OAAO,EAAqD,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAG7G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,uBAAuB,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACvG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,aAAa,GAAG;IACnB,eAAe,IAAI,SAAS,MAAM,EAAE,CAAA;IACpC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAA;IACxG,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;CAC5C,CAAA;AAED,sEAAsE;AACtE,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,cAAc,GAAG,KAAK,CAAC,CAAA;IAC7D,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,iBAAiB,CAAC,CAAA;IACrG,GAAG,IAAI,MAAM,CAAA;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;IAC5F,aAAa,IAAI,OAAO,CAAC,SAAS,cAAc,EAAE,CAAC,CAAA;IACnD,cAAc,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,QAAQ,IAAI,IAAI,CAAC;QAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC,CAAA;IAChI,mBAAmB,CAAC,IAAI,EAAE,YAAY,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACnF,SAAS,CAAC,MAAM,EAAE,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC/C,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC9F;+EAC2E;IAC3E,iBAAiB,CAAC,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC/C,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;CAC7C;iBAsDiB,IAAI;;kBA3CH,IAAI;oBAKF,IAAI;4BAOI,IAAI;+BA0MC,aAAa,KAAG,IAAI;gBA5JrC,SAAS,YAAY,EAAE;wBA0LlB,MAAM,IAAI;uBAlJA,MAAM,KAAG,OAAO,CAAC,uBAAuB,CAAC;sBA6D1C,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;wBAgBvB,MAAM,KAAG,OAAO,CAAC,YAAY,CAAC;sBAehC,MAAM,KAAG,OAAO,CAAC,YAAY,EAAE,CAAC;uBAQ/B,MAAM,QAAQ,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC;;uBAiDlD,SAAS,cAAc,EAAE;sBAG1B,MAAM;EAE3B"}
@@ -9,6 +9,7 @@ import { t } from '../../i18n.js';
9
9
  import { AGENT_VIEW_STATUS_ORDER, agentViewLivePreview, agentViewStatusOf, foldAgentViewEvents, oneLine, sessionTitleFallback, } from '../agent-view.js';
10
10
  import { snapshotLiveSessionEvents } from '../compat/liveSession.js';
11
11
  import { composePreset } from '../presets.js';
12
+ import { reserveNewSession } from '../../sessionMounts.js';
12
13
  import { locateSession, previewSession } from '../sessions/index.js';
13
14
  import { attachSessionToWorkspace } from '../workspace.js';
14
15
  import { logForDebugging } from '../../utils/debug.js';
@@ -75,7 +76,7 @@ export function createAgentViewProjection(ctx, deps) {
75
76
  // These process-wide roster observations still belong to this channel
76
77
  // owner. Revoke them so a retained Context cannot wake a dead projection.
77
78
  disposeStatus = ctx.on('agent/status', () => schedule());
78
- disposeCreated = ctx.on('agent/created', () => notify());
79
+ disposeCreated = ctx.on('agent/created', () => { notify(); });
79
80
  disposeDisposed = ctx.on('agent/disposed', ({ agent }) => {
80
81
  folds.delete(String(agent.id ?? ''));
81
82
  backgroundHandles.delete(String(agent.id ?? ''));
@@ -133,6 +134,10 @@ export function createAgentViewProjection(ctx, deps) {
133
134
  return { ok: false, reason: 'unavailable' };
134
135
  }
135
136
  const sessionId = SessionId(randomUUID());
137
+ // Announce the id before the factory: the background session's log is
138
+ // created here, and the publisher only learns the id from the registry on
139
+ // its next beat.
140
+ const { reservation } = await reserveNewSession(String(sessionId));
136
141
  let detached;
137
142
  try {
138
143
  deps.owner.assertActive();
@@ -149,12 +154,14 @@ export function createAgentViewProjection(ctx, deps) {
149
154
  }));
150
155
  }
151
156
  catch (error) {
157
+ reservation.abandon();
152
158
  const message = error instanceof Error ? error.message : String(error);
153
159
  deps.notify(t('agentview-dispatch-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
154
160
  return { ok: false, reason: 'failed', error: message };
155
161
  }
156
162
  if (!deps.owner.current()) {
157
163
  await detached.release();
164
+ reservation.abandon();
158
165
  return { ok: false, reason: 'failed', error: 'Channel lifetime ended' };
159
166
  }
160
167
  try {
@@ -163,10 +170,14 @@ export function createAgentViewProjection(ctx, deps) {
163
170
  catch { /* optional ledger */ }
164
171
  if (!deps.owner.current()) {
165
172
  await detached.release();
173
+ reservation.abandon();
166
174
  return { ok: false, reason: 'failed', error: 'Channel lifetime ended' };
167
175
  }
168
176
  detached.transfer();
169
177
  backgroundHandles.set(String(sessionId), detached.handle);
178
+ // The session is this process's background handle from here on, so the
179
+ // reservation has done its job: the registry is the authority now.
180
+ reservation.settle();
170
181
  touchAgentViewSession(String(sessionId));
171
182
  touchSession(sessionId);
172
183
  // transfer intentionally moves disposal to the background ledger; check
@@ -209,13 +220,19 @@ export function createAgentViewProjection(ctx, deps) {
209
220
  const attach = async (sessionId) => {
210
221
  if (sessionId === String(deps.binding.agent.session.id))
211
222
  return { ok: true };
223
+ const capture = deps.binding.capture();
224
+ // A retiring Agent can remain in the registry until its JSONL writer has
225
+ // drained. Read the live target only after that close, just like /resume.
226
+ await deps.binding.waitForDisposal(sessionId);
227
+ if (!deps.binding.isCurrent(capture))
228
+ return { ok: false, reason: 'cancelled' };
212
229
  // Capture the exact live target before the async host decision. If the
213
230
  // registry replaces it while parked, never silently adopt that arbitrary
214
231
  // replacement; a later user action can make a fresh, explicit choice.
215
232
  const live = agents()?.get(SessionId(sessionId));
216
233
  if (await deps.sessionSwitchVetoed('agent-view', sessionId))
217
234
  return { ok: false, reason: 'cancelled' };
218
- if (live !== undefined && agents()?.get(SessionId(sessionId)) !== live)
235
+ if (!deps.binding.isCurrent(capture) || agents()?.get(SessionId(sessionId)) !== live)
219
236
  return { ok: false, reason: 'cancelled' };
220
237
  return live === undefined ? deps.resumeInto(sessionId, 'agent-view', true) : deps.adoptLive(live);
221
238
  };
@@ -1 +1 @@
1
- {"version":3,"file":"background-action.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/background-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAUlD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEhE,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEtD;iFACiF;AACjF,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,IAAI,CAAC,YAAY,EACtB,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,eAAe,GAAG,eAAe,GAChE,cAAc,GAAG,iBAAiB,GAAG,MAAM,GAAG,QAAQ,CAAC,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,EACxG,OAAO,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAC9H,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC,CAAA;IACjF,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAC3C,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACzB,cAAc,IAAI,IAAI,CAAA;IACtB,cAAc,IAAI,IAAI,CAAA;IACtB,SAAS,IAAI,IAAI,CAAA;IACjB,mBAAmB,IAAI,IAAI,CAAA;IAC3B,SAAS,IAAI,IAAI,CAAA;IACjB,eAAe,IAAI,IAAI,CAAA;IACvB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,iBAAiB,IAAI,IAAI,CAAA;IACzB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7F,4EAA4E;IAC5E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;IAC5F,eAAe,IAAI,IAAI,CAAA;CACxB,GACA,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAwEjC"}
1
+ {"version":3,"file":"background-action.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/background-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAYlD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEhE,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEtD;iFACiF;AACjF,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,IAAI,CAAC,YAAY,EACtB,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,eAAe,GAAG,eAAe,GAChE,cAAc,GAAG,iBAAiB,GAAG,MAAM,GAAG,QAAQ,CAAC,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,EACxG,OAAO,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAC9H,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC,CAAA;IACjF,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAC3C,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACzB,cAAc,IAAI,IAAI,CAAA;IACtB,cAAc,IAAI,IAAI,CAAA;IACtB,SAAS,IAAI,IAAI,CAAA;IACjB,mBAAmB,IAAI,IAAI,CAAA;IAC3B,SAAS,IAAI,IAAI,CAAA;IACjB,eAAe,IAAI,IAAI,CAAA;IACvB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,iBAAiB,IAAI,IAAI,CAAA;IACzB,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7F,4EAA4E;IAC5E,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;IAC5F,eAAe,IAAI,IAAI,CAAA;CACxB,GACA,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAwFjC"}
@@ -5,6 +5,8 @@ import { readModelPref } from '../../modelPrefs.js';
5
5
  import { readPresetPref } from '../../presetPrefs.js';
6
6
  import { clearResumeTarget, touchAgentViewSession, touchSession } from '../../sessionHistory.js';
7
7
  import { t } from '../../i18n.js';
8
+ import { reserveMount } from '../../sessionMounts.js';
9
+ import { mountFailureText } from '../../sessions/resumeFailure.js';
8
10
  import { composePreset } from '../presets.js';
9
11
  import { attachSessionToWorkspace } from '../workspace.js';
10
12
  import { resetSessionProjection } from './session-reset.js';
@@ -19,6 +21,13 @@ export function createBackgroundCurrentAction(ctx, state, options, deps) {
19
21
  return { ok: false, reason: 'unavailable' };
20
22
  }
21
23
  const sessionId = SessionId(randomUUID());
24
+ // Reserve before the factory: from the moment `agents.create` returns this
25
+ // process holds the only write handle on a log the publisher has not named
26
+ // yet, and a peer listing sessions in that gap would be told it is free.
27
+ const reserved = await reserveMount(sessionId);
28
+ if (!reserved.ok && reserved.reason !== 'occupied')
29
+ deps.notify(mountFailureText(reserved), { color: 'warning', timeoutMs: 8000 });
30
+ const reservation = reserved.ok ? reserved.reservation : { settle: () => { }, abandon: () => { } };
22
31
  try {
23
32
  const composed = await composePreset(ctx, options.configuredPreset ?? readPresetPref());
24
33
  const route = await validateModelRoute(ctx.get('llm'), resolveModelRoute({ provider: options.configuredProvider, model: options.configuredModel }, readModelPref(), { provider: options.provider, model: options.model }), { provider: options.provider, model: options.model });
@@ -30,6 +39,7 @@ export function createBackgroundCurrentAction(ctx, state, options, deps) {
30
39
  }));
31
40
  if (!deps.binding.isCurrent(adoption)) {
32
41
  await deps.binding.abandon(handle);
42
+ reservation.abandon();
33
43
  return { ok: false, reason: 'failed', error: 'Channel lifetime ended' };
34
44
  }
35
45
  try {
@@ -45,39 +55,52 @@ export function createBackgroundCurrentAction(ctx, state, options, deps) {
45
55
  // agent; the prepared candidate is abandoned instead.
46
56
  if (!deps.binding.isCurrent(adoption)) {
47
57
  await deps.binding.abandon(handle);
58
+ reservation.abandon();
48
59
  return { ok: false, reason: 'failed', error: 'Channel lifetime ended' };
49
60
  }
50
- return deps.binding.adopt(handle, adoption, (previous, disposePrevious) => {
51
- const previousSessionId = String(previous.agent.session.id);
52
- if (previous.handle !== undefined) {
53
- deps.backgroundHandles.set(previousSessionId, previous.handle);
54
- disposePrevious('park');
55
- }
56
- resetSessionProjection(state, deps.rowIds, deps.resetProjector, deps.resetSubagents, deps.resetJobs);
57
- state.status = handle.agent.status;
58
- state.agentId = handle.agent.id;
59
- state.loadedContext = undefined;
60
- state.contextWindow = undefined;
61
- state.effortLevels = undefined;
62
- state.reasoningEffort = undefined;
63
- deps.refreshEffortLevels();
64
- // Reset the input FIFO and pending-decision indicators BEFORE the first
65
- // emit (main's bind → clear → refresh order).
66
- deps.clearStagedImages();
67
- deps.bindAgent();
68
- deps.refreshCommands();
69
- void deps.refreshLoadedContext();
70
- void deps.refreshSkillCommands();
71
- clearResumeTarget();
72
- touchSession(handle.agent.id);
73
- touchAgentViewSession(previousSessionId);
74
- touchAgentViewSession(String(handle.agent.id));
75
- deps.notifySessionSwitched('background', String(handle.agent.id), previousSessionId);
76
- deps.notifyAgentView();
77
- return { ok: true, backgroundedSessionId: previousSessionId };
78
- });
61
+ let committed = false;
62
+ try {
63
+ const result = deps.binding.adopt(handle, adoption, (previous, disposePrevious) => {
64
+ const previousSessionId = String(previous.agent.session.id);
65
+ if (previous.handle !== undefined) {
66
+ deps.backgroundHandles.set(previousSessionId, previous.handle);
67
+ disposePrevious('park');
68
+ }
69
+ resetSessionProjection(state, deps.rowIds, deps.resetProjector, deps.resetSubagents, deps.resetJobs);
70
+ state.status = handle.agent.status;
71
+ state.agentId = handle.agent.id;
72
+ state.loadedContext = undefined;
73
+ state.contextWindow = undefined;
74
+ state.effortLevels = undefined;
75
+ state.reasoningEffort = undefined;
76
+ deps.refreshEffortLevels();
77
+ // Reset the input FIFO and pending-decision indicators BEFORE the first
78
+ // emit (main's bind → clear → refresh order).
79
+ deps.clearStagedImages();
80
+ deps.bindAgent();
81
+ deps.refreshCommands();
82
+ void deps.refreshLoadedContext();
83
+ void deps.refreshSkillCommands();
84
+ clearResumeTarget();
85
+ touchSession(handle.agent.id);
86
+ touchAgentViewSession(previousSessionId);
87
+ touchAgentViewSession(String(handle.agent.id));
88
+ deps.notifySessionSwitched('background', String(handle.agent.id), previousSessionId);
89
+ deps.notifyAgentView();
90
+ return { ok: true, backgroundedSessionId: previousSessionId };
91
+ });
92
+ committed = true;
93
+ return result;
94
+ }
95
+ finally {
96
+ if (committed)
97
+ reservation.settle();
98
+ else
99
+ reservation.abandon();
100
+ }
79
101
  }
80
102
  catch (error) {
103
+ reservation.abandon();
81
104
  const message = error instanceof Error ? error.message : String(error);
82
105
  deps.notify(t('agentview-dispatch-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
83
106
  return { ok: false, reason: 'failed', error: message };
@@ -24,10 +24,27 @@ export declare function createChannelBinding(initial: Agent, handle: AgentHandle
24
24
  readonly generation: number;
25
25
  capture(): BindingCapture;
26
26
  isCurrent(capture: BindingCapture): boolean;
27
- /** Create a candidate without giving it authority over the live binding. */
27
+ /**
28
+ * Create a candidate without giving it authority over the live binding.
29
+ *
30
+ * When the capture went stale the candidate is closed and the rejection
31
+ * WAITS for that close. The caller's `abandon(handle)` can no longer see the
32
+ * handle — it was never in `pending` — so waiting here is the only way the
33
+ * handle is known to have stopped writing before the caller gives its
34
+ * ledger reservation back.
35
+ */
28
36
  prepare(capture: BindingCapture, create: () => Promise<AgentHandle>): Promise<AgentHandle>;
29
- /** Dispose an uncommitted candidate exactly once. Live handles are inert. */
37
+ /**
38
+ * Dispose an uncommitted candidate exactly once and WAIT for the close.
39
+ *
40
+ * Waiting is the point: callers use this on the path where they are about
41
+ * to give a cross-process ledger reservation back, and that reservation may
42
+ * only be released once the handle has actually stopped writing. A live
43
+ * handle that this cell never owned is inert here.
44
+ */
30
45
  abandon(candidate: AgentHandle): Promise<void>;
46
+ /** A registry may retain an agent while its async handle close drains. */
47
+ waitForDisposal(sessionId: string): Promise<void>;
31
48
  /** Perform one explicit synchronous prepared-handle adoption transaction. */
32
49
  adopt: <T>(candidate: AgentHandle, capture: BindingCapture, tail: (previous: BindingCommit, disposition: (next: PreviousDisposition) => void) => T) => T;
33
50
  /** Perform one explicit synchronous already-live-agent adoption transaction. */
@@ -1 +1 @@
1
- {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,KAAK,mBAAmB,GAAG,SAAS,GAAG,MAAM,CAAA;AAE7C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,KAAK,EAAE,YAAY;;;;eAiK1F,cAAc;uBACN,cAAc;IAEjC,4EAA4E;qBACrD,cAAc,UAAU,MAAM,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBhG,6EAA6E;uBACpD,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpD,6EAA6E;YAvIhE,CAAC,aACH,WAAW,WACb,cAAc,QACjB,CAAC,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,KAAK,CAAC,KACrF,CAAC;IAqIF,gFAAgF;eA5EhE,CAAC,SACV,KAAK,cACA,WAAW,GAAG,SAAS,QAC7B,CAAC,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,KAAK,CAAC,KACrF,CAAC;;uBAsFiB,MAAM,IAAI;qCAvLS,MAAM,IAAI,KAAG,OAAO;EA0L7D;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA"}
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,KAAK,mBAAmB,GAAG,SAAS,GAAG,MAAM,CAAA;AAE7C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,KAAK,EAAE,YAAY;;;;eAsM1F,cAAc;uBACN,cAAc;IAEjC;;;;;;;;OAQG;qBACoB,cAAc,UAAU,MAAM,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAkBhG;;;;;;;OAOG;uBACsB,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,0EAA0E;+BACzC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,6EAA6E;YA7JhE,CAAC,aACH,WAAW,WACb,cAAc,QACjB,CAAC,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,KAAK,CAAC,KACrF,CAAC;IA2JF,gFAAgF;eAlGhE,CAAC,SACV,KAAK,cACA,WAAW,GAAG,SAAS,QAC7B,CAAC,QAAQ,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,KAAK,CAAC,KACrF,CAAC;;uBA4GiB,MAAM,IAAI;qCAvNS,MAAM,IAAI,KAAG,OAAO;EA0N7D;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA"}
@@ -14,17 +14,46 @@ export function createChannelBinding(initial, handle, owner) {
14
14
  let subscriptions = [];
15
15
  let handoff;
16
16
  const pending = new Map();
17
- const disposed = new WeakSet();
17
+ /**
18
+ * The in-flight (or finished) close of a handle this cell owns, keyed by the
19
+ * handle. A Promise rather than a "has it started" flag, because the callers
20
+ * that matter need to know when the handle has actually STOPPED writing: a
21
+ * ledger reservation is only safe to give back after that, and a second
22
+ * `dispose()` must not start a parallel close.
23
+ */
24
+ const closing = new WeakMap();
25
+ const closingSessions = new Map();
26
+ /** Close `candidate` exactly once; the result resolves however it ends. */
18
27
  const dispose = (candidate) => {
19
- if (disposed.has(candidate))
20
- return;
21
- disposed.add(candidate);
22
- void candidate.dispose().catch(() => undefined);
28
+ const started = closing.get(candidate);
29
+ if (started !== undefined)
30
+ return started;
31
+ // Kicked off SYNCHRONOUSLY: revocation is part of a synchronous transaction
32
+ // boundary, and a caller that abandons a candidate may check it right away.
33
+ // Only the waiting is deferred, never the call.
34
+ let close;
35
+ try {
36
+ close = Promise.resolve(candidate.dispose()).catch(() => undefined);
37
+ }
38
+ catch {
39
+ close = Promise.resolve();
40
+ }
41
+ closing.set(candidate, close);
42
+ const id = String(candidate.agent.session.id);
43
+ closingSessions.set(id, close);
44
+ void close.then(() => {
45
+ if (closingSessions.get(id) === close)
46
+ closingSessions.delete(id);
47
+ });
48
+ return close;
23
49
  };
24
50
  const disposePending = (candidate) => {
25
- if (!pending.delete(candidate))
26
- return;
27
- dispose(candidate);
51
+ if (pending.delete(candidate))
52
+ return dispose(candidate);
53
+ // Already un-pended (a revocation path took it out from under the caller):
54
+ // hand back the close that is already running, so a caller that HAS to
55
+ // wait — one about to give a ledger reservation back — still can.
56
+ return closing.get(candidate);
28
57
  };
29
58
  const clearSubscriptions = (afterEach) => {
30
59
  const active = subscriptions.splice(0);
@@ -49,23 +78,33 @@ export function createChannelBinding(initial, handle, owner) {
49
78
  return failure;
50
79
  };
51
80
  const isCaptureCurrent = (capture) => owner.current() && capture.agent === currentAgent && capture.generation === generation;
81
+ /**
82
+ * Start a close without waiting for it. The callers below are synchronous
83
+ * transaction boundaries — they must not turn into awaits — but the close
84
+ * itself stays observable through {@link dispose}, so a caller that DOES have
85
+ * to wait (an `abandon` handing a ledger reservation back) still can.
86
+ */
87
+ const startClose = (close) => {
88
+ void close;
89
+ };
52
90
  const assertPrepared = (candidate, capture) => {
53
91
  if (pending.get(candidate) !== capture || !isCaptureCurrent(capture)) {
54
- disposePending(candidate);
92
+ startClose(disposePending(candidate));
55
93
  throw new Error('dsh-tui: Channel binding changed before adoption');
56
94
  }
57
95
  };
58
96
  const settlePrevious = (previous, disposition) => {
59
97
  // Parking deliberately transfers ownership to the caller's background
60
98
  // ledger. Disposal is centralised here so no adoption tail can double-call
61
- // an old handle while its transaction is being revoked.
99
+ // an old handle while its transaction is being revoked. Neither path waits:
100
+ // an ordinary session switch must not block on the handle it just left.
62
101
  if (disposition === 'dispose' && previous.handle !== undefined && previous.handle !== currentHandle) {
63
- dispose(previous.handle);
102
+ startClose(dispose(previous.handle));
64
103
  }
65
104
  };
66
105
  owner.own(clearSubscriptions);
67
106
  owner.own(() => { for (const candidate of [...pending.keys()])
68
- disposePending(candidate); });
107
+ startClose(disposePending(candidate)); });
69
108
  const adopt = (candidate, capture, tail) => {
70
109
  if (handoff !== undefined) {
71
110
  // Mark the outer transaction superseded as well as rejecting this one;
@@ -120,7 +159,7 @@ export function createChannelBinding(initial, handle, owner) {
120
159
  if (currentHandle === candidate) {
121
160
  currentHandle = undefined;
122
161
  }
123
- dispose(candidate);
162
+ startClose(dispose(candidate));
124
163
  owner.dispose();
125
164
  throw error;
126
165
  }
@@ -185,27 +224,48 @@ export function createChannelBinding(initial, handle, owner) {
185
224
  get generation() { return generation; },
186
225
  capture() { return { agent: currentAgent, generation }; },
187
226
  isCurrent(capture) { return isCaptureCurrent(capture); },
188
- /** Create a candidate without giving it authority over the live binding. */
227
+ /**
228
+ * Create a candidate without giving it authority over the live binding.
229
+ *
230
+ * When the capture went stale the candidate is closed and the rejection
231
+ * WAITS for that close. The caller's `abandon(handle)` can no longer see the
232
+ * handle — it was never in `pending` — so waiting here is the only way the
233
+ * handle is known to have stopped writing before the caller gives its
234
+ * ledger reservation back.
235
+ */
189
236
  async prepare(capture, create) {
190
237
  owner.assertActive();
191
238
  if (!isCaptureCurrent(capture))
192
239
  throw new Error('dsh-tui: Channel binding changed before preparation');
193
240
  const candidate = await create();
194
241
  if (!isCaptureCurrent(capture)) {
195
- dispose(candidate);
242
+ await dispose(candidate);
196
243
  throw new Error('dsh-tui: Channel binding changed during preparation');
197
244
  }
198
245
  pending.set(candidate, capture);
199
246
  // Owner disposal may synchronously occur through embedding hooks while
200
247
  // ownership is registered; verify the pending entry itself as well.
201
248
  if (!isCaptureCurrent(capture) || pending.get(candidate) !== capture) {
202
- disposePending(candidate);
249
+ await disposePending(candidate);
203
250
  throw new Error('dsh-tui: Channel binding changed during preparation');
204
251
  }
205
252
  return candidate;
206
253
  },
207
- /** Dispose an uncommitted candidate exactly once. Live handles are inert. */
208
- async abandon(candidate) { disposePending(candidate); },
254
+ /**
255
+ * Dispose an uncommitted candidate exactly once and WAIT for the close.
256
+ *
257
+ * Waiting is the point: callers use this on the path where they are about
258
+ * to give a cross-process ledger reservation back, and that reservation may
259
+ * only be released once the handle has actually stopped writing. A live
260
+ * handle that this cell never owned is inert here.
261
+ */
262
+ async abandon(candidate) {
263
+ await disposePending(candidate);
264
+ },
265
+ /** A registry may retain an agent while its async handle close drains. */
266
+ async waitForDisposal(sessionId) {
267
+ await closingSessions.get(sessionId);
268
+ },
209
269
  /** Perform one explicit synchronous prepared-handle adoption transaction. */
210
270
  adopt,
211
271
  /** Perform one explicit synchronous already-live-agent adoption transaction. */
@@ -19,6 +19,11 @@ export declare function firstStaleComposerToken(text: string, ordered: ReadonlyM
19
19
  export interface ComposerImageLimits {
20
20
  readonly maxImageBytes: number;
21
21
  readonly maxImagesPerMessage: number;
22
+ /** Per-side intrinsic pixel cap mirrored from the upstream store so the
23
+ * ingress gate can resample before staging (issue #938). */
24
+ readonly maxImageDimension: number;
25
+ /** Total-pixel cap ditto. */
26
+ readonly maxImagePixels: number;
22
27
  }
23
28
  /** The composer's staged-image capability store plus its draft-binding rules. */
24
29
  export interface ComposerImages {
@@ -1 +1 @@
1
- {"version":3,"file":"composer-images.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/composer-images.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,YAAY,CAAA;AAEnB,iEAAiE;AACjE,eAAO,MAAM,oBAAoB,QAAqB,CAAA;AAEtD;6EAC6E;AAC7E,eAAO,MAAM,0BAA0B,QAAsB,CAAA;AAE7D,eAAO,MAAM,sBAAsB,GAAI,WAAW,MAAM,KAAG,MACe,CAAA;AAE1E;;;2EAG2E;AAC3E,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,GAC7B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAehB;AAED;;4EAE4E;AAC5E,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC,MAAM,GAAG,SAAS,CAKpB;AAOD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;CACrC;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B;sEACkE;IAClE,qBAAqB,IAAI,MAAM,CAAA;IAC/B;yEACqE;IACrE,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACpD;iFAC6E;IAC7E,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC3F,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;IACxC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAA;IACzD,iBAAiB,IAAI,mBAAmB,GAAG,SAAS,CAAA;IACpD;0EACsE;IACtE,iBAAiB,IAAI,IAAI,CAAA;IACzB,6EAA6E;IAC7E,QAAQ,IAAI,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAA;IAChE;4DACwD;IACxD,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAAA;IACtG,wEAAwE;IACxE,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAAA;CACnG;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE;IAAE,UAAU,IAAI,MAAM,CAAA;CAAE,GAC7B,cAAc,CAsKhB"}
1
+ {"version":3,"file":"composer-images.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/composer-images.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAMlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAEhB,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EAChB,MAAM,YAAY,CAAA;AAEnB,iEAAiE;AACjE,eAAO,MAAM,oBAAoB,QAAqB,CAAA;AAEtD;6EAC6E;AAC7E,eAAO,MAAM,0BAA0B,QAAsB,CAAA;AAE7D,eAAO,MAAM,sBAAsB,GAAI,WAAW,MAAM,KAAG,MACe,CAAA;AAE1E;;;2EAG2E;AAC3E,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,GAC7B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAehB;AAED;;4EAE4E;AAC5E,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC,MAAM,GAAG,SAAS,CAKpB;AAOD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAA;IACpC;iEAC6D;IAC7D,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,6BAA6B;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAChC;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc;IAC7B;sEACkE;IAClE,qBAAqB,IAAI,MAAM,CAAA;IAC/B;yEACqE;IACrE,UAAU,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACpD;iFAC6E;IAC7E,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC3F,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAA;IACxC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAA;IACzD,iBAAiB,IAAI,mBAAmB,GAAG,SAAS,CAAA;IACpD;0EACsE;IACtE,iBAAiB,IAAI,IAAI,CAAA;IACzB,6EAA6E;IAC7E,QAAQ,IAAI,WAAW,CAAC,MAAM,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAA;IAChE;4DACwD;IACxD,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAAA;IACtG,wEAAwE;IACxE,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAAG,SAAS,gBAAgB,EAAE,CAAA;CACnG;AA0BD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE;IAAE,UAAU,IAAI,MAAM,CAAA;CAAE,GAC7B,cAAc,CAyPhB"}