@runcore-sh/runcore 0.1.7 → 0.1.9

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 (173) hide show
  1. package/dist/access/manifest.d.ts +59 -0
  2. package/dist/access/manifest.d.ts.map +1 -0
  3. package/dist/access/manifest.js +251 -0
  4. package/dist/access/manifest.js.map +1 -0
  5. package/dist/activity/log.d.ts +1 -1
  6. package/dist/activity/log.d.ts.map +1 -1
  7. package/dist/agents/autonomous.d.ts.map +1 -1
  8. package/dist/agents/autonomous.js +38 -0
  9. package/dist/agents/autonomous.js.map +1 -1
  10. package/dist/agents/governance.d.ts +70 -0
  11. package/dist/agents/governance.d.ts.map +1 -0
  12. package/dist/agents/governance.js +220 -0
  13. package/dist/agents/governance.js.map +1 -0
  14. package/dist/agents/governed-spawn.d.ts +83 -0
  15. package/dist/agents/governed-spawn.d.ts.map +1 -0
  16. package/dist/agents/governed-spawn.js +186 -0
  17. package/dist/agents/governed-spawn.js.map +1 -0
  18. package/dist/agents/heartbeat.d.ts +91 -0
  19. package/dist/agents/heartbeat.d.ts.map +1 -0
  20. package/dist/agents/heartbeat.js +323 -0
  21. package/dist/agents/heartbeat.js.map +1 -0
  22. package/dist/agents/index.d.ts +4 -1
  23. package/dist/agents/index.d.ts.map +1 -1
  24. package/dist/agents/index.js +6 -1
  25. package/dist/agents/index.js.map +1 -1
  26. package/dist/agents/spawn-policy.d.ts +45 -0
  27. package/dist/agents/spawn-policy.d.ts.map +1 -0
  28. package/dist/agents/spawn-policy.js +202 -0
  29. package/dist/agents/spawn-policy.js.map +1 -0
  30. package/dist/alert.d.ts +16 -0
  31. package/dist/alert.d.ts.map +1 -0
  32. package/dist/alert.js +70 -0
  33. package/dist/alert.js.map +1 -0
  34. package/dist/cli.js +35 -27
  35. package/dist/cli.js.map +1 -1
  36. package/dist/credentials/store.d.ts +1 -1
  37. package/dist/credentials/store.d.ts.map +1 -1
  38. package/dist/credentials/store.js +14 -3
  39. package/dist/credentials/store.js.map +1 -1
  40. package/dist/crystallizer.d.ts +56 -0
  41. package/dist/crystallizer.d.ts.map +1 -0
  42. package/dist/crystallizer.js +159 -0
  43. package/dist/crystallizer.js.map +1 -0
  44. package/dist/distiller.d.ts +48 -0
  45. package/dist/distiller.d.ts.map +1 -0
  46. package/dist/distiller.js +140 -0
  47. package/dist/distiller.js.map +1 -0
  48. package/dist/google/auth.d.ts +2 -0
  49. package/dist/google/auth.d.ts.map +1 -1
  50. package/dist/google/auth.js +2 -0
  51. package/dist/google/auth.js.map +1 -1
  52. package/dist/integrations/gate.d.ts +40 -0
  53. package/dist/integrations/gate.d.ts.map +1 -0
  54. package/dist/integrations/gate.js +100 -0
  55. package/dist/integrations/gate.js.map +1 -0
  56. package/dist/lib/audit.d.ts +43 -0
  57. package/dist/lib/audit.d.ts.map +1 -0
  58. package/dist/lib/audit.js +120 -0
  59. package/dist/lib/audit.js.map +1 -0
  60. package/dist/lib/brain-io.d.ts.map +1 -1
  61. package/dist/lib/brain-io.js +52 -0
  62. package/dist/lib/brain-io.js.map +1 -1
  63. package/dist/lib/dpapi.d.ts +14 -0
  64. package/dist/lib/dpapi.d.ts.map +1 -0
  65. package/dist/lib/dpapi.js +104 -0
  66. package/dist/lib/dpapi.js.map +1 -0
  67. package/dist/lib/glob-match.d.ts +22 -0
  68. package/dist/lib/glob-match.d.ts.map +1 -0
  69. package/dist/lib/glob-match.js +64 -0
  70. package/dist/lib/glob-match.js.map +1 -0
  71. package/dist/lib/locked.d.ts +40 -0
  72. package/dist/lib/locked.d.ts.map +1 -0
  73. package/dist/lib/locked.js +130 -0
  74. package/dist/lib/locked.js.map +1 -0
  75. package/dist/llm/complete.d.ts.map +1 -1
  76. package/dist/llm/complete.js +5 -2
  77. package/dist/llm/complete.js.map +1 -1
  78. package/dist/llm/fetch-guard.d.ts +16 -0
  79. package/dist/llm/fetch-guard.d.ts.map +1 -0
  80. package/dist/llm/fetch-guard.js +61 -0
  81. package/dist/llm/fetch-guard.js.map +1 -0
  82. package/dist/llm/guard.d.ts +40 -0
  83. package/dist/llm/guard.d.ts.map +1 -0
  84. package/dist/llm/guard.js +88 -0
  85. package/dist/llm/guard.js.map +1 -0
  86. package/dist/llm/membrane.d.ts +46 -0
  87. package/dist/llm/membrane.d.ts.map +1 -0
  88. package/dist/llm/membrane.js +123 -0
  89. package/dist/llm/membrane.js.map +1 -0
  90. package/dist/llm/providers/index.d.ts +5 -1
  91. package/dist/llm/providers/index.d.ts.map +1 -1
  92. package/dist/llm/providers/index.js +8 -1
  93. package/dist/llm/providers/index.js.map +1 -1
  94. package/dist/llm/redact.d.ts +39 -0
  95. package/dist/llm/redact.d.ts.map +1 -0
  96. package/dist/llm/redact.js +155 -0
  97. package/dist/llm/redact.js.map +1 -0
  98. package/dist/llm/sensitive-registry.d.ts +33 -0
  99. package/dist/llm/sensitive-registry.d.ts.map +1 -0
  100. package/dist/llm/sensitive-registry.js +106 -0
  101. package/dist/llm/sensitive-registry.js.map +1 -0
  102. package/dist/mcp-server.d.ts +11 -0
  103. package/dist/mcp-server.d.ts.map +1 -0
  104. package/dist/mcp-server.js +520 -0
  105. package/dist/mcp-server.js.map +1 -0
  106. package/dist/mdns.d.ts +17 -0
  107. package/dist/mdns.d.ts.map +1 -0
  108. package/dist/mdns.js +110 -0
  109. package/dist/mdns.js.map +1 -0
  110. package/dist/nerve/push.d.ts +26 -0
  111. package/dist/nerve/push.d.ts.map +1 -0
  112. package/dist/nerve/push.js +170 -0
  113. package/dist/nerve/push.js.map +1 -0
  114. package/dist/nerve/state.d.ts +35 -0
  115. package/dist/nerve/state.d.ts.map +1 -0
  116. package/dist/nerve/state.js +257 -0
  117. package/dist/nerve/state.js.map +1 -0
  118. package/dist/resend/inbox.d.ts +23 -0
  119. package/dist/resend/inbox.d.ts.map +1 -0
  120. package/dist/resend/inbox.js +198 -0
  121. package/dist/resend/inbox.js.map +1 -0
  122. package/dist/resend/webhooks.d.ts +30 -0
  123. package/dist/resend/webhooks.d.ts.map +1 -0
  124. package/dist/resend/webhooks.js +244 -0
  125. package/dist/resend/webhooks.js.map +1 -0
  126. package/dist/server.d.ts +2 -0
  127. package/dist/server.d.ts.map +1 -1
  128. package/dist/server.js +585 -16
  129. package/dist/server.js.map +1 -1
  130. package/dist/settings.d.ts +14 -1
  131. package/dist/settings.d.ts.map +1 -1
  132. package/dist/settings.js +35 -1
  133. package/dist/settings.js.map +1 -1
  134. package/dist/updater.d.ts +32 -0
  135. package/dist/updater.d.ts.map +1 -0
  136. package/dist/updater.js +145 -0
  137. package/dist/updater.js.map +1 -0
  138. package/dist/vault/policy.d.ts +42 -0
  139. package/dist/vault/policy.d.ts.map +1 -0
  140. package/dist/vault/policy.js +159 -0
  141. package/dist/vault/policy.js.map +1 -0
  142. package/dist/vault/store.d.ts +6 -0
  143. package/dist/vault/store.d.ts.map +1 -1
  144. package/dist/vault/store.js +15 -5
  145. package/dist/vault/store.js.map +1 -1
  146. package/dist/vault/transfer.d.ts +33 -0
  147. package/dist/vault/transfer.d.ts.map +1 -0
  148. package/dist/vault/transfer.js +187 -0
  149. package/dist/vault/transfer.js.map +1 -0
  150. package/dist/voucher.d.ts +39 -0
  151. package/dist/voucher.d.ts.map +1 -0
  152. package/dist/voucher.js +105 -0
  153. package/dist/voucher.js.map +1 -0
  154. package/dist/webhooks/handlers.d.ts +10 -0
  155. package/dist/webhooks/handlers.d.ts.map +1 -1
  156. package/dist/webhooks/handlers.js +53 -0
  157. package/dist/webhooks/handlers.js.map +1 -1
  158. package/dist/webhooks/index.d.ts +2 -2
  159. package/dist/webhooks/index.d.ts.map +1 -1
  160. package/dist/webhooks/index.js +2 -2
  161. package/dist/webhooks/index.js.map +1 -1
  162. package/dist/webhooks/verify.d.ts +8 -0
  163. package/dist/webhooks/verify.d.ts.map +1 -1
  164. package/dist/webhooks/verify.js +56 -0
  165. package/dist/webhooks/verify.js.map +1 -1
  166. package/package.json +8 -2
  167. package/public/board.html +8 -3
  168. package/public/browser.html +8 -3
  169. package/public/library.html +8 -3
  170. package/public/observatory.html +8 -3
  171. package/public/ops.html +8 -3
  172. package/public/registry.html +627 -0
  173. package/public/roadmap.html +975 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Global fetch interceptor — defense-in-depth for privateMode + sensitive data redaction.
