privateer-agent 0.15.4 → 0.15.6

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 (229) hide show
  1. package/README.md +11 -0
  2. package/bin/boot-bundle.d.mts +2 -0
  3. package/bin/boot-bundle.mjs +44 -0
  4. package/bin/fresh-supervisor.d.mts +5 -1
  5. package/bin/fresh-supervisor.mjs +47 -10
  6. package/bin/privateer-launch.mjs +27 -3
  7. package/bin/privateer-subagent.mjs +5 -2
  8. package/bin/privateer-verify.mjs +12 -0
  9. package/bin/session-recovery.d.mts +53 -0
  10. package/bin/session-recovery.mjs +240 -0
  11. package/bin/session-seal-preload.mjs +17 -0
  12. package/bin/session-seal.d.mts +25 -0
  13. package/bin/session-seal.mjs +242 -0
  14. package/dist-boot/boot.mjs +3 -0
  15. package/dist-boot/c-27JKSMWL.mjs +1460 -0
  16. package/dist-boot/c-2BMRFV24.mjs +269 -0
  17. package/dist-boot/c-2CTF6JA3.mjs +166 -0
  18. package/dist-boot/c-2G3BBHRJ.mjs +84 -0
  19. package/dist-boot/c-2IDLKXX6.mjs +250 -0
  20. package/dist-boot/c-2NJD5VYM.mjs +8 -0
  21. package/dist-boot/c-2S5EEQPW.mjs +1602 -0
  22. package/dist-boot/c-2WRDCUVI.mjs +25 -0
  23. package/dist-boot/c-2YN3M256.mjs +14 -0
  24. package/dist-boot/c-37GM3GMU.mjs +187 -0
  25. package/dist-boot/c-3STKRSVO.mjs +2480 -0
  26. package/dist-boot/c-3TPV6TQQ.mjs +2320 -0
  27. package/dist-boot/c-3UVHLQDJ.mjs +225 -0
  28. package/dist-boot/c-42PUUZYC.mjs +697 -0
  29. package/dist-boot/c-4D5UMWVW.mjs +683 -0
  30. package/dist-boot/c-4ETZRMTA.mjs +46 -0
  31. package/dist-boot/c-4QT25BD5.mjs +1482 -0
  32. package/dist-boot/c-4WVR32AP.mjs +2579 -0
  33. package/dist-boot/c-5EMM567G.mjs +152 -0
  34. package/dist-boot/c-5FA4CV63.mjs +145 -0
  35. package/dist-boot/c-5GV3X3RB.mjs +14 -0
  36. package/dist-boot/c-5JCEPRIY.mjs +52 -0
  37. package/dist-boot/c-5NDKIIWT.mjs +16 -0
  38. package/dist-boot/c-5QL3AR26.mjs +4665 -0
  39. package/dist-boot/c-5XY6MFKN.mjs +5142 -0
  40. package/dist-boot/c-65UZIZC7.mjs +2897 -0
  41. package/dist-boot/c-6FUXKHOK.mjs +1969 -0
  42. package/dist-boot/c-6JHWQE4V.mjs +18242 -0
  43. package/dist-boot/c-6LOBR4I6.mjs +5280 -0
  44. package/dist-boot/c-6QHOZXKQ.mjs +14394 -0
  45. package/dist-boot/c-6ZO2SADQ.mjs +178 -0
  46. package/dist-boot/c-6ZORVVZP.mjs +1449 -0
  47. package/dist-boot/c-76QH4UCP.mjs +2441 -0
  48. package/dist-boot/c-77GO5L5Y.mjs +20982 -0
  49. package/dist-boot/c-7M23XFTN.mjs +5052 -0
  50. package/dist-boot/c-7TC367E2.mjs +32 -0
  51. package/dist-boot/c-7UOG7POX.mjs +1524 -0
  52. package/dist-boot/c-7VHP3FLN.mjs +376 -0
  53. package/dist-boot/c-A73JHZP2.mjs +13207 -0
  54. package/dist-boot/c-ABXULAFL.mjs +71 -0
  55. package/dist-boot/c-ANDD2AQO.mjs +74 -0
  56. package/dist-boot/c-AOY77AZL.mjs +1430 -0
  57. package/dist-boot/c-AXEPZ22I.mjs +108 -0
  58. package/dist-boot/c-B2Y7VNHU.mjs +427 -0
  59. package/dist-boot/c-B7VJUH73.mjs +77 -0
  60. package/dist-boot/c-BFW6CDVL.mjs +24 -0
  61. package/dist-boot/c-BHNIF2AR.mjs +188 -0
  62. package/dist-boot/c-BKF2O6BL.mjs +23 -0
  63. package/dist-boot/c-BOZXFTZN.mjs +8 -0
  64. package/dist-boot/c-BRNYJWZL.mjs +28 -0
  65. package/dist-boot/c-C3N4Z2HO.mjs +93 -0
  66. package/dist-boot/c-C4IV7ZFG.mjs +717 -0
  67. package/dist-boot/c-C5NK6UYV.mjs +793 -0
  68. package/dist-boot/c-C67644MB.mjs +1659 -0
  69. package/dist-boot/c-C6S2YB53.mjs +620 -0
  70. package/dist-boot/c-CDBEJSBF.mjs +37 -0
  71. package/dist-boot/c-CG47HBKU.mjs +435 -0
  72. package/dist-boot/c-CKCTWCT5.mjs +51 -0
  73. package/dist-boot/c-CN77DCGF.mjs +60 -0
  74. package/dist-boot/c-CRBXNKFO.mjs +1795 -0
  75. package/dist-boot/c-CRIHASQ6.mjs +973 -0
  76. package/dist-boot/c-CU4NGAUO.mjs +3786 -0
  77. package/dist-boot/c-CWRFLGQU.mjs +1883 -0
  78. package/dist-boot/c-CYQB2USO.mjs +32 -0
  79. package/dist-boot/c-DEE67DJO.mjs +379 -0
  80. package/dist-boot/c-DIFDBYOP.mjs +1258 -0
  81. package/dist-boot/c-DOSGVXDZ.mjs +34879 -0
  82. package/dist-boot/c-DR7XQI32.mjs +381 -0
  83. package/dist-boot/c-DTGPSB46.mjs +14666 -0
  84. package/dist-boot/c-DTHDNA4P.mjs +320 -0
  85. package/dist-boot/c-E4Z3Y5B5.mjs +3864 -0
  86. package/dist-boot/c-E66Z2TI7.mjs +203 -0
  87. package/dist-boot/c-EBU6RLRV.mjs +1527 -0
  88. package/dist-boot/c-EBZZNGSF.mjs +432 -0
  89. package/dist-boot/c-EC3LJ2KF.mjs +1032 -0
  90. package/dist-boot/c-EQBKXWNX.mjs +15829 -0
  91. package/dist-boot/c-EUEPIW4S.mjs +574 -0
  92. package/dist-boot/c-EX4YVWAT.mjs +378 -0
  93. package/dist-boot/c-EYZGSAJU.mjs +98 -0
  94. package/dist-boot/c-F75VRKY4.mjs +172 -0
  95. package/dist-boot/c-FH4NXKHV.mjs +177 -0
  96. package/dist-boot/c-FHNUCTEM.mjs +140 -0
  97. package/dist-boot/c-FJ5T42DS.mjs +5349 -0
  98. package/dist-boot/c-FPCNRL7G.mjs +1728 -0
  99. package/dist-boot/c-G4DLN7Q4.mjs +8 -0
  100. package/dist-boot/c-G56GYQFZ.mjs +2507 -0
  101. package/dist-boot/c-GLIOERDQ.mjs +1200 -0
  102. package/dist-boot/c-GMJ5HUBM.mjs +14 -0
  103. package/dist-boot/c-H3HSGMAQ.mjs +461 -0
  104. package/dist-boot/c-HIKTO6P7.mjs +275 -0
  105. package/dist-boot/c-I2MCJ34O.mjs +464 -0
  106. package/dist-boot/c-I3C6LBW4.mjs +765 -0
  107. package/dist-boot/c-I54OKQHW.mjs +387 -0
  108. package/dist-boot/c-IFSP3R4D.mjs +80 -0
  109. package/dist-boot/c-IZK77VB7.mjs +171 -0
  110. package/dist-boot/c-J3Y25UGE.mjs +54 -0
  111. package/dist-boot/c-J5IESS7W.mjs +27750 -0
  112. package/dist-boot/c-JJWZBY7L.mjs +254 -0
  113. package/dist-boot/c-JKTFXTGF.mjs +463 -0
  114. package/dist-boot/c-JN57AXQ4.mjs +77 -0
  115. package/dist-boot/c-KFIO6LUW.mjs +1096 -0
  116. package/dist-boot/c-KIEMEFWW.mjs +478 -0
  117. package/dist-boot/c-KIVXWBYD.mjs +28 -0
  118. package/dist-boot/c-KP6CRIP4.mjs +580 -0
  119. package/dist-boot/c-L26VOJX4.mjs +11558 -0
  120. package/dist-boot/c-L5GHWG5G.mjs +5988 -0
  121. package/dist-boot/c-M7NSQAKK.mjs +353 -0
  122. package/dist-boot/c-MCEWOPBW.mjs +56 -0
  123. package/dist-boot/c-MGPIEG7I.mjs +20 -0
  124. package/dist-boot/c-NREXJV3M.mjs +393 -0
  125. package/dist-boot/c-O5PP6CTX.mjs +162 -0
  126. package/dist-boot/c-OBQDHSXM.mjs +1774 -0
  127. package/dist-boot/c-OLQHHDD4.mjs +21 -0
  128. package/dist-boot/c-OXW4JQQH.mjs +319 -0
  129. package/dist-boot/c-P3D7ZUWN.mjs +1288 -0
  130. package/dist-boot/c-P4PQZVV5.mjs +948 -0
  131. package/dist-boot/c-P63T3XL2.mjs +26373 -0
  132. package/dist-boot/c-P6N5DQGZ.mjs +194 -0
  133. package/dist-boot/c-P7MLKQFC.mjs +58 -0
  134. package/dist-boot/c-PB3PZJL5.mjs +1276 -0
  135. package/dist-boot/c-PTN4S6G2.mjs +1259 -0
  136. package/dist-boot/c-PYCE5J7U.mjs +1083 -0
  137. package/dist-boot/c-Q4HYCRLB.mjs +19410 -0
  138. package/dist-boot/c-QUXMETGG.mjs +68 -0
  139. package/dist-boot/c-RDMJZCOX.mjs +356 -0
  140. package/dist-boot/c-ROGJK76M.mjs +17 -0
  141. package/dist-boot/c-RQJJ26T4.mjs +708 -0
  142. package/dist-boot/c-RSKCUPJF.mjs +7559 -0
  143. package/dist-boot/c-RVJU4O6P.mjs +37 -0
  144. package/dist-boot/c-S2RVHR3M.mjs +165 -0
  145. package/dist-boot/c-SKMK3PEE.mjs +91120 -0
  146. package/dist-boot/c-TI7SM4EG.mjs +20 -0
  147. package/dist-boot/c-TILJ6GXD.mjs +1235 -0
  148. package/dist-boot/c-TT6BCUJD.mjs +114 -0
  149. package/dist-boot/c-U4ITOXAN.mjs +2107 -0
  150. package/dist-boot/c-UGERXVO2.mjs +7120 -0
  151. package/dist-boot/c-UPBH5AXY.mjs +1363 -0
  152. package/dist-boot/c-US2PUNZL.mjs +597 -0
  153. package/dist-boot/c-UU4WZPPE.mjs +20 -0
  154. package/dist-boot/c-UWVIYPQS.mjs +39 -0
  155. package/dist-boot/c-V4RHQBPQ.mjs +17683 -0
  156. package/dist-boot/c-V5MRVK47.mjs +351 -0
  157. package/dist-boot/c-V6CL266R.mjs +1755 -0
  158. package/dist-boot/c-VJTMFKYE.mjs +14975 -0
  159. package/dist-boot/c-VJTRUKIB.mjs +773 -0
  160. package/dist-boot/c-W4IFSHPW.mjs +43 -0
  161. package/dist-boot/c-WETGB2CF.mjs +113 -0
  162. package/dist-boot/c-WO3QSZUU.mjs +1337 -0
  163. package/dist-boot/c-WQLH73TY.mjs +242 -0
  164. package/dist-boot/c-XL735JW5.mjs +500 -0
  165. package/dist-boot/c-Y7HADL5B.mjs +1107 -0
  166. package/dist-boot/c-YGQHSVSK.mjs +41 -0
  167. package/dist-boot/c-YMP2AMEB.mjs +10350 -0
  168. package/dist-boot/c-YRDO2WEF.mjs +12012 -0
  169. package/dist-boot/c-YUSTRATJ.mjs +8 -0
  170. package/dist-boot/c-Z2262R22.mjs +2697 -0
  171. package/dist-boot/c-Z4ALQMJC.mjs +709 -0
  172. package/dist-boot/c-Z5ZUQVUY.mjs +62 -0
  173. package/dist-boot/c-ZDZUKXF7.mjs +874 -0
  174. package/dist-boot/c-ZJYLGIC3.mjs +32 -0
  175. package/dist-boot/c-ZNA5CDMI.mjs +77 -0
  176. package/dist-boot/c-ZSO6DI3M.mjs +204 -0
  177. package/dist-boot/c-ZWUVHKMK.mjs +100 -0
  178. package/dist-boot/c-ZY4MESEW.mjs +832 -0
  179. package/dist-boot/c-ZZ5REI4L.mjs +329 -0
  180. package/dist-boot/cli.mjs +51 -0
  181. package/dist-boot/ext-anthropic-attribution.mjs +207 -0
  182. package/dist-boot/ext-pi-background-tasks.mjs +7234 -0
  183. package/dist-boot/ext-pi-subagents.mjs +37 -0
  184. package/dist-boot/ext-privateer-account.mjs +4426 -0
  185. package/dist-boot/ext-privateer-brand.mjs +5274 -0
  186. package/dist-boot/ext-privateer-codemode.mjs +37 -0
  187. package/dist-boot/ext-privateer-computer.mjs +626 -0
  188. package/dist-boot/ext-privateer-connect.mjs +1862 -0
  189. package/dist-boot/ext-privateer-context.mjs +323 -0
  190. package/dist-boot/ext-privateer-desktop.mjs +92 -0
  191. package/dist-boot/ext-privateer-final-audit.mjs +48 -0
  192. package/dist-boot/ext-privateer-fresh.mjs +81 -0
  193. package/dist-boot/ext-privateer-gate.mjs +5959 -0
  194. package/dist-boot/ext-privateer-hints.mjs +236 -0
  195. package/dist-boot/ext-privateer-media.mjs +2626 -0
  196. package/dist-boot/ext-privateer-models.mjs +3397 -0
  197. package/dist-boot/ext-privateer-posture.mjs +3006 -0
  198. package/dist-boot/ext-privateer-privacy.mjs +4578 -0
  199. package/dist-boot/ext-privateer-recovery.mjs +287 -0
  200. package/dist-boot/ext-privateer-speak.mjs +4754 -0
  201. package/dist-boot/ext-privateer-terminals.mjs +725 -0
  202. package/dist-boot/ext-privateer-tools.mjs +528 -0
  203. package/dist-boot/ext-privateer-truncation.mjs +74 -0
  204. package/dist-boot/ext-privateer-update.mjs +210 -0
  205. package/dist-boot/ext-privateer-vision.mjs +148 -0
  206. package/dist-boot/ext-privateer-web.mjs +543 -0
  207. package/dist-boot/ext-rpiv-ask-user-question.mjs +710 -0
  208. package/dist-boot/manifest.json +9 -0
  209. package/extensions/privateer-brand.ts +3 -1
  210. package/extensions/privateer-context.ts +4 -2
  211. package/extensions/privateer-final-audit.ts +17 -0
  212. package/extensions/privateer-gate.ts +55 -9
  213. package/extensions/privateer-recovery.ts +175 -0
  214. package/extensions/privateer-web.ts +4 -4
  215. package/package.json +3 -1
  216. package/patches/@earendil-works+pi-coding-agent+1.3.0.patch +199 -2
  217. package/patches/pi-background-tasks+2.6.10.patch +91 -7
  218. package/src/cli/chat.ts +7 -1
  219. package/src/config/moatManifest.json +2 -0
  220. package/src/config/whatsNew.json +4 -4
  221. package/src/context.ts +33 -5
  222. package/src/engine/finalAudit.ts +38 -0
  223. package/src/engine/stallWatchdog.ts +8 -1
  224. package/src/providers/account.ts +34 -3
  225. package/src/remote/attentionPush.ts +40 -0
  226. package/src/remote/liveTaskSession.ts +7 -1
  227. package/src/remote/relayClient.ts +38 -4
  228. package/src/remote/remoteBridge.ts +17 -0
  229. package/src/remote/transcriptSnapshot.ts +104 -0
