@desplega.ai/agent-swarm 1.113.0 → 1.115.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (237) hide show
  1. package/README.md +2 -2
  2. package/dist/{actions-q4n7cz6e.js → actions-17ya5wde.js} +8 -9
  3. package/dist/{app-es4nzc71.js → app-4f718yk0.js} +6 -6
  4. package/dist/{assistant-0x8ey0vs.js → assistant-ah7csnjz.js} +19 -18
  5. package/dist/{boot-reembed-69agkhv6.js → boot-reembed-4jpxhb25.js} +6 -6
  6. package/dist/{boot-reembed-p0ny05t8.js → boot-reembed-qata34xa.js} +5 -5
  7. package/dist/{boot-scrub-logs-yybrkmvr.js → boot-scrub-logs-tv48nn3z.js} +4 -4
  8. package/dist/{claude-adapter-mma9a1yr.js → claude-adapter-3csn5bks.js} +3 -3
  9. package/dist/{claude-managed-adapter-pv2nbqy4.js → claude-managed-adapter-kysmkz89.js} +2 -2
  10. package/dist/cli-0pygq9r1.js +429 -0
  11. package/dist/{cli-hsetkkd3.js → cli-12vb0xg6.js} +33 -9
  12. package/dist/{cli-ncfgsqbd.js → cli-15y7qhzp.js} +262 -33
  13. package/dist/{cli-7ga43spr.js → cli-1yjrbg1f.js} +1 -1
  14. package/dist/{cli-36ymmpmp.js → cli-3samkyap.js} +2 -2
  15. package/dist/cli-52fg7s7x.js +269 -0
  16. package/dist/{cli-hjhvekf4.js → cli-65ekje01.js} +11 -8
  17. package/dist/{cli-h3k622d0.js → cli-6aq8xhvm.js} +90 -3
  18. package/dist/{cli-f3qa069v.js → cli-7xje5amz.js} +1 -1
  19. package/dist/cli-86fdnerz.js +405 -0
  20. package/dist/{cli-9cy6nc5m.js → cli-8fjwym53.js} +1 -1
  21. package/dist/{cli-c3frqf65.js → cli-b23axgz4.js} +3 -3
  22. package/dist/{cli-a0t7vapf.js → cli-bc89my0z.js} +1 -1
  23. package/dist/{cli-bnzbn8j8.js → cli-beea4a3y.js} +73 -24
  24. package/dist/{cli-q21d49ac.js → cli-bw80ck94.js} +1 -1
  25. package/dist/{cli-pwc5e83c.js → cli-cchksf5q.js} +12 -7
  26. package/dist/{cli-30bbaveh.js → cli-cmw9rzy5.js} +2 -2
  27. package/dist/{cli-y6mrptcn.js → cli-cnwssmsr.js} +1 -1
  28. package/dist/{cli-r5g6ngtn.js → cli-dcca6j8m.js} +46 -5
  29. package/dist/{cli-tkmm5nf9.js → cli-e61fs2hp.js} +59 -2
  30. package/dist/{cli-6wscj9z0.js → cli-e7306swv.js} +14 -14
  31. package/dist/{cli-dcfyyh3t.js → cli-ftyf58xj.js} +2 -2
  32. package/dist/{cli-4xyj9jtq.js → cli-hrhye2rm.js} +1062 -478
  33. package/dist/cli-jkhwmmgg.js +136 -0
  34. package/dist/{cli-y4ycrnjc.js → cli-k3y4z3h5.js} +1 -1
  35. package/dist/{cli-6xd0bvf8.js → cli-ma04faw5.js} +1 -1
  36. package/dist/{cli-2fc97xm5.js → cli-mnbgv3w7.js} +4 -4
  37. package/dist/{cli-trmapn9k.js → cli-pet3aev6.js} +95 -10
  38. package/dist/{cli-z46pnksz.js → cli-q1rx7g0d.js} +175 -28
  39. package/dist/{cli-qwgtacd6.js → cli-r9f6ngrf.js} +1244 -336
  40. package/dist/{cli-gtkqc144.js → cli-shvaxw27.js} +13 -7
  41. package/dist/{cli-ywez6yps.js → cli-sq7f72d6.js} +1 -1
  42. package/dist/cli-t0vqevnm.js +393 -0
  43. package/dist/{cli-tbw13sx8.js → cli-tece6zgm.js} +1 -1
  44. package/dist/{cli-bgvskydy.js → cli-tffvrh6j.js} +1 -1
  45. package/dist/cli-wh91zby6.js +187222 -0
  46. package/dist/{cli-anrj584m.js → cli-xz9aq0rf.js} +1 -1
  47. package/dist/{cli-mq580e06.js → cli-zsrdty74.js} +18 -18
  48. package/dist/cli.js +32 -15
  49. package/dist/{codex-adapter-5cmy858w.js → codex-adapter-de3w0nch.js} +10 -10
  50. package/dist/{codex-session-runner-46q8mrba.js → codex-session-runner-bnw8cysk.js} +10 -10
  51. package/dist/{commands-z5dbwta3.js → commands-hch4e2k1.js} +4 -4
  52. package/dist/{db-bbgahh4z.js → db-p3h2j06x.js} +14 -4
  53. package/dist/{handlers-6b44317z.js → handlers-9er4zx9q.js} +17 -31
  54. package/dist/{hook-sbp5fmps.js → hook-4axs756z.js} +7 -7
  55. package/dist/{http-2xz43gz3.js → http-809fvtz2.js} +1934 -886
  56. package/dist/{index-wz1ea15p.js → index-2dgfj9av.js} +3 -3
  57. package/dist/{index-nhat35em.js → index-47em3d2n.js} +14 -11
  58. package/dist/{index-5ghxn8s6.js → index-9fqaktjr.js} +12 -9
  59. package/dist/{index-0gzmqj4k.js → index-9ht8gr56.js} +13 -10
  60. package/dist/{index-vfedgdw6.js → index-rm85cfdx.js} +368 -40
  61. package/dist/{index-ykvs43z4.js → index-t2snazez.js} +4 -4
  62. package/dist/{keepalive-62gfj91d.js → keepalive-3cps6e3e.js} +7 -6
  63. package/dist/{lead-ag0akn0v.js → lead-fkk95wgq.js} +36 -32
  64. package/dist/{maintenance-xxrrzk5x.js → maintenance-fweqh0yj.js} +6 -6
  65. package/dist/{mistral-conversations-26yrnbf1.js → mistral-conversations-7gqnz2wt.js} +7 -7
  66. package/dist/oauth-refresh-sweep-sycqtbph.js +114 -0
  67. package/dist/{onboard-64zyfcbs.js → onboard-cy0rkgvx.js} +2 -2
  68. package/dist/{opencode-adapter-mqsxr170.js → opencode-adapter-p6bxnbm4.js} +2 -2
  69. package/dist/{otel-zjd9zaxs.js → otel-g299k41b.js} +2 -2
  70. package/dist/{otel-impl-b86qseaa.js → otel-impl-wbns8hf7.js} +1 -1
  71. package/dist/{pi-mono-adapter-kxjxpfgy.js → pi-mono-adapter-05sck1ds.js} +17 -109
  72. package/dist/{pricing-refresh-cgtkffpa.js → pricing-refresh-0hvazmkf.js} +6 -6
  73. package/dist/rbac-roles-kfesjy9y.js +28 -0
  74. package/dist/rbac-roles-mwcwhbxc.js +283 -0
  75. package/dist/{seed-pricing-1r1y1xkq.js → seed-pricing-h2kdq4cq.js} +5 -5
  76. package/dist/{setup-mkcgkjf7.js → setup-8506q97w.js} +2 -2
  77. package/dist/{worker-351sze63.js → worker-4r5c6zyb.js} +36 -32
  78. package/openapi.json +1327 -20
  79. package/package.json +3 -3
  80. package/src/agentmail/handlers.ts +16 -6
  81. package/src/agentmail/templates.ts +25 -5
  82. package/src/be/audit-user.ts +28 -4
  83. package/src/be/db-queries/oauth.ts +42 -0
  84. package/src/be/db.ts +314 -28
  85. package/src/be/identity.ts +79 -0
  86. package/src/be/mcp-proxy.ts +132 -0
  87. package/src/be/migrations/109_rbac_roles.sql +79 -0
  88. package/src/be/migrations/111_oauth_credential_bindings.sql +6 -0
  89. package/src/be/migrations/112_script_connections_graphql.sql +96 -0
  90. package/src/be/migrations/113_task_routing_affinity.sql +9 -0
  91. package/src/be/migrations/114_backfill_gpt_5_6_pricing.sql +15 -0
  92. package/src/be/modelsdev-cache.json +135 -0
  93. package/src/be/oauth-credential-bindings.ts +35 -0
  94. package/src/be/oauth-refresh-sweep.ts +159 -0
  95. package/src/be/rbac-audit.ts +52 -13
  96. package/src/be/rbac-roles.ts +424 -0
  97. package/src/be/script-connections.ts +459 -40
  98. package/src/be/script-credential-broker.ts +29 -3
  99. package/src/be/scripts/typecheck.ts +51 -7
  100. package/src/be/seed-scripts/catalog/catalog-report.inline.ts +325 -24
  101. package/src/be/seed-scripts/catalog/catalog-report.ts +325 -24
  102. package/src/be/users.ts +24 -0
  103. package/src/cli.tsx +17 -0
  104. package/src/github/handlers.ts +10 -9
  105. package/src/github/templates.ts +45 -9
  106. package/src/gitlab/handlers.ts +20 -8
  107. package/src/gitlab/templates.ts +18 -6
  108. package/src/heartbeat/heartbeat.ts +143 -21
  109. package/src/http/all-routes.ts +3 -0
  110. package/src/http/approval-requests.ts +12 -0
  111. package/src/http/config.ts +44 -10
  112. package/src/http/core.ts +30 -3
  113. package/src/http/favorites.ts +1 -5
  114. package/src/http/index.ts +25 -1
  115. package/src/http/mcp-oauth.ts +2 -0
  116. package/src/http/mcp-servers.ts +44 -3
  117. package/src/http/oauth-generic.ts +105 -0
  118. package/src/http/poll.ts +20 -3
  119. package/src/http/script-connection-proxy.ts +116 -0
  120. package/src/http/script-connections.ts +1657 -0
  121. package/src/http/scripts.ts +27 -7
  122. package/src/http/skills.ts +11 -0
  123. package/src/http/tasks.ts +27 -13
  124. package/src/http/x.ts +6 -2
  125. package/src/integrations/kapso/inbound.ts +23 -6
  126. package/src/jira/sync.ts +111 -17
  127. package/src/jira/templates.ts +10 -2
  128. package/src/mcp-client/http-client.ts +194 -0
  129. package/src/oauth/app-validation.ts +16 -0
  130. package/src/oauth/ensure-token.ts +69 -14
  131. package/src/oauth/mcp-wrapper.ts +15 -0
  132. package/src/oauth/wrapper.ts +48 -20
  133. package/src/prompts/session-templates.ts +3 -3
  134. package/src/providers/codex-models.ts +100 -30
  135. package/src/providers/modelsdev-reasoning.json +51 -0
  136. package/src/providers/pi-mono-mcp-client.ts +2 -133
  137. package/src/providers/reasoning-effort.ts +11 -5
  138. package/src/rbac/admission.ts +68 -0
  139. package/src/rbac/index.ts +2 -0
  140. package/src/rbac/legacy-policy.ts +12 -0
  141. package/src/rbac/permissions.ts +48 -0
  142. package/src/scheduler/scheduler.ts +9 -1
  143. package/src/scripts-runtime/api-client.ts +151 -19
  144. package/src/scripts-runtime/api-types.ts +38 -6
  145. package/src/scripts-runtime/credential-broker/default-bindings.ts +1 -0
  146. package/src/scripts-runtime/credential-broker/types.ts +8 -0
  147. package/src/scripts-runtime/ctx.ts +11 -1
  148. package/src/scripts-runtime/eval-harness.ts +5 -1
  149. package/src/scripts-runtime/executors/types.ts +2 -1
  150. package/src/scripts-runtime/loader.ts +3 -1
  151. package/src/scripts-runtime/mcp-client.ts +87 -0
  152. package/src/scripts-runtime/sdk-allowlist.ts +1 -0
  153. package/src/scripts-runtime/types/stdlib.d.ts +6 -0
  154. package/src/scripts-runtime/types/swarm-sdk.d.ts +19 -0
  155. package/src/server-user.ts +114 -59
  156. package/src/server.ts +12 -0
  157. package/src/slack/assistant.ts +14 -6
  158. package/src/slack/enrich.ts +17 -0
  159. package/src/slack/handlers.ts +8 -22
  160. package/src/slack/responses.ts +91 -1
  161. package/src/slack/thread-buffer.ts +6 -3
  162. package/src/slack/watcher.ts +9 -6
  163. package/src/tasks/worker-follow-up.ts +100 -3
  164. package/src/tests/agentmail-handlers.test.ts +32 -3
  165. package/src/tests/approval-requests.test.ts +61 -0
  166. package/src/tests/audit-user.test.ts +47 -1
  167. package/src/tests/codex-adapter.test.ts +46 -23
  168. package/src/tests/credential-broker.test.ts +5 -2
  169. package/src/tests/delete-page-tool.test.ts +197 -0
  170. package/src/tests/github-handlers.test.ts +28 -2
  171. package/src/tests/gitlab-handlers.test.ts +125 -0
  172. package/src/tests/heartbeat.test.ts +48 -3
  173. package/src/tests/identity.test.ts +117 -0
  174. package/src/tests/jira-sync.test.ts +99 -1
  175. package/src/tests/kapso-inbound.test.ts +7 -0
  176. package/src/tests/mcp-tools-user.test.ts +60 -6
  177. package/src/tests/model-control.test.ts +8 -2
  178. package/src/tests/oauth-credential-bindings.test.ts +490 -0
  179. package/src/tests/oauth-refresh-sweep.test.ts +160 -0
  180. package/src/tests/pool-affinity.test.ts +401 -0
  181. package/src/tests/prompt-template-remaining.test.ts +2 -2
  182. package/src/tests/rbac-admission-e2e.test.ts +386 -0
  183. package/src/tests/rbac-admission.test.ts +492 -0
  184. package/src/tests/rbac-e2e-helpers.ts +3 -0
  185. package/src/tests/rbac-engine.test.ts +50 -0
  186. package/src/tests/rbac-mcp-admission-e2e.test.ts +362 -0
  187. package/src/tests/rbac-roles.test.ts +328 -0
  188. package/src/tests/reasoning-effort.test.ts +15 -2
  189. package/src/tests/schedule-target-type.test.ts +50 -0
  190. package/src/tests/scheduled-tasks.test.ts +40 -0
  191. package/src/tests/script-apis-mcp.test.ts +37 -13
  192. package/src/tests/script-connections-http.test.ts +1158 -0
  193. package/src/tests/script-connections-mcp.test.ts +894 -0
  194. package/src/tests/script-connections.test.ts +754 -6
  195. package/src/tests/scripts-external-api.test.ts +49 -1
  196. package/src/tests/scripts-runtime-secret-egress.test.ts +1 -0
  197. package/src/tests/scripts-runtime.test.ts +291 -0
  198. package/src/tests/seed-scripts.test.ts +36 -0
  199. package/src/tests/slack-identity-resolution.test.ts +37 -3
  200. package/src/tests/slack-inline-output.test.ts +65 -0
  201. package/src/tests/slack-thread-buffer.test.ts +28 -0
  202. package/src/tests/tool-annotations.test.ts +1 -0
  203. package/src/tests/user-token-rest-auth.test.ts +140 -1
  204. package/src/tests/workflow-swarm-script.test.ts +44 -0
  205. package/src/tests/workflow-triggers-v2.test.ts +300 -1
  206. package/src/tools/credential-bindings/tool.ts +277 -18
  207. package/src/tools/delete-page.ts +145 -0
  208. package/src/tools/mcp-servers/mcp-server-delete.ts +7 -4
  209. package/src/tools/request-human-input.ts +2 -0
  210. package/src/tools/resolve-user.ts +77 -27
  211. package/src/tools/script-apis.ts +62 -3
  212. package/src/tools/script-connections/tool.ts +275 -31
  213. package/src/tools/send-task.ts +15 -1
  214. package/src/tools/task-action.ts +34 -2
  215. package/src/tools/templates.ts +53 -1
  216. package/src/tools/tool-config.ts +2 -1
  217. package/src/tools/utils.ts +2 -0
  218. package/src/tools/workflows/create-workflow.ts +8 -1
  219. package/src/tools/workflows/update-workflow.ts +11 -2
  220. package/src/types.ts +90 -11
  221. package/src/utils/scoped-resource.ts +25 -0
  222. package/src/workflows/executors/human-in-the-loop.ts +1 -0
  223. package/src/workflows/executors/swarm-script.ts +7 -1
  224. package/src/workflows/triggers.ts +146 -18
  225. package/templates/skills/swarm-scripts/content.md +33 -8
  226. package/dist/cli-bgef578c.js +0 -771
  227. package/dist/cli-c9mtm09b.js +0 -43
  228. package/dist/cli-ceewqajw.js +0 -1698
  229. package/dist/{anthropic-messages-8nc6r2dw.js → anthropic-messages-kq3pjhbp.js} +6 -6
  230. package/dist/{azure-openai-responses-6yfzw3qw.js → azure-openai-responses-gm1ejtmn.js} +7 -7
  231. package/dist/{cli-vbraamxr.js → cli-6b4wwmm8.js} +9 -9
  232. package/dist/{cli-2r7s8jhp.js → cli-sznberje.js} +3 -3
  233. package/dist/{google-generative-ai-vfq6ymz9.js → google-generative-ai-hyrra2jm.js} +3 -3
  234. package/dist/{google-vertex-mj5y5rfj.js → google-vertex-8bs2pzgn.js} +3 -3
  235. package/dist/{openai-codex-responses-8h6kvfv8.js → openai-codex-responses-3nkvje79.js} +4 -4
  236. package/dist/{openai-completions-x6kjnqjd.js → openai-completions-0asftkwg.js} +11 -11
  237. package/dist/{openai-responses-7jwyz3ds.js → openai-responses-pmatek45.js} +10 -10
