@desplega.ai/agent-swarm 1.161.0 → 1.162.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 (224) hide show
  1. package/README.md +3 -2
  2. package/dist/{acp-adapter-xx489vvb.js → acp-adapter-74m73ae9.js} +1 -1
  3. package/dist/{actions-ykjkn0m9.js → actions-4sr14gnt.js} +34 -31
  4. package/dist/{anthropic-messages-5qx80j9k.js → anthropic-messages-8xxrzky9.js} +22 -26
  5. package/dist/{app-0hdqa4k1.js → app-pt68fbwp.js} +13 -11
  6. package/dist/{artifact-3v5t9jtg.js → artifact-fey7dvj9.js} +3 -2
  7. package/dist/{assistant-am2g5g8a.js → assistant-cv3jytks.js} +37 -34
  8. package/dist/{azure-openai-responses-fszbkp0e.js → azure-openai-responses-f5cqsyj8.js} +15 -10
  9. package/dist/{boot-reembed-xy9t7avg.js → boot-reembed-4ht4gqth.js} +13 -11
  10. package/dist/{boot-reembed-gt5gjkt4.js → boot-reembed-nxpcvpj9.js} +12 -10
  11. package/dist/{boot-scrub-logs-9x0985y5.js → boot-scrub-logs-k93kyw8k.js} +9 -7
  12. package/dist/{boot-seeds-trv0d4vg.js → boot-seeds-1skcrs0e.js} +13 -10
  13. package/dist/{claude-adapter-jb6e1e54.js → claude-adapter-r5g0tzge.js} +23 -20
  14. package/dist/{claude-managed-setup-d1q9hv2b.js → claude-managed-setup-pkyrgshb.js} +3 -2
  15. package/dist/{claude-sdk-session-zga700s2.js → claude-sdk-session-jxyxnrwd.js} +5565 -5512
  16. package/dist/{cli-we0q7t0e.js → cli-2zy95vhm.js} +137 -64
  17. package/dist/{cli-35pj2yva.js → cli-4cwxn7gp.js} +38 -11
  18. package/dist/{cli-ad4qxm6j.js → cli-4dcpaefp.js} +2 -2
  19. package/dist/{cli-82c1pacn.js → cli-4x0csdjq.js} +71 -37
  20. package/dist/{cli-sqm3r5ps.js → cli-5bcd7sfy.js} +14 -18
  21. package/dist/{cli-d56tgd45.js → cli-5k6k8ta3.js} +2 -2
  22. package/dist/{cli-1er0rhz8.js → cli-5xhpb3kv.js} +15 -15
  23. package/dist/{cli-yrg7c73k.js → cli-7af8yhav.js} +10 -183
  24. package/dist/{cli-r6fsxc76.js → cli-7tre70zp.js} +513 -961
  25. package/dist/{cli-bh7jed3k.js → cli-8ev9xqnx.js} +34 -18
  26. package/dist/cli-bsky8g7w.js +449 -0
  27. package/dist/{cli-hn04t9db.js → cli-cebmgwsc.js} +14 -10
  28. package/dist/{cli-cfkrztza.js → cli-cvatsam7.js} +2 -2
  29. package/dist/{cli-2658f6at.js → cli-cvk6yzw5.js} +6 -8
  30. package/dist/{cli-ptr8h7kp.js → cli-eg0b3p0k.js} +5 -218
  31. package/dist/{cli-zb6xbk61.js → cli-g739zqzt.js} +4 -4
  32. package/dist/{cli-dv6cdjs2.js → cli-gd4favx9.js} +28 -7
  33. package/dist/{cli-5p3x4zna.js → cli-gpqpmns7.js} +2 -2
  34. package/dist/{cli-5nbmpy8w.js → cli-hb5c2x5y.js} +1 -1
  35. package/dist/{cli-jpf7e1zq.js → cli-hmsw1gpy.js} +3 -3
  36. package/dist/{cli-1cz0c18q.js → cli-jf7wmv1r.js} +2521 -733
  37. package/dist/{cli-qwsnmjdd.js → cli-jxs2ygaw.js} +168 -61
  38. package/dist/cli-kdkwd3yr.js +615 -0
  39. package/dist/{cli-hgmnf38m.js → cli-kh04eqwe.js} +1 -1
  40. package/dist/{cli-0383bp7f.js → cli-kp42yek0.js} +425 -90
  41. package/dist/{cli-ybgv4kw2.js → cli-mbssg1xv.js} +9 -2
  42. package/dist/{cli-jsp14b6c.js → cli-mzfth0bp.js} +38 -1
  43. package/dist/{cli-8g6mc14k.js → cli-nq7yqw7b.js} +16 -5
  44. package/dist/{cli-ee8bc46j.js → cli-nt8hecg9.js} +33 -32
  45. package/dist/{cli-9s68w752.js → cli-p0r8sxj5.js} +1 -1
  46. package/dist/{cli-58e8yr0d.js → cli-pkd6s2wy.js} +4 -2
  47. package/dist/{cli-17xpf7cm.js → cli-q24re23n.js} +6 -4
  48. package/dist/cli-qjjvzwzd.js +222 -0
  49. package/dist/cli-qrgt5bqf.js +189 -0
  50. package/dist/{cli-et8m9xsv.js → cli-rfjw9jw2.js} +22 -10
  51. package/dist/{cli-798vkbks.js → cli-rj0gqwwp.js} +2 -2
  52. package/dist/{cli-121ph74b.js → cli-rnyzn44p.js} +1 -1
  53. package/dist/{cli-vnnz5ncg.js → cli-s3hwpt9v.js} +95 -16
  54. package/dist/{cli-7119ja3w.js → cli-sptrj5f5.js} +2 -2
  55. package/dist/{cli-a8c82p2t.js → cli-sqvfa090.js} +13 -6
  56. package/dist/{cli-phfg8vy8.js → cli-swfmp1fx.js} +26 -26
  57. package/dist/{cli-nsdxtcbg.js → cli-tcen9p5w.js} +8 -5
  58. package/dist/{cli-9m03pzga.js → cli-vc9bp061.js} +5 -5
  59. package/dist/{cli-rtx9dygp.js → cli-vxe6qa4h.js} +1 -1
  60. package/dist/{cli-ecfxwnyb.js → cli-vy3ed88x.js} +1 -1
  61. package/dist/{cli-0c7ata9h.js → cli-vyqs9ya8.js} +6 -6
  62. package/dist/cli-wbvnn6tk.js +861 -0
  63. package/dist/{cli-kgd3p0bm.js → cli-wjgs5wxe.js} +6 -6
  64. package/dist/cli-wxkkd0e0.js +33 -0
  65. package/dist/{cli-akfj32vj.js → cli-xqpk3rp7.js} +5 -4
  66. package/dist/cli-zp38xvtm.js +9487 -0
  67. package/dist/{cli-3zc55ctx.js → cli-zvhd3f64.js} +2 -2
  68. package/dist/{cli-ze1d3wzv.js → cli-zxqx25nz.js} +5 -5
  69. package/dist/cli.js +32 -25
  70. package/dist/{cloudflare-workers-ai-system-one-k6fbxw9d.js → cloudflare-workers-ai-system-one-k8ddqjkq.js} +8 -6
  71. package/dist/{codex-adapter-04v97vep.js → codex-adapter-esvmnhn7.js} +13 -13
  72. package/dist/{codex-hook-20me6byw.js → codex-hook-m73jmqgx.js} +19 -3
  73. package/dist/{codex-session-runner-ax69mqq3.js → codex-session-runner-ban9excr.js} +13 -13
  74. package/dist/{commands-18gy08n1.js → commands-75413p2n.js} +9 -7
  75. package/dist/{db-d2pvjpyt.js → db-7f5pecwg.js} +11 -7
  76. package/dist/{dispatcher-8wbwyzjg.js → dispatcher-fh9at8nz.js} +33 -30
  77. package/dist/{dsh-adapter-ysqk9snv.js → dsh-adapter-wpze65d4.js} +3 -3
  78. package/dist/{e2b-esmt1r16.js → e2b-gjgdjp91.js} +10 -9
  79. package/dist/{pi-logo-animation-e9a0t68z.js → easter-egg-3d-3w5c2n4c.js} +122 -68
  80. package/dist/{execute-cmagrbrz.js → execute-f50wg9nh.js} +29 -6
  81. package/dist/{google-generative-ai-k6n5nbpp.js → google-generative-ai-jxxpnegy.js} +2 -2
  82. package/dist/{google-vertex-44xxtcvp.js → google-vertex-x7q1angm.js} +5 -5
  83. package/dist/{handlers-6b69m5kp.js → handlers-w13787bb.js} +36 -33
  84. package/dist/{hook-srnkt0hb.js → hook-46g1jrvx.js} +28 -15
  85. package/dist/{hook-k3mydf2n.js → hook-4tpts2ry.js} +20 -17
  86. package/dist/{http-hrf80ca1.js → http-19pzbfrt.js} +964 -176
  87. package/dist/{index-9rxdvj6w.js → index-8fd4dm4h.js} +34 -31
  88. package/dist/{index-1asbaj62.js → index-dqmp15bq.js} +35 -32
  89. package/dist/{index-45evq4ee.js → index-qb4q1y3e.js} +33 -30
  90. package/dist/{index-k4nj18qs.js → index-s75wc4wd.js} +12 -10
  91. package/dist/{index-xmevk2b9.js → index-scptvdxg.js} +27 -25
  92. package/dist/{keepalive-sfdz6ksp.js → keepalive-xa5ts9cd.js} +11 -9
  93. package/dist/{lead-ej91e9hw.js → lead-qp4xkvjq.js} +45 -41
  94. package/dist/{maintenance-80dnd7ke.js → maintenance-rm5885hm.js} +13 -11
  95. package/dist/{mistral-conversations-pc3ntnxt.js → mistral-conversations-rw7fbt22.js} +4 -4
  96. package/dist/{node-a5e00qhp.js → node-8zkdq42h.js} +834 -833
  97. package/dist/{oauth-refresh-sweep-m1etk2mx.js → oauth-refresh-sweep-1csdfebf.js} +10 -8
  98. package/dist/{onboard-0q8sdfqc.js → onboard-r6b78cs4.js} +10 -10
  99. package/dist/{openai-codex-responses-n7n683p2.js → openai-codex-responses-efvrgrq3.js} +4 -4
  100. package/dist/{openai-completions-jpaj5g2b.js → openai-completions-bbbrrz4z.js} +12 -8
  101. package/dist/{openai-responses-9a3nme9j.js → openai-responses-9jjyrcm1.js} +15 -10
  102. package/dist/{opencode-adapter-4wcgmvgf.js → opencode-adapter-kb3g6pkf.js} +2 -2
  103. package/dist/{otel-impl-hnqm9mxq.js → otel-impl-0y9knpky.js} +5 -5
  104. package/dist/{pi-mono-adapter-d6sqm9wr.js → pi-mono-adapter-z6s1bme2.js} +100 -19
  105. package/dist/{pricing-refresh-y60z64pm.js → pricing-refresh-mth26tgp.js} +11 -9
  106. package/dist/{rbac-roles-8pa0754b.js → rbac-roles-kwntb23q.js} +13 -9
  107. package/dist/rbac-roles-vpk4ebm8.js +45 -0
  108. package/dist/{registry-v5wytg0s.js → registry-km6t1jd2.js} +1 -1
  109. package/dist/{render-v2-418s1vj7.js → render-v2-hcm7h00y.js} +13 -11
  110. package/dist/{runtime-3pknc168.js → runtime-vfjsfsc5.js} +4 -4
  111. package/dist/{setup-yghdkj4x.js → setup-hbyzd65h.js} +2 -2
  112. package/dist/{virtual-modules-jcceevt5.js → virtual-modules-9bv7ry91.js} +9 -8
  113. package/dist/{work-objects-j1sz20v9.js → work-objects-m09sxnhe.js} +36 -33
  114. package/dist/{worker-d4gcvkjt.js → worker-0h35rvk4.js} +45 -41
  115. package/openapi.json +491 -12
  116. package/package.json +13 -6
  117. package/src/be/api-drain.ts +184 -0
  118. package/src/be/db/tasks/read.ts +31 -6
  119. package/src/be/db/tasks/write.ts +5 -4
  120. package/src/be/db.ts +42 -18
  121. package/src/be/memory/link-resolver.ts +29 -3
  122. package/src/be/migrations/192_favorites_agent_fs_path.sql +36 -0
  123. package/src/be/onboarding.ts +7 -1
  124. package/src/be/seed-prompt-templates.ts +1 -0
  125. package/src/be/swarm-config-guard.ts +35 -16
  126. package/src/be/task-citations.ts +11 -5
  127. package/src/be/users.ts +3 -0
  128. package/src/cli.tsx +3 -2
  129. package/src/comb/agent-fs.ts +39 -0
  130. package/src/comb/config.ts +37 -0
  131. package/src/comb/markers.ts +37 -0
  132. package/src/comb/review-batch.ts +486 -0
  133. package/src/comb/templates.ts +88 -0
  134. package/src/commands/context-preamble.ts +4 -1
  135. package/src/commands/hook.ts +2 -1
  136. package/src/commands/runner.ts +262 -68
  137. package/src/commands/templates.ts +10 -0
  138. package/src/extensions/contract-types.generated.ts +1 -1
  139. package/src/fs/agent-fs-provider.ts +106 -2
  140. package/src/heartbeat/heartbeat.ts +4 -0
  141. package/src/hooks/codex-hook.ts +25 -1
  142. package/src/hooks/hook.ts +13 -0
  143. package/src/hooks/pr-body-guard.ts +983 -0
  144. package/src/http/all-routes.ts +1 -0
  145. package/src/http/assets.ts +5 -17
  146. package/src/http/comb.ts +131 -0
  147. package/src/http/core.ts +2 -1
  148. package/src/http/fs.ts +9 -17
  149. package/src/http/index.ts +23 -9
  150. package/src/http/onboarding.ts +21 -2
  151. package/src/http/poll.ts +30 -3
  152. package/src/http/realtime.ts +62 -0
  153. package/src/http/request-principal.ts +22 -0
  154. package/src/http/status.ts +34 -2
  155. package/src/kv-reserved-namespaces.ts +10 -5
  156. package/src/providers/dsh-adapter.ts +1 -1
  157. package/src/providers/pi-mono-adapter.ts +123 -0
  158. package/src/rbac/legacy-policy.ts +1 -0
  159. package/src/rbac/permissions.ts +4 -0
  160. package/src/realtime/auth.ts +63 -1
  161. package/src/realtime/tickets.ts +52 -0
  162. package/src/realtime/transport.ts +51 -22
  163. package/src/scripts-runtime/types/swarm-extension.d.ts +3 -1
  164. package/src/telemetry-context.ts +175 -0
  165. package/src/telemetry-contract/EVENTS.md +81 -0
  166. package/src/telemetry-contract/SOURCE.json +9 -0
  167. package/src/telemetry-contract/catalog.json +1335 -0
  168. package/src/telemetry-contract/index.ts +60 -0
  169. package/src/telemetry-contract/types.gen.ts +1971 -0
  170. package/src/telemetry-identity.ts +129 -0
  171. package/src/telemetry-snapshot.ts +211 -0
  172. package/src/telemetry-trigger.ts +125 -0
  173. package/src/telemetry-types.typetest.ts +40 -0
  174. package/src/telemetry.ts +271 -67
  175. package/src/tests/api-drain-e2e.test.ts +249 -0
  176. package/src/tests/api-drain-worker-e2e.test.ts +216 -0
  177. package/src/tests/api-drain.test.ts +351 -0
  178. package/src/tests/comb-config-guard.test.ts +38 -0
  179. package/src/tests/comb-links.test.ts +162 -0
  180. package/src/tests/comb-markers.test.ts +68 -0
  181. package/src/tests/comb-review-batch.test.ts +770 -0
  182. package/src/tests/comb-status.test.ts +152 -0
  183. package/src/tests/db-task-read-contract.test.ts +2 -0
  184. package/src/tests/db-task-write-contract.test.ts +1 -0
  185. package/src/tests/docs-index-footprint.test.js +3 -3
  186. package/src/tests/dsh-adapter.test.ts +1 -1
  187. package/src/tests/favorites-agent-fs-path.test.ts +226 -0
  188. package/src/tests/memory-link-resolver.test.ts +34 -0
  189. package/src/tests/onboarding.test.ts +108 -0
  190. package/src/tests/pi-mono-adapter.test.ts +265 -2
  191. package/src/tests/pr-body-leaks.test.ts +789 -0
  192. package/src/tests/preload.ts +8 -0
  193. package/src/tests/rbac-engine.test.ts +19 -0
  194. package/src/tests/realtime-room-auth.test.ts +104 -1
  195. package/src/tests/realtime-tickets.test.ts +74 -0
  196. package/src/tests/realtime-transport.test.ts +131 -4
  197. package/src/tests/runner-api-drain.test.ts +301 -0
  198. package/src/tests/runner-fallback-output.test.ts +61 -0
  199. package/src/tests/runner-fenced-json-output.test.ts +178 -0
  200. package/src/tests/script-connections.test.ts +2 -0
  201. package/src/tests/send-task-requested-by.test.ts +133 -15
  202. package/src/tests/task-lifecycle-telemetry.test.ts +8 -3
  203. package/src/tests/telemetry-context.test.ts +314 -0
  204. package/src/tests/telemetry-init.test.ts +51 -19
  205. package/src/tests/telemetry-snapshot.test.ts +229 -0
  206. package/src/tests/telemetry-trigger.test.ts +200 -0
  207. package/src/tests/template-registry-helpers.ts +1 -0
  208. package/src/tests/workflow-agent-task-retry.test.ts +363 -0
  209. package/src/tests/workflow-definition-telemetry.test.ts +4 -4
  210. package/src/tools/poll-task.ts +20 -1
  211. package/src/tools/send-task.ts +9 -1
  212. package/src/tools/task-action.ts +11 -0
  213. package/src/types.ts +2 -1
  214. package/src/utils/api-drain.ts +24 -0
  215. package/src/utils/constants.ts +57 -16
  216. package/src/utils/pr-body-leaks.ts +172 -0
  217. package/src/utils/task-attachment-links.ts +14 -8
  218. package/src/workflows/recovery.ts +26 -1
  219. package/src/workflows/resume.ts +31 -4
  220. package/src/workflows/task-step-routing.ts +75 -2
  221. package/tsconfig.json +3 -0
  222. package/dist/cli-tjf4cn8z.js +0 -9119
  223. package/dist/cli-tsk3nz8a.js +0 -582
  224. package/dist/{cli-t6v2ah3r.js → cli-twrh04ap.js} +3 -3
