@tai42/api-client 16.0.0 → 16.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 (370) hide show
  1. package/dist/client/agents-client.d.ts +26 -0
  2. package/dist/client/agents-client.d.ts.map +1 -0
  3. package/dist/client/agents-client.js +19 -0
  4. package/dist/client/agents-client.js.map +1 -0
  5. package/dist/client/auth-client.d.ts +205 -0
  6. package/dist/client/auth-client.d.ts.map +1 -0
  7. package/dist/client/auth-client.js +59 -0
  8. package/dist/client/auth-client.js.map +1 -0
  9. package/dist/client/backend-client.d.ts +39 -0
  10. package/dist/client/backend-client.d.ts.map +1 -0
  11. package/dist/client/backend-client.js +22 -0
  12. package/dist/client/backend-client.js.map +1 -0
  13. package/dist/client/backup-client.d.ts +56 -0
  14. package/dist/client/backup-client.d.ts.map +1 -0
  15. package/dist/client/backup-client.js +12 -0
  16. package/dist/client/backup-client.js.map +1 -0
  17. package/dist/client/capabilities-client.d.ts +35 -0
  18. package/dist/client/capabilities-client.d.ts.map +1 -0
  19. package/dist/client/capabilities-client.js +19 -0
  20. package/dist/client/capabilities-client.js.map +1 -0
  21. package/dist/client/channels-client.d.ts +7 -0
  22. package/dist/client/channels-client.d.ts.map +1 -0
  23. package/dist/client/channels-client.js +10 -0
  24. package/dist/client/channels-client.js.map +1 -0
  25. package/dist/client/config-client.d.ts +59 -0
  26. package/dist/client/config-client.d.ts.map +1 -0
  27. package/dist/client/config-client.js +18 -0
  28. package/dist/client/config-client.js.map +1 -0
  29. package/dist/client/connectors-client.d.ts +225 -0
  30. package/dist/client/connectors-client.d.ts.map +1 -0
  31. package/dist/client/connectors-client.js +31 -0
  32. package/dist/client/connectors-client.js.map +1 -0
  33. package/dist/client/conversations-client.d.ts +399 -0
  34. package/dist/client/conversations-client.d.ts.map +1 -0
  35. package/dist/client/conversations-client.js +120 -0
  36. package/dist/client/conversations-client.js.map +1 -0
  37. package/dist/client/hooks-client.d.ts +113 -0
  38. package/dist/client/hooks-client.d.ts.map +1 -0
  39. package/dist/client/hooks-client.js +25 -0
  40. package/dist/client/hooks-client.js.map +1 -0
  41. package/dist/{client.d.ts → client/index.d.ts} +2235 -2666
  42. package/dist/client/index.d.ts.map +1 -0
  43. package/dist/client/index.js +79 -0
  44. package/dist/client/index.js.map +1 -0
  45. package/dist/client/interactions-client.d.ts +52 -0
  46. package/dist/client/interactions-client.d.ts.map +1 -0
  47. package/dist/client/interactions-client.js +84 -0
  48. package/dist/client/interactions-client.js.map +1 -0
  49. package/dist/client/login-client.d.ts +56 -0
  50. package/dist/client/login-client.d.ts.map +1 -0
  51. package/dist/client/login-client.js +103 -0
  52. package/dist/client/login-client.js.map +1 -0
  53. package/dist/client/manifest-client.d.ts +354 -0
  54. package/dist/client/manifest-client.d.ts.map +1 -0
  55. package/dist/client/manifest-client.js +76 -0
  56. package/dist/client/manifest-client.js.map +1 -0
  57. package/dist/client/marketplace-client.d.ts +274 -0
  58. package/dist/client/marketplace-client.d.ts.map +1 -0
  59. package/dist/client/marketplace-client.js +46 -0
  60. package/dist/client/marketplace-client.js.map +1 -0
  61. package/dist/client/mcp-config-schema-client.d.ts +5 -0
  62. package/dist/client/mcp-config-schema-client.d.ts.map +1 -0
  63. package/dist/client/mcp-config-schema-client.js +9 -0
  64. package/dist/client/mcp-config-schema-client.js.map +1 -0
  65. package/dist/client/notifications-client.d.ts +27 -0
  66. package/dist/client/notifications-client.d.ts.map +1 -0
  67. package/dist/client/notifications-client.js +11 -0
  68. package/dist/client/notifications-client.js.map +1 -0
  69. package/dist/client/observability-client.d.ts +103 -0
  70. package/dist/client/observability-client.d.ts.map +1 -0
  71. package/dist/client/observability-client.js +43 -0
  72. package/dist/client/observability-client.js.map +1 -0
  73. package/dist/client/plugins-client.d.ts +17 -0
  74. package/dist/client/plugins-client.d.ts.map +1 -0
  75. package/dist/client/plugins-client.js +9 -0
  76. package/dist/client/plugins-client.js.map +1 -0
  77. package/dist/client/policy-client.d.ts +37 -0
  78. package/dist/client/policy-client.d.ts.map +1 -0
  79. package/dist/client/policy-client.js +26 -0
  80. package/dist/client/policy-client.js.map +1 -0
  81. package/dist/client/presets-client.d.ts +335 -0
  82. package/dist/client/presets-client.d.ts.map +1 -0
  83. package/dist/client/presets-client.js +38 -0
  84. package/dist/client/presets-client.js.map +1 -0
  85. package/dist/client/profiles-client.d.ts +102 -0
  86. package/dist/client/profiles-client.d.ts.map +1 -0
  87. package/dist/client/profiles-client.js +37 -0
  88. package/dist/client/profiles-client.js.map +1 -0
  89. package/dist/client/schedules-client.d.ts +28 -0
  90. package/dist/client/schedules-client.d.ts.map +1 -0
  91. package/dist/client/schedules-client.js +13 -0
  92. package/dist/client/schedules-client.js.map +1 -0
  93. package/dist/client/settings-schema-client.d.ts +23 -0
  94. package/dist/client/settings-schema-client.d.ts.map +1 -0
  95. package/dist/client/settings-schema-client.js +9 -0
  96. package/dist/client/settings-schema-client.js.map +1 -0
  97. package/dist/client/states-client.d.ts +471 -0
  98. package/dist/client/states-client.d.ts.map +1 -0
  99. package/dist/client/states-client.js +144 -0
  100. package/dist/client/states-client.js.map +1 -0
  101. package/dist/client/storage-client.d.ts +40 -0
  102. package/dist/client/storage-client.d.ts.map +1 -0
  103. package/dist/client/storage-client.js +43 -0
  104. package/dist/client/storage-client.js.map +1 -0
  105. package/dist/client/sub-mcp-client.d.ts +17 -0
  106. package/dist/client/sub-mcp-client.d.ts.map +1 -0
  107. package/dist/client/sub-mcp-client.js +14 -0
  108. package/dist/client/sub-mcp-client.js.map +1 -0
  109. package/dist/client/system-client.d.ts +11 -0
  110. package/dist/client/system-client.d.ts.map +1 -0
  111. package/dist/client/system-client.js +13 -0
  112. package/dist/client/system-client.js.map +1 -0
  113. package/dist/client/templates-client.d.ts +29 -0
  114. package/dist/client/templates-client.d.ts.map +1 -0
  115. package/dist/client/templates-client.js +21 -0
  116. package/dist/client/templates-client.js.map +1 -0
  117. package/dist/client/tool-extensions-client.d.ts +54 -0
  118. package/dist/client/tool-extensions-client.d.ts.map +1 -0
  119. package/dist/client/tool-extensions-client.js +21 -0
  120. package/dist/client/tool-extensions-client.js.map +1 -0
  121. package/dist/client/tool-meta-client.d.ts +69 -0
  122. package/dist/client/tool-meta-client.d.ts.map +1 -0
  123. package/dist/client/tool-meta-client.js +38 -0
  124. package/dist/client/tool-meta-client.js.map +1 -0
  125. package/dist/client/tools-client.d.ts +83 -0
  126. package/dist/client/tools-client.d.ts.map +1 -0
  127. package/dist/client/tools-client.js +38 -0
  128. package/dist/client/tools-client.js.map +1 -0
  129. package/dist/client/transport.d.ts +13 -0
  130. package/dist/client/transport.d.ts.map +1 -0
  131. package/dist/client/transport.js +10 -0
  132. package/dist/client/transport.js.map +1 -0
  133. package/dist/client/trigger-links-client.d.ts +44 -0
  134. package/dist/client/trigger-links-client.d.ts.map +1 -0
  135. package/dist/client/trigger-links-client.js +30 -0
  136. package/dist/client/trigger-links-client.js.map +1 -0
  137. package/dist/client/web-entry-gate-client.d.ts +34 -0
  138. package/dist/client/web-entry-gate-client.d.ts.map +1 -0
  139. package/dist/client/web-entry-gate-client.js +25 -0
  140. package/dist/client/web-entry-gate-client.js.map +1 -0
  141. package/dist/http.d.ts.map +1 -1
  142. package/dist/http.js +33 -17
  143. package/dist/http.js.map +1 -1
  144. package/dist/schemas/api-keys.d.ts +34 -0
  145. package/dist/schemas/api-keys.d.ts.map +1 -0
  146. package/dist/schemas/api-keys.js +22 -0
  147. package/dist/schemas/api-keys.js.map +1 -0
  148. package/dist/schemas/auth-routes.d.ts +78 -0
  149. package/dist/schemas/auth-routes.d.ts.map +1 -0
  150. package/dist/schemas/auth-routes.js +42 -0
  151. package/dist/schemas/auth-routes.js.map +1 -0
  152. package/dist/schemas/backup.d.ts +78 -0
  153. package/dist/schemas/backup.d.ts.map +1 -0
  154. package/dist/schemas/backup.js +33 -0
  155. package/dist/schemas/backup.js.map +1 -0
  156. package/dist/schemas/capabilities.d.ts +99 -0
  157. package/dist/schemas/capabilities.d.ts.map +1 -0
  158. package/dist/schemas/capabilities.js +74 -0
  159. package/dist/schemas/capabilities.js.map +1 -0
  160. package/dist/schemas/channels.d.ts +7 -0
  161. package/dist/schemas/channels.d.ts.map +1 -0
  162. package/dist/schemas/channels.js +9 -0
  163. package/dist/schemas/channels.js.map +1 -0
  164. package/dist/schemas/config.d.ts +17 -0
  165. package/dist/schemas/config.d.ts.map +1 -0
  166. package/dist/schemas/config.js +17 -0
  167. package/dist/schemas/config.js.map +1 -0
  168. package/dist/schemas/connectors.d.ts +383 -0
  169. package/dist/schemas/connectors.d.ts.map +1 -0
  170. package/dist/schemas/connectors.js +122 -0
  171. package/dist/schemas/connectors.js.map +1 -0
  172. package/dist/schemas/conversations.d.ts +600 -0
  173. package/dist/schemas/conversations.d.ts.map +1 -0
  174. package/dist/schemas/conversations.js +208 -0
  175. package/dist/schemas/conversations.js.map +1 -0
  176. package/dist/schemas/extensions.d.ts +44 -0
  177. package/dist/schemas/extensions.d.ts.map +1 -0
  178. package/dist/schemas/extensions.js +29 -0
  179. package/dist/schemas/extensions.js.map +1 -0
  180. package/dist/schemas/fleet.d.ts +271 -0
  181. package/dist/schemas/fleet.d.ts.map +1 -0
  182. package/dist/schemas/fleet.js +132 -0
  183. package/dist/schemas/fleet.js.map +1 -0
  184. package/dist/schemas/hooks.d.ts +120 -0
  185. package/dist/schemas/hooks.d.ts.map +1 -0
  186. package/dist/schemas/hooks.js +44 -0
  187. package/dist/schemas/hooks.js.map +1 -0
  188. package/dist/schemas/index.d.ts +38 -0
  189. package/dist/schemas/index.d.ts.map +1 -0
  190. package/dist/schemas/index.js +38 -0
  191. package/dist/schemas/index.js.map +1 -0
  192. package/dist/schemas/interactions.d.ts +156 -0
  193. package/dist/schemas/interactions.d.ts.map +1 -0
  194. package/dist/schemas/interactions.js +124 -0
  195. package/dist/schemas/interactions.js.map +1 -0
  196. package/dist/schemas/login.d.ts +86 -0
  197. package/dist/schemas/login.d.ts.map +1 -0
  198. package/dist/schemas/login.js +54 -0
  199. package/dist/schemas/login.js.map +1 -0
  200. package/dist/schemas/manifest-mcp.d.ts +125 -0
  201. package/dist/schemas/manifest-mcp.d.ts.map +1 -0
  202. package/dist/schemas/manifest-mcp.js +61 -0
  203. package/dist/schemas/manifest-mcp.js.map +1 -0
  204. package/dist/schemas/marketplace.d.ts +708 -0
  205. package/dist/schemas/marketplace.d.ts.map +1 -0
  206. package/dist/schemas/marketplace.js +387 -0
  207. package/dist/schemas/marketplace.js.map +1 -0
  208. package/dist/schemas/notifications.d.ts +61 -0
  209. package/dist/schemas/notifications.d.ts.map +1 -0
  210. package/dist/schemas/notifications.js +38 -0
  211. package/dist/schemas/notifications.js.map +1 -0
  212. package/dist/schemas/observability.d.ts +114 -0
  213. package/dist/schemas/observability.d.ts.map +1 -0
  214. package/dist/schemas/observability.js +74 -0
  215. package/dist/schemas/observability.js.map +1 -0
  216. package/dist/schemas/policy.d.ts +58 -0
  217. package/dist/schemas/policy.d.ts.map +1 -0
  218. package/dist/schemas/policy.js +32 -0
  219. package/dist/schemas/policy.js.map +1 -0
  220. package/dist/schemas/presets.d.ts +258 -0
  221. package/dist/schemas/presets.d.ts.map +1 -0
  222. package/dist/schemas/presets.js +95 -0
  223. package/dist/schemas/presets.js.map +1 -0
  224. package/dist/schemas/profiles.d.ts +180 -0
  225. package/dist/schemas/profiles.d.ts.map +1 -0
  226. package/dist/schemas/profiles.js +100 -0
  227. package/dist/schemas/profiles.js.map +1 -0
  228. package/dist/schemas/roles.d.ts +125 -0
  229. package/dist/schemas/roles.d.ts.map +1 -0
  230. package/dist/schemas/roles.js +46 -0
  231. package/dist/schemas/roles.js.map +1 -0
  232. package/dist/schemas/schedules.d.ts +25 -0
  233. package/dist/schemas/schedules.d.ts.map +1 -0
  234. package/dist/schemas/schedules.js +24 -0
  235. package/dist/schemas/schedules.js.map +1 -0
  236. package/dist/schemas/served.d.ts +626 -0
  237. package/dist/schemas/served.d.ts.map +1 -0
  238. package/dist/schemas/served.js +28 -0
  239. package/dist/schemas/served.js.map +1 -0
  240. package/dist/schemas/settings-schema.d.ts +23 -0
  241. package/dist/schemas/settings-schema.d.ts.map +1 -0
  242. package/dist/schemas/settings-schema.js +25 -0
  243. package/dist/schemas/settings-schema.js.map +1 -0
  244. package/dist/schemas/shared.d.ts +20 -0
  245. package/dist/schemas/shared.d.ts.map +1 -0
  246. package/dist/schemas/shared.js +20 -0
  247. package/dist/schemas/shared.js.map +1 -0
  248. package/dist/schemas/states.d.ts +499 -0
  249. package/dist/schemas/states.d.ts.map +1 -0
  250. package/dist/schemas/states.js +220 -0
  251. package/dist/schemas/states.js.map +1 -0
  252. package/dist/schemas/storage.d.ts +37 -0
  253. package/dist/schemas/storage.d.ts.map +1 -0
  254. package/dist/schemas/storage.js +24 -0
  255. package/dist/schemas/storage.js.map +1 -0
  256. package/dist/schemas/studio-plugins.d.ts +30 -0
  257. package/dist/schemas/studio-plugins.d.ts.map +1 -0
  258. package/dist/schemas/studio-plugins.js +20 -0
  259. package/dist/schemas/studio-plugins.js.map +1 -0
  260. package/dist/schemas/sub-mcp.d.ts +29 -0
  261. package/dist/schemas/sub-mcp.d.ts.map +1 -0
  262. package/dist/schemas/sub-mcp.js +22 -0
  263. package/dist/schemas/sub-mcp.js.map +1 -0
  264. package/dist/schemas/system-kinds.d.ts +122 -0
  265. package/dist/schemas/system-kinds.d.ts.map +1 -0
  266. package/dist/schemas/system-kinds.js +40 -0
  267. package/dist/schemas/system-kinds.js.map +1 -0
  268. package/dist/schemas/templates.d.ts +26 -0
  269. package/dist/schemas/templates.d.ts.map +1 -0
  270. package/dist/schemas/templates.js +11 -0
  271. package/dist/schemas/templates.js.map +1 -0
  272. package/dist/schemas/tool-meta.d.ts +64 -0
  273. package/dist/schemas/tool-meta.d.ts.map +1 -0
  274. package/dist/schemas/tool-meta.js +44 -0
  275. package/dist/schemas/tool-meta.js.map +1 -0
  276. package/dist/schemas/tools.d.ts +62 -0
  277. package/dist/schemas/tools.d.ts.map +1 -0
  278. package/dist/schemas/tools.js +53 -0
  279. package/dist/schemas/tools.js.map +1 -0
  280. package/dist/schemas/trigger-links.d.ts +71 -0
  281. package/dist/schemas/trigger-links.d.ts.map +1 -0
  282. package/dist/schemas/trigger-links.js +46 -0
  283. package/dist/schemas/trigger-links.js.map +1 -0
  284. package/dist/schemas/web-entry.d.ts +45 -0
  285. package/dist/schemas/web-entry.d.ts.map +1 -0
  286. package/dist/schemas/web-entry.js +36 -0
  287. package/dist/schemas/web-entry.js.map +1 -0
  288. package/dist/tool-runs.d.ts +4 -4
  289. package/package.json +1 -1
  290. package/src/client/agents-client.ts +23 -0
  291. package/src/client/auth-client.ts +144 -0
  292. package/src/client/backend-client.ts +25 -0
  293. package/src/client/backup-client.ts +16 -0
  294. package/src/client/capabilities-client.ts +22 -0
  295. package/src/client/channels-client.ts +11 -0
  296. package/src/client/config-client.ts +21 -0
  297. package/src/client/connectors-client.ts +52 -0
  298. package/src/client/conversations-client.ts +240 -0
  299. package/src/client/hooks-client.ts +44 -0
  300. package/src/client/index.ts +122 -0
  301. package/src/client/interactions-client.ts +89 -0
  302. package/src/client/login-client.ts +109 -0
  303. package/src/client/manifest-client.ts +118 -0
  304. package/src/client/marketplace-client.ts +122 -0
  305. package/src/client/mcp-config-schema-client.ts +11 -0
  306. package/src/client/notifications-client.ts +13 -0
  307. package/src/client/observability-client.ts +82 -0
  308. package/src/client/plugins-client.ts +11 -0
  309. package/src/client/policy-client.ts +41 -0
  310. package/src/client/presets-client.ts +124 -0
  311. package/src/client/profiles-client.ts +51 -0
  312. package/src/client/schedules-client.ts +22 -0
  313. package/src/client/settings-schema-client.ts +11 -0
  314. package/src/client/states-client.ts +292 -0
  315. package/src/client/storage-client.ts +63 -0
  316. package/src/client/sub-mcp-client.ts +17 -0
  317. package/src/client/system-client.ts +16 -0
  318. package/src/client/templates-client.ts +28 -0
  319. package/src/client/tool-extensions-client.ts +24 -0
  320. package/src/client/tool-meta-client.ts +65 -0
  321. package/src/client/tools-client.ts +66 -0
  322. package/src/client/transport.ts +18 -0
  323. package/src/client/trigger-links-client.ts +51 -0
  324. package/src/client/web-entry-gate-client.ts +45 -0
  325. package/src/http.ts +48 -24
  326. package/src/schemas/api-keys.ts +26 -0
  327. package/src/schemas/auth-routes.ts +48 -0
  328. package/src/schemas/backup.ts +42 -0
  329. package/src/schemas/capabilities.ts +87 -0
  330. package/src/schemas/channels.ts +11 -0
  331. package/src/schemas/config.ts +17 -0
  332. package/src/schemas/connectors.ts +138 -0
  333. package/src/schemas/conversations.ts +256 -0
  334. package/src/schemas/extensions.ts +34 -0
  335. package/src/schemas/fleet.ts +153 -0
  336. package/src/schemas/hooks.ts +56 -0
  337. package/src/schemas/index.ts +37 -0
  338. package/src/schemas/interactions.ts +146 -0
  339. package/src/schemas/login.ts +62 -0
  340. package/src/schemas/manifest-mcp.ts +69 -0
  341. package/src/schemas/marketplace.ts +447 -0
  342. package/src/schemas/notifications.ts +42 -0
  343. package/src/schemas/observability.ts +86 -0
  344. package/src/schemas/policy.ts +37 -0
  345. package/src/schemas/presets.ts +113 -0
  346. package/src/schemas/profiles.ts +119 -0
  347. package/src/schemas/roles.ts +57 -0
  348. package/src/schemas/schedules.ts +27 -0
  349. package/src/schemas/served.ts +49 -0
  350. package/src/schemas/settings-schema.ts +31 -0
  351. package/src/schemas/shared.ts +21 -0
  352. package/src/schemas/states.ts +272 -0
  353. package/src/schemas/storage.ts +31 -0
  354. package/src/schemas/studio-plugins.ts +21 -0
  355. package/src/schemas/sub-mcp.ts +24 -0
  356. package/src/schemas/system-kinds.ts +46 -0
  357. package/src/schemas/templates.ts +11 -0
  358. package/src/schemas/tool-meta.ts +51 -0
  359. package/src/schemas/tools.ts +61 -0
  360. package/src/schemas/trigger-links.ts +54 -0
  361. package/src/schemas/web-entry.ts +46 -0
  362. package/dist/client.d.ts.map +0 -1
  363. package/dist/client.js +0 -920
  364. package/dist/client.js.map +0 -1
  365. package/dist/schemas.d.ts +0 -5172
  366. package/dist/schemas.d.ts.map +0 -1
  367. package/dist/schemas.js +0 -2268
  368. package/dist/schemas.js.map +0 -1
  369. package/src/client.ts +0 -1771
  370. package/src/schemas.ts +0 -2760
