@starlove/orbit 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (329) hide show
  1. package/Dockerfile +10 -8
  2. package/README.md +146 -164
  3. package/dist/full/csp.txt +1 -0
  4. package/dist/full/fonts/JetBrains_Mono/JetBrainsMono-subset.woff2 +0 -0
  5. package/dist/full/fonts/JetBrains_Mono/README.txt +5 -0
  6. package/dist/full/fonts/Mona_Sans/MonaSans-subset.woff2 +0 -0
  7. package/{public/fonts/Open_Sans → dist/full/fonts/Mona_Sans}/OFL.txt +93 -93
  8. package/dist/full/fonts/Mona_Sans/README.txt +10 -0
  9. package/{public/fonts/JetBrains_Mono → dist/full/fonts/Newsreader}/OFL.txt +93 -93
  10. package/dist/full/fonts/Newsreader/README.txt +8 -0
  11. package/dist/full/fonts/Newsreader/newsreader.woff2 +0 -0
  12. package/dist/full/index.html +36 -653
  13. package/dist/full/js/app.BmuponPJ.js +32 -0
  14. package/dist/full/js/app.BmuponPJ.js.map +1 -0
  15. package/dist/full/js/chunk.1fJGQGbW.js +2 -0
  16. package/dist/full/js/chunk.1fJGQGbW.js.map +1 -0
  17. package/dist/full/js/chunk.5D7JO2zp.js +2 -0
  18. package/dist/full/js/chunk.5D7JO2zp.js.map +1 -0
  19. package/dist/full/js/chunk.BrNn_D79.js +2 -0
  20. package/dist/full/js/chunk.BrNn_D79.js.map +1 -0
  21. package/dist/full/js/chunk.CokJXIqM.js +2 -0
  22. package/dist/full/js/chunk.CokJXIqM.js.map +1 -0
  23. package/dist/full/js/chunk.D_Haqy6R.js +2 -0
  24. package/dist/full/js/chunk.D_Haqy6R.js.map +1 -0
  25. package/dist/full/js/chunk.RO0jsH6I.js +15 -0
  26. package/dist/full/js/chunk.RO0jsH6I.js.map +1 -0
  27. package/dist/full/js/chunk.dqWOEtBM.js +2 -0
  28. package/dist/full/js/chunk.dqWOEtBM.js.map +1 -0
  29. package/dist/full/js/warm.f260bbbc.js +15 -0
  30. package/dist/full/orbits-favicon.png +0 -0
  31. package/dist/full/orbits-logo-bg-wide.png +0 -0
  32. package/dist/full/orbits-logo-bg.png +0 -0
  33. package/dist/full/orbits-logo.png +0 -0
  34. package/dist/full/styles.2b154ffe.css +1 -0
  35. package/dist/full/vendor/starlove-ui/theme-init.js +66 -0
  36. package/dist/full/version.json +2 -2
  37. package/docs/AGENT_GUIDE.md +114 -0
  38. package/docs/UPDATING.md +249 -64
  39. package/lib/adapters/indexeddb/blobs.d.ts +3 -0
  40. package/lib/adapters/indexeddb/blobs.js +54 -0
  41. package/lib/adapters/indexeddb/database.d.ts +60 -0
  42. package/lib/adapters/indexeddb/database.js +126 -0
  43. package/lib/adapters/indexeddb/index.d.ts +5 -0
  44. package/lib/adapters/indexeddb/index.js +3 -0
  45. package/lib/adapters/indexeddb/locks.d.ts +14 -0
  46. package/lib/adapters/indexeddb/locks.js +40 -0
  47. package/lib/adapters/indexeddb/store.d.ts +40 -0
  48. package/lib/adapters/indexeddb/store.js +424 -0
  49. package/lib/adapters/indexeddb/tables.d.ts +43 -0
  50. package/lib/adapters/indexeddb/tables.js +127 -0
  51. package/lib/adapters/memory/blobs.d.ts +3 -0
  52. package/lib/adapters/memory/blobs.js +44 -0
  53. package/lib/adapters/memory/board.d.ts +41 -0
  54. package/lib/adapters/memory/board.js +521 -0
  55. package/lib/adapters/memory/index.d.ts +2 -0
  56. package/lib/adapters/memory/index.js +2 -0
  57. package/lib/adapters/memory/search.d.ts +6 -0
  58. package/lib/adapters/memory/search.js +30 -0
  59. package/lib/adapters/memory/store.d.ts +34 -0
  60. package/lib/adapters/memory/store.js +266 -0
  61. package/lib/adapters/sqlite/backups.d.ts +53 -0
  62. package/lib/adapters/sqlite/backups.js +232 -0
  63. package/lib/adapters/sqlite/blobs.d.ts +14 -0
  64. package/lib/adapters/sqlite/blobs.js +109 -0
  65. package/lib/adapters/sqlite/board.d.ts +77 -0
  66. package/lib/adapters/sqlite/board.js +328 -0
  67. package/lib/adapters/sqlite/database.d.ts +67 -0
  68. package/lib/adapters/sqlite/database.js +285 -0
  69. package/lib/adapters/sqlite/index.d.ts +3 -0
  70. package/lib/adapters/sqlite/index.js +2 -0
  71. package/lib/adapters/sqlite/lock.d.ts +11 -0
  72. package/lib/adapters/sqlite/lock.js +34 -0
  73. package/lib/adapters/sqlite/records.d.ts +54 -0
  74. package/lib/adapters/sqlite/records.js +546 -0
  75. package/lib/adapters/sqlite/schema.d.ts +54 -0
  76. package/lib/adapters/sqlite/schema.js +276 -0
  77. package/lib/adapters/sqlite/store.d.ts +39 -0
  78. package/lib/adapters/sqlite/store.js +385 -0
  79. package/lib/browser/in-page.d.ts +31 -0
  80. package/lib/browser/in-page.js +46 -0
  81. package/lib/browser/old-preview.d.ts +13 -0
  82. package/lib/browser/old-preview.js +80 -0
  83. package/lib/browser/preview-board.d.ts +8 -0
  84. package/lib/browser/preview-board.js +69 -0
  85. package/lib/cli/main.d.ts +7 -0
  86. package/lib/cli/main.js +343 -0
  87. package/lib/cli/orbit.d.ts +8 -0
  88. package/lib/cli/orbit.js +25 -0
  89. package/lib/client/http.d.ts +38 -0
  90. package/lib/client/http.js +334 -0
  91. package/lib/client/local.d.ts +26 -0
  92. package/lib/client/local.js +190 -0
  93. package/lib/client/object-url.d.ts +8 -0
  94. package/lib/client/object-url.js +21 -0
  95. package/lib/client/types.d.ts +268 -0
  96. package/lib/client/types.js +8 -0
  97. package/lib/commands/agent-tools.d.ts +31 -0
  98. package/lib/commands/agent-tools.js +100 -0
  99. package/lib/commands/index.d.ts +56 -0
  100. package/lib/commands/index.js +298 -0
  101. package/lib/commands/parse.d.ts +73 -0
  102. package/lib/commands/parse.js +231 -0
  103. package/lib/commands/present.d.ts +134 -0
  104. package/lib/commands/present.js +198 -0
  105. package/lib/commands/session.d.ts +85 -0
  106. package/lib/commands/session.js +138 -0
  107. package/lib/commands/verbs.d.ts +35 -0
  108. package/lib/commands/verbs.js +842 -0
  109. package/lib/core/actors.d.ts +18 -0
  110. package/lib/core/actors.js +45 -0
  111. package/lib/core/agent.d.ts +128 -0
  112. package/lib/core/agent.js +323 -0
  113. package/lib/core/boards.d.ts +113 -0
  114. package/lib/core/boards.js +288 -0
  115. package/lib/core/bootstrap.d.ts +47 -0
  116. package/lib/core/bootstrap.js +100 -0
  117. package/lib/core/bytes.d.ts +10 -0
  118. package/lib/core/bytes.js +52 -0
  119. package/lib/core/card-paths.d.ts +25 -0
  120. package/lib/core/card-paths.js +44 -0
  121. package/lib/core/context.d.ts +60 -0
  122. package/lib/core/context.js +42 -0
  123. package/lib/core/cross-board.d.ts +81 -0
  124. package/lib/core/cross-board.js +228 -0
  125. package/lib/core/describe.d.ts +21 -0
  126. package/lib/core/describe.js +81 -0
  127. package/lib/core/errors.d.ts +159 -0
  128. package/lib/core/errors.js +160 -0
  129. package/lib/core/files.d.ts +79 -0
  130. package/lib/core/files.js +262 -0
  131. package/lib/core/history.d.ts +20 -0
  132. package/lib/core/history.js +53 -0
  133. package/lib/core/image-types.d.ts +16 -0
  134. package/lib/core/image-types.js +36 -0
  135. package/lib/core/model.d.ts +263 -0
  136. package/lib/core/model.js +34 -0
  137. package/lib/core/nebula-link.d.ts +14 -0
  138. package/lib/core/nebula-link.js +39 -0
  139. package/lib/core/relations.d.ts +58 -0
  140. package/lib/core/relations.js +112 -0
  141. package/lib/core/search.d.ts +38 -0
  142. package/lib/core/search.js +92 -0
  143. package/lib/core/sessions.d.ts +44 -0
  144. package/lib/core/sessions.js +158 -0
  145. package/lib/core/snapshot-schema.d.ts +295 -0
  146. package/lib/core/snapshot-schema.js +82 -0
  147. package/lib/core/snapshots.d.ts +75 -0
  148. package/lib/core/snapshots.js +409 -0
  149. package/lib/core/states.d.ts +25 -0
  150. package/lib/core/states.js +126 -0
  151. package/lib/core/text.d.ts +40 -0
  152. package/lib/core/text.js +113 -0
  153. package/lib/core/tickets.d.ts +69 -0
  154. package/lib/core/tickets.js +311 -0
  155. package/lib/core/trello.d.ts +17 -0
  156. package/lib/core/trello.js +269 -0
  157. package/lib/core/verdicts.d.ts +28 -0
  158. package/lib/core/verdicts.js +92 -0
  159. package/lib/core/views.d.ts +95 -0
  160. package/lib/core/views.js +186 -0
  161. package/lib/http/app.d.ts +46 -0
  162. package/lib/http/app.js +491 -0
  163. package/lib/mcp-server.d.ts +10 -0
  164. package/lib/mcp-server.js +120 -0
  165. package/lib/node/assets.d.ts +7 -0
  166. package/lib/node/assets.js +68 -0
  167. package/lib/node/data-folder.d.ts +21 -0
  168. package/lib/node/data-folder.js +34 -0
  169. package/lib/node/env.d.ts +38 -0
  170. package/lib/node/env.js +49 -0
  171. package/lib/node/host-check.d.ts +24 -0
  172. package/lib/node/host-check.js +53 -0
  173. package/lib/node/host-services.d.ts +9 -0
  174. package/lib/node/host-services.js +150 -0
  175. package/lib/node/local.d.ts +22 -0
  176. package/lib/node/local.js +61 -0
  177. package/lib/node/migrate.d.ts +53 -0
  178. package/lib/node/migrate.js +635 -0
  179. package/lib/node/package.d.ts +7 -0
  180. package/lib/node/package.js +14 -0
  181. package/lib/node/repo-board.d.ts +49 -0
  182. package/lib/node/repo-board.js +245 -0
  183. package/lib/node/serve.d.ts +8 -0
  184. package/lib/node/serve.js +106 -0
  185. package/lib/orbit.d.ts +47 -0
  186. package/lib/orbit.js +44 -0
  187. package/lib/quiet-sqlite-warning.d.ts +1 -0
  188. package/lib/quiet-sqlite-warning.js +18 -0
  189. package/lib/store/blob-path.d.ts +6 -0
  190. package/lib/store/blob-path.js +12 -0
  191. package/lib/store/conformance.d.ts +52 -0
  192. package/lib/store/conformance.js +1449 -0
  193. package/lib/store/search-words.d.ts +10 -0
  194. package/lib/store/search-words.js +19 -0
  195. package/lib/store/types.d.ts +452 -0
  196. package/lib/store/types.js +34 -0
  197. package/llms.txt +63 -0
  198. package/package.json +109 -25
  199. package/src/cli/orbit.js +11 -658
  200. package/src/mcp-server.js +16 -876
  201. package/SKILL-ORBIT.md +0 -151
  202. package/build.mjs +0 -210
  203. package/dist/full/js/app.62905fcb.js +0 -952
  204. package/dist/full/js/app.62905fcb.js.map +0 -7
  205. package/dist/full/styles.4e60d824.css +0 -1
  206. package/docs/AGENT_PROTOCOL.md +0 -424
  207. package/docs/MCP_SETUP.md +0 -54
  208. package/docs/ORBIT_DISPATCH.md +0 -280
  209. package/docs/SS-Orbit-Dark.png +0 -0
  210. package/docs/SS-Orbit-Light.png +0 -0
  211. package/docs/mcp-client.example.json +0 -13
  212. package/docs/setup/orbit-init-wizard.md +0 -174
  213. package/public/apple-touch-icon.png +0 -0
  214. package/public/cursors/live_arrow.cur +0 -0
  215. package/public/cursors/live_link.cur +0 -0
  216. package/public/cursors/live_move.cur +0 -0
  217. package/public/cursors/live_select.cur +0 -0
  218. package/public/cursors/live_unavail.cur +0 -0
  219. package/public/favicon-32x32.png +0 -0
  220. package/public/fonts/Chakra_Petch/ChakraPetch-Bold.ttf +0 -0
  221. package/public/fonts/Chakra_Petch/ChakraPetch-BoldItalic.ttf +0 -0
  222. package/public/fonts/Chakra_Petch/ChakraPetch-Italic.ttf +0 -0
  223. package/public/fonts/Chakra_Petch/ChakraPetch-Light.ttf +0 -0
  224. package/public/fonts/Chakra_Petch/ChakraPetch-LightItalic.ttf +0 -0
  225. package/public/fonts/Chakra_Petch/ChakraPetch-Medium.ttf +0 -0
  226. package/public/fonts/Chakra_Petch/ChakraPetch-MediumItalic.ttf +0 -0
  227. package/public/fonts/Chakra_Petch/ChakraPetch-Regular.ttf +0 -0
  228. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBold.ttf +0 -0
  229. package/public/fonts/Chakra_Petch/ChakraPetch-SemiBoldItalic.ttf +0 -0
  230. package/public/fonts/Chakra_Petch/OFL.txt +0 -93
  231. package/public/fonts/Inter/Inter-Italic-VariableFont_opsz,wght.ttf +0 -0
  232. package/public/fonts/Inter/Inter-VariableFont_opsz,wght.ttf +0 -0
  233. package/public/fonts/Inter/OFL.txt +0 -93
  234. package/public/fonts/Inter/README.txt +0 -118
  235. package/public/fonts/JetBrains_Mono/JetBrainsMono-Italic-VariableFont_wght.ttf +0 -0
  236. package/public/fonts/JetBrains_Mono/JetBrainsMono-VariableFont_wght.ttf +0 -0
  237. package/public/fonts/JetBrains_Mono/README.txt +0 -79
  238. package/public/fonts/OpenRing-Regular.ttf +0 -0
  239. package/public/fonts/Open_Sans/OpenSans-Italic-VariableFont_wdth,wght.ttf +0 -0
  240. package/public/fonts/Open_Sans/OpenSans-VariableFont_wdth,wght.ttf +0 -0
  241. package/public/fonts/Open_Sans/README.txt +0 -100
  242. package/public/fonts/Orbitron/OFL.txt +0 -93
  243. package/public/fonts/Orbitron/Orbitron-VariableFont_wght.ttf +0 -0
  244. package/public/fonts/Orbitron/README.txt +0 -68
  245. package/public/fonts/Rajdhani/OFL.txt +0 -93
  246. package/public/fonts/Rajdhani/Rajdhani-Bold.ttf +0 -0
  247. package/public/fonts/Rajdhani/Rajdhani-Light.ttf +0 -0
  248. package/public/fonts/Rajdhani/Rajdhani-Medium.ttf +0 -0
  249. package/public/fonts/Rajdhani/Rajdhani-Regular.ttf +0 -0
  250. package/public/fonts/Rajdhani/Rajdhani-SemiBold.ttf +0 -0
  251. package/public/fonts/runescape-uf.ttf +0 -0
  252. package/public/index.html +0 -769
  253. package/public/js/actor-labels.js +0 -65
  254. package/public/js/api.js +0 -39
  255. package/public/js/app.js +0 -45
  256. package/public/js/artifact-drawer.js +0 -79
  257. package/public/js/board-export.js +0 -161
  258. package/public/js/board-menu.js +0 -374
  259. package/public/js/card-actions.js +0 -545
  260. package/public/js/config.js +0 -20
  261. package/public/js/create-card.js +0 -190
  262. package/public/js/dom.js +0 -19
  263. package/public/js/drawer.js +0 -273
  264. package/public/js/font-preference.js +0 -229
  265. package/public/js/format.js +0 -433
  266. package/public/js/help-menu.js +0 -31
  267. package/public/js/icons.js +0 -16
  268. package/public/js/kanban.js +0 -694
  269. package/public/js/local-backend.js +0 -1015
  270. package/public/js/main.js +0 -161
  271. package/public/js/modal.js +0 -33
  272. package/public/js/motion-preference.js +0 -64
  273. package/public/js/panel.js +0 -58
  274. package/public/js/router.js +0 -134
  275. package/public/js/search.js +0 -56
  276. package/public/js/settings-tabs.js +0 -7
  277. package/public/js/settings.js +0 -1634
  278. package/public/js/sse.js +0 -159
  279. package/public/js/starscape.js +0 -332
  280. package/public/js/state.js +0 -129
  281. package/public/js/text-editing.js +0 -128
  282. package/public/js/theme-preference.js +0 -95
  283. package/public/js/ticket-detail.js +0 -1516
  284. package/public/js/ticket-typeahead.js +0 -49
  285. package/public/js/toast.js +0 -89
  286. package/public/js/topbar-notes.js +0 -155
  287. package/public/js/transport.js +0 -22
  288. package/public/js/typeahead.js +0 -206
  289. package/public/js/unread.js +0 -121
  290. package/public/js/update-check.js +0 -58
  291. package/public/js/url-routes.js +0 -94
  292. package/public/orbits-favicon.png +0 -0
  293. package/public/orbits-logo-bg-wide.png +0 -0
  294. package/public/orbits-logo-bg.png +0 -0
  295. package/public/orbits-logo.png +0 -0
  296. package/public/styles.css +0 -7709
  297. package/src/core/agent.js +0 -387
  298. package/src/core/agents-md.js +0 -109
  299. package/src/core/artifacts.js +0 -149
  300. package/src/core/attachments.js +0 -238
  301. package/src/core/auth.js +0 -85
  302. package/src/core/backups.js +0 -142
  303. package/src/core/board.js +0 -148
  304. package/src/core/boards.js +0 -409
  305. package/src/core/bootstrap.js +0 -170
  306. package/src/core/css-bundle.js +0 -30
  307. package/src/core/db.js +0 -315
  308. package/src/core/delete-board.js +0 -163
  309. package/src/core/dispatch.js +0 -1007
  310. package/src/core/events.js +0 -314
  311. package/src/core/http.js +0 -115
  312. package/src/core/migrate.js +0 -198
  313. package/src/core/paths.js +0 -55
  314. package/src/core/provision-repo-board.js +0 -119
  315. package/src/core/queries.js +0 -246
  316. package/src/core/registry.js +0 -211
  317. package/src/core/relations.js +0 -163
  318. package/src/core/review-verdicts.js +0 -138
  319. package/src/core/router.js +0 -556
  320. package/src/core/search.js +0 -183
  321. package/src/core/seed.js +0 -234
  322. package/src/core/snapshots.js +0 -692
  323. package/src/core/states.js +0 -167
  324. package/src/core/system-picker.js +0 -109
  325. package/src/core/tickets.js +0 -400
  326. package/src/core/util.js +0 -106
  327. package/src/core/version-check.js +0 -154
  328. package/src/mcp/orbit-client.js +0 -287
  329. package/src/server.js +0 -115
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Who is asking. Orbit has no accounts: a person in the app is the owner, and
3
+ * an agent says its name (ORBIT_AGENT_NAME, or x-orbit-agent-name over HTTP).
4
+ * A host with accounts passes its own actors instead.
5
+ */
6
+ import type { Actor } from "./model.ts";
7
+ /** The person using the app on their own Orbit. */
8
+ export declare function localOwnerActor(): Actor;
9
+ /** An agent that didn't say its name. */
10
+ export declare function localAgentActor(): Actor;
11
+ /** An agent that says its name. */
12
+ export declare function namedAgentActor(name: unknown): Actor;
13
+ /**
14
+ * The actor a request's headers say: an agent when it says it is one
15
+ * (`x-orbit-actor-type: agent`) or names itself (`x-orbit-agent-name`), else
16
+ * the owner.
17
+ */
18
+ export declare function actorFromHeaders(header: (name: string) => string | undefined): Actor;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Who is asking. Orbit has no accounts: a person in the app is the owner, and
3
+ * an agent says its name (ORBIT_AGENT_NAME, or x-orbit-agent-name over HTTP).
4
+ * A host with accounts passes its own actors instead.
5
+ */
6
+ import { asText } from "./text.js";
7
+ const ALL = ["owner", "read", "write", "claim"];
8
+ /** The person using the app on their own Orbit. */
9
+ export function localOwnerActor() {
10
+ return { id: "local", name: "local", type: "human", role: "owner", permissions: [...ALL], boards: ["*"] };
11
+ }
12
+ /** An agent that didn't say its name. */
13
+ export function localAgentActor() {
14
+ return { id: "local-agent", name: "agent", type: "agent", role: "owner", permissions: [...ALL], boards: ["*"] };
15
+ }
16
+ function cleanField(value, fallback) {
17
+ const clean = asText(value).trim().replace(/\s+/g, " ").slice(0, 80);
18
+ return clean || fallback;
19
+ }
20
+ function idFromName(name) {
21
+ return (name
22
+ .toLowerCase()
23
+ .replace(/[^a-z0-9]+/g, "-")
24
+ .replace(/^-+|-+$/g, "")
25
+ .slice(0, 80) || "agent");
26
+ }
27
+ /** An agent that says its name. */
28
+ export function namedAgentActor(name) {
29
+ const clean = cleanField(name, "agent");
30
+ return { ...localAgentActor(), id: idFromName(clean), name: clean };
31
+ }
32
+ /**
33
+ * The actor a request's headers say: an agent when it says it is one
34
+ * (`x-orbit-actor-type: agent`) or names itself (`x-orbit-agent-name`), else
35
+ * the owner.
36
+ */
37
+ export function actorFromHeaders(header) {
38
+ const type = (header("x-orbit-actor-type") ?? "").trim().toLowerCase();
39
+ const agentName = header("x-orbit-agent-name") ?? "";
40
+ if (type !== "agent" && !agentName.trim())
41
+ return localOwnerActor();
42
+ const name = cleanField(header("x-orbit-actor-name") || agentName, "agent");
43
+ const id = cleanField(header("x-orbit-actor-id"), idFromName(name));
44
+ return { ...localAgentActor(), id, name };
45
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * The agent flow: an agent takes a card (the next AI Ready one, or one it is
3
+ * named), reads it with the board's rules, asks when the goal is unclear, and
4
+ * finishes into Review. Context packs give an agent a card with what is
5
+ * around it, capped to what it can afford to read.
6
+ */
7
+ import { boardManual } from "./boards.ts";
8
+ import type { BoardManual } from "./boards.ts";
9
+ import type { CrossCard } from "./cross-board.ts";
10
+ import type { BoardCtx } from "./context.ts";
11
+ import type { Comment, Id, Label, Ticket } from "./model.ts";
12
+ import type { CompactTicket, LanedTicket, TicketView } from "./views.ts";
13
+ import type { BoardReader } from "../store/types.ts";
14
+ /** The comment Orbit writes on every claim. */
15
+ export declare const CLAIM_NOTE = "Agent claimed this ticket and is starting work.";
16
+ export interface Lookup {
17
+ ticket_id?: unknown;
18
+ number?: unknown;
19
+ title?: unknown;
20
+ }
21
+ /** A card by id, number or exact title (any case), on this board, or null. */
22
+ export declare function lookupTicket(r: BoardReader, lookup: Lookup): Promise<Ticket | null>;
23
+ export interface ReadTicket {
24
+ ticket: {
25
+ id: Id;
26
+ number: number;
27
+ title: string;
28
+ description: string;
29
+ state_name: string;
30
+ type: Ticket["type"];
31
+ priority: Ticket["priority"];
32
+ labels: Label[];
33
+ };
34
+ board_manual: BoardManual;
35
+ }
36
+ /** A card's title, description, lane and labels, with the board's rules and journal. */
37
+ export declare function readTicket(c: BoardCtx, lookup: Lookup): Promise<ReadTicket>;
38
+ /** A card's comments, oldest first, each with whether it is hidden from agents; an agent never gets a hidden one. */
39
+ export declare function readComments(c: BoardCtx, lookup: Lookup): Promise<{
40
+ ticket_id: Id;
41
+ comments: Comment[];
42
+ }>;
43
+ export type ClaimAnswer = {
44
+ claimed: true;
45
+ ticket_id: Id;
46
+ context: ReadTicket;
47
+ } | {
48
+ claimed: false;
49
+ reason: "no_schedulable_ticket";
50
+ } | {
51
+ claimed: false;
52
+ reason: "blocked";
53
+ ticket: CompactTicket;
54
+ blockers: CompactTicket[];
55
+ } | {
56
+ claimed: false;
57
+ reason: "already_done";
58
+ ticket: CompactTicket;
59
+ };
60
+ /**
61
+ * Takes the next card waiting in AI Ready on this board: highest priority,
62
+ * then oldest, skipping epics (unless asked) and blocked cards. Moves it to
63
+ * In Progress with a note.
64
+ */
65
+ export declare function claimNext(c: BoardCtx, input?: {
66
+ type?: unknown;
67
+ include_epics?: unknown;
68
+ }): Promise<ClaimAnswer>;
69
+ /**
70
+ * Takes one named card. A blocked or done card is refused unless `force`. A
71
+ * card already In Progress stays where it is; with `becomeHolder` (the
72
+ * command language's `take`) the caller becomes its holder, noting a takeover.
73
+ */
74
+ export declare function claimTicket(c: BoardCtx, input: Lookup & {
75
+ force?: unknown;
76
+ }, options?: {
77
+ becomeHolder?: boolean;
78
+ }): Promise<ClaimAnswer>;
79
+ /** Pauses for a person: the card goes to Review with the question as a checkpoint comment. */
80
+ export declare function checkpointTicket(c: BoardCtx, input: {
81
+ ticket_id?: unknown;
82
+ message?: unknown;
83
+ }): Promise<ContextPack>;
84
+ export interface CompleteInput {
85
+ ticket_id?: unknown;
86
+ summary?: unknown;
87
+ updates?: unknown;
88
+ pr_url?: unknown;
89
+ next_state?: unknown;
90
+ implementation_artifact_path?: unknown;
91
+ summary_artifact_path?: unknown;
92
+ }
93
+ /** Hands finished work to a person: the card goes to Review (or the lane named) with the summary saved. */
94
+ export declare function completeTicket(c: BoardCtx, input: CompleteInput): Promise<ContextPack>;
95
+ export interface ContextOptions {
96
+ max_chars_per_field?: unknown;
97
+ include_parent_full?: unknown;
98
+ include_related_full?: unknown;
99
+ comment_limit?: unknown;
100
+ }
101
+ export interface ContextPack {
102
+ ticket: TicketView & {
103
+ labels: Label[];
104
+ };
105
+ board: {
106
+ id: Id;
107
+ slug: string;
108
+ name: string;
109
+ default_branch: string;
110
+ };
111
+ parent_ticket: unknown;
112
+ child_tickets: unknown[];
113
+ relations: unknown[];
114
+ blockers: LanedTicket[];
115
+ /** Cards on other boards this card waits on; present only when there are some. */
116
+ waiting_on?: CrossCard[];
117
+ related_tickets: unknown[];
118
+ board_manual?: BoardManual;
119
+ }
120
+ /**
121
+ * A card with what is around it: its board, epic, sub-cards (with their
122
+ * labels and blockers), relations, blockers and related cards. `full` adds
123
+ * the board's rules and journal.
124
+ */
125
+ export declare function contextPack(c: BoardCtx, ticketId: Id, options?: ContextOptions, full?: boolean): Promise<ContextPack>;
126
+ /** A lean packet for handing one card to an agent: capped text, blockers, the lanes it will move through, recent visible comments. */
127
+ export declare function dispatchPacket(c: BoardCtx, ticketId: Id, options?: ContextOptions): Promise<Record<string, unknown>>;
128
+ export { boardManual };
@@ -0,0 +1,323 @@
1
+ /**
2
+ * The agent flow: an agent takes a card (the next AI Ready one, or one it is
3
+ * named), reads it with the board's rules, asks when the goal is unclear, and
4
+ * finishes into Review. Context packs give an agent a card with what is
5
+ * around it, capped to what it can afford to read.
6
+ */
7
+ import { boardManual, manualOf } from "./boards.js";
8
+ import { cardLinkFromInput } from "./card-paths.js";
9
+ import { crossBlockers, crossBlockersFor } from "./cross-board.js";
10
+ import { isAgent } from "./context.js";
11
+ import { OrbitError } from "./errors.js";
12
+ import { record, reindex, touchTicket } from "./history.js";
13
+ import { normalizeTicketType, appendNote, cap, nonNegativeInt, positiveInt, requiredString, asText } from "./text.js";
14
+ import { ticketOn } from "./tickets.js";
15
+ import { Lanes, compactTicket, currentHolder, isHolder, labelsByTicket, laned, relationRows, ticketView, unresolvedBlockers } from "./views.js";
16
+ /** Another board's blocking card, in the short form a claim's answer lists blockers in. */
17
+ function compactCross(card) {
18
+ return { id: card.id, board_id: card.board_id, board_slug: card.board_slug, number: card.number, title: card.title, type: card.type, state_name: card.state_name, state_role: card.state_role, priority: card.priority };
19
+ }
20
+ /** The comment Orbit writes on every claim. */
21
+ export const CLAIM_NOTE = "Agent claimed this ticket and is starting work.";
22
+ /** A card by id, number or exact title (any case), on this board, or null. */
23
+ export async function lookupTicket(r, lookup) {
24
+ if (lookup.ticket_id)
25
+ return r.tickets.get(asText(lookup.ticket_id));
26
+ if (lookup.number !== undefined && lookup.number !== null && lookup.number !== "") {
27
+ const n = Number(lookup.number);
28
+ if (!Number.isInteger(n))
29
+ throw new OrbitError("invalid_number");
30
+ return r.tickets.byNumber(n);
31
+ }
32
+ if (lookup.title)
33
+ return r.tickets.byTitle(asText(lookup.title));
34
+ throw new OrbitError("ticket_id_or_number_or_title_required");
35
+ }
36
+ /** A card's title, description, lane and labels, with the board's rules and journal. */
37
+ export async function readTicket(c, lookup) {
38
+ return c.data.read(async (r) => {
39
+ const ticket = await lookupTicket(r, lookup);
40
+ if (!ticket)
41
+ throw new OrbitError("ticket_not_found");
42
+ const [lanes, labels, board, entries] = await Promise.all([Lanes.of(r), labelsByTicket(r, [ticket.id]), r.board.get(), r.entries.list()]);
43
+ return {
44
+ ticket: {
45
+ id: ticket.id,
46
+ number: ticket.number,
47
+ title: ticket.title,
48
+ description: ticket.description,
49
+ state_name: lanes.nameOf(ticket),
50
+ type: ticket.type,
51
+ priority: ticket.priority,
52
+ labels: labels.get(ticket.id) ?? []
53
+ },
54
+ board_manual: manualOf(c, board, entries)
55
+ };
56
+ });
57
+ }
58
+ /** A card's comments, oldest first, each with whether it is hidden from agents; an agent never gets a hidden one. */
59
+ export async function readComments(c, lookup) {
60
+ return c.data.read(async (r) => {
61
+ const ticket = await lookupTicket(r, lookup);
62
+ if (!ticket)
63
+ throw new OrbitError("ticket_not_found");
64
+ const comments = await r.comments.list(ticket.id);
65
+ return { ticket_id: ticket.id, comments: isAgent(c.actor) ? comments.filter((comment) => !comment.omitted_for_ai) : comments };
66
+ });
67
+ }
68
+ async function inProgressLane(lanes) {
69
+ const lane = lanes.forRole("in_progress", "In Progress");
70
+ if (!lane)
71
+ throw new OrbitError("missing_in_progress_state");
72
+ return Promise.resolve(lane);
73
+ }
74
+ async function noteComment(w, c, ticketId, kind, body, at) {
75
+ await w.comments.insert({ id: c.ids.next(), ticket_id: ticketId, author: c.actor.name, kind, body, omitted_for_ai: 0, created_at: at });
76
+ }
77
+ /**
78
+ * Takes the next card waiting in AI Ready on this board: highest priority,
79
+ * then oldest, skipping epics (unless asked) and blocked cards. Moves it to
80
+ * In Progress with a note.
81
+ */
82
+ export async function claimNext(c, input = {}) {
83
+ const type = input.type ? normalizeTicketType(input.type) : "";
84
+ const includeEpics = Boolean(input.include_epics);
85
+ const claimed = await c.data.write(async (w) => {
86
+ const lanes = await Lanes.of(w);
87
+ const ready = lanes.list.filter((state) => state.role === "ai_ready" || (state.role === null && state.name === "AI Ready"));
88
+ const candidates = [];
89
+ for (const lane of ready)
90
+ candidates.push(...(await w.tickets.list({ state: lane.id, archived: false })));
91
+ candidates.sort((a, b) => b.priority - a.priority || a.created_at.localeCompare(b.created_at) || a.number - b.number);
92
+ // Cards waiting on another board's card: one read per other board, for every candidate at once.
93
+ const elsewhere = await crossBlockers(c, await w.crossBoardRelations.list());
94
+ for (const candidate of candidates) {
95
+ if (type ? candidate.type !== type : !includeEpics && candidate.type === "epic")
96
+ continue;
97
+ if ((await unresolvedBlockers(w, candidate.id, lanes)).length || elsewhere.get(candidate.id)?.length)
98
+ continue;
99
+ const inProgress = await inProgressLane(lanes);
100
+ const at = c.clock.now();
101
+ await w.tickets.update(candidate.id, { state_id: inProgress.id, updated_at: at });
102
+ await noteComment(w, c, candidate.id, "agent_note", CLAIM_NOTE, at);
103
+ await record(w, c, "agent_claimed", candidate.id, { agent_id: c.actor.id, from: lanes.nameOf(candidate), to: inProgress.name });
104
+ await reindex(w, candidate.id);
105
+ await touchTicket(w, candidate.parent_ticket_id, at);
106
+ return candidate.id;
107
+ }
108
+ return null;
109
+ });
110
+ if (!claimed)
111
+ return { claimed: false, reason: "no_schedulable_ticket" };
112
+ c.changed(c.board.id);
113
+ return { claimed: true, ticket_id: claimed, context: await readTicket(c, { ticket_id: claimed }) };
114
+ }
115
+ /**
116
+ * Takes one named card. A blocked or done card is refused unless `force`. A
117
+ * card already In Progress stays where it is; with `becomeHolder` (the
118
+ * command language's `take`) the caller becomes its holder, noting a takeover.
119
+ */
120
+ export async function claimTicket(c, input, options = {}) {
121
+ const force = Boolean(input.force);
122
+ const result = await c.data.write(async (w) => {
123
+ const ticket = await lookupTicket(w, input);
124
+ if (!ticket || ticket.archived_at)
125
+ throw new OrbitError("ticket_not_found");
126
+ const lanes = await Lanes.of(w);
127
+ const view = { ...laned(ticket, lanes), board_slug: c.board.slug };
128
+ const blockers = [...(await unresolvedBlockers(w, ticket.id, lanes)).map(compactTicket), ...(await crossBlockersFor(c, w, ticket.id)).map(compactCross)];
129
+ if (blockers.length && !force)
130
+ return { claimed: false, reason: "blocked", ticket: compactTicket(view), blockers };
131
+ if (lanes.roleOf(ticket) === "done" && !force)
132
+ return { claimed: false, reason: "already_done", ticket: compactTicket(view) };
133
+ const inProgress = await inProgressLane(lanes);
134
+ const at = c.clock.now();
135
+ if (ticket.state_id !== inProgress.id) {
136
+ await w.tickets.update(ticket.id, { state_id: inProgress.id, updated_at: at });
137
+ await noteComment(w, c, ticket.id, "agent_note", CLAIM_NOTE, at);
138
+ await record(w, c, "agent_claimed", ticket.id, { agent_id: c.actor.id, from: lanes.nameOf(ticket), to: inProgress.name });
139
+ await reindex(w, ticket.id);
140
+ await touchTicket(w, ticket.parent_ticket_id, at);
141
+ return { claimed: true, ticket_id: ticket.id, wrote: true };
142
+ }
143
+ if (options.becomeHolder) {
144
+ const holder = await currentHolder(w, ticket.id);
145
+ if (!isHolder(holder, c.actor)) {
146
+ await w.tickets.update(ticket.id, { updated_at: at });
147
+ if (holder)
148
+ await noteComment(w, c, ticket.id, "agent_note", `Took over from @${holder.name}.`, at);
149
+ await record(w, c, "agent_claimed", ticket.id, { agent_id: c.actor.id, from: lanes.nameOf(ticket), to: inProgress.name, ...(holder ? { took_over_from: holder.name } : {}) });
150
+ await reindex(w, ticket.id);
151
+ return { claimed: true, ticket_id: ticket.id, wrote: true };
152
+ }
153
+ }
154
+ return { claimed: true, ticket_id: ticket.id, wrote: false };
155
+ });
156
+ if (!result.claimed)
157
+ return result;
158
+ if ("wrote" in result && result.wrote)
159
+ c.changed(c.board.id);
160
+ return { claimed: true, ticket_id: result.ticket_id, context: await readTicket(c, { ticket_id: result.ticket_id }) };
161
+ }
162
+ /** Pauses for a person: the card goes to Review with the question as a checkpoint comment. */
163
+ export async function checkpointTicket(c, input) {
164
+ const ticketId = requiredString(input.ticket_id, "ticket_id");
165
+ await c.data.write(async (w) => {
166
+ const ticket = await ticketOn(w, ticketId);
167
+ const lanes = await Lanes.of(w);
168
+ const review = lanes.forRole("review", "Review");
169
+ if (!review)
170
+ throw new OrbitError("missing_review_state");
171
+ const message = requiredString(input.message, "message");
172
+ const at = c.clock.now();
173
+ await w.tickets.update(ticket.id, { state_id: review.id, updated_at: at });
174
+ await noteComment(w, c, ticket.id, "checkpoint", message, at);
175
+ await record(w, c, "checkpoint_requested", ticket.id, { from: lanes.nameOf(ticket), state: review.name, to: review.name, actor_type: c.actor.type, actor_id: c.actor.id });
176
+ await reindex(w, ticket.id);
177
+ await touchTicket(w, ticket.parent_ticket_id, at);
178
+ });
179
+ c.changed(c.board.id);
180
+ return contextPack(c, ticketId);
181
+ }
182
+ /** Hands finished work to a person: the card goes to Review (or the lane named) with the summary saved. */
183
+ export async function completeTicket(c, input) {
184
+ const ticketId = requiredString(input.ticket_id, "ticket_id");
185
+ await c.data.write(async (w) => {
186
+ const ticket = await ticketOn(w, ticketId);
187
+ const lanes = await Lanes.of(w);
188
+ const next = input.next_state ? lanes.byName(asText(input.next_state)) : lanes.forRole("review", "Review");
189
+ if (!next)
190
+ throw new OrbitError("missing_next_state");
191
+ const summary = requiredString(input.summary, "summary");
192
+ const prUrl = typeof input.pr_url === "string" ? input.pr_url : "";
193
+ const at = c.clock.now();
194
+ await noteComment(w, c, ticket.id, "completion", [summary, ...(prUrl ? [`PR: ${prUrl}`] : [])].join("\n\n"), at);
195
+ const given = input.implementation_artifact_path || input.summary_artifact_path;
196
+ const artifact = given ? cardLinkFromInput(c, c.board.id, given) : ticket.implementation_artifact_path;
197
+ await w.tickets.update(ticket.id, {
198
+ state_id: next.id,
199
+ implementation_summary: summary,
200
+ implementation_artifact_path: artifact,
201
+ implementation_updates: input.updates ? appendNote(ticket.implementation_updates, input.updates) : ticket.implementation_updates,
202
+ updated_at: at
203
+ });
204
+ await record(w, c, "agent_completed", ticket.id, { from: lanes.nameOf(ticket), next_state: next.name, to: next.name, pr_url: prUrl, actor_type: c.actor.type, actor_id: c.actor.id });
205
+ await reindex(w, ticket.id);
206
+ await touchTicket(w, ticket.parent_ticket_id, at);
207
+ });
208
+ c.changed(c.board.id);
209
+ return contextPack(c, ticketId);
210
+ }
211
+ const LONG_FIELDS = ["description", "ai_plan", "implementation_summary", "implementation_updates"];
212
+ function capFields(ticket, max) {
213
+ if (!max)
214
+ return ticket;
215
+ const copy = { ...ticket };
216
+ for (const field of LONG_FIELDS)
217
+ if (Object.hasOwn(copy, field))
218
+ copy[field] = cap(copy[field], max);
219
+ return copy;
220
+ }
221
+ /**
222
+ * A card with what is around it: its board, epic, sub-cards (with their
223
+ * labels and blockers), relations, blockers and related cards. `full` adds
224
+ * the board's rules and journal.
225
+ */
226
+ export async function contextPack(c, ticketId, options = {}, full = false) {
227
+ const max = positiveInt(options.max_chars_per_field, 0);
228
+ const parentFull = Boolean(options.include_parent_full);
229
+ const relatedFull = Boolean(options.include_related_full);
230
+ const pack = await c.data.read(async (r) => {
231
+ const ticket = await ticketOn(r, ticketId);
232
+ const [lanes, board] = await Promise.all([Lanes.of(r), r.board.get()]);
233
+ const view = (t) => ticketView(t, lanes, c.board);
234
+ const relations = await relationRows(r, ticketId, lanes, c.board);
235
+ const blockers = await unresolvedBlockers(r, ticketId, lanes);
236
+ const parent = ticket.parent_ticket_id ? await r.tickets.get(ticket.parent_ticket_id) : null;
237
+ const children = await r.tickets.list({ parent: ticketId, archived: false });
238
+ const grandchildren = children.length ? await r.tickets.list({ archived: false }) : [];
239
+ const related = new Map();
240
+ for (const t of [...relations.map((row) => row.other_ticket), ...children.map(view), ...(parent ? [view(parent)] : [])])
241
+ if (!related.has(t.id))
242
+ related.set(t.id, t);
243
+ const labels = await labelsByTicket(r, [ticketId, ...children.map((t) => t.id), ...(parent ? [parent.id] : []), ...related.keys()]);
244
+ const labelsOf = (id) => labels.get(id) ?? [];
245
+ const childRows = [];
246
+ for (const child of children) {
247
+ childRows.push({
248
+ ...capFields({ ...view(child), labels: labelsOf(child.id), blockers: await unresolvedBlockers(r, child.id, lanes) }, max),
249
+ child_count: grandchildren.filter((t) => t.parent_ticket_id === child.id).length
250
+ });
251
+ }
252
+ const pack = {
253
+ ticket: capFields({ ...view(ticket), labels: labelsOf(ticketId) }, max),
254
+ board: { id: board.id, slug: board.slug, name: board.name, default_branch: board.default_branch || "main" },
255
+ parent_ticket: parent ? (parentFull ? capFields({ ...view(parent), labels: labelsOf(parent.id) }, max) : compactTicket(view(parent))) : null,
256
+ child_tickets: childRows,
257
+ relations: relations.map((row) => ({ ...row, other_ticket: relatedFull ? capFields({ ...row.other_ticket, labels: labelsOf(row.other_ticket.id) }, max) : compactTicket(row.other_ticket) })),
258
+ blockers,
259
+ related_tickets: [...related.values()].map((t) => (relatedFull ? capFields({ ...t, labels: labelsOf(t.id) }, max) : compactTicket(t)))
260
+ };
261
+ const elsewhere = await crossBlockersFor(c, r, ticketId);
262
+ if (elsewhere.length)
263
+ pack.waiting_on = elsewhere;
264
+ if (full)
265
+ pack.board_manual = manualOf(c, board, await r.entries.list());
266
+ return pack;
267
+ });
268
+ return pack;
269
+ }
270
+ function acceptance(description) {
271
+ const match = /(?:^|\n)\s*(?:acceptance criteria|acceptance)\s*:?\s*\n([\s\S]*)/i.exec(description);
272
+ return match?.[1]?.trim() ?? "";
273
+ }
274
+ /** A lean packet for handing one card to an agent: capped text, blockers, the lanes it will move through, recent visible comments. */
275
+ export async function dispatchPacket(c, ticketId, options = {}) {
276
+ const max = positiveInt(options.max_chars_per_field, 4000);
277
+ const commentLimit = Math.min(nonNegativeInt(options.comment_limit, 5), 20);
278
+ return c.data.read(async (r) => {
279
+ const ticket = await ticketOn(r, ticketId);
280
+ const [lanes, board, labels, comments] = await Promise.all([Lanes.of(r), r.board.get(), labelsByTicket(r, [ticketId]), r.comments.list(ticketId)]);
281
+ const parent = ticket.parent_ticket_id ? await r.tickets.get(ticket.parent_ticket_id) : null;
282
+ const blockers = [...(await unresolvedBlockers(r, ticketId, lanes)).map(compactTicket), ...(await crossBlockersFor(c, r, ticketId)).map(compactCross)];
283
+ const visible = comments.filter((comment) => !comment.omitted_for_ai);
284
+ const recent = (commentLimit ? visible.slice(-commentLimit) : [])
285
+ .map((comment) => ({ id: comment.id, author: comment.author, kind: comment.kind, body: cap(comment.body, max), created_at: comment.created_at }));
286
+ const id = (role, name) => lanes.forRole(role, name)?.id ?? null;
287
+ return {
288
+ board: { id: board.id, slug: board.slug, name: board.name, default_branch: board.default_branch || "main", agent_instructions: cap(board.agent_instructions, max) },
289
+ ticket: {
290
+ id: ticket.id,
291
+ ticket_id: ticket.id,
292
+ number: ticket.number,
293
+ title: ticket.title,
294
+ type: ticket.type,
295
+ priority: ticket.priority,
296
+ state_id: ticket.state_id,
297
+ state_name: lanes.nameOf(ticket),
298
+ state_role: lanes.roleOf(ticket),
299
+ description: cap(ticket.description, max),
300
+ acceptance: cap(acceptance(ticket.description), max),
301
+ ai_plan: cap(ticket.ai_plan, max),
302
+ plan_artifact_path: ticket.plan_artifact_path,
303
+ implementation_summary: cap(ticket.implementation_summary, max),
304
+ implementation_artifact_path: ticket.implementation_artifact_path,
305
+ implementation_updates: cap(ticket.implementation_updates, max),
306
+ labels: (labels.get(ticketId) ?? []).map((label) => label.name)
307
+ },
308
+ blockers: { can_start: blockers.length === 0, blockers },
309
+ parent_ticket: parent ? compactTicket({ ...laned(parent, lanes), board_slug: c.board.slug }) : null,
310
+ relevant_state_ids: {
311
+ current: ticket.state_id,
312
+ current_role: lanes.roleOf(ticket),
313
+ ai_ready: id("ai_ready", "AI Ready"),
314
+ in_progress: id("in_progress", "In Progress"),
315
+ review: id("review", "Review"),
316
+ done: id("done", "Done"),
317
+ todo: lanes.byName("Todo")?.id ?? null
318
+ },
319
+ recent_comments: recent
320
+ };
321
+ });
322
+ }
323
+ export { boardManual };
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Boards: making one with its lanes, its settings, its journal, its context
3
+ * for agents, and deleting it.
4
+ */
5
+ import type { BoardCtx, Ctx } from "./context.ts";
6
+ import type { Board, BoardEntry, Id, RegisteredBoard } from "./model.ts";
7
+ import type { TicketView } from "./views.ts";
8
+ import type { BoardWriter } from "../store/types.ts";
9
+ export interface NewBoardInput {
10
+ name?: unknown;
11
+ slug?: unknown;
12
+ repo_url?: unknown;
13
+ default_branch?: unknown;
14
+ ai_enabled?: unknown;
15
+ /** The lanes to start with, as [name, role] pairs; the usual seven (six without AI) when absent. */
16
+ states?: unknown;
17
+ }
18
+ /** Makes a board with its lanes, and lists it. Answers the board's own record. */
19
+ export declare function createBoard(c: Ctx, input: NewBoardInput): Promise<Board>;
20
+ /**
21
+ * Makes sure the lanes the agent flow needs exist: AI Ready right after Todo
22
+ * (so taking work has a predictable lane), In Progress and Review. Changes
23
+ * only what isn't so.
24
+ */
25
+ export declare function ensureAgentLanes(w: BoardWriter, c: Ctx, boardId: Id): Promise<void>;
26
+ export interface BoardUpdate {
27
+ name?: unknown;
28
+ repo_url?: unknown;
29
+ default_branch?: unknown;
30
+ project_notes?: unknown;
31
+ agent_instructions?: unknown;
32
+ ai_enabled?: unknown;
33
+ }
34
+ /** Changes a board's settings; turning AI on adds the agent lanes. */
35
+ export declare function updateBoard(c: BoardCtx, input: BoardUpdate): Promise<Board>;
36
+ export interface BoardManual {
37
+ board: Omit<Board, "project_notes">;
38
+ entries: BoardEntry[];
39
+ /** Where this Orbit runs, for the person's own app; never in an agent's answer. */
40
+ deployment?: {
41
+ app_root: string;
42
+ data_dir: string;
43
+ data_dir_default: boolean;
44
+ mcp_server_path: string;
45
+ db_path: string;
46
+ };
47
+ }
48
+ /** The board's rules for agents and its journal (newest first; struck entries only when asked). */
49
+ export declare function boardManual(c: BoardCtx, options?: {
50
+ includeStruck?: boolean;
51
+ }): Promise<BoardManual>;
52
+ export declare function manualOf(c: BoardCtx, board: Board, entries: BoardEntry[], options?: {
53
+ includeStruck?: boolean;
54
+ }): BoardManual;
55
+ export interface NewEntryInput {
56
+ type?: unknown;
57
+ title?: unknown;
58
+ body?: unknown;
59
+ ticket_id?: unknown;
60
+ }
61
+ /** A decision or a lesson in the board's journal, optionally about a card. */
62
+ export declare function createEntry(c: BoardCtx, input: NewEntryInput): Promise<BoardEntry>;
63
+ /** Strikes a journal entry, or brings it back. */
64
+ export declare function strikeEntry(c: BoardCtx, entryId: Id, input: {
65
+ struck?: unknown;
66
+ }): Promise<BoardEntry>;
67
+ /**
68
+ * Deletes a struck journal entry for good. Only a person may: an agent strikes
69
+ * an entry, which takes it out of agents' context and leaves the person to
70
+ * decide. An entry still live is refused, so nothing goes in one step.
71
+ */
72
+ export declare function deleteEntry(c: BoardCtx, entryId: Id): Promise<{
73
+ ok: true;
74
+ deleted_id: Id;
75
+ }>;
76
+ /** A board's archived cards, most recently archived first. */
77
+ export declare function archivedTickets(c: BoardCtx): Promise<TicketView[]>;
78
+ export interface BoardListing {
79
+ id: Id;
80
+ slug: string;
81
+ name: string;
82
+ repo_url: string;
83
+ default_branch: string;
84
+ /** Where the board's data is, for the person's own app; never for an agent. */
85
+ db_path?: string;
86
+ last_active_at: string | null;
87
+ }
88
+ /** Every board, by name: a small list without opening any board. */
89
+ export declare function listBoards(c: Ctx): Promise<{
90
+ boards: BoardListing[];
91
+ }>;
92
+ export interface RemovedBoard {
93
+ ok: true;
94
+ deleted_board_id: Id;
95
+ deleted_board_slug: string;
96
+ deleted_files: boolean;
97
+ /** What was deleted, for the person's own app; never for an agent. */
98
+ artifacts?: {
99
+ path: string;
100
+ removed: true;
101
+ }[];
102
+ }
103
+ /**
104
+ * Deletes a board once its slug is typed back. A copy is kept first where the
105
+ * storage can make one. With `delete_files: false` the board leaves the list
106
+ * and its data stays. Never an agent's, as with a replace: refused before
107
+ * anything is read, whatever the transport (the JSON API, or a host's
108
+ * in-process client).
109
+ */
110
+ export declare function deleteBoard(c: Ctx, board: RegisteredBoard, input: {
111
+ confirm_slug?: unknown;
112
+ delete_files?: unknown;
113
+ }): Promise<RemovedBoard>;