3
+ *
4
+ * Two layers:
5
+ * 1. privateMode enforcement — blocks outbound requests to known cloud LLM API hosts.
6
+ * 2. Sensitive data redaction — strips secrets/PII from LLM request bodies before network egress.
7
+ *
8
+ * The function-level guard in guard.ts → assertProviderAllowed() remains the
9
+ * primary enforcement. This is the safety net.
10
+ */
11
+ import { isPrivateMode, PrivateModeError } from "./guard.js";
12
+ import { redactRequestBody } from "./redact.js";
13
+ import { createLogger } from "../utils/logger.js";
14
+ const log = createLogger("llm.fetch-guard");
15
+ const BLOCKED_HOSTS = new Set([
16
+ "api.openai.com",
17
+ "api.anthropic.com",
18
+ "openrouter.ai",
19
+ "api.perplexity.ai",
20
+ ]);
21
+ /** Hosts where outbound request bodies should be redacted (LLM API endpoints). */
22
+ const LLM_HOSTS = new Set([
23
+ ...BLOCKED_HOSTS,
24
+ "generativelanguage.googleapis.com",
25
+ ]);
26
+ let installed = false;
27
+ /**
28
+ * Monkey-patch globalThis.fetch to block cloud LLM hosts in privateMode.
29
+ * Safe to call multiple times — only installs once.
30
+ */
31
+ export function installFetchGuard() {
32
+ if (installed)
33
+ return;
34
+ installed = true;
35
+ const originalFetch = globalThis.fetch;
36
+ globalThis.fetch = function guardedFetch(input, init) {
37
+ let hostname;
38
+ try {
39
+ const raw = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
40
+ hostname = new URL(raw).hostname;
41
+ }
42
+ catch {
43
+ // URL parse failure — skip host-based logic
44
+ }
45
+ // Layer 1: privateMode enforcement
46
+ if (isPrivateMode() && hostname && BLOCKED_HOSTS.has(hostname)) {
47
+ log.error("Fetch guard blocked outbound request", { host: hostname });
48
+ throw new PrivateModeError("cloud-api");
49
+ }
50
+ // Layer 2: redact sensitive data from LLM request bodies
51
+ if (hostname && LLM_HOSTS.has(hostname) && init?.body && typeof init.body === "string") {
52
+ const redacted = redactRequestBody(init.body);
53
+ if (redacted !== init.body) {
54
+ init = { ...init, body: redacted };
55
+ }
56
+ }
57
+ return originalFetch.call(globalThis, input, init);
58
+ };
59
+ log.info("Fetch guard installed — privateMode enforcement + sensitive data redaction");
60
+ }
61
+ //# sourceMappingURL=fetch-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-guard.js","sourceRoot":"","sources":["../../src/llm/fetch-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,GAAG,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAC;AAE5C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,gBAAgB;IAChB,mBAAmB;IACnB,eAAe;IACf,mBAAmB;CACpB,CAAC,CAAC;AAEH,kFAAkF;AAClF,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC;IACxB,GAAG,aAAa;IAChB,mCAAmC;CACpC,CAAC,CAAC;AAEH,IAAI,SAAS,GAAG,KAAK,CAAC;AAEtB;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,IAAI,SAAS;QAAE,OAAO;IACtB,SAAS,GAAG,IAAI,CAAC;IAEjB,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;IAEvC,UAAU,CAAC,KAAK,GAAG,SAAS,YAAY,CAAC,KAA6B,EAAE,IAAkB;QACxF,IAAI,QAA4B,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,KAAiB,CAAC,GAAG,CAAC;YAC3G,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,4CAA4C;QAC9C,CAAC;QAED,mCAAmC;QACnC,IAAI,aAAa,EAAE,IAAI,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,KAAK,CAAC,sCAAsC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YACtE,MAAM,IAAI,gBAAgB,CAAC,WAAkB,CAAC,CAAC;QACjD,CAAC;QAED,yDAAyD;QACzD,IAAI,QAAQ,IAAI,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,IAAI,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvF,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,QAAQ,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC3B,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;YACrC,CAAC;QACH,CAAC;QAED,OAAO,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,GAAG,CAAC,IAAI,CAAC,4EAA4E,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * LLM request guard — enforces privateMode at the request layer.
3
+ *
4
+ * When privateMode is enabled, ALL outbound LLM API calls to cloud providers
5
+ * are blocked. Only local providers (Ollama) are allowed through.
6
+ * This is network-level isolation, not just provider swapping.
7
+ */
8
+ import type { ProviderName } from "./providers/types.js";
9
+ /**
10
+ * Error thrown when a cloud LLM call is attempted in private mode.
11
+ * Intentionally loud — this should never be silently swallowed.
12
+ */
13
+ export declare class PrivateModeError extends Error {
14
+ constructor(provider: ProviderName);
15
+ }
16
+ /**
17
+ * Check whether the current settings enforce private mode.
18
+ * privateMode takes precedence over airplaneMode — it's the hard enforcement layer.
19
+ */
20
+ export declare function isPrivateMode(): boolean;
21
+ /**
22
+ * Assert that a provider is allowed under current mode.
23
+ * Throws PrivateModeError if a cloud provider is requested while privateMode is on.
24
+ * Call this at the request boundary, before any fetch() is made.
25
+ */
26
+ export declare function assertProviderAllowed(provider: ProviderName): void;
27
+ /**
28
+ * Lightweight Ollama health probe. Returns a result object instead of throwing,
29
+ * useful for non-critical paths (SSE error hints, status endpoints).
30
+ */
31
+ export declare function checkOllamaHealth(): Promise<{
32
+ ok: boolean;
33
+ message: string;
34
+ }>;
35
+ /**
36
+ * Check if Ollama is reachable. Used to fail loudly at startup when
37
+ * privateMode is on but Ollama isn't available.
38
+ */
39
+ export declare function assertOllamaAvailable(): Promise<void>;
40
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/llm/guard.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOzD;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,QAAQ,EAAE,YAAY;CAQnC;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAGvC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAOlE;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAenF;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkB3D"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * LLM request guard — enforces privateMode at the request layer.
3
+ *
4
+ * When privateMode is enabled, ALL outbound LLM API calls to cloud providers
5
+ * are blocked. Only local providers (Ollama) are allowed through.
6
+ * This is network-level isolation, not just provider swapping.
7
+ */
8
+ import { getSettings } from "../settings.js";
9
+ import { createLogger } from "../utils/logger.js";
10
+ const log = createLogger("llm.guard");
11
+ /** Providers that are allowed in private mode (local-only, no outbound network). */
12
+ const LOCAL_PROVIDERS = new Set(["ollama"]);
13
+ /**
14
+ * Error thrown when a cloud LLM call is attempted in private mode.
15
+ * Intentionally loud — this should never be silently swallowed.
16
+ */
17
+ export class PrivateModeError extends Error {
18
+ constructor(provider) {
19
+ super(`[PRIVATE MODE] Blocked outbound LLM request to "${provider}". ` +
20
+ `privateMode is enabled — only local providers (Ollama) are allowed. ` +
21
+ `Disable privateMode in brain/settings.json to use cloud providers.`);
22
+ this.name = "PrivateModeError";
23
+ }
24
+ }
25
+ /**
26
+ * Check whether the current settings enforce private mode.
27
+ * privateMode takes precedence over airplaneMode — it's the hard enforcement layer.
28
+ */
29
+ export function isPrivateMode() {
30
+ const settings = getSettings();
31
+ return settings.privateMode === true;
32
+ }
33
+ /**
34
+ * Assert that a provider is allowed under current mode.
35
+ * Throws PrivateModeError if a cloud provider is requested while privateMode is on.
36
+ * Call this at the request boundary, before any fetch() is made.
37
+ */
38
+ export function assertProviderAllowed(provider) {
39
+ if (!isPrivateMode())
40
+ return;
41
+ if (!LOCAL_PROVIDERS.has(provider)) {
42
+ log.error("Blocked cloud LLM request in private mode", { provider });
43
+ throw new PrivateModeError(provider);
44
+ }
45
+ }
46
+ /**
47
+ * Lightweight Ollama health probe. Returns a result object instead of throwing,
48
+ * useful for non-critical paths (SSE error hints, status endpoints).
49
+ */
50
+ export async function checkOllamaHealth() {
51
+ const baseUrl = process.env.OLLAMA_URL ?? "http://localhost:11434";
52
+ try {
53
+ const res = await fetch(`${baseUrl}/api/tags`, {
54
+ method: "HEAD",
55
+ signal: AbortSignal.timeout(3_000),
56
+ });
57
+ if (!res.ok) {
58
+ return { ok: false, message: `Ollama responded with status ${res.status}` };
59
+ }
60
+ return { ok: true, message: "Ollama is reachable" };
61
+ }
62
+ catch (err) {
63
+ const msg = err instanceof Error ? err.message : String(err);
64
+ return { ok: false, message: `Ollama not available at ${baseUrl}: ${msg}` };
65
+ }
66
+ }
67
+ /**
68
+ * Check if Ollama is reachable. Used to fail loudly at startup when
69
+ * privateMode is on but Ollama isn't available.
70
+ */
71
+ export async function assertOllamaAvailable() {
72
+ const baseUrl = process.env.OLLAMA_URL ?? "http://localhost:11434";
73
+ try {
74
+ const res = await fetch(`${baseUrl}/api/tags`, {
75
+ signal: AbortSignal.timeout(5_000),
76
+ });
77
+ if (!res.ok) {
78
+ throw new Error(`Ollama responded with status ${res.status}`);
79
+ }
80
+ }
81
+ catch (err) {
82
+ const msg = err instanceof Error ? err.message : String(err);
83
+ throw new Error(`[PRIVATE MODE] Ollama is required but not available at ${baseUrl}. ` +
84
+ `privateMode blocks all cloud providers — Ollama must be running. ` +
85
+ `Error: ${msg}`);
86
+ }
87
+ }
88
+ //# sourceMappingURL=guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.js","sourceRoot":"","sources":["../../src/llm/guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * PrivacyMembrane — reversible typed-placeholder redaction.
3
+ *
4
+ * Outbound: replaces sensitive spans with `<<CATEGORY_N>>` placeholders.
5
+ * Inbound: restores placeholders to original values.
6
+ *
7
+ * Same value always maps to the same placeholder across turns.
8
+ * Audit log tracks categories + counts (never raw values).
9
+ */
10
+ import type { SensitiveRegistry } from "./sensitive-registry.js";
11
+ interface AuditEntry {
12
+ timestamp: string;
13
+ direction: "apply" | "rehydrate";
14
+ categories: Record<string, number>;
15
+ }
16
+ export declare class PrivacyMembrane {
17
+ private readonly registry;
18
+ /** original value → placeholder */
19
+ private readonly forward;
20
+ /** placeholder → original value */
21
+ private readonly reverse;
22
+ /** category → next index counter */
23
+ private readonly counters;
24
+ /** audit log (categories + counts only, never values) */
25
+ private readonly audit;
26
+ constructor(registry: SensitiveRegistry);
27
+ /**
28
+ * Replace sensitive content with typed placeholders.
29
+ * Same value always gets the same placeholder.
30
+ */
31
+ apply(text: string): string;
32
+ /**
33
+ * Restore placeholders to original values.
34
+ */
35
+ rehydrate(text: string): string;
36
+ /** Get audit log (safe — no raw values). */
37
+ getAuditLog(): readonly AuditEntry[];
38
+ /** Number of unique sensitive values tracked. */
39
+ get size(): number;
40
+ /**
41
+ * Get existing placeholder or create a new one for a value+category pair.
42
+ */
43
+ private getOrCreatePlaceholder;
44
+ }
45
+ export {};
46
+ //# sourceMappingURL=membrane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"membrane.d.ts","sourceRoot":"","sources":["../../src/llm/membrane.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAQjE,UAAU,UAAU;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,GAAG,WAAW,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,qBAAa,eAAe;IAUd,OAAO,CAAC,QAAQ,CAAC,QAAQ;IATrC,mCAAmC;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,mCAAmC;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,yDAAyD;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;gBAEb,QAAQ,EAAE,iBAAiB;IAExD;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAgD3B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAyB/B,4CAA4C;IAC5C,WAAW,IAAI,SAAS,UAAU,EAAE;IAIpC,iDAAiD;IACjD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,OAAO,CAAC,sBAAsB;CAY/B"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * PrivacyMembrane — reversible typed-placeholder redaction.
3
+ *
4
+ * Outbound: replaces sensitive spans with `<<CATEGORY_N>>` placeholders.
5
+ * Inbound: restores placeholders to original values.
6
+ *
7
+ * Same value always maps to the same placeholder across turns.
8
+ * Audit log tracks categories + counts (never raw values).
9
+ */
10
+ import { createLogger } from "../utils/logger.js";
11
+ const log = createLogger("llm.membrane");
12
+ /** Matches our placeholder format: <<CATEGORY_N>> */
13
+ const PLACEHOLDER_RE = /<<([A-Z_]+_\d+)>>/g;
14
+ export class PrivacyMembrane {
15
+ registry;
16
+ /** original value → placeholder */
17
+ forward = new Map();
18
+ /** placeholder → original value */
19
+ reverse = new Map();
20
+ /** category → next index counter */
21
+ counters = new Map();
22
+ /** audit log (categories + counts only, never values) */
23
+ audit = [];
24
+ constructor(registry) {
25
+ this.registry = registry;
26
+ }
27
+ /**
28
+ * Replace sensitive content with typed placeholders.
29
+ * Same value always gets the same placeholder.
30
+ */
31
+ apply(text) {
32
+ let result = text;
33
+ const categoryCounts = {};
34
+ // 1. Explicit terms (longest-first, from registry)
35
+ for (const term of this.registry.sensitiveTerms) {
36
+ if (!result.includes(term.value))
37
+ continue;
38
+ const ph = this.getOrCreatePlaceholder(term.value, term.category);
39
+ // Use split+join for literal replacement (no regex escaping needed)
40
+ const before = result;
41
+ result = result.split(term.value).join(ph);
42
+ if (result !== before) {
43
+ const count = (before.length - result.length + ph.length * ((before.length - result.length) / (term.value.length - ph.length) || 1));
44
+ categoryCounts[term.category] = (categoryCounts[term.category] ?? 0) + 1;
45
+ }
46
+ }
47
+ // 2. Pattern-based detection (built-in + custom from registry)
48
+ for (const rule of this.registry.patterns) {
49
+ rule.pattern.lastIndex = 0;
50
+ const matches = result.match(rule.pattern);
51
+ rule.pattern.lastIndex = 0;
52
+ if (!matches)
53
+ continue;
54
+ for (const match of matches) {
55
+ // Skip false positives for CARD rule
56
+ if (rule.category === "CARD" && match.replace(/[\s-]/g, "").length < 13)
57
+ continue;
58
+ // Don't re-redact something already replaced with a placeholder
59
+ if (match.startsWith("<<") && match.endsWith(">>"))
60
+ continue;
61
+ const ph = this.getOrCreatePlaceholder(match, rule.category);
62
+ result = result.replace(match, ph);
63
+ categoryCounts[rule.category] = (categoryCounts[rule.category] ?? 0) + 1;
64
+ }
65
+ }
66
+ if (Object.keys(categoryCounts).length > 0) {
67
+ this.audit.push({
68
+ timestamp: new Date().toISOString(),
69
+ direction: "apply",
70
+ categories: categoryCounts,
71
+ });
72
+ log.info("Membrane applied", { categories: categoryCounts });
73
+ }
74
+ return result;
75
+ }
76
+ /**
77
+ * Restore placeholders to original values.
78
+ */
79
+ rehydrate(text) {
80
+ const categoryCounts = {};
81
+ const result = text.replace(PLACEHOLDER_RE, (match) => {
82
+ const original = this.reverse.get(match);
83
+ if (original) {
84
+ const cat = match.replace(/<<|_\d+>>/g, "");
85
+ categoryCounts[cat] = (categoryCounts[cat] ?? 0) + 1;
86
+ return original;
87
+ }
88
+ return match; // unknown placeholder — leave as-is
89
+ });
90
+ if (Object.keys(categoryCounts).length > 0) {
91
+ this.audit.push({
92
+ timestamp: new Date().toISOString(),
93
+ direction: "rehydrate",
94
+ categories: categoryCounts,
95
+ });
96
+ log.debug("Membrane rehydrated", { categories: categoryCounts });
97
+ }
98
+ return result;
99
+ }
100
+ /** Get audit log (safe — no raw values). */
101
+ getAuditLog() {
102
+ return this.audit;
103
+ }
104
+ /** Number of unique sensitive values tracked. */
105
+ get size() {
106
+ return this.forward.size;
107
+ }
108
+ /**
109
+ * Get existing placeholder or create a new one for a value+category pair.
110
+ */
111
+ getOrCreatePlaceholder(value, category) {
112
+ const existing = this.forward.get(value);
113
+ if (existing)
114
+ return existing;
115
+ const idx = this.counters.get(category) ?? 0;
116
+ this.counters.set(category, idx + 1);
117
+ const placeholder = `<<${category}_${idx}>>`;
118
+ this.forward.set(value, placeholder);
119
+ this.reverse.set(placeholder, value);
120
+ return placeholder;
121
+ }
122
+ }
123
+ //# sourceMappingURL=membrane.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"membrane.js","sourceRoot":"","sources":["../../src/llm/membrane.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,10 @@
3
3
  * Manages provider instances and provides a single entry point for LLM operations.