@@ -0,0 +1,269 @@
1
+ import {
2
+ PermissionVerbSchema
3
+ } from "./cli-86fdnerz.js";
4
+ import {
5
+ getDb,
6
+ init_db
7
+ } from "./cli-15y7qhzp.js";
8
+
9
+ // src/be/rbac-roles.ts
10
+ init_db();
11
+ var DEFAULT_ROLE_ID = "rbac-role-admin";
12
+ var BUILTIN_ROLES = [
13
+ {
14
+ id: DEFAULT_ROLE_ID,
15
+ name: "admin",
16
+ description: "Full access including verb-less routes (legacy-equivalent default).",
17
+ isBuiltin: true,
18
+ grantsAll: true,
19
+ verbs: []
20
+ },
21
+ {
22
+ id: "rbac-role-requester",
23
+ name: "requester",
24
+ description: "Own-task lifecycle: what legacy policy grants user principals.",
25
+ isBuiltin: true,
26
+ grantsAll: false,
27
+ verbs: [
28
+ "task.create.own",
29
+ "task.read.own",
30
+ "task.cancel.own",
31
+ "task.action.own",
32
+ "task.fs.mutate",
33
+ "favorite.write.own"
34
+ ]
35
+ }
36
+ ];
37
+ var CREATE_USER_DEFAULT_ROLE_TRIGGER_SQL = `
38
+ CREATE TRIGGER IF NOT EXISTS trg_users_default_role
39
+ AFTER INSERT ON users
40
+ BEGIN
41
+ INSERT OR IGNORE INTO principal_roles (principalType, principalId, roleId)
42
+ SELECT 'user', NEW.id, 'rbac-role-admin'
43
+ WHERE EXISTS (SELECT 1 FROM roles WHERE id = 'rbac-role-admin');
44
+ END;
45
+ `;
46
+ function roleRowToUserRole(row) {
47
+ return {
48
+ id: row.id,
49
+ name: row.name,
50
+ description: row.description,
51
+ isBuiltin: row.isBuiltin === 1,
52
+ grantsAll: row.grantsAll === 1,
53
+ createdAt: row.createdAt
54
+ };
55
+ }
56
+ function requireRoleId(roleName) {
57
+ const row = getDb().prepare("SELECT id FROM roles WHERE name = ?").get(roleName);
58
+ if (!row) {
59
+ throw new Error(`Unknown RBAC role: ${roleName}`);
60
+ }
61
+ return row.id;
62
+ }
63
+ function validateBuiltinVerb(roleName, verb) {
64
+ const parsed = PermissionVerbSchema.safeParse(verb);
65
+ if (!parsed.success) {
66
+ throw new Error(`Invalid RBAC permission verb "${verb}" in built-in role "${roleName}"`);
67
+ }
68
+ return parsed.data;
69
+ }
70
+ function validatedBuiltinVerbSets() {
71
+ const byRoleId = new Map;
72
+ for (const role of BUILTIN_ROLES) {
73
+ const verbs = new Set;
74
+ for (const verb of role.verbs) {
75
+ verbs.add(validateBuiltinVerb(role.name, verb));
76
+ }
77
+ byRoleId.set(role.id, verbs);
78
+ }
79
+ return byRoleId;
80
+ }
81
+ var loggedInvalidGrantVerbs = new Set;
82
+ function parseDatabaseGrantVerb(roleId, verb) {
83
+ const parsed = PermissionVerbSchema.safeParse(verb);
84
+ if (parsed.success) {
85
+ return parsed.data;
86
+ }
87
+ const logKey = `${roleId}:${verb}`;
88
+ if (!loggedInvalidGrantVerbs.has(logKey)) {
89
+ loggedInvalidGrantVerbs.add(logKey);
90
+ console.error(`[rbac] Ignoring invalid role_permissions verb "${verb}" for roleId="${roleId}". ` + "Invalid RBAC verbs grant nothing; remove or repair the row.");
91
+ }
92
+ return null;
93
+ }
94
+ function getUserGrant(userId) {
95
+ const rows = getDb().prepare(`SELECT r.id AS roleId, r.grantsAll, rp.verb
96
+ FROM principal_roles pr
97
+ JOIN roles r ON r.id = pr.roleId
98
+ LEFT JOIN role_permissions rp ON rp.roleId = r.id
99
+ WHERE pr.principalType = 'user' AND pr.principalId = ?
100
+ ORDER BY r.grantsAll DESC`).all(userId);
101
+ if (rows.length === 0) {
102
+ return { grantsAll: false, verbs: new Set };
103
+ }
104
+ const verbs = new Set;
105
+ for (const row of rows) {
106
+ if (row.grantsAll === 1) {
107
+ return { grantsAll: true, verbs: new Set };
108
+ }
109
+ if (row.verb) {
110
+ const verb = parseDatabaseGrantVerb(row.roleId, row.verb);
111
+ if (verb) {
112
+ verbs.add(verb);
113
+ }
114
+ }
115
+ }
116
+ return { grantsAll: false, verbs };
117
+ }
118
+ function attachRole(userId, roleName) {
119
+ const db = getDb();
120
+ db.transaction(() => {
121
+ const roleId = requireRoleId(roleName);
122
+ db.prepare(`INSERT OR IGNORE INTO principal_roles (principalType, principalId, roleId)
123
+ VALUES ('user', ?, ?)`).run(userId, roleId);
124
+ })();
125
+ }
126
+ function detachRole(userId, roleName) {
127
+ const db = getDb();
128
+ db.transaction(() => {
129
+ const roleId = requireRoleId(roleName);
130
+ db.prepare(`DELETE FROM principal_roles
131
+ WHERE principalType = 'user' AND principalId = ? AND roleId = ?`).run(userId, roleId);
132
+ })();
133
+ }
134
+ function listUserRoles(userId) {
135
+ return getDb().prepare(`SELECT r.id, r.name, r.description, r.isBuiltin, r.grantsAll, r.createdAt
136
+ FROM principal_roles pr
137
+ JOIN roles r ON r.id = pr.roleId
138
+ WHERE pr.principalType = 'user' AND pr.principalId = ?
139
+ ORDER BY r.name`).all(userId).map(roleRowToUserRole);
140
+ }
141
+ function ensureRbacSeedsSynced(opts) {
142
+ const db = getDb();
143
+ const desiredVerbsByRoleId = validatedBuiltinVerbSets();
144
+ const insertRole = db.prepare(`INSERT OR IGNORE INTO roles (id, name, description, isBuiltin, grantsAll)
145
+ VALUES (?, ?, ?, 1, ?)`);
146
+ const updateRole = db.prepare(`UPDATE roles
147
+ SET name = ?, description = ?, isBuiltin = 1, grantsAll = ?, lastUpdatedAt = CURRENT_TIMESTAMP
148
+ WHERE id = ?
149
+ AND (
150
+ name <> ?
151
+ OR COALESCE(description, '') <> COALESCE(?, '')
152
+ OR isBuiltin <> 1
153
+ OR grantsAll <> ?
154
+ )`);
155
+ const selectRoleById = db.prepare("SELECT id, name FROM roles WHERE id = ?");
156
+ const selectRoleByName = db.prepare("SELECT id, name FROM roles WHERE name = ?");
157
+ const selectRolePermissions = db.prepare("SELECT verb FROM role_permissions WHERE roleId = ?");
158
+ const deleteRolePermission = db.prepare("DELETE FROM role_permissions WHERE roleId = ? AND verb = ?");
159
+ const insertRolePermission = db.prepare("INSERT OR IGNORE INTO role_permissions (roleId, verb) VALUES (?, ?)");
160
+ const backfillDefaultRoleForZeroRoleUsers = db.prepare(`INSERT OR IGNORE INTO principal_roles (principalType, principalId, roleId)
161
+ SELECT 'user', u.id, ?
162
+ FROM users u
163
+ WHERE NOT EXISTS (
164
+ SELECT 1
165
+ FROM principal_roles pr
166
+ WHERE pr.principalType = 'user'
167
+ AND pr.principalId = u.id
168
+ )`);
169
+ const stats = {
170
+ rolesInserted: 0,
171
+ rolesUpdated: 0,
172
+ permissionsInserted: 0,
173
+ permissionsDeleted: 0,
174
+ usersBackfilled: 0
175
+ };
176
+ db.transaction(() => {
177
+ for (const role of BUILTIN_ROLES) {
178
+ const grantsAll = role.grantsAll ? 1 : 0;
179
+ const insertResult = insertRole.run(role.id, role.name, role.description, grantsAll);
180
+ stats.rolesInserted += insertResult.changes;
181
+ if (insertResult.changes === 0) {
182
+ const updateResult = updateRole.run(role.name, role.description, grantsAll, role.id, role.name, role.description, grantsAll);
183
+ stats.rolesUpdated += updateResult.changes;
184
+ }
185
+ }
186
+ for (const role of BUILTIN_ROLES) {
187
+ const row = selectRoleById.get(role.id);
188
+ if (row) {
189
+ continue;
190
+ }
191
+ const colliding = selectRoleByName.get(role.name);
192
+ if (colliding) {
193
+ throw new Error(`RBAC built-in role "${role.name}" (${role.id}) is missing because role ` + `"${colliding.name}" (${colliding.id}) already uses that name. ` + "Rename or remove the conflicting role, then rerun `rbac bootstrap`.");
194
+ }
195
+ throw new Error(`RBAC built-in role "${role.name}" (${role.id}) is missing after seed sync. ` + "Rerun `rbac bootstrap`; if it still fails, inspect the roles table.");
196
+ }
197
+ for (const role of BUILTIN_ROLES) {
198
+ const desiredVerbs = desiredVerbsByRoleId.get(role.id) ?? new Set;
199
+ const existingRows = selectRolePermissions.all(role.id);
200
+ for (const row of existingRows) {
201
+ if (!desiredVerbs.has(row.verb)) {
202
+ stats.permissionsDeleted += deleteRolePermission.run(role.id, row.verb).changes;
203
+ }
204
+ }
205
+ for (const verb of desiredVerbs) {
206
+ stats.permissionsInserted += insertRolePermission.run(role.id, verb).changes;
207
+ }
208
+ }
209
+ db.run(CREATE_USER_DEFAULT_ROLE_TRIGGER_SQL);
210
+ stats.usersBackfilled += backfillDefaultRoleForZeroRoleUsers.run(DEFAULT_ROLE_ID).changes;
211
+ })();
212
+ if (!opts?.quiet) {
213
+ console.log(`[rbac] seed sync: roles inserted=${stats.rolesInserted}, updated=${stats.rolesUpdated}; permissions inserted=${stats.permissionsInserted}, deleted=${stats.permissionsDeleted}; trigger=ensured; users backfilled=${stats.usersBackfilled}`);
214
+ }
215
+ return stats;
216
+ }
217
+ function describeRbacFlag() {
218
+ const raw = process.env.RBAC_ENABLED;
219
+ if (raw === undefined)
220
+ return "unset (off)";
221
+ return `${raw} (${raw === "true" ? "on" : "off"})`;
222
+ }
223
+ function printRolesTable(rows) {
224
+ const tableRows = rows.map((row) => [
225
+ row.name,
226
+ row.isBuiltin === 1 ? "yes" : "no",
227
+ row.grantsAll === 1 ? "yes" : "no",
228
+ String(row.verbCount),
229
+ String(row.attachedUserCount)
230
+ ]);
231
+ const headers = ["name", "builtin", "grantsAll", "verbs", "attachedUsers"];
232
+ const widths = headers.map((header, column) => Math.max(header.length, ...tableRows.map((row) => row[column]?.length ?? 0)));
233
+ const format = (row) => row.map((cell, column) => cell.padEnd(widths[column] ?? cell.length)).join(" ");
234
+ console.log("Roles:");
235
+ console.log(format(headers));
236
+ console.log(format(widths.map((width) => "-".repeat(width))));
237
+ for (const row of tableRows) {
238
+ console.log(format(row));
239
+ }
240
+ }
241
+ function getRbacRoleSummary() {
242
+ return getDb().prepare(`SELECT
243
+ r.name,
244
+ r.isBuiltin,
245
+ r.grantsAll,
246
+ COUNT(DISTINCT rp.verb) AS verbCount,
247
+ COUNT(DISTINCT CASE WHEN pr.principalType = 'user' THEN pr.principalId END)
248
+ AS attachedUserCount
249
+ FROM roles r
250
+ LEFT JOIN role_permissions rp ON rp.roleId = r.id
251
+ LEFT JOIN principal_roles pr ON pr.roleId = r.id
252
+ GROUP BY r.id, r.name, r.isBuiltin, r.grantsAll
253
+ ORDER BY r.isBuiltin DESC, r.name`).all();
254
+ }
255
+ async function runRbacCliCommand(args) {
256
+ const subcommand = args[0];
257
+ if (args.length !== 1 || subcommand !== "bootstrap") {
258
+ throw new Error(`Unknown RBAC command: ${args.join(" ") || "(none)"}`);
259
+ }
260
+ const stats = ensureRbacSeedsSynced({ quiet: true });
261
+ const roles = getRbacRoleSummary();
262
+ console.log("RBAC bootstrap complete");
263
+ console.log(`RBAC_ENABLED: ${describeRbacFlag()}`);
264
+ console.log(`Users backfilled this run: ${stats.usersBackfilled}`);
265
+ console.log("");
266
+ printRolesTable(roles);
267
+ }
268
+
269
+ export { DEFAULT_ROLE_ID, BUILTIN_ROLES, getUserGrant, attachRole, detachRole, listUserRoles, ensureRbacSeedsSynced, runRbacCliCommand };
@@ -1,18 +1,21 @@
1
1
  import {
2
2
  createAdditiveBuffer,
3
3
  extractSlackMessageText
4
- } from "./cli-bgvskydy.js";
4
+ } from "./cli-tffvrh6j.js";
5
5
  import {
6
6
  getSlackApp,
7
7
  registerTreeMessage
8
- } from "./cli-trmapn9k.js";
8
+ } from "./cli-pet3aev6.js";
9
+ import {
10
+ rewriteSlackMentions
11
+ } from "./cli-cchksf5q.js";
9
12
  import {
10
13
  buildBufferFlushBlocks
11
- } from "./cli-6xd0bvf8.js";
14
+ } from "./cli-ma04faw5.js";
12
15
  import {
13
16
  createTaskWithSiblingAwareness,
14
17
  slackContextKey
15
- } from "./cli-y4ycrnjc.js";
18
+ } from "./cli-k3y4z3h5.js";
16
19
  import {
17
20
  getLatestActiveTaskInThread,
18
21
  getLeadAgent,
@@ -20,7 +23,7 @@ import {
20
23
  init_db,
21
24
  init_registry,
22
25
  registerTemplate
23
- } from "./cli-ncfgsqbd.js";
26
+ } from "./cli-15y7qhzp.js";
24
27
 
