toolroll 0.9.54 → 0.9.56

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 (350) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +29 -12
  3. package/dist/action-ledger.d.ts +2 -4
  4. package/dist/action-ledger.js +2 -10
  5. package/dist/agent-onboard.js +4 -1
  6. package/dist/approval-policy.d.ts +5 -5
  7. package/dist/approval-policy.js +4 -4
  8. package/dist/approval-rules-ui.d.ts +3 -2
  9. package/dist/approval-rules-ui.js +17 -22
  10. package/dist/assignment-ui.d.ts +3 -2
  11. package/dist/assignment-ui.js +4 -11
  12. package/dist/backup-ui.d.ts +2 -1
  13. package/dist/backup-ui.js +16 -27
  14. package/dist/brand-mark.d.ts +3 -2
  15. package/dist/brand-mark.js +4 -4
  16. package/dist/browser/workspace.css +1 -1
  17. package/dist/browser/workspace.js +25 -25
  18. package/dist/browser-shell.d.ts +2 -1
  19. package/dist/browser-shell.js +9 -9
  20. package/dist/browser-workspace.d.ts +14 -15
  21. package/dist/browser-workspace.js +0 -3
  22. package/dist/budget-alerts.js +3 -3
  23. package/dist/chat-actions.d.ts +23 -18
  24. package/dist/chat-actions.js +110 -88
  25. package/dist/chat-ask.d.ts +2 -2
  26. package/dist/chat-ask.js +5 -5
  27. package/dist/chat-channel.d.ts +15 -15
  28. package/dist/chat-channel.js +20 -20
  29. package/dist/chat-controls.d.ts +0 -4
  30. package/dist/chat-controls.js +0 -1
  31. package/dist/chat-core.js +1 -1
  32. package/dist/chat-delivery-state.js +2 -2
  33. package/dist/chat-delivery.d.ts +2 -2
  34. package/dist/chat-delivery.js +27 -34
  35. package/dist/chat-flow.js +3 -3
  36. package/dist/chat-messages.d.ts +1 -1
  37. package/dist/chat-messages.js +4 -4
  38. package/dist/chat-migration.d.ts +1 -1
  39. package/dist/chat-migration.js +11 -11
  40. package/dist/chat-polish.d.ts +5 -4
  41. package/dist/chat-polish.js +9 -10
  42. package/dist/chat-rooms.js +3 -3
  43. package/dist/chat-warmth.d.ts +2 -2
  44. package/dist/check-levels-ui.d.ts +2 -1
  45. package/dist/check-levels-ui.js +26 -53
  46. package/dist/cli.d.ts +3 -1
  47. package/dist/cli.js +15 -10
  48. package/dist/coding-shipping-ui.d.ts +2 -1
  49. package/dist/coding-shipping-ui.js +6 -6
  50. package/dist/coding-ui.d.ts +3 -1
  51. package/dist/coding-ui.js +30 -29
  52. package/dist/contracts/chat-actions.d.ts +32 -27
  53. package/dist/contracts/chat-actions.js +29 -11
  54. package/dist/contracts/chat-content.d.ts +1 -1
  55. package/dist/contracts/chat-content.js +1 -1
  56. package/dist/contracts/chat-tables.js +4 -4
  57. package/dist/contracts/cli.d.ts +1 -3
  58. package/dist/contracts/console-api.d.ts +7 -7
  59. package/dist/contracts/console-api.js +13 -12
  60. package/dist/contracts/flow-propose.d.ts +3 -3
  61. package/dist/contracts/flow.d.ts +132 -125
  62. package/dist/contracts/flow.js +27 -12
  63. package/dist/contracts/health.d.ts +1 -3
  64. package/dist/contracts/lead-context.d.ts +1 -1
  65. package/dist/contracts/lead-context.js +2 -2
  66. package/dist/contracts/lead-tools.d.ts +20 -18
  67. package/dist/contracts/lead-tools.js +15 -15
  68. package/dist/contracts/stage-output.d.ts +1 -1
  69. package/dist/contracts/stage-output.js +1 -1
  70. package/dist/contracts/store-json.d.ts +23 -22
  71. package/dist/contracts/store-json.js +17 -17
  72. package/dist/contracts/{teammate-turn.d.ts → subagent-turn.d.ts} +5 -5
  73. package/dist/contracts/{teammate-turn.js → subagent-turn.js} +7 -7
  74. package/dist/control-ui.d.ts +5 -4
  75. package/dist/control-ui.js +9 -34
  76. package/dist/converse.d.ts +23 -23
  77. package/dist/converse.js +28 -28
  78. package/dist/coordinator-proposals.d.ts +2 -2
  79. package/dist/coordinator-proposals.js +1 -1
  80. package/dist/coordinator.js +1 -1
  81. package/dist/credentials-ui.d.ts +5 -4
  82. package/dist/credentials-ui.js +23 -24
  83. package/dist/demo-chat.d.ts +3 -3
  84. package/dist/demo-chat.js +29 -76
  85. package/dist/deprecations.d.ts +12 -0
  86. package/dist/deprecations.js +20 -0
  87. package/dist/desktop-update-gate.js +8 -4
  88. package/dist/desktop-update.js +6 -1
  89. package/dist/discord-chat.js +6 -6
  90. package/dist/discord-settings.d.ts +3 -2
  91. package/dist/discord-settings.js +17 -42
  92. package/dist/dispatch.d.ts +1 -1
  93. package/dist/dispatch.js +1 -1
  94. package/dist/envelope.js +10 -1
  95. package/dist/evidence-pack.d.ts +2 -1
  96. package/dist/evidence-pack.js +45 -49
  97. package/dist/export-ui.d.ts +7 -2
  98. package/dist/export-ui.js +4 -10
  99. package/dist/export.js +7 -7
  100. package/dist/flow-engine.d.ts +2 -2
  101. package/dist/flow-engine.js +21 -21
  102. package/dist/flow-gallery-ui.d.ts +4 -3
  103. package/dist/flow-gallery-ui.js +28 -43
  104. package/dist/flow-insights.d.ts +1 -1
  105. package/dist/flow-share.js +13 -8
  106. package/dist/flow-starters-ui.d.ts +2 -1
  107. package/dist/flow-starters-ui.js +12 -15
  108. package/dist/flow-steps.d.ts +5 -5
  109. package/dist/flow-steps.js +19 -19
  110. package/dist/flow-triggers.d.ts +2 -1
  111. package/dist/flow-triggers.js +17 -15
  112. package/dist/flows-cli.js +1 -1
  113. package/dist/flows-ui.d.ts +4 -3
  114. package/dist/flows-ui.js +38 -49
  115. package/dist/flows.js +21 -20
  116. package/dist/guides.js +112 -6
  117. package/dist/html.d.ts +76 -0
  118. package/dist/html.js +173 -0
  119. package/dist/integrations-ui.d.ts +2 -1
  120. package/dist/integrations-ui.js +19 -23
  121. package/dist/invoke.js +1 -1
  122. package/dist/kits-ui.d.ts +5 -4
  123. package/dist/kits-ui.js +22 -24
  124. package/dist/kits.d.ts +12 -12
  125. package/dist/kits.js +31 -31
  126. package/dist/knowledge-ui.d.ts +6 -5
  127. package/dist/knowledge-ui.js +26 -25
  128. package/dist/{mate-cli.d.ts → lead-cli.d.ts} +11 -11
  129. package/dist/{mate-cli.js → lead-cli.js} +38 -38
  130. package/dist/lead-commitments.js +4 -4
  131. package/dist/lead-context.d.ts +12 -2
  132. package/dist/lead-context.js +15 -17
  133. package/dist/{mate-contract.d.ts → lead-contract.d.ts} +3 -3
  134. package/dist/{mate-contract.js → lead-contract.js} +7 -6
  135. package/dist/{mate-doors.d.ts → lead-doors.d.ts} +6 -6
  136. package/dist/{mate-doors.js → lead-doors.js} +24 -24
  137. package/dist/lead-follow.d.ts +5 -5
  138. package/dist/lead-follow.js +9 -9
  139. package/dist/lead-people.d.ts +5 -5
  140. package/dist/lead-people.js +14 -14
  141. package/dist/{mate-progress.d.ts → lead-progress.d.ts} +9 -9
  142. package/dist/{mate-progress.js → lead-progress.js} +3 -3
  143. package/dist/{mate-tools.d.ts → lead-tools.d.ts} +19 -19
  144. package/dist/{mate-tools.js → lead-tools.js} +109 -101
  145. package/dist/{mate.d.ts → lead.d.ts} +27 -27
  146. package/dist/{mate.js → lead.js} +102 -102
  147. package/dist/ledger-view.d.ts +2 -1
  148. package/dist/ledger-view.js +27 -29
  149. package/dist/limits-ui.d.ts +3 -2
  150. package/dist/limits-ui.js +8 -8
  151. package/dist/live-bus.d.ts +50 -8
  152. package/dist/live-bus.js +133 -50
  153. package/dist/mcp-connect.d.ts +1 -1
  154. package/dist/mcp-connect.js +3 -3
  155. package/dist/mcp-core.js +1 -1
  156. package/dist/mcp-oauth.js +35 -36
  157. package/dist/memory-pass.d.ts +2 -2
  158. package/dist/memory-pass.js +7 -7
  159. package/dist/models-ui.d.ts +2 -1
  160. package/dist/models-ui.js +25 -35
  161. package/dist/monitoring-ui.d.ts +4 -3
  162. package/dist/monitoring-ui.js +13 -26
  163. package/dist/notification-rows.d.ts +2 -2
  164. package/dist/openrouter-models.d.ts +2 -1
  165. package/dist/openrouter-models.js +5 -9
  166. package/dist/operate.d.ts +3 -3
  167. package/dist/operate.js +46 -41
  168. package/dist/people-audit-ui.d.ts +2 -1
  169. package/dist/people-audit-ui.js +13 -13
  170. package/dist/policy-ui.d.ts +3 -2
  171. package/dist/policy-ui.js +17 -23
  172. package/dist/policy.d.ts +1 -1
  173. package/dist/principal.d.ts +1 -1
  174. package/dist/principal.js +2 -2
  175. package/dist/project-delete-ui.d.ts +4 -3
  176. package/dist/project-delete-ui.js +16 -28
  177. package/dist/project-delete.d.ts +1 -1
  178. package/dist/project-delete.js +26 -26
  179. package/dist/project-memory.js +1 -1
  180. package/dist/project-tools.d.ts +1 -1
  181. package/dist/project-tools.js +1 -1
  182. package/dist/pull-request-ui.d.ts +2 -1
  183. package/dist/pull-request-ui.js +17 -32
  184. package/dist/recipe-ui.d.ts +8 -7
  185. package/dist/recipe-ui.js +39 -32
  186. package/dist/release-adapters.d.ts +60 -0
  187. package/dist/release-adapters.js +435 -0
  188. package/dist/release-cli.d.ts +24 -0
  189. package/dist/release-cli.js +181 -0
  190. package/dist/release-gate.d.ts +80 -0
  191. package/dist/release-gate.js +150 -0
  192. package/dist/release-links.d.ts +29 -0
  193. package/dist/release-links.js +102 -0
  194. package/dist/release.d.ts +331 -0
  195. package/dist/release.js +550 -0
  196. package/dist/reply-shape.d.ts +2 -1
  197. package/dist/reply-shape.js +5 -5
  198. package/dist/repository-context-ui.d.ts +2 -1
  199. package/dist/repository-context-ui.js +2 -7
  200. package/dist/request-budget-ui.d.ts +2 -1
  201. package/dist/request-budget-ui.js +16 -16
  202. package/dist/result-review.d.ts +2 -3
  203. package/dist/result-review.js +3 -9
  204. package/dist/retention-ui.d.ts +7 -1
  205. package/dist/retention-ui.js +7 -11
  206. package/dist/retention.js +4 -4
  207. package/dist/scope.d.ts +2 -2
  208. package/dist/scout-report.d.ts +1 -1
  209. package/dist/scout-report.js +1 -1
  210. package/dist/serve.d.ts +11 -2
  211. package/dist/serve.js +105 -99
  212. package/dist/server/chat.d.ts +7 -10
  213. package/dist/server/chat.js +984 -1021
  214. package/dist/server/chrome.d.ts +291 -0
  215. package/dist/server/chrome.js +747 -0
  216. package/dist/server/console.css +2819 -0
  217. package/dist/server/flows.d.ts +2 -6
  218. package/dist/server/flows.js +854 -773
  219. package/dist/server/guards.d.ts +1 -1
  220. package/dist/server/guards.js +8 -5
  221. package/dist/server/handler-context.d.ts +1 -1
  222. package/dist/server/handler-context.js +1 -1
  223. package/dist/server/handler-registry.d.ts +8 -6
  224. package/dist/server/handler-registry.js +14 -2
  225. package/dist/server/http.d.ts +274 -0
  226. package/dist/server/http.js +239 -0
  227. package/dist/server/live.d.ts +34 -0
  228. package/dist/server/live.js +309 -0
  229. package/dist/server/pages.d.ts +2 -6
  230. package/dist/server/pages.js +808 -789
  231. package/dist/server/people-tokens.d.ts +1 -6
  232. package/dist/server/people-tokens.js +581 -573
  233. package/dist/server/remote.d.ts +4 -7
  234. package/dist/server/remote.js +39 -70
  235. package/dist/server/render-chat.d.ts +402 -0
  236. package/dist/server/render-chat.js +927 -0
  237. package/dist/server/render-pages.d.ts +535 -0
  238. package/dist/server/render-pages.js +1277 -0
  239. package/dist/server/render-people.d.ts +32 -0
  240. package/dist/server/render-people.js +121 -0
  241. package/dist/server/render-results.d.ts +744 -0
  242. package/dist/server/render-results.js +2237 -0
  243. package/dist/server/render-settings.d.ts +154 -0
  244. package/dist/server/render-settings.js +580 -0
  245. package/dist/server/render-tasks.d.ts +632 -0
  246. package/dist/server/render-tasks.js +2345 -0
  247. package/dist/server/request-context.d.ts +46 -0
  248. package/dist/server/request-context.js +17 -0
  249. package/dist/server/route-table.d.ts +25 -3
  250. package/dist/server/route-table.js +167 -77
  251. package/dist/server/runtime.d.ts +28 -20
  252. package/dist/server/runtime.js +7 -1
  253. package/dist/server/session.d.ts +138 -0
  254. package/dist/server/session.js +94 -0
  255. package/dist/server/settings.d.ts +1 -5
  256. package/dist/server/settings.js +2186 -2121
  257. package/dist/server/task-views.d.ts +11 -4
  258. package/dist/server/task-views.js +26 -22
  259. package/dist/server/tasks.d.ts +9 -9
  260. package/dist/server/tasks.js +1489 -1420
  261. package/dist/server-telemetry.d.ts +4 -3
  262. package/dist/server-telemetry.js +4 -8
  263. package/dist/setup-guide.d.ts +2 -2
  264. package/dist/skills-ui.d.ts +4 -3
  265. package/dist/skills-ui.js +14 -17
  266. package/dist/skills.d.ts +1 -1
  267. package/dist/skills.js +2 -1
  268. package/dist/slack-chat.d.ts +2 -2
  269. package/dist/slack-chat.js +6 -6
  270. package/dist/slack-settings.d.ts +3 -2
  271. package/dist/slack-settings.js +17 -42
  272. package/dist/spend-ui.d.ts +5 -4
  273. package/dist/spend-ui.js +17 -40
  274. package/dist/spend.d.ts +14 -14
  275. package/dist/spend.js +31 -31
  276. package/dist/sso-ui.d.ts +3 -2
  277. package/dist/sso-ui.js +26 -27
  278. package/dist/storage-ui.d.ts +2 -1
  279. package/dist/storage-ui.js +14 -33
  280. package/dist/store-baseline.d.ts +19 -0
  281. package/dist/store-baseline.js +2873 -0
  282. package/dist/store.d.ts +197 -249
  283. package/dist/store.js +747 -3084
  284. package/dist/subagent-admin.d.ts +53 -0
  285. package/dist/{teammate-admin.js → subagent-admin.js} +37 -37
  286. package/dist/{teammate-desk.d.ts → subagent-desk.d.ts} +22 -23
  287. package/dist/{teammate-desk.js → subagent-desk.js} +26 -35
  288. package/dist/{teammate-memory.d.ts → subagent-memory.d.ts} +17 -17
  289. package/dist/{teammate-memory.js → subagent-memory.js} +41 -41
  290. package/dist/{teammate-question.d.ts → subagent-question.d.ts} +6 -6
  291. package/dist/{teammate-question.js → subagent-question.js} +17 -17
  292. package/dist/{teammate-tools.d.ts → subagent-tools.d.ts} +18 -18
  293. package/dist/{teammate-tools.js → subagent-tools.js} +36 -36
  294. package/dist/{teammate-week.d.ts → subagent-week.d.ts} +10 -10
  295. package/dist/{teammate-week.js → subagent-week.js} +23 -23
  296. package/dist/{teammate-work.d.ts → subagent-work.d.ts} +21 -21
  297. package/dist/{teammate-work.js → subagent-work.js} +50 -50
  298. package/dist/subagents-ui.d.ts +22 -0
  299. package/dist/subagents-ui.js +137 -0
  300. package/dist/{teammates.d.ts → subagents.d.ts} +11 -11
  301. package/dist/{teammates.js → subagents.js} +21 -21
  302. package/dist/subscription-chat.d.ts +13 -13
  303. package/dist/subscription-chat.js +19 -19
  304. package/dist/surface.d.ts +19 -2
  305. package/dist/surface.js +20 -33
  306. package/dist/task-composer.d.ts +2 -1
  307. package/dist/task-composer.js +7 -7
  308. package/dist/task-status.d.ts +4 -3
  309. package/dist/task-status.js +15 -16
  310. package/dist/team-cli.d.ts +1 -0
  311. package/dist/team-cli.js +14 -2
  312. package/dist/team-http.d.ts +5 -9
  313. package/dist/team-http.js +5 -63
  314. package/dist/team-leads.js +13 -13
  315. package/dist/team-runtime.d.ts +2 -2
  316. package/dist/team-runtime.js +8 -8
  317. package/dist/team-ui.d.ts +2 -1
  318. package/dist/team-ui.js +2 -5
  319. package/dist/teams-chat.js +6 -6
  320. package/dist/teams-settings.d.ts +3 -2
  321. package/dist/teams-settings.js +20 -31
  322. package/dist/telegram-flow.d.ts +3 -3
  323. package/dist/telegram-flow.js +3 -3
  324. package/dist/{telegram-mate.d.ts → telegram-lead.d.ts} +5 -5
  325. package/dist/{telegram-mate.js → telegram-lead.js} +27 -27
  326. package/dist/telegram-settings.d.ts +3 -2
  327. package/dist/telegram-settings.js +14 -25
  328. package/dist/telegram-status.js +1 -1
  329. package/dist/telegram-team.d.ts +1 -1
  330. package/dist/telegram-team.js +5 -5
  331. package/dist/telegram.d.ts +1 -1
  332. package/dist/telegram.js +9 -30
  333. package/dist/text-limits.d.ts +18 -18
  334. package/dist/text-limits.js +18 -18
  335. package/dist/toolroll-update-ui.d.ts +3 -2
  336. package/dist/toolroll-update-ui.js +35 -39
  337. package/dist/toolroll-update.d.ts +8 -0
  338. package/dist/toolroll-update.js +45 -8
  339. package/dist/tools-ui.d.ts +5 -2
  340. package/dist/tools-ui.js +31 -31
  341. package/dist/when-html.d.ts +11 -3
  342. package/dist/when-html.js +5 -6
  343. package/dist/workspace-ui.d.ts +4 -2
  344. package/dist/workspace-ui.js +10 -10
  345. package/package.json +1 -2
  346. package/dist/server/shared.d.ts +0 -3151
  347. package/dist/server/shared.js +0 -12001
  348. package/dist/teammate-admin.d.ts +0 -53
  349. package/dist/teammates-ui.d.ts +0 -20
  350. package/dist/teammates-ui.js +0 -166
