mixdog 0.9.147 → 0.9.149

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 (265) hide show
  1. package/package.json +23 -3
  2. package/scripts/build-voice-runtime-unix.sh +101 -0
  3. package/scripts/embedding-quality-probe.mjs +96 -0
  4. package/scripts/generate-voice-runtime-manifest.mjs +300 -0
  5. package/scripts/generate-voice-runtime-manifest.test.mjs +135 -0
  6. package/scripts/native-binary-arch.mjs +30 -13
  7. package/scripts/native-binary-arch.test.mjs +40 -4
  8. package/scripts/plan-voice-runtime-release.mjs +73 -0
  9. package/scripts/prepare-native-assets.mjs +50 -9
  10. package/scripts/prune-embedding-runtime.mjs +3 -3
  11. package/scripts/sync-voice-runtime-manifest.mjs +46 -0
  12. package/scripts/verify-release-assets.mjs +106 -6
  13. package/scripts/verify-voice-runtime-release.mjs +89 -0
  14. package/scripts/voice-runtime-config.json +52 -0
  15. package/src/app.mjs +42 -5
  16. package/src/headless-exec.mjs +58 -32
  17. package/src/headless-exec.test.mjs +74 -13
  18. package/src/rules/lead/01-general.md +2 -0
  19. package/src/rules/shared/10-tool-workflow.md +7 -4
  20. package/src/rules/shared/30-exploration.md +5 -1
  21. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +28 -9
  22. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +6 -1
  23. package/src/runtime/agent/orchestrator/config.mjs +53 -18
  24. package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +139 -0
  25. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +34 -5
  26. package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -10
  27. package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +5 -0
  28. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +98 -0
  29. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +90 -0
  30. package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +67 -16
  31. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +4 -82
  32. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +0 -5
  33. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +62 -121
  34. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +48 -0
  35. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +215 -0
  36. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +44 -48
  37. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +54 -31
  38. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +56 -2
  39. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +34 -2
  40. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +61 -1
  41. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -1
  42. package/src/runtime/agent/orchestrator/providers/gemini.mjs +61 -15
  43. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +120 -1
  44. package/src/runtime/agent/orchestrator/providers/lib/env-utils.mjs +9 -0
  45. package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +0 -3
  46. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +187 -84
  47. package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +32 -9
  48. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +78 -33
  49. package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +21 -0
  50. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +36 -36
  51. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +16 -6
  52. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +50 -213
  53. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +135 -126
  54. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +20 -11
  55. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +112 -36
  56. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +220 -50
  57. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +112 -9
  58. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +11 -0
  59. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +130 -60
  60. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
  61. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +28 -10
  62. package/src/runtime/agent/orchestrator/providers/registry.mjs +122 -95
  63. package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +69 -3
  64. package/src/runtime/agent/orchestrator/providers/tool-stream-state.mjs +4 -22
  65. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +7 -14
  66. package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +128 -0
  67. package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +5 -0
  68. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +27 -48
  69. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +19 -14
  70. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +9 -43
  71. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +5 -1
  72. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +43 -5
  73. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -2
  74. package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +325 -0
  75. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +599 -195
  76. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +22 -0
  77. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +9 -2
  78. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +2 -0
  79. package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
  80. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +26 -16
  81. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +23 -11
  82. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +118 -3
  83. package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +332 -0
  84. package/src/runtime/agent/orchestrator/session/store.mjs +38 -1
  85. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +27 -6
  86. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -20
  87. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +291 -128
  88. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -9
  89. package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +224 -0
  90. package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +53 -3
  91. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +255 -25
  92. package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +87 -0
  93. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +1 -1
  94. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +132 -9
  95. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +10 -0
  96. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-pattern-fanout.mjs +29 -3
  97. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +14 -0
  99. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +180 -31
  100. package/src/runtime/agent/orchestrator/tools/builtin/local-search-telemetry.mjs +4 -1
  101. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +32 -5
  102. package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +5 -2
  103. package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +10 -1
  104. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +38 -10
  105. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +12 -0
  106. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +31 -1
  107. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +11 -1
  108. package/src/runtime/agent/orchestrator/tools/builtin/read-windows.mjs +22 -4
  109. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +6 -8
  110. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +25 -24
  111. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +152 -133
  112. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +114 -42
  113. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +129 -15
  114. package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +72 -0
  115. package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +7 -8
  116. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +4 -4
  117. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +201 -19
  118. package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +26 -3
  119. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +10 -1
  120. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +55 -0
  121. package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +115 -25
  122. package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +268 -0
  123. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -3
  124. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +296 -46
  125. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +104 -0
  126. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +136 -0
  127. package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +127 -38
  128. package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +661 -0
  129. package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +350 -9
  130. package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +418 -44
  131. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +27 -3
  132. package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +552 -0
  133. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +65 -15
  134. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
  135. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +284 -109
  136. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +51 -21
  137. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +44 -27
  138. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +6 -1
  139. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
  140. package/src/runtime/attachments/store.mjs +11 -3
  141. package/src/runtime/channels/data/voice-runtime-manifest.json +46 -70
  142. package/src/runtime/channels/lib/event-queue.mjs +51 -41
  143. package/src/runtime/channels/lib/scheduler.mjs +23 -28
  144. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -17
  145. package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +17 -0
  146. package/src/runtime/channels/lib/webhook.mjs +39 -20
  147. package/src/runtime/channels/lib/whisper-server.mjs +46 -1
  148. package/src/runtime/channels/lib/worker-main.mjs +3 -1
  149. package/src/runtime/media/adapters/gemini-video.mjs +20 -4
  150. package/src/runtime/media/jobs.mjs +13 -0
  151. package/src/runtime/media/renditions.mjs +7 -1
  152. package/src/runtime/media/store.mjs +42 -22
  153. package/src/runtime/memory/index.mjs +4 -9
  154. package/src/runtime/memory/lib/agent-ipc.mjs +1 -6
  155. package/src/runtime/memory/lib/archive-security.test.mjs +21 -1
  156. package/src/runtime/memory/lib/core-memory-store.mjs +28 -50
  157. package/src/runtime/memory/lib/embedding-model-config.mjs +37 -1
  158. package/src/runtime/memory/lib/embedding-model-config.test.mjs +33 -0
  159. package/src/runtime/memory/lib/embedding-provider.mjs +34 -12
  160. package/src/runtime/memory/lib/embedding-worker.mjs +37 -5
  161. package/src/runtime/memory/lib/ko-morph.mjs +46 -15
  162. package/src/runtime/memory/lib/memory-action-handlers.mjs +18 -1
  163. package/src/runtime/memory/lib/memory-cycle1.mjs +1 -18
  164. package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -18
  165. package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +98 -45
  166. package/src/runtime/memory/lib/memory-cycle2-shared.mjs +71 -0
  167. package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +78 -0
  168. package/src/runtime/memory/lib/memory-cycle2.mjs +37 -3
  169. package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
  170. package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +50 -0
  171. package/src/runtime/memory/lib/memory-recall-store.mjs +9 -24
  172. package/src/runtime/memory/lib/memory.mjs +50 -12
  173. package/src/runtime/memory/lib/pg/process.mjs +1 -10
  174. package/src/runtime/memory/lib/pg/supervisor.mjs +1 -9
  175. package/src/runtime/memory/lib/query-handlers.mjs +2 -4
  176. package/src/runtime/memory/lib/session-ingest-runtime.mjs +2 -2
  177. package/src/runtime/shared/background-tasks.mjs +15 -0
  178. package/src/runtime/shared/background-tasks.test.mjs +78 -0
  179. package/src/runtime/shared/child-guardian.mjs +2 -10
  180. package/src/runtime/shared/pid-liveness.mjs +27 -0
  181. package/src/runtime/shared/pristine-execution-contract.json +0 -1
  182. package/src/runtime/shared/runtime-root.mjs +1 -11
  183. package/src/runtime/shared/service-discovery.mjs +1 -17
  184. package/src/runtime/shared/singleton-owner.mjs +1 -11
  185. package/src/runtime/shared/staged-update.mjs +2 -12
  186. package/src/runtime/shared/tool-status.mjs +5 -1
  187. package/src/runtime/shared/tool-surface.mjs +15 -7
  188. package/src/runtime/shared/turn-snapshot-store.mjs +168 -0
  189. package/src/runtime/shared/turn-snapshot.mjs +137 -13
  190. package/src/runtime/shared/turn-worktree-snapshot.mjs +110 -18
  191. package/src/runtime/shared/webhook-session-run.mjs +16 -2
  192. package/src/runtime/web-search/lib/http-fetch.mjs +34 -4
  193. package/src/runtime/web-search/lib/state.mjs +15 -15
  194. package/src/runtime/web-search/lib/web-tools.mjs +46 -5
  195. package/src/session-runtime/context-status.mjs +4 -12
  196. package/src/session-runtime/runtime-core.mjs +20 -1
  197. package/src/session-runtime/session-lifecycle.mjs +9 -0
  198. package/src/standalone/agent-dispatch-broker.mjs +33 -2
  199. package/src/standalone/agent-tool/job-task-reconcile.mjs +78 -0
  200. package/src/standalone/agent-tool/notify.mjs +3 -1
  201. package/src/standalone/agent-tool/spawn-flow.mjs +52 -70
  202. package/src/standalone/agent-tool/tag-registry.mjs +116 -33
  203. package/src/standalone/agent-tool/tool-def.mjs +2 -2
  204. package/src/standalone/agent-tool/worker-index.mjs +8 -11
  205. package/src/standalone/agent-tool/worker-rows.mjs +4 -1
  206. package/src/standalone/agent-tool.mjs +20 -55
  207. package/src/standalone/agent-watchdog-registry.mjs +28 -4
  208. package/src/standalone/channel-client.mjs +47 -15
  209. package/src/standalone/channel-transport.mjs +44 -25
  210. package/src/standalone/daemon.mjs +31 -2
  211. package/src/standalone/hook-bus/config.mjs +25 -9
  212. package/src/standalone/hook-bus/handlers.mjs +16 -52
  213. package/src/standalone/hook-bus.mjs +29 -23
  214. package/src/standalone/memory-runtime-proxy.mjs +1 -16
  215. package/src/standalone/session-client.mjs +86 -23
  216. package/src/standalone/session-protocol.mjs +0 -2
  217. package/src/standalone/session-runtime-host-health.test.mjs +5 -4
  218. package/src/standalone/session-runtime-host.mjs +101 -6
  219. package/src/standalone/session-runtime-record.mjs +1 -5
  220. package/src/standalone/session-runtime-worker.mjs +52 -57
  221. package/src/standalone/session-service.mjs +4 -1
  222. package/src/standalone/session-transport.mjs +56 -25
  223. package/src/tui/App.jsx +40 -49
  224. package/src/tui/app/app-view.jsx +14 -21
  225. package/src/tui/app/core-memory-picker.mjs +29 -16
  226. package/src/tui/app/create-app-pickers.mjs +15 -36
  227. package/src/tui/app/extension-pickers.mjs +60 -49
  228. package/src/tui/app/maintenance-pickers.mjs +100 -57
  229. package/src/tui/app/message-selector.mjs +6 -5
  230. package/src/tui/app/model-picker.mjs +30 -13
  231. package/src/tui/app/onboarding-steps.mjs +21 -12
  232. package/src/tui/app/panel-epoch.mjs +56 -0
  233. package/src/tui/app/panel-epoch.test.mjs +1009 -0
  234. package/src/tui/app/panel-surface.mjs +98 -0
  235. package/src/tui/app/project-picker.mjs +30 -29
  236. package/src/tui/app/prompt-submit.mjs +192 -97
  237. package/src/tui/app/prompt-submit.test.mjs +206 -0
  238. package/src/tui/app/provider-setup-picker.mjs +74 -43
  239. package/src/tui/app/resume-picker.mjs +11 -8
  240. package/src/tui/app/route-pickers.mjs +45 -28
  241. package/src/tui/app/settings-picker.mjs +69 -18
  242. package/src/tui/app/shell-layout.mjs +1 -3
  243. package/src/tui/app/slash-commands.mjs +0 -2
  244. package/src/tui/app/slash-dispatch.mjs +4 -2
  245. package/src/tui/app/text-entry-policy.mjs +25 -0
  246. package/src/tui/app/theme-effort-pickers.mjs +11 -16
  247. package/src/tui/app/usage-context-panels.mjs +21 -25
  248. package/src/tui/app/use-global-key-input.mjs +3 -2
  249. package/src/tui/app/use-prompt-draft-flow.mjs +6 -18
  250. package/src/tui/app/use-prompt-handlers.mjs +3 -2
  251. package/src/tui/app/use-welcome-prompt-hint.mjs +17 -3
  252. package/src/tui/components/Picker.jsx +1 -1
  253. package/src/tui/components/TextEntryPanel.jsx +7 -2
  254. package/src/tui/dist/index.mjs +652 -561
  255. package/src/tui/lib/voice-setup.mjs +1 -2
  256. package/src/tui/session/agent-job-feed.mjs +73 -11
  257. package/src/tui/session/session-api-ext.mjs +5 -6
  258. package/src/tui/session/session-api.mjs +1 -0
  259. package/src/tui/session/tool-approval.mjs +15 -5
  260. package/src/tui/session/tui-steering-persist.mjs +33 -19
  261. package/src/tui/session/tui-steering-persist.test.mjs +104 -0
  262. package/src/tui/session-local.mjs +0 -22
  263. package/scripts/.tmp-coverage-report.mjs +0 -96
  264. package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.mjs +0 -57
  265. package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.test.mjs +0 -57
