@coool/opencode-adaptive 0.0.0-stage → 1.0.1

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 (334) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -2
  3. package/dist/adapters/codex-capacity/index.d.ts +4 -0
  4. package/dist/adapters/codex-capacity/index.js +5 -0
  5. package/dist/adapters/codex-capacity/index.js.map +1 -0
  6. package/dist/adapters/codex-capacity/parse.d.ts +28 -0
  7. package/dist/adapters/codex-capacity/parse.js +146 -0
  8. package/dist/adapters/codex-capacity/parse.js.map +1 -0
  9. package/dist/adapters/codex-capacity/reading.d.ts +9 -0
  10. package/dist/adapters/codex-capacity/reading.js +45 -0
  11. package/dist/adapters/codex-capacity/reading.js.map +1 -0
  12. package/dist/adapters/codex-capacity/sensor.d.ts +23 -0
  13. package/dist/adapters/codex-capacity/sensor.js +30 -0
  14. package/dist/adapters/codex-capacity/sensor.js.map +1 -0
  15. package/dist/adapters/codex-capacity/types.d.ts +31 -0
  16. package/dist/adapters/codex-capacity/types.js +18 -0
  17. package/dist/adapters/codex-capacity/types.js.map +1 -0
  18. package/dist/adapters/opencode/escalation-tool.d.ts +51 -0
  19. package/dist/adapters/opencode/escalation-tool.js +61 -0
  20. package/dist/adapters/opencode/escalation-tool.js.map +1 -0
  21. package/dist/adapters/opencode/events.d.ts +23 -0
  22. package/dist/adapters/opencode/events.js +68 -0
  23. package/dist/adapters/opencode/events.js.map +1 -0
  24. package/dist/adapters/opencode/hooks.d.ts +34 -0
  25. package/dist/adapters/opencode/hooks.js +29 -0
  26. package/dist/adapters/opencode/hooks.js.map +1 -0
  27. package/dist/adapters/opencode/index.d.ts +16 -0
  28. package/dist/adapters/opencode/index.js +9 -0
  29. package/dist/adapters/opencode/index.js.map +1 -0
  30. package/dist/adapters/opencode/inventory.d.ts +30 -0
  31. package/dist/adapters/opencode/inventory.js +53 -0
  32. package/dist/adapters/opencode/inventory.js.map +1 -0
  33. package/dist/adapters/opencode/report.d.ts +91 -0
  34. package/dist/adapters/opencode/report.js +145 -0
  35. package/dist/adapters/opencode/report.js.map +1 -0
  36. package/dist/adapters/opencode/session-ops.d.ts +39 -0
  37. package/dist/adapters/opencode/session-ops.js +32 -0
  38. package/dist/adapters/opencode/session-ops.js.map +1 -0
  39. package/dist/adapters/opencode/storage.d.ts +15 -0
  40. package/dist/adapters/opencode/storage.js +8 -0
  41. package/dist/adapters/opencode/storage.js.map +1 -0
  42. package/dist/adapters/opencode/tier-tool.d.ts +37 -0
  43. package/dist/adapters/opencode/tier-tool.js +72 -0
  44. package/dist/adapters/opencode/tier-tool.js.map +1 -0
  45. package/dist/adapters/opencode/types.d.ts +101 -0
  46. package/dist/adapters/opencode/types.js +2 -0
  47. package/dist/adapters/opencode/types.js.map +1 -0
  48. package/dist/core/compatibility/gate.d.ts +7 -0
  49. package/dist/core/compatibility/gate.js +51 -0
  50. package/dist/core/compatibility/gate.js.map +1 -0
  51. package/dist/core/compatibility/index.d.ts +2 -0
  52. package/dist/core/compatibility/index.js +3 -0
  53. package/dist/core/compatibility/index.js.map +1 -0
  54. package/dist/core/compatibility/types.d.ts +53 -0
  55. package/dist/core/compatibility/types.js +27 -0
  56. package/dist/core/compatibility/types.js.map +1 -0
  57. package/dist/core/scheduler/admit.d.ts +6 -0
  58. package/dist/core/scheduler/admit.js +154 -0
  59. package/dist/core/scheduler/admit.js.map +1 -0
  60. package/dist/core/scheduler/escalate.d.ts +6 -0
  61. package/dist/core/scheduler/escalate.js +45 -0
  62. package/dist/core/scheduler/escalate.js.map +1 -0
  63. package/dist/core/scheduler/index.d.ts +4 -0
  64. package/dist/core/scheduler/index.js +5 -0
  65. package/dist/core/scheduler/index.js.map +1 -0
  66. package/dist/core/scheduler/mode.d.ts +8 -0
  67. package/dist/core/scheduler/mode.js +10 -0
  68. package/dist/core/scheduler/mode.js.map +1 -0
  69. package/dist/core/scheduler/types.d.ts +136 -0
  70. package/dist/core/scheduler/types.js +75 -0
  71. package/dist/core/scheduler/types.js.map +1 -0
  72. package/dist/core/tier-policy/index.d.ts +3 -0
  73. package/dist/core/tier-policy/index.js +4 -0
  74. package/dist/core/tier-policy/index.js.map +1 -0
  75. package/dist/core/tier-policy/resolve.d.ts +18 -0
  76. package/dist/core/tier-policy/resolve.js +52 -0
  77. package/dist/core/tier-policy/resolve.js.map +1 -0
  78. package/dist/core/tier-policy/tier-list.d.ts +10 -0
  79. package/dist/core/tier-policy/tier-list.js +23 -0
  80. package/dist/core/tier-policy/tier-list.js.map +1 -0
  81. package/dist/core/tier-policy/types.d.ts +86 -0
  82. package/dist/core/tier-policy/types.js +49 -0
  83. package/dist/core/tier-policy/types.js.map +1 -0
  84. package/dist/core/work-unit/index.d.ts +3 -0
  85. package/dist/core/work-unit/index.js +4 -0
  86. package/dist/core/work-unit/index.js.map +1 -0
  87. package/dist/core/work-unit/interruption.d.ts +7 -0
  88. package/dist/core/work-unit/interruption.js +12 -0
  89. package/dist/core/work-unit/interruption.js.map +1 -0
  90. package/dist/core/work-unit/tracker.d.ts +53 -0
  91. package/dist/core/work-unit/tracker.js +156 -0
  92. package/dist/core/work-unit/tracker.js.map +1 -0
  93. package/dist/core/work-unit/types.d.ts +151 -0
  94. package/dist/core/work-unit/types.js +23 -0
  95. package/dist/core/work-unit/types.js.map +1 -0
  96. package/dist/execution/continuation/execute.d.ts +18 -0
  97. package/dist/execution/continuation/execute.js +31 -0
  98. package/dist/execution/continuation/execute.js.map +1 -0
  99. package/dist/execution/continuation/index.d.ts +4 -0
  100. package/dist/execution/continuation/index.js +4 -0
  101. package/dist/execution/continuation/index.js.map +1 -0
  102. package/dist/execution/continuation/plan.d.ts +12 -0
  103. package/dist/execution/continuation/plan.js +18 -0
  104. package/dist/execution/continuation/plan.js.map +1 -0
  105. package/dist/execution/continuation/prompts.d.ts +7 -0
  106. package/dist/execution/continuation/prompts.js +8 -0
  107. package/dist/execution/continuation/prompts.js.map +1 -0
  108. package/dist/execution/continuation/types.d.ts +68 -0
  109. package/dist/execution/continuation/types.js +15 -0
  110. package/dist/execution/continuation/types.js.map +1 -0
  111. package/dist/execution/escalation/index.d.ts +1 -0
  112. package/dist/execution/escalation/index.js +2 -0
  113. package/dist/execution/escalation/index.js.map +1 -0
  114. package/dist/execution/escalation/types.d.ts +30 -0
  115. package/dist/execution/escalation/types.js +49 -0
  116. package/dist/execution/escalation/types.js.map +1 -0
  117. package/dist/execution/index.d.ts +4 -0
  118. package/dist/execution/index.js +5 -0
  119. package/dist/execution/index.js.map +1 -0
  120. package/dist/execution/parking/index.d.ts +2 -0
  121. package/dist/execution/parking/index.js +2 -0
  122. package/dist/execution/parking/index.js.map +1 -0
  123. package/dist/execution/parking/plan.d.ts +39 -0
  124. package/dist/execution/parking/plan.js +34 -0
  125. package/dist/execution/parking/plan.js.map +1 -0
  126. package/dist/execution/retry/decide.d.ts +29 -0
  127. package/dist/execution/retry/decide.js +75 -0
  128. package/dist/execution/retry/decide.js.map +1 -0
  129. package/dist/execution/retry/index.d.ts +2 -0
  130. package/dist/execution/retry/index.js +3 -0
  131. package/dist/execution/retry/index.js.map +1 -0
  132. package/dist/execution/retry/types.d.ts +64 -0
  133. package/dist/execution/retry/types.js +9 -0
  134. package/dist/execution/retry/types.js.map +1 -0
  135. package/dist/gui-experimental.cjs +1 -0
  136. package/dist/gui.d.ts +16 -0
  137. package/dist/gui.js +73 -0
  138. package/dist/gui.js.map +1 -0
  139. package/dist/health/capacity/index.d.ts +3 -0
  140. package/dist/health/capacity/index.js +4 -0
  141. package/dist/health/capacity/index.js.map +1 -0
  142. package/dist/health/capacity/merge.d.ts +57 -0
  143. package/dist/health/capacity/merge.js +154 -0
  144. package/dist/health/capacity/merge.js.map +1 -0
  145. package/dist/health/capacity/registry.d.ts +40 -0
  146. package/dist/health/capacity/registry.js +106 -0
  147. package/dist/health/capacity/registry.js.map +1 -0
  148. package/dist/health/capacity/types.d.ts +73 -0
  149. package/dist/health/capacity/types.js +6 -0
  150. package/dist/health/capacity/types.js.map +1 -0
  151. package/dist/health/circuit-breaker/breaker.d.ts +91 -0
  152. package/dist/health/circuit-breaker/breaker.js +150 -0
  153. package/dist/health/circuit-breaker/breaker.js.map +1 -0
  154. package/dist/health/circuit-breaker/index.d.ts +2 -0
  155. package/dist/health/circuit-breaker/index.js +3 -0
  156. package/dist/health/circuit-breaker/index.js.map +1 -0
  157. package/dist/health/circuit-breaker/types.d.ts +52 -0
  158. package/dist/health/circuit-breaker/types.js +13 -0
  159. package/dist/health/circuit-breaker/types.js.map +1 -0
  160. package/dist/health/classifier/classify.d.ts +8 -0
  161. package/dist/health/classifier/classify.js +245 -0
  162. package/dist/health/classifier/classify.js.map +1 -0
  163. package/dist/health/classifier/index.d.ts +2 -0
  164. package/dist/health/classifier/index.js +3 -0
  165. package/dist/health/classifier/index.js.map +1 -0
  166. package/dist/health/classifier/types.d.ts +71 -0
  167. package/dist/health/classifier/types.js +27 -0
  168. package/dist/health/classifier/types.js.map +1 -0
  169. package/dist/health/index.d.ts +4 -0
  170. package/dist/health/index.js +5 -0
  171. package/dist/health/index.js.map +1 -0
  172. package/dist/health/resource-health/index.d.ts +4 -0
  173. package/dist/health/resource-health/index.js +5 -0
  174. package/dist/health/resource-health/index.js.map +1 -0
  175. package/dist/health/resource-health/key.d.ts +18 -0
  176. package/dist/health/resource-health/key.js +54 -0
  177. package/dist/health/resource-health/key.js.map +1 -0
  178. package/dist/health/resource-health/registry.d.ts +46 -0
  179. package/dist/health/resource-health/registry.js +276 -0
  180. package/dist/health/resource-health/registry.js.map +1 -0
  181. package/dist/health/resource-health/transitions.d.ts +9 -0
  182. package/dist/health/resource-health/transitions.js +157 -0
  183. package/dist/health/resource-health/transitions.js.map +1 -0
  184. package/dist/health/resource-health/types.d.ts +122 -0
  185. package/dist/health/resource-health/types.js +44 -0
  186. package/dist/health/resource-health/types.js.map +1 -0
  187. package/dist/index.d.ts +11 -0
  188. package/dist/index.js +105 -0
  189. package/dist/index.js.map +1 -0
  190. package/dist/plugin-id.d.ts +2 -0
  191. package/dist/plugin-id.js +3 -0
  192. package/dist/plugin-id.js.map +1 -0
  193. package/dist/rpc/definition.d.ts +123 -0
  194. package/dist/rpc/definition.js +130 -0
  195. package/dist/rpc/definition.js.map +1 -0
  196. package/dist/rpc/handlers.d.ts +53 -0
  197. package/dist/rpc/handlers.js +125 -0
  198. package/dist/rpc/handlers.js.map +1 -0
  199. package/dist/rpc/status.d.ts +42 -0
  200. package/dist/rpc/status.js +45 -0
  201. package/dist/rpc/status.js.map +1 -0
  202. package/dist/rpc.d.ts +10 -0
  203. package/dist/rpc.js +8 -0
  204. package/dist/rpc.js.map +1 -0
  205. package/dist/state/capacity/index.d.ts +3 -0
  206. package/dist/state/capacity/index.js +4 -0
  207. package/dist/state/capacity/index.js.map +1 -0
  208. package/dist/state/capacity/parse.d.ts +2 -0
  209. package/dist/state/capacity/parse.js +122 -0
  210. package/dist/state/capacity/parse.js.map +1 -0
  211. package/dist/state/capacity/store.d.ts +18 -0
  212. package/dist/state/capacity/store.js +78 -0
  213. package/dist/state/capacity/store.js.map +1 -0
  214. package/dist/state/capacity/types.d.ts +37 -0
  215. package/dist/state/capacity/types.js +11 -0
  216. package/dist/state/capacity/types.js.map +1 -0
  217. package/dist/state/health/index.d.ts +4 -0
  218. package/dist/state/health/index.js +4 -0
  219. package/dist/state/health/index.js.map +1 -0
  220. package/dist/state/health/parse.d.ts +2 -0
  221. package/dist/state/health/parse.js +137 -0
  222. package/dist/state/health/parse.js.map +1 -0
  223. package/dist/state/health/store.d.ts +17 -0
  224. package/dist/state/health/store.js +69 -0
  225. package/dist/state/health/store.js.map +1 -0
  226. package/dist/state/health/types.d.ts +23 -0
  227. package/dist/state/health/types.js +5 -0
  228. package/dist/state/health/types.js.map +1 -0
  229. package/dist/state/parked-work/index.d.ts +4 -0
  230. package/dist/state/parked-work/index.js +4 -0
  231. package/dist/state/parked-work/index.js.map +1 -0
  232. package/dist/state/parked-work/parse.d.ts +6 -0
  233. package/dist/state/parked-work/parse.js +83 -0
  234. package/dist/state/parked-work/parse.js.map +1 -0
  235. package/dist/state/parked-work/store.d.ts +17 -0
  236. package/dist/state/parked-work/store.js +98 -0
  237. package/dist/state/parked-work/store.js.map +1 -0
  238. package/dist/state/parked-work/types.d.ts +41 -0
  239. package/dist/state/parked-work/types.js +7 -0
  240. package/dist/state/parked-work/types.js.map +1 -0
  241. package/dist/state/receipts/bound.d.ts +14 -0
  242. package/dist/state/receipts/bound.js +77 -0
  243. package/dist/state/receipts/bound.js.map +1 -0
  244. package/dist/state/receipts/index.d.ts +6 -0
  245. package/dist/state/receipts/index.js +5 -0
  246. package/dist/state/receipts/index.js.map +1 -0
  247. package/dist/state/receipts/parse.d.ts +6 -0
  248. package/dist/state/receipts/parse.js +226 -0
  249. package/dist/state/receipts/parse.js.map +1 -0
  250. package/dist/state/receipts/store.d.ts +34 -0
  251. package/dist/state/receipts/store.js +148 -0
  252. package/dist/state/receipts/store.js.map +1 -0
  253. package/dist/state/receipts/types.d.ts +61 -0
  254. package/dist/state/receipts/types.js +12 -0
  255. package/dist/state/receipts/types.js.map +1 -0
  256. package/dist/state/session-modes/index.d.ts +4 -0
  257. package/dist/state/session-modes/index.js +4 -0
  258. package/dist/state/session-modes/index.js.map +1 -0
  259. package/dist/state/session-modes/parse.d.ts +8 -0
  260. package/dist/state/session-modes/parse.js +62 -0
  261. package/dist/state/session-modes/parse.js.map +1 -0
  262. package/dist/state/session-modes/store.d.ts +20 -0
  263. package/dist/state/session-modes/store.js +87 -0
  264. package/dist/state/session-modes/store.js.map +1 -0
  265. package/dist/state/session-modes/types.d.ts +41 -0
  266. package/dist/state/session-modes/types.js +7 -0
  267. package/dist/state/session-modes/types.js.map +1 -0
  268. package/dist/state/settings/index.d.ts +4 -0
  269. package/dist/state/settings/index.js +4 -0
  270. package/dist/state/settings/index.js.map +1 -0
  271. package/dist/state/settings/parse.d.ts +33 -0
  272. package/dist/state/settings/parse.js +261 -0
  273. package/dist/state/settings/parse.js.map +1 -0
  274. package/dist/state/settings/store.d.ts +21 -0
  275. package/dist/state/settings/store.js +65 -0
  276. package/dist/state/settings/store.js.map +1 -0
  277. package/dist/state/settings/types.d.ts +62 -0
  278. package/dist/state/settings/types.js +12 -0
  279. package/dist/state/settings/types.js.map +1 -0
  280. package/dist/state/tier-requests/index.d.ts +4 -0
  281. package/dist/state/tier-requests/index.js +4 -0
  282. package/dist/state/tier-requests/index.js.map +1 -0
  283. package/dist/state/tier-requests/parse.d.ts +3 -0
  284. package/dist/state/tier-requests/parse.js +53 -0
  285. package/dist/state/tier-requests/parse.js.map +1 -0
  286. package/dist/state/tier-requests/store.d.ts +27 -0
  287. package/dist/state/tier-requests/store.js +96 -0
  288. package/dist/state/tier-requests/store.js.map +1 -0
  289. package/dist/state/tier-requests/types.d.ts +30 -0
  290. package/dist/state/tier-requests/types.js +7 -0
  291. package/dist/state/tier-requests/types.js.map +1 -0
  292. package/dist/supervisor/index.d.ts +4 -0
  293. package/dist/supervisor/index.js +4 -0
  294. package/dist/supervisor/index.js.map +1 -0
  295. package/dist/supervisor/log.d.ts +10 -0
  296. package/dist/supervisor/log.js +14 -0
  297. package/dist/supervisor/log.js.map +1 -0
  298. package/dist/supervisor/supervisor.d.ts +86 -0
  299. package/dist/supervisor/supervisor.js +1629 -0
  300. package/dist/supervisor/supervisor.js.map +1 -0
  301. package/dist/supervisor/types.d.ts +49 -0
  302. package/dist/supervisor/types.js +3 -0
  303. package/dist/supervisor/types.js.map +1 -0
  304. package/dist/supervisor/view.d.ts +3 -0
  305. package/dist/supervisor/view.js +4 -0
  306. package/dist/supervisor/view.js.map +1 -0
  307. package/dist/tui.d.ts +15 -0
  308. package/dist/tui.js +256 -0
  309. package/dist/tui.js.map +1 -0
  310. package/dist/ui/gui/api.d.ts +17 -0
  311. package/dist/ui/gui/api.js +21 -0
  312. package/dist/ui/gui/api.js.map +1 -0
  313. package/dist/ui/gui/view.d.ts +26 -0
  314. package/dist/ui/gui/view.js +49 -0
  315. package/dist/ui/gui/view.js.map +1 -0
  316. package/dist/ui/tui/controller.d.ts +97 -0
  317. package/dist/ui/tui/controller.js +415 -0
  318. package/dist/ui/tui/controller.js.map +1 -0
  319. package/dist/ui/tui/menu.d.ts +55 -0
  320. package/dist/ui/tui/menu.js +176 -0
  321. package/dist/ui/tui/menu.js.map +1 -0
  322. package/dist/ui/tui/options.d.ts +19 -0
  323. package/dist/ui/tui/options.js +17 -0
  324. package/dist/ui/tui/options.js.map +1 -0
  325. package/dist/ui/tui/receipts.d.ts +21 -0
  326. package/dist/ui/tui/receipts.js +112 -0
  327. package/dist/ui/tui/receipts.js.map +1 -0
  328. package/dist/ui/tui/status.d.ts +22 -0
  329. package/dist/ui/tui/status.js +92 -0
  330. package/dist/ui/tui/status.js.map +1 -0
  331. package/dist/ui/tui/tiers.d.ts +51 -0
  332. package/dist/ui/tui/tiers.js +108 -0
  333. package/dist/ui/tui/tiers.js.map +1 -0
  334. package/package.json +85 -4