25
28
  // src/slack/event-dedup.ts
26
29
  var DEFAULT_TTL_MS = 300000;
@@ -164,7 +167,7 @@ async function getThreadContextForBuffer(channelId, threadTs) {
164
167
  return `<@${m.user}>: ${text}`;
165
168
  }).join(`
166
169
  `);
167
- return formatted;
170
+ return rewriteSlackMentions(formatted);
168
171
  } catch (error) {
169
172
  console.error("[Slack] Failed to fetch thread context for buffer:", error);
170
173
  return "";
@@ -181,9 +184,9 @@ async function slackFlush(items, key, immediate) {
181
184
  const { channelId, threadTs } = split;
182
185
  const originalRequesterId = items[0].userId;
183
186
  console.log(`[Slack] Flushing buffer: ${key} (${items.length} messages, immediate=${immediate})`);
184
- const combinedText = items.map((m) => m.text).join(`
187
+ const combinedText = rewriteSlackMentions(items.map((m) => m.text).join(`
185
188
  ---
186
- `);
189
+ `));
187
190
  const description = `[Thread follow-up — ${items.length} message(s) buffered]
188
191
 
189
192
  ${combinedText}`;
@@ -1,7 +1,10 @@
1
1
  import {
2
2
  getDb,
3
- init_db
4
- } from "./cli-ncfgsqbd.js";
3
+ getKv,
4
+ incrKv,
5
+ init_db,
6
+ upsertKv
7
+ } from "./cli-15y7qhzp.js";
5
8
 