4
4
  */
5
5
  import type { LLMProvider, ProviderName } from "./types.js";
6
- /** Get a provider by name. Throws if unknown. */
6
+ /**
7
+ * Get a provider by name. Throws if unknown.
8
+ * Enforces privateMode — cloud providers are blocked when private mode is on.
9
+ */
7
10
  export declare function getProvider(name: ProviderName): LLMProvider;
8
11
  /** Register a custom provider (for plugins or testing). */
9
12
  export declare function registerProvider(provider: LLMProvider): void;
@@ -17,4 +20,5 @@ export { anthropicProvider } from "./anthropic.js";
17
20
  export { openAIProvider } from "./openai.js";
18
21
  export { ollamaProvider } from "./ollama.js";
19
22
  export { LLMError, classifyApiError } from "../errors.js";
23
+ export { PrivateModeError, isPrivateMode } from "../guard.js";
20
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAkB3E,iDAAiD;AACjD,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAI3D;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAG5D;AAED,0CAA0C;AAC1C,wBAAgB,aAAa,IAAI,YAAY,EAAE,CAE9C;AAED,qFAAqF;AACrF,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAQrE;AAID,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/llm/providers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAmB3E;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,WAAW,CAO3D;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAG5D;AAED,0CAA0C;AAC1C,wBAAgB,aAAa,IAAI,YAAY,EAAE,CAE9C;AAED,qFAAqF;AACrF,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAQrE;AAID,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -6,6 +6,7 @@ import { openRouterProvider } from "./openrouter.js";
6
6
  import { anthropicProvider } from "./anthropic.js";
