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.
- package/AGENTS.md +27 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/opencode +336 -0
- package/bin/pty-wrapper.js +285 -0
- package/bunfig.toml +4 -0
- package/facebook_ads_library.png +0 -0
- package/nul`nif +0 -0
- package/package.json +111 -0
- package/parsers-config.ts +239 -0
- package/rird-1.0.199.tgz +0 -0
- package/script/build-windows.ts +54 -0
- package/script/build.ts +167 -0
- package/script/postinstall.mjs +544 -0
- package/script/publish-registries.ts +187 -0
- package/script/publish.ts +72 -0
- package/script/schema.ts +47 -0
- package/src/acp/README.md +164 -0
- package/src/acp/agent.ts +1063 -0
- package/src/acp/session.ts +101 -0
- package/src/acp/types.ts +22 -0
- package/src/agent/agent.ts +367 -0
- package/src/agent/generate.txt +75 -0
- package/src/agent/prompt/compaction.txt +12 -0
- package/src/agent/prompt/explore.txt +18 -0
- package/src/agent/prompt/summary.txt +10 -0
- package/src/agent/prompt/title.txt +36 -0
- package/src/auth/index.ts +70 -0
- package/src/bun/index.ts +114 -0
- package/src/bus/bus-event.ts +43 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +105 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/acp.ts +88 -0
- package/src/cli/cmd/agent.ts +256 -0
- package/src/cli/cmd/auth.ts +391 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/debug/config.ts +15 -0
- package/src/cli/cmd/debug/file.ts +91 -0
- package/src/cli/cmd/debug/index.ts +43 -0
- package/src/cli/cmd/debug/lsp.ts +48 -0
- package/src/cli/cmd/debug/ripgrep.ts +83 -0
- package/src/cli/cmd/debug/scrap.ts +15 -0
- package/src/cli/cmd/debug/skill.ts +15 -0
- package/src/cli/cmd/debug/snapshot.ts +48 -0
- package/src/cli/cmd/export.ts +88 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +1400 -0
- package/src/cli/cmd/import.ts +98 -0
- package/src/cli/cmd/mcp.ts +654 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/pr.ts +112 -0
- package/src/cli/cmd/run.ts +368 -0
- package/src/cli/cmd/serve.ts +31 -0
- package/src/cli/cmd/session.ts +106 -0
- package/src/cli/cmd/stats.ts +298 -0
- package/src/cli/cmd/tui/app.tsx +696 -0
- package/src/cli/cmd/tui/attach.ts +30 -0
- package/src/cli/cmd/tui/component/border.tsx +21 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +245 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
- package/src/cli/cmd/tui/component/logo.tsx +35 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1090 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/tips.ts +27 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/context/args.tsx +14 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +23 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +101 -0
- package/src/cli/cmd/tui/context/kv.tsx +49 -0
- package/src/cli/cmd/tui/context/local.tsx +354 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +46 -0
- package/src/cli/cmd/tui/context/sdk.tsx +74 -0
- package/src/cli/cmd/tui/context/sync.tsx +372 -0
- package/src/cli/cmd/tui/context/theme/aura.json +69 -0
- package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
- package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
- package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
- package/src/cli/cmd/tui/context/theme/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
- package/src/cli/cmd/tui/context/theme/orng.json +245 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/rird.json +245 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
- package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
- package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
- package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme.tsx +1109 -0
- package/src/cli/cmd/tui/event.ts +40 -0
- package/src/cli/cmd/tui/routes/home.tsx +138 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +1864 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +318 -0
- package/src/cli/cmd/tui/spawn.ts +60 -0
- package/src/cli/cmd/tui/thread.ts +142 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +332 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +100 -0
- package/src/cli/cmd/tui/util/clipboard.ts +127 -0
- package/src/cli/cmd/tui/util/editor.ts +32 -0
- package/src/cli/cmd/tui/util/terminal.ts +114 -0
- package/src/cli/cmd/tui/worker.ts +63 -0
- package/src/cli/cmd/uninstall.ts +344 -0
- package/src/cli/cmd/upgrade.ts +100 -0
- package/src/cli/cmd/web.ts +84 -0
- package/src/cli/error.ts +56 -0
- package/src/cli/ui.ts +84 -0
- package/src/cli/upgrade.ts +25 -0
- package/src/command/index.ts +80 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +97 -0
- package/src/config/config.ts +995 -0
- package/src/config/markdown.ts +41 -0
- package/src/env/index.ts +26 -0
- package/src/file/ignore.ts +83 -0
- package/src/file/index.ts +328 -0
- package/src/file/ripgrep.ts +393 -0
- package/src/file/time.ts +64 -0
- package/src/file/watcher.ts +103 -0
- package/src/flag/flag.ts +46 -0
- package/src/format/formatter.ts +315 -0
- package/src/format/index.ts +137 -0
- package/src/global/index.ts +52 -0
- package/src/id/id.ts +73 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +240 -0
- package/src/installation/index.ts +239 -0
- package/src/lsp/client.ts +229 -0
- package/src/lsp/index.ts +485 -0
- package/src/lsp/language.ts +116 -0
- package/src/lsp/server.ts +1895 -0
- package/src/mcp/auth.ts +135 -0
- package/src/mcp/index.ts +690 -0
- package/src/mcp/oauth-callback.ts +200 -0
- package/src/mcp/oauth-provider.ts +154 -0
- package/src/patch/index.ts +622 -0
- package/src/permission/index.ts +199 -0
- package/src/plugin/index.ts +91 -0
- package/src/project/bootstrap.ts +31 -0
- package/src/project/instance.ts +78 -0
- package/src/project/project.ts +221 -0
- package/src/project/state.ts +65 -0
- package/src/project/vcs.ts +76 -0
- package/src/provider/auth.ts +143 -0
- package/src/provider/models-macro.ts +11 -0
- package/src/provider/models.ts +106 -0
- package/src/provider/provider.ts +1071 -0
- package/src/provider/sdk/openai-compatible/src/README.md +5 -0
- package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
- package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
- package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
- package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
- package/src/provider/transform.ts +455 -0
- package/src/pty/index.ts +231 -0
- package/src/security/guardrails.test.ts +341 -0
- package/src/security/guardrails.ts +558 -0
- package/src/security/index.ts +19 -0
- package/src/server/error.ts +36 -0
- package/src/server/project.ts +79 -0
- package/src/server/server.ts +2642 -0
- package/src/server/tui.ts +71 -0
- package/src/session/compaction.ts +223 -0
- package/src/session/index.ts +461 -0
- package/src/session/llm.ts +201 -0
- package/src/session/message-v2.ts +690 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +409 -0
- package/src/session/prompt/act-switch.txt +5 -0
- package/src/session/prompt/anthropic-20250930.txt +166 -0
- package/src/session/prompt/anthropic.txt +85 -0
- package/src/session/prompt/anthropic_spoof.txt +1 -0
- package/src/session/prompt/beast.txt +103 -0
- package/src/session/prompt/codex.txt +304 -0
- package/src/session/prompt/copilot-gpt-5.txt +138 -0
- package/src/session/prompt/gemini.txt +85 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
- package/src/session/prompt/plan.txt +24 -0
- package/src/session/prompt/polaris.txt +84 -0
- package/src/session/prompt/qwen.txt +106 -0
- package/src/session/prompt.ts +1509 -0
- package/src/session/retry.ts +86 -0
- package/src/session/revert.ts +108 -0
- package/src/session/sensitive-filter.test.ts +327 -0
- package/src/session/sensitive-filter.ts +466 -0
- package/src/session/status.ts +76 -0
- package/src/session/summary.ts +194 -0
- package/src/session/system.ts +120 -0
- package/src/session/todo.ts +37 -0
- package/src/share/share-next.ts +194 -0
- package/src/share/share.ts +87 -0
- package/src/shell/shell.ts +67 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +83 -0
- package/src/snapshot/index.ts +197 -0
- package/src/storage/storage.ts +226 -0
- package/src/tests/agent.test.ts +308 -0
- package/src/tests/build-guards.test.ts +267 -0
- package/src/tests/config.test.ts +664 -0
- package/src/tests/tool-registry.test.ts +589 -0
- package/src/tool/bash.ts +317 -0
- package/src/tool/bash.txt +158 -0
- package/src/tool/batch.ts +175 -0
- package/src/tool/batch.txt +24 -0
- package/src/tool/codesearch.ts +168 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/edit.ts +675 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/glob.ts +65 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +121 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +110 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp-diagnostics.ts +26 -0
- package/src/tool/lsp-diagnostics.txt +1 -0
- package/src/tool/lsp-hover.ts +31 -0
- package/src/tool/lsp-hover.txt +1 -0
- package/src/tool/lsp.ts +87 -0
- package/src/tool/lsp.txt +19 -0
- package/src/tool/multiedit.ts +46 -0
- package/src/tool/multiedit.txt +41 -0
- package/src/tool/patch.ts +233 -0
- package/src/tool/patch.txt +1 -0
- package/src/tool/read.ts +219 -0
- package/src/tool/read.txt +12 -0
- package/src/tool/registry.ts +162 -0
- package/src/tool/skill.ts +100 -0
- package/src/tool/task.ts +136 -0
- package/src/tool/task.txt +51 -0
- package/src/tool/todo.ts +39 -0
- package/src/tool/todoread.txt +14 -0
- package/src/tool/todowrite.txt +167 -0
- package/src/tool/tool.ts +71 -0
- package/src/tool/webfetch.ts +198 -0
- package/src/tool/webfetch.txt +13 -0
- package/src/tool/websearch.ts +180 -0
- package/src/tool/websearch.txt +11 -0
- package/src/tool/write.ts +110 -0
- package/src/tool/write.txt +8 -0
- package/src/util/archive.ts +16 -0
- package/src/util/color.ts +19 -0
- package/src/util/context.ts +25 -0
- package/src/util/defer.ts +12 -0
- package/src/util/eventloop.ts +20 -0
- package/src/util/filesystem.ts +83 -0
- package/src/util/fn.ts +11 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +102 -0
- package/src/util/lazy.ts +11 -0
- package/src/util/license.ts +325 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +180 -0
- package/src/util/queue.ts +32 -0
- package/src/util/rpc.ts +42 -0
- package/src/util/scrap.ts +10 -0
- package/src/util/signal.ts +12 -0
- package/src/util/timeout.ts +14 -0
- package/src/util/token.ts +7 -0
- package/src/util/wildcard.ts +54 -0
- package/sst-env.d.ts +9 -0
- package/test/agent/agent.test.ts +146 -0
- package/test/bun.test.ts +53 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/config/agent-color.test.ts +66 -0
- package/test/config/config.test.ts +535 -0
- package/test/config/markdown.test.ts +89 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/fixture/fixture.ts +36 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/keybind.test.ts +421 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/mcp/headers.test.ts +153 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/preload.ts +57 -0
- package/test/project/project.test.ts +72 -0
- package/test/provider/provider.test.ts +1809 -0
- package/test/provider/transform.test.ts +411 -0
- package/test/session/retry.test.ts +111 -0
- package/test/session/session.test.ts +71 -0
- package/test/skill/skill.test.ts +131 -0
- package/test/snapshot/snapshot.test.ts +939 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/bash.test.ts +434 -0
- package/test/tool/grep.test.ts +108 -0
- package/test/tool/patch.test.ts +259 -0
- package/test/tool/read.test.ts +42 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/wildcard.test.ts +55 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { NamedError } from "@opencode-ai/util/error"
|
|
2
|
+
import { MessageV2 } from "./message-v2"
|
|
3
|
+
|
|
4
|
+
export namespace SessionRetry {
|
|
5
|
+
export const RETRY_INITIAL_DELAY = 2000
|
|
6
|
+
export const RETRY_BACKOFF_FACTOR = 2
|
|
7
|
+
export const RETRY_MAX_DELAY_NO_HEADERS = 30_000 // 30 seconds
|
|
8
|
+
|
|
9
|
+
export async function sleep(ms: number, signal: AbortSignal): Promise<void> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const timeout = setTimeout(resolve, ms)
|
|
12
|
+
signal.addEventListener(
|
|
13
|
+
"abort",
|
|
14
|
+
() => {
|
|
15
|
+
clearTimeout(timeout)
|
|
16
|
+
reject(new DOMException("Aborted", "AbortError"))
|
|
17
|
+
},
|
|
18
|
+
{ once: true },
|
|
19
|
+
)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function delay(attempt: number, error?: MessageV2.APIError) {
|
|
24
|
+
if (error) {
|
|
25
|
+
const headers = error.data.responseHeaders
|
|
26
|
+
if (headers) {
|
|
27
|
+
const retryAfterMs = headers["retry-after-ms"]
|
|
28
|
+
if (retryAfterMs) {
|
|
29
|
+
const parsedMs = Number.parseFloat(retryAfterMs)
|
|
30
|
+
if (!Number.isNaN(parsedMs)) {
|
|
31
|
+
return parsedMs
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const retryAfter = headers["retry-after"]
|
|
36
|
+
if (retryAfter) {
|
|
37
|
+
const parsedSeconds = Number.parseFloat(retryAfter)
|
|
38
|
+
if (!Number.isNaN(parsedSeconds)) {
|
|
39
|
+
// convert seconds to milliseconds
|
|
40
|
+
return Math.ceil(parsedSeconds * 1000)
|
|
41
|
+
}
|
|
42
|
+
// Try parsing as HTTP date format
|
|
43
|
+
const parsed = Date.parse(retryAfter) - Date.now()
|
|
44
|
+
if (!Number.isNaN(parsed) && parsed > 0) {
|
|
45
|
+
return Math.ceil(parsed)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return Math.min(RETRY_INITIAL_DELAY * Math.pow(RETRY_BACKOFF_FACTOR, attempt - 1), RETRY_MAX_DELAY_NO_HEADERS)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function retryable(error: ReturnType<NamedError["toObject"]>) {
|
|
57
|
+
if (MessageV2.APIError.isInstance(error)) {
|
|
58
|
+
if (!error.data.isRetryable) return undefined
|
|
59
|
+
return error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof error.data?.message === "string") {
|
|
63
|
+
try {
|
|
64
|
+
const json = JSON.parse(error.data.message)
|
|
65
|
+
if (json.type === "error" && json.error?.type === "too_many_requests") {
|
|
66
|
+
return "Too Many Requests"
|
|
67
|
+
}
|
|
68
|
+
if (json.code.includes("exhausted") || json.code.includes("unavailable")) {
|
|
69
|
+
return "Provider is overloaded"
|
|
70
|
+
}
|
|
71
|
+
if (json.type === "error" && json.error?.code?.includes("rate_limit")) {
|
|
72
|
+
return "Rate Limited"
|
|
73
|
+
}
|
|
74
|
+
if (
|
|
75
|
+
json.error?.message?.includes("no_kv_space") ||
|
|
76
|
+
(json.type === "error" && json.error?.type === "server_error") ||
|
|
77
|
+
!!json.error
|
|
78
|
+
) {
|
|
79
|
+
return "Provider Server Error"
|
|
80
|
+
}
|
|
81
|
+
} catch {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return undefined
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import z from "zod"
|
|
2
|
+
import { Identifier } from "../id/id"
|
|
3
|
+
import { Snapshot } from "../snapshot"
|
|
4
|
+
import { MessageV2 } from "./message-v2"
|
|
5
|
+
import { Session } from "."
|
|
6
|
+
import { Log } from "../util/log"
|
|
7
|
+
import { splitWhen } from "remeda"
|
|
8
|
+
import { Storage } from "../storage/storage"
|
|
9
|
+
import { Bus } from "../bus"
|
|
10
|
+
import { SessionPrompt } from "./prompt"
|
|
11
|
+
|
|
12
|
+
export namespace SessionRevert {
|
|
13
|
+
const log = Log.create({ service: "session.revert" })
|
|
14
|
+
|
|
15
|
+
export const RevertInput = z.object({
|
|
16
|
+
sessionID: Identifier.schema("session"),
|
|
17
|
+
messageID: Identifier.schema("message"),
|
|
18
|
+
partID: Identifier.schema("part").optional(),
|
|
19
|
+
})
|
|
20
|
+
export type RevertInput = z.infer<typeof RevertInput>
|
|
21
|
+
|
|
22
|
+
export async function revert(input: RevertInput) {
|
|
23
|
+
SessionPrompt.assertNotBusy(input.sessionID)
|
|
24
|
+
const all = await Session.messages({ sessionID: input.sessionID })
|
|
25
|
+
let lastUser: MessageV2.User | undefined
|
|
26
|
+
const session = await Session.get(input.sessionID)
|
|
27
|
+
|
|
28
|
+
let revert: Session.Info["revert"]
|
|
29
|
+
const patches: Snapshot.Patch[] = []
|
|
30
|
+
for (const msg of all) {
|
|
31
|
+
if (msg.info.role === "user") lastUser = msg.info
|
|
32
|
+
const remaining = []
|
|
33
|
+
for (const part of msg.parts) {
|
|
34
|
+
if (revert) {
|
|
35
|
+
if (part.type === "patch") {
|
|
36
|
+
patches.push(part)
|
|
37
|
+
}
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!revert) {
|
|
42
|
+
if ((msg.info.id === input.messageID && !input.partID) || part.id === input.partID) {
|
|
43
|
+
// if no useful parts left in message, same as reverting whole message
|
|
44
|
+
const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) ? input.partID : undefined
|
|
45
|
+
revert = {
|
|
46
|
+
messageID: !partID && lastUser ? lastUser.id : msg.info.id,
|
|
47
|
+
partID,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
remaining.push(part)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (revert) {
|
|
56
|
+
const session = await Session.get(input.sessionID)
|
|
57
|
+
revert.snapshot = session.revert?.snapshot ?? (await Snapshot.track())
|
|
58
|
+
await Snapshot.revert(patches)
|
|
59
|
+
if (revert.snapshot) revert.diff = await Snapshot.diff(revert.snapshot)
|
|
60
|
+
return Session.update(input.sessionID, (draft) => {
|
|
61
|
+
draft.revert = revert
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
return session
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function unrevert(input: { sessionID: string }) {
|
|
68
|
+
log.info("unreverting", input)
|
|
69
|
+
SessionPrompt.assertNotBusy(input.sessionID)
|
|
70
|
+
const session = await Session.get(input.sessionID)
|
|
71
|
+
if (!session.revert) return session
|
|
72
|
+
if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot)
|
|
73
|
+
const next = await Session.update(input.sessionID, (draft) => {
|
|
74
|
+
draft.revert = undefined
|
|
75
|
+
})
|
|
76
|
+
return next
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export async function cleanup(session: Session.Info) {
|
|
80
|
+
if (!session.revert) return
|
|
81
|
+
const sessionID = session.id
|
|
82
|
+
let msgs = await Session.messages({ sessionID })
|
|
83
|
+
const messageID = session.revert.messageID
|
|
84
|
+
const [preserve, remove] = splitWhen(msgs, (x) => x.info.id === messageID)
|
|
85
|
+
msgs = preserve
|
|
86
|
+
for (const msg of remove) {
|
|
87
|
+
await Storage.remove(["message", sessionID, msg.info.id])
|
|
88
|
+
await Bus.publish(MessageV2.Event.Removed, { sessionID: sessionID, messageID: msg.info.id })
|
|
89
|
+
}
|
|
90
|
+
const last = preserve.at(-1)
|
|
91
|
+
if (session.revert.partID && last) {
|
|
92
|
+
const partID = session.revert.partID
|
|
93
|
+
const [preserveParts, removeParts] = splitWhen(last.parts, (x) => x.id === partID)
|
|
94
|
+
last.parts = preserveParts
|
|
95
|
+
for (const part of removeParts) {
|
|
96
|
+
await Storage.remove(["part", last.info.id, part.id])
|
|
97
|
+
await Bus.publish(MessageV2.Event.PartRemoved, {
|
|
98
|
+
sessionID: sessionID,
|
|
99
|
+
messageID: last.info.id,
|
|
100
|
+
partID: part.id,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
await Session.update(sessionID, (draft) => {
|
|
105
|
+
draft.revert = undefined
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for sensitive data filter
|
|
3
|
+
* Run with: bun test sensitive-filter.test.ts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach } from "bun:test"
|
|
7
|
+
import { SensitiveFilter } from "./sensitive-filter"
|
|
8
|
+
|
|
9
|
+
describe("SensitiveFilter", () => {
|
|
10
|
+
const testSessionID = "test-session-123"
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
// Clear any previous session data
|
|
14
|
+
SensitiveFilter.clearSession(testSessionID)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
describe("Credit Card Detection", () => {
|
|
18
|
+
it("should detect and redact valid Visa card numbers", () => {
|
|
19
|
+
const text = "My card is 4532015112830366"
|
|
20
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
21
|
+
|
|
22
|
+
expect(result.text).not.toContain("4532015112830366")
|
|
23
|
+
expect(result.text).toContain("[REDACTED-CREDIT_CARD]")
|
|
24
|
+
expect(result.redactions).toHaveLength(1)
|
|
25
|
+
expect(result.redactions[0].type).toBe("CREDIT_CARD")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it("should detect card numbers with dashes", () => {
|
|
29
|
+
const text = "Card: 4532-0151-1283-0366"
|
|
30
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
31
|
+
|
|
32
|
+
expect(result.text).not.toContain("4532-0151-1283-0366")
|
|
33
|
+
expect(result.text).toContain("[REDACTED-CREDIT_CARD]")
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it("should detect card numbers with spaces", () => {
|
|
37
|
+
const text = "Card: 4532 0151 1283 0366"
|
|
38
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
39
|
+
|
|
40
|
+
expect(result.text).not.toContain("4532 0151 1283 0366")
|
|
41
|
+
expect(result.text).toContain("[REDACTED-CREDIT_CARD]")
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it("should not redact numbers that fail Luhn check", () => {
|
|
45
|
+
const text = "Random number: 1234567890123456"
|
|
46
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
47
|
+
|
|
48
|
+
// This should not be redacted as it fails Luhn
|
|
49
|
+
expect(result.redactions).toHaveLength(0)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it("should detect American Express (15 digits)", () => {
|
|
53
|
+
// Valid Amex test number
|
|
54
|
+
const text = "Amex: 378282246310005"
|
|
55
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
56
|
+
|
|
57
|
+
expect(result.text).not.toContain("378282246310005")
|
|
58
|
+
expect(result.text).toContain("[REDACTED-CREDIT_CARD]")
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
describe("SSN Detection", () => {
|
|
63
|
+
it("should detect and redact SSN format", () => {
|
|
64
|
+
const text = "SSN: 123-45-6789"
|
|
65
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
66
|
+
|
|
67
|
+
expect(result.text).not.toContain("123-45-6789")
|
|
68
|
+
expect(result.text).toContain("[REDACTED-SSN]")
|
|
69
|
+
expect(result.redactions).toHaveLength(1)
|
|
70
|
+
expect(result.redactions[0].type).toBe("SSN")
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it("should handle multiple SSNs", () => {
|
|
74
|
+
const text = "SSN1: 123-45-6789, SSN2: 987-65-4321"
|
|
75
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
76
|
+
|
|
77
|
+
expect(result.text).not.toContain("123-45-6789")
|
|
78
|
+
expect(result.text).not.toContain("987-65-4321")
|
|
79
|
+
expect(result.redactions).toHaveLength(2)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
describe("Password Detection", () => {
|
|
84
|
+
it("should detect password: value patterns", () => {
|
|
85
|
+
const text = "password: mySecretPass123"
|
|
86
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
87
|
+
|
|
88
|
+
expect(result.text).not.toContain("mySecretPass123")
|
|
89
|
+
expect(result.text).toContain("[REDACTED-PASSWORD]")
|
|
90
|
+
expect(result.text).toContain("password:")
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it("should detect pwd=value patterns", () => {
|
|
94
|
+
const text = "config: pwd=hunter2"
|
|
95
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
96
|
+
|
|
97
|
+
expect(result.text).not.toContain("hunter2")
|
|
98
|
+
expect(result.text).toContain("[REDACTED-PASSWORD]")
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("should detect quoted passwords", () => {
|
|
102
|
+
const text = 'password="super_secret_123"'
|
|
103
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
104
|
+
|
|
105
|
+
expect(result.text).not.toContain("super_secret_123")
|
|
106
|
+
expect(result.text).toContain("[REDACTED-PASSWORD]")
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it("should detect secret: patterns", () => {
|
|
110
|
+
const text = "secret: my_api_secret_value"
|
|
111
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
112
|
+
|
|
113
|
+
expect(result.text).not.toContain("my_api_secret_value")
|
|
114
|
+
expect(result.redactions).toHaveLength(1)
|
|
115
|
+
expect(result.redactions[0].type).toBe("PASSWORD")
|
|
116
|
+
})
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
describe("API Key Detection", () => {
|
|
120
|
+
it("should detect OpenAI style keys (sk-*)", () => {
|
|
121
|
+
const text = "API key: sk-proj-abc123def456ghi789jkl012mno345pqr678stu"
|
|
122
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
123
|
+
|
|
124
|
+
expect(result.text).not.toContain("sk-proj")
|
|
125
|
+
expect(result.text).toContain("[REDACTED-API_KEY]")
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it("should detect Slack tokens (xoxb-*)", () => {
|
|
129
|
+
const text = "Slack: xoxb-1234567890-1234567890123-abcdefghijklmnopqrstuvwx"
|
|
130
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
131
|
+
|
|
132
|
+
expect(result.text).not.toContain("xoxb-")
|
|
133
|
+
expect(result.text).toContain("[REDACTED-API_KEY]")
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it("should detect GitHub tokens (ghp_*)", () => {
|
|
137
|
+
const text = "GitHub PAT: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
138
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
139
|
+
|
|
140
|
+
expect(result.text).not.toContain("ghp_")
|
|
141
|
+
expect(result.text).toContain("[REDACTED-API_KEY]")
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it("should detect AWS access keys (AKIA*)", () => {
|
|
145
|
+
const text = "AWS key: AKIAIOSFODNN7EXAMPLE"
|
|
146
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
147
|
+
|
|
148
|
+
expect(result.text).not.toContain("AKIAIOSFODNN7EXAMPLE")
|
|
149
|
+
expect(result.text).toContain("[REDACTED-API_KEY]")
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it("should detect api_key= patterns", () => {
|
|
153
|
+
const text = "config: api_key=abcdef1234567890abcdef"
|
|
154
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
155
|
+
|
|
156
|
+
expect(result.text).not.toContain("abcdef1234567890abcdef")
|
|
157
|
+
expect(result.text).toContain("[REDACTED-API_KEY]")
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
describe("Bearer Token Detection", () => {
|
|
162
|
+
it("should detect Bearer tokens", () => {
|
|
163
|
+
const text = "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
|
|
164
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
165
|
+
|
|
166
|
+
expect(result.text).not.toContain("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9")
|
|
167
|
+
expect(result.text).toContain("Bearer [REDACTED-BEARER_TOKEN]")
|
|
168
|
+
})
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
describe("Email Detection (Optional)", () => {
|
|
172
|
+
it("should not redact emails by default", () => {
|
|
173
|
+
const text = "Contact: user@example.com"
|
|
174
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
175
|
+
|
|
176
|
+
expect(result.text).toContain("user@example.com")
|
|
177
|
+
expect(result.redactions).toHaveLength(0)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it("should redact emails when enabled", () => {
|
|
181
|
+
const text = "Contact: user@example.com"
|
|
182
|
+
const result = SensitiveFilter.filter(text, {
|
|
183
|
+
sessionID: testSessionID,
|
|
184
|
+
redactEmails: true,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
expect(result.text).not.toContain("user@example.com")
|
|
188
|
+
expect(result.text).toContain("[REDACTED-EMAIL]")
|
|
189
|
+
})
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
describe("Restore Functionality", () => {
|
|
193
|
+
it("should restore redacted values", () => {
|
|
194
|
+
const originalText = "password: secretValue123"
|
|
195
|
+
const filtered = SensitiveFilter.filter(originalText, { sessionID: testSessionID })
|
|
196
|
+
|
|
197
|
+
expect(filtered.text).not.toContain("secretValue123")
|
|
198
|
+
|
|
199
|
+
const restored = SensitiveFilter.restore(filtered.text, testSessionID)
|
|
200
|
+
expect(restored).toContain("secretValue123")
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it("should restore multiple redacted values", () => {
|
|
204
|
+
const originalText = "card: 4532015112830366, ssn: 123-45-6789"
|
|
205
|
+
const filtered = SensitiveFilter.filter(originalText, { sessionID: testSessionID })
|
|
206
|
+
|
|
207
|
+
expect(filtered.text).not.toContain("4532015112830366")
|
|
208
|
+
expect(filtered.text).not.toContain("123-45-6789")
|
|
209
|
+
|
|
210
|
+
const restored = SensitiveFilter.restore(filtered.text, testSessionID)
|
|
211
|
+
expect(restored).toContain("4532015112830366")
|
|
212
|
+
expect(restored).toContain("123-45-6789")
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
describe("containsSensitive Check", () => {
|
|
217
|
+
it("should detect sensitive patterns without redacting", () => {
|
|
218
|
+
const text = "My card is 4532015112830366 and SSN is 123-45-6789"
|
|
219
|
+
const check = SensitiveFilter.containsSensitive(text)
|
|
220
|
+
|
|
221
|
+
expect(check.hasSensitive).toBe(true)
|
|
222
|
+
expect(check.types).toContain("CREDIT_CARD")
|
|
223
|
+
expect(check.types).toContain("SSN")
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
it("should return false for clean text", () => {
|
|
227
|
+
const text = "This is just a regular message with no sensitive data"
|
|
228
|
+
const check = SensitiveFilter.containsSensitive(text)
|
|
229
|
+
|
|
230
|
+
expect(check.hasSensitive).toBe(false)
|
|
231
|
+
expect(check.types).toHaveLength(0)
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
describe("Session Management", () => {
|
|
236
|
+
it("should track redactions per session", () => {
|
|
237
|
+
const session1 = "session-1"
|
|
238
|
+
const session2 = "session-2"
|
|
239
|
+
|
|
240
|
+
SensitiveFilter.filter("password: secret1", { sessionID: session1 })
|
|
241
|
+
SensitiveFilter.filter("password: secret2", { sessionID: session2 })
|
|
242
|
+
|
|
243
|
+
const redactions1 = SensitiveFilter.getRedactions(session1)
|
|
244
|
+
const redactions2 = SensitiveFilter.getRedactions(session2)
|
|
245
|
+
|
|
246
|
+
expect(redactions1).toHaveLength(1)
|
|
247
|
+
expect(redactions1[0].original).toBe("secret1")
|
|
248
|
+
|
|
249
|
+
expect(redactions2).toHaveLength(1)
|
|
250
|
+
expect(redactions2[0].original).toBe("secret2")
|
|
251
|
+
|
|
252
|
+
// Clean up
|
|
253
|
+
SensitiveFilter.clearSession(session1)
|
|
254
|
+
SensitiveFilter.clearSession(session2)
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it("should clear session data", () => {
|
|
258
|
+
SensitiveFilter.filter("password: secret", { sessionID: testSessionID })
|
|
259
|
+
|
|
260
|
+
expect(SensitiveFilter.getRedactions(testSessionID)).toHaveLength(1)
|
|
261
|
+
|
|
262
|
+
SensitiveFilter.clearSession(testSessionID)
|
|
263
|
+
|
|
264
|
+
expect(SensitiveFilter.getRedactions(testSessionID)).toHaveLength(0)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it("should use unique placeholders for multiple redactions", () => {
|
|
268
|
+
const text = "password: secret1, pwd: secret2, pass: secret3"
|
|
269
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
270
|
+
|
|
271
|
+
// Should have 3 different placeholder IDs
|
|
272
|
+
const placeholders = result.text.match(/\[REDACTED-PASSWORD[^\]]*\]/g)
|
|
273
|
+
expect(placeholders).not.toBeNull()
|
|
274
|
+
expect(new Set(placeholders).size).toBe(3)
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
describe("Edge Cases", () => {
|
|
279
|
+
it("should not double-redact already redacted values", () => {
|
|
280
|
+
const text = "password: [REDACTED-PASSWORD]"
|
|
281
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
282
|
+
|
|
283
|
+
expect(result.text).toBe("password: [REDACTED-PASSWORD]")
|
|
284
|
+
expect(result.redactions).toHaveLength(0)
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it("should handle empty strings", () => {
|
|
288
|
+
const result = SensitiveFilter.filter("", { sessionID: testSessionID })
|
|
289
|
+
|
|
290
|
+
expect(result.text).toBe("")
|
|
291
|
+
expect(result.redactions).toHaveLength(0)
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
it("should handle text with no sensitive data", () => {
|
|
295
|
+
const text = "Hello, this is a normal message about coding."
|
|
296
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
297
|
+
|
|
298
|
+
expect(result.text).toBe(text)
|
|
299
|
+
expect(result.redactions).toHaveLength(0)
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it("should handle mixed sensitive data types", () => {
|
|
303
|
+
const text = `
|
|
304
|
+
Credit Card: 4532015112830366
|
|
305
|
+
SSN: 123-45-6789
|
|
306
|
+
Password: mySecret123
|
|
307
|
+
API Key: sk-proj-abcdefghijklmnopqrstuvwx
|
|
308
|
+
Bearer Token: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI
|
|
309
|
+
`
|
|
310
|
+
const result = SensitiveFilter.filter(text, { sessionID: testSessionID })
|
|
311
|
+
|
|
312
|
+
expect(result.text).not.toContain("4532015112830366")
|
|
313
|
+
expect(result.text).not.toContain("123-45-6789")
|
|
314
|
+
expect(result.text).not.toContain("mySecret123")
|
|
315
|
+
expect(result.text).not.toContain("sk-proj")
|
|
316
|
+
expect(result.text).not.toContain("eyJhbGciOiJIUzI1NiIsInR5cCI")
|
|
317
|
+
|
|
318
|
+
// Check all types were detected
|
|
319
|
+
const types = result.redactions.map((r) => r.type)
|
|
320
|
+
expect(types).toContain("CREDIT_CARD")
|
|
321
|
+
expect(types).toContain("SSN")
|
|
322
|
+
expect(types).toContain("PASSWORD")
|
|
323
|
+
expect(types).toContain("API_KEY")
|
|
324
|
+
expect(types).toContain("BEARER_TOKEN")
|
|
325
|
+
})
|
|
326
|
+
})
|
|
327
|
+
})
|