6
9
  // src/be/users.ts
7
10
  init_db();
@@ -51,6 +54,18 @@ function findUserByEmail(email) {
51
54
  }
52
55
  return null;
53
56
  }
57
+ function findUsersByName(name) {
58
+ const trimmed = name.trim();
59
+ if (!trimmed)
60
+ return [];
61
+ const db = getDb();
62
+ const exact = db.prepare("SELECT * FROM users WHERE LOWER(name) = LOWER(?)").all(trimmed);
63
+ if (exact.length > 0)
64
+ return exact.map(rowToUser);
65
+ const firstToken = trimmed.split(/\s+/)[0] ?? trimmed;
66
+ const prefixed = db.prepare("SELECT * FROM users WHERE LOWER(name) LIKE LOWER(?) || '%'").all(firstToken);
67
+ return prefixed.map(rowToUser);
68
+ }
54
69
  function getUserIdentities(userId) {
55
70
  return getDb().prepare("SELECT kind, externalId FROM user_external_ids WHERE userId = ? ORDER BY kind, externalId").all(userId);
56
71
  }
@@ -179,4 +194,76 @@ function fingerprintApiKey(rawKey) {
179
194
  return `op:${sha256Hex(rawKey).slice(0, 16)}`;
180
195
  }
181
196
 
