@4onstudios/iris-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +690 -0
  3. package/dist/api/acp/acpServer.d.ts +23 -0
  4. package/dist/api/acp/acpServer.js +814 -0
  5. package/dist/api/acp/index.d.ts +2 -0
  6. package/dist/api/acp/index.js +2 -0
  7. package/dist/api/acp/irisClient.d.ts +58 -0
  8. package/dist/api/acp/irisClient.js +214 -0
  9. package/dist/api/agent.d.ts +14 -0
  10. package/dist/api/agent.js +4171 -0
  11. package/dist/api/backendService.d.ts +4 -0
  12. package/dist/api/backendService.js +14 -0
  13. package/dist/api/core/agent/host/AgentContract.d.ts +82 -0
  14. package/dist/api/core/agent/host/AgentContract.js +1 -0
  15. package/dist/api/core/agent/host/AgentRegistry.d.ts +24 -0
  16. package/dist/api/core/agent/host/AgentRegistry.js +125 -0
  17. package/dist/api/core/agent/host/defaultRegistry.d.ts +15 -0
  18. package/dist/api/core/agent/host/defaultRegistry.js +46 -0
  19. package/dist/api/core/agent/host/externalAgentLifecycle.d.ts +65 -0
  20. package/dist/api/core/agent/host/externalAgentLifecycle.js +117 -0
  21. package/dist/api/core/agent/host/hostSessionManager.d.ts +111 -0
  22. package/dist/api/core/agent/host/hostSessionManager.js +352 -0
  23. package/dist/api/core/agent/host/index.d.ts +5 -0
  24. package/dist/api/core/agent/host/index.js +5 -0
  25. package/dist/api/core/agent/index.d.ts +87 -0
  26. package/dist/api/core/agent/index.js +1024 -0
  27. package/dist/api/core/agent/publicContracts.d.ts +35 -0
  28. package/dist/api/core/agent/publicContracts.js +103 -0
  29. package/dist/api/core/agent/tools/applyDiff.d.ts +51 -0
  30. package/dist/api/core/agent/tools/applyDiff.js +131 -0
  31. package/dist/api/core/agent/tools/backgroundTasks.d.ts +240 -0
  32. package/dist/api/core/agent/tools/backgroundTasks.js +313 -0
  33. package/dist/api/core/agent/tools/createDirectory.d.ts +43 -0
  34. package/dist/api/core/agent/tools/createDirectory.js +77 -0
  35. package/dist/api/core/agent/tools/deleteFile.d.ts +37 -0
  36. package/dist/api/core/agent/tools/deleteFile.js +66 -0
  37. package/dist/api/core/agent/tools/editFile.d.ts +73 -0
  38. package/dist/api/core/agent/tools/editFile.js +173 -0
  39. package/dist/api/core/agent/tools/executeCommand.d.ts +59 -0
  40. package/dist/api/core/agent/tools/executeCommand.js +250 -0
  41. package/dist/api/core/agent/tools/fileContent.d.ts +54 -0
  42. package/dist/api/core/agent/tools/fileContent.js +227 -0
  43. package/dist/api/core/agent/tools/findDefinition.d.ts +72 -0
  44. package/dist/api/core/agent/tools/findDefinition.js +109 -0
  45. package/dist/api/core/agent/tools/findReferences.d.ts +86 -0
  46. package/dist/api/core/agent/tools/findReferences.js +135 -0
  47. package/dist/api/core/agent/tools/formatDocument.d.ts +67 -0
  48. package/dist/api/core/agent/tools/formatDocument.js +90 -0
  49. package/dist/api/core/agent/tools/getCodeActions.d.ts +77 -0
  50. package/dist/api/core/agent/tools/getCodeActions.js +97 -0
  51. package/dist/api/core/agent/tools/getCodeCompletion.d.ts +77 -0
  52. package/dist/api/core/agent/tools/getCodeCompletion.js +174 -0
  53. package/dist/api/core/agent/tools/getCodeContext.d.ts +14 -0
  54. package/dist/api/core/agent/tools/getCodeContext.js +282 -0
  55. package/dist/api/core/agent/tools/getSignatureHelp.d.ts +61 -0
  56. package/dist/api/core/agent/tools/getSignatureHelp.js +85 -0
  57. package/dist/api/core/agent/tools/getSymbols.d.ts +50 -0
  58. package/dist/api/core/agent/tools/getSymbols.js +164 -0
  59. package/dist/api/core/agent/tools/getSymbolsLSP.d.ts +58 -0
  60. package/dist/api/core/agent/tools/getSymbolsLSP.js +147 -0
  61. package/dist/api/core/agent/tools/getTypeInfo.d.ts +70 -0
  62. package/dist/api/core/agent/tools/getTypeInfo.js +84 -0
  63. package/dist/api/core/agent/tools/getWorkspaceInfo.d.ts +80 -0
  64. package/dist/api/core/agent/tools/getWorkspaceInfo.js +281 -0
  65. package/dist/api/core/agent/tools/getWorkspaceSymbols.d.ts +53 -0
  66. package/dist/api/core/agent/tools/getWorkspaceSymbols.js +97 -0
  67. package/dist/api/core/agent/tools/grepSearch.d.ts +100 -0
  68. package/dist/api/core/agent/tools/grepSearch.js +211 -0
  69. package/dist/api/core/agent/tools/languageModelToolsIntegration.d.ts +13 -0
  70. package/dist/api/core/agent/tools/languageModelToolsIntegration.js +22 -0
  71. package/dist/api/core/agent/tools/listDirectory.d.ts +66 -0
  72. package/dist/api/core/agent/tools/listDirectory.js +161 -0
  73. package/dist/api/core/agent/tools/mcpTools.d.ts +48 -0
  74. package/dist/api/core/agent/tools/mcpTools.js +442 -0
  75. package/dist/api/core/agent/tools/queryKnowledgeGraph.d.ts +120 -0
  76. package/dist/api/core/agent/tools/queryKnowledgeGraph.js +306 -0
  77. package/dist/api/core/agent/tools/readFile.d.ts +92 -0
  78. package/dist/api/core/agent/tools/readFile.js +337 -0
  79. package/dist/api/core/agent/tools/renameFile.d.ts +49 -0
  80. package/dist/api/core/agent/tools/renameFile.js +86 -0
  81. package/dist/api/core/agent/tools/renameSymbol.d.ts +70 -0
  82. package/dist/api/core/agent/tools/renameSymbol.js +106 -0
  83. package/dist/api/core/agent/tools/runTerminalCommand.d.ts +72 -0
  84. package/dist/api/core/agent/tools/runTerminalCommand.js +69 -0
  85. package/dist/api/core/agent/tools/searchFiles.d.ts +82 -0
  86. package/dist/api/core/agent/tools/searchFiles.js +280 -0
  87. package/dist/api/core/agent/tools/terminalAutoApprove.d.ts +3 -0
  88. package/dist/api/core/agent/tools/terminalAutoApprove.js +676 -0
  89. package/dist/api/core/agent/tools/treeTraversal.d.ts +60 -0
  90. package/dist/api/core/agent/tools/treeTraversal.js +217 -0
  91. package/dist/api/core/agent/tools/webSearch.d.ts +37 -0
  92. package/dist/api/core/agent/tools/webSearch.js +80 -0
  93. package/dist/api/core/agent/tools/writeFile.d.ts +59 -0
  94. package/dist/api/core/agent/tools/writeFile.js +97 -0
  95. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.d.ts +12 -0
  96. package/dist/api/core/agent/utils/capturedWorkspaceMutationBridge.js +67 -0
  97. package/dist/api/core/agent/utils/diffUtils.d.ts +14 -0
  98. package/dist/api/core/agent/utils/diffUtils.js +39 -0
  99. package/dist/api/core/agent/utils/environmentSnapshot.d.ts +42 -0
  100. package/dist/api/core/agent/utils/environmentSnapshot.js +213 -0
  101. package/dist/api/core/agent/utils/errorRecovery.d.ts +49 -0
  102. package/dist/api/core/agent/utils/errorRecovery.js +229 -0
  103. package/dist/api/core/agent/utils/multimodalTokenLimiter.d.ts +8 -0
  104. package/dist/api/core/agent/utils/multimodalTokenLimiter.js +238 -0
  105. package/dist/api/core/agent/utils/openRouterModelSettings.d.ts +12 -0
  106. package/dist/api/core/agent/utils/openRouterModelSettings.js +34 -0
  107. package/dist/api/core/agent/utils/pathRecovery.d.ts +1 -0
  108. package/dist/api/core/agent/utils/pathRecovery.js +29 -0
  109. package/dist/api/core/agent/utils/repoMapIndex.d.ts +3 -0
  110. package/dist/api/core/agent/utils/repoMapIndex.js +197 -0
  111. package/dist/api/core/agent/utils/skillsDiscovery.d.ts +10 -0
  112. package/dist/api/core/agent/utils/skillsDiscovery.js +50 -0
  113. package/dist/api/core/agent/utils/toolCallBudget.d.ts +9 -0
  114. package/dist/api/core/agent/utils/toolCallBudget.js +55 -0
  115. package/dist/api/core/agent/utils/toolLifecycle.d.ts +30 -0
  116. package/dist/api/core/agent/utils/toolLifecycle.js +361 -0
  117. package/dist/api/core/agent/utils/toolResultSafetyProcessor.d.ts +12 -0
  118. package/dist/api/core/agent/utils/toolResultSafetyProcessor.js +57 -0
  119. package/dist/api/core/agent/utils/workspaceMutationBridge.d.ts +13 -0
  120. package/dist/api/core/agent/utils/workspaceMutationBridge.js +149 -0
  121. package/dist/api/core/agent/utils/workspacePathGuard.d.ts +7 -0
  122. package/dist/api/core/agent/utils/workspacePathGuard.js +79 -0
  123. package/dist/api/core/containers/chat/toolResultSerialization.d.ts +8 -0
  124. package/dist/api/core/containers/chat/toolResultSerialization.js +71 -0
  125. package/dist/api/core/library/BrowserManager.d.ts +11 -0
  126. package/dist/api/core/library/BrowserManager.js +66 -0
  127. package/dist/api/core/library/desktopWorkspace.d.ts +1 -0
  128. package/dist/api/core/library/desktopWorkspace.js +3 -0
  129. package/dist/api/core/library/extensionManager.d.ts +10 -0
  130. package/dist/api/core/library/extensionManager.js +27 -0
  131. package/dist/api/core/library/knowledgeGraph.d.ts +268 -0
  132. package/dist/api/core/library/knowledgeGraph.js +989 -0
  133. package/dist/api/core/library/languageModelTools.d.ts +141 -0
  134. package/dist/api/core/library/languageModelTools.js +207 -0
  135. package/dist/api/core/library/localRuntime.d.ts +23 -0
  136. package/dist/api/core/library/localRuntime.js +144 -0
  137. package/dist/api/core/library/lsp/coreLsp.d.ts +367 -0
  138. package/dist/api/core/library/lsp/coreLsp.js +2076 -0
  139. package/dist/api/core/library/lsp/protocol.d.ts +25 -0
  140. package/dist/api/core/library/lsp/protocol.js +1 -0
  141. package/dist/api/core/library/lsp/serverManager.d.ts +37 -0
  142. package/dist/api/core/library/lsp/serverManager.js +427 -0
  143. package/dist/api/core/library/mcpServerProvider.d.ts +69 -0
  144. package/dist/api/core/library/mcpServerProvider.js +152 -0
  145. package/dist/api/core/library/mcpSettings.d.ts +20 -0
  146. package/dist/api/core/library/mcpSettings.js +131 -0
  147. package/dist/api/core/library/patternMatcher.d.ts +66 -0
  148. package/dist/api/core/library/patternMatcher.js +284 -0
  149. package/dist/api/core/library/regexEscape.d.ts +4 -0
  150. package/dist/api/core/library/regexEscape.js +4 -0
  151. package/dist/api/core/library/runtimeEventBus.d.ts +27 -0
  152. package/dist/api/core/library/runtimeEventBus.js +78 -0
  153. package/dist/api/core/library/safetyMiddleware.d.ts +98 -0
  154. package/dist/api/core/library/safetyMiddleware.js +215 -0
  155. package/dist/api/core/library/tauri.d.ts +71 -0
  156. package/dist/api/core/library/tauri.js +222 -0
  157. package/dist/api/core/library/tauriImport.d.ts +5 -0
  158. package/dist/api/core/library/tauriImport.js +7 -0
  159. package/dist/api/core/library/terminalAutoApproveSettings.d.ts +32 -0
  160. package/dist/api/core/library/terminalAutoApproveSettings.js +529 -0
  161. package/dist/api/core/library/workspaceIdentity.d.ts +10 -0
  162. package/dist/api/core/library/workspaceIdentity.js +49 -0
  163. package/dist/api/core/library/workspaceSummary.d.ts +19 -0
  164. package/dist/api/core/library/workspaceSummary.js +35 -0
  165. package/dist/api/core/skills/agent-customization/SKILL.md +27 -0
  166. package/dist/api/core/skills/bug-fix/SKILL.md +21 -0
  167. package/dist/api/core/skills/create-pr/SKILL.md +32 -0
  168. package/dist/api/core/skills/dev-server/SKILL.md +20 -0
  169. package/dist/api/core/skills/fix-suggestions/SKILL.md +21 -0
  170. package/dist/api/core/skills/github-search/SKILL.md +21 -0
  171. package/dist/api/core/skills/github-summary/SKILL.md +25 -0
  172. package/dist/api/core/skills/integration-tests/SKILL.md +29 -0
  173. package/dist/api/core/skills/pr-comments/SKILL.md +23 -0
  174. package/dist/api/core/skills/project-setup/SKILL.md +20 -0
  175. package/dist/api/core/skills/search-integration/SKILL.md +19 -0
  176. package/dist/api/core/skills/search-results/SKILL.md +19 -0
  177. package/dist/api/core/skills/typescript-upgrade/SKILL.md +21 -0
  178. package/dist/api/data/runStore.d.ts +46 -0
  179. package/dist/api/data/runStore.js +244 -0
  180. package/dist/api/helpers/agentUtils.d.ts +11 -0
  181. package/dist/api/helpers/agentUtils.js +23 -0
  182. package/dist/api/helpers/modelTokenLimits.d.ts +2 -0
  183. package/dist/api/helpers/modelTokenLimits.js +158 -0
  184. package/dist/api/helpers/observationalMemory.d.ts +10 -0
  185. package/dist/api/helpers/observationalMemory.js +36 -0
  186. package/dist/api/helpers/promptBudget.d.ts +30 -0
  187. package/dist/api/helpers/promptBudget.js +216 -0
  188. package/dist/api/helpers/resolveImageMessageParts.d.ts +9 -0
  189. package/dist/api/helpers/resolveImageMessageParts.js +140 -0
  190. package/dist/api/helpers/slashCommands.d.ts +31 -0
  191. package/dist/api/helpers/slashCommands.js +129 -0
  192. package/dist/api/helpers/tokenUsage.d.ts +19 -0
  193. package/dist/api/helpers/tokenUsage.js +81 -0
  194. package/dist/api/routes/fileRoutes.d.ts +2 -0
  195. package/dist/api/routes/fileRoutes.js +108 -0
  196. package/dist/api/routes/lspDocumentRoutes.d.ts +4 -0
  197. package/dist/api/routes/lspDocumentRoutes.js +84 -0
  198. package/dist/api/routes/lspHierarchyRoutes.d.ts +4 -0
  199. package/dist/api/routes/lspHierarchyRoutes.js +122 -0
  200. package/dist/api/routes/lspPositionRoutes.d.ts +4 -0
  201. package/dist/api/routes/lspPositionRoutes.js +263 -0
  202. package/dist/api/routes/lspQueryRoutes.d.ts +4 -0
  203. package/dist/api/routes/lspQueryRoutes.js +679 -0
  204. package/dist/api/routes/lspResolveRoutes.d.ts +4 -0
  205. package/dist/api/routes/lspResolveRoutes.js +143 -0
  206. package/dist/api/routes/lspSemanticDocumentRoutes.d.ts +4 -0
  207. package/dist/api/routes/lspSemanticDocumentRoutes.js +98 -0
  208. package/dist/api/routes/semanticRoutes.d.ts +2 -0
  209. package/dist/api/routes/semanticRoutes.js +126 -0
  210. package/dist/cli.d.ts +8 -0
  211. package/dist/cli.js +160 -0
  212. package/dist/index.d.ts +1 -0
  213. package/dist/index.js +1 -0
  214. package/dist/server.d.ts +2 -0
  215. package/dist/server.js +38 -0
  216. package/package.json +100 -0
