@qredence/fleet 0.5.10 → 0.6.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 (115) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/web/client/assets/agent-activity-CwMDU7aP.js +1 -0
  3. package/dist/web/client/assets/artifacts-panel-CJ75gQub.js +1 -0
  4. package/dist/web/client/assets/chat-Ci2cEQrR.js +52 -0
  5. package/dist/web/client/assets/{chat-command-palette-DfYSPPXI.js → chat-command-palette-CkISHfKE.js} +1 -1
  6. package/dist/web/client/assets/{citations-BQ_pr_9_.js → citations-C_tPOJE_.js} +1 -1
  7. package/dist/web/client/assets/{command-DPfiaYUL.js → command-_WjnervL.js} +1 -1
  8. package/dist/web/client/assets/fleet-message-queue-BCno4XP-.js +1 -0
  9. package/dist/web/client/assets/{fleet-pi-tool-renderer-AJoMbXBa.js → fleet-pi-tool-renderer-BEDIO3al.js} +4 -4
  10. package/dist/web/client/assets/fleet-reasoning-panel-CbkO7u-v.js +1 -0
  11. package/dist/web/client/assets/{fleet-subagent-list-BnRnrWKS.js → fleet-subagent-list-iKUtLJKZ.js} +1 -1
  12. package/dist/web/client/assets/fleet-tool-timeline-C8heL7ae.js +1 -0
  13. package/dist/web/client/assets/{fork-picker-dialog-CkcSekBl.js → fork-picker-dialog-ByBOGVsa.js} +1 -1
  14. package/dist/web/client/assets/globals-B_I1Eq1u.css +2 -0
  15. package/dist/web/client/assets/horizontal-resize-CwoDojST.js +1 -0
  16. package/dist/web/client/assets/{index-DlnHLDpx.js → index-DJ1fFbQu.js} +3 -3
  17. package/dist/web/client/assets/input-group-Tb5TYbzi.js +1 -0
  18. package/dist/web/client/assets/{ipython-tool-BrMePqD-.js → ipython-tool-ldJ9F1Cw.js} +1 -1
  19. package/dist/web/client/assets/model-selector-list-Ch05B2nZ.js +1 -0
  20. package/dist/web/client/assets/{openui-renderer-DwYFJdbe.js → openui-renderer-Bf60nxNi.js} +4 -4
  21. package/dist/web/client/assets/pencil-DcUwsKLn.js +1 -0
  22. package/dist/web/client/assets/prompt-input-qPIXZ-Rb.js +1 -0
  23. package/dist/web/client/assets/prompt-suggestions-Cc--lSNR.js +1 -0
  24. package/dist/web/client/assets/{provider-label-BCJh04J5.js → provider-label-DDLRnZyC.js} +1 -1
  25. package/dist/web/client/assets/repl-panel-DsPX04Xr.js +1 -0
  26. package/dist/web/client/assets/{resources-panel-Bo8dfUdm.js → resources-panel-D867-goy.js} +1 -1
  27. package/dist/web/client/assets/right-panel-registry-Dp8yX-oL.js +2 -0
  28. package/dist/web/client/assets/routes-DpgMJZ5n.js +16 -0
  29. package/dist/web/client/assets/safe-storage-s25GrfpH.js +1 -0
  30. package/dist/web/client/assets/session-insights-panel-YnaXvTpi.js +1 -0
  31. package/dist/web/client/assets/settings-dialog-YJkvt5EB.js +2 -0
  32. package/dist/web/client/assets/skeleton-loaders-C9vHwAYV.js +1 -0
  33. package/dist/web/client/assets/{subagent-chat-panel-DseoO31p.js → subagent-chat-panel-Yujplxpb.js} +1 -1
  34. package/dist/web/client/assets/subagent-transcript-CnI-mqN0.js +1 -0
  35. package/dist/web/client/assets/subagents-panel-CjXEdimj.js +1 -0
  36. package/dist/web/client/assets/{surfaces-cW-gp56p.js → surfaces-DI-U-_2a.js} +1 -1
  37. package/dist/web/client/assets/tool-result-DfnPN0FQ.js +4 -0
  38. package/dist/web/client/assets/ui-preferences-CImQGZm_.js +1 -0
  39. package/dist/web/client/assets/virtualized-turn-list--Vynre4D.js +6 -0
  40. package/dist/web/client/assets/{workspace-panel-DI1W3ce9.js → workspace-panel-CPPIznT9.js} +1 -1
  41. package/dist/web/client/bundle-budget.json +6 -6
  42. package/dist/web/server/{_tanstack-start-manifest_v-Cdx6hy2i-I7A5YJRU.js → _tanstack-start-manifest_v-DS_CSIFQ-WB3VTKF3.js} +15 -13
  43. package/dist/web/server/{agent-activity-B-JMvecd-SLE3GGY2.js → agent-activity-CS2rbF_C-A3R26CYH.js} +2 -2
  44. package/dist/web/server/{artifacts-panel-DLMK1O8P-42T45JMA.js → artifacts-panel-CaKrX2XC-R37I4BHF.js} +12 -12
  45. package/dist/web/server/{chat-command-palette-CdYwoTMe-4LBAZF7K.js → chat-command-palette-7gbqEnm9-6XICQZN7.js} +4 -4
  46. package/dist/web/server/{chunk-EBCYJSBR.js → chunk-7SD2ZSEA.js} +3 -3
  47. package/dist/web/server/{chunk-6AW2XFTZ.js → chunk-BBBS5AS7.js} +194 -10
  48. package/dist/web/server/{chunk-EOVMHQ2H.js → chunk-CNXJN5HN.js} +7 -7
  49. package/dist/web/server/{chunk-XKKXXG6N.js → chunk-DI7XRK2H.js} +2 -2
  50. package/dist/web/server/{chunk-KNJUSMBT.js → chunk-DZ2IC3FH.js} +2 -2
  51. package/dist/web/server/{chunk-K5ESTT4A.js → chunk-EAE2QXRK.js} +11 -3
  52. package/dist/web/server/{chunk-VHBFU4TG.js → chunk-EH4BZ6QM.js} +123 -16
  53. package/dist/web/server/{chunk-54II5QXQ.js → chunk-ETTDO4UO.js} +6 -3
  54. package/dist/web/server/{chunk-EHNIN245.js → chunk-GETSIVNU.js} +5 -5
  55. package/dist/web/server/{chunk-X2R5YPQZ.js → chunk-IJPCV6DH.js} +3 -2
  56. package/dist/web/server/{chunk-AJJ2S5FF.js → chunk-JQDLJ7KI.js} +20 -20
  57. package/dist/web/server/{chunk-M564UIEY.js → chunk-KELEYPVK.js} +8 -8
  58. package/dist/web/server/{chunk-OB5OSYJK.js → chunk-LXJD6IGW.js} +53 -29
  59. package/dist/web/server/chunk-M5RSPNYN.js +22 -0
  60. package/dist/web/server/{chunk-2TRF62K5.js → chunk-N6NOTURM.js} +3 -3
  61. package/dist/web/server/{chunk-5XX7RKJ6.js → chunk-NXJ32VFR.js} +5 -5
  62. package/dist/web/server/{chunk-P7H2OPKF.js → chunk-PIOFZUOU.js} +9 -1
  63. package/dist/web/server/chunk-PTCKOR23.js +39 -0
  64. package/dist/web/server/chunk-SZJUXVWG.js +40 -0
  65. package/dist/web/server/{chunk-TCH3U26B.js → chunk-TWYHHG5N.js} +2 -10
  66. package/dist/web/server/{chunk-FED63ZHU.js → chunk-WHBVQJ4K.js} +11 -6
  67. package/dist/web/server/{chunk-ANL4B775.js → chunk-XLNCWZMZ.js} +2 -2
  68. package/dist/web/server/{chunk-GM32R7JW.js → chunk-YKVGQBZA.js} +10 -8
  69. package/dist/web/server/{chunk-NRDG2ISJ.js → chunk-YNUWKMNQ.js} +59 -52
  70. package/dist/web/server/{chunk-3PIUR75R.js → chunk-YXXRP3NS.js} +9 -9
  71. package/dist/web/server/fleet-message-queue-BS-cZ56r-GLZXUKDF.js +152 -0
  72. package/dist/web/server/{fleet-pi-tool-renderer-RIeVlM9J-D6MISCQC.js → fleet-pi-tool-renderer-umKunT3K-O4GBH35V.js} +5 -5
  73. package/dist/web/server/{fleet-reasoning-panel-D_K48UOT-GN2PA6KT.js → fleet-reasoning-panel-BxC2COxi-GPLMQSTT.js} +9 -9
  74. package/dist/web/server/{fleet-subagent-list-5luKtpds-3OTVFHJQ.js → fleet-subagent-list-CpSYzCIq-JDH2LHSS.js} +1 -1
  75. package/dist/web/server/{fleet-tool-timeline-DwRqQgOj-HUFLGMDV.js → fleet-tool-timeline-IB36_PB2-XJENTO6I.js} +1 -1
  76. package/dist/web/server/{fork-picker-dialog-B9yGc8Au-IL63F2DN.js → fork-picker-dialog-DlINz9UQ-UWIXFDJX.js} +3 -3
  77. package/dist/web/server/{model-selector-list-cb2-ewoA-KIVU6LD3.js → model-selector-list-jBTZc2Mu-OXJOS5NA.js} +7 -7
  78. package/dist/web/server/{openui-renderer-Czy5Vo2L-BLXPJD65.js → openui-renderer-Biyyjhjn-SJOEKPTJ.js} +4 -4
  79. package/dist/web/server/{prompt-suggestions-BOmHi4-J-C563CHFU.js → prompt-suggestions-D4AEEY7K-RZSP7PWV.js} +3 -3
  80. package/dist/web/server/{repl-panel-yyxhwe4c-7U6HXYPS.js → repl-panel-D5KsoJik-BQKO53JO.js} +9 -9
  81. package/dist/web/server/{resources-panel-j4fAIi9g-5C267IXV.js → resources-panel-CE5pD4vy-V5FXBTYC.js} +2 -2
  82. package/dist/web/server/{router-CgSWrAYc-5UBYSZTJ.js → router-DWALoX_M-I5CBL7FQ.js} +1461 -515
  83. package/dist/web/server/{routes-CP_BBhD0-BAWQYGKY.js → routes-BBVirYfL-5WXMOT7M.js} +908 -566
  84. package/dist/web/server/server.js +2 -2
  85. package/dist/web/server/{session-insights-panel-BHZyKDTo-LRMAMA3O.js → session-insights-panel-CUhdgvRt-VB2HUCQP.js} +4 -4
  86. package/dist/web/server/{settings-dialog-CxHe8nym-KKXDPTIW.js → settings-dialog-DndPbM-v-RFNXXEAN.js} +902 -575
  87. package/dist/web/server/{subagent-chat-panel-DlSsM59d-DBW7DI3T.js → subagent-chat-panel-C0RpnXav-J7VYO7QY.js} +11 -11
  88. package/dist/web/server/{subagents-panel-Txzk8oDb-NRKLASYN.js → subagents-panel-jDqIn9Nh-YEV6QLUY.js} +13 -13
  89. package/dist/web/server/{workspace-panel-BOs-58Cv-SWQBMZSB.js → workspace-panel-CX_Vo6As-EWO6AMPI.js} +5 -4
  90. package/package.json +1 -1
  91. package/dist/web/client/assets/agent-activity-CDcDNVC6.js +0 -1
  92. package/dist/web/client/assets/artifacts-panel-CObI-DMt.js +0 -1
  93. package/dist/web/client/assets/chat-C8XDWWXz.js +0 -52
  94. package/dist/web/client/assets/fleet-message-queue-zWM5-y18.js +0 -1
  95. package/dist/web/client/assets/fleet-reasoning-panel-MtbSdoCv.js +0 -1
  96. package/dist/web/client/assets/fleet-tool-timeline-COzPrwr8.js +0 -1
  97. package/dist/web/client/assets/generative-text-renderer-CDKHv-v_.js +0 -6
  98. package/dist/web/client/assets/globals-DisQPFtV.css +0 -2
  99. package/dist/web/client/assets/horizontal-resize-Bl3AyBJg.js +0 -1
  100. package/dist/web/client/assets/input-group-BFDsCVS9.js +0 -1
  101. package/dist/web/client/assets/model-selector-list-DQaPFUYt.js +0 -1
  102. package/dist/web/client/assets/prompt-input-DZ4cho_e.js +0 -1
  103. package/dist/web/client/assets/prompt-suggestions-CXM6knMK.js +0 -1
  104. package/dist/web/client/assets/repl-panel-BDQ5MEgV.js +0 -1
  105. package/dist/web/client/assets/right-panel-registry-VVRPktV3.js +0 -2
  106. package/dist/web/client/assets/routes-DAikUNzO.js +0 -16
  107. package/dist/web/client/assets/session-insights-panel-CQdT90Op.js +0 -1
  108. package/dist/web/client/assets/settings-dialog-DS64KmwC.js +0 -1
  109. package/dist/web/client/assets/skeleton-loaders-DjGv9LLA.js +0 -1
  110. package/dist/web/client/assets/subagent-transcript-cxVKYnh9.js +0 -1
  111. package/dist/web/client/assets/subagents-panel-CHC96I68.js +0 -1
  112. package/dist/web/client/assets/tool-result-Baj3CdL_.js +0 -4
  113. package/dist/web/client/assets/ui-preferences-DUEFuJFj.js +0 -1
  114. package/dist/web/server/chunk-QK4CHLVV.js +0 -17
  115. package/dist/web/server/fleet-message-queue-BxycG26e-2XLOTCLP.js +0 -75
@@ -1,4 +1,7 @@
1
1
  import {
2
+ ChatMcpDeleteRequestSchema,
3
+ ChatMcpOAuthLoginRequestSchema,
4
+ ChatMcpUpsertRequestSchema,
2
5
  ChatModelsDiscoverRequestSchema,
3
6
  ChatProviderOAuthLoginRequestSchema,
4
7
  ChatProviderRemoveRequestSchema,
@@ -12,18 +15,21 @@ import {
12
15
  domAnimation,
13
16
  initAnalytics,
14
17
  isServer
15
- } from "./chunk-VHBFU4TG.js";
18
+ } from "./chunk-EH4BZ6QM.js";
16
19
  import {
17
20
  DEFAULT_UI_PREFERENCES,
18
21
  isModelPatternEnabled,
19
22
  readUiPreferences
20
- } from "./chunk-54II5QXQ.js";
23
+ } from "./chunk-ETTDO4UO.js";
21
24
  import {
22
25
  Toaster
23
26
  } from "./chunk-KOUEJ3NU.js";
24
27
  import {
28
+ fallbackSessionLabel,
29
+ meaningfulSessionLabel,
25
30
  redactSessionLabelSecrets
26
- } from "./chunk-QK4CHLVV.js";
31
+ } from "./chunk-PTCKOR23.js";
32
+ import "./chunk-SZJUXVWG.js";
27
33
  import "./chunk-TNL4YV63.js";
28
34
  import {
29
35
  drag,
@@ -36,7 +42,7 @@ import {
36
42
  CardHeader,
37
43
  CardTitle,
38
44
  validateAndNormalizeOpenUIHtmlArtifact
39
- } from "./chunk-K5ESTT4A.js";
45
+ } from "./chunk-EAE2QXRK.js";
40
46
  import {
41
47
  ChatNewRequestSchema,
42
48
  ChatOpenUIArtifactUpsertRequestSchema,
@@ -54,8 +60,9 @@ import {
54
60
  ProjectRenameRequestSchema,
55
61
  SessionIdSchema,
56
62
  UploadedAttachmentSchema,
63
+ ZodError,
57
64
  external_exports
58
- } from "./chunk-NRDG2ISJ.js";
65
+ } from "./chunk-YNUWKMNQ.js";
59
66
  import {
60
67
  KNOWN_PROVIDERS,
61
68
  OPENAI_CHAT_COMPLETIONS_PROVIDER_ID,
@@ -89,7 +96,7 @@ import "./chunk-JDCCC7A7.js";
89
96
  import "./chunk-L547YHC4.js";
90
97
  import "./chunk-MLKGABMK.js";
91
98
 
92
- // web/app/dist/server/assets/router-CgSWrAYc.js
99
+ // web/app/dist/server/assets/router-DWALoX_M.js
93
100
  import { useEffect, useLayoutEffect } from "react";
94
101
  import { jsx, jsxs } from "react/jsx-runtime";
95
102
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
@@ -123,7 +130,8 @@ function NotFoundPage({ title = "404", description = "The requested page could n
123
130
  })
124
131
  });
125
132
  }