182
- export { findUserById, findUserByExternalId, findUserByEmail, getUserIdentities, listUserEvents, listUserTokens, recordIdentityEvent, findOrCreateUserByEmail, linkIdentity, unlinkIdentity, mintToken, revokeToken, resolveUserByToken, fingerprintApiKey };
197
+ // src/be/identity.ts
198
+ function resolveIdentity(kind, externalId) {
199
+ const user = findUserByExternalId(kind, externalId);
200
+ if (!user) {
201
+ return { status: "unknown", kind, externalId };
202
+ }
203
+ return {
204
+ status: "resolved",
205
+ kind,
206
+ externalId,
207
+ userId: user.id,
208
+ name: user.name,
209
+ email: user.email
210
+ };
211
+ }
212
+ function resolveIdentityByEmail(email) {
213
+ const user = findUserByEmail(email);
214
+ if (!user) {
215
+ return { status: "unknown", kind: "email", externalId: email };
216
+ }
217
+ return {
218
+ status: "resolved",
219
+ kind: "email",
220
+ externalId: email,
221
+ userId: user.id,
222
+ name: user.name,
223
+ email: user.email
224
+ };
225
+ }
226
+ function renderIdentity(resolution) {
227
+ const pair = `${resolution.kind}:${resolution.externalId}`;
228
+ if (resolution.status === "resolved") {
229
+ return `${resolution.name} (${pair})`;
230
+ }
231
+ return `${pair} (unknown user)`;
232
+ }
233
+
234
+ // src/be/unmapped-identities.ts
235
+ init_db();
236
+ var TTL_MS = 30 * 24 * 60 * 60 * 1000;
237
+ function namespace(kind) {
238
+ return `integration:unmapped:${kind}`;
239
+ }
240
+ function recordUnmappedIdentity(kind, externalId, meta) {
241
+ const ns = namespace(kind);
242
+ const now = new Date().toISOString();
243
+ const expiresAt = Date.now() + TTL_MS;
244
+ const countKey = `${externalId}:count`;
245
+ const countBefore = getKv(ns, countKey);
246
+ upsertKv({
247
+ namespace: ns,
248
+ key: `${externalId}:meta`,
249
+ value: {
250
+ lastSeenAt: now,
251
+ sampleEventType: meta.sampleEventType,
252
+ sampleContext: meta.sampleContext.slice(0, 100)
253
+ },
254
+ valueType: "json",
255
+ expiresAt
256
+ });
257
+ const incremented = incrKv(ns, countKey, 1);
258
+ if (countBefore === null) {
259
+ upsertKv({
260
+ namespace: ns,
261
+ key: countKey,
262
+ value: Number(incremented.value),
263
+ valueType: "integer",
264
+ expiresAt
265
+ });
266
+ }
267
+ }
268
+
269
+ export { findUserById, findUserByExternalId, findUsersByName, getUserIdentities, listUserEvents, listUserTokens, recordIdentityEvent, findOrCreateUserByEmail, linkIdentity, unlinkIdentity, mintToken, revokeToken, resolveUserByToken, fingerprintApiKey, resolveIdentity, resolveIdentityByEmail, renderIdentity, recordUnmappedIdentity };
@@ -6,7 +6,7 @@ import {
6
6
  getDb,
7
7
  init_db,
8
8
  isSqliteVecAvailable
9
- } from "./cli-ncfgsqbd.js";
9
+ } from "./cli-15y7qhzp.js";
10
10
  import {
11
11
  __esm,
12
12
  __export,