@desplega.ai/agent-swarm 1.152.2 → 1.153.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 +1 -1
  2. package/dist/{acp-adapter-pfeje2v5.js → acp-adapter-ahmjxhek.js} +4 -4
  3. package/dist/{actions-0sttc7f5.js → actions-jzrfpkmn.js} +30 -26
  4. package/dist/{anthropic-messages-2y8nktar.js → anthropic-messages-9vqsbznt.js} +112 -84
  5. package/dist/{app-p8teva95.js → app-03na2vwb.js} +9 -6
  6. package/dist/{assistant-r45jnsrt.js → assistant-hfkxvhgt.js} +31 -27
  7. package/dist/{azure-openai-responses-f38x2avq.js → azure-openai-responses-04qwdwfq.js} +27 -13
  8. package/dist/{boot-reembed-je26h5y7.js → boot-reembed-c98xrad3.js} +8 -6
  9. package/dist/{boot-reembed-1qmybsd4.js → boot-reembed-zt3qm9xn.js} +9 -7
  10. package/dist/{boot-scrub-logs-gte3h1mm.js → boot-scrub-logs-jcqd8ksj.js} +6 -4
  11. package/dist/{claude-adapter-csc1mzka.js → claude-adapter-mgryg8g3.js} +17 -15
  12. package/dist/{claude-managed-adapter-dsmkcrjy.js → claude-managed-adapter-ea2aga6w.js} +3 -3
  13. package/dist/{claude-sdk-session-fcd6f078.js → claude-sdk-session-jjf4zss1.js} +17 -15
  14. package/dist/{cli-r2fpe7cx.js → cli-00w2c66p.js} +61 -55
  15. package/dist/{cli-fm4ytsdk.js → cli-0d9nbqxf.js} +1 -1
  16. package/dist/cli-0x5qzvkg.js +4279 -0
  17. package/dist/{cli-d3p82erb.js → cli-18amys3y.js} +9 -9
  18. package/dist/{cli-wbyk465z.js → cli-1hwnb4hc.js} +1 -1
  19. package/dist/{cli-xw7vak96.js → cli-3bcmp015.js} +5 -5
  20. package/dist/cli-3er6gcsn.js +1849 -0
  21. package/dist/{cli-c4eeb5q2.js → cli-5b6b62tz.js} +1 -1
  22. package/dist/{cli-4ahpr4sw.js → cli-5mbmvxnn.js} +5 -5
  23. package/dist/{cli-xy1cchaf.js → cli-5taqtpze.js} +235 -8
  24. package/dist/{cli-fmy65jdn.js → cli-6ks9xf1n.js} +7 -5
  25. package/dist/cli-7g9s78jf.js +2827 -0
  26. package/dist/{cli-jew3jda5.js → cli-7pnz2v6s.js} +2 -2
  27. package/dist/{cli-r84ctdxn.js → cli-83wkg402.js} +2 -2
  28. package/dist/{cli-jvy9qgjs.js → cli-a9scg8sv.js} +4 -4
  29. package/dist/{cli-4yc5d8fk.js → cli-avkd6mqv.js} +50 -0
  30. package/dist/{cli-ht5rs63n.js → cli-b2g6mzjc.js} +15 -6
  31. package/dist/{cli-jbmc0r7r.js → cli-bw0dfwkr.js} +1 -1
  32. package/dist/{cli-8wrympd7.js → cli-bzr0ftht.js} +26 -7
  33. package/dist/{cli-wpv5rgg2.js → cli-cdq08v3r.js} +2 -2
  34. package/dist/cli-cgj4hc12.js +18902 -0
  35. package/dist/cli-d6hk10xx.js +14075 -0
  36. package/dist/{cli-zvgj5zg8.js → cli-djvxhnc4.js} +31 -3324
  37. package/dist/{cli-s25avqg9.js → cli-e781c6f9.js} +1 -0
  38. package/dist/{cli-3bpzj8me.js → cli-eazstkad.js} +2 -2
  39. package/dist/{cli-s2qjdd6z.js → cli-eetrvv91.js} +1 -1
  40. package/dist/cli-h38x0qhg.js +6912 -0
  41. package/dist/{cli-9xvdm3bx.js → cli-h60qfasa.js} +742 -278
  42. package/dist/{pi-mono-adapter-jcqt71cg.js → cli-ja7mrygn.js} +67343 -101083
  43. package/dist/{cli-j8dggbcr.js → cli-k0g1sjzm.js} +23 -31
  44. package/dist/{cli-gphgnvpf.js → cli-m04pt18h.js} +2 -2
  45. package/dist/{cli-rgvp6r2x.js → cli-mcnhgnn1.js} +193 -4
  46. package/dist/{cli-j4nv2wqy.js → cli-mzgyhxbe.js} +10 -4
  47. package/dist/{cli-qn042eh7.js → cli-n0q9b7j4.js} +46 -1870
  48. package/dist/cli-n7h1kqpf.js +29 -0
  49. package/dist/{cli-ckz5t2hb.js → cli-nq0kgyxe.js} +5 -5
  50. package/dist/{cli-z3wkng35.js → cli-qrqhc31j.js} +151 -125
  51. package/dist/{cli-56h18ng2.js → cli-r8tdds6s.js} +1 -1
  52. package/dist/{cli-ch38q5z4.js → cli-rj7de4b2.js} +6788 -2541
  53. package/dist/{cli-qd65dvr6.js → cli-s5s1f2yn.js} +56 -40
  54. package/dist/{cli-kj76p1ez.js → cli-scwq51gn.js} +13 -7
  55. package/dist/{cli-2nnd56zt.js → cli-tdh4hjr2.js} +3 -2
  56. package/dist/{cli-c2mk1kgf.js → cli-v4en75c5.js} +8 -1
  57. package/dist/{cli-b9dd0h3k.js → cli-vw03hg5t.js} +3 -2
  58. package/dist/{cli-zvkq5csn.js → cli-ybpp1srm.js} +6 -6
  59. package/dist/cli-yn0a417a.js +4069 -0
  60. package/dist/{cli-gpqcs935.js → cli-ysc1kbet.js} +2 -2
  61. package/dist/{cli-zfzznn7c.js → cli-ytxs7mn1.js} +26 -2
  62. package/dist/cli-zcsyfw2z.js +3335 -0
  63. package/dist/{cli-4j8k1m4w.js → cli-zt1227k1.js} +1162 -192
  64. package/dist/cli-zy5w9gpt.js +230 -0
  65. package/dist/cli.js +26 -34
  66. package/dist/{codex-adapter-fxmpsabw.js → codex-adapter-8548daap.js} +12 -11
  67. package/dist/{codex-session-runner-rr1e47ea.js → codex-session-runner-2dad8p4x.js} +12 -11
  68. package/dist/{commands-0f5rxz51.js → commands-355dbpa8.js} +6 -4
  69. package/dist/{db-r3jxf3wy.js → db-4jjvwz1s.js} +6 -4
  70. package/dist/{dispatcher-06vph6q4.js → dispatcher-ca590nkz.js} +29 -25
  71. package/dist/dsh-adapter-rgvj6ykp.js +19 -0
  72. package/dist/{e2b-413b8t4v.js → e2b-93x1k0j5.js} +6 -7
  73. package/dist/{google-generative-ai-ndjmj5fa.js → google-generative-ai-xtw7tggr.js} +33 -73
  74. package/dist/{google-vertex-fr665tqs.js → google-vertex-zdqpms6n.js} +38 -68
  75. package/dist/{handlers-0hgtc910.js → handlers-2y6dqpvf.js} +32 -28
  76. package/dist/{hook-9kvrv1er.js → hook-0s8knepr.js} +11 -9
  77. package/dist/{hook-fbfhkat3.js → hook-k0gec254.js} +12 -10
  78. package/dist/{http-xbyx8k5j.js → http-jp2nb4zw.js} +300 -237
  79. package/dist/{index-tfsdxyv6.js → index-0wzg3sp3.js} +39 -24
  80. package/dist/{index-rnyahfn5.js → index-3mq6ce93.js} +29 -25
  81. package/dist/{index-jsw3aynb.js → index-42s7eezq.js} +29 -25
  82. package/dist/{cli-pdz9qde4.js → index-9ch1n761.js} +10 -11
  83. package/dist/{index-gpfn1741.js → index-b56xkbc7.js} +8 -6
  84. package/dist/{index-66xz5e9j.js → index-h5gs261z.js} +521 -9
  85. package/dist/{index-2sp0384z.js → index-kkeftv5h.js} +738 -21
  86. package/dist/{index-sgk50mbg.js → index-s37dbevy.js} +30 -26
  87. package/dist/index-w097eg0x.js +5 -0
  88. package/dist/jiti-loader-b4837mtm.js +34 -0
  89. package/dist/jiti-static-loader-q2g5ds0y.js +31267 -0
  90. package/dist/{keepalive-s3xpr7sw.js → keepalive-9pfw7t02.js} +8 -6
  91. package/dist/{lead-rq4n5r16.js → lead-7vcmcdq4.js} +44 -39
  92. package/dist/{maintenance-f0atj1ab.js → maintenance-x277jbpd.js} +9 -7
  93. package/dist/{mistral-conversations-qhr84ph3.js → mistral-conversations-zx0kn554.js} +24 -18
  94. package/dist/{node-x1g4v0pq.js → node-5qtycf6a.js} +16 -11
  95. package/dist/{oauth-refresh-sweep-q0t3bxre.js → oauth-refresh-sweep-5fwa7th2.js} +7 -5
  96. package/dist/{onboard-br5j1kch.js → onboard-17sv0f51.js} +103 -35
  97. package/dist/{openai-codex-responses-wy6a43wd.js → openai-codex-responses-4vjqzvjp.js} +33 -27
  98. package/dist/{openai-completions-32rdwmze.js → openai-completions-pxsmym7g.js} +52 -64
  99. package/dist/{openai-responses-6xwf7bzg.js → openai-responses-tmn0rqp0.js} +23 -24
  100. package/dist/{opencode-adapter-28axqa09.js → opencode-adapter-qbmv4tpy.js} +6 -6
  101. package/dist/{otel-impl-vdqge2nt.js → otel-impl-srjy4be4.js} +4 -4
  102. package/dist/{pi-messages-b1mbr6gp.js → pi-messages-txpjj39q.js} +3 -3
  103. package/dist/pi-mono-adapter-946a0sdd.js +1316 -0
  104. package/dist/{pricing-refresh-hb3691fd.js → pricing-refresh-vetcm563.js} +8 -6
  105. package/dist/{rbac-roles-jh280k9g.js → rbac-roles-cfzgr6r1.js} +8 -6
  106. package/dist/{rbac-roles-t78xt39b.js → rbac-roles-cmxaddmy.js} +7 -5
  107. package/dist/{render-v2-7p8r2ymg.js → render-v2-f9hzff72.js} +9 -6
  108. package/dist/{seed-pricing-hf889enh.js → seed-pricing-jggk6rsy.js} +7 -5
  109. package/dist/{setup-w6mb03jv.js → setup-8mfceb9t.js} +4 -5
  110. package/dist/virtual-modules-ax661xp0.js +54 -0
  111. package/dist/{work-objects-nt8gy1kj.js → work-objects-vdsdcczj.js} +32 -28
  112. package/dist/{worker-dwy617t0.js → worker-cd0nhyv1.js} +44 -39
  113. package/openapi.json +36 -14
  114. package/package.json +6 -6
  115. package/src/be/automation-preflight.ts +6 -4
  116. package/src/be/extensions/db.ts +35 -4
  117. package/src/be/models-catalog.ts +2 -0
  118. package/src/be/modelsdev-cache.json +213 -0
  119. package/src/be/rbac-audit.ts +2 -0
  120. package/src/be/seed-pricing.ts +3 -1
  121. package/src/be/swarm-config-guard.ts +1 -0
  122. package/src/commands/onboard/compose-generator.ts +6 -1
  123. package/src/commands/onboard/env-generator.ts +6 -1
  124. package/src/commands/onboard/manifest.ts +1 -0
  125. package/src/commands/onboard/steps/integration-slack.tsx +56 -16
  126. package/src/commands/onboard/steps/review.tsx +5 -0
  127. package/src/commands/onboard/types.ts +6 -0
  128. package/src/commands/onboard.tsx +5 -1
  129. package/src/commands/provider-credentials.ts +14 -2
  130. package/src/extensions/lifecycle.ts +15 -21
  131. package/src/http/agents.ts +2 -2
  132. package/src/http/config.ts +1 -1
  133. package/src/http/core.ts +5 -3
  134. package/src/http/extensions.ts +108 -34
  135. package/src/http/status.ts +22 -10
  136. package/src/prompts/base-prompt.ts +15 -7
  137. package/src/providers/claude-managed-models.ts +8 -0
  138. package/src/providers/codex-models.ts +15 -1
  139. package/src/providers/dsh-adapter.ts +248 -0
  140. package/src/providers/index.ts +5 -1
  141. package/src/providers/modelsdev-reasoning.json +50 -0
  142. package/src/rbac/legacy-policy.ts +15 -1
  143. package/src/rbac/types.ts +2 -0
  144. package/src/server.ts +7 -1
  145. package/src/slack/app.ts +32 -10
  146. package/src/slack/config.ts +29 -0
  147. package/src/telemetry.ts +2 -3
  148. package/src/tests/agents-harness-provider.test.ts +19 -0
  149. package/src/tests/agents-list-model-display.test.ts +60 -1
  150. package/src/tests/base-prompt.test.ts +25 -0
  151. package/src/tests/claude-managed-adapter.test.ts +6 -0
  152. package/src/tests/codex-gpt6-release.test.ts +97 -0
  153. package/src/tests/config-api-only-keys.test.ts +9 -0
  154. package/src/tests/context-window.test.ts +1 -0
  155. package/src/tests/dsh-adapter.test.ts +276 -0
  156. package/src/tests/e2e-nightly-report.test.ts +3 -3
  157. package/src/tests/e2e-redact.test.ts +2 -0
  158. package/src/tests/env-flag.test.ts +6 -0
  159. package/src/tests/extensions-db.test.ts +41 -0
  160. package/src/tests/extensions-http.test.ts +148 -4
  161. package/src/tests/extensions-lifecycle.test.ts +18 -0
  162. package/src/tests/extensions-mcp-tools.test.ts +9 -5
  163. package/src/tests/internal-ai/complete-structured.test.ts +32 -9
  164. package/src/tests/internal-ai/credentials.test.ts +4 -4
  165. package/src/tests/internal-ai/models.test.ts +20 -0
  166. package/src/tests/migration-046-budgets.test.ts +2 -0
  167. package/src/tests/model-control.test.ts +2 -2
  168. package/src/tests/onboard-compose.test.ts +14 -0
  169. package/src/tests/onboard-env.test.ts +17 -0
  170. package/src/tests/onboard-manifest.test.ts +12 -0
  171. package/src/tests/opus-5-5-registry.test.ts +48 -0
  172. package/src/tests/pricing-refresh.test.ts +1 -1
  173. package/src/tests/pricing-routes.test.ts +15 -3
  174. package/src/tests/provider-registration.test.ts +5 -2
  175. package/src/tests/rbac-admission-e2e.test.ts +54 -0
  176. package/src/tests/rbac-e2e-helpers.ts +2 -0
  177. package/src/tests/rbac-engine.test.ts +32 -0
  178. package/src/tests/rbac-wire-e2e.test.ts +33 -0
  179. package/src/tests/reasoning-effort.test.ts +8 -0
  180. package/src/tests/reload-config.test.ts +33 -0
  181. package/src/tests/session-costs-model-key-normalize.test.ts +1 -0
  182. package/src/tests/slack-config.test.ts +136 -0
  183. package/src/tests/slack-mode-lifecycle.test.ts +69 -0
  184. package/src/tests/status.test.ts +22 -2
  185. package/src/tests/task-tool-preload-mcp.test.ts +3 -1
  186. package/src/tests/telemetry-init.test.ts +18 -0
  187. package/src/tests/workflow-llm.test.ts +3 -3
  188. package/src/tools/extension-common.ts +4 -0
  189. package/src/tools/extension-install.ts +1 -1
  190. package/src/tools/extension-list.ts +4 -3
  191. package/src/types.ts +17 -8
  192. package/src/utils/context-window.ts +1 -0
  193. package/src/utils/credentials.ts +8 -0
  194. package/src/utils/internal-ai/complete-structured.ts +8 -0
  195. package/src/utils/internal-ai/models.ts +5 -5
  196. package/templates/schedules/daily-status-report/content.md +25 -12
  197. package/templates/schedules/daily-workflow-health-audit/content.md +1 -1
  198. package/templates/skills/swarm-extensions/content.md +9 -5
  199. package/dist/cli-4wppfkcm.js +0 -16384
  200. package/dist/cli-55qrx3q8.js +0 -6041
  201. package/dist/cli-cg4vnyh4.js +0 -305
  202. package/dist/cli-jyetr64j.js +0 -52
  203. package/dist/cli-pp9ax9pa.js +0 -520
  204. package/dist/cli-qa66qaf0.js +0 -6076
  205. package/dist/cli-qghpvca2.js +0 -590
  206. package/dist/cli-rembwgt8.js +0 -861
  207. package/dist/cli-tcvj7j2r.js +0 -1971
  208. package/dist/cli-vfcth11k.js +0 -36
  209. package/dist/cli-w1d1bgkk.js +0 -1422
  210. package/dist/cli-y6x0gqtg.js +0 -5977
  211. package/dist/es5-tj3tn4jj.js +0 -844
  212. package/dist/index-1kq5hw2p.js +0 -467
  213. package/dist/index-1p8wdstt.js +0 -7
  214. package/dist/index-8fdjzb2f.js +0 -100
  215. package/dist/index-bxmwzbgw.js +0 -365
  216. package/dist/index-nzerxd5z.js +0 -7
  217. package/dist/index-pyymnzat.js +0 -1168
  218. package/dist/index-qv6atbab.js +0 -619
  219. package/dist/index-rja32qa7.js +0 -801
  220. package/dist/index-tzg5srfp.js +0 -211
  221. package/dist/index-vhncw983.js +0 -90
  222. package/dist/index-xk036zsk.js +0 -859
  223. package/dist/index-zyazbzcb.js +0 -6
  224. package/dist/{claude-managed-setup-48dwg9bv.js → claude-managed-setup-f5pmsyrp.js} +3 -3