7
7
  import { openAIProvider } from "./openai.js";
8
8
  import { ollamaProvider } from "./ollama.js";
9
+ import { assertProviderAllowed } from "../guard.js";
9
10
  import { createLogger } from "../../utils/logger.js";
10
11
  const log = createLogger("llm.providers");
11
12
  // ── Registry ────────────────────────────────────────────────────────────────
@@ -15,8 +16,13 @@ const providers = new Map([
15
16
  ["openai", openAIProvider],
16
17
  ["ollama", ollamaProvider],
17
18
  ]);
18
- /** Get a provider by name. Throws if unknown. */
19
+ /**
20
+ * Get a provider by name. Throws if unknown.
21
+ * Enforces privateMode — cloud providers are blocked when private mode is on.
22
+ */
19
23
  export function getProvider(name) {
24
+ // Guard: block cloud providers in private mode BEFORE returning the provider
25
+ assertProviderAllowed(name);
20
26
  const provider = providers.get(name);
21
27
  if (!provider)
22
28
  throw new Error(`Unknown LLM provider: ${name}`);
@@ -44,4 +50,5 @@ export { anthropicProvider } from "./anthropic.js";
44
50
  export { openAIProvider } from "./openai.js";
45
51
  export { ollamaProvider } from "./ollama.js";
46
52
  export { LLMError, classifyApiError } from "../errors.js";
53
+ export { PrivateModeError, isPrivateMode } from "../guard.js";
47
54
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/llm/providers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,GAAG,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;AAE1C,+EAA+E;AAE/E,MAAM,SAAS,GAAG,IAAI,GAAG,CAA4B;IACnD,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAClC,CAAC,WAAW,EAAE,iBAAiB,CAAC;IAChC,CAAC,QAAQ,EAAE,cAAc,CAAC;IAC1B,CAAC,QAAQ,EAAE,cAAc,CAAC;CAC3B,CAAC,CAAC;AAEH,iDAAiD;AACjD,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAAC,QAAqB;IACpD,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,aAAa;IAC3B,OAAO,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE;QACtD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACjC,CAAC,CAAC,CACH,CAAC;IACF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAqB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AAC9D,CAAC;AAKD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/llm/providers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,GAAG,GAAG,YAAY,CAAC,eAAe,CAAC,CAAC;AAE1C,+EAA+E;AAE/E,MAAM,SAAS,GAAG,IAAI,GAAG,CAA4B;IACnD,CAAC,YAAY,EAAE,kBAAkB,CAAC;IAClC,CAAC,WAAW,EAAE,iBAAiB,CAAC;IAChC,CAAC,QAAQ,EAAE,cAAc,CAAC;IAC1B,CAAC,QAAQ,EAAE,cAAc,CAAC;CAC3B,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,6EAA6E;IAC7E,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAE5B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAAC,QAAqB;IACpD,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvC,GAAG,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,aAAa;IAC3B,OAAO,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE;QACtD,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACjC,CAAC,CAAC,CACH,CAAC;IACF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAqB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AAC9D,CAAC;AAKD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Sensitive data redaction for outbound LLM requests.
3
+ * Pattern-based detection — if it looks like a secret, redact it before network egress.
4
+ * Runs inside the fetch guard, after message assembly, before the wire.
5
+ *
6
+ * When a PrivacyMembrane is active, delegates to it for reversible typed-placeholder
7
+ * redaction. Falls back to one-way [REDACTED:category] replacement otherwise.
8
+ */
9
+ import type { PrivacyMembrane } from "./membrane.js";
10
+ /** Set the active PrivacyMembrane for reversible redaction. */
11
+ export declare function setActiveMembrane(membrane: PrivacyMembrane): void;
12
+ /** Get the active membrane (or null if none). */
13
+ export declare function getActiveMembrane(): PrivacyMembrane | null;
14
+ /**
15
+ * Rehydrate placeholders in an LLM response back to original values.
16
+ * No-op if no membrane is active.
17
+ */
18
+ export declare function rehydrateResponse(text: string): string;
19
+ /** Track redaction stats per request for audit logging. */
20
+ interface RedactionStats {
21
+ totalRedactions: number;
22
+ categories: Record<string, number>;
23
+ }
24
+ /**
25
+ * Redact sensitive patterns from a string (one-way fallback).
26
+ * Returns the cleaned string and stats about what was redacted.
27
+ */
28
+ export declare function redactSensitive(text: string): {
29
+ cleaned: string;
30
+ stats: RedactionStats;
31
+ };
32
+ /**
33
+ * Redact sensitive data from an LLM request body (JSON string).
34
+ * When a membrane is active, delegates to it for reversible redaction.
35
+ * Otherwise falls back to one-way pattern replacement.
36
+ */
37
+ export declare function redactRequestBody(bodyStr: string): string;
38
+ export {};
39
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/llm/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AASrD,+DAA+D;AAC/D,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAGjE;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,IAAI,eAAe,GAAG,IAAI,CAE1D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGtD;AAoCD,2DAA2D;AAC3D,UAAU,cAAc;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CA0BxF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAqEzD"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Sensitive data redaction for outbound LLM requests.
3
+ * Pattern-based detection — if it looks like a secret, redact it before network egress.
4
+ * Runs inside the fetch guard, after message assembly, before the wire.
5
+ *
6
+ * When a PrivacyMembrane is active, delegates to it for reversible typed-placeholder
7
+ * redaction. Falls back to one-way [REDACTED:category] replacement otherwise.
8
+ */
9
+ import { createLogger } from "../utils/logger.js";
10
+ const log = createLogger("llm.redact");
11
+ // --- Active membrane (singleton, set at startup) ---
12
+ let activeMembrane = null;
13
+ /** Set the active PrivacyMembrane for reversible redaction. */
14
+ export function setActiveMembrane(membrane) {
15
+ activeMembrane = membrane;
16
+ log.info("PrivacyMembrane activated");
17
+ }
18
+ /** Get the active membrane (or null if none). */
19
+ export function getActiveMembrane() {
20
+ return activeMembrane;
21
+ }
22
+ /**
23
+ * Rehydrate placeholders in an LLM response back to original values.
24
+ * No-op if no membrane is active.
25
+ */
26
+ export function rehydrateResponse(text) {
27
+ if (!activeMembrane)
28
+ return text;
29
+ return activeMembrane.rehydrate(text);
30
+ }
31
+ // --- One-way fallback (original behavior) ---
32
+ /** Placeholder for redacted content. Includes the category so the LLM knows what was removed. */
33
+ function placeholder(category) {
34
+ return `[REDACTED:${category}]`;
35
+ }
36
+ /**
37
+ * Redaction rules: [pattern, category, description].
38
+ * Order matters — more specific patterns first to avoid partial matches.
39
+ */
40
+ const RULES = [
41
+ // SSN: 123-45-6789 or 123 45 6789
42
+ [/\b\d{3}[-\s]\d{2}[-\s]\d{4}\b/g, "SSN"],
43
+ // Credit card: 13-19 digits, optionally grouped by spaces or dashes
44
+ [/\b(?:\d[ -]*?){13,19}\b/g, "CARD"],
45
+ // API keys: common prefixes (sk-, pk-, api-, key-, token_)
46
+ [/\b(?:sk|pk|api|key|token)[_-][A-Za-z0-9_-]{20,}\b/g, "API_KEY"],
47
+ // Bearer tokens in content (not headers — those are legitimate)
48
+ [/\bBearer\s+[A-Za-z0-9_.-]{20,}\b/g, "BEARER_TOKEN"],
49
+ // AWS access keys: AKIA + 16 alphanumeric
50
+ [/\bAKIA[0-9A-Z]{16}\b/g, "AWS_KEY"],
51
+ // Private keys (PEM blocks)
52
+ [/-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----[\s\S]*?-----END\s+(RSA\s+)?PRIVATE\s+KEY-----/g, "PRIVATE_KEY"],
53
+ // Generic long hex/base64 secrets (40+ chars, likely tokens)
54
+ [/\b[A-Fa-f0-9]{40,}\b/g, "HEX_SECRET"],
55
+ ];
56
+ /**
57
+ * Redact sensitive patterns from a string (one-way fallback).
58
+ * Returns the cleaned string and stats about what was redacted.
59
+ */
60
+ export function redactSensitive(text) {
61
+ const stats = { totalRedactions: 0, categories: {} };
62
+ let cleaned = text;
63
+ for (const [pattern, category] of RULES) {
64
+ // Reset lastIndex for global patterns
65
+ pattern.lastIndex = 0;
66
+ const matches = cleaned.match(pattern);
67
+ if (matches) {
68
+ // Filter out false positives: skip short numeric sequences for CARD rule
69
+ const validMatches = category === "CARD"
70
+ ? matches.filter((m) => m.replace(/[\s-]/g, "").length >= 13)
71
+ : matches;
72
+ if (validMatches.length > 0) {
73
+ for (const match of validMatches) {
74
+ cleaned = cleaned.replace(match, placeholder(category));
75
+ }
76
+ stats.totalRedactions += validMatches.length;
77
+ stats.categories[category] = (stats.categories[category] ?? 0) + validMatches.length;
78
+ }
79
+ }
80
+ pattern.lastIndex = 0;
81
+ }
82
+ return { cleaned, stats };
83
+ }
84
+ /**
85
+ * Redact sensitive data from an LLM request body (JSON string).
86
+ * When a membrane is active, delegates to it for reversible redaction.
87
+ * Otherwise falls back to one-way pattern replacement.
88
+ */
89
+ export function redactRequestBody(bodyStr) {
90
+ let parsed;
91
+ try {
92
+ parsed = JSON.parse(bodyStr);
93
+ }
94
+ catch {
95
+ return bodyStr; // not JSON, pass through
96
+ }
97
+ // Only process if it looks like an LLM API request (has messages array)
98
+ const messages = parsed.messages;
99
+ if (!Array.isArray(messages))
100
+ return bodyStr;
101
+ // Delegate to membrane when active (reversible typed placeholders)
102
+ if (activeMembrane) {
103
+ for (const msg of messages) {
104
+ if (typeof msg.content === "string") {
105
+ msg.content = activeMembrane.apply(msg.content);
106
+ }
107
+ if (Array.isArray(msg.content)) {
108
+ for (const block of msg.content) {
109
+ if (block.type === "text" && typeof block.text === "string") {
110
+ block.text = activeMembrane.apply(block.text);
111
+ }
112
+ }
113
+ }
114
+ }
115
+ return JSON.stringify(parsed);
116
+ }
117
+ // Fallback: one-way redaction
118
+ let totalRedactions = 0;
119
+ const allCategories = {};
120
+ for (const msg of messages) {
121
+ if (typeof msg.content === "string") {
122
+ const { cleaned, stats } = redactSensitive(msg.content);
123
+ if (stats.totalRedactions > 0) {
124
+ msg.content = cleaned;
125
+ totalRedactions += stats.totalRedactions;
126
+ for (const [cat, count] of Object.entries(stats.categories)) {
127
+ allCategories[cat] = (allCategories[cat] ?? 0) + count;
128
+ }
129
+ }
130
+ }
131
+ // Handle Anthropic-style content blocks: [{ type: "text", text: "..." }]
132
+ if (Array.isArray(msg.content)) {
133
+ for (const block of msg.content) {
134
+ if (block.type === "text" && typeof block.text === "string") {
135
+ const { cleaned, stats } = redactSensitive(block.text);
136
+ if (stats.totalRedactions > 0) {
137
+ block.text = cleaned;
138
+ totalRedactions += stats.totalRedactions;
139
+ for (const [cat, count] of Object.entries(stats.categories)) {
140
+ allCategories[cat] = (allCategories[cat] ?? 0) + count;
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ if (totalRedactions > 0) {
148
+ log.warn("Redacted sensitive data from outbound LLM request", {
149
+ redactions: totalRedactions,
150
+ categories: allCategories,
151
+ });
152
+ }
153
+ return JSON.stringify(parsed);
154
+ }
155
+ //# sourceMappingURL=redact.js.map