126
- var globals_default = "/assets/globals-DisQPFtV.css";
133
+ var globals_default = "/assets/globals-B_I1Eq1u.css";
134
+ var inter_latin_wght_normal_default = "/assets/inter-latin-wght-normal-Dx4kXJAl.woff2";
127
135
  function makeQueryClient() {
128
136
  return new QueryClient({ defaultOptions: { queries: {
129
137
  staleTime: 6e4,
@@ -137,7 +145,7 @@ function getQueryClient() {
137
145
  if (!browserQueryClient) browserQueryClient = makeQueryClient();
138
146
  return browserQueryClient;
139
147
  }
140
- var Route$27 = createRootRoute({
148
+ var Route$29 = createRootRoute({
141
149
  head: () => ({
142
150
  meta: [
143
151
  { charSet: "utf-8" },
@@ -152,6 +160,13 @@ var Route$27 = createRootRoute({
152
160
  rel: "stylesheet",
153
161
  href: globals_default
154
162
  },
163
+ {
164
+ rel: "preload",
165
+ href: inter_latin_wght_normal_default,
166
+ as: "font",
167
+ type: "font/woff2",
168
+ crossOrigin: "anonymous"
169
+ },
155
170
  {
156
171
  rel: "icon",
157
172
  href: "/favicon-dark.svg",
@@ -200,8 +215,8 @@ function RootDocument({ children }) {
200
215
  ] })]
201
216
  });
202
217
  }
203
- var $$splitComponentImporter = () => import("./routes-CP_BBhD0-BAWQYGKY.js");
204
- var Route$26 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter, "component") });
218
+ var $$splitComponentImporter = () => import("./routes-BBVirYfL-5WXMOT7M.js");
219
+ var Route$28 = createFileRoute("/")({ component: lazyRouteComponent($$splitComponentImporter, "component") });
205
220
  var ManagedAttachmentValidationError = class extends Error {
206
221
  status;
207
222
  constructor(message, status) {
@@ -1950,6 +1965,34 @@ var defaultPersistence = {
1950
1965
  };
1951
1966
  var TOKEN_TTL_MS = 6e5;
1952
1967
  var MAX_DIRECTORY_ENTRIES = 200;
1968
+ var SENSITIVE_HOME_DIRECTORY_NAMES = [
1969
+ ".ssh",
1970
+ ".gnupg",
1971
+ ".aws",
1972
+ ".pki",
1973
+ ".docker",
1974
+ ".kube",
1975
+ ".azure",
1976
+ ".gcloud",
1977
+ ".gsutil",
1978
+ ".password-store",
1979
+ ".config/gh"
1980
+ ];
1981
+ function isSameOrWithin(candidate, ancestor) {
1982
+ return candidate === ancestor || candidate.startsWith(`${ancestor}${sep}`);
1983
+ }
1984
+ async function assertAllowedProjectDirectory(canonical) {
1985
+ if (dirname(canonical) === canonical) throw new Error("Project path must not be a filesystem root");
1986
+ const home = await realpath(homedir()).catch(() => homedir());
1987
+ if (canonical === home) throw new Error("Project path must not be the home directory");
1988
+ for (const name of SENSITIVE_HOME_DIRECTORY_NAMES) if (isSameOrWithin(canonical, join(home, name))) throw new Error("Project path must not be a credentials directory");
1989
+ const xdgConfigHome = process.env.XDG_CONFIG_HOME;
1990
+ if (xdgConfigHome && isAbsolute(xdgConfigHome)) {
1991
+ const canonicalConfigHome = await realpath(xdgConfigHome).catch(() => resolve(xdgConfigHome));
1992
+ if (isSameOrWithin(canonical, join(canonicalConfigHome, "gh"))) throw new Error("Project path must not be a credentials directory");
1993
+ }
1994
+ if (isSameOrWithin(canonical, await realpath("/etc").catch(() => "/etc"))) throw new Error("Project path must not be a system directory");
1995
+ }
1953
1996
  var ProjectRegistryPersistenceError = class extends Error {
1954
1997
  constructor(operation, cause) {
1955
1998
  super(`Could not ${operation}. Please try again.`, { cause });
@@ -2047,6 +2090,7 @@ var ProjectRegistry = class {
2047
2090
  if (!candidate || !isAbsolute(candidate)) throw new Error("Project path must be absolute");
2048
2091
  const canonical = await realpath(candidate);
2049
2092
  if (!(await stat(canonical)).isDirectory()) throw new Error("Project path must be a directory");
2093
+ await assertAllowedProjectDirectory(canonical);
2050
2094
  return canonical;
2051
2095
  }
2052
2096
  #findByPath(canonicalPath) {
@@ -2167,7 +2211,7 @@ var ProjectRegistry = class {
2167
2211
  if (options.token) {
2168
2212
  const token = this.#directoryTokens.get(options.token);
2169
2213
  if (!token || token.expiresAt < Date.now()) throw new Error("Directory token expired");
2170
- path = token.path;
2214
+ path = await this.#canonicalDirectory(token.path);
2171
2215
  } else path = await this.#canonicalDirectory(options.path ?? this.#initialPath);
2172
2216
  const entries = await readdir(path, { withFileTypes: true });
2173
2217
  const directories = [];
@@ -4689,6 +4733,13 @@ function fallbackSessionPath(connectionState, cwd) {
4689
4733
  const sessionDir = connectionState.sessionDir ?? sessionDirectoryForCwd(cwd);
4690
4734
  return resolve(sessionDir, `${connectionState.sessionId}.jsonl`);
4691
4735
  }
4736
+ var SessionExportPathError = class extends Error {
4737
+ status = 400;
4738
+ constructor() {
4739
+ super("Export path must stay inside the session project directory");
4740
+ this.name = "SessionExportPathError";
4741
+ }
4742
+ };
4692
4743
  function findSessionTreeEntry(nodes, entryId) {
4693
4744
  for (const node of nodes) {
4694
4745
  if (node.entry.id === entryId) return node.entry;
@@ -5094,10 +5145,7 @@ var PrimeBridge = class {
5094
5145
  async resumeSessionById(sessionId, requestedProjectId, options) {
5095
5146
  const live = this.#sessions.get(sessionId);
5096
5147
  if (live) {
5097
- if (requestedProjectId && live.projectId !== requestedProjectId) {
5098
- const forkedId = await this.forkSessionIntoProject(sessionId, requestedProjectId);
5099
- return this.#requireSession(forkedId);
5100
- }
5148
+ if (requestedProjectId && live.projectId !== requestedProjectId) return void 0;
5101
5149
  return live;
5102
5150
  }
5103
5151
  const match = (await this.#sessionLister()).find((info) => info.sessionId === sessionId || info.id === sessionId);
@@ -5110,10 +5158,7 @@ var PrimeBridge = class {
5110
5158
  if (isSessionNotResumableError(error)) return void 0;
5111
5159
  throw error;
5112
5160
  }
5113
- if (requestedProjectId && resumed.projectId !== requestedProjectId) {
5114
- const forkedId = await this.forkSessionIntoProject(resumed.sessionId, requestedProjectId);
5115
- return this.#requireSession(forkedId);
5116
- }
5161
+ if (requestedProjectId && resumed.projectId !== requestedProjectId) return void 0;
5117
5162
  return resumed;
5118
5163
  }
5119
5164
  async listSessions(cwd) {
@@ -5586,9 +5631,14 @@ var PrimeBridge = class {
5586
5631
  async followUp(sessionId, text) {
5587
5632
  await this.#requireSession(sessionId).connection.followUp(text);
5588
5633
  }
5589
- async deleteQueuedMessage(sessionId, lane, index, expectedText) {
5634
+ async mutateQueuedMessage(sessionId, lane, index, expectedText, mutation) {
5590
5635
  const session = this.#requireSession(sessionId);
5591
- const status = await session.connection.mutateQueuedMessage(lane, index, expectedText, { type: "delete" });
5636
+ const upstreamMutation = mutation.type === "delete" ? { type: "delete" } : {
5637
+ type: "replace",
5638
+ text: mutation.text,
5639
+ lane: mutation.lane ?? lane
5640
+ };
5641
+ const status = await session.connection.mutateQueuedMessage(lane, index, expectedText, upstreamMutation);
5592
5642
  const queue = await session.connection.getQueue();
5593
5643
  return {
5594
5644
  status,
@@ -5598,6 +5648,9 @@ var PrimeBridge = class {
5598
5648
  }
5599
5649
  };
5600
5650
  }
5651
+ async deleteQueuedMessage(sessionId, lane, index, expectedText) {
5652
+ return this.mutateQueuedMessage(sessionId, lane, index, expectedText, { type: "delete" });
5653
+ }
5601
5654
  async abort(sessionId) {
5602
5655
  const session = this.#requireSession(sessionId);
5603
5656
  this.#dialogs.cancelAll(sessionId, "user-abort");
@@ -5714,15 +5767,43 @@ var PrimeBridge = class {
5714
5767
  /** /export — write the session to HTML (default) or JSONL (path ends with .jsonl). */
5715
5768
  async exportSession(sessionId, outputPath) {
5716
5769
  const session = this.#requireSession(sessionId);
5770
+ const confinedPath = await this.#confineExportPath(session, outputPath);
5717
5771
  if (outputPath?.endsWith(".jsonl")) return {
5718
- path: await session.connection.exportToJsonl(outputPath),
5772
+ path: await session.connection.exportToJsonl(confinedPath),
5719
5773
  format: "jsonl"
5720
5774
  };
5721
5775
  return {
5722
- path: await session.connection.exportToHtml(outputPath),
5776
+ path: await session.connection.exportToHtml(confinedPath),
5723
5777
  format: "html"
5724
5778
  };
5725
5779
  }
5780
+ /**
5781
+ * Confine an explicit /export path inside the session's project directory
5782
+ * (the connection's live cwd). Absolute paths and `..` escapes that leave
5783
+ * the directory throw a 400-status error; an omitted path passes through
5784
+ * untouched so the default export location is unchanged.
5785
+ */
5786
+ async #confineExportPath(session, outputPath) {
5787
+ if (outputPath === void 0) return void 0;
5788
+ const { cwd } = await session.connection.getState();
5789
+ const root2 = resolve(cwd);
5790
+ const canonicalRoot = await realpath(root2).catch(() => root2);
5791
+ const candidate = resolve(root2, outputPath);
5792
+ const existingCandidate = await realpath(candidate).catch(() => void 0);
5793
+ if (existingCandidate && existingCandidate !== canonicalRoot && !existingCandidate.startsWith(`${canonicalRoot}${sep}`)) throw new SessionExportPathError();
5794
+ let ancestor = dirname(candidate);
5795
+ while (true) {
5796
+ const resolvedAncestor = await realpath(ancestor).catch(() => void 0);
5797
+ if (resolvedAncestor) {
5798
+ if (resolvedAncestor !== canonicalRoot && !resolvedAncestor.startsWith(`${canonicalRoot}${sep}`)) throw new SessionExportPathError();
5799
+ break;
5800
+ }
5801
+ const parent = dirname(ancestor);
5802
+ if (parent === ancestor) throw new SessionExportPathError();
5803
+ ancestor = parent;
5804
+ }
5805
+ return candidate;
5806
+ }
5726
5807
  /** /reload — re-scan keybindings, extensions, skills, prompts, themes. */
5727
5808
  async reloadResources(_sessionId) {
5728
5809
  await Promise.all([...this.#sessions.values()].map((session) => session.connection.reload?.()));
@@ -5815,9 +5896,9 @@ var PrimeBridge = class {
5815
5896
  }
5816
5897
  /** Fork the complete persisted history into another registered project. */
5817
5898
  async forkSessionIntoProject(sessionId, targetProjectId) {
5899
+ const targetCwd = await getPrimeConfig().projectRegistry.cwdForProject(targetProjectId);
5818
5900
  const source = this.#sessions.get(sessionId) ?? await this.resumeSessionById(sessionId);
5819
5901
  if (!source) throw new Error(`Unknown session: ${sessionId}`);
5820
- const targetCwd = await getPrimeConfig().projectRegistry.cwdForProject(targetProjectId);
5821
5902
  await source.connection.waitForIdle();
5822
5903
  const sourceState = await source.connection.getState();
5823
5904
  const sourcePath = sourceState.sessionFile;
@@ -5838,7 +5919,7 @@ var PrimeBridge = class {
5838
5919
  await copyManagedPlanPresentationsForFork(source, forked, (await this.getMessages(forked.sessionId)).length);
5839
5920
  forked.mapperState.presentation = source.mapperState.presentation;
5840
5921
  this.#persistPresentation(forked.sessionId, forked.sessionPath, forked.mapperState.presentation);
5841
- return forked.sessionId;
5922
+ return this.#requireProjectSession(forked.sessionId, targetProjectId).sessionId;
5842
5923
  }
5843
5924
  /** Reads message history from a live session, or from the JSONL transcript for a cold session. */
5844
5925
  async getMessages(sessionId) {
@@ -5880,6 +5961,17 @@ var PrimeBridge = class {
5880
5961
  if (!session) throw new Error(`Unknown session: ${sessionId}`);
5881
5962
  return session;
5882
5963
  }
5964
+ /**
5965
+ * Project-membership gate over the bridge's session records. Returns the
5966
+ * live session only when its project linkage matches the caller-asserted
5967
+ * project, and throws `Unknown session` otherwise so foreign sessions are
5968
+ * indistinguishable from missing ones.
5969
+ */
5970
+ #requireProjectSession(sessionId, projectId) {
5971
+ const session = this.#requireSession(sessionId);
5972
+ if (session.projectId !== projectId) throw new Error(`Unknown session: ${sessionId}`);
5973
+ return session;
5974
+ }
5883
5975
  };
5884
5976
  var globalStore = globalThis;
5885
5977
  function getBridge() {
@@ -5890,7 +5982,20 @@ function getBridge() {
5890
5982
  });
5891
5983
  return globalStore.__primeBridge;
5892
5984
  }
5985
+ function isLoopbackHostname(hostname) {
5986
+ const host = hostname.toLowerCase().replace(/\.$/, "");
5987
+ if (host === "localhost") return true;
5988
+ const bare = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
5989
+ const mapped = bare.startsWith("::ffff:") ? bare.slice(7) : bare;
5990
+ if (mapped === "::1" || mapped === "0:0:0:0:0:0:0:1") return true;
5991
+ if (mapped.startsWith("127.")) {
5992
+ const parts = mapped.split(".");
5993
+ if (parts.length === 4 && parts.every((p) => /^\d{1,3}$/.test(p) && Number(p) <= 255)) return true;
5994
+ }
5995
+ return false;
5996
+ }
5893
5997
  function getResponseStatus(error) {
5998
+ if (error instanceof ZodError || error instanceof Error && error.name === "ZodError") return 422;
5894
5999
  if (error && typeof error === "object" && "status" in error) {
5895
6000
  const status = error.status;
5896
6001
  if (typeof status === "number" && status >= 400 && status < 600) return status;
@@ -5907,8 +6012,14 @@ function getErrorMessage(error) {
5907
6012
  }
5908
6013
  }
5909
6014
  function safeErrorMessage(error) {
5910
- return getErrorMessage(error).replace(/(['"])(\/(?!\/)[^'"\n]*|[A-Za-z]:\\[^'"\n]*)\1/g, "$1[local path]$1").replace(/(^|[\s'"(])\/(?!\/)[^'"\s)]+/g, "$1[local path]").replace(/[A-Za-z]:\\[^'"\s)]+/g, "[local path]");
6015
+ const message = getErrorMessage(error);
6016
+ return SECRET_PATTERNS.reduce((scrubbed, [pattern, replacement]) => scrubbed.replace(pattern, replacement), message.replace(/(['"])(\/(?!\/)[^'"\n]*|[A-Za-z]:\\[^'"\n]*)\1/g, "$1[local path]$1").replace(/(^|[\s'"(])\/(?!\/)[^'"\s)]+/g, "$1[local path]").replace(/[A-Za-z]:\\[^'"\s)]+/g, "[local path]"));
5911
6017
  }
6018
+ var SECRET_PATTERNS = [
6019
+ [/\b(bearer\s+)[A-Za-z0-9\-._~+/=]{8,}/gi, "$1[redacted]"],
6020
+ [/(api[_-]?key\s*[:=]\s*)["']?[^"'\s;,]+["']?/gi, "$1[redacted]"],
6021
+ [/\bkey(\s*=\s*)[^"'\s;&,]+/gi, "key$1[redacted]"]
6022
+ ];
5912
6023
  var DAEMON_DISCONNECT_PATTERNS = [/cannot send daemon command/i, /daemon is not connected/i];
5913
6024
  function isDaemonDisconnectError(error) {
5914
6025
  const message = getErrorMessage(error);
@@ -5928,14 +6039,43 @@ function chatErrorEnvelope(error) {
5928
6039
  message: safeErrorMessage(error)
5929
6040
  };
5930
6041
  }
5931
- function wrapApiHandler(handler) {
6042
+ function isOriginAllowed(request) {
6043
+ const origin = request.headers.get("origin");
6044
+ if (!origin) return true;
6045
+ let originUrl;
6046
+ try {
6047
+ originUrl = new URL(origin);
6048
+ } catch {
6049
+ return false;
6050
+ }
6051
+ if (isLoopbackHostname(originUrl.hostname)) return true;
6052
+ try {
6053
+ return originUrl.host.toLowerCase() === new URL(request.url).host.toLowerCase();
6054
+ } catch {
6055
+ return true;
6056
+ }
6057
+ }
6058
+ function wrapApiHandler(handler, request) {
6059
+ if (request && request.method.toUpperCase() !== "GET" && !isOriginAllowed(request)) return Promise.resolve(Response.json({
6060
+ code: "UNKNOWN_ERROR",
6061
+ message: "Cross-origin request rejected."
6062
+ }, { status: 403 }));
5932
6063
  return handler().catch((error) => {
5933
6064
  const envelope = chatErrorEnvelope(error);
5934
- if (envelope.code === "NETWORK_DISCONNECTED") process.stderr.write(`[api] daemon transport failure: ${getErrorMessage(error)}
6065
+ if (envelope.code === "NETWORK_DISCONNECTED") process.stderr.write(`[api] daemon transport failure: ${safeErrorMessage(error)}
5935
6066
  `);
5936
6067
  return Response.json(envelope, { status: getResponseStatus(error) });
5937
6068
  });
5938
6069
  }
6070
+ async function requireProjectSession(session) {
6071
+ if (!session?.projectId) return false;
6072
+ try {
6073
+ await getPrimeConfig().projectRegistry.get(session.projectId);
6074
+ } catch {
6075
+ return false;
6076
+ }
6077
+ return true;
6078
+ }
5939
6079
  function resolveChatStreamingBehavior(streamingBehavior) {
5940
6080
  return streamingBehavior ?? "steer";
5941
6081
  }
@@ -5944,7 +6084,8 @@ function chooseChatStartId(mapperState, streamingBehavior, sessionIsStreaming =
5944
6084
  return crypto.randomUUID();
5945
6085
  }
5946
6086
  async function handleChatChildPost(bridge, parentSessionId, childId, message, streamingBehavior) {
5947
- if (!(bridge.getSession(parentSessionId) ?? await bridge.resumeSessionById(parentSessionId, void 0, {}))) return Response.json({ message: `Unknown session: ${parentSessionId}` }, { status: 404 });
6087
+ const session = bridge.getSession(parentSessionId) ?? await bridge.resumeSessionById(parentSessionId, void 0, {});
6088
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${parentSessionId}` }, { status: 404 });
5948
6089
  const encoder = new TextEncoder();
5949
6090
  const stream = new ReadableStream({ start(controller) {
5950
6091
  let closed = false;
@@ -6032,7 +6173,7 @@ function handleChatPost(request) {
6032
6173
  if (process.env.PRIME_BRIDGE_DEBUG === "1") process.stderr.write(`[chat] received session=${targetSessionId.slice(0, 8)} bytes=${message.length}
6033
6174
  `);
6034
6175
  const session = bridge.getSession(targetSessionId) ?? await bridge.resumeSessionById(targetSessionId, void 0, { openUI });
6035
- if (!session) return Response.json({ message: `Unknown session: ${targetSessionId}` }, { status: 404 });
6176
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${targetSessionId}` }, { status: 404 });
6036
6177
  const uploadAttachments = (body.attachments ?? []).filter((attachment) => attachment.kind === "upload");
6037
6178
  let uploadInspections;
6038
6179
  try {
@@ -6186,7 +6327,7 @@ ${attachmentContext.join("\n\n")}` : message;
6186
6327
  Connection: "keep-alive",
6187
6328
  "X-Accel-Buffering": "no"
6188
6329
  } });
6189
- });
6330
+ }, request);
6190
6331
  }
6191
6332
  var BodySchema$3 = external_exports.object({
6192
6333
  sessionId: SessionIdSchema.optional(),
@@ -6199,6 +6340,7 @@ function handleChatAbortPost(request) {
6199
6340
  const sessionId = body.sessionId;
6200
6341
  if (!sessionId) return Response.json({ message: "abort requires sessionId" }, { status: 400 });
6201
6342
  const bridge = getBridge();
6343
+ if (!await requireProjectSession(bridge.getSession(sessionId) ?? await bridge.resumeSessionById(sessionId))) return Response.json({ message: `Unknown session: ${sessionId}` }, { status: 404 });
6202
6344
  if (body.childId) {
6203
6345
  if (!await bridge.abortRlmChild(sessionId, body.childId)) return Response.json({ message: `No active subagent turn: ${body.childId}` }, { status: 404 });
6204
6346
  return Response.json({
@@ -6212,13 +6354,33 @@ function handleChatAbortPost(request) {
6212
6354
  ok: true,
6213
6355
  sessionId
6214
6356
  });
6215
- });
6357
+ }, request);
6216
6358
  }
6217
6359
  var AttachmentIdSchema = external_exports.uuid();
6218
6360
  var ATTACHMENT_WRITE_CONCURRENCY = 8;
6361
+ function isSafeRasterImage(mimeType, data) {
6362
+ if (mimeType === "image/png") return data.length >= 8 && data.subarray(0, 8).every((byte, index) => byte === [
6363
+ 137,
6364
+ 80,
6365
+ 78,
6366
+ 71,
6367
+ 13,
6368
+ 10,
6369
+ 26,
6370
+ 10
6371
+ ][index]);
6372
+ if (mimeType === "image/jpeg") return data.length >= 3 && data[0] === 255 && data[1] === 216 && data[2] === 255;
6373
+ if (mimeType === "image/gif") {
6374
+ const header = new TextDecoder().decode(data.subarray(0, 6));
6375
+ return header === "GIF87a" || header === "GIF89a";
6376
+ }
6377
+ return mimeType === "image/webp" && data.length >= 12 && new TextDecoder().decode(data.subarray(0, 4)) === "RIFF" && new TextDecoder().decode(data.subarray(8, 12)) === "WEBP";
6378
+ }
6219
6379
  async function resolveSession(sessionId) {
6220
6380
  const bridge = getBridge();
6221
- return bridge.getSession(sessionId) ?? await bridge.resumeSessionById(sessionId);
6381
+ const session = bridge.getSession(sessionId) ?? await bridge.resumeSessionById(sessionId);
6382
+ if (!await requireProjectSession(session)) return void 0;
6383
+ return session;
6222
6384
  }
6223
6385
  function handleChatAttachmentsPost(request) {
6224
6386
  return wrapApiHandler(async () => {
@@ -6241,7 +6403,7 @@ function handleChatAttachmentsPost(request) {
6241
6403
  }
6242
6404
  const attachments = settled.flatMap((result) => result.status === "fulfilled" ? [result.value] : []);
6243
6405
  return Response.json({ attachments });
6244
- });
6406
+ }, request);
6245
6407
  }
6246
6408
  function handleChatAttachmentGet(request) {
6247
6409
  return wrapApiHandler(async () => {
@@ -6252,18 +6414,19 @@ function handleChatAttachmentGet(request) {
6252
6414
  if (!session) return Response.json({ message: `Unknown session: ${sessionId}` }, { status: 404 });
6253
6415
  const attachment = await readManagedAttachment(session, attachmentId).catch(() => void 0);
6254
6416
  if (!attachment) return Response.json({ message: "Attachment not found" }, { status: 404 });
6417
+ const safeRasterImage = isSafeRasterImage(attachment.metadata.mimeType, attachment.data);
6255
6418
  return new Response(attachment.data, { headers: {
6256
- "Content-Type": attachment.metadata.mimeType,
6257
- "Content-Disposition": `inline; filename*=UTF-8''${encodeURIComponent(attachment.metadata.name)}`,
6419
+ "Content-Type": safeRasterImage ? attachment.metadata.mimeType : "application/octet-stream",
6420
+ ...safeRasterImage ? {} : { "Content-Disposition": `attachment; filename*=UTF-8''${encodeURIComponent(attachment.metadata.name)}` },
6258
6421
  "Cache-Control": "private, no-store",
6259
6422
  "X-Content-Type-Options": "nosniff"
6260
6423
  } });
6261
- });
6424
+ }, request);
6262
6425
  }
6263
6426
  var BodySchema$2 = external_exports.object({
6264
6427
  sessionId: SessionIdSchema,
6265
6428
  command: external_exports.string(),
6266
- args: external_exports.string().optional()
6429
+ args: external_exports.string().max(1024).optional()
6267
6430
  });
6268
6431
  function handleChatCommandPost(request) {
6269
6432
  return wrapApiHandler(async () => {
@@ -6338,7 +6501,7 @@ function handleChatCommandPost(request) {
6338
6501
  default:
6339
6502
  return Response.json({ message: `Unknown slash command: /${body.command}` }, { status: 404 });
6340
6503
  }
6341
- });
6504
+ }, request);
6342
6505
  }
6343
6506
  async function cwdForRequest(request) {
6344
6507
  const projectIdParam = new URL(request.url).searchParams.get("projectId");
@@ -6556,7 +6719,7 @@ function handleChatCommandsGet(request) {
6556
6719
  ],
6557
6720
  diagnostics
6558
6721
  });
6559
- });
6722
+ }, request);
6560
6723
  }
6561
6724
  function shouldReplaySseEvent(event, pendingQuestionIds) {
6562
6725
  if (!pendingQuestionIds) return true;
@@ -6705,7 +6868,10 @@ function handleChatEventsGet(request) {
6705
6868
  const childId = SessionIdSchema.safeParse(childIdParam);
6706
6869
  if (!parentSessionId.success || !childId.success) return Response.json({ message: "Invalid subagent stream identifiers" }, { status: 400 });
6707
6870
  if (streamGenerationParam !== null && !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(streamGenerationParam)) return Response.json({ message: "Invalid child stream generation" }, { status: 400 });
6708
- const childStream = await getBridge().openRlmChildStream(parentSessionId.data, childId.data, {
6871
+ const bridge = getBridge();
6872
+ const parent = bridge.getSession(parentSessionId.data) ?? await bridge.resumeSessionById(parentSessionId.data);
6873
+ if (!parent || !await requireProjectSession(parent)) return Response.json({ message: "Unknown live subagent stream" }, { status: 404 });
6874
+ const childStream = await bridge.openRlmChildStream(parentSessionId.data, childId.data, {
6709
6875
  ...streamGenerationParam ? { streamGeneration: streamGenerationParam } : {},
6710
6876
  lastEventId: lastEventIdFor(request, url)
6711
6877
  });
@@ -6725,367 +6891,13 @@ function handleChatEventsGet(request) {
6725
6891
  if (!sessionIdParam) return Response.json({ message: "SSE requires ?sessionId=" }, { status: 400 });
6726
6892
  const sessionId = SessionIdSchema.safeParse(sessionIdParam);
6727
6893
  if (!sessionId.success) return Response.json({ message: "Invalid session identifier" }, { status: 400 });
6728
- return createSseResponse(request, getBridge(), { sessionId: sessionId.data }, lastEventIdFor(request, url));
6729
- });
6730
- }
6731
- var BodySchema$1 = external_exports.object({
6732
- sessionId: SessionIdSchema,
6733
- model: external_exports.object({
6734
- provider: external_exports.string(),
6735
- id: external_exports.string()
6736
- })
6737
- });
6738
- function handleChatModelPost(request) {
6739
- return wrapApiHandler(async () => {
6740
- const raw = await request.json().catch(() => ({}));
6741
- const body = BodySchema$1.parse(raw);
6742
- await getBridge().setModel(body.sessionId, {
6743
- provider: body.model.provider,
6744
- id: body.model.id
6745
- });
6746
- return Response.json({ ok: true });
6747
- });
6748
- }
6749
- function toPatternCandidate(model) {
6750
- return {
6751
- id: model.id,
6752
- name: model.name,
6753
- provider: model.provider,
6754
- modelId: model.id,
6755
- key: `${model.provider}/${model.id}`
6756
- };
6757
- }
6758
- function handleChatModelsGet(request) {
6759
- return wrapApiHandler(async () => {
6760
- const config = getPrimeConfig();
6761
- const cwd = await cwdForRequest(request);
6762
- const settings = config.settingsFor(cwd);
6763
- const registry = config.modelRegistry;
6764
- const scope = new URL(request.url).searchParams.get("scope") === "all" ? "all" : "enabled";
6765
- let models = registry.getAll();
6766
- if (scope === "enabled") {
6767
- models = models.filter((model) => registry.hasConfiguredAuth(model));
6768
- const patterns = settings.getEnabledModels();
6769
- if (patterns !== void 0) models = models.filter((model) => isModelPatternEnabled(toPatternCandidate(model), patterns));
6770
- }
6771
- const diagnostics = [];
6772
- const defaultProvider = settings.getDefaultProvider();
6773
- const defaultModel = settings.getDefaultModel();
6774
- const defaultThinkingLevel = settings.getDefaultThinkingLevel();
6775
- const selectedModelKey = defaultProvider && defaultModel ? `${defaultProvider}/${defaultModel}` : void 0;
6776
- return Response.json({
6777
- models: models.map((m) => {
6778
- const thinkingLevels = getSupportedThinkingLevels(m);
6779
- const fallback = m.reasoning ? "medium" : "off";
6780
- return {
6781
- key: `${m.provider}/${m.id}`,
6782
- provider: m.provider,
6783
- id: m.id,
6784
- name: m.name,
6785
- reasoning: m.reasoning,
6786
- input: m.input,
6787
- contextWindow: m.contextWindow,
6788
- maxTokens: m.maxTokens,
6789
- available: registry.hasConfiguredAuth(m),
6790
- thinkingLevels,
6791
- defaultThinkingLevel: thinkingLevels.includes(fallback) ? fallback : thinkingLevels[0] ?? "off"
6792
- };
6793
- }),
6794
- ...selectedModelKey ? { selectedModelKey } : {},
6795
- ...defaultProvider ? { defaultProvider } : {},
6796
- ...defaultModel ? { defaultModel } : {},
6797
- ...defaultThinkingLevel ? { defaultThinkingLevel } : {},
6798
- diagnostics
6799
- });
6800
- });
6801
- }
6802
- function storedApiForCustomProvider(api) {
6803
- return api === "google-genai" ? "google-generative-ai" : api;
6804
- }
6805
- function uiApiForCustomProvider(value) {
6806
- if (value === "google-generative-ai") return "google-genai";
6807
- return isPiCustomProviderApi(value) ? value : void 0;
6808
- }
6809
- function envVarNameForManagedProvider(providerId) {
6810
- if (providerId === "openai-chat-completions") return "OPENAI_CHAT_COMPLETIONS_API_KEY";
6811
- const slug = providerId.slice(providerId.indexOf("+") + 1).toUpperCase().replace(/[^A-Z0-9]+/g, "_");
6812
- return isOccProviderId(providerId) ? `OPENAI_CHAT_COMPLETIONS_${slug}_API_KEY` : `CUSTOM_${slug}_API_KEY`;
6813
- }
6814
- function readModelsJson(filePath) {
6815
- try {
6816
- if (!existsSync(filePath)) return {};
6817
- return parseModelsJson(readFileSync(filePath, "utf-8"));
6818
- } catch {
6819
- return {};
6820
- }
6821
- }
6822
- function stripJsonComments(input) {
6823
- return input.replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, (match) => match[0] === '"' ? match : "").replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (match, tail) => tail ?? (match[0] === '"' ? match : ""));
6824
- }
6825
- function parseModelsJson(raw) {
6826
- const parsed = JSON.parse(stripJsonComments(raw));
6827
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("models.json must contain an object");
6828
- return parsed;
6829
- }
6830
- function readModelsJsonForWrite(filePath) {
6831
- if (!existsSync(filePath)) return {};
6832
- try {
6833
- return parseModelsJson(readFileSync(filePath, "utf-8"));
6834
- } catch (error) {
6835
- throw new Error(`Refusing to update models.json because it could not be parsed: ${error instanceof Error ? error.message : error}`);
6836
- }
6837
- }
6838
- function mergeModelDefinitions(existing, incoming) {
6839
- const existingById = new Map((existing ?? []).map((model) => [model.id, model]));
6840
- const seen = /* @__PURE__ */ new Set();
6841
- return incoming.flatMap((model) => {
6842
- if (seen.has(model.id)) return [];
6843
- seen.add(model.id);
6844
- return [{
6845
- ...existingById.get(model.id),
6846
- ...model
6847
- }];
6848
- });
6849
- }
6850
- function listCustomProviders(filePath) {
6851
- return readModelsJson(filePath).providers ?? {};
6852
- }
6853
- function writeModelsJson(filePath, data) {
6854
- mkdirSync(dirname(filePath), {
6855
- recursive: true,
6856
- mode: 448
6857
- });
6858
- const serialized = `${JSON.stringify(data, null, " ")}
6859
- `;
6860
- const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
6861
- writeFileSync(tmpPath, serialized, "utf-8");
6862
- renameSync(tmpPath, filePath);
6863
- }
6864
- function upsertCustomProvider(filePath, providerId, input) {
6865
- const data = readModelsJsonForWrite(filePath);
6866
- const providers = { ...data.providers ?? {} };
6867
- const existing = providers[providerId] ?? {};
6868
- providers[providerId] = {
6869
- ...existing,
6870
- name: input.name ?? existing.name,
6871
- baseUrl: input.baseUrl,
6872
- api: storedApiForCustomProvider(input.api),
6873
- apiKey: input.apiKey,
6874
- models: mergeModelDefinitions(existing.models, input.models)
6875
- };
6876
- writeModelsJson(filePath, {
6877
- ...data,
6878
- providers
6879
- });
6880
- }
6881
- function addCustomProviderModelIds(filePath, providerId, modelIds) {
6882
- const data = readModelsJsonForWrite(filePath);
6883
- const providers = { ...data.providers ?? {} };
6884
- const existing = providers[providerId];
6885
- if (!existing) return;
6886
- providers[providerId] = {
6887
- ...existing,
6888
- models: mergeModelDefinitions(existing.models, [...existing.models ?? [], ...modelIds.map((id) => ({ id }))])
6889
- };
6890
- writeModelsJson(filePath, {
6891
- ...data,
6892
- providers
6893
- });
6894
- }
6895
- function removeCustomProvider(filePath, providerId) {
6896
- const data = readModelsJsonForWrite(filePath);
6897
- const providers = data.providers;
6898
- if (!providers || !(providerId in providers)) return;
6899
- const next = { ...providers };
6900
- delete next[providerId];
6901
- writeModelsJson(filePath, {
6902
- ...data,
6903
- providers: next
6904
- });
6905
- }
6906
- function isDiscoverableEntry(entry) {
6907
- const baseUrl = entry?.baseUrl;
6908
- if (!baseUrl) return false;
6909
- const api = uiApiForCustomProvider(entry?.api);
6910
- if (api && !isOccFamilyApi(api)) return false;
6911
- if (entry?.api && !api) return false;
6912
- try {
6913
- const parsed = new URL(baseUrl);
6914
- return parsed.protocol === "https:" || parsed.protocol === "http:";
6915
- } catch {
6916
- return false;
6917
- }
6918
- }
6919
- function openAiModelsUrl(baseUrl) {
6920
- const parsed = new URL(baseUrl);
6921
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error("model discovery requires an http(s) URL");
6922
- const path = parsed.pathname.replace(/\/+$/, "");
6923
- parsed.pathname = path.endsWith("/v1") ? `${path}/models` : `${path}/v1/models`;
6924
- parsed.search = "";
6925
- parsed.hash = "";
6926
- return parsed;
6927
- }
6928
- function handleChatModelsDiscoverPost(request) {
6929
- return wrapApiHandler(async () => {
6930
- const raw = await request.json().catch(() => ({}));
6931
- const body = ChatModelsDiscoverRequestSchema.parse(raw);
6932
- const config = getPrimeConfig();
6933
- const apiKey = await config.modelRegistry.getApiKeyForProvider(body.providerId);
6934
- if (!apiKey) return Response.json({
6935
- providerId: body.providerId,
6936
- models: []
6937
- });
6938
- const provider = body.providerId;
6939
- const entry = listCustomProviders(`${config.agentDir}/models.json`)[provider];
6940
- const api = uiApiForCustomProvider(entry?.api);
6941
- if (entry?.api && !api || api && !isOccFamilyApi(api)) return Response.json({
6942
- providerId: provider,
6943
- models: []
6944
- });
6945
- const baseUrl = entry?.baseUrl;
6946
- if (!baseUrl) return Response.json({
6947
- providerId: provider,
6948
- models: []
6949
- });
6950
- try {
6951
- const modelsUrl = openAiModelsUrl(baseUrl);
6952
- const response = await fetch(modelsUrl, {
6953
- headers: { Authorization: `Bearer ${apiKey}` },
6954
- signal: AbortSignal.timeout(1e4)
6955
- });
6956
- if (!response.ok) return Response.json({
6957
- providerId: provider,
6958
- models: []
6959
- });
6960
- const models = ((await response.json()).data ?? []).map((m) => ({
6961
- key: `${provider}/${m.id}`,
6962
- provider,
6963
- id: m.id,
6964
- name: m.id,
6965
- reasoning: false,
6966
- input: ["text"],
6967
- available: true,
6968
- defaultThinkingLevel: "off",
6969
- thinkingLevels: ["off"]
6970
- }));
6971
- if (models.length > 0) {
6972
- addCustomProviderModelIds(`${config.agentDir}/models.json`, provider, models.map((model) => model.id));
6973
- config.reloadAuth();
6974
- }
6975
- return Response.json({
6976
- providerId: provider,
6977
- models
6978
- });
6979
- } catch {
6980
- return Response.json({
6981
- providerId: provider,
6982
- models: []
6983
- });
6894
+ {
6895
+ const bridge = getBridge();
6896
+ const session = bridge.getSession(sessionId.data) ?? await bridge.resumeSessionById(sessionId.data);
6897
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${sessionId.data}` }, { status: 404 });
6984
6898
  }
6985
- });
6986
- }
6987
- function toThinkingLevel(level) {
6988
- return level;
6989
- }
6990
- function handleChatNewPost(request) {
6991
- return wrapApiHandler(async () => {
6992
- const bridge = getBridge();
6993
- const registry = getPrimeConfig().projectRegistry;
6994
- const raw = await request.json().catch(() => ({}));
6995
- const body = ChatNewRequestSchema.parse(raw);
6996
- const projectId = body.projectId ?? await registry.projectIdForCwd(getPrimeConfig().defaultCwd) ?? (await registry.register(getPrimeConfig().defaultCwd)).projectId;
6997
- const project = await registry.get(projectId);
6998
- bridge.ensureKernelReady(project.canonicalPath).catch(() => {
6999
- });
7000
- const session = await bridge.createSession({
7001
- cwd: project.canonicalPath,
7002
- projectId,
7003
- thinkingLevel: toThinkingLevel(body.thinkingLevel),
7004
- mode: body.mode,
7005
- openUI: body.openUI
7006
- });
7007
- if (body.model) {
7008
- await bridge.setModel(session.sessionId, body.model);
7009
- const thinking = body.model.thinkingLevel ?? body.thinkingLevel;
7010
- if (thinking) await session.connection.setThinkingLevel(toThinkingLevel(thinking));
7011
- }
7012
- return Response.json({
7013
- session: {
7014
- sessionId: session.sessionId,
7015
- projectId
7016
- },
7017
- messages: [],
7018
- planPresentations: [],
7019
- presentation: bridge.getPresentation(session.sessionId)
7020
- });
7021
- });
7022
- }
7023
- function record(value) {
7024
- return typeof value === "object" && value !== null ? value : void 0;
7025
- }
7026
- function artifactIndexFor(artifact) {
7027
- const input = record(artifact.input);
7028
- return typeof input?.artifactIndex === "number" ? input.artifactIndex : void 0;
7029
- }
7030
- function handleChatOpenUIArtifactPut(request) {
7031
- return wrapApiHandler(async () => {
7032
- const raw = await request.json().catch(() => ({}));
7033
- const parsed = ChatOpenUIArtifactUpsertRequestSchema.safeParse(raw);
7034
- if (!parsed.success) return Response.json({ message: "Invalid OpenUI artifact request." }, { status: 400 });
7035
- const body = parsed.data;
7036
- const validation = validateAndNormalizeOpenUIHtmlArtifact(body.artifact);
7037
- if (!validation.ok) return Response.json({ message: validation.reason }, { status: validation.status ?? 400 });
7038
- const bridge = getBridge();
7039
- const session = bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId);
7040
- if (!session) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
7041
- const messages = await bridge.getMessages(session.sessionId);
7042
- const incomingId = body.assistantMessageId;
7043
- const exact = messages.find((message) => message.role === "assistant" && message.id === incomingId);
7044
- const existing = bridge.getPresentation(session.sessionId).artifactRuns.flatMap((run) => run.artifacts).filter((artifact2) => artifact2.kind === "openui-html").find((artifact2) => artifactIndexFor(artifact2) === body.artifactIndex && (record(artifact2.input)?.clientMessageId === incomingId || artifact2.sourceMessageId === incomingId));
7045
- const fallback = existing ? void 0 : [...messages].reverse().find((message) => message.role === "assistant");
7046
- const assistantMessageId = exact?.id ?? existing?.sourceMessageId ?? fallback?.id;
7047
- if (!assistantMessageId) return Response.json({ message: "Unable to associate OpenUI artifact with an assistant message." }, { status: 409 });
7048
- const artifact = {
7049
- id: stablePresentationId(`${assistantMessageId}:openui-html:${body.artifactIndex}`),
7050
- runId: stablePresentationId(`${assistantMessageId}:openui`),
7051
- sourceMessageId: assistantMessageId,
7052
- kind: "openui-html",
7053
- title: validation.value.title,
7054
- status: "success",
7055
- input: {
7056
- clientMessageId: incomingId,
7057
- artifactIndex: body.artifactIndex
7058
- },
7059
- output: validation.value,
7060
- timestamp: Date.now()
7061
- };
7062
- const presentation = await bridge.upsertPresentationArtifact(session.sessionId, artifact);
7063
- return Response.json({
7064
- artifact,
7065
- presentation
7066
- });
7067
- });
7068
- }
7069
- function handleChatPlanPresentationPut(request) {
7070
- return wrapApiHandler(async () => {
7071
- const body = ChatPlanPresentationUpsertRequestSchema.parse(await request.json().catch(() => ({})));
7072
- const bridge = getBridge();
7073
- const session = bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId);
7074
- if (!session) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
7075
- const messages = await bridge.getMessages(body.sessionId);
7076
- const incomingId = body.presentation.assistantMessageId;
7077
- const known = messages.find((message) => message.id === incomingId && message.role === "assistant");
7078
- const existing = (await loadManagedPlanPresentations(session)).find((item) => item.assistantMessageId === incomingId || item.clientMessageId === incomingId);
7079
- const fallback = existing ? void 0 : [...messages].reverse().find((message) => message.role === "assistant");
7080
- const assistantMessageId = known?.id ?? existing?.assistantMessageId ?? fallback?.id;
7081
- if (!assistantMessageId) return Response.json({ message: "Unable to associate Plan presentation with an assistant message" }, { status: 409 });
7082
- const clientMessageId = incomingId !== assistantMessageId ? existing?.clientMessageId ?? incomingId : existing?.clientMessageId;
7083
- return Response.json({ presentation: await upsertManagedPlanPresentation(session, {
7084
- ...body.presentation,
7085
- assistantMessageId,
7086
- ...clientMessageId ? { clientMessageId } : {}
7087
- }) });
7088
- });
6899
+ return createSseResponse(request, getBridge(), { sessionId: sessionId.data }, lastEventIdFor(request, url));
6900
+ }, request);
7089
6901
  }
7090
6902
  var PRIME_BUTTERFLY_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 178 178" aria-hidden="true"><path fill="currentColor" d="m 123.322,84.092671 c -0.192,0.0065 -0.43,0.0147 -0.74,0.0147 l -0.018,-0.0247 c -0.873,0.1977 -1.958,0.1266 -3.067,0.0537 -3.29,-0.216 -6.799,-0.4465 -5.635,6.2824 0.259,1.4822 -1.538,1.8465 -2.73,1.9082 -3.384,0.1853 -6.78,0.2594 -10.171,0.1915 -0.641,-0.0106 -1.2979,-0.5506 -1.8904,-1.0388 -0.1022,-0.0844 -0.2034,-0.1673 -0.3016,-0.2457 -0.1052,-0.0865 0.2898,-1.2042 0.4942,-1.2166 3.3078,-0.1931 4.5068,-2.4361 5.7028,-4.6724 0.603,-1.1253 1.204,-2.2488 2.072,-3.1088 7.856,-7.7874 15.878,-15.4265 25.054,-21.7008 0.895,-0.6114 1.989,-1.1733 2.73,-0.1111 0.571,0.8205 0.036,1.2854 -0.512,1.7627 -0.24,0.2088 -0.482,0.4199 -0.637,0.6642 -0.554,0.8792 -1.538,1.5273 -2.514,2.1717 -1.92,1.2655 -3.82,2.5172 -2.408,5.4798 1.225,2.5651 0.129,3.0202 -1.555,3.7198 l -0.069,0.0287 c -0.72,0.3021 -1.458,0.5764 -2.194,0.8506 -1.568,0.5835 -3.134,1.1662 -4.537,2.0149 -1.408,0.8522 -2.081,2.5134 0.222,3.4954 5.033,2.1428 18.064,-1.2784 20.608,-5.9965 2.377,-4.4147 5.931,-7.6891 9.481,-10.9611 1.992,-1.836 3.984,-3.6712 5.767,-5.7067 3.844,-4.3849 8.344,-8.1939 12.846,-12.005 2.239,-1.8944 4.478,-3.78938 6.637,-5.75588 1.624,-1.48207 2.297,-3.43353 1.026,-5.56412 -1.267,-2.118215 -3.416,-2.402287 -5.435,-1.926802 -13.309,3.130982 -26.166,7.355122 -37.585,15.241302 -18.23,12.5919 -36.4911,25.1406 -54.9002,37.4669 -6.2743,4.2056 -10.0723,2.1491 -12.1657,-5.1318 -4.6502,-16.1676 -11.4681,-30.2664 -31.0569,-32.4587 -7.1574,-0.8028 -12.4066,3.6807 -11.3692,10.7949 0.5249,3.6065 0.0495,7.003 -1.6858,10.3872 -0.3827,0.7472 -0.7689,1.4947 -1.1556,2.243 -2.672,5.1706 -5.3671,10.3857 -7.0394,15.8885 -0.1399,0.4602 -0.3002,0.9444 -0.4654,1.4437 -1.4909,4.5052 -3.3862,10.2323 5.659,10.5492 0.3705,0.0123 1.0808,0.944799 0.9943,1.290699 -0.1976,0.8213 -0.5991,1.797 -1.2413,2.2725 -8.6705,6.3917 -15.79706,14.1294 -18.860151,24.5971 -1.550066,5.2865 -0.524909,10.7765 3.989391,14.8705 3.22984,2.928 7.35516,4.625 10.97396,2.162 3.3009,-2.247 6.9232,-3.754 10.538,-5.259 3.1819,-1.324 6.358,-2.646 9.3041,-4.468 0.7101,-0.439 1.5727,-0.841 2.4456,-1.248 2.4138,-1.1228 4.9063,-2.2843 4.4709,-4.3841 -0.6854,-3.2908 -4.1623,-6.3418 -7.0586,-8.7384 -2.8716,-2.3769 -10.1897,-18.3411 -9.3189,-22.176099 1.2193,-5.3704 3.9645,-10.0201 6.7125,-14.6744 2.3458,-3.9731 4.6935,-7.9497 6.0956,-12.3806 0.5804,-1.828 2.8037,-2.8284 4.9466,-2.0997 1.4543,0.4928 1.2068,1.8278 0.9762,3.0715 -0.0063,0.0344 -0.0127,0.0685 -0.019,0.1027 -0.8688,4.7802 -1.721,9.566 -2.5733,14.3517 -0.4261,2.3924 -0.8521,4.7847 -1.2803,7.1762 -0.2717,1.5316 0.2038,2.7544 1.7909,3.0631 3.0569,0.599 2.989,1.9762 1.6119,4.341499 -1.4636,2.5072 -1.6674,5.5703 -0.0309,7.8861 1.6427,2.3282 4.3908,2.8161 7.1821,1.4204 1.7415,-0.8646 3.094,-0.0927 3.0755,1.6612 -0.1112,9.3688 3.8103,7.2561 8.584,3.1681 0.439,-0.3763 1.0373,-0.5846 1.6152,-0.7857 0.1048,-0.0366 0.2091,-0.0728 0.3116,-0.1098 0.4529,-0.1616 0.9062,-0.3224 1.3595,-0.4833 8.986,-3.1884 17.9603,-6.3725 23.0334,-15.573099 0.392,-0.7187 1.6783,-1.0669 2.6795,-1.3377 0.057,-0.0154 0.113,-0.0307 0.1681,-0.0456 5.8013,-1.5766 11.7248,-1.6001 17.6438,-1.6237 4.445,-0.0176 8.888,-0.0352 13.277,-0.7107 4.329,-0.667 9.047,-3.1928 9.084,-7.3736 0.035,-3.3707 -2.546,-3.1612 -5.123,-2.9519 -1.115,0.0906 -2.231,0.1811 -3.134,-0.0185 -0.182,-0.0381 -0.375,-0.0316 -0.686,-0.0209 z"/><path fill="currentColor" d="m 55.1325,131.29447 c -1.0745,7.1515 1.6551,13.2715 12.8266,13.1915 h -0.0062 c 9.5413,-0.389 20.3365,-6.164 30.8838,-13.5492 6.9653,-4.8787 12.9873,-10.0236 16.8903,-17.6253 2.872,-5.5889 1.395,-10.0723 -2.933,-13.993799 -1.908,-1.7291 -3.73,-1.9205 -5.867,0.1667 -7.5842,7.423099 -17.0261,11.474299 -26.7218,15.550099 -2.4706,1.0393 -5.2847,1.5582 -8.1187,2.0809 -7.5511,1.3924 -15.2431,2.8103 -16.954,14.1791 z"/></svg>`;
7091
6903
  function escapeHtml(value) {
@@ -7214,10 +7026,10 @@ var decode$1 = (s) => atob(s);
7214
7026
  var CLIENT_ID$2 = decode$1("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
7215
7027
  var AUTHORIZE_URL$1 = "https://claude.ai/oauth/authorize";
7216
7028
  var TOKEN_URL$1 = "https://platform.claude.com/v1/oauth/token";
7217
- var CALLBACK_HOST$1 = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
7029
+ var CALLBACK_HOST$2 = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
7218
7030
  var CALLBACK_PORT = 53692;
7219
- var CALLBACK_PATH = "/callback";
7220
- var REDIRECT_URI$1 = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH}`;
7031
+ var CALLBACK_PATH$1 = "/callback";
7032
+ var REDIRECT_URI$1 = `http://localhost:${CALLBACK_PORT}${CALLBACK_PATH$1}`;
7221
7033
  var SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
7222
7034
  async function getNodeApis() {
7223
7035
  if (nodeApis) return nodeApis;
@@ -7267,7 +7079,7 @@ function formatErrorDetails(error) {
7267
7079
  }
7268
7080
  return String(error);
7269
7081
  }
7270
- async function startCallbackServer(expectedState) {
7082
+ async function startCallbackServer$1(expectedState) {
7271
7083
  const { createServer } = await getNodeApis();
7272
7084
  return new Promise((resolve2, reject) => {
7273
7085
  let settleWait;
@@ -7282,7 +7094,7 @@ async function startCallbackServer(expectedState) {
7282
7094
  const server = createServer((req, res) => {
7283
7095
  try {
7284
7096
  const url = new URL(req.url || "", "http://localhost");
7285
- if (url.pathname !== CALLBACK_PATH) {
7097
+ if (url.pathname !== CALLBACK_PATH$1) {
7286
7098
  res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
7287
7099
  res.end(oauthErrorHtml("Callback route not found."));
7288
7100
  return;
@@ -7319,7 +7131,7 @@ async function startCallbackServer(expectedState) {
7319
7131
  server.on("error", (err) => {
7320
7132
  reject(err);
7321
7133
  });
7322
- server.listen(CALLBACK_PORT, CALLBACK_HOST$1, () => {
7134
+ server.listen(CALLBACK_PORT, CALLBACK_HOST$2, () => {
7323
7135
  resolve2({
7324
7136
  server,
7325
7137
  redirectUri: REDIRECT_URI$1,
@@ -7373,7 +7185,7 @@ async function exchangeAuthorizationCode$1(code, state, verifier, redirectUri) {
7373
7185
  }
7374
7186
  async function loginAnthropic(options) {
7375
7187
  const { verifier, challenge } = await generatePKCE();
7376
- const server = await startCallbackServer(verifier);
7188
+ const server = await startCallbackServer$1(verifier);
7377
7189
  let code;
7378
7190
  let state;
7379
7191
  let redirectUriForExchange = REDIRECT_URI$1;
@@ -34448,7 +34260,7 @@ function getGitHubCopilotBaseUrl(token, enterpriseDomain) {
34448
34260
  if (enterpriseDomain) return `https://copilot-api.${enterpriseDomain}`;
34449
34261
  return "https://api.individual.githubcopilot.com";
34450
34262
  }
34451
- async function fetchJson(url, init) {
34263
+ async function fetchJson$1(url, init) {
34452
34264
  const response = await fetch(url, init);
34453
34265
  if (!response.ok) {
34454
34266
  const text = await response.text();
@@ -34457,7 +34269,7 @@ async function fetchJson(url, init) {
34457
34269
  return response.json();
34458
34270
  }
34459
34271
  async function startDeviceFlow(domain) {
34460
- const data = await fetchJson(getUrls(domain).deviceCodeUrl, {
34272
+ const data = await fetchJson$1(getUrls(domain).deviceCodeUrl, {
34461
34273
  method: "POST",
34462
34274
  headers: {
34463
34275
  Accept: "application/json",
@@ -34507,7 +34319,7 @@ async function pollForGitHubAccessToken(domain, deviceCode, intervalSeconds, exp
34507
34319
  if (signal?.aborted) throw new Error("Login cancelled");
34508
34320
  const remainingMs = deadline - Date.now();
34509
34321
  await abortableSleep(Math.min(Math.ceil(intervalMs * intervalMultiplier), remainingMs), signal);
34510
- const raw = await fetchJson(urls.accessTokenUrl, {
34322
+ const raw = await fetchJson$1(urls.accessTokenUrl, {
34511
34323
  method: "POST",
34512
34324
  headers: {
34513
34325
  Accept: "application/json",
@@ -34538,7 +34350,7 @@ async function pollForGitHubAccessToken(domain, deviceCode, intervalSeconds, exp
34538
34350
  throw new Error("Device flow timed out");
34539
34351
  }
34540
34352
  async function refreshGitHubCopilotToken(refreshToken, enterpriseDomain) {
34541
- const raw = await fetchJson(getUrls(enterpriseDomain || "github.com").copilotTokenUrl, { headers: {
34353
+ const raw = await fetchJson$1(getUrls(enterpriseDomain || "github.com").copilotTokenUrl, { headers: {
34542
34354
  Accept: "application/json",
34543
34355
  Authorization: `Bearer ${refreshToken}`,
34544
34356
  ...COPILOT_HEADERS
@@ -34638,7 +34450,7 @@ if (typeof process !== "undefined" && (process.versions?.node || process.version
34638
34450
  _http = m;
34639
34451
  });
34640
34452
  }
34641
- var CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
34453
+ var CALLBACK_HOST$1 = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
34642
34454
  var CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
34643
34455
  var AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
34644
34456
  var TOKEN_URL = "https://auth.openai.com/oauth/token";
@@ -34820,7 +34632,7 @@ function startLocalOAuthServer(state) {
34820
34632
  }
34821
34633
  });
34822
34634
  return new Promise((resolve2) => {
34823
- server.listen(1455, CALLBACK_HOST, () => {
34635
+ server.listen(1455, CALLBACK_HOST$1, () => {
34824
34636
  resolve2({
34825
34637
  close: () => server.close(),
34826
34638
  cancelWait: () => {
@@ -34946,9 +34758,652 @@ var oauthProviderRegistry = new Map([
34946
34758
  function getOAuthProvider(id) {
34947
34759
  return oauthProviderRegistry.get(id);
34948
34760
  }
34761
+ function registerOAuthProvider(provider) {
34762
+ oauthProviderRegistry.set(provider.id, provider);
34763
+ }
34949
34764
  function getOAuthProviders() {
34950
34765
  return Array.from(oauthProviderRegistry.values());
34951
34766
  }
34767
+ var CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
34768
+ var CALLBACK_PORT_BASE = Number(process.env.PI_MCP_OAUTH_CALLBACK_PORT || 53700);
34769
+ var CALLBACK_PORT_COUNT = 10;
34770
+ var CALLBACK_PATH = "/callback";
34771
+ var CALLBACK_PORTS = Array.from({ length: CALLBACK_PORT_COUNT }, (_, i) => CALLBACK_PORT_BASE + i);
34772
+ var redirectUriFor = (port) => `http://localhost:${port}${CALLBACK_PATH}`;
34773
+ var ALL_REDIRECT_URIS = CALLBACK_PORTS.map(redirectUriFor);
34774
+ var TOKEN_EXPIRY_BUFFER_MS = 3e5;
34775
+ function validatedHttpsUrl(value, name) {
34776
+ let url;
34777
+ try {
34778
+ url = new URL(value);
34779
+ } catch {
34780
+ throw new Error(`${name} must be an absolute HTTPS URL`);
34781
+ }
34782
+ if (url.protocol !== "https:" || url.username || url.password || url.hash) throw new Error(`${name} must be an absolute HTTPS URL without credentials or a fragment`);
34783
+ return url;
34784
+ }
34785
+ function canonicalResource(url) {
34786
+ if (url.pathname === "/" && !url.search) return url.origin;
34787
+ return `${url.origin}${url.pathname}${url.search}`;
34788
+ }
34789
+ function authorizationServerMetadataUrls(issuer) {
34790
+ const url = validatedHttpsUrl(issuer, "Authorization server issuer");
34791
+ if (url.search) throw new Error("Authorization server issuer must not contain a query string");
34792
+ const path = url.pathname === "/" ? "" : url.pathname.replace(/\/$/, "");
34793
+ return [new URL(`/.well-known/oauth-authorization-server${path}`, url.origin).toString(), new URL(`${path}/.well-known/openid-configuration`, url.origin).toString()];
34794
+ }
34795
+ async function fetchResponse(url, init) {
34796
+ return fetch(url, {
34797
+ ...init,
34798
+ redirect: "error"
34799
+ });
34800
+ }
34801
+ async function fetchJson(url, init) {
34802
+ const res = await fetchResponse(url, init);
34803
+ if (!res.ok) throw new Error(`${init?.method ?? "GET"} ${url} failed: ${res.status}`);
34804
+ return res.json();
34805
+ }
34806
+ function authorizationServerMetadata(value, issuer, requireExactIssuer) {
34807
+ if (!value || typeof value !== "object") throw new Error(`Authorization server metadata for ${issuer} is invalid`);
34808
+ const metadata = value;
34809
+ if (typeof metadata.issuer !== "string") throw new Error(`Authorization server metadata for ${issuer} is missing its issuer`);
34810
+ if (requireExactIssuer) {
34811
+ if (metadata.issuer !== issuer) throw new Error(`Authorization server metadata issuer does not exactly match ${issuer}`);
34812
+ } else {
34813
+ const advertisedIssuer = validatedHttpsUrl(metadata.issuer, "Authorization server metadata issuer");
34814
+ if (advertisedIssuer.origin !== new URL(issuer).origin || advertisedIssuer.search) throw new Error(`Origin authorization server metadata issuer must stay on ${new URL(issuer).origin}`);
34815
+ }
34816
+ if (typeof metadata.authorization_endpoint !== "string" || typeof metadata.token_endpoint !== "string") throw new Error(`Authorization server metadata for ${issuer} is missing required endpoints`);
34817
+ validatedHttpsUrl(metadata.authorization_endpoint, "Authorization endpoint");
34818
+ validatedHttpsUrl(metadata.token_endpoint, "Token endpoint");
34819
+ if (metadata.registration_endpoint) validatedHttpsUrl(metadata.registration_endpoint, "Registration endpoint");
34820
+ return metadata;
34821
+ }
34822
+ async function jsonMetadata(response, url) {
34823
+ if (response.status !== 200) throw new Error(`GET ${url} failed: ${response.status}`);
34824
+ if (response.headers.get("content-type")?.split(";", 1)[0].trim().toLowerCase() !== "application/json") throw new Error(`GET ${url} did not return application/json`);
34825
+ return response.json();
34826
+ }
34827
+ async function discoverAuthorizationServer(issuer, requireExactIssuer) {
34828
+ const candidates = authorizationServerMetadataUrls(issuer);
34829
+ let lastError;
34830
+ for (const candidate of candidates) try {
34831
+ const response = await fetchResponse(candidate);
34832
+ if (response.status === 404) continue;
34833
+ return authorizationServerMetadata(await jsonMetadata(response, candidate), issuer, requireExactIssuer);
34834
+ } catch (error) {
34835
+ lastError = error;
34836
+ }
34837
+ throw new Error(`Could not discover OAuth metadata for ${issuer}. Tried ${candidates.join(", ")}. Last error: ${String(lastError)}`);
34838
+ }
34839
+ function randomState() {
34840
+ const bytes = /* @__PURE__ */ new Uint8Array(32);
34841
+ crypto.getRandomValues(bytes);
34842
+ return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
34843
+ }
34844
+ function resourceMetadata(value, resource) {
34845
+ if (!value || typeof value !== "object") throw new Error("Protected-resource metadata is invalid");
34846
+ const metadata = value;
34847
+ if (metadata.resource !== resource) throw new Error(`Protected-resource metadata resource does not exactly match ${resource}`);
34848
+ if (!Array.isArray(metadata.authorization_servers) || metadata.authorization_servers.length === 0) throw new Error("Protected-resource metadata has no authorization_servers");
34849
+ for (const issuer of metadata.authorization_servers) {
34850
+ if (typeof issuer !== "string") throw new Error("Protected-resource metadata has an invalid authorization server");
34851
+ validatedHttpsUrl(issuer, "Authorization server issuer");
34852
+ }
34853
+ return metadata;
34854
+ }
34855
+ function resourceMetadataUrl(resource) {
34856
+ const path = resource.pathname === "/" ? "" : resource.pathname;
34857
+ return `${resource.origin}/.well-known/oauth-protected-resource${path}${resource.search}`;
34858
+ }
34859
+ function headerResourceMetadata(value) {
34860
+ const match = value?.match(/(?:^|[,\s])resource_metadata\s*=\s*"((?:[^"\\]|\\.)*)"/i);
34861
+ if (!match) return void 0;
34862
+ return match[1].replace(/\\(.)/g, "$1");
34863
+ }
34864
+ async function tryProtectedResourceMetadata(url) {
34865
+ const resource = validatedHttpsUrl(url, "MCP endpoint");
34866
+ let headerUrl;
34867
+ try {
34868
+ const response2 = await fetchResponse(resource.toString());
34869
+ headerUrl = headerResourceMetadata(response2.headers.get("www-authenticate"));
34870
+ await response2.body?.cancel();
34871
+ } catch {
34872
+ }
34873
+ const candidate = headerUrl ? validatedHttpsUrl(headerUrl, "resource_metadata").toString() : resourceMetadataUrl(resource);
34874
+ const response = await fetchResponse(candidate);
34875
+ if (response.status === 404 && !headerUrl) return void 0;
34876
+ return resourceMetadata(await jsonMetadata(response, candidate), canonicalResource(resource));
34877
+ }
34878
+ async function discover(url) {
34879
+ const protectedResource = await tryProtectedResourceMetadata(url);
34880
+ if (protectedResource) {
34881
+ const issuer2 = protectedResource.authorization_servers[0];
34882
+ return {
34883
+ metadata: await discoverAuthorizationServer(issuer2, true),
34884
+ resource: protectedResource.resource,
34885
+ issuer: issuer2
34886
+ };
34887
+ }
34888
+ const issuer = validatedHttpsUrl(url, "MCP endpoint").origin;
34889
+ return { metadata: await discoverAuthorizationServer(issuer, false) };
34890
+ }
34891
+ async function registerClient(registrationEndpoint, label) {
34892
+ validatedHttpsUrl(registrationEndpoint, "Registration endpoint");
34893
+ const data = await fetchJson(registrationEndpoint, {
34894
+ method: "POST",
34895
+ headers: { "Content-Type": "application/json" },
34896
+ body: JSON.stringify({
34897
+ client_name: label,
34898
+ redirect_uris: ALL_REDIRECT_URIS,
34899
+ grant_types: ["authorization_code", "refresh_token"],
34900
+ response_types: ["code"],
34901
+ token_endpoint_auth_method: "none"
34902
+ })
34903
+ });
34904
+ if (typeof data.client_id !== "string" || !data.client_id) throw new Error(`Dynamic client registration at ${registrationEndpoint} returned no client_id`);
34905
+ return data.client_id;
34906
+ }
34907
+ async function startCallbackServer(label) {
34908
+ const { createServer } = await import("node:http");
34909
+ let settle;
34910
+ const waitPromise = new Promise((resolve2) => {
34911
+ let settled = false;
34912
+ settle = (value) => {
34913
+ if (!settled) {
34914
+ settled = true;
34915
+ resolve2(value);
34916
+ }
34917
+ };
34918
+ });
34919
+ const handler = (req, res) => {
34920
+ const url = new URL(req.url || "", "http://localhost");
34921
+ if (url.pathname !== CALLBACK_PATH) {
34922
+ res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
34923
+ res.end(oauthErrorHtml("Callback route not found."));
34924
+ return;
34925
+ }
34926
+ const error = url.searchParams.get("error");
34927
+ const code = url.searchParams.get("code");
34928
+ const state = url.searchParams.get("state");
34929
+ res.writeHead(error || !code ? 400 : 200, { "Content-Type": "text/html; charset=utf-8" });
34930
+ if (error) {
34931
+ res.end(oauthErrorHtml(`${label} authentication failed.`, `Error: ${error}`));
34932
+ settle?.(null);
34933
+ return;
34934
+ }
34935
+ if (!code || !state) {
34936
+ res.end(oauthErrorHtml("Missing code or state parameter."));
34937
+ settle?.(null);
34938
+ return;
34939
+ }
34940
+ res.end(oauthSuccessHtml(`${label} authentication completed. You can close this window.`));
34941
+ settle?.({
34942
+ code,
34943
+ state
34944
+ });
34945
+ };
34946
+ let lastError;
34947
+ for (const port of CALLBACK_PORTS) {
34948
+ const server = createServer(handler);
34949
+ let bindErr;
34950
+ server.on("error", (err) => bindErr?.(err));
34951
+ try {
34952
+ if (await new Promise((resolve2) => {
34953
+ bindErr = () => resolve2(false);
34954
+ server.listen(port, CALLBACK_HOST, () => {
34955
+ bindErr = void 0;
34956
+ resolve2(true);
34957
+ });
34958
+ })) return {
34959
+ server,
34960
+ redirectUri: redirectUriFor(port),
34961
+ cancel: () => settle?.(null),
34962
+ waitForCode: () => waitPromise
34963
+ };
34964
+ lastError = `port ${port} in use`;
34965
+ server.close();
34966
+ } catch (err) {
34967
+ lastError = err;
34968
+ server.close();
34969
+ }
34970
+ }
34971
+ throw new Error(`Could not start the OAuth callback server: ports ${CALLBACK_PORT_BASE}-${CALLBACK_PORT_BASE + CALLBACK_PORT_COUNT - 1} are all in use. Close other login attempts and retry. (${String(lastError)})`);
34972
+ }
34973
+ function parseRedirectInput(input, expectedState) {
34974
+ const value = input.trim();
34975
+ let code;
34976
+ let state;
34977
+ try {
34978
+ const url = new URL(value);
34979
+ code = url.searchParams.get("code") ?? void 0;
34980
+ state = url.searchParams.get("state") ?? void 0;
34981
+ } catch {
34982
+ const params = new URLSearchParams(value);
34983
+ code = params.get("code") ?? value;
34984
+ state = params.get("state") ?? void 0;
34985
+ }
34986
+ if (state && state !== expectedState) throw new Error("OAuth state mismatch");
34987
+ if (!code) throw new Error("Missing authorization code");
34988
+ return {
34989
+ code,
34990
+ state: state ?? expectedState
34991
+ };
34992
+ }
34993
+ async function exchangeToken(tokenEndpoint, params) {
34994
+ validatedHttpsUrl(tokenEndpoint, "Token endpoint");
34995
+ const res = await fetchResponse(tokenEndpoint, {
34996
+ method: "POST",
34997
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
34998
+ body: new URLSearchParams(params).toString()
34999
+ });
35000
+ const text = await res.text();
35001
+ if (!res.ok) throw new Error(`Token request to ${tokenEndpoint} failed: ${res.status}`);
35002
+ let token;
35003
+ try {
35004
+ token = JSON.parse(text);
35005
+ } catch {
35006
+ throw new Error(`Token request to ${tokenEndpoint} returned invalid JSON`);
35007
+ }
35008
+ if (!token || typeof token !== "object" || typeof token.access_token !== "string") throw new Error(`Token request to ${tokenEndpoint} returned no access_token`);
35009
+ const result = token;
35010
+ if (!result.access_token) throw new Error(`Token request to ${tokenEndpoint} returned no access_token`);
35011
+ if (result.refresh_token !== void 0 && typeof result.refresh_token !== "string") throw new Error(`Token request to ${tokenEndpoint} returned an invalid refresh_token`);
35012
+ if (result.expires_in !== void 0 && (typeof result.expires_in !== "number" || !Number.isFinite(result.expires_in))) throw new Error(`Token request to ${tokenEndpoint} returned an invalid expires_in`);
35013
+ return result;
35014
+ }
35015
+ function toCredentials(token, tokenEndpoint, clientId, endpoint, resource, issuer, previousRefresh) {
35016
+ return {
35017
+ access: token.access_token,
35018
+ refresh: token.refresh_token ?? previousRefresh ?? "",
35019
+ expires: token.expires_in ? Date.now() + token.expires_in * 1e3 - TOKEN_EXPIRY_BUFFER_MS : Date.now() + 36e5 - TOKEN_EXPIRY_BUFFER_MS,
35020
+ tokenEndpoint,
35021
+ clientId,
35022
+ endpoint,
35023
+ resource,
35024
+ issuer
35025
+ };
35026
+ }
35027
+ function createMcpOAuthProvider(config) {
35028
+ const label = config.label ?? config.server;
35029
+ async function login(callbacks) {
35030
+ const discovery = await discover(config.url);
35031
+ const { metadata: meta } = discovery;
35032
+ callbacks.onProgress?.(`Discovered ${discovery.issuer ?? meta.issuer}`);
35033
+ let clientId = config.clientId;
35034
+ if (!clientId) {
35035
+ if (!meta.registration_endpoint) throw new Error(`${label} does not support dynamic client registration and no clientId was configured. Set a pre-registered client id for this server.`);
35036
+ callbacks.onProgress?.("Registering OAuth client\u2026");
35037
+ clientId = await registerClient(meta.registration_endpoint, `Prime Agent (${label})`);
35038
+ }
35039
+ const { verifier, challenge } = await generatePKCE();
35040
+ const state = randomState();
35041
+ const scope = config.scopes ?? meta.scopes_supported?.join(" ");
35042
+ const cb = await startCallbackServer(label);
35043
+ try {
35044
+ const authParams = new URLSearchParams({
35045
+ client_id: clientId,
35046
+ response_type: "code",
35047
+ redirect_uri: cb.redirectUri,
35048
+ code_challenge: challenge,
35049
+ code_challenge_method: "S256",
35050
+ state
35051
+ });
35052
+ if (scope) authParams.set("scope", scope);
35053
+ if (discovery.resource) authParams.set("resource", discovery.resource);
35054
+ const authorizationUrl = new URL(meta.authorization_endpoint);
35055
+ for (const [name, value] of authParams) authorizationUrl.searchParams.set(name, value);
35056
+ callbacks.onAuth({
35057
+ url: authorizationUrl.toString(),
35058
+ instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
35059
+ });
35060
+ let result;
35061
+ let manualCancelled = false;
35062
+ let manualError;
35063
+ if (callbacks.onManualCodeInput) {
35064
+ const manual = callbacks.onManualCodeInput().then((input) => {
35065
+ const parsed = parseRedirectInput(input, state);
35066
+ cb.cancel();
35067
+ return parsed;
35068
+ }).catch(async (err) => {
35069
+ if (err instanceof Error && /state mismatch|authorization code/i.test(err.message)) manualError = err;
35070
+ else manualCancelled = true;
35071
+ await new Promise((r) => setTimeout(r, 500));
35072
+ cb.cancel();
35073
+ return null;
35074
+ });
35075
+ result = await cb.waitForCode() ?? await manual;
35076
+ if (!result && manualError) throw manualError;
35077
+ } else {
35078
+ result = await cb.waitForCode();
35079
+ if (!result) result = parseRedirectInput(await callbacks.onPrompt({
35080
+ message: "Paste the authorization code or full redirect URL:",
35081
+ placeholder: cb.redirectUri
35082
+ }), state);
35083
+ }
35084
+ if (!result) throw new Error(manualCancelled ? "Login cancelled" : "Missing authorization code");
35085
+ if (result.state !== state) throw new Error("OAuth state mismatch");
35086
+ callbacks.onProgress?.("Exchanging authorization code for tokens\u2026");
35087
+ return toCredentials(await exchangeToken(meta.token_endpoint, {
35088
+ grant_type: "authorization_code",
35089
+ code: result.code,
35090
+ redirect_uri: cb.redirectUri,
35091
+ client_id: clientId,
35092
+ code_verifier: verifier,
35093
+ ...discovery.resource ? { resource: discovery.resource } : {}
35094
+ }), meta.token_endpoint, clientId, config.url, discovery.resource, discovery.issuer);
35095
+ } finally {
35096
+ cb.server.close();
35097
+ }
35098
+ }
35099
+ async function refreshToken(credentials) {
35100
+ const creds = credentials;
35101
+ if (creds.endpoint !== config.url) throw new Error(`Stored OAuth credentials are not bound to ${config.url}; re-run /mcp login ${config.server}`);
35102
+ const configuredResource = canonicalResource(validatedHttpsUrl(config.url, "MCP endpoint"));
35103
+ if (creds.resource !== void 0 && creds.resource !== configuredResource) throw new Error(`Stored OAuth credentials are not bound to ${configuredResource}; re-run /mcp login ${config.server}`);
35104
+ if (creds.resource === void 0 !== (creds.issuer === void 0)) throw new Error(`Stored OAuth credentials for ${label} have incomplete resource binding; re-run /mcp login ${config.server}`);
35105
+ if (creds.issuer !== void 0) validatedHttpsUrl(creds.issuer, "Stored authorization server issuer");
35106
+ if (!creds.refresh) throw new Error(`No refresh token stored for ${label}; re-run /mcp login ${config.server}`);
35107
+ const discovery = await discover(config.url);
35108
+ if (creds.resource === void 0 !== (discovery.resource === void 0)) throw new Error(`OAuth discovery mode changed for ${config.url}; re-run /mcp login ${config.server}`);
35109
+ if (creds.resource) {
35110
+ if (discovery.resource !== creds.resource || discovery.issuer !== creds.issuer) throw new Error(`Stored OAuth credentials do not match current protected-resource metadata for ${config.url}`);
35111
+ }
35112
+ const tokenEndpoint = creds.tokenEndpoint ?? discovery.metadata.token_endpoint;
35113
+ if (creds.tokenEndpoint && discovery.metadata.token_endpoint !== creds.tokenEndpoint) throw new Error(`Stored OAuth token endpoint does not match current authorization-server metadata for ${config.url}`);
35114
+ const clientId = creds.clientId ?? config.clientId;
35115
+ if (!tokenEndpoint) throw new Error(`No token endpoint stored for ${label}; re-run /mcp login ${config.server}`);
35116
+ return toCredentials(await exchangeToken(tokenEndpoint, {
35117
+ grant_type: "refresh_token",
35118
+ refresh_token: creds.refresh,
35119
+ ...clientId ? { client_id: clientId } : {},
35120
+ ...creds.resource ? { resource: creds.resource } : {}
35121
+ }), tokenEndpoint, clientId ?? "", creds.endpoint, creds.resource, creds.issuer, creds.refresh);
35122
+ }
35123
+ return {
35124
+ id: `mcp:${config.server}`,
35125
+ name: label,
35126
+ usesCallbackServer: true,
35127
+ login,
35128
+ refreshToken,
35129
+ getApiKey: (credentials) => credentials.access
35130
+ };
35131
+ }
35132
+ var BUILTIN_MCP_CATALOG = [{
35133
+ server: "linear",
35134
+ label: "Linear",
35135
+ url: "https://mcp.linear.app/mcp",
35136
+ oauth: {
35137
+ kind: "oauth",
35138
+ label: "Linear"
35139
+ }
35140
+ }, {
35141
+ server: "notion",
35142
+ label: "Notion",
35143
+ url: "https://mcp.notion.com/mcp",
35144
+ oauth: {
35145
+ kind: "oauth",
35146
+ label: "Notion"
35147
+ }
35148
+ }];
35149
+ function getCatalogEntry(server) {
35150
+ return BUILTIN_MCP_CATALOG.find((entry) => entry.server === server);
35151
+ }
35152
+ function registerBuiltinMcpOAuthProviders() {
35153
+ for (const entry of BUILTIN_MCP_CATALOG) {
35154
+ if (entry.oauth?.kind !== "oauth") continue;
35155
+ if (getOAuthProvider(`mcp:${entry.server}`)) continue;
35156
+ registerOAuthProvider(createMcpOAuthProvider({
35157
+ server: entry.server,
35158
+ label: entry.label,
35159
+ url: entry.url,
35160
+ scopes: entry.oauth.scopes,
35161
+ clientId: entry.oauth.clientId
35162
+ }));
35163
+ }
35164
+ }
35165
+ var MCP_AUTH_PREFIX = "mcp:";
35166
+ function mcpProviderId(name) {
35167
+ return `${MCP_AUTH_PREFIX}${name}`;
35168
+ }
35169
+ var McpRequestError = class extends Error {
35170
+ status = 400;
35171
+ constructor(message) {
35172
+ super(message);
35173
+ this.name = "McpRequestError";
35174
+ }
35175
+ };
35176
+ function validateHttpMcpUrl(value) {
35177
+ let url;
35178
+ try {
35179
+ url = new URL(value);
35180
+ } catch {
35181
+ throw new McpRequestError("Invalid MCP URL.");
35182
+ }
35183
+ if (url.protocol !== "http:" && url.protocol !== "https:" || !url.hostname || url.username || url.password) throw new McpRequestError("MCP URL must be an http(s) URL without embedded credentials.");
35184
+ return url.toString();
35185
+ }
35186
+ function stdioCommandPreview(config) {
35187
+ return [config.command, ...config.args ?? []].join(" ");
35188
+ }
35189
+ function mcpEnvBindings(config) {
35190
+ if (config.type !== "stdio" || !config.env) return void 0;
35191
+ const bindings = Object.entries(config.env).map(([child, mapping]) => ({
35192
+ child,
35193
+ source: mapping.env
35194
+ }));
35195
+ return bindings.length > 0 ? bindings : void 0;
35196
+ }
35197
+ function isMcpAuthed(server, auth, env) {
35198
+ if (server.config.enabled === false) return false;
35199
+ if (server.userDeclared && getCatalogEntry(server.name)) return false;
35200
+ if (server.config.type === "stdio") return true;
35201
+ const usesOAuth = server.config.type === "http" && server.config.oauth === true;
35202
+ const bearerTokenEnvVar = server.config.type === "http" ? server.config.bearerTokenEnvVar : void 0;
35203
+ if (!usesOAuth && !bearerTokenEnvVar) return true;
35204
+ if (bearerTokenEnvVar && env[bearerTokenEnvVar]?.trim()) return true;
35205
+ const cred = auth.get(mcpProviderId(server.name));
35206
+ if (cred === void 0) return false;
35207
+ if (!server.userDeclared) return true;
35208
+ const endpoint = cred.endpoint;
35209
+ return typeof endpoint === "string" && server.config.type === "http" && endpoint === server.config.url;
35210
+ }
35211
+ function mcpConnectionStatus(server, authed) {
35212
+ if (server.config.enabled === false) return "disabled";
35213
+ if (authed) return server.config.type === "http" && server.config.oauth !== true && !server.config.bearerTokenEnvVar ? "anonymous" : "connected";
35214
+ return "needs_auth";
35215
+ }
35216
+ function toMcpConnectionInfo(server, auth, env) {
35217
+ const usesOAuth = server.config.type === "http" && server.config.oauth === true;
35218
+ const catalogShadow = server.userDeclared && Boolean(getCatalogEntry(server.name));
35219
+ const authed = isMcpAuthed(server, auth, env);
35220
+ const envBindings = mcpEnvBindings(server.config);
35221
+ return {
35222
+ name: server.name,
35223
+ label: server.label,
35224
+ source: server.source,
35225
+ transport: server.config.type,
35226
+ usesOAuth,
35227
+ enabled: authed,
35228
+ status: mcpConnectionStatus(server, authed),
35229
+ ...server.config.type === "http" ? { url: server.config.url } : {},
35230
+ ...server.config.type === "stdio" ? { commandPreview: stdioCommandPreview(server.config) } : {},
35231
+ ...server.config.type === "stdio" && server.config.cwd ? { cwdPreview: safePathLabel(server.config.cwd) } : {},
35232
+ ...server.config.type === "http" && server.config.bearerTokenEnvVar ? { bearerTokenEnvVar: server.config.bearerTokenEnvVar } : {},
35233
+ ...envBindings ? { envBindings } : {},
35234
+ ...catalogShadow ? { error: "This name is reserved for a built-in integration." } : {}
35235
+ };
35236
+ }
35237
+ function listMcpServers(userServers) {
35238
+ const listed = [];
35239
+ for (const entry of BUILTIN_MCP_CATALOG) listed.push({
35240
+ name: entry.server,
35241
+ label: entry.label,
35242
+ source: "builtin",
35243
+ userDeclared: false,
35244
+ config: {
35245
+ type: "http",
35246
+ url: entry.url,
35247
+ oauth: true
35248
+ }
35249
+ });
35250
+ for (const [name, config] of Object.entries(userServers ?? {}).sort(([left], [right]) => left.localeCompare(right))) listed.push({
35251
+ name,
35252
+ label: name,
35253
+ source: "user",
35254
+ userDeclared: true,
35255
+ config
35256
+ });
35257
+ return listed;
35258
+ }
35259
+ function listMcpConnections(userServers, auth, env = process.env) {
35260
+ return listMcpServers(userServers).map((server) => toMcpConnectionInfo(server, auth, env));
35261
+ }
35262
+ function dropMcpServerCredentials(name, auth) {
35263
+ if (getCatalogEntry(name)) return;
35264
+ try {
35265
+ auth.removeVerified(mcpProviderId(name));
35266
+ } catch (error) {
35267
+ throw new McpRequestError(`Could not remove stored credentials for this MCP server: ${error instanceof Error ? error.message : String(error)}`);
35268
+ }
35269
+ }
35270
+ async function confineMcpStdioCwd(cwd, workspaceRoot) {
35271
+ if (cwd === void 0 || cwd.trim() === "") return void 0;
35272
+ const root2 = resolve(workspaceRoot);
35273
+ const canonicalRoot = await realpath(root2).catch(() => root2);
35274
+ const candidate = resolve(root2, cwd);
35275
+ const existingCandidate = await realpath(candidate).catch(() => void 0);
35276
+ if (existingCandidate && existingCandidate !== canonicalRoot && !existingCandidate.startsWith(`${canonicalRoot}${sep}`)) throw new McpRequestError("MCP stdio working directory must stay inside the workspace.");
35277
+ let ancestor = dirname(candidate);
35278
+ while (true) {
35279
+ const resolvedAncestor = await realpath(ancestor).catch(() => void 0);
35280
+ if (resolvedAncestor) {
35281
+ if (resolvedAncestor !== canonicalRoot && !resolvedAncestor.startsWith(`${canonicalRoot}${sep}`)) throw new McpRequestError("MCP stdio working directory must stay inside the workspace.");
35282
+ break;
35283
+ }
35284
+ const parent = dirname(ancestor);
35285
+ if (parent === ancestor) throw new McpRequestError("MCP stdio working directory must stay inside the workspace.");
35286
+ ancestor = parent;
35287
+ }
35288
+ return candidate;
35289
+ }
35290
+ function storedApiForCustomProvider(api) {
35291
+ return api === "google-genai" ? "google-generative-ai" : api;
35292
+ }
35293
+ function uiApiForCustomProvider(value) {
35294
+ if (value === "google-generative-ai") return "google-genai";
35295
+ return isPiCustomProviderApi(value) ? value : void 0;
35296
+ }
35297
+ function envVarNameForManagedProvider(providerId) {
35298
+ if (providerId === "openai-chat-completions") return "OPENAI_CHAT_COMPLETIONS_API_KEY";
35299
+ const slug = providerId.slice(providerId.indexOf("+") + 1).toUpperCase().replace(/[^A-Z0-9]+/g, "_");
35300
+ return isOccProviderId(providerId) ? `OPENAI_CHAT_COMPLETIONS_${slug}_API_KEY` : `CUSTOM_${slug}_API_KEY`;
35301
+ }
35302
+ function readModelsJson(filePath) {
35303
+ try {
35304
+ if (!existsSync(filePath)) return {};
35305
+ return parseModelsJson(readFileSync(filePath, "utf-8"));
35306
+ } catch {
35307
+ return {};
35308
+ }
35309
+ }
35310
+ function stripJsonComments(input) {
35311
+ return input.replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, (match) => match[0] === '"' ? match : "").replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (match, tail) => tail ?? (match[0] === '"' ? match : ""));
35312
+ }
35313
+ function parseModelsJson(raw) {
35314
+ const parsed = JSON.parse(stripJsonComments(raw));
35315
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("models.json must contain an object");
35316
+ return parsed;
35317
+ }
35318
+ function readModelsJsonForWrite(filePath) {
35319
+ if (!existsSync(filePath)) return {};
35320
+ try {
35321
+ return parseModelsJson(readFileSync(filePath, "utf-8"));
35322
+ } catch (error) {
35323
+ throw new Error(`Refusing to update models.json because it could not be parsed: ${error instanceof Error ? error.message : error}`);
35324
+ }
35325
+ }
35326
+ function mergeModelDefinitions(existing, incoming) {
35327
+ const existingById = new Map((existing ?? []).map((model) => [model.id, model]));
35328
+ const seen = /* @__PURE__ */ new Set();
35329
+ return incoming.flatMap((model) => {
35330
+ if (seen.has(model.id)) return [];
35331
+ seen.add(model.id);
35332
+ return [{
35333
+ ...existingById.get(model.id),
35334
+ ...model
35335
+ }];
35336
+ });
35337
+ }
35338
+ function listCustomProviders(filePath) {
35339
+ return readModelsJson(filePath).providers ?? {};
35340
+ }
35341
+ function writeModelsJson(filePath, data) {
35342
+ mkdirSync(dirname(filePath), {
35343
+ recursive: true,
35344
+ mode: 448
35345
+ });
35346
+ const serialized = `${JSON.stringify(data, null, " ")}
35347
+ `;
35348
+ const tmpPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
35349
+ writeFileSync(tmpPath, serialized, "utf-8");
35350
+ renameSync(tmpPath, filePath);
35351
+ }
35352
+ function upsertCustomProvider(filePath, providerId, input) {
35353
+ const data = readModelsJsonForWrite(filePath);
35354
+ const providers = { ...data.providers ?? {} };
35355
+ const existing = providers[providerId] ?? {};
35356
+ providers[providerId] = {
35357
+ ...existing,
35358
+ name: input.name ?? existing.name,
35359
+ baseUrl: input.baseUrl,
35360
+ api: storedApiForCustomProvider(input.api),
35361
+ apiKey: input.apiKey,
35362
+ models: mergeModelDefinitions(existing.models, input.models)
35363
+ };
35364
+ writeModelsJson(filePath, {
35365
+ ...data,
35366
+ providers
35367
+ });
35368
+ }
35369
+ function addCustomProviderModelIds(filePath, providerId, modelIds) {
35370
+ const data = readModelsJsonForWrite(filePath);
35371
+ const providers = { ...data.providers ?? {} };
35372
+ const existing = providers[providerId];
35373
+ if (!existing) return;
35374
+ providers[providerId] = {
35375
+ ...existing,
35376
+ models: mergeModelDefinitions(existing.models, [...existing.models ?? [], ...modelIds.map((id) => ({ id }))])
35377
+ };
35378
+ writeModelsJson(filePath, {
35379
+ ...data,
35380
+ providers
35381
+ });
35382
+ }
35383
+ function removeCustomProvider(filePath, providerId) {
35384
+ const data = readModelsJsonForWrite(filePath);
35385
+ const providers = data.providers;
35386
+ if (!providers || !(providerId in providers)) return;
35387
+ const next = { ...providers };
35388
+ delete next[providerId];
35389
+ writeModelsJson(filePath, {
35390
+ ...data,
35391
+ providers: next
35392
+ });
35393
+ }
35394
+ function isDiscoverableEntry(entry) {
35395
+ const baseUrl = entry?.baseUrl;
35396
+ if (!baseUrl) return false;
35397
+ const api = uiApiForCustomProvider(entry?.api);
35398
+ if (api && !isOccFamilyApi(api)) return false;
35399
+ if (entry?.api && !api) return false;
35400
+ try {
35401
+ const parsed = new URL(baseUrl);
35402
+ return parsed.protocol === "https:" || parsed.protocol === "http:";
35403
+ } catch {
35404
+ return false;
35405
+ }
35406
+ }
34952
35407
  var PRIME_PROVIDER_ENV_MAP = {
34953
35408
  openai: "OPENAI_API_KEY",
34954
35409
  "azure-openai-responses": "AZURE_OPENAI_API_KEY",
@@ -35072,7 +35527,7 @@ function listChatProviders() {
35072
35527
  function handleChatProvidersGet(_request) {
35073
35528
  return wrapApiHandler(async () => {
35074
35529
  return Response.json({ providers: buildProviders() });
35075
- });
35530
+ }, _request);
35076
35531
  }
35077
35532
  function requireBaseUrl(baseUrl, providerId) {
35078
35533
  const trimmed = baseUrl?.trim() ?? "";
@@ -35141,7 +35596,7 @@ function handleChatProvidersPost(request) {
35141
35596
  success: true,
35142
35597
  providers: buildProviders()
35143
35598
  });
35144
- });
35599
+ }, request);
35145
35600
  }
35146
35601
  function handleChatProvidersDelete(request) {
35147
35602
  return wrapApiHandler(async () => {
@@ -35155,7 +35610,7 @@ function handleChatProvidersDelete(request) {
35155
35610
  success: true,
35156
35611
  providers: buildProviders()
35157
35612
  });
35158
- });
35613
+ }, request);
35159
35614
  }
35160
35615
  var LOGIN_TTL_MS = 96e4;
35161
35616
  var sessions = /* @__PURE__ */ new Map();
@@ -35285,7 +35740,7 @@ function cancelOAuthLogin(loginId) {
35285
35740
  error: "Login cancelled"
35286
35741
  };
35287
35742
  }
35288
- function oauthDeps() {
35743
+ function oauthDeps$1() {
35289
35744
  const config = getPrimeConfig();
35290
35745
  return {
35291
35746
  login: (providerId, callbacks) => config.authStorage.login(providerId, callbacks),
@@ -35312,8 +35767,423 @@ function handleChatProvidersOAuthPost(request) {
35312
35767
  status: "error",
35313
35768
  error: `Unknown OAuth provider: ${body.providerId}`
35314
35769
  }, { status: 400 });
35315
- return Response.json(startOAuthLogin(body.providerId, oauthDeps()));
35316
- });
35770
+ return Response.json(startOAuthLogin(body.providerId, oauthDeps$1()));
35771
+ }, request);
35772
+ }
35773
+ function defaultMcpDeps() {
35774
+ const config = getPrimeConfig();
35775
+ const settings = config.defaultSettings;
35776
+ return {
35777
+ getUserServers: () => settings.getGlobalMcpServers(),
35778
+ setUserServer: (name, serverConfig, force) => {
35779
+ settings.setGlobalMcpServer(name, serverConfig, force);
35780
+ },
35781
+ removeUserServer: (name) => settings.removeGlobalMcpServer(name),
35782
+ flushSettings: async () => {
35783
+ await settings.flush();
35784
+ const error = settings.drainErrors("global")[0];
35785
+ if (error) throw error.error;
35786
+ },
35787
+ auth: config.authStorage,
35788
+ workspaceRoot: config.defaultCwd,
35789
+ env: process.env,
35790
+ reloadAuth: () => {
35791
+ config.reloadAuth();
35792
+ },
35793
+ reloadResources: async () => {
35794
+ await getBridge().reloadResources();
35795
+ }
35796
+ };
35797
+ }
35798
+ function listedConnections(deps) {
35799
+ return listMcpConnections(deps.getUserServers(), deps.auth, deps.env);
35800
+ }
35801
+ function listResponse(deps) {
35802
+ return { connections: listedConnections(deps) };
35803
+ }
35804
+ function toMcpOAuthResponse(snapshot2, connections) {
35805
+ return {
35806
+ status: snapshot2.status,
35807
+ ...snapshot2.loginId ? { loginId: snapshot2.loginId } : {},
35808
+ ...snapshot2.authUrl ? { authUrl: snapshot2.authUrl } : {},
35809
+ ...snapshot2.userCode ? { userCode: snapshot2.userCode } : {},
35810
+ ...snapshot2.instructions ? { instructions: snapshot2.instructions } : {},
35811
+ ...snapshot2.prompt ? { prompt: snapshot2.prompt } : {},
35812
+ ...snapshot2.error ? { error: snapshot2.error } : {},
35813
+ ...connections ? { connections } : {}
35814
+ };
35815
+ }
35816
+ function ensureMcpOAuthProvider(name, deps) {
35817
+ registerBuiltinMcpOAuthProviders();
35818
+ if (getCatalogEntry(name)) return;
35819
+ const config = deps.getUserServers()?.[name];
35820
+ if (!config || config.type !== "http" || config.oauth !== true) throw new McpRequestError("This MCP server does not use OAuth.");
35821
+ registerOAuthProvider(createMcpOAuthProvider({
35822
+ server: name,
35823
+ label: name,
35824
+ url: config.url
35825
+ }));
35826
+ }
35827
+ function oauthDeps(deps) {
35828
+ return {
35829
+ login: (providerId, callbacks) => deps.auth.login(providerId, callbacks),
35830
+ reloadAuth: () => {
35831
+ deps.reloadAuth();
35832
+ deps.reloadResources();
35833
+ },
35834
+ listProviders: () => []
35835
+ };
35836
+ }
35837
+ async function logoutMcp(name, deps) {
35838
+ try {
35839
+ deps.auth.removeVerified(mcpProviderId(name));
35840
+ } catch (error) {
35841
+ throw new McpRequestError(`Could not remove stored credentials: ${error instanceof Error ? error.message : String(error)}`);
35842
+ }
35843
+ deps.reloadAuth();
35844
+ await deps.reloadResources();
35845
+ return {
35846
+ status: "success",
35847
+ connections: listedConnections(deps)
35848
+ };
35849
+ }
35850
+ function handleChatMcpGet(_request, deps = defaultMcpDeps()) {
35851
+ return wrapApiHandler(async () => Response.json(listResponse(deps)), _request);
35852
+ }
35853
+ function handleChatMcpPost(request, deps = defaultMcpDeps()) {
35854
+ return wrapApiHandler(async () => {
35855
+ const body = ChatMcpUpsertRequestSchema.parse(await request.json().catch(() => ({})));
35856
+ if (getCatalogEntry(body.name)) throw new McpRequestError(`MCP server name "${body.name}" is reserved for a built-in integration.`);
35857
+ if (deps.getUserServers()?.[body.name] && body.force !== true) throw new McpRequestError(`MCP server "${body.name}" already exists. Replace it to continue.`);
35858
+ let config;
35859
+ switch (body.transport) {
35860
+ case "http":
35861
+ if (body.bearerTokenEnvVar && body.oauth) throw new McpRequestError("OAuth and a bearer-token environment variable cannot be combined.");
35862
+ config = {
35863
+ type: "http",
35864
+ url: validateHttpMcpUrl(body.url),
35865
+ ...body.bearerTokenEnvVar ? { bearerTokenEnvVar: body.bearerTokenEnvVar } : {},
35866
+ ...body.oauth ? { oauth: true } : {}
35867
+ };
35868
+ break;
35869
+ case "stdio": {
35870
+ if (body.command.includes("\0") || body.args?.some((part) => part.includes("\0"))) throw new McpRequestError("MCP command arguments cannot contain NUL.");
35871
+ const env = Object.fromEntries((body.env ?? []).map((binding) => [binding.child, { env: binding.source }]));
35872
+ const cwd = await confineMcpStdioCwd(body.cwd, deps.workspaceRoot);
35873
+ config = {
35874
+ type: "stdio",
35875
+ command: body.command,
35876
+ ...body.args && body.args.length > 0 ? { args: body.args } : {},
35877
+ ...cwd ? { cwd } : {},
35878
+ ...Object.keys(env).length > 0 ? { env } : {}
35879
+ };
35880
+ break;
35881
+ }
35882
+ default:
35883
+ throw new McpRequestError(`Unsupported MCP transport: ${String(body)}`);
35884
+ }
35885
+ dropMcpServerCredentials(body.name, deps.auth);
35886
+ deps.setUserServer(body.name, config, true);
35887
+ await deps.flushSettings();
35888
+ deps.reloadAuth();
35889
+ await deps.reloadResources();
35890
+ return Response.json(listResponse(deps));
35891
+ }, request);
35892
+ }
35893
+ function handleChatMcpDelete(request, deps = defaultMcpDeps()) {
35894
+ return wrapApiHandler(async () => {
35895
+ const body = ChatMcpDeleteRequestSchema.parse(await request.json().catch(() => ({})));
35896
+ if (!deps.removeUserServer(body.name)) throw new McpRequestError(`MCP server "${body.name}" was not found.`);
35897
+ await deps.flushSettings();
35898
+ dropMcpServerCredentials(body.name, deps.auth);
35899
+ deps.reloadAuth();
35900
+ await deps.reloadResources();
35901
+ return Response.json(listResponse(deps));
35902
+ }, request);
35903
+ }
35904
+ function handleChatMcpOAuthPost(request, deps = defaultMcpDeps()) {
35905
+ return wrapApiHandler(async () => {
35906
+ const body = ChatMcpOAuthLoginRequestSchema.parse(await request.json().catch(() => ({})));
35907
+ if (![...BUILTIN_MCP_CATALOG.map((entry) => entry.server), ...Object.keys(deps.getUserServers() ?? {})].includes(body.name) && !body.loginId) throw new McpRequestError(`Unknown MCP server: ${body.name}`);
35908
+ if (body.cancel) {
35909
+ if (!body.loginId) return Response.json({
35910
+ status: "error",
35911
+ error: "loginId is required to cancel"
35912
+ }, { status: 400 });
35913
+ return Response.json(toMcpOAuthResponse(cancelOAuthLogin(body.loginId)));
35914
+ }
35915
+ if (body.loginId && body.promptAnswer !== void 0) return Response.json(toMcpOAuthResponse(continueOAuthLogin(body.loginId, body.promptAnswer)));
35916
+ if (body.loginId) {
35917
+ const snapshot2 = pollOAuthLogin(body.loginId);
35918
+ return Response.json(toMcpOAuthResponse(snapshot2, snapshot2.status === "success" ? listedConnections(deps) : void 0));
35919
+ }
35920
+ const action = body.action ?? "login";
35921
+ switch (action) {
35922
+ case "logout":
35923
+ return Response.json(await logoutMcp(body.name, deps));
35924
+ case "reconnect":
35925
+ await logoutMcp(body.name, deps);
35926
+ break;
35927
+ case "login":
35928
+ break;
35929
+ default:
35930
+ throw new McpRequestError(`Unsupported MCP OAuth action: ${String(action)}`);
35931
+ }
35932
+ ensureMcpOAuthProvider(body.name, deps);
35933
+ const providerId = mcpProviderId(body.name);
35934
+ if (!getOAuthProvider(providerId)) return Response.json({
35935
+ status: "error",
35936
+ error: `Unknown OAuth provider: ${providerId}`
35937
+ }, { status: 400 });
35938
+ return Response.json(toMcpOAuthResponse(startOAuthLogin(providerId, oauthDeps(deps))));
35939
+ }, request);
35940
+ }
35941
+ var BodySchema$1 = external_exports.object({
35942
+ sessionId: SessionIdSchema,
35943
+ model: external_exports.object({
35944
+ provider: external_exports.string(),
35945
+ id: external_exports.string()
35946
+ })
35947
+ });
35948
+ function handleChatModelPost(request) {
35949
+ return wrapApiHandler(async () => {
35950
+ const raw = await request.json().catch(() => ({}));
35951
+ const body = BodySchema$1.parse(raw);
35952
+ const bridge = getBridge();
35953
+ const session = bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId);
35954
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
35955
+ await bridge.setModel(body.sessionId, {
35956
+ provider: body.model.provider,
35957
+ id: body.model.id
35958
+ });
35959
+ return Response.json({ ok: true });
35960
+ }, request);
35961
+ }
35962
+ function toPatternCandidate(model) {
35963
+ return {
35964
+ id: model.id,
35965
+ name: model.name,
35966
+ provider: model.provider,
35967
+ modelId: model.id,
35968
+ key: `${model.provider}/${model.id}`
35969
+ };
35970
+ }
35971
+ function handleChatModelsGet(request) {
35972
+ return wrapApiHandler(async () => {
35973
+ const config = getPrimeConfig();
35974
+ const cwd = await cwdForRequest(request);
35975
+ const settings = config.settingsFor(cwd);
35976
+ const registry = config.modelRegistry;
35977
+ const scope = new URL(request.url).searchParams.get("scope") === "all" ? "all" : "enabled";
35978
+ let models = registry.getAll();
35979
+ if (scope === "enabled") {
35980
+ models = models.filter((model) => registry.hasConfiguredAuth(model));
35981
+ const patterns = settings.getEnabledModels();
35982
+ if (patterns !== void 0) models = models.filter((model) => isModelPatternEnabled(toPatternCandidate(model), patterns));
35983
+ }
35984
+ const diagnostics = [];
35985
+ const defaultProvider = settings.getDefaultProvider();
35986
+ const defaultModel = settings.getDefaultModel();
35987
+ const defaultThinkingLevel = settings.getDefaultThinkingLevel();
35988
+ const selectedModelKey = defaultProvider && defaultModel ? `${defaultProvider}/${defaultModel}` : void 0;
35989
+ return Response.json({
35990
+ models: models.map((m) => {
35991
+ const thinkingLevels = getSupportedThinkingLevels(m);
35992
+ const fallback = m.reasoning ? "medium" : "off";
35993
+ return {
35994
+ key: `${m.provider}/${m.id}`,
35995
+ provider: m.provider,
35996
+ id: m.id,
35997
+ name: m.name,
35998
+ reasoning: m.reasoning,
35999
+ input: m.input,
36000
+ contextWindow: m.contextWindow,
36001
+ maxTokens: m.maxTokens,
36002
+ available: registry.hasConfiguredAuth(m),
36003
+ thinkingLevels,
36004
+ defaultThinkingLevel: thinkingLevels.includes(fallback) ? fallback : thinkingLevels[0] ?? "off"
36005
+ };
36006
+ }),
36007
+ ...selectedModelKey ? { selectedModelKey } : {},
36008
+ ...defaultProvider ? { defaultProvider } : {},
36009
+ ...defaultModel ? { defaultModel } : {},
36010
+ ...defaultThinkingLevel ? { defaultThinkingLevel } : {},
36011
+ diagnostics
36012
+ });
36013
+ }, request);
36014
+ }
36015
+ function openAiModelsUrl(baseUrl) {
36016
+ const parsed = new URL(baseUrl);
36017
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error("model discovery requires an http(s) URL");
36018
+ if (parsed.protocol === "http:" && !isLoopbackHostname(parsed.hostname)) throw new Error("model discovery requires https for non-loopback hosts");
36019
+ const path = parsed.pathname.replace(/\/+$/, "");
36020
+ parsed.pathname = path.endsWith("/v1") ? `${path}/models` : `${path}/v1/models`;
36021
+ parsed.search = "";
36022
+ parsed.hash = "";
36023
+ return parsed;
36024
+ }
36025
+ function handleChatModelsDiscoverPost(request) {
36026
+ return wrapApiHandler(async () => {
36027
+ const raw = await request.json().catch(() => ({}));
36028
+ const body = ChatModelsDiscoverRequestSchema.parse(raw);
36029
+ const config = getPrimeConfig();
36030
+ const apiKey = await config.modelRegistry.getApiKeyForProvider(body.providerId);
36031
+ if (!apiKey) return Response.json({
36032
+ providerId: body.providerId,
36033
+ models: []
36034
+ });
36035
+ const provider = body.providerId;
36036
+ const entry = listCustomProviders(`${config.agentDir}/models.json`)[provider];
36037
+ const api = uiApiForCustomProvider(entry?.api);
36038
+ if (entry?.api && !api || api && !isOccFamilyApi(api)) return Response.json({
36039
+ providerId: provider,
36040
+ models: []
36041
+ });
36042
+ const baseUrl = entry?.baseUrl;
36043
+ if (!baseUrl) return Response.json({
36044
+ providerId: provider,
36045
+ models: []
36046
+ });
36047
+ try {
36048
+ const modelsUrl = openAiModelsUrl(baseUrl);
36049
+ const response = await fetch(modelsUrl, {
36050
+ headers: { Authorization: `Bearer ${apiKey}` },
36051
+ redirect: "error",
36052
+ signal: AbortSignal.timeout(1e4)
36053
+ });
36054
+ if (!response.ok) return Response.json({
36055
+ providerId: provider,
36056
+ models: []
36057
+ });
36058
+ const models = ((await response.json()).data ?? []).map((m) => ({
36059
+ key: `${provider}/${m.id}`,
36060
+ provider,
36061
+ id: m.id,
36062
+ name: m.id,
36063
+ reasoning: false,
36064
+ input: ["text"],
36065
+ available: true,
36066
+ defaultThinkingLevel: "off",
36067
+ thinkingLevels: ["off"]
36068
+ }));
36069
+ if (models.length > 0) {
36070
+ addCustomProviderModelIds(`${config.agentDir}/models.json`, provider, models.map((model) => model.id));
36071
+ config.reloadAuth();
36072
+ }
36073
+ return Response.json({
36074
+ providerId: provider,
36075
+ models
36076
+ });
36077
+ } catch {
36078
+ return Response.json({
36079
+ providerId: provider,
36080
+ models: []
36081
+ });
36082
+ }
36083
+ }, request);
36084
+ }
36085
+ function toThinkingLevel(level) {
36086
+ return level;
36087
+ }
36088
+ function handleChatNewPost(request) {
36089
+ return wrapApiHandler(async () => {
36090
+ const bridge = getBridge();
36091
+ const registry = getPrimeConfig().projectRegistry;
36092
+ const raw = await request.json().catch(() => ({}));
36093
+ const body = ChatNewRequestSchema.parse(raw);
36094
+ const projectId = body.projectId ?? await registry.projectIdForCwd(getPrimeConfig().defaultCwd) ?? (await registry.register(getPrimeConfig().defaultCwd)).projectId;
36095
+ const project = await registry.get(projectId);
36096
+ bridge.ensureKernelReady(project.canonicalPath).catch(() => {
36097
+ });
36098
+ const session = await bridge.createSession({
36099
+ cwd: project.canonicalPath,
36100
+ projectId,
36101
+ thinkingLevel: toThinkingLevel(body.thinkingLevel),
36102
+ mode: body.mode,
36103
+ openUI: body.openUI
36104
+ });
36105
+ if (body.model) {
36106
+ await bridge.setModel(session.sessionId, body.model);
36107
+ const thinking = body.model.thinkingLevel ?? body.thinkingLevel;
36108
+ if (thinking) await session.connection.setThinkingLevel(toThinkingLevel(thinking));
36109
+ }
36110
+ return Response.json({
36111
+ session: {
36112
+ sessionId: session.sessionId,
36113
+ projectId
36114
+ },
36115
+ messages: [],
36116
+ planPresentations: [],
36117
+ presentation: bridge.getPresentation(session.sessionId)
36118
+ });
36119
+ }, request);
36120
+ }
36121
+ function record(value) {
36122
+ return typeof value === "object" && value !== null ? value : void 0;
36123
+ }
36124
+ function artifactIndexFor(artifact) {
36125
+ const input = record(artifact.input);
36126
+ return typeof input?.artifactIndex === "number" ? input.artifactIndex : void 0;
36127
+ }
36128
+ function handleChatOpenUIArtifactPut(request) {
36129
+ return wrapApiHandler(async () => {
36130
+ const raw = await request.json().catch(() => ({}));
36131
+ const parsed = ChatOpenUIArtifactUpsertRequestSchema.safeParse(raw);
36132
+ if (!parsed.success) return Response.json({ message: "Invalid OpenUI artifact request." }, { status: 400 });
36133
+ const body = parsed.data;
36134
+ const validation = validateAndNormalizeOpenUIHtmlArtifact(body.artifact);
36135
+ if (!validation.ok) return Response.json({ message: validation.reason }, { status: validation.status ?? 400 });
36136
+ const bridge = getBridge();
36137
+ const session = bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId);
36138
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
36139
+ const messages = await bridge.getMessages(session.sessionId);
36140
+ const incomingId = body.assistantMessageId;
36141
+ const exact = messages.find((message) => message.role === "assistant" && message.id === incomingId);
36142
+ const existing = bridge.getPresentation(session.sessionId).artifactRuns.flatMap((run) => run.artifacts).filter((artifact2) => artifact2.kind === "openui-html").find((artifact2) => artifactIndexFor(artifact2) === body.artifactIndex && (record(artifact2.input)?.clientMessageId === incomingId || artifact2.sourceMessageId === incomingId));
36143
+ const fallback = existing ? void 0 : [...messages].reverse().find((message) => message.role === "assistant");
36144
+ const assistantMessageId = exact?.id ?? existing?.sourceMessageId ?? fallback?.id;
36145
+ if (!assistantMessageId) return Response.json({ message: "Unable to associate OpenUI artifact with an assistant message." }, { status: 409 });
36146
+ const artifact = {
36147
+ id: stablePresentationId(`${assistantMessageId}:openui-html:${body.artifactIndex}`),
36148
+ runId: stablePresentationId(`${assistantMessageId}:openui`),
36149
+ sourceMessageId: assistantMessageId,
36150
+ kind: "openui-html",
36151
+ title: validation.value.title,
36152
+ status: "success",
36153
+ input: {
36154
+ clientMessageId: incomingId,
36155
+ artifactIndex: body.artifactIndex
36156
+ },
36157
+ output: validation.value,
36158
+ timestamp: Date.now()
36159
+ };
36160
+ const presentation = await bridge.upsertPresentationArtifact(session.sessionId, artifact);
36161
+ return Response.json({
36162
+ artifact,
36163
+ presentation
36164
+ });
36165
+ }, request);
36166
+ }
36167
+ function handleChatPlanPresentationPut(request) {
36168
+ return wrapApiHandler(async () => {
36169
+ const body = ChatPlanPresentationUpsertRequestSchema.parse(await request.json().catch(() => ({})));
36170
+ const bridge = getBridge();
36171
+ const session = bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId);
36172
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
36173
+ const messages = await bridge.getMessages(body.sessionId);
36174
+ const incomingId = body.presentation.assistantMessageId;
36175
+ const known = messages.find((message) => message.id === incomingId && message.role === "assistant");
36176
+ const existing = (await loadManagedPlanPresentations(session)).find((item) => item.assistantMessageId === incomingId || item.clientMessageId === incomingId);
36177
+ const fallback = existing ? void 0 : [...messages].reverse().find((message) => message.role === "assistant");
36178
+ const assistantMessageId = known?.id ?? existing?.assistantMessageId ?? fallback?.id;
36179
+ if (!assistantMessageId) return Response.json({ message: "Unable to associate Plan presentation with an assistant message" }, { status: 409 });
36180
+ const clientMessageId = incomingId !== assistantMessageId ? existing?.clientMessageId ?? incomingId : existing?.clientMessageId;
36181
+ return Response.json({ presentation: await upsertManagedPlanPresentation(session, {
36182
+ ...body.presentation,
36183
+ assistantMessageId,
36184
+ ...clientMessageId ? { clientMessageId } : {}
36185
+ }) });
36186
+ }, request);
35317
36187
  }
35318
36188
  function handleChatQuestionPost(request) {
35319
36189
  return wrapApiHandler(async () => {
@@ -35325,19 +36195,27 @@ function handleChatQuestionPost(request) {
35325
36195
  ok: false,
35326
36196
  message: "answer requires sessionId and toolCallId"
35327
36197
  }, { status: 400 });
35328
- if (!getBridge().answerDialog(sessionId, toolCallId, body.answer)) return Response.json({
36198
+ const bridge = getBridge();
36199
+ if (!await requireProjectSession(bridge.getSession(sessionId) ?? await bridge.resumeSessionById(sessionId))) return Response.json({
36200
+ ok: false,
36201
+ message: `Unknown session: ${sessionId}`
36202
+ }, { status: 404 });
36203
+ if (!bridge.answerDialog(sessionId, toolCallId, body.answer)) return Response.json({
35329
36204
  ok: false,
35330
36205
  message: "Question is no longer active"
35331
36206
  }, { status: 404 });
35332
36207
  return Response.json({ ok: true });
35333
- });
36208
+ }, request);
35334
36209
  }
35335
36210
  function handleChatQueueMutationPost(request) {
35336
36211
  return wrapApiHandler(async () => {
35337
36212
  const body = ChatQueueMutationRequestSchema.parse(await request.json().catch(() => ({})));
35338
- const result = await getBridge().deleteQueuedMessage(body.sessionId, body.lane, body.index, body.expectedText);
36213
+ const bridge = getBridge();
36214
+ if (!await requireProjectSession(bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId))) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
36215
+ const mutation = body.mutation ?? { type: "delete" };
36216
+ const result = await bridge.mutateQueuedMessage(body.sessionId, body.lane, body.index, body.expectedText, mutation);
35339
36217
  return Response.json(ChatQueueMutationResponseSchema.parse(result));
35340
- });
36218
+ }, request);
35341
36219
  }
35342
36220
  function packageSourceToString(source) {
35343
36221
  if (typeof source === "string") return { name: source };
@@ -35394,7 +36272,7 @@ function handleChatResourcesGet(request) {
35394
36272
  })),
35395
36273
  diagnostics
35396
36274
  });
35397
- });
36275
+ }, request);
35398
36276
  }
35399
36277
  var BodySchema = ChatSessionMetadataSchema.extend({ sessionId: SessionIdSchema });
35400
36278
  function handleChatResumePost(request) {
@@ -35416,7 +36294,7 @@ function handleChatResumePost(request) {
35416
36294
  });
35417
36295
  }
35418
36296
  return Response.json({ message: "resume requires sessionId" }, { status: 400 });
35419
- });
36297
+ }, request);
35420
36298
  }
35421
36299
  function handleChatSessionGet(request) {
35422
36300
  return wrapApiHandler(async () => {
@@ -35428,7 +36306,10 @@ function handleChatSessionGet(request) {
35428
36306
  const parsedParentSessionId = SessionIdSchema.safeParse(parentSessionId);
35429
36307
  const parsedChildId = SessionIdSchema.safeParse(childId);
35430
36308
  if (!parsedParentSessionId.success || !parsedChildId.success) return Response.json({ message: "Invalid subagent session identifiers" }, { status: 400 });
35431
- const child = await getBridge().loadRlmChildTranscript(parsedParentSessionId.data, parsedChildId.data);
36309
+ const bridge2 = getBridge();
36310
+ const liveParent = bridge2.getSession(parsedParentSessionId.data);
36311
+ if (liveParent && !await requireProjectSession(liveParent)) return Response.json({ message: `Unknown session: ${parsedParentSessionId.data}` }, { status: 404 });
36312
+ const child = await bridge2.loadRlmChildTranscript(parsedParentSessionId.data, parsedChildId.data);
35432
36313
  if (!child) return Response.json({ message: "Unknown subagent transcript" }, { status: 404 });
35433
36314
  return Response.json({
35434
36315
  session: {
@@ -35447,7 +36328,7 @@ function handleChatSessionGet(request) {
35447
36328
  const bridge = getBridge();
35448
36329
  const projectId = requestedProjectId ? ProjectIdSchema.parse(requestedProjectId) : void 0;
35449
36330
  const existing = projectId ? await bridge.resumeSessionById(sessionId, projectId, { openUI }) : bridge.getSession(sessionId) ?? await bridge.resumeSessionById(sessionId, void 0, { openUI });
35450
- if (!existing) return Response.json({ message: `Unknown session: ${sessionId}` }, { status: 404 });
36331
+ if (!existing || !await requireProjectSession(existing)) return Response.json({ message: `Unknown session: ${sessionId}` }, { status: 404 });
35451
36332
  return Response.json({
35452
36333
  session: {
35453
36334
  sessionId: existing.sessionId,
@@ -35457,7 +36338,7 @@ function handleChatSessionGet(request) {
35457
36338
  planPresentations: await loadManagedPlanPresentations(existing),
35458
36339
  presentation: bridge.getPresentation(existing.sessionId)
35459
36340
  });
35460
- });
36341
+ }, request);
35461
36342
  }
35462
36343
  var RenameSessionSchema = external_exports.object({
35463
36344
  sessionId: SessionIdSchema,
@@ -35468,24 +36349,28 @@ function handleChatSessionRenamePatch(request) {
35468
36349
  return wrapApiHandler(async () => {
35469
36350
  const body = RenameSessionSchema.parse(await request.json().catch(() => ({})));
35470
36351
  const bridge = getBridge();
35471
- if (!(bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId))) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
36352
+ const session = bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId);
36353
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
35472
36354
  bridge.setSessionName(body.sessionId, body.title);
35473
36355
  return Response.json({
35474
36356
  ok: true,
35475
36357
  sessionId: body.sessionId,
35476
36358
  title: body.title
35477
36359
  });
35478
- });
36360
+ }, request);
35479
36361
  }
35480
36362
  function handleChatSessionDelete(request) {
35481
36363
  return wrapApiHandler(async () => {
35482
36364
  const body = DeleteSessionSchema.parse(await request.json().catch(() => ({})));
35483
- if (!await getBridge().deleteSession(body.sessionId)) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
36365
+ const bridge = getBridge();
36366
+ const session = bridge.getSession(body.sessionId) ?? await bridge.resumeSessionById(body.sessionId);
36367
+ if (!session || !await requireProjectSession(session)) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
36368
+ if (!await bridge.deleteSession(body.sessionId)) return Response.json({ message: `Unknown session: ${body.sessionId}` }, { status: 404 });
35484
36369
  return Response.json({
35485
36370
  ok: true,
35486
36371
  sessionId: body.sessionId
35487
36372
  });
35488
- });
36373
+ }, request);
35489
36374
  }
35490
36375
  function stringValue(value) {
35491
36376
  return typeof value === "string" && value.length > 0 ? value : void 0;
@@ -35509,14 +36394,53 @@ function normalizeSessionListRow(source) {
35509
36394
  source,
35510
36395
  sessionId,
35511
36396
  cwd,
35512
- title: stringValue(raw.sessionName) ?? stringValue(raw.name),
36397
+ title: meaningfulSessionLabel(stringValue(raw.sessionName) ?? stringValue(raw.name)),
35513
36398
  createdAt,
35514
36399
  updatedAt,
35515
36400
  messageCount: messageCountValue(raw.messageCount),
35516
- firstMessage: typeof raw.firstMessage === "string" ? raw.firstMessage : "",
36401
+ firstMessage: meaningfulSessionLabel(typeof raw.firstMessage === "string" ? raw.firstMessage : void 0) ?? "",
35517
36402
  isSubagent
35518
36403
  };
35519
36404
  }
36405
+ function sessionSourcePath(source) {
36406
+ const raw = source;
36407
+ return stringValue(raw.sessionFile) ?? stringValue(raw.path);
36408
+ }
36409
+ function latestBy(entries, time) {
36410
+ if (entries.length === 0) return void 0;
36411
+ return entries.reduce((latest, entry) => time(entry) >= time(latest) ? entry : latest);
36412
+ }
36413
+ function presentationListActivity(presentation) {
36414
+ if (!presentation) return { activityCount: 0 };
36415
+ const artifacts = presentation.artifactRuns.flatMap((run) => run.artifacts);
36416
+ const latestRefinement = latestBy(presentation.refinements, (entry) => entry.timestamp);
36417
+ const latestArtifact = latestBy(artifacts, (entry) => entry.timestamp);
36418
+ const latestBash = latestBy(presentation.userBash, (entry) => entry.startedAt);
36419
+ return {
36420
+ title: meaningfulSessionLabel(presentation.sessionName) ?? meaningfulSessionLabel(latestRefinement?.summary) ?? meaningfulSessionLabel(latestArtifact?.title) ?? meaningfulSessionLabel(latestBash?.command),
36421
+ activityCount: presentation.refinements.length + artifacts.length + presentation.userBash.length
36422
+ };
36423
+ }
36424
+ function applyPresentationToSessionRow(row, presentation) {
36425
+ const { title: presentationTitle, activityCount } = presentationListActivity(presentation);
36426
+ const firstMessage = row.firstMessage || presentationTitle || "";
36427
+ return {
36428
+ title: row.title ?? meaningfulSessionLabel(firstMessage) ?? presentationTitle ?? fallbackSessionLabel(row.sessionId),
36429
+ firstMessage,
36430
+ messageCount: row.messageCount > 0 ? row.messageCount : activityCount > 0 ? 1 : 0
36431
+ };
36432
+ }
36433
+ function sessionNeedsPresentationJoin(row) {
36434
+ return row.messageCount === 0 || !row.title || !row.firstMessage;
36435
+ }
36436
+ async function loadSessionListPresentation(row, source, livePresentation) {
36437
+ const liveActivity = presentationListActivity(livePresentation);
36438
+ if (liveActivity.activityCount > 0 || liveActivity.title) return livePresentation;
36439
+ if (!sessionNeedsPresentationJoin(row)) return livePresentation;
36440
+ const path = sessionSourcePath(source);
36441
+ if (!path) return livePresentation;
36442
+ return await loadManagedPrimePresentation({ sessionPath: path }) ?? livePresentation;
36443
+ }
35520
36444
  function sessionStatus(info, live) {
35521
36445
  if (live?.isStreaming || live?.session?.isStreaming || info.isStreaming) return "running";
35522
36446
  if (info.activity === "working" && (info.isSessionActive || info.hasRunningRlmChildren === true)) return "running";
@@ -35544,22 +36468,24 @@ function handleProjectsGet(_request) {
35544
36468
  for (const projectId of assignments.values()) if (projectId) counts.set(projectId, (counts.get(projectId) ?? 0) + 1);
35545
36469
  return Response.json({
35546
36470
  projects: await getPrimeConfig().projectRegistry.list(counts),
35547
- sessions: sessions2.map((source) => {
36471
+ sessions: await Promise.all(sessions2.map(async (source) => {
35548
36472
  const session = normalizeSessionListRow(source);
36473
+ const liveSession = getBridge().getSession(session.sessionId);
36474
+ const fields = applyPresentationToSessionRow(session, await loadSessionListPresentation(session, source, liveSession?.mapperState?.presentation));
35549
36475
  return {
35550
36476
  sessionId: session.sessionId,
35551
36477
  projectId: assignments.get(session.sessionId) ?? null,
35552
- title: redactSessionLabelSecrets(session.title || session.firstMessage || session.sessionId.slice(0, 8)),
36478
+ title: redactSessionLabelSecrets(fields.title),
35553
36479
  createdAt: session.createdAt,
35554
36480
  updatedAt: session.updatedAt,
35555
- status: sessionStatus(session.source, getBridge().getSession(session.sessionId)),
35556
- messageCount: session.messageCount,
35557
- firstMessage: session.firstMessage ? redactSessionLabelSecrets(session.firstMessage) : "",
36481
+ status: sessionStatus(session.source, liveSession),
36482
+ messageCount: fields.messageCount,
36483
+ firstMessage: fields.firstMessage ? redactSessionLabelSecrets(fields.firstMessage) : "",
35558
36484
  ...session.isSubagent ? { isSubagent: true } : {}
35559
36485
  };
35560
- })
36486
+ }))
35561
36487
  });
35562
- });
36488
+ }, _request);
35563
36489
  }
35564
36490
  function handleProjectsPost(request) {
35565
36491
  return wrapApiHandler(async () => {
@@ -35567,20 +36493,20 @@ function handleProjectsPost(request) {
35567
36493
  const registry = getPrimeConfig().projectRegistry;
35568
36494
  const path = await registry.resolveDirectoryInput(body);
35569
36495
  return Response.json({ project: await registry.register(path, body.name) }, { status: 201 });
35570
- });
36496
+ }, request);
35571
36497
  }
35572
36498
  function handleProjectPatch(request) {
35573
36499
  return wrapApiHandler(async () => {
35574
36500
  const projectId = ProjectIdSchema.parse(new URL(request.url).searchParams.get("projectId") ?? "");
35575
36501
  const body = ProjectRenameRequestSchema.parse(await request.json().catch(() => ({})));
35576
36502
  return Response.json({ project: await getPrimeConfig().projectRegistry.rename(projectId, body.name) });
35577
- });
36503
+ }, request);
35578
36504
  }
35579
36505
  function handleProjectDelete(request) {
35580
36506
  return wrapApiHandler(async () => {
35581
36507
  const projectId = ProjectIdSchema.parse(new URL(request.url).searchParams.get("projectId") ?? "");
35582
36508
  return Response.json({ project: await getPrimeConfig().projectRegistry.unregister(projectId) });
35583
- });
36509
+ }, request);
35584
36510
  }
35585
36511
  function handleProjectSessionFork(request) {
35586
36512
  return wrapApiHandler(async () => {
@@ -35590,7 +36516,7 @@ function handleProjectSessionFork(request) {
35590
36516
  sessionId,
35591
36517
  projectId: body.targetProjectId
35592
36518
  }, { status: 201 });
35593
- });
36519
+ }, request);
35594
36520
  }
35595
36521
  function handleProjectBrowseGet(request) {
35596
36522
  return wrapApiHandler(async () => {
@@ -35600,7 +36526,7 @@ function handleProjectBrowseGet(request) {
35600
36526
  token: url.searchParams.get("token") ?? void 0
35601
36527
  });
35602
36528
  return Response.json(result);
35603
- });
36529
+ }, request);
35604
36530
  }
35605
36531
  function handleChatSessionsGet(_request) {
35606
36532
  return wrapApiHandler(async () => {
@@ -35611,20 +36537,21 @@ function handleChatSessionsGet(_request) {
35611
36537
  const formatted = await Promise.all(sessions2.map(async (s) => {
35612
36538
  const session = normalizeSessionListRow(s);
35613
36539
  const liveSession = bridge.getSession(session.sessionId);
36540
+ const fields = applyPresentationToSessionRow(session, await loadSessionListPresentation(session, s, liveSession?.mapperState?.presentation));
35614
36541
  return {
35615
36542
  sessionId: session.sessionId,
35616
36543
  projectId: await getPrimeConfig().projectRegistry.projectIdForSession(session.sessionId, liveSession?.cwd ?? session.cwd),
35617
- title: session.title || session.firstMessage || session.sessionId.slice(0, 8),
36544
+ title: fields.title,
35618
36545
  createdAt: session.createdAt,
35619
36546
  updatedAt: session.updatedAt,
35620
36547
  status: sessionStatus(session.source, liveSession),
35621
- messageCount: session.messageCount,
35622
- firstMessage: session.firstMessage,
36548
+ messageCount: fields.messageCount,
36549
+ firstMessage: fields.firstMessage,
35623
36550
  ...session.isSubagent ? { isSubagent: true } : {}
35624
36551
  };
35625
36552
  }));
35626
36553
  return Response.json({ sessions: projectId ? formatted.filter((session) => session.projectId === projectId) : formatted });
35627
- });
36554
+ }, _request);
35628
36555
  }
35629
36556
  function toProjectShape(project) {
35630
36557
  const out = {};
@@ -35694,7 +36621,7 @@ function handleChatSettingsGet(request) {
35694
36621
  return wrapApiHandler(async () => {
35695
36622
  const cwd = await cwdForRequest(request);
35696
36623
  return Response.json(buildResponse(cwd));
35697
- });
36624
+ }, request);
35698
36625
  }
35699
36626
  function handleChatSettingsPatch(request) {
35700
36627
  return wrapApiHandler(async () => {
@@ -35719,7 +36646,7 @@ function handleChatSettingsPatch(request) {
35719
36646
  if (patch.themes !== void 0) manager.setThemePaths(patch.themes);
35720
36647
  if (patch.packages !== void 0) manager.setPackages(patch.packages);
35721
36648
  return Response.json(buildResponse(cwd));
35722
- });
36649
+ }, request);
35723
36650
  }
35724
36651
  function handleHealthGet(request) {
35725
36652
  return wrapApiHandler(async () => {
@@ -35729,12 +36656,12 @@ function handleHealthGet(request) {
35729
36656
  kernel,
35730
36657
  uptimeMs: process.uptime() * 1e3
35731
36658
  });
35732
- });
36659
+ }, request);
35733
36660
  }
35734
36661
  function handleWorkspaceBrowseGet(request) {
35735
36662
  return wrapApiHandler(async () => {
35736
36663
  return Response.json({ message: "Workspace root switching is disabled; use /api/projects/browse for opaque directory tokens." }, { status: 410 });
35737
- });
36664
+ }, request);
35738
36665
  }
35739
36666
  function canonicalizePath(path) {
35740
36667
  try {
@@ -35855,7 +36782,7 @@ function handleWorkspaceFileGet(request) {
35855
36782
  const result = await readWorkspaceFile(await cwdForRequest(request), path);
35856
36783
  if (result.kind === "error") return Response.json({ message: result.message }, { status: result.status });
35857
36784
  return Response.json(result.body);
35858
- });
36785
+ }, request);
35859
36786
  }
35860
36787
  var IGNORED = /* @__PURE__ */ new Set([
35861
36788
  ".git",
@@ -35935,179 +36862,195 @@ function handleWorkspaceTreeGet(_request) {
35935
36862
  nodes,
35936
36863
  diagnostics
35937
36864
  });
35938
- });
36865
+ }, _request);
35939
36866
  }
35940
- var Route$25 = createFileRoute("/api/chat")({ server: { handlers: { POST: ({ request }) => handleChatPost(request) } } });
35941
- var Route$24 = createFileRoute("/api/health")({ server: { handlers: { GET: ({ request }) => handleHealthGet(request) } } });
35942
- var Route$23 = createFileRoute("/api/projects")({ server: { handlers: {
36867
+ var Route$27 = createFileRoute("/api/chat")({ server: { handlers: { POST: ({ request }) => handleChatPost(request) } } });
36868
+ var Route$26 = createFileRoute("/api/health")({ server: { handlers: { GET: ({ request }) => handleHealthGet(request) } } });
36869
+ var Route$25 = createFileRoute("/api/projects")({ server: { handlers: {
35943
36870
  DELETE: ({ request }) => handleProjectDelete(request),
35944
36871
  GET: ({ request }) => handleProjectsGet(request),
35945
36872
  PATCH: ({ request }) => handleProjectPatch(request),
35946
36873
  POST: ({ request }) => handleProjectsPost(request)
35947
36874
  } } });
35948
- var Route$22 = createFileRoute("/api/chat/abort")({ server: { handlers: { POST: ({ request }) => handleChatAbortPost(request) } } });
35949
- var Route$21 = createFileRoute("/api/chat/artifacts")({ server: { handlers: { PUT: ({ request }) => handleChatOpenUIArtifactPut(request) } } });
35950
- var Route$20 = createFileRoute("/api/chat/command")({ server: { handlers: { POST: ({ request }) => handleChatCommandPost(request) } } });
35951
- var Route$19 = createFileRoute("/api/chat/commands")({ server: { handlers: { GET: ({ request }) => handleChatCommandsGet(request) } } });
35952
- var Route$18 = createFileRoute("/api/chat/events")({ server: { handlers: { GET: ({ request }) => handleChatEventsGet(request) } } });
35953
- var Route$17 = createFileRoute("/api/chat/model")({ server: { handlers: { POST: ({ request }) => handleChatModelPost(request) } } });
35954
- var Route$16 = createFileRoute("/api/chat/models")({ server: { handlers: { GET: ({ request }) => handleChatModelsGet(request) } } });
35955
- var Route$15 = createFileRoute("/api/chat/new")({ server: { handlers: { POST: ({ request }) => handleChatNewPost(request) } } });
35956
- var Route$14 = createFileRoute("/api/chat/providers")({ server: { handlers: {
36875
+ var Route$24 = createFileRoute("/api/chat/abort")({ server: { handlers: { POST: ({ request }) => handleChatAbortPost(request) } } });
36876
+ var Route$23 = createFileRoute("/api/chat/artifacts")({ server: { handlers: { PUT: ({ request }) => handleChatOpenUIArtifactPut(request) } } });
36877
+ var Route$22 = createFileRoute("/api/chat/command")({ server: { handlers: { POST: ({ request }) => handleChatCommandPost(request) } } });
36878
+ var Route$21 = createFileRoute("/api/chat/commands")({ server: { handlers: { GET: ({ request }) => handleChatCommandsGet(request) } } });
36879
+ var Route$20 = createFileRoute("/api/chat/events")({ server: { handlers: { GET: ({ request }) => handleChatEventsGet(request) } } });
36880
+ var Route$19 = createFileRoute("/api/chat/mcp")({ server: { handlers: {
36881
+ GET: ({ request }) => handleChatMcpGet(request),
36882
+ POST: ({ request }) => handleChatMcpPost(request),
36883
+ DELETE: ({ request }) => handleChatMcpDelete(request)
36884
+ } } });
36885
+ var Route$18 = createFileRoute("/api/chat/model")({ server: { handlers: { POST: ({ request }) => handleChatModelPost(request) } } });
36886
+ var Route$17 = createFileRoute("/api/chat/models")({ server: { handlers: { GET: ({ request }) => handleChatModelsGet(request) } } });
36887
+ var Route$16 = createFileRoute("/api/chat/new")({ server: { handlers: { POST: ({ request }) => handleChatNewPost(request) } } });
36888
+ var Route$15 = createFileRoute("/api/chat/providers")({ server: { handlers: {
35957
36889
  GET: ({ request }) => handleChatProvidersGet(request),
35958
36890
  POST: ({ request }) => handleChatProvidersPost(request),
35959
36891
  DELETE: ({ request }) => handleChatProvidersDelete(request)
35960
36892
  } } });
35961
- var Route$13 = createFileRoute("/api/chat/question")({ server: { handlers: { POST: ({ request }) => handleChatQuestionPost(request) } } });
35962
- var Route$12 = createFileRoute("/api/chat/resources")({ server: { handlers: { GET: ({ request }) => handleChatResourcesGet(request) } } });
35963
- var Route$11 = createFileRoute("/api/chat/resume")({ server: { handlers: { POST: ({ request }) => handleChatResumePost(request) } } });
35964
- var Route$10 = createFileRoute("/api/chat/session")({ server: { handlers: {
36893
+ var Route$14 = createFileRoute("/api/chat/question")({ server: { handlers: { POST: ({ request }) => handleChatQuestionPost(request) } } });
36894
+ var Route$13 = createFileRoute("/api/chat/resources")({ server: { handlers: { GET: ({ request }) => handleChatResourcesGet(request) } } });
36895
+ var Route$12 = createFileRoute("/api/chat/resume")({ server: { handlers: { POST: ({ request }) => handleChatResumePost(request) } } });
36896
+ var Route$11 = createFileRoute("/api/chat/session")({ server: { handlers: {
35965
36897
  GET: ({ request }) => new URL(request.url).searchParams.has("attachmentId") ? handleChatAttachmentGet(request) : handleChatSessionGet(request),
35966
36898
  POST: ({ request }) => handleChatAttachmentsPost(request),
35967
36899
  PUT: ({ request }) => handleChatPlanPresentationPut(request),
35968
36900
  PATCH: ({ request }) => handleChatQueueMutationPost(request)
35969
36901
  } } });
35970
- var Route$9 = createFileRoute("/api/chat/sessions")({ server: { handlers: {
36902
+ var Route$10 = createFileRoute("/api/chat/sessions")({ server: { handlers: {
35971
36903
  DELETE: ({ request }) => handleChatSessionDelete(request),
35972
36904
  GET: ({ request }) => handleChatSessionsGet(request),
35973
36905
  PATCH: ({ request }) => handleChatSessionRenamePatch(request)
35974
36906
  } } });
35975
- var Route$8 = createFileRoute("/api/chat/settings")({ server: { handlers: {
36907
+ var Route$9 = createFileRoute("/api/chat/settings")({ server: { handlers: {
35976
36908
  GET: ({ request }) => handleChatSettingsGet(request),
35977
36909
  PATCH: ({ request }) => handleChatSettingsPatch(request)
35978
36910
  } } });
35979
- var Route$7 = createFileRoute("/api/projects/browse")({ server: { handlers: { GET: ({ request }) => handleProjectBrowseGet(request) } } });
35980
- var Route$6 = createFileRoute("/api/projects/fork")({ server: { handlers: { POST: ({ request }) => handleProjectSessionFork(request) } } });
35981
- var Route$5 = createFileRoute("/api/workspace/browse")({ server: { handlers: { GET: ({ request }) => handleWorkspaceBrowseGet(request) } } });
35982
- var Route$4 = createFileRoute("/api/workspace/file")({ server: { handlers: { GET: ({ request }) => handleWorkspaceFileGet(request) } } });
35983
- var Route$3 = createFileRoute("/api/workspace/root")({ server: { handlers: { POST: () => Response.json({ message: "The workspace root is fixed when Fleet starts" }, { status: 405 }) } } });
35984
- var Route$2 = createFileRoute("/api/workspace/tree")({ server: { handlers: { GET: ({ request }) => handleWorkspaceTreeGet(request) } } });
36911
+ var Route$8 = createFileRoute("/api/projects/browse")({ server: { handlers: { GET: ({ request }) => handleProjectBrowseGet(request) } } });
36912
+ var Route$7 = createFileRoute("/api/projects/fork")({ server: { handlers: { POST: ({ request }) => handleProjectSessionFork(request) } } });
36913
+ var Route$6 = createFileRoute("/api/workspace/browse")({ server: { handlers: { GET: ({ request }) => handleWorkspaceBrowseGet(request) } } });
36914
+ var Route$5 = createFileRoute("/api/workspace/file")({ server: { handlers: { GET: ({ request }) => handleWorkspaceFileGet(request) } } });
36915
+ var Route$4 = createFileRoute("/api/workspace/root")({ server: { handlers: { POST: () => Response.json({ message: "The workspace root is fixed when Fleet starts" }, { status: 405 }) } } });
36916
+ var Route$3 = createFileRoute("/api/workspace/tree")({ server: { handlers: { GET: ({ request }) => handleWorkspaceTreeGet(request) } } });
36917
+ var Route$2 = createFileRoute("/api/chat/mcp/oauth")({ server: { handlers: { POST: ({ request }) => handleChatMcpOAuthPost(request) } } });
35985
36918
  var Route$1 = createFileRoute("/api/chat/models/discover")({ server: { handlers: { POST: ({ request }) => handleChatModelsDiscoverPost(request) } } });
35986
36919
  var Route = createFileRoute("/api/chat/providers/oauth")({ server: { handlers: { POST: ({ request }) => handleChatProvidersOAuthPost(request) } } });
35987
- var IndexRoute = Route$26.update({
36920
+ var IndexRoute = Route$28.update({
35988
36921
  id: "/",
35989
36922
  path: "/",
35990
- getParentRoute: () => Route$27
36923
+ getParentRoute: () => Route$29
35991
36924
  });
35992
- var ApiChatRoute = Route$25.update({
36925
+ var ApiChatRoute = Route$27.update({
35993
36926
  id: "/api/chat",
35994
36927
  path: "/api/chat",
35995
- getParentRoute: () => Route$27
36928
+ getParentRoute: () => Route$29
35996
36929
  });
35997
- var ApiHealthRoute = Route$24.update({
36930
+ var ApiHealthRoute = Route$26.update({
35998
36931
  id: "/api/health",
35999
36932
  path: "/api/health",
36000
- getParentRoute: () => Route$27
36933
+ getParentRoute: () => Route$29
36001
36934
  });
36002
- var ApiProjectsRoute = Route$23.update({
36935
+ var ApiProjectsRoute = Route$25.update({
36003
36936
  id: "/api/projects",
36004
36937
  path: "/api/projects",
36005
- getParentRoute: () => Route$27
36938
+ getParentRoute: () => Route$29
36006
36939
  });
36007
- var ApiChatAbortRoute = Route$22.update({
36940
+ var ApiChatAbortRoute = Route$24.update({
36008
36941
  id: "/abort",
36009
36942
  path: "/abort",
36010
36943
  getParentRoute: () => ApiChatRoute
36011
36944
  });
36012
- var ApiChatArtifactsRoute = Route$21.update({
36945
+ var ApiChatArtifactsRoute = Route$23.update({
36013
36946
  id: "/artifacts",
36014
36947
  path: "/artifacts",
36015
36948
  getParentRoute: () => ApiChatRoute
36016
36949
  });
36017
- var ApiChatCommandRoute = Route$20.update({
36950
+ var ApiChatCommandRoute = Route$22.update({
36018
36951
  id: "/command",
36019
36952
  path: "/command",
36020
36953
  getParentRoute: () => ApiChatRoute
36021
36954
  });
36022
- var ApiChatCommandsRoute = Route$19.update({
36955
+ var ApiChatCommandsRoute = Route$21.update({
36023
36956
  id: "/commands",
36024
36957
  path: "/commands",
36025
36958
  getParentRoute: () => ApiChatRoute
36026
36959
  });
36027
- var ApiChatEventsRoute = Route$18.update({
36960
+ var ApiChatEventsRoute = Route$20.update({
36028
36961
  id: "/events",
36029
36962
  path: "/events",
36030
36963
  getParentRoute: () => ApiChatRoute
36031
36964
  });
36032
- var ApiChatModelRoute = Route$17.update({
36965
+ var ApiChatMcpRoute = Route$19.update({
36966
+ id: "/mcp",
36967
+ path: "/mcp",
36968
+ getParentRoute: () => ApiChatRoute
36969
+ });
36970
+ var ApiChatModelRoute = Route$18.update({
36033
36971
  id: "/model",
36034
36972
  path: "/model",
36035
36973
  getParentRoute: () => ApiChatRoute
36036
36974
  });
36037
- var ApiChatModelsRoute = Route$16.update({
36975
+ var ApiChatModelsRoute = Route$17.update({
36038
36976
  id: "/models",
36039
36977
  path: "/models",
36040
36978
  getParentRoute: () => ApiChatRoute
36041
36979
  });
36042
- var ApiChatNewRoute = Route$15.update({
36980
+ var ApiChatNewRoute = Route$16.update({
36043
36981
  id: "/new",
36044
36982
  path: "/new",
36045
36983
  getParentRoute: () => ApiChatRoute
36046
36984
  });
36047
- var ApiChatProvidersRoute = Route$14.update({
36985
+ var ApiChatProvidersRoute = Route$15.update({
36048
36986
  id: "/providers",
36049
36987
  path: "/providers",
36050
36988
  getParentRoute: () => ApiChatRoute
36051
36989
  });
36052
- var ApiChatQuestionRoute = Route$13.update({
36990
+ var ApiChatQuestionRoute = Route$14.update({
36053
36991
  id: "/question",
36054
36992
  path: "/question",
36055
36993
  getParentRoute: () => ApiChatRoute
36056
36994
  });
36057
- var ApiChatResourcesRoute = Route$12.update({
36995
+ var ApiChatResourcesRoute = Route$13.update({
36058
36996
  id: "/resources",
36059
36997
  path: "/resources",
36060
36998
  getParentRoute: () => ApiChatRoute
36061
36999
  });
36062
- var ApiChatResumeRoute = Route$11.update({
37000
+ var ApiChatResumeRoute = Route$12.update({
36063
37001
  id: "/resume",
36064
37002
  path: "/resume",
36065
37003
  getParentRoute: () => ApiChatRoute
36066
37004
  });
36067
- var ApiChatSessionRoute = Route$10.update({
37005
+ var ApiChatSessionRoute = Route$11.update({
36068
37006
  id: "/session",
36069
37007
  path: "/session",
36070
37008
  getParentRoute: () => ApiChatRoute
36071
37009
  });
36072
- var ApiChatSessionsRoute = Route$9.update({
37010
+ var ApiChatSessionsRoute = Route$10.update({
36073
37011
  id: "/sessions",
36074
37012
  path: "/sessions",
36075
37013
  getParentRoute: () => ApiChatRoute
36076
37014
  });
36077
- var ApiChatSettingsRoute = Route$8.update({
37015
+ var ApiChatSettingsRoute = Route$9.update({
36078
37016
  id: "/settings",
36079
37017
  path: "/settings",
36080
37018
  getParentRoute: () => ApiChatRoute
36081
37019
  });
36082
- var ApiProjectsBrowseRoute = Route$7.update({
37020
+ var ApiProjectsBrowseRoute = Route$8.update({
36083
37021
  id: "/browse",
36084
37022
  path: "/browse",
36085
37023
  getParentRoute: () => ApiProjectsRoute
36086
37024
  });
36087
- var ApiProjectsForkRoute = Route$6.update({
37025
+ var ApiProjectsForkRoute = Route$7.update({
36088
37026
  id: "/fork",
36089
37027
  path: "/fork",
36090
37028
  getParentRoute: () => ApiProjectsRoute
36091
37029
  });
36092
- var ApiWorkspaceBrowseRoute = Route$5.update({
37030
+ var ApiWorkspaceBrowseRoute = Route$6.update({
36093
37031
  id: "/api/workspace/browse",
36094
37032
  path: "/api/workspace/browse",
36095
- getParentRoute: () => Route$27
37033
+ getParentRoute: () => Route$29
36096
37034
  });
36097
- var ApiWorkspaceFileRoute = Route$4.update({
37035
+ var ApiWorkspaceFileRoute = Route$5.update({
36098
37036
  id: "/api/workspace/file",
36099
37037
  path: "/api/workspace/file",
36100
- getParentRoute: () => Route$27
37038
+ getParentRoute: () => Route$29
36101
37039
  });
36102
- var ApiWorkspaceRootRoute = Route$3.update({
37040
+ var ApiWorkspaceRootRoute = Route$4.update({
36103
37041
  id: "/api/workspace/root",
36104
37042
  path: "/api/workspace/root",
36105
- getParentRoute: () => Route$27
37043
+ getParentRoute: () => Route$29
36106
37044
  });
36107
- var ApiWorkspaceTreeRoute = Route$2.update({
37045
+ var ApiWorkspaceTreeRoute = Route$3.update({
36108
37046
  id: "/api/workspace/tree",
36109
37047
  path: "/api/workspace/tree",
36110
- getParentRoute: () => Route$27
37048
+ getParentRoute: () => Route$29
37049
+ });
37050
+ var ApiChatMcpOauthRoute = Route$2.update({
37051
+ id: "/oauth",
37052
+ path: "/oauth",
37053
+ getParentRoute: () => ApiChatMcpRoute
36111
37054
  });
36112
37055
  var ApiChatModelsDiscoverRoute = Route$1.update({
36113
37056
  id: "/discover",
@@ -36119,6 +37062,8 @@ var ApiChatProvidersOauthRoute = Route.update({
36119
37062
  path: "/oauth",
36120
37063
  getParentRoute: () => ApiChatProvidersRoute
36121
37064
  });
37065
+ var ApiChatMcpRouteChildren = { ApiChatMcpOauthRoute };
37066
+ var ApiChatMcpRouteWithChildren = ApiChatMcpRoute._addFileChildren(ApiChatMcpRouteChildren);
36122
37067
  var ApiChatModelsRouteChildren = { ApiChatModelsDiscoverRoute };
36123
37068
  var ApiChatModelsRouteWithChildren = ApiChatModelsRoute._addFileChildren(ApiChatModelsRouteChildren);
36124
37069
  var ApiChatProvidersRouteChildren = { ApiChatProvidersOauthRoute };
@@ -36128,6 +37073,7 @@ var ApiChatRouteChildren = {
36128
37073
  ApiChatCommandRoute,
36129
37074
  ApiChatCommandsRoute,
36130
37075
  ApiChatEventsRoute,
37076
+ ApiChatMcpRoute: ApiChatMcpRouteWithChildren,
36131
37077
  ApiChatModelRoute,
36132
37078
  ApiChatModelsRoute: ApiChatModelsRouteWithChildren,
36133
37079
  ApiChatNewRoute,
@@ -36154,7 +37100,7 @@ var rootRouteChildren = {
36154
37100
  ApiWorkspaceRootRoute,
36155
37101
  ApiWorkspaceTreeRoute
36156
37102
  };
36157
- var routeTree = Route$27._addFileChildren(rootRouteChildren)._addFileTypes();
37103
+ var routeTree = Route$29._addFileChildren(rootRouteChildren)._addFileTypes();
36158
37104
  function getRouter() {
36159
37105
  return createRouter({
36160
37106
  routeTree,