package/src/schemas.ts DELETED
@@ -1,2760 +0,0 @@
1
- /**
2
- * One zod schema per consumed endpoint. These mirror the skeleton's router
3
- * response shapes exactly; a drift throws (ApiSchemaError) and the UI shows the
4
- * error state. JSON-schema-shaped fields are kept as permissive records — the
5
- * auto-form renderer interprets them at runtime.
6
- */
7
- import { z } from 'zod';
8
-
9
- import { templatedText } from './templated-text';
10
- import * as generated from './generated/served-schemas';
11
-
12
- export { templatedText, requiredTemplatedText } from './templated-text';
13
- export type { TemplatedText } from './templated-text';
14
-
15
- // Served-document schemas are GENERATED from the platform contract JSON-schema
16
- // bundle (src/generated/served-schemas.ts) and re-exported here under the names the
17
- // rest of the client uses. The generator maps every `x-tai42-templated-text` field
18
- // to the shared `templatedText` zod, so a served body can never be a bare string.
19
- // The plain response DTOs below (list wrappers, acks, version rows) stay hand-written.
20
- export const stateInjection = generated.stateInjection;
21
- export type StateInjection = z.infer<typeof stateInjection>;
22
- export const stateUpdate = generated.stateUpdate;
23
- export type StateUpdate = z.infer<typeof stateUpdate>;
24
- export const stateAttach = generated.stateAttach;
25
- export type StateAttach = z.infer<typeof stateAttach>;
26
- export const stateBinding = generated.stateBinding;
27
- export type StateBinding = z.infer<typeof stateBinding>;
28
- export const presetBody = generated.presetBody;
29
- export type PresetBody = z.input<typeof presetBody>;
30
- export const conversationRoute = generated.conversationRoute;
31
- export type ConversationRoute = z.infer<typeof conversationRoute>;
32
- export const conversationRouteCreate = generated.conversationRouteCreate;
33
- export type ConversationRouteCreate = z.input<typeof conversationRouteCreate>;
34
- export const targetConversationConfig = generated.targetConversationConfig;
35
- export type TargetConversationConfig = z.input<typeof targetConversationConfig>;
36
- export const channelTemplate = generated.channelTemplate;
37
- export type ChannelTemplate = z.infer<typeof channelTemplate>;
38
- export const hookSubject = generated.hookSubject;
39
- export type HookSubject = z.infer<typeof hookSubject>;
40
- export const hookParams = generated.hookParams;
41
- export type HookParams = z.infer<typeof hookParams>;
42
- export const hookRegister = generated.hookRegister;
43
- export type HookRegister = z.input<typeof hookRegister>;
44
- export const stateDeclaration = generated.stateDeclaration;
45
- export type StateDeclaration = z.infer<typeof stateDeclaration>;
46
- export const stateTemplateDocument = generated.stateTemplateDocument;
47
- export type StateTemplateDocument = z.infer<typeof stateTemplateDocument>;
48
- export const templateJq = generated.stateTemplateJq;
49
- export type TemplateJq = z.infer<typeof templateJq>;
50
- export const templateReconcile = generated.stateTemplateReconcile;
51
- export type TemplateReconcile = z.infer<typeof templateReconcile>;
52
- export const templateDeclarations = generated.stateTemplateDeclarations;
53
- export type TemplateDeclarations = z.infer<typeof templateDeclarations>;
54
- export const policyBody = generated.accessPolicy;
55
- export type PolicyBody = z.infer<typeof policyBody>;
56
- export const roleBody = generated.roleDefinition;
57
- export type RoleBody = z.infer<typeof roleBody>;
58
- /** An arbitrary JSON value (tool results, rendered payloads). */
59
- export const jsonValue: z.ZodType = z.unknown();
60
-
61
- /** A JSON Schema object (Pydantic-emitted). Permissive; parsed by the auto-form. */
62
- export const jsonSchema = z.record(z.string(), z.unknown());
63
-
64
- // -- tools -------------------------------------------------------------------
65
-
66
- export const toolNames = z.array(z.string());
67
-
68
- export const toolSchema = z.object({
69
- input: jsonSchema,
70
- output: jsonSchema.nullable(),
71
- description: z.string().nullable(),
72
- });
73
- export type ToolSchema = z.infer<typeof toolSchema>;
74
-
75
- export const allToolSchemas = z.record(z.string(), toolSchema);
76
-
77
- export const runToolResult = jsonValue;
78
-
79
- /**
80
- * The MEDIA content shape a direct tool run may return — fastmcp's serialized
81
- * `Image`/`Audio` MediaBlock (`{ type, data: <base64>, mimeType }`). One recognised
82
- * shape of the otherwise-arbitrary `runToolResult`, kept as its own schema so the
83
- * viewer detects media by parse, not by ad-hoc shape check.
84
- *
85
- * SECURITY: the refinement pins `mimeType` to its kind — `image` MUST be `image/*`,
86
- * `audio` MUST be `audio/*` — so a drifting/arbitrary mime fails the parse and is
87
- * never turned into a `data:` URI (a `File` MediaBlock's distinct `EmbeddedResource`
88
- * shape also fails here, by design).
89
- */
90
- export const toolMediaResult = z
91
- .object({
92
- type: z.enum(['image', 'audio']),
93
- data: z.string(),
94
- mimeType: z.string(),
95
- })
96
- .refine((media) => media.mimeType.startsWith(`${media.type}/`), {
97
- message: 'media mimeType must match its media kind (image/* for image, audio/* for audio)',
98
- path: ['mimeType'],
99
- });
100
- export type ToolMediaResult = z.infer<typeof toolMediaResult>;
101
-
102
- /**
103
- * The ADDITIVE per-tool native surface (`GET /api/tools/tags`). Distinct from the
104
- * flat `toolNames` contract (`GET /api/tools`, unchanged): each entry carries the
105
- * tool's read-only FastMCP `tags` — so the shared `ToolPicker` can group/filter its
106
- * options — plus `hidden`, the tool's OWN plugin-declared visibility (read from the
107
- * FastMCP `meta` under `tai42/hidden`; a tool that never declared it is not hidden).
108
- * The tool_meta overlay's tri-state override is merged on top client-side; this read
109
- * exposes only the declaration.
110
- *
111
- * `badges` are the tool's plugin-DECLARED capability labels — purely INFORMATIONAL
112
- * (e.g. `network`, `filesystem`), never a gate the server enforces. The overlay's
113
- * own badges are merged on top client-side; this read exposes only the declaration.
114
- */
115
- export const toolTagEntry = z.object({
116
- name: z.string(),
117
- tags: z.array(z.string()),
118
- badges: z.array(z.string()),
119
- hidden: z.boolean(),
120
- });
121
- export const toolTags = z.array(toolTagEntry);
122
- export type ToolTagEntry = z.infer<typeof toolTagEntry>;
123
-
124
- // -- extensions --------------------------------------------------------------
125
-
126
- export const extension = z.object({ name: z.string(), kind: z.string() });
127
- export const extensions = z.array(extension);
128
- export type Extension = z.infer<typeof extension>;
129
-
130
- /**
131
- * One element of an extension combo: a bare extension NAME, or a
132
- * `{ name, config }` mapping binding author config (e.g. an `output_schema` JSON
133
- * Schema, or an `ask_external` verifier). Both wire forms are admitted so a
134
- * config-bearing combo parses without dropping server data. A drift throws
135
- * `ApiSchemaError`.
136
- */
137
- export const presetExtensionElement = z.union([
138
- z.string(),
139
- z.object({ name: z.string(), config: z.record(z.string(), z.unknown()) }),
140
- ]);
141
- export type PresetExtensionElement = z.infer<typeof presetExtensionElement>;
142
-
143
- /**
144
- * `GET /api/tools/{name}/extensions` — a tool's FULL lossless list-of-combos plus
145
- * the extension catalog. `combos` is a list of combos, each an ordered list of
146
- * extension ELEMENTS (a bare name or a config-bearing `{ name, config }` mapping —
147
- * `output_schema` carries its schema this way); `[]` when the tool has no map entry.
148
- * It carries EVERY combo mapped to the base tool (the union across every `tools`/`mcp`
149
- * config), never a single flattened one. `available` is the same flat catalog
150
- * `GET /api/extensions` returns. A drift throws `ApiSchemaError`.
151
- */
152
- export const toolExtensions = z.object({
153
- combos: z.array(z.array(presetExtensionElement)),
154
- available: extensions,
155
- });
156
- export type ToolExtensions = z.infer<typeof toolExtensions>;
157
-
158
- // -- presets -----------------------------------------------------------------
159
-
160
- /** A list of extension COMBOS — each combo an ordered list of elements. */
161
- export const presetExtensions = z.array(z.array(presetExtensionElement));
162
-
163
- /**
164
- * One preset as `GET /api/presets` returns it. Every preset is versioned, so
165
- * `active_version` is always an int; `conflicted` marks a QUARANTINED row (its
166
- * name collided with a foreign tool at boot — delete-only). `extensions` are the
167
- * ACTIVE version's extension combos, `output_schema` the optional author-set
168
- * output JSON Schema, and `input_schema` the optional author-set input JSON
169
- * Schema. Categorization tags live in the tool_meta overlay
170
- * (`GET /api/tool-meta`), never on the record. A drift throws `ApiSchemaError`;
171
- * nothing is coerced.
172
- */
173
- export const presetRecord = z.object({
174
- name: z.string(),
175
- base_tool: z.string(),
176
- description: z.string(),
177
- active_version: z.number(),
178
- extensions: presetExtensions,
179
- output_schema: z.record(z.string(), z.unknown()).nullable(),
180
- input_schema: z.record(z.string(), z.unknown()).nullable(),
181
- conflicted: z.boolean(),
182
- // The human-readable cause a row is quarantined (its name collided with a
183
- // foreign tool at boot), or `null` when the row is not conflicted. Rendered
184
- // verbatim; nothing is coerced.
185
- conflicted_reason: z.string().nullable(),
186
- // Sorted preset-name cross-references: `uses` names the presets this one
187
- // composes as tools; `used_by` names the presets that compose this one.
188
- uses: z.array(z.string()),
189
- used_by: z.array(z.string()),
190
- });
191
- export type PresetRecord = z.infer<typeof presetRecord>;
192
-
193
- export const presetList = z.array(presetRecord);
194
-
195
- /** `GET /api/presets/{name}` — the record plus the active version's baked kwargs. */
196
- export const presetDetail = presetRecord.extend({
197
- fixed_kwargs: z.record(z.string(), z.unknown()),
198
- });
199
- export type PresetDetail = z.infer<typeof presetDetail>;
200
-
201
- /**
202
- * One immutable version row (`GET /api/presets/{name}/versions[/{version}]`).
203
- * `is_current` flags the active version; `tags` here is the GENERIC per-version
204
- * label list (kind-agnostic) — the only preset tags that remain, distinct from the
205
- * removed record-level categorization and from the tool_meta overlay.
206
- */
207
- export const presetVersion = z.object({
208
- version: z.number(),
209
- body: presetBody,
210
- tags: z.array(z.string()),
211
- created_at: z.string(),
212
- is_current: z.boolean(),
213
- });
214
- export type PresetVersion = z.infer<typeof presetVersion>;
215
-
216
- export const presetVersionList = z.array(presetVersion);
217
-
218
- /** `POST /api/presets/{name}/rollback` — the re-pointed active version. */
219
- export const presetRollback = z.object({ name: z.string(), active_version: z.number() });
220
-
221
- /** `DELETE /api/presets/{name}` — the removed preset. */
222
- export const presetDeleted = z.object({ name: z.string(), deleted: z.literal(true) });
223
-
224
- /** `POST /api/presets/{name}/rename` — the re-keyed record identity. */
225
- export const presetRenamed = z.object({
226
- name: z.string(),
227
- renamed_from: z.string(),
228
- active_version: z.number(),
229
- });
230
- export type PresetRenamed = z.infer<typeof presetRenamed>;
231
-
232
- /**
233
- * `GET /api/presets/{name}/referees` — every OTHER preset whose active authored
234
- * composition names this one as a tool (the referees a rename would strand). An
235
- * empty list is the clean case; a non-empty list means a rename is blocked until
236
- * those presets are updated. A drift throws `ApiSchemaError`.
237
- */
238
- export const presetReferees = z.object({
239
- name: z.string(),
240
- referees: z.array(z.string()),
241
- });
242
- export type PresetReferees = z.infer<typeof presetReferees>;
243
-
244
- /**
245
- * `POST /api/presets/validate` — the dry-run verdict for a preset draft. `valid`
246
- * is the absence of an `error`; an invalid draft is a SUCCESSFUL validation (HTTP
247
- * 200), so `error` carries the server's verbatim message. A drift throws
248
- * `ApiSchemaError`.
249
- */
250
- export const presetValidation = z.object({
251
- valid: z.boolean(),
252
- error: z.string().nullable(),
253
- });
254
- export type PresetValidation = z.infer<typeof presetValidation>;
255
-
256
- /**
257
- * `PUT /api/presets/{name}/versions/{version}/tags` — the re-annotated version.
258
- * Version tags are labels on an immutable body, so this rebinds nothing; the
259
- * response echoes the identity plus the new tag list. A drift throws
260
- * `ApiSchemaError`.
261
- */
262
- export const presetVersionTags = z.object({
263
- name: z.string(),
264
- version: z.number(),
265
- tags: z.array(z.string()),
266
- });
267
- export type PresetVersionTags = z.infer<typeof presetVersionTags>;
268
-
269
- // -- tool_meta (the organizational overlay) ----------------------------------
270
-
271
- /**
272
- * One folder in the tool-organization tree (`tool_folders`). `parent_id` is `null`
273
- * for a root folder and otherwise the containing folder's `id`; nesting is endless
274
- * via the parent chain. Ids are UUID strings. A drift throws `ApiSchemaError`.
275
- */
276
- export const folderRecord = z.object({
277
- id: z.string(),
278
- name: z.string(),
279
- parent_id: z.string().nullable(),
280
- });
281
- export type FolderRecord = z.infer<typeof folderRecord>;
282
-
283
- /**
284
- * The organizational overlay for one tool, keyed by `tool_name`. `display_name`
285
- * overrides the rendered label (`display_name ?? name`); `folder_id` places the tool
286
- * in a folder (`null` = unfiled); `tags` is the user's editable categorization,
287
- * merged with the tool's read-only native tags by the UI; `badges` is the operator's
288
- * editable capability-label overlay, merged with the tool's read-only plugin-declared
289
- * badges by the UI (INFORMATIONAL, never an enforced gate); `hidden` is TRI-STATE —
290
- * `null` = defer to the plugin declaration, `true` = force hidden, `false` = force
291
- * visible. A drift throws `ApiSchemaError`.
292
- */
293
- export const toolMetaRecord = z.object({
294
- tool_name: z.string(),
295
- display_name: z.string().nullable(),
296
- folder_id: z.string().nullable(),
297
- tags: z.array(z.string()),
298
- badges: z.array(z.string()),
299
- hidden: z.boolean().nullable(),
300
- });
301
- export type ToolMetaRecord = z.infer<typeof toolMetaRecord>;
302
-
303
- /**
304
- * `GET /api/tool-meta` — the whole overlay in one read: the flat folder tree plus
305
- * every per-tool row. The UI builds the tree and merges the rows against the live
306
- * tool list client-side. A drift throws `ApiSchemaError`.
307
- */
308
- export const toolMetaOverlay = z.object({
309
- folders: z.array(folderRecord),
310
- meta: z.array(toolMetaRecord),
311
- });
312
- export type ToolMetaOverlay = z.infer<typeof toolMetaOverlay>;
313
-
314
- /** `DELETE /api/tool-meta/tools/{tool_name}` — the dropped overlay row (idempotent). */
315
- export const toolMetaDeleted = z.object({ tool_name: z.string(), deleted: z.literal(true) });
316
-
317
- /** `DELETE /api/tool-meta/folders/{folder_id}` — the removed empty folder. */
318
- export const folderDeleted = z.object({ folder_id: z.string(), deleted: z.literal(true) });
319
-
320
- // -- templates ---------------------------------------------------------------
321
-
322
- export const templateNames = z.array(z.string());
323
- export const templateDetail = z.object({ template: z.string(), schema: jsonSchema });
324
- export const templateUploaded = z.object({ path: z.string(), uploaded: z.literal(true) });
325
- export const templateDeleted = z.object({ path: z.string(), deleted: z.literal(true) });
326
- export const templateDirDeleted = z.object({ path: z.string(), deleted: z.literal(true) });
327
- export const templateRendered = z.object({ rendered: z.string() });
328
- export const templateCacheCleared = z.object({ cleared: z.literal(true) });
329
-
330
- // -- storage -----------------------------------------------------------------
331
-
332
- /** `GET /api/storage` — the storage-provider identity, or the absent sentinel
333
- * (`present: false`, a 200) when no storage-provider plugin is installed. */
334
- export const storageInfo = z.object({
335
- present: z.boolean(),
336
- provider: z.string().nullable(),
337
- module: z.string().nullable(),
338
- });
339
- export type StorageInfo = z.infer<typeof storageInfo>;
340
-
341
- /** `GET /api/storage/resources` — the full sorted resource-id list. */
342
- export const storageResourceList = z.object({ resources: z.array(z.string()) });
343
-
344
- /** `GET /api/storage/resources/{id}/stat` — an object's inferred content type;
345
- * `null` when the provider cannot infer one (e.g. an unknown suffix). */
346
- export const storageResourceStat = z.object({
347
- id: z.string(),
348
- content_type: z.string().nullable(),
349
- });
350
- export type StorageResourceStat = z.infer<typeof storageResourceStat>;
351
-
352
- /** `POST /api/storage/resources` — the stored object's id. */
353
- export const storageResourceStored = z.object({ id: z.string(), stored: z.literal(true) });
354
-
355
- /** `DELETE /api/storage/resources/{id}` — the removed object's id. */
356
- export const storageResourceDeleted = z.object({ id: z.string(), deleted: z.literal(true) });
357
-
358
- /** `DELETE /api/storage/dirs/{path}` — the removed directory subtree. */
359
- export const storageDirDeleted = z.object({ dir: z.string(), deleted: z.literal(true) });
360
-
361
- // -- backend identity + fleet (worker bus) -----------------------------------
362
-
363
- /** `GET /api/backend` — the execution-backend identity, or the absent sentinel
364
- * (`present: false`, a 200) when no backend plugin is registered. Backend identity
365
- * is kept distinct from the fleet: the fleet doors ride the app's worker bus and
366
- * need no backend at all. */
367
- export const backendInfo = z.object({
368
- present: z.boolean(),
369
- backend: z.string().nullable(),
370
- module: z.string().nullable(),
371
- });
372
- export type BackendInfo = z.infer<typeof backendInfo>;
373
-
374
- /** The two worker kinds that join the bus: an ASGI `serve` worker or a
375
- * `backend` runtime process. A drift throws `ApiSchemaError`. */
376
- export const workerKind = z.enum(['serve', 'backend']);
377
- export type WorkerKind = z.infer<typeof workerKind>;
378
-
379
- /** The lifecycle state a worker advertises in its presence value: `ready` once its
380
- * resync has converged, `resyncing` while a boot/reconnect resync runs, `recycling`
381
- * once it has begun a graceful self-exit. A drift throws `ApiSchemaError`. */
382
- export const workerState = z.enum(['ready', 'resyncing', 'recycling']);
383
- export type WorkerState = z.infer<typeof workerState>;
384
-
385
- /** The last op a worker applied, stamped onto its presence row after the terminal
386
- * reply. `outcome` is the free op-outcome string; `at` is the ISO instant it landed. */
387
- export const workerLastOp = z.object({
388
- op: z.string(),
389
- outcome: z.string(),
390
- at: z.string(),
391
- });
392
- export type WorkerLastOp = z.infer<typeof workerLastOp>;
393
-
394
- /**
395
- * One live worker on the bus — a presence-census row. `name` is the stable slot
396
- * (`{kind}-{n}`) that doubles as a reload target; `generation` is the monotonic life
397
- * counter minted with the worker's claim; `kind` tells the two worker kinds apart;
398
- * `pid` is the process id. `joined_at`/`beat_at` are ISO instants (first-seen and last
399
- * heartbeat) for the cosmetic seen-since display; `state` is the advertised lifecycle
400
- * state; `stale` is SERVER-computed from the row's remaining presence TTL against the
401
- * bus cadence (a quiet row past the freshness bound — reconnecting or dead) and is the
402
- * only freshness signal a client reads, never a client-side threshold; `last_op` is the
403
- * worker's last applied op, or `null` before it has applied one.
404
- */
405
- export const fleetWorker = z.object({
406
- name: z.string(),
407
- kind: workerKind,
408
- pid: z.number(),
409
- generation: z.number(),
410
- joined_at: z.string(),
411
- beat_at: z.string(),
412
- state: workerState,
413
- stale: z.boolean(),
414
- last_op: workerLastOp.nullable(),
415
- });
416
- export type FleetWorker = z.infer<typeof fleetWorker>;
417
-
418
- /**
419
- * `GET /api/fleet/workers` — the live worker fleet from the bus presence census.
420
- * The census lists ALL subscribed workers (ASGI `serve` workers and backend-runtime
421
- * processes alike), not only backend workers. A census read that fails surfaces as a
422
- * loud 500, never a fabricated empty fleet.
423
- */
424
- export const fleetWorkers = z.object({ workers: z.array(fleetWorker) });
425
- export type FleetWorkers = z.infer<typeof fleetWorkers>;
426
-
427
- /**
428
- * One worker's outcome within a fleet broadcast. `applied`/`failed` are terminal wire
429
- * replies from a subscriber; `missing`/`departed`/`timed_out` are publisher-computed
430
- * from the presence census (a silent worker never sends them); `resyncing`/`recycling`/
431
- * `stale` are the gap outcomes for a row that failed the ready+fresh gate — reported as
432
- * its own actual condition rather than dropped (`resyncing` converges on its resync,
433
- * `recycling` is departing, `stale` is a quiet row past the freshness bound carrying no
434
- * convergence promise).
435
- */
436
- export const fleetOutcome = z.enum([
437
- 'applied',
438
- 'failed',
439
- 'missing',
440
- 'departed',
441
- 'timed_out',
442
- 'resyncing',
443
- 'recycling',
444
- 'stale',
445
- ]);
446
- export type FleetOutcome = z.infer<typeof fleetOutcome>;
447
-
448
- /**
449
- * One worker's result within a {@link fleetResult}. `payload` carries query-op data
450
- * (opaque to the UI); `error` carries a failed apply's message; `detail` carries the
451
- * publisher's report text for a computed gap outcome (`missing`/`departed`/`timed_out`/
452
- * `resyncing`/`recycling`/`stale`). A drift throws `ApiSchemaError`.
453
- */
454
- export const fleetWorkerResult = z.object({
455
- name: z.string(),
456
- outcome: fleetOutcome,
457
- payload: jsonValue,
458
- error: z.string().nullable(),
459
- detail: z.string().nullable(),
460
- });
461
- export type FleetWorkerResult = z.infer<typeof fleetWorkerResult>;
462
-
463
- /**
464
- * The awaited per-worker report of one fleet broadcast. Two honest shapes ride the
465
- * same schema. Reachable (`reachable: true`): `results` holds one entry per expected
466
- * worker (the serving worker's own entry included) plus the gap rows, so an unconfirmed
467
- * sibling is a visible `failed`/`missing`/`departed`/`timed_out`/`resyncing`/`recycling`/
468
- * `stale` entry, never a silent stale worker. Bus-unreachable (`reachable: false`): the
469
- * transport failed before any worker could reply, so `results` is empty and only `error`
470
- * is carried. This is the bare body of `POST /api/fleet/reload-config` and the single-MCP
471
- * reload; it is also the body the mode-wrapped {@link fleetReportFanout} carries under its
472
- * `fleet` / `unreachable` modes.
473
- */
474
- export const fleetResult = z.object({
475
- op: z.string(),
476
- reachable: z.boolean(),
477
- local_only: z.boolean(),
478
- results: z.array(fleetWorkerResult),
479
- error: z.string().nullable(),
480
- });
481
- export type FleetResult = z.infer<typeof fleetResult>;
482
-
483
- /**
484
- * `POST /api/fleet/reload-config` — soft-restart the fleet and report per-worker. The
485
- * serving worker applies its own reload first, then broadcasts; the returned report
486
- * names every worker's outcome. A failed local apply does not abort the broadcast, so
487
- * the report is embedded even on the convergence-failure path.
488
- */
489
- export const fleetReloadResult = fleetResult;
490
- export type FleetReloadResult = z.infer<typeof fleetReloadResult>;
491
-
492
- /**
493
- * The mode-wrapped fan-out summary a mutation embeds under its `fanout` field. Three
494
- * honest modes, discriminated on `mode`:
495
- * - `local-only` — the broadcast reached no sibling (a lone worker / no bus), a
496
- * benign single-process note.
497
- * - `fleet` — a reachable multi-worker broadcast; carries the full {@link
498
- * fleetResult} so an unconfirmed sibling is visible.
499
- * - `unreachable` — the bus itself could not be reached; carries the
500
- * bus-unreachable {@link fleetResult} (no worker list, only `error`).
501
- *
502
- * Every mutation whose response embeds this parses it EXPLICITLY: zod's default
503
- * unknown-key stripping would otherwise silently drop the field, hiding a failed
504
- * propagation from the shared fleet-report handler. A drift throws `ApiSchemaError`.
505
- */
506
- export const fleetReportFanout = z.discriminatedUnion('mode', [
507
- z.object({ mode: z.literal('local-only'), note: z.string() }),
508
- fleetResult.extend({ mode: z.literal('fleet') }),
509
- fleetResult.extend({ mode: z.literal('unreachable') }),
510
- ]);
511
- export type FleetReportFanout = z.infer<typeof fleetReportFanout>;
512
-
513
- // -- manifest / mcp ----------------------------------------------------------
514
-
515
- /**
516
- * The back-reference a connector writes onto a manifest MCP entry to mark it
517
- * connector-owned: the connection and the provider sub-service that own it.
518
- * Hand-authored entries carry no `managed` marker.
519
- */
520
- export const connectorRef = z.object({
521
- connection_id: z.string(),
522
- provider_id: z.string(),
523
- sub_service: z.string(),
524
- });
525
- export type ConnectorRef = z.infer<typeof connectorRef>;
526
-
527
- /**
528
- * One entry of the live manifest's MCP section. Only the `managed` provenance
529
- * marker is typed — the connect/disconnect flows write it server-side to point a
530
- * connector-owned entry back at its connection, and the editor renders such
531
- * entries read-only. `.nullish()` because a hand-authored entry carries `null`
532
- * (present) or omits the key. Every other field (title, config, extensions) rides
533
- * through `.loose()` unmodeled so the config editor keeps editing the whole
534
- * entry — narrowing it to a typed subset would silently strip the fields it saves.
535
- */
536
- export const manifestMcpEntry = z
537
- .object({
538
- managed: connectorRef.nullish(),
539
- })
540
- .loose();
541
- export type ManifestMcpEntry = z.infer<typeof manifestMcpEntry>;
542
-
543
- export const manifestView = z.object({
544
- mcp: z.array(manifestMcpEntry),
545
- user_tools: z.array(z.string()),
546
- });
547
-
548
- /**
549
- * One `!ENV ${VAR[:default]}` marker the manifest's MCP section carries
550
- * (`GET /api/manifest/mcp-env-refs`) — NAMES and BOOLEANS only, NEVER values.
551
- * `pointer` is the RFC 6901 json-pointer of the leaf (`/mcp/<i>/config/...`);
552
- * `has_default` is whether the marker carries a `:default`; `set` is whether the
553
- * var resolves in the EFFECTIVE env (the resolution truth the install-time
554
- * dangling refusal reads), so a var supplied by the deployment shows green, never
555
- * a false red. The checklist derives from THIS alone — no env value is ever fetched.
556
- */
557
- export const mcpEnvRef = z.object({
558
- var: z.string(),
559
- pointer: z.string(),
560
- has_default: z.boolean(),
561
- set: z.boolean(),
562
- });
563
- export type McpEnvRef = z.infer<typeof mcpEnvRef>;
564
- export const mcpEnvRefs = z.array(mcpEnvRef);
565
- export type McpEnvRefs = z.infer<typeof mcpEnvRefs>;
566
- /**
567
- * The apply result every config-writer mutation returns (`POST /api/mcp-config`,
568
- * `POST /api/config/env`): the serving worker's local reload result (`status` +
569
- * `env_keys`) plus the mode-wrapped fleet fan-out of the reload it broadcast. The
570
- * `fanout` is parsed EXPLICITLY so the shared fleet-report handler can surface a
571
- * failed propagation — zod's unknown-key stripping would otherwise hide it. A drift
572
- * throws `ApiSchemaError`.
573
- */
574
- export const reloadConfigResult = z.object({
575
- status: z.string(),
576
- env_keys: z.number(),
577
- fanout: fleetReportFanout,
578
- });
579
- export type ReloadConfigResult = z.infer<typeof reloadConfigResult>;
580
-
581
- // -- system kinds ------------------------------------------------------------
582
- // `GET /api/system/kinds` — one row per pluggable kind (identity, accounts,
583
- // monitoring, storage, backend, channels, webhook_verifiers, config,
584
- // studio_plugins). `state` is the kind's active/default/off status: `active` = a
585
- // real plugin serves it, `default` = a built-in fallback serves it, `off` =
586
- // nothing is registered (a legal state, reported not "fixed"). `plugin` is the
587
- // provider/module name when known, else null. Plain (non-strict) `z.object` so a
588
- // server that adds a field to the row still parses.
589
-
590
- export const kindStatus = z.object({
591
- kind: z.string(),
592
- state: z.enum(['active', 'default', 'off']),
593
- plugin: z.string().nullable(),
594
- detail: z.string(),
595
- });
596
- export const kindStatusList = z.array(kindStatus);
597
- export type KindStatus = z.infer<typeof kindStatus>;
598
-
599
- /**
600
- * `POST /api/tools/{name}/extensions` — the apply result. The write persists the
601
- * manifest, reloads locally, then broadcasts the reload to the whole fleet. The
602
- * response is the one apply-result shape every config-writer returns: the local
603
- * reload result (`status` + `env_keys`) plus the mode-wrapped fleet `fanout`. The
604
- * fan-out rides EVERY deployment (a lone worker reports `local-only`), so this flat
605
- * shape parses every save and the shared fleet-report handler renders any failed
606
- * propagation from `fanout`.
607
- */
608
- export const toolExtensionsApplyResult = reloadConfigResult;
609
- export type ToolExtensionsApplyResult = z.infer<typeof toolExtensionsApplyResult>;
610
- export const mcpStatus = z.object({
611
- bound: z.record(z.string(), z.array(z.string())),
612
- failed: z.array(z.object({ title: z.string(), status: z.string() })),
613
- });
614
-
615
- /**
616
- * `POST /api/mcp-status/{title}/reload` — re-probe a single MCP server and broadcast
617
- * the reload to the fleet. The response is the bare per-worker {@link fleetResult}
618
- * (the serving worker's re-probe rides its own `results` entry as the payload); there
619
- * is no local/workers split. A drift throws `ApiSchemaError`.
620
- */
621
- export const mcpReloadResult = fleetResult;
622
- export type McpReloadResult = z.infer<typeof mcpReloadResult>;
623
-
624
- // -- sub-mcp -----------------------------------------------------------------
625
-
626
- /**
627
- * One registered sub-MCP mount as it rides the registry list and the projection:
628
- * the tools it exposes and the transport it serves on. In `GET /api/sub-mcp` the
629
- * slug is the map key, so the mount value carries no slug; {@link subMcpEntry}
630
- * extends this with `slug` for the capability projection where mounts are a flat
631
- * list. `transport` is always present on the wire (the server defaults it to
632
- * `http`), so it is required — its absence is a drift, not a legal shape.
633
- */
634
- export const subMcpMount = z.object({
635
- tools: z.array(z.string()),
636
- transport: z.string(),
637
- });
638
- export type SubMcpMount = z.infer<typeof subMcpMount>;
639
-
640
- export const subMcpList = z.record(z.string(), subMcpMount);
641
- export const subMcpCreated = z.object({
642
- slug: z.string(),
643
- tools: z.array(z.string()),
644
- transport: z.string(),
645
- });
646
- export const subMcpRemoved = z.object({ slug: z.string(), removed: z.literal(true) });
647
-
648
- // -- config ------------------------------------------------------------------
649
-
650
- export const envConfig = z.object({
651
- env: z.record(z.string(), z.string()),
652
- secret_keys: z.array(z.string()),
653
- });
654
- /**
655
- * `GET /api/config/mode` — the active config backend mode (`file` / `k8s`). The
656
- * skeleton emits only `config_mode`; `read_only` is a UI-side gate, tolerated-absent
657
- * and defaulting to `false` (writable) so a normal response never drifts, while a
658
- * genuinely read-only deployment can opt in by emitting `read_only: true`.
659
- */
660
- export const configMode = z.object({
661
- config_mode: z.string(),
662
- read_only: z.boolean().default(false),
663
- });
664
-
665
- // -- settings profiles -------------------------------------------------------
666
- // The admin-only `/api/config/profiles/*` surface. Secrets ride the wire in the
667
- // clear (secret/fenced routes); masking is CLIENT-SIDE per the secret-mask union. A drift
668
- // on any of these throws `ApiSchemaError`.
669
-
670
- /**
671
- * `GET /api/config/profiles` — one row per stored profile. The list is
672
- * names+descriptions ONLY (action=read); no `env`, no `secret_keys`, no bodies.
673
- * (No `last_modified`: the list carries identity, not history — version metadata
674
- * is the `/versions` surface — so the list renders no last-modified column.)
675
- */
676
- export const settingsProfileSummary = z.object({
677
- name: z.string(),
678
- description: z.string(),
679
- });
680
- export type SettingsProfileSummary = z.infer<typeof settingsProfileSummary>;
681
- export const settingsProfileList = z.array(settingsProfileSummary);
682
-
683
- /**
684
- * `GET /api/config/profiles/{name}` and each version body — the stored profile
685
- * document (action=secret). `env` carries REAL values; `secret_keys` is the
686
- * per-profile secret-marks band folded into the secret-mask union for masking.
687
- */
688
- export const settingsProfileBody = z.object({
689
- description: z.string(),
690
- env: z.record(z.string(), z.string()),
691
- secret_keys: z.array(z.string()),
692
- });
693
- export type SettingsProfileBody = z.infer<typeof settingsProfileBody>;
694
-
695
- /** `PUT /api/config/profiles/{name}` — a minimal save ack; `version` is the new store version. */
696
- export const settingsProfileSaved = z.object({
697
- ok: z.literal(true),
698
- version: z.number().optional(),
699
- });
700
- export type SettingsProfileSaved = z.infer<typeof settingsProfileSaved>;
701
-
702
- /** `DELETE /api/config/profiles/{name}` — a minimal removal ack. */
703
- export const settingsProfileDeleted = z.object({ ok: z.literal(true) });
704
-
705
- /** `POST /api/config/profiles/{name}/rollback` — the restore ack; `version` is the resulting active version. */
706
- export const settingsProfileRollback = z.object({
707
- ok: z.literal(true),
708
- version: z.number(),
709
- });
710
- export type SettingsProfileRollback = z.infer<typeof settingsProfileRollback>;
711
-
712
- /**
713
- * `POST /api/config/profiles/{name}/diff` — the SAVED profile vs the stored env
714
- * (action=secret). `changed` carries real `old`/`new` values (UI masks via
715
- * JsonDiff); `recycle_keys`/`refused_keys` are the diff's recycle-class and
716
- * boundary-refused key names, called out in the UI before apply.
717
- */
718
- export const settingsProfileDiff = z.object({
719
- added: z.array(z.string()),
720
- removed: z.array(z.string()),
721
- changed: z.array(z.object({ key: z.string(), old: z.string(), new: z.string() })),
722
- recycle_keys: z.array(z.string()),
723
- refused_keys: z.array(z.string()),
724
- });
725
- export type SettingsProfileDiff = z.infer<typeof settingsProfileDiff>;
726
-
727
- /**
728
- * `POST /api/config/profiles/{name}/apply` — the dedicated `profile_apply_response`
729
- * (action=fenced, destructive). NAMES-ONLY (no env value in any
730
- * section): `hot` is the hot-swapped key names; `recycle` is the orchestrator's
731
- * per-worker section — one `{name, kind, status, generation_before}` row per recycled
732
- * target, `generation_before` the life that was recycled, the applier's OWN row carrying
733
- * the `self-deferred` status literal when the diff has serve-affecting recycle keys;
734
- * `fresh` is the per-kind list of NEW ready lives observed since the pre-apply snapshot
735
- * (each `{name, kind, generation}`), evidence of fresh capacity and never claimed as any
736
- * target's successor; `refused` is `[]` on success by construction (populated
737
- * `{key, reason}` only on a refusal), the populated refusal surfaces being the diff
738
- * preview and the refusal error report. `fanout` is the mode-wrapped fleet broadcast,
739
- * parsed EXPLICITLY so a failed propagation reaches the shared fleet-report handler. A
740
- * drift throws `ApiSchemaError`.
741
- */
742
- export const profileApplyResponse = z.object({
743
- hot: z.array(z.string()),
744
- recycle: z.array(
745
- z.object({
746
- name: z.string(),
747
- kind: z.string(),
748
- status: z.string(),
749
- generation_before: z.number(),
750
- }),
751
- ),
752
- fresh: z.array(z.object({ name: z.string(), kind: z.string(), generation: z.number() })),
753
- refused: z.array(z.object({ key: z.string(), reason: z.string() })),
754
- fanout: fleetReportFanout,
755
- });
756
- export type ProfileApplyResponse = z.infer<typeof profileApplyResponse>;
757
-
758
- /**
759
- * `GET /api/config/profiles/{name}/versions` — version METADATA only (action=read;
760
- * no bodies), the generic versioned-store row minus `body`. `is_current` flags the
761
- * active pointer.
762
- */
763
- export const settingsProfileVersionMeta = z.object({
764
- version: z.number(),
765
- tags: z.array(z.string()),
766
- created_at: z.string(),
767
- is_current: z.boolean(),
768
- });
769
- export type SettingsProfileVersionMeta = z.infer<typeof settingsProfileVersionMeta>;
770
- export const settingsProfileVersionList = z.array(settingsProfileVersionMeta);
771
-
772
- /**
773
- * `GET /api/config/profiles/{name}/versions/{version}` — one immutable version WITH
774
- * its `body` (action=secret; body rendered masked per the secret-mask union). The generic
775
- * versioned-store row shape.
776
- */
777
- export const settingsProfileVersion = settingsProfileVersionMeta.extend({
778
- body: settingsProfileBody,
779
- });
780
- export type SettingsProfileVersion = z.infer<typeof settingsProfileVersion>;
781
-
782
- // -- connectors --------------------------------------------------------------
783
-
784
- export const subServiceView = z.object({
785
- id: z.string(),
786
- display_name: z.string(),
787
- description: z.string(),
788
- scopes: z.array(z.string()),
789
- });
790
-
791
- export const configFieldView = z.object({
792
- key: z.string(),
793
- label: z.string(),
794
- target: z.enum(['env', 'header']),
795
- required: z.boolean(),
796
- secret: z.boolean(),
797
- });
798
-
799
- export const providerView = z.object({
800
- id: z.string(),
801
- display_name: z.string(),
802
- description: z.string(),
803
- // Required and a real URL: the contract's `ProviderDescriptor.icon_url` runs
804
- // `check_web_url(schemes=("https",))`, which rejects an empty/relative value, so
805
- // an empty or malformed one here is drift and fails the parse loudly.
806
- icon_url: z.url(),
807
- kind: z.enum(['oauth', 'none']),
808
- origin: z.enum(['system', 'community']),
809
- category: z.string(),
810
- sub_services: z.array(subServiceView),
811
- config_fields: z.array(configFieldView),
812
- });
813
- export type ProviderView = z.infer<typeof providerView>;
814
-
815
- /**
816
- * One `connector_category` grouping row served alongside the catalog: the UI's
817
- * label and sort key for the providers it groups, so the list can group, order,
818
- * and label providers by category without a second read.
819
- */
820
- export const connectorCategoryView = z.object({
821
- id: z.string(),
822
- display_name: z.string(),
823
- sort_order: z.number(),
824
- });
825
- export type ConnectorCategoryView = z.infer<typeof connectorCategoryView>;
826
-
827
- /**
828
- * `GET /api/connectors/providers` — the provider catalog: the providers plus the
829
- * category groupings the UI arranges them under. Reshaped from a bare provider
830
- * array so the category label/order surface rides the same read.
831
- */
832
- export const providers = z.object({
833
- providers: z.array(providerView),
834
- categories: z.array(connectorCategoryView),
835
- });
836
- export type ProviderCatalogResponse = z.infer<typeof providers>;
837
-
838
- export const connectionView = z.object({
839
- connection_id: z.string(),
840
- provider_id: z.string(),
841
- alias: z.string(),
842
- kind: z.enum(['oauth', 'none']),
843
- account_identity: z.string().nullable(),
844
- enabled_sub_services: z.array(z.string()),
845
- granted_scopes: z.array(z.string()),
846
- // Sub-services whose MCP server did not answer a live reachability probe.
847
- // Populated only on the single-connection GET (the list projection is
848
- // probe-free and always sends `[]`). Reachability is distinct from
849
- // `auth_health_state`: a healthy connection can still have a down sub-service.
850
- // Defaulted so a projection that omits it (an older worker mid-rollout) still
851
- // parses rather than false-failing the whole view.
852
- unreachable_sub_services: z.array(z.string()).default([]),
853
- auth_health_state: z.enum(['healthy', 'reconnect_required', 'refresh_failing']),
854
- created_at: z.string(),
855
- });
856
- export type ConnectionView = z.infer<typeof connectionView>;
857
-
858
- export const connectionsList = z.object({
859
- items: z.array(connectionView),
860
- total: z.number(),
861
- // Count of connections whose `auth_health_state` is not healthy across the whole
862
- // set (independent of any filter/limit) — the attention-badge count. Optional:
863
- // no UI consumes it, and an older worker mid-rollout omits it.
864
- unhealthy: z.number().optional(),
865
- });
866
-
867
- // A connector mutation that writes the manifest embeds the mode-wrapped fleet
868
- // `fanout` of the reload it broadcast; it is `null` when the response path mutated no
869
- // manifest (an OAuth START opens a flow before any write). Parsed EXPLICITLY so the
870
- // shared fleet-report handler surfaces a failed propagation on every connector flow —
871
- // zod's unknown-key stripping would otherwise drop it silently.
872
- export const startConnectResult = z.union([
873
- z.object({ flow_id: z.string(), authorize_url: z.string() }),
874
- z.object({
875
- connection_id: z.string(),
876
- added_manifest_entries: z.array(z.string()),
877
- fanout: fleetReportFanout.nullable(),
878
- }),
879
- ]);
880
-
881
- // A reconnect START opens an OAuth flow and writes no manifest, so it carries no
882
- // fan-out (the manifest write lands when the flow completes).
883
- export const reconnectResult = z.object({ flow_id: z.string(), authorize_url: z.string() });
884
-
885
- export const disconnectResult = z.object({
886
- connection_id: z.string(),
887
- upstream_revoke_outcome: z.enum(['success', 'failed', 'skipped']),
888
- upstream_revoke_status: z.number().nullable(),
889
- removed_manifest_entries: z.array(z.string()),
890
- fanout: fleetReportFanout.nullable(),
891
- });
892
-
893
- export const patchSubServicesResult = z.object({
894
- connection_id: z.string(),
895
- enabled_sub_services: z.array(z.string()),
896
- consent_required: z.boolean(),
897
- flow_id: z.string().nullable(),
898
- authorize_url: z.string().nullable(),
899
- added_manifest_entries: z.array(z.string()),
900
- removed_manifest_entries: z.array(z.string()),
901
- // `null` on a consent-only toggle that forks an OAuth flow without a manifest write.
902
- fanout: fleetReportFanout.nullable(),
903
- });
904
-
905
- export const oauthCompleteResult = z.discriminatedUnion('kind', [
906
- z.object({
907
- kind: z.literal('success'),
908
- connection_id: z.string(),
909
- return_url: z.string(),
910
- // `null` when completing the flow mutated no manifest (a reconcile that changed
911
- // nothing).
912
- fanout: fleetReportFanout.nullable(),
913
- }),
914
- z.object({ kind: z.literal('failed'), reason: z.string() }),
915
- z.object({ kind: z.literal('cancelled'), message: z.string() }),
916
- ]);
917
- export type OAuthCompleteResult = z.infer<typeof oauthCompleteResult>;
918
-
919
- // -- studio plugin registry --------------------------------------------------
920
-
921
- export const studioPluginManifest = z.object({
922
- name: z.string(),
923
- version: z.string(),
924
- api_version: z.number(),
925
- entry: z.string(),
926
- integrity: z.record(z.string(), z.string()),
927
- contributions: z.object({
928
- tool_panels: z.record(z.string(), z.string()).default({}),
929
- pages: z.array(z.string()).default([]),
930
- settings_tabs: z.array(z.string()).default([]),
931
- // Optional with a default so a manifest without the field still parses; the
932
- // non-strict object would otherwise SILENTLY STRIP an undeclared field, so a
933
- // server that serves `nav_entries` would never reach the UI.
934
- nav_entries: z.array(z.string()).default([]),
935
- }),
936
- });
937
- export const studioPluginRegistry = z.array(studioPluginManifest);
938
- export type StudioPluginManifest = z.infer<typeof studioPluginManifest>;
939
-
940
- /**
941
- * The page window every paged read door carries; `next_page` is null on the last
942
- * page. `truncated` is TRUE when the door had more matches than it would scan or
943
- * return under its cap — the UI must surface that LOUDLY, never a silent cut — and
944
- * false whenever the window is exhaustive.
945
- */
946
- const pageWindow = {
947
- total: z.number(),
948
- page: z.number(),
949
- page_size: z.number(),
950
- next_page: z.number().nullable(),
951
- truncated: z.boolean(),
952
- };
953
-
954
- // -- interactions ------------------------------------------------------------
955
- // Shapes match the skeleton's interactions SSE contract: the SSE
956
- // `interaction.add` frame carries the question and its answer format; the answer
957
- // door returns { interaction_id, status }. `answered` is not a wire field — it
958
- // is a client-side flag the stream hook flips on an `interaction.answered` event.
959
-
960
- export const answerFormat = z.enum(['text', 'confirm', 'select', 'form', 'external']);
961
- export type AnswerFormat = z.infer<typeof answerFormat>;
962
-
963
- /** The kind of a question's display-only media item: a remote/inline image, or a link. */
964
- export const mediaKind = z.enum(['image', 'link']);
965
- export type MediaKind = z.infer<typeof mediaKind>;
966
-
967
- /**
968
- * One item of a question's display-only media. Applied PER ITEM by the renderer
969
- * (never as a strict array on the frame): the frame's `media` field is a loose
970
- * `z.array(z.unknown())`, and the renderer `safeParse`s each item with this schema
971
- * so one malformed/hostile item is a loud per-item notice, not a whole-frame parse
972
- * failure that would silently vanish an answerable question.
973
- *
974
- * `caption` is `.nullish()` (absent OR `null` both parse to a caption-less item):
975
- * a caption-less item may arrive with the key omitted OR with an explicit `null`, so
976
- * `.optional()` here would false-fail the explicit `null` and render a good image as
977
- * malformed. `url` and `kind` are the only load-bearing fields; the image
978
- * src scheme gate and the link neutralization are enforced by the renderer.
979
- */
980
- export const interactionMediaItem = z.object({
981
- kind: mediaKind,
982
- url: z.string(),
983
- caption: z.string().nullish(),
984
- });
985
- export type InteractionMediaItem = z.infer<typeof interactionMediaItem>;
986
-
987
- /**
988
- * One per-send choice for a `form` property whose schema is a string (or array of
989
- * strings). `value` is what the answer carries; `label` (absent OR null both parse
990
- * to no label) is the human text shown in its place. A send may replace a property's
991
- * `enum` this way for one ask without republishing the form. Applied per property by
992
- * the form preview (safeParsed), so a malformed entry is a loud notice, never silent.
993
- */
994
- export const formOption = z.object({
995
- value: z.string(),
996
- label: z.string().nullish(),
997
- });
998
- export type FormOption = z.infer<typeof formOption>;
999
-
1000
- /**
1001
- * Per-send `form` data: `values` prefills top-level properties (keyed by property
1002
- * name), `options` supplies per-send choice lists (keyed by property name). Both
1003
- * default to empty. The form preview safeParses this off `format_payload.data`, so a
1004
- * malformed block is a loud notice rather than a whole-frame parse failure.
1005
- */
1006
- export const formData = z.object({
1007
- values: z.record(z.string(), z.unknown()).default({}),
1008
- options: z.record(z.string(), z.array(formOption)).default({}),
1009
- });
1010
- export type FormData = z.infer<typeof formData>;
1011
-
1012
- /**
1013
- * One page of a stepped `form`: a `title` and the ordered top-level property names it
1014
- * groups. `format_payload.pages` is the ordered list of these; absent = one page. The
1015
- * form preview safeParses the list, so a malformed page is a loud notice, never silent.
1016
- */
1017
- export const formPage = z.object({
1018
- title: z.string(),
1019
- fields: z.array(z.string()),
1020
- });
1021
- export type FormPage = z.infer<typeof formPage>;
1022
-
1023
- /** The `form` pages list (`format_payload.pages`), safeParsed as a whole by the preview. */
1024
- export const formPages = z.array(formPage);
1025
- export type FormPages = z.infer<typeof formPages>;
1026
-
1027
- export const interaction = z.object({
1028
- interaction_id: z.string(),
1029
- group_id: z.string(),
1030
- question: z.string().default(''),
1031
- answer_format: answerFormat,
1032
- // Format-specific payload: select options, form JSON schema (with optional per-send
1033
- // `data` and `pages`), external url. The wire sends `null` for formats that carry
1034
- // none (text/confirm); it is normalized to `{}` so consumers always see an object.
1035
- format_payload: z
1036
- .record(z.string(), z.unknown())
1037
- .nullish()
1038
- .transform((value) => value ?? {}),
1039
- created_at: z.string(),
1040
- timeout_at: z.string(),
1041
- // Whether the answer body is sensitive and never persisted server-side. Rides
1042
- // every add frame; an absent value (older frame) normalizes to false.
1043
- sensitive: z.boolean().default(false),
1044
- // True only for an external question answered by a signed server-to-server
1045
- // callback (never by a human); the frame never carries the verifier object.
1046
- // Absent/undefined for every other question.
1047
- server_verified: z.boolean().optional(),
1048
- // Name of the channel plugin the question was also delivered through (e.g.
1049
- // "telegram"); absent when the question lives only in this inbox.
1050
- channel: z.string().optional(),
1051
- // Interaction attribution, each additive and absent when unset. `recipient` is
1052
- // the channel delivery address the question went to; `origin` is the run id of
1053
- // the background tool run that asked; `audience` is the user_id the question is
1054
- // addressed to. Display/binding data only, never an authorization axis; a
1055
- // present non-string is malformed.
1056
- recipient: z.string().optional(),
1057
- origin: z.string().optional(),
1058
- audience: z.string().optional(),
1059
- // Display-only media shown WITH the question (images and/or links). Absent when
1060
- // the question carries none. Deliberately LOOSE (`z.array(z.unknown())`): each
1061
- // item is `safeParse`d per item by the renderer against `interactionMediaItem`,
1062
- // so one malformed/hostile item can never fail the whole frame parse and hide an
1063
- // answerable question. A present non-array value is still malformed.
1064
- media: z.array(z.unknown()).optional(),
1065
- });
1066
- export type Interaction = z.infer<typeof interaction>;
1067
-
1068
- /** The answer door's response — the blocked caller was woken. */
1069
- export const interactionAnswered = z.object({
1070
- interaction_id: z.string(),
1071
- status: z.string(),
1072
- });
1073
- export type InteractionAnswered = z.infer<typeof interactionAnswered>;
1074
-
1075
- /**
1076
- * The cancel door's response — a pending ask was WITHDRAWN without an answer (the
1077
- * asking flow does not resume). `status` is `"cancelled"`; the shape mirrors the
1078
- * answer door's terminal reply, but the door is distinct so it carries its own
1079
- * schema.
1080
- */
1081
- export const interactionCancelled = z.object({
1082
- interaction_id: z.string(),
1083
- status: z.string(),
1084
- });
1085
- export type InteractionCancelled = z.infer<typeof interactionCancelled>;
1086
-
1087
- /**
1088
- * A page of pending interactions from `GET /api/interactions?page=&pageSize=`. Each
1089
- * item is the same record the stream's `interaction.add` frame carries. The pending
1090
- * set is the paged base the tail-only stream applies live deltas over.
1091
- */
1092
- export const interactionsPage = z.object({
1093
- items: z.array(interaction),
1094
- ...pageWindow,
1095
- });
1096
- export type InteractionsPage = z.infer<typeof interactionsPage>;
1097
-
1098
- // -- channels ----------------------------------------------------------------
1099
- // The installed channel-plugin catalog: names registered via the manifest's
1100
- // channel modules. A deployment with none bound answers questions only in this
1101
- // inbox (the Studio default).
1102
-
1103
- export const channels = z.object({
1104
- channels: z.array(z.string()),
1105
- });
1106
- export type Channels = z.infer<typeof channels>;
1107
-
1108
- // -- conversations -----------------------------------------------------------
1109
- // The cross-channel conversation monitor's READ side: the stored routing rows
1110
- // (the route picker) from `GET /api/conversations`, a route's threads from
1111
- // `GET /api/conversations/{route}/threads`, and one thread's transcript from
1112
- // `GET /api/conversations/{route}/transcript?thread_id=`. Timestamps are float
1113
- // epoch seconds, not ISO strings, and both paged doors carry the same window
1114
- // fields; the transcript adds the direction it was read in.
1115
-
1116
- /** The inbound door a route is bound to: the authed API, or a channel medium. */
1117
- export const conversationDoor = z.enum(['api', 'channel']);
1118
- export type ConversationDoor = z.infer<typeof conversationDoor>;
1119
-
1120
- /** What a route's turn runs: a registered agent, or a tool dispatch. */
1121
- export const conversationTargetKind = z.enum(['agent', 'tool']);
1122
- export type ConversationTargetKind = z.infer<typeof conversationTargetKind>;
1123
-
1124
- /**
1125
- * A route's default control mode (`initial_mode`) when a thread carries no
1126
- * per-thread override: `agent` runs the target turn, `manual` suppresses it for an
1127
- * operator to answer. The same value vocabulary as a per-thread mode override, but
1128
- * a distinct concept (the route default vs. one thread's override).
1129
- */
1130
- export const conversationMode = z.enum(['agent', 'manual']);
1131
- export type ConversationMode = z.infer<typeof conversationMode>;
1132
-
1133
- /**
1134
- * Where a record sits between intake and a terminal outcome. `failed` is the one
1135
- * the monitor surfaces loudly; `shed` and `silent` are terminal by design and
1136
- * never send.
1137
- */
1138
- export const conversationDeliveryStatus = z.enum([
1139
- 'accepted',
1140
- 'pending_delivery',
1141
- 'provisional',
1142
- 'delivered',
1143
- 'failed',
1144
- 'shed',
1145
- 'silent',
1146
- ]);
1147
- export type ConversationDeliveryStatus = z.infer<typeof conversationDeliveryStatus>;
1148
-
1149
- /** The nature of a turn's outcome, orthogonal to where its delivery stands. */
1150
- export const conversationAnswerStatus = z.enum(['answered', 'error', 'silent']);
1151
- export type ConversationAnswerStatus = z.infer<typeof conversationAnswerStatus>;
1152
-
1153
- /**
1154
- * Who authored a record's outgoing side. `client` is the ordinary turn the flow
1155
- * answered; `operator` is a message a human sent into the thread, whose text
1156
- * rides `answer` (its `inbound_text` is empty).
1157
- */
1158
- export const conversationRecordOrigin = z.enum(['client', 'operator']);
1159
- export type ConversationRecordOrigin = z.infer<typeof conversationRecordOrigin>;
1160
-
1161
- export const conversationRoutes = z.object({
1162
- items: z.array(conversationRoute),
1163
- total: z.number(),
1164
- });
1165
- export type ConversationRoutes = z.infer<typeof conversationRoutes>;
1166
-
1167
- /**
1168
- * The create/replace reply: whether THIS call created the row (vs. replaced an
1169
- * existing one), the row itself (its `callback_secret` withheld), and the
1170
- * `callback_secret` returned ONCE for an `api`-door row (`null` for a `channel`
1171
- * row). The secret signs delivery callbacks and is never re-read from any door, so
1172
- * a caller that needs it must capture it from this reply.
1173
- */
1174
- export const conversationRouteWritten = z.object({
1175
- created: z.boolean(),
1176
- route_name: z.string(),
1177
- route: conversationRoute,
1178
- callback_secret: z.string().nullable(),
1179
- });
1180
- export type ConversationRouteWritten = z.infer<typeof conversationRouteWritten>;
1181
-
1182
- /**
1183
- * The delete reply: `removed` says whether THIS call removed the routing row (a
1184
- * retryable reclamation of orphaned thread indexes answers `false` rather than a
1185
- * 404).
1186
- */
1187
- export const conversationRouteDeleted = z.object({
1188
- removed: z.boolean(),
1189
- route_name: z.string(),
1190
- });
1191
- export type ConversationRouteDeleted = z.infer<typeof conversationRouteDeleted>;
1192
-
1193
- /**
1194
- * One thread of a route, summarized from its newest record. A `thread_id` holds
1195
- * an api-door address (`{principal}/{end user}`, percent-encoded), so it never
1196
- * goes back into a URL as a path segment — the transcript door takes it as a
1197
- * query value.
1198
- */
1199
- export const conversationThread = z.object({
1200
- thread_id: z.string(),
1201
- client_address: z.string(),
1202
- last_activity_at: z.number(),
1203
- message_count: z.number(),
1204
- last_delivery_status: conversationDeliveryStatus,
1205
- });
1206
- export type ConversationThread = z.infer<typeof conversationThread>;
1207
-
1208
- export const conversationThreadsPage = z.object({
1209
- items: z.array(conversationThread),
1210
- ...pageWindow,
1211
- });
1212
- export type ConversationThreadsPage = z.infer<typeof conversationThreadsPage>;
1213
-
1214
- /**
1215
- * One exchange: the visitor's `inbound_text` — every record answers a message, so
1216
- * both projections always carry it — and the agent's `answer`, plus where that
1217
- * answer's delivery stands. The fields after `updated_at` are ADMIN ONLY —
1218
- * the same door serves a caller-scoped projection that withholds them — so they
1219
- * are optional here and absence means "not published to this reader".
1220
- */
1221
- export const conversationMessage = z.object({
1222
- message_id: z.string(),
1223
- route_name: z.string(),
1224
- door: conversationDoor,
1225
- thread_id: z.string(),
1226
- client_address: z.string(),
1227
- caller_principal: z.string().nullable(),
1228
- inbound_text: z.string(),
1229
- answer_status: conversationAnswerStatus.nullable(),
1230
- answer: z.string().nullable(),
1231
- origin: conversationRecordOrigin,
1232
- delivery_status: conversationDeliveryStatus,
1233
- created_at: z.number(),
1234
- updated_at: z.number(),
1235
- channel: z.string().nullable().optional(),
1236
- our_identity: z.string().nullable().optional(),
1237
- provider_message_id: z.string().nullable().optional(),
1238
- callback_url: z.string().nullable().optional(),
1239
- error: z.string().nullable().optional(),
1240
- outbound_message_ids: z.array(z.string()).optional(),
1241
- attempts: z.number().optional(),
1242
- });
1243
- export type ConversationMessage = z.infer<typeof conversationMessage>;
1244
-
1245
- /**
1246
- * The direction a transcript page reads in. `asc` is the reading order (oldest
1247
- * first); `desc` is the live-tail order, where page 1 is always the newest page
1248
- * and paging forward walks BACKWARDS through the thread's history.
1249
- */
1250
- export const transcriptOrder = z.enum(['asc', 'desc']);
1251
- export type TranscriptOrder = z.infer<typeof transcriptOrder>;
1252
-
1253
- /** A transcript page, echoing the order it was read in. */
1254
- export const conversationTranscriptPage = z.object({
1255
- items: z.array(conversationMessage),
1256
- ...pageWindow,
1257
- order: transcriptOrder,
1258
- });
1259
- export type ConversationTranscriptPage = z.infer<typeof conversationTranscriptPage>;
1260
-
1261
- /**
1262
- * A route-scoped message search (`GET /api/conversations/{route}/messages/search?q`):
1263
- * every record on the route whose text matches `q`, across threads, newest first.
1264
- * The record shape is the transcript's exactly, so a hit renders with the same
1265
- * projection. `truncated` says the match set outran the door's scan cap — surfaced
1266
- * loudly, never a silent cut.
1267
- */
1268
- export const conversationMessageSearchPage = z.object({
1269
- items: z.array(conversationMessage),
1270
- ...pageWindow,
1271
- });
1272
- export type ConversationMessageSearchPage = z.infer<typeof conversationMessageSearchPage>;
1273
-
1274
- /**
1275
- * Who answers a thread's incoming messages: `agent` lets the flow reply on its
1276
- * own; `manual` holds the flow back so a human answers. `source` says whether the
1277
- * setting is the thread's own (`thread`) or inherited from its route (`route`).
1278
- */
1279
- export const conversationThreadMode = z.enum(['agent', 'manual']);
1280
- export type ConversationThreadMode = z.infer<typeof conversationThreadMode>;
1281
-
1282
- export const conversationThreadModeState = z.object({
1283
- mode: conversationThreadMode,
1284
- source: z.enum(['thread', 'route']),
1285
- });
1286
- export type ConversationThreadModeState = z.infer<typeof conversationThreadModeState>;
1287
-
1288
- /** The receipt a thread-message write returns: the stored id and its thread. */
1289
- export const conversationThreadMessageSent = z.object({
1290
- message_id: z.string(),
1291
- thread_id: z.string(),
1292
- });
1293
- export type ConversationThreadMessageSent = z.infer<typeof conversationThreadMessageSent>;
1294
-
1295
- /** Every stored per-target config (`GET /api/conversation-configs`). */
1296
- export const conversationConfigs = z.object({
1297
- items: z.array(targetConversationConfig),
1298
- total: z.number(),
1299
- });
1300
- export type ConversationConfigs = z.infer<typeof conversationConfigs>;
1301
-
1302
- /**
1303
- * The upsert reply: whether THIS call created the config (vs. replaced an existing
1304
- * one), its key, and the stored row.
1305
- */
1306
- export const conversationConfigWritten = z.object({
1307
- created: z.boolean(),
1308
- target_kind: conversationTargetKind,
1309
- target_name: z.string(),
1310
- config: targetConversationConfig,
1311
- });
1312
- export type ConversationConfigWritten = z.infer<typeof conversationConfigWritten>;
1313
-
1314
- /** The config-delete reply: `removed` is always `true` on success (an unknown key is a 404). */
1315
- export const conversationConfigDeleted = z.object({
1316
- removed: z.boolean(),
1317
- target_kind: conversationTargetKind,
1318
- target_name: z.string(),
1319
- });
1320
- export type ConversationConfigDeleted = z.infer<typeof conversationConfigDeleted>;
1321
-
1322
- /**
1323
- * The admin-only failed-delivery listing (`GET /api/conversations/messages/failed`):
1324
- * every answer record whose delivery ended `failed`, across every route and caller.
1325
- * The records are the ADMIN projection (delivery bookkeeping and `error` included),
1326
- * so the row shape is {@link conversationMessage} exactly. It is NOT a paged window —
1327
- * the door returns `{items, total}` and nothing more.
1328
- */
1329
- export const conversationFailedMessages = z.object({
1330
- items: z.array(conversationMessage),
1331
- total: z.number(),
1332
- });
1333
- export type ConversationFailedMessages = z.infer<typeof conversationFailedMessages>;
1334
-
1335
- /**
1336
- * The thread-delete reply (`DELETE /api/conversations/{route_name}/thread?thread_id=`).
1337
- * `removed` is a COUNT of the answer records this call deleted — `0` when a prior
1338
- * run already cleared them, when they had aged out under retention, or when the id
1339
- * was never stored (forgetting is absolute, never a 404 on a valid id).
1340
- */
1341
- export const conversationThreadDeleted = z.object({
1342
- removed: z.number(),
1343
- route_name: z.string(),
1344
- thread_id: z.string(),
1345
- });
1346
- export type ConversationThreadDeleted = z.infer<typeof conversationThreadDeleted>;
1347
-
1348
- /**
1349
- * The person-erase reply (`DELETE /api/conversations/persons/{person_id}`).
1350
- * `removed` counts the answer records deleted across the person's routes; `erased`
1351
- * says whether THIS call removed the person row (`false` on a retry or an
1352
- * already-gone person — never a 404).
1353
- */
1354
- export const conversationPersonDeleted = z.object({
1355
- person_id: z.string(),
1356
- removed: z.number(),
1357
- erased: z.boolean(),
1358
- });
1359
- export type ConversationPersonDeleted = z.infer<typeof conversationPersonDeleted>;
1360
-
1361
- // -- channel-web entry gate --------------------------------------------------
1362
- // The web channel's entry gate: a web route can be gated so its chat page is
1363
- // served only to a navigation carrying a live entry code. These four doors are
1364
- // keyed by the route's `our_identity`. A raw code exists only in flight (the
1365
- // mint reply); a listed code carries its hash id, never a raw value (none is
1366
- // stored).
1367
-
1368
- /** One minted entry code as the gate read door lists it — its hash id, its
1369
- * optional label, and when it was created / dies. Never a raw code. */
1370
- export const webEntryCode = z.object({
1371
- code_id: z.string(),
1372
- label: z.string().nullable(),
1373
- created_at: z.string(),
1374
- expires_at: z.string().nullable(),
1375
- });
1376
- export type WebEntryCode = z.infer<typeof webEntryCode>;
1377
-
1378
- /** `GET /api/channels/web/gates/{identity}` — the gate flag and its live codes. */
1379
- export const webEntryGate = z.object({
1380
- enabled: z.boolean(),
1381
- codes: z.array(webEntryCode),
1382
- });
1383
- export type WebEntryGate = z.infer<typeof webEntryGate>;
1384
-
1385
- /** `PUT /api/channels/web/gates/{identity}` — the gate flag after the write. */
1386
- export const webEntryGateState = z.object({ enabled: z.boolean() });
1387
- export type WebEntryGateState = z.infer<typeof webEntryGateState>;
1388
-
1389
- /**
1390
- * `POST /api/channels/web/gates/{identity}/codes` — a freshly minted code. The
1391
- * raw `code` rides this reply ONCE (nothing else stores or lists it — a listed
1392
- * code's raw value is unrecoverable by design); `code_id` is its hash id and
1393
- * `expires_at` an ISO-8601 instant, or `null` for a code that never expires.
1394
- */
1395
- export const webEntryCodeMinted = z.object({
1396
- code: z.string(),
1397
- code_id: z.string(),
1398
- expires_at: z.string().nullable(),
1399
- });
1400
- export type WebEntryCodeMinted = z.infer<typeof webEntryCodeMinted>;
1401
-
1402
- /** `DELETE /api/channels/web/gates/{identity}/codes/{code_id}` — the revoked code. */
1403
- export const webEntryCodeRevoked = z.object({ status: z.literal('revoked') });
1404
- export type WebEntryCodeRevoked = z.infer<typeof webEntryCodeRevoked>;
1405
-
1406
- // -- notifications -----------------------------------------------------------
1407
- // The internal notifications sink: the messages the `notify_user` operation records
1408
- // with no channel, so they land only in the Studio inbox. Each record carries the
1409
- // server-minted id + timestamp; `recipient` is null when the message names none.
1410
- // The sink also stores the richer-send forms a notify carried — `media`
1411
- // (images/links shown WITH the message), `template` (a pre-approved out-of-window
1412
- // send), `options` (tappable option labels) — plus the `audience` identity the
1413
- // record is addressed to; the inbox renders them.
1414
-
1415
- export const notification = z.object({
1416
- id: z.string(),
1417
- message: z.string(),
1418
- // A channel DELIVERY address the message went to; null when the send named none.
1419
- recipient: z.string().nullable(),
1420
- // The IDENTITY (a user_id) whose in-app inbox the record is addressed to, distinct
1421
- // from `recipient`. `.nullish()` accepts both an older record that predates the
1422
- // field (key omitted) and a broadcast send (explicit null) — both parse to "none".
1423
- audience: z.string().nullish(),
1424
- // Display-only media stored WITH the message (images and/or links). Deliberately
1425
- // LOOSE (`z.array(z.unknown())`), like the interactions frame: each item is
1426
- // `safeParse`d per item by the renderer against `interactionMediaItem`, so one
1427
- // malformed/hostile item is a loud per-item notice, never a whole-feed parse
1428
- // failure that would silently vanish every other notification. `.nullish()` covers
1429
- // an older record (key omitted) and a plain send (explicit null).
1430
- media: z.array(z.unknown()).nullish(),
1431
- // A pre-approved template the notify carried, parsed strictly: a drifting one is a
1432
- // loud feed-level `ApiSchemaError`, consistent with the record contract.
1433
- template: channelTemplate.nullish(),
1434
- // Tappable option labels the notify offered; display-only in the operator inbox
1435
- // (there is no conversation for the sink to enter). Strictly a string list.
1436
- options: z.array(z.string()).nullish(),
1437
- created_at: z.string(),
1438
- });
1439
- export type Notification = z.infer<typeof notification>;
1440
-
1441
- export const notifications = z.object({
1442
- notifications: z.array(notification),
1443
- });
1444
- export type Notifications = z.infer<typeof notifications>;
1445
-
1446
- // -- hooks -------------------------------------------------------------------
1447
- // The hand-written hook response DTOs: the fire-door authority enum, the list rows and
1448
- // their wrapper, and the per-topic verifier/trigger metadata. The served hook DOCUMENT
1449
- // schemas (`hookSubject`/`hookParams`/`hookRegister`) are generated from the contract
1450
- // bundle and re-exported at the top of this file, not defined here.
1451
-
1452
- /** A fire door — who may trigger a path, not what the fire may do. A string enum. */
1453
- export const triggerAuth = z.enum([
1454
- 'public',
1455
- 'verifier',
1456
- 'token',
1457
- 'token+api_key',
1458
- 'out-of-service',
1459
- ]);
1460
- export type TriggerAuth = z.infer<typeof triggerAuth>;
1461
-
1462
- export const hookList = z.object({
1463
- items: z.array(hookParams),
1464
- total: z.number(),
1465
- // Per-topic verifier bindings: the topic name maps to the verifier bound to it
1466
- // and that verifier's config (which carries a `secret_env` name, never a secret
1467
- // value). Tolerant of an older/empty response, which omits the key entirely.
1468
- topic_verifiers: z
1469
- .record(
1470
- z.string(),
1471
- z.object({ verifier: z.string(), config: z.record(z.string(), z.unknown()) }),
1472
- )
1473
- .default({}),
1474
- // Per-topic fire door (topic → door). Tolerant of an older response omitting it.
1475
- trigger_auth: z.record(z.string(), triggerAuth).default({}),
1476
- });
1477
- export type HookList = z.infer<typeof hookList>;
1478
-
1479
- export const hookRegistered = z.object({
1480
- registered: z.boolean(),
1481
- name: z.string(),
1482
- });
1483
-
1484
- export const hookRemoved = z.object({
1485
- removed: z.boolean(),
1486
- name: z.string(),
1487
- });
1488
-
1489
- // The registered webhook-verifier catalog (`GET /api/hooks/verifiers`) — the
1490
- // sorted verifier NAMES only, never any verifier's config. An empty registry is a
1491
- // valid empty array. This is the ONLY source the topic-verifier bind picker reads.
1492
- export const hookVerifiers = z.array(z.string());
1493
-
1494
- /** `PUT /api/hooks/topics/{topic}/verifier` — the bound topic + verifier name. */
1495
- export const topicVerifierSet = z.object({ topic: z.string(), verifier: z.string() });
1496
-
1497
- /** `DELETE /api/hooks/topics/{topic}/verifier` — the unbound topic. */
1498
- export const topicVerifierRemoved = z.object({ removed: z.boolean(), topic: z.string() });
1499
-
1500
- // -- trigger links -----------------------------------------------------------
1501
- // A trigger link is a minted, token-bearing PUBLIC URL (`GET|POST /trigger/{token}`)
1502
- // that resolves to a hook TOPIC and fires it. Studio mints, lists, and revokes them
1503
- // on the hooks page; the QR encodes the composed absolute URL.
1504
-
1505
- /**
1506
- * `POST /api/hooks/trigger-links` — a freshly minted trigger link. `trigger_path`
1507
- * is a relative path (`/trigger/<token>`, never an absolute URL — Studio composes
1508
- * the API origin); `token` is the raw token returned exactly ONCE, in this response
1509
- * (nothing else stores or lists it — a listed link's QR is unrecoverable by
1510
- * design). `expires_at` is an ISO-8601 instant, or `null` for a permanent link.
1511
- */
1512
- export const triggerLinkCreated = z.object({
1513
- name: z.string(),
1514
- trigger_path: z.string(),
1515
- token: z.string(),
1516
- topic: z.string(),
1517
- expires_at: z.string().nullable(),
1518
- });
1519
- export type TriggerLinkCreated = z.infer<typeof triggerLinkCreated>;
1520
-
1521
- /**
1522
- * One listed trigger-link record (`GET /api/hooks/trigger-links` item). `tool_kwargs`
1523
- * is the per-link params merged last into every fire (`null` or `{}` = none);
1524
- * `created_by` is the ambient creator identity (`null` on an identity-less,
1525
- * gate-off deployment); `token_hash_prefix` is a short hash prefix for log
1526
- * correlation — never a raw token (none is stored).
1527
- */
1528
- export const triggerLinkRecord = z.object({
1529
- name: z.string(),
1530
- topic: z.string(),
1531
- execution_key: z.string().min(1),
1532
- trigger_auth: triggerAuth,
1533
- tool_kwargs: z.record(z.string(), z.unknown()).nullable(),
1534
- created_by: z.string().nullable(),
1535
- created_at: z.string(),
1536
- expires_at: z.string().nullable(),
1537
- token_hash_prefix: z.string(),
1538
- });
1539
- export type TriggerLinkRecord = z.infer<typeof triggerLinkRecord>;
1540
-
1541
- /** `GET /api/hooks/trigger-links` — every live trigger-link record. */
1542
- export const triggerLinkList = z.object({
1543
- items: z.array(triggerLinkRecord),
1544
- total: z.number(),
1545
- });
1546
- export type TriggerLinkList = z.infer<typeof triggerLinkList>;
1547
-
1548
- /** `DELETE /api/hooks/trigger-links/{name}` — the revoked link. */
1549
- export const triggerLinkDeleted = z.object({ removed: z.boolean(), name: z.string() });
1550
- export type TriggerLinkDeleted = z.infer<typeof triggerLinkDeleted>;
1551
-
1552
- // -- settings schema ---------------------------------------------------------
1553
- // The settings form is auto-rendered from a declarative group/field description
1554
- // the skeleton emits from its typed settings models.
1555
-
1556
- export const settingsSchema = z.object({
1557
- groups: z.array(
1558
- z.object({
1559
- name: z.string(),
1560
- module: z.string(),
1561
- qualname: z.string(),
1562
- fields: z.array(
1563
- z.object({
1564
- name: z.string(),
1565
- env_var: z.string(),
1566
- type: z.string(),
1567
- default: jsonValue.nullable(),
1568
- required: z.boolean(),
1569
- secret: z.boolean(),
1570
- description: z.string().nullable(),
1571
- nested_group: z.string().nullable(),
1572
- default_namespace_var: z.string().nullable(),
1573
- value: jsonValue.nullable(),
1574
- }),
1575
- ),
1576
- }),
1577
- ),
1578
- });
1579
- export type SettingsSchema = z.infer<typeof settingsSchema>;
1580
-
1581
- // -- auth: scopes + api keys -------------------------------------------------
1582
-
1583
- /** `{ url: scope_id }` — every URL currently mapped to a scope. */
1584
- export const authScopes = z.record(z.string(), z.string());
1585
-
1586
- export const addUrlToScopeResult = z.object({ scope_id: z.string(), url: z.string() });
1587
- export const removeUrlFromScopeResult = z.object({ url: z.string() });
1588
- export const removeScopeResult = z.object({ scope_id: z.string(), deleted_keys: z.number() });
1589
-
1590
- /**
1591
- * A route's authorization ACTION class, joined onto the catalog from the route
1592
- * registry. `read`/`write` routes are GRANTABLE — a role's per-tag level opens
1593
- * them; `fenced`/`secret` routes are ADMIN-ONLY and never grantable (no per-tag
1594
- * level reaches them). `null` marks a path the registry carries no metadata for
1595
- * (an unregistered/ungated path — no feature tags, no action).
1596
- */
1597
- export const routeAction = z.enum(['read', 'write', 'fenced', 'secret']);
1598
- export type RouteAction = z.infer<typeof routeAction>;
1599
-
1600
- /**
1601
- * One row of the route catalog (`GET /api/auth/routes`) — a plain HTTP route with
1602
- * its current mapping, JOINED with its route-registry metadata. `mapped` is the
1603
- * scope id, the public marker, or `null` when the path is unmapped (the mapper's
1604
- * "unassigned" bucket source). `methods` is the route's method set with `HEAD`
1605
- * removed. `tags` are the route's feature-group labels, `summary` its one-line
1606
- * description, and `action` its authorization class — the join fields the Roles
1607
- * grant editor reads to derive grantable feature groups and mark the admin-only
1608
- * (fenced/secret) routes it must never offer a grant for. An unregistered path
1609
- * carries `tags: []`, `summary: ''`, `action: null`.
1610
- */
1611
- export const authRoute = z.object({
1612
- path: z.string(),
1613
- methods: z.array(z.string()),
1614
- mapped: z.string().nullable(),
1615
- tags: z.array(z.string()),
1616
- summary: z.string(),
1617
- action: routeAction.nullable(),
1618
- });
1619
- export type AuthRoute = z.infer<typeof authRoute>;
1620
- export const authRoutes = z.array(authRoute);
1621
-
1622
- // -- auth: roles (editable per-tag RBAC) -------------------------------------
1623
-
1624
- /** A role's per-tag ACCESS LEVEL: `none` (no access), `read`, or `write`. */
1625
- export const grantLevel = z.enum(['none', 'read', 'write']);
1626
- export type GrantLevel = z.infer<typeof grantLevel>;
1627
-
1628
- /** A role's editable grant map: feature-group TAG name → the role's access level. */
1629
- export const roleGrants = z.record(z.string(), grantLevel);
1630
- export type RoleGrants = z.infer<typeof roleGrants>;
1631
-
1632
- export const roleList = z.array(roleBody);
1633
-
1634
- /** `DELETE /api/auth/roles/{name}` — the deleted role's name. */
1635
- export const roleDeleted = z.object({ name: z.string(), deleted: z.boolean() });
1636
-
1637
- /**
1638
- * One immutable role-version row (`GET /api/auth/roles/{name}/versions` →
1639
- * `versions`). `body` is the role body at that version; `is_current` flags the
1640
- * active pointer. Mirrors the generic versioned-store row shape.
1641
- */
1642
- export const roleVersion = z.object({
1643
- version: z.number(),
1644
- body: roleBody,
1645
- tags: z.array(z.string()),
1646
- created_at: z.string(),
1647
- is_current: z.boolean(),
1648
- });
1649
- export type RoleVersion = z.infer<typeof roleVersion>;
1650
-
1651
- /**
1652
- * One who/action/before→after audit row (`GET /api/auth/roles/{name}/versions` →
1653
- * `audit`). Its `body` carries the mutation kind, the actor, and the before/after
1654
- * role bodies (both `null` for a create's `before` / a delete's `after`).
1655
- */
1656
- export const roleAuditEvent = z.object({
1657
- version: z.number(),
1658
- body: z.object({
1659
- action: z.string(),
1660
- actor: z.string().nullable(),
1661
- before: jsonValue.nullable(),
1662
- after: jsonValue.nullable(),
1663
- }),
1664
- tags: z.array(z.string()),
1665
- created_at: z.string(),
1666
- is_current: z.boolean(),
1667
- });
1668
- export type RoleAuditEvent = z.infer<typeof roleAuditEvent>;
1669
-
1670
- /** `GET /api/auth/roles/{name}/versions` — the append-only history + audit trail. */
1671
- export const roleVersions = z.object({
1672
- versions: z.array(roleVersion),
1673
- audit: z.array(roleAuditEvent),
1674
- });
1675
- export type RoleVersions = z.infer<typeof roleVersions>;
1676
-
1677
- /** `GET /api/auth/public-routes` — the sorted urls pinned to the public marker. */
1678
- export const publicRoutes = z.array(z.string());
1679
-
1680
- /** `POST /api/auth/public-routes` — the pinned url. */
1681
- export const pinPublicResult = z.object({ url: z.string() });
1682
- /** `DELETE /api/auth/public-routes` — the unpinned url (404 when not pinned). */
1683
- export const unpinPublicResult = z.object({ url: z.string() });
1684
-
1685
- /** The per-key payloads (`GET /api/auth/tokens-payload`). The stable per-mint
1686
- * `key_fingerprint` is nested under `policy_data`; the picker reads it there. */
1687
- export const tokensPayload = z.array(
1688
- z.object({
1689
- // The value an `execution_key` binding names.
1690
- user_id: z.string().min(1),
1691
- description: z.string(),
1692
- scopes: z.array(z.string()),
1693
- policy_data: jsonValue,
1694
- condition: templatedText.nullable().optional(),
1695
- }),
1696
- );
1697
- export type TokensPayload = z.infer<typeof tokensPayload>;
1698
-
1699
- /** Mint reply `{ api_key, key_fingerprint }` narrowed to the raw `sk-…` key,
1700
- * returned once at creation and never again. */
1701
- export const createdApiKey = z
1702
- .object({ api_key: z.string(), key_fingerprint: z.string() })
1703
- .transform((minted) => minted.api_key);
1704
- export const editApiKeyResult = z.object({ user_id: z.string(), updated: z.boolean() });
1705
- export const revokeApiKeyResult = z.object({ user_id: z.string(), revoked: z.boolean() });
1706
-
1707
- // -- login methods (the public sign-in aggregator) ---------------------------
1708
-
1709
- /**
1710
- * One field of a `form` login method. `secret` fields render as password
1711
- * inputs; `autocomplete` passes through to the input (e.g. "email",
1712
- * "current-password", "new-password").
1713
- */
1714
- export const loginFormField = z.object({
1715
- name: z.string().min(1),
1716
- label: z.string().min(1),
1717
- secret: z.boolean(),
1718
- autocomplete: z.string().optional(),
1719
- });
1720
-
1721
- /**
1722
- * The two — and only two — login-method shapes. Discriminated on `shape`; an
1723
- * unknown shape fails validation loudly (contract drift, not a soft skip).
1724
- * Optional fields are `.optional()` (absent), never `.nullable()`: the server
1725
- * serializes with `exclude_none`, so an absent field is OMITTED, never `null`.
1726
- */
1727
- export const loginMethod = z.discriminatedUnion('shape', [
1728
- z.object({
1729
- shape: z.literal('form'),
1730
- id: z.string().min(1),
1731
- title: z.string().min(1),
1732
- purpose: z.enum(['login', 'bootstrap', 'invite']).default('login'),
1733
- fields: z.array(loginFormField).min(1),
1734
- submit_path: z.string().min(1),
1735
- }),
1736
- z.object({
1737
- shape: z.literal('button'),
1738
- id: z.string().min(1),
1739
- label: z.string().min(1),
1740
- icon: z.string().optional(),
1741
- href: z.string().min(1),
1742
- }),
1743
- ]);
1744
-
1745
- /**
1746
- * `GET /api/login/methods` (PUBLIC). `bootstrap: true` ⇒ the deployment has zero
1747
- * accounts and `methods` contains the owner-creation form. Non-strict — the
1748
- * skeleton may grow additive fields.
1749
- */
1750
- export const loginMethods = z.object({
1751
- methods: z.array(loginMethod),
1752
- bootstrap: z.boolean(),
1753
- });
1754
-
1755
- /**
1756
- * A successful login/exchange: the minted session token (opaque, `tai-sess-`
1757
- * prefixed — the client treats it as an opaque string) + its user.
1758
- */
1759
- export const loginResult = z.object({
1760
- token: z.string().min(1),
1761
- user_id: z.string().min(1),
1762
- });
1763
-
1764
- export type LoginFormField = z.infer<typeof loginFormField>;
1765
- export type LoginMethod = z.infer<typeof loginMethod>;
1766
- export type LoginMethods = z.infer<typeof loginMethods>;
1767
- export type LoginResult = z.infer<typeof loginResult>;
1768
-
1769
- // -- auth: the caller's capability projection (GET /api/auth/me) --------------
1770
-
1771
- /** A concrete route the caller can reach, with the methods that pass its jq fence. */
1772
- export const routeEntry = z.object({
1773
- path: z.string(),
1774
- methods: z.array(z.string()),
1775
- });
1776
- export type RouteEntry = z.infer<typeof routeEntry>;
1777
-
1778
- /**
1779
- * A dynamic route pattern the caller can reach — a mount/pattern surface not
1780
- * enumerable into concrete paths, projected only when its scope AND jq admit it.
1781
- */
1782
- export const patternEntry = z.object({
1783
- pattern: z.string(),
1784
- scope_id: z.string(),
1785
- });
1786
- export type PatternEntry = z.infer<typeof patternEntry>;
1787
-
1788
- /**
1789
- * A sub-MCP mount the caller can reach, with the tools it exposes and its
1790
- * transport. Shares the {@link subMcpMount} shape with the registry list, adding
1791
- * the `slug` the projection carries inline (the list keys mounts by slug instead).
1792
- */
1793
- export const subMcpEntry = subMcpMount.extend({ slug: z.string() });
1794
- export type SubMcpEntry = z.infer<typeof subMcpEntry>;
1795
-
1796
- /**
1797
- * `GET /api/auth/me` — the caller's derived capability projection: the concrete
1798
- * (path, method) surface, dynamic patterns, sub-MCP mounts, tools, and agents it
1799
- * can reach RIGHT NOW (derived server-side, never stored). `admin` is the
1800
- * condition-free ownerless `"*"` discriminator (a TOTAL projection); a scoped
1801
- * session carries `admin: false` and a jq-exact `routes` list. `owner_user_id` is
1802
- * the key's owner claim, `null` for an ownerless key. The invariant is
1803
- * projection ⊆ gate: every projected surface is one the server would admit, so
1804
- * the UI can filter on it without ever advertising a door the gate denies.
1805
- */
1806
- export const meProjection = z.object({
1807
- user_id: z.string(),
1808
- owner_user_id: z.string().nullable(),
1809
- admin: z.boolean(),
1810
- scopes: z.array(z.string()),
1811
- routes: z.array(routeEntry),
1812
- route_patterns: z.array(patternEntry),
1813
- sub_mcp: z.array(subMcpEntry),
1814
- tools: z.array(z.string()),
1815
- agents: z.array(z.string()),
1816
- mintable: z.boolean(),
1817
- });
1818
- export type MeProjection = z.infer<typeof meProjection>;
1819
-
1820
- /**
1821
- * `POST /api/auth/claim-links` — a one-time claim link minted in the key-create
1822
- * flow. `claim_path` is a fragment-carrier path (`/login#claim=<token>`, never an
1823
- * absolute URL — the Studio composes the origin); `token` is the raw claim token
1824
- * returned exactly ONCE; `expires_at` is an ISO-8601 instant.
1825
- */
1826
- export const claimLinkCreated = z.object({
1827
- claim_path: z.string(),
1828
- token: z.string(),
1829
- expires_at: z.string(),
1830
- });
1831
- export type ClaimLinkCreated = z.infer<typeof claimLinkCreated>;
1832
-
1833
- /**
1834
- * `POST /api/auth/logout` — the single authed logout dispatcher's result.
1835
- * `revoked` is `true` when a live accounts session was revoked; a plain `sk-` key
1836
- * has nothing to revoke and answers `404` (a loud absent-session signal), never a
1837
- * `revoked: false` body. Non-strict, so a SUCCESSFUL logout never throws
1838
- * `ApiSchemaError` on an additive field.
1839
- */
1840
- export const logoutResult = z.object({ revoked: z.boolean() });
1841
- export type LogoutResult = z.infer<typeof logoutResult>;
1842
-
1843
- /**
1844
- * `GET /api/auth/capabilities` — whether this deployment can MINT API keys
1845
- * locally. `mintable: false` means every configured identity provider is
1846
- * validator-only (keys are issued at an external issuer), so the key-creation
1847
- * UI disables up front instead of surfacing a raw mint failure. Non-strict.
1848
- */
1849
- export const authCapabilities = z.object({
1850
- mintable: z.boolean(),
1851
- providers: z.array(z.object({ name: z.string(), mintable: z.boolean() })),
1852
- });
1853
- export type AuthCapabilities = z.infer<typeof authCapabilities>;
1854
-
1855
- // -- access-control policy (authoring + versioning) --------------------------
1856
-
1857
- /**
1858
- * `POST /api/auth/validate-condition` — the fail-closed jq guard. `ok` is the
1859
- * compile/render result; `result` is the boolean of a sample evaluation, or
1860
- * `null` when no `sample_context` was supplied (compile-only). A broken condition
1861
- * is a `400 {error}` surfaced by the transport, NEVER a false `ok` — so a
1862
- * syntactically-broken condition can be caught before it locks a key out. A drift
1863
- * throws `ApiSchemaError`.
1864
- */
1865
- export const validateConditionResult = z.object({
1866
- ok: z.boolean(),
1867
- result: z.boolean().nullable(),
1868
- });
1869
- export type ValidateConditionResult = z.infer<typeof validateConditionResult>;
1870
-
1871
- /**
1872
- * One immutable policy version row
1873
- * (`GET /api/auth/api-keys/{user_id}/policy/versions`). `is_current` flags the
1874
- * active pointer; `tags` is the generic per-version label list (kind-agnostic),
1875
- * mirroring the preset version shape.
1876
- */
1877
- export const policyVersion = z.object({
1878
- version: z.number(),
1879
- body: policyBody,
1880
- tags: z.array(z.string()),
1881
- created_at: z.string(),
1882
- is_current: z.boolean(),
1883
- });
1884
- export type PolicyVersion = z.infer<typeof policyVersion>;
1885
-
1886
- export const policyVersionList = z.array(policyVersion);
1887
-
1888
- /** `POST /api/auth/api-keys/{user_id}/policy/rollback` — the re-pointed version. */
1889
- export const policyRollback = z.object({ user_id: z.string(), active_version: z.number() });
1890
-
1891
- // -- backup ------------------------------------------------------------------
1892
-
1893
- export const backupSections = z.array(z.object({ name: z.string(), secret: z.boolean() }));
1894
-
1895
- /** The exported backup document — validated when the UI reads a picked file. */
1896
- export const backupDocument = z.object({
1897
- version: z.literal(1),
1898
- created_at: z.string(),
1899
- sections: z.record(z.string(), z.unknown()),
1900
- errors: z.record(z.string(), z.string()).optional(),
1901
- });
1902
- export type BackupDocument = z.infer<typeof backupDocument>;
1903
-
1904
- export const backupImportReport = z.object({
1905
- ok: z.boolean(),
1906
- sections: z.record(
1907
- z.string(),
1908
- z.object({
1909
- created: z.number(),
1910
- updated: z.number(),
1911
- skipped: z.number(),
1912
- errors: z.array(z.string()),
1913
- new_api_keys: z
1914
- .array(
1915
- z.object({
1916
- user_id: z.string(),
1917
- description: z.string(),
1918
- api_key: z.string(),
1919
- }),
1920
- )
1921
- .optional(),
1922
- // The `manifest` and `env` sections apply through the config pipeline, so their
1923
- // report carries the mode-wrapped fleet `fanout` of the restore's reload; other
1924
- // sections omit it. Parsed EXPLICITLY so the shared fleet-report handler can
1925
- // surface a failed restore propagation — otherwise zod would strip it silently.
1926
- fanout: fleetReportFanout.optional(),
1927
- }),
1928
- ),
1929
- });
1930
- export type BackupImportReport = z.infer<typeof backupImportReport>;
1931
-
1932
- // -- schedules ---------------------------------------------------------------
1933
- // The skeleton returns opaque backend-tool results; the table reads a few known
1934
- // fields and passes the rest through untouched.
1935
-
1936
- export const scheduleItem = z
1937
- .object({
1938
- name: z.string(),
1939
- enabled: z.boolean().optional(),
1940
- schedule: jsonValue.optional(),
1941
- target: jsonValue.optional(),
1942
- args: z.array(jsonValue).optional(),
1943
- kwargs: z.record(z.string(), jsonValue).optional(),
1944
- })
1945
- .loose();
1946
- export const scheduleList = z.array(scheduleItem);
1947
- export type ScheduleItem = z.infer<typeof scheduleItem>;
1948
-
1949
- export const serverDateTime = z
1950
- .object({
1951
- utc: jsonValue,
1952
- local: jsonValue.optional(),
1953
- system: jsonValue.optional(),
1954
- })
1955
- .loose();
1956
-
1957
- // -- observability -----------------------------------------------------------
1958
-
1959
- export const dashboardMetrics = z.object({
1960
- summary: z.object({
1961
- totalRuns: z.number(),
1962
- totalCost: z.number(),
1963
- totalTokens: z.number(),
1964
- averageLatencyMs: z.number(),
1965
- avgCostPerRun: z.number(),
1966
- avgTokensPerRun: z.number(),
1967
- timeToFirstTokenMs: z.number().nullable(),
1968
- }),
1969
- timeSeries: z.array(
1970
- z.object({
1971
- bucket: z.string().nullable(),
1972
- runs: z.number(),
1973
- cost: z.number(),
1974
- avgLatencyMs: z.number(),
1975
- totalTokens: z.number(),
1976
- }),
1977
- ),
1978
- byModel: z.array(
1979
- z.object({
1980
- model: z.string(),
1981
- calls: z.number(),
1982
- cost: z.number(),
1983
- totalTokens: z.number(),
1984
- avgLatencyMs: z.number(),
1985
- }),
1986
- ),
1987
- granularity: z.enum(['hour', 'day', 'week']),
1988
- });
1989
- export type DashboardMetrics = z.infer<typeof dashboardMetrics>;
1990
-
1991
- export const run = z.object({
1992
- id: z.string(),
1993
- traceId: z.string(),
1994
- createdAt: z.string().nullable(),
1995
- tags: z.array(z.string()),
1996
- status: z.enum(['error', 'success']),
1997
- cost: z.number().nullable(),
1998
- latencyMs: z.number().nullable(),
1999
- totalTokens: z.number().nullable(),
2000
- inputPreview: jsonValue,
2001
- outputPreview: jsonValue,
2002
- });
2003
- export type Run = z.infer<typeof run>;
2004
-
2005
- export const runsPage = z.object({
2006
- items: z.array(run),
2007
- page: z.number(),
2008
- nextPage: z.number().nullable(),
2009
- });
2010
-
2011
- export const runSpan = z.object({
2012
- id: z.string(),
2013
- parentId: z.string().nullable(),
2014
- traceId: z.string().nullable(),
2015
- name: z.string().nullable(),
2016
- type: z.string().nullable(),
2017
- level: z.string().nullable(),
2018
- statusMessage: z.string().nullable(),
2019
- start: z.string().nullable(),
2020
- end: z.string().nullable(),
2021
- model: z.string().nullable(),
2022
- usage: jsonValue.nullable(),
2023
- metadata: jsonValue.nullable(),
2024
- input: jsonValue,
2025
- output: jsonValue,
2026
- nodeId: z.string().nullable(),
2027
- });
2028
- export type RunSpan = z.infer<typeof runSpan>;
2029
-
2030
- export const runTrace = z.object({
2031
- traceId: z.string(),
2032
- timestamp: z.string().nullable(),
2033
- tags: z.array(z.string()),
2034
- totalCost: z.number().nullable(),
2035
- input: jsonValue,
2036
- output: jsonValue,
2037
- metadata: jsonValue.nullable(),
2038
- spans: z.array(runSpan),
2039
- });
2040
- export type RunTrace = z.infer<typeof runTrace>;
2041
-
2042
- // -- marketplace -------------------------------------------------------------
2043
-
2044
- /**
2045
- * The CLOSED HTTP-method enum fixed by the contract's RouteMethod, the one source
2046
- * for every route-method field (declaration and install/preview responses alike):
2047
- * a wire method the contract does not define fails loudly as drift rather than
2048
- * passing through silently.
2049
- */
2050
- export const routeMethod = z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
2051
- export type RouteMethod = z.infer<typeof routeMethod>;
2052
-
2053
- /**
2054
- * How the plugin is delivered, derived server-side from whether its spec names a
2055
- * package: `package` is pip-installed, `descriptor` (marketplace `source` `spec`)
2056
- * installs nothing but the manifest entry. A CLOSED enum, so a wire value the
2057
- * server does not derive fails loudly as drift.
2058
- */
2059
- export const marketplaceDelivery = z.enum(['package', 'descriptor']);
2060
- export type MarketplaceDelivery = z.infer<typeof marketplaceDelivery>;
2061
-
2062
- /**
2063
- * The install-provenance vocabulary a listing/version resolves from: a pip index
2064
- * (`pypi`), a git tag (`github`), or a descriptor yml (`spec`). CLOSED, so a wire
2065
- * value outside it fails as drift.
2066
- */
2067
- export const marketplaceSource = z.enum(['pypi', 'github', 'spec']);
2068
- export type MarketplaceSource = z.infer<typeof marketplaceSource>;
2069
-
2070
- /**
2071
- * One env var an item requires before install: its `name` and whether its value is
2072
- * a `secret` (an OAuth client secret is; a client id or a plain marker var is not).
2073
- * The install dialog seeds each toggle from `secret` and collects the missing ones.
2074
- */
2075
- export const marketplaceRequiredEnvVar = z.object({ name: z.string(), secret: z.boolean() });
2076
- export type MarketplaceRequiredEnvVar = z.infer<typeof marketplaceRequiredEnvVar>;
2077
-
2078
- /**
2079
- * One HTTP route an item declares in its `tai-plugin.yml`: a `path` relative to
2080
- * the item's mount base, the `methods` it answers, and whether it is served
2081
- * `public` (without authentication). The absolute path an operator sees is
2082
- * resolved from the base at install time.
2083
- */
2084
- export const marketplaceRouteDecl = z.object({
2085
- path: z.string(),
2086
- methods: z.array(routeMethod),
2087
- public: z.boolean(),
2088
- });
2089
- export type MarketplaceRouteDecl = z.infer<typeof marketplaceRouteDecl>;
2090
-
2091
- /**
2092
- * A route-carrying item's declared mount: the default `base` prefix the routes
2093
- * hang off (an operator may remap it at install) and the `paths` it registers.
2094
- */
2095
- export const marketplaceRoutesDecl = z.object({
2096
- base: z.string(),
2097
- paths: z.array(marketplaceRouteDecl),
2098
- });
2099
- export type MarketplaceRoutesDecl = z.infer<typeof marketplaceRoutesDecl>;
2100
-
2101
- /**
2102
- * One contained item of a marketplace plugin's published version: its kind
2103
- * (tool / agent / extension / …), name, description, free-form tags, and the
2104
- * logical group it belongs to (`null` when ungrouped). `routes` is present only
2105
- * on a route-carrying item (a router or channel); absent/null on every other.
2106
- * `required_env` is the env vars the item needs before install — but the REGISTRY's
2107
- * plugin-detail body does NOT carry it per item: required-env is server-computed
2108
- * (`required_env_for_spec`) and rides the install PREVIEW, never the listing detail,
2109
- * so the registry OMITS it here and it parses as `undefined`. Optional (not
2110
- * defaulted) so the type mirrors the wire; the authority the install dialog collects
2111
- * from is the preview's `required_env`/`missing_env`, never this field.
2112
- */
2113
- export const marketplaceItem = z.object({
2114
- kind: z.string(),
2115
- name: z.string(),
2116
- description: z.string(),
2117
- tags: z.array(z.string()),
2118
- group: z.string().nullable(),
2119
- routes: marketplaceRoutesDecl.nullish(),
2120
- required_env: z.array(marketplaceRequiredEnvVar).optional(),
2121
- });
2122
- export type MarketplaceItem = z.infer<typeof marketplaceItem>;
2123
-
2124
- /**
2125
- * One kind summary of a search row: an item kind, how many UNGROUPED items of
2126
- * that kind the listing's latest published version carries, and their names.
2127
- * The registry orders the array count DESC then kind ASC, and each entry's
2128
- * `names` ASC; the UI renders in that order.
2129
- */
2130
- export const marketplaceSearchKind = z.object({
2131
- kind: z.string(),
2132
- count: z.number(),
2133
- names: z.array(z.string()),
2134
- });
2135
- export type MarketplaceSearchKind = z.infer<typeof marketplaceSearchKind>;
2136
-
2137
- /**
2138
- * One logical group of a search row: the group name and how many items the
2139
- * listing's latest published version places in it. The registry orders the
2140
- * array count DESC then name ASC; the UI renders in that order.
2141
- */
2142
- export const marketplaceSearchGroup = z.object({
2143
- name: z.string(),
2144
- count: z.number(),
2145
- });
2146
- export type MarketplaceSearchGroup = z.infer<typeof marketplaceSearchGroup>;
2147
-
2148
- /**
2149
- * One listing-level search row (`GET /api/marketplace/search`). `ref` is the
2150
- * "namespace/name" install target, so every row carries enough to link to its
2151
- * plugin. `latest_version` is nullable defensively; the search relation only
2152
- * emits listings with a published version. `kinds` summarizes that latest
2153
- * version's UNGROUPED items; `groups` summarizes its logical groups (`[]` when
2154
- * none). Both keys are always present.
2155
- */
2156
- export const marketplaceSearchRow = z.object({
2157
- ref: z.string(),
2158
- namespace: z.string(),
2159
- name: z.string(),
2160
- display_name: z.string().nullable(),
2161
- icon_url: z.string().nullable(),
2162
- // Null for a descriptor listing (`source` `spec`): it names no package.
2163
- package: z.string().nullable(),
2164
- description: z.string(),
2165
- categories: z.array(z.string()),
2166
- tags: z.array(z.string()),
2167
- trust_tier: z.string(),
2168
- pricing: z.string(),
2169
- // A display-only premium flag: a badge, never a payment surface. Nullable
2170
- // + optional: an older registry omits it, and it is absent on non-premium rows.
2171
- premium: z.boolean().nullish(),
2172
- latest_version: z.string().nullable(),
2173
- downloads: z.number(),
2174
- updated_at: z.string(),
2175
- kinds: z.array(marketplaceSearchKind),
2176
- groups: z.array(marketplaceSearchGroup),
2177
- });
2178
- export type MarketplaceSearchRow = z.infer<typeof marketplaceSearchRow>;
2179
-
2180
- /**
2181
- * One page of listing rows. The wire carries no next-page field — has-next is
2182
- * COMPUTED: `page * page_size < total`.
2183
- */
2184
- export const marketplaceSearchPage = z.object({
2185
- listings: z.array(marketplaceSearchRow),
2186
- total: z.number(),
2187
- page: z.number(),
2188
- page_size: z.number(),
2189
- });
2190
- export type MarketplaceSearchPage = z.infer<typeof marketplaceSearchPage>;
2191
-
2192
- /**
2193
- * One version of a listing as the versions list carries it (`version`, its
2194
- * publication `status`, the `contract_range` it was validated against, and when
2195
- * it was `published_at`). `contract_range` and `published_at` are null until a
2196
- * version reaches the published state, so both are nullable.
2197
- */
2198
- export const marketplaceVersion = z.object({
2199
- version: z.string(),
2200
- contract_range: z.string().nullable(),
2201
- status: z.string(),
2202
- published_at: z.string().nullable(),
2203
- });
2204
- export type MarketplaceVersion = z.infer<typeof marketplaceVersion>;
2205
-
2206
- /**
2207
- * The listing's latest published version as embedded in its detail: the version
2208
- * (with the `marketplaceVersion` lifecycle fields) and the items it contains. The
2209
- * registry's detail body carries NO per-version `delivery` field — delivery is
2210
- * server-computed from whether the spec names a package, and the detail surface
2211
- * derives it from the listing's `package` (null ⇒ `descriptor`, else `package`).
2212
- * Env to collect comes from the install preview (`required_env` / `missing_env`),
2213
- * never from these items.
2214
- */
2215
- export const marketplaceLatestVersion = marketplaceVersion.extend({
2216
- items: z.array(marketplaceItem),
2217
- });
2218
- export type MarketplaceLatestVersion = z.infer<typeof marketplaceLatestVersion>;
2219
-
2220
- /** One advisory row; `withdrawn_at` non-null means it no longer applies. */
2221
- export const marketplaceAdvisory = z.object({
2222
- id: z.number(),
2223
- // The "namespace/name" listing ref exactly as the registry serves it — the
2224
- // wire field consumers match installed plugins against.
2225
- listing: z.string(),
2226
- affected_versions: z.string(),
2227
- severity: z.string(),
2228
- summary: z.string(),
2229
- created_at: z.string(),
2230
- withdrawn_at: z.string().nullable(),
2231
- });
2232
- export type MarketplaceAdvisory = z.infer<typeof marketplaceAdvisory>;
2233
-
2234
- /**
2235
- * A plugin's full detail (`GET /api/marketplace/plugins/{ns}/{name}`): the
2236
- * listing, its latest published version (`null` when nothing is published yet,
2237
- * carrying the contained items), and the known version history.
2238
- */
2239
- export const marketplacePluginDetail = z.object({
2240
- namespace: z.string(),
2241
- name: z.string(),
2242
- display_name: z.string().nullable(),
2243
- icon_url: z.string().nullable(),
2244
- // Null for a descriptor listing (`source` `spec`): it names no package.
2245
- package: z.string().nullable(),
2246
- // The install-provenance vocabulary (`pypi` / `github` / `spec`). Nullish: an
2247
- // older registry omits it, and a listing with nothing published carries none.
2248
- source: marketplaceSource.nullish(),
2249
- description: z.string(),
2250
- readme_md: z.string().nullable(),
2251
- license: z.string().nullable(),
2252
- homepage_url: z.string().nullable(),
2253
- repository_url: z.string().nullable(),
2254
- // The marketplace-stored docs-site URL: the store is the source, the UI a
2255
- // link. Nullable + optional: absent on a listing with no published docs.
2256
- docs_url: z.string().nullish(),
2257
- categories: z.array(z.string()),
2258
- tags: z.array(z.string()),
2259
- trust_tier: z.string(),
2260
- pricing: z.string(),
2261
- // A display-only premium flag: a badge, never a payment surface.
2262
- premium: z.boolean().nullish(),
2263
- downloads: z.number(),
2264
- latest: marketplaceLatestVersion.nullable(),
2265
- versions: z.array(marketplaceVersion),
2266
- });
2267
- export type MarketplacePluginDetail = z.infer<typeof marketplacePluginDetail>;
2268
-
2269
- /**
2270
- * One installed plugin's compat verdict against the RUNNING tai42-contract:
2271
- * `compatible` / `incompatible` per its declared contract range, `unknown` when
2272
- * no verdict could be computed. `reason` is the human-readable explanation of a
2273
- * non-`compatible` verdict, `null` when compatible. CLOSED (`.strict()`): the
2274
- * server is built against exactly this shape, so an unknown key is drift and
2275
- * throws instead of being stripped.
2276
- */
2277
- export const marketplaceInstalledCompat = z
2278
- .object({
2279
- status: z.enum(['compatible', 'incompatible', 'unknown']),
2280
- reason: z.string().nullable(),
2281
- })
2282
- .strict();
2283
- export type MarketplaceInstalledCompat = z.infer<typeof marketplaceInstalledCompat>;
2284
-
2285
- /**
2286
- * One installed marketplace plugin as the local attribution store records it.
2287
- * `latest` is the newest registry version (`null` when unknown);
2288
- * `update_available` advertises only a newer COMPATIBLE version, and
2289
- * `incompatible_newer` names the newest version blocked by the running
2290
- * contract (`null` when none), so "an update exists but needs a newer core" is
2291
- * visible. `missing_upstream` flags a plugin the registry no longer lists.
2292
- * `compat` is the row's verdict against the running contract. CLOSED, like
2293
- * every shape of the installed listing.
2294
- */
2295
- export const marketplaceInstalledPlugin = z
2296
- .object({
2297
- ref: z.string(),
2298
- version: z.string(),
2299
- source: z.string(),
2300
- // How the row was delivered: `package` for a pip-installed plugin, `descriptor`
2301
- // for a descriptor-only plugin whose stored spec names no package.
2302
- delivery: marketplaceDelivery,
2303
- installed_at: z.string(),
2304
- latest: z.string().nullable(),
2305
- update_available: z.boolean(),
2306
- incompatible_newer: z.string().nullable(),
2307
- missing_upstream: z.boolean(),
2308
- compat: marketplaceInstalledCompat,
2309
- // The `{kind, name}` of every item this plugin's stored spec provides
2310
- // (local truth). The connectors page's McpServersSection joins the mcp-server
2311
- // item names against the manifest's mcp-entry titles to render an
2312
- // installer-written entry read-only.
2313
- items: z.array(marketplaceItem.pick({ kind: true, name: true })),
2314
- // The persisted `{item_name: base}` mount this plugin is installed at (local
2315
- // truth; `{}` means every item at its declared base). Studio seeds the update
2316
- // flow and renders routes at the ACTUAL mounted base from this.
2317
- route_mounts: z.record(z.string(), z.string()),
2318
- })
2319
- .strict();
2320
- export type MarketplaceInstalledPlugin = z.infer<typeof marketplaceInstalledPlugin>;
2321
-
2322
- /** One plugin the boot pass quarantined instead of loading, with why. CLOSED. */
2323
- export const marketplaceQuarantinedPlugin = z
2324
- .object({
2325
- name: z.string(),
2326
- reason: z.string(),
2327
- })
2328
- .strict();
2329
- export type MarketplaceQuarantinedPlugin = z.infer<typeof marketplaceQuarantinedPlugin>;
2330
-
2331
- /**
2332
- * The installed inventory (`GET /api/marketplace/installed`): the attributed
2333
- * rows plus every plugin the boot pass quarantined instead of loading. CLOSED.
2334
- */
2335
- export const marketplaceInstalled = z
2336
- .object({
2337
- installed: z.array(marketplaceInstalledPlugin),
2338
- quarantined: z.array(marketplaceQuarantinedPlugin),
2339
- })
2340
- .strict();
2341
- export type MarketplaceInstalled = z.infer<typeof marketplaceInstalled>;
2342
-
2343
- /**
2344
- * One per-plugin outcome of `POST /api/marketplace/upgrade-all`: `upgraded`,
2345
- * already `up-to-date`, `no-compatible-version` in the registry, or `failed`;
2346
- * `detail` always carries the human-readable specifics. CLOSED.
2347
- */
2348
- export const marketplaceUpgradeAllRow = z
2349
- .object({
2350
- ref: z.string(),
2351
- outcome: z.enum(['upgraded', 'up-to-date', 'no-compatible-version', 'failed']),
2352
- detail: z.string(),
2353
- })
2354
- .strict();
2355
- export type MarketplaceUpgradeAllRow = z.infer<typeof marketplaceUpgradeAllRow>;
2356
-
2357
- /** The whole upgrade-all readout: one outcome row per installed plugin. CLOSED. */
2358
- export const marketplaceUpgradeAllResult = z
2359
- .object({
2360
- results: z.array(marketplaceUpgradeAllRow),
2361
- })
2362
- .strict();
2363
- export type MarketplaceUpgradeAllResult = z.infer<typeof marketplaceUpgradeAllResult>;
2364
-
2365
- /** The advisory state for installed plugins, with when it was last fetched. */
2366
- export const marketplaceAdvisories = z.object({
2367
- advisories: z.array(marketplaceAdvisory),
2368
- fetched_at: z.string(),
2369
- });
2370
-
2371
- /** The registry's controlled category list, proxied through the skeleton. */
2372
- export const marketplaceCategories = z.array(z.string());
2373
-
2374
- /** The registry's controlled item-kind list, proxied through the skeleton. */
2375
- export const marketplaceKinds = z.array(z.string());
2376
-
2377
- /**
2378
- * One route the install / update actually mounted: the item that owns it, the
2379
- * absolute `full_path` it now answers, the `methods`, and whether it is served
2380
- * `public`. The receipt lists these so the operator sees what was opened where.
2381
- */
2382
- export const marketplaceMountedRoute = z.object({
2383
- item: z.string(),
2384
- full_path: z.string(),
2385
- methods: z.array(routeMethod),
2386
- public: z.boolean(),
2387
- });
2388
- export type MarketplaceMountedRoute = z.infer<typeof marketplaceMountedRoute>;
2389
-
2390
- /**
2391
- * The receipt of an install / update: what landed and at which version.
2392
- * `notes` (env-selected items naming their activating env var) and any
2393
- * non-critical `advisories` ride BOTH the install and update receipts.
2394
- * `routes` is the mounted-route list — empty for a plugin that registers none.
2395
- * The wire also carries `package`, `reload`, and `pip_output`, which the receipt
2396
- * does not model — zod's default strip drops them; the UI renders the receipt,
2397
- * not the raw install log.
2398
- */
2399
- export const marketplaceInstallResult = z.object({
2400
- ref: z.string(),
2401
- version: z.string(),
2402
- notes: z.array(z.string()),
2403
- advisories: z.array(marketplaceAdvisory),
2404
- routes: z.array(marketplaceMountedRoute),
2405
- });
2406
- export type MarketplaceInstallResult = z.infer<typeof marketplaceInstallResult>;
2407
-
2408
- /** One resolved route in an install preview: the declared `path`, its resolved
2409
- * absolute `full_path`, the `methods`, and whether it is `public`. */
2410
- export const marketplacePreviewRoute = z.object({
2411
- path: z.string(),
2412
- full_path: z.string(),
2413
- methods: z.array(routeMethod),
2414
- public: z.boolean(),
2415
- });
2416
- export type MarketplacePreviewRoute = z.infer<typeof marketplacePreviewRoute>;
2417
-
2418
- /** One route-carrying item in an install preview: its resolved `base` (the
2419
- * override or the default), the `default_base` it declares, and the routes that
2420
- * mount under it. */
2421
- export const marketplacePreviewItem = z.object({
2422
- item: z.string(),
2423
- kind: z.string(),
2424
- base: z.string(),
2425
- default_base: z.string(),
2426
- routes: z.array(marketplacePreviewRoute),
2427
- });
2428
- export type MarketplacePreviewItem = z.infer<typeof marketplacePreviewItem>;
2429
-
2430
- /** One collision the preview found: a route whose resolved shape+method clashes
2431
- * with an already-owned route. `conflict_owner` is `core` or `plugin:<ref>`; the
2432
- * remedy is to remap the item's base. */
2433
- export const marketplacePreviewCollision = z.object({
2434
- item: z.string(),
2435
- full_path: z.string(),
2436
- methods: z.array(routeMethod),
2437
- conflict_owner: z.string(),
2438
- conflict_path: z.string(),
2439
- });
2440
- export type MarketplacePreviewCollision = z.infer<typeof marketplacePreviewCollision>;
2441
-
2442
- /** One public route the operator is asked to accept: served WITHOUT
2443
- * authentication once installed. */
2444
- export const marketplacePreviewPublicRoute = z.object({
2445
- item: z.string(),
2446
- full_path: z.string(),
2447
- methods: z.array(routeMethod),
2448
- });
2449
- export type MarketplacePreviewPublicRoute = z.infer<typeof marketplacePreviewPublicRoute>;
2450
-
2451
- /**
2452
- * The install preview (`POST /api/marketplace/install/preview`): the resolved
2453
- * routes per item with any base overrides applied, the collisions against the
2454
- * live registry, the public routes requiring acceptance, and whether acceptance
2455
- * is required at all. `new_public_routes` narrows to rows not already approved
2456
- * (the update case); on a fresh install it equals `public_routes`.
2457
- *
2458
- * `required_env` is every env var the spec requires with its derived secret-ness;
2459
- * `missing_env` is the bare names not already present in the env store union
2460
- * process env (server-computed — the ONE authority the dialog collects), and
2461
- * `delivery` is the one word the surface shows (`package` / `descriptor`).
2462
- */
2463
- export const marketplaceInstallPreview = z.object({
2464
- ref: z.string(),
2465
- version: z.string(),
2466
- items: z.array(marketplacePreviewItem),
2467
- collisions: z.array(marketplacePreviewCollision),
2468
- public_routes: z.array(marketplacePreviewPublicRoute),
2469
- new_public_routes: z.array(marketplacePreviewPublicRoute),
2470
- requires_public_acceptance: z.boolean(),
2471
- required_env: z.array(marketplaceRequiredEnvVar),
2472
- missing_env: z.array(z.string()),
2473
- delivery: marketplaceDelivery,
2474
- });
2475
- export type MarketplaceInstallPreview = z.infer<typeof marketplaceInstallPreview>;
2476
-
2477
- /**
2478
- * The receipt of an uninstall. `notes` carries operator warnings (e.g. a config
2479
- * provider still selected by its env var); render them like the install
2480
- * receipt's notes.
2481
- */
2482
- export const marketplaceUninstallResult = z.object({
2483
- ref: z.string(),
2484
- uninstalled: z.literal(true),
2485
- notes: z.array(z.string()),
2486
- });
2487
- export type MarketplaceUninstallResult = z.infer<typeof marketplaceUninstallResult>;
2488
-
2489
- // Background tool-run schemas live in their own module; re-exported here so the
2490
- // shared fixture-validation contract resolves them by name alongside the rest.
2491
- export { toolRunSubmitResult, toolRunRecord, toolRunList } from './tool-runs';
2492
-
2493
- // -- states ------------------------------------------------------------------
2494
- // The hand-written state response DTOs — list rows and detail (built on the generated
2495
- // `stateDeclaration`), attachments, per-subject records and subjects, stats and consumers.
2496
- // The served DOCUMENT schemas (`stateDeclaration`/`stateTemplateDocument` and the template
2497
- // sub-shapes) are generated from the contract bundle and re-exported at the top of this
2498
- // file. The composed `effective_schema` and write `regimes` stay permissive JSON records
2499
- // (the auto-form and JsonTree interpret them at runtime) and are server-computed, so they
2500
- // are modelled optional and a response that omits them still parses.
2501
-
2502
- /** One addressed subject: the conversation-target scope plus the (kind, key) within it. */
2503
- export const stateSubject = z.object({
2504
- target_kind: conversationTargetKind,
2505
- target_name: z.string(),
2506
- kind: z.string(),
2507
- key: z.string(),
2508
- });
2509
- export type StateSubject = z.infer<typeof stateSubject>;
2510
-
2511
- /** One absolute write-regime rule composed over the attachments: `{path, regime}`, permissive. */
2512
- export const stateRegime = z.record(z.string(), z.unknown());
2513
-
2514
- /**
2515
- * One row of `GET /api/states` — a served declaration (`list_states` dumps every
2516
- * `StateDeclaration`) plus `updated_at` (the row's last-write timestamp, ISO or
2517
- * `null`). The list carries no record count; the `Records` column reads
2518
- * `getStateStats` lazily per row.
2519
- */
2520
- export const stateListItem = stateDeclaration.extend({
2521
- updated_at: z.string().nullable().default(null),
2522
- });
2523
- export type StateListItem = z.infer<typeof stateListItem>;
2524
- export const stateList = z.array(stateListItem);
2525
-
2526
- /** One template attachment on a state: where the fragment lands + its param/declaration values. */
2527
- export const stateAttachment = z.object({
2528
- template: z.string(),
2529
- path: z.array(z.string()),
2530
- parameters: z.record(z.string(), z.unknown()).default({}),
2531
- declarations: z.record(z.string(), z.unknown()).default({}),
2532
- });
2533
- export type StateAttachment = z.infer<typeof stateAttachment>;
2534
- export const stateAttachmentList = z.array(stateAttachment);
2535
-
2536
- /** `GET /api/states/{name}` — the declaration (with effective schema + regimes) plus attachments. */
2537
- export const stateDetail = stateDeclaration.extend({
2538
- attachments: z.array(stateAttachment).default([]),
2539
- });
2540
- export type StateDetail = z.infer<typeof stateDetail>;
2541
-
2542
- /**
2543
- * `GET /api/states/{name}/stats` — `records` (total documents), `per_field` (the count
2544
- * of records carrying each base-schema property), `per_kind` (records per subject kind)
2545
- * and `consumers` (the count of listable consumers). The Records tab shows `records`;
2546
- * the states list reads it lazily for its Records column.
2547
- */
2548
- export const stateStats = z.object({
2549
- records: z.number(),
2550
- per_field: z.record(z.string(), z.number()).default({}),
2551
- per_kind: z.record(z.string(), z.number()).default({}),
2552
- consumers: z.number().default(0),
2553
- });
2554
- export type StateStats = z.infer<typeof stateStats>;
2555
-
2556
- /**
2557
- * One row of `GET /api/state-templates`. The template document plus the two derived columns
2558
- * the Templates tab shows: `attached_to` (how many states attach it — a delete is refused
2559
- * while > 0) and `shipped_default` (a platform-shipped template). Both are server-derived
2560
- * columns; the document fields are verbatim.
2561
- */
2562
- export const stateTemplateListItem = stateTemplateDocument.extend({
2563
- attached_to: z.number().default(0),
2564
- shipped_default: z.boolean().default(false),
2565
- });
2566
- export type StateTemplateListItem = z.infer<typeof stateTemplateListItem>;
2567
- export const stateTemplateList = z.array(stateTemplateListItem);
2568
-
2569
- /** A read record: the state, its subject, the document + monotonic seq, and any fold. */
2570
- export const stateRecord = z.object({
2571
- state: z.string(),
2572
- subject: stateSubject,
2573
- data: z.record(z.string(), z.unknown()).default({}),
2574
- seq: z.number(),
2575
- canonical_subject: stateSubject,
2576
- folded_from: z.array(stateSubject).default([]),
2577
- });
2578
- export type StateRecord = z.infer<typeof stateRecord>;
2579
-
2580
- /** The outcome of an `apply` (a delta batch): whether it applied + the resulting doc. */
2581
- export const applyResult = z.object({
2582
- applied: z.boolean(),
2583
- data: z.record(z.string(), z.unknown()).nullable().default(null),
2584
- seq: z.number().nullable().default(null),
2585
- skipped: z.array(z.record(z.string(), z.unknown())).default([]),
2586
- });
2587
- export type ApplyResult = z.infer<typeof applyResult>;
2588
-
2589
- /**
2590
- * `GET …/template-jq/{name}` — the evaluated template jq's `name`, its `purpose`, and
2591
- * its `value` (the `input` jq's read). GET evaluates ONLY an `input`-purpose jq; a GET
2592
- * on an `update`-purpose name is refused with 422, so the result's purpose is always
2593
- * `input`.
2594
- */
2595
- export const templateJqResult = z.object({
2596
- name: z.string(),
2597
- purpose: z.literal('input'),
2598
- value: z.unknown(),
2599
- });
2600
- export type TemplateJqResult = z.infer<typeof templateJqResult>;
2601
-
2602
- /**
2603
- * `POST …/template-jq/{name}` — the applied template jq's outcome. The jq lands an op
2604
- * batch through the same `apply` chokepoint as a delta, so `applied`/`data`/`seq`/`skipped`
2605
- * carry that outcome.
2606
- */
2607
- export const templateJqApplyResult = z.object({
2608
- name: z.string(),
2609
- applied: z.boolean(),
2610
- data: z.record(z.string(), z.unknown()).nullable().default(null),
2611
- seq: z.number().nullable().default(null),
2612
- skipped: z.array(z.record(z.string(), z.unknown())).default([]),
2613
- });
2614
- export type TemplateJqApplyResult = z.infer<typeof templateJqApplyResult>;
2615
-
2616
- /**
2617
- * One paged subject row: the addressed `subject` plus its record's last-write
2618
- * `updated_at` — epoch seconds (the store's `extract(epoch FROM updated_at)`), served
2619
- * raw (no `mode="json"` dump), and `NOT NULL` on every record, so a plain number.
2620
- */
2621
- export const subjectRow = z.object({
2622
- subject: stateSubject,
2623
- updated_at: z.number(),
2624
- });
2625
- export type SubjectRow = z.infer<typeof subjectRow>;
2626
-
2627
- /**
2628
- * `GET /api/states/{name}/subjects` — a keyset page of subject rows (optionally of one
2629
- * `kind`): the matched `subjects` plus the `next_cursor` a "Load more" carries back
2630
- * (`null` at the end).
2631
- */
2632
- export const subjectPage = z.object({
2633
- subjects: z.array(subjectRow),
2634
- next_cursor: z.string().nullable().default(null),
2635
- });
2636
- export type SubjectPage = z.infer<typeof subjectPage>;
2637
-
2638
- /**
2639
- * `POST /api/states/{name}/records/search` — a keyset page of the subject rows whose
2640
- * document contains the filters (a JSONB containment match). Each hit's `subject` opens
2641
- * the record page; `next_cursor` pages the rest.
2642
- */
2643
- export const recordSearchPage = z.object({
2644
- matches: z.array(subjectRow),
2645
- next_cursor: z.string().nullable().default(null),
2646
- });
2647
- export type RecordSearchPage = z.infer<typeof recordSearchPage>;
2648
-
2649
- /** A WriteEntry's origin completed by the platform chokepoint (mirrors CompletedOrigin). */
2650
- export const completedOrigin = z.object({
2651
- consumer: z.string().nullable().default(null),
2652
- // Opaque consumer-supplied provenance JSON the platform stores as is.
2653
- meta: z.record(z.string(), z.unknown()).nullable().default(null),
2654
- run_id: z.string().nullable().default(null),
2655
- op_id: z.string().nullable().default(null),
2656
- door: z.string(),
2657
- actor: z.string().nullable().default(null),
2658
- turn_id: z.string().nullable().default(null),
2659
- inbound_id: z.string().nullable().default(null),
2660
- });
2661
- export type CompletedOrigin = z.infer<typeof completedOrigin>;
2662
-
2663
- /** One row of a subject's audit trail (mirrors WriteEntry). `paths` are absolute. */
2664
- export const writeEntry = z.object({
2665
- seq: z.number(),
2666
- at: z.string(),
2667
- origin: completedOrigin,
2668
- paths: z.array(z.array(z.union([z.string(), z.number()]))).default([]),
2669
- });
2670
- export type WriteEntry = z.infer<typeof writeEntry>;
2671
-
2672
- /** `GET /api/states/{name}/records/.../writes` — a keyset page of the audit trail. */
2673
- export const writesPage = z.object({
2674
- items: z.array(writeEntry),
2675
- next_cursor: z.string().nullable().default(null),
2676
- });
2677
- export type WritesPage = z.infer<typeof writesPage>;
2678
-
2679
- /** Where the Studio opens a consumer (mirrors ConsumerLink): a token+search OR plugin path. */
2680
- export const consumerLink = z.object({
2681
- token: z.string().nullable().default(null),
2682
- plugin_path: z.string().nullable().default(null),
2683
- search: z.record(z.string(), z.unknown()).nullable().default(null),
2684
- });
2685
- export type ConsumerLink = z.infer<typeof consumerLink>;
2686
-
2687
- /**
2688
- * One thing that binds a state (mirrors ConsumerRow): its `kind` (flow / hook / schedule
2689
- * / agent), `name`, human `detail` and optional `link`. `unavailable` marks a consumer
2690
- * family that cannot be listed on this deployment (e.g. no scheduling backend), surfaced
2691
- * as a muted line — never swallowed.
2692
- */
2693
- export const consumerRow = z.object({
2694
- kind: z.string(),
2695
- name: z.string().nullable().default(null),
2696
- detail: z.string().nullable().default(null),
2697
- link: consumerLink.nullable().default(null),
2698
- unavailable: z.string().nullable().default(null),
2699
- });
2700
- export type ConsumerRow = z.infer<typeof consumerRow>;
2701
-
2702
- /** `GET /api/states/{name}/consumers` — the union of every registered lister's rows. */
2703
- export const stateConsumers = z.array(consumerRow);
2704
- export type StateConsumers = z.infer<typeof stateConsumers>;
2705
-
2706
- /** `DELETE /api/states/{name}` — the removed state. */
2707
- export const stateDeleted = z.object({ name: z.string(), deleted: z.literal(true) });
2708
-
2709
- /** `PUT /api/states/{name}/attachments/{template}` — the template now attached to the state. */
2710
- export const stateAttached = z.object({
2711
- attached: z.literal(true),
2712
- state: z.string(),
2713
- template: z.string(),
2714
- });
2715
- export type StateAttached = z.infer<typeof stateAttached>;
2716
-
2717
- /** `PATCH /api/states/{name}/attachments/{template}` — the attachment whose declarations were rewritten. */
2718
- export const stateAttachmentUpdated = z.object({
2719
- updated: z.literal(true),
2720
- state: z.string(),
2721
- template: z.string(),
2722
- });
2723
- export type StateAttachmentUpdated = z.infer<typeof stateAttachmentUpdated>;
2724
-
2725
- /** `DELETE /api/states/{name}/attachments/{template}` — the template detached from the state. */
2726
- export const stateDetached = z.object({
2727
- detached: z.literal(true),
2728
- state: z.string(),
2729
- template: z.string(),
2730
- });
2731
- export type StateDetached = z.infer<typeof stateDetached>;
2732
-
2733
- /**
2734
- * `POST /api/states/{name}/records/.../fold` — the fold report: the `mode`, the
2735
- * `{kind, key}` of the folded `from` and surviving `into` subjects, whether the fold
2736
- * was `already` in place, and how many records were `flattened` into the survivor.
2737
- */
2738
- export const stateFoldReport = z.object({
2739
- mode: z.enum(['switch', 'merge']),
2740
- from: z.object({ kind: z.string(), key: z.string() }),
2741
- into: z.object({ kind: z.string(), key: z.string() }),
2742
- already: z.boolean(),
2743
- flattened: z.number().default(0),
2744
- });
2745
- export type StateFoldReport = z.infer<typeof stateFoldReport>;
2746
-
2747
- /** `DELETE /api/state-templates/{name}` — the removed template document. */
2748
- export const stateTemplateDeleted = z.object({ name: z.string(), deleted: z.literal(true) });
2749
-
2750
- /** `DELETE /api/states/{name}/records/...` — the erased record marker. */
2751
- export const recordErased = z.object({ erased: z.literal(true) });
2752
-
2753
- /**
2754
- * `POST /api/state-retention/prune` — the retention sweep's per-state deleted counts
2755
- * (`{}` when nothing expired); the UI sums the values for its total.
2756
- */
2757
- export const stateRetentionPruned = z.object({
2758
- pruned: z.record(z.string(), z.number()).default({}),
2759
- });
2760
- export type StateRetentionPruned = z.infer<typeof stateRetentionPruned>;