package/dist/html.js ADDED
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Server HTML, escaped by construction.
3
+ *
4
+ * - `Html` is markup this module made. Nothing else can make one: the class
5
+ * is private and branded, so a string never passes for markup.
6
+ * - `html` is the only way to write markup. Its literal text is the
7
+ * author's; every interpolated value is text and is escaped, unless it is
8
+ * already `Html`. Arrays render in order; null, undefined and booleans
9
+ * render nothing, so `${cond && html`…`}` reads naturally.
10
+ * - `postForm` is the only way to write a POST form. It adds the request's
11
+ * CSRF field itself, so a form cannot forget it.
12
+ * - Page sinks (the shell, `respond` for text/html, the workspace's page
13
+ * snapshot) take `Html` and turn it into bytes with `htmlString`.
14
+ *
15
+ * The script, style and JSON constructors exist for the page shell: their
16
+ * sources are code, and each refuses content that could close its element.
17
+ *
18
+ * This module has no server dependencies (the browser bundle shares time
19
+ * helpers that use it); the server's request context supplies the CSRF token.
20
+ */
21
+ const MADE_HERE = Symbol("html");
22
+ /** Markup this module made. Only this module holds the token its constructor asks for. */
23
+ export class Html {
24
+ #markup;
25
+ constructor(token, markup) {
26
+ if (token !== MADE_HERE)
27
+ throw new TypeError("Html is made by html`…`");
28
+ this.#markup = markup;
29
+ }
30
+ /** The bytes, for a sink. */
31
+ static markupOf(fragment) { return fragment.#markup; }
32
+ // Coercing markup to a string (`${fragment}` in a plain template, `+`, join) would lose the brand and
33
+ // be escaped again downstream. Under test that is an error; a running server keeps serving the markup.
34
+ toString() {
35
+ if (STRICT)
36
+ throw new Error("Html coerced to a string: interpolate it with html`…` or hand it to a sink");
37
+ return this.#markup;
38
+ }
39
+ toJSON() {
40
+ if (STRICT)
41
+ throw new Error("Html serialized as JSON: convert it with htmlString() first");
42
+ return this.#markup;
43
+ }
44
+ }
45
+ // Under test, a coercion is an error to fix; a running server keeps serving the markup.
46
+ const STRICT = typeof process !== "undefined" && process.env["VITEST"] !== undefined;
47
+ const make = (markup) => new Html(MADE_HERE, markup);
48
+ export const isHtml = (value) => value instanceof Html;
49
+ /** The markup of an `Html` value, for a sink that writes bytes. */
50
+ export function htmlString(fragment) {
51
+ if (!(fragment instanceof Html))
52
+ throw new TypeError("htmlString needs Html");
53
+ return Html.markupOf(fragment);
54
+ }
55
+ const ENTITIES = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
56
+ const escapeText = (text) => text.replace(/[&<>"']/g, char => ENTITIES[char]);
57
+ function render(value) {
58
+ if (value === null || value === undefined || typeof value === "boolean")
59
+ return "";
60
+ if (value instanceof Html)
61
+ return Html.markupOf(value);
62
+ if (Array.isArray(value))
63
+ return value.map(render).join("");
64
+ if (typeof value === "string")
65
+ return escapeText(value);
66
+ if (typeof value === "number" || typeof value === "bigint")
67
+ return String(value);
68
+ throw new TypeError(`html: cannot interpolate ${typeof value}`);
69
+ }
70
+ // A template's literal text is checked once per call site: POST forms and CSRF fields come only from postForm.
71
+ const checked = new WeakSet();
72
+ const POST_FORM = /<form\b[^>]*\bmethod\s*=\s*["']?post\b/i;
73
+ const CSRF_FIELD = /\bname\s*=\s*["']?csrf\b/i;
74
+ function checkLiteral(strings) {
75
+ if (checked.has(strings))
76
+ return;
77
+ const text = strings.join("\u0000");
78
+ if (POST_FORM.test(text))
79
+ throw new Error("html: write POST forms with postForm, which adds the CSRF field");
80
+ if (CSRF_FIELD.test(text))
81
+ throw new Error("html: postForm adds the CSRF field; don't write one");
82
+ checked.add(strings);
83
+ }
84
+ /** Markup with every interpolation escaped as text, except values that are already `Html`. */
85
+ export function html(strings, ...values) {
86
+ checkLiteral(strings);
87
+ let out = strings[0];
88
+ for (let index = 0; index < values.length; index++)
89
+ out += render(values[index]) + strings[index + 1];
90
+ return make(out);
91
+ }
92
+ /** Parts in order, with an optional separator between them. */
93
+ export function joinHtml(parts, separator = "") {
94
+ const between = render(separator);
95
+ return make([...parts].map(render).join(between));
96
+ }
97
+ /** Plain text, escaped (for a sink that takes Html when the content is only words). */
98
+ export const textHtml = (text) => make(escapeText(text));
99
+ /** Attributes as `name="value"` pairs: true is a bare attribute; false, null and undefined are left out. */
100
+ export function attributes(values) {
101
+ let out = "";
102
+ for (const [name, value] of Object.entries(values)) {
103
+ if (!/^[a-z][a-z0-9-]*$/.test(name))
104
+ throw new Error(`attributes: bad name ${name}`);
105
+ if (value === false || value === null || value === undefined)
106
+ continue;
107
+ out += value === true ? ` ${name}` : ` ${name}="${escapeText(String(value))}"`;
108
+ }
109
+ return make(out);
110
+ }
111
+ let formToken = () => undefined;
112
+ /** The server's request context supplies the request's CSRF token (server/request-context.ts). */
113
+ export function provideFormToken(source) { formToken = source; }
114
+ /**
115
+ * A POST form: the action, the request's one CSRF field, the optional
116
+ * project revision and return fields, other hidden fields, then the body.
117
+ * Outside a console request there is no CSRF to give, so it refuses.
118
+ */
119
+ export function postForm(action, body, options = {}) {
120
+ let csrf = null;
121
+ if (options.signedOut !== true) {
122
+ const token = formToken();
123
+ if (token === undefined)
124
+ throw new Error(`postForm(${action}) outside a console request`);
125
+ csrf = token;
126
+ }
127
+ const hidden = [];
128
+ if (csrf !== null)
129
+ hidden.push(["csrf", csrf]);
130
+ if (options.projectRevision !== undefined && options.projectRevision !== null)
131
+ hidden.push(["projectRevision", options.projectRevision]);
132
+ if (options.returnTo !== undefined && options.returnTo !== null)
133
+ hidden.push(["return", options.returnTo]);
134
+ for (const [name, value] of Object.entries(options.hidden ?? {})) {
135
+ if (name === "csrf")
136
+ throw new Error("postForm adds the CSRF field itself");
137
+ if (value !== null && value !== undefined)
138
+ hidden.push([name, value]);
139
+ }
140
+ const fields = hidden.map(([name, value]) => `<input type="hidden" name="${escapeText(name)}" value="${escapeText(String(value))}">`).join("");
141
+ return make(`<form method="post" action="${escapeText(action)}"${htmlString(attributes(options.attrs ?? {}))}>${fields}${render(body)}</form>`);
142
+ }
143
+ /**
144
+ * Markup edited after rendering: each match of `pattern` in the page is replaced by markup the replacer builds
145
+ * (with html`…`), or kept as it was (null), so nothing unescaped can enter. For whole-page passes (a sign-in that
146
+ * stands in for passwords, the browser shell's insertions).
147
+ */
148
+ export function replaceMarkup(page, pattern, replace) {
149
+ const groups = new RegExp(`${pattern.source}|`).exec("").length - 1;
150
+ return make(htmlString(page).replace(pattern, (match, ...rest) => {
151
+ const replaced = replace(match, ...rest.slice(0, groups).map(one => typeof one === "string" ? one : ""));
152
+ return replaced === null ? match : htmlString(replaced);
153
+ }));
154
+ }
155
+ // ---- the page shell's own elements ---------------------------------------------------------------------------
156
+ /** An inline script whose source is code. Its text may not close the element or open a comment. */
157
+ export function scriptElement(source, attrs = {}) {
158
+ if (/<\/script|<!--/i.test(source))
159
+ throw new Error("scriptElement: the source would end its element");
160
+ return make(`<script${htmlString(attributes(attrs))}>${source}</script>`);
161
+ }
162
+ /** A style element whose rules are code. */
163
+ export function styleElement(css, attrs = {}) {
164
+ if (/<\/style/i.test(css))
165
+ throw new Error("styleElement: the rules would end their element");
166
+ return make(`<style${htmlString(attributes(attrs))}>${css}</style>`);
167
+ }
168
+ /** Inert JSON for a page script to read: no character of it can end the element. */
169
+ export function jsonScript(value, attrs = {}) {
170
+ // An escaped "<" can neither close the element nor open a comment; the line separators keep it valid script.
171
+ const json = JSON.stringify(value).replace(/[<\u2028\u2029]/g, char => `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`);
172
+ return make(`<script type="application/json"${htmlString(attributes(attrs))}>${json}</script>`);
173
+ }
@@ -4,10 +4,11 @@
4
4
  * failed, what uses it, and one action. The list comes from saved checks; a
5
5
  * render never waits on one (see integrations.ts).
6
6
  */
7
+ import { type Html } from "./html.js";
7
8
  import { type Integration } from "./integrations.js";
8
9
  export declare const INTEGRATIONS_CSS: string;
9
10
  export declare function integrationsHtml(list: readonly Integration[], csrf: string, notice: {
10
11
  said?: string | null;
11
12
  problem?: string | null;
12
13
  checking?: boolean;
13
- }): string;
14
+ }): Html;
@@ -4,10 +4,10 @@
4
4
  * failed, what uses it, and one action. The list comes from saved checks; a
5
5
  * render never waits on one (see integrations.ts).
6
6
  */
7
+ import { html, joinHtml, postForm } from "./html.js";
7
8
  import { STATE_WORDS } from "./integrations.js";
8
9
  import { whenUtc } from "./when-html.js";
9
10
  import { brandMarkHtml } from "./brand-mark.js";
10
- const e = (value) => String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
11
11
  const when = (at) => whenUtc(at);
12
12
  export const INTEGRATIONS_CSS = `.integrations{max-width:760px;min-width:0}.integrations h2{margin:24px 0 6px;font-size:.9375rem}` +
13
13
  `.integrations .integration{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:4px 16px;align-items:start;padding:12px 0;border-top:1px solid var(--so-line);min-width:0}` +
@@ -31,13 +31,12 @@ const GROUPS = [
31
31
  function action(one, csrf) {
32
32
  const a = one.action;
33
33
  if (a.kind === "test") {
34
- return csrf === "" ? "" : `<form method="post" action="/settings/integrations/test" class="integration-action"><input type="hidden" name="csrf" value="${e(csrf)}">` +
35
- `<input type="hidden" name="key" value="${e(one.key)}"><button type="submit">Send test</button></form>`;
34
+ return csrf === "" ? "" : postForm("/settings/integrations/test", html `<button type="submit">Send test</button>`, { attrs: { class: "integration-action" }, hidden: { key: one.key } });
36
35
  }
37
36
  // A command is run where Toolroll runs; a page is linked. Fix and Set up both say which.
38
37
  // Fix is the one filled action on the page; Set up stays quiet so a list of unused services doesn't shout.
39
38
  if (a.href !== null)
40
- return `<p class="integration-action"><a class="button-link${a.kind === "setup" ? " integration-quiet" : ""}" href="${e(a.href)}">${a.label}</a></p>`;
39
+ return html `<p class="integration-action"><a class="button-link${a.kind === "setup" ? " integration-quiet" : ""}" href="${a.href}">${a.label}</a></p>`;
41
40
  return "";
42
41
  }
43
42
  /** Which mark a row shows: email is Gmail's only when the account is a Gmail address; a custom tool is a letter. */
@@ -48,30 +47,27 @@ function markFor(one) {
48
47
  }
49
48
  function row(one, csrf) {
50
49
  const badge = !one.checked && one.state === "connected"
51
- ? `<span class="integration-state integration-state--checking"><i aria-hidden="true"></i>Checking</span>`
52
- : `<span class="integration-state integration-state--${one.state}"><i aria-hidden="true"></i>${STATE_WORDS[one.state]}</span>`;
53
- const facts = [one.account, one.detail].filter(Boolean).map(e).join(" · ");
50
+ ? html `<span class="integration-state integration-state--checking"><i aria-hidden="true"></i>Checking</span>`
51
+ : html `<span class="integration-state integration-state--${one.state}"><i aria-hidden="true"></i>${STATE_WORDS[one.state]}</span>`;
52
+ const facts = [one.account, one.detail].filter(Boolean).join(" · ");
54
53
  const command = one.action.kind !== "test" ? one.action.command : null;
55
- const fix = one.action.kind === "fix" ? `<p class="integration-fix" role="status">${e(one.action.words)}</p>` : "";
56
- const run = command === null || (one.action.kind === "fix" && one.action.words.includes(command)) ? "" : `<p class="meta">Run <code>${e(command)}</code> on the computer running Toolroll.</p>`;
57
- const seen = [one.lastSuccessAt === null ? null : `Last success ${when(one.lastSuccessAt)}`, one.usedBy.length === 0 ? null : `Used by ${e(one.usedBy.join(", "))}`].filter(Boolean);
58
- const history = one.state === "not-set-up" || seen.length === 0 ? "" : `<p class="meta">${seen.join(" · ")}</p>`;
54
+ const fix = one.action.kind === "fix" ? html `<p class="integration-fix" role="status">${one.action.words}</p>` : "";
55
+ const run = command === null || (one.action.kind === "fix" && one.action.words.includes(command)) ? "" : html `<p class="meta">Run <code>${command}</code> on the computer running Toolroll.</p>`;
56
+ const seen = [one.lastSuccessAt === null ? null : html `Last success ${when(one.lastSuccessAt)}`, one.usedBy.length === 0 ? null : html `Used by ${one.usedBy.join(", ")}`].filter(Boolean);
57
+ const history = one.state === "not-set-up" || seen.length === 0 ? "" : html `<p class="meta">${joinHtml(seen, " · ")}</p>`;
59
58
  const lastError = one.lastError === null || one.state === "not-set-up" ? "" : one.state === "broken" && one.action.kind === "fix" && one.action.words === one.lastError
60
- ? `<p class="meta">Failed ${when(one.lastErrorAt ?? "")}</p>`
61
- : `<details><summary>Last error ${one.lastErrorAt === null ? "" : when(one.lastErrorAt)}</summary><p class="meta">${e(one.lastError)}</p></details>`;
62
- return `<div class="integration" data-integration="${e(one.key)}" data-state="${one.state}">` +
63
- `<p class="integration-head">${brandMarkHtml(markFor(one), one.name, one.state === "connected")}<strong>${e(one.name)}</strong> ${badge}</p>` +
64
- action(one, csrf) +
65
- `<div class="integration-body">${facts === "" ? "" : `<p class="meta">${facts}</p>`}${fix}${run}${history}${lastError}</div></div>`;
59
+ ? html `<p class="meta">Failed ${when(one.lastErrorAt ?? "")}</p>`
60
+ : html `<details><summary>Last error ${one.lastErrorAt === null ? "" : when(one.lastErrorAt)}</summary><p class="meta">${one.lastError}</p></details>`;
61
+ return html `<div class="integration" data-integration="${one.key}" data-state="${one.state}"><p class="integration-head">${brandMarkHtml(markFor(one), one.name, one.state === "connected")}<strong>${one.name}</strong> ${badge}</p>${action(one, csrf)}<div class="integration-body">${facts === "" ? "" : html `<p class="meta">${facts}</p>`}${fix}${run}${history}${lastError}</div></div>`;
66
62
  }
67
63
  export function integrationsHtml(list, csrf, notice) {
68
- const note = notice.problem ? `<p class="problem" role="alert">${e(notice.problem)}</p>` : notice.said ? `<p role="status">${e(notice.said)}</p>` : "";
64
+ const note = notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : notice.said ? html `<p role="status">${notice.said}</p>` : "";
69
65
  const broken = list.filter(one => one.state === "broken").length;
70
- const summary = broken > 0 ? `<p><strong>${broken} need${broken === 1 ? "s" : ""} fixing.</strong></p>`
71
- : notice.checking || list.some(one => !one.checked) ? `<p class="meta" role="status">Checking in the background. Refresh in a few seconds.</p>` : "";
66
+ const summary = broken > 0 ? html `<p><strong>${broken} need${broken === 1 ? "s" : ""} fixing.</strong></p>`
67
+ : notice.checking || list.some(one => !one.checked) ? html `<p class="meta" role="status">Checking in the background. Refresh in a few seconds.</p>` : "";
72
68
  const sections = GROUPS.map(([group, title]) => {
73
69
  const rows = list.filter(one => one.group === group);
74
- return rows.length === 0 ? "" : `<h2>${title}</h2>${rows.map(one => row(one, csrf)).join("")}`;
75
- }).join("");
76
- return `<section class="integrations">${note}${summary}${sections}</section>`;
70
+ return rows.length === 0 ? "" : html `<h2>${title}</h2>${rows.map(one => row(one, csrf))}`;
71
+ });
72
+ return html `<section class="integrations">${note}${summary}${sections}</section>`;
77
73
  }
package/dist/invoke.js CHANGED
@@ -353,7 +353,7 @@ export async function invokeAgent(store, runId, spec, invocation, options) {
353
353
  const evidence = spec.provider === "claude" ? claudeBillingFrom(billingSeen)
354
354
  // Codex signed in with ChatGPT is its plan, unless its plan is itself billed by use (seen by the console's probe).
355
355
  : codexLogin === "subscription" ? seenBilling(store.handle, "codex") ?? "subscription" : codexLogin;
356
- // What this computer's Claude does with no key from us (teammates and drafts bill the same way): learnt only from a
356
+ // What this computer's Claude does with no key from us (subagents and drafts bill the same way): learnt only from a
357
357
  // run we gave no key that got as far as an answer.
358
358
  if (spec.provider === "claude" && managedKey === null && authMode === "subscription" && billingSeen.answered && evidence !== null) {
359
359
  store.recordProviderBilling("claude", evidence, new Date());
package/dist/kits-ui.d.ts CHANGED
@@ -5,13 +5,14 @@
5
5
  */
6
6
  import type { Store } from "./store.js";
7
7
  import { type Kit } from "./kits.js";
8
+ import { type Html } from "./html.js";
8
9
  export declare const KITS_CSS: string;
9
10
  /** The gallery: every kit, set up in a project with one button (or opened, when it's already there). */
10
- export declare function kitsGalleryHtml(store: Store, projects: readonly string[], projectName: (repo: string) => string, csrf: string, canSetUp: boolean, notice: {
11
+ export declare function kitsGalleryHtml(store: Store, projects: readonly string[], projectName: (repo: string) => string, canSetUp: boolean, notice: {
11
12
  problem?: string | null;
12
- }): string;
13
+ }): Html;
13
14
  /** A kit's own page: what's set up, and the next step on each line. */
14
- export declare function kitPageHtml(store: Store, kit: Kit, repo: string, dir: string | null, csrf: string, canSetUp: boolean, notice: {
15
+ export declare function kitPageHtml(store: Store, kit: Kit, repo: string, dir: string | null, canSetUp: boolean, notice: {
15
16
  said?: string | null;
16
17
  problem?: string | null;
17
- }): string;
18
+ }): Html;
package/dist/kits-ui.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { kitChecklist, kitInstalled, KITS } from "./kits.js";
2
- import { TEAMMATE_TEMPLATES } from "./teammates.js";
3
- const e = (text) => text.replace(/[&<>"']/g, one => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[one]);
4
- const hidden = (csrf) => `<input type="hidden" name="csrf" value="${e(csrf)}">`;
5
- const CHECK = `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
6
- const RING = `<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>`;
2
+ import { SUBAGENT_TEMPLATES } from "./subagents.js";
3
+ import { html, postForm } from "./html.js";
4
+ const CHECK = html `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
5
+ const RING = html `<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/></svg>`;
7
6
  export const KITS_CSS = `.kits{max-width:960px;min-width:0}.kits-lede{color:var(--so-muted);margin:.25rem 0 1.5rem;max-width:40rem}.kit-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}` +
8
7
  `.kit{display:flex;flex-direction:column;gap:10px;padding:20px;border:1px solid var(--so-line);border-radius:12px;background:var(--so-paper)}.kit h2{font-size:1.1rem;margin:0}.kit p{margin:0}.kit .kit-promise{line-height:1.5}` +
9
8
  `.kit .kit-gets{font-size:.85rem;color:var(--so-muted);line-height:1.5}.kit form{display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end;margin-top:auto;padding-top:6px}.kit form label{display:grid;gap:4px;font-size:.85rem;flex:1 1 140px}` +
@@ -13,34 +12,33 @@ export const KITS_CSS = `.kits{max-width:960px;min-width:0}.kits-lede{color:var(
13
12
  `.kit-steps .kit-step-said{flex:1;min-width:0}.kit-steps li[data-done="true"] .kit-step-said{color:var(--so-muted)}.kit-steps form{margin:0}.kit-steps button,.kit-steps .kit-step-go{min-height:40px;white-space:nowrap}` +
14
13
  `.kit-steps .kit-step-go{display:inline-flex;align-items:center;padding:0 12px;border:1px solid var(--so-line);border-radius:8px;text-decoration:none;font-size:.9rem}` +
15
14
  `@media(max-width:600px){.kit-steps li{flex-wrap:wrap}.kit-steps .kit-step-said{flex-basis:calc(100% - 40px)}.kit form select{font-size:16px}}`;
16
- const teammateWords = (kit) => { const template = TEAMMATE_TEMPLATES.find(one => one.id === kit.teammate.template); return template === undefined ? kit.teammate.handle : template.label.toLowerCase(); };
15
+ const subagentWords = (kit) => { const template = SUBAGENT_TEMPLATES.find(one => one.id === kit.subagent.template); return template === undefined ? kit.subagent.handle : template.label.toLowerCase(); };
17
16
  /** The gallery: every kit, set up in a project with one button (or opened, when it's already there). */
18
- export function kitsGalleryHtml(store, projects, projectName, csrf, canSetUp, notice) {
17
+ export function kitsGalleryHtml(store, projects, projectName, canSetUp, notice) {
19
18
  const cards = KITS.map(kit => {
20
19
  const here = projects.filter(repo => kitInstalled(store, kit, repo) !== null);
21
20
  const open = projects.length === 1 && here.length === 1;
22
- const name = kit.teammate.handle.charAt(0).toUpperCase() + kit.teammate.handle.slice(1);
23
- const gets = `${name} (${teammateWords(kit)}) · the ${kit.flowName} flow${kit.tools.length === 0 ? "" : ` · works with ${kit.tools.map(one => one.label).join(" and ")}`}`;
24
- const action = open ? `<a class="kit-open" href="/kits/${kit.id}?repo=${encodeURIComponent(projects[0])}">Open ${e(kit.name)} →</a>`
21
+ const name = kit.subagent.handle.charAt(0).toUpperCase() + kit.subagent.handle.slice(1);
22
+ const gets = `${name} (${subagentWords(kit)}) · the ${kit.flowName} flow${kit.tools.length === 0 ? "" : ` · works with ${kit.tools.map(one => one.label).join(" and ")}`}`;
23
+ const action = open ? html `<a class="kit-open" href="/kits/${kit.id}?repo=${encodeURIComponent(projects[0])}">Open ${kit.name} →</a>`
25
24
  : !canSetUp || projects.length === 0 ? ""
26
- : `<form method="post" action="/kits/${kit.id}/setup">${hidden(csrf)}${projects.length === 1 ? `<input type="hidden" name="repo" value="${e(projects[0])}">`
27
- : `<label>Project<select name="repo">${projects.map(repo => `<option value="${e(repo)}">${e(projectName(repo))}${here.includes(repo) ? " (set up)" : ""}</option>`).join("")}</select></label>`}<button>Set it up</button></form>`;
28
- return `<article class="kit" data-kit="${kit.id}"><h2>${e(kit.name)}</h2><p class="kit-promise">${e(kit.promise)}</p><p class="kit-gets">${e(gets)}</p>${action}</article>`;
29
- }).join("");
30
- return `<section class="kits">${notice.problem ? `<p class="problem" role="alert">${e(notice.problem)}</p>` : ""}<p class="kits-lede">A working setup in one click: an AI teammate, the flow it works, and what to connect next. Nothing goes out without you approving it.</p><div class="kit-grid">${cards}</div></section>`;
25
+ : postForm(`/kits/${kit.id}/setup`, html `${projects.length === 1 ? ""
26
+ : html `<label>Project<select name="repo">${projects.map(repo => html `<option value="${repo}">${projectName(repo)}${here.includes(repo) ? " (set up)" : ""}</option>`)}</select></label>`}<button>Set it up</button>`, { hidden: { repo: projects.length === 1 ? projects[0] : null } });
27
+ return html `<article class="kit" data-kit="${kit.id}"><h2>${kit.name}</h2><p class="kit-promise">${kit.promise}</p><p class="kit-gets">${gets}</p>${action}</article>`;
28
+ });
29
+ return html `<section class="kits">${notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : ""}<p class="kits-lede">A working setup in one click: a subagent, the flow it works, and what to connect next. Nothing goes out without you approving it.</p><div class="kit-grid">${cards}</div></section>`;
31
30
  }
32
31
  /** A kit's own page: what's set up, and the next step on each line. */
33
- export function kitPageHtml(store, kit, repo, dir, csrf, canSetUp, notice) {
32
+ export function kitPageHtml(store, kit, repo, dir, canSetUp, notice) {
34
33
  const steps = kitChecklist(store, kit, repo, dir);
35
34
  const set = kitInstalled(store, kit, repo);
36
35
  const rows = steps.map(step => {
37
- const go = step.done ? (step.href === null ? "" : `<a class="kit-step-go" href="${e(step.href)}">Open</a>`)
36
+ const go = step.done ? (step.href === null ? "" : html `<a class="kit-step-go" href="${step.href}">Open</a>`)
38
37
  : step.action === "sample" || step.action === "github"
39
- ? (canSetUp ? `<form method="post" action="/kits/${kit.id}/${step.action}">${hidden(csrf)}<input type="hidden" name="repo" value="${e(repo)}"><button${step.action === "sample" ? "" : ' class="secondary"'}>${step.action === "sample" ? "Try it" : "Bring them in"}</button></form>` : "")
40
- : step.href === null ? "" : `<a class="kit-step-go" href="${e(step.href)}">${step.action === "connect" ? "Connect" : "Set up"}</a>`;
41
- return `<li data-step="${e(step.id)}" data-done="${step.done}">${step.done ? CHECK : RING}<span class="kit-step-said">${e(step.said)}</span>${go}</li>`;
42
- }).join("");
43
- const setUp = set === null && canSetUp ? `<form method="post" action="/kits/${kit.id}/setup">${hidden(csrf)}<input type="hidden" name="repo" value="${e(repo)}"><button>Set up ${e(kit.name)}</button></form>` : "";
44
- return `<section class="kits">${notice.problem ? `<p class="problem" role="alert">${e(notice.problem)}</p>` : ""}${notice.said ? `<p class="said" role="status">${e(notice.said)}</p>` : ""}` +
45
- `<p class="kits-lede">${e(kit.promise)}</p>${setUp}${rows === "" ? "" : `<ol class="kit-steps">${rows}</ol>`}<p class="meta"><a href="/kits">All starter kits</a></p></section>`;
38
+ ? (canSetUp ? postForm(`/kits/${kit.id}/${step.action}`, html `<button${step.action === "sample" ? "" : html ` class="secondary"`}>${step.action === "sample" ? "Try it" : "Bring them in"}</button>`, { hidden: { repo } }) : "")
39
+ : step.href === null ? "" : html `<a class="kit-step-go" href="${step.href}">${step.action === "connect" ? "Connect" : "Set up"}</a>`;
40
+ return html `<li data-step="${step.id}" data-done="${String(step.done)}">${step.done ? CHECK : RING}<span class="kit-step-said">${step.said}</span>${go}</li>`;
41
+ });
42
+ const setUp = set === null && canSetUp ? postForm(`/kits/${kit.id}/setup`, html `<button>Set up ${kit.name}</button>`, { hidden: { repo } }) : "";
43
+ return html `<section class="kits">${notice.problem ? html `<p class="problem" role="alert">${notice.problem}</p>` : ""}${notice.said ? html `<p class="said" role="status">${notice.said}</p>` : ""}<p class="kits-lede">${kit.promise}</p>${setUp}${rows.length === 0 ? "" : html `<ol class="kit-steps">${rows}</ol>`}<p class="meta"><a href="/kits">All starter kits</a></p></section>`;
46
44
  }
package/dist/kits.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type FlowStepInput } from "./flows.js";
2
- import type { FlowRow, Store, TeammateRow } from "./store.js";
3
- import { type ToolIo } from "./teammate-tools.js";
4
- /** A tool a kit's teammate works better with: the catalog entry that connects it, and why. */
2
+ import type { FlowRow, Store, SubagentRow } from "./store.js";
3
+ import { type ToolIo } from "./subagent-tools.js";
4
+ /** A tool a kit's subagent works better with: the catalog entry that connects it, and why. */
5
5
  export type KitTool = {
6
6
  tool: string;
7
7
  label: string;
@@ -11,8 +11,8 @@ export type Kit = {
11
11
  id: string;
12
12
  name: string;
13
13
  promise: string;
14
- /** The teammate it hires: a template, and the name it starts with. */
15
- teammate: {
14
+ /** The subagent it hires: a template, and the name it starts with. */
15
+ subagent: {
16
16
  template: string;
17
17
  handle: string;
18
18
  };
@@ -38,23 +38,23 @@ type Done = {
38
38
  ok: true;
39
39
  said: string;
40
40
  flow: number;
41
- teammate: number;
41
+ subagent: number;
42
42
  } | {
43
43
  ok: false;
44
44
  said: string;
45
45
  };
46
- /** A kit set up in a project: its flow (by name) and its teammate, when both are there. */
46
+ /** A kit set up in a project: its flow (by name) and its subagent, when both are there. */
47
47
  export declare function kitInstalled(store: Store, kit: Kit, repo: string): {
48
48
  flow: FlowRow;
49
- mate: TeammateRow;
49
+ mate: SubagentRow;
50
50
  } | null;
51
51
  /**
52
- * Set a kit up: its teammate (kept if one of that name is already there), its
53
- * flow, and its button triggers; then its teammate may use whichever of the
52
+ * Set a kit up: its subagent (kept if one of that name is already there), its
53
+ * flow, and its button triggers; then its subagent may use whichever of the
54
54
  * kit's tools the project already has (reading free, writing asking first).
55
55
  */
56
56
  export declare function setUpKit(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, io?: ToolIo): Promise<Done>;
57
- /** The same, all at once (a chat confirmation can't wait on a tool): its teammate is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
57
+ /** The same, all at once (a chat confirmation can't wait on a tool): its subagent is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
58
58
  export declare function setUpKitNow(store: Store, kit: Kit, repo: string, by: string, now: Date, dir: string | null, grantByName?: boolean): Done;
59
59
  /** One line of a kit's checklist: done, or the next step with where to take it. */
60
60
  export type KitStep = {
@@ -67,7 +67,7 @@ export type KitStep = {
67
67
  };
68
68
  /** What's set up and what's left, read fresh each time. */
69
69
  export declare function kitChecklist(store: Store, kit: Kit, repo: string, dir: string | null): KitStep[];
70
- /** A sample card in the kit's first zone, so its teammate starts on it at once. */
70
+ /** A sample card in the kit's first zone, so its subagent starts on it at once. */
71
71
  export declare function addKitSample(store: Store, kit: Kit, repo: string, by: string, now: Date): {
72
72
  ok: true;
73
73
  href: string;
package/dist/kits.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Starter kits: a working setup in one click — an AI teammate, the flow
2
+ * Starter kits: a working setup in one click — a subagent, the flow
3
3
  * it works, and the triggers that are safe to add straight away — plus a
4
4
  * checklist of what's left (connect email, connect a tool) and a sample card
5
- * that shows the teammate at work within seconds.
5
+ * that shows the subagent at work within seconds.
6
6
  *
7
7
  * A kit adds nothing a person couldn't add by hand, and nothing that acts
8
8
  * outside: replies go out only after you approve them, a code change is filed
@@ -16,17 +16,17 @@ import { googleConnected } from "./google-mail.js";
16
16
  import { readEmailSettings } from "./email-settings.js";
17
17
  import { mailboxReady } from "./mailbox.js";
18
18
  import { projectToolsOf } from "./project-tools.js";
19
- import { createTeammateFrom, nameOf } from "./teammate-admin.js";
20
- import { grantListed, grantTool } from "./teammate-tools.js";
21
- import { TEAMMATE_TEMPLATES } from "./teammates.js";
19
+ import { createSubagentFrom, nameOf } from "./subagent-admin.js";
20
+ import { grantListed, grantTool } from "./subagent-tools.js";
21
+ import { SUBAGENT_TEMPLATES } from "./subagents.js";
22
22
  export const KITS = [
23
23
  {
24
24
  id: "support-desk", name: "Support desk",
25
25
  promise: "Maya answers customer emails, looks orders up and refunds within limits you set. You approve each reply before it goes out.",
26
- teammate: { template: "support", handle: "maya" },
26
+ subagent: { template: "support", handle: "maya" },
27
27
  flowName: "Support desk",
28
28
  steps: [
29
- { id: "answer", title: "Maya answers", kind: "teammate", teammate: "maya",
29
+ { id: "answer", title: "Maya answers", kind: "subagent", subagent: "maya",
30
30
  instructions: "Read the customer's message. Look things up (and refund, within your rules) with your tools when it helps. Write the reply we'd send in \"text\". Pick Reply when it's ready for the customer, or Needs a person when your rules say to ask first or you can't help.",
31
31
  routes: [{ answer: "Reply", goesTo: "You check the reply" }, { answer: "Needs a person", goesTo: "For you" }], ifFails: "For you" },
32
32
  { id: "check", title: "You check the reply", kind: "approval", decider: "owner", next: "Send it", ifFails: "Maya answers" },
@@ -44,10 +44,10 @@ export const KITS = [
44
44
  {
45
45
  id: "bug-triage", name: "Bug triage",
46
46
  promise: "Theo reads every new issue, says what it is and how urgent, answers questions, and turns real bugs into fixes you approve.",
47
- teammate: { template: "triage", handle: "theo" },
47
+ subagent: { template: "triage", handle: "theo" },
48
48
  flowName: "Bug triage",
49
49
  steps: [
50
- { id: "sort", title: "Theo sorts it", kind: "teammate", teammate: "theo",
50
+ { id: "sort", title: "Theo sorts it", kind: "subagent", subagent: "theo",
51
51
  instructions: "Read the report. In \"text\", say in one or two sentences what it is and how urgent (for a bug, add what you'd check first; for a question, write the answer). Pick Bug for something broken in this project's code, Question for someone asking how something works, or Duplicate or noise for anything else, saying why.",
52
52
  routes: [{ answer: "Bug", goesTo: "Fix it" }, { answer: "Question", goesTo: "Answered" }, { answer: "Duplicate or noise", goesTo: "Closed" }], ifFails: "For you" },
53
53
  { id: "fix", title: "Fix it", kind: "task", planning: "auto", instructions: "Fix the bug this card reports. Theo's notes on it: {{stage.sort}}", next: "You review the fix" },
@@ -67,10 +67,10 @@ export const KITS = [
67
67
  {
68
68
  id: "sales-follow-up", name: "Sales follow-up",
69
69
  promise: "Leo writes back to every new lead within minutes, you approve the email, and it waits for their answer, so no lead goes cold.",
70
- teammate: { template: "sales", handle: "leo" },
70
+ subagent: { template: "sales", handle: "leo" },
71
71
  flowName: "Sales follow-up",
72
72
  steps: [
73
- { id: "write", title: "Leo writes back", kind: "teammate", teammate: "leo",
73
+ { id: "write", title: "Leo writes back", kind: "subagent", subagent: "leo",
74
74
  instructions: "Read the lead. In \"text\", write a short, friendly first reply that answers what they asked and suggests one clear next step. Pick Write back, or Not a fit (say why).",
75
75
  routes: [{ answer: "Write back", goesTo: "You check it" }, { answer: "Not a fit", goesTo: "Not a fit" }], ifFails: "Not a fit" },
76
76
  { id: "check", title: "You check it", kind: "approval", decider: "owner", next: "Send it", ifFails: "Leo writes back" },
@@ -87,10 +87,10 @@ export const KITS = [
87
87
  {
88
88
  id: "ops-requests", name: "Ops requests",
89
89
  promise: "Ada approves routine requests under $200 without you and brings you everything else, so the team gets answers the same day.",
90
- teammate: { template: "ops", handle: "ada" },
90
+ subagent: { template: "ops", handle: "ada" },
91
91
  flowName: "Ops requests",
92
92
  steps: [
93
- { id: "decide", title: "Ada decides", kind: "approval", teammate: "ada", decider: "owner", next: "Tell the team", ifFails: "Needs more" },
93
+ { id: "decide", title: "Ada decides", kind: "approval", subagent: "ada", decider: "owner", next: "Tell the team", ifFails: "Needs more" },
94
94
  { id: "tell", title: "Tell the team", kind: "notify", message: "Approved: {{card.title}}", next: "Done" },
95
95
  { id: "more", title: "Needs more", kind: "inbox" },
96
96
  { id: "done", title: "Done", kind: "done" },
@@ -104,22 +104,22 @@ export const KITS = [
104
104
  },
105
105
  ];
106
106
  export const kitOf = (id) => KITS.find(one => one.id === id) ?? null;
107
- /** A kit set up in a project: its flow (by name) and its teammate, when both are there. */
107
+ /** A kit set up in a project: its flow (by name) and its subagent, when both are there. */
108
108
  export function kitInstalled(store, kit, repo) {
109
- const mate = store.teammateByHandle(repo, kit.teammate.handle);
109
+ const mate = store.subagentByHandle(repo, kit.subagent.handle);
110
110
  const flow = store.listFlows([repo]).find(one => one.name === kit.flowName && one.state === "active") ?? null;
111
111
  return mate === null || flow === null ? null : { flow, mate };
112
112
  }
113
113
  /**
114
- * Set a kit up: its teammate (kept if one of that name is already there), its
115
- * flow, and its button triggers; then its teammate may use whichever of the
114
+ * Set a kit up: its subagent (kept if one of that name is already there), its
115
+ * flow, and its button triggers; then its subagent may use whichever of the
116
116
  * kit's tools the project already has (reading free, writing asking first).
117
117
  */
118
118
  export async function setUpKit(store, kit, repo, by, now, dir, io = {}) {
119
119
  const made = setUpKitNow(store, kit, repo, by, now, dir);
120
120
  if (!made.ok || made.said.endsWith("already set up here."))
121
121
  return made;
122
- const mate = store.getTeammate(made.teammate);
122
+ const mate = store.getSubagent(made.subagent);
123
123
  const present = new Set(projectToolsOf(store, repo).map(one => one.name));
124
124
  for (const tool of kit.tools.filter(one => present.has(one.tool))) {
125
125
  try {
@@ -129,20 +129,20 @@ export async function setUpKit(store, kit, repo, by, now, dir, io = {}) {
129
129
  }
130
130
  return made;
131
131
  }
132
- /** The same, all at once (a chat confirmation can't wait on a tool): its teammate is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
132
+ /** The same, all at once (a chat confirmation can't wait on a tool): its subagent is given the kit's connected tools by the names their last test found, and each is listed properly on its first turn. */
133
133
  export function setUpKitNow(store, kit, repo, by, now, dir, grantByName = false) {
134
134
  const had = kitInstalled(store, kit, repo);
135
135
  if (had !== null)
136
- return { ok: true, said: `${kit.name} is already set up here.`, flow: had.flow.id, teammate: had.mate.id };
137
- let mate = store.teammateByHandle(repo, kit.teammate.handle);
136
+ return { ok: true, said: `${kit.name} is already set up here.`, flow: had.flow.id, subagent: had.mate.id };
137
+ let mate = store.subagentByHandle(repo, kit.subagent.handle);
138
138
  if (mate === null) {
139
- const template = TEAMMATE_TEMPLATES.find(one => one.id === kit.teammate.template);
139
+ const template = SUBAGENT_TEMPLATES.find(one => one.id === kit.subagent.template);
140
140
  if (template === undefined)
141
- return { ok: false, said: "That kit's teammate template is missing." };
142
- const made = createTeammateFrom(store, { repo, template: template.id, by }, now);
141
+ return { ok: false, said: "That kit's subagent template is missing." };
142
+ const made = createSubagentFrom(store, { repo, template: template.id, by }, now);
143
143
  if (!made.ok)
144
144
  return { ok: false, said: made.said };
145
- mate = store.getTeammate(made.id);
145
+ mate = store.getSubagent(made.id);
146
146
  }
147
147
  let definition;
148
148
  try {
@@ -158,7 +158,7 @@ export function setUpKitNow(store, kit, repo, by, now, dir, grantByName = false)
158
158
  if (grantByName)
159
159
  for (const tool of projectToolsOf(store, repo).filter(one => kit.tools.some(each => each.tool === one.name)))
160
160
  grantListed(store, mate, tool, null, by, now);
161
- return { ok: true, said: `${kit.name} is ready: ${nameOf(mate)} works its ${kit.flowName} flow.`, flow: flowId, teammate: mate.id };
161
+ return { ok: true, said: `${kit.name} is ready: ${nameOf(mate)} works its ${kit.flowName} flow.`, flow: flowId, subagent: mate.id };
162
162
  }
163
163
  /** What's set up and what's left, read fresh each time. */
164
164
  export function kitChecklist(store, kit, repo, dir) {
@@ -167,7 +167,7 @@ export function kitChecklist(store, kit, repo, dir) {
167
167
  return [];
168
168
  const name = nameOf(set.mate);
169
169
  const steps = [
170
- { id: "teammate", done: true, said: `${name} joined the team`, href: `/teammates/${set.mate.id}` },
170
+ { id: "subagent", done: true, said: `${name} joined the team`, href: `/settings/lead/subagents/${set.mate.id}` },
171
171
  { id: "flow", done: true, said: `The ${kit.flowName} flow is ready`, href: `/flows/${set.flow.id}` },
172
172
  ];
173
173
  if (kit.email === true) {
@@ -184,18 +184,18 @@ export function kitChecklist(store, kit, repo, dir) {
184
184
  const tools = projectToolsOf(store, repo);
185
185
  for (const tool of kit.tools) {
186
186
  const connected = tools.some(one => one.name === tool.tool);
187
- const granted = store.teammateGrant(set.mate.id, tool.tool) !== null;
187
+ const granted = store.subagentGrant(set.mate.id, tool.tool) !== null;
188
188
  steps.push(connected && granted
189
- ? { id: `tool-${tool.tool}`, done: true, said: `${name} can use ${tool.label}`, href: `/teammates/${set.mate.id}#tools` }
189
+ ? { id: `tool-${tool.tool}`, done: true, said: `${name} can use ${tool.label}`, href: `/settings/lead/subagents/${set.mate.id}#tools` }
190
190
  : connected
191
- ? { id: `tool-${tool.tool}`, done: false, said: `Let ${name} use ${tool.label}, ${tool.why}`, href: `/teammates/${set.mate.id}#tools` }
191
+ ? { id: `tool-${tool.tool}`, done: false, said: `Let ${name} use ${tool.label}, ${tool.why}`, href: `/settings/lead/subagents/${set.mate.id}#tools` }
192
192
  : { id: `tool-${tool.tool}`, done: false, said: `Connect ${tool.label}, ${tool.why}`, href: `/settings/tools?repo=${encodeURIComponent(repo)}&kit=${kit.id}&connect=${tool.tool}#connect`, action: "connect", tool: tool.tool });
193
193
  }
194
194
  const tried = store.flowCards(set.flow.id, true).length > 0;
195
195
  steps.push({ id: "sample", done: tried, said: tried ? `${name} has worked a card` : `See ${name} work a sample card`, href: tried ? `/flows/${set.flow.id}` : null, ...(tried ? {} : { action: "sample" }) });
196
196
  return steps;
197
197
  }
198
- /** A sample card in the kit's first zone, so its teammate starts on it at once. */
198
+ /** A sample card in the kit's first zone, so its subagent starts on it at once. */
199
199
  export function addKitSample(store, kit, repo, by, now) {
200
200
  const set = kitInstalled(store, kit, repo);
201
201
  if (set === null)