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
@@ -24,9 +24,9 @@ import { CHAT_CONTROLS, isChatControl } from "./chat-controls.js";
24
24
  import { LIMITS } from "./decision.js";
25
25
  import { CHAT_TASK_ACTIONS, chatTaskRun, chatTaskStamp, isChatTaskAction } from "./chat-task-actions.js";
26
26
  /**
27
- * The mate's tools (mate arc §2): reads over the approver's ceiling and
27
+ * The lead's tools (mate arc §2): reads over the approver's ceiling and
28
28
  * proposals that become rows — never a write. Every result passes through
29
- * `mateView`, a fail-closed choke point (ruling 4; slice-1 review finding
29
+ * `leadView`, a fail-closed choke point (ruling 4; slice-1 review finding
30
30
  * 9): repos use `r1..rN` in the principal's order, and every string
31
31
  * that leaves is scrubbed of path-shaped text, digests, and account names
32
32
  * — titles, questions, and reasons included, because a human typed those
@@ -36,7 +36,7 @@ import { CHAT_TASK_ACTIONS, chatTaskRun, chatTaskStamp, isChatTaskAction } from
36
36
  * projects after scrubbing; full paths and arbitrary text remain redacted.
37
37
  */
38
38
  import { Buffer } from "node:buffer";
39
- import { MATE_ASK_OTHER } from "./store.js";
39
+ import { LEAD_ASK_OTHER } from "./store.js";
40
40
  import { hasDisguisedText, hasForbiddenControls } from "./decision.js";
41
41
  import { readVerifiedArtifact, readVerifiedReport, scanForSecrets } from "./evidence.js";
42
42
  import { parseAcceptanceCriteria } from "./scope.js";
@@ -54,27 +54,27 @@ import { flowDecisionAt } from "./telegram-flow.js";
54
54
  import { flowPersonOf } from "./flow-send.js";
55
55
  import { flowInsights } from "./flow-insights.js";
56
56
  import { describeTrigger, FLOW_TRIGGER_KINDS, triggerConfigOf } from "./flow-triggers.js";
57
- export const MATE_MAX_PROPOSALS_PER_TURN = 5;
57
+ export const LEAD_MAX_PROPOSALS_PER_TURN = 5;
58
58
  export { reportSummaryFor } from "./report-summary.js";
59
59
  import { reportSummaryFor } from "./report-summary.js";
60
- import { labelOf, nameOf, summaryOf, zonesOf } from "./teammate-admin.js";
61
- import { TEAMMATE_TEMPLATES, withSection } from "./teammates.js";
62
- import { callOutcome, callWords, defaultRule, ruleWords } from "./teammate-tools.js";
63
- import { deskOf, routinesOf } from "./teammate-desk.js";
60
+ import { labelOf, nameOf, summaryOf, zonesOf } from "./subagent-admin.js";
61
+ import { SUBAGENT_TEMPLATES, withSection } from "./subagents.js";
62
+ import { callOutcome, callWords, defaultRule, ruleWords } from "./subagent-tools.js";
63
+ import { deskOf, routinesOf } from "./subagent-desk.js";
64
64
  import { KITS } from "./kits.js";
65
65
  import { STARTER_IDS } from "./flow-starters.js";
66
- import { undoFor, weekOf, weekWords } from "./teammate-week.js";
66
+ import { undoFor, weekOf, weekWords } from "./subagent-week.js";
67
67
  import { PERSON_ID, personEntry, teamRoomOf } from "./lead-people.js";
68
68
  import { aboutYouOf, checkAboutYouLine } from "./lead-about.js";
69
69
  const REPO_ID = /^r[0-9]{1,3}$/;
70
70
  const TASK_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
71
71
  const ISO_STAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/;
72
- // ------------------------------------------------------------- mateView
72
+ // ------------------------------------------------------------- leadView
73
73
  /** What a mate-facing string may not carry: an absolute path, a hex digest of 32+ digits, an account name. */