@@ -1,59 +1,38 @@
1
1
  import {
2
- PermissionVerbSchema
3
- } from "./cli-tsk3nz8a.js";
4
- import {
5
- seedPricingFromModelsDev
6
- } from "./cli-ecfxwnyb.js";
2
+ PermissionVerbSchema,
3
+ init_rbac
4
+ } from "./cli-kdkwd3yr.js";
7
5
  import {
8
6
  getDb,
9
7
  getDbClient,
10
8
  init_db
11
- } from "./cli-1cz0c18q.js";
9
+ } from "./cli-jf7wmv1r.js";
12
10
  import {
13
- init_env_flag
11
+ init_env_flag,
12
+ isEnvFlagEnabled
14
13
  } from "./cli-ej6gbxfb.js";
15
-
16
- // src/be/boot-seeds.ts
17
- init_db();
14
+ import {
15
+ __esm
16
+ } from "./cli-bc05qbss.js";
18
17
 
19
18
  // src/be/rbac-roles.ts
20
- init_env_flag();
21
- init_db();
22
- var DEFAULT_ROLE_ID = "rbac-role-admin";
23
- var BUILTIN_ROLES = [
24
- {
25
- id: DEFAULT_ROLE_ID,
26
- name: "admin",
27
- description: "Full access including verb-less routes (legacy-equivalent default).",
28
- isBuiltin: true,
29
- grantsAll: true,
30
- verbs: []
31
- },
32
- {
33
- id: "rbac-role-requester",
34
- name: "requester",
35
- description: "Own-task lifecycle: what legacy policy grants user principals.",
36
- isBuiltin: true,
37
- grantsAll: false,
38
- verbs: [
39
- "task.create.own",
40
- "task.read.own",
41
- "task.cancel.own",
42
- "task.action.own",
43
- "task.fs.mutate",
44
- "favorite.write.own"
45
- ]
19
+ function roleRowToUserRole(row) {
20
+ return {
21
+ id: row.id,
22
+ name: row.name,
23
+ description: row.description,
24
+ isBuiltin: row.isBuiltin === 1,
25
+ grantsAll: row.grantsAll === 1,
26
+ createdAt: row.createdAt
27
+ };
28
+ }
29
+ async function requireRoleId(roleName) {
30
+ const row = await getDbClient().get("SELECT id FROM roles WHERE name = ?", [roleName]);
31
+ if (!row) {
32
+ throw new Error(`Unknown RBAC role: ${roleName}`);
46
33
  }
47
- ];
48
- var CREATE_USER_DEFAULT_ROLE_TRIGGER_SQL = `
49
- CREATE TRIGGER IF NOT EXISTS trg_users_default_role
50
- AFTER INSERT ON users
51
- BEGIN
52
- INSERT OR IGNORE INTO principal_roles (principalType, principalId, roleId)
53
- SELECT 'user', NEW.id, 'rbac-role-admin'
54
- WHERE EXISTS (SELECT 1 FROM roles WHERE id = 'rbac-role-admin');
55
- END;
56
- `;
34
+ return row.id;
35
+ }
57
36
  function validateBuiltinVerb(roleName, verb) {
58
37
  const parsed = PermissionVerbSchema.safeParse(verb);
59
38
  if (!parsed.success) {
@@ -72,7 +51,6 @@ function validatedBuiltinVerbSets() {
72
51
  }
73
52
  return byRoleId;
74
53
  }
75
- var loggedInvalidGrantVerbs = new Set;
76
54
  function parseDatabaseGrantVerb(roleId, verb) {
77
55
  const parsed = PermissionVerbSchema.safeParse(verb);
78
56
  if (parsed.success) {
@@ -109,6 +87,28 @@ async function getUserGrant(userId) {
109
87
  }
110
88
  return { grantsAll: false, verbs };
111
89
  }
90
+ async function attachRole(userId, roleName) {
91
+ await getDbClient().transaction(async (tx) => {
92
+ const roleId = await requireRoleId(roleName);
93
+ await tx.run(`INSERT OR IGNORE INTO principal_roles (principalType, principalId, roleId)
94
+ VALUES ('user', ?, ?)`, [userId, roleId]);
95
+ });
96
+ }
97
+ async function detachRole(userId, roleName) {
98
+ await getDbClient().transaction(async (tx) => {
99
+ const roleId = await requireRoleId(roleName);
100
+ await tx.run(`DELETE FROM principal_roles
101
+ WHERE principalType = 'user' AND principalId = ? AND roleId = ?`, [userId, roleId]);
102
+ });
103
+ }
104
+ async function listUserRoles(userId) {
105
+ const rows = await getDbClient().query(`SELECT r.id, r.name, r.description, r.isBuiltin, r.grantsAll, r.createdAt
106
+ FROM principal_roles pr
107
+ JOIN roles r ON r.id = pr.roleId
108
+ WHERE pr.principalType = 'user' AND pr.principalId = ?
109
+ ORDER BY r.name`, [userId]);
110
+ return rows.map(roleRowToUserRole);
111
+ }
112
112
  function ensureRbacSeedsSynced(opts) {
113
113
  const db = getDb();
114
114
  const desiredVerbsByRoleId = validatedBuiltinVerbSets();
@@ -185,23 +185,96 @@ function ensureRbacSeedsSynced(opts) {
185
185
  }
186
186
  return stats;
187
187
  }
188
-
189
- // src/be/boot-seeds.ts
190
- var pricingSeededDbs = new WeakSet;
191
- var rbacSyncedDbs = new WeakSet;
192
- function ensurePricingSeeded() {
193
- const db = getDb();
194
- if (pricingSeededDbs.has(db))
195
- return;
196
- seedPricingFromModelsDev();
197
- pricingSeededDbs.add(db);
188
+ function describeRbacFlag() {
189
+ const raw = process.env.RBAC_ENABLED;
190
+ if (raw === undefined)
191
+ return "unset (on)";
192
+ return `${raw} (${isEnvFlagEnabled("RBAC_ENABLED", true) ? "on" : "off"})`;
198
193
  }
199
- function ensureRbacSeeded() {
200
- const db = getDb();
201
- if (rbacSyncedDbs.has(db))
202
- return;
203
- ensureRbacSeedsSynced();
204
- rbacSyncedDbs.add(db);
194
+ function printRolesTable(rows) {
195
+ const tableRows = rows.map((row) => [
196
+ row.name,
197
+ row.isBuiltin === 1 ? "yes" : "no",
198
+ row.grantsAll === 1 ? "yes" : "no",
199
+ String(row.verbCount),
200
+ String(row.attachedUserCount)
201
+ ]);
202
+ const headers = ["name", "builtin", "grantsAll", "verbs", "attachedUsers"];
203
+ const widths = headers.map((header, column) => Math.max(header.length, ...tableRows.map((row) => row[column]?.length ?? 0)));
204
+ const format = (row) => row.map((cell, column) => cell.padEnd(widths[column] ?? cell.length)).join(" ");
205
+ console.log("Roles:");
206
+ console.log(format(headers));
207
+ console.log(format(widths.map((width) => "-".repeat(width))));
208
+ for (const row of tableRows) {
209
+ console.log(format(row));
210
+ }
211
+ }
212
+ async function getRbacRoleSummary() {
213
+ return getDbClient().query(`SELECT
214
+ r.name,
215
+ r.isBuiltin,
216
+ r.grantsAll,
217
+ COUNT(DISTINCT rp.verb) AS verbCount,
218
+ COUNT(DISTINCT CASE WHEN pr.principalType = 'user' THEN pr.principalId END)
219
+ AS attachedUserCount
220
+ FROM roles r
221
+ LEFT JOIN role_permissions rp ON rp.roleId = r.id
222
+ LEFT JOIN principal_roles pr ON pr.roleId = r.id
223
+ GROUP BY r.id, r.name, r.isBuiltin, r.grantsAll
224
+ ORDER BY r.isBuiltin DESC, r.name`);
225
+ }
226
+ async function runRbacCliCommand(args) {
227
+ const subcommand = args[0];
228
+ if (args.length !== 1 || subcommand !== "bootstrap") {
229
+ throw new Error(`Unknown RBAC command: ${args.join(" ") || "(none)"}`);
230
+ }
231
+ const stats = ensureRbacSeedsSynced({ quiet: true });
232
+ const roles = await getRbacRoleSummary();
233
+ console.log("RBAC bootstrap complete");
234
+ console.log(`RBAC_ENABLED: ${describeRbacFlag()}`);
235
+ console.log(`Users backfilled this run: ${stats.usersBackfilled}`);
236
+ console.log("");
237
+ printRolesTable(roles);
205
238
  }
239
+ var DEFAULT_ROLE_ID = "rbac-role-admin", BUILTIN_ROLES, CREATE_USER_DEFAULT_ROLE_TRIGGER_SQL = `
240
+ CREATE TRIGGER IF NOT EXISTS trg_users_default_role
241
+ AFTER INSERT ON users
242
+ BEGIN
243
+ INSERT OR IGNORE INTO principal_roles (principalType, principalId, roleId)
244
+ SELECT 'user', NEW.id, 'rbac-role-admin'
245
+ WHERE EXISTS (SELECT 1 FROM roles WHERE id = 'rbac-role-admin');
246
+ END;
247
+ `, loggedInvalidGrantVerbs;
248
+ var init_rbac_roles = __esm(() => {
249
+ init_rbac();
250
+ init_env_flag();
251
+ init_db();
252
+ BUILTIN_ROLES = [
253
+ {
254
+ id: DEFAULT_ROLE_ID,
255
+ name: "admin",
256
+ description: "Full access including verb-less routes (legacy-equivalent default).",
257
+ isBuiltin: true,
258
+ grantsAll: true,
259
+ verbs: []
260
+ },
261
+ {
262
+ id: "rbac-role-requester",
263
+ name: "requester",
264
+ description: "Own-task lifecycle: what legacy policy grants user principals.",
265
+ isBuiltin: true,
266
+ grantsAll: false,
267
+ verbs: [
268
+ "task.create.own",
269
+ "task.read.own",
270
+ "task.cancel.own",
271
+ "task.action.own",
272
+ "task.fs.mutate",
273
+ "favorite.write.own"
274
+ ]
275
+ }
276
+ ];
277
+ loggedInvalidGrantVerbs = new Set;
278
+ });
206
279
 
207
- export { getUserGrant, ensurePricingSeeded, ensureRbacSeeded };
280
+ export { DEFAULT_ROLE_ID, BUILTIN_ROLES, getUserGrant, attachRole, detachRole, listUserRoles, ensureRbacSeedsSynced, runRbacCliCommand, init_rbac_roles };
@@ -2,13 +2,13 @@ import {
2
2
  mcpNamespace,
3
3
  require_proper_lockfile,
4
4
  validateMcpServerConfig
5
- } from "./cli-bh7jed3k.js";
5
+ } from "./cli-8ev9xqnx.js";
6
6
  import {
7
7
  CONFIG_DIR_NAME,
8
8
  canonicalizePath,
9
9
  resolvePath,
10
10
  stripBom
11
- } from "./cli-vnnz5ncg.js";
11
+ } from "./cli-s3hwpt9v.js";
12
12
  import {
13
13
  __toESM
14
14
  } from "./cli-bc05qbss.js";
@@ -212,6 +212,10 @@ function openBrowser(target) {
212
212
  // node_modules/@earendil-works/pi-coding-agent/dist/extensions/mcp/config.js
213
213
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
214
214
  import { dirname as dirname2, join as join2 } from "node:path";
215
+ var OVERRIDE_KEYS = ["enabled", "exposure", "toolExposure"];
216
+ function isOverride(value) {
217
+ return value.command === undefined && value.url === undefined && value.type === undefined;
218
+ }
215
219
  function isRecord(value) {
216
220
  return typeof value === "object" && value !== null && !Array.isArray(value);
217
221
  }
@@ -235,6 +239,22 @@ function readConfigFile(path, scope, state) {
235
239
  else if (parsed.autoEnableCodemode !== undefined)
236
240
  errors.push(`${path}: autoEnableCodemode must be a boolean`);
237
241
  for (const [name, value] of Object.entries(parsed.mcpServers ?? {})) {
242
+ if (scope === "project" && isRecord(value) && isOverride(value)) {
243
+ const base = servers.get(name);
244
+ const extra = Object.keys(value).filter((key) => !OVERRIDE_KEYS.includes(key));
245
+ if (!base) {
246
+ errors.push(`${path}: server "${name}" needs "command" or "url", or a global server to override`);
247
+ } else if (extra.length > 0) {
248
+ errors.push(`${path}: server "${name}": an override can only set ${OVERRIDE_KEYS.join(", ")}`);
249
+ } else {
250
+ const config2 = validateMcpServerConfig(name, { ...base.config, ...value });
251
+ if (typeof config2 === "string")
252
+ errors.push(`${path}: ${config2}`);
253
+ else
254
+ servers.set(name, { ...base, config: config2, override: path });
255
+ }
256
+ continue;
257
+ }
238
258
  const config = validateMcpServerConfig(name, value);
239
259
  if (typeof config === "string") {
240
260
  errors.push(`${path}: ${config}`);
@@ -255,27 +275,34 @@ function readConfigFile(path, scope, state) {
255
275
  function loadMcpConfig(options) {
256
276
  const state = { servers: new Map, errors: [] };
257
277
  readConfigFile(join2(options.agentDir, "mcp.json"), "global", state);
258
- if (options.projectTrusted)
259
- readConfigFile(join2(options.cwd, CONFIG_DIR_NAME, "mcp.json"), "project", state);
278
+ const projectConfig = options.projectTrusted ? join2(options.cwd, CONFIG_DIR_NAME, "mcp.json") : undefined;
279
+ if (projectConfig)
280
+ readConfigFile(projectConfig, "project", state);
260
281
  return {
261
282
  servers: [...state.servers.values()],
262
283
  ...state.autoEnableCodemode === undefined ? {} : { autoEnableCodemode: state.autoEnableCodemode },
263
- errors: state.errors
284
+ errors: state.errors,
285
+ ...projectConfig ? { projectConfig } : {}
264
286
  };
265
287
  }
266
- function updateMcpServerConfig(path, name, patch) {
267
- editMcpServers(path, (servers) => {
268
- const server = servers?.[name];
288
+ function updateMcpServerConfig(path, name, patch, options = {}) {
289
+ editMcpServers(path, (servers, parsed) => {
290
+ let server = servers?.[name];
291
+ if (server === undefined && options.override) {
292
+ server = {};
293
+ parsed.mcpServers = { ...servers, [name]: server };
294
+ }
269
295
  if (!isRecord(server))
270
296
  throw new Error(`${path} does not define MCP server "${name}"`);
297
+ const keepDefaults = isOverride(server);
271
298
  if (patch.enabled !== undefined) {
272
- if (patch.enabled)
299
+ if (patch.enabled && !keepDefaults)
273
300
  delete server.enabled;
274
301
  else
275
- server.enabled = false;
302
+ server.enabled = patch.enabled;
276
303
  }
277
304
  if (patch.exposure !== undefined) {
278
- if (patch.exposure === "codemode")
305
+ if (patch.exposure === "codemode" && !keepDefaults)
279
306
  delete server.exposure;
280
307
  else
281
308
  server.exposure = patch.exposure;
@@ -6,7 +6,7 @@ import {
6
6
  import {
7
7
  parseModelStr,
8
8
  resolveCredential
9
- } from "./cli-sqm3r5ps.js";
9
+ } from "./cli-5bcd7sfy.js";
10
10
  import {
11
11
  DEFAULT_OPENROUTER_BASE_URL,
12
12
  getOpenRouterBaseUrl
@@ -14,7 +14,7 @@ import {
14
14
  import {
15
15
  complete,
16
16
  getBuiltinModel
17
- } from "./cli-ee8bc46j.js";
17
+ } from "./cli-nt8hecg9.js";
18
18
  import {
19
19
  exports_typebox
20
20
  } from "./cli-rb9m6rpe.js";
@@ -15,12 +15,12 @@ import {
15
15
  require_proper_lockfile,
16
16
  resolveConfigValueOrThrow,
17
17
  resolveHeadersOrThrow
18
- } from "./cli-bh7jed3k.js";
18
+ } from "./cli-8ev9xqnx.js";
19
19
  import {
20
20
  APP_NAME,
21
21
  VERSION,
22
22
  getAgentDir
23
- } from "./cli-vnnz5ncg.js";
23
+ } from "./cli-s3hwpt9v.js";
24
24
  import {
25
25
  oauthErrorHtml,
26
26
  oauthSuccessHtml
@@ -47,14 +47,14 @@ ${page.details}` : page.message;
47
47
  class OAuthCallbackServer {
48
48
  redirectUrl;
49
49
  server;
50
- path;
50
+ paths;
51
51
  timeoutMs;
52
52
  renderPage;
53
53
  pending = new Map;
54
- constructor(server, redirectUrl, path, timeoutMs, renderPage) {
54
+ constructor(server, redirectUrl, paths, timeoutMs, renderPage) {
55
55
  this.server = server;
56
56
  this.redirectUrl = redirectUrl;
57
- this.path = path;
57
+ this.paths = paths;
58
58
  this.timeoutMs = timeoutMs;
59
59
  this.renderPage = renderPage;
60
60
  }
@@ -74,10 +74,10 @@ class OAuthCallbackServer {
74
74
  const address = server.address();
75
75
  if (!address || typeof address === "string")
76
76
  throw new Error("OAuth callback server did not bind to TCP");
77
- instance = new OAuthCallbackServer(server, `http://${redirectHost.includes(":") ? `[${redirectHost}]` : redirectHost}:${address.port}${path}`, path, options.timeoutMs ?? 5 * 60000, options.renderPage);
77
+ instance = new OAuthCallbackServer(server, `http://${redirectHost.includes(":") ? `[${redirectHost}]` : redirectHost}:${address.port}${path}`, [path, ...options.extraPaths ?? []], options.timeoutMs ?? 5 * 60000, options.renderPage);
78
78
  return instance;
79
79
  }
80
- waitForCallback(state) {
80
+ waitForCallback(state, path) {
81
81
  if (this.pending.has(state))
82
82
  throw new Error("OAuth state is already pending");
83
83
  return new Promise((resolve, reject) => {
@@ -85,7 +85,7 @@ class OAuthCallbackServer {
85
85
  this.pending.delete(state);
86
86
  reject(new Error("OAuth callback timed out"));
87
87
  }, this.timeoutMs);
88
- this.pending.set(state, { resolve, reject, timer });
88
+ this.pending.set(state, { resolve, reject, timer, path });
89
89
  });
90
90
  }
91
91
  async close() {
@@ -108,7 +108,7 @@ class OAuthCallbackServer {
108
108
  }
109
109
  handle(rawUrl, response) {
110
110
  const url = new URL(rawUrl, this.redirectUrl);
111
- if (url.pathname !== this.path) {
111
+ if (!this.paths.includes(url.pathname)) {
112
112
  this.reply(response, 404, { ok: false, message: "Not found" });
113
113
  return;
114
114
  }
@@ -120,6 +120,11 @@ class OAuthCallbackServer {
120
120
  }
121
121
  clearTimeout(pending.timer);
122
122
  this.pending.delete(state);
123
+ if (pending.path !== undefined && url.pathname !== pending.path) {
124
+ pending.reject(new Error("The authorization response arrived on another redirect URI"));
125
+ this.reply(response, 400, { ok: false, message: "Unexpected redirect URI" });
126
+ return;
127
+ }
123
128
  const error = url.searchParams.get("error");
124
129
  if (error) {
125
130
  const description = url.searchParams.get("error_description") ?? error;
@@ -574,28 +579,28 @@ async function runFlow(provider, options) {
574
579
  const metadata = discovered.authorizationServerMetadata;
575
580
  const resource = selectResource(options.serverUrl, discovered.resourceMetadata);
576
581
  const scope = options.scope || discovered.resourceMetadata?.scopes_supported?.join(" ") || provider.clientMetadata.scope;
577
- let client = await provider.clientInformation();
582
+ const stored = await provider.clientInformation();
583
+ const clientDocument = stored ? undefined : provider.clientMetadataDocument?.(metadata);
584
+ if (clientDocument) {
585
+ const url = new URL(clientDocument.url);
586
+ if (url.protocol !== "https:" || url.pathname === "/")
587
+ throw new Error("Invalid OAuth client metadata URL");
588
+ }
589
+ let client = stored ?? (clientDocument && { client_id: clientDocument.url });
578
590
  if (!client) {
579
591
  if (options.authorizationCode)
580
592
  throw new Error("OAuth client information is missing during code exchange");
581
- if (metadata?.client_id_metadata_document_supported && provider.clientMetadataUrl) {
582
- const url = new URL(provider.clientMetadataUrl);
583
- if (url.protocol !== "https:" || url.pathname === "/")
584
- throw new Error("Invalid OAuth client metadata URL");
585
- client = { client_id: provider.clientMetadataUrl };
586
- await provider.saveClientInformation?.(client);
587
- } else {
588
- if (!provider.saveClientInformation)
589
- throw new Error("OAuth client information cannot be persisted");
590
- client = await registerClient(discovered.authorizationServerUrl, {
591
- metadata,
592
- clientMetadata: provider.clientMetadata,
593
- scope,
594
- fetch: options.fetch
595
- });
596
- await provider.saveClientInformation(client);
597
- }
593
+ if (!provider.saveClientInformation)
594
+ throw new Error("OAuth client information cannot be persisted");
595
+ client = await registerClient(discovered.authorizationServerUrl, {
596
+ metadata,
597
+ clientMetadata: provider.clientMetadata,
598
+ scope,
599
+ fetch: options.fetch
600
+ });
601
+ await provider.saveClientInformation(client);
598
602
  }
603
+ const redirectUrl = clientDocument?.redirectUrl ?? provider.redirectUrl;
599
604
  const tokenOptions = {
600
605
  metadata,
601
606
  clientInformation: client,
@@ -613,7 +618,7 @@ async function runFlow(provider, options) {
613
618
  ...tokenOptions,
614
619
  code: options.authorizationCode,
615
620
  codeVerifier: await provider.codeVerifier(),
616
- redirectUrl: provider.redirectUrl
621
+ redirectUrl
617
622
  });
618
623
  await provider.saveTokens(withScope(tokens, scope));
619
624
  return "AUTHORIZED";
@@ -638,7 +643,7 @@ async function runFlow(provider, options) {
638
643
  const authorization = await startAuthorization(discovered.authorizationServerUrl, {
639
644
  metadata,
640
645
  clientInformation: client,
641
- redirectUrl: provider.redirectUrl,
646
+ redirectUrl,
642
647
  scope,
643
648
  state,
644
649
  resource
@@ -676,6 +681,7 @@ class MemoryOAuthStateStore {
676
681
  class McpOAuthProvider {
677
682
  redirectUrl;
678
683
  clientMetadata;
684
+ clientMetadataDocument;
679
685
  serverUrl;
680
686
  configuredClient;
681
687
  store;
@@ -691,6 +697,7 @@ class McpOAuthProvider {
691
697
  response_types: options.clientMetadata.response_types ?? ["code"],
692
698
  token_endpoint_auth_method: options.clientMetadata.token_endpoint_auth_method ?? (options.clientSecret ? "client_secret_post" : "none")
693
699
  };
700
+ this.clientMetadataDocument = options.clientMetadataDocument;
694
701
  this.configuredClient = options.clientId ? { client_id: options.clientId, ...options.clientSecret ? { client_secret: options.clientSecret } : {} } : undefined;
695
702
  this.store = options.store ?? new MemoryOAuthStateStore;
696
703
  this.onRedirect = options.onRedirect;
@@ -782,6 +789,7 @@ import { join } from "node:path";
782
789
  var import_proper_lockfile = __toESM(require_proper_lockfile(), 1);
783
790
  var CALLBACK_HOST = "127.0.0.1";
784
791
  var CALLBACK_PATH = "/callback";
792
+ var CLIENT_METADATA_BASE_URL = "https://pi.dev/oauth";
785
793
  var FALLBACK_REDIRECT_URL = `http://${CALLBACK_HOST}${CALLBACK_PATH}`;
786
794
  var REFRESH_SKEW_MS = 30000;
787
795
  var REFRESH_REQUEST_TIMEOUT_MS = 15000;
@@ -901,11 +909,29 @@ class McpOAuthCredentialStore {
901
909
  function registeredRedirectUrls(client) {
902
910
  return client && "redirect_uris" in client ? client.redirect_uris : [];
903
911
  }
912
+ function callbackId(serverUrl) {
913
+ const url = new URL(serverUrl);
914
+ url.hash = "";
915
+ return createHash("sha256").update(url.href).digest().subarray(0, 9).toString("base64url");
916
+ }
917
+ function clientMetadataDocument(serverUrl, redirectUrl, metadata) {
918
+ if (!metadata?.client_id_metadata_document_supported || !metadata.token_endpoint_auth_methods_supported?.includes("none")) {
919
+ throw new Error('The authorization server does not support Client ID Metadata Documents for public clients; remove oauth.clientRegistration "cimd"');
920
+ }
921
+ if (metadata.authorization_response_iss_parameter_supported) {
922
+ return { url: `${CLIENT_METADATA_BASE_URL}/client.json`, redirectUrl };
923
+ }
924
+ const id = callbackId(serverUrl);
925
+ const redirect = new URL(redirectUrl);
926
+ redirect.pathname = `${CALLBACK_PATH}/${id}`;
927
+ return { url: `${CLIENT_METADATA_BASE_URL}/${id}/client.json`, redirectUrl: redirect.href };
928
+ }
904
929
  function createProvider(serverUrl, store, settings, redirectUrl, onRedirect) {
905
930
  return new McpOAuthProvider({
906
931
  serverUrl,
907
932
  redirectUrl,
908
933
  clientMetadata: { client_name: settings.clientName ?? APP_NAME },
934
+ clientMetadataDocument: settings.clientRegistration === "cimd" ? (metadata) => clientMetadataDocument(serverUrl, redirectUrl, metadata) : undefined,
909
935
  clientId: settings.clientId,
910
936
  clientSecret: settings.clientSecret,
911
937
  store,
@@ -975,13 +1001,16 @@ class McpSignInCancelledError extends Error {
975
1001
  this.name = "McpSignInCancelledError";
976
1002
  }
977
1003
  }
978
- function responseFromRedirectUrl(input, state) {
1004
+ function responseFromRedirectUrl(input, state, redirectUrl) {
979
1005
  let url;
980
1006
  try {
981
1007
  url = new URL(input.trim());
982
1008
  } catch {
983
1009
  throw new Error("Expected the full redirect URL from the browser address bar");
984
1010
  }
1011
+ if (url.origin !== redirectUrl.origin || url.pathname !== redirectUrl.pathname) {
1012
+ throw new Error("The redirect URL does not match this sign-in's redirect URI");
1013
+ }
985
1014
  const error = url.searchParams.get("error");
986
1015
  if (error)
987
1016
  throw new Error(url.searchParams.get("error_description") ?? error);
@@ -992,13 +1021,13 @@ function responseFromRedirectUrl(input, state) {
992
1021
  throw new Error("The redirect URL does not contain an authorization code");
993
1022
  return { code, iss: url.searchParams.get("iss") ?? undefined };
994
1023
  }
995
- async function waitForAuthorizationResponse(callback, state, prompt) {
1024
+ async function waitForAuthorizationResponse(callback, state, redirectUrl, prompt) {
996
1025
  const controller = new AbortController;
997
- const fromBrowser = callback.waitForCallback(state);
1026
+ const fromBrowser = callback.waitForCallback(state, redirectUrl.pathname);
998
1027
  const fromUser = prompt.promptForRedirectUrl(controller.signal).then((input) => {
999
1028
  if (!input?.trim())
1000
1029
  throw new McpSignInCancelledError;
1001
- return responseFromRedirectUrl(input, state);
1030
+ return responseFromRedirectUrl(input, state, redirectUrl);
1002
1031
  });
1003
1032
  try {
1004
1033
  return await Promise.race([fromBrowser, fromUser]);
@@ -1012,11 +1041,12 @@ async function waitForAuthorizationResponse(callback, state, prompt) {
1012
1041
  });
1013
1042
  }
1014
1043
  }
1015
- async function listenForCallback(settings, port, required) {
1044
+ async function listenForCallback(settings, extraPaths, port, required) {
1016
1045
  const options = {
1017
1046
  host: settings.host,
1018
1047
  redirectHost: settings.redirectHost,
1019
1048
  path: settings.path,
1049
+ extraPaths,
1020
1050
  renderPage: (page) => page.ok ? oauthSuccessHtml("Signed in to the MCP server. You may now close this page.") : oauthErrorHtml(page.message, page.details)
1021
1051
  };
1022
1052
  try {
@@ -1034,13 +1064,15 @@ async function signInMcpServer(options) {
1034
1064
  const callbackOptions = callbackSettings(settings);
1035
1065
  const registered = registeredRedirectUrls(stored?.clientInformation)[0];
1036
1066
  const preferredPort = callbackOptions.port ?? (registered ? Number(new URL(registered).port) || undefined : undefined);
1037
- const callback = await listenForCallback(callbackOptions, preferredPort, callbackOptions.port !== undefined);
1067
+ const cimd = settings.clientRegistration === "cimd";
1068
+ const callback = await listenForCallback(callbackOptions, cimd ? [`${CALLBACK_PATH}/${callbackId(serverUrl)}`] : [], preferredPort, callbackOptions.port !== undefined);
1038
1069
  const redirectUrl = callbackOptions.fixedRedirectUrl ?? callback.redirectUrl;
1039
1070
  try {
1040
1071
  if (stored) {
1041
1072
  const next = { ...stored };
1042
1073
  delete next.oauthState;
1043
- if (!settings.clientId && !registeredRedirectUrls(stored.clientInformation).includes(redirectUrl)) {
1074
+ const keepClient = settings.clientId || (cimd ? !stored.clientInformation : registeredRedirectUrls(stored.clientInformation).includes(redirectUrl));
1075
+ if (!keepClient) {
1044
1076
  delete next.clientInformation;
1045
1077
  delete next.tokens;
1046
1078
  delete next.tokensExpireAt;
@@ -1063,8 +1095,9 @@ async function signInMcpServer(options) {
1063
1095
  if (!authorizationUrl)
1064
1096
  throw new Error("OAuth flow did not produce an authorization URL");
1065
1097
  const state = await provider.state();
1098
+ const authorizationRedirectUrl = new URL(authorizationUrl.searchParams.get("redirect_uri") ?? redirectUrl);
1066
1099
  options.prompt.showAuthorizationUrl(authorizationUrl);
1067
- const { code, iss } = await waitForAuthorizationResponse(callback, state, options.prompt);
1100
+ const { code, iss } = await waitForAuthorizationResponse(callback, state, authorizationRedirectUrl, options.prompt);
1068
1101
  await authorizeMcp(provider, { ...flow, authorizationCode: code, iss });
1069
1102
  } finally {
1070
1103
  await callback.close();
@@ -1261,6 +1294,7 @@ class McpServerConnection {
1261
1294
  callbackUrl: oauth.callbackUrl,
1262
1295
  scope: oauth.scope,
1263
1296
  clientName: oauth.clientName,
1297
+ clientRegistration: oauth.clientRegistration,
1264
1298
  authServerMetadataUrl: oauth.authServerMetadataUrl ? new URL(oauth.authServerMetadataUrl) : undefined
1265
1299
  };
1266
1300
  }