@@ -0,0 +1,4578 @@
1
+ import{createRequire as __pvCR}from"node:module";import{fileURLToPath as __pvF}from"node:url";import{dirname as __pvD}from"node:path";const require=__pvCR(import.meta.url);const __pvUrl=(r)=>new URL(r,import.meta.url).href;const __pvFile=(r)=>__pvF(new URL(r,import.meta.url));
2
+ import {
3
+ SecureClient
4
+ } from "./c-FJ5T42DS.mjs";
5
+ import "./c-L26VOJX4.mjs";
6
+ import "./c-C67644MB.mjs";
7
+ import "./c-65UZIZC7.mjs";
8
+ import "./c-U4ITOXAN.mjs";
9
+ import {
10
+ require_undici
11
+ } from "./c-P63T3XL2.mjs";
12
+ import {
13
+ x25519
14
+ } from "./c-4WVR32AP.mjs";
15
+ import {
16
+ __name,
17
+ __toESM
18
+ } from "./c-CN77DCGF.mjs";
19
+
20
+ // src/auth/privateer.ts?ext=privateer-privacy
21
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4, chmodSync as chmodSync4, rmSync as rmSync3 } from "node:fs";
22
+ import { hostname, userInfo } from "node:os";
23
+
24
+ // src/config/paths.ts?ext=privateer-privacy
25
+ import { homedir } from "node:os";
26
+ import { join } from "node:path";
27
+ function globalDir() {
28
+ return process.env.PRIVATEER_HOME ?? join(homedir(), ".privateer");
29
+ }
30
+ __name(globalDir, "globalDir");
31
+ function agentDir() {
32
+ return join(globalDir(), "agent");
33
+ }
34
+ __name(agentDir, "agentDir");
35
+ function credentialsPath() {
36
+ return join(globalDir(), "credentials.json");
37
+ }
38
+ __name(credentialsPath, "credentialsPath");
39
+ function configPath() {
40
+ return join(globalDir(), "config.json");
41
+ }
42
+ __name(configPath, "configPath");
43
+ function accountSessionsDir() {
44
+ return join(globalDir(), "account-sessions");
45
+ }
46
+ __name(accountSessionsDir, "accountSessionsDir");
47
+ function accountSessionsPath() {
48
+ return join(globalDir(), "account-sessions.json");
49
+ }
50
+ __name(accountSessionsPath, "accountSessionsPath");
51
+
52
+ // src/auth/accountSessions.ts?ext=privateer-privacy
53
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, renameSync, writeFileSync, chmodSync } from "node:fs";
54
+ import { join as join2 } from "node:path";
55
+ var ENTRY = /^(\d+)\.json$/;
56
+ var CLAIM = /^(\d+)\.claim-(\d+)$/;
57
+ function entryPath(pid) {
58
+ return join2(accountSessionsDir(), `${pid}.json`);
59
+ }
60
+ __name(entryPath, "entryPath");
61
+ function claimPath(pid, claimant = process.pid) {
62
+ return join2(accountSessionsDir(), `${pid}.claim-${claimant}`);
63
+ }
64
+ __name(claimPath, "claimPath");
65
+ function tryChmod(path, mode) {
66
+ try {
67
+ chmodSync(path, mode);
68
+ } catch {
69
+ }
70
+ }
71
+ __name(tryChmod, "tryChmod");
72
+ function tryRm(path) {
73
+ try {
74
+ rmSync(path, { force: true });
75
+ } catch {
76
+ }
77
+ }
78
+ __name(tryRm, "tryRm");
79
+ function readEntry(path, pid) {
80
+ try {
81
+ const raw = JSON.parse(readFileSync(path, "utf8"));
82
+ if (typeof raw?.refresh !== "string" || !raw.refresh) return null;
83
+ return { pid, refresh: raw.refresh, expires: typeof raw.expires === "number" ? raw.expires : 0 };
84
+ } catch {
85
+ return null;
86
+ }
87
+ }
88
+ __name(readEntry, "readEntry");
89
+ function isAlive(pid) {
90
+ if (!Number.isInteger(pid) || pid <= 0) return false;
91
+ try {
92
+ process.kill(pid, 0);
93
+ return true;
94
+ } catch (e) {
95
+ return e.code === "EPERM";
96
+ }
97
+ }
98
+ __name(isAlive, "isAlive");
99
+ function recordOwnedSession(cred) {
100
+ try {
101
+ mkdirSync(accountSessionsDir(), { recursive: true });
102
+ tryChmod(globalDir(), 448);
103
+ tryChmod(accountSessionsDir(), 448);
104
+ const path = entryPath(process.pid);
105
+ const tmp = `${path}.${process.pid}.tmp`;
106
+ writeFileSync(tmp, JSON.stringify({ refresh: cred.refresh, expires: cred.expires }) + "\n", {
107
+ encoding: "utf8",
108
+ mode: 384
109
+ });
110
+ tryChmod(tmp, 384);
111
+ renameSync(tmp, path);
112
+ } catch {
113
+ }
114
+ }
115
+ __name(recordOwnedSession, "recordOwnedSession");
116
+ function orphanedSessions(now = Date.now()) {
117
+ let names;
118
+ try {
119
+ names = readdirSync(accountSessionsDir());
120
+ } catch {
121
+ return [];
122
+ }
123
+ const orphans = [];
124
+ for (const name of names) {
125
+ const path = join2(accountSessionsDir(), name);
126
+ const claim = CLAIM.exec(name);
127
+ if (claim) {
128
+ if (!isAlive(Number(claim[2]))) tryRm(path);
129
+ continue;
130
+ }
131
+ const m = ENTRY.exec(name);
132
+ if (!m) continue;
133
+ const pid = Number(m[1]);
134
+ if (pid === process.pid || isAlive(pid)) continue;
135
+ const entry = readEntry(path, pid);
136
+ if (!entry || entry.expires > 0 && entry.expires <= now) {
137
+ tryRm(path);
138
+ continue;
139
+ }
140
+ orphans.push(entry);
141
+ }
142
+ return orphans;
143
+ }
144
+ __name(orphanedSessions, "orphanedSessions");
145
+ function claimOrphan(pid) {
146
+ try {
147
+ renameSync(entryPath(pid), claimPath(pid));
148
+ return true;
149
+ } catch {
150
+ return false;
151
+ }
152
+ }
153
+ __name(claimOrphan, "claimOrphan");
154
+ function releaseOrphan(pid) {
155
+ try {
156
+ renameSync(claimPath(pid), entryPath(pid));
157
+ } catch {
158
+ tryRm(claimPath(pid));
159
+ }
160
+ }
161
+ __name(releaseOrphan, "releaseOrphan");
162
+ function dropOwnedSession(pid) {
163
+ tryRm(claimPath(pid));
164
+ if (pid !== process.pid) tryRm(entryPath(pid));
165
+ }
166
+ __name(dropOwnedSession, "dropOwnedSession");
167
+ function clearOwnedSessions() {
168
+ tryRm(accountSessionsPath());
169
+ try {
170
+ if (existsSync(accountSessionsDir())) rmSync(accountSessionsDir(), { recursive: true, force: true });
171
+ } catch {
172
+ }
173
+ }
174
+ __name(clearOwnedSessions, "clearOwnedSessions");
175
+
176
+ // src/engine/errors.ts?ext=privateer-privacy
177
+ var CAP_CODE = /CAP|QUOTA|LIMIT_REACHED|INSUFFICIENT|TOP_?UP/i;
178
+ function isAccountCapCode(code) {
179
+ return typeof code === "string" && CAP_CODE.test(code);
180
+ }
181
+ __name(isAccountCapCode, "isAccountCapCode");
182
+
183
+ // src/crypto/terminalKey.ts?ext=privateer-privacy
184
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, chmodSync as chmodSync2, mkdirSync as mkdirSync2 } from "node:fs";
185
+ import { join as join3 } from "node:path";
186
+ function keyPath() {
187
+ return join3(globalDir(), "terminal-key.json");
188
+ }
189
+ __name(keyPath, "keyPath");
190
+ var cached;
191
+ function b64(bytes) {
192
+ return Buffer.from(bytes).toString("base64");
193
+ }
194
+ __name(b64, "b64");
195
+ function fromB64(s, label) {
196
+ const buf = Buffer.from(s, "base64");
197
+ if (buf.length !== 32) throw new Error(`${label} must be 32 bytes`);
198
+ return new Uint8Array(buf);
199
+ }
200
+ __name(fromB64, "fromB64");
201
+ function loadOrCreate() {
202
+ if (cached) return cached;
203
+ try {
204
+ const parsed = JSON.parse(readFileSync2(keyPath(), "utf8"));
205
+ if (parsed?.v === 1 && parsed.publicKey && parsed.secretKey) {
206
+ cached = {
207
+ publicKey: fromB64(parsed.publicKey, "terminal public key"),
208
+ secretKey: fromB64(parsed.secretKey, "terminal secret key")
209
+ };
210
+ return cached;
211
+ }
212
+ } catch {
213
+ }
214
+ const secretKey = x25519.utils.randomPrivateKey();
215
+ const publicKey = x25519.getPublicKey(secretKey);
216
+ const file = { v: 1, publicKey: b64(publicKey), secretKey: b64(secretKey) };
217
+ mkdirSync2(globalDir(), { recursive: true, mode: 448 });
218
+ writeFileSync2(keyPath(), JSON.stringify(file), { mode: 384 });
219
+ try {
220
+ chmodSync2(keyPath(), 384);
221
+ } catch {
222
+ }
223
+ cached = { publicKey, secretKey };
224
+ return cached;
225
+ }
226
+ __name(loadOrCreate, "loadOrCreate");
227
+ function terminalPublicKeyBase64() {
228
+ return b64(loadOrCreate().publicKey);
229
+ }
230
+ __name(terminalPublicKeyBase64, "terminalPublicKeyBase64");
231
+
232
+ // src/crypto/accountTrust.ts?ext=privateer-privacy
233
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, chmodSync as chmodSync3, rmSync as rmSync2 } from "node:fs";
234
+ import { join as join4 } from "node:path";
235
+ function trustPath() {
236
+ return join4(globalDir(), "account-trust.json");
237
+ }
238
+ __name(trustPath, "trustPath");
239
+ function pinAccountSignKey(pub) {
240
+ const value = (pub ?? "").trim();
241
+ if (!value) return;
242
+ try {
243
+ const file = { v: 1, accountSignPub: value };
244
+ writeFileSync3(trustPath(), JSON.stringify(file), { mode: 384 });
245
+ try {
246
+ chmodSync3(trustPath(), 384);
247
+ } catch {
248
+ }
249
+ } catch {
250
+ }
251
+ }
252
+ __name(pinAccountSignKey, "pinAccountSignKey");
253
+ function clearAccountSignKey() {
254
+ try {
255
+ rmSync2(trustPath(), { force: true });
256
+ } catch {
257
+ }
258
+ }
259
+ __name(clearAccountSignKey, "clearAccountSignKey");
260
+
261
+ // src/auth/privateer.ts?ext=privateer-privacy
262
+ var DEFAULT_SERVER_URL = "https://helix-server-m1ac.onrender.com";
263
+ function isSafeServerUrl(raw) {
264
+ let u;
265
+ try {
266
+ u = new URL(raw);
267
+ } catch {
268
+ return false;
269
+ }
270
+ if (u.protocol === "https:") return true;
271
+ if (u.protocol === "http:" && (u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "::1")) {
272
+ return true;
273
+ }
274
+ return false;
275
+ }
276
+ __name(isSafeServerUrl, "isSafeServerUrl");
277
+ function serverBaseUrl() {
278
+ const env = process.env.PRIVATEER_SERVER_URL?.replace(/\/$/, "");
279
+ if (env) {
280
+ if (!isSafeServerUrl(env)) {
281
+ throw new Error(
282
+ `Refusing PRIVATEER_SERVER_URL=${env}: must be https:// (http allowed only for localhost). This protects your account token from being sent over an insecure or attacker-controlled connection.`
283
+ );
284
+ }
285
+ return env;
286
+ }
287
+ const base = (loadCredentials()?.serverBaseUrl || DEFAULT_SERVER_URL).replace(/\/$/, "");
288
+ if (!isSafeServerUrl(base)) {
289
+ throw new Error(`Stored Privateer server URL is not https (${base}). Run /logout then /login again.`);
290
+ }
291
+ return base;
292
+ }
293
+ __name(serverBaseUrl, "serverBaseUrl");
294
+ function defaultDeviceLabel() {
295
+ try {
296
+ return `${userInfo().username}@${hostname()}`;
297
+ } catch {
298
+ return "terminal";
299
+ }
300
+ }
301
+ __name(defaultDeviceLabel, "defaultDeviceLabel");
302
+ var SHARED = /* @__PURE__ */ Symbol.for("privateer.auth.shared");
303
+ function shared() {
304
+ const g = globalThis;
305
+ return g[SHARED] ??= { cache: null, signedIn: /* @__PURE__ */ new Set(), expired: /* @__PURE__ */ new Set() };
306
+ }
307
+ __name(shared, "shared");
308
+ var _child = null;
309
+ var _spawnInFlight = null;
310
+ var _refreshInFlight = null;
311
+ var _account = null;
312
+ function loadCredentials() {
313
+ const state = shared();
314
+ if (state.cache) return state.cache;
315
+ const path = credentialsPath();
316
+ if (!existsSync2(path)) return null;
317
+ try {
318
+ state.cache = JSON.parse(readFileSync4(path, "utf8"));
319
+ } catch {
320
+ return null;
321
+ }
322
+ return state.cache;
323
+ }
324
+ __name(loadCredentials, "loadCredentials");
325
+ function saveCredentials(creds) {
326
+ const dir = globalDir();
327
+ mkdirSync3(dir, { recursive: true });
328
+ tryChmod2(dir, 448);
329
+ const path = credentialsPath();
330
+ writeFileSync4(path, JSON.stringify(creds, null, 2) + "\n", { encoding: "utf8", mode: 384 });
331
+ tryChmod2(path, 384);
332
+ shared().cache = creds;
333
+ }
334
+ __name(saveCredentials, "saveCredentials");
335
+ var ARMED_SLOT = /* @__PURE__ */ Symbol.for("privateer.accountCredential");
336
+ function forgetArmedAccountCredential() {
337
+ const slot = globalThis[ARMED_SLOT];
338
+ if (slot) slot.cred = void 0;
339
+ }
340
+ __name(forgetArmedAccountCredential, "forgetArmedAccountCredential");
341
+ function clearCredentials() {
342
+ try {
343
+ rmSync3(credentialsPath(), { force: true });
344
+ } catch {
345
+ }
346
+ clearAccountSignKey();
347
+ shared().cache = null;
348
+ _child = null;
349
+ _account = null;
350
+ forgetArmedAccountCredential();
351
+ }
352
+ __name(clearCredentials, "clearCredentials");
353
+ function hasCredentials() {
354
+ return loadCredentials() !== null;
355
+ }
356
+ __name(hasCredentials, "hasCredentials");
357
+ function currentUser() {
358
+ return loadCredentials()?.user ?? null;
359
+ }
360
+ __name(currentUser, "currentUser");
361
+ function tryChmod2(path, mode) {
362
+ try {
363
+ chmodSync4(path, mode);
364
+ } catch {
365
+ }
366
+ }
367
+ __name(tryChmod2, "tryChmod");
368
+ function notifySessionExpired() {
369
+ for (const listener of shared().expired) {
370
+ try {
371
+ listener();
372
+ } catch {
373
+ }
374
+ }
375
+ }
376
+ __name(notifySessionExpired, "notifySessionExpired");
377
+ function notifySignedIn() {
378
+ for (const listener of shared().signedIn) {
379
+ try {
380
+ listener();
381
+ } catch {
382
+ }
383
+ }
384
+ }
385
+ __name(notifySignedIn, "notifySignedIn");
386
+ async function postJson(base, path, body, init = {}) {
387
+ return fetch(`${base}${path}`, {
388
+ ...init,
389
+ method: "POST",
390
+ headers: { "Content-Type": "application/json", ...init.headers || {} },
391
+ body: JSON.stringify(body)
392
+ });
393
+ }
394
+ __name(postJson, "postJson");
395
+ async function requestDeviceCode(deviceLabel = defaultDeviceLabel()) {
396
+ const base = serverBaseUrl();
397
+ let terminalPub;
398
+ try {
399
+ terminalPub = terminalPublicKeyBase64();
400
+ } catch {
401
+ }
402
+ const res = await postJson(base, "/auth/device/code", { deviceLabel, terminalPub });
403
+ if (!res.ok) {
404
+ throw new Error(`Couldn't start login (${res.status}). Check your connection or PRIVATEER_SERVER_URL.`);
405
+ }
406
+ return await res.json();
407
+ }
408
+ __name(requestDeviceCode, "requestDeviceCode");
409
+ async function pollForToken(code, opts = {}) {
410
+ const base = serverBaseUrl();
411
+ let interval = Math.max(1, code.interval || 5) * 1e3;
412
+ const deadline = Date.now() + (code.expires_in || 600) * 1e3;
413
+ while (Date.now() < deadline) {
414
+ if (opts.signal?.aborted) throw new Error("Login cancelled.");
415
+ await sleep(interval, opts.signal);
416
+ const res = await postJson(base, "/auth/device/token", { device_code: code.device_code });
417
+ if (res.ok) {
418
+ const data = await res.json();
419
+ const creds = { accessToken: data.accessToken, refreshToken: data.refreshToken, user: data.user, serverBaseUrl: base };
420
+ saveCredentials(creds);
421
+ pinAccountSignKey(data.accountSignPub);
422
+ notifySignedIn();
423
+ return creds;
424
+ }
425
+ let err = "";
426
+ try {
427
+ err = (await res.json()).error || "";
428
+ } catch {
429
+ }
430
+ if (err === "authorization_pending") {
431
+ opts.onPoll?.("pending");
432
+ } else if (err === "slow_down") {
433
+ interval += 2e3;
434
+ opts.onPoll?.("slow_down");
435
+ } else if (err === "access_denied") {
436
+ throw new Error("Login was denied in the app.");
437
+ } else if (err === "expired_token") {
438
+ throw new Error("This login code expired. Run /login again.");
439
+ } else {
440
+ throw new Error(`Login failed (${res.status}${err ? `: ${err}` : ""}).`);
441
+ }
442
+ }
443
+ throw new Error("This login code expired. Run /login again.");
444
+ }
445
+ __name(pollForToken, "pollForToken");
446
+ async function runDeviceLogin(opts) {
447
+ const code = await requestDeviceCode(opts.deviceLabel);
448
+ opts.onCode(code);
449
+ const creds = await pollForToken(code, { onPoll: opts.onPoll, signal: opts.signal });
450
+ return creds.user;
451
+ }
452
+ __name(runDeviceLogin, "runDeviceLogin");
453
+ async function spawnFailure(res) {
454
+ if (res.status === 401) {
455
+ if (hasCredentials()) {
456
+ clearCredentials();
457
+ notifySessionExpired();
458
+ }
459
+ return new Error("Your Privateer session expired. Run /login to sign in again.");
460
+ }
461
+ let message;
462
+ try {
463
+ message = (await res.json()).message;
464
+ } catch {
465
+ }
466
+ return new Error(message?.trim() || `Couldn't start a Privateer session (HTTP ${res.status}).`);
467
+ }
468
+ __name(spawnFailure, "spawnFailure");
469
+ async function spawnChildSession() {
470
+ const parent = loadCredentials();
471
+ if (!parent) throw new Error("Not logged in to Privateer. Run /login.");
472
+ const res = await postJson(serverBaseUrl(), "/auth/session/spawn", {
473
+ refreshToken: parent.refreshToken,
474
+ deviceLabel: defaultDeviceLabel()
475
+ }, {
476
+ headers: { Authorization: `Bearer ${parent.accessToken}` }
477
+ });
478
+ if (!res.ok) throw await spawnFailure(res);
479
+ const { accessToken, refreshToken } = await res.json();
480
+ _child = { accessToken, refreshToken };
481
+ return _child;
482
+ }
483
+ __name(spawnChildSession, "spawnChildSession");
484
+ function ensureChildSession() {
485
+ if (_child) return Promise.resolve(_child);
486
+ if (!_spawnInFlight) {
487
+ _spawnInFlight = spawnChildSession().finally(() => {
488
+ _spawnInFlight = null;
489
+ });
490
+ }
491
+ return _spawnInFlight;
492
+ }
493
+ __name(ensureChildSession, "ensureChildSession");
494
+ function refreshChildSession() {
495
+ if (_refreshInFlight) return _refreshInFlight;
496
+ _refreshInFlight = (async () => {
497
+ if (_child) {
498
+ const res = await postJson(serverBaseUrl(), "/auth/refresh", { refreshToken: _child.refreshToken });
499
+ if (res.ok) {
500
+ const { accessToken, refreshToken } = await res.json();
501
+ _child = { accessToken, refreshToken };
502
+ return _child;
503
+ }
504
+ }
505
+ _child = null;
506
+ return spawnChildSession();
507
+ })().finally(() => {
508
+ _refreshInFlight = null;
509
+ });
510
+ return _refreshInFlight;
511
+ }
512
+ __name(refreshChildSession, "refreshChildSession");
513
+ async function authedFetch(input, init = {}) {
514
+ const bodyBuf = init.body;
515
+ const withAuth = /* @__PURE__ */ __name((token) => {
516
+ const headers = new Headers(init.headers);
517
+ headers.set("Authorization", `Bearer ${token}`);
518
+ return { ...init, headers };
519
+ }, "withAuth");
520
+ let child = await ensureChildSession();
521
+ let res = await fetch(input, withAuth(child.accessToken));
522
+ if (res.status === 401) {
523
+ child = await refreshChildSession();
524
+ res = await fetch(input, { ...withAuth(child.accessToken), body: bodyBuf });
525
+ }
526
+ return await defuseRetryableCap(res);
527
+ }
528
+ __name(authedFetch, "authedFetch");
529
+ async function defuseRetryableCap(res) {
530
+ if (res.status !== 429) return res;
531
+ const body = await res.text();
532
+ let code;
533
+ try {
534
+ const parsed = JSON.parse(body);
535
+ code = parsed.code ?? parsed.error?.code;
536
+ } catch {
537
+ }
538
+ const status = isAccountCapCode(typeof code === "string" ? code : void 0) ? 402 : 429;
539
+ return new Response(body, { status, statusText: res.statusText, headers: res.headers });
540
+ }
541
+ __name(defuseRetryableCap, "defuseRetryableCap");
542
+ async function deleteSession(accessToken, timeoutMs) {
543
+ try {
544
+ await fetch(`${serverBaseUrl()}/auth/session/current`, {
545
+ method: "DELETE",
546
+ headers: { Authorization: `Bearer ${accessToken}` },
547
+ signal: AbortSignal.timeout(timeoutMs)
548
+ });
549
+ } catch {
550
+ }
551
+ }
552
+ __name(deleteSession, "deleteSession");
553
+ async function logout() {
554
+ const parent = loadCredentials();
555
+ if (parent) {
556
+ try {
557
+ const fresh = await rotateSession(parent.refreshToken);
558
+ await deleteSession(fresh.access, 5e3);
559
+ } catch {
560
+ }
561
+ }
562
+ _child = null;
563
+ _account = null;
564
+ clearOwnedSessions();
565
+ clearCredentials();
566
+ }
567
+ __name(logout, "logout");
568
+ function jwtExpMs(token, fallbackMs = 5 * 6e4) {
569
+ try {
570
+ const seg = token.split(".")[1];
571
+ const payload = JSON.parse(Buffer.from(seg, "base64").toString("utf8"));
572
+ if (typeof payload.exp === "number") return payload.exp * 1e3 - 3e4;
573
+ } catch {
574
+ }
575
+ return Date.now() + fallbackMs;
576
+ }
577
+ __name(jwtExpMs, "jwtExpMs");
578
+ async function spawnAccountCredentials() {
579
+ const parent = loadCredentials();
580
+ if (!parent) throw new Error("Not logged in to Privateer. Run /login.");
581
+ const res = await postJson(
582
+ serverBaseUrl(),
583
+ "/auth/session/spawn",
584
+ { refreshToken: parent.refreshToken, deviceLabel: defaultDeviceLabel() },
585
+ { headers: { Authorization: `Bearer ${parent.accessToken}` } }
586
+ );
587
+ if (!res.ok) throw await spawnFailure(res);
588
+ const { accessToken, refreshToken } = await res.json();
589
+ _account = { accessToken };
590
+ const cred = { access: accessToken, refresh: refreshToken, expires: jwtExpMs(accessToken) };
591
+ recordOwnedSession(cred);
592
+ return cred;
593
+ }
594
+ __name(spawnAccountCredentials, "spawnAccountCredentials");
595
+ function isRefreshRejection(e) {
596
+ return e instanceof Error && typeof e.status === "number";
597
+ }
598
+ __name(isRefreshRejection, "isRefreshRejection");
599
+ async function rotateSession(refresh) {
600
+ const res = await postJson(serverBaseUrl(), "/auth/refresh", { refreshToken: refresh });
601
+ if (!res.ok) {
602
+ const err = new Error(`account refresh failed (${res.status})`);
603
+ err.status = res.status;
604
+ throw err;
605
+ }
606
+ const { accessToken, refreshToken } = await res.json();
607
+ return { access: accessToken, refresh: refreshToken, expires: jwtExpMs(accessToken) };
608
+ }
609
+ __name(rotateSession, "rotateSession");
610
+ async function refreshAccountCredentials(refresh) {
611
+ const cred = await rotateSession(refresh);
612
+ _account = { accessToken: cred.access };
613
+ recordOwnedSession(cred);
614
+ return cred;
615
+ }
616
+ __name(refreshAccountCredentials, "refreshAccountCredentials");
617
+ async function acquireAccountCredential() {
618
+ const orphans = orphanedSessions();
619
+ let adopted = null;
620
+ let attempted = 0;
621
+ while (attempted < orphans.length && !adopted) {
622
+ const orphan = orphans[attempted++];
623
+ if (!claimOrphan(orphan.pid)) continue;
624
+ try {
625
+ adopted = await refreshAccountCredentials(orphan.refresh);
626
+ dropOwnedSession(orphan.pid);
627
+ } catch (e) {
628
+ if (isRefreshRejection(e)) dropOwnedSession(orphan.pid);
629
+ else releaseOrphan(orphan.pid);
630
+ }
631
+ }
632
+ const leftovers = orphans.slice(attempted);
633
+ if (leftovers.length) void revokeOrphanedSessions(leftovers);
634
+ return adopted ?? await spawnAccountCredentials();
635
+ }
636
+ __name(acquireAccountCredential, "acquireAccountCredential");
637
+ async function revokeOrphanedSessions(orphans, timeoutMs = 1500) {
638
+ for (const orphan of orphans) {
639
+ if (!claimOrphan(orphan.pid)) continue;
640
+ try {
641
+ const cred = await rotateSession(orphan.refresh);
642
+ await deleteSession(cred.access, timeoutMs);
643
+ dropOwnedSession(orphan.pid);
644
+ } catch (e) {
645
+ if (isRefreshRejection(e)) dropOwnedSession(orphan.pid);
646
+ else releaseOrphan(orphan.pid);
647
+ }
648
+ }
649
+ }
650
+ __name(revokeOrphanedSessions, "revokeOrphanedSessions");
651
+ function sleep(ms, signal) {
652
+ return new Promise((resolve, reject) => {
653
+ const t = setTimeout(resolve, ms);
654
+ signal?.addEventListener(
655
+ "abort",
656
+ () => {
657
+ clearTimeout(t);
658
+ reject(new Error("Login cancelled."));
659
+ },
660
+ { once: true }
661
+ );
662
+ });
663
+ }
664
+ __name(sleep, "sleep");
665
+
666
+ // src/providers/account.ts?ext=privateer-privacy
667
+ import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "node:fs";
668
+ import { join as join7 } from "node:path";
669
+
670
+ // src/util/openBrowser.ts?ext=privateer-privacy
671
+ import { spawn } from "node:child_process";
672
+ function canOpenBrowser(env = process.env, platform = process.platform) {
673
+ if (env.PRIVATEER_NO_BROWSER?.trim()) return false;
674
+ if (env.SSH_TTY || env.SSH_CONNECTION) return false;
675
+ if (platform !== "darwin" && platform !== "win32" && !env.DISPLAY && !env.WAYLAND_DISPLAY) return false;
676
+ return true;
677
+ }
678
+ __name(canOpenBrowser, "canOpenBrowser");
679
+ function browsableUrl(raw) {
680
+ const s = (raw ?? "").trim();
681
+ if (!s) return void 0;
682
+ try {
683
+ const u = new URL(s);
684
+ if (u.protocol !== "https:" && u.protocol !== "http:") return void 0;
685
+ return u.href;
686
+ } catch {
687
+ return void 0;
688
+ }
689
+ }
690
+ __name(browsableUrl, "browsableUrl");
691
+ function openInBrowser(rawUrl) {
692
+ const url = browsableUrl(rawUrl);
693
+ if (!url) return Promise.resolve(false);
694
+ let cmd;
695
+ let args;
696
+ if (process.platform === "darwin") {
697
+ cmd = "open";
698
+ args = [url];
699
+ } else if (process.platform === "win32") {
700
+ cmd = "cmd";
701
+ args = ["/c", "start", "", url.replace(/&/g, "^&")];
702
+ } else {
703
+ cmd = "xdg-open";
704
+ args = [url];
705
+ }
706
+ return new Promise((resolve) => {
707
+ let settled = false;
708
+ const done = /* @__PURE__ */ __name((ok) => {
709
+ if (!settled) {
710
+ settled = true;
711
+ resolve(ok);
712
+ }
713
+ }, "done");
714
+ try {
715
+ const child = spawn(cmd, args, { stdio: "ignore", detached: true });
716
+ child.once("error", () => done(false));
717
+ child.once("exit", (code) => done(code === 0));
718
+ child.unref();
719
+ setTimeout(() => done(true), 2e3).unref?.();
720
+ } catch {
721
+ done(false);
722
+ }
723
+ });
724
+ }
725
+ __name(openInBrowser, "openInBrowser");
726
+
727
+ // node_modules/pi-privacy/src/posture/tiers.ts?ext=privateer-privacy
728
+ var TIERS = {
729
+ "tee-verified": {
730
+ tier: "tee-verified",
731
+ label: "Verified TEE",
732
+ verifiability: "cryptographic",
733
+ posture: "green",
734
+ blurb: "Confidential-enclave inference, cryptographically verified: remote attestation proved genuine TEE hardware and the live TLS key matched the attestation report."
735
+ },
736
+ "tee-unverified": {
737
+ tier: "tee-unverified",
738
+ label: "TEE (unconfirmed)",
739
+ verifiability: "none",
740
+ posture: "yellow",
741
+ blurb: "Provider claims a TEE, but attestation was incomplete or the live key could not be matched here \u2014 treat as unverified until it goes green."
742
+ },
743
+ local: {
744
+ tier: "local",
745
+ label: "On-device",
746
+ verifiability: "observable",
747
+ posture: "green",
748
+ blurb: "Runs against a loopback endpoint \u2014 inference is local; no prompt leaves the machine."
749
+ },
750
+ "zdr-enforced": {
751
+ tier: "zdr-enforced",
752
+ label: "ZDR (enforced)",
753
+ verifiability: "observable",
754
+ posture: "green",
755
+ blurb: "Routing is constrained to providers that contractually don't retain data \u2014 verified observable: OpenRouter refuses to serve when the policy can't be met. Enforcement of a policy, not hardware attestation."
756
+ },
757
+ "zdr-policy": {
758
+ tier: "zdr-policy",
759
+ label: "ZDR (by policy)",
760
+ verifiability: "policy",
761
+ posture: "yellow",
762
+ blurb: "The provider states it doesn't retain data, but this is a policy promise we can't verify \u2014 not hardware, not attested."
763
+ },
764
+ standard: {
765
+ tier: "standard",
766
+ label: "Standard",
767
+ verifiability: "none",
768
+ posture: "neutral",
769
+ blurb: "No special privacy guarantee."
770
+ }
771
+ };
772
+ var ORDER = [
773
+ "tee-verified",
774
+ "local",
775
+ "zdr-enforced",
776
+ "tee-unverified",
777
+ "zdr-policy",
778
+ "standard"
779
+ ];
780
+ function tierRank(t) {
781
+ const i = ORDER.indexOf(t);
782
+ return i === -1 ? ORDER.length : i;
783
+ }
784
+ __name(tierRank, "tierRank");
785
+ function tierFromTeePosture(p) {
786
+ return p === "green" ? "tee-verified" : p === "yellow" ? "tee-unverified" : "standard";
787
+ }
788
+ __name(tierFromTeePosture, "tierFromTeePosture");
789
+
790
+ // node_modules/pi-privacy/src/providers/catalog.ts?ext=privateer-privacy
791
+ var PRIVACY_PROVIDERS = [
792
+ {
793
+ id: "privateer",
794
+ label: "Privateer",
795
+ // Posture-aware, exactly like OpenRouter — tee-verified is the CEILING, not a
796
+ // promise. Privateer's in-app ACCOUNT channel offers verified-TEE inference, but
797
+ // pi-privacy only exercises the PUBLIC developer-key channel (sk-priv-…), which is
798
+ // server-proxied: the proxy mediates attestation, so a pi client can't verify the
799
+ // enclave itself. The public key therefore floors to zdr-policy at runtime (see
800
+ // effectiveTier) — verified TEE is never claimed from it alone. NOT `attestable`
801
+ // here: pi-privacy does not attest Privateer itself. The account channel (its own
802
+ // OAuth session + account server + sealed relay) lives in the host (privateer-agent),
803
+ // which reuses this package's interpretReport/teePosture and injects the verified
804
+ // verdict through the extension's resolveTier hook; the picker's `verifiedTee` signal
805
+ // reflects that the host channel is active (see PiPrivacyOptions.privateerVerifiedTee).
806
+ tier: "tee-verified",
807
+ // ceiling; resolves to zdr-policy on the public developer key
808
+ postureAware: true,
809
+ baseUrl: "https://api.privateer.pro/v1",
810
+ api: "openai-completions",
811
+ keyEnv: "${PRIVATEER_API_KEY}",
812
+ note: "privateer.pro \u2192 API keys (sk-priv-\u2026). Public developer key is server-proxied, zero-retention by policy (NOT TEE-attested here); verified TEE is the in-app account channel."
813
+ },
814
+ {
815
+ id: "tinfoil",
816
+ label: "Tinfoil (private TEE inference)",
817
+ tier: "tee-verified",
818
+ attestable: true,
819
+ baseUrl: "https://inference.tinfoil.sh/v1",
820
+ api: "openai-completions",
821
+ keyEnv: "${TINFOIL_API_KEY}",
822
+ note: "tinfoil.sh \u2192 dashboard \u2192 API Keys. Confidential-enclave inference; verified by attestation + live-TLS-key match."
823
+ },
824
+ {
825
+ id: "nearai",
826
+ label: "NEAR AI (private TEE inference)",
827
+ tier: "tee-verified",
828
+ attestable: true,
829
+ baseUrl: "https://cloud-api.near.ai/v1",
830
+ api: "openai-completions",
831
+ keyEnv: "${NEARAI_API_KEY}",
832
+ note: "cloud.near.ai \u2192 API Keys. Confidential-compute TEE; attestation carried in the report body (verified over HTTPS)."
833
+ },
834
+ {
835
+ id: "venice",
836
+ label: "Venice (no-retention inference)",
837
+ tier: "zdr-policy",
838
+ baseUrl: "https://api.venice.ai/api/v1",
839
+ api: "openai-completions",
840
+ keyEnv: "${VENICE_API_KEY}",
841
+ // Honest copy: policy, not hardware. Venice injects a body param to disable its
842
+ // system prompt — handled by a before_provider_request hook, not config.
843
+ note: 'venice.ai \u2192 API Keys (no retention by policy, not TEE-attested; "anonymized" models proxy upstream).',
844
+ compat: { veniceDisableSystemPrompt: true }
845
+ },
846
+ {
847
+ id: "fireworks",
848
+ label: "Fireworks (no-retention inference)",
849
+ tier: "zdr-policy",
850
+ baseUrl: "https://api.fireworks.ai/inference/v1",
851
+ api: "openai-completions",
852
+ keyEnv: "${FIREWORKS_API_KEY}",
853
+ // Honest copy: ZDR is the default for OPEN models only; Fireworks's own f1 /
854
+ // FireFunction may log. Not TEE-attested.
855
+ note: "fireworks.ai \u2192 API Keys (open models: zero retention by default, not TEE-attested; Fireworks's own f1/FireFunction may log)."
856
+ },
857
+ {
858
+ id: "openrouter",
859
+ label: "OpenRouter",
860
+ tier: "zdr-enforced",
861
+ // ceiling; posture-aware — see postureAware
862
+ postureAware: true,
863
+ baseUrl: "https://openrouter.ai/api/v1",
864
+ api: "openai-completions",
865
+ keyEnv: "${OPENROUTER_API_KEY}",
866
+ // Honest copy: ZDR is per-model/account. Yellow (zdr-policy) until enforcement
867
+ // pins requests to zero-retention endpoints, then green (zdr-enforced).
868
+ note: "openrouter.ai/keys. ZDR is per-model; not guaranteed until ZDR routing is enforced (then requests only hit zero-retention endpoints).",
869
+ compat: { openRouterRouting: { zdr: true } }
870
+ },
871
+ {
872
+ id: "ollama",
873
+ label: "Ollama (local)",
874
+ tier: "local",
875
+ local: true,
876
+ baseUrl: "http://localhost:11434/v1",
877
+ // OpenAI-compat surface, not native /api
878
+ api: "openai-completions",
879
+ note: "runs locally \u2014 no key needed. Inference on-device; nothing leaves the machine."
880
+ },
881
+ {
882
+ id: "custom",
883
+ label: "Custom (OpenAI-compatible)",
884
+ tier: "standard",
885
+ // resolved to `local` at runtime when the baseUrl is loopback
886
+ api: "openai-completions",
887
+ note: "any OpenAI-compatible endpoint \u2014 LM Studio, vLLM, llama.cpp, a proxy. Detected as on-device when the URL is loopback."
888
+ }
889
+ ];
890
+ var PROVIDER_BY_ID = Object.fromEntries(
891
+ PRIVACY_PROVIDERS.map((p) => [p.id, p])
892
+ );
893
+ function isLocalEndpoint(baseUrl) {
894
+ if (!baseUrl) return false;
895
+ let host;
896
+ try {
897
+ host = new URL(baseUrl).hostname.toLowerCase();
898
+ } catch {
899
+ return false;
900
+ }
901
+ if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
902
+ const zone = host.indexOf("%");
903
+ if (zone !== -1) host = host.slice(0, zone);
904
+ if (host === "localhost" || host.endsWith(".localhost")) return true;
905
+ if (host === "::1" || host === "::") return true;
906
+ let v4 = host;
907
+ if (host.startsWith("::ffff:")) {
908
+ const rest = host.slice(7);
909
+ if (rest.includes(".")) v4 = rest;
910
+ else {
911
+ const groups = rest.split(":");
912
+ if (groups.length !== 2) return false;
913
+ const hi = Number.parseInt(groups[0], 16);
914
+ const lo = Number.parseInt(groups[1], 16);
915
+ if (!Number.isInteger(hi) || !Number.isInteger(lo) || hi > 65535 || lo > 65535) return false;
916
+ v4 = `${hi >>> 8}.${hi & 255}.${lo >>> 8}.${lo & 255}`;
917
+ }
918
+ }
919
+ const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(v4);
920
+ if (!m) return false;
921
+ const octets = m.slice(1).map(Number);
922
+ if (octets.some((n) => n > 255)) return false;
923
+ return octets[0] === 127 || v4 === "0.0.0.0";
924
+ }
925
+ __name(isLocalEndpoint, "isLocalEndpoint");
926
+
927
+ // node_modules/pi-privacy/src/attest/attestation.ts?ext=privateer-privacy
928
+ import { randomBytes, createHash, X509Certificate } from "node:crypto";
929
+ import { request as httpsRequest } from "node:https";
930
+ import { gunzipSync } from "node:zlib";
931
+ var TIMEOUT_MS = 12e3;
932
+ var NEARAI_BASE_URL = "https://cloud-api.near.ai/v1";
933
+ var TINFOIL_BASE_URL = "https://inference.tinfoil.sh/v1";
934
+ function baseFor(cfg) {
935
+ return (cfg.baseURL ?? NEARAI_BASE_URL).replace(/\/+$/, "");
936
+ }
937
+ __name(baseFor, "baseFor");
938
+ function randomNonce() {
939
+ return randomBytes(32).toString("hex");
940
+ }
941
+ __name(randomNonce, "randomNonce");
942
+ function deepFindString(obj, keys) {
943
+ const want = new Set(keys.map((k) => k.toLowerCase()));
944
+ const stack = [obj];
945
+ while (stack.length) {
946
+ const cur = stack.pop();
947
+ if (Array.isArray(cur)) {
948
+ stack.push(...cur);
949
+ } else if (cur && typeof cur === "object") {
950
+ for (const [k, v] of Object.entries(cur)) {
951
+ if (typeof v === "string" && want.has(k.toLowerCase()) && v.trim()) return v.trim();
952
+ if (v && typeof v === "object") stack.push(v);
953
+ }
954
+ }
955
+ }
956
+ return void 0;
957
+ }
958
+ __name(deepFindString, "deepFindString");
959
+ async function fetchAttestation(cfg, modelId) {
960
+ if (!cfg.apiKey) throw new Error("no API key");
961
+ const nonce = randomNonce();
962
+ const url = `${baseFor(cfg)}/attestation/report?model=${encodeURIComponent(modelId)}&signing_algo=ecdsa&nonce=${nonce}`;
963
+ const ac = new AbortController();
964
+ const timer = setTimeout(() => ac.abort(), TIMEOUT_MS);
965
+ let raw;
966
+ try {
967
+ const res = await fetch(url, {
968
+ headers: { authorization: `Bearer ${cfg.apiKey}` },
969
+ signal: ac.signal
970
+ });
971
+ if (!res.ok) {
972
+ const body = await res.text().catch(() => "");
973
+ const hint = body.slice(0, 200).trim();
974
+ throw new Error(`HTTP ${res.status} ${res.statusText}${hint ? ` \u2014 ${hint}` : ""}`);
975
+ }
976
+ raw = await res.json();
977
+ } catch (err) {
978
+ if (err instanceof Error && err.name === "AbortError") {
979
+ throw new Error(`timed out after ${TIMEOUT_MS / 1e3}s`);
980
+ }
981
+ throw err;
982
+ } finally {
983
+ clearTimeout(timer);
984
+ }
985
+ return interpretReport(modelId, nonce, raw);
986
+ }
987
+ __name(fetchAttestation, "fetchAttestation");
988
+ function interpretReport(modelId, nonce, raw) {
989
+ const signingAddress = deepFindString(raw, ["signing_address", "signingAddress", "address"]);
990
+ const blob = JSON.stringify(raw).toLowerCase();
991
+ const hardware = [];
992
+ if (/nvidia|gpu/.test(blob)) hardware.push("NVIDIA");
993
+ if (/intel|tdx/.test(blob)) hardware.push("Intel TDX");
994
+ const nonceEchoed = nonce.length >= 16 && blob.includes(nonce.toLowerCase());
995
+ return { model: modelId, nonce, signingAddress, nonceEchoed, hardware, raw };
996
+ }
997
+ __name(interpretReport, "interpretReport");
998
+ function teePosture(att) {
999
+ if (!att.signingAddress && att.hardware.length === 0) return "red";
1000
+ if (att.signingAddress && att.hardware.length > 0 && att.nonceEchoed) return "green";
1001
+ return "yellow";
1002
+ }
1003
+ __name(teePosture, "teePosture");
1004
+ var httpsTransport = /* @__PURE__ */ __name((host) => new Promise((resolve, reject) => {
1005
+ const url = new URL(`https://${host}/.well-known/tinfoil-attestation`);
1006
+ const req = httpsRequest(
1007
+ {
1008
+ hostname: url.hostname,
1009
+ port: url.port ? Number(url.port) : 443,
1010
+ path: url.pathname,
1011
+ method: "GET",
1012
+ timeout: TIMEOUT_MS,
1013
+ // Force a fresh connection + full TLS handshake every time. A pooled/
1014
+ // resumed TLS session skips re-sending the server cert, so
1015
+ // getPeerCertificate() returns {} on the abbreviated handshake and the
1016
+ // key binding silently fails (posture flips green→yellow on re-verify).
1017
+ // Attestation must observe a real handshake anyway, so no session reuse.
1018
+ agent: false
1019
+ },
1020
+ (res) => {
1021
+ let liveTlsKeyFp;
1022
+ try {
1023
+ const peer = res.socket.getPeerCertificate(true);
1024
+ if (peer?.raw) {
1025
+ const spki = new X509Certificate(peer.raw).publicKey.export({ type: "spki", format: "der" });
1026
+ liveTlsKeyFp = createHash("sha256").update(spki).digest("hex");
1027
+ }
1028
+ } catch {
1029
+ }
1030
+ let body = "";
1031
+ res.on("data", (chunk) => body += chunk);
1032
+ res.on("end", () => {
1033
+ if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
1034
+ const hint = body.slice(0, 200).trim();
1035
+ reject(new Error(`HTTP ${res.statusCode} ${res.statusMessage ?? ""}${hint ? ` \u2014 ${hint}` : ""}`));
1036
+ return;
1037
+ }
1038
+ try {
1039
+ resolve({ doc: JSON.parse(body), liveTlsKeyFp });
1040
+ } catch {
1041
+ reject(new Error("attestation endpoint returned non-JSON"));
1042
+ }
1043
+ });
1044
+ res.on("error", reject);
1045
+ }
1046
+ );
1047
+ req.on("timeout", () => req.destroy(new Error(`timed out after ${TIMEOUT_MS / 1e3}s`)));
1048
+ req.on("error", reject);
1049
+ req.end();
1050
+ }), "httpsTransport");
1051
+ async function fetchTinfoilAttestation(cfg, transport = httpsTransport) {
1052
+ const host = new URL(cfg.baseURL ?? TINFOIL_BASE_URL).host;
1053
+ const { doc, liveTlsKeyFp } = await transport(host);
1054
+ return interpretTinfoilDoc(host, doc, liveTlsKeyFp);
1055
+ }
1056
+ __name(fetchTinfoilAttestation, "fetchTinfoilAttestation");
1057
+ function interpretTinfoilDoc(host, doc, liveTlsKeyFp) {
1058
+ const d = doc ?? {};
1059
+ const format = typeof d.format === "string" ? d.format : "";
1060
+ const fmt = format.toLowerCase();
1061
+ const hardware = [];
1062
+ if (/sev|snp/.test(fmt)) hardware.push("AMD SEV-SNP");
1063
+ if (/tdx/.test(fmt)) hardware.push("Intel TDX");
1064
+ if (/nitro/.test(fmt)) hardware.push("AWS Nitro");
1065
+ let report;
1066
+ if (typeof d.body === "string" && d.body) {
1067
+ try {
1068
+ let bytes = Buffer.from(d.body, "base64");
1069
+ if (bytes[0] === 31 && bytes[1] === 139) bytes = gunzipSync(bytes);
1070
+ report = bytes;
1071
+ } catch {
1072
+ }
1073
+ }
1074
+ const attestedTlsKeyFp = /sev-snp-guest/.test(fmt) && report && report.length >= 144 ? report.subarray(80, 112).toString("hex") : void 0;
1075
+ const tlsKeyMatched = !!liveTlsKeyFp && !!report && (attestedTlsKeyFp === liveTlsKeyFp || report.includes(Buffer.from(liveTlsKeyFp, "hex")));
1076
+ return { host, format, hardware, attestedTlsKeyFp, liveTlsKeyFp, tlsKeyMatched, raw: doc };
1077
+ }
1078
+ __name(interpretTinfoilDoc, "interpretTinfoilDoc");
1079
+ function tinfoilTeePosture(att) {
1080
+ if (att.hardware.length === 0 && !att.attestedTlsKeyFp) return "red";
1081
+ if (att.hardware.length > 0 && att.tlsKeyMatched) return "green";
1082
+ return "yellow";
1083
+ }
1084
+ __name(tinfoilTeePosture, "tinfoilTeePosture");
1085
+
1086
+ // node_modules/pi-privacy/src/attest/dispatcher.ts?ext=privateer-privacy
1087
+ var import_undici = __toESM(require_undici());
1088
+ import crypto2 from "node:crypto";
1089
+ var captured = /* @__PURE__ */ new Map();
1090
+ var installed = false;
1091
+ function installAttestationDispatcher() {
1092
+ if (installed) return;
1093
+ installed = true;
1094
+ const baseConnect = (0, import_undici.buildConnector)({});
1095
+ const attestingConnector = /* @__PURE__ */ __name((opts, cb) => baseConnect(opts, (err, socket) => {
1096
+ const host = opts.hostname;
1097
+ if (!err && socket && host && !captured.has(host) && typeof socket.getPeerCertificate === "function") {
1098
+ try {
1099
+ const cert = socket.getPeerCertificate(true);
1100
+ if (cert && cert.raw) {
1101
+ const spkiDer = new crypto2.X509Certificate(cert.raw).publicKey.export({
1102
+ type: "spki",
1103
+ format: "der"
1104
+ });
1105
+ captured.set(host, {
1106
+ subject: cert.subject?.CN ?? JSON.stringify(cert.subject),
1107
+ issuer: cert.issuer?.O ?? cert.issuer?.CN ?? JSON.stringify(cert.issuer),
1108
+ spkiSha256: crypto2.createHash("sha256").update(spkiDer).digest("hex"),
1109
+ fingerprint256: cert.fingerprint256
1110
+ });
1111
+ }
1112
+ } catch (e) {
1113
+ captured.set(host, { error: String(e) });
1114
+ }
1115
+ }
1116
+ cb(err, socket);
1117
+ }), "attestingConnector");
1118
+ (0, import_undici.setGlobalDispatcher)(new import_undici.Agent({ connect: attestingConnector, connectTimeout: 8e3 }));
1119
+ }
1120
+ __name(installAttestationDispatcher, "installAttestationDispatcher");
1121
+ function getCapturedCert(host) {
1122
+ return captured.get(host);
1123
+ }
1124
+ __name(getCapturedCert, "getCapturedCert");
1125
+ var dispatcherTransport = /* @__PURE__ */ __name(async (host) => {
1126
+ const res = await fetch(`https://${host}/.well-known/tinfoil-attestation`);
1127
+ if (!res.ok) {
1128
+ const hint = (await res.text().catch(() => "")).slice(0, 200).trim();
1129
+ throw new Error(`HTTP ${res.status} ${res.statusText}${hint ? ` \u2014 ${hint}` : ""}`);
1130
+ }
1131
+ const doc = await res.json();
1132
+ const cert = getCapturedCert(host);
1133
+ return { doc, liveTlsKeyFp: cert && !cert.error ? cert.spkiSha256 : void 0 };
1134
+ }, "dispatcherTransport");
1135
+
1136
+ // node_modules/pi-privacy/src/posture/effective.ts?ext=privateer-privacy
1137
+ function effectiveTier(providerId, opts = {}) {
1138
+ const p = PROVIDER_BY_ID[providerId];
1139
+ if (!p) return "standard";
1140
+ if (p.local || isLocalEndpoint(opts.baseUrl)) return "local";
1141
+ if (p.postureAware && p.id === "openrouter") {
1142
+ return opts.zdrEnforced ? "zdr-enforced" : "zdr-policy";
1143
+ }
1144
+ if (p.postureAware && p.id === "privateer") {
1145
+ return opts.verifiedTee ? "tee-verified" : "zdr-policy";
1146
+ }
1147
+ return p.tier;
1148
+ }
1149
+ __name(effectiveTier, "effectiveTier");
1150
+
1151
+ // node_modules/pi-privacy/src/posture/picker.ts?ext=privateer-privacy
1152
+ function capabilityTier(providerId, baseUrl, opts = {}) {
1153
+ if (isLocalEndpoint(baseUrl)) return "local";
1154
+ return effectiveTier(providerId ?? "", {
1155
+ baseUrl,
1156
+ zdrEnforced: opts.zdrEnforced,
1157
+ verifiedTee: opts.verifiedTee
1158
+ });
1159
+ }
1160
+ __name(capabilityTier, "capabilityTier");
1161
+ function capabilityBadge(tier, attestable) {
1162
+ switch (tier) {
1163
+ case "tee-verified":
1164
+ return attestable ? { glyph: "\u25C6", label: "Verifiable TEE" } : { glyph: "\u{1F6E1}", label: "Verified TEE" };
1165
+ case "local":
1166
+ return { glyph: "\u{1F6E1}", label: "On-device" };
1167
+ case "zdr-enforced":
1168
+ return { glyph: "\u{1F6E1}", label: "ZDR (enforced)" };
1169
+ case "tee-unverified":
1170
+ return { glyph: "\u26A0", label: "TEE (unconfirmed)" };
1171
+ case "zdr-policy":
1172
+ return { glyph: "\u26A0", label: "ZDR (by policy)" };
1173
+ default:
1174
+ return { glyph: "\u2022", label: "Standard" };
1175
+ }
1176
+ }
1177
+ __name(capabilityBadge, "capabilityBadge");
1178
+ function pickerEntry(model, opts = {}) {
1179
+ const provider = model.provider;
1180
+ const verifiedTee = typeof opts.verifiedTee === "function" ? !!opts.verifiedTee(model) : !!opts.verifiedTee;
1181
+ const attestable = provider === "privateer" ? verifiedTee : !!(provider && PROVIDER_BY_ID[provider]?.attestable);
1182
+ const tier = capabilityTier(provider, model.baseUrl, { zdrEnforced: opts.zdrEnforced, verifiedTee });
1183
+ const { glyph, label } = capabilityBadge(tier, attestable);
1184
+ return { model, capabilityTier: tier, rank: tierRank(tier), glyph, label, attestable };
1185
+ }
1186
+ __name(pickerEntry, "pickerEntry");
1187
+ function rankModels(models, opts = {}) {
1188
+ return models.map((m) => pickerEntry(m, opts)).sort(
1189
+ (a, b) => a.rank - b.rank || (a.model.provider ?? "").localeCompare(b.model.provider ?? "") || (a.model.id ?? "").localeCompare(b.model.id ?? "")
1190
+ );
1191
+ }
1192
+ __name(rankModels, "rankModels");
1193
+ function pickerOptionLabel(e, current = false) {
1194
+ const who = `${e.model.provider ?? "?"}/${e.model.id ?? "?"}`;
1195
+ return `${e.glyph} ${e.label} \xB7 ${who}${current ? " (current)" : ""}`;
1196
+ }
1197
+ __name(pickerOptionLabel, "pickerOptionLabel");
1198
+
1199
+ // node_modules/pi-privacy/src/pii/detect.ts?ext=privateer-privacy
1200
+ var PLACEHOLDER = {
1201
+ email: "\xABemail\xBB",
1202
+ phone: "\xABphone\xBB",
1203
+ ssn: "\xABssn\xBB",
1204
+ "credit-card": "\xABcard\xBB",
1205
+ ip: "\xABip\xBB",
1206
+ iban: "\xABiban\xBB",
1207
+ mac: "\xABmac\xBB",
1208
+ "aws-key": "\xABaws-key\xBB",
1209
+ "gh-token": "\xABtoken\xBB",
1210
+ "api-key": "\xABapi-key\xBB",
1211
+ jwt: "\xABjwt\xBB",
1212
+ "private-key": "\xABprivate-key\xBB"
1213
+ };
1214
+ var SECRET_TYPES = /* @__PURE__ */ new Set([
1215
+ "aws-key",
1216
+ "gh-token",
1217
+ "api-key",
1218
+ "jwt",
1219
+ "private-key"
1220
+ ]);
1221
+ var PATTERNS = [
1222
+ { type: "email", re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, validate: mailboxShaped },
1223
+ { type: "ssn", re: /\b\d{3}-\d{2}-\d{4}\b/g },
1224
+ // ── secrets (prefix-anchored, high precision) ──────────────────────────────
1225
+ // PEM private-key block — match the whole block so redaction removes the key body.
1226
+ { type: "private-key", re: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY-----/g },
1227
+ // AWS access key id (AKIA / ASIA + 16 upper-alnum).
1228
+ { type: "aws-key", re: /\bA(?:KIA|SIA)[0-9A-Z]{16}\b/g },
1229
+ // GitHub tokens: ghp_/gho_/ghu_/ghs_/ghr_ + 36+ base62.
1230
+ { type: "gh-token", re: /\bgh[pousr]_[A-Za-z0-9]{36,255}\b/g },
1231
+ // JWT: three base64url segments. `eyJ` is base64 of `{"` — a strong header marker.
1232
+ { type: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g },
1233
+ // OpenAI/Anthropic/Privateer & generic `sk-` secret keys.
1234
+ { type: "api-key", re: /\bsk-[A-Za-z0-9_-]{16,}\b/g },
1235
+ // Slack tokens (bot/user/app/refresh/legacy).
1236
+ { type: "api-key", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g },
1237
+ // Google API key.
1238
+ { type: "api-key", re: /\bAIza[0-9A-Za-z_-]{35}\b/g },
1239
+ // Stripe live secret / restricted keys.
1240
+ { type: "api-key", re: /\b[sr]k_live_[0-9A-Za-z]{20,}\b/g },
1241
+ // ── consumer PII ───────────────────────────────────────────────────────────
1242
+ // 13–19 digit runs (optionally space/dash grouped) that pass the Luhn check — this
1243
+ // sharply cuts false positives vs "any long number".
1244
+ { type: "credit-card", re: /\b(?:\d[ -]?){13,19}\b/g, validate: cardShaped },
1245
+ // IPv4 with each octet 0–255. The lookarounds keep it out of longer dotted runs —
1246
+ // `1.2.3.4.5` (an OID), `v1.2.3.4` (a version) — see the precision note above.
1247
+ { type: "ip", re: /(?<![\w.])(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)(?!\.?\d)/g },
1248
+ // IBAN: 2-letter country + 2 check digits + 11–30 alphanumerics, mod-97 validated
1249
+ // (cuts false positives on random alphanumeric runs sharply).
1250
+ { type: "iban", re: /\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b/g, validate: ibanValid },
1251
+ // MAC address (colon or dash separated). A MAC is exactly six octets, so a run that
1252
+ // CONTINUES on either side is a hex dump or a certificate fingerprint, not an address.
1253
+ { type: "mac", re: /(?<![0-9A-Fa-f][:-])(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}(?![:-][0-9A-Fa-f]{2})\b/g },
1254
+ // North-American / international-ish phone. Deliberately last + conservative to
1255
+ // avoid eating IDs; requires a plausible separator or leading +.
1256
+ { type: "phone", re: /(?<![\d.])(?:\+\d{1,3}[\s.-]?)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}\b/g }
1257
+ ];
1258
+ var NOT_A_TLD = /* @__PURE__ */ new Set([
1259
+ "png",
1260
+ "jpg",
1261
+ "jpeg",
1262
+ "gif",
1263
+ "webp",
1264
+ "svg",
1265
+ "ico",
1266
+ "bmp",
1267
+ "tif",
1268
+ "tiff",
1269
+ "avif",
1270
+ "mp3",
1271
+ "mp4",
1272
+ "wav",
1273
+ "avi",
1274
+ "mkv",
1275
+ "webm",
1276
+ "ogg",
1277
+ "flac",
1278
+ "pdf",
1279
+ "docx",
1280
+ "xlsx",
1281
+ "pptx",
1282
+ "csv",
1283
+ "json",
1284
+ "yaml",
1285
+ "yml",
1286
+ "toml",
1287
+ "xml",
1288
+ "html",
1289
+ "htm",
1290
+ "css",
1291
+ "scss",
1292
+ "sass",
1293
+ "less",
1294
+ "tsx",
1295
+ "jsx",
1296
+ "mjs",
1297
+ "cjs",
1298
+ "java",
1299
+ "php",
1300
+ "sql",
1301
+ "txt",
1302
+ "log",
1303
+ "lock",
1304
+ "exe",
1305
+ "dll",
1306
+ "dmg",
1307
+ "iso",
1308
+ "tar",
1309
+ "gz",
1310
+ "tgz",
1311
+ "bz2",
1312
+ "rar",
1313
+ "woff",
1314
+ "woff2",
1315
+ "ttf",
1316
+ "otf",
1317
+ "eot",
1318
+ "bak",
1319
+ "tmp",
1320
+ "swp",
1321
+ "map",
1322
+ "min",
1323
+ "snap",
1324
+ "patch",
1325
+ "diff"
1326
+ ]);
1327
+ var VERSION_DOMAIN = /^\d+(?:\.\d+)+(?:-[A-Za-z0-9.]+)?$/;
1328
+ function mailboxShaped(value) {
1329
+ const domain = value.slice(value.lastIndexOf("@") + 1).toLowerCase();
1330
+ if (VERSION_DOMAIN.test(domain)) return false;
1331
+ const tld = domain.slice(domain.lastIndexOf(".") + 1);
1332
+ return !NOT_A_TLD.has(tld);
1333
+ }
1334
+ __name(mailboxShaped, "mailboxShaped");
1335
+ var CARD_LENGTHS = /* @__PURE__ */ new Set([13, 14, 15, 16, 19]);
1336
+ function cardShaped(s) {
1337
+ const digits = s.replace(/\D/g, "");
1338
+ if (!CARD_LENGTHS.has(digits.length)) return false;
1339
+ const iin = digits.charCodeAt(0) - 48;
1340
+ if (iin < 2 || iin > 6) return false;
1341
+ return luhn(s);
1342
+ }
1343
+ __name(cardShaped, "cardShaped");
1344
+ function luhn(s) {
1345
+ const digits = s.replace(/\D/g, "");
1346
+ if (digits.length < 13 || digits.length > 19) return false;
1347
+ let sum = 0;
1348
+ let alt = false;
1349
+ for (let i = digits.length - 1; i >= 0; i--) {
1350
+ let d = digits.charCodeAt(i) - 48;
1351
+ if (alt) {
1352
+ d *= 2;
1353
+ if (d > 9) d -= 9;
1354
+ }
1355
+ sum += d;
1356
+ alt = !alt;
1357
+ }
1358
+ return sum % 10 === 0;
1359
+ }
1360
+ __name(luhn, "luhn");
1361
+ function ibanValid(s) {
1362
+ const iban = s.toUpperCase();
1363
+ if (iban.length < 15 || iban.length > 34) return false;
1364
+ const rearranged = iban.slice(4) + iban.slice(0, 4);
1365
+ let remainder = 0;
1366
+ for (const ch of rearranged) {
1367
+ const code = ch >= "A" && ch <= "Z" ? (ch.charCodeAt(0) - 55).toString() : ch;
1368
+ for (const d of code) remainder = (remainder * 10 + (d.charCodeAt(0) - 48)) % 97;
1369
+ }
1370
+ return remainder === 1;
1371
+ }
1372
+ __name(ibanValid, "ibanValid");
1373
+ function maskPii(type, value) {
1374
+ switch (type) {
1375
+ case "email": {
1376
+ const at = value.lastIndexOf("@");
1377
+ const local = value.slice(0, at);
1378
+ const domain = value.slice(at);
1379
+ const head = local.length > 2 ? `${local[0]}\u2026${local[local.length - 1]}` : "\u2026";
1380
+ return `${head}${domain}`;
1381
+ }
1382
+ case "ip":
1383
+ return value.replace(/\.\d{1,3}$/, ".\u2022");
1384
+ case "mac":
1385
+ return `${value.slice(0, 8)}:\u2022\u2022:\u2022\u2022:\u2022\u2022`;
1386
+ case "private-key":
1387
+ return "-----BEGIN PRIVATE KEY----- block";
1388
+ case "aws-key":
1389
+ case "gh-token":
1390
+ case "api-key":
1391
+ case "jwt":
1392
+ return `${value.slice(0, 6)}\u2026 (${value.length} chars)`;
1393
+ default:
1394
+ return void 0;
1395
+ }
1396
+ }
1397
+ __name(maskPii, "maskPii");
1398
+ function scanPii(text, opts = {}) {
1399
+ if (!text) return { hits: [], suppressed: [], samples: [] };
1400
+ const { allow, samples: sampleCap = 4 } = opts;
1401
+ const counts = /* @__PURE__ */ new Map();
1402
+ const skipped = /* @__PURE__ */ new Map();
1403
+ const seen = /* @__PURE__ */ new Map();
1404
+ for (const { type, re, validate } of PATTERNS) {
1405
+ for (const m of text.matchAll(re)) {
1406
+ if (validate && !validate(m[0])) continue;
1407
+ if (allow?.(type, m[0])) {
1408
+ skipped.set(type, (skipped.get(type) ?? 0) + 1);
1409
+ continue;
1410
+ }
1411
+ counts.set(type, (counts.get(type) ?? 0) + 1);
1412
+ if (sampleCap > 0) {
1413
+ const masked = maskPii(type, m[0]);
1414
+ if (masked) {
1415
+ const set = seen.get(type) ?? /* @__PURE__ */ new Set();
1416
+ if (set.size < sampleCap) set.add(masked);
1417
+ seen.set(type, set);
1418
+ }
1419
+ }
1420
+ }
1421
+ }
1422
+ const toHits = /* @__PURE__ */ __name((m) => [...m.entries()].map(([type, count]) => ({ type, count })), "toHits");
1423
+ return {
1424
+ hits: toHits(counts),
1425
+ suppressed: toHits(skipped),
1426
+ samples: [...seen.entries()].flatMap(([type, set]) => [...set].map((masked) => ({ type, masked })))
1427
+ };
1428
+ }
1429
+ __name(scanPii, "scanPii");
1430
+ function detectPii(text, opts = {}) {
1431
+ return scanPii(text, { ...opts, samples: 0 }).hits;
1432
+ }
1433
+ __name(detectPii, "detectPii");
1434
+ function hasSecrets(hits) {
1435
+ return hits.some((h) => SECRET_TYPES.has(h.type));
1436
+ }
1437
+ __name(hasSecrets, "hasSecrets");
1438
+ function secretHits(hits) {
1439
+ return hits.filter((h) => SECRET_TYPES.has(h.type));
1440
+ }
1441
+ __name(secretHits, "secretHits");
1442
+ function redactPii(text, only, allow) {
1443
+ let out = text;
1444
+ for (const { type, re, validate } of PATTERNS) {
1445
+ if (only && !only.has(type)) continue;
1446
+ out = out.replace(re, (m) => {
1447
+ if (validate && !validate(m)) return m;
1448
+ if (allow?.(type, m)) return m;
1449
+ return PLACEHOLDER[type];
1450
+ });
1451
+ }
1452
+ return out;
1453
+ }
1454
+ __name(redactPii, "redactPii");
1455
+ function summarizePii(hits) {
1456
+ const label = {
1457
+ email: ["email", "emails"],
1458
+ phone: ["phone number", "phone numbers"],
1459
+ ssn: ["SSN", "SSNs"],
1460
+ "credit-card": ["card number", "card numbers"],
1461
+ ip: ["IP address", "IP addresses"],
1462
+ iban: ["IBAN", "IBANs"],
1463
+ mac: ["MAC address", "MAC addresses"],
1464
+ "aws-key": ["AWS key", "AWS keys"],
1465
+ "gh-token": ["GitHub token", "GitHub tokens"],
1466
+ "api-key": ["API key", "API keys"],
1467
+ jwt: ["JWT", "JWTs"],
1468
+ "private-key": ["private key", "private keys"]
1469
+ };
1470
+ return hits.map((h) => `${h.count} ${label[h.type][h.count === 1 ? 0 : 1]}`).join(", ");
1471
+ }
1472
+ __name(summarizePii, "summarizePii");
1473
+ function piiDetail(scan) {
1474
+ const lines = [];
1475
+ for (const hit of scan.hits) {
1476
+ const shown = scan.samples.filter((s) => s.type === hit.type).map((s) => s.masked);
1477
+ const more = hit.count - shown.length;
1478
+ const tail = shown.length ? `: ${shown.join(", ")}${more > 0 ? `, +${more} more` : ""}` : " (masked \u2014 not shown)";
1479
+ lines.push(` \u2022 ${summarizePii([hit])}${tail}`);
1480
+ }
1481
+ if (scan.suppressed.length) {
1482
+ lines.push(` \u2022 allowlisted (not counted): ${summarizePii(scan.suppressed)}`);
1483
+ }
1484
+ lines.push(" Values are masked here; the full values are in what would be sent.");
1485
+ return lines.join("\n");
1486
+ }
1487
+ __name(piiDetail, "piiDetail");
1488
+ function piiInline(scan) {
1489
+ return scan.hits.map((h) => {
1490
+ const shown = scan.samples.filter((s) => s.type === h.type).map((s) => s.masked);
1491
+ const more = h.count - shown.length;
1492
+ const tail = shown.length ? ` (${shown.join(", ")}${more > 0 ? `, +${more} more` : ""})` : "";
1493
+ return `${summarizePii([h])}${tail}`;
1494
+ }).join(", ");
1495
+ }
1496
+ __name(piiInline, "piiInline");
1497
+ function newPii(hits, baseline) {
1498
+ const out = [];
1499
+ for (const h of hits) {
1500
+ const seen = baseline.get(h.type) ?? 0;
1501
+ if (h.count > seen) out.push({ type: h.type, count: h.count - seen });
1502
+ }
1503
+ return out;
1504
+ }
1505
+ __name(newPii, "newPii");
1506
+ function mergePiiBaseline(baseline, hits) {
1507
+ const out = new Map(baseline);
1508
+ for (const h of hits) out.set(h.type, Math.max(out.get(h.type) ?? 0, h.count));
1509
+ return out;
1510
+ }
1511
+ __name(mergePiiBaseline, "mergePiiBaseline");
1512
+
1513
+ // node_modules/pi-privacy/src/posture/downgrade.ts?ext=privateer-privacy
1514
+ var EXPOSURE = {
1515
+ "tee-verified": 0,
1516
+ // provider cannot read the payload
1517
+ local: 0,
1518
+ // payload never leaves the machine
1519
+ "zdr-enforced": 1,
1520
+ // provider reads it; retention observably refused
1521
+ "zdr-policy": 2,
1522
+ // provider reads it; retention refused by promise only
1523
+ "tee-unverified": 2,
1524
+ // enclave claimed, unproven — assume it reads it
1525
+ standard: 3
1526
+ // read and retained at the provider's discretion
1527
+ };
1528
+ function exposureLevel(tier) {
1529
+ return EXPOSURE[tier] ?? 3;
1530
+ }
1531
+ __name(exposureLevel, "exposureLevel");
1532
+ function assessDowngrade(from, to, hits = []) {
1533
+ const a = from ?? "standard";
1534
+ const b = to ?? "standard";
1535
+ return {
1536
+ downgrade: from !== void 0 && to !== void 0 && exposureLevel(b) > exposureLevel(a),
1537
+ from: a,
1538
+ to: b,
1539
+ severity: hits.length === 0 ? "none" : hasSecrets(hits) ? "secret" : "pii"
1540
+ };
1541
+ }
1542
+ __name(assessDowngrade, "assessDowngrade");
1543
+ function downgradeWarning(a, hits, modelLabel) {
1544
+ const target = modelLabel ? ` to ${modelLabel}` : "";
1545
+ const carried = hits.length ? `carrying ${summarizePii(hits)}` : "already in context";
1546
+ return `\u26A0 Privacy downgrade: ${TIERS[a.from].label} \u2192 ${TIERS[a.to].label}. This session's history \u2014 ${carried} \u2014 will be re-sent${target} on the next turn. Detection is best-effort structured PII/secrets, so treat it as a floor, not a full inventory.`;
1547
+ }
1548
+ __name(downgradeWarning, "downgradeWarning");
1549
+
1550
+ // node_modules/pi-privacy/src/posture/verify.ts?ext=privateer-privacy
1551
+ async function verifyModelPosture(providerId, modelId, opts = {}) {
1552
+ const p = PROVIDER_BY_ID[providerId];
1553
+ if (p?.id === "tinfoil") {
1554
+ try {
1555
+ const att = await fetchTinfoilAttestation(
1556
+ { baseURL: opts.baseUrl },
1557
+ opts.transport ?? httpsTransport
1558
+ );
1559
+ const tp = tinfoilTeePosture(att);
1560
+ return { providerId, modelId, tier: tierFromTeePosture(tp), teePosture: tp, attestation: att };
1561
+ } catch (e) {
1562
+ return { providerId, modelId, tier: "tee-unverified", error: e.message };
1563
+ }
1564
+ }
1565
+ if (p?.id === "nearai") {
1566
+ try {
1567
+ const att = await fetchAttestation({ apiKey: opts.apiKey, baseURL: opts.baseUrl }, modelId);
1568
+ const tp = teePosture(att);
1569
+ return { providerId, modelId, tier: tierFromTeePosture(tp), teePosture: tp, attestation: att };
1570
+ } catch (e) {
1571
+ return { providerId, modelId, tier: "tee-unverified", error: e.message };
1572
+ }
1573
+ }
1574
+ return {
1575
+ providerId,
1576
+ modelId,
1577
+ tier: effectiveTier(providerId, { baseUrl: opts.baseUrl, zdrEnforced: opts.zdrEnforced })
1578
+ };
1579
+ }
1580
+ __name(verifyModelPosture, "verifyModelPosture");
1581
+
1582
+ // node_modules/pi-privacy/src/ext/patches.ts?ext=privateer-privacy
1583
+ function asObject(payload) {
1584
+ return payload && typeof payload === "object" ? { ...payload } : {};
1585
+ }
1586
+ __name(asObject, "asObject");
1587
+ function veniceRequestPatch(payload) {
1588
+ const p = asObject(payload);
1589
+ const vp = p.venice_parameters && typeof p.venice_parameters === "object" ? p.venice_parameters : {};
1590
+ return { ...p, venice_parameters: { ...vp, include_venice_system_prompt: false } };
1591
+ }
1592
+ __name(veniceRequestPatch, "veniceRequestPatch");
1593
+ function openRouterZdrPatch(payload) {
1594
+ const p = asObject(payload);
1595
+ const provider = p.provider && typeof p.provider === "object" ? p.provider : {};
1596
+ return { ...p, provider: { ...provider, zdr: true, data_collection: "deny" } };
1597
+ }
1598
+ __name(openRouterZdrPatch, "openRouterZdrPatch");
1599
+
1600
+ // node_modules/pi-privacy/src/pii/allow.ts?ext=privateer-privacy
1601
+ var DEFAULT_ALLOW = [
1602
+ // RFC 2606 example domains + RFC 6761 special-use TLDs, and their subdomains.
1603
+ "@example.com",
1604
+ "@example.org",
1605
+ "@example.net",
1606
+ "@example",
1607
+ "@test",
1608
+ "@invalid",
1609
+ "@localhost",
1610
+ "@local",
1611
+ // Git/GitHub machine senders — the single biggest source of "N emails detected"
1612
+ // in a repository, and by construction not a mailbox anyone reads.
1613
+ "@users.noreply.github.com",
1614
+ "noreply@*",
1615
+ "no-reply@*",
1616
+ "donotreply@*",
1617
+ "do-not-reply@*",
1618
+ // Loopback, unspecified, broadcast, link-local. NOTE private LAN ranges
1619
+ // (10/8, 172.16/12, 192.168/16) are deliberately NOT here: they identify a real
1620
+ // host on a real network, which is exactly the thing the gate is for.
1621
+ "127.0.0.0/8",
1622
+ "0.0.0.0",
1623
+ "255.255.255.255",
1624
+ "169.254.0.0/16"
1625
+ ];
1626
+ var IPV4 = /^\d{1,3}(?:\.\d{1,3}){3}$/;
1627
+ function ipToInt(ip) {
1628
+ const parts = ip.split(".").map(Number);
1629
+ if (parts.length !== 4 || parts.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return void 0;
1630
+ return (parts[0] << 24 | parts[1] << 16 | parts[2] << 8 | parts[3]) >>> 0;
1631
+ }
1632
+ __name(ipToInt, "ipToInt");
1633
+ function globToRe(pattern) {
1634
+ const body = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
1635
+ return new RegExp(`^${body}$`, "i");
1636
+ }
1637
+ __name(globToRe, "globToRe");
1638
+ function parseRule(raw) {
1639
+ const entry = raw.trim();
1640
+ if (!entry) return void 0;
1641
+ if (/^\*+$/.test(entry)) return void 0;
1642
+ const cidr = /^(\d{1,3}(?:\.\d{1,3}){3})\/(\d{1,2})$/.exec(entry);
1643
+ if (cidr) {
1644
+ const net = ipToInt(cidr[1]);
1645
+ const len = Number(cidr[2]);
1646
+ if (net === void 0 || len > 32) return void 0;
1647
+ const mask = len === 0 ? 0 : 4294967295 << 32 - len >>> 0;
1648
+ return { kind: "cidr", net: (net & mask) >>> 0, mask };
1649
+ }
1650
+ if (entry.startsWith("@")) {
1651
+ const domain = entry.slice(1).toLowerCase();
1652
+ return domain ? { kind: "domain", domain } : void 0;
1653
+ }
1654
+ if (!entry.includes("@") && entry.includes(".") && !entry.includes("*") && !IPV4.test(entry)) {
1655
+ return { kind: "domain", domain: entry.toLowerCase() };
1656
+ }
1657
+ return { kind: "glob", re: globToRe(entry) };
1658
+ }
1659
+ __name(parseRule, "parseRule");
1660
+ function ruleMatches(rule, type, value) {
1661
+ switch (rule.kind) {
1662
+ case "domain": {
1663
+ if (type !== "email") return false;
1664
+ const at = value.lastIndexOf("@");
1665
+ if (at < 0) return false;
1666
+ const domain = value.slice(at + 1).toLowerCase();
1667
+ return domain === rule.domain || domain.endsWith(`.${rule.domain}`);
1668
+ }
1669
+ case "cidr": {
1670
+ if (type !== "ip") return false;
1671
+ const n = ipToInt(value);
1672
+ return n !== void 0 && (n & rule.mask) >>> 0 === rule.net;
1673
+ }
1674
+ case "glob":
1675
+ return rule.re.test(value);
1676
+ }
1677
+ }
1678
+ __name(ruleMatches, "ruleMatches");
1679
+ function compileAllow(entries = [], opts = {}) {
1680
+ const { defaults = true, warn } = opts;
1681
+ const rules = [];
1682
+ for (const raw of [...defaults ? DEFAULT_ALLOW : [], ...entries]) {
1683
+ const rule = parseRule(raw);
1684
+ if (rule) rules.push(rule);
1685
+ else warn?.(`piiAllow entry ${JSON.stringify(raw)} is not a usable pattern \u2014 ignoring it.`);
1686
+ }
1687
+ return (type, value) => rules.some((r) => ruleMatches(r, type, value));
1688
+ }
1689
+ __name(compileAllow, "compileAllow");
1690
+
1691
+ // node_modules/pi-privacy/src/pii/command.ts?ext=privateer-privacy
1692
+ var PII_COMMAND_WORDS = ["off", "warn", "redact", "on", "status"];
1693
+ var SYNONYMS = {
1694
+ off: { kind: "set", mode: "off" },
1695
+ bypass: { kind: "set", mode: "off" },
1696
+ disable: { kind: "set", mode: "off" },
1697
+ warn: { kind: "set", mode: "warn" },
1698
+ ask: { kind: "set", mode: "warn" },
1699
+ prompt: { kind: "set", mode: "warn" },
1700
+ redact: { kind: "set", mode: "redact" },
1701
+ mask: { kind: "set", mode: "redact" },
1702
+ on: { kind: "restore" },
1703
+ restore: { kind: "restore" },
1704
+ reset: { kind: "restore" },
1705
+ default: { kind: "restore" },
1706
+ status: { kind: "status" },
1707
+ show: { kind: "status" }
1708
+ };
1709
+ function parsePiiCommand(args) {
1710
+ const word = (typeof args === "string" ? args : "").trim().split(/\s+/)[0]?.toLowerCase() ?? "";
1711
+ if (!word) return { kind: "status" };
1712
+ return SYNONYMS[word] ?? { kind: "unknown", word };
1713
+ }
1714
+ __name(parsePiiCommand, "parsePiiCommand");
1715
+ var MODE_BLURB = {
1716
+ warn: "you are asked before PII goes down an unverified channel",
1717
+ redact: "PII is masked before send, without asking",
1718
+ off: "outbound requests are not gated on PII at all"
1719
+ };
1720
+ function stillOn(s) {
1721
+ const on = [];
1722
+ if (s.toolExfilPolicy !== "off") on.push("credentials leaving via a tool call");
1723
+ if (s.toolResultPolicy !== "off") on.push("credentials arriving in a tool result");
1724
+ if (s.downgradePolicy !== "off") on.push("a model downgrade carrying credentials");
1725
+ return on;
1726
+ }
1727
+ __name(stillOn, "stillOn");
1728
+ function statusLine(s) {
1729
+ const effective = s.session ?? s.configured;
1730
+ const source = s.session ? `set by /${s.commandName} this session` : "configured";
1731
+ return `PII gate: ${effective} (${source}) \u2014 ${MODE_BLURB[effective]}. Configured: ${s.configured}. Tool exfil: ${s.toolExfilPolicy}. Tool result: ${s.toolResultPolicy}. Downgrade: ${s.downgradePolicy}.`;
1732
+ }
1733
+ __name(statusLine, "statusLine");
1734
+ var usage = /* @__PURE__ */ __name((name) => `Usage: /${name} ${PII_COMMAND_WORDS.join(" | ")}.`, "usage");
1735
+ function runPiiCommand(args, s) {
1736
+ const action = parsePiiCommand(args);
1737
+ const before = s.session ?? s.configured;
1738
+ if (action.kind === "status")
1739
+ return { session: s.session, changed: false, message: `${statusLine(s)} ${usage(s.commandName)}`, level: "info" };
1740
+ if (action.kind === "unknown")
1741
+ return {
1742
+ session: s.session,
1743
+ changed: false,
1744
+ // Say what is still in force, so a mistyped bypass can't be mistaken for a
1745
+ // successful one: the user who typed `/pii of` must not walk away believing
1746
+ // the gate is down.
1747
+ message: `"${action.word}" is not a /${s.commandName} setting \u2014 nothing changed. ${statusLine(s)} ${usage(s.commandName)}`,
1748
+ level: "warning"
1749
+ };
1750
+ if (action.kind === "restore") {
1751
+ if (s.configured === "off")
1752
+ return {
1753
+ session: void 0,
1754
+ changed: before !== "off",
1755
+ message: `Your configured PII policy is "off", so there is nothing to restore \u2014 the gate stays off. Use /${s.commandName} warn or /${s.commandName} redact to turn it on for this session.`,
1756
+ level: "warning"
1757
+ };
1758
+ return {
1759
+ session: void 0,
1760
+ changed: before !== s.configured,
1761
+ message: `PII gate restored to your configured policy: ${s.configured} \u2014 ${MODE_BLURB[s.configured]}.`,
1762
+ level: "info"
1763
+ };
1764
+ }
1765
+ const changed = before !== action.mode;
1766
+ if (action.mode === "off") {
1767
+ const on = stillOn(s);
1768
+ const remains = on.length ? `Still on: ${on.join(", ")}.` : `Nothing else is gating this session \u2014 the tool, result and downgrade guards are all off too.`;
1769
+ return {
1770
+ session: "off",
1771
+ changed,
1772
+ message: `\u2691 PII gate OFF for this session. Outbound requests are no longer scanned-and-gated for PII, and a tool call carrying PII but no credential no longer prompts. ${remains} This lasts for this session only \u2014 /${s.commandName} on restores "${s.configured}", and so does a restart.`,
1773
+ // A bypass is not routine output. It stays at warning level so it reads as the
1774
+ // guard coming down, which is what it is.
1775
+ level: "warning"
1776
+ };
1777
+ }
1778
+ return {
1779
+ session: action.mode,
1780
+ changed,
1781
+ message: `PII gate: ${action.mode} for this session \u2014 ${MODE_BLURB[action.mode]}.` + (action.mode === s.configured ? "" : ` (Configured: ${s.configured}.)`) + (changed ? " Earlier answers this session are cleared, so the gate re-asks about PII already in context." : ""),
1782
+ level: "info"
1783
+ };
1784
+ }
1785
+ __name(runPiiCommand, "runPiiCommand");
1786
+
1787
+ // node_modules/pi-privacy/src/ext/toolgate.ts?ext=privateer-privacy
1788
+ var LOCAL_TOOLS = /* @__PURE__ */ new Set(["read", "grep", "find", "ls", "edit", "write"]);
1789
+ var EGRESS_CMD = /(?:^|[\s|&;(`$])(?:curl|wget|nc|ncat|netcat|scp|sftp|rsync|telnet|ftp|xh|httpie|kubectl|aws|gcloud|az)\b|>\s*\/dev\/tcp\/|\bgit\s+push\b|\bssh\s/i;
1790
+ var URL_RE = /\bhttps?:\/\/[^\s"'`)<>]+/gi;
1791
+ var TOK_START = `(?:^|[\\s"'\`=@<(|])`;
1792
+ var TOK_END = `(?=$|[\\s"'\`)>;,|&])`;
1793
+ var DIR = `(?:[\\w.~/-]*/)?`;
1794
+ var SENSITIVE_FILES = [
1795
+ { label: ".env file", re: new RegExp(`${TOK_START}${DIR}\\.env(?:\\.[\\w-]+)?${TOK_END}`, "i") },
1796
+ { label: "SSH key material", re: new RegExp(`${TOK_START}${DIR}\\.ssh(?:/[\\w.-]+)?${TOK_END}`, "i") },
1797
+ // id_rsa but NOT id_rsa.pub — the trailing "." fails TOK_END, so the public half
1798
+ // (which is not a secret) never fires the gate.
1799
+ { label: "SSH private key", re: new RegExp(`${TOK_START}${DIR}id_(?:rsa|dsa|ecdsa|ed25519)${TOK_END}`, "i") },
1800
+ { label: "AWS credentials", re: new RegExp(`${DIR}\\.aws/(?:credentials|config)${TOK_END}`, "i") },
1801
+ { label: "kubeconfig", re: new RegExp(`${DIR}\\.kube/config${TOK_END}`, "i") },
1802
+ {
1803
+ label: "stored credentials",
1804
+ re: new RegExp(`${TOK_START}${DIR}\\.(?:npmrc|netrc|pypirc|git-credentials|docker/config\\.json)${TOK_END}`, "i")
1805
+ },
1806
+ // Private-key / keystore files. Requires a literal dot before the extension, so
1807
+ // header names like `x-api-key` don't match.
1808
+ {
1809
+ label: "key or certificate file",
1810
+ re: new RegExp(`${TOK_START}${DIR}[\\w.-]+\\.(?:pem|key|p12|pfx|jks|keystore|kdbx)${TOK_END}`, "i")
1811
+ },
1812
+ {
1813
+ label: "secrets file",
1814
+ re: new RegExp(`${TOK_START}${DIR}(?:secrets?|credentials?)\\.(?:json|ya?ml|env|txt|toml|csv)${TOK_END}`, "i")
1815
+ }
1816
+ ];
1817
+ function sensitiveFileRefs(text) {
1818
+ const out = [];
1819
+ for (const { label, re } of SENSITIVE_FILES) {
1820
+ if (re.test(text) && !out.includes(label)) out.push(label);
1821
+ }
1822
+ return out;
1823
+ }
1824
+ __name(sensitiveFileRefs, "sensitiveFileRefs");
1825
+ function safeStringify(input) {
1826
+ if (typeof input === "string") return input;
1827
+ try {
1828
+ return JSON.stringify(input ?? "");
1829
+ } catch {
1830
+ return String(input ?? "");
1831
+ }
1832
+ }
1833
+ __name(safeStringify, "safeStringify");
1834
+ function firstRemoteUrl(text) {
1835
+ const urls = text.match(URL_RE);
1836
+ if (!urls) return void 0;
1837
+ return urls.find((u) => !isLocalEndpoint(u));
1838
+ }
1839
+ __name(firstRemoteUrl, "firstRemoteUrl");
1840
+ function splitCommands(cmd) {
1841
+ return cmd.split(/\|\||&&|[;\n|]/).map((s) => s.trim()).filter(Boolean);
1842
+ }
1843
+ __name(splitCommands, "splitCommands");
1844
+ function assessToolCall(toolName, input) {
1845
+ const name = toolName ?? "";
1846
+ if (LOCAL_TOOLS.has(name)) return { egress: false };
1847
+ const text = safeStringify(input);
1848
+ const remote = firstRemoteUrl(text);
1849
+ if (name === "bash") {
1850
+ const cmd = typeof input?.command === "string" ? input.command : text;
1851
+ let target;
1852
+ let egress = false;
1853
+ const files2 = [];
1854
+ for (const seg of splitCommands(cmd)) {
1855
+ const remote2 = firstRemoteUrl(seg);
1856
+ const hasUrl = URL_RE.test(seg);
1857
+ URL_RE.lastIndex = 0;
1858
+ if (remote2 || !hasUrl && EGRESS_CMD.test(seg)) {
1859
+ egress = true;
1860
+ target ??= remote2;
1861
+ }
1862
+ }
1863
+ if (egress) files2.push(...sensitiveFileRefs(cmd));
1864
+ return { egress, target, sensitiveFiles: files2.length ? files2 : void 0 };
1865
+ }
1866
+ if (!remote) return { egress: false };
1867
+ const files = sensitiveFileRefs(text);
1868
+ return { egress: true, target: remote, sensitiveFiles: files.length ? files : void 0 };
1869
+ }
1870
+ __name(assessToolCall, "assessToolCall");
1871
+
1872
+ // node_modules/pi-privacy/src/surface/tools.ts?ext=privateer-privacy
1873
+ var BUILTIN_TOOLS = /* @__PURE__ */ new Set([
1874
+ "bash",
1875
+ "read",
1876
+ "write",
1877
+ "edit",
1878
+ "grep",
1879
+ "find",
1880
+ "ls"
1881
+ ]);
1882
+ var SHELL_TOOLS = /* @__PURE__ */ new Set(["bash"]);
1883
+ var URL_PARAM = /^(?:url|uri|endpoint|host|hostname|origin|webhook|server|base_?url|address)$/i;
1884
+ var URL_IN_TEXT = /\bhttps?:\/\//i;
1885
+ function schemaNamesNetwork(schema, depth = 0, seen = /* @__PURE__ */ new Set()) {
1886
+ if (depth > 6 || schema === null || typeof schema !== "object") return false;
1887
+ if (seen.has(schema)) return false;
1888
+ seen.add(schema);
1889
+ if (Array.isArray(schema)) return schema.some((s) => schemaNamesNetwork(s, depth + 1, seen));
1890
+ const obj = schema;
1891
+ const props = obj.properties;
1892
+ if (props && typeof props === "object" && !Array.isArray(props)) {
1893
+ for (const [key, val] of Object.entries(props)) {
1894
+ if (URL_PARAM.test(key)) return true;
1895
+ if (schemaNamesNetwork(val, depth + 1, seen)) return true;
1896
+ }
1897
+ }
1898
+ for (const key of ["items", "additionalProperties", "oneOf", "anyOf", "allOf", "$defs"]) {
1899
+ if (key in obj && schemaNamesNetwork(obj[key], depth + 1, seen)) return true;
1900
+ }
1901
+ return false;
1902
+ }
1903
+ __name(schemaNamesNetwork, "schemaNamesNetwork");
1904
+ function toolProvenance(info) {
1905
+ const si = info.sourceInfo;
1906
+ if (si?.scope === "project") return "project";
1907
+ if (si?.scope === "temporary") return "temporary";
1908
+ if (info.name && BUILTIN_TOOLS.has(info.name)) return "builtin";
1909
+ if (si?.origin === "package") return "package";
1910
+ if (si?.scope === "user") return "user";
1911
+ return "unknown";
1912
+ }
1913
+ __name(toolProvenance, "toolProvenance");
1914
+ function toolReach(info, provenance) {
1915
+ const name = info.name ?? "";
1916
+ if (SHELL_TOOLS.has(name) && provenance === "builtin") return "shell";
1917
+ if (LOCAL_TOOLS.has(name) && provenance === "builtin") return "local";
1918
+ if (SHELL_TOOLS.has(name)) return "shell";
1919
+ if (schemaNamesNetwork(info.parameters)) return "network";
1920
+ if (URL_IN_TEXT.test(info.description ?? "") || URL_IN_TEXT.test(info.promptGuidelines ?? "")) return "network";
1921
+ return "unknown";
1922
+ }
1923
+ __name(toolReach, "toolReach");
1924
+ var PROVENANCE_LABEL = {
1925
+ builtin: "builtin",
1926
+ user: "user",
1927
+ package: "package",
1928
+ project: "project",
1929
+ temporary: "temporary",
1930
+ unknown: "unattributed"
1931
+ };
1932
+ var REACH_LABEL = {
1933
+ local: "local files only",
1934
+ shell: "shell (unbounded)",
1935
+ network: "network (declared)",
1936
+ unknown: "unassessed"
1937
+ };
1938
+ function isRepoSupplied(p) {
1939
+ return p === "project" || p === "temporary";
1940
+ }
1941
+ __name(isRepoSupplied, "isRepoSupplied");
1942
+ function classifyTool(info) {
1943
+ const provenance = toolProvenance(info);
1944
+ const reach = toolReach(info, provenance);
1945
+ const name = info.name ?? "(unnamed)";
1946
+ const flagged = isRepoSupplied(provenance) || provenance === "unknown";
1947
+ return {
1948
+ name,
1949
+ provenance,
1950
+ reach,
1951
+ sourcePath: info.sourceInfo?.path,
1952
+ glyph: flagged ? "\u26A0" : "\u2022",
1953
+ provenanceLabel: PROVENANCE_LABEL[provenance],
1954
+ reachLabel: REACH_LABEL[reach],
1955
+ concern: concernFor(provenance, info.sourceInfo?.path)
1956
+ };
1957
+ }
1958
+ __name(classifyTool, "classifyTool");
1959
+ function concernFor(p, path) {
1960
+ const where = path ? ` (${path})` : "";
1961
+ switch (p) {
1962
+ case "project":
1963
+ return `supplied by this project${where}, not by you \u2014 it arrived with the repository`;
1964
+ case "temporary":
1965
+ return `supplied by a CLI override for this run${where}, not by your configuration`;
1966
+ case "unknown":
1967
+ return "no source metadata \u2014 pi-privacy can't say where this came from";
1968
+ default:
1969
+ return void 0;
1970
+ }
1971
+ }
1972
+ __name(concernFor, "concernFor");
1973
+ var PROVENANCE_ORDER = ["project", "temporary", "unknown", "package", "user", "builtin"];
1974
+ function rankSurface(tools) {
1975
+ return tools.map(classifyTool).sort(
1976
+ (a, b) => PROVENANCE_ORDER.indexOf(a.provenance) - PROVENANCE_ORDER.indexOf(b.provenance) || a.name.localeCompare(b.name)
1977
+ );
1978
+ }
1979
+ __name(rankSurface, "rankSurface");
1980
+ function summarizeSurface(entries) {
1981
+ const byProvenance = {
1982
+ builtin: 0,
1983
+ user: 0,
1984
+ package: 0,
1985
+ project: 0,
1986
+ temporary: 0,
1987
+ unknown: 0
1988
+ };
1989
+ for (const e of entries) byProvenance[e.provenance]++;
1990
+ return {
1991
+ total: entries.length,
1992
+ notYours: byProvenance.project + byProvenance.temporary + byProvenance.unknown,
1993
+ byProvenance
1994
+ };
1995
+ }
1996
+ __name(summarizeSurface, "summarizeSurface");
1997
+ function surfaceLine(e) {
1998
+ const where = e.sourcePath ? ` ${e.sourcePath}` : "";
1999
+ return `${e.glyph} ${e.provenanceLabel.padEnd(9)} ${e.name.padEnd(14)}${where} \u2014 ${e.reachLabel}`;
2000
+ }
2001
+ __name(surfaceLine, "surfaceLine");
2002
+ function surfaceReport(entries, collapseBuiltins = true) {
2003
+ const s = summarizeSurface(entries);
2004
+ const head = `${s.total} tool${s.total === 1 ? "" : "s"} available \xB7 ` + (s.notYours === 0 ? "all supplied by you or pi" : `${s.notYours} not supplied by you`);
2005
+ const shown = collapseBuiltins ? entries.filter((e) => e.provenance !== "builtin") : entries;
2006
+ const lines = shown.map(surfaceLine);
2007
+ if (collapseBuiltins && s.byProvenance.builtin > 0) lines.push(` \u2026 ${s.byProvenance.builtin} builtin`);
2008
+ return [head, ...lines];
2009
+ }
2010
+ __name(surfaceReport, "surfaceReport");
2011
+
2012
+ // node_modules/pi-privacy/src/extension.ts?ext=privateer-privacy
2013
+ import { readFileSync as readFileSync5 } from "node:fs";
2014
+
2015
+ // node_modules/pi-privacy/src/surface/ledger.ts?ext=privateer-privacy
2016
+ var UNNAMED_HOST = "(unnamed host)";
2017
+ function createLedger() {
2018
+ return { entries: /* @__PURE__ */ new Map() };
2019
+ }
2020
+ __name(createLedger, "createLedger");
2021
+ function hostOf(assessment) {
2022
+ const target = assessment.target;
2023
+ if (!target) return UNNAMED_HOST;
2024
+ try {
2025
+ const h = new URL(target).hostname;
2026
+ return h || UNNAMED_HOST;
2027
+ } catch {
2028
+ return UNNAMED_HOST;
2029
+ }
2030
+ }
2031
+ __name(hostOf, "hostOf");
2032
+ function recordEgress(led, tool, assessment, opts = {}) {
2033
+ if (!assessment.egress) return;
2034
+ const host = hostOf(assessment);
2035
+ const key = `${tool}\0${host}`;
2036
+ const cur = led.entries.get(key) ?? { tool, host, calls: 0, withPii: 0, blocked: 0 };
2037
+ cur.calls++;
2038
+ if (opts.pii) cur.withPii++;
2039
+ if (opts.blocked) cur.blocked++;
2040
+ led.entries.set(key, cur);
2041
+ }
2042
+ __name(recordEgress, "recordEgress");
2043
+ function ledgerHosts(led) {
2044
+ return [...led.entries.values()].sort(
2045
+ (a, b) => b.calls - a.calls || a.tool.localeCompare(b.tool) || a.host.localeCompare(b.host)
2046
+ );
2047
+ }
2048
+ __name(ledgerHosts, "ledgerHosts");
2049
+ function observationLine(o) {
2050
+ const notes = [];
2051
+ if (o.withPii) notes.push(`${o.withPii} carried PII/credentials`);
2052
+ if (o.blocked) notes.push(`${o.blocked} blocked`);
2053
+ const suffix = notes.length ? ` (${notes.join(", ")})` : "";
2054
+ return `${o.tool} \u2192 ${o.host} ${o.calls} call${o.calls === 1 ? "" : "s"}${suffix}`;
2055
+ }
2056
+ __name(observationLine, "observationLine");
2057
+ function ledgerReport(led) {
2058
+ return ledgerHosts(led).map(observationLine);
2059
+ }
2060
+ __name(ledgerReport, "ledgerReport");
2061
+
2062
+ // node_modules/pi-privacy/src/ext/results.ts?ext=privateer-privacy
2063
+ function toolResultText(content) {
2064
+ if (content === void 0 || content === null) return "";
2065
+ if (typeof content === "string") return content;
2066
+ if (Array.isArray(content)) {
2067
+ return content.map((p) => typeof p === "string" ? p : typeof p?.text === "string" ? p.text : "").join("\n");
2068
+ }
2069
+ try {
2070
+ return JSON.stringify(content);
2071
+ } catch {
2072
+ return String(content);
2073
+ }
2074
+ }
2075
+ __name(toolResultText, "toolResultText");
2076
+ function redactToolResultContent(content, only = SECRET_TYPES, allow) {
2077
+ if (typeof content === "string") {
2078
+ const out = redactPii(content, only, allow);
2079
+ return out === content ? void 0 : out;
2080
+ }
2081
+ if (Array.isArray(content)) {
2082
+ let changed = false;
2083
+ const out = content.map((p) => {
2084
+ if (typeof p === "string") {
2085
+ const r = redactPii(p, only, allow);
2086
+ if (r !== p) changed = true;
2087
+ return r;
2088
+ }
2089
+ const text = p?.text;
2090
+ if (typeof text === "string") {
2091
+ const r = redactPii(text, only, allow);
2092
+ if (r !== text) changed = true;
2093
+ return { ...p, text: r };
2094
+ }
2095
+ return p;
2096
+ });
2097
+ return changed ? out : void 0;
2098
+ }
2099
+ return void 0;
2100
+ }
2101
+ __name(redactToolResultContent, "redactToolResultContent");
2102
+
2103
+ // node_modules/pi-privacy/src/ext/badge.ts?ext=privateer-privacy
2104
+ var DEFAULT_BADGE_SINKS = ["status", "widget", "title"];
2105
+ function postureBadge(tier) {
2106
+ if (!tier) return "\u22EF checking privacy";
2107
+ const info = TIERS[tier];
2108
+ const glyph = info.posture === "green" ? "\u{1F6E1}" : info.posture === "yellow" ? "\u26A0" : info.posture === "red" ? "\u26D4" : "\u2022";
2109
+ return `${glyph} ${info.label}`;
2110
+ }
2111
+ __name(postureBadge, "postureBadge");
2112
+ function renderBadgeTo(ui, sink, key, badge, tier) {
2113
+ switch (sink) {
2114
+ case "status":
2115
+ if (typeof ui.setStatus === "function") return ui.setStatus(key, badge), true;
2116
+ return false;
2117
+ case "widget":
2118
+ if (typeof ui.setWidget === "function") return ui.setWidget(key, [badge]), true;
2119
+ return false;
2120
+ case "title":
2121
+ if (typeof ui.setTitle === "function") return ui.setTitle(badge), true;
2122
+ return false;
2123
+ case "notify":
2124
+ if (typeof ui.notify === "function")
2125
+ return ui.notify(badge, TIERS[tier ?? "standard"].posture === "green" ? "info" : "warning"), true;
2126
+ return false;
2127
+ default:
2128
+ return false;
2129
+ }
2130
+ }
2131
+ __name(renderBadgeTo, "renderBadgeTo");
2132
+
2133
+ // node_modules/pi-privacy/src/ext/register.ts?ext=privateer-privacy
2134
+ var BUILTIN = /* @__PURE__ */ new Set(["openrouter", "fireworks"]);
2135
+ var SEED_MODELS = {
2136
+ tinfoil: "deepseek-v4-pro",
2137
+ nearai: "zai-org/GLM-5.1-FP8",
2138
+ venice: "qwen3-coder-480b-a35b-instruct-turbo",
2139
+ ollama: "llama3.1",
2140
+ privateer: "near/zai-org/GLM-5.1-FP8"
2141
+ };
2142
+ function registerable(p) {
2143
+ return !!p.baseUrl && !BUILTIN.has(p.id) && p.id !== "custom";
2144
+ }
2145
+ __name(registerable, "registerable");
2146
+ function providerConfig(p) {
2147
+ const seed = SEED_MODELS[p.id];
2148
+ const models = seed ? [
2149
+ {
2150
+ id: seed,
2151
+ name: seed,
2152
+ reasoning: false,
2153
+ input: ["text"],
2154
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
2155
+ contextWindow: 128e3,
2156
+ maxTokens: 4096
2157
+ }
2158
+ ] : [];
2159
+ const cfg = { name: p.label, baseUrl: p.baseUrl, api: p.api, models };
2160
+ if (p.keyEnv) {
2161
+ cfg.apiKey = p.keyEnv;
2162
+ cfg.authHeader = true;
2163
+ } else if (p.local && models.length) {
2164
+ cfg.apiKey = "local";
2165
+ }
2166
+ return cfg;
2167
+ }
2168
+ __name(providerConfig, "providerConfig");
2169
+ function nearApiKey(env = process.env) {
2170
+ return env.NEARAI_API_KEY ?? env.NEAR_AI_API_KEY;
2171
+ }
2172
+ __name(nearApiKey, "nearApiKey");
2173
+
2174
+ // node_modules/pi-privacy/src/ext/payload.ts?ext=privateer-privacy
2175
+ function payloadText(payload) {
2176
+ try {
2177
+ return JSON.stringify(payload?.messages ?? payload ?? "");
2178
+ } catch {
2179
+ return "";
2180
+ }
2181
+ }
2182
+ __name(payloadText, "payloadText");
2183
+ function redactPayloadPii(payload, allow) {
2184
+ if (!payload || typeof payload !== "object" || !Array.isArray(payload.messages)) return payload;
2185
+ const mask = /* @__PURE__ */ __name((s) => redactPii(s, void 0, allow), "mask");
2186
+ const messages = payload.messages.map((m) => {
2187
+ if (typeof m?.content === "string") return { ...m, content: mask(m.content) };
2188
+ if (Array.isArray(m?.content)) {
2189
+ return {
2190
+ ...m,
2191
+ content: m.content.map((p) => typeof p?.text === "string" ? { ...p, text: mask(p.text) } : p)
2192
+ };
2193
+ }
2194
+ return m;
2195
+ });
2196
+ return { ...payload, messages };
2197
+ }
2198
+ __name(redactPayloadPii, "redactPayloadPii");
2199
+
2200
+ // node_modules/pi-privacy/src/extension.ts?ext=privateer-privacy
2201
+ function isVerifiedPrivate(tier) {
2202
+ return tier === "tee-verified" || tier === "local";
2203
+ }
2204
+ __name(isVerifiedPrivate, "isVerifiedPrivate");
2205
+ var PII_WORD_HELP = {
2206
+ off: "bypass the PII gate for this session (credentials still gated)",
2207
+ warn: "ask before PII goes down an unverified channel",
2208
+ redact: "mask PII before send, without asking",
2209
+ on: "restore the configured policy",
2210
+ status: "what is in force right now"
2211
+ };
2212
+ function makePiPrivacyExtension(opts = {}) {
2213
+ const {
2214
+ installDispatcher = true,
2215
+ registerProviders = true,
2216
+ enforceOpenRouterZdr = false,
2217
+ onPosture,
2218
+ useDispatcherTransport = true,
2219
+ piiPolicy = "warn",
2220
+ piiUnattended = false,
2221
+ renderPiiAutoRedact: renderPiiAutoRedact2,
2222
+ piiAllow = [],
2223
+ piiAllowDefaults = true,
2224
+ showBadge = true,
2225
+ badgeSinks = DEFAULT_BADGE_SINKS,
2226
+ badgeKey = "pi-privacy",
2227
+ renderBadge,
2228
+ toolExfilPolicy = "warn",
2229
+ toolResultPolicy = "warn",
2230
+ downgradePolicy = "warn",
2231
+ modelPicker = true,
2232
+ modelPickerCommand = "models",
2233
+ toolSurfacePolicy = "warn",
2234
+ toolSurfaceCommand = "surface",
2235
+ piiCommand = true,
2236
+ piiCommandName = "pii",
2237
+ resolveTier,
2238
+ privateerVerifiedTee = false
2239
+ } = opts;
2240
+ return /* @__PURE__ */ __name(function piPrivacy(pi) {
2241
+ if (installDispatcher) installAttestationDispatcher();
2242
+ if (registerProviders && typeof pi.registerProvider === "function") {
2243
+ for (const p of PRIVACY_PROVIDERS) {
2244
+ if (registerable(p)) pi.registerProvider(p.id, providerConfig(p));
2245
+ }
2246
+ }
2247
+ let currentProviderId;
2248
+ let currentModelId;
2249
+ let currentTier;
2250
+ let piiChoice = "ask";
2251
+ let piiSession;
2252
+ const piiMode = /* @__PURE__ */ __name(() => piiSession ?? piiPolicy, "piiMode");
2253
+ const piiBypassed = /* @__PURE__ */ __name(() => piiSession === "off", "piiBypassed");
2254
+ const piiUnattendedNow = /* @__PURE__ */ __name(() => typeof piiUnattended === "function" ? !!piiUnattended() : !!piiUnattended, "piiUnattendedNow");
2255
+ let piiSeen = /* @__PURE__ */ new Map();
2256
+ let piiLastAction;
2257
+ const piiAllowEntries2 = /* @__PURE__ */ __name(() => (typeof piiAllow === "function" ? piiAllow() : piiAllow) ?? [], "piiAllowEntries");
2258
+ let allowKey;
2259
+ let allowMatcher;
2260
+ const piiAllowed = /* @__PURE__ */ __name((type, value) => {
2261
+ const entries = piiAllowEntries2();
2262
+ const key = entries.join("\0");
2263
+ if (key !== allowKey || !allowMatcher) {
2264
+ allowKey = key;
2265
+ allowMatcher = compileAllow(entries, {
2266
+ defaults: piiAllowDefaults,
2267
+ warn: /* @__PURE__ */ __name((m) => console.warn(`[pi-privacy] ${m}`), "warn")
2268
+ });
2269
+ }
2270
+ return allowMatcher(type, value);
2271
+ }, "piiAllowed");
2272
+ const scanOpts = { allow: piiAllowed };
2273
+ let toolAllow = false;
2274
+ let resultChoice = "ask";
2275
+ let toolSnapshot = [];
2276
+ let surfaceByName = /* @__PURE__ */ new Map();
2277
+ const ledger = createLedger();
2278
+ const surfaceOn = toolSurfacePolicy !== "off";
2279
+ const captureTools = /* @__PURE__ */ __name((ctx) => {
2280
+ if (!surfaceOn || typeof ctx?.getAllTools !== "function") return;
2281
+ try {
2282
+ const tools = ctx.getAllTools();
2283
+ if (!Array.isArray(tools)) return;
2284
+ toolSnapshot = tools;
2285
+ surfaceByName = new Map(rankSurface(tools).map((e) => [e.name, e]));
2286
+ } catch {
2287
+ }
2288
+ }, "captureTools");
2289
+ const provenanceSeen = /* @__PURE__ */ new Set();
2290
+ let projectToolsAllowed = false;
2291
+ const previewSource = /* @__PURE__ */ __name((entry) => {
2292
+ const path = entry.sourcePath;
2293
+ if (!path) return "No source path recorded for this tool \u2014 nothing to show.";
2294
+ try {
2295
+ const text = readFileSync5(path, "utf8");
2296
+ const clipped = text.length > 4e3 ? `${text.slice(0, 4e3)}
2297
+ \u2026 (truncated)` : text;
2298
+ return `${path}:
2299
+ ${clipped}`;
2300
+ } catch (e) {
2301
+ return `Could not read ${path}: ${e.message}`;
2302
+ }
2303
+ }, "previewSource");
2304
+ const guardProvenance = /* @__PURE__ */ __name(async (toolName, ctx) => {
2305
+ if (toolSurfacePolicy !== "warn" || projectToolsAllowed) return "allow";
2306
+ const entry = surfaceByName.get(toolName);
2307
+ if (!entry || !isRepoSupplied(entry.provenance)) return "allow";
2308
+ if (provenanceSeen.has(toolName)) return "allow";
2309
+ const warning = `\u26A0 \`${toolName}\` was ${entry.concern}. It is about to run for the first time this session. This says where it came FROM, not that it is unsafe.`;
2310
+ if (!canPrompt(ctx)) {
2311
+ provenanceSeen.add(toolName);
2312
+ notify(ctx, warning, "warning");
2313
+ return "allow";
2314
+ }
2315
+ for (let i = 0; i < 3; i++) {
2316
+ const choice = await ask(ctx, warning, [
2317
+ "Run it",
2318
+ "Show me the file",
2319
+ "Allow project tools for this session",
2320
+ "Block"
2321
+ ]);
2322
+ if (choice === "Show me the file") {
2323
+ notify(ctx, previewSource(entry), "info");
2324
+ continue;
2325
+ }
2326
+ if (choice === "Allow project tools for this session") {
2327
+ projectToolsAllowed = true;
2328
+ provenanceSeen.add(toolName);
2329
+ return "allow";
2330
+ }
2331
+ if (choice === "Run it") {
2332
+ provenanceSeen.add(toolName);
2333
+ return "allow";
2334
+ }
2335
+ return "block";
2336
+ }
2337
+ return "block";
2338
+ }, "guardProvenance");
2339
+ let previousTier;
2340
+ let previousModel;
2341
+ let downgradeHandled = true;
2342
+ let contextHits = [];
2343
+ let lastUi;
2344
+ let lastCtx;
2345
+ let lastBadge;
2346
+ let lastHasUI = false;
2347
+ const captureUi = /* @__PURE__ */ __name((ctx) => {
2348
+ if (ctx?.ui) lastUi = ctx.ui, lastCtx = ctx, lastHasUI = !!ctx.hasUI;
2349
+ }, "captureUi");
2350
+ const uiOf = /* @__PURE__ */ __name((ctx) => ctx?.ui ?? lastUi, "uiOf");
2351
+ const canPrompt = /* @__PURE__ */ __name((ctx) => (ctx?.hasUI ?? lastHasUI) && typeof uiOf(ctx)?.select === "function", "canPrompt");
2352
+ const ask = /* @__PURE__ */ __name((ctx, title, options) => canPrompt(ctx) ? uiOf(ctx).select(title, options) : Promise.resolve(void 0), "ask");
2353
+ const notify = /* @__PURE__ */ __name((ctx, message, level) => uiOf(ctx)?.notify?.(message, level), "notify");
2354
+ const postureOpts = /* @__PURE__ */ __name(() => ({
2355
+ apiKey: currentProviderId === "nearai" ? nearApiKey() : void 0,
2356
+ zdrEnforced: currentProviderId === "openrouter" && enforceOpenRouterZdr,
2357
+ transport: useDispatcherTransport && installDispatcher ? dispatcherTransport : void 0
2358
+ }), "postureOpts");
2359
+ const paintBadge = /* @__PURE__ */ __name(() => {
2360
+ if (!showBadge || !lastUi) return;
2361
+ const badge = postureBadge(currentTier);
2362
+ if (badge === lastBadge) return;
2363
+ let rendered = false;
2364
+ if (renderBadge) renderBadge(badge, currentTier, lastCtx), rendered = true;
2365
+ else {
2366
+ for (const sink of badgeSinks) {
2367
+ if (renderBadgeTo(lastUi, sink, badgeKey, badge, currentTier)) {
2368
+ rendered = true;
2369
+ break;
2370
+ }
2371
+ }
2372
+ }
2373
+ if (rendered) lastBadge = badge;
2374
+ }, "paintBadge");
2375
+ const refreshPosture = /* @__PURE__ */ __name(async () => {
2376
+ if (!currentProviderId || !currentModelId) return;
2377
+ if (resolveTier) {
2378
+ const t = await resolveTier(currentProviderId, currentModelId);
2379
+ if (t !== void 0) {
2380
+ currentTier = t;
2381
+ onPosture?.({ providerId: currentProviderId, modelId: currentModelId, tier: t });
2382
+ paintBadge();
2383
+ void guardDowngrade(t);
2384
+ return;
2385
+ }
2386
+ }
2387
+ const result = await verifyModelPosture(currentProviderId, currentModelId, postureOpts());
2388
+ currentTier = result.tier;
2389
+ onPosture?.(result);
2390
+ paintBadge();
2391
+ void guardDowngrade(result.tier);
2392
+ }, "refreshPosture");
2393
+ const guardDowngrade = /* @__PURE__ */ __name(async (toTier, ctx) => {
2394
+ if (downgradePolicy === "off" || downgradeHandled) return;
2395
+ const a = assessDowngrade(previousTier, toTier, contextHits);
2396
+ if (!a.downgrade || a.severity === "none") return;
2397
+ if (piiBypassed() && a.severity !== "secret") return;
2398
+ downgradeHandled = true;
2399
+ const label = currentProviderId ? `${currentProviderId}${currentModelId ? `/${currentModelId}` : ""}` : void 0;
2400
+ const warning = downgradeWarning(a, contextHits, label);
2401
+ const revert = /* @__PURE__ */ __name(async () => {
2402
+ if (previousModel === void 0 || typeof pi.setModel !== "function") {
2403
+ notify(ctx, `${warning} Could not revert the switch automatically.`, "warning");
2404
+ return;
2405
+ }
2406
+ await pi.setModel(previousModel);
2407
+ notify(ctx, `Reverted to ${TIERS[a.from].label} \u2014 session context stays put.`, "info");
2408
+ }, "revert");
2409
+ if (downgradePolicy === "block") return revert();
2410
+ if (canPrompt(ctx)) {
2411
+ const choice = await ask(ctx, warning, [
2412
+ "Stay on the previous model",
2413
+ "Switch anyway",
2414
+ "Switch, redacting PII from now on"
2415
+ ]);
2416
+ if (choice === "Switch anyway") return;
2417
+ if (choice === "Switch, redacting PII from now on") {
2418
+ piiChoice = "redact";
2419
+ return;
2420
+ }
2421
+ return revert();
2422
+ }
2423
+ if (a.severity === "secret") return revert();
2424
+ notify(ctx, warning, "warning");
2425
+ }, "guardDowngrade");
2426
+ pi.on("session_start", (_event, ctx) => {
2427
+ captureUi(ctx);
2428
+ captureTools(ctx);
2429
+ });
2430
+ pi.on("model_select", (event, ctx) => {
2431
+ const model = event?.model;
2432
+ previousTier = currentTier;
2433
+ previousModel = event?.previousModel;
2434
+ downgradeHandled = false;
2435
+ if (model?.provider !== currentProviderId) {
2436
+ piiSeen = /* @__PURE__ */ new Map();
2437
+ piiLastAction = void 0;
2438
+ }
2439
+ currentProviderId = model?.provider;
2440
+ currentModelId = model?.id;
2441
+ captureUi(ctx);
2442
+ currentTier = void 0;
2443
+ paintBadge();
2444
+ void guardDowngrade(
2445
+ effectiveTier(currentProviderId ?? "", {
2446
+ zdrEnforced: currentProviderId === "openrouter" && enforceOpenRouterZdr
2447
+ }),
2448
+ ctx
2449
+ );
2450
+ void refreshPosture();
2451
+ });
2452
+ pi.on("before_provider_request", async (event, ctx) => {
2453
+ captureUi(ctx);
2454
+ paintBadge();
2455
+ let payload = event?.payload;
2456
+ if (currentProviderId === "venice") payload = veniceRequestPatch(payload);
2457
+ else if (currentProviderId === "openrouter" && enforceOpenRouterZdr) payload = openRouterZdrPatch(payload);
2458
+ const scan = scanPii(payloadText(payload), scanOpts);
2459
+ const hits = scan.hits;
2460
+ contextHits = hits;
2461
+ if (!piiBypassed() && !isVerifiedPrivate(currentTier)) {
2462
+ if (hits.length > 0) {
2463
+ let action = piiChoice !== "ask" ? piiChoice : piiMode() === "redact" ? "redact" : "send";
2464
+ const fresh = newPii(hits, piiSeen);
2465
+ const decided = piiChoice !== "ask" ? piiChoice : piiLastAction;
2466
+ if (fresh.length === 0 && decided) action = decided;
2467
+ if (fresh.length > 0 && piiChoice === "ask" && piiMode() === "warn" && piiUnattendedNow()) {
2468
+ action = "redact";
2469
+ const notice = `\u2691 unattended \u2014 PII auto-redacted before send: ${piiInline(scan)} \u2192 ${TIERS[currentTier ?? "standard"].label} channel. Best-effort structured detection only, not a guarantee.`;
2470
+ const rendered = renderPiiAutoRedact2?.(notice);
2471
+ notify(ctx, rendered ?? notice, rendered ? "info" : "warning");
2472
+ } else if (fresh.length > 0 && piiChoice === "ask" && piiMode() === "warn" && canPrompt(ctx)) {
2473
+ const tierLabel = TIERS[currentTier ?? "standard"].label;
2474
+ const kind = hasSecrets(hits) ? "secrets/PII" : "structured PII";
2475
+ const again = piiLastAction ? ` (${summarizePii(fresh)} new since your last answer)` : "";
2476
+ const head = `\u26A0 ${summarizePii(hits)} detected${again} \u2014 sending to an unverified channel (${tierLabel}). Best-effort ${kind} detection only, not a guarantee.`;
2477
+ let title = head;
2478
+ for (let round = 0; round < 2; round++) {
2479
+ const options = [
2480
+ "Send as-is",
2481
+ "Redact PII",
2482
+ ...round === 0 ? ["Show what was detected"] : [],
2483
+ "Redact + remember for session",
2484
+ "Send + remember for session"
2485
+ ];
2486
+ const choice = await ask(ctx, title, options);
2487
+ if (choice === "Show what was detected") {
2488
+ title = `${head}
2489
+
2490
+ ${piiDetail(scan)}`;
2491
+ continue;
2492
+ }
2493
+ if (choice === "Redact PII") action = "redact";
2494
+ else if (choice === "Redact + remember for session") action = "redact", piiChoice = "redact";
2495
+ else if (choice === "Send + remember for session") action = "send", piiChoice = "send";
2496
+ else action = "send";
2497
+ break;
2498
+ }
2499
+ }
2500
+ piiSeen = mergePiiBaseline(piiSeen, hits);
2501
+ piiLastAction = action;
2502
+ if (action === "redact") payload = redactPayloadPii(payload, piiAllowed);
2503
+ }
2504
+ }
2505
+ return payload === event?.payload ? void 0 : payload;
2506
+ });
2507
+ const egressVerdict = /* @__PURE__ */ __name((assessment, label, input) => {
2508
+ if (!assessment.egress) return void 0;
2509
+ const hits = detectPii(payloadText(input), scanOpts);
2510
+ const files = assessment.sensitiveFiles ?? [];
2511
+ if (hits.length === 0 && files.length === 0) return void 0;
2512
+ const secret = hasSecrets(hits) || files.length > 0;
2513
+ const what = [files.length ? `${files.join(", ")} contents` : "", hits.length ? summarizePii(hits) : ""].filter(Boolean).join(" + ");
2514
+ const dest = assessment.target ? ` \u2192 ${assessment.target}` : "";
2515
+ const reason = `pi-privacy blocked ${secret ? "credential" : "PII"} exfiltration via ${label}`;
2516
+ const warning = `\u26A0 ${label} is about to send ${what} off this machine${dest}. A private (TEE/ZDR) model does NOT protect a tool call. Best-effort detection, not a guarantee.`;
2517
+ return { warning, reason, secret };
2518
+ }, "egressVerdict");
2519
+ const egressGated = /* @__PURE__ */ __name((v) => v.secret || !piiBypassed(), "egressGated");
2520
+ const decideEgress = /* @__PURE__ */ __name(async (v, ctx) => {
2521
+ if (toolAllow) {
2522
+ notify(ctx, v.warning, "warning");
2523
+ return "allow";
2524
+ }
2525
+ if (toolExfilPolicy === "block") return "block";
2526
+ if (canPrompt(ctx)) {
2527
+ const choice = await ask(ctx, v.warning, ["Block", "Allow once", "Allow for session"]);
2528
+ if (choice === "Allow for session") {
2529
+ toolAllow = true;
2530
+ return "allow";
2531
+ }
2532
+ if (choice === "Allow once") return "allow";
2533
+ return "block";
2534
+ }
2535
+ if (v.secret) return "block";
2536
+ notify(ctx, v.warning, "warning");
2537
+ return "allow";
2538
+ }, "decideEgress");
2539
+ pi.on("tool_call", async (event, ctx) => {
2540
+ if (toolExfilPolicy === "off" && !surfaceOn) return;
2541
+ captureUi(ctx);
2542
+ const name = event?.toolName ?? "a tool";
2543
+ if (await guardProvenance(name, ctx) === "block")
2544
+ return {
2545
+ block: true,
2546
+ reason: `pi-privacy blocked ${name}: supplied by this project, not by you`
2547
+ };
2548
+ const assessment = assessToolCall(event?.toolName, event?.input);
2549
+ const v = egressVerdict(assessment, name, event?.input);
2550
+ let blocked = false;
2551
+ if (toolExfilPolicy !== "off" && v && egressGated(v)) blocked = await decideEgress(v, ctx) === "block";
2552
+ if (surfaceOn) recordEgress(ledger, name, assessment, { pii: !!v, blocked });
2553
+ if (blocked) return { block: true, reason: v.reason };
2554
+ return;
2555
+ });
2556
+ pi.on("user_bash", async (event, ctx) => {
2557
+ if (toolExfilPolicy === "off" && !surfaceOn) return;
2558
+ captureUi(ctx);
2559
+ const command = typeof event?.command === "string" ? event.command : "";
2560
+ if (!command) return;
2561
+ const assessment = assessToolCall("bash", { command });
2562
+ const v = egressVerdict(assessment, "this ! command", { command });
2563
+ let blocked = false;
2564
+ if (toolExfilPolicy !== "off" && v && egressGated(v)) blocked = await decideEgress(v, ctx) === "block";
2565
+ if (surfaceOn) recordEgress(ledger, "! command", assessment, { pii: !!v, blocked });
2566
+ if (blocked) {
2567
+ return {
2568
+ result: {
2569
+ output: `${v.reason}. Set PI_PRIVACY_TOOL_EXFIL_POLICY=off (or allow it when prompted) if this is intended.`,
2570
+ exitCode: 1,
2571
+ cancelled: false,
2572
+ truncated: false
2573
+ }
2574
+ };
2575
+ }
2576
+ return;
2577
+ });
2578
+ pi.on("tool_result", async (event, ctx) => {
2579
+ if (toolResultPolicy === "off") return;
2580
+ captureUi(ctx);
2581
+ const content = event?.content;
2582
+ const hits = secretHits(detectPii(toolResultText(content), scanOpts));
2583
+ if (hits.length === 0) return;
2584
+ const summary = summarizePii(hits);
2585
+ const warning = `\u26A0 ${event?.toolName ?? "a tool"} returned ${summary}. Keeping ${hits.length === 1 && hits[0].count === 1 ? "it" : "them"} in context means re-sending to the provider on every later turn, and writing to the session file on disk in plaintext. Best-effort secret detection, not a guarantee.`;
2586
+ let action = resultChoice !== "ask" ? resultChoice : toolResultPolicy === "redact" ? "redact" : "keep";
2587
+ if (resultChoice === "ask" && toolResultPolicy === "warn") {
2588
+ if (canPrompt(ctx)) {
2589
+ const choice = await ask(ctx, warning, [
2590
+ "Redact the credentials",
2591
+ "Keep them in context",
2592
+ "Redact for the rest of the session",
2593
+ "Keep for the rest of the session"
2594
+ ]);
2595
+ if (choice === "Redact the credentials") action = "redact";
2596
+ else if (choice === "Redact for the rest of the session") action = "redact", resultChoice = "redact";
2597
+ else if (choice === "Keep for the rest of the session") action = "keep", resultChoice = "keep";
2598
+ else action = "keep";
2599
+ } else {
2600
+ action = "redact";
2601
+ notify(ctx, `${warning} Redacted (no UI to ask).`, "warning");
2602
+ }
2603
+ }
2604
+ if (action !== "redact") return;
2605
+ const redacted = redactToolResultContent(content, void 0, piiAllowed);
2606
+ if (redacted === void 0) {
2607
+ notify(
2608
+ ctx,
2609
+ `Could not redact ${summary} from ${event?.toolName ?? "the tool"} result \u2014 unrecognized result shape. It stays in context.`,
2610
+ "warning"
2611
+ );
2612
+ return;
2613
+ }
2614
+ return { content: redacted };
2615
+ });
2616
+ const surfaceSection = /* @__PURE__ */ __name((ctx, full) => {
2617
+ captureTools(ctx);
2618
+ const lines = [];
2619
+ const entries = rankSurface(toolSnapshot);
2620
+ if (entries.length === 0) {
2621
+ if (full) lines.push("Tools inventory unavailable \u2014 this host did not expose the tool list.");
2622
+ } else {
2623
+ const report = surfaceReport(entries, !full);
2624
+ lines.push(`Tools ${report[0]}`);
2625
+ for (const l of report.slice(1)) lines.push(` ${l}`);
2626
+ if (summarizeSurface(entries).notYours > 0)
2627
+ lines.push(
2628
+ " \u26A0 marks a tool that came with this working directory or a CLI flag, not from your configuration. That is where it came FROM \u2014 pi-privacy does not judge what it does."
2629
+ );
2630
+ }
2631
+ const obs = ledgerReport(ledger);
2632
+ if (obs.length) {
2633
+ lines.push(`Observed ${obs[0]}`);
2634
+ for (const l of obs.slice(1)) lines.push(` ${l}`);
2635
+ lines.push(
2636
+ " (observed via tool calls only \u2014 an extension's own fetch() never appears here, so this is a floor)"
2637
+ );
2638
+ }
2639
+ return lines.join("\n");
2640
+ }, "surfaceSection");
2641
+ if (typeof pi.registerCommand === "function") {
2642
+ pi.registerCommand("verify", {
2643
+ description: "Verify the current model's privacy posture (TEE attestation)",
2644
+ handler: /* @__PURE__ */ __name(async (_args, ctx) => {
2645
+ if (!currentProviderId || !currentModelId) {
2646
+ ctx.ui?.notify?.("No model selected.", "warning");
2647
+ return;
2648
+ }
2649
+ const res = await verifyModelPosture(currentProviderId, currentModelId, postureOpts());
2650
+ const info = TIERS[res.tier];
2651
+ const detail = res.teePosture ? ` [${res.teePosture}]` : "";
2652
+ const err = res.error ? ` \u2014 ${res.error}` : "";
2653
+ ctx.ui?.notify?.(`${info.label}${detail}: ${info.blurb}${err}`, "info");
2654
+ if (res.attestation !== void 0) {
2655
+ let report;
2656
+ try {
2657
+ report = JSON.stringify(res.attestation, null, 2);
2658
+ } catch {
2659
+ report = String(res.attestation);
2660
+ }
2661
+ ctx.ui?.notify?.(`attestation report (verify independently):
2662
+ ${report}`, "info");
2663
+ }
2664
+ if (surfaceOn) {
2665
+ const section = surfaceSection(ctx, false);
2666
+ if (section) ctx.ui?.notify?.(section, "info");
2667
+ }
2668
+ }, "handler")
2669
+ });
2670
+ if (piiCommand) {
2671
+ pi.registerCommand(piiCommandName, {
2672
+ description: "Set or bypass the PII gate for this session (off | warn | redact | on | status)",
2673
+ handler: /* @__PURE__ */ __name((args, ctx) => {
2674
+ const out = runPiiCommand(args, {
2675
+ configured: piiPolicy,
2676
+ session: piiSession,
2677
+ toolExfilPolicy,
2678
+ toolResultPolicy,
2679
+ downgradePolicy,
2680
+ commandName: piiCommandName
2681
+ });
2682
+ piiSession = out.session;
2683
+ if (out.changed) {
2684
+ piiChoice = "ask";
2685
+ piiSeen = /* @__PURE__ */ new Map();
2686
+ piiLastAction = void 0;
2687
+ }
2688
+ ctx.ui?.notify?.(out.message, out.level);
2689
+ }, "handler"),
2690
+ // Hint the words rather than making the user find them in a README.
2691
+ getArgumentCompletions: /* @__PURE__ */ __name((prefix) => {
2692
+ const p = prefix.trim().toLowerCase();
2693
+ return PII_COMMAND_WORDS.filter((w) => w.startsWith(p)).map((w) => ({
2694
+ value: w,
2695
+ label: w,
2696
+ description: PII_WORD_HELP[w]
2697
+ }));
2698
+ }, "getArgumentCompletions")
2699
+ });
2700
+ }
2701
+ if (surfaceOn) {
2702
+ pi.registerCommand(toolSurfaceCommand, {
2703
+ description: "List the session's tools by who supplied them, plus observed egress",
2704
+ handler: /* @__PURE__ */ __name((_args, ctx) => {
2705
+ ctx.ui?.notify?.(surfaceSection(ctx, true), "info");
2706
+ }, "handler")
2707
+ });
2708
+ }
2709
+ if (modelPicker) {
2710
+ pi.registerCommand(modelPickerCommand, {
2711
+ description: "Pick a model ranked by privacy (verified TEE / on-device / ZDR first)",
2712
+ handler: /* @__PURE__ */ __name(async (_args, ctx) => {
2713
+ const reg = ctx.modelRegistry;
2714
+ const models = reg?.getAvailable?.() ?? reg?.getAll?.() ?? [];
2715
+ if (models.length === 0) {
2716
+ ctx.ui?.notify?.(
2717
+ "No models to rank (none with configured auth were found).",
2718
+ "warning"
2719
+ );
2720
+ return;
2721
+ }
2722
+ const ranked = rankModels(models, {
2723
+ zdrEnforced: enforceOpenRouterZdr,
2724
+ verifiedTee: privateerVerifiedTee
2725
+ });
2726
+ const cur = ctx.getModel?.() ?? { provider: currentProviderId, id: currentModelId };
2727
+ const isCurrent = /* @__PURE__ */ __name((m) => m.provider === cur.provider && m.id === cur.id, "isCurrent");
2728
+ const byLabel = /* @__PURE__ */ new Map();
2729
+ const options = ranked.map((e) => {
2730
+ const label = pickerOptionLabel(e, isCurrent(e.model));
2731
+ byLabel.set(label, e.model);
2732
+ return label;
2733
+ });
2734
+ if (!ctx.hasUI || typeof ctx.ui?.select !== "function") {
2735
+ ctx.ui?.notify?.(
2736
+ `Models by privacy (strongest first):
2737
+ ${options.join("\n")}
2738
+ \u25C6 = TEE that verifies when selected. Switch with /model.`,
2739
+ "info"
2740
+ );
2741
+ return;
2742
+ }
2743
+ const choice = await ctx.ui.select(
2744
+ "Pick a model (strongest privacy first \u2014 \u25C6 verifies on select):",
2745
+ options
2746
+ );
2747
+ if (!choice) return;
2748
+ const model = byLabel.get(choice);
2749
+ if (!model) return;
2750
+ if (isCurrent(model)) {
2751
+ ctx.ui?.notify?.("Already on that model.", "info");
2752
+ return;
2753
+ }
2754
+ if (typeof pi.setModel !== "function") {
2755
+ ctx.ui?.notify?.("This host can't switch models programmatically.", "warning");
2756
+ return;
2757
+ }
2758
+ const ok = await pi.setModel(model);
2759
+ if (!ok)
2760
+ ctx.ui?.notify?.(
2761
+ `Could not switch to ${model.provider}/${model.id} \u2014 no API key configured for it.`,
2762
+ "warning"
2763
+ );
2764
+ }, "handler")
2765
+ });
2766
+ }
2767
+ }
2768
+ }, "piPrivacy");
2769
+ }
2770
+ __name(makePiPrivacyExtension, "makePiPrivacyExtension");
2771
+ var extension_default = makePiPrivacyExtension();
2772
+
2773
+ // node_modules/pi-privacy/src/options.ts?ext=privateer-privacy
2774
+ var CODE_ONLY_OPTIONS = [
2775
+ "onPosture",
2776
+ "resolveTier",
2777
+ "renderBadge",
2778
+ "privateerVerifiedTee",
2779
+ "piiUnattended",
2780
+ "renderPiiAutoRedact"
2781
+ ];
2782
+
2783
+ // node_modules/pi-privacy/src/config.ts?ext=privateer-privacy
2784
+ import { readFileSync as readFileSync6 } from "node:fs";
2785
+ var POLICY3 = ["warn", "redact", "off"];
2786
+ var TOOL_POLICY = ["warn", "block", "off"];
2787
+ var DOWNGRADE_POLICY = ["warn", "block", "off"];
2788
+ var SURFACE_POLICY = ["warn", "report", "off"];
2789
+ var SINKS = ["status", "widget", "title", "notify"];
2790
+ function parseBool(name, raw, warn) {
2791
+ const v = raw.trim().toLowerCase();
2792
+ if (["1", "true", "yes", "on"].includes(v)) return true;
2793
+ if (["0", "false", "no", "off"].includes(v)) return false;
2794
+ warn(`${name}="${raw}" is not a boolean (true/false) \u2014 ignoring, using the default.`);
2795
+ return void 0;
2796
+ }
2797
+ __name(parseBool, "parseBool");
2798
+ function parseEnum(name, raw, allowed, warn) {
2799
+ const v = raw.trim().toLowerCase();
2800
+ if (allowed.includes(v)) return v;
2801
+ warn(`${name}="${raw}" is not one of ${allowed.join("|")} \u2014 ignoring, using the default.`);
2802
+ return void 0;
2803
+ }
2804
+ __name(parseEnum, "parseEnum");
2805
+ function parseSinks(name, raw, warn) {
2806
+ const parts = raw.split(/[,\s]+/).map((s) => s.trim().toLowerCase()).filter(Boolean);
2807
+ const out = [];
2808
+ for (const p of parts) {
2809
+ if (SINKS.includes(p)) out.push(p);
2810
+ else warn(`${name}: "${p}" is not a badge sink (${SINKS.join("|")}) \u2014 dropping it.`);
2811
+ }
2812
+ return out.length ? out : (warn(`${name}="${raw}" has no valid sinks \u2014 using the default chain.`), void 0);
2813
+ }
2814
+ __name(parseSinks, "parseSinks");
2815
+ function boolVar(opts, env, name, key, warn) {
2816
+ const raw = env[name];
2817
+ if (raw === void 0 || raw === "") return;
2818
+ const b = parseBool(name, raw, warn);
2819
+ if (b !== void 0) opts[key] = b;
2820
+ }
2821
+ __name(boolVar, "boolVar");
2822
+ function optionsFromEnv(env, warn) {
2823
+ const opts = {};
2824
+ const pii = env.PI_PRIVACY_PII_POLICY;
2825
+ if (pii) {
2826
+ const v = parseEnum("PI_PRIVACY_PII_POLICY", pii, POLICY3, warn);
2827
+ if (v) opts.piiPolicy = v;
2828
+ }
2829
+ const tool = env.PI_PRIVACY_TOOL_EXFIL_POLICY;
2830
+ if (tool) {
2831
+ const v = parseEnum("PI_PRIVACY_TOOL_EXFIL_POLICY", tool, TOOL_POLICY, warn);
2832
+ if (v) opts.toolExfilPolicy = v;
2833
+ }
2834
+ const result = env.PI_PRIVACY_TOOL_RESULT_POLICY;
2835
+ if (result) {
2836
+ const v = parseEnum("PI_PRIVACY_TOOL_RESULT_POLICY", result, POLICY3, warn);
2837
+ if (v) opts.toolResultPolicy = v;
2838
+ }
2839
+ const down = env.PI_PRIVACY_DOWNGRADE_POLICY;
2840
+ if (down) {
2841
+ const v = parseEnum("PI_PRIVACY_DOWNGRADE_POLICY", down, DOWNGRADE_POLICY, warn);
2842
+ if (v) opts.downgradePolicy = v;
2843
+ }
2844
+ const surface = env.PI_PRIVACY_TOOL_SURFACE_POLICY;
2845
+ if (surface) {
2846
+ const v = parseEnum("PI_PRIVACY_TOOL_SURFACE_POLICY", surface, SURFACE_POLICY, warn);
2847
+ if (v) opts.toolSurfacePolicy = v;
2848
+ }
2849
+ boolVar(opts, env, "PI_PRIVACY_ENFORCE_OPENROUTER_ZDR", "enforceOpenRouterZdr", warn);
2850
+ boolVar(opts, env, "PI_PRIVACY_SHOW_BADGE", "showBadge", warn);
2851
+ boolVar(opts, env, "PI_PRIVACY_INSTALL_DISPATCHER", "installDispatcher", warn);
2852
+ boolVar(opts, env, "PI_PRIVACY_REGISTER_PROVIDERS", "registerProviders", warn);
2853
+ boolVar(opts, env, "PI_PRIVACY_USE_DISPATCHER_TRANSPORT", "useDispatcherTransport", warn);
2854
+ boolVar(opts, env, "PI_PRIVACY_MODEL_PICKER", "modelPicker", warn);
2855
+ boolVar(opts, env, "PI_PRIVACY_PII_COMMAND", "piiCommand", warn);
2856
+ const sinks = env.PI_PRIVACY_BADGE_SINKS;
2857
+ if (sinks) {
2858
+ const v = parseSinks("PI_PRIVACY_BADGE_SINKS", sinks, warn);
2859
+ if (v) opts.badgeSinks = v;
2860
+ }
2861
+ const allow = env.PI_PRIVACY_PII_ALLOW;
2862
+ if (allow) {
2863
+ const parts = allow.split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
2864
+ if (parts.length) opts.piiAllow = parts;
2865
+ else warn(`PI_PRIVACY_PII_ALLOW="${allow}" has no entries \u2014 ignoring it.`);
2866
+ }
2867
+ boolVar(opts, env, "PI_PRIVACY_PII_ALLOW_DEFAULTS", "piiAllowDefaults", warn);
2868
+ const key = env.PI_PRIVACY_BADGE_KEY;
2869
+ if (key && key.trim()) opts.badgeKey = key.trim();
2870
+ const cmd = env.PI_PRIVACY_MODEL_PICKER_COMMAND;
2871
+ if (cmd && cmd.trim()) opts.modelPickerCommand = cmd.trim();
2872
+ const scmd = env.PI_PRIVACY_TOOL_SURFACE_COMMAND;
2873
+ if (scmd && scmd.trim()) opts.toolSurfaceCommand = scmd.trim();
2874
+ const pcmd = env.PI_PRIVACY_PII_COMMAND_NAME;
2875
+ if (pcmd && pcmd.trim()) opts.piiCommandName = pcmd.trim();
2876
+ return opts;
2877
+ }
2878
+ __name(optionsFromEnv, "optionsFromEnv");
2879
+ function sanitizeConfig(raw, warn) {
2880
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
2881
+ warn(`config file is not a JSON object \u2014 ignoring it.`);
2882
+ return {};
2883
+ }
2884
+ const src = raw;
2885
+ const opts = {};
2886
+ const enumKey = /* @__PURE__ */ __name((k, allowed, set) => {
2887
+ if (!(k in src)) return;
2888
+ const val = src[k];
2889
+ if (typeof val === "string" && allowed.includes(val.toLowerCase()))
2890
+ set(val.toLowerCase());
2891
+ else warn(`config.${k}=${JSON.stringify(val)} is not one of ${allowed.join("|")} \u2014 ignoring.`);
2892
+ }, "enumKey");
2893
+ const boolKey = /* @__PURE__ */ __name((k) => {
2894
+ if (!(k in src)) return;
2895
+ const val = src[k];
2896
+ if (typeof val === "boolean") opts[k] = val;
2897
+ else warn(`config.${k}=${JSON.stringify(val)} is not a boolean \u2014 ignoring.`);
2898
+ }, "boolKey");
2899
+ enumKey("piiPolicy", POLICY3, (v) => opts.piiPolicy = v);
2900
+ enumKey("toolExfilPolicy", TOOL_POLICY, (v) => opts.toolExfilPolicy = v);
2901
+ enumKey("toolResultPolicy", POLICY3, (v) => opts.toolResultPolicy = v);
2902
+ enumKey("downgradePolicy", DOWNGRADE_POLICY, (v) => opts.downgradePolicy = v);
2903
+ enumKey("toolSurfacePolicy", SURFACE_POLICY, (v) => opts.toolSurfacePolicy = v);
2904
+ boolKey("enforceOpenRouterZdr");
2905
+ boolKey("showBadge");
2906
+ boolKey("installDispatcher");
2907
+ boolKey("registerProviders");
2908
+ boolKey("useDispatcherTransport");
2909
+ boolKey("modelPicker");
2910
+ boolKey("piiAllowDefaults");
2911
+ boolKey("piiCommand");
2912
+ if ("piiAllow" in src) {
2913
+ const val = src.piiAllow;
2914
+ if (Array.isArray(val)) {
2915
+ const out = val.filter((s) => typeof s === "string" && s.trim().length > 0).map((s) => s.trim());
2916
+ if (out.length !== val.length) warn(`config.piiAllow has non-string/empty entries \u2014 dropping them.`);
2917
+ if (out.length) opts.piiAllow = out;
2918
+ } else warn(`config.piiAllow is not an array of strings \u2014 ignoring.`);
2919
+ }
2920
+ if ("badgeSinks" in src) {
2921
+ const val = src.badgeSinks;
2922
+ if (Array.isArray(val)) {
2923
+ const out = val.filter(
2924
+ (s) => typeof s === "string" && SINKS.includes(s)
2925
+ );
2926
+ if (out.length) opts.badgeSinks = out;
2927
+ else warn(`config.badgeSinks has no valid sinks (${SINKS.join("|")}) \u2014 using the default chain.`);
2928
+ } else warn(`config.badgeSinks is not an array \u2014 ignoring.`);
2929
+ }
2930
+ if ("badgeKey" in src) {
2931
+ const val = src.badgeKey;
2932
+ if (typeof val === "string" && val.trim()) opts.badgeKey = val.trim();
2933
+ else warn(`config.badgeKey=${JSON.stringify(val)} is not a non-empty string \u2014 ignoring.`);
2934
+ }
2935
+ if ("modelPickerCommand" in src) {
2936
+ const val = src.modelPickerCommand;
2937
+ if (typeof val === "string" && val.trim()) opts.modelPickerCommand = val.trim();
2938
+ else warn(`config.modelPickerCommand=${JSON.stringify(val)} is not a non-empty string \u2014 ignoring.`);
2939
+ }
2940
+ if ("toolSurfaceCommand" in src) {
2941
+ const val = src.toolSurfaceCommand;
2942
+ if (typeof val === "string" && val.trim()) opts.toolSurfaceCommand = val.trim();
2943
+ else warn(`config.toolSurfaceCommand=${JSON.stringify(val)} is not a non-empty string \u2014 ignoring.`);
2944
+ }
2945
+ if ("piiCommandName" in src) {
2946
+ const val = src.piiCommandName;
2947
+ if (typeof val === "string" && val.trim()) opts.piiCommandName = val.trim();
2948
+ else warn(`config.piiCommandName=${JSON.stringify(val)} is not a non-empty string \u2014 ignoring.`);
2949
+ }
2950
+ for (const k of Object.keys(src)) {
2951
+ if (CODE_ONLY_OPTIONS.includes(k))
2952
+ warn(`config.${k} is a code-only option and can't be set from JSON \u2014 import makePiPrivacyExtension() to use it.`);
2953
+ }
2954
+ return opts;
2955
+ }
2956
+ __name(sanitizeConfig, "sanitizeConfig");
2957
+ var PROTECTIVENESS = {
2958
+ piiPolicy: { off: 0, warn: 1, redact: 2 },
2959
+ toolExfilPolicy: { off: 0, warn: 1, block: 2 },
2960
+ toolResultPolicy: { off: 0, warn: 1, redact: 2 },
2961
+ downgradePolicy: { off: 0, warn: 1, block: 2 },
2962
+ // "report" keeps the inventory but stops warning; "off" removes the axis entirely.
2963
+ // Both are weaker than the default, and this is the sharpest case the floor exists
2964
+ // for: the tool-surface axis is what reports tools THE PROJECT SUPPLIED, so a
2965
+ // project-local config turning it down is not a hypothetical attack — it is the
2966
+ // whole attack, and it would leave no trace by construction.
2967
+ toolSurfacePolicy: { off: 0, report: 1, warn: 2 }
2968
+ };
2969
+ var DEFAULT_POLICY = {
2970
+ piiPolicy: "warn",
2971
+ toolExfilPolicy: "warn",
2972
+ toolResultPolicy: "warn",
2973
+ downgradePolicy: "warn",
2974
+ toolSurfacePolicy: "warn"
2975
+ };
2976
+ var PROTECTIVE_WHEN_TRUE = [
2977
+ "showBadge",
2978
+ // hides the live posture badge
2979
+ "installDispatcher",
2980
+ // no dispatcher → no TLS-key binding for Tinfoil attestation
2981
+ "useDispatcherTransport",
2982
+ // attestation stops binding to the real inference connection
2983
+ "registerProviders",
2984
+ // removes the private providers from the model list
2985
+ "modelPicker"
2986
+ // removes the privacy-ranked picker
2987
+ ];
2988
+ var PROJECT_MAY_NOT_SET = {
2989
+ // The sharpest of the lot: `{"piiAllow": ["*@*", "@acme.com"]}` in a cloned repo is
2990
+ // piiPolicy:"off" for exactly the data that repo cares about, and it would read as a
2991
+ // gate that simply found nothing. An allowlist only ever REMOVES detection, so it
2992
+ // can only come from you.
2993
+ piiAllow: "an allowlist entry can hide PII from the gate entirely",
2994
+ badgeSinks: "a sink list can hide the posture badge",
2995
+ toolSurfaceCommand: "renaming the command can hide the tool-surface listing",
2996
+ modelPickerCommand: "renaming the command can hide the privacy-ranked model picker",
2997
+ // Not because hiding /pii would weaken anything — it wouldn't — but because a name
2998
+ // is a way to SHADOW: `{"piiCommandName": "surface"}` registers the PII lever over
2999
+ // the tool-surface listing, and the listing is exactly what would have named the
3000
+ // repo's own tools.
3001
+ piiCommandName: "renaming the command can shadow another command's name"
3002
+ };
3003
+ function clampProjectConfig(opts, warn) {
3004
+ const src = opts;
3005
+ const out = {};
3006
+ const note = /* @__PURE__ */ __name((key, val, floor) => warn(
3007
+ `project-local pi-privacy.config.json sets ${key}=${JSON.stringify(val)}, which is WEAKER than the built-in default (${floor}) \u2014 ignoring it. A project you open must not be able to turn off your privacy guards. If you meant it, set the PI_PRIVACY_* env var, or point PI_PRIVACY_CONFIG at this file.`
3008
+ ), "note");
3009
+ for (const [key, val] of Object.entries(src)) {
3010
+ const ranks = PROTECTIVENESS[key];
3011
+ if (ranks) {
3012
+ const floor = DEFAULT_POLICY[key];
3013
+ if ((ranks[String(val)] ?? 0) < ranks[floor]) {
3014
+ note(key, val, `"${floor}"`);
3015
+ continue;
3016
+ }
3017
+ } else if (PROTECTIVE_WHEN_TRUE.includes(key) && val === false) {
3018
+ note(key, val, "true");
3019
+ continue;
3020
+ } else if (key in PROJECT_MAY_NOT_SET) {
3021
+ warn(
3022
+ `project-local pi-privacy.config.json sets ${key} \u2014 ignoring it, since ${PROJECT_MAY_NOT_SET[key]}. Set the PI_PRIVACY_* env var, or point PI_PRIVACY_CONFIG at this file, if you meant it.`
3023
+ );
3024
+ continue;
3025
+ }
3026
+ out[key] = val;
3027
+ }
3028
+ return out;
3029
+ }
3030
+ __name(clampProjectConfig, "clampProjectConfig");
3031
+ function loadConfig(deps = {}) {
3032
+ const env = deps.env ?? process.env;
3033
+ const warn = deps.warn ?? ((m) => console.warn(`[pi-privacy] ${m}`));
3034
+ const fromFile = loadFileConfig(env, deps, warn);
3035
+ const fromEnv = optionsFromEnv(env, warn);
3036
+ return { ...fromFile, ...fromEnv };
3037
+ }
3038
+ __name(loadConfig, "loadConfig");
3039
+ function loadFileConfig(env, deps, warn) {
3040
+ const explicit = env.PI_PRIVACY_CONFIG?.trim();
3041
+ const cwd = deps.cwd ?? process.cwd();
3042
+ const path = explicit && explicit.length ? explicit : `${cwd}/pi-privacy.config.json`;
3043
+ let text;
3044
+ try {
3045
+ const read = deps.readFile ?? ((p) => readFileSync6(p, "utf8"));
3046
+ text = read(path);
3047
+ } catch (e) {
3048
+ if (explicit) warn(`config file "${path}" could not be read: ${e.message}`);
3049
+ return {};
3050
+ }
3051
+ let parsed;
3052
+ try {
3053
+ parsed = JSON.parse(text);
3054
+ } catch (e) {
3055
+ warn(`config file "${path}" is not valid JSON: ${e.message} \u2014 ignoring it.`);
3056
+ return {};
3057
+ }
3058
+ const opts = sanitizeConfig(parsed, warn);
3059
+ if (explicit || deps.projectTrusted) return opts;
3060
+ return clampProjectConfig(opts, warn);
3061
+ }
3062
+ __name(loadFileConfig, "loadFileConfig");
3063
+
3064
+ // src/providers/defaultModel.ts?ext=privateer-privacy
3065
+ import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
3066
+ import { join as join5 } from "node:path";
3067
+ var TINFOIL_MODEL_ID = "tinfoil/gemma4-31b";
3068
+ var TINFOIL_DEFAULT_SPEC = TINFOIL_MODEL_ID;
3069
+ var ACCOUNT_DEFAULT_MODEL_ID = TINFOIL_MODEL_ID;
3070
+ var ACCOUNT_DEFAULT_SPEC = `privateer/${ACCOUNT_DEFAULT_MODEL_ID}`;
3071
+ var ACCOUNT_NEAR_MODEL_ID = "near/zai-org/GLM-5.1-FP8";
3072
+ var LEGACY_BYO_FALLBACK = "openrouter/openai/gpt-4o-mini";
3073
+ var BYO_BY_KEY = [
3074
+ { env: "ANTHROPIC_API_KEY", spec: "anthropic/claude-opus-4-8" },
3075
+ { env: "OPENAI_API_KEY", spec: "openai/gpt-5.5" },
3076
+ { env: "OPENROUTER_API_KEY", spec: LEGACY_BYO_FALLBACK }
3077
+ ];
3078
+ function savedPiDefaultSpec() {
3079
+ try {
3080
+ const raw = readFileSync7(join5(agentDir(), "settings.json"), "utf8").trim();
3081
+ if (!raw) return null;
3082
+ const s = JSON.parse(raw);
3083
+ const provider = typeof s.defaultProvider === "string" ? s.defaultProvider.trim() : "";
3084
+ const modelId = typeof s.defaultModel === "string" ? s.defaultModel.trim() : "";
3085
+ return provider && modelId ? `${provider}/${modelId}` : null;
3086
+ } catch {
3087
+ return null;
3088
+ }
3089
+ }
3090
+ __name(savedPiDefaultSpec, "savedPiDefaultSpec");
3091
+ function resolveDefaultModel(opts = {}) {
3092
+ const env = opts.env ?? process.env;
3093
+ const explicit = opts.explicit?.trim();
3094
+ if (explicit) return explicit;
3095
+ const fromEnv = env.PRIVATEER_MODEL?.trim();
3096
+ if (fromEnv) return fromEnv;
3097
+ const saved = opts.saved === void 0 ? savedPiDefaultSpec() : opts.saved;
3098
+ if (saved) return saved;
3099
+ if (env.TINFOIL_API_KEY?.trim()) return TINFOIL_DEFAULT_SPEC;
3100
+ const signedIn = opts.signedIn ?? hasCredentials();
3101
+ if (signedIn) return ACCOUNT_DEFAULT_SPEC;
3102
+ for (const { env: keyName, spec } of BYO_BY_KEY) {
3103
+ if (env[keyName]?.trim()) return spec;
3104
+ }
3105
+ return ACCOUNT_DEFAULT_SPEC;
3106
+ }
3107
+ __name(resolveDefaultModel, "resolveDefaultModel");
3108
+ function resolveSignedInModel(env = process.env) {
3109
+ return resolveDefaultModel({ env, signedIn: true, saved: null });
3110
+ }
3111
+ __name(resolveSignedInModel, "resolveSignedInModel");
3112
+ function splitSpec(spec) {
3113
+ const slash = spec.indexOf("/");
3114
+ if (slash <= 0 || slash === spec.length - 1) return null;
3115
+ return { provider: spec.slice(0, slash), modelId: spec.slice(slash + 1) };
3116
+ }
3117
+ __name(splitSpec, "splitSpec");
3118
+ function ensurePiDefaultModel(spec = resolveSignedInModel()) {
3119
+ return writeSettingsDefaultModel(spec, true);
3120
+ }
3121
+ __name(ensurePiDefaultModel, "ensurePiDefaultModel");
3122
+ function writePiDefaultModel(spec) {
3123
+ return writeSettingsDefaultModel(spec, false);
3124
+ }
3125
+ __name(writePiDefaultModel, "writePiDefaultModel");
3126
+ function writeSettingsDefaultModel(spec, onlyIfUnset) {
3127
+ const parts = splitSpec(spec);
3128
+ if (!parts) return null;
3129
+ const dir = agentDir();
3130
+ const settingsPath = join5(dir, "settings.json");
3131
+ try {
3132
+ mkdirSync4(dir, { recursive: true });
3133
+ let settings = {};
3134
+ if (existsSync3(settingsPath)) {
3135
+ const raw = readFileSync7(settingsPath, "utf8").trim();
3136
+ if (raw) settings = JSON.parse(raw);
3137
+ }
3138
+ if (onlyIfUnset && typeof settings.defaultModel === "string" && settings.defaultModel.trim()) {
3139
+ return null;
3140
+ }
3141
+ settings.defaultProvider = parts.provider;
3142
+ settings.defaultModel = parts.modelId;
3143
+ writeFileSync5(settingsPath, `${JSON.stringify(settings, null, 2)}
3144
+ `);
3145
+ return spec;
3146
+ } catch {
3147
+ return null;
3148
+ }
3149
+ }
3150
+ __name(writeSettingsDefaultModel, "writeSettingsDefaultModel");
3151
+
3152
+ // src/providers/vision.ts?ext=privateer-privacy
3153
+ var VISION_PATTERNS = [
3154
+ // ── Named in the id ────────────────────────────────────────────────────────
3155
+ // Qwen's vision line (qwen3-vl-*, qwen2.5-vl-72b, near/Qwen/Qwen3-VL-30B-A3B) and
3156
+ // Baidu's (ernie-4.5-vl-424b). Qwen ships VL as a SEPARATE line, which is also why
3157
+ // the plain qwen3.x ids below are deliberately absent.
3158
+ /-vl[-_.]/i,
3159
+ // Z.ai marks vision with a trailing v on the version: glm-4.5v, glm-4.6v,
3160
+ // glm-5v-turbo. Same reasoning — the unsuffixed glm-5.x are their text siblings.
3161
+ /\bglm-\d+(?:\.\d+)?v\b/i,
3162
+ // ByteDance's GUI agent reads screenshots; that is the whole point of it.
3163
+ /\bui-tars\b/i,
3164
+ // ── Families that are multimodal throughout ────────────────────────────────
3165
+ // Every Claude from 3 onwards takes images, and 3 is the oldest in the catalog.
3166
+ /(^|\/)anthropic\/claude-/i,
3167
+ // Gemini has been multimodal since 1.0.
3168
+ /(^|\/)google\/gemini-/i,
3169
+ // Gemma from 3 on is multimodal; gemma-2 is not, so the version is part of the
3170
+ // match. Covers google/gemma-3-27b-it, google/gemma-4-31b-it, tinfoil/gemma4-31b,
3171
+ // near/google/gemma-4-31B-it, phala/google/gemma-4-31b-it and the Phala
3172
+ // gemma-4 derivatives.
3173
+ /gemma-?[34]/i,
3174
+ // gpt-4o, gpt-4.1 and the whole gpt-5 line. Deliberately NOT `openai/gpt-4`
3175
+ // (the original is text-only), not gpt-3.5, and not gpt-oss — the open-weights
3176
+ // models are text-only, which is what our own default was until this change.
3177
+ /(^|\/)openai\/gpt-(?:4o|4\.1|5)/i,
3178
+ // Llama 4 (maverick, scout) is natively multimodal; 3.x is not.
3179
+ /(^|\/)meta-llama\/llama-4-/i,
3180
+ // Nova lite/pro/premier take images; nova-micro is text-only.
3181
+ /(^|\/)amazon\/nova-(?:2-)?(?:lite|pro|premier)/i,
3182
+ // Grok 4 and up.
3183
+ /(^|\/)x-ai\/grok-4/i,
3184
+ // Mistral's multimodal tiers: small 3.2 and the medium 3 line.
3185
+ /(^|\/)mistralai\/mistral-(?:small-3\.2|medium-3)/i
3186
+ ];
3187
+ function acceptsImages(modelId) {
3188
+ return VISION_PATTERNS.some((re) => re.test(modelId));
3189
+ }
3190
+ __name(acceptsImages, "acceptsImages");
3191
+ function visionInput(modelId) {
3192
+ return acceptsImages(modelId) ? ["text", "image"] : ["text"];
3193
+ }
3194
+ __name(visionInput, "visionInput");
3195
+
3196
+ // src/providers/phala/aci-verifier/errors.ts?ext=privateer-privacy
3197
+ var AciError = class extends Error {
3198
+ static {
3199
+ __name(this, "AciError");
3200
+ }
3201
+ constructor(message) {
3202
+ super(message);
3203
+ this.name = "AciError";
3204
+ }
3205
+ };
3206
+ var AciFormatError = class extends AciError {
3207
+ static {
3208
+ __name(this, "AciFormatError");
3209
+ }
3210
+ constructor(message) {
3211
+ super(message);
3212
+ this.name = "AciFormatError";
3213
+ }
3214
+ };
3215
+
3216
+ // src/providers/phala/aci-verifier/jcs.ts?ext=privateer-privacy
3217
+ function canonicalize(value) {
3218
+ if (value === null) return "null";
3219
+ switch (typeof value) {
3220
+ case "boolean":
3221
+ return value ? "true" : "false";
3222
+ case "number":
3223
+ if (!Number.isInteger(value)) {
3224
+ throw new AciFormatError(
3225
+ `JCS: ACI restricts numbers to integers, got ${value} (\xA73)`
3226
+ );
3227
+ }
3228
+ return Object.is(value, -0) ? "0" : String(value);
3229
+ case "string":
3230
+ return JSON.stringify(value);
3231
+ case "object":
3232
+ if (Array.isArray(value)) {
3233
+ return "[" + value.map(canonicalize).join(",") + "]";
3234
+ }
3235
+ return serializeObject(value);
3236
+ default:
3237
+ throw new AciFormatError(`JCS: unsupported type ${typeof value}`);
3238
+ }
3239
+ }
3240
+ __name(canonicalize, "canonicalize");
3241
+ function serializeObject(obj) {
3242
+ const keys = Object.keys(obj).filter((k) => obj[k] !== void 0).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
3243
+ let out = "{";
3244
+ for (let i = 0; i < keys.length; i++) {
3245
+ const k = keys[i];
3246
+ if (i > 0) out += ",";
3247
+ out += JSON.stringify(k) + ":" + canonicalize(obj[k]);
3248
+ }
3249
+ return out + "}";
3250
+ }
3251
+ __name(serializeObject, "serializeObject");
3252
+ function jcsBytes(value) {
3253
+ return new TextEncoder().encode(canonicalize(value));
3254
+ }
3255
+ __name(jcsBytes, "jcsBytes");
3256
+
3257
+ // src/providers/phala/aci-verifier/crypto.ts?ext=privateer-privacy
3258
+ var subtle = globalThis.crypto.subtle;
3259
+ function toHex(bytes) {
3260
+ let out = "";
3261
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
3262
+ return out;
3263
+ }
3264
+ __name(toHex, "toHex");
3265
+ function fromHex(hex) {
3266
+ const h = hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
3267
+ if (h.length % 2 !== 0) {
3268
+ throw new AciFormatError(`hex string has odd length: ${hex.length} chars`);
3269
+ }
3270
+ const out = new Uint8Array(h.length / 2);
3271
+ for (let i = 0; i < out.length; i++) {
3272
+ const byte = Number.parseInt(h.slice(i * 2, i * 2 + 2), 16);
3273
+ if (Number.isNaN(byte)) {
3274
+ throw new AciFormatError(`invalid hex at offset ${i * 2}: "${h.slice(i * 2, i * 2 + 2)}"`);
3275
+ }
3276
+ out[i] = byte;
3277
+ }
3278
+ return out;
3279
+ }
3280
+ __name(fromHex, "fromHex");
3281
+ async function sha256(bytes) {
3282
+ return new Uint8Array(await subtle.digest("SHA-256", bytes));
3283
+ }
3284
+ __name(sha256, "sha256");
3285
+ async function sha256Hex(bytes) {
3286
+ return toHex(await sha256(bytes));
3287
+ }
3288
+ __name(sha256Hex, "sha256Hex");
3289
+ async function sha256Prefixed(bytes) {
3290
+ return "sha256:" + await sha256Hex(bytes);
3291
+ }
3292
+ __name(sha256Prefixed, "sha256Prefixed");
3293
+
3294
+ // src/providers/phala/aci-verifier/digest.ts?ext=privateer-privacy
3295
+ var DIGEST_RE = /^sha256:[0-9a-f]{64}$/;
3296
+ var NONCE_RE = /^[0-9a-f]{64}$/;
3297
+ async function computeKeysetDigest(keyset) {
3298
+ return sha256Prefixed(jcsBytes(keyset));
3299
+ }
3300
+ __name(computeKeysetDigest, "computeKeysetDigest");
3301
+ function attestationStatement(keysetDigest, nonce) {
3302
+ if (!DIGEST_RE.test(keysetDigest)) {
3303
+ throw new AciFormatError(`keyset digest is not sha256:<64-hex>: "${keysetDigest}"`);
3304
+ }
3305
+ if (nonce != null && !NONCE_RE.test(nonce)) {
3306
+ throw new AciFormatError("nonce must be exactly 64 lowercase hex characters (\xA73.2)");
3307
+ }
3308
+ const noncePart = nonce == null ? "null" : `"${nonce}"`;
3309
+ return new TextEncoder().encode(
3310
+ `{"keyset_digest":"${keysetDigest}","nonce":${noncePart},"purpose":"aci.report_data.v1"}`
3311
+ );
3312
+ }
3313
+ __name(attestationStatement, "attestationStatement");
3314
+ async function computeReportData(keysetDigest, nonce) {
3315
+ return sha256Hex(attestationStatement(keysetDigest, nonce));
3316
+ }
3317
+ __name(computeReportData, "computeReportData");
3318
+
3319
+ // src/providers/phala/aci-verifier/e2ee-channel.ts?ext=privateer-privacy
3320
+ var subtle2 = globalThis.crypto.subtle;
3321
+ var enc = new TextEncoder();
3322
+ var dec = new TextDecoder();
3323
+ var HKDF_INFO = enc.encode("aci.e2ee.v2.x25519");
3324
+
3325
+ // src/providers/phala/aci-verifier/report.ts?ext=privateer-privacy
3326
+ async function verifyReportBinding(report, nonce, options = {}) {
3327
+ const now = options.now ?? Math.floor(Date.now() / 1e3);
3328
+ const checks = [];
3329
+ const versionOk = report.api_version === "aci/1";
3330
+ checks.push({
3331
+ name: "api_version",
3332
+ ok: versionOk,
3333
+ ...versionOk ? {} : { detail: `api_version "${report.api_version}" is not "aci/1"` }
3334
+ });
3335
+ const keysetValue = report.attestation.workload_keyset;
3336
+ if (keysetValue === null || typeof keysetValue !== "object" || Array.isArray(keysetValue)) {
3337
+ const detail = "workload_keyset is not a JSON object";
3338
+ for (const name of ["workload_keyset_digest", "report_data", "not_after"]) {
3339
+ checks.push({ name, ok: false, detail });
3340
+ }
3341
+ return { ok: false, checks };
3342
+ }
3343
+ const digest = await computeKeysetDigest(keysetValue);
3344
+ pushEqual(checks, "workload_keyset_digest", report.workload_keyset_digest, digest);
3345
+ const expectedReportData = await computeReportData(digest, nonce);
3346
+ pushEqual(checks, "report_data", report.attestation.report_data, expectedReportData);
3347
+ const keyset = keysetValue;
3348
+ if (typeof keyset.not_after !== "number") {
3349
+ checks.push({
3350
+ name: "not_after",
3351
+ ok: false,
3352
+ detail: "keyset has no numeric not_after"
3353
+ });
3354
+ } else {
3355
+ const ok = now < keyset.not_after;
3356
+ checks.push({
3357
+ name: "not_after",
3358
+ ok,
3359
+ ...ok ? {} : { detail: `now ${now} >= not_after ${keyset.not_after}` }
3360
+ });
3361
+ }
3362
+ return {
3363
+ ok: checks.every((c2) => c2.ok),
3364
+ checks,
3365
+ workloadKeysetDigest: digest,
3366
+ keyset
3367
+ };
3368
+ }
3369
+ __name(verifyReportBinding, "verifyReportBinding");
3370
+ function pushEqual(checks, name, actual, expected) {
3371
+ const ok = actual === expected;
3372
+ checks.push({ name, ok, ...ok ? {} : { detail: `report ${actual} != recomputed ${expected}` } });
3373
+ }
3374
+ __name(pushEqual, "pushEqual");
3375
+
3376
+ // src/providers/phala/measurements.ts?ext=privateer-privacy
3377
+ import { createHash as createHash2 } from "node:crypto";
3378
+ var RTMR_KEYS = ["rtMr0", "rtMr1", "rtMr2", "rtMr3"];
3379
+ function parseEventLog(raw) {
3380
+ let value = raw;
3381
+ if (typeof raw === "string") {
3382
+ try {
3383
+ value = JSON.parse(raw);
3384
+ } catch {
3385
+ return [];
3386
+ }
3387
+ }
3388
+ if (!Array.isArray(value)) return [];
3389
+ return value.filter(
3390
+ (e) => !!e && typeof e === "object" && typeof e.imr === "number" && typeof e.digest === "string"
3391
+ );
3392
+ }
3393
+ __name(parseEventLog, "parseEventLog");
3394
+ function replayRtmrs(events) {
3395
+ const out = {};
3396
+ RTMR_KEYS.forEach((key, imr) => {
3397
+ let acc = Buffer.alloc(48);
3398
+ for (const ev of events) {
3399
+ if (ev.imr !== imr) continue;
3400
+ let digest;
3401
+ try {
3402
+ digest = Buffer.from(ev.digest, "hex");
3403
+ } catch {
3404
+ continue;
3405
+ }
3406
+ acc = createHash2("sha384").update(Buffer.concat([acc, digest])).digest();
3407
+ }
3408
+ out[key] = acc.toString("hex");
3409
+ });
3410
+ return out;
3411
+ }
3412
+ __name(replayRtmrs, "replayRtmrs");
3413
+ function appIdentityFrom(events) {
3414
+ const byName = /* @__PURE__ */ new Map();
3415
+ for (const ev of events) {
3416
+ if (ev.imr === 3 && ev.event && typeof ev.event_payload === "string" && ev.event_payload) {
3417
+ byName.set(ev.event, ev.event_payload);
3418
+ }
3419
+ }
3420
+ return {
3421
+ appId: byName.get("app-id"),
3422
+ composeHash: byName.get("compose-hash"),
3423
+ osImageHash: byName.get("os-image-hash"),
3424
+ instanceId: byName.get("instance-id"),
3425
+ mrKms: byName.get("mr-kms"),
3426
+ keyProvider: byName.get("key-provider")
3427
+ };
3428
+ }
3429
+ __name(appIdentityFrom, "appIdentityFrom");
3430
+ function computeComposeHash(appCompose) {
3431
+ if (typeof appCompose !== "string" || !appCompose) return void 0;
3432
+ return createHash2("sha256").update(Buffer.from(appCompose, "utf8")).digest("hex");
3433
+ }
3434
+ __name(computeComposeHash, "computeComposeHash");
3435
+ function checkReportConsistency(args) {
3436
+ const checks = [];
3437
+ const skipped = [];
3438
+ if (args.events.length === 0) {
3439
+ skipped.push("rtmr-replay");
3440
+ } else {
3441
+ const replayed = replayRtmrs(args.events);
3442
+ for (const key of RTMR_KEYS) {
3443
+ const ok = replayed[key] === args.quoted[key];
3444
+ checks.push({
3445
+ name: `rtmr-replay:${key}`,
3446
+ ok,
3447
+ detail: ok ? void 0 : `replayed ${replayed[key].slice(0, 16)}\u2026 but the quote signed ${args.quoted[key].slice(0, 16)}\u2026`
3448
+ });
3449
+ }
3450
+ }
3451
+ const computed = computeComposeHash(args.appCompose);
3452
+ if (!computed || !args.identity.composeHash) {
3453
+ skipped.push("compose-hash");
3454
+ } else {
3455
+ const ok = computed === args.identity.composeHash;
3456
+ checks.push({
3457
+ name: "compose-hash",
3458
+ ok,
3459
+ detail: ok ? void 0 : `sha256(app_compose)=${computed.slice(0, 16)}\u2026 but the log attests ${args.identity.composeHash.slice(0, 16)}\u2026`
3460
+ });
3461
+ }
3462
+ return { checks, ok: checks.every((c2) => c2.ok), skipped };
3463
+ }
3464
+ __name(checkReportConsistency, "checkReportConsistency");
3465
+
3466
+ // src/providers/phala/pin.ts?ext=privateer-privacy
3467
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
3468
+ import { join as join6 } from "node:path";
3469
+ var PIN_VERSION = 1;
3470
+ function pinPath() {
3471
+ return join6(globalDir(), "phala-enclave-pin.json");
3472
+ }
3473
+ __name(pinPath, "pinPath");
3474
+ function readPin() {
3475
+ try {
3476
+ const parsed = JSON.parse(readFileSync8(pinPath(), "utf8"));
3477
+ if (parsed?.v !== PIN_VERSION || !parsed.pin || typeof parsed.pin !== "object") return null;
3478
+ return parsed;
3479
+ } catch {
3480
+ return null;
3481
+ }
3482
+ }
3483
+ __name(readPin, "readPin");
3484
+ function writePin(pin, firstSeenAt) {
3485
+ try {
3486
+ mkdirSync5(globalDir(), { recursive: true });
3487
+ writeFileSync6(pinPath(), JSON.stringify({ v: PIN_VERSION, firstSeenAt, pin }, null, 2) + "\n", "utf8");
3488
+ } catch {
3489
+ }
3490
+ }
3491
+ __name(writePin, "writePin");
3492
+ function checkPin(current, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
3493
+ const stored = readPin();
3494
+ if (!stored?.pin) {
3495
+ const firstSeenAt = now();
3496
+ writePin(current, firstSeenAt);
3497
+ return { state: "first-seen", changed: [], firstSeenAt };
3498
+ }
3499
+ const changed = [];
3500
+ for (const key of Object.keys(current)) {
3501
+ const was = stored.pin[key];
3502
+ const is = current[key];
3503
+ if (was && is && was !== is) changed.push(`${key}: ${was.slice(0, 12)}\u2026 \u2192 ${is.slice(0, 12)}\u2026`);
3504
+ }
3505
+ if (changed.length === 0) return { state: "unchanged", changed: [], firstSeenAt: stored.firstSeenAt };
3506
+ writePin(current, stored.firstSeenAt ?? now());
3507
+ return { state: "changed", changed, firstSeenAt: stored.firstSeenAt };
3508
+ }
3509
+ __name(checkPin, "checkPin");
3510
+
3511
+ // src/providers/phalaSeal.ts?ext=privateer-privacy
3512
+ var DEFAULT_ACCEPTABLE_TCB = ["UpToDate"];
3513
+ function relayBase() {
3514
+ return `${serverBaseUrl().replace(/\/+$/, "")}/api/sealed/phala`;
3515
+ }
3516
+ __name(relayBase, "relayBase");
3517
+ function requireQuote() {
3518
+ const v = process.env.PRIVATEER_PHALA_REQUIRE_QUOTE;
3519
+ return !(v === "0" || v === "false");
3520
+ }
3521
+ __name(requireQuote, "requireQuote");
3522
+ function pccsUrl() {
3523
+ return process.env.PRIVATEER_PHALA_PCCS_URL || void 0;
3524
+ }
3525
+ __name(pccsUrl, "pccsUrl");
3526
+ function acceptableTcb() {
3527
+ const v = process.env.PRIVATEER_PHALA_TCB;
3528
+ const list = v ? v.split(",").map((s) => s.trim()).filter(Boolean) : DEFAULT_ACCEPTABLE_TCB;
3529
+ return new Set(list);
3530
+ }
3531
+ __name(acceptableTcb, "acceptableTcb");
3532
+ function extractQuoteMeasurements(report) {
3533
+ const td = report.asTd10?.() ?? report.asTd15?.()?.base;
3534
+ const fields = [td?.mrTd, td?.rtMr0, td?.rtMr1, td?.rtMr2, td?.rtMr3];
3535
+ if (fields.some((f) => !f?.length)) return void 0;
3536
+ const [mrTd, rtMr0, rtMr1, rtMr2, rtMr3] = fields.map((f) => toHex(new Uint8Array(f)));
3537
+ return { mrTd, rtMr0, rtMr1, rtMr2, rtMr3 };
3538
+ }
3539
+ __name(extractQuoteMeasurements, "extractQuoteMeasurements");
3540
+ function extractQuoteReportData(report) {
3541
+ const td10 = report.asTd10?.();
3542
+ if (td10?.reportData) return new Uint8Array(td10.reportData);
3543
+ const td15 = report.asTd15?.();
3544
+ if (td15?.base?.reportData) return new Uint8Array(td15.base.reportData);
3545
+ const data = report.data;
3546
+ if (data?.reportData) return new Uint8Array(data.reportData);
3547
+ throw new Error("phala: verified quote report has no reportData");
3548
+ }
3549
+ __name(extractQuoteReportData, "extractQuoteReportData");
3550
+ var attestationPromise = null;
3551
+ function attest() {
3552
+ if (!attestationPromise) {
3553
+ attestationPromise = establishAttestation().catch((err) => {
3554
+ attestationPromise = null;
3555
+ throw err;
3556
+ });
3557
+ }
3558
+ return attestationPromise;
3559
+ }
3560
+ __name(attest, "attest");
3561
+ async function establishAttestation() {
3562
+ const nonce = toHex(globalThis.crypto.getRandomValues(new Uint8Array(32)));
3563
+ const res = await fetch(`${relayBase()}/attestation?nonce=${nonce}`, { method: "GET" });
3564
+ if (!res.ok) throw new Error(`phala attestation HTTP ${res.status}`);
3565
+ const report = await res.json();
3566
+ const verification = await verifyReportBinding(report, nonce);
3567
+ if (!verification.ok) {
3568
+ const failed = verification.checks.filter((c2) => !c2.ok).map((c2) => c2.name).join(", ");
3569
+ throw new Error(`phala attestation binding failed: ${failed}`);
3570
+ }
3571
+ const enclave = await verifyHardwareQuote(report);
3572
+ return { report, verification, enclave };
3573
+ }
3574
+ __name(establishAttestation, "establishAttestation");
3575
+ async function verifyHardwareQuote(report) {
3576
+ if (!requireQuote()) return void 0;
3577
+ const attestation = report.attestation;
3578
+ const teeType = String(attestation?.tee_type || "");
3579
+ if (teeType !== "tdx") throw new Error(`phala: unsupported/absent tee_type "${teeType}" (only tdx is wired)`);
3580
+ const quoteHex = attestation.evidence?.quote;
3581
+ if (typeof quoteHex !== "string" || !quoteHex) throw new Error("phala: attestation evidence has no TDX quote");
3582
+ const reportDataHex = String(attestation.report_data || "").toLowerCase();
3583
+ if (!reportDataHex) throw new Error("phala: report has no report_data");
3584
+ const { getCollateralAndVerify } = await import("./c-77GO5L5Y.mjs");
3585
+ const verified = await getCollateralAndVerify(fromHex(quoteHex), pccsUrl());
3586
+ const status = String(verified.status);
3587
+ if (!acceptableTcb().has(status)) throw new Error(`phala: TDX quote TCB status not accepted: "${status}"`);
3588
+ const quoteReportData = extractQuoteReportData(verified.report);
3589
+ if (toHex(quoteReportData.slice(0, 32)) !== reportDataHex) {
3590
+ throw new Error("phala: TDX quote report_data does not bind the attested report_data");
3591
+ }
3592
+ const declared = attestation.evidence?.quote_report_data;
3593
+ if (typeof declared === "string" && declared && toHex(quoteReportData) !== declared.toLowerCase()) {
3594
+ throw new Error("phala: evidence.quote_report_data does not match the verified quote");
3595
+ }
3596
+ const measurements = extractQuoteMeasurements(verified.report);
3597
+ if (!measurements) return void 0;
3598
+ const events = parseEventLog(attestation.evidence?.event_log);
3599
+ const identity = appIdentityFrom(events);
3600
+ const consistency = checkReportConsistency({
3601
+ events,
3602
+ quoted: measurements,
3603
+ appCompose: attestation.evidence?.app_compose,
3604
+ identity
3605
+ });
3606
+ if (!consistency.ok) {
3607
+ const failed = consistency.checks.filter((c2) => !c2.ok).map((c2) => `${c2.name} (${c2.detail})`).join("; ");
3608
+ throw new Error(`phala: attestation self-consistency failed: ${failed}`);
3609
+ }
3610
+ const pin = checkPin({
3611
+ mrTd: measurements.mrTd,
3612
+ rtMr0: measurements.rtMr0,
3613
+ rtMr1: measurements.rtMr1,
3614
+ rtMr2: measurements.rtMr2,
3615
+ appId: identity.appId,
3616
+ composeHash: identity.composeHash,
3617
+ osImageHash: identity.osImageHash,
3618
+ mrKms: identity.mrKms
3619
+ });
3620
+ const provenance = attestation.source_provenance;
3621
+ const downstream = attestation.evidence?.downstream_tls_binding;
3622
+ return {
3623
+ measurements,
3624
+ identity,
3625
+ pin,
3626
+ skippedChecks: consistency.skipped,
3627
+ // Named so a human can go read the source; self-declared by the enclave and NOT
3628
+ // proven by the quote (image_digest/image_provenance come back null), so it is a
3629
+ // pointer to audit, never evidence that this binary came from that commit.
3630
+ repoUrl: provenance?.repo_url,
3631
+ repoCommit: provenance?.repo_commit,
3632
+ // The attested enclave forwards to this downstream host over TLS. Surfaced because
3633
+ // it marks where our attested boundary ENDS — that host is a separate trust domain
3634
+ // we do not attest.
3635
+ downstreamDomain: downstream?.domain,
3636
+ downstreamSpkiSha256: downstream?.spki_sha256
3637
+ };
3638
+ }
3639
+ __name(verifyHardwareQuote, "verifyHardwareQuote");
3640
+ async function attestPhala() {
3641
+ try {
3642
+ const { enclave } = await attest();
3643
+ return { ok: true, enclaveIdentity: enclave };
3644
+ } catch (e) {
3645
+ return { ok: false, error: e.message };
3646
+ }
3647
+ }
3648
+ __name(attestPhala, "attestPhala");
3649
+
3650
+ // src/providers/sealedShim.ts?ext=privateer-privacy
3651
+ function sealedEnabled() {
3652
+ const v = process.env.PRIVATEER_SEALED;
3653
+ return !(v === "0" || v === "false");
3654
+ }
3655
+ __name(sealedEnabled, "sealedEnabled");
3656
+ function sealedProviderFor(modelId) {
3657
+ if (modelId.startsWith("tinfoil/")) return "tinfoil";
3658
+ if (modelId.startsWith("phala/")) return "phala";
3659
+ return null;
3660
+ }
3661
+ __name(sealedProviderFor, "sealedProviderFor");
3662
+ function relayBase2(provider) {
3663
+ return `${serverBaseUrl().replace(/\/+$/, "")}/api/sealed/${provider}`;
3664
+ }
3665
+ __name(relayBase2, "relayBase");
3666
+ var clients = /* @__PURE__ */ new Map();
3667
+ var readyPromises = /* @__PURE__ */ new Map();
3668
+ function client(provider) {
3669
+ let c2 = clients.get(provider);
3670
+ if (!c2) {
3671
+ const base = relayBase2(provider);
3672
+ c2 = new SecureClient({ baseURL: base, attestationBundleURL: base, transport: "ehbp" });
3673
+ clients.set(provider, c2);
3674
+ }
3675
+ return c2;
3676
+ }
3677
+ __name(client, "client");
3678
+ function ready(provider) {
3679
+ let p = readyPromises.get(provider);
3680
+ if (!p) {
3681
+ p = client(provider).ready().catch((err) => {
3682
+ readyPromises.delete(provider);
3683
+ throw err;
3684
+ });
3685
+ readyPromises.set(provider, p);
3686
+ }
3687
+ return p;
3688
+ }
3689
+ __name(ready, "ready");
3690
+ async function attestSealed(provider) {
3691
+ if (provider === "phala") return attestPhala();
3692
+ try {
3693
+ await ready(provider);
3694
+ return { ok: true, enclave: client(provider).getEnclaveURL() };
3695
+ } catch (e) {
3696
+ return { ok: false, error: e.message };
3697
+ }
3698
+ }
3699
+ __name(attestSealed, "attestSealed");
3700
+ var shimBase = null;
3701
+ function sealedShimBase() {
3702
+ return shimBase;
3703
+ }
3704
+ __name(sealedShimBase, "sealedShimBase");
3705
+
3706
+ // src/providers/account.ts?ext=privateer-privacy
3707
+ var DEFAULT_MODELS = [
3708
+ ACCOUNT_DEFAULT_MODEL_ID,
3709
+ ACCOUNT_NEAR_MODEL_ID,
3710
+ // All three former defaults (see TINFOIL_MODEL_ID for the dates). They stay in the
3711
+ // floor so a user who saved any of them as their own default still resolves it
3712
+ // synchronously at launch, rather than falling through to "first model with
3713
+ // configured auth" — the BYO dead end this seed list exists to prevent.
3714
+ "tinfoil/gpt-oss-120b",
3715
+ "tinfoil/kimi-k2-6",
3716
+ "tinfoil/glm-5-2",
3717
+ "anthropic/claude-opus-5",
3718
+ "anthropic/claude-sonnet-5",
3719
+ "openai/gpt-5.6-sol",
3720
+ "deepseek/deepseek-v4-flash",
3721
+ // 2026-08 releases, confirmed live on OpenRouter (ZDR-covered ids reach the
3722
+ // account catalog automatically; these seeds just make them resolve at launch).
3723
+ "moonshotai/kimi-k3",
3724
+ "z-ai/glm-5.2",
3725
+ // Both verified 2026-08-19 against /endpoints/zdr and GET /api/models: ZDR-covered
3726
+ // and servable. Same reason as the line above — the live catalog already carries
3727
+ // them, this only closes the first-launch window where a saved default that isn't
3728
+ // yet in the cache would fall through to a BYO provider.
3729
+ "x-ai/grok-4.6",
3730
+ "openai/gpt-5.6-luna"
3731
+ ];
3732
+ var DEFAULT_CONTEXT_WINDOW = 128e3;
3733
+ var MIN_PUBLISHED_CONTEXT_WINDOW = 8192;
3734
+ var MAX_PUBLISHED_CONTEXT_WINDOW = 1e7;
3735
+ function validContextWindow(value) {
3736
+ return typeof value === "number" && Number.isFinite(value) && value >= MIN_PUBLISHED_CONTEXT_WINDOW && value <= MAX_PUBLISHED_CONTEXT_WINDOW ? Math.floor(value) : void 0;
3737
+ }
3738
+ __name(validContextWindow, "validContextWindow");
3739
+ var DEFAULT_MAX_OUTPUT_TOKENS = 16384;
3740
+ var ACCOUNT_MAX_OUTPUT_TOKENS = 32e3;
3741
+ var MIN_PUBLISHED_MAX_OUTPUT = 1024;
3742
+ function validMaxOutputTokens(value) {
3743
+ return typeof value === "number" && Number.isFinite(value) && value >= MIN_PUBLISHED_MAX_OUTPUT ? Math.min(Math.floor(value), ACCOUNT_MAX_OUTPUT_TOKENS) : void 0;
3744
+ }
3745
+ __name(validMaxOutputTokens, "validMaxOutputTokens");
3746
+ function seedModel(id) {
3747
+ return {
3748
+ id,
3749
+ name: id,
3750
+ // reasoning + how to steer it, for the enclave models where we verified the
3751
+ // control shape live; `reasoning: false` (Pi's "not a thinking model") for the rest.
3752
+ ...thinkingProfile(id) ?? { reasoning: false },
3753
+ // Honest modalities. This was hardcoded to text for the whole catalog, which made
3754
+ // Pi strip every image from every account request — including the ones `read`
3755
+ // attaches when the user points at a screenshot. See providers/vision.ts.
3756
+ input: visionInput(id),
3757
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
3758
+ // The model's real window when the server published one, else the fallback.
3759
+ // This is not cosmetic: pi sizes each turn's answer budget as
3760
+ // (contextWindow − estimated prompt − 4096) and clamps max_tokens to it
3761
+ // (pi-ai clampMaxTokensToContext, and see src/engine/contextBudget.ts). A window
3762
+ // that under-states the model's real one therefore throttles the answer — and
3763
+ // used to collapse it to a single token — while the model still has room.
3764
+ contextWindow: accountContextWindow(id) ?? DEFAULT_CONTEXT_WINDOW,
3765
+ // The model's real output cap when published. Not cosmetic either: reasoning and
3766
+ // the answer share this one budget, and pi only compact-and-retries a length stop
3767
+ // that ended BELOW it — a thinking model that spends all of a too-small budget
3768
+ // reasoning ends the run with "Response was truncated before completion."
3769
+ // (measured: glm-5.3-flash spending all 16384 on 50k+ chars of reasoning, whose
3770
+ // real cap is far higher). See also ext/truncationRecovery.ts.
3771
+ maxTokens: accountMaxOutputTokens(id) ?? DEFAULT_MAX_OUTPUT_TOKENS
3772
+ };
3773
+ }
3774
+ __name(seedModel, "seedModel");
3775
+ var CHAT_TEMPLATE_THINKING = {
3776
+ reasoning: true,
3777
+ thinkingLevelMap: { minimal: null, low: null, high: null, xhigh: null },
3778
+ compat: { thinkingFormat: "qwen-chat-template" }
3779
+ };
3780
+ var REASONING_EFFORT_THINKING = {
3781
+ reasoning: true,
3782
+ thinkingLevelMap: { off: "low", minimal: "low", xhigh: null }
3783
+ };
3784
+ var OPENROUTER_THINKING = {
3785
+ reasoning: true,
3786
+ thinkingLevelMap: { off: "low", minimal: "low" },
3787
+ compat: { thinkingFormat: "openrouter" }
3788
+ };
3789
+ var PROXIED_THINKING_MODEL = [
3790
+ /^google\/gemini-/i,
3791
+ // gemini-2.5+ all take an effort; gemma-* deliberately excluded
3792
+ /^z-ai\/glm-/i,
3793
+ // the whole GLM line reasons, vision variants included
3794
+ /^deepseek\/deepseek-(r1|v3\.[12]|v4)/i,
3795
+ // NOT deepseek-chat*, which is the non-thinking split
3796
+ /^x-ai\/grok-4/i,
3797
+ // grok-4.x; grok-build-0.1 is unprobed
3798
+ /^openai\/gpt-(5|6|oss)/i
3799
+ // gpt-4*/gpt-3.5* do not reason
3800
+ ];
3801
+ var NON_THINKING_VARIANT = /(?:^|[-/])(?:chat|instruct)(?:[-.]|$)/i;
3802
+ var TEE_MODEL = /^(tinfoil|phala|near)\//;
3803
+ function teeThinkingProfile(id) {
3804
+ if (/instruct/i.test(id)) return null;
3805
+ if (/gpt-oss/i.test(id)) return REASONING_EFFORT_THINKING;
3806
+ if (/glm|qwen/i.test(id)) return CHAT_TEMPLATE_THINKING;
3807
+ return null;
3808
+ }
3809
+ __name(teeThinkingProfile, "teeThinkingProfile");
3810
+ function proxiedThinkingProfile(id) {
3811
+ if (NON_THINKING_VARIANT.test(id)) return null;
3812
+ return PROXIED_THINKING_MODEL.some((re) => re.test(id)) ? OPENROUTER_THINKING : null;
3813
+ }
3814
+ __name(proxiedThinkingProfile, "proxiedThinkingProfile");
3815
+ function thinkingProfile(id) {
3816
+ const profile = TEE_MODEL.test(id) ? teeThinkingProfile(id) : proxiedThinkingProfile(id);
3817
+ return profile && { ...profile, thinkingLevelMap: { ...profile.thinkingLevelMap }, ...profile.compat ? { compat: { ...profile.compat } } : {} };
3818
+ }
3819
+ __name(thinkingProfile, "thinkingProfile");
3820
+ var CATALOG_CACHE_MAX = 2e3;
3821
+ function catalogCachePath() {
3822
+ return join7(globalDir(), "account-models.json");
3823
+ }
3824
+ __name(catalogCachePath, "catalogCachePath");
3825
+ var cachedCapabilities = null;
3826
+ function cachedCapabilityMaps() {
3827
+ if (cachedCapabilities) return cachedCapabilities;
3828
+ cachedCapabilities = { windows: /* @__PURE__ */ new Map(), maxOutputs: /* @__PURE__ */ new Map() };
3829
+ try {
3830
+ const path = catalogCachePath();
3831
+ if (existsSync4(path)) {
3832
+ const parsed = JSON.parse(readFileSync9(path, "utf8"));
3833
+ readCapability(parsed.windows, validContextWindow, cachedCapabilities.windows);
3834
+ readCapability(parsed.maxOutputs, validMaxOutputTokens, cachedCapabilities.maxOutputs);
3835
+ }
3836
+ } catch {
3837
+ }
3838
+ return cachedCapabilities;
3839
+ }
3840
+ __name(cachedCapabilityMaps, "cachedCapabilityMaps");
3841
+ function readCapability(raw, valid, into) {
3842
+ if (!raw || typeof raw !== "object") return;
3843
+ for (const [id, value] of Object.entries(raw)) {
3844
+ const n = valid(value);
3845
+ if (n !== void 0 && into.size < CATALOG_CACHE_MAX) into.set(id, n);
3846
+ }
3847
+ }
3848
+ __name(readCapability, "readCapability");
3849
+ function cachedContextWindows() {
3850
+ return cachedCapabilityMaps().windows;
3851
+ }
3852
+ __name(cachedContextWindows, "cachedContextWindows");
3853
+ function loadCachedCatalogIds() {
3854
+ try {
3855
+ const path = catalogCachePath();
3856
+ if (!existsSync4(path)) return [];
3857
+ const parsed = JSON.parse(readFileSync9(path, "utf8"));
3858
+ if (!Array.isArray(parsed.ids)) return [];
3859
+ return parsed.ids.filter((id) => typeof id === "string" && id.length > 0).slice(0, CATALOG_CACHE_MAX);
3860
+ } catch {
3861
+ return [];
3862
+ }
3863
+ }
3864
+ __name(loadCachedCatalogIds, "loadCachedCatalogIds");
3865
+ function isServableAccountModel(id) {
3866
+ if (!id.startsWith("phala/")) return true;
3867
+ return sealedEnabled() && sealedShimBase() !== null;
3868
+ }
3869
+ __name(isServableAccountModel, "isServableAccountModel");
3870
+ function seedCatalogIds() {
3871
+ const ids = [...DEFAULT_MODELS];
3872
+ const seen = new Set(ids);
3873
+ for (const id of loadCachedCatalogIds()) {
3874
+ if (!seen.has(id)) {
3875
+ seen.add(id);
3876
+ ids.push(id);
3877
+ }
3878
+ }
3879
+ return ids;
3880
+ }
3881
+ __name(seedCatalogIds, "seedCatalogIds");
3882
+ var accountWindowMap = /* @__PURE__ */ new Map();
3883
+ function accountContextWindow(id) {
3884
+ return accountWindowMap.get(id) ?? cachedContextWindows().get(id);
3885
+ }
3886
+ __name(accountContextWindow, "accountContextWindow");
3887
+ var accountMaxOutputMap = /* @__PURE__ */ new Map();
3888
+ function accountMaxOutputTokens(id) {
3889
+ return accountMaxOutputMap.get(id) ?? cachedCapabilityMaps().maxOutputs.get(id);
3890
+ }
3891
+ __name(accountMaxOutputTokens, "accountMaxOutputTokens");
3892
+ function verificationLink(raw) {
3893
+ const uri = (raw ?? "").trim();
3894
+ if (!uri) return "";
3895
+ if (/^https?:\/\//i.test(uri)) return uri;
3896
+ return `https://${uri.replace(/^\/+/, "")}`;
3897
+ }
3898
+ __name(verificationLink, "verificationLink");
3899
+ var privateerOAuthProvider = {
3900
+ name: "Privateer account",
3901
+ usesCallbackServer: false,
3902
+ // Pi's login dialog passes `signal` (its cancel AbortController) alongside the
3903
+ // callbacks. We MUST thread it into runDeviceLogin — otherwise escape/ctrl+c
3904
+ // aborts the dialog's signal but our poll loop never sees it, the login()
3905
+ // promise never settles, and Pi never restores the editor: the "Waiting for
3906
+ // authentication…" screen hangs with no way out. See auth/privateer.ts
3907
+ // pollForToken, which checks the signal and rejects with "Login cancelled.".
3908
+ async login(cb) {
3909
+ let wasLinked = hasCredentials();
3910
+ if (wasLinked && cb.onSelect) {
3911
+ const user = currentUser();
3912
+ const who = user?.email ?? user?.id ?? "this account";
3913
+ const choice = await cb.onSelect({
3914
+ message: `Already signed in as ${who}.`,
3915
+ options: [
3916
+ { id: "keep", label: `Stay signed in as ${who}` },
3917
+ { id: "switch", label: "Sign in as a different account (signs this machine out first)" }
3918
+ ]
3919
+ });
3920
+ if (choice === void 0) throw new Error("Login cancelled");
3921
+ if (choice === "switch") {
3922
+ await logout();
3923
+ wasLinked = false;
3924
+ }
3925
+ }
3926
+ if (!wasLinked) {
3927
+ try {
3928
+ await runDeviceLogin({
3929
+ signal: cb.signal,
3930
+ onCode: /* @__PURE__ */ __name((code) => {
3931
+ const uri = verificationLink(code.verification_uri_complete ?? code.verification_uri);
3932
+ if (uri && canOpenBrowser()) void openInBrowser(uri);
3933
+ cb.onDeviceCode?.({
3934
+ userCode: code.user_code,
3935
+ verificationUri: uri,
3936
+ intervalSeconds: code.interval,
3937
+ expiresInSeconds: code.expires_in
3938
+ });
3939
+ }, "onCode")
3940
+ });
3941
+ } catch (e) {
3942
+ if (cb.signal?.aborted) throw new Error("Login cancelled");
3943
+ throw e;
3944
+ }
3945
+ }
3946
+ if (cb.signal?.aborted) throw new Error("Login cancelled");
3947
+ const creds = await accountCredential();
3948
+ ensurePiDefaultModel();
3949
+ notifySignedIn();
3950
+ return creds;
3951
+ },
3952
+ async refreshToken(creds) {
3953
+ const mine = armSlot().cred?.refresh;
3954
+ const refresh = mine && creds.refresh !== mine ? mine : creds.refresh;
3955
+ let next;
3956
+ try {
3957
+ next = await refreshAccountCredentials(refresh);
3958
+ } catch {
3959
+ next = await acquireAccountCredential();
3960
+ }
3961
+ rememberAccountCredential(next);
3962
+ return next;
3963
+ },
3964
+ getApiKey(creds) {
3965
+ return creds.access;
3966
+ }
3967
+ };
3968
+ var TEE_PREFIXES = ["near/", "tinfoil/", "phala/"];
3969
+ function privateerChannel(modelId) {
3970
+ return TEE_PREFIXES.some((p) => modelId.startsWith(p)) ? "tee" : "zdr";
3971
+ }
3972
+ __name(privateerChannel, "privateerChannel");
3973
+ async function accountPosture(modelId) {
3974
+ if (privateerChannel(modelId) === "zdr") {
3975
+ return { tier: "zdr-policy" };
3976
+ }
3977
+ const sealedProvider = sealedEnabled() ? sealedProviderFor(modelId) : null;
3978
+ if (sealedProvider) {
3979
+ const att = await attestSealed(sealedProvider);
3980
+ return att.ok ? { tier: "tee-verified", enclaveIdentity: att.enclaveIdentity } : { tier: "tee-unverified", error: att.error };
3981
+ }
3982
+ if (!modelId.startsWith("near/")) {
3983
+ return { tier: "tee-unverified" };
3984
+ }
3985
+ try {
3986
+ const res = await authedFetch(
3987
+ `${serverBaseUrl()}/api/models/near/attestation?model=${encodeURIComponent(modelId)}`
3988
+ );
3989
+ if (!res.ok) return { tier: "tee-unverified", error: `HTTP ${res.status}` };
3990
+ const data = await res.json();
3991
+ const att = interpretReport(modelId, data.nonce ?? "", data.report ?? {});
3992
+ const tp = teePosture(att);
3993
+ const tier = tierFromTeePosture(tp);
3994
+ return { tier, teePosture: tp };
3995
+ } catch (e) {
3996
+ return { tier: "tee-unverified", error: e.message };
3997
+ }
3998
+ }
3999
+ __name(accountPosture, "accountPosture");
4000
+ function modelEntry(id) {
4001
+ const base = seedModel(id);
4002
+ const provider = sealedEnabled() ? sealedProviderFor(id) : null;
4003
+ const shim = sealedShimBase();
4004
+ return provider && shim ? { ...base, baseUrl: `${shim}/${provider}/v1` } : base;
4005
+ }
4006
+ __name(modelEntry, "modelEntry");
4007
+ function accountProviderConfig(ids) {
4008
+ return {
4009
+ name: "Privateer account",
4010
+ baseUrl: `${serverBaseUrl()}/api/agent/v1`,
4011
+ api: "openai-completions",
4012
+ oauth: privateerOAuthProvider,
4013
+ // Filter HERE rather than at the catalog, so callers keep passing the server's
4014
+ // full list and every registration re-evaluates servability against the CURRENT
4015
+ // shim state. That is what lets the post-shim re-registration in makeAccountProvider
4016
+ // put `phala/*` back: had the ids been filtered upstream, the sealed-only models
4017
+ // would have been dropped from `lastIds` before the shim ever finished starting and
4018
+ // nothing would have brought them back.
4019
+ models: ids.filter(isServableAccountModel).map(modelEntry)
4020
+ };
4021
+ }
4022
+ __name(accountProviderConfig, "accountProviderConfig");
4023
+ function registerAccountModels(pi) {
4024
+ pi.registerProvider?.("privateer", accountProviderConfig(seedCatalogIds()));
4025
+ }
4026
+ __name(registerAccountModels, "registerAccountModels");
4027
+ var ARMED = /* @__PURE__ */ Symbol.for("privateer.accountCredential");
4028
+ function armSlot() {
4029
+ const g = globalThis;
4030
+ return g[ARMED] ??= {};
4031
+ }
4032
+ __name(armSlot, "armSlot");
4033
+ function rememberAccountCredential(cred) {
4034
+ armSlot().cred = cred;
4035
+ }
4036
+ __name(rememberAccountCredential, "rememberAccountCredential");
4037
+ function liveAccountCredential() {
4038
+ const cred = armSlot().cred;
4039
+ return cred && cred.expires > Date.now() + 6e4 ? cred : void 0;
4040
+ }
4041
+ __name(liveAccountCredential, "liveAccountCredential");
4042
+ async function accountCredential() {
4043
+ const live = liveAccountCredential();
4044
+ if (live) return live;
4045
+ const slot = armSlot();
4046
+ slot.inFlight ??= acquireAccountCredential().then((cred) => {
4047
+ slot.cred = cred;
4048
+ return cred;
4049
+ }).finally(() => {
4050
+ slot.inFlight = void 0;
4051
+ });
4052
+ return slot.inFlight;
4053
+ }
4054
+ __name(accountCredential, "accountCredential");
4055
+
4056
+ // src/config/privacyDisabled.ts?ext=privateer-privacy
4057
+ var ENV = "PRIVATEER_PRIVACY_OFF";
4058
+ var ALT_ENV = "PI_PRIVACY_OFF";
4059
+ var NO_QUARTER_PRIVACY_MARK = "PRIVATEER_PRIVACY_OFF_BY_NO_QUARTER";
4060
+ function privacyDisabled() {
4061
+ return process.env[ENV] === "1" || process.env[ALT_ENV] === "1";
4062
+ }
4063
+ __name(privacyDisabled, "privacyDisabled");
4064
+ var listeners = /* @__PURE__ */ new Set();
4065
+ function setPrivacyDisabled(disabled) {
4066
+ const before = privacyDisabled();
4067
+ delete process.env[NO_QUARTER_PRIVACY_MARK];
4068
+ if (disabled) {
4069
+ process.env[ENV] = "1";
4070
+ process.env[ALT_ENV] = "1";
4071
+ } else {
4072
+ delete process.env[ENV];
4073
+ delete process.env[ALT_ENV];
4074
+ }
4075
+ if (before !== disabled) {
4076
+ for (const fn of listeners) {
4077
+ try {
4078
+ fn(disabled);
4079
+ } catch {
4080
+ }
4081
+ }
4082
+ }
4083
+ return disabled;
4084
+ }
4085
+ __name(setPrivacyDisabled, "setPrivacyDisabled");
4086
+
4087
+ // src/permissions/noQuarter.ts?ext=privateer-privacy
4088
+ var ENV2 = "PRIVATEER_NO_QUARTER";
4089
+ function noQuarterActive() {
4090
+ return process.env[ENV2] === "1";
4091
+ }
4092
+ __name(noQuarterActive, "noQuarterActive");
4093
+
4094
+ // src/config/piiAllow.ts?ext=privateer-privacy
4095
+ import { readFileSync as readFileSync10, statSync as statSync2 } from "node:fs";
4096
+
4097
+ // src/util/atomicWrite.ts?ext=privateer-privacy
4098
+ import { chmodSync as chmodSync5, realpathSync, renameSync as renameSync2, statSync, unlinkSync, writeFileSync as writeFileSync8 } from "node:fs";
4099
+ import { randomBytes as randomBytes2 } from "node:crypto";
4100
+ function writeFileAtomic(path, data, opts = {}) {
4101
+ let target = path;
4102
+ let mode = opts.mode ?? 384;
4103
+ try {
4104
+ target = realpathSync(path);
4105
+ if (opts.mode === void 0) mode = statSync(target).mode & 511;
4106
+ } catch {
4107
+ }
4108
+ const tmp = `${target}.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`;
4109
+ try {
4110
+ writeFileSync8(tmp, data, { encoding: "utf8", mode });
4111
+ try {
4112
+ chmodSync5(tmp, mode);
4113
+ } catch {
4114
+ }
4115
+ renameSync2(tmp, target);
4116
+ } catch (e) {
4117
+ try {
4118
+ unlinkSync(tmp);
4119
+ } catch {
4120
+ }
4121
+ throw e;
4122
+ }
4123
+ }
4124
+ __name(writeFileAtomic, "writeFileAtomic");
4125
+
4126
+ // src/config/piiAllow.ts?ext=privateer-privacy
4127
+ var cached2 = [];
4128
+ var cachedStamp = "";
4129
+ function parse(raw) {
4130
+ const list = raw?.privacy?.piiAllow;
4131
+ if (!Array.isArray(list)) return [];
4132
+ return list.filter((e) => typeof e === "string" && e.trim() !== "").map((e) => e.trim());
4133
+ }
4134
+ __name(parse, "parse");
4135
+ function piiAllowEntries() {
4136
+ try {
4137
+ const st = statSync2(configPath());
4138
+ const stamp = `${st.mtimeMs}:${st.size}`;
4139
+ if (stamp === cachedStamp) return cached2;
4140
+ cachedStamp = stamp;
4141
+ cached2 = parse(JSON.parse(readFileSync10(configPath(), "utf8")));
4142
+ } catch {
4143
+ cachedStamp = "";
4144
+ cached2 = [];
4145
+ }
4146
+ return cached2;
4147
+ }
4148
+ __name(piiAllowEntries, "piiAllowEntries");
4149
+ function invalidate() {
4150
+ cachedStamp = "";
4151
+ }
4152
+ __name(invalidate, "invalidate");
4153
+ function invalidReason(entry) {
4154
+ if (!entry) return "an empty entry matches nothing";
4155
+ if (/^\*+$/.test(entry)) return "a bare `*` would allowlist everything \u2014 turn the gate off explicitly if that's what you mean";
4156
+ return void 0;
4157
+ }
4158
+ __name(invalidReason, "invalidReason");
4159
+ function write(entries) {
4160
+ let cfg = {};
4161
+ try {
4162
+ cfg = JSON.parse(readFileSync10(configPath(), "utf8"));
4163
+ } catch {
4164
+ }
4165
+ const privacy2 = { ...cfg.privacy ?? {}, piiAllow: entries };
4166
+ writeFileAtomic(configPath(), JSON.stringify({ ...cfg, privacy: privacy2 }, null, 2) + "\n");
4167
+ invalidate();
4168
+ }
4169
+ __name(write, "write");
4170
+ function addPiiAllow(raw) {
4171
+ const entry = raw.trim();
4172
+ const bad = invalidReason(entry);
4173
+ if (bad) return { ok: false, message: bad, entries: piiAllowEntries() };
4174
+ const entries = [...piiAllowEntries()];
4175
+ if (entries.includes(entry)) return { ok: true, message: `${entry} is already allowlisted`, entries };
4176
+ entries.push(entry);
4177
+ write(entries);
4178
+ return { ok: true, message: `${entry} is no longer treated as PII in this and future sessions`, entries };
4179
+ }
4180
+ __name(addPiiAllow, "addPiiAllow");
4181
+ function removePiiAllow(raw) {
4182
+ const entry = raw.trim();
4183
+ const entries = [...piiAllowEntries()];
4184
+ const i = entries.indexOf(entry);
4185
+ if (i < 0) return { ok: false, message: `${entry || "(empty)"} is not in the allowlist`, entries };
4186
+ entries.splice(i, 1);
4187
+ write(entries);
4188
+ return { ok: true, message: `${entry} is gated again`, entries };
4189
+ }
4190
+ __name(removePiiAllow, "removePiiAllow");
4191
+
4192
+ // src/ui/palette.ts?ext=privateer-privacy
4193
+ var ESC = "\x1B[";
4194
+ var RESET = `${ESC}0m`;
4195
+ var BOLD = `${ESC}1m`;
4196
+ var c = /* @__PURE__ */ __name((n) => `${ESC}38;5;${n}m`, "c");
4197
+ var FALLBACK = {
4198
+ RESET,
4199
+ BOLD,
4200
+ INK: c(231),
4201
+ ACCENT: c(231),
4202
+ BORDER: c(231),
4203
+ DIM: `${ESC}90m`,
4204
+ GREEN: `${ESC}32m`,
4205
+ YELLOW: `${ESC}33m`,
4206
+ RED: `${ESC}31m`,
4207
+ SKY: c(117),
4208
+ WHITE: c(231)
4209
+ };
4210
+ function paletteFor(theme) {
4211
+ if (!theme || typeof theme.getFgAnsi !== "function") return FALLBACK;
4212
+ const g = /* @__PURE__ */ __name((name, fallback) => {
4213
+ try {
4214
+ const a = theme.getFgAnsi(name);
4215
+ return typeof a === "string" && a.length > 0 ? a : fallback;
4216
+ } catch {
4217
+ return fallback;
4218
+ }
4219
+ }, "g");
4220
+ return {
4221
+ RESET,
4222
+ BOLD,
4223
+ INK: g("text", FALLBACK.INK),
4224
+ ACCENT: g("accent", FALLBACK.ACCENT),
4225
+ BORDER: g("border", FALLBACK.BORDER),
4226
+ DIM: g("dim", FALLBACK.DIM),
4227
+ GREEN: g("success", FALLBACK.GREEN),
4228
+ YELLOW: g("warning", FALLBACK.YELLOW),
4229
+ RED: g("error", FALLBACK.RED),
4230
+ // Fixed colours, not theme tokens — but a pale blue or white vanishes on a light
4231
+ // background, so a light theme gets a deeper blue and plain ink instead.
4232
+ ...lightAppearance(theme) ? { SKY: c(32), WHITE: g("text", FALLBACK.INK) } : { SKY: FALLBACK.SKY, WHITE: FALLBACK.WHITE }
4233
+ };
4234
+ }
4235
+ __name(paletteFor, "paletteFor");
4236
+ function lightAppearance(theme) {
4237
+ try {
4238
+ return theme.appearance === "light";
4239
+ } catch {
4240
+ return false;
4241
+ }
4242
+ }
4243
+ __name(lightAppearance, "lightAppearance");
4244
+ var CLI_DARK = {
4245
+ RESET,
4246
+ BOLD,
4247
+ DIM: `${ESC}2m`,
4248
+ GREEN: `${ESC}32m`,
4249
+ YELLOW: `${ESC}33m`,
4250
+ RED: `${ESC}31m`,
4251
+ CYAN: `${ESC}36m`
4252
+ };
4253
+ var CLI_LIGHT = {
4254
+ RESET,
4255
+ BOLD,
4256
+ DIM: c(243),
4257
+ // solid medium gray instead of faint
4258
+ GREEN: c(28),
4259
+ // dark green
4260
+ YELLOW: c(130),
4261
+ // dark amber (plain 33m is invisible-pale on white)
4262
+ RED: c(124),
4263
+ // dark red
4264
+ CYAN: c(24)
4265
+ // dark teal-blue accent
4266
+ };
4267
+ function detectScheme(env = process.env) {
4268
+ const fgbg = env.COLORFGBG;
4269
+ if (fgbg) {
4270
+ const parts = fgbg.split(";");
4271
+ const bg = parseInt(parts[parts.length - 1], 10);
4272
+ if (!Number.isNaN(bg)) return bg === 7 || bg === 15 ? "light" : "dark";
4273
+ }
4274
+ return "dark";
4275
+ }
4276
+ __name(detectScheme, "detectScheme");
4277
+ function cliPalette(scheme = detectScheme()) {
4278
+ return scheme === "light" ? CLI_LIGHT : CLI_DARK;
4279
+ }
4280
+ __name(cliPalette, "cliPalette");
4281
+
4282
+ // extensions/privateer-posture.ts?ext=privateer-privacy
4283
+ var DOT = { green: "\u{1F7E2}", yellow: "\u{1F7E1}", red: "\u{1F534}", neutral: "\u26AA" };
4284
+ function badgeLabel(tier, p) {
4285
+ if (tier === "tee-verified") return `${p.GREEN}\u26C9 Trusted Execution${p.RESET}`;
4286
+ if (tier === "tee-unverified") return `${p.YELLOW}\u26C9 Trusted Execution (unconfirmed)${p.RESET}`;
4287
+ return null;
4288
+ }
4289
+ __name(badgeLabel, "badgeLabel");
4290
+ async function badgeFor(provider, modelId, p) {
4291
+ const res = provider === "privateer" ? await accountPosture(modelId) : await verifyModelPosture(provider, modelId, {
4292
+ apiKey: provider === "nearai" ? process.env.NEARAI_API_KEY ?? process.env.NEAR_AI_API_KEY : void 0
4293
+ });
4294
+ const shield = badgeLabel(res.tier, p);
4295
+ if (shield) return shield;
4296
+ const info = TIERS[res.tier];
4297
+ return `${DOT[info.posture] ?? "\u26AA"} ${info.label}`;
4298
+ }
4299
+ __name(badgeFor, "badgeFor");
4300
+ async function updatePostureBadge(ctx, provider, modelId) {
4301
+ if (!ctx?.ui?.setStatus) return;
4302
+ if (privacyDisabled()) {
4303
+ ctx.ui.setStatus("privacy", "\u2691 privacy off");
4304
+ ctx.ui.setStatus("pi-privacy", void 0);
4305
+ return;
4306
+ }
4307
+ const p = provider ?? ctx?.model?.provider;
4308
+ const m = modelId ?? ctx?.model?.id;
4309
+ if (!p || !m) {
4310
+ ctx.ui.setStatus("privacy", void 0);
4311
+ return;
4312
+ }
4313
+ try {
4314
+ ctx.ui.setStatus("privacy", "\u26C9 \u2026");
4315
+ const badge = await badgeFor(p, m, paletteFor(ctx?.ui?.theme));
4316
+ ctx.ui.setStatus("privacy", badge);
4317
+ } catch {
4318
+ ctx.ui.setStatus("privacy", void 0);
4319
+ }
4320
+ }
4321
+ __name(updatePostureBadge, "updatePostureBadge");
4322
+
4323
+ // src/config/privacyPolicy.ts?ext=privateer-privacy
4324
+ function renderPiiAutoRedact(notice) {
4325
+ const p = cliPalette(detectScheme());
4326
+ const body = notice.startsWith("\u2691 ") ? notice.slice(2) : notice;
4327
+ return `${p.RED}\u2691${p.RESET} ${p.CYAN}${body}${p.RESET} ${p.DIM}\xB7 /privacy allow <value> if it shouldn't be masked${p.RESET}`;
4328
+ }
4329
+ __name(renderPiiAutoRedact, "renderPiiAutoRedact");
4330
+ function sharedPrivacyOptions() {
4331
+ const ambient = loadConfig();
4332
+ return {
4333
+ ...ambient,
4334
+ disabled: privacyDisabled,
4335
+ // The account channel's real posture. pi-privacy ships a `privateer` provider, but it
4336
+ // is the PUBLIC developer-key channel (sk-priv-…, server-proxied and unverifiable
4337
+ // end-to-end), so from the package alone every privateer/* model floors to
4338
+ // zdr-policy. The in-app ACCOUNT channel is a different thing — its own OAuth
4339
+ // session, account server and sealed relay — and only the host can say what it
4340
+ // resolves to: tee-verified for a quote WE checked over the sealed path,
4341
+ // tee-unverified for a proxied enclave we can't bind to this connection, zdr-policy
4342
+ // for the ZDR-channel models. Without this hook a session running an ATTESTED TEE
4343
+ // model is judged unverified, so the badge lies and the PII gate asks about every
4344
+ // turn — the over-warning c2ee0fa fixed for the terminal and nowhere else.
4345
+ resolveTier: /* @__PURE__ */ __name(async (provider, modelId) => {
4346
+ if (provider !== "privateer") return void 0;
4347
+ return (await accountPosture(modelId)).tier;
4348
+ }, "resolveTier"),
4349
+ // pi-privacy ships its own `/models`, a plain select over the models you hold keys
4350
+ // for. Ours (extensions/privateer-models.ts) is the searchable one over the whole
4351
+ // account catalog, and with both registered Pi renamed them /models:1 and /models:2 —
4352
+ // so typing `/models` could land you in the short, unsearchable list.
4353
+ modelPicker: false,
4354
+ // No quarter = unattended. No quarter also takes the whole filter down (noQuarter.ts),
4355
+ // so this only answers when the operator has run `/privacy on` with the moat down.
4356
+ // The PII send-or-redact question would stall a session the
4357
+ // operator explicitly stepped away from, so pi-privacy swallows it the SAFE way —
4358
+ // redact, then send — and reports what it masked as output instead of asking. A live
4359
+ // function, not a boolean: shift+tab / `/no-quarter` flips this mid-session and the
4360
+ // gate re-reads it on every request.
4361
+ piiUnattended: noQuarterActive,
4362
+ renderPiiAutoRedact,
4363
+ // pi-privacy's INGEST gate: credentials arriving in a tool result are masked before
4364
+ // they enter context (otherwise they're re-sent every turn and written to the session
4365
+ // file on disk). We already redact tool output in src/ext/permissionGate.ts, so its
4366
+ // default "warn" would put an interactive prompt in front of something this app has
4367
+ // always handled silently — "redact" keeps our UX and still takes the added coverage.
4368
+ //
4369
+ // The two redactors are COMPLEMENTARY, not duplicative, which is why we run both:
4370
+ // ours masks the configured provider keys by exact value (from env/config) plus the
4371
+ // provider-specific shapes (sk-/AIza/xai-/gsk_/csk-/vapi_/fw_/Z.ai, auth headers);
4372
+ // pi-privacy's catches what shows up in USER code and shell output — AWS AKIA/ASIA,
4373
+ // GitHub gh[pousr]_, JWTs, PEM private-key blocks, Slack, Stripe — none of which our
4374
+ // patterns match.
4375
+ //
4376
+ // Order between the two is NOT guaranteed: pi discovers extensions with a bare
4377
+ // readdirSync and never sorts, so it's filesystem-dependent (alphabetical on this box
4378
+ // today, not by contract). "redact" makes that moot — both handlers run
4379
+ // unconditionally and each masks its own patterns, so the surviving content is the
4380
+ // same either way. Under "warn" the order WOULD matter, since it decides whether the
4381
+ // prompt is raised on a raw key or one we already masked.
4382
+ toolResultPolicy: ambient.toolResultPolicy ?? "redact",
4383
+ // The operator's own "that is not personal data" list, read LIVE (see
4384
+ // ./piiAllow.ts): `/privacy allow @acme.com` applies on the next turn, not the next
4385
+ // launch. Ambient config entries come first — both lists are additive, since an
4386
+ // allowlist can only ever remove detection and there is no sense in which one of
4387
+ // them should silence the other.
4388
+ piiAllow: /* @__PURE__ */ __name(() => [...ambient.piiAllow ?? [], ...piiAllowEntries()], "piiAllow")
4389
+ };
4390
+ }
4391
+ __name(sharedPrivacyOptions, "sharedPrivacyOptions");
4392
+ function registerPrivacyCommand(pi) {
4393
+ const handler = /* @__PURE__ */ __name(async (args, ctx) => {
4394
+ const raw = String(args ?? "").trim();
4395
+ const [verb, ...rest] = raw.split(/\s+/);
4396
+ const value = rest.join(" ").trim();
4397
+ const notify = /* @__PURE__ */ __name((msg, level = "info") => ctx?.ui?.notify?.(msg, level), "notify");
4398
+ if (verb === "off" || verb === "disable") {
4399
+ setPrivacyDisabled(true);
4400
+ await updatePostureBadge(ctx);
4401
+ return notify(
4402
+ "\u2691 pi-privacy is completely OFF for this session. Outbound requests will not be scanned or gated for PII, and tool exfiltration, result, and downgrade guards are disabled. Run /privacy on to restore.",
4403
+ "warning"
4404
+ );
4405
+ }
4406
+ if (verb === "on" || verb === "enable" || verb === "restore") {
4407
+ setPrivacyDisabled(false);
4408
+ await updatePostureBadge(ctx);
4409
+ return notify(
4410
+ "pi-privacy is ON for this session. PII scanning, tool exfiltration guards, and privacy checks restored.",
4411
+ "info"
4412
+ );
4413
+ }
4414
+ if (verb === "status") {
4415
+ const state2 = privacyDisabled() ? "OFF (disabled for this session)" : "ON (active)";
4416
+ const mine2 = piiAllowEntries();
4417
+ return notify(
4418
+ [
4419
+ `pi-privacy status: ${state2}`,
4420
+ mine2.length ? `PII allowlist (~/.privateer/config.json):
4421
+ ${mine2.join("\n ")}` : "PII allowlist: empty",
4422
+ "Reserved shapes (example.com, loopback, noreply@\u2026) are allowed by default.",
4423
+ "Commands: /privacy off | /privacy on | /privacy status | /privacy allow <value> | /privacy unallow <value>"
4424
+ ].join("\n"),
4425
+ "info"
4426
+ );
4427
+ }
4428
+ if (verb === "allow" || verb === "unallow") {
4429
+ if (!value) return notify(`usage: /privacy ${verb} <value>`, "warning");
4430
+ const r = verb === "allow" ? addPiiAllow(value) : removePiiAllow(value);
4431
+ return notify(r.message, r.ok ? "info" : "warning");
4432
+ }
4433
+ if (verb && verb !== "help") {
4434
+ return notify(`unknown option "${verb}" \u2014 usage: /privacy [off | on | status | allow <value> | unallow <value>]`, "warning");
4435
+ }
4436
+ const state = privacyDisabled() ? "OFF (disabled for this session)" : "ON (active)";
4437
+ const mine = piiAllowEntries();
4438
+ notify(
4439
+ [
4440
+ `pi-privacy status: ${state}`,
4441
+ mine.length ? `PII allowlist (~/.privateer/config.json):
4442
+ ${mine.join("\n ")}` : "PII allowlist: empty",
4443
+ "Reserved shapes (example.com, loopback, noreply@\u2026) are allowed by default and not listed here.",
4444
+ "Commands:",
4445
+ " /privacy off \u2014 turn off pi-privacy completely for this session",
4446
+ " /privacy on \u2014 restore pi-privacy protections",
4447
+ " /privacy status \u2014 show current status and allowlist",
4448
+ " /privacy allow <value> \u2014 add value to PII allowlist",
4449
+ " /privacy unallow <value> \u2014 remove value from PII allowlist"
4450
+ ].join("\n"),
4451
+ "info"
4452
+ );
4453
+ }, "handler");
4454
+ const spec = {
4455
+ description: "Manage privacy protections: /privacy [off | on | status | allow <value> | unallow <value>]",
4456
+ handler,
4457
+ getArgumentCompletions: /* @__PURE__ */ __name((prefix) => {
4458
+ const p = prefix.trim().toLowerCase();
4459
+ const verbs = ["off", "on", "status", "allow", "unallow"];
4460
+ return verbs.filter((v) => v.startsWith(p)).map((v) => ({ value: v, label: v }));
4461
+ }, "getArgumentCompletions")
4462
+ };
4463
+ pi.registerCommand?.("privacy", spec);
4464
+ pi.registerCommand?.("pi-privacy", {
4465
+ ...spec,
4466
+ description: "Alias for /privacy: /pi-privacy [off | on | status | allow <value> | unallow <value>]"
4467
+ });
4468
+ }
4469
+ __name(registerPrivacyCommand, "registerPrivacyCommand");
4470
+ function gatingPrivacyPi(pi) {
4471
+ const on = /* @__PURE__ */ __name((event, handler) => {
4472
+ if (typeof pi?.on !== "function") return;
4473
+ if (typeof handler !== "function") return pi.on(event, handler);
4474
+ if (event === "before_provider_request" || event === "tool_call" || event === "user_bash" || event === "tool_result" || event === "model_select") {
4475
+ return pi.on(event, async (ev, ctx) => {
4476
+ if (privacyDisabled()) return void 0;
4477
+ return handler(ev, ctx);
4478
+ });
4479
+ }
4480
+ return pi.on(event, handler);
4481
+ }, "on");
4482
+ const registerCommand = /* @__PURE__ */ __name((name, spec) => {
4483
+ if (typeof pi?.registerCommand !== "function") return;
4484
+ if (name === "pii" && spec && typeof spec.handler === "function") {
4485
+ const orig = spec.handler;
4486
+ const wrappedHandler = /* @__PURE__ */ __name(async (args, ctx) => {
4487
+ const raw = String(args ?? "").trim().toLowerCase();
4488
+ const [verb] = raw.split(/\s+/);
4489
+ if (verb === "off" || verb === "disable") {
4490
+ setPrivacyDisabled(true);
4491
+ await updatePostureBadge(ctx);
4492
+ } else if (verb === "on" || verb === "enable" || verb === "restore") {
4493
+ setPrivacyDisabled(false);
4494
+ await updatePostureBadge(ctx);
4495
+ }
4496
+ return orig(args, ctx);
4497
+ }, "wrappedHandler");
4498
+ return pi.registerCommand(name, { ...spec, handler: wrappedHandler });
4499
+ }
4500
+ return pi.registerCommand(name, spec);
4501
+ }, "registerCommand");
4502
+ return new Proxy(pi, {
4503
+ get(target, prop, receiver) {
4504
+ if (prop === "on") return on;
4505
+ if (prop === "registerCommand") return registerCommand;
4506
+ const value = Reflect.get(target, prop, receiver);
4507
+ return typeof value === "function" ? value.bind(target) : value;
4508
+ }
4509
+ });
4510
+ }
4511
+ __name(gatingPrivacyPi, "gatingPrivacyPi");
4512
+ function privacyExtension() {
4513
+ const privacy2 = makePiPrivacyExtension(sharedPrivacyOptions());
4514
+ return /* @__PURE__ */ __name(function privateerPrivacyCore(pi) {
4515
+ privacy2(gatingPrivacyPi(persistingModelPicks(pi)));
4516
+ registerPrivacyCommand(pi);
4517
+ }, "privateerPrivacyCore");
4518
+ }
4519
+ __name(privacyExtension, "privacyExtension");
4520
+ function persistingModelPicks(pi) {
4521
+ if (typeof pi?.setModel !== "function") return pi;
4522
+ const setModel = /* @__PURE__ */ __name(async (model) => {
4523
+ const ok = await pi.setModel(model);
4524
+ if (ok !== false && model?.provider && model?.id) writePiDefaultModel(`${model.provider}/${model.id}`);
4525
+ return ok;
4526
+ }, "setModel");
4527
+ return new Proxy(pi, {
4528
+ get(target, prop, receiver) {
4529
+ if (prop === "setModel") return setModel;
4530
+ const value = Reflect.get(target, prop, receiver);
4531
+ return typeof value === "function" ? value.bind(target) : value;
4532
+ }
4533
+ });
4534
+ }
4535
+ __name(persistingModelPicks, "persistingModelPicks");
4536
+
4537
+ // extensions/privateer-privacy.ts?ext=privateer-privacy
4538
+ var TINFOIL_MODELS = [
4539
+ "gemma4-31b",
4540
+ "kimi-k2-6",
4541
+ "glm-5-2",
4542
+ "deepseek-v4-pro",
4543
+ "gpt-oss-120b",
4544
+ "gpt-oss-safeguard-120b",
4545
+ "llama3-3-70b"
4546
+ ];
4547
+ function tinfoilModel(id) {
4548
+ return {
4549
+ id,
4550
+ name: id,
4551
+ reasoning: false,
4552
+ // Tinfoil ids are bare here (`gemma4-31b`), so scope it before asking — the
4553
+ // allowlist is written against full `provider/model` ids. Getting this wrong is
4554
+ // not cosmetic: `input` is what Pi checks before it will send an image at all.
4555
+ input: visionInput(`tinfoil/${id}`),
4556
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
4557
+ contextWindow: 128e3,
4558
+ maxTokens: 4096
4559
+ };
4560
+ }
4561
+ __name(tinfoilModel, "tinfoilModel");
4562
+ var privacy = privacyExtension();
4563
+ function privateerPrivacy(pi) {
4564
+ privacy(pi);
4565
+ pi.registerProvider?.("tinfoil", {
4566
+ name: "Tinfoil (private TEE inference)",
4567
+ baseUrl: "https://inference.tinfoil.sh/v1",
4568
+ api: "openai-completions",
4569
+ apiKey: "${TINFOIL_API_KEY}",
4570
+ authHeader: true,
4571
+ models: TINFOIL_MODELS.map(tinfoilModel)
4572
+ });
4573
+ registerAccountModels(pi);
4574
+ }
4575
+ __name(privateerPrivacy, "privateerPrivacy");
4576
+ export {
4577
+ privateerPrivacy as default
4578
+ };