74
74
  const PATH_SHAPED = /(?:^|[\s"'`(<[=:,])(\/(?:[A-Za-z0-9._~-]+\/)*[A-Za-z0-9._~-]+)/g;
75
75
  const HEX_DIGEST = /\b[0-9a-f]{32,}\b/gi;
76
76
  /** Scrub one string. Exact repo paths and their basenames go first (v13: no paths, no basenames), then shapes. */
77
- export function redactForMate(text, view) {
77
+ export function redactForLead(text, view) {
78
78
  let out = text;
79
79
  for (const repo of view.repos) {
80
80
  if (repo === "")
@@ -95,10 +95,10 @@ export function redactForMate(text, view) {
95
95
  return out;
96
96
  }
97
97
  /** The choke point: every string inside a tool result, recursively, scrubbed. */
98
- export function mateView(value, view) {
98
+ export function leadView(value, view) {
99
99
  const walk = (node) => {
100
100
  if (typeof node === "string")
101
- return redactForMate(node, view);
101
+ return redactForLead(node, view);
102
102
  if (Array.isArray(node))
103
103
  return node.map(walk);
104
104
  if (typeof node === "object" && node !== null) {
@@ -111,14 +111,14 @@ export function mateView(value, view) {
111
111
  };
112
112
  return walk(value);
113
113
  }
114
- export function mateViewContextFor(store, who) {
114
+ export function leadViewContextFor(store, who) {
115
115
  return { repos: who.repos, names: store.listApprovers().map(one => one.name) };
116
116
  }
117
117
  /** Project names are deliberate display metadata, not a general basename
118
118
  * exemption. Invalid, sensitive or account-identifying labels stay opaque. */
119
- export function projectLabelForMate(path, index, names) {
119
+ export function projectLabelForLead(path, index, names) {
120
120
  const base = path.replace(/[\\/]+$/, "").split(/[\\/]/).pop() ?? "";
121
- const label = redactForMate(base, { repos: [], names });
121
+ const label = redactForLead(base, { repos: [], names });
122
122
  return honest(label, 80) && /^[\p{L}\p{N}][\p{L}\p{N} ._()-]*$/u.test(label)
123
123
  && !/\b\d{5,}:[A-Za-z0-9_-]{20,}\b/.test(label)
124
124
  ? label : `Project ${index + 1}`;
@@ -182,7 +182,7 @@ export function readAcceptanceArg(value) {
182
182
  return null;
183
183
  return parsed.criteria;
184
184
  }
185
- const tooMany = () => ({ ok: false, message: `this turn already holds ${MATE_MAX_PROPOSALS_PER_TURN} proposals` });
185
+ const tooMany = () => ({ ok: false, message: `this turn already holds ${LEAD_MAX_PROPOSALS_PER_TURN} proposals` });
186
186
  /** How far back a task search reads: enough for a busy month, bounded. */
187
187
  const SEARCH_FAMILIES = 400;
188
188
  /** Words that never name work on their own. */
@@ -198,9 +198,9 @@ function searchHits(words, fields) {
198
198
  }
199
199
  const notFound = () => ({ ok: false, message: "not-found: no such task in your projects" });
200
200
  // ------------------------------------------------- shared queries (§2)
201
- // The one set of queries both the mate and the MCP gateway read. Every
201
+ // The one set of queries both the lead and the MCP gateway read. Every
202
202
  // row names its repo by INDEX into the caller's admitted list; the view
203
- // then labels it — `rN` for the mate, the path for a coordinator whose
203
+ // then labels it — `rN` for the lead, the path for a coordinator whose
204
204
  // allowlist is its authority. Consequences and recommendations are never
205
205
  // read here at all.
206
206
  /** Replace every `repoIndex` with a `repo` label, recursively. */
@@ -361,13 +361,13 @@ export function agentsOver(store, taskId, now) {
361
361
  demands: route.demands.filter(reason => !/review/i.test(reason)),
362
362
  agents: route.legs.filter(leg => leg.phase !== "review").map(leg => ({ role: ROLE_WORD[leg.phase], provider: leg.provider, model: leg.model, chosen: chosenWords(leg), reasons: leg.reasons.map(reason => reason.replace("builder and reviewer", "builder")), problem: leg.problem })),
363
363
  problems: routeProblems(route),
364
- // Only SELECTABLE choices are offered to the mate: a current agent the
364
+ // Only SELECTABLE choices are offered to the lead: a current agent the
365
365
  // configuration no longer names appears under `agents` (what runs
366
366
  // today) and nowhere a proposal could pick it.
367
367
  choices: Object.fromEntries(PHASES.filter(phase => phase !== "review").map(phase => [ROLE_WORD[phase], choices[phase].filter(one => one.selectable).map(one => ({ provider: one.provider, model: one.model, current: one.current }))])),
368
368
  };
369
369
  }
370
- const MATE_TOOL_HANDLERS = {
370
+ const LEAD_TOOL_HANDLERS = {
371
371
  get_brief: {
372
372
  description: 'Read current tasks, decisions, results and project knowledge from the local database. No model or mutation.',
373
373
  handle: (ctx, args) => {
@@ -392,15 +392,15 @@ const MATE_TOOL_HANDLERS = {
392
392
  get_action_status: {
393
393
  description: "Read the saved proposal outcome after confirmation. Opening a link proves no completion.",
394
394
  handle: (ctx, args) => {
395
- const proposal = ctx.store.getMateProposal(Number(args['proposal'])), action = proposal?.kind === 'action' ? sharedActionPayload(proposal.payload) : null;
396
- if (!proposal || !action || ctx.store.getMateThread(proposal.thread)?.approver !== ctx.who.name || !ctx.who.repos.includes(action.repo) || !ctx.store.accountCanAccess(ctx.who.name, action.repo))
395
+ const proposal = ctx.store.getLeadProposal(Number(args['proposal'])), action = proposal?.kind === 'action' ? sharedActionPayload(proposal.payload) : null;
396
+ if (!proposal || !action || ctx.store.getLeadThread(proposal.thread)?.approver !== ctx.who.name || !ctx.who.repos.includes(action.repo) || !ctx.store.accountCanAccess(ctx.who.name, action.repo))
397
397
  return { ok: false, message: 'That action is outside your access.' };
398
398
  return { ok: true, body: { proposal: proposal.id, operation: action.operation, state: proposal.state, outcome: proposal.outcome, finishedAt: proposal.resolvedAt } };
399
399
  },
400
400
  },
401
401
  get_actions: {
402
402
  description: "List shared actions and required inputs. All channels use the same approvals.",
403
- handle: () => ({ ok: true, body: { actions: Object.entries(CHAT_ACTIONS).filter(([operation]) => !operation.startsWith('flow_') && !operation.startsWith('teammate_')).map(([operation, value]) => ({ operation, label: value.label, secureReview: value.protected, inputs: CHAT_ACTION_FIELDS[operation].filter(field => field !== 'nonce' && field !== 'files') })), notice: 'Passwords and credentials never belong in a tool call or conversation. Secure review links complete protected actions.' } }),
403
+ handle: () => ({ ok: true, body: { actions: Object.entries(CHAT_ACTIONS).filter(([operation]) => !operation.startsWith('flow_') && !operation.startsWith('subagent_')).map(([operation, value]) => ({ operation, label: value.label, secureReview: value.protected, inputs: CHAT_ACTION_FIELDS[operation].filter(field => field !== 'nonce' && field !== 'files') })), notice: 'Passwords and credentials never belong in a tool call or conversation. Secure review links complete protected actions.' } }),
404
404
  },
405
405
  propose_action: {
406
406
  description: "Read get_actions and relevant skills/evidence first. Save an exact-state proposal only; protected or long terms require full secure review.",
@@ -410,8 +410,8 @@ const MATE_TOOL_HANDLERS = {
410
410
  return { ok: false, message: 'Choose an action from get_actions.' };
411
411
  if (operation.startsWith('flow_'))
412
412
  return { ok: false, message: 'Use propose_flow for flows.' };
413
- if (operation.startsWith('teammate_'))
414
- return { ok: false, message: 'Use propose_teammate for teammates.' };
413
+ if (operation.startsWith('subagent_'))
414
+ return { ok: false, message: 'Use propose_subagent for subagents.' };
415
415
  const input = { ...args };
416
416
  delete input['operation'];
417
417
  if (operation.startsWith('skill_') || operation.startsWith('knowledge_') || operation.startsWith('tool_')) {
@@ -764,7 +764,7 @@ const MATE_TOOL_HANDLERS = {
764
764
  },
765
765
  },
766
766
  get_flows: {
767
- description: "Read the flows in the operator's projects (each a process drawn as zones that cards move through): their steps in order and their cards — where each card is, what it waits on, whether it needs the operator, its task. flow reads one flow in full. Each card has its owner and latest comments; flow with card reads that card's whole discussion; read it before acting on what teammates said. Zones: Holding, Build and Research (each files an ordinary task), Person decides, Message, Done. Triggers start cards on their own: a button with questions, a schedule, GitHub (new issues, a label being added, new pull requests, failed checks), Linear, another flow's cards reaching a zone, email arriving in the operator's mailbox (optionally only from some senders or with words in the subject; reading mail is set up in Settings → Email), or a plane review (every morning, one card per problem from Toolroll's last 24 hours; a problem that comes back joins its card). A Slack, Discord or Teams channel feeds a flow when a paired approver sends 'flow <the flow's number>' in that channel (never from here); each message there becomes a card, and an 'update' step answers in its thread. Webhook addresses and the Linear key are set on the flow's Triggers panel, never in chat. Two steps run without a model: 'check' runs one of the project's scripts (named scripts in Python, Node or shell that belong to the project, not a flow) with the card as its input; what it prints is passed to later steps, a last line 'goto: <answer>' picks where the card goes, it can run in an empty folder or a copy of the card's work, and it takes the failure path if it fails; a schedule trigger can run a script and make a card of each item it prints. 'update' comments on the GitHub or Linear issue the card came from and can close it, or answers in the chat thread it came from. A Build whose project checks fail takes its failure path too. A 'sort' step has Jev (a fast decision model on the operator's OpenRouter account) read the card and send it where the answer it picks leads, or to its not-sure step, noting scores or yes/no answers too. A 'draft' step has Claude write a reply, summary or note from the card in seconds and sends nothing itself. 'request' calls a web address, 'email' sends mail from the operator's email account (answering the sender of a card that came from email keeps the reply in that thread), 'tool' uses one of the project's tools; their secrets are saved on the canvas or Tools page, never in chat. A 'wait' step after an 'email' step waits for the person to reply (the reply moves the card on and is kept for later steps; with none in time, the card takes its no-reply step), or just waits a set time. A 'pull-request' step opens a pull request for a Build's result and follows its CI (green moves on, red goes back to the build naming the failing check); it merges only when set to, and only after a person approved the card. AI teammates (get_teammates) are agents on the operator's team with a soul file (who they are, how they write, what they know, what they decide on their own, what they ask first, what they never do). They work flow cards within those rules: a teammate named on an approval step decides it (and hands hard ones to that step's person), and a 'teammate' step has one pick where the card goes and write what the next steps send. They never approve code tasks or merges. Each has a memory (what people told it and facts it kept from cards) and a desk (a flow): the operator can message it by name in their chat app ('@maya, …') and its answer goes back to whoever asked; its desk can send a card to a Build zone that files an ordinary task under the usual approvals. get_teammates with a teammate gives its week (what it did, cost, what the operator overrode). For where flows break, read get_flow_insights, and read a failed run's log before explaining it.",
767
+ description: "Read the flows in the operator's projects (each a process drawn as zones that cards move through): their steps in order and their cards — where each card is, what it waits on, whether it needs the operator, its task. flow reads one flow in full. Each card has its owner and latest comments; flow with card reads that card's whole discussion; read it before acting on what subagents said. Zones: Holding, Build and Research (each files an ordinary task), Person decides, Message, Done. Triggers start cards on their own: a button with questions, a schedule, GitHub (new issues, a label being added, new pull requests, failed checks), Linear, another flow's cards reaching a zone, email arriving in the operator's mailbox (optionally only from some senders or with words in the subject; reading mail is set up in Settings → Email), or a plane review (every morning, one card per problem from Toolroll's last 24 hours; a problem that comes back joins its card). A Slack, Discord or Teams channel feeds a flow when a paired approver sends 'flow <the flow's number>' in that channel (never from here); each message there becomes a card, and an 'update' step answers in its thread. Webhook addresses and the Linear key are set on the flow's Triggers panel, never in chat. Two steps run without a model: 'check' runs one of the project's scripts (named scripts in Python, Node or shell that belong to the project, not a flow) with the card as its input; what it prints is passed to later steps, a last line 'goto: <answer>' picks where the card goes, it can run in an empty folder or a copy of the card's work, and it takes the failure path if it fails; a schedule trigger can run a script and make a card of each item it prints. 'update' comments on the GitHub or Linear issue the card came from and can close it, or answers in the chat thread it came from. A Build whose project checks fail takes its failure path too. A 'sort' step has Jev (a fast decision model on the operator's OpenRouter account) read the card and send it where the answer it picks leads, or to its not-sure step, noting scores or yes/no answers too. A 'draft' step has Claude write a reply, summary or note from the card in seconds and sends nothing itself. 'request' calls a web address, 'email' sends mail from the operator's email account (answering the sender of a card that came from email keeps the reply in that thread), 'tool' uses one of the project's tools; their secrets are saved on the canvas or Tools page, never in chat. A 'wait' step after an 'email' step waits for the person to reply (the reply moves the card on and is kept for later steps; with none in time, the card takes its no-reply step), or just waits a set time. A 'pull-request' step opens a pull request for a Build's result and follows its CI (green moves on, red goes back to the build naming the failing check); it merges only when set to, and only after a person approved the card. subagents (get_subagents) are agents on the operator's team with a soul file (who they are, how they write, what they know, what they decide on their own, what they ask first, what they never do). They work flow cards within those rules: a subagent named on an approval step decides it (and hands hard ones to that step's person), and a 'subagent' step has one pick where the card goes and write what the next steps send. They never approve code tasks or merges. Each has a memory (what people told it and facts it kept from cards) and a desk (a flow): the operator asks it through you ('ask Maya to …', or '@maya, …'): propose_subagent ask puts it on its desk once they confirm, and its answer goes back to them; its desk can send a card to a Build zone that files an ordinary task under the usual approvals. get_subagents with a subagent gives its week (what it did, cost, what the operator overrode). For where flows break, read get_flow_insights, and read a failed run's log before explaining it.",
768
768
  handle: (ctx, args) => {
769
769
  const reachable = (repo) => ctx.who.repos.includes(repo) && ctx.store.accountCanAccess(ctx.who.name, repo);
770
770
  const needsYou = (flow, definition, card) => {
@@ -804,8 +804,8 @@ const MATE_TOOL_HANDLERS = {
804
804
  sureAt: Math.round(stage.sort.sureAt * 100), ...(stage.sort.notes.length === 0 ? {} : { alsoNote: stage.sort.notes.map(one => ({ question: one.question, kind: one.kind, ...(one.levels === null ? {} : { levels: one.levels }) })) }) } : {}),
805
805
  ...(stage.wait === undefined ? {} : stage.wait.for === "hours" ? { waitFor: "hours", from: stage.wait.from, until: stage.wait.to } : { waitFor: stage.wait.for, wait: durationWords(stage.wait.minutes) }),
806
806
  ...(stage.merge === undefined ? {} : { merge: stage.merge }),
807
- ...(stage.teammate === undefined ? {} : { teammate: stage.teammate }),
808
- ...(stage.kind === "teammate" && stage.routes !== undefined ? { routes: stage.routes.map(one => ({ answer: one.answer, goesTo: titleOf(one.to) })) } : {}),
807
+ ...(stage.subagent === undefined ? {} : { subagent: stage.subagent }),
808
+ ...(stage.kind === "subagent" && stage.routes !== undefined ? { routes: stage.routes.map(one => ({ answer: one.answer, goesTo: titleOf(one.to) })) } : {}),
809
809
  ...(stage.kind === "choose" ? { options: (stage.options ?? []).map((one, index) => ({ choice: index + 1, label: one.label, goesTo: one.to === "end" ? "end" : titleOf(one.to) })) } : {}),
810
810
  ...(stage.kind === "task" && stage.repo !== undefined ? { repo: repoIdOf(ctx.who, stage.repo) ?? "another project" } : {}),
811
811
  ...(stage.limit === undefined ? {} : { remindAfter: durationWords(stage.limit.minutes), ...(stage.limit.to === null ? {} : { thenMoveTo: titleOf(stage.limit.to) }) }),
@@ -820,11 +820,11 @@ const MATE_TOOL_HANDLERS = {
820
820
  : card.state !== "active" || definition.stages.find(one => one.id === card.stage)?.kind !== "approval" || card.waiting === null ? card.waiting
821
821
  : needsYou(flow, definition, card) ? "Waiting for you to approve or send it back" : "Waiting for someone else to decide",
822
822
  task: card.task ?? card.primaryTask, ...(card.note === null ? {} : { lastNote: card.note.slice(0, 300) }),
823
- // Names never reach the model: people read as you or a teammate.
824
- owner: card.owner === null ? null : card.owner === ctx.who.name ? "you" : "a teammate", following: ctx.store.flowCardWatchers(card.id).includes(ctx.who.name),
823
+ // Names never reach the model: people read as you or a subagent.
824
+ owner: card.owner === null ? null : card.owner === ctx.who.name ? "you" : "another person", following: ctx.store.flowCardWatchers(card.id).includes(ctx.who.name),
825
825
  ...(() => {
826
826
  const said = ctx.store.flowComments(card.id).filter(one => one.kind === "comment");
827
- return said.length === 0 ? {} : { comments: said.length, discussion: said.slice(args["card"] === card.id ? -30 : -3).map(one => ({ by: one.author === ctx.who.name ? "you" : one.author.includes("@") ? "an email reply" : "a teammate", at: one.at, text: one.body.slice(0, args["card"] === card.id ? 2000 : 400) })) };
827
+ return said.length === 0 ? {} : { comments: said.length, discussion: said.slice(args["card"] === card.id ? -30 : -3).map(one => ({ by: one.author === ctx.who.name ? "you" : one.author.includes("@") ? "an email reply" : "another person", at: one.at, text: one.body.slice(0, args["card"] === card.id ? 2000 : 400) })) };
828
828
  })(),
829
829
  })).filter(card => args["card"] === undefined || card.card === args["card"]),
830
830
  scripts: ctx.store.flowScripts(flow.repo).map(script => ({ name: script.name, about: script.about, version: script.version, timeoutMinutes: script.timeoutMinutes, body: script.body.slice(0, 1500) })),
@@ -855,7 +855,7 @@ const MATE_TOOL_HANDLERS = {
855
855
  },
856
856
  },
857
857
  propose_flow: {
858
- description: "Draft a flow change as a card the operator confirms. To make a flow, create with a template or the steps in plain names; leave instructions out unless the operator gave them, and use decider 'me' when they decide. To change one, edit with the whole step list, keeping existing steps by id. For cards use add_card, move_card, approve, send_back with the operator's note, or cancel_card; find the card by what it is about; comment (with @name to ping someone), assign and follow here too. For triage, spam, lead, effort or exception routing, start from the matching template, with the sort as the first step. Use remindAfter and thenMoveTo for follow-ups and decisions that stall. The Issues to PRs template runs GitHub issues labelled toolroll through build, approval, pull request and a comment on the issue. When the operator asks for something to happen every time (fix CI when it fails, turn labelled issues into tasks, queue work overnight, review what went wrong every morning), offer the matching starter; each is one yes, and none merges without a person. When someone new wants a support desk, bug triage, sales follow-up or requests handled, a kit sets up the teammate and its flow in one card. Teammates: propose_teammate adds one (from a template: support, sales, ops, triage), changes one rule section (edit_section), pauses, resumes or removes it, passes it a note, or answers its question for the operator; put it to work with a flow here. When the operator says something like 'Maya can approve refunds up to $100 now', change that section of her soul file; when it's for this week or a one-off, pass it as a note. Teammates use the project's tools under a rule per action (do it, ask first: the operator approves each exact call, never, or up to a limit on a number); propose_teammate use_tool, stop_tool and tool_rule change them, and when a rule has a limit also change the soul file so its words agree. forget or edit_memory when the operator says a teammate has something wrong; a rule change it suggests is a question for the operator, answered only as they say. propose_teammate add_routine puts a card on its desk on a schedule; propose_teammate undo undoes a tool call only when its action has an undo set (tool_rule undoWith). Actions: starter: switch on a starter flow in a project (repo, starter: ci-fix files a fix task when CI fails on the main branch, issue-task makes a task of each GitHub issue labelled toolroll, overnight holds cards added in the day until 22:00 and leaves results for the morning, plane-review reviews the plane's last 24 hours every morning and turns each problem worth fixing into a researched fix and a pull request) — its zones and trigger in one card; offer the matching one when the operator says to do something every time. kit: set up a starter kit in a project (repo, kit: support-desk, bug-triage, sales-follow-up or ops-requests) — a teammate, the flow it works and its buttons, in one card; prefer it when the operator wants a support desk, bug triage, sales follow-up or requests handled. create: a template, or the steps in order (each leads to the next; Done is added; instructions may be left out). 'request' calls a web address (method, url with its host written out, headers — {{secret.NAME}} uses a secret the operator saved on the step, never in chat — and body); 'email' sends mail (to, subject, body; {{card.email}} is the card's email address); 'tool' calls one of the project's tools (server: the tool's name from get_project_tools, tool: its function, args: an object). Put a decision before any of these when they send what a model wrote or what an outsider sent. A 'draft' step has Claude write something from the card (instructions: what to write); follow it with an approval step (decider 'owner' asks the flow's owner in their chat app, where they can approve, edit or send it back), then an 'update' or 'notify' step whose message is '{{stage.<draft id>}}'. A 'sort' step has Jev pick one of its answers; make it the first step (never a holding step before it, or new cards wait unsorted): question, answers (answer, means: a few words Jev reads, goesTo: a step), sureAt (percent, default 80), ifNotSure (a step; otherwise the card waits for a person), and up to 3 alsoNote (score with levels lowest first, or yes-no); a sort has no next, so give each branch's last step its own next. A 'pull-request' step opens a pull request for the card's built result and waits for CI: next when it passes, ifFails when it fails (only this step defaults it: to the build before it, as a revision carrying the failing check); merge ('squash', 'merge' or 'rebase') merges once checks pass, and needs an approval step before it on every path. waitFor 'hours' with from and until (like '22:00' and '06:00') holds cards until the clock is inside those hours. A 'wait' step, after an 'email' step, waits for a reply to that email from someone it went to (waitFor 'reply', the default): next is where a reply goes (the reply is {{stage.<wait id>}}), ifNoReply where the card goes when none comes within wait (like '3 days', up to 30 days); waitFor 'time' just waits, then next. Any step but wait and done can have remindAfter (like '2 days': whoever it waits on is reminded once; 'none' removes it) and, on holding and approval steps, thenMoveTo (a step the card moves to then). AI teammates (get_teammates): an approval step with teammate (its short name) is decided by that teammate within its rules, and it hands hard ones to the step's decider; a 'teammate' step (teammate, instructions, routes of answer and goesTo) has it read the card, pick where it goes and write what the next steps send (the email body is then {{stage.<id>}}), asking the flow's owner when its rules say to. edit: the full step list, keeping existing steps by id — what a kept step leaves out carries over. add_card (starts in the first zone unless zone is named), move_card, approve, send_back (needs a note), cancel_card, comment (note; @name pings that person), assign (owner: a name, 'me', or 'nobody'), follow, unfollow, save_script (repo, and script: name, about, language python|node|shell, and either body (short) or file (a path in the project, like scripts/enrich.py), and timeoutMinutes; scripts belong to the project, so no flow is needed; a 'check' step in any of its flows names it). A 'check' step runs its script with the card as JSON on stdin (and in $FLOW_INPUT); what it prints is its result for later steps ({{stage.<id>}}); runIn 'folder' (an empty folder: for scripts that work on data) or 'copy' (a copy of the card's work, after setup: for tests on code; the default); routes (answer, goesTo) that a last printed line 'goto: <answer>' picks; ifFails is where a failing script sends the card, such as back to the build (it has no default: without it the card waits there); secrets (names of saved secrets it gets as variables). add_trigger with settings (kind button: label, questions; schedule: schedule like 'daily 09:00 Europe/London', and title, or script (a saved script whose printed items — one per line, a title or JSON with title, description, key — each become a card, once) with secrets; github: repo owner/name, watch issues|pulls|checks, label, branch, from team|anyone; linear: team, state, label; flow: follow (another flow's id), when (its zone); email: folder (default INBOX), sender (addresses or domains), subject (words it must contain); plane-review: at (HH:MM, default 07:30), timeZone — every day it reads the plane's last 24 hours and makes one card per problem worth fixing, a returning problem joining its card); pause_trigger, resume_trigger, remove_trigger with trigger. A 'send' step ('Send to me') sends the card's owner what the step before produced (summary, links, screenshots) in their chat apps, then moves on; a 'choose' step sends the same with 2 to 4 options (label, goesTo: a step, or 'end' to ignore the card) as buttons, ifReplied (where a reply goes as {{note}}; the build before by default), and remindAfter with ifNoReply. choose (card, and choice by number or a note to reply) answers one for the operator. A 'task' step may build in another project: repo (from list_repos). Read get_flows first except to create.",
858
+ description: "Draft a flow change as a card the operator confirms. To make a flow, create with a template or the steps in plain names; leave instructions out unless the operator gave them, and use decider 'me' when they decide. To change one, edit with the whole step list, keeping existing steps by id. For cards use add_card, move_card, approve, send_back with the operator's note, or cancel_card; find the card by what it is about; comment (with @name to ping someone), assign and follow here too. For triage, spam, lead, effort or exception routing, start from the matching template, with the sort as the first step. Use remindAfter and thenMoveTo for follow-ups and decisions that stall. The Issues to PRs template runs GitHub issues labelled toolroll through build, approval, pull request and a comment on the issue. When the operator asks for something to happen every time (fix CI when it fails, turn labelled issues into tasks, queue work overnight, review what went wrong every morning), offer the matching starter; each is one yes, and none merges without a person. When someone new wants a support desk, bug triage, sales follow-up or requests handled, a kit sets up the subagent and its flow in one card. Subagents: propose_subagent adds one (from a template: support, sales, ops, triage), changes one rule section (edit_section), pauses, resumes or removes it, passes it a note, or answers its question for the operator; put it to work with a flow here. When the operator says something like 'Maya can approve refunds up to $100 now', change that section of her soul file; when it's for this week or a one-off, pass it as a note. Subagents use the project's tools under a rule per action (do it, ask first: the operator approves each exact call, never, or up to a limit on a number); propose_subagent use_tool, stop_tool and tool_rule change them, and when a rule has a limit also change the soul file so its words agree. forget or edit_memory when the operator says a subagent has something wrong; a rule change it suggests is a question for the operator, answered only as they say. propose_subagent add_routine puts a card on its desk on a schedule; propose_subagent undo undoes a tool call only when its action has an undo set (tool_rule undoWith). Actions: starter: switch on a starter flow in a project (repo, starter: ci-fix files a fix task when CI fails on the main branch, issue-task makes a task of each GitHub issue labelled toolroll, overnight holds cards added in the day until 22:00 and leaves results for the morning, plane-review reviews the plane's last 24 hours every morning and turns each problem worth fixing into a researched fix and a pull request) — its zones and trigger in one card; offer the matching one when the operator says to do something every time. kit: set up a starter kit in a project (repo, kit: support-desk, bug-triage, sales-follow-up or ops-requests) — a subagent, the flow it works and its buttons, in one card; prefer it when the operator wants a support desk, bug triage, sales follow-up or requests handled. create: a template, or the steps in order (each leads to the next; Done is added; instructions may be left out). 'request' calls a web address (method, url with its host written out, headers — {{secret.NAME}} uses a secret the operator saved on the step, never in chat — and body); 'email' sends mail (to, subject, body; {{card.email}} is the card's email address); 'tool' calls one of the project's tools (server: the tool's name from get_project_tools, tool: its function, args: an object). Put a decision before any of these when they send what a model wrote or what an outsider sent. A 'draft' step has Claude write something from the card (instructions: what to write); follow it with an approval step (decider 'owner' asks the flow's owner in their chat app, where they can approve, edit or send it back), then an 'update' or 'notify' step whose message is '{{stage.<draft id>}}'. A 'sort' step has Jev pick one of its answers; make it the first step (never a holding step before it, or new cards wait unsorted): question, answers (answer, means: a few words Jev reads, goesTo: a step), sureAt (percent, default 80), ifNotSure (a step; otherwise the card waits for a person), and up to 3 alsoNote (score with levels lowest first, or yes-no); a sort has no next, so give each branch's last step its own next. A 'pull-request' step opens a pull request for the card's built result and waits for CI: next when it passes, ifFails when it fails (only this step defaults it: to the build before it, as a revision carrying the failing check); merge ('squash', 'merge' or 'rebase') merges once checks pass, and needs an approval step before it on every path. waitFor 'hours' with from and until (like '22:00' and '06:00') holds cards until the clock is inside those hours. A 'wait' step, after an 'email' step, waits for a reply to that email from someone it went to (waitFor 'reply', the default): next is where a reply goes (the reply is {{stage.<wait id>}}), ifNoReply where the card goes when none comes within wait (like '3 days', up to 30 days); waitFor 'time' just waits, then next. Any step but wait and done can have remindAfter (like '2 days': whoever it waits on is reminded once; 'none' removes it) and, on holding and approval steps, thenMoveTo (a step the card moves to then). subagents (get_subagents): an approval step with subagent (its short name) is decided by that subagent within its rules, and it hands hard ones to the step's decider; a 'subagent' step (subagent, instructions, routes of answer and goesTo) has it read the card, pick where it goes and write what the next steps send (the email body is then {{stage.<id>}}), asking the flow's owner when its rules say to. edit: the full step list, keeping existing steps by id — what a kept step leaves out carries over. add_card (starts in the first zone unless zone is named), move_card, approve, send_back (needs a note), cancel_card, comment (note; @name pings that person), assign (owner: a name, 'me', or 'nobody'), follow, unfollow, save_script (repo, and script: name, about, language python|node|shell, and either body (short) or file (a path in the project, like scripts/enrich.py), and timeoutMinutes; scripts belong to the project, so no flow is needed; a 'check' step in any of its flows names it). A 'check' step runs its script with the card as JSON on stdin (and in $FLOW_INPUT); what it prints is its result for later steps ({{stage.<id>}}); runIn 'folder' (an empty folder: for scripts that work on data) or 'copy' (a copy of the card's work, after setup: for tests on code; the default); routes (answer, goesTo) that a last printed line 'goto: <answer>' picks; ifFails is where a failing script sends the card, such as back to the build (it has no default: without it the card waits there); secrets (names of saved secrets it gets as variables). add_trigger with settings (kind button: label, questions; schedule: schedule like 'daily 09:00 Europe/London', and title, or script (a saved script whose printed items — one per line, a title or JSON with title, description, key — each become a card, once) with secrets; github: repo owner/name, watch issues|pulls|checks, label, branch, from team|anyone; linear: team, state, label; flow: follow (another flow's id), when (its zone); email: folder (default INBOX), sender (addresses or domains), subject (words it must contain); plane-review: at (HH:MM, default 07:30), timeZone — every day it reads the plane's last 24 hours and makes one card per problem worth fixing, a returning problem joining its card); pause_trigger, resume_trigger, remove_trigger with trigger. A 'send' step ('Send to me') sends the card's owner what the step before produced (summary, links, screenshots) in their chat apps, then moves on; a 'choose' step sends the same with 2 to 4 options (label, goesTo: a step, or 'end' to ignore the card) as buttons, ifReplied (where a reply goes as {{note}}; the build before by default), and remindAfter with ifNoReply. choose (card, and choice by number or a note to reply) answers one for the operator. A 'task' step may build in another project: repo (from list_repos). Read get_flows first except to create.",
859
859
  // An edit's kept steps may leave their kind and name to the zone they keep: they are filled in before the call is read.
860
860
  prepare: (ctx, given) => {
861
861
  const editing = given["operation"] === "edit" && Number.isSafeInteger(given["flow"]) ? ctx.store.getFlow(Number(given["flow"])) : null;
@@ -892,7 +892,7 @@ const MATE_TOOL_HANDLERS = {
892
892
  break;
893
893
  }
894
894
  case "kit": {
895
- // a starter kit — a teammate, the flow it works, its buttons.
895
+ // a starter kit — a subagent, the flow it works, its buttons.
896
896
  const repo = repoPathOf(ctx.who, args["repo"]);
897
897
  if (repo === null)
898
898
  return { ok: false, message: "Choose a project from list_repos." };
@@ -1006,46 +1006,46 @@ const MATE_TOOL_HANDLERS = {
1006
1006
  }
1007
1007
  },
1008
1008
  },
1009
- get_teammates: {
1010
- description: "The project's AI teammates: who each is (its soul file: role, rules), what it works on, what it did today, and the questions it's waiting on you for. With teammate, one in full with its recent log.",
1009
+ get_subagents: {
1010
+ description: "The project's subagents: who each is (its soul file: role, rules), what it works on, what it did today, and the questions it's waiting on you for. With subagent, one in full with its recent log.",
1011
1011
  handle: (ctx, args) => {
1012
1012
  const repos = args["repo"] === undefined ? ctx.who.repos : [repoPathOf(ctx.who, args["repo"])].filter((one) => one !== null);
1013
1013
  if (repos.length === 0)
1014
1014
  return { ok: false, message: "Choose a project from list_repos." };
1015
1015
  const today = new Date(ctx.now);
1016
1016
  today.setHours(0, 0, 0, 0);
1017
- const whoIs = (name) => name === null ? null : name === ctx.who.name ? "you" : "a teammate";
1018
- const mates = ctx.store.teammates(repos).filter(one => args["teammate"] === undefined || one.id === args["teammate"]);
1019
- if (args["teammate"] !== undefined && mates.length === 0)
1020
- return { ok: false, message: "No such teammate in your projects." };
1017
+ const whoIs = (name) => name === null ? null : name === ctx.who.name ? "you" : "another person";
1018
+ const mates = ctx.store.subagents(repos).filter(one => args["subagent"] === undefined || one.id === args["subagent"]);
1019
+ if (args["subagent"] !== undefined && mates.length === 0)
1020
+ return { ok: false, message: "No such subagent in your projects." };
1021
1021
  return { ok: true, body: {
1022
- teammates: mates.map(mate => ({
1023
- teammate: mate.id, name: nameOf(mate), label: labelOf(mate), handle: mate.handle, project: repoIdOf(ctx.who, mate.repo), working: mate.state === "active",
1024
- reportsTo: whoIs(mate.manager), soul: args["teammate"] === undefined ? mate.soul.slice(0, 600) : mate.soul, version: mate.version,
1022
+ subagents: mates.map(mate => ({
1023
+ subagent: mate.id, name: nameOf(mate), label: labelOf(mate), handle: mate.handle, project: repoIdOf(ctx.who, mate.repo), working: mate.state === "active",
1024
+ reportsTo: whoIs(mate.manager), soul: args["subagent"] === undefined ? mate.soul.slice(0, 600) : mate.soul, version: mate.version,
1025
1025
  worksOn: zonesOf(ctx.store, mate).map(one => ({ flow: one.flow, flowName: one.flowName, zone: one.title, how: one.kind })),
1026
1026
  today: summaryOf(ctx.store, mate, today.toISOString()).said,
1027
- questions: ctx.store.openTeammateQuestions([mate.id]).map(one => ({ question: one.id, card: one.card, asks: one.question, options: one.options.map(option => option.label), askedOf: whoIs(one.askedOf), ...(one.toolCall === null ? {} : { approvesToolCall: true }), ...(one.suggestion === null ? {} : { suggestsRuleChange: true }) })),
1027
+ questions: ctx.store.openSubagentQuestions([mate.id]).map(one => ({ question: one.id, card: one.card, asks: one.question, options: one.options.map(option => option.label), askedOf: whoIs(one.askedOf), ...(one.toolCall === null ? {} : { approvesToolCall: true }), ...(one.suggestion === null ? {} : { suggestsRuleChange: true }) })),
1028
1028
  // v96: where messages to it by name and its routines land, and its routines.
1029
1029
  desk: deskOf(ctx.store, mate)?.id ?? null,
1030
1030
  routines: routinesOf(ctx.store, mate).map(one => ({ routine: one.id, schedule: one.schedule, text: one.text, working: one.state === "active", last: one.lastOutcome })),
1031
1031
  // v95: what it remembers (what people told it, and what it kept), newest first.
1032
- memory: ctx.store.teammateMemories(mate.id, { limit: args["teammate"] === undefined ? 5 : 40 }).map(one => ({ memory: one.id, text: one.text, from: one.source === "person" ? whoIs(one.createdBy) : "itself" })),
1032
+ memory: ctx.store.subagentMemories(mate.id, { limit: args["subagent"] === undefined ? 5 : 40 }).map(one => ({ memory: one.id, text: one.text, from: one.source === "person" ? whoIs(one.createdBy) : "itself" })),
1033
1033
  // v94: the tools it may use, and its rule for each action.
1034
- tools: ctx.store.teammateGrants(mate.id).map(grant => ({ tool: grant.tool, actions: grant.actions.map(action => ({ action: action.name, rule: ruleWords(grant.rules[action.name] ?? defaultRule(action)) })) })),
1035
- ...(args["teammate"] === undefined ? {} : {
1036
- recent: ctx.store.teammateEvents(mate.id, 30).map(one => ({ kind: one.kind, said: one.said, at: one.at })),
1037
- toolCalls: ctx.store.teammateCallsOf(mate.id, 20).map(one => ({ call: one.id, card: one.card, what: callWords(one.tool, one.action, one.input, 200), outcome: callOutcome(one), at: one.createdAt, ...(undoFor(ctx.store, one) === null ? {} : { undoWith: undoFor(ctx.store, one) }) })),
1034
+ tools: ctx.store.subagentGrants(mate.id).map(grant => ({ tool: grant.tool, actions: grant.actions.map(action => ({ action: action.name, rule: ruleWords(grant.rules[action.name] ?? defaultRule(action)) })) })),
1035
+ ...(args["subagent"] === undefined ? {} : {
1036
+ recent: ctx.store.subagentEvents(mate.id, 30).map(one => ({ kind: one.kind, said: one.said, at: one.at })),
1037
+ toolCalls: ctx.store.subagentCallsOf(mate.id, 20).map(one => ({ call: one.id, card: one.card, what: callWords(one.tool, one.action, one.input, 200), outcome: callOutcome(one), at: one.createdAt, ...(undoFor(ctx.store, one) === null ? {} : { undoWith: undoFor(ctx.store, one) }) })),
1038
1038
  // v97: its week, as its manager's report reads.
1039
1039
  week: weekWords(mate, weekOf(ctx.store, mate, ctx.now)),
1040
1040
  }),
1041
1041
  })),
1042
- templates: TEAMMATE_TEMPLATES.map(one => ({ template: one.id, about: one.about })),
1043
- rule: "Change teammates with propose_teammate. They decide flow work within their rules and never approve code tasks or merges.",
1042
+ templates: SUBAGENT_TEMPLATES.map(one => ({ template: one.id, about: one.about })),
1043
+ rule: "Change subagents with propose_subagent. They decide flow work within their rules and never approve code tasks or merges.",
1044
1044
  } };
1045
1045
  },
1046
1046
  },
1047
- propose_teammate: {
1048
- description: "Draft a teammate change as a card the operator confirms. create: a teammate in a project (repo) from a template (support, sales, ops, triage; name renames it) or a whole soul file (markdown: front matter with name and role, then sections like ## Who you are, ## How you write, ## What you know, ## Decide on your own, ## Ask first, ## Never). edit_section: replace one section of its soul file (section: its title, like 'Decide on your own'; text: the new section, as lines; a new title adds the section) — use this for changing its rules; edit_soul: the whole new soul file, when it's short (keep its name). pause, resume, remove. note: something for it to remember (note, one line); forget and edit_memory change what it remembers (memory: its id from get_teammates, text for the new words). add_routine gives it a routine (schedule like 'weekdays 09:00', 'daily 17:00 Europe/London', 'monday 09:00' or 'every 2 hours'; text: what to do each time, one line) — a card on its desk each time, its answer to its manager; stop_routine (routine: its id). answer: answer its open question (question, and choice: one of its options, or text); a tool call waiting for approval is a question too (choice approve or deny, or text to say what to do instead). Tools (get_teammates lists each teammate's tools and their actions; get_project_tools the project's): use_tool lets it use one of the project's tools (tool: the tool's name, like shop) — actions that only read start as do-it and the rest ask first; stop_tool; tool_rule sets one action's rule (tool: the tool's name, like shop; action: one of its actions, like refund_order; use: free, ask or never; with free, limitField and limitOver make it ask first above that number, like limitField amount and limitOver 100; undoWith: another action of the tool that undoes it, like remove_label for add_label, so a person can press Undo on its receipts). undo: undo one of its tool calls (call: its id from get_teammates) with the action its rule names. Put a teammate to work with propose_flow (an approval step's teammate, or a 'teammate' step).",
1047
+ propose_subagent: {
1048
+ description: "Draft a subagent change as a card the operator confirms. ask: delegate to a subagent (subagent, text: what the operator wants, in their words) — whenever they say 'ask Rosa to …' or write to one by name ('@rosa, …'); once they confirm, it lands on its desk, it works it within its own rules and tools (asking them first where its rules say to), and its answer goes back to them. Don't do its work yourself or answer for it. create: a subagent in a project (repo) from a template (support, sales, ops, triage; name renames it) or a whole soul file (markdown: front matter with name and role, then sections like ## Who you are, ## How you write, ## What you know, ## Decide on your own, ## Ask first, ## Never). edit_section: replace one section of its soul file (section: its title, like 'Decide on your own'; text: the new section, as lines; a new title adds the section) — use this for changing its rules; edit_soul: the whole new soul file, when it's short (keep its name). pause, resume, remove. note: something for it to remember (note, one line); forget and edit_memory change what it remembers (memory: its id from get_subagents, text for the new words). add_routine gives it a routine (schedule like 'weekdays 09:00', 'daily 17:00 Europe/London', 'monday 09:00' or 'every 2 hours'; text: what to do each time, one line) — a card on its desk each time, its answer to its manager; stop_routine (routine: its id). answer: answer its open question (question, and choice: one of its options, or text); a tool call waiting for approval is a question too (choice approve or deny, or text to say what to do instead). Tools (get_subagents lists each subagent's tools and their actions; get_project_tools the project's): use_tool lets it use one of the project's tools (tool: the tool's name, like shop) — actions that only read start as do-it and the rest ask first; stop_tool; tool_rule sets one action's rule (tool: the tool's name, like shop; action: one of its actions, like refund_order; use: free, ask or never; with free, limitField and limitOver make it ask first above that number, like limitField amount and limitOver 100; undoWith: another action of the tool that undoes it, like remove_label for add_label, so a person can press Undo on its receipts). undo: undo one of its tool calls (call: its id from get_subagents) with the action its rule names. Put a subagent to work with propose_flow (an approval step's subagent, or a 'subagent' step).",
1049
1049
  handle: (ctx, args) => {
1050
1050
  const given = args;
1051
1051
  const pick = (keys) => Object.fromEntries(keys.filter(key => given[key] !== undefined).map(key => [key, given[key]]));
@@ -1056,44 +1056,52 @@ const MATE_TOOL_HANDLERS = {
1056
1056
  const repo = repoPathOf(ctx.who, args["repo"]);
1057
1057
  if (repo === null)
1058
1058
  return { ok: false, message: "Choose a project from list_repos." };
1059
- operation = "teammate_create";
1059
+ operation = "subagent_create";
1060
1060
  input = { repo, ...pick(["template", "name", "soul"]) };
1061
1061
  break;
1062
1062
  }
1063
1063
  case "edit_soul":
1064
- operation = "teammate_soul";
1065
- input = pick(["teammate", "soul"]);
1064
+ operation = "subagent_soul";
1065
+ input = pick(["subagent", "soul"]);
1066
1066
  break;
1067
1067
  case "edit_section": {
1068
- const mate = Number.isSafeInteger(args["teammate"]) ? ctx.store.getTeammate(Number(args["teammate"])) : null;
1068
+ const mate = Number.isSafeInteger(args["subagent"]) ? ctx.store.getSubagent(Number(args["subagent"])) : null;
1069
1069
  if (mate === null || !ctx.who.repos.includes(mate.repo))
1070
- return { ok: false, message: "Choose a teammate from get_teammates." };
1070
+ return { ok: false, message: "Choose a subagent from get_subagents." };
1071
1071
  if (typeof args["section"] !== "string" || args["section"].trim() === "" || typeof args["text"] !== "string")
1072
1072
  return { ok: false, message: "Give the section's title and its new text." };
1073
- operation = "teammate_soul";
1074
- input = { teammate: mate.id, soul: withSection(mate.soul, args["section"], args["text"]) };
1073
+ operation = "subagent_soul";
1074
+ input = { subagent: mate.id, soul: withSection(mate.soul, args["section"], args["text"]) };
1075
1075
  break;
1076
1076
  }
1077
1077
  case "pause":
1078
1078
  case "resume":
1079
1079
  case "remove":
1080
- operation = "teammate_state";
1081
- input = { ...pick(["teammate"]), state: args["operation"] === "pause" ? "paused" : args["operation"] === "resume" ? "active" : "removed" };
1080
+ operation = "subagent_state";
1081
+ input = { ...pick(["subagent"]), state: args["operation"] === "pause" ? "paused" : args["operation"] === "resume" ? "active" : "removed" };
1082
1082
  break;
1083
1083
  case "note":
1084
- operation = "teammate_note";
1085
- input = pick(["teammate", "note"]);
1084
+ operation = "subagent_note";
1085
+ input = pick(["subagent", "note"]);
1086
1086
  break;
1087
+ case "ask": {
1088
+ const mate = Number.isSafeInteger(args["subagent"]) ? ctx.store.getSubagent(Number(args["subagent"])) : null;
1089
+ if (mate === null || !ctx.who.repos.includes(mate.repo))
1090
+ return { ok: false, message: "Choose a subagent from get_subagents." };
1091
+ operation = "subagent_ask";
1092
+ input = pick(["subagent", "text"]);
1093
+ break;
1094
+ }
1087
1095
  case "answer":
1088
- operation = "teammate_answer";
1096
+ operation = "subagent_answer";
1089
1097
  input = pick(["question", "choice", "text"]);
1090
1098
  break;
1091
1099
  case "use_tool":
1092
1100
  case "stop_tool":
1093
1101
  case "tool_rule": {
1094
- const mate = Number.isSafeInteger(args["teammate"]) ? ctx.store.getTeammate(Number(args["teammate"])) : null;
1102
+ const mate = Number.isSafeInteger(args["subagent"]) ? ctx.store.getSubagent(Number(args["subagent"])) : null;
1095
1103
  if (mate === null || !ctx.who.repos.includes(mate.repo))
1096
- return { ok: false, message: "Choose a teammate from get_teammates." };
1104
+ return { ok: false, message: "Choose a subagent from get_subagents." };
1097
1105
  // A tool's name and one of its actions: "shop" and "refund_order", also taken as "shop.refund_order", or the action alone when one tool has it.
1098
1106
  let tool = typeof args["tool"] === "string" ? args["tool"].trim() : "", action = typeof args["action"] === "string" ? args["action"].trim() : "";
1099
1107
  const dot = tool.search(/\.|→/);
@@ -1103,7 +1111,7 @@ const MATE_TOOL_HANDLERS = {
1103
1111
  }
1104
1112
  if (action.includes("."))
1105
1113
  action = action.slice(action.lastIndexOf(".") + 1);
1106
- const grants = ctx.store.teammateGrants(mate.id);
1114
+ const grants = ctx.store.subagentGrants(mate.id);
1107
1115
  if (args["operation"] !== "use_tool" && !grants.some(one => one.tool === tool)) {
1108
1116
  const owners = grants.filter(one => one.actions.some(each => each.name === tool));
1109
1117
  if (owners.length === 1 && (action === "" || action === tool)) {
@@ -1111,32 +1119,32 @@ const MATE_TOOL_HANDLERS = {
1111
1119
  tool = owners[0].tool;
1112
1120
  }
1113
1121
  }
1114
- operation = "teammate_tools";
1115
- input = { ...pick(["teammate", "use", "limitField", "limitOver", "undoWith"]), tool, ...(action === "" ? {} : { action }), change: args["operation"] === "use_tool" ? "grant" : args["operation"] === "stop_tool" ? "revoke" : "rule" };
1122
+ operation = "subagent_tools";
1123
+ input = { ...pick(["subagent", "use", "limitField", "limitOver", "undoWith"]), tool, ...(action === "" ? {} : { action }), change: args["operation"] === "use_tool" ? "grant" : args["operation"] === "stop_tool" ? "revoke" : "rule" };
1116
1124
  break;
1117
1125
  }
1118
1126
  case "forget":
1119
1127
  case "edit_memory":
1120
- operation = "teammate_memory";
1121
- input = { ...pick(["teammate", "memory", "text"]), change: args["operation"] === "forget" ? "forget" : "edit" };
1128
+ operation = "subagent_memory";
1129
+ input = { ...pick(["subagent", "memory", "text"]), change: args["operation"] === "forget" ? "forget" : "edit" };
1122
1130
  break;
1123
1131
  case "undo":
1124
- operation = "teammate_undo";
1125
- input = pick(["teammate", "call"]);
1132
+ operation = "subagent_undo";
1133
+ input = pick(["subagent", "call"]);
1126
1134
  break;
1127
1135
  case "add_routine":
1128
1136
  case "stop_routine":
1129
- operation = "teammate_routine";
1130
- input = { ...pick(["teammate", "schedule", "text", "routine"]), change: args["operation"] === "add_routine" ? "add" : "remove" };
1137
+ operation = "subagent_routine";
1138
+ input = { ...pick(["subagent", "schedule", "text", "routine"]), change: args["operation"] === "add_routine" ? "add" : "remove" };
1131
1139
  break;
1132
- default: return { ok: false, message: "Choose create, edit_section, edit_soul, pause, resume, remove, note, answer, use_tool, stop_tool, tool_rule, forget, edit_memory, add_routine, stop_routine or undo." };
1140
+ default: return { ok: false, message: "Choose ask, create, edit_section, edit_soul, pause, resume, remove, note, answer, use_tool, stop_tool, tool_rule, forget, edit_memory, add_routine, stop_routine or undo." };
1133
1141
  }
1134
1142
  const action = prepareSharedAction(ctx.store, ctx.who, operation, input, ctx.evidenceRoot, ctx.now);
1135
1143
  const id = ctx.draft("action", { ...action });
1136
1144
  return id === null ? tooMany() : { ok: true, body: { proposal: id, label: action.title, awaiting: sharedActionNeedsReview(action) ? "human review in the secure confirmation screen" : "human confirmation", executed: false } };
1137
1145
  }
1138
1146
  catch (error) {
1139
- return { ok: false, message: error instanceof Error ? error.message : "That teammate change couldn't be drafted." };
1147
+ return { ok: false, message: error instanceof Error ? error.message : "That subagent change couldn't be drafted." };
1140
1148
  }
1141
1149
  },
1142
1150
  },
@@ -1212,10 +1220,10 @@ const MATE_TOOL_HANDLERS = {
1212
1220
  const family = ctx.store.taskFamilyOf(taskId, ctx.who.repos, false);
1213
1221
  const root = family?.root.id ?? taskId;
1214
1222
  const limit = Number.isSafeInteger(args["limit"]) ? Math.max(1, Math.min(30, Number(args["limit"]))) : 12;
1215
- const thread = ctx.store.liveMateThreadFor(ctx.who.name, { kind: "task", key: root });
1223
+ const thread = ctx.store.liveLeadThreadFor(ctx.who.name, { kind: "task", key: root });
1216
1224
  if (thread === null || thread.ceilingDigest !== ctx.who.ceilingDigest)
1217
1225
  return { ok: true, body: { task: root, messages: [], notice: "No conversation about this task yet." } };
1218
- const messages = ctx.store.listMateMessages(thread.id, limit).map(one => ({ from: one.role === "operator" ? "operator" : "lead", text: one.text.length > 1_200 ? `${one.text.slice(0, 1_200)}…` : one.text, at: one.createdAt }));
1226
+ const messages = ctx.store.listLeadMessages(thread.id, limit).map(one => ({ from: one.role === "operator" ? "operator" : "lead", text: one.text.length > 1_200 ? `${one.text.slice(0, 1_200)}…` : one.text, at: one.createdAt }));
1219
1227
  return { ok: true, body: { task: root, title: family?.root.title ?? null, messages, notice: messages.length === limit ? "Only the most recent messages are shown." : null } };
1220
1228
  },
1221
1229
  },
@@ -1317,7 +1325,7 @@ const MATE_TOOL_HANDLERS = {
1317
1325
  return { ok: false, message: "Say the correction in one sentence." };
1318
1326
  const text = String(args["text"]).trim();
1319
1327
  // One card per correction: a card still waiting in this conversation for the same change is the one to confirm.
1320
- const waiting = ctx.thread === undefined ? [] : ctx.store.listMateProposals(ctx.thread, ["drafting", "pending", "confirming"])
1328
+ const waiting = ctx.thread === undefined ? [] : ctx.store.listLeadProposals(ctx.thread, ["drafting", "pending", "confirming"])
1321
1329
  .filter(one => one.kind === "action" && one.payload["repo"] === repo);
1322
1330
  const same = (words) => typeof words === "string" && words.trim().toLowerCase() === text.toLowerCase();
1323
1331
  let operation, input;
@@ -1363,7 +1371,7 @@ const MATE_TOOL_HANDLERS = {
1363
1371
  },
1364
1372
  },
1365
1373
  get_person: {
1366
- description: "One person, AI teammate or team chat from your catch-up's people index, in full, with their open tasks: its id as the index shows it (p3fa91c2e, t5, c07b1d9a4) or a name. Read it before answering a question about that person, teammate or team. Read-only.",
1374
+ description: "One person, subagent or team chat from your catch-up's people index, in full, with their open tasks: its id as the index shows it (p3fa91c2e, t5, c07b1d9a4) or a name. Read it before answering a question about that person, subagent or team. Read-only.",
1367
1375
  handle: (ctx, args) => {
1368
1376
  const id = typeof args["id"] === "string" && PERSON_ID.test(args["id"]) ? args["id"] : undefined;
1369
1377
  const name = typeof args["name"] === "string" && args["name"].trim() !== "" ? args["name"] : undefined;
@@ -1410,7 +1418,7 @@ const MATE_TOOL_HANDLERS = {
1410
1418
  },
1411
1419
  },
1412
1420
  ask_owner: {
1413
- description: `Ask the owner one question as tappable buttons: 2-4 short options; "${MATE_ASK_OTHER}" is added. The tapped option comes back as their next message. Only when the answer changes the work; at most once per reply. Then finish with a short reply that leads into it; the question and buttons follow it.`,
1421
+ description: `Ask the owner one question as tappable buttons: 2-4 short options; "${LEAD_ASK_OTHER}" is added. The tapped option comes back as their next message. Only when the answer changes the work; at most once per reply. Then finish with a short reply that leads into it; the question and buttons follow it.`,
1414
1422
  handle: (ctx, args) => {
1415
1423
  if (ctx.ask === undefined)
1416
1424
  return { ok: false, message: "Questions with buttons are not available here. Ask in your reply instead." };
@@ -1422,11 +1430,11 @@ const MATE_TOOL_HANDLERS = {
1422
1430
  if (options.length < 2 || options.length > 4 || options.some(one => one === "" || !honest(one, 40)))
1423
1431
  return { ok: false, message: "Give 2 to 4 short options of up to 40 characters each." };
1424
1432
  const seen = new Set(options.map(one => one.toLowerCase()));
1425
- if (seen.size !== options.length || seen.has(MATE_ASK_OTHER.toLowerCase()))
1426
- return { ok: false, message: `Each option must differ, and "${MATE_ASK_OTHER}" is added for you.` };
1433
+ if (seen.size !== options.length || seen.has(LEAD_ASK_OTHER.toLowerCase()))
1434
+ return { ok: false, message: `Each option must differ, and "${LEAD_ASK_OTHER}" is added for you.` };
1427
1435
  if (!ctx.ask(question, options))
1428
1436
  return { ok: false, message: "This reply already asks a question. Ask one at a time." };
1429
- return { ok: true, body: { asked: question, options: [...options, MATE_ASK_OTHER], shown: "as buttons after your reply; the tapped option arrives as the owner's next message" } };
1437
+ return { ok: true, body: { asked: question, options: [...options, LEAD_ASK_OTHER], shown: "as buttons after your reply; the tapped option arrives as the owner's next message" } };
1430
1438
  },
1431
1439
  },
1432
1440
  search_project_memory: {
@@ -1918,13 +1926,13 @@ const MATE_TOOL_HANDLERS = {
1918
1926
  },
1919
1927
  };
1920
1928
  /** The lead's tools, in the order the model is shown them: each one's inputSchema is its contract, derived. */
1921
- export const MATE_TOOLS = Object.keys(LEAD_TOOL_INPUTS).map((name) => {
1929
+ export const LEAD_TOOLS = Object.keys(LEAD_TOOL_INPUTS).map((name) => {
1922
1930
  const input = LEAD_TOOL_INPUTS[name];
1923
1931
  // Flow proposals already used their strict contract in 0.9.36; keep that validation unchanged. A shared action's
1924
1932
  // unknown keys reach prepareSharedAction, whose per-operation check refused them in 0.9.36 and still does. Every other
1925
1933
  // tool ignored unknown arguments and still strips them.
1926
1934
  const reader = name === "propose_flow" ? LEAD_TOOL_READ_INPUTS[name] : name === "propose_action" ? LEAD_TOOL_READ_INPUTS[name].loose() : LEAD_TOOL_READ_INPUTS[name].strip();
1927
- const handler = MATE_TOOL_HANDLERS[name];
1935
+ const handler = LEAD_TOOL_HANDLERS[name];
1928
1936
  return {
1929
1937
  name,
1930
1938
  description: handler.description,
@@ -1974,7 +1982,7 @@ function integrationIoOf(ctx) {
1974
1982
  const dir = file === null ? null : dirname(file);
1975
1983
  return { store: ctx.store, dir, telegramTokenFile: dir === null ? null : join(dir, "telegram-token"), env: process.env, repos: ctx.who.repos };
1976
1984
  }
1977
- /** The admitted project a proposal is about, when its arguments name one: the project, task, result, flow, card, teammate or decision. */
1985
+ /** The admitted project a proposal is about, when its arguments name one: the project, task, result, flow, card, subagent or decision. */
1978
1986
  function proposalRepoOf(ctx, args) {
1979
1987
  const { store } = ctx;
1980
1988
  const id = (key) => Number.isSafeInteger(args[key]) && Number(args[key]) > 0 ? Number(args[key]) : null;
@@ -1987,7 +1995,7 @@ function proposalRepoOf(ctx, args) {
1987
1995
  : id("run") !== null ? ofRun(id("run"))
1988
1996
  : id("flow") !== null ? store.getFlow(id("flow"))?.repo ?? null
1989
1997
  : id("card") !== null ? (() => { const card = store.getFlowCard(id("card")); return card === null ? null : store.getFlow(card.flow)?.repo ?? null; })()
1990
- : id("teammate") !== null ? store.getTeammate(id("teammate"))?.repo ?? null
1998
+ : id("subagent") !== null ? store.getSubagent(id("subagent"))?.repo ?? null
1991
1999
  : id("decision") !== null ? ofRun(store.getDecision(id("decision"))?.run ?? null)
1992
2000
  : null;
1993
2001
  return found !== null && ctx.who.repos.includes(found) ? found : null;
@@ -2043,7 +2051,7 @@ function rememberAboutYou(ctx, args) {
2043
2051
  if (args["replaces"] !== undefined && Number(args["replaces"]) > lines.length)
2044
2052
  return { ok: false, message: `replaces is a line number from your catch-up's aboutYou (1 to ${lines.length}).` };
2045
2053
  const replaces = args["replaces"] !== undefined ? Number(args["replaces"]) : 0;
2046
- const waiting = ctx.thread === undefined ? [] : ctx.store.listMateProposals(ctx.thread, ["drafting", "pending", "confirming"])
2054
+ const waiting = ctx.thread === undefined ? [] : ctx.store.listLeadProposals(ctx.thread, ["drafting", "pending", "confirming"])
2047
2055
  .filter(one => one.kind === "action" && one.payload["operation"] === "lead_about_you");
2048
2056
  const twin = waiting.find(one => String(one.payload["request"]?.["line"] ?? "").toLowerCase() === checked.line.toLowerCase());
2049
2057
  if (twin !== undefined)
@@ -2060,7 +2068,7 @@ function rememberAboutYou(ctx, args) {
2060
2068
  ...(replaces === 0 ? {} : { replaces: { line: replaces, was: lines[replaces - 1] } }),
2061
2069
  next: "Once confirmed it is in your next catch-up's aboutYou." } };
2062
2070
  }
2063
- /** get_person names people, teammates and team chat members on purpose; the scrub took them out with the rest. */
2071
+ /** get_person names people, subagents and team chat members on purpose; the scrub took them out with the rest. */
2064
2072
  function personNamesBack(raw, scrubbed) {
2065
2073
  if (!raw.ok || !scrubbed.ok)
2066
2074
  return scrubbed;
@@ -2077,17 +2085,17 @@ function personNamesBack(raw, scrubbed) {
2077
2085
  listed[index].name = one.name; });
2078
2086
  return scrubbed;
2079
2087
  }
2080
- export const MATE_TOOL_SCHEMAS = MATE_TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }));
2081
- export function isMateTool(name) {
2082
- return MATE_TOOLS.some(one => one.name === name);
2088
+ export const LEAD_TOOL_SCHEMAS = LEAD_TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }));
2089
+ export function isLeadTool(name) {
2090
+ return LEAD_TOOLS.some(one => one.name === name);
2083
2091
  }
2084
2092
  /** Scrub every result. Only list_repos then adds validated display metadata
2085
2093
  * from the same admitted project list; no user-authored result gets an exemption. */
2086
- export function executeMateTool(ctx, name, args, view) {
2087
- const tool = MATE_TOOLS.find(one => one.name === name);
2088
- const scrub = view ?? mateViewContextFor(ctx.store, ctx.who);
2094
+ export function executeLeadTool(ctx, name, args, view) {
2095
+ const tool = LEAD_TOOLS.find(one => one.name === name);
2096
+ const scrub = view ?? leadViewContextFor(ctx.store, ctx.who);
2089
2097
  if (tool === undefined)
2090
- return { ok: false, message: `no tool named ${redactForMate(name, scrub)}` };
2098
+ return { ok: false, message: `no tool named ${redactForLead(name, scrub)}` };
2091
2099
  let result;
2092
2100
  try {
2093
2101
  // The call is read by the contract's compatible reader, before any check or handler: a refusal is every path-named
@@ -2107,10 +2115,10 @@ export function executeMateTool(ctx, name, args, view) {
2107
2115
  // A result is checked against its output schema before it is scrubbed; a disagreement is reported, never refused.
2108
2116
  if (result.ok)
2109
2117
  reportToolOutput("lead", name, LEAD_TOOL_OUTPUTS[name], result.body);
2110
- const sanitized = name === "get_person" ? personNamesBack(result, mateView(result, scrub)) : mateView(result, scrub);
2118
+ const sanitized = name === "get_person" ? personNamesBack(result, leadView(result, scrub)) : leadView(result, scrub);
2111
2119
  if (name === "list_repos" && sanitized.ok) {
2112
2120
  return { ok: true, body: { repos: ctx.who.repos.map((path, index) => ({
2113
- repo: `r${index + 1}`, name: projectLabelForMate(path, index, scrub.names),
2121
+ repo: `r${index + 1}`, name: projectLabelForLead(path, index, scrub.names),
2114
2122
  })) } };
2115
2123
  }
2116
2124
  return sanitized;