@slaw-ai/server 2026.611.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 (1198) hide show
  1. package/LICENSE +26 -0
  2. package/dist/adapters/builtin-adapter-types.d.ts +5 -0
  3. package/dist/adapters/builtin-adapter-types.d.ts.map +1 -0
  4. package/dist/adapters/builtin-adapter-types.js +18 -0
  5. package/dist/adapters/builtin-adapter-types.js.map +1 -0
  6. package/dist/adapters/codex-models.d.ts +5 -0
  7. package/dist/adapters/codex-models.d.ts.map +1 -0
  8. package/dist/adapters/codex-models.js +105 -0
  9. package/dist/adapters/codex-models.js.map +1 -0
  10. package/dist/adapters/cursor-models.d.ts +13 -0
  11. package/dist/adapters/cursor-models.d.ts.map +1 -0
  12. package/dist/adapters/cursor-models.js +148 -0
  13. package/dist/adapters/cursor-models.js.map +1 -0
  14. package/dist/adapters/http/execute.d.ts +3 -0
  15. package/dist/adapters/http/execute.d.ts.map +1 -0
  16. package/dist/adapters/http/execute.js +51 -0
  17. package/dist/adapters/http/execute.js.map +1 -0
  18. package/dist/adapters/http/execute.test.d.ts +2 -0
  19. package/dist/adapters/http/execute.test.d.ts.map +1 -0
  20. package/dist/adapters/http/execute.test.js +40 -0
  21. package/dist/adapters/http/execute.test.js.map +1 -0
  22. package/dist/adapters/http/index.d.ts +3 -0
  23. package/dist/adapters/http/index.d.ts.map +1 -0
  24. package/dist/adapters/http/index.js +20 -0
  25. package/dist/adapters/http/index.js.map +1 -0
  26. package/dist/adapters/http/test.d.ts +3 -0
  27. package/dist/adapters/http/test.d.ts.map +1 -0
  28. package/dist/adapters/http/test.js +106 -0
  29. package/dist/adapters/http/test.js.map +1 -0
  30. package/dist/adapters/index.d.ts +4 -0
  31. package/dist/adapters/index.d.ts.map +1 -0
  32. package/dist/adapters/index.js +3 -0
  33. package/dist/adapters/index.js.map +1 -0
  34. package/dist/adapters/plugin-loader.d.ts +28 -0
  35. package/dist/adapters/plugin-loader.d.ts.map +1 -0
  36. package/dist/adapters/plugin-loader.js +196 -0
  37. package/dist/adapters/plugin-loader.js.map +1 -0
  38. package/dist/adapters/process/execute.d.ts +3 -0
  39. package/dist/adapters/process/execute.d.ts.map +1 -0
  40. package/dist/adapters/process/execute.js +70 -0
  41. package/dist/adapters/process/execute.js.map +1 -0
  42. package/dist/adapters/process/index.d.ts +3 -0
  43. package/dist/adapters/process/index.d.ts.map +1 -0
  44. package/dist/adapters/process/index.js +23 -0
  45. package/dist/adapters/process/index.js.map +1 -0
  46. package/dist/adapters/process/test.d.ts +3 -0
  47. package/dist/adapters/process/test.d.ts.map +1 -0
  48. package/dist/adapters/process/test.js +77 -0
  49. package/dist/adapters/process/test.js.map +1 -0
  50. package/dist/adapters/registry.d.ts +69 -0
  51. package/dist/adapters/registry.d.ts.map +1 -0
  52. package/dist/adapters/registry.js +598 -0
  53. package/dist/adapters/registry.js.map +1 -0
  54. package/dist/adapters/types.d.ts +2 -0
  55. package/dist/adapters/types.d.ts.map +1 -0
  56. package/dist/adapters/types.js +2 -0
  57. package/dist/adapters/types.js.map +1 -0
  58. package/dist/adapters/utils.d.ts +43 -0
  59. package/dist/adapters/utils.d.ts.map +1 -0
  60. package/dist/adapters/utils.js +52 -0
  61. package/dist/adapters/utils.js.map +1 -0
  62. package/dist/agent-auth-jwt.d.ts +14 -0
  63. package/dist/agent-auth-jwt.d.ts.map +1 -0
  64. package/dist/agent-auth-jwt.js +117 -0
  65. package/dist/agent-auth-jwt.js.map +1 -0
  66. package/dist/app.d.ts +39 -0
  67. package/dist/app.d.ts.map +1 -0
  68. package/dist/app.js +386 -0
  69. package/dist/app.js.map +1 -0
  70. package/dist/attachment-types.d.ts +23 -0
  71. package/dist/attachment-types.d.ts.map +1 -0
  72. package/dist/attachment-types.js +98 -0
  73. package/dist/attachment-types.js.map +1 -0
  74. package/dist/auth/better-auth.d.ts +40 -0
  75. package/dist/auth/better-auth.d.ts.map +1 -0
  76. package/dist/auth/better-auth.js +148 -0
  77. package/dist/auth/better-auth.js.map +1 -0
  78. package/dist/config-file.d.ts +24 -0
  79. package/dist/config-file.d.ts.map +1 -0
  80. package/dist/config-file.js +73 -0
  81. package/dist/config-file.js.map +1 -0
  82. package/dist/config.d.ts +44 -0
  83. package/dist/config.d.ts.map +1 -0
  84. package/dist/config.js +247 -0
  85. package/dist/config.js.map +1 -0
  86. package/dist/dev-runner-worktree.d.ts +15 -0
  87. package/dist/dev-runner-worktree.d.ts.map +1 -0
  88. package/dist/dev-runner-worktree.js +101 -0
  89. package/dist/dev-runner-worktree.js.map +1 -0
  90. package/dist/dev-server-status.d.ts +33 -0
  91. package/dist/dev-server-status.d.ts.map +1 -0
  92. package/dist/dev-server-status.js +89 -0
  93. package/dist/dev-server-status.js.map +1 -0
  94. package/dist/dev-watch-ignore.d.ts +2 -0
  95. package/dist/dev-watch-ignore.d.ts.map +1 -0
  96. package/dist/dev-watch-ignore.js +36 -0
  97. package/dist/dev-watch-ignore.js.map +1 -0
  98. package/dist/errors.d.ts +12 -0
  99. package/dist/errors.d.ts.map +1 -0
  100. package/dist/errors.js +28 -0
  101. package/dist/errors.js.map +1 -0
  102. package/dist/first-admin-claim.d.ts +17 -0
  103. package/dist/first-admin-claim.d.ts.map +1 -0
  104. package/dist/first-admin-claim.js +30 -0
  105. package/dist/first-admin-claim.js.map +1 -0
  106. package/dist/home-paths.d.ts +15 -0
  107. package/dist/home-paths.d.ts.map +1 -0
  108. package/dist/home-paths.js +48 -0
  109. package/dist/home-paths.js.map +1 -0
  110. package/dist/http/body-limits.d.ts +4 -0
  111. package/dist/http/body-limits.d.ts.map +1 -0
  112. package/dist/http/body-limits.js +4 -0
  113. package/dist/http/body-limits.js.map +1 -0
  114. package/dist/index.d.ts +10 -0
  115. package/dist/index.d.ts.map +1 -0
  116. package/dist/index.js +786 -0
  117. package/dist/index.js.map +1 -0
  118. package/dist/instance-claim.d.ts +23 -0
  119. package/dist/instance-claim.d.ts.map +1 -0
  120. package/dist/instance-claim.js +126 -0
  121. package/dist/instance-claim.js.map +1 -0
  122. package/dist/lib/join-request-dedupe.d.ts +11 -0
  123. package/dist/lib/join-request-dedupe.d.ts.map +1 -0
  124. package/dist/lib/join-request-dedupe.js +49 -0
  125. package/dist/lib/join-request-dedupe.js.map +1 -0
  126. package/dist/log-redaction.d.ts +11 -0
  127. package/dist/log-redaction.d.ts.map +1 -0
  128. package/dist/log-redaction.js +122 -0
  129. package/dist/log-redaction.js.map +1 -0
  130. package/dist/middleware/auth.d.ts +12 -0
  131. package/dist/middleware/auth.d.ts.map +1 -0
  132. package/dist/middleware/auth.js +302 -0
  133. package/dist/middleware/auth.js.map +1 -0
  134. package/dist/middleware/error-handler.d.ts +17 -0
  135. package/dist/middleware/error-handler.d.ts.map +1 -0
  136. package/dist/middleware/error-handler.js +46 -0
  137. package/dist/middleware/error-handler.js.map +1 -0
  138. package/dist/middleware/http-log-policy.d.ts +2 -0
  139. package/dist/middleware/http-log-policy.d.ts.map +1 -0
  140. package/dist/middleware/http-log-policy.js +52 -0
  141. package/dist/middleware/http-log-policy.js.map +1 -0
  142. package/dist/middleware/index.d.ts +4 -0
  143. package/dist/middleware/index.d.ts.map +1 -0
  144. package/dist/middleware/index.js +4 -0
  145. package/dist/middleware/index.js.map +1 -0
  146. package/dist/middleware/logger.d.ts +4 -0
  147. package/dist/middleware/logger.d.ts.map +1 -0
  148. package/dist/middleware/logger.js +92 -0
  149. package/dist/middleware/logger.js.map +1 -0
  150. package/dist/middleware/operator-mutation-guard.d.ts +3 -0
  151. package/dist/middleware/operator-mutation-guard.d.ts.map +1 -0
  152. package/dist/middleware/operator-mutation-guard.js +70 -0
  153. package/dist/middleware/operator-mutation-guard.js.map +1 -0
  154. package/dist/middleware/private-hostname-guard.d.ts +11 -0
  155. package/dist/middleware/private-hostname-guard.d.ts.map +1 -0
  156. package/dist/middleware/private-hostname-guard.js +78 -0
  157. package/dist/middleware/private-hostname-guard.js.map +1 -0
  158. package/dist/middleware/validate.d.ts +4 -0
  159. package/dist/middleware/validate.d.ts.map +1 -0
  160. package/dist/middleware/validate.js +7 -0
  161. package/dist/middleware/validate.js.map +1 -0
  162. package/dist/onboarding-assets/default/AGENTS.md +18 -0
  163. package/dist/onboarding-assets/squad_lead/AGENTS.md +61 -0
  164. package/dist/onboarding-assets/squad_lead/HEARTBEAT.md +85 -0
  165. package/dist/onboarding-assets/squad_lead/SOUL.md +33 -0
  166. package/dist/onboarding-assets/squad_lead/TOOLS.md +3 -0
  167. package/dist/paths.d.ts +3 -0
  168. package/dist/paths.d.ts.map +1 -0
  169. package/dist/paths.js +31 -0
  170. package/dist/paths.js.map +1 -0
  171. package/dist/realtime/live-events-ws.d.ts +28 -0
  172. package/dist/realtime/live-events-ws.d.ts.map +1 -0
  173. package/dist/realtime/live-events-ws.js +187 -0
  174. package/dist/realtime/live-events-ws.js.map +1 -0
  175. package/dist/redaction.d.ts +5 -0
  176. package/dist/redaction.d.ts.map +1 -0
  177. package/dist/redaction.js +125 -0
  178. package/dist/redaction.js.map +1 -0
  179. package/dist/routes/access.d.ts +75 -0
  180. package/dist/routes/access.d.ts.map +1 -0
  181. package/dist/routes/access.js +3070 -0
  182. package/dist/routes/access.js.map +1 -0
  183. package/dist/routes/activity.d.ts +3 -0
  184. package/dist/routes/activity.d.ts.map +1 -0
  185. package/dist/routes/activity.js +90 -0
  186. package/dist/routes/activity.js.map +1 -0
  187. package/dist/routes/adapters.d.ts +16 -0
  188. package/dist/routes/adapters.d.ts.map +1 -0
  189. package/dist/routes/adapters.js +539 -0
  190. package/dist/routes/adapters.js.map +1 -0
  191. package/dist/routes/agents.d.ts +6 -0
  192. package/dist/routes/agents.d.ts.map +1 -0
  193. package/dist/routes/agents.js +2733 -0
  194. package/dist/routes/agents.js.map +1 -0
  195. package/dist/routes/approvals.d.ts +6 -0
  196. package/dist/routes/approvals.d.ts.map +1 -0
  197. package/dist/routes/approvals.js +300 -0
  198. package/dist/routes/approvals.js.map +1 -0
  199. package/dist/routes/assets.d.ts +4 -0
  200. package/dist/routes/assets.d.ts.map +1 -0
  201. package/dist/routes/assets.js +309 -0
  202. package/dist/routes/assets.js.map +1 -0
  203. package/dist/routes/auth.d.ts +3 -0
  204. package/dist/routes/auth.d.ts.map +1 -0
  205. package/dist/routes/auth.js +82 -0
  206. package/dist/routes/auth.js.map +1 -0
  207. package/dist/routes/authz.d.ts +19 -0
  208. package/dist/routes/authz.d.ts.map +1 -0
  209. package/dist/routes/authz.js +75 -0
  210. package/dist/routes/authz.js.map +1 -0
  211. package/dist/routes/botfather.d.ts +9 -0
  212. package/dist/routes/botfather.d.ts.map +1 -0
  213. package/dist/routes/botfather.js +127 -0
  214. package/dist/routes/botfather.js.map +1 -0
  215. package/dist/routes/cloud-upstreams.d.ts +5 -0
  216. package/dist/routes/cloud-upstreams.d.ts.map +1 -0
  217. package/dist/routes/cloud-upstreams.js +103 -0
  218. package/dist/routes/cloud-upstreams.js.map +1 -0
  219. package/dist/routes/costs.d.ts +11 -0
  220. package/dist/routes/costs.d.ts.map +1 -0
  221. package/dist/routes/costs.js +285 -0
  222. package/dist/routes/costs.js.map +1 -0
  223. package/dist/routes/dashboard.d.ts +3 -0
  224. package/dist/routes/dashboard.d.ts.map +1 -0
  225. package/dist/routes/dashboard.js +15 -0
  226. package/dist/routes/dashboard.js.map +1 -0
  227. package/dist/routes/environment-selection.d.ts +13 -0
  228. package/dist/routes/environment-selection.d.ts.map +1 -0
  229. package/dist/routes/environment-selection.js +30 -0
  230. package/dist/routes/environment-selection.js.map +1 -0
  231. package/dist/routes/environments.d.ts +6 -0
  232. package/dist/routes/environments.d.ts.map +1 -0
  233. package/dist/routes/environments.js +414 -0
  234. package/dist/routes/environments.js.map +1 -0
  235. package/dist/routes/execution-workspaces.d.ts +3 -0
  236. package/dist/routes/execution-workspaces.d.ts.map +1 -0
  237. package/dist/routes/execution-workspaces.js +537 -0
  238. package/dist/routes/execution-workspaces.js.map +1 -0
  239. package/dist/routes/goals.d.ts +3 -0
  240. package/dist/routes/goals.d.ts.map +1 -0
  241. package/dist/routes/goals.js +95 -0
  242. package/dist/routes/goals.js.map +1 -0
  243. package/dist/routes/health.d.ts +9 -0
  244. package/dist/routes/health.d.ts.map +1 -0
  245. package/dist/routes/health.js +143 -0
  246. package/dist/routes/health.js.map +1 -0
  247. package/dist/routes/inbox-dismissals.d.ts +3 -0
  248. package/dist/routes/inbox-dismissals.d.ts.map +1 -0
  249. package/dist/routes/inbox-dismissals.js +58 -0
  250. package/dist/routes/inbox-dismissals.js.map +1 -0
  251. package/dist/routes/index.d.ts +24 -0
  252. package/dist/routes/index.d.ts.map +1 -0
  253. package/dist/routes/index.js +24 -0
  254. package/dist/routes/index.js.map +1 -0
  255. package/dist/routes/instance-database-backups.d.ts +15 -0
  256. package/dist/routes/instance-database-backups.d.ts.map +1 -0
  257. package/dist/routes/instance-database-backups.js +12 -0
  258. package/dist/routes/instance-database-backups.js.map +1 -0
  259. package/dist/routes/instance-settings.d.ts +3 -0
  260. package/dist/routes/instance-settings.d.ts.map +1 -0
  261. package/dist/routes/instance-settings.js +110 -0
  262. package/dist/routes/instance-settings.js.map +1 -0
  263. package/dist/routes/issue-tree-control.d.ts +3 -0
  264. package/dist/routes/issue-tree-control.d.ts.map +1 -0
  265. package/dist/routes/issue-tree-control.js +373 -0
  266. package/dist/routes/issue-tree-control.js.map +1 -0
  267. package/dist/routes/issues-checkout-wakeup.d.ts +9 -0
  268. package/dist/routes/issues-checkout-wakeup.d.ts.map +1 -0
  269. package/dist/routes/issues-checkout-wakeup.js +12 -0
  270. package/dist/routes/issues-checkout-wakeup.js.map +1 -0
  271. package/dist/routes/issues.d.ts +15 -0
  272. package/dist/routes/issues.d.ts.map +1 -0
  273. package/dist/routes/issues.js +5276 -0
  274. package/dist/routes/issues.js.map +1 -0
  275. package/dist/routes/llms.d.ts +3 -0
  276. package/dist/routes/llms.d.ts.map +1 -0
  277. package/dist/routes/llms.js +80 -0
  278. package/dist/routes/llms.js.map +1 -0
  279. package/dist/routes/openapi.d.ts +4 -0
  280. package/dist/routes/openapi.d.ts.map +1 -0
  281. package/dist/routes/openapi.js +3284 -0
  282. package/dist/routes/openapi.js.map +1 -0
  283. package/dist/routes/org-chart-svg.d.ts +25 -0
  284. package/dist/routes/org-chart-svg.d.ts.map +1 -0
  285. package/dist/routes/org-chart-svg.js +656 -0
  286. package/dist/routes/org-chart-svg.js.map +1 -0
  287. package/dist/routes/plugin-ui-static.d.ts +69 -0
  288. package/dist/routes/plugin-ui-static.d.ts.map +1 -0
  289. package/dist/routes/plugin-ui-static.js +411 -0
  290. package/dist/routes/plugin-ui-static.js.map +1 -0
  291. package/dist/routes/plugins.d.ts +121 -0
  292. package/dist/routes/plugins.d.ts.map +1 -0
  293. package/dist/routes/plugins.js +2390 -0
  294. package/dist/routes/plugins.js.map +1 -0
  295. package/dist/routes/projects.d.ts +3 -0
  296. package/dist/routes/projects.d.ts.map +1 -0
  297. package/dist/routes/projects.js +566 -0
  298. package/dist/routes/projects.js.map +1 -0
  299. package/dist/routes/resource-memberships.d.ts +3 -0
  300. package/dist/routes/resource-memberships.d.ts.map +1 -0
  301. package/dist/routes/resource-memberships.js +97 -0
  302. package/dist/routes/resource-memberships.js.map +1 -0
  303. package/dist/routes/routines.d.ts +6 -0
  304. package/dist/routes/routines.d.ts.map +1 -0
  305. package/dist/routes/routines.js +411 -0
  306. package/dist/routes/routines.js.map +1 -0
  307. package/dist/routes/secrets.d.ts +3 -0
  308. package/dist/routes/secrets.d.ts.map +1 -0
  309. package/dist/routes/secrets.js +419 -0
  310. package/dist/routes/secrets.js.map +1 -0
  311. package/dist/routes/sidebar-badges.d.ts +3 -0
  312. package/dist/routes/sidebar-badges.d.ts.map +1 -0
  313. package/dist/routes/sidebar-badges.js +68 -0
  314. package/dist/routes/sidebar-badges.js.map +1 -0
  315. package/dist/routes/sidebar-preferences.d.ts +3 -0
  316. package/dist/routes/sidebar-preferences.d.ts.map +1 -0
  317. package/dist/routes/sidebar-preferences.js +63 -0
  318. package/dist/routes/sidebar-preferences.js.map +1 -0
  319. package/dist/routes/squad-import-paths.d.ts +3 -0
  320. package/dist/routes/squad-import-paths.d.ts.map +1 -0
  321. package/dist/routes/squad-import-paths.js +3 -0
  322. package/dist/routes/squad-import-paths.js.map +1 -0
  323. package/dist/routes/squad-skills.d.ts +3 -0
  324. package/dist/routes/squad-skills.d.ts.map +1 -0
  325. package/dist/routes/squad-skills.js +366 -0
  326. package/dist/routes/squad-skills.js.map +1 -0
  327. package/dist/routes/squads.d.ts +4 -0
  328. package/dist/routes/squads.d.ts.map +1 -0
  329. package/dist/routes/squads.js +450 -0
  330. package/dist/routes/squads.js.map +1 -0
  331. package/dist/routes/user-profiles.d.ts +3 -0
  332. package/dist/routes/user-profiles.d.ts.map +1 -0
  333. package/dist/routes/user-profiles.js +337 -0
  334. package/dist/routes/user-profiles.js.map +1 -0
  335. package/dist/routes/workspace-command-authz.d.ts +14 -0
  336. package/dist/routes/workspace-command-authz.d.ts.map +1 -0
  337. package/dist/routes/workspace-command-authz.js +83 -0
  338. package/dist/routes/workspace-command-authz.js.map +1 -0
  339. package/dist/routes/workspace-runtime-service-authz.d.ts +12 -0
  340. package/dist/routes/workspace-runtime-service-authz.d.ts.map +1 -0
  341. package/dist/routes/workspace-runtime-service-authz.js +96 -0
  342. package/dist/routes/workspace-runtime-service-authz.js.map +1 -0
  343. package/dist/runtime-api.d.ts +19 -0
  344. package/dist/runtime-api.d.ts.map +1 -0
  345. package/dist/runtime-api.js +137 -0
  346. package/dist/runtime-api.js.map +1 -0
  347. package/dist/secrets/aws-secrets-manager-provider.d.ts +87 -0
  348. package/dist/secrets/aws-secrets-manager-provider.d.ts.map +1 -0
  349. package/dist/secrets/aws-secrets-manager-provider.js +964 -0
  350. package/dist/secrets/aws-secrets-manager-provider.js.map +1 -0
  351. package/dist/secrets/configured-provider.d.ts +3 -0
  352. package/dist/secrets/configured-provider.d.ts.map +1 -0
  353. package/dist/secrets/configured-provider.js +8 -0
  354. package/dist/secrets/configured-provider.js.map +1 -0
  355. package/dist/secrets/external-stub-providers.d.ts +5 -0
  356. package/dist/secrets/external-stub-providers.d.ts.map +1 -0
  357. package/dist/secrets/external-stub-providers.js +71 -0
  358. package/dist/secrets/external-stub-providers.js.map +1 -0
  359. package/dist/secrets/local-encrypted-provider.d.ts +3 -0
  360. package/dist/secrets/local-encrypted-provider.d.ts.map +1 -0
  361. package/dist/secrets/local-encrypted-provider.js +244 -0
  362. package/dist/secrets/local-encrypted-provider.js.map +1 -0
  363. package/dist/secrets/provider-registry.d.ts +6 -0
  364. package/dist/secrets/provider-registry.d.ts.map +1 -0
  365. package/dist/secrets/provider-registry.js +24 -0
  366. package/dist/secrets/provider-registry.js.map +1 -0
  367. package/dist/secrets/types.d.ts +138 -0
  368. package/dist/secrets/types.d.ts.map +1 -0
  369. package/dist/secrets/types.js +36 -0
  370. package/dist/secrets/types.js.map +1 -0
  371. package/dist/services/access.d.ts +184 -0
  372. package/dist/services/access.d.ts.map +1 -0
  373. package/dist/services/access.js +542 -0
  374. package/dist/services/access.js.map +1 -0
  375. package/dist/services/activity-log.d.ts +19 -0
  376. package/dist/services/activity-log.d.ts.map +1 -0
  377. package/dist/services/activity-log.js +99 -0
  378. package/dist/services/activity-log.js.map +1 -0
  379. package/dist/services/activity.d.ts +462 -0
  380. package/dist/services/activity.d.ts.map +1 -0
  381. package/dist/services/activity.js +443 -0
  382. package/dist/services/activity.js.map +1 -0
  383. package/dist/services/adapter-plugin-store.d.ts +36 -0
  384. package/dist/services/adapter-plugin-store.d.ts.map +1 -0
  385. package/dist/services/adapter-plugin-store.js +154 -0
  386. package/dist/services/adapter-plugin-store.js.map +1 -0
  387. package/dist/services/agent-instructions.d.ts +91 -0
  388. package/dist/services/agent-instructions.d.ts.map +1 -0
  389. package/dist/services/agent-instructions.js +580 -0
  390. package/dist/services/agent-instructions.js.map +1 -0
  391. package/dist/services/agent-permissions.d.ts +6 -0
  392. package/dist/services/agent-permissions.d.ts.map +1 -0
  393. package/dist/services/agent-permissions.js +20 -0
  394. package/dist/services/agent-permissions.js.map +1 -0
  395. package/dist/services/agent-start-lock.d.ts +2 -0
  396. package/dist/services/agent-start-lock.d.ts.map +1 -0
  397. package/dist/services/agent-start-lock.js +43 -0
  398. package/dist/services/agent-start-lock.js.map +1 -0
  399. package/dist/services/agents.d.ts +2253 -0
  400. package/dist/services/agents.d.ts.map +1 -0
  401. package/dist/services/agents.js +609 -0
  402. package/dist/services/agents.js.map +1 -0
  403. package/dist/services/approvals.d.ts +546 -0
  404. package/dist/services/approvals.d.ts.map +1 -0
  405. package/dist/services/approvals.js +212 -0
  406. package/dist/services/approvals.js.map +1 -0
  407. package/dist/services/assets.d.ts +33 -0
  408. package/dist/services/assets.d.ts.map +1 -0
  409. package/dist/services/assets.js +17 -0
  410. package/dist/services/assets.js.map +1 -0
  411. package/dist/services/authorization.d.ts +67 -0
  412. package/dist/services/authorization.d.ts.map +1 -0
  413. package/dist/services/authorization.js +608 -0
  414. package/dist/services/authorization.js.map +1 -0
  415. package/dist/services/botfather/authoring-lock.d.ts +17 -0
  416. package/dist/services/botfather/authoring-lock.d.ts.map +1 -0
  417. package/dist/services/botfather/authoring-lock.js +23 -0
  418. package/dist/services/botfather/authoring-lock.js.map +1 -0
  419. package/dist/services/botfather/authoring-lock.test.d.ts +2 -0
  420. package/dist/services/botfather/authoring-lock.test.d.ts.map +1 -0
  421. package/dist/services/botfather/authoring-lock.test.js +25 -0
  422. package/dist/services/botfather/authoring-lock.test.js.map +1 -0
  423. package/dist/services/botfather/client.d.ts +26 -0
  424. package/dist/services/botfather/client.d.ts.map +1 -0
  425. package/dist/services/botfather/client.js +113 -0
  426. package/dist/services/botfather/client.js.map +1 -0
  427. package/dist/services/botfather/credentials.d.ts +15 -0
  428. package/dist/services/botfather/credentials.d.ts.map +1 -0
  429. package/dist/services/botfather/credentials.js +39 -0
  430. package/dist/services/botfather/credentials.js.map +1 -0
  431. package/dist/services/botfather/enrollment.d.ts +49 -0
  432. package/dist/services/botfather/enrollment.d.ts.map +1 -0
  433. package/dist/services/botfather/enrollment.js +145 -0
  434. package/dist/services/botfather/enrollment.js.map +1 -0
  435. package/dist/services/botfather/instance-limit-enforcement.d.ts +44 -0
  436. package/dist/services/botfather/instance-limit-enforcement.d.ts.map +1 -0
  437. package/dist/services/botfather/instance-limit-enforcement.js +83 -0
  438. package/dist/services/botfather/instance-limit-enforcement.js.map +1 -0
  439. package/dist/services/botfather/instance-limit-enforcement.test.d.ts +2 -0
  440. package/dist/services/botfather/instance-limit-enforcement.test.d.ts.map +1 -0
  441. package/dist/services/botfather/instance-limit-enforcement.test.js +66 -0
  442. package/dist/services/botfather/instance-limit-enforcement.test.js.map +1 -0
  443. package/dist/services/botfather/limits-store.d.ts +36 -0
  444. package/dist/services/botfather/limits-store.d.ts.map +1 -0
  445. package/dist/services/botfather/limits-store.js +94 -0
  446. package/dist/services/botfather/limits-store.js.map +1 -0
  447. package/dist/services/botfather/limits-store.test.d.ts +2 -0
  448. package/dist/services/botfather/limits-store.test.d.ts.map +1 -0
  449. package/dist/services/botfather/limits-store.test.js +70 -0
  450. package/dist/services/botfather/limits-store.test.js.map +1 -0
  451. package/dist/services/botfather/reporter.d.ts +41 -0
  452. package/dist/services/botfather/reporter.d.ts.map +1 -0
  453. package/dist/services/botfather/reporter.js +448 -0
  454. package/dist/services/botfather/reporter.js.map +1 -0
  455. package/dist/services/botfather/service.d.ts +84 -0
  456. package/dist/services/botfather/service.d.ts.map +1 -0
  457. package/dist/services/botfather/service.js +229 -0
  458. package/dist/services/botfather/service.js.map +1 -0
  459. package/dist/services/botfather/service.test.d.ts +2 -0
  460. package/dist/services/botfather/service.test.d.ts.map +1 -0
  461. package/dist/services/botfather/service.test.js +120 -0
  462. package/dist/services/botfather/service.test.js.map +1 -0
  463. package/dist/services/botfather/skill-catalog.d.ts +28 -0
  464. package/dist/services/botfather/skill-catalog.d.ts.map +1 -0
  465. package/dist/services/botfather/skill-catalog.js +101 -0
  466. package/dist/services/botfather/skill-catalog.js.map +1 -0
  467. package/dist/services/botfather/skill-catalog.test.d.ts +2 -0
  468. package/dist/services/botfather/skill-catalog.test.d.ts.map +1 -0
  469. package/dist/services/botfather/skill-catalog.test.js +151 -0
  470. package/dist/services/botfather/skill-catalog.test.js.map +1 -0
  471. package/dist/services/budgets.d.ts +38 -0
  472. package/dist/services/budgets.d.ts.map +1 -0
  473. package/dist/services/budgets.js +833 -0
  474. package/dist/services/budgets.js.map +1 -0
  475. package/dist/services/catalog-provenance.d.ts +7 -0
  476. package/dist/services/catalog-provenance.d.ts.map +1 -0
  477. package/dist/services/catalog-provenance.js +64 -0
  478. package/dist/services/catalog-provenance.js.map +1 -0
  479. package/dist/services/cloud-upstreams.d.ts +42 -0
  480. package/dist/services/cloud-upstreams.d.ts.map +1 -0
  481. package/dist/services/cloud-upstreams.js +1071 -0
  482. package/dist/services/cloud-upstreams.js.map +1 -0
  483. package/dist/services/costs.d.ts +127 -0
  484. package/dist/services/costs.d.ts.map +1 -0
  485. package/dist/services/costs.js +409 -0
  486. package/dist/services/costs.js.map +1 -0
  487. package/dist/services/cron.d.ts +80 -0
  488. package/dist/services/cron.d.ts.map +1 -0
  489. package/dist/services/cron.js +300 -0
  490. package/dist/services/cron.js.map +1 -0
  491. package/dist/services/dashboard.d.ts +34 -0
  492. package/dist/services/dashboard.d.ts.map +1 -0
  493. package/dist/services/dashboard.js +142 -0
  494. package/dist/services/dashboard.js.map +1 -0
  495. package/dist/services/default-agent-instructions.d.ts +9 -0
  496. package/dist/services/default-agent-instructions.d.ts.map +1 -0
  497. package/dist/services/default-agent-instructions.js +20 -0
  498. package/dist/services/default-agent-instructions.js.map +1 -0
  499. package/dist/services/document-annotations.d.ts +160 -0
  500. package/dist/services/document-annotations.d.ts.map +1 -0
  501. package/dist/services/document-annotations.js +324 -0
  502. package/dist/services/document-annotations.js.map +1 -0
  503. package/dist/services/documents.d.ts +347 -0
  504. package/dist/services/documents.d.ts.map +1 -0
  505. package/dist/services/documents.js +638 -0
  506. package/dist/services/documents.js.map +1 -0
  507. package/dist/services/environment-config.d.ts +55 -0
  508. package/dist/services/environment-config.d.ts.map +1 -0
  509. package/dist/services/environment-config.js +441 -0
  510. package/dist/services/environment-config.js.map +1 -0
  511. package/dist/services/environment-execution-target.d.ts +21 -0
  512. package/dist/services/environment-execution-target.d.ts.map +1 -0
  513. package/dist/services/environment-execution-target.js +121 -0
  514. package/dist/services/environment-execution-target.js.map +1 -0
  515. package/dist/services/environment-probe.d.ts +9 -0
  516. package/dist/services/environment-probe.d.ts.map +1 -0
  517. package/dist/services/environment-probe.js +106 -0
  518. package/dist/services/environment-probe.js.map +1 -0
  519. package/dist/services/environment-run-orchestrator.d.ts +124 -0
  520. package/dist/services/environment-run-orchestrator.d.ts.map +1 -0
  521. package/dist/services/environment-run-orchestrator.js +392 -0
  522. package/dist/services/environment-run-orchestrator.js.map +1 -0
  523. package/dist/services/environment-runtime.d.ts +90 -0
  524. package/dist/services/environment-runtime.d.ts.map +1 -0
  525. package/dist/services/environment-runtime.js +968 -0
  526. package/dist/services/environment-runtime.js.map +1 -0
  527. package/dist/services/environments.d.ts +36 -0
  528. package/dist/services/environments.d.ts.map +1 -0
  529. package/dist/services/environments.js +260 -0
  530. package/dist/services/environments.js.map +1 -0
  531. package/dist/services/execution-workspace-policy.d.ts +42 -0
  532. package/dist/services/execution-workspace-policy.d.ts.map +1 -0
  533. package/dist/services/execution-workspace-policy.js +262 -0
  534. package/dist/services/execution-workspace-policy.js.map +1 -0
  535. package/dist/services/execution-workspaces.d.ts +30 -0
  536. package/dist/services/execution-workspaces.d.ts.map +1 -0
  537. package/dist/services/execution-workspaces.js +645 -0
  538. package/dist/services/execution-workspaces.js.map +1 -0
  539. package/dist/services/finance.d.ts +93 -0
  540. package/dist/services/finance.d.ts.map +1 -0
  541. package/dist/services/finance.js +120 -0
  542. package/dist/services/finance.js.map +1 -0
  543. package/dist/services/github-fetch.d.ts +4 -0
  544. package/dist/services/github-fetch.d.ts.map +1 -0
  545. package/dist/services/github-fetch.js +23 -0
  546. package/dist/services/github-fetch.js.map +1 -0
  547. package/dist/services/goals.d.ts +433 -0
  548. package/dist/services/goals.d.ts.map +1 -0
  549. package/dist/services/goals.js +54 -0
  550. package/dist/services/goals.js.map +1 -0
  551. package/dist/services/heartbeat-circuit-breaker.d.ts +89 -0
  552. package/dist/services/heartbeat-circuit-breaker.d.ts.map +1 -0
  553. package/dist/services/heartbeat-circuit-breaker.js +156 -0
  554. package/dist/services/heartbeat-circuit-breaker.js.map +1 -0
  555. package/dist/services/heartbeat-circuit-breaker.test.d.ts +2 -0
  556. package/dist/services/heartbeat-circuit-breaker.test.d.ts.map +1 -0
  557. package/dist/services/heartbeat-circuit-breaker.test.js +97 -0
  558. package/dist/services/heartbeat-circuit-breaker.test.js.map +1 -0
  559. package/dist/services/heartbeat-run-summary.d.ts +7 -0
  560. package/dist/services/heartbeat-run-summary.d.ts.map +1 -0
  561. package/dist/services/heartbeat-run-summary.js +84 -0
  562. package/dist/services/heartbeat-run-summary.js.map +1 -0
  563. package/dist/services/heartbeat-stop-metadata.d.ts +28 -0
  564. package/dist/services/heartbeat-stop-metadata.d.ts.map +1 -0
  565. package/dist/services/heartbeat-stop-metadata.js +86 -0
  566. package/dist/services/heartbeat-stop-metadata.js.map +1 -0
  567. package/dist/services/heartbeat-stop-metadata.test.d.ts +2 -0
  568. package/dist/services/heartbeat-stop-metadata.test.d.ts.map +1 -0
  569. package/dist/services/heartbeat-stop-metadata.test.js +93 -0
  570. package/dist/services/heartbeat-stop-metadata.test.js.map +1 -0
  571. package/dist/services/heartbeat.d.ts +1578 -0
  572. package/dist/services/heartbeat.d.ts.map +1 -0
  573. package/dist/services/heartbeat.js +8274 -0
  574. package/dist/services/heartbeat.js.map +1 -0
  575. package/dist/services/hire-hook.d.ts +14 -0
  576. package/dist/services/hire-hook.d.ts.map +1 -0
  577. package/dist/services/hire-hook.js +85 -0
  578. package/dist/services/hire-hook.js.map +1 -0
  579. package/dist/services/inbox-dismissals.d.ts +22 -0
  580. package/dist/services/inbox-dismissals.d.ts.map +1 -0
  581. package/dist/services/inbox-dismissals.js +33 -0
  582. package/dist/services/inbox-dismissals.js.map +1 -0
  583. package/dist/services/index.d.ts +50 -0
  584. package/dist/services/index.d.ts.map +1 -0
  585. package/dist/services/index.js +49 -0
  586. package/dist/services/index.js.map +1 -0
  587. package/dist/services/instance-settings.d.ts +12 -0
  588. package/dist/services/instance-settings.d.ts.map +1 -0
  589. package/dist/services/instance-settings.js +142 -0
  590. package/dist/services/instance-settings.js.map +1 -0
  591. package/dist/services/invite-grants.d.ts +15 -0
  592. package/dist/services/invite-grants.d.ts.map +1 -0
  593. package/dist/services/invite-grants.js +50 -0
  594. package/dist/services/invite-grants.js.map +1 -0
  595. package/dist/services/issue-approvals.d.ts +56 -0
  596. package/dist/services/issue-approvals.d.ts.map +1 -0
  597. package/dist/services/issue-approvals.js +153 -0
  598. package/dist/services/issue-approvals.js.map +1 -0
  599. package/dist/services/issue-assignment-wakeup.d.ts +29 -0
  600. package/dist/services/issue-assignment-wakeup.d.ts.map +1 -0
  601. package/dist/services/issue-assignment-wakeup.js +22 -0
  602. package/dist/services/issue-assignment-wakeup.js.map +1 -0
  603. package/dist/services/issue-continuation-summary.d.ts +71 -0
  604. package/dist/services/issue-continuation-summary.d.ts.map +1 -0
  605. package/dist/services/issue-continuation-summary.js +222 -0
  606. package/dist/services/issue-continuation-summary.js.map +1 -0
  607. package/dist/services/issue-execution-policy.d.ts +93 -0
  608. package/dist/services/issue-execution-policy.d.ts.map +1 -0
  609. package/dist/services/issue-execution-policy.js +838 -0
  610. package/dist/services/issue-execution-policy.js.map +1 -0
  611. package/dist/services/issue-goal-fallback.d.ts +18 -0
  612. package/dist/services/issue-goal-fallback.d.ts.map +1 -0
  613. package/dist/services/issue-goal-fallback.js +33 -0
  614. package/dist/services/issue-goal-fallback.js.map +1 -0
  615. package/dist/services/issue-liveness.d.ts +3 -0
  616. package/dist/services/issue-liveness.d.ts.map +1 -0
  617. package/dist/services/issue-liveness.js +2 -0
  618. package/dist/services/issue-liveness.js.map +1 -0
  619. package/dist/services/issue-recovery-actions.d.ts +40 -0
  620. package/dist/services/issue-recovery-actions.d.ts.map +1 -0
  621. package/dist/services/issue-recovery-actions.js +204 -0
  622. package/dist/services/issue-recovery-actions.js.map +1 -0
  623. package/dist/services/issue-references.d.ts +22 -0
  624. package/dist/services/issue-references.d.ts.map +1 -0
  625. package/dist/services/issue-references.js +341 -0
  626. package/dist/services/issue-references.js.map +1 -0
  627. package/dist/services/issue-thread-interactions.d.ts +81 -0
  628. package/dist/services/issue-thread-interactions.d.ts.map +1 -0
  629. package/dist/services/issue-thread-interactions.js +1017 -0
  630. package/dist/services/issue-thread-interactions.js.map +1 -0
  631. package/dist/services/issue-thread-interactions.test.d.ts +2 -0
  632. package/dist/services/issue-thread-interactions.test.d.ts.map +1 -0
  633. package/dist/services/issue-thread-interactions.test.js +195 -0
  634. package/dist/services/issue-thread-interactions.test.js.map +1 -0
  635. package/dist/services/issue-tree-control.d.ts +89 -0
  636. package/dist/services/issue-tree-control.d.ts.map +1 -0
  637. package/dist/services/issue-tree-control.js +933 -0
  638. package/dist/services/issue-tree-control.js.map +1 -0
  639. package/dist/services/issues.d.ts +898 -0
  640. package/dist/services/issues.d.ts.map +1 -0
  641. package/dist/services/issues.js +4705 -0
  642. package/dist/services/issues.js.map +1 -0
  643. package/dist/services/json-schema-secret-refs.d.ts +5 -0
  644. package/dist/services/json-schema-secret-refs.d.ts.map +1 -0
  645. package/dist/services/json-schema-secret-refs.js +67 -0
  646. package/dist/services/json-schema-secret-refs.js.map +1 -0
  647. package/dist/services/live-events.d.ts +17 -0
  648. package/dist/services/live-events.d.ts.map +1 -0
  649. package/dist/services/live-events.js +33 -0
  650. package/dist/services/live-events.js.map +1 -0
  651. package/dist/services/local-service-supervisor.d.ts +56 -0
  652. package/dist/services/local-service-supervisor.d.ts.map +1 -0
  653. package/dist/services/local-service-supervisor.js +284 -0
  654. package/dist/services/local-service-supervisor.js.map +1 -0
  655. package/dist/services/operator-auth.d.ts +271 -0
  656. package/dist/services/operator-auth.d.ts.map +1 -0
  657. package/dist/services/operator-auth.js +361 -0
  658. package/dist/services/operator-auth.js.map +1 -0
  659. package/dist/services/plugin-capability-validator.d.ts +108 -0
  660. package/dist/services/plugin-capability-validator.d.ts.map +1 -0
  661. package/dist/services/plugin-capability-validator.js +314 -0
  662. package/dist/services/plugin-capability-validator.js.map +1 -0
  663. package/dist/services/plugin-config-validator.d.ts +26 -0
  664. package/dist/services/plugin-config-validator.d.ts.map +1 -0
  665. package/dist/services/plugin-config-validator.js +41 -0
  666. package/dist/services/plugin-config-validator.js.map +1 -0
  667. package/dist/services/plugin-database.d.ts +49 -0
  668. package/dist/services/plugin-database.d.ts.map +1 -0
  669. package/dist/services/plugin-database.js +475 -0
  670. package/dist/services/plugin-database.js.map +1 -0
  671. package/dist/services/plugin-dev-watcher.d.ts +30 -0
  672. package/dist/services/plugin-dev-watcher.d.ts.map +1 -0
  673. package/dist/services/plugin-dev-watcher.js +246 -0
  674. package/dist/services/plugin-dev-watcher.js.map +1 -0
  675. package/dist/services/plugin-environment-driver.d.ts +126 -0
  676. package/dist/services/plugin-environment-driver.d.ts.map +1 -0
  677. package/dist/services/plugin-environment-driver.js +226 -0
  678. package/dist/services/plugin-environment-driver.js.map +1 -0
  679. package/dist/services/plugin-event-bus.d.ts +149 -0
  680. package/dist/services/plugin-event-bus.d.ts.map +1 -0
  681. package/dist/services/plugin-event-bus.js +258 -0
  682. package/dist/services/plugin-event-bus.js.map +1 -0
  683. package/dist/services/plugin-host-service-cleanup.d.ts +14 -0
  684. package/dist/services/plugin-host-service-cleanup.d.ts.map +1 -0
  685. package/dist/services/plugin-host-service-cleanup.js +37 -0
  686. package/dist/services/plugin-host-service-cleanup.js.map +1 -0
  687. package/dist/services/plugin-host-services.d.ts +17 -0
  688. package/dist/services/plugin-host-services.d.ts.map +1 -0
  689. package/dist/services/plugin-host-services.js +2460 -0
  690. package/dist/services/plugin-host-services.js.map +1 -0
  691. package/dist/services/plugin-job-coordinator.d.ts +81 -0
  692. package/dist/services/plugin-job-coordinator.d.ts.map +1 -0
  693. package/dist/services/plugin-job-coordinator.js +172 -0
  694. package/dist/services/plugin-job-coordinator.js.map +1 -0
  695. package/dist/services/plugin-job-scheduler.d.ts +163 -0
  696. package/dist/services/plugin-job-scheduler.d.ts.map +1 -0
  697. package/dist/services/plugin-job-scheduler.js +454 -0
  698. package/dist/services/plugin-job-scheduler.js.map +1 -0
  699. package/dist/services/plugin-job-store.d.ts +208 -0
  700. package/dist/services/plugin-job-store.d.ts.map +1 -0
  701. package/dist/services/plugin-job-store.js +350 -0
  702. package/dist/services/plugin-job-store.js.map +1 -0
  703. package/dist/services/plugin-lifecycle.d.ts +203 -0
  704. package/dist/services/plugin-lifecycle.d.ts.map +1 -0
  705. package/dist/services/plugin-lifecycle.js +501 -0
  706. package/dist/services/plugin-lifecycle.js.map +1 -0
  707. package/dist/services/plugin-loader.d.ts +453 -0
  708. package/dist/services/plugin-loader.d.ts.map +1 -0
  709. package/dist/services/plugin-loader.js +1295 -0
  710. package/dist/services/plugin-loader.js.map +1 -0
  711. package/dist/services/plugin-local-folders.d.ts +49 -0
  712. package/dist/services/plugin-local-folders.d.ts.map +1 -0
  713. package/dist/services/plugin-local-folders.js +510 -0
  714. package/dist/services/plugin-local-folders.js.map +1 -0
  715. package/dist/services/plugin-log-retention.d.ts +20 -0
  716. package/dist/services/plugin-log-retention.d.ts.map +1 -0
  717. package/dist/services/plugin-log-retention.js +63 -0
  718. package/dist/services/plugin-log-retention.js.map +1 -0
  719. package/dist/services/plugin-managed-agents.d.ts +15 -0
  720. package/dist/services/plugin-managed-agents.d.ts.map +1 -0
  721. package/dist/services/plugin-managed-agents.js +457 -0
  722. package/dist/services/plugin-managed-agents.js.map +1 -0
  723. package/dist/services/plugin-managed-routines.d.ts +42 -0
  724. package/dist/services/plugin-managed-routines.d.ts.map +1 -0
  725. package/dist/services/plugin-managed-routines.js +416 -0
  726. package/dist/services/plugin-managed-routines.js.map +1 -0
  727. package/dist/services/plugin-managed-skills.d.ts +14 -0
  728. package/dist/services/plugin-managed-skills.d.ts.map +1 -0
  729. package/dist/services/plugin-managed-skills.js +264 -0
  730. package/dist/services/plugin-managed-skills.js.map +1 -0
  731. package/dist/services/plugin-manifest-validator.d.ts +79 -0
  732. package/dist/services/plugin-manifest-validator.d.ts.map +1 -0
  733. package/dist/services/plugin-manifest-validator.js +84 -0
  734. package/dist/services/plugin-manifest-validator.js.map +1 -0
  735. package/dist/services/plugin-registry.d.ts +2550 -0
  736. package/dist/services/plugin-registry.d.ts.map +1 -0
  737. package/dist/services/plugin-registry.js +581 -0
  738. package/dist/services/plugin-registry.js.map +1 -0
  739. package/dist/services/plugin-runtime-sandbox.d.ts +40 -0
  740. package/dist/services/plugin-runtime-sandbox.d.ts.map +1 -0
  741. package/dist/services/plugin-runtime-sandbox.js +154 -0
  742. package/dist/services/plugin-runtime-sandbox.js.map +1 -0
  743. package/dist/services/plugin-secrets-handler.d.ts +83 -0
  744. package/dist/services/plugin-secrets-handler.d.ts.map +1 -0
  745. package/dist/services/plugin-secrets-handler.js +168 -0
  746. package/dist/services/plugin-secrets-handler.js.map +1 -0
  747. package/dist/services/plugin-state-store.d.ts +92 -0
  748. package/dist/services/plugin-state-store.d.ts.map +1 -0
  749. package/dist/services/plugin-state-store.js +190 -0
  750. package/dist/services/plugin-state-store.js.map +1 -0
  751. package/dist/services/plugin-stream-bus.d.ts +29 -0
  752. package/dist/services/plugin-stream-bus.d.ts.map +1 -0
  753. package/dist/services/plugin-stream-bus.js +48 -0
  754. package/dist/services/plugin-stream-bus.js.map +1 -0
  755. package/dist/services/plugin-tool-dispatcher.d.ts +181 -0
  756. package/dist/services/plugin-tool-dispatcher.d.ts.map +1 -0
  757. package/dist/services/plugin-tool-dispatcher.js +224 -0
  758. package/dist/services/plugin-tool-dispatcher.js.map +1 -0
  759. package/dist/services/plugin-tool-registry.d.ts +192 -0
  760. package/dist/services/plugin-tool-registry.d.ts.map +1 -0
  761. package/dist/services/plugin-tool-registry.js +224 -0
  762. package/dist/services/plugin-tool-registry.js.map +1 -0
  763. package/dist/services/plugin-worker-manager.d.ts +262 -0
  764. package/dist/services/plugin-worker-manager.d.ts.map +1 -0
  765. package/dist/services/plugin-worker-manager.js +942 -0
  766. package/dist/services/plugin-worker-manager.js.map +1 -0
  767. package/dist/services/portable-path.d.ts +2 -0
  768. package/dist/services/portable-path.d.ts.map +1 -0
  769. package/dist/services/portable-path.js +15 -0
  770. package/dist/services/portable-path.js.map +1 -0
  771. package/dist/services/principal-access-compatibility.d.ts +26 -0
  772. package/dist/services/principal-access-compatibility.d.ts.map +1 -0
  773. package/dist/services/principal-access-compatibility.js +94 -0
  774. package/dist/services/principal-access-compatibility.js.map +1 -0
  775. package/dist/services/productivity-review.d.ts +83 -0
  776. package/dist/services/productivity-review.d.ts.map +1 -0
  777. package/dist/services/productivity-review.js +652 -0
  778. package/dist/services/productivity-review.js.map +1 -0
  779. package/dist/services/project-workspace-runtime-config.d.ts +4 -0
  780. package/dist/services/project-workspace-runtime-config.d.ts.map +1 -0
  781. package/dist/services/project-workspace-runtime-config.js +54 -0
  782. package/dist/services/project-workspace-runtime-config.js.map +1 -0
  783. package/dist/services/projects.d.ts +99 -0
  784. package/dist/services/projects.d.ts.map +1 -0
  785. package/dist/services/projects.js +879 -0
  786. package/dist/services/projects.js.map +1 -0
  787. package/dist/services/quota-windows.d.ts +9 -0
  788. package/dist/services/quota-windows.d.ts.map +1 -0
  789. package/dist/services/quota-windows.js +56 -0
  790. package/dist/services/quota-windows.js.map +1 -0
  791. package/dist/services/recovery/index.d.ts +10 -0
  792. package/dist/services/recovery/index.d.ts.map +1 -0
  793. package/dist/services/recovery/index.js +6 -0
  794. package/dist/services/recovery/index.js.map +1 -0
  795. package/dist/services/recovery/issue-graph-liveness.d.ts +85 -0
  796. package/dist/services/recovery/issue-graph-liveness.d.ts.map +1 -0
  797. package/dist/services/recovery/issue-graph-liveness.js +356 -0
  798. package/dist/services/recovery/issue-graph-liveness.js.map +1 -0
  799. package/dist/services/recovery/model-profile-hint.d.ts +21 -0
  800. package/dist/services/recovery/model-profile-hint.d.ts.map +1 -0
  801. package/dist/services/recovery/model-profile-hint.js +36 -0
  802. package/dist/services/recovery/model-profile-hint.js.map +1 -0
  803. package/dist/services/recovery/model-profile-hint.test.d.ts +2 -0
  804. package/dist/services/recovery/model-profile-hint.test.d.ts.map +1 -0
  805. package/dist/services/recovery/model-profile-hint.test.js +38 -0
  806. package/dist/services/recovery/model-profile-hint.test.js.map +1 -0
  807. package/dist/services/recovery/origins.d.ts +36 -0
  808. package/dist/services/recovery/origins.d.ts.map +1 -0
  809. package/dist/services/recovery/origins.js +45 -0
  810. package/dist/services/recovery/origins.js.map +1 -0
  811. package/dist/services/recovery/pause-hold-guard.d.ts +6 -0
  812. package/dist/services/recovery/pause-hold-guard.d.ts.map +1 -0
  813. package/dist/services/recovery/pause-hold-guard.js +6 -0
  814. package/dist/services/recovery/pause-hold-guard.js.map +1 -0
  815. package/dist/services/recovery/run-liveness-continuations.d.ts +50 -0
  816. package/dist/services/recovery/run-liveness-continuations.d.ts.map +1 -0
  817. package/dist/services/recovery/run-liveness-continuations.js +117 -0
  818. package/dist/services/recovery/run-liveness-continuations.js.map +1 -0
  819. package/dist/services/recovery/service.d.ts +258 -0
  820. package/dist/services/recovery/service.d.ts.map +1 -0
  821. package/dist/services/recovery/service.js +2892 -0
  822. package/dist/services/recovery/service.js.map +1 -0
  823. package/dist/services/recovery/successful-run-handoff.d.ts +89 -0
  824. package/dist/services/recovery/successful-run-handoff.d.ts.map +1 -0
  825. package/dist/services/recovery/successful-run-handoff.js +304 -0
  826. package/dist/services/recovery/successful-run-handoff.js.map +1 -0
  827. package/dist/services/recovery/successful-run-handoff.test.d.ts +2 -0
  828. package/dist/services/recovery/successful-run-handoff.test.d.ts.map +1 -0
  829. package/dist/services/recovery/successful-run-handoff.test.js +276 -0
  830. package/dist/services/recovery/successful-run-handoff.test.js.map +1 -0
  831. package/dist/services/resource-memberships.d.ts +55 -0
  832. package/dist/services/resource-memberships.d.ts.map +1 -0
  833. package/dist/services/resource-memberships.js +213 -0
  834. package/dist/services/resource-memberships.js.map +1 -0
  835. package/dist/services/routines.d.ts +170 -0
  836. package/dist/services/routines.d.ts.map +1 -0
  837. package/dist/services/routines.js +2015 -0
  838. package/dist/services/routines.js.map +1 -0
  839. package/dist/services/run-continuations.d.ts +3 -0
  840. package/dist/services/run-continuations.d.ts.map +1 -0
  841. package/dist/services/run-continuations.js +2 -0
  842. package/dist/services/run-continuations.js.map +1 -0
  843. package/dist/services/run-liveness.d.ts +46 -0
  844. package/dist/services/run-liveness.d.ts.map +1 -0
  845. package/dist/services/run-liveness.js +275 -0
  846. package/dist/services/run-liveness.js.map +1 -0
  847. package/dist/services/run-log-store.d.ts +34 -0
  848. package/dist/services/run-log-store.d.ts.map +1 -0
  849. package/dist/services/run-log-store.js +111 -0
  850. package/dist/services/run-log-store.js.map +1 -0
  851. package/dist/services/sandbox-provider-runtime.d.ts +132 -0
  852. package/dist/services/sandbox-provider-runtime.d.ts.map +1 -0
  853. package/dist/services/sandbox-provider-runtime.js +216 -0
  854. package/dist/services/sandbox-provider-runtime.js.map +1 -0
  855. package/dist/services/secrets.d.ts +1991 -0
  856. package/dist/services/secrets.d.ts.map +1 -0
  857. package/dist/services/secrets.js +1781 -0
  858. package/dist/services/secrets.js.map +1 -0
  859. package/dist/services/session-workspace-cwd.d.ts +2 -0
  860. package/dist/services/session-workspace-cwd.d.ts.map +1 -0
  861. package/dist/services/session-workspace-cwd.js +24 -0
  862. package/dist/services/session-workspace-cwd.js.map +1 -0
  863. package/dist/services/session-workspace-cwd.test.d.ts +2 -0
  864. package/dist/services/session-workspace-cwd.test.d.ts.map +1 -0
  865. package/dist/services/session-workspace-cwd.test.js +25 -0
  866. package/dist/services/session-workspace-cwd.test.js.map +1 -0
  867. package/dist/services/sidebar-badges.d.ts +14 -0
  868. package/dist/services/sidebar-badges.d.ts.map +1 -0
  869. package/dist/services/sidebar-badges.js +48 -0
  870. package/dist/services/sidebar-badges.js.map +1 -0
  871. package/dist/services/sidebar-preferences.d.ts +9 -0
  872. package/dist/services/sidebar-preferences.d.ts.map +1 -0
  873. package/dist/services/sidebar-preferences.js +82 -0
  874. package/dist/services/sidebar-preferences.js.map +1 -0
  875. package/dist/services/skills-catalog.d.ts +14 -0
  876. package/dist/services/skills-catalog.d.ts.map +1 -0
  877. package/dist/services/skills-catalog.js +171 -0
  878. package/dist/services/skills-catalog.js.map +1 -0
  879. package/dist/services/squad-export-readme.d.ts +17 -0
  880. package/dist/services/squad-export-readme.d.ts.map +1 -0
  881. package/dist/services/squad-export-readme.js +148 -0
  882. package/dist/services/squad-export-readme.js.map +1 -0
  883. package/dist/services/squad-member-roles.d.ts +9 -0
  884. package/dist/services/squad-member-roles.d.ts.map +1 -0
  885. package/dist/services/squad-member-roles.js +48 -0
  886. package/dist/services/squad-member-roles.js.map +1 -0
  887. package/dist/services/squad-portability.d.ts +24 -0
  888. package/dist/services/squad-portability.d.ts.map +1 -0
  889. package/dist/services/squad-portability.js +4093 -0
  890. package/dist/services/squad-portability.js.map +1 -0
  891. package/dist/services/squad-search-rate-limit.d.ts +22 -0
  892. package/dist/services/squad-search-rate-limit.d.ts.map +1 -0
  893. package/dist/services/squad-search-rate-limit.js +38 -0
  894. package/dist/services/squad-search-rate-limit.js.map +1 -0
  895. package/dist/services/squad-search.d.ts +8 -0
  896. package/dist/services/squad-search.d.ts.map +1 -0
  897. package/dist/services/squad-search.js +626 -0
  898. package/dist/services/squad-search.js.map +1 -0
  899. package/dist/services/squad-skills.d.ts +107 -0
  900. package/dist/services/squad-skills.d.ts.map +1 -0
  901. package/dist/services/squad-skills.js +3044 -0
  902. package/dist/services/squad-skills.js.map +1 -0
  903. package/dist/services/squads.d.ts +154 -0
  904. package/dist/services/squads.d.ts.map +1 -0
  905. package/dist/services/squads.js +278 -0
  906. package/dist/services/squads.js.map +1 -0
  907. package/dist/services/wake-cycle-guard.d.ts +44 -0
  908. package/dist/services/wake-cycle-guard.d.ts.map +1 -0
  909. package/dist/services/wake-cycle-guard.js +79 -0
  910. package/dist/services/wake-cycle-guard.js.map +1 -0
  911. package/dist/services/wake-cycle-guard.test.d.ts +2 -0
  912. package/dist/services/wake-cycle-guard.test.d.ts.map +1 -0
  913. package/dist/services/wake-cycle-guard.test.js +67 -0
  914. package/dist/services/wake-cycle-guard.test.js.map +1 -0
  915. package/dist/services/work-products.d.ts +14 -0
  916. package/dist/services/work-products.d.ts.map +1 -0
  917. package/dist/services/work-products.js +100 -0
  918. package/dist/services/work-products.js.map +1 -0
  919. package/dist/services/workspace-operation-log-store.d.ts +33 -0
  920. package/dist/services/workspace-operation-log-store.d.ts.map +1 -0
  921. package/dist/services/workspace-operation-log-store.js +110 -0
  922. package/dist/services/workspace-operation-log-store.js.map +1 -0
  923. package/dist/services/workspace-operations.d.ts +44 -0
  924. package/dist/services/workspace-operations.d.ts.map +1 -0
  925. package/dist/services/workspace-operations.js +211 -0
  926. package/dist/services/workspace-operations.js.map +1 -0
  927. package/dist/services/workspace-realization.d.ts +33 -0
  928. package/dist/services/workspace-realization.d.ts.map +1 -0
  929. package/dist/services/workspace-realization.js +221 -0
  930. package/dist/services/workspace-realization.js.map +1 -0
  931. package/dist/services/workspace-runtime-read-model.d.ts +92 -0
  932. package/dist/services/workspace-runtime-read-model.d.ts.map +1 -0
  933. package/dist/services/workspace-runtime-read-model.js +67 -0
  934. package/dist/services/workspace-runtime-read-model.js.map +1 -0
  935. package/dist/services/workspace-runtime.d.ts +252 -0
  936. package/dist/services/workspace-runtime.d.ts.map +1 -0
  937. package/dist/services/workspace-runtime.js +2519 -0
  938. package/dist/services/workspace-runtime.js.map +1 -0
  939. package/dist/startup-banner.d.ts +32 -0
  940. package/dist/startup-banner.d.ts.map +1 -0
  941. package/dist/startup-banner.js +118 -0
  942. package/dist/startup-banner.js.map +1 -0
  943. package/dist/static-index-html.d.ts +2 -0
  944. package/dist/static-index-html.d.ts.map +1 -0
  945. package/dist/static-index-html.js +7 -0
  946. package/dist/static-index-html.js.map +1 -0
  947. package/dist/storage/index.d.ts +6 -0
  948. package/dist/storage/index.d.ts.map +1 -0
  949. package/dist/storage/index.js +29 -0
  950. package/dist/storage/index.js.map +1 -0
  951. package/dist/storage/local-disk-provider.d.ts +3 -0
  952. package/dist/storage/local-disk-provider.d.ts.map +1 -0
  953. package/dist/storage/local-disk-provider.js +85 -0
  954. package/dist/storage/local-disk-provider.js.map +1 -0
  955. package/dist/storage/provider-registry.d.ts +4 -0
  956. package/dist/storage/provider-registry.d.ts.map +1 -0
  957. package/dist/storage/provider-registry.js +15 -0
  958. package/dist/storage/provider-registry.js.map +1 -0
  959. package/dist/storage/s3-provider.d.ts +11 -0
  960. package/dist/storage/s3-provider.d.ts.map +1 -0
  961. package/dist/storage/s3-provider.js +124 -0
  962. package/dist/storage/s3-provider.js.map +1 -0
  963. package/dist/storage/service.d.ts +3 -0
  964. package/dist/storage/service.d.ts.map +1 -0
  965. package/dist/storage/service.js +120 -0
  966. package/dist/storage/service.js.map +1 -0
  967. package/dist/storage/types.d.ts +59 -0
  968. package/dist/storage/types.d.ts.map +1 -0
  969. package/dist/storage/types.js +2 -0
  970. package/dist/storage/types.js.map +1 -0
  971. package/dist/ui-branding.d.ts +13 -0
  972. package/dist/ui-branding.d.ts.map +1 -0
  973. package/dist/ui-branding.js +187 -0
  974. package/dist/ui-branding.js.map +1 -0
  975. package/dist/version.d.ts +2 -0
  976. package/dist/version.d.ts.map +1 -0
  977. package/dist/version.js +5 -0
  978. package/dist/version.js.map +1 -0
  979. package/dist/vite-html-renderer.d.ts +18 -0
  980. package/dist/vite-html-renderer.d.ts.map +1 -0
  981. package/dist/vite-html-renderer.js +61 -0
  982. package/dist/vite-html-renderer.js.map +1 -0
  983. package/dist/worktree-config.d.ts +19 -0
  984. package/dist/worktree-config.d.ts.map +1 -0
  985. package/dist/worktree-config.js +373 -0
  986. package/dist/worktree-config.js.map +1 -0
  987. package/package.json +92 -0
  988. package/skills/diagnose-why-work-stopped/SKILL.md +161 -0
  989. package/skills/para-memory-files/SKILL.md +104 -0
  990. package/skills/para-memory-files/references/schemas.md +35 -0
  991. package/skills/slaw/SKILL.md +371 -0
  992. package/skills/slaw/references/api-reference.md +879 -0
  993. package/skills/slaw/references/artifacts.md +44 -0
  994. package/skills/slaw/references/issue-workspaces.md +80 -0
  995. package/skills/slaw/references/routines.md +187 -0
  996. package/skills/slaw/references/squad-skills.md +258 -0
  997. package/skills/slaw/references/workflows.md +113 -0
  998. package/skills/slaw/scripts/slaw-upload-artifact.sh +371 -0
  999. package/skills/slaw-converting-plans-to-tasks/SKILL.md +42 -0
  1000. package/skills/slaw-create-agent/SKILL.md +163 -0
  1001. package/skills/slaw-create-agent/references/agent-instruction-templates.md +123 -0
  1002. package/skills/slaw-create-agent/references/agents/coder.md +64 -0
  1003. package/skills/slaw-create-agent/references/agents/qa.md +88 -0
  1004. package/skills/slaw-create-agent/references/agents/securityengineer.md +135 -0
  1005. package/skills/slaw-create-agent/references/agents/uxdesigner.md +115 -0
  1006. package/skills/slaw-create-agent/references/api-reference.md +110 -0
  1007. package/skills/slaw-create-agent/references/baseline-role-guide.md +168 -0
  1008. package/skills/slaw-create-agent/references/draft-review-checklist.md +95 -0
  1009. package/skills/slaw-create-plugin/SKILL.md +154 -0
  1010. package/skills/slaw-dev/SKILL.md +267 -0
  1011. package/skills/terminal-bench-loop/SKILL.md +236 -0
  1012. package/ui-dist/android-chrome-192x192.png +0 -0
  1013. package/ui-dist/android-chrome-512x512.png +0 -0
  1014. package/ui-dist/apple-touch-icon.png +0 -0
  1015. package/ui-dist/assets/apl-B4CMkyY2.js +1 -0
  1016. package/ui-dist/assets/arc-xbLjL0VN.js +1 -0
  1017. package/ui-dist/assets/architectureDiagram-3BPJPVTR-KcFd4B-U.js +36 -0
  1018. package/ui-dist/assets/asciiarmor-Df11BRmG.js +1 -0
  1019. package/ui-dist/assets/asn1-EdZsLKOL.js +1 -0
  1020. package/ui-dist/assets/asterisk-B-8jnY81.js +1 -0
  1021. package/ui-dist/assets/blockDiagram-GPEHLZMM-CSD4otEL.js +132 -0
  1022. package/ui-dist/assets/brainfuck-C4LP7Hcl.js +1 -0
  1023. package/ui-dist/assets/c4Diagram-AAUBKEIU-Cre_NEHp.js +10 -0
  1024. package/ui-dist/assets/channel-BFN8obi8.js +1 -0
  1025. package/ui-dist/assets/chunk-2J33WTMH-CssLBsbh.js +1 -0
  1026. package/ui-dist/assets/chunk-4BX2VUAB-DjiavNFv.js +1 -0
  1027. package/ui-dist/assets/chunk-55IACEB6-C_F0yeYq.js +1 -0
  1028. package/ui-dist/assets/chunk-727SXJPM-B1FAOW4a.js +206 -0
  1029. package/ui-dist/assets/chunk-AQP2D5EJ-Do1241W-.js +231 -0
  1030. package/ui-dist/assets/chunk-FMBD7UC4-BQRrOMZD.js +15 -0
  1031. package/ui-dist/assets/chunk-ND2GUHAM-BPSt3kZ1.js +1 -0
  1032. package/ui-dist/assets/chunk-QZHKN3VN-BSpmhWDD.js +1 -0
  1033. package/ui-dist/assets/classDiagram-4FO5ZUOK-1Ay0zFCU.js +1 -0
  1034. package/ui-dist/assets/classDiagram-v2-Q7XG4LA2-1Ay0zFCU.js +1 -0
  1035. package/ui-dist/assets/clike-B9uivgTg.js +1 -0
  1036. package/ui-dist/assets/clojure-BMjYHr_A.js +1 -0
  1037. package/ui-dist/assets/cmake-BQqOBYOt.js +1 -0
  1038. package/ui-dist/assets/cobol-CWcv1MsR.js +1 -0
  1039. package/ui-dist/assets/coffeescript-S37ZYGWr.js +1 -0
  1040. package/ui-dist/assets/commonlisp-DBKNyK5s.js +1 -0
  1041. package/ui-dist/assets/cose-bilkent-S5V4N54A-CK2f2Te4.js +1 -0
  1042. package/ui-dist/assets/crystal-SjHAIU92.js +1 -0
  1043. package/ui-dist/assets/css-BnMrqG3P.js +1 -0
  1044. package/ui-dist/assets/cypher-C_CwsFkJ.js +1 -0
  1045. package/ui-dist/assets/cytoscape.esm-D8joxN9f.js +321 -0
  1046. package/ui-dist/assets/d-pRatUO7H.js +1 -0
  1047. package/ui-dist/assets/dagre-BM42HDAG-DaOXTN9-.js +4 -0
  1048. package/ui-dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  1049. package/ui-dist/assets/diagram-2AECGRRQ-D0ScQUGy.js +43 -0
  1050. package/ui-dist/assets/diagram-5GNKFQAL-7mH4Cncd.js +10 -0
  1051. package/ui-dist/assets/diagram-KO2AKTUF-aA9kuK-7.js +3 -0
  1052. package/ui-dist/assets/diagram-LMA3HP47-C9UXfmdK.js +24 -0
  1053. package/ui-dist/assets/diagram-OG6HWLK6-Ba3U-x1r.js +24 -0
  1054. package/ui-dist/assets/diff-DbItnlRl.js +1 -0
  1055. package/ui-dist/assets/dockerfile-BKs6k2Af.js +1 -0
  1056. package/ui-dist/assets/dtd-DF_7sFjM.js +1 -0
  1057. package/ui-dist/assets/dylan-DwRh75JA.js +1 -0
  1058. package/ui-dist/assets/ebnf-CDyGwa7X.js +1 -0
  1059. package/ui-dist/assets/ecl-Cabwm37j.js +1 -0
  1060. package/ui-dist/assets/eiffel-CnydiIhH.js +1 -0
  1061. package/ui-dist/assets/elm-vLlmbW-K.js +1 -0
  1062. package/ui-dist/assets/erDiagram-TEJ5UH35-CmskPKH1.js +85 -0
  1063. package/ui-dist/assets/erlang-BNw1qcRV.js +1 -0
  1064. package/ui-dist/assets/factor-kuTfRLto.js +1 -0
  1065. package/ui-dist/assets/fcl-Kvtd6kyn.js +1 -0
  1066. package/ui-dist/assets/flowDiagram-I6XJVG4X-B0iEPqGd.js +162 -0
  1067. package/ui-dist/assets/forth-Ffai-XNe.js +1 -0
  1068. package/ui-dist/assets/fortran-DYz_wnZ1.js +1 -0
  1069. package/ui-dist/assets/ganttDiagram-6RSMTGT7-DtpxlgWQ.js +292 -0
  1070. package/ui-dist/assets/gas-Bneqetm1.js +1 -0
  1071. package/ui-dist/assets/gherkin-heZmZLOM.js +1 -0
  1072. package/ui-dist/assets/gitGraphDiagram-PVQCEYII-VefBjqya.js +106 -0
  1073. package/ui-dist/assets/graph-CAnANduQ.js +1 -0
  1074. package/ui-dist/assets/groovy-D9Dt4D0W.js +1 -0
  1075. package/ui-dist/assets/haskell-Cw1EW3IL.js +1 -0
  1076. package/ui-dist/assets/haxe-H-WmDvRZ.js +1 -0
  1077. package/ui-dist/assets/http-DBlCnlav.js +1 -0
  1078. package/ui-dist/assets/idl-BEugSyMb.js +1 -0
  1079. package/ui-dist/assets/index-B9KxOFt-.js +1 -0
  1080. package/ui-dist/assets/index-BMPCuc-W.js +1 -0
  1081. package/ui-dist/assets/index-Bbfs2D7R.js +1 -0
  1082. package/ui-dist/assets/index-BrgHE5Lg.js +1 -0
  1083. package/ui-dist/assets/index-C5q-Cwlp.js +7 -0
  1084. package/ui-dist/assets/index-C6LpKpr3.js +1 -0
  1085. package/ui-dist/assets/index-CIzt5DFV.js +1 -0
  1086. package/ui-dist/assets/index-CRwAuYPj.js +1 -0
  1087. package/ui-dist/assets/index-CTEnIXsJ.js +1 -0
  1088. package/ui-dist/assets/index-CXGemv2V.js +1 -0
  1089. package/ui-dist/assets/index-ClDiS51u.js +1 -0
  1090. package/ui-dist/assets/index-CvKYfvpz.js +1 -0
  1091. package/ui-dist/assets/index-D2IqxlXD.js +1 -0
  1092. package/ui-dist/assets/index-D97fJMFR.js +522 -0
  1093. package/ui-dist/assets/index-DDHdUa2f.js +1 -0
  1094. package/ui-dist/assets/index-DMZ0QXqi.js +1 -0
  1095. package/ui-dist/assets/index-DMi4KpxO.js +6 -0
  1096. package/ui-dist/assets/index-DZB48Gve.js +1 -0
  1097. package/ui-dist/assets/index-Drr9zRdK.css +1 -0
  1098. package/ui-dist/assets/index-DtGqpE43.js +1 -0
  1099. package/ui-dist/assets/index-Du18kURt.js +2 -0
  1100. package/ui-dist/assets/index-KaLXuTqA.js +1 -0
  1101. package/ui-dist/assets/index-j5NgiILm.js +13 -0
  1102. package/ui-dist/assets/index-u0SfLZ3g.js +3 -0
  1103. package/ui-dist/assets/infoDiagram-5YYISTIA-D2OGH-dO.js +2 -0
  1104. package/ui-dist/assets/init-Gi6I4Gst.js +1 -0
  1105. package/ui-dist/assets/ishikawaDiagram-YF4QCWOH-CnMf3BJj.js +70 -0
  1106. package/ui-dist/assets/javascript-iXu5QeM3.js +1 -0
  1107. package/ui-dist/assets/journeyDiagram-JHISSGLW-BaXdD53T.js +139 -0
  1108. package/ui-dist/assets/julia-DuME0IfC.js +1 -0
  1109. package/ui-dist/assets/kanban-definition-UN3LZRKU-Brt7LjHm.js +89 -0
  1110. package/ui-dist/assets/katex-yT8l5JNH.js +257 -0
  1111. package/ui-dist/assets/layout-DGIYPm2g.js +1 -0
  1112. package/ui-dist/assets/linear-536T6Mkh.js +1 -0
  1113. package/ui-dist/assets/livescript-BwQOo05w.js +1 -0
  1114. package/ui-dist/assets/lua-VAEuO923.js +1 -0
  1115. package/ui-dist/assets/mathematica-DTrFuWx2.js +1 -0
  1116. package/ui-dist/assets/mbox-CNhZ1qSd.js +1 -0
  1117. package/ui-dist/assets/mermaid.core-CURTLVBm.js +303 -0
  1118. package/ui-dist/assets/mindmap-definition-RKZ34NQL-S2tDCU-U.js +96 -0
  1119. package/ui-dist/assets/mirc-CjQqDB4T.js +1 -0
  1120. package/ui-dist/assets/mllike-CXdrOF99.js +1 -0
  1121. package/ui-dist/assets/modelica-Dc1JOy9r.js +1 -0
  1122. package/ui-dist/assets/mscgen-BA5vi2Kp.js +1 -0
  1123. package/ui-dist/assets/mumps-BT43cFF4.js +1 -0
  1124. package/ui-dist/assets/nginx-DdIZxoE0.js +1 -0
  1125. package/ui-dist/assets/nsis-LdVXkNf5.js +1 -0
  1126. package/ui-dist/assets/ntriples-BfvgReVJ.js +1 -0
  1127. package/ui-dist/assets/octave-Ck1zUtKM.js +1 -0
  1128. package/ui-dist/assets/ordinal-Cboi1Yqb.js +1 -0
  1129. package/ui-dist/assets/oz-BzwKVEFT.js +1 -0
  1130. package/ui-dist/assets/pascal--L3eBynH.js +1 -0
  1131. package/ui-dist/assets/perl-CdXCOZ3F.js +1 -0
  1132. package/ui-dist/assets/pieDiagram-4H26LBE5-DD_Ih32z.js +30 -0
  1133. package/ui-dist/assets/pig-CevX1Tat.js +1 -0
  1134. package/ui-dist/assets/powershell-CFHJl5sT.js +1 -0
  1135. package/ui-dist/assets/properties-C78fOPTZ.js +1 -0
  1136. package/ui-dist/assets/protobuf-ChK-085T.js +1 -0
  1137. package/ui-dist/assets/pug-DeIclll2.js +1 -0
  1138. package/ui-dist/assets/puppet-DMA9R1ak.js +1 -0
  1139. package/ui-dist/assets/python-BuPzkPfP.js +1 -0
  1140. package/ui-dist/assets/q-pXgVlZs6.js +1 -0
  1141. package/ui-dist/assets/quadrantDiagram-W4KKPZXB-DA5BPBIK.js +7 -0
  1142. package/ui-dist/assets/r-B6wPVr8A.js +1 -0
  1143. package/ui-dist/assets/requirementDiagram-4Y6WPE33-Em8SPCro.js +84 -0
  1144. package/ui-dist/assets/rpm-CTu-6PCP.js +1 -0
  1145. package/ui-dist/assets/ruby-B2Rjki9n.js +1 -0
  1146. package/ui-dist/assets/sankeyDiagram-5OEKKPKP-BJVC4haY.js +40 -0
  1147. package/ui-dist/assets/sas-B4kiWyti.js +1 -0
  1148. package/ui-dist/assets/scheme-C41bIUwD.js +1 -0
  1149. package/ui-dist/assets/sequenceDiagram-3UESZ5HK-Cskntadf.js +162 -0
  1150. package/ui-dist/assets/shell-CjFT_Tl9.js +1 -0
  1151. package/ui-dist/assets/sieve-C3Gn_uJK.js +1 -0
  1152. package/ui-dist/assets/simple-mode-GW_nhZxv.js +1 -0
  1153. package/ui-dist/assets/smalltalk-CnHTOXQT.js +1 -0
  1154. package/ui-dist/assets/solr-DehyRSwq.js +1 -0
  1155. package/ui-dist/assets/sparql-DkYu6x3z.js +1 -0
  1156. package/ui-dist/assets/spreadsheet-BCZA_wO0.js +1 -0
  1157. package/ui-dist/assets/sql-D0XecflT.js +1 -0
  1158. package/ui-dist/assets/stateDiagram-AJRCARHV-CxlfdaOi.js +1 -0
  1159. package/ui-dist/assets/stateDiagram-v2-BHNVJYJU-eTgftUjW.js +1 -0
  1160. package/ui-dist/assets/stex-C3f8Ysf7.js +1 -0
  1161. package/ui-dist/assets/stylus-B533Al4x.js +1 -0
  1162. package/ui-dist/assets/swift-BzpIVaGY.js +1 -0
  1163. package/ui-dist/assets/tcl-DVfN8rqt.js +1 -0
  1164. package/ui-dist/assets/textile-CnDTJFAw.js +1 -0
  1165. package/ui-dist/assets/tiddlywiki-DO-Gjzrf.js +1 -0
  1166. package/ui-dist/assets/tiki-DGYXhP31.js +1 -0
  1167. package/ui-dist/assets/timeline-definition-PNZ67QCA-LOdaWSSa.js +120 -0
  1168. package/ui-dist/assets/toml-Bm5Em-hy.js +1 -0
  1169. package/ui-dist/assets/troff-wAsdV37c.js +1 -0
  1170. package/ui-dist/assets/ttcn-CfJYG6tj.js +1 -0
  1171. package/ui-dist/assets/ttcn-cfg-B9xdYoR4.js +1 -0
  1172. package/ui-dist/assets/turtle-B1tBg_DP.js +1 -0
  1173. package/ui-dist/assets/vb-CmGdzxic.js +1 -0
  1174. package/ui-dist/assets/vbscript-BuJXcnF6.js +1 -0
  1175. package/ui-dist/assets/velocity-D8B20fx6.js +1 -0
  1176. package/ui-dist/assets/vennDiagram-CIIHVFJN-CJ4ji6B3.js +34 -0
  1177. package/ui-dist/assets/verilog-C6RDOZhf.js +1 -0
  1178. package/ui-dist/assets/vhdl-lSbBsy5d.js +1 -0
  1179. package/ui-dist/assets/wardley-L42UT6IY-CxnVdUVH.js +153 -0
  1180. package/ui-dist/assets/wardleyDiagram-YWT4CUSO-CgGDttpl.js +78 -0
  1181. package/ui-dist/assets/webidl-ZXfAyPTL.js +1 -0
  1182. package/ui-dist/assets/xquery-DzFWVndE.js +1 -0
  1183. package/ui-dist/assets/xychartDiagram-2RQKCTM6-zuQa7bqx.js +7 -0
  1184. package/ui-dist/assets/yacas-BJ4BC0dw.js +1 -0
  1185. package/ui-dist/assets/z80-Hz9HOZM7.js +1 -0
  1186. package/ui-dist/brands/opencode-logo-dark-square.svg +18 -0
  1187. package/ui-dist/brands/opencode-logo-light-square.svg +18 -0
  1188. package/ui-dist/favicon-16x16.png +0 -0
  1189. package/ui-dist/favicon-32x32.png +0 -0
  1190. package/ui-dist/favicon.ico +0 -0
  1191. package/ui-dist/favicon.svg +8 -0
  1192. package/ui-dist/index.html +46 -0
  1193. package/ui-dist/site.webmanifest +30 -0
  1194. package/ui-dist/sw.js +42 -0
  1195. package/ui-dist/worktree-favicon-16x16.png +0 -0
  1196. package/ui-dist/worktree-favicon-32x32.png +0 -0
  1197. package/ui-dist/worktree-favicon.ico +0 -0
  1198. package/ui-dist/worktree-favicon.svg +9 -0