@@ -25,12 +25,9 @@ import { normalizeSlashCommandName } from './slash-commands.mjs';
25
25
  export function createAppPickers({
26
26
  state,
27
27
  store,
28
- setPicker,
28
+ surface,
29
29
  setProviderPrompt,
30
- setChannelPrompt,
31
- setHookPrompt,
32
30
  setSettingsPrompt,
33
- setContextPanel,
34
31
  setOnboardingActive,
35
32
  closeUsagePanel,
36
33
  oauthSubmitRef,
@@ -41,6 +38,7 @@ export function createAppPickers({
41
38
  modelPickerRequestRef,
42
39
  onboardingPrefetchSeqRef,
43
40
  settingsHeavyCacheRef,
41
+ settingsRequestRef,
44
42
  livePickerRef,
45
43
  disabledSkills,
46
44
  setDisabledSkills,
@@ -55,19 +53,16 @@ export function createAppPickers({
55
53
  const { openThemePicker, openEffortPicker } = createThemeEffortPickers({
56
54
  state,
57
55
  store,
58
- setPicker,
56
+ surface,
59
57
  setProviderPrompt,
60
- setChannelPrompt,
61
- setHookPrompt,
62
58
  setSettingsPrompt,
63
- setContextPanel,
64
59
  closeUsagePanel,
65
60
  clean,
66
61
  });
67
- // Resume picker — independent (store + setPicker + two session formatters).
62
+ // Resume picker — independent (store + surface + two session formatters).
68
63
  const { openResumePicker } = createResumePicker({
69
64
  store,
70
- setPicker,
65
+ surface,
71
66
  formatSessionUpdatedAt,
72
67
  formatSessionMessageCount,
73
68
  });
@@ -79,7 +74,7 @@ export function createAppPickers({
79
74
  // the picker's own onSelect closures), so they stay unbound here.
80
75
  const { openMemoryCorePicker } = createCoreMemoryPicker({
81
76
  store,
82
- setPicker,
77
+ surface,
83
78
  setSettingsPrompt,
84
79
  parseMemoryCoreRows,
85
80
  });
@@ -99,11 +94,9 @@ export function createAppPickers({
99
94
  theme,
100
95
  clean,
101
96
  copyToClipboard,
102
- setPicker,
97
+ surface,
103
98
  getPicker: () => livePickerRef.current,
104
99
  setProviderPrompt,
105
- setChannelPrompt,
106
- setHookPrompt,
107
100
  setSettingsPrompt,
108
101
  getDisabledSkills: () => disabledSkills,
109
102
  setDisabledSkills,
@@ -116,12 +109,9 @@ export function createAppPickers({
116
109
  store,
117
110
  theme,
118
111
  formatDuration,
119
- setPicker,
112
+ surface,
120
113
  setProviderPrompt,
121
- setChannelPrompt,
122
- setHookPrompt,
123
114
  setSettingsPrompt,
124
- setContextPanel,
125
115
  closeUsagePanel,
126
116
  });
127
117
  // Onboarding wizard + channel setup picker factories. Instantiated here —
@@ -130,10 +120,8 @@ export function createAppPickers({
130
120
  // that resolve the live binding at call time.
131
121
  const { onboardingWarnReopen, openOnboardingAuthStep } = createOnboardingSteps({
132
122
  store,
133
- setPicker,
123
+ surface,
134
124
  setProviderPrompt,
135
- setChannelPrompt,
136
- setHookPrompt,
137
125
  setSettingsPrompt,
138
126
  setOnboardingActive,
139
127
  onboardingRef,
@@ -145,12 +133,9 @@ export function createAppPickers({
145
133
  });
146
134
  const { openProviderSetupPicker } = createProviderSetupPicker({
147
135
  store,
148
- setPicker,
136
+ surface,
149
137
  setProviderPrompt,
150
- setChannelPrompt,
151
- setHookPrompt,
152
138
  setSettingsPrompt,
153
- setContextPanel,
154
139
  closeUsagePanel,
155
140
  oauthSubmitRef,
156
141
  clearModelCaches,
@@ -158,10 +143,8 @@ export function createAppPickers({
158
143
  const { openModelPicker } = createModelPicker({
159
144
  store,
160
145
  getState: () => state,
161
- setPicker,
146
+ surface,
162
147
  setProviderPrompt,
163
- setChannelPrompt,
164
- setHookPrompt,
165
148
  setSettingsPrompt,
166
149
  providerModelsCacheRef,
167
150
  webSearchModelsCacheRef,
@@ -178,12 +161,9 @@ export function createAppPickers({
178
161
  } = createRoutePickers({
179
162
  store,
180
163
  state,
181
- setPicker,
164
+ surface,
182
165
  setProviderPrompt,
183
- setChannelPrompt,
184
- setHookPrompt,
185
166
  setSettingsPrompt,
186
- setContextPanel,
187
167
  closeUsagePanel,
188
168
  clean,
189
169
  routeLabel,
@@ -195,12 +175,11 @@ export function createAppPickers({
195
175
  const { openSettingsPicker } = createSettingsPicker({
196
176
  store,
197
177
  state,
198
- setPicker,
178
+ surface,
199
179
  setProviderPrompt,
200
- setChannelPrompt,
201
- setHookPrompt,
202
180
  setSettingsPrompt,
203
181
  settingsHeavyCacheRef,
182
+ settingsRequestRef,
204
183
  formatDuration,
205
184
  displayModelName,
206
185
  routeModelLabel,
@@ -227,7 +206,7 @@ export function createAppPickers({
227
206
  state,
228
207
  store,
229
208
  normalizeSlashCommandName,
230
- setContextPanel,
209
+ surface,
231
210
  closeUsagePanel,
232
211
  openModelPicker: (...a) => openModelPicker(...a),
233
212
  modelSwitchNotice,
@@ -2,9 +2,9 @@
2
2
  * extension-pickers.mjs — MCP / Skills / Plugins / Hooks picker cluster.
3
3
  *
4
4
  * Extracted from App.jsx behavior-preservingly as a dependency-injection
5
- * factory. These openers drive setPicker + setSettingsPrompt and read live
6
- * store state, so they can't be pure. Every function body is the original App
7
- * logic verbatim, with closure identifiers threaded through the factory
5
+ * factory. These openers drive the panel surface + setSettingsPrompt and read
6
+ * live store state, so they can't be pure. Every function body is the original
7
+ * App logic verbatim, with closure identifiers threaded through the factory
8
8
  * argument. `disabledSkills` is read fresh via getDisabledSkills() so toggles
9
9
  * observe the latest state at call time. openMcpPicker is exposed for the
10
10
  * plugin-detail enable-mcp path (also aliased on the App body).
@@ -14,11 +14,9 @@ export function createExtensionPickers({
14
14
  theme,
15
15
  clean,
16
16
  copyToClipboard,
17
- setPicker,
17
+ surface,
18
18
  getPicker,
19
19
  setProviderPrompt,
20
- setChannelPrompt,
21
- setHookPrompt,
22
20
  setSettingsPrompt,
23
21
  getDisabledSkills,
24
22
  setDisabledSkills,
@@ -43,6 +41,9 @@ export function createExtensionPickers({
43
41
  };
44
42
 
45
43
  const openMcpServersPicker = async (options = {}) => {
44
+ // Surface claim (panel-surface.mjs): the status read below is a daemon
45
+ // round-trip, so Esc can land before this panel ever paints.
46
+ const own = surface.claim();
46
47
  const status = await mcpStatus();
47
48
  if (!status) return;
48
49
  const servers = status.servers || [];
@@ -72,9 +73,8 @@ export function createExtensionPickers({
72
73
  _enabled: enabled,
73
74
  });
74
75
  }
76
+ if (!own.owns()) return;
75
77
  setProviderPrompt(null);
76
- setChannelPrompt(null);
77
- setHookPrompt(null);
78
78
  setSettingsPrompt(null);
79
79
  const toggleServer = (item) => {
80
80
  if (item._action !== 'server' || !item._server?.name) return;
@@ -101,7 +101,7 @@ export function createExtensionPickers({
101
101
  settle(() => openMcpServersPicker({ highlightValue }));
102
102
  });
103
103
  };
104
- setPicker({
104
+ own.paint({
105
105
  _kind: 'mcp-servers',
106
106
  title: 'MCP servers',
107
107
  description: 'Enable or disable configured MCP servers.',
@@ -114,7 +114,7 @@ export function createExtensionPickers({
114
114
  // Leaving the picker invalidates any in-flight settle.
115
115
  mcpActive = false;
116
116
  mcpEpoch++;
117
- setPicker(null);
117
+ own.close();
118
118
  },
119
119
  });
120
120
  mcpActive = true;
@@ -136,6 +136,7 @@ export function createExtensionPickers({
136
136
  };
137
137
 
138
138
  const openProjectSkillsPicker = async () => {
139
+ const own = surface.claim();
139
140
  const status = await skillsStatus();
140
141
  if (!status) return;
141
142
  const skills = status.skills || [];
@@ -157,27 +158,27 @@ export function createExtensionPickers({
157
158
  _skill: skill,
158
159
  });
159
160
  }
161
+ if (!own.owns()) return;
160
162
  setProviderPrompt(null);
161
- setChannelPrompt(null);
162
- setHookPrompt(null);
163
163
  setSettingsPrompt(null);
164
- setPicker({
164
+ own.paint({
165
165
  title: 'Project skills',
166
166
  description: 'Skills bundled with this project.',
167
167
  items,
168
168
  onSelect: (_value, item) => {
169
- setPicker(null);
169
+ own.close();
170
170
  if (item._action !== 'view') return;
171
171
  openSkillDetailPicker(item._skill);
172
172
  },
173
173
  onCancel: () => {
174
- setPicker(null);
174
+ own.close();
175
175
  void openSkillsPicker();
176
176
  },
177
177
  });
178
178
  };
179
179
 
180
180
  const openSkillsPicker = async (options = {}) => {
181
+ const own = surface.claim();
181
182
  // Reuse the skills list already fetched by the opening call when a toggle
182
183
  // reopens the picker: avoids a store.skillsStatus() round-trip per keypress.
183
184
  let skills;
@@ -211,9 +212,8 @@ export function createExtensionPickers({
211
212
  _enabled: enabled,
212
213
  });
213
214
  }
215
+ if (!own.owns()) return;
214
216
  setProviderPrompt(null);
215
- setChannelPrompt(null);
216
- setHookPrompt(null);
217
217
  setSettingsPrompt(null);
218
218
  const toggleSkill = (item) => {
219
219
  if (item._action !== 'skill' || !item._skill?.name) return;
@@ -227,7 +227,7 @@ export function createExtensionPickers({
227
227
  );
228
228
  openSkillsPicker({ highlightValue: name, disabledOverride: next, skills });
229
229
  };
230
- setPicker({
230
+ own.paint({
231
231
  _kind: 'skills',
232
232
  title: 'Skills',
233
233
  description: 'Enable or disable project skills.',
@@ -237,14 +237,17 @@ export function createExtensionPickers({
237
237
  onLeft: (item) => toggleSkill(item),
238
238
  onRight: (item) => toggleSkill(item),
239
239
  onCancel: () => {
240
- setPicker(null);
240
+ own.close();
241
241
  },
242
242
  });
243
243
  };
244
244
 
245
245
  const openSkillDetailPicker = (skill) => {
246
+ // Synchronous detail panel for an Enter on a skill row: an ordinary claimed
247
+ // paint — nothing async precedes it, and the claim proves it anyway.
248
+ const own = surface.claim();
246
249
  const disabled = getDisabledSkills().has(skill.name);
247
- setPicker({
250
+ own.paint({
248
251
  title: `Skill · ${skill.name}`,
249
252
  description: clean(skill.description) || 'Enable, disable, or run this skill.',
250
253
  items: [
@@ -262,7 +265,7 @@ export function createExtensionPickers({
262
265
  },
263
266
  ],
264
267
  onSelect: (_value, item) => {
265
- setPicker(null);
268
+ own.close();
266
269
  if (item._action === 'enable') {
267
270
  setDisabledSkills((current) => {
268
271
  const next = new Set(current);
@@ -294,7 +297,7 @@ export function createExtensionPickers({
294
297
  }
295
298
  },
296
299
  onCancel: () => {
297
- setPicker(null);
300
+ own.close();
298
301
  void openSkillsPicker();
299
302
  },
300
303
  });
@@ -312,12 +315,15 @@ export function createExtensionPickers({
312
315
  };
313
316
 
314
317
  const beginAddPlugin = () => {
315
- setPicker(null);
318
+ surface.claim().close();
316
319
  setSettingsPrompt({ kind: 'plugin-add', label: 'Plugin URL', hint: 'Git URL, owner/repo, or local path' });
317
320
  };
318
321
 
319
322
  const openPluginDetailPicker = (p) => {
320
- setPicker({
323
+ // Synchronous detail panel for an Enter on a plugin row: an ordinary
324
+ // claimed paint — the data is already in hand.
325
+ const own = surface.claim();
326
+ own.paint({
321
327
  title: p.title || p.name,
322
328
  description: clean(p.description) || 'Update, MCP, or uninstall this plugin.',
323
329
  items: [
@@ -359,7 +365,8 @@ export function createExtensionPickers({
359
365
  },
360
366
  ],
361
367
  onSelect: (_detailValue, detail) => {
362
- setPicker(null);
368
+ // Clear-and-continue: the reopens below are bound to this keypress.
369
+ own.paint(null);
363
370
  if (detail._action === 'info') {
364
371
  store.pushNotice([
365
372
  `${p.title || p.name}${p.version ? ` ${p.version}` : ''}`,
@@ -372,14 +379,16 @@ export function createExtensionPickers({
372
379
  return;
373
380
  }
374
381
  if (detail._action === 'update') {
382
+ // Post-write delegation: the reopen is bound to the claim of this
383
+ // keypress, so an ack after Esc cannot re-open the plugin list.
375
384
  void store.updatePlugin?.(p)
376
- .then(() => openInstalledPluginsPicker())
385
+ .then(own.defer(() => openInstalledPluginsPicker()))
377
386
  .catch((e) => store.pushNotice(`plugin update failed: ${e?.message || e}`, 'error'));
378
387
  return;
379
388
  }
380
389
  if (detail._action === 'enable-mcp') {
381
390
  void store.enablePluginMcp?.(p)
382
- .then(() => openMcpPicker())
391
+ .then(own.defer(() => openMcpPicker()))
383
392
  .catch((e) => store.pushNotice(`plugin MCP enable failed: ${e?.message || e}`, 'error'));
384
393
  return;
385
394
  }
@@ -397,18 +406,19 @@ export function createExtensionPickers({
397
406
  }
398
407
  if (detail._action === 'uninstall') {
399
408
  void store.removePlugin?.(p)
400
- .then(() => openInstalledPluginsPicker())
409
+ .then(own.defer(() => openInstalledPluginsPicker()))
401
410
  .catch((e) => store.pushNotice(`plugin uninstall failed: ${e?.message || e}`, 'error'));
402
411
  }
403
412
  },
404
413
  onCancel: () => {
405
- setPicker(null);
414
+ own.close();
406
415
  void openInstalledPluginsPicker();
407
416
  },
408
417
  });
409
418
  };
410
419
 
411
420
  const openInstalledPluginsPicker = async () => {
421
+ const own = surface.claim();
412
422
  const status = await pluginStatus();
413
423
  if (!status) return;
414
424
  const plugins = status.plugins || [];
@@ -430,34 +440,33 @@ export function createExtensionPickers({
430
440
  _plugin: plugin,
431
441
  });
432
442
  }
443
+ if (!own.owns()) return;
433
444
  setProviderPrompt(null);
434
- setChannelPrompt(null);
435
- setHookPrompt(null);
436
445
  setSettingsPrompt(null);
437
- setPicker({
446
+ own.paint({
438
447
  title: 'Installed plugins',
439
448
  description: 'Open an installed plugin to manage it.',
440
449
  items,
441
450
  onSelect: (_value, item) => {
442
- setPicker(null);
451
+ own.close();
443
452
  if (item._action !== 'plugin') return;
444
453
  openPluginDetailPicker(item._plugin);
445
454
  },
446
455
  onCancel: () => {
447
- setPicker(null);
456
+ own.close();
448
457
  void openPluginsPicker();
449
458
  },
450
459
  });
451
460
  };
452
461
 
453
462
  const openPluginsPicker = async () => {
463
+ const own = surface.claim();
454
464
  const status = await pluginStatus();
455
465
  if (!status) return;
466
+ if (!own.owns()) return;
456
467
  setProviderPrompt(null);
457
- setChannelPrompt(null);
458
- setHookPrompt(null);
459
468
  setSettingsPrompt(null);
460
- setPicker({
469
+ own.paint({
461
470
  title: 'Plugins',
462
471
  description: 'Add or manage local plugin integrations.',
463
472
  items: [
@@ -475,7 +484,7 @@ export function createExtensionPickers({
475
484
  },
476
485
  ],
477
486
  onSelect: (_value, item) => {
478
- setPicker(null);
487
+ own.close();
479
488
  if (item._action === 'installed') {
480
489
  openInstalledPluginsPicker();
481
490
  return;
@@ -483,12 +492,13 @@ export function createExtensionPickers({
483
492
  if (item._action === 'add') beginAddPlugin();
484
493
  },
485
494
  onCancel: () => {
486
- setPicker(null);
495
+ own.close();
487
496
  },
488
497
  });
489
498
  };
490
499
 
491
500
  const openHooksPicker = async () => {
501
+ const own = surface.claim();
492
502
  let status;
493
503
  try {
494
504
  status = (await store.hooksStatus?.()) || { events: [], recent: [] };
@@ -513,20 +523,21 @@ export function createExtensionPickers({
513
523
  _action: 'noop',
514
524
  }]),
515
525
  ];
526
+ if (!own.owns()) return;
516
527
  setProviderPrompt(null);
517
- setChannelPrompt(null);
518
- setHookPrompt(null);
519
528
  setSettingsPrompt(null);
520
529
  const toggleRule = (item) => {
521
530
  if (item._action !== 'rule') return;
522
- try {
523
- store.setHookRuleEnabled?.(item._rule.index, !item._rule.enabled);
524
- void openHooksPicker();
525
- } catch (e) {
526
- store.pushNotice(`hook toggle failed: ${e?.message || e}`, 'error');
527
- }
531
+ // setHookRuleEnabled is a daemon write: reloading before it resolves
532
+ // re-rendered the OLD rule state (toggle looked ineffective) and a
533
+ // rejection escaped as an unhandled rejection. The reload is bound to
534
+ // the claim of this keypress, so a write acking after Esc cannot re-open
535
+ // the Hooks picker the user just closed.
536
+ void Promise.resolve(store.setHookRuleEnabled?.(item._rule.index, !item._rule.enabled))
537
+ .then(own.defer(() => openHooksPicker()))
538
+ .catch((e) => store.pushNotice(`hook toggle failed: ${e?.message || e}`, 'error'));
528
539
  };
529
- setPicker({
540
+ own.paint({
530
541
  _kind: 'hooks',
531
542
  title: 'Hooks',
532
543
  description: 'Before-tool hook rules; Enter toggles a rule.',
@@ -535,7 +546,7 @@ export function createExtensionPickers({
535
546
  onLeft: (item) => toggleRule(item),
536
547
  onRight: (item) => toggleRule(item),
537
548
  onCancel: () => {
538
- setPicker(null);
549
+ own.close();
539
550
  },
540
551
  });
541
552
  };