@@ -0,0 +1,2827 @@
1
+ import {
2
+ getValidCodexOAuth,
3
+ persistCodexOAuth
4
+ } from "./cli-e7d20cty.js";
5
+ import {
6
+ GITHUB_COPILOT_MODELS,
7
+ builtinModels,
8
+ getEnvApiKey,
9
+ normalizeRadiusGatewayUrl,
10
+ registerBundledOAuthFlowLoaders
11
+ } from "./cli-d6hk10xx.js";
12
+ import {
13
+ init_zod
14
+ } from "./cli-sd0mxa3c.js";
15
+ import {
16
+ exports_external
17
+ } from "./cli-w5e1d1vv.js";
18
+ import {
19
+ getProviderEnvValue
20
+ } from "./cli-btbzvq95.js";
21
+ import {
22
+ InMemoryCredentialStore
23
+ } from "./cli-mcnhgnn1.js";
24
+ import {
25
+ __require
26
+ } from "./cli-bc05qbss.js";
27
+
28
+ // src/be/memory/raters/llm.ts
29
+ init_zod();
30
+
31
+ // src/be/memory/raters/llm-client.ts
32
+ var DEFAULT_TIMEOUT_MS = 30000;
33
+ var PROMPT_TEMPLATE = `You are scoring the usefulness of one retrieved memory.
34
+
35
+ Return ONLY a JSON object with these fields (no prose, no markdown):
36
+ {
37
+ "score": number, // 0 = misleading/unhelpful, 1 = highly useful
38
+ "reasoning": string // 1..500 chars, why
39
+ }
40
+
41
+ QUERY:
42
+ \${query}
43
+
44
+ MEMORY:
45
+ id: \${memoryId}
46
+ name: \${memoryName}
47
+ content: \${memoryContent}
48
+
49
+ AGENT RESPONSE / SUMMARY:
50
+ \${response}
51
+
52
+ Score 0..1.`;
53
+ var PLACEHOLDER_PREFIX = "$";
54
+ var QUERY_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{query}`;
55
+ var MEMORY_ID_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{memoryId}`;
56
+ var MEMORY_NAME_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{memoryName}`;
57
+ var MEMORY_CONTENT_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{memoryContent}`;
58
+ var RESPONSE_PLACEHOLDER = `${PLACEHOLDER_PREFIX}{response}`;
59
+ function buildPrompt(input) {
60
+ return PROMPT_TEMPLATE.replace(QUERY_PLACEHOLDER, input.query).replace(MEMORY_ID_PLACEHOLDER, input.memory.id).replace(MEMORY_NAME_PLACEHOLDER, input.memory.name).replace(MEMORY_CONTENT_PLACEHOLDER, input.memory.content).replace(RESPONSE_PLACEHOLDER, input.response);
61
+ }
62
+ function parseScoreAndReasoning(raw) {
63
+ if (typeof raw !== "string")
64
+ return null;
65
+ let parsed;
66
+ try {
67
+ parsed = JSON.parse(raw.trim());
68
+ } catch {
69
+ return null;
70
+ }
71
+ if (!parsed || typeof parsed !== "object")
72
+ return null;
73
+ const obj = parsed;
74
+ const score = typeof obj.score === "number" ? obj.score : null;
75
+ const reasoning = typeof obj.reasoning === "string" ? obj.reasoning : null;
76
+ if (score == null || reasoning == null)
77
+ return null;
78
+ if (!Number.isFinite(score) || score < 0 || score > 1)
79
+ return null;
80
+ if (reasoning.length === 0 || reasoning.length > 500)
81
+ return null;
82
+ return { score, reasoning };
83
+ }
84
+
85
+ class ClaudeCliLlmRaterClient {
86
+ model;
87
+ timeoutMs;
88
+ constructor(opts = {}) {
89
+ this.model = opts.model ?? process.env.MEMORY_LLM_RATER_MODEL ?? "haiku";
90
+ this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
91
+ }
92
+ async rate(input) {
93
+ const prompt = buildPrompt(input);
94
+ const tmpFile = `/tmp/llm-rater-${Date.now()}-${Math.random().toString(36).slice(2)}.txt`;
95
+ let stdout = "";
96
+ try {
97
+ await Bun.write(tmpFile, prompt);
98
+ const proc = Bun.spawn(["bash", "-c", `cat "${tmpFile}" | claude -p --model ${this.model} --output-format json`], {
99
+ stdout: "pipe",
100
+ stderr: "pipe",
101
+ env: { ...process.env, SKIP_SESSION_SUMMARY: "1" }
102
+ });
103
+ const timeoutId = setTimeout(() => proc.kill(), this.timeoutMs);
104
+ stdout = await new Response(proc.stdout).text();
105
+ clearTimeout(timeoutId);
106
+ } catch (err) {
107
+ console.error("[memory-rater:llm] claude -p shell-out failed:", err.message);
108
+ return null;
109
+ } finally {
110
+ try {
111
+ await Bun.$`rm -f ${tmpFile}`.quiet();
112
+ } catch {}
113
+ }
114
+ let envelope;
115
+ try {
116
+ envelope = JSON.parse(stdout);
117
+ } catch {
118
+ return null;
119
+ }
120
+ return parseScoreAndReasoning(envelope.result);
121
+ }
122
+ }
123
+
124
+ // src/be/memory/raters/types.ts
125
+ var REFERENCES_SOURCE_MAX_LENGTH = 512;
126
+ var NUL_CHAR_CODE = 0;
127
+ var DEL_CHAR_CODE = 127;
128
+ var FIRST_PRINTABLE_ASCII = 32;
129
+ function sanitizeReferencesSource(input) {
130
+ let stripped = "";
131
+ for (let i = 0;i < input.length; i++) {
132
+ const code = input.charCodeAt(i);
133
+ if (code === NUL_CHAR_CODE)
134
+ return null;
135
+ if (code < FIRST_PRINTABLE_ASCII || code === DEL_CHAR_CODE) {
136
+ continue;
137
+ }
138
+ stripped += input[i];
139
+ }
140
+ if (stripped.length === 0)
141
+ return null;
142
+ return stripped;
143
+ }
144
+
145
+ // src/be/memory/raters/llm.ts
146
+ var LLM_RATER_WEIGHT = 0.8;
147
+ var RatingSchema = exports_external.object({
148
+ id: exports_external.string().min(1),
149
+ score: exports_external.number().min(0).max(1),
150
+ reasoning: exports_external.string().min(1).max(500),
151
+ referencesSource: exports_external.string().min(1).max(REFERENCES_SOURCE_MAX_LENGTH).optional()
152
+ });
153
+ var SummaryWithRatingsSchema = exports_external.object({
154
+ summary: exports_external.string(),
155
+ ratings: exports_external.array(RatingSchema).default([])
156
+ });
157
+ var BASE_SUMMARIZE_PROMPT = `You are summarizing an AI agent's work session. Extract ONLY high-value learnings.
158
+
159
+ DO NOT include:
160
+ - Generic descriptions of what was done ("worked on task X")
161
+ - Tool calls or file reads
162
+ - Routine progress updates
163
+
164
+ DO include (if present):
165
+ - **Mistakes made and corrections** — what went wrong and what fixed it
166
+ - **Discovered patterns** — reusable approaches, APIs, or codebase conventions
167
+ - **Codebase knowledge** — important file paths, architecture decisions, gotchas
168
+ - **Environment knowledge** — service URLs, config details, tool quirks
169
+ - **Failed approaches** — what was tried and didn't work (and why)
170
+
171
+ Format as a bulleted list of concrete, reusable facts. If the session was routine with no significant learnings, respond with exactly: "No significant learnings."`;
172
+
173
+ class LlmRater {
174
+ client;
175
+ name = "llm";
176
+ constructor(client = new ClaudeCliLlmRaterClient) {
177
+ this.client = client;
178
+ }
179
+ async rate(ctx) {
180
+ const enriched = ctx;
181
+ if (enriched.retrievedMemoryIds.length === 0)
182
+ return [];
183
+ const memories = enriched.retrievedMemories;
184
+ if (!memories || memories.length === 0)
185
+ return [];
186
+ const events = [];
187
+ for (const memoryId of enriched.retrievedMemoryIds) {
188
+ const memory = memories.find((m) => m.id === memoryId);
189
+ if (!memory)
190
+ continue;
191
+ let result;
192
+ try {
193
+ result = await this.client.rate({
194
+ query: enriched.query ?? "",
195
+ memory,
196
+ response: enriched.response ?? enriched.evidence ?? ""
197
+ });
198
+ } catch (err) {
199
+ console.error(`[memory-rater:llm] client.rate threw for memoryId=${memoryId}:`, err.message);
200
+ continue;
201
+ }
202
+ if (!result)
203
+ continue;
204
+ events.push({
205
+ memoryId,
206
+ signal: 2 * result.score - 1,
207
+ weight: LLM_RATER_WEIGHT,
208
+ source: "",
209
+ reasoning: result.reasoning
210
+ });
211
+ }
212
+ return events;
213
+ }
214
+ }
215
+ function buildRatingsFromLlm(ratings, retrievals) {
216
+ const allowed = new Set(retrievals.map((r) => r.id));
217
+ const events = [];
218
+ for (const r of ratings) {
219
+ if (!allowed.has(r.id))
220
+ continue;
221
+ let cleanedReferencesSource;
222
+ if (r.referencesSource !== undefined) {
223
+ const cleaned = sanitizeReferencesSource(r.referencesSource);
224
+ if (cleaned !== null) {
225
+ cleanedReferencesSource = cleaned;
226
+ }
227
+ }
228
+ events.push({
229
+ memoryId: r.id,
230
+ signal: 2 * r.score - 1,
231
+ weight: LLM_RATER_WEIGHT,
232
+ source: "llm",
233
+ reasoning: r.reasoning,
234
+ ...cleanedReferencesSource !== undefined ? { referencesSource: cleanedReferencesSource } : {}
235
+ });
236
+ }
237
+ return events;
238
+ }
239
+ var RETRIEVAL_PROMPT_CONTENT_CAP = 600;
240
+ function buildSummaryWithRatingsPrompt(basePrompt, retrievals) {
241
+ if (retrievals.length === 0)
242
+ return basePrompt;
243
+ const memoryBlock = retrievals.map((m, i) => {
244
+ const content = m.content.length > RETRIEVAL_PROMPT_CONTENT_CAP ? `${m.content.slice(0, RETRIEVAL_PROMPT_CONTENT_CAP)}…` : m.content;
245
+ return `Memory #${i + 1}
246
+ id: ${m.id}
247
+ name: ${m.name}
248
+ content: ${content}`;
249
+ }).join(`
250
+
251
+ `);
252
+ return `${basePrompt}
253
+
254
+ CRITICAL: Your entire response MUST be a single JSON object that conforms to the schema below. Do NOT wrap it in triple-backtick fences (no \`\`\`json or \`\`\`), do NOT add a prose preamble, do NOT add trailing commentary. Just the JSON object, nothing else.
255
+
256
+ Schema:
257
+ {
258
+ "summary": string, // your existing summary text
259
+ "ratings": [ // one entry per memory you can score
260
+ {
261
+ "id": string, // memory id, copied from the list below
262
+ "score": number, // 0 = misleading/unhelpful, 1 = highly useful
263
+ "reasoning": string, // 1..500 chars, why
264
+ "referencesSource": string // OPTIONAL — see note below
265
+ }
266
+ ]
267
+ }
268
+
269
+ Score ONLY memories present in the list below. Use the exact ids. Omit any you cannot evaluate.
270
+
271
+ Optionally for each rating, if the memory clearly references a specific external source (a GitHub PR/issue, a Linear issue, a customer, a Slack thread, an AgentMail thread, etc.), include a \`referencesSource\` string using the convention "<source>:<identifier>" (e.g. "github:owner/repo#N", "linear:KEY-N", "customer:<slug>"). Any prefix is fine — pick what matches the source. Omit the field if no clear external source.
272
+
273
+ Memories retrieved during this session:
274
+
275
+ ${memoryBlock}`;
276
+ }
277
+ function isLlmRaterEnabled() {
278
+ const raw = process.env.MEMORY_RATERS;
279
+ if (!raw || raw.trim() === "")
280
+ return false;
281
+ return raw.split(",").map((s) => s.trim()).includes("llm");
282
+ }
283
+ function dedupeRetrievalsForRater(rows) {
284
+ const seenSchedules = new Set;
285
+ const out = [];
286
+ for (const row of rows) {
287
+ const scheduleId = row.scheduleId;
288
+ if (typeof scheduleId === "string" && scheduleId.length > 0) {
289
+ if (seenSchedules.has(scheduleId))
290
+ continue;
291
+ seenSchedules.add(scheduleId);
292
+ }
293
+ out.push(row);
294
+ }
295
+ return out;
296
+ }
297
+ async function fetchRetrievalsForTask(opts) {
298
+ const fetchFn = opts.fetchImpl ?? fetch;
299
+ try {
300
+ const url = `${opts.apiUrl}/api/memory/retrievals?taskId=${encodeURIComponent(opts.taskId)}`;
301
+ const res = await fetchFn(url, {
302
+ headers: {
303
+ "X-Agent-ID": opts.agentId,
304
+ ...opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {}
305
+ }
306
+ });
307
+ if (!res.ok) {
308
+ console.error(`[memory-rater:llm] GET /api/memory/retrievals failed: ${res.status} ${res.statusText}`);
309
+ return [];
310
+ }
311
+ const body = await res.json();
312
+ return body.results ?? [];
313
+ } catch (err) {
314
+ console.error("[memory-rater:llm] fetchRetrievalsForTask threw:", err.message);
315
+ return [];
316
+ }
317
+ }
318
+ async function postRatings(opts) {
319
+ if (opts.events.length === 0)
320
+ return { ok: true, status: 0 };
321
+ const fetchFn = opts.fetchImpl ?? fetch;
322
+ const events = opts.events.map((e) => ({
323
+ memoryId: e.memoryId,
324
+ signal: e.signal,
325
+ weight: e.weight,
326
+ source: e.source,
327
+ ...e.reasoning !== undefined ? { reasoning: e.reasoning } : {},
328
+ ...e.referencesSource !== undefined ? { referencesSource: e.referencesSource } : {},
329
+ ...opts.taskId ? { taskId: opts.taskId } : {}
330
+ }));
331
+ try {
332
+ const res = await fetchFn(`${opts.apiUrl}/api/memory/rate`, {
333
+ method: "POST",
334
+ headers: {
335
+ "Content-Type": "application/json",
336
+ "X-Agent-ID": opts.agentId,
337
+ ...opts.apiKey ? { Authorization: `Bearer ${opts.apiKey}` } : {}
338
+ },
339
+ body: JSON.stringify({ events })
340
+ });
341
+ if (!res.ok) {
342
+ const text = await res.text().catch(() => "");
343
+ console.error(`[memory-rater:llm] POST /api/memory/rate failed: ${res.status} ${res.statusText} ${text.slice(0, 200)}`);
344
+ }
345
+ return { ok: res.ok, status: res.status };
346
+ } catch (err) {
347
+ console.error("[memory-rater:llm] postRatings threw:", err.message);
348
+ return { ok: false, status: 0 };
349
+ }
350
+ }
351
+
352
+ // src/utils/internal-ai/models.ts
353
+ var DEFAULT_MODEL = {
354
+ openrouter: "openrouter/deepseek/deepseek-v4.1-flash",
355
+ anthropic: "anthropic/claude-haiku-4-5",
356
+ openai: "openai/gpt-6-luna",
357
+ "openai-codex": "openai-codex/gpt-6-luna",
358
+ "claude-cli": "haiku"
359
+ };
360
+ function resolveModelString(kind) {
361
+ return process.env.MEMORY_RATER_MODEL ?? DEFAULT_MODEL[kind];
362
+ }
363
+ function parseModelStr(modelStr) {
364
+ const idx = modelStr.indexOf("/");
365
+ if (idx < 0)
366
+ throw new Error(`invalid model string (no '/'): ${modelStr}`);
367
+ return [modelStr.slice(0, idx), modelStr.slice(idx + 1)];
368
+ }
369
+
370
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/oauth-page.js
371
+ var LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>`;
372
+ function escapeHtml(value) {
373
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
374
+ }
375
+ function renderPage(options) {
376
+ const title = escapeHtml(options.title);
377
+ const heading = escapeHtml(options.heading);
378
+ const message = escapeHtml(options.message);
379
+ const details = options.details ? escapeHtml(options.details) : undefined;
380
+ return `<!doctype html>
381
+ <html lang="en">
382
+ <head>
383
+ <meta charset="utf-8" />
384
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
385
+ <title>${title}</title>
386
+ <style>
387
+ :root {
388
+ --text: #fafafa;
389
+ --text-dim: #a1a1aa;
390
+ --page-bg: #09090b;
391
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
392
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
393
+ }
394
+ * { box-sizing: border-box; }
395
+ html { color-scheme: dark; }
396
+ body {
397
+ margin: 0;
398
+ min-height: 100vh;
399
+ display: flex;
400
+ align-items: center;
401
+ justify-content: center;
402
+ padding: 24px;
403
+ background: var(--page-bg);
404
+ color: var(--text);
405
+ font-family: var(--font-sans);
406
+ text-align: center;
407
+ }
408
+ main {
409
+ width: 100%;
410
+ max-width: 560px;
411
+ display: flex;
412
+ flex-direction: column;
413
+ align-items: center;
414
+ justify-content: center;
415
+ }
416
+ .logo {
417
+ width: 72px;
418
+ height: 72px;
419
+ display: block;
420
+ margin-bottom: 24px;
421
+ }
422
+ h1 {
423
+ margin: 0 0 10px;
424
+ font-size: 28px;
425
+ line-height: 1.15;
426
+ font-weight: 650;
427
+ color: var(--text);
428
+ }
429
+ p {
430
+ margin: 0;
431
+ line-height: 1.7;
432
+ color: var(--text-dim);
433
+ font-size: 15px;
434
+ }
435
+ .details {
436
+ margin-top: 16px;
437
+ font-family: var(--font-mono);
438
+ font-size: 13px;
439
+ color: var(--text-dim);
440
+ white-space: pre-wrap;
441
+ word-break: break-word;
442
+ }
443
+ </style>
444
+ </head>
445
+ <body>
446
+ <main>
447
+ <div class="logo">${LOGO_SVG}</div>
448
+ <h1>${heading}</h1>
449
+ <p>${message}</p>
450
+ ${details ? `<div class="details">${details}</div>` : ""}
451
+ </main>
452
+ </body>
453
+ </html>`;
454
+ }
455
+ function oauthSuccessHtml(message) {
456
+ return renderPage({
457
+ title: "Authentication successful",
458
+ heading: "Authentication successful",
459
+ message
460
+ });
461
+ }
462
+ function oauthErrorHtml(message, details) {
463
+ return renderPage({
464
+ title: "Authentication failed",
465
+ heading: "Authentication failed",
466
+ message,
467
+ details
468
+ });
469
+ }
470
+
471
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/pkce.js
472
+ function base64urlEncode(bytes) {
473
+ let binary = "";
474
+ for (const byte of bytes) {
475
+ binary += String.fromCharCode(byte);
476
+ }
477
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
478
+ }
479
+ async function generatePKCE() {
480
+ const verifierBytes = new Uint8Array(32);
481
+ crypto.getRandomValues(verifierBytes);
482
+ const verifier = base64urlEncode(verifierBytes);
483
+ const encoder = new TextEncoder;
484
+ const data = encoder.encode(verifier);
485
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
486
+ const challenge = base64urlEncode(new Uint8Array(hashBuffer));
487
+ return { verifier, challenge };
488
+ }
489
+
490
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.js
491
+ var nodeApis = null;
492
+ var nodeApisPromise = null;
493
+ var decode = (s) => atob(s);
494
+ var CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
495
+ var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
496
+ var TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
497
+ var CALLBACK_HOST = getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
498
+ var CALLBACK_PORT = 53692;
499
+ var CALLBACK_PATH = "/callback";
500
+ var REDIRECT_URI = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
501
+ var SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
502
+ async function getNodeApis() {
503
+ if (nodeApis)
504
+ return nodeApis;
505
+ if (!nodeApisPromise) {
506
+ if (typeof process === "undefined" || !process.versions?.node && !process.versions?.bun) {
507
+ throw new Error("Anthropic OAuth is only available in Node.js environments");
508
+ }
509
+ nodeApisPromise = import("node:http").then((httpModule) => ({
510
+ createServer: httpModule.createServer
511
+ }));
512
+ }
513
+ nodeApis = await nodeApisPromise;
514
+ return nodeApis;
515
+ }
516
+ function parseAuthorizationInput(input) {
517
+ const value = input.trim();
518
+ if (!value)
519
+ return {};
520
+ try {
521
+ const url = new URL(value);
522
+ return {
523
+ code: url.searchParams.get("code") ?? undefined,
524
+ state: url.searchParams.get("state") ?? undefined
525
+ };
526
+ } catch {}
527
+ if (value.includes("#")) {
528
+ const [code, state] = value.split("#", 2);
529
+ return { code, state };
530
+ }
531
+ if (value.includes("code=")) {
532
+ const params = new URLSearchParams(value);
533
+ return {
534
+ code: params.get("code") ?? undefined,
535
+ state: params.get("state") ?? undefined
536
+ };
537
+ }
538
+ return { code: value };
539
+ }
540
+ function formatErrorDetails(error) {
541
+ if (error instanceof Error) {
542
+ const details = [`${error.name}: ${error.message}`];
543
+ const errorWithCode = error;
544
+ if (errorWithCode.code)
545
+ details.push(`code=${errorWithCode.code}`);
546
+ if (typeof errorWithCode.errno !== "undefined")
547
+ details.push(`errno=${String(errorWithCode.errno)}`);
548
+ if (typeof error.cause !== "undefined") {
549
+ details.push(`cause=${formatErrorDetails(error.cause)}`);
550
+ }
551
+ if (error.stack) {
552
+ details.push(`stack=${error.stack}`);
553
+ }
554
+ return details.join("; ");
555
+ }
556
+ return String(error);
557
+ }
558
+ async function startCallbackServer(expectedState) {
559
+ const { createServer } = await getNodeApis();
560
+ return new Promise((resolve, reject) => {
561
+ let settleWait;
562
+ const waitForCodePromise = new Promise((resolveWait) => {
563
+ let settled = false;
564
+ settleWait = (value) => {
565
+ if (settled)
566
+ return;
567
+ settled = true;
568
+ resolveWait(value);
569
+ };
570
+ });
571
+ const server = createServer((req, res) => {
572
+ try {
573
+ const url = new URL(req.url || "", "http://localhost");
574
+ if (url.pathname !== CALLBACK_PATH) {
575
+ res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
576
+ res.end(oauthErrorHtml("Callback route not found."));
577
+ return;
578
+ }
579
+ const code = url.searchParams.get("code");
580
+ const state = url.searchParams.get("state");
581
+ const error = url.searchParams.get("error");
582
+ if (error) {
583
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
584
+ res.end(oauthErrorHtml("Anthropic authentication did not complete.", `Error: ${error}`));
585
+ return;
586
+ }
587
+ if (!code || !state) {
588
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
589
+ res.end(oauthErrorHtml("Missing code or state parameter."));
590
+ return;
591
+ }
592
+ if (state !== expectedState) {
593
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
594
+ res.end(oauthErrorHtml("State mismatch."));
595
+ return;
596
+ }
597
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
598
+ res.end(oauthSuccessHtml("Anthropic authentication completed. You can close this window."));
599
+ settleWait?.({ code, state });
600
+ } catch {
601
+ res.writeHead(500, { "Content-Type": "text/plain; charset=utf-8" });
602
+ res.end("Internal error");
603
+ }
604
+ });
605
+ server.on("error", (err) => {
606
+ reject(err);
607
+ });
608
+ server.listen(CALLBACK_PORT, CALLBACK_HOST, () => {
609
+ resolve({
610
+ server,
611
+ redirectUri: REDIRECT_URI,
612
+ cancelWait: () => {
613
+ settleWait?.(null);
614
+ },
615
+ waitForCode: () => waitForCodePromise
616
+ });
617
+ });
618
+ });
619
+ }
620
+ async function postJson(url, body, signal) {
621
+ const response = await fetch(url, {
622
+ method: "POST",
623
+ headers: {
624
+ "Content-Type": "application/json",
625
+ Accept: "application/json"
626
+ },
627
+ body: JSON.stringify(body),
628
+ signal: AbortSignal.any([signal, AbortSignal.timeout(30000)])
629
+ });
630
+ const responseBody = await response.text();
631
+ if (!response.ok) {
632
+ throw new Error(`HTTP request failed. status=${response.status}; url=${url}; body=${responseBody}`);
633
+ }
634
+ return responseBody;
635
+ }
636
+ async function exchangeAuthorizationCode(code, state, verifier, redirectUri, signal) {
637
+ let responseBody;
638
+ try {
639
+ responseBody = await postJson(TOKEN_URL, {
640
+ grant_type: "authorization_code",
641
+ client_id: CLIENT_ID,
642
+ code,
643
+ state,
644
+ redirect_uri: redirectUri,
645
+ code_verifier: verifier
646
+ }, signal);
647
+ } catch (error) {
648
+ throw new Error(`Token exchange request failed. url=${TOKEN_URL}; redirect_uri=${redirectUri}; response_type=authorization_code; details=${formatErrorDetails(error)}`);
649
+ }
650
+ let tokenData;
651
+ try {
652
+ tokenData = JSON.parse(responseBody);
653
+ } catch (error) {
654
+ throw new Error(`Token exchange returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
655
+ }
656
+ return {
657
+ type: "oauth",
658
+ refresh: tokenData.refresh_token,
659
+ access: tokenData.access_token,
660
+ expires: Date.now() + tokenData.expires_in * 1000 - 300000
661
+ };
662
+ }
663
+ async function loginAnthropic(interaction) {
664
+ const { verifier, challenge } = await generatePKCE();
665
+ const server = await startCallbackServer(verifier);
666
+ const manualAbort = new AbortController;
667
+ const onAbort = () => server.cancelWait();
668
+ interaction.signal.addEventListener("abort", onAbort, { once: true });
669
+ if (interaction.signal.aborted)
670
+ onAbort();
671
+ let code;
672
+ let state;
673
+ let manualInput;
674
+ let manualError;
675
+ try {
676
+ const authParams = new URLSearchParams({
677
+ code: "true",
678
+ client_id: CLIENT_ID,
679
+ response_type: "code",
680
+ redirect_uri: REDIRECT_URI,
681
+ scope: SCOPES,
682
+ code_challenge: challenge,
683
+ code_challenge_method: "S256",
684
+ state: verifier
685
+ });
686
+ interaction.notify({
687
+ type: "auth_url",
688
+ url: `${AUTHORIZE_URL}?${authParams.toString()}`,
689
+ instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
690
+ });
691
+ const manualPromise = interaction.prompt({
692
+ type: "manual_code",
693
+ message: "Complete login in your browser, or paste the authorization code / redirect URL here:",
694
+ placeholder: REDIRECT_URI,
695
+ signal: manualAbort.signal
696
+ }).then((input) => {
697
+ manualInput = input;
698
+ server.cancelWait();
699
+ }).catch((error) => {
700
+ manualError = error instanceof Error ? error : new Error(String(error));
701
+ server.cancelWait();
702
+ });
703
+ const result = await server.waitForCode();
704
+ if (manualError)
705
+ throw manualError;
706
+ if (result?.code) {
707
+ code = result.code;
708
+ state = result.state;
709
+ } else if (manualInput) {
710
+ const parsed = parseAuthorizationInput(manualInput);
711
+ if (parsed.state && parsed.state !== verifier)
712
+ throw new Error("OAuth state mismatch");
713
+ code = parsed.code;
714
+ state = parsed.state ?? verifier;
715
+ }
716
+ if (!code) {
717
+ await manualPromise;
718
+ if (manualError)
719
+ throw manualError;
720
+ if (manualInput) {
721
+ const parsed = parseAuthorizationInput(manualInput);
722
+ if (parsed.state && parsed.state !== verifier)
723
+ throw new Error("OAuth state mismatch");
724
+ code = parsed.code;
725
+ state = parsed.state ?? verifier;
726
+ }
727
+ }
728
+ if (!code)
729
+ throw new Error("Missing authorization code");
730
+ if (!state)
731
+ throw new Error("Missing OAuth state");
732
+ interaction.notify({ type: "progress", message: "Exchanging authorization code for tokens..." });
733
+ return exchangeAuthorizationCode(code, state, verifier, REDIRECT_URI, interaction.signal);
734
+ } finally {
735
+ interaction.signal.removeEventListener("abort", onAbort);
736
+ manualAbort.abort();
737
+ server.server.close();
738
+ }
739
+ }
740
+ async function refreshAnthropicToken(refreshToken, signal) {
741
+ let responseBody;
742
+ try {
743
+ responseBody = await postJson(TOKEN_URL, {
744
+ grant_type: "refresh_token",
745
+ client_id: CLIENT_ID,
746
+ refresh_token: refreshToken
747
+ }, signal);
748
+ } catch (error) {
749
+ throw new Error(`Anthropic token refresh request failed. url=${TOKEN_URL}; details=${formatErrorDetails(error)}`);
750
+ }
751
+ let data;
752
+ try {
753
+ data = JSON.parse(responseBody);
754
+ } catch (error) {
755
+ throw new Error(`Anthropic token refresh returned invalid JSON. url=${TOKEN_URL}; body=${responseBody}; details=${formatErrorDetails(error)}`);
756
+ }
757
+ return {
758
+ type: "oauth",
759
+ refresh: data.refresh_token,
760
+ access: data.access_token,
761
+ expires: Date.now() + data.expires_in * 1000 - 300000
762
+ };
763
+ }
764
+ var anthropicOAuth = {
765
+ name: "Anthropic (Claude Pro/Max)",
766
+ isSubscription: true,
767
+ login: loginAnthropic,
768
+ refresh: (credential, signal) => refreshAnthropicToken(credential.refresh, signal),
769
+ async toAuth(credential) {
770
+ return { apiKey: credential.access };
771
+ }
772
+ };
773
+
774
+ // node_modules/@earendil-works/pi-ai/dist/utils/sleep.js
775
+ function sleep(ms, signal) {
776
+ return new Promise((resolve, reject) => {
777
+ signal.throwIfAborted();
778
+ const onAbort = () => {
779
+ clearTimeout(timeout);
780
+ reject(signal.reason);
781
+ };
782
+ const timeout = setTimeout(() => {
783
+ signal.removeEventListener("abort", onAbort);
784
+ resolve();
785
+ }, ms);
786
+ signal.addEventListener("abort", onAbort, { once: true });
787
+ });
788
+ }
789
+
790
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/device-code.js
791
+ var CANCEL_MESSAGE = "Login cancelled";
792
+ var TIMEOUT_MESSAGE = "Device flow timed out";
793
+ var SLOW_DOWN_TIMEOUT_MESSAGE = "Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";
794
+ var MINIMUM_INTERVAL_MS = 1000;
795
+ var DEFAULT_POLL_INTERVAL_SECONDS = 5;
796
+ var SLOW_DOWN_INTERVAL_INCREMENT_MS = 5000;
797
+ function abortableSleep(ms, signal, cancelMessage) {
798
+ return new Promise((resolve, reject) => {
799
+ if (signal.aborted) {
800
+ reject(new Error(cancelMessage));
801
+ return;
802
+ }
803
+ const onAbort = () => {
804
+ clearTimeout(timeout);
805
+ reject(new Error(cancelMessage));
806
+ };
807
+ const timeout = setTimeout(() => {
808
+ signal.removeEventListener("abort", onAbort);
809
+ resolve();
810
+ }, ms);
811
+ signal.addEventListener("abort", onAbort, { once: true });
812
+ });
813
+ }
814
+ async function pollOAuthDeviceCodeFlow(options) {
815
+ const deadline = typeof options.expiresInSeconds === "number" ? Date.now() + options.expiresInSeconds * 1000 : Number.POSITIVE_INFINITY;
816
+ let intervalMs = Math.max(MINIMUM_INTERVAL_MS, Math.floor((options.intervalSeconds ?? DEFAULT_POLL_INTERVAL_SECONDS) * 1000));
817
+ let slowDownResponses = 0;
818
+ if (options.waitBeforeFirstPoll) {
819
+ const remainingMs = deadline - Date.now();
820
+ if (remainingMs > 0) {
821
+ await abortableSleep(Math.min(intervalMs, remainingMs), options.signal, CANCEL_MESSAGE);
822
+ }
823
+ }
824
+ while (Date.now() < deadline) {
825
+ if (options.signal.aborted) {
826
+ throw new Error(CANCEL_MESSAGE);
827
+ }
828
+ const result = await options.poll();
829
+ if (result.status === "complete") {
830
+ return result.value;
831
+ }
832
+ if (result.status === "failed") {
833
+ throw new Error(result.message);
834
+ }
835
+ if (result.status === "slow_down") {
836
+ slowDownResponses += 1;
837
+ intervalMs = typeof result.intervalSeconds === "number" && Number.isFinite(result.intervalSeconds) && result.intervalSeconds > 0 ? Math.max(MINIMUM_INTERVAL_MS, Math.floor(result.intervalSeconds * 1000)) : Math.max(MINIMUM_INTERVAL_MS, intervalMs + SLOW_DOWN_INTERVAL_INCREMENT_MS);
838
+ }
839
+ const remainingMs = deadline - Date.now();
840
+ if (remainingMs <= 0) {
841
+ break;
842
+ }
843
+ await abortableSleep(Math.min(intervalMs, remainingMs), options.signal, CANCEL_MESSAGE);
844
+ }
845
+ throw new Error(slowDownResponses > 0 ? SLOW_DOWN_TIMEOUT_MESSAGE : TIMEOUT_MESSAGE);
846
+ }
847
+
848
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js
849
+ var decode2 = (s) => atob(s);
850
+ var CLIENT_ID2 = decode2("SXYxLmI1MDdhMDhjODdlY2ZlOTg=");
851
+ var COPILOT_HEADERS = {
852
+ "User-Agent": "GitHubCopilotChat/0.35.0",
853
+ "Editor-Version": "vscode/1.107.0",
854
+ "Editor-Plugin-Version": "copilot-chat/0.35.0",
855
+ "Copilot-Integration-Id": "vscode-chat"
856
+ };
857
+ var COPILOT_API_VERSION = "2026-06-01";
858
+ function normalizeDomain(input) {
859
+ const trimmed = input.trim();
860
+ if (!trimmed)
861
+ return null;
862
+ try {
863
+ const url = trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`);
864
+ return url.hostname;
865
+ } catch {
866
+ return null;
867
+ }
868
+ }
869
+ function getUrls(domain) {
870
+ return {
871
+ deviceCodeUrl: `https://${domain}/login/device/code`,
872
+ accessTokenUrl: `https://${domain}/login/oauth/access_token`,
873
+ copilotTokenUrl: `https://api.${domain}/copilot_internal/v2/token`
874
+ };
875
+ }
876
+ function getBaseUrlFromToken(token) {
877
+ const match = token.match(/proxy-ep=([^;]+)/);
878
+ if (!match)
879
+ return null;
880
+ const proxyHost = match[1];
881
+ const apiHost = proxyHost.replace(/^proxy\./, "api.");
882
+ return `https://${apiHost}`;
883
+ }
884
+ function getGitHubCopilotBaseUrl(token, enterpriseDomain) {
885
+ if (token) {
886
+ const urlFromToken = getBaseUrlFromToken(token);
887
+ if (urlFromToken)
888
+ return urlFromToken;
889
+ }
890
+ if (enterpriseDomain)
891
+ return `https://copilot-api.${enterpriseDomain}`;
892
+ return "https://api.individual.githubcopilot.com";
893
+ }
894
+ function asRecord(value) {
895
+ return value && typeof value === "object" ? value : undefined;
896
+ }
897
+ function parseGitHubCopilotModelCatalog(raw, allowPolicyFallback) {
898
+ const data = asRecord(raw)?.data;
899
+ if (!Array.isArray(data)) {
900
+ throw new Error("Invalid Copilot models response");
901
+ }
902
+ const accountModels = data.flatMap((rawItem) => {
903
+ const item = asRecord(rawItem);
904
+ const id = item?.id;
905
+ if (!item || typeof id !== "string")
906
+ return [];
907
+ const capabilities = asRecord(item.capabilities);
908
+ const supports = asRecord(capabilities?.supports);
909
+ if (supports?.tool_calls === false)
910
+ return [];
911
+ return [
912
+ {
913
+ id,
914
+ pickerEnabled: item.model_picker_enabled === true,
915
+ policyState: asRecord(item.policy)?.state
916
+ }
917
+ ];
918
+ });
919
+ const pickerModelIds = accountModels.filter((model) => model.pickerEnabled && model.policyState !== "disabled").map((model) => model.id);
920
+ const usePolicyFallback = allowPolicyFallback && pickerModelIds.length === 0;
921
+ const availableModelIds = pickerModelIds.length > 0 || !allowPolicyFallback ? pickerModelIds : accountModels.filter((model) => model.policyState === "enabled").map((model) => model.id);
922
+ const policyModelIds = accountModels.filter((model) => model.policyState === "unconfigured" && Object.hasOwn(GITHUB_COPILOT_MODELS, model.id) && (model.pickerEnabled || usePolicyFallback)).map((model) => model.id);
923
+ return { availableModelIds, policyModelIds };
924
+ }
925
+ async function fetchWithRateLimitRetry(url, init, signal, retryPolicy) {
926
+ const retryBudgetSignal = retryPolicy.maxRetries > 0 && retryPolicy.maxElapsedMs > 0 ? AbortSignal.timeout(retryPolicy.maxElapsedMs) : undefined;
927
+ const requestSignal = retryBudgetSignal ? AbortSignal.any([signal, retryBudgetSignal]) : signal;
928
+ const retryDeadline = retryBudgetSignal ? Date.now() + retryPolicy.maxElapsedMs : undefined;
929
+ for (let retry = 0;; retry++) {
930
+ const response = await fetch(url, {
931
+ ...init,
932
+ signal: AbortSignal.any([requestSignal, AbortSignal.timeout(5000)])
933
+ });
934
+ if (response.status !== 429 || retry === retryPolicy.maxRetries)
935
+ return response;
936
+ const retryAfter = response.headers.get("retry-after");
937
+ let delayMs = 500 * 2 ** retry;
938
+ if (retryAfter) {
939
+ const seconds = Number.parseFloat(retryAfter);
940
+ delayMs = Number.isNaN(seconds) ? Date.parse(retryAfter) - Date.now() : seconds * 1000;
941
+ if (!Number.isFinite(delayMs))
942
+ return response;
943
+ }
944
+ delayMs = Math.max(0, delayMs);
945
+ if (retryDeadline !== undefined && delayMs >= retryDeadline - Date.now())
946
+ return response;
947
+ await response.body?.cancel();
948
+ await sleep(delayMs, requestSignal);
949
+ }
950
+ }
951
+ async function fetchGitHubCopilotModels(copilotToken, enterpriseDomain, signal, retryPolicy) {
952
+ const baseUrl = getGitHubCopilotBaseUrl(copilotToken, enterpriseDomain);
953
+ const allowPolicyFallback = baseUrl === "https://api.individual.githubcopilot.com";
954
+ const response = await fetchWithRateLimitRetry(`${baseUrl}/models`, {
955
+ headers: {
956
+ Accept: "application/json",
957
+ Authorization: `Bearer ${copilotToken}`,
958
+ ...COPILOT_HEADERS,
959
+ "X-GitHub-Api-Version": COPILOT_API_VERSION
960
+ }
961
+ }, signal, retryPolicy);
962
+ if (!response.ok) {
963
+ throw new Error(`${response.status} ${response.statusText}: ${await response.text()}`);
964
+ }
965
+ return parseGitHubCopilotModelCatalog(await response.json(), allowPolicyFallback);
966
+ }
967
+ async function fetchJson(url, init) {
968
+ const response = await fetch(url, init);
969
+ if (!response.ok) {
970
+ const text = await response.text();
971
+ throw new Error(`${response.status} ${response.statusText}: ${text}`);
972
+ }
973
+ return response.json();
974
+ }
975
+ async function startDeviceFlow(domain, signal) {
976
+ const urls = getUrls(domain);
977
+ const data = await fetchJson(urls.deviceCodeUrl, {
978
+ method: "POST",
979
+ headers: {
980
+ Accept: "application/json",
981
+ "Content-Type": "application/x-www-form-urlencoded",
982
+ "User-Agent": "GitHubCopilotChat/0.35.0"
983
+ },
984
+ body: new URLSearchParams({
985
+ client_id: CLIENT_ID2,
986
+ scope: "read:user"
987
+ }),
988
+ signal
989
+ });
990
+ if (!data || typeof data !== "object") {
991
+ throw new Error("Invalid device code response");
992
+ }
993
+ const deviceCode = data.device_code;
994
+ const userCode = data.user_code;
995
+ const verificationUri = data.verification_uri;
996
+ const interval = data.interval;
997
+ const expiresIn = data.expires_in;
998
+ if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string" || interval !== undefined && typeof interval !== "number" || typeof expiresIn !== "number") {
999
+ throw new Error("Invalid device code response fields");
1000
+ }
1001
+ let parsedUri;
1002
+ try {
1003
+ parsedUri = new URL(verificationUri);
1004
+ } catch {
1005
+ throw new Error("Untrusted verification_uri in device code response");
1006
+ }
1007
+ if (parsedUri.protocol !== "https:" && parsedUri.protocol !== "http:") {
1008
+ throw new Error("Untrusted verification_uri in device code response");
1009
+ }
1010
+ return {
1011
+ device_code: deviceCode,
1012
+ user_code: userCode,
1013
+ verification_uri: parsedUri.href,
1014
+ interval,
1015
+ expires_in: expiresIn
1016
+ };
1017
+ }
1018
+ async function pollForGitHubAccessToken(domain, device, signal) {
1019
+ const urls = getUrls(domain);
1020
+ return pollOAuthDeviceCodeFlow({
1021
+ intervalSeconds: device.interval,
1022
+ expiresInSeconds: device.expires_in,
1023
+ waitBeforeFirstPoll: true,
1024
+ signal,
1025
+ poll: async () => {
1026
+ const raw = await fetchJson(urls.accessTokenUrl, {
1027
+ method: "POST",
1028
+ headers: {
1029
+ Accept: "application/json",
1030
+ "Content-Type": "application/x-www-form-urlencoded",
1031
+ "User-Agent": "GitHubCopilotChat/0.35.0"
1032
+ },
1033
+ body: new URLSearchParams({
1034
+ client_id: CLIENT_ID2,
1035
+ device_code: device.device_code,
1036
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
1037
+ }),
1038
+ signal
1039
+ });
1040
+ if (raw && typeof raw === "object" && typeof raw.access_token === "string") {
1041
+ return { status: "complete", value: raw.access_token };
1042
+ }
1043
+ if (raw && typeof raw === "object" && typeof raw.error === "string") {
1044
+ const { error, error_description: description, interval } = raw;
1045
+ if (error === "authorization_pending") {
1046
+ return { status: "pending" };
1047
+ }
1048
+ if (error === "slow_down") {
1049
+ return { status: "slow_down", intervalSeconds: typeof interval === "number" ? interval : undefined };
1050
+ }
1051
+ const descriptionSuffix = description ? `: ${description}` : "";
1052
+ return { status: "failed", message: `Device flow failed: ${error}${descriptionSuffix}` };
1053
+ }
1054
+ return { status: "failed", message: "Invalid device token response" };
1055
+ }
1056
+ });
1057
+ }
1058
+ async function refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal) {
1059
+ const domain = enterpriseDomain || "github.com";
1060
+ const urls = getUrls(domain);
1061
+ const raw = await fetchJson(urls.copilotTokenUrl, {
1062
+ headers: {
1063
+ Accept: "application/json",
1064
+ Authorization: `Bearer ${refreshToken}`,
1065
+ ...COPILOT_HEADERS
1066
+ },
1067
+ signal
1068
+ });
1069
+ if (!raw || typeof raw !== "object") {
1070
+ throw new Error("Invalid Copilot token response");
1071
+ }
1072
+ const token = raw.token;
1073
+ const expiresAt = raw.expires_at;
1074
+ if (typeof token !== "string" || typeof expiresAt !== "number") {
1075
+ throw new Error("Invalid Copilot token response fields");
1076
+ }
1077
+ return {
1078
+ type: "oauth",
1079
+ refresh: refreshToken,
1080
+ access: token,
1081
+ expires: expiresAt * 1000 - 5 * 60 * 1000,
1082
+ enterpriseUrl: enterpriseDomain
1083
+ };
1084
+ }
1085
+ async function refreshGitHubCopilotToken(refreshToken, enterpriseDomain, signal) {
1086
+ const credentials = await refreshGitHubCopilotAccessToken(refreshToken, enterpriseDomain, signal);
1087
+ const { availableModelIds } = await fetchGitHubCopilotModels(credentials.access, enterpriseDomain, signal, {
1088
+ maxRetries: 0,
1089
+ maxElapsedMs: 0
1090
+ });
1091
+ return {
1092
+ ...credentials,
1093
+ availableModelIds
1094
+ };
1095
+ }
1096
+ async function enableGitHubCopilotModel(token, modelId, enterpriseDomain, signal) {
1097
+ const baseUrl = getGitHubCopilotBaseUrl(token, enterpriseDomain);
1098
+ const url = `${baseUrl}/models/${modelId}/policy`;
1099
+ let response;
1100
+ try {
1101
+ response = await fetchWithRateLimitRetry(url, {
1102
+ method: "POST",
1103
+ headers: {
1104
+ "Content-Type": "application/json",
1105
+ Authorization: `Bearer ${token}`,
1106
+ ...COPILOT_HEADERS,
1107
+ "openai-intent": "chat-policy",
1108
+ "x-interaction-type": "chat-policy"
1109
+ },
1110
+ body: JSON.stringify({ state: "enabled" })
1111
+ }, signal, { maxRetries: 2, maxElapsedMs: 5000 });
1112
+ } catch (error) {
1113
+ if (signal.aborted)
1114
+ throw error;
1115
+ return false;
1116
+ }
1117
+ if (response.status === 429) {
1118
+ throw new Error(`${response.status} ${response.statusText}: ${await response.text()}`);
1119
+ }
1120
+ return response.ok;
1121
+ }
1122
+ async function enableGitHubCopilotModels(token, modelIds, enterpriseDomain, signal) {
1123
+ const enabledModelIds = [];
1124
+ for (const modelId of modelIds) {
1125
+ try {
1126
+ if (await enableGitHubCopilotModel(token, modelId, enterpriseDomain, signal)) {
1127
+ enabledModelIds.push(modelId);
1128
+ }
1129
+ } catch (error) {
1130
+ if (signal.aborted)
1131
+ throw error;
1132
+ break;
1133
+ }
1134
+ }
1135
+ return enabledModelIds;
1136
+ }
1137
+ async function loginGitHubCopilot(interaction) {
1138
+ const input = await interaction.prompt({
1139
+ type: "text",
1140
+ message: "GitHub Enterprise URL/domain (blank for github.com)",
1141
+ placeholder: "company.ghe.com"
1142
+ });
1143
+ if (interaction.signal.aborted)
1144
+ throw new Error("Login cancelled");
1145
+ const trimmed = input.trim();
1146
+ const enterpriseDomain = normalizeDomain(input);
1147
+ if (trimmed && !enterpriseDomain)
1148
+ throw new Error("Invalid GitHub Enterprise URL/domain");
1149
+ const domain = enterpriseDomain || "github.com";
1150
+ const device = await startDeviceFlow(domain, interaction.signal);
1151
+ interaction.notify({
1152
+ type: "device_code",
1153
+ userCode: device.user_code,
1154
+ verificationUri: device.verification_uri,
1155
+ intervalSeconds: device.interval,
1156
+ expiresInSeconds: device.expires_in
1157
+ });
1158
+ const githubAccessToken = await pollForGitHubAccessToken(domain, device, interaction.signal);
1159
+ const credentials = await refreshGitHubCopilotAccessToken(githubAccessToken, enterpriseDomain ?? undefined, interaction.signal);
1160
+ const models = await fetchGitHubCopilotModels(credentials.access, enterpriseDomain ?? undefined, interaction.signal, {
1161
+ maxRetries: 2,
1162
+ maxElapsedMs: 5000
1163
+ });
1164
+ let enabledModelIds = [];
1165
+ if (models.policyModelIds.length > 0) {
1166
+ interaction.notify({ type: "progress", message: "Enabling models..." });
1167
+ enabledModelIds = await enableGitHubCopilotModels(credentials.access, models.policyModelIds, enterpriseDomain ?? undefined, interaction.signal);
1168
+ }
1169
+ return {
1170
+ ...credentials,
1171
+ availableModelIds: [...new Set([...models.availableModelIds, ...enabledModelIds])]
1172
+ };
1173
+ }
1174
+ function copilotEnterpriseDomain(credential) {
1175
+ const enterpriseUrl = credential.enterpriseUrl;
1176
+ if (typeof enterpriseUrl !== "string" || !enterpriseUrl)
1177
+ return;
1178
+ return normalizeDomain(enterpriseUrl) ?? undefined;
1179
+ }
1180
+ var githubCopilotOAuth = {
1181
+ name: "GitHub Copilot",
1182
+ isSubscription: true,
1183
+ login: loginGitHubCopilot,
1184
+ refresh: (credential, signal) => refreshGitHubCopilotToken(credential.refresh, copilotEnterpriseDomain(credential), signal),
1185
+ async toAuth(credential) {
1186
+ return {
1187
+ apiKey: credential.access,
1188
+ baseUrl: getGitHubCopilotBaseUrl(credential.access, copilotEnterpriseDomain(credential))
1189
+ };
1190
+ }
1191
+ };
1192
+
1193
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js
1194
+ var CLIENT_ID3 = "17e5f671-d194-4dfb-9706-5516cb48c098";
1195
+ var DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
1196
+ var DEVICE_CODE_TIMEOUT_SECONDS = 15 * 60;
1197
+ var DEFAULT_POLL_INTERVAL_SECONDS2 = 5;
1198
+ var REQUEST_TIMEOUT_MS = 30 * 1000;
1199
+ var REFRESH_MAX_RETRIES = 3;
1200
+ function getOauthHost() {
1201
+ const override = getProviderEnvValue("KIMI_CODE_OAUTH_HOST") || getProviderEnvValue("KIMI_OAUTH_HOST");
1202
+ return (override || DEFAULT_OAUTH_HOST).replace(/\/+$/, "");
1203
+ }
1204
+ function requestSignal(signal) {
1205
+ return AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal]);
1206
+ }
1207
+ function formUrlEncode(fields) {
1208
+ return new URLSearchParams(fields).toString();
1209
+ }
1210
+ async function readJson(response) {
1211
+ try {
1212
+ const json = await response.json();
1213
+ return json && typeof json === "object" ? json : null;
1214
+ } catch {
1215
+ return null;
1216
+ }
1217
+ }
1218
+ function trustedHttpUrl(value) {
1219
+ if (typeof value !== "string" || !value)
1220
+ return null;
1221
+ try {
1222
+ const url = new URL(value);
1223
+ if (url.protocol !== "https:" && url.protocol !== "http:")
1224
+ return null;
1225
+ return url.href;
1226
+ } catch {
1227
+ return null;
1228
+ }
1229
+ }
1230
+ async function startDeviceAuthorization(oauthHost, signal) {
1231
+ const response = await fetch(`${oauthHost}/api/oauth/device_authorization`, {
1232
+ method: "POST",
1233
+ headers: {
1234
+ "Content-Type": "application/x-www-form-urlencoded",
1235
+ Accept: "application/json"
1236
+ },
1237
+ body: formUrlEncode({ client_id: CLIENT_ID3 }),
1238
+ signal: requestSignal(signal)
1239
+ });
1240
+ if (!response.ok) {
1241
+ const text = await response.text().catch(() => "");
1242
+ throw new Error(`Kimi Code device authorization failed with status ${response.status}${text ? `: ${text}` : ""}`);
1243
+ }
1244
+ const json = await readJson(response);
1245
+ const deviceCode = json?.device_code;
1246
+ const userCode = json?.user_code;
1247
+ const verificationUri = json?.verification_uri;
1248
+ const verificationUriComplete = json?.verification_uri_complete;
1249
+ if (typeof deviceCode !== "string" || typeof userCode !== "string" || typeof verificationUri !== "string" || typeof verificationUriComplete !== "string" || !trustedHttpUrl(verificationUriComplete) || !trustedHttpUrl(verificationUri)) {
1250
+ throw new Error(`Invalid Kimi Code device authorization response: ${JSON.stringify(json)}`);
1251
+ }
1252
+ const interval = json?.interval;
1253
+ const expiresIn = json?.expires_in;
1254
+ return {
1255
+ deviceCode,
1256
+ userCode,
1257
+ verificationUri,
1258
+ verificationUriComplete,
1259
+ intervalSeconds: typeof interval === "number" && Number.isFinite(interval) && interval > 0 ? interval : DEFAULT_POLL_INTERVAL_SECONDS2,
1260
+ expiresInSeconds: typeof expiresIn === "number" && Number.isFinite(expiresIn) && expiresIn > 0 ? expiresIn : DEVICE_CODE_TIMEOUT_SECONDS
1261
+ };
1262
+ }
1263
+ function parseTokenResponse(json, operation) {
1264
+ const accessToken = json?.access_token;
1265
+ const refreshToken = json?.refresh_token;
1266
+ const expiresIn = json?.expires_in;
1267
+ if (typeof accessToken !== "string" || !accessToken || typeof refreshToken !== "string" || !refreshToken || typeof expiresIn !== "number" || !Number.isFinite(expiresIn) || expiresIn <= 0) {
1268
+ throw new Error(`Kimi Code token ${operation} response missing fields: ${JSON.stringify(json)}`);
1269
+ }
1270
+ return {
1271
+ access: accessToken,
1272
+ refresh: refreshToken,
1273
+ expires: Date.now() + expiresIn * 1000
1274
+ };
1275
+ }
1276
+ async function pollForToken(oauthHost, device, signal) {
1277
+ return pollOAuthDeviceCodeFlow({
1278
+ intervalSeconds: device.intervalSeconds,
1279
+ expiresInSeconds: device.expiresInSeconds,
1280
+ waitBeforeFirstPoll: true,
1281
+ signal,
1282
+ poll: async () => {
1283
+ const response = await fetch(`${oauthHost}/api/oauth/token`, {
1284
+ method: "POST",
1285
+ headers: {
1286
+ "Content-Type": "application/x-www-form-urlencoded",
1287
+ Accept: "application/json"
1288
+ },
1289
+ body: formUrlEncode({
1290
+ client_id: CLIENT_ID3,
1291
+ device_code: device.deviceCode,
1292
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
1293
+ }),
1294
+ signal: requestSignal(signal)
1295
+ });
1296
+ if (response.status >= 500) {
1297
+ const text = await response.text().catch(() => "");
1298
+ return {
1299
+ status: "failed",
1300
+ message: `Kimi Code device token request failed with status ${response.status}${text ? `: ${text}` : ""}`
1301
+ };
1302
+ }
1303
+ const json = await readJson(response);
1304
+ if (response.ok && typeof json?.access_token === "string") {
1305
+ try {
1306
+ return { status: "complete", value: parseTokenResponse(json, "poll") };
1307
+ } catch (error2) {
1308
+ return { status: "failed", message: error2 instanceof Error ? error2.message : String(error2) };
1309
+ }
1310
+ }
1311
+ const error = json?.error;
1312
+ const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
1313
+ if (error === "authorization_pending") {
1314
+ return { status: "pending" };
1315
+ }
1316
+ if (error === "slow_down") {
1317
+ const interval = json?.interval;
1318
+ return {
1319
+ status: "slow_down",
1320
+ intervalSeconds: typeof interval === "number" && interval > 0 ? interval : undefined
1321
+ };
1322
+ }
1323
+ if (error === "expired_token") {
1324
+ return { status: "failed", message: "Kimi Code device authorization expired. Please restart login." };
1325
+ }
1326
+ if (error === "access_denied") {
1327
+ return { status: "failed", message: "Kimi Code login was denied." };
1328
+ }
1329
+ return {
1330
+ status: "failed",
1331
+ message: `Kimi Code device token request failed (status ${response.status})${typeof error === "string" ? `: ${error}${description}` : ""}`
1332
+ };
1333
+ }
1334
+ });
1335
+ }
1336
+ function isRetryableRefreshFailure(response) {
1337
+ return response.status === 429 || response.status >= 500;
1338
+ }
1339
+ async function refreshToken(oauthHost, refreshTokenValue, signal) {
1340
+ let lastError;
1341
+ for (let attempt = 0;attempt <= REFRESH_MAX_RETRIES; attempt++) {
1342
+ if (attempt > 0) {
1343
+ await sleep(1000 * 2 ** (attempt - 1), signal);
1344
+ }
1345
+ if (signal.aborted) {
1346
+ throw new Error("Kimi Code token refresh aborted");
1347
+ }
1348
+ let response;
1349
+ try {
1350
+ response = await fetch(`${oauthHost}/api/oauth/token`, {
1351
+ method: "POST",
1352
+ headers: {
1353
+ "Content-Type": "application/x-www-form-urlencoded",
1354
+ Accept: "application/json"
1355
+ },
1356
+ body: formUrlEncode({
1357
+ client_id: CLIENT_ID3,
1358
+ grant_type: "refresh_token",
1359
+ refresh_token: refreshTokenValue
1360
+ }),
1361
+ signal: requestSignal(signal)
1362
+ });
1363
+ } catch (error) {
1364
+ lastError = error instanceof Error ? error : new Error(String(error));
1365
+ continue;
1366
+ }
1367
+ const json = await readJson(response);
1368
+ if (response.ok) {
1369
+ return parseTokenResponse(json, "refresh");
1370
+ }
1371
+ if (response.status === 401 || response.status === 403 || json?.error === "invalid_grant") {
1372
+ const description = typeof json?.error_description === "string" ? `: ${json.error_description}` : "";
1373
+ throw new Error(`Kimi Code token refresh unauthorized (status ${response.status})${description}`);
1374
+ }
1375
+ if (isRetryableRefreshFailure(response) && attempt < REFRESH_MAX_RETRIES) {
1376
+ lastError = new Error(`Kimi Code token refresh failed with status ${response.status}`);
1377
+ continue;
1378
+ }
1379
+ const text = JSON.stringify(json);
1380
+ throw new Error(`Kimi Code token refresh failed with status ${response.status}${text ? `: ${text}` : ""}`);
1381
+ }
1382
+ throw lastError ?? new Error("Kimi Code token refresh failed");
1383
+ }
1384
+ async function loginKimiCoding(interaction) {
1385
+ const oauthHost = getOauthHost();
1386
+ const device = await startDeviceAuthorization(oauthHost, interaction.signal);
1387
+ interaction.notify({
1388
+ type: "device_code",
1389
+ userCode: device.userCode,
1390
+ verificationUri: device.verificationUriComplete,
1391
+ intervalSeconds: device.intervalSeconds,
1392
+ expiresInSeconds: device.expiresInSeconds
1393
+ });
1394
+ const token = await pollForToken(oauthHost, device, interaction.signal);
1395
+ return { type: "oauth", access: token.access, refresh: token.refresh, expires: token.expires };
1396
+ }
1397
+ var kimiCodingOAuth = {
1398
+ name: "Kimi Code (subscription)",
1399
+ isSubscription: true,
1400
+ loginLabel: "Sign in with Kimi Code",
1401
+ login: loginKimiCoding,
1402
+ refresh: async (credential, signal) => {
1403
+ const token = await refreshToken(getOauthHost(), credential.refresh, signal);
1404
+ return { type: "oauth", access: token.access, refresh: token.refresh, expires: token.expires };
1405
+ },
1406
+ async toAuth(credential) {
1407
+ return { headers: { Authorization: `Bearer ${credential.access}` } };
1408
+ }
1409
+ };
1410
+
1411
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/meta.js
1412
+ var CLIENT_ID4 = "1031625952748946";
1413
+ var AUTH_HOST = "https://auth.meta.com";
1414
+ var DEVICE_AUTHORIZATION_URL = `${AUTH_HOST}/oidc/device/authorization/`;
1415
+ var DEVICE_TOKEN_URL = `${AUTH_HOST}/oidc/device/token/`;
1416
+ var API_KEY_MINT_URL = "https://api.meta.ai/muse-code/key";
1417
+ var API_KEY_LIFETIME_MS = 24 * 60 * 60 * 1000;
1418
+ var REQUEST_TIMEOUT_MS2 = 30 * 1000;
1419
+ function requestSignal2(signal) {
1420
+ return AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS2), signal]);
1421
+ }
1422
+ async function readJson2(response) {
1423
+ try {
1424
+ const json = await response.json();
1425
+ return json && typeof json === "object" ? json : null;
1426
+ } catch {
1427
+ return null;
1428
+ }
1429
+ }
1430
+ function errorDetail(json) {
1431
+ for (const key of ["error_description", "detail", "message", "error"]) {
1432
+ const value = json?.[key];
1433
+ if (typeof value === "string" && value.trim())
1434
+ return `: ${value.trim()}`;
1435
+ }
1436
+ return "";
1437
+ }
1438
+ function trustedHttpUrl2(value) {
1439
+ if (typeof value !== "string" || !value)
1440
+ return null;
1441
+ try {
1442
+ const url = new URL(value);
1443
+ if (url.protocol !== "https:" && url.protocol !== "http:")
1444
+ return null;
1445
+ return url.href;
1446
+ } catch {
1447
+ return null;
1448
+ }
1449
+ }
1450
+ function positiveNumber(value) {
1451
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
1452
+ }
1453
+ async function startDeviceAuthorization2(signal) {
1454
+ const response = await fetch(DEVICE_AUTHORIZATION_URL, {
1455
+ method: "POST",
1456
+ headers: {
1457
+ "Content-Type": "application/x-www-form-urlencoded",
1458
+ Accept: "application/json"
1459
+ },
1460
+ body: new URLSearchParams({ client_id: CLIENT_ID4 }).toString(),
1461
+ signal: requestSignal2(signal)
1462
+ });
1463
+ const json = await readJson2(response);
1464
+ if (!response.ok) {
1465
+ throw new Error(`Meta device authorization failed with status ${response.status}${errorDetail(json)}`);
1466
+ }
1467
+ const deviceCode = json?.device_code;
1468
+ const userCode = json?.user_code;
1469
+ const verificationUri = trustedHttpUrl2(json?.verification_uri_complete) ?? trustedHttpUrl2(json?.verification_uri);
1470
+ if (typeof deviceCode !== "string" || !deviceCode || typeof userCode !== "string" || !userCode || !verificationUri) {
1471
+ throw new Error(`Invalid Meta device authorization response: ${JSON.stringify(json)}`);
1472
+ }
1473
+ return {
1474
+ deviceCode,
1475
+ userCode,
1476
+ verificationUri,
1477
+ intervalSeconds: positiveNumber(json?.interval),
1478
+ expiresInSeconds: positiveNumber(json?.expires_in)
1479
+ };
1480
+ }
1481
+ async function pollForIdentityToken(device, signal) {
1482
+ return pollOAuthDeviceCodeFlow({
1483
+ intervalSeconds: device.intervalSeconds,
1484
+ expiresInSeconds: device.expiresInSeconds,
1485
+ waitBeforeFirstPoll: true,
1486
+ signal,
1487
+ poll: async () => {
1488
+ const response = await fetch(DEVICE_TOKEN_URL, {
1489
+ method: "POST",
1490
+ headers: {
1491
+ "Content-Type": "application/x-www-form-urlencoded",
1492
+ Accept: "application/json"
1493
+ },
1494
+ body: new URLSearchParams({
1495
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
1496
+ device_code: device.deviceCode,
1497
+ client_id: CLIENT_ID4
1498
+ }).toString(),
1499
+ signal: requestSignal2(signal)
1500
+ });
1501
+ const json = await readJson2(response);
1502
+ if (response.ok && typeof json?.access_token === "string" && json.access_token) {
1503
+ return { status: "complete", value: json.access_token };
1504
+ }
1505
+ switch (json?.error) {
1506
+ case "authorization_pending":
1507
+ return { status: "pending" };
1508
+ case "slow_down":
1509
+ return { status: "slow_down", intervalSeconds: positiveNumber(json?.interval) };
1510
+ case "access_denied":
1511
+ return { status: "failed", message: "Meta login was denied." };
1512
+ case "expired_token":
1513
+ return { status: "failed", message: "Meta device authorization expired. Please restart login." };
1514
+ default:
1515
+ return {
1516
+ status: "failed",
1517
+ message: `Meta device token request failed with status ${response.status}${errorDetail(json)}`
1518
+ };
1519
+ }
1520
+ }
1521
+ });
1522
+ }
1523
+ async function mintApiKey(identityToken, signal) {
1524
+ const response = await fetch(API_KEY_MINT_URL, {
1525
+ method: "POST",
1526
+ headers: {
1527
+ Accept: "application/json",
1528
+ Authorization: `Bearer ${identityToken}`,
1529
+ "Content-Type": "application/json",
1530
+ "x-api-version": "1.0.0"
1531
+ },
1532
+ body: "{}",
1533
+ signal: requestSignal2(signal)
1534
+ });
1535
+ const json = await readJson2(response);
1536
+ if (response.status === 401 || response.status === 403) {
1537
+ throw new Error(`Meta session expired (status ${response.status}). Run \`/login meta\` to sign in again.${errorDetail(json)}`);
1538
+ }
1539
+ if (!response.ok) {
1540
+ throw new Error(`Meta API key mint failed with status ${response.status}${errorDetail(json)}`);
1541
+ }
1542
+ const apiKey = json?.api_key;
1543
+ if (typeof apiKey !== "string" || !apiKey) {
1544
+ const actionUrl = trustedHttpUrl2(json?.action_url);
1545
+ throw new Error(`Meta did not issue an API key.${actionUrl ? ` Complete setup at ${actionUrl}` : ""}`);
1546
+ }
1547
+ return { type: "oauth", refresh: identityToken, access: apiKey, expires: Date.now() + API_KEY_LIFETIME_MS };
1548
+ }
1549
+ async function loginMeta(interaction) {
1550
+ try {
1551
+ const device = await startDeviceAuthorization2(interaction.signal);
1552
+ interaction.notify({
1553
+ type: "device_code",
1554
+ userCode: device.userCode,
1555
+ verificationUri: device.verificationUri,
1556
+ intervalSeconds: device.intervalSeconds,
1557
+ expiresInSeconds: device.expiresInSeconds
1558
+ });
1559
+ const identityToken = await pollForIdentityToken(device, interaction.signal);
1560
+ interaction.notify({ type: "progress", message: "Enabling Meta Model API access..." });
1561
+ return await mintApiKey(identityToken, interaction.signal);
1562
+ } catch (e) {
1563
+ if (interaction.signal.aborted)
1564
+ throw new Error("Login cancelled");
1565
+ throw e;
1566
+ }
1567
+ }
1568
+ var metaOAuth = {
1569
+ name: "Meta (Muse subscription)",
1570
+ isSubscription: true,
1571
+ loginLabel: "Sign in with Meta",
1572
+ login: loginMeta,
1573
+ refresh: (credential, signal) => mintApiKey(credential.refresh, signal),
1574
+ async toAuth(credential) {
1575
+ return { apiKey: credential.access };
1576
+ }
1577
+ };
1578
+
1579
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/openai-codex.js
1580
+ var _randomBytes = null;
1581
+ var _http = null;
1582
+ if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
1583
+ import("node:crypto").then((m) => {
1584
+ _randomBytes = m.randomBytes;
1585
+ });
1586
+ import("node:http").then((m) => {
1587
+ _http = m;
1588
+ });
1589
+ }
1590
+ var CLIENT_ID5 = "app_EMoamEEZ73f0CkXaXp7hrann";
1591
+ var AUTH_BASE_URL = "https://auth.openai.com";
1592
+ var AUTHORIZE_URL2 = `${AUTH_BASE_URL}/oauth/authorize`;
1593
+ var TOKEN_URL2 = `${AUTH_BASE_URL}/oauth/token`;
1594
+ var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
1595
+ var DEVICE_USER_CODE_URL = `${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`;
1596
+ var DEVICE_TOKEN_URL2 = `${AUTH_BASE_URL}/api/accounts/deviceauth/token`;
1597
+ var DEVICE_VERIFICATION_URI = `${AUTH_BASE_URL}/codex/device`;
1598
+ var DEVICE_REDIRECT_URI = `${AUTH_BASE_URL}/deviceauth/callback`;
1599
+ var DEVICE_CODE_TIMEOUT_SECONDS2 = 900;
1600
+ var OPENAI_CODEX_BROWSER_LOGIN_METHOD = "browser";
1601
+ var OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = "device_code";
1602
+ var SCOPE = "openid profile email offline_access";
1603
+ var JWT_CLAIM_PATH = "https://api.openai.com/auth";
1604
+ function getCallbackHost() {
1605
+ return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
1606
+ }
1607
+ function createState() {
1608
+ if (!_randomBytes) {
1609
+ throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
1610
+ }
1611
+ return _randomBytes(16).toString("hex");
1612
+ }
1613
+ function parseAuthorizationInput2(input) {
1614
+ const value = input.trim();
1615
+ if (!value)
1616
+ return {};
1617
+ try {
1618
+ const url = new URL(value);
1619
+ return {
1620
+ code: url.searchParams.get("code") ?? undefined,
1621
+ state: url.searchParams.get("state") ?? undefined
1622
+ };
1623
+ } catch {}
1624
+ if (value.includes("#")) {
1625
+ const [code, state] = value.split("#", 2);
1626
+ return { code, state };
1627
+ }
1628
+ if (value.includes("code=")) {
1629
+ const params = new URLSearchParams(value);
1630
+ return {
1631
+ code: params.get("code") ?? undefined,
1632
+ state: params.get("state") ?? undefined
1633
+ };
1634
+ }
1635
+ return { code: value };
1636
+ }
1637
+ function decodeJwt(token) {
1638
+ try {
1639
+ const parts = token.split(".");
1640
+ if (parts.length !== 3)
1641
+ return null;
1642
+ const payload = parts[1] ?? "";
1643
+ const decoded = atob(payload);
1644
+ return JSON.parse(decoded);
1645
+ } catch {
1646
+ return null;
1647
+ }
1648
+ }
1649
+ async function fetchWithLoginCancellation(input, init) {
1650
+ try {
1651
+ return await fetch(input, init);
1652
+ } catch (error) {
1653
+ if (init.signal?.aborted) {
1654
+ throw new Error("Login cancelled");
1655
+ }
1656
+ throw error;
1657
+ }
1658
+ }
1659
+ async function readTokenResponse(response, operation) {
1660
+ if (!response.ok) {
1661
+ const text = await response.text().catch(() => "");
1662
+ throw new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text || response.statusText}`);
1663
+ }
1664
+ const rawJson = await response.json();
1665
+ const json = rawJson;
1666
+ if (!json?.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
1667
+ throw new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);
1668
+ }
1669
+ return {
1670
+ access: json.access_token,
1671
+ refresh: json.refresh_token,
1672
+ expires: Date.now() + json.expires_in * 1000
1673
+ };
1674
+ }
1675
+ async function exchangeAuthorizationCode2(code, verifier, redirectUri, signal) {
1676
+ const response = await fetchWithLoginCancellation(TOKEN_URL2, {
1677
+ method: "POST",
1678
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1679
+ body: new URLSearchParams({
1680
+ grant_type: "authorization_code",
1681
+ client_id: CLIENT_ID5,
1682
+ code,
1683
+ code_verifier: verifier,
1684
+ redirect_uri: redirectUri
1685
+ }),
1686
+ signal
1687
+ });
1688
+ return readTokenResponse(response, "exchange");
1689
+ }
1690
+ async function refreshAccessToken(refreshToken2, signal) {
1691
+ let response;
1692
+ try {
1693
+ response = await fetch(TOKEN_URL2, {
1694
+ method: "POST",
1695
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1696
+ body: new URLSearchParams({
1697
+ grant_type: "refresh_token",
1698
+ refresh_token: refreshToken2,
1699
+ client_id: CLIENT_ID5
1700
+ }),
1701
+ signal
1702
+ });
1703
+ } catch (error) {
1704
+ throw new Error(`OpenAI Codex token refresh error: ${error instanceof Error ? error.message : String(error)}`);
1705
+ }
1706
+ return readTokenResponse(response, "refresh");
1707
+ }
1708
+ async function startOpenAICodexDeviceAuth(signal) {
1709
+ const response = await fetchWithLoginCancellation(DEVICE_USER_CODE_URL, {
1710
+ method: "POST",
1711
+ headers: { "Content-Type": "application/json" },
1712
+ body: JSON.stringify({ client_id: CLIENT_ID5 }),
1713
+ signal
1714
+ });
1715
+ if (!response.ok) {
1716
+ if (response.status === 404) {
1717
+ throw new Error("OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.");
1718
+ }
1719
+ const responseBody = await response.text().catch(() => "");
1720
+ throw new Error(`OpenAI Codex device code request failed with status ${response.status}${responseBody ? `: ${responseBody}` : ""}`);
1721
+ }
1722
+ const rawJson = await response.json();
1723
+ const json = rawJson;
1724
+ const intervalSeconds = typeof json?.interval === "string" ? Number(json.interval.trim()) : json?.interval;
1725
+ if (!json?.device_auth_id || !json.user_code || typeof intervalSeconds !== "number" || !Number.isFinite(intervalSeconds) || intervalSeconds < 0) {
1726
+ throw new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);
1727
+ }
1728
+ return {
1729
+ deviceAuthId: json.device_auth_id,
1730
+ userCode: json.user_code,
1731
+ intervalSeconds
1732
+ };
1733
+ }
1734
+ async function pollOpenAICodexDeviceAuth(device, signal) {
1735
+ return pollOAuthDeviceCodeFlow({
1736
+ intervalSeconds: device.intervalSeconds,
1737
+ expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS2,
1738
+ signal,
1739
+ poll: async () => {
1740
+ const response = await fetchWithLoginCancellation(DEVICE_TOKEN_URL2, {
1741
+ method: "POST",
1742
+ headers: { "Content-Type": "application/json" },
1743
+ body: JSON.stringify({
1744
+ device_auth_id: device.deviceAuthId,
1745
+ user_code: device.userCode
1746
+ }),
1747
+ signal
1748
+ });
1749
+ if (response.ok) {
1750
+ const rawJson = await response.json();
1751
+ const json = rawJson;
1752
+ if (!json?.authorization_code || !json.code_verifier) {
1753
+ return {
1754
+ status: "failed",
1755
+ message: `Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`
1756
+ };
1757
+ }
1758
+ return {
1759
+ status: "complete",
1760
+ value: { authorizationCode: json.authorization_code, codeVerifier: json.code_verifier }
1761
+ };
1762
+ }
1763
+ if (response.status === 403 || response.status === 404) {
1764
+ return { status: "pending" };
1765
+ }
1766
+ const responseBody = await response.text().catch(() => "");
1767
+ let errorCode;
1768
+ try {
1769
+ const json = JSON.parse(responseBody);
1770
+ const error = json?.error;
1771
+ errorCode = typeof error === "object" ? error?.code : error;
1772
+ } catch {}
1773
+ if (errorCode === "deviceauth_authorization_pending") {
1774
+ return { status: "pending" };
1775
+ }
1776
+ if (errorCode === "slow_down") {
1777
+ return { status: "slow_down" };
1778
+ }
1779
+ return {
1780
+ status: "failed",
1781
+ message: `OpenAI Codex device auth failed with status ${response.status}${responseBody ? `: ${responseBody}` : ""}`
1782
+ };
1783
+ }
1784
+ });
1785
+ }
1786
+ async function createAuthorizationFlow(originator = "pi") {
1787
+ const { verifier, challenge } = await generatePKCE();
1788
+ const state = createState();
1789
+ const url = new URL(AUTHORIZE_URL2);
1790
+ url.searchParams.set("response_type", "code");
1791
+ url.searchParams.set("client_id", CLIENT_ID5);
1792
+ url.searchParams.set("redirect_uri", REDIRECT_URI2);
1793
+ url.searchParams.set("scope", SCOPE);
1794
+ url.searchParams.set("code_challenge", challenge);
1795
+ url.searchParams.set("code_challenge_method", "S256");
1796
+ url.searchParams.set("state", state);
1797
+ url.searchParams.set("id_token_add_organizations", "true");
1798
+ url.searchParams.set("codex_cli_simplified_flow", "true");
1799
+ url.searchParams.set("originator", originator);
1800
+ return { verifier, state, url: url.toString() };
1801
+ }
1802
+ function startLocalOAuthServer(state) {
1803
+ if (!_http) {
1804
+ throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
1805
+ }
1806
+ let settleWait;
1807
+ const waitForCodePromise = new Promise((resolve) => {
1808
+ let settled = false;
1809
+ settleWait = (value) => {
1810
+ if (settled)
1811
+ return;
1812
+ settled = true;
1813
+ resolve(value);
1814
+ };
1815
+ });
1816
+ const server = _http.createServer((req, res) => {
1817
+ try {
1818
+ const url = new URL(req.url || "", "http://localhost");
1819
+ if (url.pathname !== "/auth/callback") {
1820
+ res.statusCode = 404;
1821
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1822
+ res.end(oauthErrorHtml("Callback route not found."));
1823
+ return;
1824
+ }
1825
+ if (url.searchParams.get("state") !== state) {
1826
+ res.statusCode = 400;
1827
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1828
+ res.end(oauthErrorHtml("State mismatch."));
1829
+ return;
1830
+ }
1831
+ const code = url.searchParams.get("code");
1832
+ if (!code) {
1833
+ res.statusCode = 400;
1834
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1835
+ res.end(oauthErrorHtml("Missing authorization code."));
1836
+ return;
1837
+ }
1838
+ res.statusCode = 200;
1839
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1840
+ res.end(oauthSuccessHtml("OpenAI authentication completed. You can close this window."));
1841
+ settleWait?.({ code });
1842
+ } catch {
1843
+ res.statusCode = 500;
1844
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
1845
+ res.end(oauthErrorHtml("Internal error while processing OAuth callback."));
1846
+ }
1847
+ });
1848
+ return new Promise((resolve) => {
1849
+ server.listen(1455, getCallbackHost(), () => {
1850
+ resolve({
1851
+ close: () => server.close(),
1852
+ cancelWait: () => {
1853
+ settleWait?.(null);
1854
+ },
1855
+ waitForCode: () => waitForCodePromise
1856
+ });
1857
+ }).on("error", (_err) => {
1858
+ settleWait?.(null);
1859
+ resolve({
1860
+ close: () => {
1861
+ try {
1862
+ server.close();
1863
+ } catch {}
1864
+ },
1865
+ cancelWait: () => {},
1866
+ waitForCode: async () => null
1867
+ });
1868
+ });
1869
+ });
1870
+ }
1871
+ function getAccountId(accessToken) {
1872
+ const payload = decodeJwt(accessToken);
1873
+ const auth = payload?.[JWT_CLAIM_PATH];
1874
+ const accountId = auth?.chatgpt_account_id;
1875
+ return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
1876
+ }
1877
+ function credentialsFromToken(token) {
1878
+ const accountId = getAccountId(token.access);
1879
+ if (!accountId) {
1880
+ throw new Error("Failed to extract accountId from token");
1881
+ }
1882
+ return {
1883
+ type: "oauth",
1884
+ access: token.access,
1885
+ refresh: token.refresh,
1886
+ expires: token.expires,
1887
+ accountId
1888
+ };
1889
+ }
1890
+ async function exchangeAuthorizationCodeForCredentials(code, verifier, redirectUri, signal) {
1891
+ return credentialsFromToken(await exchangeAuthorizationCode2(code, verifier, redirectUri, signal));
1892
+ }
1893
+ async function loginOpenAICodexDeviceCode(interaction) {
1894
+ const device = await startOpenAICodexDeviceAuth(interaction.signal);
1895
+ interaction.notify({
1896
+ type: "device_code",
1897
+ userCode: device.userCode,
1898
+ verificationUri: DEVICE_VERIFICATION_URI,
1899
+ intervalSeconds: device.intervalSeconds,
1900
+ expiresInSeconds: DEVICE_CODE_TIMEOUT_SECONDS2
1901
+ });
1902
+ const code = await pollOpenAICodexDeviceAuth(device, interaction.signal);
1903
+ return exchangeAuthorizationCodeForCredentials(code.authorizationCode, code.codeVerifier, DEVICE_REDIRECT_URI, interaction.signal);
1904
+ }
1905
+ async function loginOpenAICodex(interaction) {
1906
+ const { verifier, state, url } = await createAuthorizationFlow();
1907
+ const server = await startLocalOAuthServer(state);
1908
+ const manualAbort = new AbortController;
1909
+ const onAbort = () => server.cancelWait();
1910
+ interaction.signal.addEventListener("abort", onAbort, { once: true });
1911
+ if (interaction.signal.aborted)
1912
+ onAbort();
1913
+ let code;
1914
+ let manualCode;
1915
+ let manualError;
1916
+ interaction.notify({
1917
+ type: "auth_url",
1918
+ url,
1919
+ instructions: "A browser window should open. Complete login to finish."
1920
+ });
1921
+ try {
1922
+ const manualPromise = interaction.prompt({
1923
+ type: "manual_code",
1924
+ message: "Complete login in your browser, or paste the authorization code / redirect URL here:",
1925
+ placeholder: REDIRECT_URI2,
1926
+ signal: manualAbort.signal
1927
+ }).then((input) => {
1928
+ manualCode = input;
1929
+ server.cancelWait();
1930
+ }).catch((error) => {
1931
+ manualError = error instanceof Error ? error : new Error(String(error));
1932
+ server.cancelWait();
1933
+ });
1934
+ const result = await server.waitForCode();
1935
+ if (manualError)
1936
+ throw manualError;
1937
+ if (result?.code) {
1938
+ code = result.code;
1939
+ } else if (manualCode) {
1940
+ const parsed = parseAuthorizationInput2(manualCode);
1941
+ if (parsed.state && parsed.state !== state)
1942
+ throw new Error("State mismatch");
1943
+ code = parsed.code;
1944
+ }
1945
+ if (!code) {
1946
+ await manualPromise;
1947
+ if (manualError)
1948
+ throw manualError;
1949
+ if (manualCode) {
1950
+ const parsed = parseAuthorizationInput2(manualCode);
1951
+ if (parsed.state && parsed.state !== state)
1952
+ throw new Error("State mismatch");
1953
+ code = parsed.code;
1954
+ }
1955
+ }
1956
+ if (!code)
1957
+ throw new Error("Missing authorization code");
1958
+ return exchangeAuthorizationCodeForCredentials(code, verifier, REDIRECT_URI2, interaction.signal);
1959
+ } finally {
1960
+ interaction.signal.removeEventListener("abort", onAbort);
1961
+ manualAbort.abort();
1962
+ server.close();
1963
+ }
1964
+ }
1965
+ async function refreshOpenAICodexToken(refreshToken2, signal) {
1966
+ return credentialsFromToken(await refreshAccessToken(refreshToken2, signal));
1967
+ }
1968
+ var openaiCodexOAuth = {
1969
+ name: "OpenAI (ChatGPT Plus/Pro)",
1970
+ isSubscription: true,
1971
+ async login(interaction) {
1972
+ const method = await interaction.prompt({
1973
+ type: "select",
1974
+ message: "Select OpenAI Codex login method:",
1975
+ options: [
1976
+ { id: OPENAI_CODEX_BROWSER_LOGIN_METHOD, label: "Browser login (default)" },
1977
+ { id: OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, label: "Device code login (headless)" }
1978
+ ]
1979
+ });
1980
+ if (method === OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD) {
1981
+ return loginOpenAICodexDeviceCode(interaction);
1982
+ }
1983
+ if (method !== OPENAI_CODEX_BROWSER_LOGIN_METHOD) {
1984
+ throw new Error(`Unknown OpenAI Codex login method: ${method}`);
1985
+ }
1986
+ return loginOpenAICodex(interaction);
1987
+ },
1988
+ refresh: (credential, signal) => refreshOpenAICodexToken(credential.refresh, signal),
1989
+ async toAuth(credential) {
1990
+ return { apiKey: credential.access };
1991
+ }
1992
+ };
1993
+
1994
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/openrouter.js
1995
+ import { createServer } from "node:http";
1996
+ var AUTHORIZE_URL3 = "https://openrouter.ai/auth";
1997
+ var TOKEN_URL3 = "https://openrouter.ai/api/v1/auth/keys";
1998
+ var LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
1999
+ var TOKEN_EXCHANGE_TIMEOUT_MS = 30000;
2000
+ function getCallbackHost2() {
2001
+ return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.1";
2002
+ }
2003
+ function sendHtml(response, status, html) {
2004
+ response.statusCode = status;
2005
+ response.setHeader("content-type", "text/html; charset=utf-8");
2006
+ response.setHeader("cache-control", "no-store");
2007
+ response.end(html);
2008
+ }
2009
+ function parseAuthorizationInput3(input) {
2010
+ const value = input.trim();
2011
+ if (!value)
2012
+ return;
2013
+ try {
2014
+ return new URL(value).searchParams.get("code") ?? undefined;
2015
+ } catch {}
2016
+ if (value.includes("code=")) {
2017
+ return new URLSearchParams(value).get("code") ?? undefined;
2018
+ }
2019
+ return value;
2020
+ }
2021
+ function errorDetail2(body) {
2022
+ if (typeof body.error_description === "string")
2023
+ return body.error_description;
2024
+ if (typeof body.message === "string")
2025
+ return body.message;
2026
+ if (typeof body.error === "string")
2027
+ return body.error;
2028
+ if (body.error && typeof body.error === "object" && !Array.isArray(body.error)) {
2029
+ const message = body.error.message;
2030
+ if (typeof message === "string")
2031
+ return message;
2032
+ }
2033
+ return;
2034
+ }
2035
+ async function exchangeAuthorizationCode3(code, verifier, signal) {
2036
+ if (signal.aborted)
2037
+ throw new Error("Login cancelled");
2038
+ const controller = new AbortController;
2039
+ const onAbort = () => controller.abort(signal.reason);
2040
+ signal.addEventListener("abort", onAbort, { once: true });
2041
+ const timeout = setTimeout(() => controller.abort(new Error("OpenRouter OAuth token exchange timed out")), TOKEN_EXCHANGE_TIMEOUT_MS);
2042
+ let response;
2043
+ let body = {};
2044
+ try {
2045
+ response = await fetch(TOKEN_URL3, {
2046
+ method: "POST",
2047
+ headers: { accept: "application/json", "content-type": "application/json" },
2048
+ body: JSON.stringify({ code, code_verifier: verifier, code_challenge_method: "S256" }),
2049
+ signal: controller.signal
2050
+ });
2051
+ try {
2052
+ const parsed = await response.json();
2053
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
2054
+ body = parsed;
2055
+ } catch {
2056
+ if (response.ok)
2057
+ throw new Error("OpenRouter OAuth returned invalid JSON");
2058
+ }
2059
+ } catch (error) {
2060
+ if (signal.aborted)
2061
+ throw new Error("Login cancelled");
2062
+ if (controller.signal.aborted)
2063
+ throw new Error("OpenRouter OAuth token exchange timed out");
2064
+ throw error;
2065
+ } finally {
2066
+ clearTimeout(timeout);
2067
+ signal.removeEventListener("abort", onAbort);
2068
+ }
2069
+ if (!response.ok) {
2070
+ const detail = errorDetail2(body);
2071
+ throw new Error(`OpenRouter OAuth key exchange failed (HTTP ${response.status})${detail ? `: ${detail}` : ""}`);
2072
+ }
2073
+ if (typeof body.key !== "string" || body.key.length === 0) {
2074
+ throw new Error('OpenRouter OAuth response carries no "key"');
2075
+ }
2076
+ return {
2077
+ type: "oauth",
2078
+ access: body.key,
2079
+ refresh: "",
2080
+ expires: Number.MAX_SAFE_INTEGER
2081
+ };
2082
+ }
2083
+ async function startCallbackServer2(callbackPath, verifier, signal) {
2084
+ if (signal.aborted)
2085
+ throw new Error("Login cancelled");
2086
+ const callbackHost = getCallbackHost2();
2087
+ let resolveCredential = () => {};
2088
+ let rejectCredential = () => {};
2089
+ const credential = new Promise((resolve, reject) => {
2090
+ resolveCredential = resolve;
2091
+ rejectCredential = reject;
2092
+ });
2093
+ let server;
2094
+ let claimed = false;
2095
+ let settled = false;
2096
+ let timeout;
2097
+ let onAbort;
2098
+ const close = () => {
2099
+ if (timeout)
2100
+ clearTimeout(timeout);
2101
+ if (onAbort)
2102
+ signal.removeEventListener("abort", onAbort);
2103
+ server.close();
2104
+ };
2105
+ const finish = (result) => {
2106
+ if (settled)
2107
+ return;
2108
+ settled = true;
2109
+ close();
2110
+ if ("credential" in result)
2111
+ resolveCredential(result.credential);
2112
+ else
2113
+ rejectCredential(result.error);
2114
+ };
2115
+ server = createServer((request, response) => {
2116
+ (async () => {
2117
+ const requestUrl = new URL(request.url ?? "/", `http://${callbackHost}`);
2118
+ if (request.method !== "GET" || requestUrl.pathname !== callbackPath) {
2119
+ sendHtml(response, 404, oauthErrorHtml("OAuth callback route not found."));
2120
+ return;
2121
+ }
2122
+ if (claimed || settled) {
2123
+ sendHtml(response, 409, oauthErrorHtml("This OAuth callback has already been used."));
2124
+ return;
2125
+ }
2126
+ const oauthError = requestUrl.searchParams.get("error");
2127
+ if (oauthError) {
2128
+ const description = requestUrl.searchParams.get("error_description") ?? oauthError;
2129
+ sendHtml(response, 400, oauthErrorHtml("OpenRouter authorization was denied.", description));
2130
+ finish({ error: new Error(`OpenRouter authorization failed: ${description}`) });
2131
+ return;
2132
+ }
2133
+ const code = requestUrl.searchParams.get("code");
2134
+ if (!code) {
2135
+ sendHtml(response, 400, oauthErrorHtml("OpenRouter returned no authorization code."));
2136
+ return;
2137
+ }
2138
+ claimed = true;
2139
+ try {
2140
+ const result = await exchangeAuthorizationCode3(code, verifier, signal);
2141
+ sendHtml(response, 200, oauthSuccessHtml("Signed in to OpenRouter. You may now close this page."));
2142
+ finish({ credential: result });
2143
+ } catch (error) {
2144
+ const message = error instanceof Error ? error.message : "Unknown token exchange error";
2145
+ sendHtml(response, 502, oauthErrorHtml("OpenRouter key exchange failed.", message));
2146
+ finish({ error: error instanceof Error ? error : new Error(message) });
2147
+ }
2148
+ })();
2149
+ });
2150
+ await new Promise((resolve, reject) => {
2151
+ server.once("error", reject);
2152
+ server.listen(0, callbackHost, () => {
2153
+ server.removeListener("error", reject);
2154
+ resolve();
2155
+ });
2156
+ });
2157
+ server.on("error", (error) => finish({ error }));
2158
+ onAbort = () => finish({ error: new Error("Login cancelled") });
2159
+ signal.addEventListener("abort", onAbort, { once: true });
2160
+ if (signal.aborted) {
2161
+ close();
2162
+ throw new Error("Login cancelled");
2163
+ }
2164
+ timeout = setTimeout(() => finish({ error: new Error("OpenRouter OAuth login timed out") }), LOGIN_TIMEOUT_MS);
2165
+ const address = server.address();
2166
+ if (!address || typeof address === "string") {
2167
+ close();
2168
+ throw new Error("Could not determine the OpenRouter OAuth callback port");
2169
+ }
2170
+ return {
2171
+ callbackUrl: `http://${callbackHost}:${address.port}${callbackPath}`,
2172
+ close,
2173
+ cancelWait: () => {
2174
+ if (!claimed)
2175
+ finish({ credential: null });
2176
+ },
2177
+ waitForCredential: () => credential
2178
+ };
2179
+ }
2180
+ async function loginOpenRouter(interaction) {
2181
+ const { verifier, challenge } = await generatePKCE();
2182
+ const callbackPath = `/oauth/callback/${crypto.randomUUID()}`;
2183
+ const callback = await startCallbackServer2(callbackPath, verifier, interaction.signal);
2184
+ const manualAbort = new AbortController;
2185
+ let manualInput;
2186
+ let manualError;
2187
+ try {
2188
+ const authorizeUrl = new URL(AUTHORIZE_URL3);
2189
+ authorizeUrl.search = new URLSearchParams({
2190
+ callback_url: callback.callbackUrl,
2191
+ code_challenge: challenge,
2192
+ code_challenge_method: "S256"
2193
+ }).toString();
2194
+ interaction.notify({
2195
+ type: "progress",
2196
+ message: `Listening for OpenRouter OAuth callback on ${callback.callbackUrl}`
2197
+ });
2198
+ interaction.notify({
2199
+ type: "auth_url",
2200
+ url: authorizeUrl.toString(),
2201
+ instructions: "Complete sign-in in your browser. If the browser is on another machine, paste the final redirect URL here."
2202
+ });
2203
+ const manualPromise = interaction.prompt({
2204
+ type: "manual_code",
2205
+ message: "Complete sign-in in your browser, or paste the authorization code / redirect URL here:",
2206
+ placeholder: callback.callbackUrl,
2207
+ signal: manualAbort.signal
2208
+ }).then((input) => {
2209
+ manualInput = input;
2210
+ callback.cancelWait();
2211
+ }).catch((error) => {
2212
+ manualError = error instanceof Error ? error : new Error(String(error));
2213
+ callback.cancelWait();
2214
+ });
2215
+ const credential = await callback.waitForCredential();
2216
+ if (manualError)
2217
+ throw manualError;
2218
+ if (credential)
2219
+ return credential;
2220
+ await manualPromise;
2221
+ if (manualError)
2222
+ throw manualError;
2223
+ const code = manualInput ? parseAuthorizationInput3(manualInput) : undefined;
2224
+ if (!code)
2225
+ throw new Error("Missing authorization code");
2226
+ interaction.notify({ type: "progress", message: "Exchanging authorization code for an API key..." });
2227
+ return await exchangeAuthorizationCode3(code, verifier, interaction.signal);
2228
+ } finally {
2229
+ manualAbort.abort();
2230
+ callback.close();
2231
+ }
2232
+ }
2233
+ var openRouterOAuth = {
2234
+ name: "OpenRouter OAuth",
2235
+ loginLabel: "Sign in with OpenRouter",
2236
+ login: loginOpenRouter,
2237
+ async refresh(credential, _signal) {
2238
+ return credential;
2239
+ },
2240
+ async toAuth(credential) {
2241
+ return { apiKey: credential.access };
2242
+ }
2243
+ };
2244
+
2245
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.js
2246
+ var _http2 = null;
2247
+ if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
2248
+ import("node:http").then((m) => {
2249
+ _http2 = m;
2250
+ });
2251
+ }
2252
+ var CALLBACK_HOST2 = "127.0.0.1";
2253
+ var CALLBACK_PORT2 = 1456;
2254
+ var CALLBACK_PATH2 = "/oauth/callback";
2255
+ var REDIRECT_URI3 = `http://${CALLBACK_HOST2}:${CALLBACK_PORT2}${CALLBACK_PATH2}`;
2256
+ var TOKEN_EXPIRY_SKEW_MS = 60000;
2257
+ var LOGIN_METHOD_BROWSER = "browser";
2258
+ var LOGIN_METHOD_DEVICE_CODE = "device-code";
2259
+ var OAUTH_CLIENT_ID = "pi-gateway";
2260
+ var OAUTH_SCOPE = "gateway offline_access";
2261
+ var OAUTH_DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
2262
+ async function loadRadiusOAuthDiscovery(gateway, signal) {
2263
+ const response = await fetch(new URL("/v1/oauth", gateway), {
2264
+ headers: { accept: "application/json" },
2265
+ signal
2266
+ });
2267
+ if (!response.ok) {
2268
+ throw new Error(`Could not load Radius OAuth config from ${gateway}: ${response.status} ${await response.text()}`);
2269
+ }
2270
+ const discovery = await response.json();
2271
+ if (typeof discovery.authorizationEndpoint !== "string") {
2272
+ throw new Error(`Invalid Radius OAuth config from ${gateway}`);
2273
+ }
2274
+ return { authorizationEndpoint: discovery.authorizationEndpoint };
2275
+ }
2276
+
2277
+ class OAuthResponseError extends Error {
2278
+ status;
2279
+ oauthError;
2280
+ constructor(status, oauthError, description, message) {
2281
+ const detail = oauthError ? description ? `${oauthError}: ${description}` : oauthError : description || String(status);
2282
+ super(`${message}: ${detail}`);
2283
+ this.status = status;
2284
+ this.oauthError = oauthError;
2285
+ }
2286
+ }
2287
+ async function readOAuthResponseError(response, message) {
2288
+ const text = await response.text().catch(() => "");
2289
+ let oauthError;
2290
+ let description;
2291
+ if (text) {
2292
+ try {
2293
+ const data = JSON.parse(text);
2294
+ oauthError = typeof data.error === "string" ? data.error : undefined;
2295
+ description = typeof data.error_description === "string" ? data.error_description : undefined;
2296
+ } catch {
2297
+ description = text;
2298
+ }
2299
+ }
2300
+ return new OAuthResponseError(response.status, oauthError, description, message);
2301
+ }
2302
+ async function requestOAuthToken(gateway, body, signal) {
2303
+ let response;
2304
+ try {
2305
+ response = await fetch(new URL("/v1/oauth/token", gateway), {
2306
+ method: "POST",
2307
+ headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" },
2308
+ body,
2309
+ signal
2310
+ });
2311
+ } catch (error) {
2312
+ if (signal.aborted) {
2313
+ throw new Error("Login cancelled");
2314
+ }
2315
+ throw error;
2316
+ }
2317
+ if (!response.ok) {
2318
+ throw await readOAuthResponseError(response, "Radius OAuth token request failed");
2319
+ }
2320
+ const data = await response.json();
2321
+ return {
2322
+ type: "oauth",
2323
+ access: data.access_token,
2324
+ refresh: data.refresh_token,
2325
+ expires: Date.now() + data.expires_in * 1000 - TOKEN_EXPIRY_SKEW_MS,
2326
+ scope: data.scope
2327
+ };
2328
+ }
2329
+ function startOAuthCallbackServer(expectedState, signal) {
2330
+ if (!_http2) {
2331
+ throw new Error("Radius OAuth is only available in Node.js environments");
2332
+ }
2333
+ let settle = () => {};
2334
+ let settled = false;
2335
+ const wait = new Promise((resolve) => {
2336
+ settle = resolve;
2337
+ });
2338
+ const finish = (code) => {
2339
+ if (settled) {
2340
+ return;
2341
+ }
2342
+ settled = true;
2343
+ signal.removeEventListener("abort", onAbort);
2344
+ settle(code);
2345
+ };
2346
+ const onAbort = () => finish(null);
2347
+ signal.addEventListener("abort", onAbort, { once: true });
2348
+ const sendPage = (response, status, html) => {
2349
+ response.statusCode = status;
2350
+ response.setHeader("content-type", "text/html; charset=utf-8");
2351
+ response.end(html);
2352
+ };
2353
+ const server = _http2.createServer((request, response) => {
2354
+ const url = new URL(request.url ?? "/", REDIRECT_URI3);
2355
+ if (url.pathname !== CALLBACK_PATH2) {
2356
+ sendPage(response, 404, oauthErrorHtml("Callback route not found."));
2357
+ return;
2358
+ }
2359
+ if (url.searchParams.get("state") !== expectedState) {
2360
+ sendPage(response, 400, oauthErrorHtml("OAuth state mismatch."));
2361
+ return;
2362
+ }
2363
+ const error = url.searchParams.get("error");
2364
+ if (error) {
2365
+ sendPage(response, 400, oauthErrorHtml(url.searchParams.get("error_description") ?? error));
2366
+ finish(null);
2367
+ return;
2368
+ }
2369
+ const code = url.searchParams.get("code");
2370
+ if (!code) {
2371
+ sendPage(response, 400, oauthErrorHtml("Missing authorization code."));
2372
+ return;
2373
+ }
2374
+ sendPage(response, 200, oauthSuccessHtml("Signed in to Radius. You may now close this page."));
2375
+ finish(code);
2376
+ });
2377
+ return new Promise((resolve) => {
2378
+ server.listen(CALLBACK_PORT2, CALLBACK_HOST2, () => {
2379
+ resolve({
2380
+ waitForCode: () => wait,
2381
+ close: () => {
2382
+ finish(null);
2383
+ server.close();
2384
+ }
2385
+ });
2386
+ }).once("error", () => {
2387
+ finish(null);
2388
+ resolve({ waitForCode: async () => null, close: () => {} });
2389
+ });
2390
+ });
2391
+ }
2392
+ async function loginWithBrowser(gateway, authorizationEndpoint, interaction) {
2393
+ const { verifier, challenge } = await generatePKCE();
2394
+ const state = crypto.randomUUID();
2395
+ const authorizeUrl = new URL(authorizationEndpoint);
2396
+ authorizeUrl.search = new URLSearchParams({
2397
+ response_type: "code",
2398
+ client_id: OAUTH_CLIENT_ID,
2399
+ redirect_uri: REDIRECT_URI3,
2400
+ scope: OAUTH_SCOPE,
2401
+ code_challenge: challenge,
2402
+ code_challenge_method: "S256",
2403
+ handoff: "url",
2404
+ state
2405
+ }).toString();
2406
+ const callbackServer = await startOAuthCallbackServer(state, interaction.signal);
2407
+ interaction.notify({ type: "progress", message: `Listening for OAuth callback on ${REDIRECT_URI3}` });
2408
+ interaction.notify({
2409
+ type: "auth_url",
2410
+ url: authorizeUrl.toString(),
2411
+ instructions: "Continue in your browser."
2412
+ });
2413
+ try {
2414
+ const code = await callbackServer.waitForCode();
2415
+ if (!code) {
2416
+ if (interaction.signal.aborted) {
2417
+ throw new Error("Login cancelled");
2418
+ }
2419
+ throw new Error("OAuth callback did not complete.");
2420
+ }
2421
+ return await requestOAuthToken(gateway, new URLSearchParams({
2422
+ grant_type: "authorization_code",
2423
+ client_id: OAUTH_CLIENT_ID,
2424
+ redirect_uri: REDIRECT_URI3,
2425
+ code,
2426
+ code_verifier: verifier
2427
+ }), interaction.signal);
2428
+ } finally {
2429
+ callbackServer.close();
2430
+ }
2431
+ }
2432
+ async function requestDeviceAuthorization(gateway, signal) {
2433
+ let response;
2434
+ try {
2435
+ response = await fetch(new URL("/v1/oauth/device", gateway), {
2436
+ method: "POST",
2437
+ headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" },
2438
+ body: new URLSearchParams({ client_id: OAUTH_CLIENT_ID, scope: OAUTH_SCOPE }),
2439
+ signal
2440
+ });
2441
+ } catch (error) {
2442
+ if (signal.aborted) {
2443
+ throw new Error("Login cancelled");
2444
+ }
2445
+ throw error;
2446
+ }
2447
+ if (!response.ok) {
2448
+ throw await readOAuthResponseError(response, "Radius OAuth device authorization failed");
2449
+ }
2450
+ const data = await response.json();
2451
+ if (!data.device_code || !data.user_code || !data.verification_uri || !data.expires_in) {
2452
+ throw new Error("Radius OAuth device authorization response is missing required fields");
2453
+ }
2454
+ return {
2455
+ device_code: data.device_code,
2456
+ user_code: data.user_code,
2457
+ verification_uri: data.verification_uri,
2458
+ expires_in: data.expires_in,
2459
+ interval: data.interval
2460
+ };
2461
+ }
2462
+ async function loginWithDeviceCode(gateway, interaction) {
2463
+ const device = await requestDeviceAuthorization(gateway, interaction.signal);
2464
+ interaction.notify({
2465
+ type: "device_code",
2466
+ userCode: device.user_code,
2467
+ verificationUri: device.verification_uri,
2468
+ intervalSeconds: device.interval,
2469
+ expiresInSeconds: device.expires_in
2470
+ });
2471
+ return pollOAuthDeviceCodeFlow({
2472
+ intervalSeconds: device.interval,
2473
+ expiresInSeconds: device.expires_in,
2474
+ signal: interaction.signal,
2475
+ poll: async () => {
2476
+ try {
2477
+ const credentials = await requestOAuthToken(gateway, new URLSearchParams({
2478
+ grant_type: OAUTH_DEVICE_CODE_GRANT_TYPE,
2479
+ client_id: OAUTH_CLIENT_ID,
2480
+ device_code: device.device_code
2481
+ }), interaction.signal);
2482
+ return { status: "complete", value: credentials };
2483
+ } catch (error) {
2484
+ if (!(error instanceof OAuthResponseError)) {
2485
+ throw error;
2486
+ }
2487
+ switch (error.oauthError) {
2488
+ case "authorization_pending":
2489
+ return { status: "pending" };
2490
+ case "slow_down":
2491
+ return { status: "slow_down" };
2492
+ case "expired_token":
2493
+ return { status: "failed", message: "Device authorization expired." };
2494
+ case "access_denied":
2495
+ return { status: "failed", message: "Device authorization was denied." };
2496
+ default:
2497
+ throw error;
2498
+ }
2499
+ }
2500
+ }
2501
+ });
2502
+ }
2503
+ function createRadiusOAuth(options) {
2504
+ const gateway = normalizeRadiusGatewayUrl(options.gateway);
2505
+ return {
2506
+ name: options.name,
2507
+ async login(interaction) {
2508
+ const loginMethod = await interaction.prompt({
2509
+ type: "select",
2510
+ message: `Sign in to ${options.name}:`,
2511
+ options: [
2512
+ { id: LOGIN_METHOD_BROWSER, label: "Sign in with browser (recommended)" },
2513
+ {
2514
+ id: LOGIN_METHOD_DEVICE_CODE,
2515
+ label: "Sign in with device code (when signing in from another device)"
2516
+ }
2517
+ ]
2518
+ });
2519
+ if (loginMethod === LOGIN_METHOD_DEVICE_CODE) {
2520
+ return loginWithDeviceCode(gateway, interaction);
2521
+ }
2522
+ if (loginMethod === LOGIN_METHOD_BROWSER) {
2523
+ const discovery = await loadRadiusOAuthDiscovery(gateway, interaction.signal);
2524
+ return loginWithBrowser(gateway, discovery.authorizationEndpoint, interaction);
2525
+ }
2526
+ throw new Error(`Unknown ${options.name} sign-in method: ${loginMethod}`);
2527
+ },
2528
+ async refresh(credential, signal) {
2529
+ const refreshed = await requestOAuthToken(gateway, new URLSearchParams({
2530
+ grant_type: "refresh_token",
2531
+ client_id: OAUTH_CLIENT_ID,
2532
+ refresh_token: credential.refresh
2533
+ }), signal);
2534
+ return refreshed;
2535
+ },
2536
+ async toAuth(credential) {
2537
+ return { apiKey: credential.access };
2538
+ }
2539
+ };
2540
+ }
2541
+
2542
+ // node_modules/@earendil-works/pi-ai/dist/auth/oauth/xai.js
2543
+ var XAI_CLIENT_ID = "b1a00492-073a-47ea-816f-4c329264a828";
2544
+ var XAI_SCOPE = "openid profile email offline_access grok-cli:access api:access";
2545
+ var XAI_DEVICE_CODE_URL = "https://auth.x.ai/oauth2/device/code";
2546
+ var XAI_TOKEN_URL = "https://auth.x.ai/oauth2/token";
2547
+ var REFRESH_SKEW_MS = 5 * 60 * 1000;
2548
+ var DEFAULT_TOKEN_LIFETIME_SECONDS = 3600;
2549
+ function requiredString(body, field) {
2550
+ const value = body[field];
2551
+ if (typeof value !== "string" || value.length === 0) {
2552
+ throw new Error(`Invalid xAI OAuth response field: ${field}`);
2553
+ }
2554
+ return value;
2555
+ }
2556
+ function positiveNumber2(body, field) {
2557
+ const value = body[field];
2558
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
2559
+ throw new Error(`Invalid xAI OAuth response field: ${field}`);
2560
+ }
2561
+ return value;
2562
+ }
2563
+ function validateVerificationUri(raw) {
2564
+ let url;
2565
+ try {
2566
+ url = new URL(raw);
2567
+ } catch {
2568
+ throw new Error("Untrusted verification URI in xAI OAuth response");
2569
+ }
2570
+ if (url.protocol !== "https:") {
2571
+ throw new Error("Untrusted verification URI in xAI OAuth response");
2572
+ }
2573
+ return url.href;
2574
+ }
2575
+ async function postForm(url, fields, signal) {
2576
+ let response;
2577
+ try {
2578
+ response = await fetch(url, {
2579
+ method: "POST",
2580
+ headers: {
2581
+ Accept: "application/json",
2582
+ "Content-Type": "application/x-www-form-urlencoded"
2583
+ },
2584
+ body: new URLSearchParams(fields),
2585
+ signal
2586
+ });
2587
+ } catch (error) {
2588
+ if (signal.aborted) {
2589
+ throw new Error("Login cancelled");
2590
+ }
2591
+ throw error;
2592
+ }
2593
+ let body;
2594
+ try {
2595
+ const parsed = await response.json();
2596
+ body = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
2597
+ } catch {
2598
+ if (signal.aborted) {
2599
+ throw new Error("Login cancelled");
2600
+ }
2601
+ throw new Error(`xAI OAuth returned invalid JSON (HTTP ${response.status})`);
2602
+ }
2603
+ return {
2604
+ ok: response.ok,
2605
+ status: response.status,
2606
+ body
2607
+ };
2608
+ }
2609
+ function requestFailure(action, response) {
2610
+ const error = typeof response.body.error === "string" ? response.body.error : undefined;
2611
+ const description = typeof response.body.error_description === "string" ? response.body.error_description : undefined;
2612
+ const detail = [error, description].filter(Boolean).join(": ");
2613
+ return new Error(`xAI OAuth ${action} failed (HTTP ${response.status})${detail ? `: ${detail}` : ""}`);
2614
+ }
2615
+ function parseDeviceCode(body) {
2616
+ const interval = body.interval;
2617
+ const intervalSeconds = typeof interval === "number" && Number.isFinite(interval) && interval > 0 ? interval : undefined;
2618
+ const verificationUriComplete = typeof body.verification_uri_complete === "string" && body.verification_uri_complete.length > 0 ? validateVerificationUri(body.verification_uri_complete) : undefined;
2619
+ return {
2620
+ deviceCode: requiredString(body, "device_code"),
2621
+ userCode: requiredString(body, "user_code"),
2622
+ verificationUri: validateVerificationUri(requiredString(body, "verification_uri")),
2623
+ verificationUriComplete,
2624
+ intervalSeconds,
2625
+ expiresInSeconds: positiveNumber2(body, "expires_in")
2626
+ };
2627
+ }
2628
+ function credentialsFromTokenResponse(body, previousRefreshToken) {
2629
+ const access = requiredString(body, "access_token");
2630
+ const refresh = body.refresh_token === undefined && previousRefreshToken ? previousRefreshToken : requiredString(body, "refresh_token");
2631
+ const expiresInSeconds = body.expires_in === undefined ? DEFAULT_TOKEN_LIFETIME_SECONDS : positiveNumber2(body, "expires_in");
2632
+ return {
2633
+ type: "oauth",
2634
+ access,
2635
+ refresh,
2636
+ expires: Date.now() + expiresInSeconds * 1000 - REFRESH_SKEW_MS
2637
+ };
2638
+ }
2639
+ async function requestDeviceCode(signal) {
2640
+ const response = await postForm(XAI_DEVICE_CODE_URL, {
2641
+ client_id: XAI_CLIENT_ID,
2642
+ scope: XAI_SCOPE,
2643
+ referrer: "pi"
2644
+ }, signal);
2645
+ if (!response.ok) {
2646
+ throw requestFailure("device authorization", response);
2647
+ }
2648
+ return parseDeviceCode(response.body);
2649
+ }
2650
+ async function pollForTokens(device, signal) {
2651
+ return pollOAuthDeviceCodeFlow({
2652
+ intervalSeconds: device.intervalSeconds,
2653
+ expiresInSeconds: device.expiresInSeconds,
2654
+ waitBeforeFirstPoll: true,
2655
+ signal,
2656
+ poll: async () => {
2657
+ const response = await postForm(XAI_TOKEN_URL, {
2658
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
2659
+ client_id: XAI_CLIENT_ID,
2660
+ device_code: device.deviceCode
2661
+ }, signal);
2662
+ if (response.ok) {
2663
+ return { status: "complete", value: credentialsFromTokenResponse(response.body) };
2664
+ }
2665
+ const error = response.body.error;
2666
+ if (error === "authorization_pending") {
2667
+ return { status: "pending" };
2668
+ }
2669
+ if (error === "slow_down") {
2670
+ const interval = response.body.interval;
2671
+ return { status: "slow_down", intervalSeconds: typeof interval === "number" ? interval : undefined };
2672
+ }
2673
+ if (error === "access_denied" || error === "authorization_denied") {
2674
+ return { status: "failed", message: "xAI device authorization was denied" };
2675
+ }
2676
+ if (error === "expired_token") {
2677
+ return { status: "failed", message: "xAI device code expired" };
2678
+ }
2679
+ return { status: "failed", message: requestFailure("device token polling", response).message };
2680
+ }
2681
+ });
2682
+ }
2683
+ async function loginXai(interaction) {
2684
+ const device = await requestDeviceCode(interaction.signal);
2685
+ interaction.notify({
2686
+ type: "device_code",
2687
+ userCode: device.userCode,
2688
+ verificationUri: device.verificationUriComplete ?? device.verificationUri,
2689
+ intervalSeconds: device.intervalSeconds,
2690
+ expiresInSeconds: device.expiresInSeconds
2691
+ });
2692
+ return pollForTokens(device, interaction.signal);
2693
+ }
2694
+ async function refreshXaiToken(refreshToken2, signal) {
2695
+ const response = await postForm(XAI_TOKEN_URL, {
2696
+ grant_type: "refresh_token",
2697
+ client_id: XAI_CLIENT_ID,
2698
+ refresh_token: refreshToken2
2699
+ }, signal);
2700
+ if (!response.ok) {
2701
+ throw requestFailure("token refresh", response);
2702
+ }
2703
+ return credentialsFromTokenResponse(response.body, refreshToken2);
2704
+ }
2705
+ var xaiOAuth = {
2706
+ name: "xAI (Grok/X subscription)",
2707
+ isSubscription: true,
2708
+ loginLabel: "Sign in with SuperGrok or X Premium",
2709
+ login: loginXai,
2710
+ refresh: (credential, signal) => refreshXaiToken(credential.refresh, signal),
2711
+ async toAuth(credential) {
2712
+ return { apiKey: credential.access };
2713
+ }
2714
+ };
2715
+
2716
+ // node_modules/@earendil-works/pi-ai/dist/bun-oauth.js
2717
+ function registerBunOAuthFlows() {
2718
+ registerBundledOAuthFlowLoaders({
2719
+ anthropic: () => anthropicOAuth,
2720
+ openaiCodex: () => openaiCodexOAuth,
2721
+ githubCopilot: () => githubCopilotOAuth,
2722
+ openrouter: () => openRouterOAuth,
2723
+ kimiCoding: () => kimiCodingOAuth,
2724
+ meta: () => metaOAuth,
2725
+ xai: () => xaiOAuth,
2726
+ radius: createRadiusOAuth
2727
+ });
2728
+ }
2729
+
2730
+ // src/utils/internal-ai/credentials.ts
2731
+ registerBunOAuthFlows();
2732
+ var resolveOAuthApiKey = async (providerId, credentials) => {
2733
+ const input = credentials[providerId];
2734
+ if (!input)
2735
+ return null;
2736
+ const store = new InMemoryCredentialStore;
2737
+ const initial = { ...input, type: "oauth" };
2738
+ await store.modify(providerId, async () => initial);
2739
+ const models = builtinModels({ credentials: store });
2740
+ const resolved = await models.getAuth(providerId);
2741
+ const apiKey = resolved?.auth.apiKey;
2742
+ if (!apiKey)
2743
+ return null;
2744
+ const current = await store.read(providerId);
2745
+ const changed = current?.type === "oauth" && (current.access !== initial.access || current.refresh !== initial.refresh || current.expires !== initial.expires);
2746
+ return {
2747
+ apiKey,
2748
+ newCredentials: changed && current?.type === "oauth" ? current : undefined
2749
+ };
2750
+ };
2751
+ async function resolveCredential(opts = {}) {
2752
+ const env = opts.env ?? process.env;
2753
+ const getEnvKey = opts._getEnvApiKey ?? getEnvApiKey;
2754
+ const getCodex = opts._getValidCodexOAuth ?? getValidCodexOAuth;
2755
+ const getOAuth = opts._getOAuthApiKey ?? resolveOAuthApiKey;
2756
+ const persistCodex = opts._persistCodexOAuth ?? persistCodexOAuth;
2757
+ const openrouterKey = env.OPENROUTER_API_KEY ?? getEnvKey("openrouter");
2758
+ if (openrouterKey) {
2759
+ return {
2760
+ kind: "openrouter",
2761
+ apiKey: openrouterKey,
2762
+ modelDefault: resolveModelString("openrouter")
2763
+ };
2764
+ }
2765
+ const anthropicKey = env.ANTHROPIC_API_KEY ?? getEnvKey("anthropic");
2766
+ if (anthropicKey) {
2767
+ return {
2768
+ kind: "anthropic",
2769
+ apiKey: anthropicKey,
2770
+ modelDefault: resolveModelString("anthropic")
2771
+ };
2772
+ }
2773
+ const openaiKey = env.OPENAI_API_KEY ?? getEnvKey("openai");
2774
+ if (openaiKey) {
2775
+ return {
2776
+ kind: "openai",
2777
+ apiKey: openaiKey,
2778
+ modelDefault: resolveModelString("openai")
2779
+ };
2780
+ }
2781
+ if (opts.apiUrl && opts.apiKey) {
2782
+ try {
2783
+ const codexCreds = await getCodex(opts.apiUrl, opts.apiKey);
2784
+ if (codexCreds) {
2785
+ const credMap = {
2786
+ "openai-codex": {
2787
+ access: codexCreds.access,
2788
+ refresh: codexCreds.refresh,
2789
+ expires: codexCreds.expires
2790
+ }
2791
+ };
2792
+ const oauthResult = await getOAuth("openai-codex", credMap);
2793
+ if (oauthResult) {
2794
+ if (oauthResult.newCredentials) {
2795
+ const updated = oauthResult.newCredentials;
2796
+ try {
2797
+ await persistCodex(opts.apiUrl, opts.apiKey, {
2798
+ access: String(updated.access),
2799
+ refresh: String(updated.refresh),
2800
+ expires: Number(updated.expires),
2801
+ accountId: codexCreds.accountId
2802
+ });
2803
+ } catch (err) {
2804
+ console.error(`internal-ai: persistCodexOAuth failed (callerTag=${opts.callerTag ?? "<unset>"}):`, err);
2805
+ }
2806
+ }
2807
+ return {
2808
+ kind: "openai-codex",
2809
+ apiKey: oauthResult.apiKey,
2810
+ modelDefault: resolveModelString("openai-codex")
2811
+ };
2812
+ }
2813
+ }
2814
+ } catch (err) {
2815
+ console.error(`internal-ai: codex OAuth probe failed (callerTag=${opts.callerTag ?? "<unset>"}):`, err);
2816
+ }
2817
+ }
2818
+ if (env.AGENT_SWARM_CLAUDE_OAUTH_TOKEN || env.CLAUDE_CODE_OAUTH_TOKEN) {
2819
+ return {
2820
+ kind: "claude-cli",
2821
+ modelDefault: resolveModelString("claude-cli")
2822
+ };
2823
+ }
2824
+ return null;
2825
+ }
2826
+
2827
+ export { REFERENCES_SOURCE_MAX_LENGTH, sanitizeReferencesSource, SummaryWithRatingsSchema, BASE_SUMMARIZE_PROMPT, LlmRater, buildRatingsFromLlm, buildSummaryWithRatingsPrompt, isLlmRaterEnabled, dedupeRetrievalsForRater, fetchRetrievalsForTask, postRatings, DEFAULT_MODEL, parseModelStr, resolveCredential };