@@ -0,0 +1,3070 @@
1
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { lookup as dnsLookup } from "node:dns/promises";
3
+ import fs from "node:fs";
4
+ import { request as httpRequest } from "node:http";
5
+ import { request as httpsRequest } from "node:https";
6
+ import { isIP } from "node:net";
7
+ import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import { Router } from "express";
10
+ import { and, desc, eq, gt, inArray, isNotNull, isNull, lte, ne, sql } from "drizzle-orm";
11
+ import { assets, agentApiKeys, authUsers, squads, squadLogos, squadMemberships, instanceUserRoles, invites, joinRequests, principalPermissionGrants, } from "@slaw-ai/db";
12
+ import { acceptInviteSchema, createCliAuthChallengeSchema, claimJoinRequestApiKeySchema, createOperatorApiKeySchema, createSquadInviteSchema, listSquadInvitesQuerySchema, listJoinRequestsQuerySchema, resolveCliAuthChallengeSchema, searchAdminUsersQuerySchema, updateSquadMemberWithPermissionsSchema, updateSquadMemberSchema, archiveSquadMemberSchema, updateMemberPermissionsSchema, updateUserSquadAccessSchema, PERMISSION_KEYS, isUuidLike, } from "@slaw-ai/shared";
13
+ import { forbidden, conflict, notFound, unauthorized, badRequest } from "../errors.js";
14
+ import { logger } from "../middleware/logger.js";
15
+ import { validate } from "../middleware/validate.js";
16
+ import { collectReachableInterfaceHosts } from "../runtime-api.js";
17
+ import { accessService, agentService, operatorAuthService, deduplicateAgentName, logActivity, notifyHireApproved } from "../services/index.js";
18
+ import { grantsForHumanRole, normalizeHumanRole, resolveHumanInviteRole, } from "../services/squad-member-roles.js";
19
+ import { humanJoinGrantsFromDefaults } from "../services/invite-grants.js";
20
+ import { collapseDuplicatePendingHumanJoinRequests, findReusableHumanJoinRequest, } from "../lib/join-request-dedupe.js";
21
+ import { assertAuthenticated, assertSquadAccess } from "./authz.js";
22
+ import { claimInstanceOwnership, inspectInstanceClaimChallenge } from "../instance-claim.js";
23
+ import { claimFirstInstanceAdmin } from "../first-admin-claim.js";
24
+ import { getStorageService } from "../storage/index.js";
25
+ function hashToken(token) {
26
+ return createHash("sha256").update(token).digest("hex");
27
+ }
28
+ const INVITE_TOKEN_PREFIX = "pcp_invite_";
29
+ const INVITE_TOKEN_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
30
+ const INVITE_TOKEN_SUFFIX_LENGTH = 8;
31
+ const INVITE_TOKEN_MAX_RETRIES = 5;
32
+ const SQUAD_INVITE_TTL_MS = 72 * 60 * 60 * 1000;
33
+ const INVITE_RESOLUTION_DNS_TIMEOUT_MS = 3_000;
34
+ function createInviteToken() {
35
+ const bytes = randomBytes(INVITE_TOKEN_SUFFIX_LENGTH);
36
+ let suffix = "";
37
+ for (let idx = 0; idx < INVITE_TOKEN_SUFFIX_LENGTH; idx += 1) {
38
+ suffix += INVITE_TOKEN_ALPHABET[bytes[idx] % INVITE_TOKEN_ALPHABET.length];
39
+ }
40
+ return `${INVITE_TOKEN_PREFIX}${suffix}`;
41
+ }
42
+ function createClaimSecret() {
43
+ return `pcp_claim_${randomBytes(24).toString("hex")}`;
44
+ }
45
+ export function squadInviteExpiresAt(nowMs = Date.now()) {
46
+ return new Date(nowMs + SQUAD_INVITE_TTL_MS);
47
+ }
48
+ function tokenHashesMatch(left, right) {
49
+ const leftBytes = Buffer.from(left, "utf8");
50
+ const rightBytes = Buffer.from(right, "utf8");
51
+ return (leftBytes.length === rightBytes.length &&
52
+ timingSafeEqual(leftBytes, rightBytes));
53
+ }
54
+ function requestBaseUrl(req) {
55
+ const forwardedProto = req.header("x-forwarded-proto");
56
+ const proto = forwardedProto?.split(",")[0]?.trim() || req.protocol || "http";
57
+ const host = req.header("x-forwarded-host")?.split(",")[0]?.trim() || req.header("host");
58
+ if (!host)
59
+ return "";
60
+ return `${proto}://${host}`;
61
+ }
62
+ function buildCliAuthApprovalPath(challengeId, token) {
63
+ return `/cli-auth/${challengeId}?token=${encodeURIComponent(token)}`;
64
+ }
65
+ function readSkillMarkdown(skillName) {
66
+ const normalized = skillName.trim().toLowerCase();
67
+ if (!isSafeSkillName(normalized)) {
68
+ return null;
69
+ }
70
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
71
+ const claudeSkillsDir = resolveClaudeSkillsDir();
72
+ const candidates = [
73
+ claudeSkillsDir ? path.resolve(claudeSkillsDir, normalized, "SKILL.md") : null,
74
+ path.resolve(moduleDir, "../../skills", normalized, "SKILL.md"), // published: dist/routes/ -> <pkg>/skills/
75
+ path.resolve(process.cwd(), "skills", normalized, "SKILL.md"), // cwd (e.g. monorepo root)
76
+ path.resolve(moduleDir, "../../../skills", normalized, "SKILL.md") // dev: src/routes/ -> repo root/skills/
77
+ ].filter((candidate) => Boolean(candidate));
78
+ for (const skillPath of candidates) {
79
+ try {
80
+ return fs.readFileSync(skillPath, "utf8");
81
+ }
82
+ catch {
83
+ // Continue to next candidate.
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+ function isSafeSkillName(skillName) {
89
+ return /^[a-z0-9][a-z0-9._-]*$/.test(skillName);
90
+ }
91
+ /** Resolve the Slaw repo skills directory (built-in / managed skills). */
92
+ function resolveSlawSkillsDir() {
93
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
94
+ const candidates = [
95
+ path.resolve(moduleDir, "../../skills"), // published
96
+ path.resolve(process.cwd(), "skills"), // cwd (monorepo root)
97
+ path.resolve(moduleDir, "../../../skills"), // dev
98
+ ];
99
+ for (const candidate of candidates) {
100
+ try {
101
+ if (fs.statSync(candidate).isDirectory())
102
+ return candidate;
103
+ }
104
+ catch { /* skip */ }
105
+ }
106
+ return null;
107
+ }
108
+ /** Parse YAML frontmatter from a SKILL.md file to extract the description. */
109
+ function parseSkillFrontmatter(markdown) {
110
+ const match = markdown.match(/^---\n([\s\S]*?)\n---/);
111
+ if (!match)
112
+ return { description: "" };
113
+ const yaml = match[1];
114
+ // Extract description — handles both single-line and multi-line YAML values
115
+ const descMatch = yaml.match(/^description:\s*(?:>\s*\n((?:\s{2,}[^\n]*\n?)+)|[|]\s*\n((?:\s{2,}[^\n]*\n?)+)|["']?(.*?)["']?\s*$)/m);
116
+ if (!descMatch)
117
+ return { description: "" };
118
+ const raw = descMatch[1] ?? descMatch[2] ?? descMatch[3] ?? "";
119
+ return {
120
+ description: raw
121
+ .split("\n")
122
+ .map((l) => l.trim())
123
+ .filter(Boolean)
124
+ .join(" ")
125
+ .trim(),
126
+ };
127
+ }
128
+ /** Discover all available Claude Code skills from CLAUDE_HOME or ~/.claude. */
129
+ function resolveClaudeSkillsDir() {
130
+ const configuredClaudeHome = process.env.CLAUDE_HOME?.trim();
131
+ const claudeHome = configuredClaudeHome
132
+ ? path.resolve(configuredClaudeHome)
133
+ : path.join(process.env.HOME || process.env.USERPROFILE || "", ".claude");
134
+ return path.join(claudeHome, "skills");
135
+ }
136
+ function listAvailableSkills() {
137
+ const claudeSkillsDir = resolveClaudeSkillsDir();
138
+ const slawSkillsDir = resolveSlawSkillsDir();
139
+ // Build set of Slaw-managed skill names
140
+ const slawSkillNames = new Set();
141
+ if (slawSkillsDir) {
142
+ try {
143
+ for (const entry of fs.readdirSync(slawSkillsDir, { withFileTypes: true })) {
144
+ if (entry.isDirectory())
145
+ slawSkillNames.add(entry.name);
146
+ }
147
+ }
148
+ catch { /* skip */ }
149
+ }
150
+ const skills = [];
151
+ try {
152
+ const entries = fs.readdirSync(claudeSkillsDir, { withFileTypes: true });
153
+ for (const entry of entries) {
154
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
155
+ continue;
156
+ if (entry.name.startsWith("."))
157
+ continue;
158
+ const skillMdPath = path.join(claudeSkillsDir, entry.name, "SKILL.md");
159
+ let description = "";
160
+ try {
161
+ const md = fs.readFileSync(skillMdPath, "utf8");
162
+ description = parseSkillFrontmatter(md).description;
163
+ }
164
+ catch { /* no SKILL.md or unreadable */ }
165
+ skills.push({
166
+ name: entry.name,
167
+ description,
168
+ isSlawManaged: slawSkillNames.has(entry.name),
169
+ });
170
+ }
171
+ }
172
+ catch { /* Claude skills directory doesn't exist */ }
173
+ if (slawSkillsDir) {
174
+ const existingNames = new Set(skills.map((skill) => skill.name));
175
+ try {
176
+ for (const entry of fs.readdirSync(slawSkillsDir, { withFileTypes: true })) {
177
+ if (!entry.isDirectory() || entry.name.startsWith(".") || existingNames.has(entry.name))
178
+ continue;
179
+ const skillMdPath = path.join(slawSkillsDir, entry.name, "SKILL.md");
180
+ let description = "";
181
+ try {
182
+ const md = fs.readFileSync(skillMdPath, "utf8");
183
+ description = parseSkillFrontmatter(md).description;
184
+ }
185
+ catch { /* no SKILL.md or unreadable */ }
186
+ skills.push({
187
+ name: entry.name,
188
+ description,
189
+ isSlawManaged: true,
190
+ });
191
+ }
192
+ }
193
+ catch { /* skip Slaw skills directory */ }
194
+ }
195
+ skills.sort((a, b) => a.name.localeCompare(b.name));
196
+ return skills;
197
+ }
198
+ function toJoinRequestResponse(row) {
199
+ const { claimSecretHash: _claimSecretHash, ...safe } = row;
200
+ return safe;
201
+ }
202
+ function isPlainObject(value) {
203
+ return typeof value === "object" && value !== null && !Array.isArray(value);
204
+ }
205
+ function isLoopbackHost(hostname) {
206
+ const value = hostname.trim().toLowerCase();
207
+ return value === "localhost" || value === "127.0.0.1" || value === "::1";
208
+ }
209
+ function normalizeHostname(value) {
210
+ if (!value)
211
+ return null;
212
+ const trimmed = value.trim();
213
+ if (!trimmed)
214
+ return null;
215
+ if (trimmed.startsWith("[")) {
216
+ const end = trimmed.indexOf("]");
217
+ return end > 1
218
+ ? trimmed.slice(1, end).toLowerCase()
219
+ : trimmed.toLowerCase();
220
+ }
221
+ const firstColon = trimmed.indexOf(":");
222
+ if (firstColon > -1)
223
+ return trimmed.slice(0, firstColon).toLowerCase();
224
+ return trimmed.toLowerCase();
225
+ }
226
+ function normalizeHeaderValue(value, depth = 0) {
227
+ const direct = nonEmptyTrimmedString(value);
228
+ if (direct)
229
+ return direct;
230
+ if (!isPlainObject(value) || depth >= 3)
231
+ return null;
232
+ const candidateKeys = [
233
+ "value",
234
+ "token",
235
+ "secret",
236
+ "apiKey",
237
+ "api_key",
238
+ "auth",
239
+ "authToken",
240
+ "auth_token",
241
+ "accessToken",
242
+ "access_token",
243
+ "authorization",
244
+ "bearer",
245
+ "header",
246
+ "raw",
247
+ "text",
248
+ "string"
249
+ ];
250
+ for (const key of candidateKeys) {
251
+ if (!Object.prototype.hasOwnProperty.call(value, key))
252
+ continue;
253
+ const normalized = normalizeHeaderValue(value[key], depth + 1);
254
+ if (normalized)
255
+ return normalized;
256
+ }
257
+ const entries = Object.entries(value);
258
+ if (entries.length === 1) {
259
+ const [singleKey, singleValue] = entries[0];
260
+ const normalizedKey = singleKey.trim().toLowerCase();
261
+ if (normalizedKey !== "type" &&
262
+ normalizedKey !== "version" &&
263
+ normalizedKey !== "secretid" &&
264
+ normalizedKey !== "secret_id") {
265
+ const normalized = normalizeHeaderValue(singleValue, depth + 1);
266
+ if (normalized)
267
+ return normalized;
268
+ }
269
+ }
270
+ return null;
271
+ }
272
+ function extractHeaderEntries(input) {
273
+ if (isPlainObject(input)) {
274
+ return Object.entries(input);
275
+ }
276
+ if (!Array.isArray(input)) {
277
+ return [];
278
+ }
279
+ const entries = [];
280
+ for (const item of input) {
281
+ if (Array.isArray(item)) {
282
+ const key = nonEmptyTrimmedString(item[0]);
283
+ if (!key)
284
+ continue;
285
+ entries.push([key, item[1]]);
286
+ continue;
287
+ }
288
+ if (!isPlainObject(item))
289
+ continue;
290
+ const mapped = item;
291
+ const explicitKey = nonEmptyTrimmedString(mapped.key) ??
292
+ nonEmptyTrimmedString(mapped.name) ??
293
+ nonEmptyTrimmedString(mapped.header);
294
+ if (explicitKey) {
295
+ const explicitValue = Object.prototype.hasOwnProperty.call(mapped, "value")
296
+ ? mapped.value
297
+ : Object.prototype.hasOwnProperty.call(mapped, "token")
298
+ ? mapped.token
299
+ : Object.prototype.hasOwnProperty.call(mapped, "secret")
300
+ ? mapped.secret
301
+ : mapped;
302
+ entries.push([explicitKey, explicitValue]);
303
+ continue;
304
+ }
305
+ const singleEntry = Object.entries(mapped);
306
+ if (singleEntry.length === 1) {
307
+ entries.push(singleEntry[0]);
308
+ }
309
+ }
310
+ return entries;
311
+ }
312
+ function normalizeHeaderMap(input) {
313
+ const entries = extractHeaderEntries(input);
314
+ if (entries.length === 0)
315
+ return undefined;
316
+ const out = {};
317
+ for (const [key, value] of entries) {
318
+ const normalizedValue = normalizeHeaderValue(value);
319
+ if (!normalizedValue)
320
+ continue;
321
+ const trimmedKey = key.trim();
322
+ const trimmedValue = normalizedValue.trim();
323
+ if (!trimmedKey || !trimmedValue)
324
+ continue;
325
+ out[trimmedKey] = trimmedValue;
326
+ }
327
+ return Object.keys(out).length > 0 ? out : undefined;
328
+ }
329
+ function nonEmptyTrimmedString(value) {
330
+ if (typeof value !== "string")
331
+ return null;
332
+ const trimmed = value.trim();
333
+ return trimmed.length > 0 ? trimmed : null;
334
+ }
335
+ function headerMapHasKeyIgnoreCase(headers, targetKey) {
336
+ const normalizedTarget = targetKey.trim().toLowerCase();
337
+ return Object.keys(headers).some((key) => key.trim().toLowerCase() === normalizedTarget);
338
+ }
339
+ function headerMapGetIgnoreCase(headers, targetKey) {
340
+ const normalizedTarget = targetKey.trim().toLowerCase();
341
+ const key = Object.keys(headers).find((candidate) => candidate.trim().toLowerCase() === normalizedTarget);
342
+ if (!key)
343
+ return null;
344
+ const value = headers[key];
345
+ return typeof value === "string" ? value : null;
346
+ }
347
+ export function buildJoinDefaultsPayloadForAccept(input) {
348
+ return input.defaultsPayload;
349
+ }
350
+ export function mergeJoinDefaultsPayloadForReplay(existingDefaultsPayload, nextDefaultsPayload) {
351
+ if (!isPlainObject(existingDefaultsPayload) &&
352
+ !isPlainObject(nextDefaultsPayload)) {
353
+ return nextDefaultsPayload ?? existingDefaultsPayload;
354
+ }
355
+ if (!isPlainObject(existingDefaultsPayload)) {
356
+ return nextDefaultsPayload;
357
+ }
358
+ if (!isPlainObject(nextDefaultsPayload)) {
359
+ return existingDefaultsPayload;
360
+ }
361
+ const merged = {
362
+ ...existingDefaultsPayload,
363
+ ...nextDefaultsPayload
364
+ };
365
+ const existingHeaders = normalizeHeaderMap(existingDefaultsPayload.headers);
366
+ const nextHeaders = normalizeHeaderMap(nextDefaultsPayload.headers);
367
+ if (existingHeaders || nextHeaders) {
368
+ merged.headers = {
369
+ ...(existingHeaders ?? {}),
370
+ ...(nextHeaders ?? {})
371
+ };
372
+ }
373
+ else if (Object.prototype.hasOwnProperty.call(merged, "headers")) {
374
+ delete merged.headers;
375
+ }
376
+ return merged;
377
+ }
378
+ export function normalizeAgentDefaultsForJoin(input) {
379
+ const fatalErrors = [];
380
+ const diagnostics = [];
381
+ const normalized = isPlainObject(input.defaultsPayload)
382
+ ? input.defaultsPayload
383
+ : null;
384
+ return { normalized, diagnostics, fatalErrors };
385
+ }
386
+ function toInviteSummaryResponse(req, token, invite, squad = null) {
387
+ const squadInfo = typeof squad === "string"
388
+ ? { name: squad, brandColor: null, logoUrl: null }
389
+ : squad;
390
+ const baseUrl = requestBaseUrl(req);
391
+ const invitePath = `/invite/${token}`;
392
+ const onboardingPath = `/api/invites/${token}/onboarding`;
393
+ const onboardingTextPath = `/api/invites/${token}/onboarding.txt`;
394
+ const skillIndexPath = `/api/invites/${token}/skills/index`;
395
+ const inviteMessage = extractInviteMessage(invite);
396
+ return {
397
+ id: invite.id,
398
+ squadId: invite.squadId,
399
+ squadName: squadInfo?.name ?? null,
400
+ squadLogoUrl: squadInfo?.logoUrl ?? null,
401
+ squadBrandColor: squadInfo?.brandColor ?? null,
402
+ inviteType: invite.inviteType,
403
+ allowedJoinTypes: invite.allowedJoinTypes,
404
+ humanRole: extractInviteHumanRole(invite),
405
+ expiresAt: invite.expiresAt,
406
+ invitePath,
407
+ inviteUrl: baseUrl ? `${baseUrl}${invitePath}` : invitePath,
408
+ onboardingPath,
409
+ onboardingUrl: baseUrl ? `${baseUrl}${onboardingPath}` : onboardingPath,
410
+ onboardingTextPath,
411
+ onboardingTextUrl: baseUrl
412
+ ? `${baseUrl}${onboardingTextPath}`
413
+ : onboardingTextPath,
414
+ skillIndexPath,
415
+ skillIndexUrl: baseUrl
416
+ ? `${baseUrl}${skillIndexPath}`
417
+ : skillIndexPath,
418
+ inviteMessage
419
+ };
420
+ }
421
+ function actorHasActiveUserMembership(req, squadId) {
422
+ return (req.actor.type === "operator" &&
423
+ typeof req.actor.userId === "string" &&
424
+ Array.isArray(req.actor.memberships) &&
425
+ req.actor.memberships.some((membership) => membership.squadId === squadId && membership.status === "active"));
426
+ }
427
+ async function loadUsersById(db, userIds) {
428
+ if (userIds.length === 0)
429
+ return new Map();
430
+ const rows = await db
431
+ .select({
432
+ id: authUsers.id,
433
+ email: authUsers.email,
434
+ name: authUsers.name,
435
+ image: authUsers.image,
436
+ })
437
+ .from(authUsers)
438
+ .where(inArray(authUsers.id, userIds));
439
+ return new Map(rows.map((row) => [row.id, toUserProfile(row)]));
440
+ }
441
+ async function loadSquadAccessSummary(req, access, squadId) {
442
+ if (req.actor.type !== "operator") {
443
+ return {
444
+ currentUserRole: null,
445
+ canManageMembers: false,
446
+ canInviteUsers: false,
447
+ canApproveJoinRequests: false,
448
+ };
449
+ }
450
+ if (isLocalImplicit(req)) {
451
+ return {
452
+ currentUserRole: "owner",
453
+ canManageMembers: true,
454
+ canInviteUsers: true,
455
+ canApproveJoinRequests: true,
456
+ };
457
+ }
458
+ const userId = req.actor.userId ?? null;
459
+ const membership = userId ? await access.getMembership(squadId, "user", userId) : null;
460
+ const [canManageMembers, canInviteUsers, canApproveJoinRequests] = await Promise.all([
461
+ access.canUser(squadId, userId, "users:manage_permissions"),
462
+ access.canUser(squadId, userId, "users:invite"),
463
+ access.canUser(squadId, userId, "joins:approve"),
464
+ ]);
465
+ return {
466
+ currentUserRole: membership?.status === "active" && membership.membershipRole
467
+ ? normalizeHumanRole(membership.membershipRole, "operator")
468
+ : null,
469
+ canManageMembers,
470
+ canInviteUsers,
471
+ canApproveJoinRequests,
472
+ };
473
+ }
474
+ async function loadSquadMemberRecords(db, squadId, options = {}) {
475
+ const members = await db
476
+ .select()
477
+ .from(squadMemberships)
478
+ .where(and(eq(squadMemberships.squadId, squadId), eq(squadMemberships.principalType, "user"), options.includeArchived ? undefined : ne(squadMemberships.status, "archived")))
479
+ .orderBy(desc(squadMemberships.updatedAt));
480
+ const userIds = [...new Set(members.map((member) => member.principalId))];
481
+ const [userMap, grants] = await Promise.all([
482
+ loadUsersById(db, userIds),
483
+ userIds.length > 0
484
+ ? db
485
+ .select()
486
+ .from(principalPermissionGrants)
487
+ .where(and(eq(principalPermissionGrants.squadId, squadId), eq(principalPermissionGrants.principalType, "user"), inArray(principalPermissionGrants.principalId, userIds)))
488
+ : Promise.resolve([]),
489
+ ]);
490
+ const grantsByPrincipalId = new Map();
491
+ for (const grant of grants) {
492
+ const existing = grantsByPrincipalId.get(grant.principalId) ?? [];
493
+ existing.push(grant);
494
+ grantsByPrincipalId.set(grant.principalId, existing);
495
+ }
496
+ return members.map((member) => ({
497
+ ...member,
498
+ principalType: "user",
499
+ membershipRole: member.membershipRole
500
+ ? normalizeHumanRole(member.membershipRole, "operator")
501
+ : null,
502
+ user: userMap.get(member.principalId) ?? null,
503
+ grants: grantsByPrincipalId.get(member.principalId) ?? [],
504
+ }));
505
+ }
506
+ const humanRoleRank = {
507
+ viewer: 1,
508
+ operator: 2,
509
+ admin: 3,
510
+ owner: 4,
511
+ };
512
+ async function resolveActorHumanRole(req, access, squadId) {
513
+ if (req.actor.type !== "operator")
514
+ return null;
515
+ if (isLocalImplicit(req) || req.actor.isInstanceAdmin)
516
+ return "owner";
517
+ const userId = req.actor.userId ?? null;
518
+ if (!userId)
519
+ return null;
520
+ const membership = await access.getMembership(squadId, "user", userId);
521
+ if (membership?.status !== "active" || !membership.membershipRole)
522
+ return null;
523
+ return normalizeHumanRole(membership.membershipRole, "operator");
524
+ }
525
+ async function getProtectedMemberReason(req, access, squadId, member, opts) {
526
+ if (member.principalType !== "user")
527
+ return "Only human squad members can be removed.";
528
+ if (req.actor.type !== "operator")
529
+ return "Operator access is required to remove members.";
530
+ if (member.principalId === req.actor.userId)
531
+ return "You cannot remove yourself.";
532
+ const isTargetInstanceAdmin = opts?.instanceAdminUserIds
533
+ ? opts.instanceAdminUserIds.has(member.principalId)
534
+ : await access.isInstanceAdmin(member.principalId);
535
+ if (isTargetInstanceAdmin) {
536
+ return "Instance admins cannot be removed from squad access.";
537
+ }
538
+ const targetRole = member.membershipRole
539
+ ? normalizeHumanRole(member.membershipRole, "operator")
540
+ : "operator";
541
+ if (opts?.operation === "archive") {
542
+ if (targetRole === "owner")
543
+ return "Operator owners cannot be removed from squad access.";
544
+ if (targetRole === "admin")
545
+ return "Squad admins cannot be removed from squad access.";
546
+ }
547
+ const actorRole = opts?.actorRole ?? await resolveActorHumanRole(req, access, squadId);
548
+ if (!actorRole)
549
+ return "Only active squad members can remove users.";
550
+ if (humanRoleRank[targetRole] >= humanRoleRank[actorRole]) {
551
+ return "You can only remove users below your squad role.";
552
+ }
553
+ return null;
554
+ }
555
+ async function assertCanManageSquadMember(req, access, squadId, member, operation = "update") {
556
+ const reason = await getProtectedMemberReason(req, access, squadId, member, { operation });
557
+ if (reason)
558
+ throw forbidden(reason);
559
+ }
560
+ async function addSquadMemberRemovalAccess(req, db, access, squadId, members) {
561
+ const actorRole = await resolveActorHumanRole(req, access, squadId);
562
+ const userIds = [...new Set(members
563
+ .filter((member) => member.principalType === "user")
564
+ .map((member) => member.principalId))];
565
+ const instanceAdminUserIds = userIds.length > 0
566
+ ? new Set(await db
567
+ .select({ userId: instanceUserRoles.userId })
568
+ .from(instanceUserRoles)
569
+ .where(and(inArray(instanceUserRoles.userId, userIds), eq(instanceUserRoles.role, "instance_admin")))
570
+ .then((rows) => rows.map((row) => row.userId)))
571
+ : new Set();
572
+ return Promise.all(members.map(async (member) => {
573
+ const reason = await getProtectedMemberReason(req, access, squadId, member, {
574
+ actorRole,
575
+ instanceAdminUserIds,
576
+ operation: "archive",
577
+ });
578
+ return {
579
+ ...member,
580
+ removal: {
581
+ canArchive: !reason,
582
+ reason,
583
+ },
584
+ };
585
+ }));
586
+ }
587
+ async function loadSquadUserDirectory(db, squadId) {
588
+ const members = await db
589
+ .select({
590
+ principalId: squadMemberships.principalId,
591
+ status: squadMemberships.status,
592
+ })
593
+ .from(squadMemberships)
594
+ .where(and(eq(squadMemberships.squadId, squadId), eq(squadMemberships.principalType, "user"), eq(squadMemberships.status, "active")))
595
+ .orderBy(desc(squadMemberships.updatedAt));
596
+ const userIds = [...new Set(members.map((member) => member.principalId))];
597
+ const userMap = await loadUsersById(db, userIds);
598
+ return members.map((member) => ({
599
+ principalId: member.principalId,
600
+ status: "active",
601
+ user: userMap.get(member.principalId) ?? null,
602
+ }));
603
+ }
604
+ function inviteStateWhereClause(state) {
605
+ const now = new Date();
606
+ switch (state) {
607
+ case "active":
608
+ return and(isNull(invites.revokedAt), isNull(invites.acceptedAt), gt(invites.expiresAt, now));
609
+ case "accepted":
610
+ return isNotNull(invites.acceptedAt);
611
+ case "expired":
612
+ return and(isNull(invites.revokedAt), isNull(invites.acceptedAt), lte(invites.expiresAt, now));
613
+ case "revoked":
614
+ return isNotNull(invites.revokedAt);
615
+ default:
616
+ return undefined;
617
+ }
618
+ }
619
+ async function loadSquadInviteRecords(db, squadId, options) {
620
+ const whereClause = inviteStateWhereClause(options.state);
621
+ const rows = await db
622
+ .select()
623
+ .from(invites)
624
+ .where(whereClause ? and(eq(invites.squadId, squadId), whereClause) : eq(invites.squadId, squadId))
625
+ .orderBy(desc(invites.createdAt))
626
+ .limit(options.limit + 1)
627
+ .offset(options.offset);
628
+ const hasMore = rows.length > options.limit;
629
+ const visibleRows = hasMore ? rows.slice(0, options.limit) : rows;
630
+ const userIds = [
631
+ ...new Set(visibleRows
632
+ .map((invite) => invite.invitedByUserId)
633
+ .filter((value) => Boolean(value))),
634
+ ];
635
+ const [userMap, joinRows, squadName] = await Promise.all([
636
+ loadUsersById(db, userIds),
637
+ visibleRows.length
638
+ ? db
639
+ .select({ id: joinRequests.id, inviteId: joinRequests.inviteId })
640
+ .from(joinRequests)
641
+ .where(and(eq(joinRequests.squadId, squadId), inArray(joinRequests.inviteId, visibleRows.map((invite) => invite.id))))
642
+ : Promise.resolve([]),
643
+ db
644
+ .select({ name: squads.name })
645
+ .from(squads)
646
+ .where(eq(squads.id, squadId))
647
+ .then((squadRows) => squadRows[0]?.name ?? null),
648
+ ]);
649
+ const joinRequestIdByInviteId = new Map(joinRows.map((row) => [row.inviteId, row.id]));
650
+ return {
651
+ invites: visibleRows.map((invite) => ({
652
+ ...invite,
653
+ squadName,
654
+ humanRole: extractInviteHumanRole(invite),
655
+ inviteMessage: extractInviteMessage(invite),
656
+ state: inviteState(invite),
657
+ invitedByUser: invite.invitedByUserId
658
+ ? userMap.get(invite.invitedByUserId) ?? null
659
+ : null,
660
+ relatedJoinRequestId: joinRequestIdByInviteId.get(invite.id) ?? null,
661
+ })),
662
+ nextOffset: hasMore ? options.offset + options.limit : null,
663
+ };
664
+ }
665
+ async function loadJoinRequestRecords(db, squadId) {
666
+ const rows = collapseDuplicatePendingHumanJoinRequests(await db
667
+ .select()
668
+ .from(joinRequests)
669
+ .where(eq(joinRequests.squadId, squadId))
670
+ .orderBy(desc(joinRequests.createdAt)));
671
+ const inviteIds = [...new Set(rows.map((row) => row.inviteId))];
672
+ const inviteRows = inviteIds.length
673
+ ? await db
674
+ .select()
675
+ .from(invites)
676
+ .where(inArray(invites.id, inviteIds))
677
+ : [];
678
+ const userIds = [
679
+ ...new Set([
680
+ ...rows.map((row) => row.requestingUserId),
681
+ ...rows.map((row) => row.approvedByUserId),
682
+ ...rows.map((row) => row.rejectedByUserId),
683
+ ...inviteRows.map((invite) => invite.invitedByUserId),
684
+ ].filter((value) => Boolean(value))),
685
+ ];
686
+ const userMap = await loadUsersById(db, userIds);
687
+ const inviteMap = new Map(inviteRows.map((invite) => [invite.id, invite]));
688
+ return rows.map((row) => {
689
+ const invite = inviteMap.get(row.inviteId) ?? null;
690
+ return {
691
+ ...toJoinRequestResponse(row),
692
+ requesterUser: row.requestingUserId
693
+ ? userMap.get(row.requestingUserId) ?? null
694
+ : null,
695
+ approvedByUser: row.approvedByUserId
696
+ ? userMap.get(row.approvedByUserId) ?? null
697
+ : null,
698
+ rejectedByUser: row.rejectedByUserId
699
+ ? userMap.get(row.rejectedByUserId) ?? null
700
+ : null,
701
+ invite: invite
702
+ ? {
703
+ id: invite.id,
704
+ inviteType: invite.inviteType,
705
+ allowedJoinTypes: invite.allowedJoinTypes,
706
+ humanRole: extractInviteHumanRole(invite),
707
+ inviteMessage: extractInviteMessage(invite),
708
+ createdAt: invite.createdAt,
709
+ expiresAt: invite.expiresAt,
710
+ revokedAt: invite.revokedAt,
711
+ acceptedAt: invite.acceptedAt,
712
+ invitedByUser: invite.invitedByUserId
713
+ ? userMap.get(invite.invitedByUserId) ?? null
714
+ : null,
715
+ }
716
+ : null,
717
+ };
718
+ });
719
+ }
720
+ async function loadUserSquadAccessResponse(db, access, userId) {
721
+ const [memberships, user, isInstanceAdmin] = await Promise.all([
722
+ access.listUserSquadAccess(userId),
723
+ db
724
+ .select({
725
+ id: authUsers.id,
726
+ email: authUsers.email,
727
+ name: authUsers.name,
728
+ image: authUsers.image,
729
+ })
730
+ .from(authUsers)
731
+ .where(eq(authUsers.id, userId))
732
+ .then((rows) => rows[0] ?? null),
733
+ access.isInstanceAdmin(userId),
734
+ ]);
735
+ const squadIds = [...new Set(memberships.map((membership) => membership.squadId))];
736
+ const squadRows = squadIds.length
737
+ ? await db
738
+ .select({
739
+ id: squads.id,
740
+ name: squads.name,
741
+ status: squads.status,
742
+ })
743
+ .from(squads)
744
+ .where(inArray(squads.id, squadIds))
745
+ : [];
746
+ const squadMap = new Map(squadRows.map((squad) => [squad.id, squad]));
747
+ return {
748
+ user: user
749
+ ? {
750
+ ...toUserProfile(user),
751
+ isInstanceAdmin,
752
+ }
753
+ : null,
754
+ squadAccess: memberships.map((membership) => {
755
+ const squad = squadMap.get(membership.squadId) ?? null;
756
+ return {
757
+ ...membership,
758
+ principalType: "user",
759
+ squadName: squad?.name ?? null,
760
+ squadStatus: squad?.status ?? null,
761
+ };
762
+ }),
763
+ };
764
+ }
765
+ function buildOnboardingDiscoveryDiagnostics(input) {
766
+ const diagnostics = [];
767
+ let apiHost = null;
768
+ if (input.apiBaseUrl) {
769
+ try {
770
+ apiHost = normalizeHostname(new URL(input.apiBaseUrl).hostname);
771
+ }
772
+ catch {
773
+ apiHost = null;
774
+ }
775
+ }
776
+ const bindHost = normalizeHostname(input.bindHost);
777
+ const allowSet = new Set(input.allowedHostnames
778
+ .map((entry) => normalizeHostname(entry))
779
+ .filter((entry) => Boolean(entry)));
780
+ if (apiHost && isLoopbackHost(apiHost)) {
781
+ diagnostics.push({
782
+ code: "onboarding_api_loopback",
783
+ level: "warn",
784
+ message: "Onboarding URL resolves to loopback hostname. Remote agents cannot reach localhost on your Slaw host.",
785
+ hint: "Use a reachable hostname/IP (for example Tailscale hostname, Docker host alias, or public domain)."
786
+ });
787
+ }
788
+ if (input.deploymentMode === "authenticated" &&
789
+ input.deploymentExposure === "private" &&
790
+ (!bindHost || isLoopbackHost(bindHost))) {
791
+ diagnostics.push({
792
+ code: "onboarding_private_loopback_bind",
793
+ level: "warn",
794
+ message: "Slaw is bound to loopback in authenticated/private mode.",
795
+ hint: "Use a reachable private bind mode such as `pnpm dev --bind lan` or `pnpm dev --bind tailnet` for private-network onboarding."
796
+ });
797
+ }
798
+ if (input.deploymentMode === "authenticated" &&
799
+ input.deploymentExposure === "private" &&
800
+ apiHost &&
801
+ !isLoopbackHost(apiHost) &&
802
+ allowSet.size > 0 &&
803
+ !allowSet.has(apiHost)) {
804
+ diagnostics.push({
805
+ code: "onboarding_private_host_not_allowed",
806
+ level: "warn",
807
+ message: `Onboarding host "${apiHost}" is not in allowed hostnames for authenticated/private mode.`,
808
+ hint: `Run pnpm slaw allowed-hostname ${apiHost}`
809
+ });
810
+ }
811
+ return diagnostics;
812
+ }
813
+ function buildOnboardingConnectionCandidates(input) {
814
+ let base = null;
815
+ try {
816
+ if (input.apiBaseUrl) {
817
+ base = new URL(input.apiBaseUrl);
818
+ }
819
+ }
820
+ catch {
821
+ base = null;
822
+ }
823
+ const protocol = base?.protocol ?? "http:";
824
+ const port = base?.port ? `:${base.port}` : "";
825
+ const candidates = new Set();
826
+ if (base) {
827
+ candidates.add(base.origin);
828
+ }
829
+ const bindHost = normalizeHostname(input.bindHost);
830
+ if (bindHost && !isLoopbackHost(bindHost)) {
831
+ candidates.add(`${protocol}//${bindHost}${port}`);
832
+ }
833
+ for (const rawHost of input.allowedHostnames) {
834
+ const host = normalizeHostname(rawHost);
835
+ if (!host)
836
+ continue;
837
+ candidates.add(`${protocol}//${host}${port}`);
838
+ }
839
+ if (base && isLoopbackHost(base.hostname)) {
840
+ candidates.add(`${protocol}//host.docker.internal${port}`);
841
+ }
842
+ for (const host of collectReachableInterfaceHosts()) {
843
+ const formattedHost = host.includes(":") && !host.startsWith("[") && !host.endsWith("]") ? `[${host}]` : host;
844
+ candidates.add(`${protocol}//${formattedHost}${port}`);
845
+ }
846
+ return Array.from(candidates);
847
+ }
848
+ function buildInviteOnboardingManifest(req, token, invite, opts) {
849
+ const baseUrl = requestBaseUrl(req);
850
+ const skillPath = `/api/invites/${token}/skills/slaw`;
851
+ const skillUrl = baseUrl ? `${baseUrl}${skillPath}` : skillPath;
852
+ const registrationEndpointPath = `/api/invites/${token}/accept`;
853
+ const registrationEndpointUrl = baseUrl
854
+ ? `${baseUrl}${registrationEndpointPath}`
855
+ : registrationEndpointPath;
856
+ const onboardingTextPath = `/api/invites/${token}/onboarding.txt`;
857
+ const onboardingTextUrl = baseUrl
858
+ ? `${baseUrl}${onboardingTextPath}`
859
+ : onboardingTextPath;
860
+ const discoveryDiagnostics = buildOnboardingDiscoveryDiagnostics({
861
+ apiBaseUrl: baseUrl,
862
+ deploymentMode: opts.deploymentMode,
863
+ deploymentExposure: opts.deploymentExposure,
864
+ bindHost: opts.bindHost,
865
+ allowedHostnames: opts.allowedHostnames
866
+ });
867
+ const connectionCandidates = buildOnboardingConnectionCandidates({
868
+ apiBaseUrl: baseUrl,
869
+ bindHost: opts.bindHost,
870
+ allowedHostnames: opts.allowedHostnames
871
+ });
872
+ return {
873
+ invite: toInviteSummaryResponse(req, token, invite, opts.squadName ?? null),
874
+ onboarding: {
875
+ instructions: "Join as an external Slaw agent, save your one-time claim secret, wait for operator approval, then claim your API key. Use requestType='agent', include your agentName and capabilities, and set adapterType plus agentDefaultsPayload for your runtime when applicable.",
876
+ inviteMessage: extractInviteMessage(invite),
877
+ recommendedAdapterType: null,
878
+ requiredFields: {
879
+ requestType: "agent",
880
+ agentName: "Display name for this agent",
881
+ adapterType: "Adapter type for this runtime.",
882
+ capabilities: "Optional capability summary",
883
+ agentDefaultsPayload: "Runtime-specific adapter config. Include the config your adapter expects."
884
+ },
885
+ registrationEndpoint: {
886
+ method: "POST",
887
+ path: registrationEndpointPath,
888
+ url: registrationEndpointUrl
889
+ },
890
+ claimEndpointTemplate: {
891
+ method: "POST",
892
+ path: "/api/join-requests/{requestId}/claim-api-key",
893
+ body: {
894
+ claimSecret: "one-time claim secret returned when the join request is created"
895
+ }
896
+ },
897
+ connectivity: {
898
+ deploymentMode: opts.deploymentMode,
899
+ deploymentExposure: opts.deploymentExposure,
900
+ bindHost: opts.bindHost,
901
+ allowedHostnames: opts.allowedHostnames,
902
+ connectionCandidates,
903
+ diagnostics: discoveryDiagnostics,
904
+ guidance: opts.deploymentMode === "authenticated" &&
905
+ opts.deploymentExposure === "private"
906
+ ? "If your agent runs on another machine, ensure the Slaw hostname is reachable and allowed via `pnpm slaw allowed-hostname <host>`."
907
+ : "Ensure your agent can reach this Slaw API base URL for invite, claim, and skill bootstrap calls."
908
+ },
909
+ textInstructions: {
910
+ path: onboardingTextPath,
911
+ url: onboardingTextUrl,
912
+ contentType: "text/plain"
913
+ },
914
+ skill: {
915
+ name: "slaw",
916
+ path: skillPath,
917
+ url: skillUrl,
918
+ installPath: "runtime-specific Slaw skill location"
919
+ }
920
+ }
921
+ };
922
+ }
923
+ export function buildInviteOnboardingTextDocument(req, token, invite, opts) {
924
+ const manifest = buildInviteOnboardingManifest(req, token, invite, opts);
925
+ const onboarding = manifest.onboarding;
926
+ const diagnostics = Array.isArray(onboarding.connectivity?.diagnostics)
927
+ ? onboarding.connectivity.diagnostics
928
+ : [];
929
+ const lines = [];
930
+ const appendBlock = (block) => {
931
+ const trimmed = block.replace(/^\n/, "").replace(/\n\s*$/, "");
932
+ const lineIndentation = trimmed
933
+ .split("\n")
934
+ .filter((line) => line.trim().length > 0)
935
+ .map((line) => line.match(/^(\s*)/)?.[0].length ?? 0);
936
+ const minIndent = lineIndentation.length > 0 ? Math.min(...lineIndentation) : 0;
937
+ for (const line of trimmed.split("\n")) {
938
+ lines.push(line.slice(minIndent));
939
+ }
940
+ };
941
+ appendBlock(`
942
+ # Slaw Agent Onboarding
943
+
944
+ This document is meant to be readable by both humans and agents.
945
+
946
+ ## Invite
947
+ - inviteType: ${invite.inviteType}
948
+ - allowedJoinTypes: ${invite.allowedJoinTypes}
949
+ - expiresAt: ${invite.expiresAt.toISOString()}
950
+ `);
951
+ if (manifest.invite.squadName) {
952
+ lines.push(`- squadName: ${manifest.invite.squadName}`);
953
+ }
954
+ if (onboarding.inviteMessage) {
955
+ appendBlock(`
956
+ ## Message from inviter
957
+ ${onboarding.inviteMessage}
958
+ `);
959
+ }
960
+ appendBlock(`
961
+ ## Step 0
962
+
963
+ Decide which Slaw adapter type matches your runtime.
964
+
965
+ Use adapterType only when there is a matching Slaw adapter. Put runtime-specific settings in agentDefaultsPayload.
966
+
967
+ ## Step 1: Submit agent join request
968
+ ${onboarding.registrationEndpoint.method} ${onboarding.registrationEndpoint.url}
969
+
970
+ Body (JSON):
971
+ {
972
+ "requestType": "agent",
973
+ "agentName": "My Agent",
974
+ "adapterType": "adapter_type_for_this_runtime",
975
+ "capabilities": "Short summary of what this agent can do",
976
+ "agentDefaultsPayload": {}
977
+ }
978
+
979
+ Expected response includes:
980
+ - request id
981
+ - one-time claimSecret
982
+ - claimApiKeyPath
983
+
984
+ ## Step 2: Wait for operator approval
985
+ The operator approves the join request in Slaw before key claim is allowed.
986
+
987
+ ## Step 3: Claim API key (one-time)
988
+ ${onboarding.claimEndpointTemplate.method} /api/join-requests/{requestId}/claim-api-key
989
+
990
+ Body (JSON):
991
+ {
992
+ "claimSecret": "<one-time-claim-secret>"
993
+ }
994
+
995
+ On successful claim, save the full JSON response somewhere private for your runtime and set SLAW_API_KEY and SLAW_API_URL for future Slaw API calls.
996
+
997
+ Important:
998
+ - claim secrets expire
999
+ - claim secrets are single-use
1000
+ - claim fails before operator approval
1001
+
1002
+ ## Step 4: Install Slaw skill
1003
+ GET ${onboarding.skill.url}
1004
+ Install path: ${onboarding.skill.installPath}
1005
+
1006
+ Use your runtime's normal skill or instruction installation path.
1007
+
1008
+ ## Text onboarding URL
1009
+ ${onboarding.textInstructions.url}
1010
+
1011
+ ## Connectivity guidance
1012
+ ${onboarding.connectivity?.guidance ??
1013
+ "Ensure Slaw is reachable from your agent runtime."}
1014
+ `);
1015
+ const connectionCandidates = Array.isArray(onboarding.connectivity?.connectionCandidates)
1016
+ ? onboarding.connectivity.connectionCandidates.filter((entry) => Boolean(entry))
1017
+ : [];
1018
+ if (connectionCandidates.length > 0) {
1019
+ lines.push("## Suggested Slaw base URLs to try");
1020
+ for (const candidate of connectionCandidates) {
1021
+ lines.push(`- ${candidate}`);
1022
+ }
1023
+ appendBlock(`
1024
+
1025
+ Test each candidate with:
1026
+ - GET <candidate>/api/health
1027
+ - set the first reachable candidate as agentDefaultsPayload.slawApiUrl when submitting your join request
1028
+
1029
+ If none are reachable: ask your human operator for a reachable hostname/address and help them update network configuration.
1030
+ For authenticated/private mode, they may need:
1031
+ - pnpm slaw allowed-hostname <host>
1032
+ - then restart Slaw and retry onboarding.
1033
+ `);
1034
+ }
1035
+ if (diagnostics.length > 0) {
1036
+ lines.push("## Connectivity diagnostics");
1037
+ for (const diag of diagnostics) {
1038
+ lines.push(`- [${diag.level}] ${diag.message}`);
1039
+ if (diag.hint)
1040
+ lines.push(` hint: ${diag.hint}`);
1041
+ }
1042
+ }
1043
+ appendBlock(`
1044
+
1045
+ ## Helpful endpoints
1046
+ ${onboarding.registrationEndpoint.path}
1047
+ ${onboarding.claimEndpointTemplate.path}
1048
+ ${onboarding.skill.path}
1049
+ ${manifest.invite.onboardingPath}
1050
+ `);
1051
+ return `${lines.join("\n")}\n`;
1052
+ }
1053
+ function extractInviteMessage(invite) {
1054
+ const rawDefaults = invite.defaultsPayload;
1055
+ if (!rawDefaults ||
1056
+ typeof rawDefaults !== "object" ||
1057
+ Array.isArray(rawDefaults)) {
1058
+ return null;
1059
+ }
1060
+ const rawMessage = rawDefaults.agentMessage;
1061
+ if (typeof rawMessage !== "string") {
1062
+ return null;
1063
+ }
1064
+ const trimmed = rawMessage.trim();
1065
+ return trimmed.length ? trimmed : null;
1066
+ }
1067
+ function mergeInviteDefaults(defaultsPayload, agentMessage, humanRole = null) {
1068
+ const merged = defaultsPayload && typeof defaultsPayload === "object"
1069
+ ? { ...defaultsPayload }
1070
+ : {};
1071
+ if (humanRole) {
1072
+ const existingHuman = isPlainObject(merged.human) ? { ...merged.human } : {};
1073
+ merged.human = {
1074
+ ...existingHuman,
1075
+ role: humanRole,
1076
+ grants: grantsForHumanRole(humanRole),
1077
+ };
1078
+ }
1079
+ if (agentMessage) {
1080
+ merged.agentMessage = agentMessage;
1081
+ }
1082
+ return Object.keys(merged).length ? merged : null;
1083
+ }
1084
+ function requestIp(req) {
1085
+ const forwarded = req.header("x-forwarded-for");
1086
+ if (forwarded) {
1087
+ const first = forwarded.split(",")[0]?.trim();
1088
+ if (first)
1089
+ return first;
1090
+ }
1091
+ return req.ip || "unknown";
1092
+ }
1093
+ function inviteExpired(invite) {
1094
+ return invite.expiresAt.getTime() <= Date.now();
1095
+ }
1096
+ function inviteState(invite) {
1097
+ if (invite.revokedAt)
1098
+ return "revoked";
1099
+ if (invite.acceptedAt)
1100
+ return "accepted";
1101
+ if (inviteExpired(invite))
1102
+ return "expired";
1103
+ return "active";
1104
+ }
1105
+ function extractInviteHumanRole(invite) {
1106
+ if (invite.allowedJoinTypes === "agent")
1107
+ return null;
1108
+ return resolveHumanInviteRole(invite.defaultsPayload);
1109
+ }
1110
+ function isLocalImplicit(req) {
1111
+ return req.actor.type === "operator" && req.actor.source === "local_implicit";
1112
+ }
1113
+ function toUserProfile(user) {
1114
+ if (!user)
1115
+ return null;
1116
+ return {
1117
+ id: user.id,
1118
+ email: user.email ?? null,
1119
+ name: user.name ?? null,
1120
+ image: user.image ?? null,
1121
+ };
1122
+ }
1123
+ async function resolveActorEmail(db, req) {
1124
+ if (isLocalImplicit(req))
1125
+ return "local@slaw.local";
1126
+ const userId = req.actor.userId;
1127
+ if (!userId)
1128
+ return null;
1129
+ const user = await db
1130
+ .select({ email: authUsers.email })
1131
+ .from(authUsers)
1132
+ .where(eq(authUsers.id, userId))
1133
+ .then((rows) => rows[0] ?? null);
1134
+ return user?.email ?? null;
1135
+ }
1136
+ async function resolveAcceptedInviteJoinRequest(db, req, invite) {
1137
+ if (!invite?.acceptedAt)
1138
+ return null;
1139
+ const directJoinRequest = await db
1140
+ .select({
1141
+ requestType: joinRequests.requestType,
1142
+ status: joinRequests.status,
1143
+ requestingUserId: joinRequests.requestingUserId,
1144
+ requestEmailSnapshot: joinRequests.requestEmailSnapshot,
1145
+ })
1146
+ .from(joinRequests)
1147
+ .where(eq(joinRequests.inviteId, invite.id))
1148
+ .then((rows) => rows[0] ?? null);
1149
+ if (directJoinRequest)
1150
+ return directJoinRequest;
1151
+ if (!invite.squadId)
1152
+ return null;
1153
+ const actorRequestingUserId = isLocalImplicit(req)
1154
+ ? "local-operator"
1155
+ : req.actor.userId ?? null;
1156
+ const actorEmail = await resolveActorEmail(db, req);
1157
+ if (!actorRequestingUserId && !actorEmail)
1158
+ return null;
1159
+ return findReusableHumanJoinRequest(await db
1160
+ .select({
1161
+ id: joinRequests.id,
1162
+ requestType: joinRequests.requestType,
1163
+ status: joinRequests.status,
1164
+ requestingUserId: joinRequests.requestingUserId,
1165
+ requestEmailSnapshot: joinRequests.requestEmailSnapshot,
1166
+ })
1167
+ .from(joinRequests)
1168
+ .where(and(eq(joinRequests.squadId, invite.squadId), eq(joinRequests.requestType, "human")))
1169
+ .orderBy(desc(joinRequests.createdAt)), {
1170
+ requestingUserId: actorRequestingUserId,
1171
+ requestEmailSnapshot: actorEmail,
1172
+ });
1173
+ }
1174
+ function grantsFromDefaults(defaultsPayload, key) {
1175
+ if (!defaultsPayload || typeof defaultsPayload !== "object")
1176
+ return [];
1177
+ const scoped = defaultsPayload[key];
1178
+ if (!scoped || typeof scoped !== "object")
1179
+ return [];
1180
+ const grants = scoped.grants;
1181
+ if (!Array.isArray(grants))
1182
+ return [];
1183
+ const validPermissionKeys = new Set(PERMISSION_KEYS);
1184
+ const result = [];
1185
+ for (const item of grants) {
1186
+ if (!item || typeof item !== "object")
1187
+ continue;
1188
+ const record = item;
1189
+ if (typeof record.permissionKey !== "string")
1190
+ continue;
1191
+ if (!validPermissionKeys.has(record.permissionKey))
1192
+ continue;
1193
+ result.push({
1194
+ permissionKey: record.permissionKey,
1195
+ scope: record.scope &&
1196
+ typeof record.scope === "object" &&
1197
+ !Array.isArray(record.scope)
1198
+ ? record.scope
1199
+ : null
1200
+ });
1201
+ }
1202
+ return result;
1203
+ }
1204
+ export function agentJoinGrantsFromDefaults(defaultsPayload) {
1205
+ const grants = grantsFromDefaults(defaultsPayload, "agent");
1206
+ if (grants.some((grant) => grant.permissionKey === "tasks:assign")) {
1207
+ return grants;
1208
+ }
1209
+ return [
1210
+ ...grants,
1211
+ {
1212
+ permissionKey: "tasks:assign",
1213
+ scope: null
1214
+ }
1215
+ ];
1216
+ }
1217
+ export function resolveJoinRequestAgentManagerId(candidates) {
1218
+ const leadCandidates = candidates.filter((candidate) => candidate.role === "squad_lead");
1219
+ if (leadCandidates.length === 0)
1220
+ return null;
1221
+ const rootLead = leadCandidates.find((candidate) => candidate.reportsTo === null);
1222
+ return (rootLead ?? leadCandidates[0] ?? null)?.id ?? null;
1223
+ }
1224
+ function isInviteTokenHashCollisionError(error) {
1225
+ const candidates = [
1226
+ error,
1227
+ error?.cause ?? null
1228
+ ];
1229
+ for (const candidate of candidates) {
1230
+ if (!candidate || typeof candidate !== "object")
1231
+ continue;
1232
+ const code = "code" in candidate && typeof candidate.code === "string"
1233
+ ? candidate.code
1234
+ : null;
1235
+ const message = "message" in candidate && typeof candidate.message === "string"
1236
+ ? candidate.message
1237
+ : "";
1238
+ const constraint = "constraint" in candidate && typeof candidate.constraint === "string"
1239
+ ? candidate.constraint
1240
+ : null;
1241
+ if (code !== "23505")
1242
+ continue;
1243
+ if (constraint === "invites_token_hash_unique_idx")
1244
+ return true;
1245
+ if (message.includes("invites_token_hash_unique_idx"))
1246
+ return true;
1247
+ }
1248
+ return false;
1249
+ }
1250
+ function isAbortError(error) {
1251
+ return error instanceof Error && error.name === "AbortError";
1252
+ }
1253
+ function parseIpv4Address(address) {
1254
+ const parts = address.split(".");
1255
+ if (parts.length !== 4)
1256
+ return null;
1257
+ const parsed = parts.map((part) => {
1258
+ if (!/^\d+$/.test(part))
1259
+ return NaN;
1260
+ return Number(part);
1261
+ });
1262
+ if (parsed.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
1263
+ return null;
1264
+ }
1265
+ return parsed;
1266
+ }
1267
+ function isPrivateOrReservedIpv4(address) {
1268
+ const octets = parseIpv4Address(address);
1269
+ if (!octets)
1270
+ return true;
1271
+ const [a, b, c] = octets;
1272
+ if (a === 0)
1273
+ return true;
1274
+ if (a === 10)
1275
+ return true;
1276
+ if (a === 100 && b >= 64 && b <= 127)
1277
+ return true;
1278
+ if (a === 127)
1279
+ return true;
1280
+ if (a === 169 && b === 254)
1281
+ return true;
1282
+ if (a === 172 && b >= 16 && b <= 31)
1283
+ return true;
1284
+ if (a === 192 && b === 0 && c === 0)
1285
+ return true;
1286
+ if (a === 192 && b === 168)
1287
+ return true;
1288
+ if (a === 192 && b === 0 && c === 2)
1289
+ return true;
1290
+ if (a === 192 && b === 88 && c === 99)
1291
+ return true;
1292
+ if (a === 198 && (b === 18 || b === 19))
1293
+ return true;
1294
+ if (a === 198 && b === 51 && c === 100)
1295
+ return true;
1296
+ if (a === 203 && b === 0 && c === 113)
1297
+ return true;
1298
+ if (a >= 224)
1299
+ return true;
1300
+ return false;
1301
+ }
1302
+ function parseMappedIpv4Hex(address) {
1303
+ const match = address.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
1304
+ if (!match)
1305
+ return null;
1306
+ const hi = Number.parseInt(match[1], 16);
1307
+ const lo = Number.parseInt(match[2], 16);
1308
+ if (!Number.isInteger(hi) || !Number.isInteger(lo))
1309
+ return null;
1310
+ return `${hi >> 8}.${hi & 0xff}.${lo >> 8}.${lo & 0xff}`;
1311
+ }
1312
+ function isPrivateOrReservedIpv6(address) {
1313
+ const lower = address.toLowerCase();
1314
+ if (lower.startsWith("::ffff:")) {
1315
+ const mappedIpv4 = lower.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/);
1316
+ if (mappedIpv4?.[1])
1317
+ return isPrivateOrReservedIpv4(mappedIpv4[1]);
1318
+ const mappedIpv4Hex = parseMappedIpv4Hex(lower);
1319
+ if (mappedIpv4Hex)
1320
+ return isPrivateOrReservedIpv4(mappedIpv4Hex);
1321
+ return true;
1322
+ }
1323
+ if (lower === "::" || lower === "::1")
1324
+ return true;
1325
+ if (lower.startsWith("fc") || lower.startsWith("fd"))
1326
+ return true;
1327
+ if (/^fe[89ab]/.test(lower))
1328
+ return true;
1329
+ if (lower.startsWith("ff"))
1330
+ return true;
1331
+ if (lower === "100::" || lower.startsWith("100:"))
1332
+ return true;
1333
+ if (lower.startsWith("2001:db8:") || lower === "2001:db8::")
1334
+ return true;
1335
+ if (lower.startsWith("2001:2:") || lower === "2001:2::")
1336
+ return true;
1337
+ if (lower.startsWith("2002:"))
1338
+ return true;
1339
+ if (lower.startsWith("64:ff9b:"))
1340
+ return true;
1341
+ return false;
1342
+ }
1343
+ function isPublicIpAddress(address) {
1344
+ const ipVersion = isIP(address);
1345
+ if (ipVersion === 4)
1346
+ return !isPrivateOrReservedIpv4(address);
1347
+ if (ipVersion === 6)
1348
+ return !isPrivateOrReservedIpv6(address);
1349
+ return false;
1350
+ }
1351
+ function hostnameForResolution(url) {
1352
+ return url.hostname.replace(/^\[|\]$/g, "");
1353
+ }
1354
+ async function defaultInviteResolutionLookup(hostname) {
1355
+ return dnsLookup(hostname, { all: true, verbatim: true });
1356
+ }
1357
+ async function defaultInviteResolutionHeadRequest(target, timeoutMs) {
1358
+ return new Promise((resolve, reject) => {
1359
+ const url = target.url;
1360
+ const request = url.protocol === "https:" ? httpsRequest : httpRequest;
1361
+ const options = {
1362
+ protocol: url.protocol,
1363
+ hostname: target.resolvedAddress,
1364
+ port: url.port || undefined,
1365
+ method: "HEAD",
1366
+ path: `${url.pathname}${url.search}`,
1367
+ headers: {
1368
+ Host: target.hostHeader
1369
+ }
1370
+ };
1371
+ if (target.tlsServername) {
1372
+ options.servername = target.tlsServername;
1373
+ }
1374
+ let settled = false;
1375
+ const req = request(options, (response) => {
1376
+ settled = true;
1377
+ response.resume();
1378
+ resolve({ httpStatus: response.statusCode ?? null });
1379
+ });
1380
+ req.setTimeout(timeoutMs, () => {
1381
+ if (settled)
1382
+ return;
1383
+ const error = new Error("Invite resolution probe timed out");
1384
+ error.name = "AbortError";
1385
+ req.destroy(error);
1386
+ });
1387
+ req.on("error", (error) => {
1388
+ if (settled)
1389
+ return;
1390
+ settled = true;
1391
+ reject(error);
1392
+ });
1393
+ req.end();
1394
+ });
1395
+ }
1396
+ const defaultInviteResolutionNetwork = {
1397
+ lookup: defaultInviteResolutionLookup,
1398
+ requestHead: defaultInviteResolutionHeadRequest
1399
+ };
1400
+ let inviteResolutionNetwork = defaultInviteResolutionNetwork;
1401
+ export function setInviteResolutionNetworkForTest(network) {
1402
+ inviteResolutionNetwork = network
1403
+ ? { ...defaultInviteResolutionNetwork, ...network }
1404
+ : defaultInviteResolutionNetwork;
1405
+ }
1406
+ async function lookupInviteResolutionHostname(hostname, network = inviteResolutionNetwork) {
1407
+ let timeout = null;
1408
+ try {
1409
+ return await Promise.race([
1410
+ network.lookup(hostname),
1411
+ new Promise((_, reject) => {
1412
+ timeout = setTimeout(() => reject(badRequest(`url hostname DNS lookup timed out after ${INVITE_RESOLUTION_DNS_TIMEOUT_MS}ms`)), INVITE_RESOLUTION_DNS_TIMEOUT_MS);
1413
+ })
1414
+ ]);
1415
+ }
1416
+ catch (error) {
1417
+ if (error instanceof Error && "status" in error)
1418
+ throw error;
1419
+ throw badRequest("url hostname could not be resolved");
1420
+ }
1421
+ finally {
1422
+ if (timeout)
1423
+ clearTimeout(timeout);
1424
+ }
1425
+ }
1426
+ async function resolveInviteResolutionTarget(url, network = inviteResolutionNetwork) {
1427
+ const hostname = hostnameForResolution(url);
1428
+ if (parseIpv4Address(hostname)) {
1429
+ if (!isPublicIpAddress(hostname)) {
1430
+ throw badRequest("url resolves to a private, local, multicast, or reserved address");
1431
+ }
1432
+ return {
1433
+ url,
1434
+ resolvedAddress: hostname,
1435
+ resolvedAddresses: [hostname],
1436
+ hostHeader: url.host,
1437
+ tlsServername: undefined,
1438
+ };
1439
+ }
1440
+ const literalIpVersion = isIP(hostname);
1441
+ if (literalIpVersion !== 0) {
1442
+ if (!isPublicIpAddress(hostname)) {
1443
+ throw badRequest("url resolves to a private, local, multicast, or reserved address");
1444
+ }
1445
+ return {
1446
+ url,
1447
+ resolvedAddress: hostname,
1448
+ resolvedAddresses: [hostname],
1449
+ hostHeader: url.host,
1450
+ tlsServername: undefined,
1451
+ };
1452
+ }
1453
+ const results = await lookupInviteResolutionHostname(hostname, network);
1454
+ if (results.length === 0) {
1455
+ throw badRequest("url hostname did not resolve to any addresses");
1456
+ }
1457
+ const resolvedAddresses = results.map((result) => result.address);
1458
+ const unsafeAddress = resolvedAddresses.find((address) => !isPublicIpAddress(address));
1459
+ if (unsafeAddress) {
1460
+ throw badRequest("url resolves to a private, local, multicast, or reserved address");
1461
+ }
1462
+ return {
1463
+ url,
1464
+ resolvedAddress: resolvedAddresses[0],
1465
+ resolvedAddresses,
1466
+ hostHeader: url.host,
1467
+ tlsServername: url.protocol === "https:" && isIP(hostname) === 0
1468
+ ? hostname
1469
+ : undefined
1470
+ };
1471
+ }
1472
+ async function probeInviteResolutionTarget(target, timeoutMs, network = inviteResolutionNetwork) {
1473
+ const startedAt = Date.now();
1474
+ try {
1475
+ const response = await network.requestHead(target, timeoutMs);
1476
+ const durationMs = Date.now() - startedAt;
1477
+ if (response.httpStatus !== null &&
1478
+ ((response.httpStatus >= 200 && response.httpStatus < 300) ||
1479
+ response.httpStatus === 401 ||
1480
+ response.httpStatus === 403 ||
1481
+ response.httpStatus === 404 ||
1482
+ response.httpStatus === 405 ||
1483
+ response.httpStatus === 422 ||
1484
+ response.httpStatus === 500 ||
1485
+ response.httpStatus === 501)) {
1486
+ return {
1487
+ status: "reachable",
1488
+ method: "HEAD",
1489
+ durationMs,
1490
+ httpStatus: response.httpStatus,
1491
+ message: `Webhook endpoint responded to HEAD with HTTP ${response.httpStatus}.`
1492
+ };
1493
+ }
1494
+ return {
1495
+ status: "unreachable",
1496
+ method: "HEAD",
1497
+ durationMs,
1498
+ httpStatus: response.httpStatus,
1499
+ message: response.httpStatus === null
1500
+ ? "Webhook endpoint probe did not return an HTTP status."
1501
+ : `Webhook endpoint probe returned HTTP ${response.httpStatus}.`
1502
+ };
1503
+ }
1504
+ catch (error) {
1505
+ const durationMs = Date.now() - startedAt;
1506
+ if (isAbortError(error)) {
1507
+ return {
1508
+ status: "timeout",
1509
+ method: "HEAD",
1510
+ durationMs,
1511
+ httpStatus: null,
1512
+ message: `Webhook endpoint probe timed out after ${timeoutMs}ms.`
1513
+ };
1514
+ }
1515
+ return {
1516
+ status: "unreachable",
1517
+ method: "HEAD",
1518
+ durationMs,
1519
+ httpStatus: null,
1520
+ message: error instanceof Error
1521
+ ? error.message
1522
+ : "Webhook endpoint probe failed."
1523
+ };
1524
+ }
1525
+ }
1526
+ export function accessRoutes(db, opts) {
1527
+ const router = Router();
1528
+ const access = accessService(db);
1529
+ const operatorAuth = operatorAuthService(db);
1530
+ const agents = agentService(db);
1531
+ const routeInviteResolutionNetwork = opts.inviteResolutionNetwork
1532
+ ? { ...defaultInviteResolutionNetwork, ...opts.inviteResolutionNetwork }
1533
+ : inviteResolutionNetwork;
1534
+ async function assertInstanceAdmin(req) {
1535
+ if (req.actor.type !== "operator")
1536
+ throw unauthorized();
1537
+ if (isLocalImplicit(req))
1538
+ return;
1539
+ const allowed = await access.isInstanceAdmin(req.actor.userId);
1540
+ if (!allowed)
1541
+ throw forbidden("Instance admin required");
1542
+ }
1543
+ router.get("/instance-claim/:token", async (req, res) => {
1544
+ const token = req.params.token.trim();
1545
+ const code = typeof req.query.code === "string" ? req.query.code.trim() : undefined;
1546
+ if (!token)
1547
+ throw notFound("Operator claim challenge not found");
1548
+ const challenge = inspectInstanceClaimChallenge(token, code);
1549
+ if (challenge.status === "invalid")
1550
+ throw notFound("Operator claim challenge not found");
1551
+ res.json(challenge);
1552
+ });
1553
+ router.post("/instance-claim/:token/claim", async (req, res) => {
1554
+ const token = req.params.token.trim();
1555
+ const code = typeof req.body?.code === "string" ? req.body.code.trim() : undefined;
1556
+ if (!token)
1557
+ throw notFound("Operator claim challenge not found");
1558
+ if (!code)
1559
+ throw badRequest("Claim code is required");
1560
+ if (req.actor.type !== "operator" ||
1561
+ req.actor.source !== "session" ||
1562
+ !req.actor.userId) {
1563
+ throw unauthorized("Sign in before claiming operator ownership");
1564
+ }
1565
+ const claimed = await claimInstanceOwnership(db, {
1566
+ token,
1567
+ code,
1568
+ userId: req.actor.userId
1569
+ });
1570
+ if (claimed.status === "invalid")
1571
+ throw notFound("Operator claim challenge not found");
1572
+ if (claimed.status === "expired")
1573
+ throw conflict("Operator claim challenge expired. Restart server to generate a new one.");
1574
+ if (claimed.status === "claimed") {
1575
+ res.json({
1576
+ claimed: true,
1577
+ userId: claimed.claimedByUserId ?? req.actor.userId
1578
+ });
1579
+ return;
1580
+ }
1581
+ throw conflict("Operator claim challenge is no longer available");
1582
+ });
1583
+ router.post("/bootstrap/claim", async (req, res) => {
1584
+ if (opts.deploymentMode !== "authenticated" ||
1585
+ opts.deploymentExposure !== "private") {
1586
+ throw notFound("Browser first-admin claim is not available");
1587
+ }
1588
+ if (req.actor.type !== "operator" ||
1589
+ req.actor.source !== "session" ||
1590
+ !req.actor.userId) {
1591
+ throw unauthorized("Sign in from a browser session before claiming first admin");
1592
+ }
1593
+ const claimed = await claimFirstInstanceAdmin(db, {
1594
+ userId: req.actor.userId,
1595
+ });
1596
+ if (claimed.status === "already_claimed") {
1597
+ throw conflict("Someone else has already claimed this instance");
1598
+ }
1599
+ res.json({ claimed: true, userId: claimed.userId });
1600
+ });
1601
+ router.post("/cli-auth/challenges", validate(createCliAuthChallengeSchema), async (req, res) => {
1602
+ const created = await operatorAuth.createCliAuthChallenge(req.body);
1603
+ const approvalPath = buildCliAuthApprovalPath(created.challenge.id, created.challengeSecret);
1604
+ const baseUrl = requestBaseUrl(req);
1605
+ res.status(201).json({
1606
+ id: created.challenge.id,
1607
+ token: created.challengeSecret,
1608
+ operatorApiToken: created.pendingOperatorToken,
1609
+ approvalPath,
1610
+ approvalUrl: baseUrl ? `${baseUrl}${approvalPath}` : null,
1611
+ pollPath: `/cli-auth/challenges/${created.challenge.id}`,
1612
+ expiresAt: created.challenge.expiresAt.toISOString(),
1613
+ suggestedPollIntervalMs: 1000,
1614
+ });
1615
+ });
1616
+ router.get("/cli-auth/challenges/:id", async (req, res) => {
1617
+ const id = req.params.id.trim();
1618
+ const token = typeof req.query.token === "string" ? req.query.token.trim() : "";
1619
+ if (!id || !token)
1620
+ throw notFound("CLI auth challenge not found");
1621
+ const challenge = await operatorAuth.describeCliAuthChallenge(id, token);
1622
+ if (!challenge)
1623
+ throw notFound("CLI auth challenge not found");
1624
+ const isSignedInOperatorUser = req.actor.type === "operator" &&
1625
+ (req.actor.source === "session" || isLocalImplicit(req)) &&
1626
+ Boolean(req.actor.userId);
1627
+ const canApprove = isSignedInOperatorUser &&
1628
+ (challenge.requestedAccess !== "instance_admin_required" ||
1629
+ isLocalImplicit(req) ||
1630
+ Boolean(req.actor.isInstanceAdmin));
1631
+ res.json({
1632
+ ...challenge,
1633
+ requiresSignIn: !isSignedInOperatorUser,
1634
+ canApprove,
1635
+ currentUserId: req.actor.type === "operator" ? req.actor.userId ?? null : null,
1636
+ });
1637
+ });
1638
+ router.post("/cli-auth/challenges/:id/approve", validate(resolveCliAuthChallengeSchema), async (req, res) => {
1639
+ const id = req.params.id.trim();
1640
+ if (req.actor.type !== "operator" ||
1641
+ (!req.actor.userId && !isLocalImplicit(req))) {
1642
+ throw unauthorized("Sign in before approving CLI access");
1643
+ }
1644
+ const userId = req.actor.userId ?? "local-operator";
1645
+ const approved = await operatorAuth.approveCliAuthChallenge(id, req.body.token, userId);
1646
+ if (approved.status === "approved") {
1647
+ const squadIds = await operatorAuth.resolveOperatorActivitySquadIds({
1648
+ userId,
1649
+ requestedSquadId: approved.challenge.requestedSquadId,
1650
+ operatorApiKeyId: approved.challenge.operatorApiKeyId,
1651
+ });
1652
+ for (const squadId of squadIds) {
1653
+ await logActivity(db, {
1654
+ squadId,
1655
+ actorType: "user",
1656
+ actorId: userId,
1657
+ action: "operator_api_key.created",
1658
+ entityType: "user",
1659
+ entityId: userId,
1660
+ details: {
1661
+ operatorApiKeyId: approved.challenge.operatorApiKeyId,
1662
+ requestedAccess: approved.challenge.requestedAccess,
1663
+ requestedSquadId: approved.challenge.requestedSquadId,
1664
+ challengeId: approved.challenge.id,
1665
+ },
1666
+ });
1667
+ }
1668
+ }
1669
+ res.json({
1670
+ approved: approved.status === "approved",
1671
+ status: approved.status,
1672
+ userId,
1673
+ keyId: approved.challenge.operatorApiKeyId ?? null,
1674
+ expiresAt: approved.challenge.expiresAt.toISOString(),
1675
+ });
1676
+ });
1677
+ router.post("/cli-auth/challenges/:id/cancel", validate(resolveCliAuthChallengeSchema), async (req, res) => {
1678
+ const id = req.params.id.trim();
1679
+ const cancelled = await operatorAuth.cancelCliAuthChallenge(id, req.body.token);
1680
+ res.json({
1681
+ status: cancelled.status,
1682
+ cancelled: cancelled.status === "cancelled",
1683
+ });
1684
+ });
1685
+ router.get("/cli-auth/me", async (req, res) => {
1686
+ if (req.actor.type !== "operator" || !req.actor.userId) {
1687
+ throw unauthorized("Operator authentication required");
1688
+ }
1689
+ const accessSnapshot = await operatorAuth.resolveOperatorAccess(req.actor.userId);
1690
+ res.json({
1691
+ user: accessSnapshot.user,
1692
+ userId: req.actor.userId,
1693
+ isInstanceAdmin: accessSnapshot.isInstanceAdmin,
1694
+ squadIds: accessSnapshot.squadIds,
1695
+ memberships: accessSnapshot.memberships,
1696
+ source: req.actor.source ?? "none",
1697
+ keyId: req.actor.source === "operator_key" ? req.actor.keyId ?? null : null,
1698
+ });
1699
+ });
1700
+ router.get("/operator-api-keys", async (req, res) => {
1701
+ if (req.actor.type !== "operator" || !req.actor.userId) {
1702
+ throw unauthorized("Operator authentication required");
1703
+ }
1704
+ const keys = await operatorAuth.listOperatorApiKeys(req.actor.userId, {
1705
+ includeInactive: req.query.includeInactive === "true",
1706
+ });
1707
+ res.json(keys);
1708
+ });
1709
+ router.post("/operator-api-keys", validate(createOperatorApiKeySchema), async (req, res) => {
1710
+ if (req.actor.type !== "operator" || !req.actor.userId) {
1711
+ throw unauthorized("Operator authentication required");
1712
+ }
1713
+ if (req.body.requestedSquadId) {
1714
+ assertSquadAccess(req, req.body.requestedSquadId);
1715
+ }
1716
+ const key = await operatorAuth.createNamedOperatorApiKey({
1717
+ userId: req.actor.userId,
1718
+ name: req.body.name,
1719
+ expiresAt: req.body.expiresAt === undefined ? undefined : req.body.expiresAt,
1720
+ });
1721
+ const squadIds = await operatorAuth.resolveOperatorActivitySquadIds({
1722
+ userId: req.actor.userId,
1723
+ requestedSquadId: req.body.requestedSquadId ?? null,
1724
+ operatorApiKeyId: key.id,
1725
+ });
1726
+ for (const squadId of squadIds) {
1727
+ await logActivity(db, {
1728
+ squadId,
1729
+ actorType: "user",
1730
+ actorId: req.actor.userId,
1731
+ action: "operator_api_key.created",
1732
+ entityType: "user",
1733
+ entityId: req.actor.userId,
1734
+ details: {
1735
+ operatorApiKeyId: key.id,
1736
+ name: key.name,
1737
+ requestedSquadId: req.body.requestedSquadId ?? null,
1738
+ expiresAt: key.expiresAt?.toISOString() ?? null,
1739
+ },
1740
+ });
1741
+ }
1742
+ res.status(201).json(key);
1743
+ });
1744
+ router.delete("/operator-api-keys/:keyId", async (req, res) => {
1745
+ if (req.actor.type !== "operator" || !req.actor.userId) {
1746
+ throw unauthorized("Operator authentication required");
1747
+ }
1748
+ const keyId = req.params.keyId.trim();
1749
+ if (!isUuidLike(keyId)) {
1750
+ throw badRequest("Invalid operator API key ID");
1751
+ }
1752
+ const key = await operatorAuth.getOperatorApiKeyForUser(keyId, req.actor.userId);
1753
+ if (!key)
1754
+ throw notFound("Operator API key not found");
1755
+ const revoked = await operatorAuth.revokeOperatorApiKey(key.id);
1756
+ if (!revoked)
1757
+ throw notFound("Operator API key not found");
1758
+ const squadIds = await operatorAuth.resolveOperatorActivitySquadIds({
1759
+ userId: req.actor.userId,
1760
+ operatorApiKeyId: key.id,
1761
+ });
1762
+ for (const squadId of squadIds) {
1763
+ await logActivity(db, {
1764
+ squadId,
1765
+ actorType: "user",
1766
+ actorId: req.actor.userId,
1767
+ action: "operator_api_key.revoked",
1768
+ entityType: "user",
1769
+ entityId: req.actor.userId,
1770
+ details: {
1771
+ operatorApiKeyId: key.id,
1772
+ name: key.name,
1773
+ revokedVia: "operator_api_key_lifecycle",
1774
+ },
1775
+ });
1776
+ }
1777
+ res.json({ ok: true, keyId: key.id });
1778
+ });
1779
+ router.post("/cli-auth/revoke-current", async (req, res) => {
1780
+ if (req.actor.type !== "operator" || req.actor.source !== "operator_key") {
1781
+ throw badRequest("Current operator API key context is required");
1782
+ }
1783
+ const key = await operatorAuth.assertCurrentOperatorKey(req.actor.keyId, req.actor.userId);
1784
+ await operatorAuth.revokeOperatorApiKey(key.id);
1785
+ const squadIds = await operatorAuth.resolveOperatorActivitySquadIds({
1786
+ userId: key.userId,
1787
+ operatorApiKeyId: key.id,
1788
+ });
1789
+ for (const squadId of squadIds) {
1790
+ await logActivity(db, {
1791
+ squadId,
1792
+ actorType: "user",
1793
+ actorId: key.userId,
1794
+ action: "operator_api_key.revoked",
1795
+ entityType: "user",
1796
+ entityId: key.userId,
1797
+ details: {
1798
+ operatorApiKeyId: key.id,
1799
+ revokedVia: "cli_auth_logout",
1800
+ },
1801
+ });
1802
+ }
1803
+ res.json({ revoked: true, keyId: key.id });
1804
+ });
1805
+ async function assertSquadPermission(req, squadId, permissionKey) {
1806
+ assertSquadAccess(req, squadId);
1807
+ if (req.actor.type === "agent") {
1808
+ if (!req.actor.agentId)
1809
+ throw forbidden();
1810
+ const allowed = await access.hasPermission(squadId, "agent", req.actor.agentId, permissionKey);
1811
+ if (!allowed)
1812
+ throw forbidden("Permission denied");
1813
+ return;
1814
+ }
1815
+ if (req.actor.type !== "operator")
1816
+ throw unauthorized();
1817
+ if (isLocalImplicit(req))
1818
+ return;
1819
+ const allowed = await access.canUser(squadId, req.actor.userId, permissionKey);
1820
+ if (!allowed)
1821
+ throw forbidden("Permission denied");
1822
+ }
1823
+ async function createSquadInviteForSquad(input) {
1824
+ const normalizedAgentMessage = typeof input.agentMessage === "string"
1825
+ ? input.agentMessage.trim() || null
1826
+ : null;
1827
+ const effectiveHumanRole = input.allowedJoinTypes === "agent"
1828
+ ? null
1829
+ : input.humanRole ?? "operator";
1830
+ const insertValues = {
1831
+ squadId: input.squadId,
1832
+ inviteType: "squad_join",
1833
+ allowedJoinTypes: input.allowedJoinTypes,
1834
+ defaultsPayload: mergeInviteDefaults(input.defaultsPayload ?? null, normalizedAgentMessage, effectiveHumanRole),
1835
+ expiresAt: squadInviteExpiresAt(),
1836
+ invitedByUserId: input.req.actor.userId ?? null
1837
+ };
1838
+ let token = null;
1839
+ let created = null;
1840
+ for (let attempt = 0; attempt < INVITE_TOKEN_MAX_RETRIES; attempt += 1) {
1841
+ const candidateToken = createInviteToken();
1842
+ try {
1843
+ const row = await db
1844
+ .insert(invites)
1845
+ .values({
1846
+ ...insertValues,
1847
+ tokenHash: hashToken(candidateToken)
1848
+ })
1849
+ .returning()
1850
+ .then((rows) => rows[0]);
1851
+ token = candidateToken;
1852
+ created = row;
1853
+ break;
1854
+ }
1855
+ catch (error) {
1856
+ if (!isInviteTokenHashCollisionError(error)) {
1857
+ throw error;
1858
+ }
1859
+ }
1860
+ }
1861
+ if (!token || !created) {
1862
+ throw conflict("Failed to generate a unique invite token. Please retry.");
1863
+ }
1864
+ return { token, created, normalizedAgentMessage };
1865
+ }
1866
+ async function approveHumanJoinRequestFromInvite(input) {
1867
+ if (input.joinRequest.requestType !== "human") {
1868
+ throw badRequest("Only human join requests can be approved through a human invite");
1869
+ }
1870
+ if (!input.joinRequest.requestingUserId) {
1871
+ throw conflict("Join request missing user identity");
1872
+ }
1873
+ const membershipRole = resolveHumanInviteRole(input.invite.defaultsPayload);
1874
+ await access.ensureMembership(input.squadId, "user", input.joinRequest.requestingUserId, membershipRole, "active");
1875
+ const grants = humanJoinGrantsFromDefaults(input.invite.defaultsPayload, membershipRole);
1876
+ await access.setPrincipalGrants(input.squadId, "user", input.joinRequest.requestingUserId, grants, input.invite.invitedByUserId ?? null);
1877
+ if (input.joinRequest.status === "approved") {
1878
+ return input.joinRequest;
1879
+ }
1880
+ const approvedAt = new Date();
1881
+ const approvedByUserId = input.invite.invitedByUserId ?? (isLocalImplicit(input.req) ? "local-operator" : null);
1882
+ const approved = await db
1883
+ .update(joinRequests)
1884
+ .set({
1885
+ status: "approved",
1886
+ approvedByUserId,
1887
+ approvedAt,
1888
+ updatedAt: approvedAt,
1889
+ })
1890
+ .where(eq(joinRequests.id, input.joinRequest.id))
1891
+ .returning()
1892
+ .then((rows) => rows[0] ?? null);
1893
+ await logActivity(db, {
1894
+ squadId: input.squadId,
1895
+ actorType: "user",
1896
+ actorId: approvedByUserId ?? "operator",
1897
+ action: "join.approved",
1898
+ entityType: "join_request",
1899
+ entityId: input.joinRequest.id,
1900
+ details: {
1901
+ requestType: "human",
1902
+ inviteId: input.invite.id,
1903
+ source: "human_invite_accept",
1904
+ },
1905
+ });
1906
+ return approved ?? {
1907
+ ...input.joinRequest,
1908
+ status: "approved",
1909
+ approvedByUserId,
1910
+ approvedAt,
1911
+ updatedAt: approvedAt,
1912
+ };
1913
+ }
1914
+ async function getInviteSquadBranding(squadId, inviteToken = null) {
1915
+ if (!squadId) {
1916
+ return { name: null, brandColor: null, logoAssetId: null, logoUrl: null };
1917
+ }
1918
+ const squad = await db
1919
+ .select({
1920
+ name: squads.name,
1921
+ brandColor: squads.brandColor,
1922
+ logoAssetId: squadLogos.assetId,
1923
+ })
1924
+ .from(squads)
1925
+ .leftJoin(squadLogos, eq(squadLogos.squadId, squads.id))
1926
+ .where(eq(squads.id, squadId))
1927
+ .then((rows) => rows[0] ?? null);
1928
+ let logoUrl = null;
1929
+ if (inviteToken && squad?.logoAssetId) {
1930
+ const logoAsset = await getInviteLogoAsset(squadId);
1931
+ if (logoAsset?.squadId) {
1932
+ try {
1933
+ const storage = getStorageService();
1934
+ const logoObject = await storage.headObject(logoAsset.squadId, logoAsset.objectKey);
1935
+ if (logoObject.exists) {
1936
+ logoUrl = `/api/invites/${inviteToken}/logo`;
1937
+ }
1938
+ }
1939
+ catch (err) {
1940
+ logger.warn({
1941
+ err,
1942
+ squadId,
1943
+ logoAssetId: squad.logoAssetId,
1944
+ }, "invite logo storage check failed");
1945
+ }
1946
+ }
1947
+ }
1948
+ return {
1949
+ name: squad?.name ?? null,
1950
+ brandColor: squad?.brandColor ?? null,
1951
+ logoAssetId: squad?.logoAssetId ?? null,
1952
+ logoUrl,
1953
+ };
1954
+ }
1955
+ async function getInviteLogoAsset(squadId) {
1956
+ if (!squadId)
1957
+ return null;
1958
+ const logoAsset = await db
1959
+ .select({
1960
+ squadId: squads.id,
1961
+ objectKey: assets.objectKey,
1962
+ contentType: assets.contentType,
1963
+ byteSize: assets.byteSize,
1964
+ originalFilename: assets.originalFilename,
1965
+ })
1966
+ .from(squads)
1967
+ .leftJoin(squadLogos, eq(squadLogos.squadId, squads.id))
1968
+ .leftJoin(assets, eq(assets.id, squadLogos.assetId))
1969
+ .where(eq(squads.id, squadId))
1970
+ .then((rows) => rows[0] ?? null);
1971
+ if (!logoAsset?.objectKey)
1972
+ return null;
1973
+ return {
1974
+ squadId: logoAsset.squadId,
1975
+ objectKey: logoAsset.objectKey,
1976
+ contentType: logoAsset.contentType,
1977
+ byteSize: logoAsset.byteSize,
1978
+ originalFilename: logoAsset.originalFilename,
1979
+ };
1980
+ }
1981
+ router.get("/skills/available", (req, res) => {
1982
+ assertAuthenticated(req);
1983
+ res.json({ skills: listAvailableSkills() });
1984
+ });
1985
+ router.get("/skills/index", (req, res) => {
1986
+ assertAuthenticated(req);
1987
+ res.json({
1988
+ skills: [
1989
+ { name: "slaw", path: "/api/skills/slaw" },
1990
+ {
1991
+ name: "para-memory-files",
1992
+ path: "/api/skills/para-memory-files"
1993
+ },
1994
+ {
1995
+ name: "slaw-create-agent",
1996
+ path: "/api/skills/slaw-create-agent"
1997
+ },
1998
+ {
1999
+ name: "slaw-converting-plans-to-tasks",
2000
+ path: "/api/skills/slaw-converting-plans-to-tasks"
2001
+ }
2002
+ ]
2003
+ });
2004
+ });
2005
+ router.get("/skills/:skillName", (req, res) => {
2006
+ assertAuthenticated(req);
2007
+ const skillName = req.params.skillName.trim().toLowerCase();
2008
+ const markdown = readSkillMarkdown(skillName);
2009
+ if (!markdown)
2010
+ throw notFound("Skill not found");
2011
+ res.type("text/markdown").send(markdown);
2012
+ });
2013
+ router.post("/squads/:squadId/invites", validate(createSquadInviteSchema), async (req, res) => {
2014
+ const squadId = req.params.squadId;
2015
+ await assertSquadPermission(req, squadId, "users:invite");
2016
+ const { token, created, normalizedAgentMessage } = await createSquadInviteForSquad({
2017
+ req,
2018
+ squadId,
2019
+ allowedJoinTypes: req.body.allowedJoinTypes,
2020
+ humanRole: req.body.humanRole ?? null,
2021
+ defaultsPayload: req.body.defaultsPayload ?? null,
2022
+ agentMessage: req.body.agentMessage ?? null
2023
+ });
2024
+ await logActivity(db, {
2025
+ squadId,
2026
+ actorType: req.actor.type === "agent" ? "agent" : "user",
2027
+ actorId: req.actor.type === "agent"
2028
+ ? req.actor.agentId ?? "unknown-agent"
2029
+ : req.actor.userId ?? "operator",
2030
+ action: "invite.created",
2031
+ entityType: "invite",
2032
+ entityId: created.id,
2033
+ details: {
2034
+ inviteType: created.inviteType,
2035
+ allowedJoinTypes: created.allowedJoinTypes,
2036
+ expiresAt: created.expiresAt.toISOString(),
2037
+ humanRole: extractInviteHumanRole(created),
2038
+ hasAgentMessage: Boolean(normalizedAgentMessage)
2039
+ }
2040
+ });
2041
+ const squadBranding = await getInviteSquadBranding(created.squadId, token);
2042
+ const inviteSummary = toInviteSummaryResponse(req, token, created, squadBranding);
2043
+ res.status(201).json({
2044
+ ...created,
2045
+ token,
2046
+ invitePath: inviteSummary.invitePath,
2047
+ inviteUrl: inviteSummary.inviteUrl,
2048
+ squadName: squadBranding.name,
2049
+ onboardingTextPath: inviteSummary.onboardingTextPath,
2050
+ onboardingTextUrl: inviteSummary.onboardingTextUrl,
2051
+ inviteMessage: inviteSummary.inviteMessage
2052
+ });
2053
+ });
2054
+ router.get("/invites/:token", async (req, res) => {
2055
+ const token = req.params.token.trim();
2056
+ if (!token)
2057
+ throw notFound("Invite not found");
2058
+ const invite = await db
2059
+ .select()
2060
+ .from(invites)
2061
+ .where(eq(invites.tokenHash, hashToken(token)))
2062
+ .then((rows) => rows[0] ?? null);
2063
+ const inviteJoinRequest = await resolveAcceptedInviteJoinRequest(db, req, invite);
2064
+ if (!invite ||
2065
+ invite.revokedAt ||
2066
+ inviteExpired(invite) ||
2067
+ (invite.acceptedAt && !inviteJoinRequest)) {
2068
+ throw notFound("Invite not found");
2069
+ }
2070
+ const squadBranding = await getInviteSquadBranding(invite.squadId, token);
2071
+ const inviterName = invite.invitedByUserId
2072
+ ? await loadUsersById(db, [invite.invitedByUserId]).then((m) => m.get(invite.invitedByUserId)?.name ?? null)
2073
+ : null;
2074
+ res.json({
2075
+ ...toInviteSummaryResponse(req, token, invite, squadBranding),
2076
+ invitedByUserName: inviterName,
2077
+ joinRequestStatus: inviteJoinRequest?.status ?? null,
2078
+ joinRequestType: inviteJoinRequest?.requestType ?? null,
2079
+ });
2080
+ });
2081
+ router.get("/invites/:token/logo", async (req, res, next) => {
2082
+ const token = req.params.token.trim();
2083
+ if (!token)
2084
+ throw notFound("Invite not found");
2085
+ const invite = await db
2086
+ .select()
2087
+ .from(invites)
2088
+ .where(eq(invites.tokenHash, hashToken(token)))
2089
+ .then((rows) => rows[0] ?? null);
2090
+ const inviteJoinRequest = await resolveAcceptedInviteJoinRequest(db, req, invite);
2091
+ if (!invite ||
2092
+ invite.revokedAt ||
2093
+ inviteExpired(invite) ||
2094
+ (invite.acceptedAt && !inviteJoinRequest)) {
2095
+ throw notFound("Invite not found");
2096
+ }
2097
+ const logoAsset = await getInviteLogoAsset(invite.squadId);
2098
+ if (!logoAsset || !logoAsset.squadId) {
2099
+ throw notFound("Invite logo not found");
2100
+ }
2101
+ const squadId = logoAsset.squadId;
2102
+ const storage = getStorageService();
2103
+ const logoHead = await storage.headObject(squadId, logoAsset.objectKey);
2104
+ if (!logoHead.exists) {
2105
+ throw notFound("Invite logo not found");
2106
+ }
2107
+ const object = await storage.getObject(squadId, logoAsset.objectKey);
2108
+ const responseContentType = logoAsset.contentType ||
2109
+ logoHead.contentType ||
2110
+ object.contentType ||
2111
+ "application/octet-stream";
2112
+ res.setHeader("Content-Type", responseContentType);
2113
+ res.setHeader("Content-Length", String(logoAsset.byteSize || logoHead.contentLength || object.contentLength || 0));
2114
+ res.setHeader("Cache-Control", "private, max-age=60");
2115
+ res.setHeader("X-Content-Type-Options", "nosniff");
2116
+ if (responseContentType === "image/svg+xml") {
2117
+ res.setHeader("Content-Security-Policy", "sandbox; default-src 'none'; img-src 'self' data:; style-src 'unsafe-inline'");
2118
+ }
2119
+ const filename = logoAsset.originalFilename ?? "squad-logo";
2120
+ res.setHeader("Content-Disposition", `inline; filename=\"${filename.replaceAll("\"", "")}\"`);
2121
+ object.stream.on("error", (err) => {
2122
+ next(err);
2123
+ });
2124
+ object.stream.pipe(res);
2125
+ });
2126
+ router.get("/invites/:token/onboarding", async (req, res) => {
2127
+ const token = req.params.token.trim();
2128
+ if (!token)
2129
+ throw notFound("Invite not found");
2130
+ const invite = await db
2131
+ .select()
2132
+ .from(invites)
2133
+ .where(eq(invites.tokenHash, hashToken(token)))
2134
+ .then((rows) => rows[0] ?? null);
2135
+ if (!invite || invite.revokedAt || inviteExpired(invite)) {
2136
+ throw notFound("Invite not found");
2137
+ }
2138
+ const squadBranding = await getInviteSquadBranding(invite.squadId);
2139
+ res.json(buildInviteOnboardingManifest(req, token, invite, {
2140
+ ...opts,
2141
+ squadName: squadBranding.name
2142
+ }));
2143
+ });
2144
+ router.get("/invites/:token/onboarding.txt", async (req, res) => {
2145
+ const token = req.params.token.trim();
2146
+ if (!token)
2147
+ throw notFound("Invite not found");
2148
+ const invite = await db
2149
+ .select()
2150
+ .from(invites)
2151
+ .where(eq(invites.tokenHash, hashToken(token)))
2152
+ .then((rows) => rows[0] ?? null);
2153
+ if (!invite || invite.revokedAt || inviteExpired(invite)) {
2154
+ throw notFound("Invite not found");
2155
+ }
2156
+ const squadBranding = await getInviteSquadBranding(invite.squadId);
2157
+ res
2158
+ .type("text/plain; charset=utf-8")
2159
+ .send(buildInviteOnboardingTextDocument(req, token, invite, {
2160
+ ...opts,
2161
+ squadName: squadBranding.name
2162
+ }));
2163
+ });
2164
+ router.get("/invites/:token/skills/index", async (req, res) => {
2165
+ const token = req.params.token.trim();
2166
+ if (!token)
2167
+ throw notFound("Invite not found");
2168
+ const invite = await db
2169
+ .select()
2170
+ .from(invites)
2171
+ .where(eq(invites.tokenHash, hashToken(token)))
2172
+ .then((rows) => rows[0] ?? null);
2173
+ if (!invite || invite.revokedAt || inviteExpired(invite)) {
2174
+ throw notFound("Invite not found");
2175
+ }
2176
+ res.json({
2177
+ skills: [
2178
+ {
2179
+ name: "slaw",
2180
+ path: `/api/invites/${token}/skills/slaw`,
2181
+ },
2182
+ ],
2183
+ });
2184
+ });
2185
+ router.get("/invites/:token/skills/:skillName", async (req, res) => {
2186
+ const token = req.params.token.trim();
2187
+ if (!token)
2188
+ throw notFound("Invite not found");
2189
+ const invite = await db
2190
+ .select()
2191
+ .from(invites)
2192
+ .where(eq(invites.tokenHash, hashToken(token)))
2193
+ .then((rows) => rows[0] ?? null);
2194
+ if (!invite || invite.revokedAt || inviteExpired(invite)) {
2195
+ throw notFound("Invite not found");
2196
+ }
2197
+ const skillName = req.params.skillName.trim().toLowerCase();
2198
+ if (skillName !== "slaw")
2199
+ throw notFound("Skill not found");
2200
+ const markdown = readSkillMarkdown(skillName);
2201
+ if (!markdown)
2202
+ throw notFound("Skill not found");
2203
+ res.type("text/markdown").send(markdown);
2204
+ });
2205
+ router.get("/invites/:token/test-resolution", async (req, res) => {
2206
+ const token = req.params.token.trim();
2207
+ if (!token)
2208
+ throw notFound("Invite not found");
2209
+ const invite = await db
2210
+ .select()
2211
+ .from(invites)
2212
+ .where(eq(invites.tokenHash, hashToken(token)))
2213
+ .then((rows) => rows[0] ?? null);
2214
+ if (!invite || invite.revokedAt || inviteExpired(invite)) {
2215
+ throw notFound("Invite not found");
2216
+ }
2217
+ const rawUrl = typeof req.query.url === "string" ? req.query.url.trim() : "";
2218
+ if (!rawUrl)
2219
+ throw badRequest("url query parameter is required");
2220
+ let target;
2221
+ try {
2222
+ target = new URL(rawUrl);
2223
+ }
2224
+ catch {
2225
+ throw badRequest("url must be an absolute http(s) URL");
2226
+ }
2227
+ if (target.protocol !== "http:" && target.protocol !== "https:") {
2228
+ throw badRequest("url must use http or https");
2229
+ }
2230
+ const parsedTimeoutMs = typeof req.query.timeoutMs === "string"
2231
+ ? Number(req.query.timeoutMs)
2232
+ : NaN;
2233
+ const timeoutMs = Number.isFinite(parsedTimeoutMs)
2234
+ ? Math.max(1000, Math.min(15000, Math.floor(parsedTimeoutMs)))
2235
+ : 5000;
2236
+ const resolvedTarget = await resolveInviteResolutionTarget(target, routeInviteResolutionNetwork);
2237
+ const probe = await probeInviteResolutionTarget(resolvedTarget, timeoutMs, routeInviteResolutionNetwork);
2238
+ res.json({
2239
+ inviteId: invite.id,
2240
+ testResolutionPath: `/api/invites/${token}/test-resolution`,
2241
+ requestedUrl: target.toString(),
2242
+ timeoutMs,
2243
+ ...probe
2244
+ });
2245
+ });
2246
+ router.post("/invites/:token/accept", validate(acceptInviteSchema), async (req, res) => {
2247
+ const token = req.params.token.trim();
2248
+ if (!token)
2249
+ throw notFound("Invite not found");
2250
+ const invite = await db
2251
+ .select()
2252
+ .from(invites)
2253
+ .where(eq(invites.tokenHash, hashToken(token)))
2254
+ .then((rows) => rows[0] ?? null);
2255
+ if (!invite || invite.revokedAt || inviteExpired(invite)) {
2256
+ throw notFound("Invite not found");
2257
+ }
2258
+ const inviteAlreadyAccepted = Boolean(invite.acceptedAt);
2259
+ const existingJoinRequestForInvite = inviteAlreadyAccepted
2260
+ ? await db
2261
+ .select()
2262
+ .from(joinRequests)
2263
+ .where(eq(joinRequests.inviteId, invite.id))
2264
+ .then((rows) => rows[0] ?? null)
2265
+ : null;
2266
+ if (invite.inviteType === "bootstrap_squad_lead") {
2267
+ if (inviteAlreadyAccepted)
2268
+ throw notFound("Invite not found");
2269
+ if (req.body.requestType !== "human") {
2270
+ throw badRequest("Bootstrap invite requires human request type");
2271
+ }
2272
+ if (req.actor.type !== "operator" ||
2273
+ (!req.actor.userId && !isLocalImplicit(req))) {
2274
+ throw unauthorized("Authenticated user required for bootstrap acceptance");
2275
+ }
2276
+ const userId = req.actor.userId ?? "local-operator";
2277
+ const claimed = await claimFirstInstanceAdmin(db, {
2278
+ userId,
2279
+ onClaim: async (tx) => {
2280
+ const updatedInvite = await tx
2281
+ .update(invites)
2282
+ .set({ acceptedAt: new Date(), updatedAt: new Date() })
2283
+ .where(and(eq(invites.id, invite.id), isNull(invites.acceptedAt), isNull(invites.revokedAt)))
2284
+ .returning()
2285
+ .then((rows) => rows[0] ?? null);
2286
+ if (!updatedInvite) {
2287
+ throw conflict("Bootstrap invite is no longer available");
2288
+ }
2289
+ return updatedInvite;
2290
+ },
2291
+ });
2292
+ if (claimed.status === "already_claimed") {
2293
+ throw conflict("Someone else has already claimed this instance");
2294
+ }
2295
+ const updatedInvite = claimed.value ?? invite;
2296
+ res.status(202).json({
2297
+ inviteId: updatedInvite.id,
2298
+ inviteType: updatedInvite.inviteType,
2299
+ bootstrapAccepted: true,
2300
+ userId
2301
+ });
2302
+ return;
2303
+ }
2304
+ const requestType = req.body.requestType;
2305
+ const squadId = invite.squadId;
2306
+ if (!squadId)
2307
+ throw conflict("Invite is missing squad scope");
2308
+ if (invite.allowedJoinTypes !== "both" &&
2309
+ invite.allowedJoinTypes !== requestType) {
2310
+ throw badRequest(`Invite does not allow ${requestType} joins`);
2311
+ }
2312
+ if (requestType === "human" && req.actor.type !== "operator") {
2313
+ throw unauthorized("Human invite acceptance requires authenticated user");
2314
+ }
2315
+ if (requestType === "human" &&
2316
+ !req.actor.userId &&
2317
+ !isLocalImplicit(req)) {
2318
+ throw unauthorized("Authenticated user is required");
2319
+ }
2320
+ if (requestType === "human" &&
2321
+ actorHasActiveUserMembership(req, squadId)) {
2322
+ throw conflict("You already belong to this squad");
2323
+ }
2324
+ if (requestType === "agent" && !req.body.agentName) {
2325
+ if (!inviteAlreadyAccepted ||
2326
+ !existingJoinRequestForInvite?.agentName) {
2327
+ throw badRequest("agentName is required for agent join requests");
2328
+ }
2329
+ }
2330
+ const actorEmail = requestType === "human" ? await resolveActorEmail(db, req) : null;
2331
+ const actorRequestingUserId = requestType === "human"
2332
+ ? req.actor.userId ?? "local-operator"
2333
+ : null;
2334
+ const canReplayHumanInviteAccept = inviteAlreadyAccepted &&
2335
+ requestType === "human" &&
2336
+ existingJoinRequestForInvite?.requestType === "human" &&
2337
+ Boolean(findReusableHumanJoinRequest([existingJoinRequestForInvite], {
2338
+ requestingUserId: actorRequestingUserId,
2339
+ requestEmailSnapshot: actorEmail,
2340
+ }));
2341
+ const adapterType = req.body.adapterType ?? null;
2342
+ if (inviteAlreadyAccepted && !canReplayHumanInviteAccept) {
2343
+ throw notFound("Invite not found");
2344
+ }
2345
+ const replayJoinRequestId = inviteAlreadyAccepted
2346
+ ? existingJoinRequestForInvite?.id ?? null
2347
+ : null;
2348
+ if (inviteAlreadyAccepted && !replayJoinRequestId) {
2349
+ throw conflict("Join request not found");
2350
+ }
2351
+ const replayMergedDefaults = inviteAlreadyAccepted
2352
+ ? mergeJoinDefaultsPayloadForReplay(existingJoinRequestForInvite?.agentDefaultsPayload ?? null, req.body.agentDefaultsPayload ?? null)
2353
+ : req.body.agentDefaultsPayload ?? null;
2354
+ const gatewayDefaultsPayload = requestType === "agent"
2355
+ ? buildJoinDefaultsPayloadForAccept({
2356
+ adapterType,
2357
+ defaultsPayload: replayMergedDefaults,
2358
+ slawApiUrl: req.body.slawApiUrl ?? null
2359
+ })
2360
+ : null;
2361
+ const joinDefaults = requestType === "agent"
2362
+ ? normalizeAgentDefaultsForJoin({
2363
+ adapterType,
2364
+ defaultsPayload: gatewayDefaultsPayload,
2365
+ deploymentMode: opts.deploymentMode,
2366
+ deploymentExposure: opts.deploymentExposure,
2367
+ bindHost: opts.bindHost,
2368
+ allowedHostnames: opts.allowedHostnames
2369
+ })
2370
+ : {
2371
+ normalized: null,
2372
+ diagnostics: [],
2373
+ fatalErrors: []
2374
+ };
2375
+ if (requestType === "agent" && joinDefaults.fatalErrors.length > 0) {
2376
+ throw badRequest(joinDefaults.fatalErrors.join("; "));
2377
+ }
2378
+ const claimSecret = requestType === "agent" && !inviteAlreadyAccepted
2379
+ ? createClaimSecret()
2380
+ : null;
2381
+ const claimSecretHash = claimSecret ? hashToken(claimSecret) : null;
2382
+ const claimSecretExpiresAt = claimSecret
2383
+ ? new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
2384
+ : null;
2385
+ const existingHumanJoinRequest = requestType === "human"
2386
+ ? findReusableHumanJoinRequest(await db
2387
+ .select()
2388
+ .from(joinRequests)
2389
+ .where(and(eq(joinRequests.squadId, squadId), eq(joinRequests.requestType, "human")))
2390
+ .orderBy(desc(joinRequests.createdAt)), {
2391
+ requestingUserId: actorRequestingUserId,
2392
+ requestEmailSnapshot: actorEmail
2393
+ })
2394
+ : null;
2395
+ let created = !inviteAlreadyAccepted
2396
+ ? existingHumanJoinRequest
2397
+ ? await db.transaction(async (tx) => {
2398
+ await tx
2399
+ .update(invites)
2400
+ .set({ acceptedAt: new Date(), updatedAt: new Date() })
2401
+ .where(and(eq(invites.id, invite.id), isNull(invites.acceptedAt), isNull(invites.revokedAt)));
2402
+ return existingHumanJoinRequest;
2403
+ })
2404
+ : await db.transaction(async (tx) => {
2405
+ await tx
2406
+ .update(invites)
2407
+ .set({ acceptedAt: new Date(), updatedAt: new Date() })
2408
+ .where(and(eq(invites.id, invite.id), isNull(invites.acceptedAt), isNull(invites.revokedAt)));
2409
+ const row = await tx
2410
+ .insert(joinRequests)
2411
+ .values({
2412
+ inviteId: invite.id,
2413
+ squadId,
2414
+ requestType,
2415
+ status: "pending_approval",
2416
+ requestIp: requestIp(req),
2417
+ requestingUserId: requestType === "human"
2418
+ ? req.actor.userId ?? "local-operator"
2419
+ : null,
2420
+ requestEmailSnapshot: requestType === "human" ? actorEmail : null,
2421
+ agentName: requestType === "agent" ? req.body.agentName : null,
2422
+ adapterType: requestType === "agent" ? adapterType : null,
2423
+ capabilities: requestType === "agent"
2424
+ ? req.body.capabilities ?? null
2425
+ : null,
2426
+ agentDefaultsPayload: requestType === "agent" ? joinDefaults.normalized : null,
2427
+ claimSecretHash,
2428
+ claimSecretExpiresAt
2429
+ })
2430
+ .returning()
2431
+ .then((rows) => rows[0]);
2432
+ return row;
2433
+ })
2434
+ : await db
2435
+ .update(joinRequests)
2436
+ .set({
2437
+ requestIp: requestIp(req),
2438
+ agentName: requestType === "agent"
2439
+ ? req.body.agentName ??
2440
+ existingJoinRequestForInvite?.agentName ??
2441
+ null
2442
+ : null,
2443
+ capabilities: requestType === "agent"
2444
+ ? req.body.capabilities ??
2445
+ existingJoinRequestForInvite?.capabilities ??
2446
+ null
2447
+ : null,
2448
+ adapterType: requestType === "agent" ? adapterType : null,
2449
+ agentDefaultsPayload: requestType === "agent" ? joinDefaults.normalized : null,
2450
+ updatedAt: new Date()
2451
+ })
2452
+ .where(eq(joinRequests.id, replayJoinRequestId))
2453
+ .returning()
2454
+ .then((rows) => rows[0]);
2455
+ if (!created) {
2456
+ throw conflict("Join request not found");
2457
+ }
2458
+ await logActivity(db, {
2459
+ squadId,
2460
+ actorType: req.actor.type === "agent" ? "agent" : "user",
2461
+ actorId: req.actor.type === "agent"
2462
+ ? req.actor.agentId ?? "invite-agent"
2463
+ : req.actor.userId ??
2464
+ (requestType === "agent" ? "invite-anon" : "operator"),
2465
+ action: inviteAlreadyAccepted
2466
+ ? "join.request_replayed"
2467
+ : "join.requested",
2468
+ entityType: "join_request",
2469
+ entityId: created.id,
2470
+ details: {
2471
+ requestType,
2472
+ requestIp: requestIp(req),
2473
+ inviteReplay: inviteAlreadyAccepted,
2474
+ reusedExistingJoinRequest: Boolean(existingHumanJoinRequest) && !inviteAlreadyAccepted
2475
+ }
2476
+ });
2477
+ if (requestType === "human") {
2478
+ created = await approveHumanJoinRequestFromInvite({
2479
+ req,
2480
+ invite,
2481
+ joinRequest: created,
2482
+ squadId,
2483
+ });
2484
+ }
2485
+ const response = toJoinRequestResponse(created);
2486
+ if (claimSecret) {
2487
+ const squadBranding = await getInviteSquadBranding(invite.squadId);
2488
+ const onboardingManifest = buildInviteOnboardingManifest(req, token, invite, {
2489
+ ...opts,
2490
+ squadName: squadBranding.name
2491
+ });
2492
+ res.status(202).json({
2493
+ ...response,
2494
+ claimSecret,
2495
+ claimApiKeyPath: `/api/join-requests/${created.id}/claim-api-key`,
2496
+ onboarding: onboardingManifest.onboarding,
2497
+ diagnostics: joinDefaults.diagnostics
2498
+ });
2499
+ return;
2500
+ }
2501
+ res.status(202).json({
2502
+ ...response,
2503
+ ...(joinDefaults.diagnostics.length > 0
2504
+ ? { diagnostics: joinDefaults.diagnostics }
2505
+ : {})
2506
+ });
2507
+ });
2508
+ router.post("/invites/:inviteId/revoke", async (req, res) => {
2509
+ const id = req.params.inviteId;
2510
+ const invite = await db
2511
+ .select()
2512
+ .from(invites)
2513
+ .where(eq(invites.id, id))
2514
+ .then((rows) => rows[0] ?? null);
2515
+ if (!invite)
2516
+ throw notFound("Invite not found");
2517
+ if (invite.inviteType === "bootstrap_squad_lead") {
2518
+ await assertInstanceAdmin(req);
2519
+ }
2520
+ else {
2521
+ if (!invite.squadId)
2522
+ throw conflict("Invite is missing squad scope");
2523
+ await assertSquadPermission(req, invite.squadId, "users:invite");
2524
+ }
2525
+ if (invite.acceptedAt)
2526
+ throw conflict("Invite already consumed");
2527
+ if (invite.revokedAt)
2528
+ return res.json(invite);
2529
+ const revoked = await db
2530
+ .update(invites)
2531
+ .set({ revokedAt: new Date(), updatedAt: new Date() })
2532
+ .where(eq(invites.id, id))
2533
+ .returning()
2534
+ .then((rows) => rows[0]);
2535
+ if (invite.squadId) {
2536
+ await logActivity(db, {
2537
+ squadId: invite.squadId,
2538
+ actorType: req.actor.type === "agent" ? "agent" : "user",
2539
+ actorId: req.actor.type === "agent"
2540
+ ? req.actor.agentId ?? "unknown-agent"
2541
+ : req.actor.userId ?? "operator",
2542
+ action: "invite.revoked",
2543
+ entityType: "invite",
2544
+ entityId: id
2545
+ });
2546
+ }
2547
+ res.json(revoked);
2548
+ });
2549
+ router.get("/squads/:squadId/invites", async (req, res) => {
2550
+ const squadId = req.params.squadId;
2551
+ await assertSquadPermission(req, squadId, "users:invite");
2552
+ const query = listSquadInvitesQuerySchema.parse(req.query);
2553
+ const invitesForSquad = await loadSquadInviteRecords(db, squadId, query);
2554
+ res.json(invitesForSquad);
2555
+ });
2556
+ router.get("/squads/:squadId/join-requests", async (req, res) => {
2557
+ const squadId = req.params.squadId;
2558
+ await assertSquadPermission(req, squadId, "joins:approve");
2559
+ const query = listJoinRequestsQuerySchema.parse(req.query);
2560
+ const all = await loadJoinRequestRecords(db, squadId);
2561
+ const filtered = all.filter((row) => {
2562
+ if (query.status && row.status !== query.status)
2563
+ return false;
2564
+ if (query.requestType && row.requestType !== query.requestType)
2565
+ return false;
2566
+ return true;
2567
+ });
2568
+ res.json(filtered);
2569
+ });
2570
+ router.post("/squads/:squadId/join-requests/:requestId/approve", async (req, res) => {
2571
+ const squadId = req.params.squadId;
2572
+ const requestId = req.params.requestId;
2573
+ await assertSquadPermission(req, squadId, "joins:approve");
2574
+ const existing = await db
2575
+ .select()
2576
+ .from(joinRequests)
2577
+ .where(and(eq(joinRequests.squadId, squadId), eq(joinRequests.id, requestId)))
2578
+ .then((rows) => rows[0] ?? null);
2579
+ if (!existing)
2580
+ throw notFound("Join request not found");
2581
+ if (existing.status !== "pending_approval")
2582
+ throw conflict("Join request is not pending");
2583
+ const invite = await db
2584
+ .select()
2585
+ .from(invites)
2586
+ .where(eq(invites.id, existing.inviteId))
2587
+ .then((rows) => rows[0] ?? null);
2588
+ if (!invite)
2589
+ throw notFound("Invite not found");
2590
+ let createdAgentId = existing.createdAgentId ?? null;
2591
+ if (existing.requestType === "human") {
2592
+ if (!existing.requestingUserId)
2593
+ throw conflict("Join request missing user identity");
2594
+ const membershipRole = resolveHumanInviteRole(invite.defaultsPayload);
2595
+ await access.ensureMembership(squadId, "user", existing.requestingUserId, membershipRole, "active");
2596
+ const grants = humanJoinGrantsFromDefaults(invite.defaultsPayload, membershipRole);
2597
+ await access.setPrincipalGrants(squadId, "user", existing.requestingUserId, grants, req.actor.userId ?? null);
2598
+ }
2599
+ else {
2600
+ const existingAgents = await agents.list(squadId);
2601
+ const managerId = resolveJoinRequestAgentManagerId(existingAgents);
2602
+ if (!managerId) {
2603
+ throw conflict("Join request cannot be approved because this squad has no active Squad Lead");
2604
+ }
2605
+ const agentName = deduplicateAgentName(existing.agentName ?? "New Agent", existingAgents.map((a) => ({
2606
+ id: a.id,
2607
+ name: a.name,
2608
+ status: a.status
2609
+ })));
2610
+ const created = await agents.create(squadId, {
2611
+ name: agentName,
2612
+ role: "general",
2613
+ title: null,
2614
+ status: "idle",
2615
+ reportsTo: managerId,
2616
+ capabilities: existing.capabilities ?? null,
2617
+ adapterType: existing.adapterType ?? "process",
2618
+ adapterConfig: existing.agentDefaultsPayload &&
2619
+ typeof existing.agentDefaultsPayload === "object"
2620
+ ? existing.agentDefaultsPayload
2621
+ : {},
2622
+ runtimeConfig: {},
2623
+ budgetMonthlyCents: 0,
2624
+ spentMonthlyCents: 0,
2625
+ permissions: {},
2626
+ lastHeartbeatAt: null,
2627
+ metadata: null
2628
+ });
2629
+ createdAgentId = created.id;
2630
+ await access.ensureMembership(squadId, "agent", created.id, "member", "active");
2631
+ const grants = agentJoinGrantsFromDefaults(invite.defaultsPayload);
2632
+ await access.setPrincipalGrants(squadId, "agent", created.id, grants, req.actor.userId ?? null);
2633
+ }
2634
+ const approved = await db
2635
+ .update(joinRequests)
2636
+ .set({
2637
+ status: "approved",
2638
+ approvedByUserId: req.actor.userId ?? (isLocalImplicit(req) ? "local-operator" : null),
2639
+ approvedAt: new Date(),
2640
+ createdAgentId,
2641
+ updatedAt: new Date()
2642
+ })
2643
+ .where(eq(joinRequests.id, requestId))
2644
+ .returning()
2645
+ .then((rows) => rows[0]);
2646
+ await logActivity(db, {
2647
+ squadId,
2648
+ actorType: "user",
2649
+ actorId: req.actor.userId ?? "operator",
2650
+ action: "join.approved",
2651
+ entityType: "join_request",
2652
+ entityId: requestId,
2653
+ details: { requestType: existing.requestType, createdAgentId }
2654
+ });
2655
+ if (createdAgentId) {
2656
+ void notifyHireApproved(db, {
2657
+ squadId,
2658
+ agentId: createdAgentId,
2659
+ source: "join_request",
2660
+ sourceId: requestId,
2661
+ approvedAt: new Date()
2662
+ }).catch(() => { });
2663
+ }
2664
+ res.json(toJoinRequestResponse(approved));
2665
+ });
2666
+ router.post("/squads/:squadId/join-requests/:requestId/reject", async (req, res) => {
2667
+ const squadId = req.params.squadId;
2668
+ const requestId = req.params.requestId;
2669
+ await assertSquadPermission(req, squadId, "joins:approve");
2670
+ const existing = await db
2671
+ .select()
2672
+ .from(joinRequests)
2673
+ .where(and(eq(joinRequests.squadId, squadId), eq(joinRequests.id, requestId)))
2674
+ .then((rows) => rows[0] ?? null);
2675
+ if (!existing)
2676
+ throw notFound("Join request not found");
2677
+ if (existing.status !== "pending_approval")
2678
+ throw conflict("Join request is not pending");
2679
+ const rejected = await db
2680
+ .update(joinRequests)
2681
+ .set({
2682
+ status: "rejected",
2683
+ rejectedByUserId: req.actor.userId ?? (isLocalImplicit(req) ? "local-operator" : null),
2684
+ rejectedAt: new Date(),
2685
+ updatedAt: new Date()
2686
+ })
2687
+ .where(eq(joinRequests.id, requestId))
2688
+ .returning()
2689
+ .then((rows) => rows[0]);
2690
+ await logActivity(db, {
2691
+ squadId,
2692
+ actorType: "user",
2693
+ actorId: req.actor.userId ?? "operator",
2694
+ action: "join.rejected",
2695
+ entityType: "join_request",
2696
+ entityId: requestId,
2697
+ details: { requestType: existing.requestType }
2698
+ });
2699
+ res.json(toJoinRequestResponse(rejected));
2700
+ });
2701
+ router.post("/join-requests/:requestId/claim-api-key", validate(claimJoinRequestApiKeySchema), async (req, res) => {
2702
+ const requestId = req.params.requestId;
2703
+ const presentedClaimSecretHash = hashToken(req.body.claimSecret);
2704
+ const joinRequest = await db
2705
+ .select()
2706
+ .from(joinRequests)
2707
+ .where(eq(joinRequests.id, requestId))
2708
+ .then((rows) => rows[0] ?? null);
2709
+ if (!joinRequest)
2710
+ throw notFound("Join request not found");
2711
+ if (joinRequest.requestType !== "agent")
2712
+ throw badRequest("Only agent join requests can claim API keys");
2713
+ if (joinRequest.status !== "approved")
2714
+ throw conflict("Join request must be approved before key claim");
2715
+ if (!joinRequest.createdAgentId)
2716
+ throw conflict("Join request has no created agent");
2717
+ if (!joinRequest.claimSecretHash)
2718
+ throw conflict("Join request is missing claim secret metadata");
2719
+ if (!tokenHashesMatch(joinRequest.claimSecretHash, presentedClaimSecretHash)) {
2720
+ throw forbidden("Invalid claim secret");
2721
+ }
2722
+ if (joinRequest.claimSecretExpiresAt &&
2723
+ joinRequest.claimSecretExpiresAt.getTime() <= Date.now()) {
2724
+ throw conflict("Claim secret expired");
2725
+ }
2726
+ if (joinRequest.claimSecretConsumedAt)
2727
+ throw conflict("Claim secret already used");
2728
+ const existingKey = await db
2729
+ .select({ id: agentApiKeys.id })
2730
+ .from(agentApiKeys)
2731
+ .where(eq(agentApiKeys.agentId, joinRequest.createdAgentId))
2732
+ .then((rows) => rows[0] ?? null);
2733
+ if (existingKey)
2734
+ throw conflict("API key already claimed");
2735
+ const consumed = await db
2736
+ .update(joinRequests)
2737
+ .set({ claimSecretConsumedAt: new Date(), updatedAt: new Date() })
2738
+ .where(and(eq(joinRequests.id, requestId), isNull(joinRequests.claimSecretConsumedAt)))
2739
+ .returning({ id: joinRequests.id })
2740
+ .then((rows) => rows[0] ?? null);
2741
+ if (!consumed)
2742
+ throw conflict("Claim secret already used");
2743
+ const created = await agents.createApiKey(joinRequest.createdAgentId, "initial-join-key");
2744
+ await logActivity(db, {
2745
+ squadId: joinRequest.squadId,
2746
+ actorType: "system",
2747
+ actorId: "join-claim",
2748
+ action: "agent_api_key.claimed",
2749
+ entityType: "agent_api_key",
2750
+ entityId: created.id,
2751
+ details: {
2752
+ agentId: joinRequest.createdAgentId,
2753
+ joinRequestId: requestId
2754
+ }
2755
+ });
2756
+ res.status(201).json({
2757
+ keyId: created.id,
2758
+ token: created.token,
2759
+ agentId: joinRequest.createdAgentId,
2760
+ createdAt: created.createdAt
2761
+ });
2762
+ });
2763
+ router.get("/squads/:squadId/members", async (req, res) => {
2764
+ const squadId = req.params.squadId;
2765
+ await assertSquadPermission(req, squadId, "users:manage_permissions");
2766
+ const [members, currentAccess] = await Promise.all([
2767
+ loadSquadMemberRecords(db, squadId),
2768
+ loadSquadAccessSummary(req, access, squadId),
2769
+ ]);
2770
+ res.json({
2771
+ members: await addSquadMemberRemovalAccess(req, db, access, squadId, members),
2772
+ access: currentAccess,
2773
+ });
2774
+ });
2775
+ router.get("/squads/:squadId/user-directory", async (req, res) => {
2776
+ const squadId = req.params.squadId;
2777
+ assertSquadAccess(req, squadId);
2778
+ const users = await loadSquadUserDirectory(db, squadId);
2779
+ res.json({ users });
2780
+ });
2781
+ router.patch("/squads/:squadId/members/:memberId", validate(updateSquadMemberSchema), async (req, res) => {
2782
+ const squadId = req.params.squadId;
2783
+ const memberId = req.params.memberId;
2784
+ await assertSquadPermission(req, squadId, "users:manage_permissions");
2785
+ const memberToUpdate = await access.getMemberById(squadId, memberId);
2786
+ if (!memberToUpdate)
2787
+ throw notFound("Member not found");
2788
+ await assertCanManageSquadMember(req, access, squadId, memberToUpdate);
2789
+ const updated = await db.transaction(async (tx) => {
2790
+ await tx.execute(sql `
2791
+ select ${squadMemberships.id}
2792
+ from ${squadMemberships}
2793
+ where ${squadMemberships.squadId} = ${squadId}
2794
+ and ${squadMemberships.principalType} = 'user'
2795
+ and ${squadMemberships.status} = 'active'
2796
+ and ${squadMemberships.membershipRole} = 'owner'
2797
+ for update
2798
+ `);
2799
+ const existing = await tx
2800
+ .select()
2801
+ .from(squadMemberships)
2802
+ .where(and(eq(squadMemberships.squadId, squadId), eq(squadMemberships.id, memberId)))
2803
+ .then((rows) => rows[0] ?? null);
2804
+ if (!existing)
2805
+ return null;
2806
+ const nextMembershipRole = req.body.membershipRole !== undefined
2807
+ ? req.body.membershipRole
2808
+ : existing.membershipRole;
2809
+ const nextStatus = req.body.status ?? existing.status;
2810
+ if (existing.principalType === "user" &&
2811
+ existing.status === "active" &&
2812
+ existing.membershipRole === "owner" &&
2813
+ (nextStatus !== "active" || nextMembershipRole !== "owner")) {
2814
+ const activeOwnerCount = await tx
2815
+ .select({ id: squadMemberships.id })
2816
+ .from(squadMemberships)
2817
+ .where(and(eq(squadMemberships.squadId, squadId), eq(squadMemberships.principalType, "user"), eq(squadMemberships.status, "active"), eq(squadMemberships.membershipRole, "owner")))
2818
+ .then((rows) => rows.length);
2819
+ if (activeOwnerCount <= 1) {
2820
+ throw conflict("Cannot remove the last active owner");
2821
+ }
2822
+ }
2823
+ return tx
2824
+ .update(squadMemberships)
2825
+ .set({
2826
+ membershipRole: nextMembershipRole,
2827
+ status: nextStatus,
2828
+ updatedAt: new Date(),
2829
+ })
2830
+ .where(eq(squadMemberships.id, existing.id))
2831
+ .returning()
2832
+ .then((rows) => rows[0] ?? existing);
2833
+ });
2834
+ if (!updated)
2835
+ throw notFound("Member not found");
2836
+ await logActivity(db, {
2837
+ squadId,
2838
+ actorType: "user",
2839
+ actorId: req.actor.userId ?? "operator",
2840
+ action: "squad_member.updated",
2841
+ entityType: "squad_membership",
2842
+ entityId: memberId,
2843
+ details: {
2844
+ membershipRole: updated.membershipRole,
2845
+ status: updated.status,
2846
+ },
2847
+ });
2848
+ const member = (await loadSquadMemberRecords(db, squadId)).find((entry) => entry.id === memberId);
2849
+ if (!member)
2850
+ throw notFound("Member not found");
2851
+ res.json(member);
2852
+ });
2853
+ router.patch("/squads/:squadId/members/:memberId/role-and-grants", validate(updateSquadMemberWithPermissionsSchema), async (req, res) => {
2854
+ const squadId = req.params.squadId;
2855
+ const memberId = req.params.memberId;
2856
+ await assertSquadPermission(req, squadId, "users:manage_permissions");
2857
+ const memberToUpdate = await access.getMemberById(squadId, memberId);
2858
+ if (!memberToUpdate)
2859
+ throw notFound("Member not found");
2860
+ await assertCanManageSquadMember(req, access, squadId, memberToUpdate);
2861
+ const updated = await db.transaction(async (tx) => {
2862
+ await tx.execute(sql `
2863
+ select ${squadMemberships.id}
2864
+ from ${squadMemberships}
2865
+ where ${squadMemberships.squadId} = ${squadId}
2866
+ and ${squadMemberships.principalType} = 'user'
2867
+ and ${squadMemberships.status} = 'active'
2868
+ and ${squadMemberships.membershipRole} = 'owner'
2869
+ for update
2870
+ `);
2871
+ const existing = await tx
2872
+ .select()
2873
+ .from(squadMemberships)
2874
+ .where(and(eq(squadMemberships.squadId, squadId), eq(squadMemberships.id, memberId)))
2875
+ .then((rows) => rows[0] ?? null);
2876
+ if (!existing)
2877
+ return null;
2878
+ const nextMembershipRole = req.body.membershipRole !== undefined
2879
+ ? req.body.membershipRole
2880
+ : existing.membershipRole;
2881
+ const nextStatus = req.body.status ?? existing.status;
2882
+ if (existing.principalType === "user" &&
2883
+ existing.status === "active" &&
2884
+ existing.membershipRole === "owner" &&
2885
+ (nextStatus !== "active" || nextMembershipRole !== "owner")) {
2886
+ const activeOwnerCount = await tx
2887
+ .select({ id: squadMemberships.id })
2888
+ .from(squadMemberships)
2889
+ .where(and(eq(squadMemberships.squadId, squadId), eq(squadMemberships.principalType, "user"), eq(squadMemberships.status, "active"), eq(squadMemberships.membershipRole, "owner")))
2890
+ .then((rows) => rows.length);
2891
+ if (activeOwnerCount <= 1) {
2892
+ throw conflict("Cannot remove the last active owner");
2893
+ }
2894
+ }
2895
+ const now = new Date();
2896
+ const updatedMember = await tx
2897
+ .update(squadMemberships)
2898
+ .set({
2899
+ membershipRole: nextMembershipRole,
2900
+ status: nextStatus,
2901
+ updatedAt: now,
2902
+ })
2903
+ .where(eq(squadMemberships.id, existing.id))
2904
+ .returning()
2905
+ .then((rows) => rows[0] ?? existing);
2906
+ await tx
2907
+ .delete(principalPermissionGrants)
2908
+ .where(and(eq(principalPermissionGrants.squadId, squadId), eq(principalPermissionGrants.principalType, existing.principalType), eq(principalPermissionGrants.principalId, existing.principalId)));
2909
+ const grants = (req.body.grants ?? []);
2910
+ if (grants.length > 0) {
2911
+ await tx.insert(principalPermissionGrants).values(grants.map((grant) => ({
2912
+ squadId,
2913
+ principalType: existing.principalType,
2914
+ principalId: existing.principalId,
2915
+ permissionKey: grant.permissionKey,
2916
+ scope: grant.scope ?? null,
2917
+ grantedByUserId: req.actor.userId ?? null,
2918
+ createdAt: now,
2919
+ updatedAt: now,
2920
+ })));
2921
+ }
2922
+ return updatedMember;
2923
+ });
2924
+ if (!updated)
2925
+ throw notFound("Member not found");
2926
+ await logActivity(db, {
2927
+ squadId,
2928
+ actorType: "user",
2929
+ actorId: req.actor.userId ?? "operator",
2930
+ action: "squad_member.access_updated",
2931
+ entityType: "squad_membership",
2932
+ entityId: memberId,
2933
+ details: {
2934
+ membershipRole: updated.membershipRole,
2935
+ status: updated.status,
2936
+ grantCount: req.body.grants?.length ?? 0,
2937
+ },
2938
+ });
2939
+ const member = (await loadSquadMemberRecords(db, squadId)).find((entry) => entry.id === memberId);
2940
+ if (!member)
2941
+ throw notFound("Member not found");
2942
+ res.json(member);
2943
+ });
2944
+ router.post("/squads/:squadId/members/:memberId/archive", validate(archiveSquadMemberSchema), async (req, res) => {
2945
+ const squadId = req.params.squadId;
2946
+ const memberId = req.params.memberId;
2947
+ await assertSquadPermission(req, squadId, "users:manage_permissions");
2948
+ const memberToArchive = await access.getMemberById(squadId, memberId);
2949
+ if (!memberToArchive)
2950
+ throw notFound("Member not found");
2951
+ await assertCanManageSquadMember(req, access, squadId, memberToArchive, "archive");
2952
+ const result = await access.archiveMember(squadId, memberId, {
2953
+ reassignment: req.body.reassignment ?? null,
2954
+ });
2955
+ if (!result)
2956
+ throw notFound("Member not found");
2957
+ await logActivity(db, {
2958
+ squadId,
2959
+ actorType: "user",
2960
+ actorId: req.actor.userId ?? "operator",
2961
+ action: "squad_member.archived",
2962
+ entityType: "squad_membership",
2963
+ entityId: memberId,
2964
+ details: {
2965
+ principalId: result.member.principalId,
2966
+ reassignedIssueCount: result.reassignedIssueCount,
2967
+ reassignment: req.body.reassignment ?? null,
2968
+ },
2969
+ });
2970
+ const member = (await loadSquadMemberRecords(db, squadId, { includeArchived: true })).find((entry) => entry.id === memberId);
2971
+ if (!member)
2972
+ throw notFound("Member not found");
2973
+ res.json({
2974
+ member,
2975
+ reassignedIssueCount: result.reassignedIssueCount,
2976
+ });
2977
+ });
2978
+ router.patch("/squads/:squadId/members/:memberId/permissions", validate(updateMemberPermissionsSchema), async (req, res) => {
2979
+ const squadId = req.params.squadId;
2980
+ const memberId = req.params.memberId;
2981
+ await assertSquadPermission(req, squadId, "users:manage_permissions");
2982
+ const memberToUpdate = await access.getMemberById(squadId, memberId);
2983
+ if (!memberToUpdate)
2984
+ throw notFound("Member not found");
2985
+ await assertCanManageSquadMember(req, access, squadId, memberToUpdate);
2986
+ const updated = await access.setMemberPermissions(squadId, memberId, req.body.grants ?? [], req.actor.userId ?? null);
2987
+ if (!updated)
2988
+ throw notFound("Member not found");
2989
+ await logActivity(db, {
2990
+ squadId,
2991
+ actorType: "user",
2992
+ actorId: req.actor.userId ?? "operator",
2993
+ action: "squad_member.permissions_updated",
2994
+ entityType: "squad_membership",
2995
+ entityId: memberId,
2996
+ details: {
2997
+ grantCount: req.body.grants?.length ?? 0,
2998
+ },
2999
+ });
3000
+ const member = (await loadSquadMemberRecords(db, squadId)).find((entry) => entry.id === memberId);
3001
+ if (!member)
3002
+ throw notFound("Member not found");
3003
+ res.json(member);
3004
+ });
3005
+ router.post("/admin/users/:userId/promote-instance-admin", async (req, res) => {
3006
+ await assertInstanceAdmin(req);
3007
+ const userId = req.params.userId;
3008
+ const result = await access.promoteInstanceAdmin(userId);
3009
+ res.status(201).json(result);
3010
+ });
3011
+ router.get("/admin/users", async (req, res) => {
3012
+ await assertInstanceAdmin(req);
3013
+ const query = searchAdminUsersQuerySchema.parse(req.query);
3014
+ const needle = query.query.trim().toLowerCase();
3015
+ const users = await db
3016
+ .select({
3017
+ id: authUsers.id,
3018
+ email: authUsers.email,
3019
+ name: authUsers.name,
3020
+ image: authUsers.image,
3021
+ })
3022
+ .from(authUsers)
3023
+ .orderBy(desc(authUsers.updatedAt));
3024
+ const filteredUsers = needle
3025
+ ? users.filter((user) => [user.name, user.email]
3026
+ .filter((value) => Boolean(value))
3027
+ .some((value) => value.toLowerCase().includes(needle)))
3028
+ : users;
3029
+ const userIds = filteredUsers.slice(0, 50).map((user) => user.id);
3030
+ const memberships = userIds.length
3031
+ ? await db
3032
+ .select({
3033
+ principalId: squadMemberships.principalId,
3034
+ })
3035
+ .from(squadMemberships)
3036
+ .where(and(eq(squadMemberships.principalType, "user"), eq(squadMemberships.status, "active"), inArray(squadMemberships.principalId, userIds)))
3037
+ : [];
3038
+ const membershipCountByUserId = new Map();
3039
+ for (const membership of memberships) {
3040
+ membershipCountByUserId.set(membership.principalId, (membershipCountByUserId.get(membership.principalId) ?? 0) + 1);
3041
+ }
3042
+ const adminIds = new Set(await Promise.all(userIds.map(async (userId) => (await access.isInstanceAdmin(userId)) ? userId : null)).then((values) => values.filter((value) => Boolean(value))));
3043
+ res.json(filteredUsers.slice(0, 50).map((user) => ({
3044
+ ...toUserProfile(user),
3045
+ isInstanceAdmin: adminIds.has(user.id),
3046
+ activeSquadMembershipCount: membershipCountByUserId.get(user.id) ?? 0,
3047
+ })));
3048
+ });
3049
+ router.post("/admin/users/:userId/demote-instance-admin", async (req, res) => {
3050
+ await assertInstanceAdmin(req);
3051
+ const userId = req.params.userId;
3052
+ const removed = await access.demoteInstanceAdmin(userId);
3053
+ if (!removed)
3054
+ throw notFound("Instance admin role not found");
3055
+ res.json(removed);
3056
+ });
3057
+ router.get("/admin/users/:userId/squad-access", async (req, res) => {
3058
+ await assertInstanceAdmin(req);
3059
+ const userId = req.params.userId;
3060
+ res.json(await loadUserSquadAccessResponse(db, access, userId));
3061
+ });
3062
+ router.put("/admin/users/:userId/squad-access", validate(updateUserSquadAccessSchema), async (req, res) => {
3063
+ await assertInstanceAdmin(req);
3064
+ const userId = req.params.userId;
3065
+ await access.setUserSquadAccess(userId, req.body.squadIds ?? [], { actorUserId: req.actor.userId ?? null });
3066
+ res.json(await loadUserSquadAccessResponse(db, access, userId));
3067
+ });
3068
+ return router;
3069
+ }
3070
+ //# sourceMappingURL=access.js.map