rird 1.0.200

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 (350) hide show
  1. package/AGENTS.md +27 -0
  2. package/Dockerfile +18 -0
  3. package/README.md +15 -0
  4. package/bin/opencode +336 -0
  5. package/bin/pty-wrapper.js +285 -0
  6. package/bunfig.toml +4 -0
  7. package/facebook_ads_library.png +0 -0
  8. package/nul`nif +0 -0
  9. package/package.json +111 -0
  10. package/parsers-config.ts +239 -0
  11. package/rird-1.0.199.tgz +0 -0
  12. package/script/build-windows.ts +54 -0
  13. package/script/build.ts +167 -0
  14. package/script/postinstall.mjs +544 -0
  15. package/script/publish-registries.ts +187 -0
  16. package/script/publish.ts +72 -0
  17. package/script/schema.ts +47 -0
  18. package/src/acp/README.md +164 -0
  19. package/src/acp/agent.ts +1063 -0
  20. package/src/acp/session.ts +101 -0
  21. package/src/acp/types.ts +22 -0
  22. package/src/agent/agent.ts +367 -0
  23. package/src/agent/generate.txt +75 -0
  24. package/src/agent/prompt/compaction.txt +12 -0
  25. package/src/agent/prompt/explore.txt +18 -0
  26. package/src/agent/prompt/summary.txt +10 -0
  27. package/src/agent/prompt/title.txt +36 -0
  28. package/src/auth/index.ts +70 -0
  29. package/src/bun/index.ts +114 -0
  30. package/src/bus/bus-event.ts +43 -0
  31. package/src/bus/global.ts +10 -0
  32. package/src/bus/index.ts +105 -0
  33. package/src/cli/bootstrap.ts +17 -0
  34. package/src/cli/cmd/acp.ts +88 -0
  35. package/src/cli/cmd/agent.ts +256 -0
  36. package/src/cli/cmd/auth.ts +391 -0
  37. package/src/cli/cmd/cmd.ts +7 -0
  38. package/src/cli/cmd/debug/config.ts +15 -0
  39. package/src/cli/cmd/debug/file.ts +91 -0
  40. package/src/cli/cmd/debug/index.ts +43 -0
  41. package/src/cli/cmd/debug/lsp.ts +48 -0
  42. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  43. package/src/cli/cmd/debug/scrap.ts +15 -0
  44. package/src/cli/cmd/debug/skill.ts +15 -0
  45. package/src/cli/cmd/debug/snapshot.ts +48 -0
  46. package/src/cli/cmd/export.ts +88 -0
  47. package/src/cli/cmd/generate.ts +38 -0
  48. package/src/cli/cmd/github.ts +1400 -0
  49. package/src/cli/cmd/import.ts +98 -0
  50. package/src/cli/cmd/mcp.ts +654 -0
  51. package/src/cli/cmd/models.ts +77 -0
  52. package/src/cli/cmd/pr.ts +112 -0
  53. package/src/cli/cmd/run.ts +368 -0
  54. package/src/cli/cmd/serve.ts +31 -0
  55. package/src/cli/cmd/session.ts +106 -0
  56. package/src/cli/cmd/stats.ts +298 -0
  57. package/src/cli/cmd/tui/app.tsx +696 -0
  58. package/src/cli/cmd/tui/attach.ts +30 -0
  59. package/src/cli/cmd/tui/component/border.tsx +21 -0
  60. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  61. package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
  62. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  63. package/src/cli/cmd/tui/component/dialog-model.tsx +245 -0
  64. package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
  65. package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
  66. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  67. package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
  68. package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
  69. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  70. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  71. package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
  72. package/src/cli/cmd/tui/component/logo.tsx +35 -0
  73. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
  74. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  75. package/src/cli/cmd/tui/component/prompt/index.tsx +1090 -0
  76. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  77. package/src/cli/cmd/tui/component/tips.ts +27 -0
  78. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  79. package/src/cli/cmd/tui/context/args.tsx +14 -0
  80. package/src/cli/cmd/tui/context/directory.ts +13 -0
  81. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  82. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  83. package/src/cli/cmd/tui/context/keybind.tsx +101 -0
  84. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  85. package/src/cli/cmd/tui/context/local.tsx +354 -0
  86. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  87. package/src/cli/cmd/tui/context/route.tsx +46 -0
  88. package/src/cli/cmd/tui/context/sdk.tsx +74 -0
  89. package/src/cli/cmd/tui/context/sync.tsx +372 -0
  90. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  91. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  92. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  93. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  94. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  95. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  96. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  97. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  98. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  99. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  100. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  101. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  102. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  103. package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
  104. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  105. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  106. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  107. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  108. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  109. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  110. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  111. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  112. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  113. package/src/cli/cmd/tui/context/theme/rird.json +245 -0
  114. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  115. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  116. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  117. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  118. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  119. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  120. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  121. package/src/cli/cmd/tui/context/theme.tsx +1109 -0
  122. package/src/cli/cmd/tui/event.ts +40 -0
  123. package/src/cli/cmd/tui/routes/home.tsx +138 -0
  124. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
  125. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
  126. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  127. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  128. package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
  129. package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
  130. package/src/cli/cmd/tui/routes/session/index.tsx +1864 -0
  131. package/src/cli/cmd/tui/routes/session/sidebar.tsx +318 -0
  132. package/src/cli/cmd/tui/spawn.ts +60 -0
  133. package/src/cli/cmd/tui/thread.ts +142 -0
  134. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  135. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  136. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  137. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
  138. package/src/cli/cmd/tui/ui/dialog-select.tsx +332 -0
  139. package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
  140. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  141. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  142. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  143. package/src/cli/cmd/tui/util/editor.ts +32 -0
  144. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  145. package/src/cli/cmd/tui/worker.ts +63 -0
  146. package/src/cli/cmd/uninstall.ts +344 -0
  147. package/src/cli/cmd/upgrade.ts +100 -0
  148. package/src/cli/cmd/web.ts +84 -0
  149. package/src/cli/error.ts +56 -0
  150. package/src/cli/ui.ts +84 -0
  151. package/src/cli/upgrade.ts +25 -0
  152. package/src/command/index.ts +80 -0
  153. package/src/command/template/initialize.txt +10 -0
  154. package/src/command/template/review.txt +97 -0
  155. package/src/config/config.ts +995 -0
  156. package/src/config/markdown.ts +41 -0
  157. package/src/env/index.ts +26 -0
  158. package/src/file/ignore.ts +83 -0
  159. package/src/file/index.ts +328 -0
  160. package/src/file/ripgrep.ts +393 -0
  161. package/src/file/time.ts +64 -0
  162. package/src/file/watcher.ts +103 -0
  163. package/src/flag/flag.ts +46 -0
  164. package/src/format/formatter.ts +315 -0
  165. package/src/format/index.ts +137 -0
  166. package/src/global/index.ts +52 -0
  167. package/src/id/id.ts +73 -0
  168. package/src/ide/index.ts +76 -0
  169. package/src/index.ts +240 -0
  170. package/src/installation/index.ts +239 -0
  171. package/src/lsp/client.ts +229 -0
  172. package/src/lsp/index.ts +485 -0
  173. package/src/lsp/language.ts +116 -0
  174. package/src/lsp/server.ts +1895 -0
  175. package/src/mcp/auth.ts +135 -0
  176. package/src/mcp/index.ts +690 -0
  177. package/src/mcp/oauth-callback.ts +200 -0
  178. package/src/mcp/oauth-provider.ts +154 -0
  179. package/src/patch/index.ts +622 -0
  180. package/src/permission/index.ts +199 -0
  181. package/src/plugin/index.ts +91 -0
  182. package/src/project/bootstrap.ts +31 -0
  183. package/src/project/instance.ts +78 -0
  184. package/src/project/project.ts +221 -0
  185. package/src/project/state.ts +65 -0
  186. package/src/project/vcs.ts +76 -0
  187. package/src/provider/auth.ts +143 -0
  188. package/src/provider/models-macro.ts +11 -0
  189. package/src/provider/models.ts +106 -0
  190. package/src/provider/provider.ts +1071 -0
  191. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  192. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  193. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  194. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  195. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  196. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  197. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  198. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  199. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  200. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  201. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  202. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  203. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  204. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  205. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  206. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  207. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  208. package/src/provider/transform.ts +455 -0
  209. package/src/pty/index.ts +231 -0
  210. package/src/security/guardrails.test.ts +341 -0
  211. package/src/security/guardrails.ts +558 -0
  212. package/src/security/index.ts +19 -0
  213. package/src/server/error.ts +36 -0
  214. package/src/server/project.ts +79 -0
  215. package/src/server/server.ts +2642 -0
  216. package/src/server/tui.ts +71 -0
  217. package/src/session/compaction.ts +223 -0
  218. package/src/session/index.ts +461 -0
  219. package/src/session/llm.ts +201 -0
  220. package/src/session/message-v2.ts +690 -0
  221. package/src/session/message.ts +189 -0
  222. package/src/session/processor.ts +409 -0
  223. package/src/session/prompt/act-switch.txt +5 -0
  224. package/src/session/prompt/anthropic-20250930.txt +166 -0
  225. package/src/session/prompt/anthropic.txt +85 -0
  226. package/src/session/prompt/anthropic_spoof.txt +1 -0
  227. package/src/session/prompt/beast.txt +103 -0
  228. package/src/session/prompt/codex.txt +304 -0
  229. package/src/session/prompt/copilot-gpt-5.txt +138 -0
  230. package/src/session/prompt/gemini.txt +85 -0
  231. package/src/session/prompt/max-steps.txt +16 -0
  232. package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
  233. package/src/session/prompt/plan.txt +24 -0
  234. package/src/session/prompt/polaris.txt +84 -0
  235. package/src/session/prompt/qwen.txt +106 -0
  236. package/src/session/prompt.ts +1509 -0
  237. package/src/session/retry.ts +86 -0
  238. package/src/session/revert.ts +108 -0
  239. package/src/session/sensitive-filter.test.ts +327 -0
  240. package/src/session/sensitive-filter.ts +466 -0
  241. package/src/session/status.ts +76 -0
  242. package/src/session/summary.ts +194 -0
  243. package/src/session/system.ts +120 -0
  244. package/src/session/todo.ts +37 -0
  245. package/src/share/share-next.ts +194 -0
  246. package/src/share/share.ts +87 -0
  247. package/src/shell/shell.ts +67 -0
  248. package/src/skill/index.ts +1 -0
  249. package/src/skill/skill.ts +83 -0
  250. package/src/snapshot/index.ts +197 -0
  251. package/src/storage/storage.ts +226 -0
  252. package/src/tests/agent.test.ts +308 -0
  253. package/src/tests/build-guards.test.ts +267 -0
  254. package/src/tests/config.test.ts +664 -0
  255. package/src/tests/tool-registry.test.ts +589 -0
  256. package/src/tool/bash.ts +317 -0
  257. package/src/tool/bash.txt +158 -0
  258. package/src/tool/batch.ts +175 -0
  259. package/src/tool/batch.txt +24 -0
  260. package/src/tool/codesearch.ts +168 -0
  261. package/src/tool/codesearch.txt +12 -0
  262. package/src/tool/edit.ts +675 -0
  263. package/src/tool/edit.txt +10 -0
  264. package/src/tool/glob.ts +65 -0
  265. package/src/tool/glob.txt +6 -0
  266. package/src/tool/grep.ts +121 -0
  267. package/src/tool/grep.txt +8 -0
  268. package/src/tool/invalid.ts +17 -0
  269. package/src/tool/ls.ts +110 -0
  270. package/src/tool/ls.txt +1 -0
  271. package/src/tool/lsp-diagnostics.ts +26 -0
  272. package/src/tool/lsp-diagnostics.txt +1 -0
  273. package/src/tool/lsp-hover.ts +31 -0
  274. package/src/tool/lsp-hover.txt +1 -0
  275. package/src/tool/lsp.ts +87 -0
  276. package/src/tool/lsp.txt +19 -0
  277. package/src/tool/multiedit.ts +46 -0
  278. package/src/tool/multiedit.txt +41 -0
  279. package/src/tool/patch.ts +233 -0
  280. package/src/tool/patch.txt +1 -0
  281. package/src/tool/read.ts +219 -0
  282. package/src/tool/read.txt +12 -0
  283. package/src/tool/registry.ts +162 -0
  284. package/src/tool/skill.ts +100 -0
  285. package/src/tool/task.ts +136 -0
  286. package/src/tool/task.txt +51 -0
  287. package/src/tool/todo.ts +39 -0
  288. package/src/tool/todoread.txt +14 -0
  289. package/src/tool/todowrite.txt +167 -0
  290. package/src/tool/tool.ts +71 -0
  291. package/src/tool/webfetch.ts +198 -0
  292. package/src/tool/webfetch.txt +13 -0
  293. package/src/tool/websearch.ts +180 -0
  294. package/src/tool/websearch.txt +11 -0
  295. package/src/tool/write.ts +110 -0
  296. package/src/tool/write.txt +8 -0
  297. package/src/util/archive.ts +16 -0
  298. package/src/util/color.ts +19 -0
  299. package/src/util/context.ts +25 -0
  300. package/src/util/defer.ts +12 -0
  301. package/src/util/eventloop.ts +20 -0
  302. package/src/util/filesystem.ts +83 -0
  303. package/src/util/fn.ts +11 -0
  304. package/src/util/iife.ts +3 -0
  305. package/src/util/keybind.ts +102 -0
  306. package/src/util/lazy.ts +11 -0
  307. package/src/util/license.ts +325 -0
  308. package/src/util/locale.ts +81 -0
  309. package/src/util/lock.ts +98 -0
  310. package/src/util/log.ts +180 -0
  311. package/src/util/queue.ts +32 -0
  312. package/src/util/rpc.ts +42 -0
  313. package/src/util/scrap.ts +10 -0
  314. package/src/util/signal.ts +12 -0
  315. package/src/util/timeout.ts +14 -0
  316. package/src/util/token.ts +7 -0
  317. package/src/util/wildcard.ts +54 -0
  318. package/sst-env.d.ts +9 -0
  319. package/test/agent/agent.test.ts +146 -0
  320. package/test/bun.test.ts +53 -0
  321. package/test/cli/github-remote.test.ts +80 -0
  322. package/test/config/agent-color.test.ts +66 -0
  323. package/test/config/config.test.ts +535 -0
  324. package/test/config/markdown.test.ts +89 -0
  325. package/test/file/ignore.test.ts +10 -0
  326. package/test/fixture/fixture.ts +36 -0
  327. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  328. package/test/ide/ide.test.ts +82 -0
  329. package/test/keybind.test.ts +421 -0
  330. package/test/lsp/client.test.ts +95 -0
  331. package/test/mcp/headers.test.ts +153 -0
  332. package/test/patch/patch.test.ts +348 -0
  333. package/test/preload.ts +57 -0
  334. package/test/project/project.test.ts +72 -0
  335. package/test/provider/provider.test.ts +1809 -0
  336. package/test/provider/transform.test.ts +411 -0
  337. package/test/session/retry.test.ts +111 -0
  338. package/test/session/session.test.ts +71 -0
  339. package/test/skill/skill.test.ts +131 -0
  340. package/test/snapshot/snapshot.test.ts +939 -0
  341. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  342. package/test/tool/bash.test.ts +434 -0
  343. package/test/tool/grep.test.ts +108 -0
  344. package/test/tool/patch.test.ts +259 -0
  345. package/test/tool/read.test.ts +42 -0
  346. package/test/util/iife.test.ts +36 -0
  347. package/test/util/lazy.test.ts +50 -0
  348. package/test/util/timeout.test.ts +21 -0
  349. package/test/util/wildcard.test.ts +55 -0
  350. package/tsconfig.json +16 -0
@@ -0,0 +1,466 @@
1
+ /**
2
+ * Sensitive Data Filter for RIRD Browser Agent
3
+ *
4
+ * Prevents sensitive user data from being sent to LLM APIs by detecting
5
+ * and redacting patterns like credit cards, SSNs, passwords, API keys, etc.
6
+ *
7
+ * The filter maintains a local mapping so responses can be de-redacted
8
+ * for the user if needed.
9
+ */
10
+
11
+ import { Log } from "@/util/log"
12
+
13
+ export namespace SensitiveFilter {
14
+ const log = Log.create({ service: "sensitive-filter" })
15
+
16
+ // Types for redaction
17
+ export type RedactionType =
18
+ | "CREDIT_CARD"
19
+ | "SSN"
20
+ | "PASSWORD"
21
+ | "API_KEY"
22
+ | "BEARER_TOKEN"
23
+ | "EMAIL"
24
+
25
+ export interface RedactionEntry {
26
+ type: RedactionType
27
+ original: string
28
+ placeholder: string
29
+ timestamp: number
30
+ }
31
+
32
+ export interface FilterResult {
33
+ text: string
34
+ redactions: RedactionEntry[]
35
+ }
36
+
37
+ // Session-scoped redaction mappings (in-memory for security - not persisted)
38
+ const redactionMappings = new Map<string, Map<string, RedactionEntry>>()
39
+
40
+ // Counter for unique placeholders per session
41
+ const placeholderCounters = new Map<string, Map<RedactionType, number>>()
42
+
43
+ /**
44
+ * Luhn algorithm to validate credit card numbers
45
+ */
46
+ function isValidLuhn(num: string): boolean {
47
+ const digits = num.replace(/\D/g, "")
48
+ if (digits.length < 13 || digits.length > 19) return false
49
+
50
+ let sum = 0
51
+ let isEven = false
52
+
53
+ for (let i = digits.length - 1; i >= 0; i--) {
54
+ let digit = parseInt(digits[i], 10)
55
+
56
+ if (isEven) {
57
+ digit *= 2
58
+ if (digit > 9) {
59
+ digit -= 9
60
+ }
61
+ }
62
+
63
+ sum += digit
64
+ isEven = !isEven
65
+ }
66
+
67
+ return sum % 10 === 0
68
+ }
69
+
70
+ /**
71
+ * Get or create a unique placeholder for a redaction
72
+ */
73
+ function getPlaceholder(sessionID: string, type: RedactionType): string {
74
+ if (!placeholderCounters.has(sessionID)) {
75
+ placeholderCounters.set(sessionID, new Map())
76
+ }
77
+ const counters = placeholderCounters.get(sessionID)!
78
+
79
+ const count = (counters.get(type) || 0) + 1
80
+ counters.set(type, count)
81
+
82
+ return `[REDACTED-${type}${count > 1 ? `-${count}` : ""}]`
83
+ }
84
+
85
+ /**
86
+ * Detect and redact credit card numbers (13-19 digits, Luhn valid)
87
+ * Handles formats: 1234567890123456, 1234-5678-9012-3456, 1234 5678 9012 3456
88
+ */
89
+ function redactCreditCards(
90
+ text: string,
91
+ sessionID: string,
92
+ redactions: RedactionEntry[]
93
+ ): string {
94
+ // Match sequences that look like credit card numbers
95
+ // Supports: plain digits, dash-separated, space-separated
96
+ const ccPattern = /\b(?:\d{4}[-\s]?){3}\d{4}\b|\b\d{13,19}\b/g
97
+
98
+ return text.replace(ccPattern, (match) => {
99
+ const digitsOnly = match.replace(/\D/g, "")
100
+
101
+ // Must be 13-19 digits and pass Luhn check
102
+ if (digitsOnly.length >= 13 && digitsOnly.length <= 19 && isValidLuhn(digitsOnly)) {
103
+ const placeholder = getPlaceholder(sessionID, "CREDIT_CARD")
104
+ const entry: RedactionEntry = {
105
+ type: "CREDIT_CARD",
106
+ original: match,
107
+ placeholder,
108
+ timestamp: Date.now(),
109
+ }
110
+ redactions.push(entry)
111
+ storeRedaction(sessionID, entry)
112
+ log.info("redacted credit card", { sessionID, placeholder })
113
+ return placeholder
114
+ }
115
+ return match
116
+ })
117
+ }
118
+
119
+ /**
120
+ * Detect and redact SSN (XXX-XX-XXXX pattern)
121
+ */
122
+ function redactSSN(
123
+ text: string,
124
+ sessionID: string,
125
+ redactions: RedactionEntry[]
126
+ ): string {
127
+ // SSN pattern: 3 digits, dash, 2 digits, dash, 4 digits
128
+ // Also match without dashes but with proper grouping context
129
+ const ssnPattern = /\b\d{3}-\d{2}-\d{4}\b/g
130
+
131
+ return text.replace(ssnPattern, (match) => {
132
+ const placeholder = getPlaceholder(sessionID, "SSN")
133
+ const entry: RedactionEntry = {
134
+ type: "SSN",
135
+ original: match,
136
+ placeholder,
137
+ timestamp: Date.now(),
138
+ }
139
+ redactions.push(entry)
140
+ storeRedaction(sessionID, entry)
141
+ log.info("redacted SSN", { sessionID, placeholder })
142
+ return placeholder
143
+ })
144
+ }
145
+
146
+ /**
147
+ * Detect and redact passwords in common patterns
148
+ * Matches: password: X, pwd: X, pass: X, passwd: X (case insensitive)
149
+ */
150
+ function redactPasswords(
151
+ text: string,
152
+ sessionID: string,
153
+ redactions: RedactionEntry[]
154
+ ): string {
155
+ // Match password patterns with various delimiters and quote styles
156
+ // password: value, password=value, password="value", etc.
157
+ const pwdPattern =
158
+ /\b(password|passwd|pwd|pass|secret)[\s]*[:=][\s]*(['"]?)([^\s'"}\]>,]+)\2/gi
159
+
160
+ return text.replace(pwdPattern, (match, label, quote, value) => {
161
+ // Don't redact placeholder-like values or very short values
162
+ if (value.length < 4 || value.startsWith("[REDACTED")) {
163
+ return match
164
+ }
165
+
166
+ const placeholder = getPlaceholder(sessionID, "PASSWORD")
167
+ const entry: RedactionEntry = {
168
+ type: "PASSWORD",
169
+ original: value,
170
+ placeholder,
171
+ timestamp: Date.now(),
172
+ }
173
+ redactions.push(entry)
174
+ storeRedaction(sessionID, entry)
175
+ log.info("redacted password", { sessionID, placeholder })
176
+
177
+ // Preserve the label and delimiter, only replace the value
178
+ return `${label}${match.includes("=") ? "=" : ":"}${quote}${placeholder}${quote}`
179
+ })
180
+ }
181
+
182
+ /**
183
+ * Detect and redact API keys in common patterns
184
+ * Matches: sk-*, api_key=*, apikey=*, api-key:*, etc.
185
+ */
186
+ function redactAPIKeys(
187
+ text: string,
188
+ sessionID: string,
189
+ redactions: RedactionEntry[]
190
+ ): string {
191
+ // Common API key patterns
192
+ const patterns = [
193
+ // sk-* (OpenAI style)
194
+ /\bsk-[a-zA-Z0-9_-]{20,}\b/g,
195
+ // xoxb-*, xoxp-* (Slack style)
196
+ /\bxox[bp]-[a-zA-Z0-9-]{20,}\b/g,
197
+ // ghp_*, gho_*, ghu_*, ghs_*, ghr_* (GitHub tokens)
198
+ /\bgh[pousr]_[a-zA-Z0-9]{36,}\b/g,
199
+ // AKIA* (AWS access keys)
200
+ /\bAKIA[A-Z0-9]{16}\b/g,
201
+ // Generic api_key=, apikey=, api-key: patterns
202
+ /\b(api[_-]?key|apikey|access[_-]?key|secret[_-]?key)[\s]*[:=][\s]*(['"]?)([a-zA-Z0-9_-]{16,})\2/gi,
203
+ // Generic *_API_KEY or *_SECRET patterns in env var style
204
+ /\b[A-Z_]+_(API_KEY|SECRET|TOKEN)[\s]*[:=][\s]*(['"]?)([a-zA-Z0-9_-]{16,})\2/g,
205
+ ]
206
+
207
+ let result = text
208
+
209
+ for (const pattern of patterns) {
210
+ result = result.replace(pattern, (match, ...args) => {
211
+ // Don't redact already redacted values
212
+ if (match.includes("[REDACTED")) {
213
+ return match
214
+ }
215
+
216
+ // For patterns with capture groups (api_key=value), only redact the value
217
+ if (typeof args[0] === "string" && args[2] && typeof args[2] === "string") {
218
+ const label = args[0]
219
+ const quote = args[1] || ""
220
+ const value = args[2]
221
+
222
+ const placeholder = getPlaceholder(sessionID, "API_KEY")
223
+ const entry: RedactionEntry = {
224
+ type: "API_KEY",
225
+ original: value,
226
+ placeholder,
227
+ timestamp: Date.now(),
228
+ }
229
+ redactions.push(entry)
230
+ storeRedaction(sessionID, entry)
231
+ log.info("redacted API key", { sessionID, placeholder })
232
+
233
+ return `${label}${match.includes("=") ? "=" : ":"}${quote}${placeholder}${quote}`
234
+ }
235
+
236
+ // For standalone patterns (sk-*, ghp_*, etc.)
237
+ const placeholder = getPlaceholder(sessionID, "API_KEY")
238
+ const entry: RedactionEntry = {
239
+ type: "API_KEY",
240
+ original: match,
241
+ placeholder,
242
+ timestamp: Date.now(),
243
+ }
244
+ redactions.push(entry)
245
+ storeRedaction(sessionID, entry)
246
+ log.info("redacted API key", { sessionID, placeholder })
247
+
248
+ return placeholder
249
+ })
250
+ }
251
+
252
+ return result
253
+ }
254
+
255
+ /**
256
+ * Detect and redact Bearer tokens
257
+ * Matches: Bearer <token>, Authorization: Bearer <token>
258
+ */
259
+ function redactBearerTokens(
260
+ text: string,
261
+ sessionID: string,
262
+ redactions: RedactionEntry[]
263
+ ): string {
264
+ // Bearer token pattern
265
+ const bearerPattern = /\bBearer\s+([a-zA-Z0-9_.-]{20,})\b/gi
266
+
267
+ return text.replace(bearerPattern, (match, token) => {
268
+ if (token.startsWith("[REDACTED")) {
269
+ return match
270
+ }
271
+
272
+ const placeholder = getPlaceholder(sessionID, "BEARER_TOKEN")
273
+ const entry: RedactionEntry = {
274
+ type: "BEARER_TOKEN",
275
+ original: token,
276
+ placeholder,
277
+ timestamp: Date.now(),
278
+ }
279
+ redactions.push(entry)
280
+ storeRedaction(sessionID, entry)
281
+ log.info("redacted Bearer token", { sessionID, placeholder })
282
+
283
+ return `Bearer ${placeholder}`
284
+ })
285
+ }
286
+
287
+ /**
288
+ * Optional: Detect and redact email addresses
289
+ * Disabled by default as emails are often needed for tasks
290
+ */
291
+ function redactEmails(
292
+ text: string,
293
+ sessionID: string,
294
+ redactions: RedactionEntry[],
295
+ enabled: boolean = false
296
+ ): string {
297
+ if (!enabled) return text
298
+
299
+ // Basic email pattern
300
+ const emailPattern = /\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g
301
+
302
+ return text.replace(emailPattern, (match) => {
303
+ const placeholder = getPlaceholder(sessionID, "EMAIL")
304
+ const entry: RedactionEntry = {
305
+ type: "EMAIL",
306
+ original: match,
307
+ placeholder,
308
+ timestamp: Date.now(),
309
+ }
310
+ redactions.push(entry)
311
+ storeRedaction(sessionID, entry)
312
+ log.info("redacted email", { sessionID, placeholder })
313
+ return placeholder
314
+ })
315
+ }
316
+
317
+ /**
318
+ * Store a redaction entry in the session mapping
319
+ */
320
+ function storeRedaction(sessionID: string, entry: RedactionEntry): void {
321
+ if (!redactionMappings.has(sessionID)) {
322
+ redactionMappings.set(sessionID, new Map())
323
+ }
324
+ redactionMappings.get(sessionID)!.set(entry.placeholder, entry)
325
+ }
326
+
327
+ /**
328
+ * Filter options
329
+ */
330
+ export interface FilterOptions {
331
+ /** Whether to redact email addresses (default: false) */
332
+ redactEmails?: boolean
333
+ /** Session ID for tracking redactions */
334
+ sessionID: string
335
+ }
336
+
337
+ /**
338
+ * Filter sensitive data from text before sending to LLM
339
+ *
340
+ * @param text The text to filter
341
+ * @param options Filter options including sessionID
342
+ * @returns FilterResult with filtered text and list of redactions made
343
+ */
344
+ export function filter(text: string, options: FilterOptions): FilterResult {
345
+ const redactions: RedactionEntry[] = []
346
+ let filtered = text
347
+
348
+ // Apply all redaction functions in order
349
+ // Order matters - more specific patterns should run first
350
+ filtered = redactCreditCards(filtered, options.sessionID, redactions)
351
+ filtered = redactSSN(filtered, options.sessionID, redactions)
352
+ filtered = redactBearerTokens(filtered, options.sessionID, redactions)
353
+ filtered = redactAPIKeys(filtered, options.sessionID, redactions)
354
+ filtered = redactPasswords(filtered, options.sessionID, redactions)
355
+ filtered = redactEmails(filtered, options.sessionID, redactions, options.redactEmails)
356
+
357
+ if (redactions.length > 0) {
358
+ log.info("filtered sensitive data", {
359
+ sessionID: options.sessionID,
360
+ redactionCount: redactions.length,
361
+ types: Array.from(new Set(redactions.map((r) => r.type))),
362
+ })
363
+ }
364
+
365
+ return {
366
+ text: filtered,
367
+ redactions,
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Restore redacted values in text (for displaying to user)
373
+ *
374
+ * @param text Text containing redaction placeholders
375
+ * @param sessionID Session ID to look up redactions
376
+ * @returns Text with placeholders replaced by original values
377
+ */
378
+ export function restore(text: string, sessionID: string): string {
379
+ const mapping = redactionMappings.get(sessionID)
380
+ if (!mapping) return text
381
+
382
+ let restored = text
383
+ for (const [placeholder, entry] of Array.from(mapping.entries())) {
384
+ restored = restored.replace(new RegExp(escapeRegex(placeholder), "g"), entry.original)
385
+ }
386
+
387
+ return restored
388
+ }
389
+
390
+ /**
391
+ * Get all redactions for a session
392
+ */
393
+ export function getRedactions(sessionID: string): RedactionEntry[] {
394
+ const mapping = redactionMappings.get(sessionID)
395
+ if (!mapping) return []
396
+ return Array.from(mapping.values())
397
+ }
398
+
399
+ /**
400
+ * Clear redaction mappings for a session (call when session ends)
401
+ */
402
+ export function clearSession(sessionID: string): void {
403
+ redactionMappings.delete(sessionID)
404
+ placeholderCounters.delete(sessionID)
405
+ log.info("cleared sensitive data mappings", { sessionID })
406
+ }
407
+
408
+ /**
409
+ * Check if text contains any sensitive patterns (without redacting)
410
+ * Useful for warnings or UI indicators
411
+ */
412
+ export function containsSensitive(text: string): { hasSensitive: boolean; types: RedactionType[] } {
413
+ const types: RedactionType[] = []
414
+
415
+ // Check for credit card patterns
416
+ const ccPattern = /\b(?:\d{4}[-\s]?){3}\d{4}\b|\b\d{13,19}\b/g
417
+ const ccMatches = text.match(ccPattern)
418
+ if (ccMatches) {
419
+ for (const match of ccMatches) {
420
+ const digits = match.replace(/\D/g, "")
421
+ if (digits.length >= 13 && digits.length <= 19 && isValidLuhn(digits)) {
422
+ types.push("CREDIT_CARD")
423
+ break
424
+ }
425
+ }
426
+ }
427
+
428
+ // Check for SSN pattern
429
+ if (/\b\d{3}-\d{2}-\d{4}\b/.test(text)) {
430
+ types.push("SSN")
431
+ }
432
+
433
+ // Check for password patterns
434
+ if (/\b(password|passwd|pwd|pass|secret)[\s]*[:=]/i.test(text)) {
435
+ types.push("PASSWORD")
436
+ }
437
+
438
+ // Check for API key patterns
439
+ if (
440
+ /\bsk-[a-zA-Z0-9_-]{20,}\b/.test(text) ||
441
+ /\bxox[bp]-[a-zA-Z0-9-]{20,}\b/.test(text) ||
442
+ /\bgh[pousr]_[a-zA-Z0-9]{36,}\b/.test(text) ||
443
+ /\bAKIA[A-Z0-9]{16}\b/.test(text) ||
444
+ /\b(api[_-]?key|apikey|access[_-]?key|secret[_-]?key)[\s]*[:=]/i.test(text)
445
+ ) {
446
+ types.push("API_KEY")
447
+ }
448
+
449
+ // Check for Bearer tokens
450
+ if (/\bBearer\s+[a-zA-Z0-9_.-]{20,}\b/i.test(text)) {
451
+ types.push("BEARER_TOKEN")
452
+ }
453
+
454
+ return {
455
+ hasSensitive: types.length > 0,
456
+ types: Array.from(new Set(types)),
457
+ }
458
+ }
459
+
460
+ /**
461
+ * Helper to escape special regex characters
462
+ */
463
+ function escapeRegex(str: string): string {
464
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
465
+ }
466
+ }
@@ -0,0 +1,76 @@
1
+ import { BusEvent } from "@/bus/bus-event"
2
+ import { Bus } from "@/bus"
3
+ import { Instance } from "@/project/instance"
4
+ import z from "zod"
5
+
6
+ export namespace SessionStatus {
7
+ export const Info = z
8
+ .union([
9
+ z.object({
10
+ type: z.literal("idle"),
11
+ }),
12
+ z.object({
13
+ type: z.literal("retry"),
14
+ attempt: z.number(),
15
+ message: z.string(),
16
+ next: z.number(),
17
+ }),
18
+ z.object({
19
+ type: z.literal("busy"),
20
+ }),
21
+ ])
22
+ .meta({
23
+ ref: "SessionStatus",
24
+ })
25
+ export type Info = z.infer<typeof Info>
26
+
27
+ export const Event = {
28
+ Status: BusEvent.define(
29
+ "session.status",
30
+ z.object({
31
+ sessionID: z.string(),
32
+ status: Info,
33
+ }),
34
+ ),
35
+ // deprecated
36
+ Idle: BusEvent.define(
37
+ "session.idle",
38
+ z.object({
39
+ sessionID: z.string(),
40
+ }),
41
+ ),
42
+ }
43
+
44
+ const state = Instance.state(() => {
45
+ const data: Record<string, Info> = {}
46
+ return data
47
+ })
48
+
49
+ export function get(sessionID: string) {
50
+ return (
51
+ state()[sessionID] ?? {
52
+ type: "idle",
53
+ }
54
+ )
55
+ }
56
+
57
+ export function list() {
58
+ return state()
59
+ }
60
+
61
+ export function set(sessionID: string, status: Info) {
62
+ Bus.publish(Event.Status, {
63
+ sessionID,
64
+ status,
65
+ })
66
+ if (status.type === "idle") {
67
+ // deprecated
68
+ Bus.publish(Event.Idle, {
69
+ sessionID,
70
+ })
71
+ delete state()[sessionID]
72
+ return
73
+ }
74
+ state()[sessionID] = status
75
+ }
76
+ }