@@ -0,0 +1,78 @@
1
+ class RuntimeEventBus {
2
+ listeners = new Map();
3
+ buffer = new Map();
4
+ eventHistory = [];
5
+ maxHistorySize = 100;
6
+ subscribe(event, listener) {
7
+ if (!this.listeners.has(event)) {
8
+ this.listeners.set(event, new Set());
9
+ }
10
+ this.listeners.get(event).add(listener);
11
+ if (this.buffer.has(event)) {
12
+ try {
13
+ listener(this.buffer.get(event));
14
+ }
15
+ catch (error) {
16
+ console.error(`Error in buffered event delivery for ${event}:`, error);
17
+ }
18
+ }
19
+ return () => {
20
+ const listeners = this.listeners.get(event);
21
+ if (listeners) {
22
+ listeners.delete(listener);
23
+ if (listeners.size === 0) {
24
+ this.listeners.delete(event);
25
+ }
26
+ }
27
+ };
28
+ }
29
+ emit(event, data = undefined) {
30
+ this.trackEvent(event, data);
31
+ const listeners = this.listeners.get(event);
32
+ if (!listeners || listeners.size === 0) {
33
+ this.buffer.set(event, data);
34
+ return;
35
+ }
36
+ this.buffer.delete(event);
37
+ listeners.forEach((listener) => {
38
+ try {
39
+ listener(data);
40
+ }
41
+ catch (error) {
42
+ console.error(`Error in listener for event ${event}:`, error);
43
+ }
44
+ });
45
+ }
46
+ trackEvent(event, data) {
47
+ this.eventHistory.push({
48
+ event,
49
+ timestamp: Date.now(),
50
+ dataSize: data ? JSON.stringify(data).length : 0,
51
+ });
52
+ if (this.eventHistory.length > this.maxHistorySize) {
53
+ this.eventHistory.shift();
54
+ }
55
+ }
56
+ getStatistics() {
57
+ const stats = {};
58
+ this.eventHistory.forEach(({ event }) => {
59
+ stats[event] = (stats[event] || 0) + 1;
60
+ });
61
+ return stats;
62
+ }
63
+ clear() {
64
+ this.listeners.clear();
65
+ this.buffer.clear();
66
+ }
67
+ }
68
+ export const runtimeEventBus = new RuntimeEventBus();
69
+ export const RuntimeEvents = {
70
+ FILE_OPEN: "file:open",
71
+ CODE_CHANGE: "code:change",
72
+ CONTEXT_UPDATE: "kg:context:update",
73
+ CONTEXT_QUERY: "kg:context:query",
74
+ TOKEN_USAGE: "kg:token:usage",
75
+ CACHE_HIT: "kg:cache:hit",
76
+ CACHE_MISS: "kg:cache:miss",
77
+ };
78
+ export default RuntimeEventBus;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Safety Middleware for LLM Requests
3
+ * Detects and redacts PII and secrets before passing to LLM providers
4
+ */
5
+ type SafetyPattern = {
6
+ name: string;
7
+ regex: RegExp;
8
+ };
9
+ type Redaction = {
10
+ type: string;
11
+ value: string;
12
+ position: number;
13
+ length: number;
14
+ };
15
+ export type SafetyMessage = {
16
+ role: string;
17
+ content: string;
18
+ [key: string]: unknown;
19
+ };
20
+ export type SafetyRequest = {
21
+ messages: SafetyMessage[];
22
+ metadata?: Record<string, unknown>;
23
+ [key: string]: unknown;
24
+ };
25
+ type AuditEvent = {
26
+ timestamp: string;
27
+ redactionCount: number;
28
+ redactionTypes: string[];
29
+ hasSecrets: boolean;
30
+ hasPII: boolean;
31
+ processingTime?: number;
32
+ };
33
+ type BlockEvent = {
34
+ reason: string;
35
+ timestamp: string;
36
+ redactionCount: number;
37
+ redactionTypes: string[];
38
+ };
39
+ type SafetyOptions = {
40
+ blockOnSecrets?: boolean;
41
+ blockOnPII?: boolean;
42
+ onAudit?: ((event: AuditEvent) => void) | null;
43
+ onBlock?: ((event: BlockEvent) => void) | null;
44
+ };
45
+ export declare const PII_PATTERNS: SafetyPattern[];
46
+ export declare const SECRET_PATTERNS: SafetyPattern[];
47
+ /**
48
+ * Redact sensitive information from text
49
+ * @param {string} text - Text to scan and redact
50
+ * @returns {{ output: string, redactions: Array<{type: string, value: string, position: number}> }}
51
+ */
52
+ export declare function redact(text: string): {
53
+ output: string;
54
+ redactions: Redaction[];
55
+ };
56
+ /**
57
+ * Redact sensitive information from messages array
58
+ * @param {Array<{role: string, content: string}>} messages
59
+ * @returns {{safeMessages: Array, totalRedactions: Array}}
60
+ */
61
+ export declare function redactMessages(messages: SafetyMessage[]): {
62
+ safeMessages: {
63
+ content: string;
64
+ role: string;
65
+ }[];
66
+ totalRedactions: {
67
+ messageIndex: number;
68
+ role: string;
69
+ redactions: Redaction[];
70
+ }[];
71
+ };
72
+ /**
73
+ * Higher-order function to wrap LLM handlers with safety middleware
74
+ * @param {Function} handler - Original LLM handler function
75
+ * @param {Object} options - Configuration options
76
+ * @param {boolean} options.blockOnSecrets - Block requests containing secrets
77
+ * @param {boolean} options.blockOnPII - Block requests containing PII
78
+ * @param {Function} options.onAudit - Audit callback function
79
+ * @param {Function} options.onBlock - Called when request is blocked
80
+ * @returns {Function} - Wrapped handler function
81
+ */
82
+ export declare function withSafetyMiddleware(handler: (req: SafetyRequest) => Promise<unknown>, options?: SafetyOptions): (req: SafetyRequest) => Promise<unknown>;
83
+ /**
84
+ * Create a safe LLM wrapper with default audit logging
85
+ * @param {Function} handler - Original LLM handler
86
+ * @param {Object} options - Options
87
+ * @returns {Function} - Safe wrapped handler
88
+ */
89
+ export declare function createSafeLLM(handler: (req: SafetyRequest) => Promise<unknown>, options?: SafetyOptions): (req: SafetyRequest) => Promise<unknown>;
90
+ declare const _default: {
91
+ redact: typeof redact;
92
+ redactMessages: typeof redactMessages;
93
+ withSafetyMiddleware: typeof withSafetyMiddleware;
94
+ createSafeLLM: typeof createSafeLLM;
95
+ PII_PATTERNS: SafetyPattern[];
96
+ SECRET_PATTERNS: SafetyPattern[];
97
+ };
98
+ export default _default;
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Safety Middleware for LLM Requests
3
+ * Detects and redacts PII and secrets before passing to LLM providers
4
+ */
5
+ // PII Patterns - can be upgraded with stronger libraries like zod-validation or microsoft-presidio
6
+ export const PII_PATTERNS = [
7
+ { name: "email", regex: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi },
8
+ { name: "phone", regex: /\b\d{3}[-.\s]?\d{3}[-.\s]?\d{4}\b/g },
9
+ { name: "credit_card", regex: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g },
10
+ { name: "ssn", regex: /\b\d{3}-\d{2}-\d{4}\b/g },
11
+ { name: "ip_address", regex: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g },
12
+ ];
13
+ // Secret Patterns - API keys, tokens, etc.
14
+ export const SECRET_PATTERNS = [
15
+ { name: "openai_key", regex: /sk-[a-zA-Z0-9]{20,}/g },
16
+ { name: "anthropic_key", regex: /sk-ant-[a-zA-Z0-9-_]{20,}/g },
17
+ { name: "aws_key", regex: /AKIA[0-9A-Z]{16}/g },
18
+ {
19
+ name: "aws_secret",
20
+ regex: /(?:aws_secret_access_key|AWS_SECRET_ACCESS_KEY)\s*[:=]\s*['"]?([A-Za-z0-9/+=]{40})['"]?/g,
21
+ },
22
+ { name: "jwt", regex: /eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+/g },
23
+ { name: "github_token", regex: /gh[pousr]_[A-Za-z0-9_]{36,}/g },
24
+ { name: "stripe_key", regex: /sk_(live|test)_[0-9a-zA-Z]{24,}/g },
25
+ { name: "huggingface_key", regex: /hf_[a-zA-Z0-9]{20,}/g },
26
+ ];
27
+ /**
28
+ * Redact sensitive information from text
29
+ * @param {string} text - Text to scan and redact
30
+ * @returns {{ output: string, redactions: Array<{type: string, value: string, position: number}> }}
31
+ */
32
+ export function redact(text) {
33
+ if (!text || typeof text !== "string") {
34
+ return { output: text, redactions: [] };
35
+ }
36
+ let redactions = [];
37
+ let output = text;
38
+ const allPatterns = [...PII_PATTERNS, ...SECRET_PATTERNS];
39
+ for (const pattern of allPatterns) {
40
+ const matches = [
41
+ ...text.matchAll(new RegExp(pattern.regex.source, pattern.regex.flags)),
42
+ ];
43
+ for (const match of matches) {
44
+ const originalValue = match[0];
45
+ const position = match.index;
46
+ redactions.push({
47
+ type: pattern.name,
48
+ value: originalValue,
49
+ position: position,
50
+ length: originalValue.length,
51
+ });
52
+ }
53
+ }
54
+ // Sort by position to replace from end to start (prevents offset issues)
55
+ redactions.sort((a, b) => b.position - a.position);
56
+ // Replace all matches
57
+ for (const redaction of redactions) {
58
+ const placeholder = `[REDACTED:${redaction.type}]`;
59
+ output =
60
+ output.substring(0, redaction.position) +
61
+ placeholder +
62
+ output.substring(redaction.position + redaction.length);
63
+ }
64
+ // Reverse to chronological order for logging
65
+ redactions.reverse();
66
+ return { output, redactions };
67
+ }
68
+ /**
69
+ * Redact sensitive information from messages array
70
+ * @param {Array<{role: string, content: string}>} messages
71
+ * @returns {{safeMessages: Array, totalRedactions: Array}}
72
+ */
73
+ export function redactMessages(messages) {
74
+ let totalRedactions = [];
75
+ const safeMessages = messages.map((msg, index) => {
76
+ const { output, redactions } = redact(msg.content);
77
+ if (redactions.length > 0) {
78
+ totalRedactions.push({
79
+ messageIndex: index,
80
+ role: msg.role,
81
+ redactions: redactions,
82
+ });
83
+ }
84
+ return { ...msg, content: output };
85
+ });
86
+ return { safeMessages, totalRedactions };
87
+ }
88
+ /**
89
+ * Higher-order function to wrap LLM handlers with safety middleware
90
+ * @param {Function} handler - Original LLM handler function
91
+ * @param {Object} options - Configuration options
92
+ * @param {boolean} options.blockOnSecrets - Block requests containing secrets
93
+ * @param {boolean} options.blockOnPII - Block requests containing PII
94
+ * @param {Function} options.onAudit - Audit callback function
95
+ * @param {Function} options.onBlock - Called when request is blocked
96
+ * @returns {Function} - Wrapped handler function
97
+ */
98
+ export function withSafetyMiddleware(
99
+ // eslint-disable-next-line no-unused-vars
100
+ handler, options = {}) {
101
+ const { blockOnSecrets = false, blockOnPII = false, onAudit = null, onBlock = null, } = options;
102
+ return async (req) => {
103
+ const startTime = Date.now();
104
+ let totalRedactions = [];
105
+ // Redact messages
106
+ const safeMessages = req.messages.map((msg) => {
107
+ const { output, redactions } = redact(msg.content);
108
+ totalRedactions.push(...redactions);
109
+ return { ...msg, content: output };
110
+ });
111
+ // Check for blocking conditions
112
+ const secretTypes = [
113
+ "openai_key",
114
+ "anthropic_key",
115
+ "aws_key",
116
+ "aws_secret",
117
+ "jwt",
118
+ "github_token",
119
+ "stripe_key",
120
+ "huggingface_key",
121
+ ];
122
+ const piiTypes = ["email", "phone", "credit_card", "ssn", "ip_address"];
123
+ const hasSecrets = totalRedactions.some((r) => secretTypes.includes(r.type));
124
+ const hasPII = totalRedactions.some((r) => piiTypes.includes(r.type));
125
+ // 🚨 Fail-closed if secrets detected
126
+ if (blockOnSecrets && hasSecrets) {
127
+ const error = new Error("Request blocked: secrets detected in prompt");
128
+ error.code = "SECURITY_BLOCK_SECRETS";
129
+ error.redactionTypes = [...new Set(totalRedactions.map((r) => r.type))];
130
+ if (onBlock) {
131
+ onBlock({
132
+ reason: "secrets_detected",
133
+ timestamp: new Date().toISOString(),
134
+ redactionCount: totalRedactions.length,
135
+ redactionTypes: error.redactionTypes,
136
+ });
137
+ }
138
+ throw error;
139
+ }
140
+ // 🚨 Fail-closed if PII detected
141
+ if (blockOnPII && hasPII) {
142
+ const error = new Error("Request blocked: PII detected in prompt");
143
+ error.code = "SECURITY_BLOCK_PII";
144
+ error.redactionTypes = [...new Set(totalRedactions.map((r) => r.type))];
145
+ if (onBlock) {
146
+ onBlock({
147
+ reason: "pii_detected",
148
+ timestamp: new Date().toISOString(),
149
+ redactionCount: totalRedactions.length,
150
+ redactionTypes: error.redactionTypes,
151
+ });
152
+ }
153
+ throw error;
154
+ }
155
+ // 🔍 Audit (no raw PII/secrets logged!)
156
+ if (onAudit && totalRedactions.length > 0) {
157
+ onAudit({
158
+ timestamp: new Date().toISOString(),
159
+ redactionCount: totalRedactions.length,
160
+ redactionTypes: [...new Set(totalRedactions.map((r) => r.type))],
161
+ hasSecrets,
162
+ hasPII,
163
+ processingTime: Date.now() - startTime,
164
+ });
165
+ }
166
+ // Call the wrapped handler with safe messages
167
+ const response = await handler({
168
+ ...req,
169
+ messages: safeMessages,
170
+ metadata: {
171
+ ...req.metadata,
172
+ redacted: totalRedactions.length > 0,
173
+ redactionCount: totalRedactions.length,
174
+ },
175
+ });
176
+ return response;
177
+ };
178
+ }
179
+ /**
180
+ * Create a safe LLM wrapper with default audit logging
181
+ * @param {Function} handler - Original LLM handler
182
+ * @param {Object} options - Options
183
+ * @returns {Function} - Safe wrapped handler
184
+ */
185
+ export function createSafeLLM(
186
+ // eslint-disable-next-line no-unused-vars
187
+ handler, options = {}) {
188
+ const defaultAudit = (event) => {
189
+ if (event.redactionCount > 0) {
190
+ console.warn("[Safety Middleware]", {
191
+ redacted: event.redactionCount,
192
+ types: event.redactionTypes,
193
+ time: event.timestamp,
194
+ });
195
+ }
196
+ };
197
+ return withSafetyMiddleware(handler, {
198
+ blockOnSecrets: true,
199
+ blockOnPII: false,
200
+ onAudit: options.onAudit || defaultAudit,
201
+ onBlock: options.onBlock ||
202
+ ((event) => {
203
+ console.error("[Safety Middleware] BLOCKED:", event);
204
+ }),
205
+ ...options,
206
+ });
207
+ }
208
+ export default {
209
+ redact,
210
+ redactMessages,
211
+ withSafetyMiddleware,
212
+ createSafeLLM,
213
+ PII_PATTERNS,
214
+ SECRET_PATTERNS,
215
+ };
@@ -0,0 +1,71 @@
1
+ type FileSystemOptions = {
2
+ recursive?: boolean;
3
+ };
4
+ type DialogOpenOptions = {
5
+ directory?: boolean;
6
+ multiple?: boolean;
7
+ title?: string;
8
+ defaultPath?: string;
9
+ recursive?: boolean;
10
+ canCreateDirectories?: boolean;
11
+ fileAccessMode?: "copy" | "scoped";
12
+ };
13
+ type FileSystemApi = {
14
+ readFile: (path: string) => Promise<string>;
15
+ writeFile: (path: string, content: string) => Promise<void>;
16
+ readDir: (path: string, options?: FileSystemOptions) => Promise<any>;
17
+ exists: (path: string) => Promise<boolean>;
18
+ mkdir: (path: string, options?: FileSystemOptions) => Promise<any>;
19
+ remove: (path: string, options?: FileSystemOptions) => Promise<any>;
20
+ rename: (oldPath: string, newPath: string) => Promise<any>;
21
+ };
22
+ type DialogApi = {
23
+ open: (options?: DialogOpenOptions) => Promise<any>;
24
+ save: (options?: Record<string, unknown>) => Promise<any>;
25
+ message: (message: string, options?: Record<string, unknown>) => Promise<any>;
26
+ confirm: (message: string, options?: Record<string, unknown>) => Promise<any>;
27
+ };
28
+ type ShellApi = {
29
+ open: (url: string) => Promise<any>;
30
+ };
31
+ type ClipboardApi = {
32
+ writeText: (text: unknown) => Promise<boolean>;
33
+ readText: () => Promise<string>;
34
+ };
35
+ type NotificationsApi = {
36
+ send: (title: string, body?: string) => Promise<any>;
37
+ requestPermission: () => Promise<"granted" | "denied" | NotificationPermission>;
38
+ };
39
+ export declare const isTauri: () => boolean;
40
+ /**
41
+ * File system operations.
42
+ */
43
+ export declare const fileSystem: FileSystemApi;
44
+ /**
45
+ * Dialog operations.
46
+ */
47
+ export declare const dialog: DialogApi;
48
+ export declare const tauriPath: {
49
+ homeDir: () => Promise<any>;
50
+ };
51
+ /**
52
+ * Shell operations.
53
+ */
54
+ export declare const shell: ShellApi;
55
+ /**
56
+ * Clipboard operations.
57
+ */
58
+ export declare const clipboard: ClipboardApi;
59
+ /**
60
+ * Notifications.
61
+ */
62
+ export declare const notifications: NotificationsApi;
63
+ declare const _default: {
64
+ isTauri: () => boolean;
65
+ fileSystem: FileSystemApi;
66
+ dialog: DialogApi;
67
+ shell: ShellApi;
68
+ clipboard: ClipboardApi;
69
+ notifications: NotificationsApi;
70
+ };
71
+ export default _default;
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Tauri integration utilities.
3
+ */
4
+ import { tauriImport } from "./tauriImport.js";
5
+ export const isTauri = () => {
6
+ if (typeof window === "undefined") {
7
+ return false;
8
+ }
9
+ // In Node.js backend service, Tauri is never available
10
+ if (typeof window === "undefined") {
11
+ return false;
12
+ }
13
+ const hostname = window.location?.hostname || "";
14
+ const protocol = window.location?.protocol || "";
15
+ const userAgent = window.navigator?.userAgent || "";
16
+ return (window.__TAURI__ !== undefined ||
17
+ window.__TAURI_IPC__ !== undefined ||
18
+ hostname === "tauri.localhost" ||
19
+ hostname.endsWith(".tauri.localhost") ||
20
+ protocol === "tauri:" ||
21
+ /\bTauri\b/i.test(userAgent));
22
+ };
23
+ /**
24
+ * File system operations.
25
+ */
26
+ export const fileSystem = {
27
+ readFile: async (path) => {
28
+ if (!isTauri()) {
29
+ throw new Error("File system operations only available in desktop app");
30
+ }
31
+ const { readTextFile } = await tauriImport("@tauri-apps/api/fs");
32
+ return await readTextFile(path);
33
+ },
34
+ writeFile: async (path, content) => {
35
+ if (!isTauri()) {
36
+ throw new Error("File system operations only available in desktop app");
37
+ }
38
+ const { writeTextFile } = await tauriImport("@tauri-apps/api/fs");
39
+ return await writeTextFile(path, content);
40
+ },
41
+ readDir: async (path, options = {}) => {
42
+ if (!isTauri()) {
43
+ throw new Error("File system operations only available in desktop app");
44
+ }
45
+ const { readDir } = await tauriImport("@tauri-apps/api/fs");
46
+ return await readDir(path, options);
47
+ },
48
+ exists: async (path) => {
49
+ if (!isTauri())
50
+ return false;
51
+ const { exists } = await tauriImport("@tauri-apps/api/fs");
52
+ return await exists(path);
53
+ },
54
+ mkdir: async (path, options = {}) => {
55
+ if (!isTauri()) {
56
+ throw new Error("File system operations only available in desktop app");
57
+ }
58
+ const { mkdir } = await tauriImport("@tauri-apps/api/fs");
59
+ return await mkdir(path, options);
60
+ },
61
+ remove: async (path, options = {}) => {
62
+ if (!isTauri()) {
63
+ throw new Error("File system operations only available in desktop app");
64
+ }
65
+ const { remove } = await tauriImport("@tauri-apps/api/fs");
66
+ return await remove(path, options);
67
+ },
68
+ rename: async (oldPath, newPath) => {
69
+ if (!isTauri()) {
70
+ throw new Error("File system operations only available in desktop app");
71
+ }
72
+ const { rename } = await tauriImport("@tauri-apps/api/fs");
73
+ return await rename(oldPath, newPath);
74
+ },
75
+ };
76
+ /**
77
+ * Dialog operations.
78
+ */
79
+ export const dialog = {
80
+ open: async (options = {}) => {
81
+ if (!isTauri()) {
82
+ return new Promise((resolve) => {
83
+ const input = document.createElement("input");
84
+ input.type = "file";
85
+ if (options.directory) {
86
+ input.webkitdirectory = true;
87
+ input.setAttribute("directory", "");
88
+ }
89
+ input.multiple = options.multiple || false;
90
+ input.onchange = (e) => {
91
+ const target = e.target;
92
+ const files = Array.from(target?.files || []);
93
+ resolve(files.length === 1 && !options.multiple ? files[0] : files);
94
+ };
95
+ input.click();
96
+ });
97
+ }
98
+ const { open } = await tauriImport("@tauri-apps/api/dialog");
99
+ return await open(options);
100
+ },
101
+ save: async (options = {}) => {
102
+ if (!isTauri()) {
103
+ throw new Error("Save dialog only available in desktop app");
104
+ }
105
+ const { save } = await tauriImport("@tauri-apps/api/dialog");
106
+ return await save(options);
107
+ },
108
+ message: async (message, options = {}) => {
109
+ if (!isTauri()) {
110
+ alert(message);
111
+ return;
112
+ }
113
+ const { message: showMessage } = await tauriImport("@tauri-apps/api/dialog");
114
+ return await showMessage(message, options);
115
+ },
116
+ confirm: async (message, options = {}) => {
117
+ if (!isTauri()) {
118
+ return confirm(message);
119
+ }
120
+ const { confirm: showConfirm } = await tauriImport("@tauri-apps/api/dialog");
121
+ return await showConfirm(message, options);
122
+ },
123
+ };
124
+ export const tauriPath = {
125
+ homeDir: async () => {
126
+ if (!isTauri()) {
127
+ return null;
128
+ }
129
+ const { homeDir } = await tauriImport("@tauri-apps/api/path");
130
+ return await homeDir();
131
+ },
132
+ };
133
+ /**
134
+ * Shell operations.
135
+ */
136
+ export const shell = {
137
+ open: async (url) => {
138
+ if (!isTauri()) {
139
+ window.open(url, "_blank");
140
+ return;
141
+ }
142
+ const { open } = await tauriImport("@tauri-apps/api/shell");
143
+ return await open(url);
144
+ },
145
+ };
146
+ /**
147
+ * Clipboard operations.
148
+ */
149
+ export const clipboard = {
150
+ writeText: async (text) => {
151
+ const value = typeof text === "string" ? text : String(text ?? "");
152
+ // 1) Prefer native Tauri clipboard in desktop builds.
153
+ if (isTauri()) {
154
+ try {
155
+ const { writeText } = await tauriImport("@tauri-apps/api/clipboard");
156
+ await writeText(value);
157
+ return true;
158
+ }
159
+ catch (error) {
160
+ console.warn("Tauri clipboard write failed, trying web fallback", error);
161
+ }
162
+ }
163
+ // 2) Standard async clipboard API.
164
+ try {
165
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
166
+ await navigator.clipboard.writeText(value);
167
+ return true;
168
+ }
169
+ }
170
+ catch (error) {
171
+ console.warn("navigator.clipboard.writeText failed", error);
172
+ }
173
+ throw new Error("Clipboard write is unavailable in this environment");
174
+ },
175
+ readText: async () => {
176
+ if (!isTauri()) {
177
+ if (navigator.clipboard) {
178
+ return await navigator.clipboard.readText();
179
+ }
180
+ return "";
181
+ }
182
+ const { readText } = await tauriImport("@tauri-apps/api/clipboard");
183
+ return await readText();
184
+ },
185
+ };
186
+ /**
187
+ * Notifications.
188
+ */
189
+ export const notifications = {
190
+ send: async (title, body) => {
191
+ if (!isTauri()) {
192
+ if ("Notification" in window && Notification.permission === "granted") {
193
+ new Notification(title, { body });
194
+ }
195
+ return;
196
+ }
197
+ const { sendNotification } = await tauriImport("@tauri-apps/api/notification");
198
+ return await sendNotification({ title, body });
199
+ },
200
+ requestPermission: async () => {
201
+ if (!isTauri()) {
202
+ if ("Notification" in window) {
203
+ return await Notification.requestPermission();
204
+ }
205
+ return "denied";
206
+ }
207
+ const { isPermissionGranted, requestPermission } = await tauriImport("@tauri-apps/api/notification");
208
+ let permission = await isPermissionGranted();
209
+ if (!permission) {
210
+ permission = await requestPermission();
211
+ }
212
+ return permission ? "granted" : "denied";
213
+ },
214
+ };
215
+ export default {
216
+ isTauri,
217
+ fileSystem,
218
+ dialog,
219
+ shell,
220
+ clipboard,
221
+ notifications,
222
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Stub implementation for backend service
3
+ * Tauri modules are not available in Node.js backend
4
+ */
5
+ export declare const tauriImport: (moduleName: string) => Promise<any>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Stub implementation for backend service
3
+ * Tauri modules are not available in Node.js backend
4
+ */
5
+ export const tauriImport = (moduleName) => {
6
+ return Promise.reject(new Error(`Tauri module '${moduleName}' not available in backend service`));
7
+ };