package/dist/tui.js ADDED
@@ -0,0 +1,256 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/solid/jsx-runtime";
2
+ /** @jsxImportSource @opentui/solid */
3
+ // The pragma pins the JSX runtime for this file. The host compiles plugin
4
+ // sources in a cache dir where its loader's tsconfig lookup finds nothing and
5
+ // its default (react/jsx-runtime) fails to resolve there (empirical, sibling
6
+ // @coool plugins); our own tsc build reads the same pragma, so dist/tui.js
7
+ // imports @opentui/solid/jsx-runtime — the renderer the slot render runs on.
8
+ /**
9
+ * TUI entrypoint (ADP-033): registers the `/adaptive` slash command and
10
+ * drives the dialog-based flows through typed RPC, plus the footer status
11
+ * chip rendered through host UI slots. The TUI is a thin view — no local
12
+ * settings state beyond the chip's reactive mode mirror; the server stays
13
+ * the single source of truth. This file is the repo's one JSX surface.
14
+ */
15
+ import { define } from "@opencode/plugin/tui/plugin";
16
+ import { TextAttributes } from "@opentui/core";
17
+ import { createStore } from "solid-js/store";
18
+ import { PLUGIN_ID } from "./plugin-id.js";
19
+ import { adaptiveRpc } from "./rpc/definition.js";
20
+ import { parseMode } from "./state/settings/index.js";
21
+ import { runAdaptive } from "./ui/tui/controller.js";
22
+ import { errorText } from "./ui/tui/options.js";
23
+ /**
24
+ * Label/color mapping for the footer chip. Pure so the mapping is testable
25
+ * without a JSX runtime: active is green (supervision engaged), observe is
26
+ * the neutral default (visible but calm), off is a soft red — noticeable,
27
+ * but calmer than full-brightness ANSI red, which reads as an error.
28
+ */
29
+ export function chipFor(mode) {
30
+ switch (mode) {
31
+ case "active":
32
+ return { text: "Adaptive · active", fg: "green" };
33
+ case "observe":
34
+ return { text: "Adaptive · observe", fg: null };
35
+ case "off":
36
+ return { text: "Adaptive · off", fg: "#e57373" };
37
+ }
38
+ }
39
+ const BOOT_FETCH_RETRIES = 5;
40
+ const BOOT_FETCH_RETRY_MS = 2000;
41
+ export default define({
42
+ id: PLUGIN_ID,
43
+ setup(context) {
44
+ const client = context.client.rpc(adaptiveRpc);
45
+ // RPC handlers resolve per location: a location-less call never reaches a
46
+ // project-local plugin's handlers ("RPC is unavailable", verified live in
47
+ // the sibling @coool plugins), so every call carries the TUI's location.
48
+ const at = () => ({ location: context.location });
49
+ const api = {
50
+ settings: () => client["settings.get"](undefined, at()),
51
+ setSettings: (patch) => client["settings.set"](patch, at()),
52
+ status: () => client["status.get"](undefined, at()),
53
+ receipts: () => client["receipts.list"](undefined, at()),
54
+ sessionMode: (sessionID) => client["session-mode.get"]({ sessionID }, at()).then((out) => out.mode),
55
+ setSessionMode: (sessionID, mode) => client["session-mode.set"]({ sessionID, mode }, at()),
56
+ tierRequest: (sessionID) => client["tier-request.get"]({ sessionID }, at()).then((out) => out.tier),
57
+ setTierRequest: (sessionID, tier) => client["tier-request.set"]({ sessionID, tier }, at()),
58
+ };
59
+ const view = {
60
+ select: (options) => context.ui.dialog.select(options),
61
+ selectPaged: (spec) =>
62
+ // Footer actions flip pages: triggering one closes the dialog and
63
+ // resolves with the action instead of a pick; the controller loop
64
+ // then reopens the browser at the new offset. The host resolves its
65
+ // own select promise undefined on the clear() — resolving our
66
+ // promise first-come means the action result wins.
67
+ new Promise((resolve, reject) => {
68
+ void context.ui.dialog
69
+ .select({
70
+ title: spec.title,
71
+ options: spec.options,
72
+ actions: spec.actions.map((action) => ({
73
+ bind: action.bind,
74
+ title: action.title,
75
+ selection: "none",
76
+ onTrigger: () => {
77
+ context.ui.dialog.clear();
78
+ resolve({ picked: undefined, action: action.value });
79
+ },
80
+ })),
81
+ })
82
+ .then((picked) => {
83
+ resolve({ picked, action: undefined });
84
+ }, reject);
85
+ }),
86
+ confirm: (options) => context.ui.dialog.confirm(options),
87
+ alert: (options) => context.ui.dialog.alert(options),
88
+ table: (spec) =>
89
+ // The host has no table dialog; a custom dialog render gives the
90
+ // column grid its own layout. The default dialog size is too narrow
91
+ // for the column grid (rows wrap mid-token), so the dialog is
92
+ // widened to xlarge right after it opens. Escape closes the dialog
93
+ // and fires onClose, which resolves the view promise.
94
+ new Promise((resolve) => {
95
+ context.ui.dialog.show(() => (
96
+ // Styled to match the host's own dialogs (bold title, dim esc
97
+ // hint). The host dialog panel already insets the content, so
98
+ // the box adds no top padding of its own; a blank spacer row
99
+ // separates the title row from the table.
100
+ _jsxs("box", { style: { flexDirection: "column", paddingBottom: 1, paddingX: 2 }, children: [_jsxs("box", { style: { flexDirection: "row", justifyContent: "space-between" }, children: [_jsx("text", { style: { attributes: TextAttributes.BOLD }, children: spec.title }), _jsx("text", { style: { attributes: TextAttributes.DIM }, children: "esc" })] }), _jsx("text", { children: " " }), spec.lines.map((line) => (_jsx("text", { children: line })))] })), () => {
101
+ resolve();
102
+ });
103
+ context.ui.dialog.set({ size: "xlarge" });
104
+ }),
105
+ toast: (message, variant) => {
106
+ context.ui.toast.show({ message, variant });
107
+ },
108
+ };
109
+ // Real OpenCode models for the tier editor search (ADP-033); structural
110
+ // summaries keep the view models free of client types.
111
+ const models = () => {
112
+ const list = context.data.location.model.list(context.location);
113
+ return (list ?? []).map((info) => ({
114
+ providerID: info.providerID,
115
+ modelID: info.modelID,
116
+ name: info.name,
117
+ variants: info.variants.map((variant) => variant.id),
118
+ }));
119
+ };
120
+ context.keymap.layer(() => ({
121
+ // "global" opts out of input-mode scoping: the default ("base") binds
122
+ // the commands to one OpenCode input mode, which kept the palette from
123
+ // matching them (the sibling @coool plugins register the same way).
124
+ mode: "global",
125
+ commands: [
126
+ {
127
+ id: "coool.adaptive.menu",
128
+ title: "Adaptive",
129
+ group: "Adaptive",
130
+ palette: true,
131
+ // Surface the command in the palette's suggested row: discovery was
132
+ // the reason users never found the menu at all.
133
+ suggested: true,
134
+ slash: { name: "adaptive" },
135
+ run: () => {
136
+ // Inside a session the flows steer that session's mode override
137
+ // (ADP-054); on home they patch the global mode.
138
+ const current = context.ui.router.current();
139
+ const sessionID = current.type === "session" ? current.sessionID : undefined;
140
+ void runAdaptive({
141
+ api,
142
+ view,
143
+ models,
144
+ ...(sessionID !== undefined ? { sessionID } : {}),
145
+ }).catch((error) => {
146
+ view.toast(errorText(error), "error");
147
+ });
148
+ },
149
+ },
150
+ ],
151
+ }));
152
+ // Footer chip state: a reactive mirror of the server's operating mode,
153
+ // fetched once at boot and kept live via the settings-changed event, plus
154
+ // the per-session overrides seen so far (ADP-054).
155
+ const [chip, setChip] = createStore({ mode: null, sessions: {} });
156
+ const fetchMode = async () => {
157
+ try {
158
+ const settings = await api.settings();
159
+ const mode = parseMode(settings.mode);
160
+ if (mode !== undefined)
161
+ setChip("mode", mode);
162
+ return true;
163
+ }
164
+ catch {
165
+ return false;
166
+ }
167
+ };
168
+ /**
169
+ * One session's override, cached so the chip never re-fetches per render.
170
+ * A fetch failure caches `null` (follow global): the chip stays correct
171
+ * for override-less sessions, and a later `session-mode-changed` event
172
+ * refreshes sessions that do carry an override.
173
+ */
174
+ const fetchSessionMode = async (sessionID) => {
175
+ try {
176
+ setChip("sessions", sessionID, await api.sessionMode(sessionID));
177
+ }
178
+ catch {
179
+ setChip("sessions", sessionID, null);
180
+ }
181
+ };
182
+ const bootFetch = async (retriesLeft) => {
183
+ if (await fetchMode())
184
+ return;
185
+ // The server plugin may not be registered yet at TUI boot; retry
186
+ // quietly. Bounded (unlike the sibling plugins' unbounded retry) so the
187
+ // timer loop provably terminates: after the cap the chip stays hidden
188
+ // until the next settings-changed event refreshes it.
189
+ if (retriesLeft > 0) {
190
+ setTimeout(() => void bootFetch(retriesLeft - 1), BOOT_FETCH_RETRY_MS);
191
+ }
192
+ };
193
+ const renderChip = (mode) => {
194
+ if (mode === null)
195
+ return _jsx("text", {});
196
+ const entry = chipFor(mode);
197
+ // The accent-colored span cannot execute in-repo: the terminal renderer
198
+ // cannot start headless and jsdom's HTML parser mangles the intrinsic
199
+ // <text><span> template (null insert parent). The chipFor tests pin the
200
+ // color mapping; the chip render test pins the plain observe shape.
201
+ /* v8 ignore start */
202
+ if (entry.fg === null)
203
+ return _jsx("text", { children: entry.text });
204
+ return (_jsx("text", { children: _jsx("span", { style: { fg: entry.fg }, children: entry.text }) }));
205
+ /* v8 ignore stop */
206
+ };
207
+ const chipNode = (sessionID) => {
208
+ // On home (no session id) the chip shows the global mode; in a session
209
+ // it shows the session's EFFECTIVE mode (override ?? global).
210
+ if (sessionID === undefined)
211
+ return renderChip(chip.mode);
212
+ const override = chip.sessions[sessionID];
213
+ if (override === undefined) {
214
+ // First render for this session: fetch the override; the store write
215
+ // re-renders this node with the fetched value. Until then the global
216
+ // mode is shown.
217
+ void fetchSessionMode(sessionID);
218
+ return renderChip(chip.mode);
219
+ }
220
+ return renderChip(override ?? chip.mode);
221
+ };
222
+ // The home footer slot only exists on the home screen; sessions render
223
+ // the prompt footer — its slot input carries the open session's id, so
224
+ // the chip there shows that session's effective mode (ADP-054).
225
+ const disposeSlots = [
226
+ context.ui.slot({ append: "home.footer.status", render: () => chipNode(undefined) }),
227
+ context.ui.slot({
228
+ append: "prompt.footer.status",
229
+ render: (input) => chipNode(input.sessionID),
230
+ }),
231
+ ];
232
+ const disposeWatch = client.events.on("settings-changed", () => {
233
+ void fetchMode();
234
+ // A global mode change re-renders override-less sessions reactively;
235
+ // cached overrides are re-fetched so a stale mirror cannot stick.
236
+ for (const sessionID of Object.keys(chip.sessions)) {
237
+ void fetchSessionMode(sessionID);
238
+ }
239
+ });
240
+ const disposeSessionWatch = client.events.on("session-mode-changed", (event) => {
241
+ // The event data crosses the wire as an untyped record: a malformed
242
+ // payload is ignored (fail-open) instead of steering a bogus session.
243
+ const sessionID = event.data.sessionID;
244
+ if (typeof sessionID === "string")
245
+ void fetchSessionMode(sessionID);
246
+ });
247
+ void bootFetch(BOOT_FETCH_RETRIES);
248
+ return () => {
249
+ for (const dispose of disposeSlots)
250
+ dispose();
251
+ disposeWatch();
252
+ disposeSessionWatch();
253
+ };
254
+ },
255
+ });
256
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"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","sourcesContent":["/** @jsxImportSource @opentui/solid */\n// The pragma pins the JSX runtime for this file. The host compiles plugin\n// sources in a cache dir where its loader's tsconfig lookup finds nothing and\n// its default (react/jsx-runtime) fails to resolve there (empirical, sibling\n// @coool plugins); our own tsc build reads the same pragma, so dist/tui.js\n// imports @opentui/solid/jsx-runtime — the renderer the slot render runs on.\n/**\n * TUI entrypoint (ADP-033): registers the `/adaptive` slash command and\n * drives the dialog-based flows through typed RPC, plus the footer status\n * chip rendered through host UI slots. The TUI is a thin view — no local\n * settings state beyond the chip's reactive mode mirror; the server stays\n * the single source of truth. This file is the repo's one JSX surface.\n */\nimport { define } from \"@opencode/plugin/tui/plugin\"\nimport type { Context } from \"@opencode/plugin/tui/plugin\"\nimport { TextAttributes } from \"@opentui/core\"\nimport { createStore } from \"solid-js/store\"\nimport { PLUGIN_ID } from \"./plugin-id.js\"\nimport { adaptiveRpc } from \"./rpc/definition.js\"\nimport type { OperatingMode } from \"./core/scheduler/types.js\"\nimport { parseMode } from \"./state/settings/index.js\"\nimport { runAdaptive } from \"./ui/tui/controller.js\"\nimport type { AdaptiveApi, AdaptiveView } from \"./ui/tui/controller.js\"\nimport type { SelectOption } from \"./ui/tui/options.js\"\nimport { errorText } from \"./ui/tui/options.js\"\nimport type { ModelSummary } from \"./ui/tui/tiers.js\"\n\n/** Footer chip appearance: label plus foreground color (null = theme default). */\nexport interface Chip {\n readonly text: string\n readonly fg: string | null\n}\n\n/**\n * Label/color mapping for the footer chip. Pure so the mapping is testable\n * without a JSX runtime: active is green (supervision engaged), observe is\n * the neutral default (visible but calm), off is a soft red — noticeable,\n * but calmer than full-brightness ANSI red, which reads as an error.\n */\nexport function chipFor(mode: OperatingMode): Chip {\n switch (mode) {\n case \"active\":\n return { text: \"Adaptive · active\", fg: \"green\" }\n case \"observe\":\n return { text: \"Adaptive · observe\", fg: null }\n case \"off\":\n return { text: \"Adaptive · off\", fg: \"#e57373\" }\n }\n}\n\nconst BOOT_FETCH_RETRIES = 5\nconst BOOT_FETCH_RETRY_MS = 2000\n\nexport default define({\n id: PLUGIN_ID,\n setup(context: Context) {\n const client = context.client.rpc(adaptiveRpc)\n // RPC handlers resolve per location: a location-less call never reaches a\n // project-local plugin's handlers (\"RPC is unavailable\", verified live in\n // the sibling @coool plugins), so every call carries the TUI's location.\n const at = () => ({ location: context.location })\n const api: AdaptiveApi = {\n settings: () => client[\"settings.get\"](undefined, at()),\n setSettings: (patch) => client[\"settings.set\"](patch, at()),\n status: () => client[\"status.get\"](undefined, at()),\n receipts: () => client[\"receipts.list\"](undefined, at()),\n sessionMode: (sessionID) =>\n client[\"session-mode.get\"]({ sessionID }, at()).then((out) => out.mode),\n setSessionMode: (sessionID, mode) => client[\"session-mode.set\"]({ sessionID, mode }, at()),\n tierRequest: (sessionID) =>\n client[\"tier-request.get\"]({ sessionID }, at()).then((out) => out.tier),\n setTierRequest: (sessionID, tier) => client[\"tier-request.set\"]({ sessionID, tier }, at()),\n }\n const view: AdaptiveView = {\n select: (options) => context.ui.dialog.select(options),\n selectPaged: <Value,>(spec: {\n title: string\n options: readonly SelectOption<Value>[]\n actions: readonly { title: string; value: string; bind: string }[]\n }) =>\n // Footer actions flip pages: triggering one closes the dialog and\n // resolves with the action instead of a pick; the controller loop\n // then reopens the browser at the new offset. The host resolves its\n // own select promise undefined on the clear() — resolving our\n // promise first-come means the action result wins.\n new Promise<{ picked: Value | undefined; action: string | undefined }>(\n (resolve, reject) => {\n void context.ui.dialog\n .select({\n title: spec.title,\n options: spec.options,\n actions: spec.actions.map((action) => ({\n bind: action.bind,\n title: action.title,\n selection: \"none\" as const,\n onTrigger: () => {\n context.ui.dialog.clear()\n resolve({ picked: undefined, action: action.value })\n },\n })),\n })\n .then((picked) => {\n resolve({ picked, action: undefined })\n }, reject)\n },\n ),\n confirm: (options) => context.ui.dialog.confirm(options),\n alert: (options) => context.ui.dialog.alert(options),\n table: (spec) =>\n // The host has no table dialog; a custom dialog render gives the\n // column grid its own layout. The default dialog size is too narrow\n // for the column grid (rows wrap mid-token), so the dialog is\n // widened to xlarge right after it opens. Escape closes the dialog\n // and fires onClose, which resolves the view promise.\n new Promise<void>((resolve) => {\n context.ui.dialog.show(\n () => (\n // Styled to match the host's own dialogs (bold title, dim esc\n // hint). The host dialog panel already insets the content, so\n // the box adds no top padding of its own; a blank spacer row\n // separates the title row from the table.\n <box style={{ flexDirection: \"column\", paddingBottom: 1, paddingX: 2 }}>\n <box style={{ flexDirection: \"row\", justifyContent: \"space-between\" }}>\n <text style={{ attributes: TextAttributes.BOLD }}>{spec.title}</text>\n <text style={{ attributes: TextAttributes.DIM }}>esc</text>\n </box>\n <text> </text>\n {spec.lines.map((line) => (\n <text>{line}</text>\n ))}\n </box>\n ),\n () => {\n resolve()\n },\n )\n context.ui.dialog.set({ size: \"xlarge\" })\n }),\n toast: (message, variant) => {\n context.ui.toast.show({ message, variant })\n },\n }\n // Real OpenCode models for the tier editor search (ADP-033); structural\n // summaries keep the view models free of client types.\n const models = (): readonly ModelSummary[] => {\n const list = context.data.location.model.list(context.location)\n return (list ?? []).map((info) => ({\n providerID: info.providerID,\n modelID: info.modelID,\n name: info.name,\n variants: info.variants.map((variant) => variant.id),\n }))\n }\n context.keymap.layer(() => ({\n // \"global\" opts out of input-mode scoping: the default (\"base\") binds\n // the commands to one OpenCode input mode, which kept the palette from\n // matching them (the sibling @coool plugins register the same way).\n mode: \"global\",\n commands: [\n {\n id: \"coool.adaptive.menu\",\n title: \"Adaptive\",\n group: \"Adaptive\",\n palette: true,\n // Surface the command in the palette's suggested row: discovery was\n // the reason users never found the menu at all.\n suggested: true,\n slash: { name: \"adaptive\" },\n run: () => {\n // Inside a session the flows steer that session's mode override\n // (ADP-054); on home they patch the global mode.\n const current = context.ui.router.current()\n const sessionID = current.type === \"session\" ? current.sessionID : undefined\n void runAdaptive({\n api,\n view,\n models,\n ...(sessionID !== undefined ? { sessionID } : {}),\n }).catch((error: unknown) => {\n view.toast(errorText(error), \"error\")\n })\n },\n },\n ],\n }))\n\n // Footer chip state: a reactive mirror of the server's operating mode,\n // fetched once at boot and kept live via the settings-changed event, plus\n // the per-session overrides seen so far (ADP-054).\n const [chip, setChip] = createStore<{\n mode: OperatingMode | null\n sessions: Record<string, OperatingMode | null>\n }>({ mode: null, sessions: {} })\n\n const fetchMode = async (): Promise<boolean> => {\n try {\n const settings = await api.settings()\n const mode = parseMode(settings.mode)\n if (mode !== undefined) setChip(\"mode\", mode)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * One session's override, cached so the chip never re-fetches per render.\n * A fetch failure caches `null` (follow global): the chip stays correct\n * for override-less sessions, and a later `session-mode-changed` event\n * refreshes sessions that do carry an override.\n */\n const fetchSessionMode = async (sessionID: string): Promise<void> => {\n try {\n setChip(\"sessions\", sessionID, await api.sessionMode(sessionID))\n } catch {\n setChip(\"sessions\", sessionID, null)\n }\n }\n\n const bootFetch = async (retriesLeft: number): Promise<void> => {\n if (await fetchMode()) return\n // The server plugin may not be registered yet at TUI boot; retry\n // quietly. Bounded (unlike the sibling plugins' unbounded retry) so the\n // timer loop provably terminates: after the cap the chip stays hidden\n // until the next settings-changed event refreshes it.\n if (retriesLeft > 0) {\n setTimeout(() => void bootFetch(retriesLeft - 1), BOOT_FETCH_RETRY_MS)\n }\n }\n\n const renderChip = (mode: OperatingMode | null) => {\n if (mode === null) return <text></text>\n const entry = chipFor(mode)\n // The accent-colored span cannot execute in-repo: the terminal renderer\n // cannot start headless and jsdom's HTML parser mangles the intrinsic\n // <text><span> template (null insert parent). The chipFor tests pin the\n // color mapping; the chip render test pins the plain observe shape.\n /* v8 ignore start */\n if (entry.fg === null) return <text>{entry.text}</text>\n return (\n <text>\n <span style={{ fg: entry.fg }}>{entry.text}</span>\n </text>\n )\n /* v8 ignore stop */\n }\n const chipNode = (sessionID: string | undefined) => {\n // On home (no session id) the chip shows the global mode; in a session\n // it shows the session's EFFECTIVE mode (override ?? global).\n if (sessionID === undefined) return renderChip(chip.mode)\n const override = chip.sessions[sessionID]\n if (override === undefined) {\n // First render for this session: fetch the override; the store write\n // re-renders this node with the fetched value. Until then the global\n // mode is shown.\n void fetchSessionMode(sessionID)\n return renderChip(chip.mode)\n }\n return renderChip(override ?? chip.mode)\n }\n // The home footer slot only exists on the home screen; sessions render\n // the prompt footer — its slot input carries the open session's id, so\n // the chip there shows that session's effective mode (ADP-054).\n const disposeSlots = [\n context.ui.slot({ append: \"home.footer.status\", render: () => chipNode(undefined) }),\n context.ui.slot({\n append: \"prompt.footer.status\",\n render: (input) => chipNode(input.sessionID),\n }),\n ]\n\n const disposeWatch = client.events.on(\"settings-changed\", () => {\n void fetchMode()\n // A global mode change re-renders override-less sessions reactively;\n // cached overrides are re-fetched so a stale mirror cannot stick.\n for (const sessionID of Object.keys(chip.sessions)) {\n void fetchSessionMode(sessionID)\n }\n })\n const disposeSessionWatch = client.events.on(\"session-mode-changed\", (event) => {\n // The event data crosses the wire as an untyped record: a malformed\n // payload is ignored (fail-open) instead of steering a bogus session.\n const sessionID = event.data.sessionID\n if (typeof sessionID === \"string\") void fetchSessionMode(sessionID)\n })\n\n void bootFetch(BOOT_FETCH_RETRIES)\n\n return () => {\n for (const dispose of disposeSlots) dispose()\n disposeWatch()\n disposeSessionWatch()\n }\n },\n})\n"]}
@@ -0,0 +1,17 @@
1
+ import type { OpenCodeClient } from "@opencode/client/promise";
2
+ import type { SettingsPatch } from "../../state/settings/index.js";
3
+ import type { Receipt } from "../../state/receipts/index.js";
4
+ import type { StatusSnapshot } from "../../rpc.js";
5
+ /**
6
+ * Thin typed-RPC adapter for the GUI surface (ADP-034). Every call resolves to `undefined` (reads) or `false`
7
+ * (writes) when the server is unreachable, so the page can render an explicit disconnected state instead of
8
+ * throwing. The server stays the single source of truth (ADP-036): the GUI holds no local settings state.
9
+ */
10
+ export interface GuiApi {
11
+ status(): Promise<StatusSnapshot | undefined>;
12
+ receipts(): Promise<Receipt[] | undefined>;
13
+ update(patch: SettingsPatch): Promise<boolean>;
14
+ /** Calls back whenever settings change on the server. Returns the unsubscribe function. */
15
+ watchSettings(onChange: () => void): () => void;
16
+ }
17
+ export declare function adaptiveGuiApi(client: OpenCodeClient): GuiApi;
@@ -0,0 +1,21 @@
1
+ import { adaptiveRpc } from "../../rpc.js";
2
+ export function adaptiveGuiApi(client) {
3
+ const rpc = client.rpc(adaptiveRpc);
4
+ return {
5
+ status: () => rpc["status.get"]().catch(() => undefined),
6
+ receipts: () => rpc["receipts.list"]().catch(() => undefined),
7
+ update: async (patch) => {
8
+ try {
9
+ const result = await rpc["settings.set"](patch);
10
+ return result.ok;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ },
16
+ watchSettings: (onChange) => rpc.events.on("settings-changed", () => {
17
+ onChange();
18
+ }),
19
+ };
20
+ }
21
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/ui/gui/api.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAgB1C,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACnC,OAAO;QACL,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;QACxD,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;QAC7D,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACtB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,CAAA;gBAC/C,OAAO,MAAM,CAAC,EAAE,CAAA;YAClB,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC;QACD,aAAa,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC1B,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE;YACrC,QAAQ,EAAE,CAAA;QACZ,CAAC,CAAC;KACL,CAAA;AACH,CAAC","sourcesContent":["import type { OpenCodeClient } from \"@opencode/client/promise\"\n\nimport type { SettingsPatch } from \"../../state/settings/index.js\"\nimport type { Receipt } from \"../../state/receipts/index.js\"\nimport { adaptiveRpc } from \"../../rpc.js\"\nimport type { StatusSnapshot } from \"../../rpc.js\"\n\n/**\n * Thin typed-RPC adapter for the GUI surface (ADP-034). Every call resolves to `undefined` (reads) or `false`\n * (writes) when the server is unreachable, so the page can render an explicit disconnected state instead of\n * throwing. The server stays the single source of truth (ADP-036): the GUI holds no local settings state.\n */\nexport interface GuiApi {\n status(): Promise<StatusSnapshot | undefined>\n receipts(): Promise<Receipt[] | undefined>\n update(patch: SettingsPatch): Promise<boolean>\n /** Calls back whenever settings change on the server. Returns the unsubscribe function. */\n watchSettings(onChange: () => void): () => void\n}\n\nexport function adaptiveGuiApi(client: OpenCodeClient): GuiApi {\n const rpc = client.rpc(adaptiveRpc)\n return {\n status: () => rpc[\"status.get\"]().catch(() => undefined),\n receipts: () => rpc[\"receipts.list\"]().catch(() => undefined),\n update: async (patch) => {\n try {\n const result = await rpc[\"settings.set\"](patch)\n return result.ok\n } catch {\n return false\n }\n },\n watchSettings: (onChange) =>\n rpc.events.on(\"settings-changed\", () => {\n onChange()\n }),\n }\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import { type OperatingMode } from "../../core/scheduler/types.js";
2
+ import type { StatusSnapshot } from "../../rpc.js";
3
+ import type { Receipt } from "../../state/receipts/index.js";
4
+ /** One display row of the GUI settings page. `tone` marks rows that need attention. */
5
+ export interface GuiRow {
6
+ readonly id: string;
7
+ readonly label: string;
8
+ readonly value: string;
9
+ readonly tone?: "warning";
10
+ }
11
+ export declare const DISCONNECTED_ROW_ID = "connection";
12
+ /**
13
+ * Pure view model for the status section. An undefined snapshot means the server is unreachable; the page then
14
+ * shows a single disconnected row (ADP-034 disconnect handling) and no stale data.
15
+ */
16
+ export declare function statusRows(snapshot: StatusSnapshot | undefined): readonly GuiRow[];
17
+ /** One choice of the mode selector; the active mode is marked. */
18
+ export interface ModeChoice {
19
+ readonly value: OperatingMode;
20
+ readonly title: string;
21
+ }
22
+ export declare function modeChoices(current: OperatingMode): readonly ModeChoice[];
23
+ /** Validity gate for the mode `<select>` change event: untrusted DOM values never reach RPC. */
24
+ export declare function parseModeValue(value: string): OperatingMode | undefined;
25
+ /** Display lines for the receipts section, reusing the TUI decision vocabulary. */
26
+ export declare function receiptLines(receipts: readonly Receipt[]): readonly string[];
@@ -0,0 +1,49 @@
1
+ import { OPERATING_MODES } from "../../core/scheduler/types.js";
2
+ import { TIERS } from "../../core/tier-policy/index.js";
3
+ import { healthTag, resourceLabel } from "../tui/tiers.js";
4
+ import { receiptLine } from "../tui/receipts.js";
5
+ export const DISCONNECTED_ROW_ID = "connection";
6
+ /**
7
+ * Pure view model for the status section. An undefined snapshot means the server is unreachable; the page then
8
+ * shows a single disconnected row (ADP-034 disconnect handling) and no stale data.
9
+ */
10
+ export function statusRows(snapshot) {
11
+ if (snapshot === undefined) {
12
+ return [{ id: DISCONNECTED_ROW_ID, label: "Server", value: "disconnected", tone: "warning" }];
13
+ }
14
+ const rows = [
15
+ { id: "mode", label: "Mode", value: snapshot.mode },
16
+ {
17
+ id: "pin",
18
+ label: "Pin",
19
+ value: snapshot.pin === null ? "none" : resourceLabel(snapshot.pin),
20
+ },
21
+ ];
22
+ for (const tier of TIERS) {
23
+ const resources = snapshot.tiers[tier];
24
+ const value = resources.length === 0
25
+ ? "empty"
26
+ : resources
27
+ .map((resource) => `${resourceLabel(resource.ref)}${healthTag(resource)}`)
28
+ .join(", ");
29
+ rows.push({ id: `tier-${tier}`, label: `Tier ${tier}`, value });
30
+ }
31
+ rows.push({ id: "parked", label: "Parked", value: String(snapshot.parked) });
32
+ rows.push({ id: "receipts", label: "Receipts", value: String(snapshot.receipts) });
33
+ return rows;
34
+ }
35
+ export function modeChoices(current) {
36
+ return OPERATING_MODES.map((mode) => ({
37
+ value: mode,
38
+ title: mode === current ? `${mode} (current)` : mode,
39
+ }));
40
+ }
41
+ /** Validity gate for the mode `<select>` change event: untrusted DOM values never reach RPC. */
42
+ export function parseModeValue(value) {
43
+ return OPERATING_MODES.find((mode) => mode === value);
44
+ }
45
+ /** Display lines for the receipts section, reusing the TUI decision vocabulary. */
46
+ export function receiptLines(receipts) {
47
+ return receipts.map((receipt) => receiptLine(receipt));
48
+ }
49
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/ui/gui/view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAsB,MAAM,+BAA+B,CAAA;AACnF,OAAO,EAAE,KAAK,EAAE,MAAM,iCAAiC,CAAA;AAEvD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAWhD,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAA;AAE/C;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,QAAoC;IAC7D,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,CAAC,EAAE,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAA;IAC/F,CAAC;IACD,MAAM,IAAI,GAAa;QACrB,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE;QACnD;YACE,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC;SACpE;KACF,CAAA;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACtC,MAAM,KAAK,GACT,SAAS,CAAC,MAAM,KAAK,CAAC;YACpB,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,SAAS;iBACN,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;iBACzE,IAAI,CAAC,IAAI,CAAC,CAAA;QACnB,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACjE,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IAC5E,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAClF,OAAO,IAAI,CAAA;AACb,CAAC;AAQD,MAAM,UAAU,WAAW,CAAC,OAAsB;IAChD,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACpC,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,IAAI;KACrD,CAAC,CAAC,CAAA;AACL,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,KAAK,CAAC,CAAA;AACvD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,QAA4B;IACvD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;AACxD,CAAC","sourcesContent":["import { OPERATING_MODES, type OperatingMode } from \"../../core/scheduler/types.js\"\nimport { TIERS } from \"../../core/tier-policy/index.js\"\nimport type { StatusSnapshot } from \"../../rpc.js\"\nimport { healthTag, resourceLabel } from \"../tui/tiers.js\"\nimport { receiptLine } from \"../tui/receipts.js\"\nimport type { Receipt } from \"../../state/receipts/index.js\"\n\n/** One display row of the GUI settings page. `tone` marks rows that need attention. */\nexport interface GuiRow {\n readonly id: string\n readonly label: string\n readonly value: string\n readonly tone?: \"warning\"\n}\n\nexport const DISCONNECTED_ROW_ID = \"connection\"\n\n/**\n * Pure view model for the status section. An undefined snapshot means the server is unreachable; the page then\n * shows a single disconnected row (ADP-034 disconnect handling) and no stale data.\n */\nexport function statusRows(snapshot: StatusSnapshot | undefined): readonly GuiRow[] {\n if (snapshot === undefined) {\n return [{ id: DISCONNECTED_ROW_ID, label: \"Server\", value: \"disconnected\", tone: \"warning\" }]\n }\n const rows: GuiRow[] = [\n { id: \"mode\", label: \"Mode\", value: snapshot.mode },\n {\n id: \"pin\",\n label: \"Pin\",\n value: snapshot.pin === null ? \"none\" : resourceLabel(snapshot.pin),\n },\n ]\n for (const tier of TIERS) {\n const resources = snapshot.tiers[tier]\n const value =\n resources.length === 0\n ? \"empty\"\n : resources\n .map((resource) => `${resourceLabel(resource.ref)}${healthTag(resource)}`)\n .join(\", \")\n rows.push({ id: `tier-${tier}`, label: `Tier ${tier}`, value })\n }\n rows.push({ id: \"parked\", label: \"Parked\", value: String(snapshot.parked) })\n rows.push({ id: \"receipts\", label: \"Receipts\", value: String(snapshot.receipts) })\n return rows\n}\n\n/** One choice of the mode selector; the active mode is marked. */\nexport interface ModeChoice {\n readonly value: OperatingMode\n readonly title: string\n}\n\nexport function modeChoices(current: OperatingMode): readonly ModeChoice[] {\n return OPERATING_MODES.map((mode) => ({\n value: mode,\n title: mode === current ? `${mode} (current)` : mode,\n }))\n}\n\n/** Validity gate for the mode `<select>` change event: untrusted DOM values never reach RPC. */\nexport function parseModeValue(value: string): OperatingMode | undefined {\n return OPERATING_MODES.find((mode) => mode === value)\n}\n\n/** Display lines for the receipts section, reusing the TUI decision vocabulary. */\nexport function receiptLines(receipts: readonly Receipt[]): readonly string[] {\n return receipts.map((receipt) => receiptLine(receipt))\n}\n"]}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * The `/adaptive` keyboard-flow controller (ADP-033): a pure-ish state machine
3
+ * over two seams — the typed RPC API and the host dialogs/toasts. It holds no
4
+ * local settings state: every mutation goes through `settings.set` and the
5
+ * returned snapshot becomes the new local view (the server is the single
6
+ * source of truth, ADP-036).
7
+ *
8
+ * Flow contract: `undefined` selections always mean "cancel this step" — the
9
+ * menu loop continues or exits; API failures toast and keep the loop alive.
10
+ */
11
+ import type { OperatingMode } from "../../core/scheduler/types.js";
12
+ import type { TierName } from "../../core/tier-policy/types.js";
13
+ import type { SettingsPatch, SettingsV1 } from "../../state/settings/index.js";
14
+ import type { PatchResult } from "../../state/settings/types.js";
15
+ import type { SessionModeSetResult } from "../../state/session-modes/index.js";
16
+ import type { TierRequestSetResult } from "../../rpc/handlers.js";
17
+ import type { Receipt } from "../../state/receipts/types.js";
18
+ import type { StatusSnapshot } from "../../rpc/status.js";
19
+ import { type MainMenuChoice } from "./menu.js";
20
+ import { type ModelSummary } from "./tiers.js";
21
+ import { type SelectOption } from "./options.js";
22
+ import { RECEIPTS_PAGE_SIZE } from "./receipts.js";
23
+ export interface AdaptiveView {
24
+ select<Value>(options: {
25
+ title: string;
26
+ placeholder?: string;
27
+ options: readonly SelectOption<Value>[];
28
+ current?: Value;
29
+ search?: (query: string, options: readonly SelectOption<Value>[]) => readonly SelectOption<Value>[];
30
+ }): Promise<Value | undefined>;
31
+ /** A select with footer actions (page flips): the action closes the
32
+ * dialog and is reported separately from an option pick. */
33
+ selectPaged<Value>(spec: {
34
+ title: string;
35
+ options: readonly SelectOption<Value>[];
36
+ actions: readonly {
37
+ title: string;
38
+ value: string;
39
+ bind: string;
40
+ }[];
41
+ }): Promise<{
42
+ picked: Value | undefined;
43
+ action: string | undefined;
44
+ }>;
45
+ confirm(options: {
46
+ title: string;
47
+ message: string;
48
+ label?: {
49
+ confirm?: string;
50
+ cancel?: string;
51
+ };
52
+ }): Promise<boolean | undefined>;
53
+ alert(options: {
54
+ title: string;
55
+ message: string;
56
+ }): Promise<void>;
57
+ /** A column-aligned text table (header row first); Escape closes it. */
58
+ table(spec: {
59
+ title: string;
60
+ lines: string[];
61
+ }): Promise<void>;
62
+ toast(message: string, variant: "info" | "success" | "warning" | "error"): void;
63
+ }
64
+ export interface AdaptiveApi {
65
+ settings: () => Promise<SettingsV1>;
66
+ setSettings: (patch: SettingsPatch) => Promise<PatchResult>;
67
+ status: () => Promise<StatusSnapshot>;
68
+ receipts: () => Promise<Receipt[]>;
69
+ /** The session's mode override (`null` = follows the global mode). */
70
+ sessionMode: (sessionID: string) => Promise<OperatingMode | null>;
71
+ setSessionMode: (sessionID: string, mode: OperatingMode | null) => Promise<SessionModeSetResult>;
72
+ /** The session's pinned tier request (`null` = follows the routing ladder). */
73
+ tierRequest: (sessionID: string) => Promise<TierName | null>;
74
+ setTierRequest: (sessionID: string, tier: string | null) => Promise<TierRequestSetResult>;
75
+ }
76
+ export type ModelLister = () => readonly ModelSummary[];
77
+ export interface AdaptiveEnv {
78
+ api: AdaptiveApi;
79
+ view: AdaptiveView;
80
+ models: ModelLister;
81
+ /**
82
+ * The session the command was opened from (ADP-054). Present only when the
83
+ * palette command ran inside a session screen — the mode menu then steers
84
+ * that session's override instead of the global mode.
85
+ */
86
+ sessionID?: string | undefined;
87
+ }
88
+ export declare const INVITE_TITLE = "OpenCode Adaptive";
89
+ export declare const INVITE_MESSAGE: string;
90
+ /** Tracks whether this dialog session already showed the save toast. */
91
+ export interface SaveFeedback {
92
+ once: boolean;
93
+ }
94
+ export declare function runAdaptive(env: AdaptiveEnv): Promise<void>;
95
+ /** Exported for tests: the receipts page size used by the receipts browser. */
96
+ export { RECEIPTS_PAGE_SIZE };
97
+ export type { MainMenuChoice, OperatingMode, StatusSnapshot, SettingsV1, SettingsPatch, PatchResult, Receipt, };