pi-information-protecter 0.9.0 → 0.10.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog / 更新日志
2
2
 
3
+ ## 0.10.0 — Pi 1.x compatibility / 兼容 Pi 1.x
4
+
5
+ ### Added / 新增
6
+
7
+ - Accept Pi 0.99.x and 1.2.0–1.3.x, established by running the full suite, including the real loader and worker integration tests, against 0.87.1, 0.99.2, 1.2.0 and 1.3.0. The runtime gate refused every 0.88+ version, so on current Pi releases the extension failed every request closed with `UNSUPPORTED_PI` while the status bar still showed it as loaded.
8
+ 接收 Pi 0.99.x 与 1.2.0–1.3.x,依据是在 0.87.1、0.99.2、1.2.0 和 1.3.0 上运行完整测试(含真实加载器与 worker 集成测试)。之前运行时门禁拒绍所有 0.88+ 版本,因此在当前 Pi 上每个请求都以 `UNSUPPORTED_PI` 被拦,状态栏却仍显示已加载。
9
+ - Document that nested model calls outside the agent loop are not redacted: a `codemode` script's `models.classify()` or `models.generateImages()`, and any `modelRegistry` call another extension makes, carry no `onPayload` callback. Pi 0.99.0 added `codemode`, MCP and tool search as built-in extensions, so this boundary is new.
10
+ 记录智能体循环之外的嵌套模型调用不会脖敏:`codemode` 脚本的 `models.classify()`、`models.generateImages()` 以及其他扩展的 `modelRegistry` 调用都不携带 `onPayload` 回调。Pi 0.99.0 新增了内置的 `codemode`、MCP 与工具检索扩展,因此这是新的边界。
11
+
12
+ ### Changed / 变更
13
+
14
+ - Refuse Pi 1.0.x and 1.1.0: both depend on `@earendil-works/pi-codemode: ^1.0.4`, which now resolves to 1.3.0, whose missing `MCP_TYPESCRIPT_PREAMBLE` export makes Pi itself throw while loading. This mirrors the existing 0.85.0 exclusion — Pi's own unresolvable import, not an extension defect.
15
+ 拒绝 Pi 1.0.x 与 1.1.0:二者依赖 `@earendil-works/pi-codemode: ^1.0.4`,它如今解析到 1.3.0,而该版本已不导出 `MCP_TYPESCRIPT_PREAMBLE`,导致 Pi 自身在加载时抛错。这与现有的 0.85.0 排除同理:问题出在 Pi 自身无法解析的引用,而非扩展缺陷。
16
+ - Build and verify against Pi 1.3.0 and widen the declared peer range to match the runtime gate exactly.
17
+ 构建与验证基线改为 Pi 1.3.0,并将声明的 peer 范围与运行时门禁保持完全一致。
18
+
3
19
  ## 0.9.0 — A dedicated summarization model / 专用摘要模型
4
20
 
5
21
  ### Added / 新增
package/README.md CHANGED
@@ -10,14 +10,14 @@ A Pi extension that inspects outgoing LLM request bodies locally, replaces confi
10
10
 
11
11
  ## Installation
12
12
 
13
- Requires Node.js 22+ and `@earendil-works/pi-coding-agent` **0.84.0–0.84.x or 0.85.1–0.87.x**. Every accepted release was verified by running the full suite, including the real loader and worker integration tests. 0.83.0 and earlier lack `registerMarkdownTransformer`, and 0.85.0 imports `@earendil-works/pi-server`, which no Pi release declares, so both fail to load. Legacy `@mariozechner/*` releases are not supported.
13
+ Requires Node.js 22+ and `@earendil-works/pi-coding-agent` **0.84.0–0.84.x, 0.85.1–0.87.x, 0.99.x or 1.2.0–1.3.x**. Every accepted release was verified by running the full suite, including the real loader and worker integration tests. 0.83.0 and earlier lack `registerMarkdownTransformer`; 0.85.0 imports `@earendil-works/pi-server`, which no Pi release declares; and 1.0.x and 1.1.0 resolve `@earendil-works/pi-codemode` to a version that no longer exports `MCP_TYPESCRIPT_PREAMBLE`, so all of them fail to load. Legacy `@mariozechner/*` releases are not supported.
14
14
 
15
15
  ```bash
16
16
  # Install the latest npm release.
17
17
  pi install npm:pi-information-protecter
18
18
 
19
19
  # Pin a version only when you have a reason to; older releases miss fixes.
20
- pi install npm:pi-information-protecter@0.7.0
20
+ pi install npm:pi-information-protecter@0.10.0
21
21
 
22
22
  # Alternatively, build and install from this repository without copying it.
23
23
  npm ci
@@ -261,7 +261,7 @@ Local response and tool-argument restoration
261
261
  - Exit, reload and session switching discard mappings. Final restored local messages remain readable; crash leftovers, old summaries and raw deltas cannot recover tokens across restarts.
262
262
  - Broad rules such as `.` or all digits may alter protocol fields, model names, tool schemas or IDs and break requests. Matching numbers become token strings. Prefer precise rules.
263
263
  - Reused tokens reveal equality relationships; context may imply identity. This is not formal anonymization.
264
- - Hooks do not cover HTTP headers, authentication keys, gateway endpoints, Pi telemetry, `/share`, independent extension SDK/fetch calls or tool networking. The gateway still receives its authentication credentials.
264
+ - Hooks do not cover HTTP headers, authentication keys, gateway endpoints, Pi telemetry, `/share`, independent extension SDK/fetch calls or tool networking. The gateway still receives its authentication credentials. Nested model calls outside the agent loop are also uncovered: a `codemode` script's `models.classify()` or `models.generateImages()`, and any `modelRegistry` call another extension makes, are sent without redaction. Pi 0.99.0 ships `codemode`, MCP and tool search as built-in extensions; keep `codemode` out of `defaultTools` and avoid MCP servers where that matters.
265
265
  - **Load after other payload rewriters and trust every installed extension.** Later handlers can reintroduce originals. Extensions have local permissions, and Pi may continue if this extension fails to load. Check status before entering secrets.
266
266
  - Local sessions, terminals, exports and files written by tools may contain plaintext. They are not encrypted or cleaned by this project.
267
267
 
@@ -290,14 +290,14 @@ API references: [Pi Extensions](https://pi.dev/docs/latest/extensions), local 0.
290
290
 
291
291
  ## 安装
292
292
 
293
- 要求 Node.js 22+ 和 `@earendil-works/pi-coding-agent` **0.84.0–0.84.x 或 0.85.1–0.87.x**。每个受支持版本都经过完整测试验证(含真实加载器与 worker 集成测试)。0.83.0 及更早缺少 `registerMarkdownTransformer`,0.85.0 引用了任何 Pi 版本都未声明的 `@earendil-works/pi-server`,两者均无法加载。不支持旧 `@mariozechner/*` 版本。
293
+ 要求 Node.js 22+ 和 `@earendil-works/pi-coding-agent` **0.84.0–0.84.x、0.85.1–0.87.x、0.99.x 或 1.2.0–1.3.x**。每个受支持版本都经过完整测试验证(含真实加载器与 worker 集成测试)。0.83.0 及更早缺少 `registerMarkdownTransformer`;0.85.0 引用了任何 Pi 版本都未声明的 `@earendil-works/pi-server`;1.0.x 与 1.1.0 将 `@earendil-works/pi-codemode` 解析到已不再导出 `MCP_TYPESCRIPT_PREAMBLE` 的版本,三者均无法加载。不支持旧 `@mariozechner/*` 版本。
294
294
 
295
295
  ```bash
296
296
  # 安装最新 npm 版本。
297
297
  pi install npm:pi-information-protecter
298
298
 
299
299
  # 确有需要时才固定版本;旧版本缺少后续修复。
300
- pi install npm:pi-information-protecter@0.7.0
300
+ pi install npm:pi-information-protecter@0.10.0
301
301
 
302
302
  # 或在本仓库构建后本地安装,不复制仓库。
303
303
  npm ci
@@ -543,7 +543,7 @@ before_provider_request:最终 JSON 文本扫描
543
543
  - 退出、重载和切换会话会丢弃映射。最终已还原消息仍可读,但崩溃残留、旧摘要和原始增量无法跨重启恢复占位符。
544
544
  - 过宽规则可能改写协议字段、模型名、工具结构和 ID,导致请求失败;命中数字变为占位符字符串,应优先使用精确规则。
545
545
  - 复用占位符会暴露值相等的关系,上下文也可能揭示身份,这不是形式化匿名化。
546
- - 钩子不覆盖认证头、API key、网关地址、遥测、分享、扩展独立网络调用和工具联网;网关仍收到其认证凭证。
546
+ - 钩子不覆盖认证头、API key、网关地址、遥测、分享、扩展独立网络调用和工具联网;网关仍收到其认证凭证。智能体循环之外的嵌套模型调用同样不覆盖:`codemode` 脚本的 `models.classify()`、`models.generateImages()` 以及其他扩展的 `modelRegistry` 调用都不经脖敏即发出。Pi 0.99.0 将 `codemode`、MCP 与工具检索内置为扩展,在意这一点时请不要将 `codemode` 加入 `defaultTools`,并避免使用 MCP 服务器。
547
547
  - **在其他请求改写扩展之后加载,并只使用可信扩展。** 后续处理器可重新加入原文;扩展有本机权限,插件加载失败时 Pi 可能继续运行,请先检查状态再输入秘密。
548
548
  - 本地会话、终端、导出和工具写出的文件可能包含明文,本项目不加密或清理它们。
549
549
 
package/SECURITY.md CHANGED
@@ -33,8 +33,10 @@ Prevent configured personal information from accidentally entering Pi LLM reques
33
33
  拦截诊断仅包含目录错误码、阶段、非负整数定位和白名单系统错误码。规则序号和节点数只是位置,不是内容。不输出原始异常消息、请求文本、命中原文、替换值、配置内容和文件路径,worker 边界同样适用,因此诊断可安全分享;错误码有意保持粗粒度,精确定位规则仍可能需要本地查看。
34
34
  - Limit worker regex scans to 2 seconds, with resource and input limits. Errors exclude original configuration, rules and request text.
35
35
  worker 正则扫描限制为 2 秒,并限制资源和输入;错误不包含原始配置、规则或请求文本。
36
- - Pi 0.84.0–0.87.x catches `before_provider_request` errors and continues with the current payload. Error paths therefore return `{}` and attempt cancellation. This does not guarantee zero HTTP requests; it prevents this handler from falling back to the original payload.
37
- Pi 0.84.0–0.87.x 会捕获该钩子异常并继续使用当前请求体,因此错误路径返回空对象并尝试取消。这不保证没有 HTTP 请求,只保证本处理器不回退原始请求体。
36
+ - Every supported Pi release, 0.84.0–0.87.x, 0.99.x and 1.2.0–1.3.x, catches `before_provider_request` errors and continues with the current payload. Error paths therefore return `{}` and attempt cancellation. This does not guarantee zero HTTP requests; it prevents this handler from falling back to the original payload.
37
+ 所有受支持的 Pi 版本(0.84.0–0.87.x、0.99.x 与 1.2.0–1.3.x)都会捕获该钩子异常并继续使用当前请求体,因此错误路径返回空对象并尝试取消。这不保证没有 HTTP 请求,只保证本处理器不回退原始请求体。
38
+ - Only the agent's own model requests carry the `onPayload` callback this extension hooks. Nested model calls made outside that loop, such as a `codemode` script's `models.classify()` or `models.generateImages()`, another extension's `modelRegistry` call, or any direct SDK call, are sent without redaction. Pi 0.99.0 added `codemode`, MCP and tool search as built-in extensions; `codemode` is active only when `defaultTools` or `--tools` enables it, and MCP servers are external programs that receive tool arguments directly. Keep both off in high-risk environments.
39
+ 只有智能体自身的模型请求会携带本扩展挂接的 `onPayload` 回调。循环之外的嵌套模型调用(如 `codemode` 脚本的 `models.classify()`、`models.generateImages()`、其他扩展的 `modelRegistry` 调用或任何直接 SDK 调用)不经脖敏即发出。Pi 0.99.0 将 `codemode`、MCP 与工具检索内置为扩展;`codemode` 仅在 `defaultTools` 或 `--tools` 启用时生效,MCP 服务器则是直接接收工具参数的外部程序。高风险环境下应将二者保持关闭。
38
40
  - Create configuration separately with Unix mode 0600. Reject links, non-regular files, multiple links and files owned by others. Users remain responsible for directories and OS permissions; TOCTOU races are not prevented.
39
41
  独立创建配置,Unix 权限为 0600;拒绝链接、非普通文件、多链接及他人所有文件。用户仍需维护目录和系统权限,无法防止检查与使用之间的竞态。
40
42
  - Direct-access blocking is supplementary. File tools inspect target paths rather than document bodies. Shell can bypass string checks, and command mentions or ancestor directory checks can still cause false positives.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /*! SPI Protecter | MIT | see LICENSE / SPI 隐私保护插件 | MIT 许可 */
2
- import{getAgentDir as St,VERSION as _t}from"@earendil-works/pi-coding-agent";import{Worker as mt}from"node:worker_threads";import{fileURLToPath as pt}from"node:url";import*as I from"node:fs";import{join as P,resolve as ot}from"node:path";import{createHash as nt,randomUUID as rt}from"node:crypto";import{Worker as it}from"node:worker_threads";import{fileURLToPath as st}from"node:url";var J="0.84.0-0.84.x, 0.85.1-0.87.x";function se(o){let e=/^(\d+)\.(\d+)\.(\d+)/.exec(o);if(!e)return!1;let[n,t,r]=e.slice(1).map(Number);return n!==0?!1:t===84?!0:t===85?r>=1:t===86||t===87}var Y={INTERNAL:["Unexpected local failure","Reload; check the installed package integrity"],NOT_READY:["Protection is not initialized","Run /protecter status for the initialization error, fix it, then /reload"],UNSUPPORTED_PI:["Unsupported Pi version",`Use Pi ${J}`],MACHINE_ID:["Machine identifier unavailable","Check local OS machine-ID access; no random fallback is used"],CONFIG_JSON:["Invalid configuration JSON","Fix JSON syntax locally, then /reload"],CONFIG_SCHEMA:["Invalid configuration structure or rule","Use version 1, a sensitiveWords array and documented fields; rule shows the 1-based index"],CONFIG_REGEX:["Invalid regex or flags","Fix the numbered rule; flags support gimsu only"],CONFIG_TARGET:["Invalid fixed replacement target","Use a non-blank string under 8192 characters without reserved __PIP_ names"],CONFIG_CONFLICT:["Conflicting rule definitions","Give the same matcher one replacement policy across all configs"],CONFIG_SIZE:["Configuration exceeds size or rule limit","Reduce configuration size or rule count; rules are never truncated"],CONFIG_IO:["Cannot access configuration","Check local permissions, ownership and free space"],CONFIG_UNSAFE:["Unsafe configuration file","Use an owned regular file without symbolic or hard links"],CONFIG_EMPTY_MATCH:["Regex matches an empty string","Require a non-empty match in the numbered rule"],CONFIG_SENSITIVE_TARGET:["Fixed target overlaps a sensitive rule","Choose a target unrelated to every sensitive matcher"],CONFIG_ALIAS_CONFLICT:["Fixed aliases overlap each other","Use distinct aliases that are not substrings of one another"],CONFIG_TIMEOUT:["Rule validation timed out","Simplify regex rules, then /reload"],MIGRATION_LOCKED:["Configuration migration lock is busy","Wait for other Pi processes; inspect a stale lock only after stopping them"],MIGRATION_CHANGED:["Configuration changed during migration","Stop concurrent edits and /reload; source files are preserved"],MIGRATION_LIMIT:["Too many configuration candidates","Consolidate old configuration files locally"],MIGRATION_IO:["Migration file operation failed","Check permissions, free space and leftover files before retrying"],PAYLOAD_JSON:["Request is not serializable JSON","Inspect the provider or another payload-modifying extension; no payload is logged"],PAYLOAD_SIZE:["Request exceeds the size limit","Reduce history or tool output size, or start a new session"],SCAN_TIMEOUT:["Redaction worker timed out","Simplify regex rules or reduce input size"],WORKER_FAILED:["Worker failed to start or exited early","Verify all dist files and the Node version; reinstall if needed"],SCAN_INTERNAL:["Unexpected scan failure","Review recent rule changes and retry with smaller input"],SCAN_NUMBER_UNSAFE:["Replacement would turn a numeric JSON value into an invalid number","Narrow the rule so it does not match inside numeric fields such as timestamps or schema limits"],SCAN_UNIQUE_FAILED:["Could not generate a unique shape-preserving replacement","The matched value is too short; match a longer value or set an explicit replacement"],SCAN_ZERO_WIDTH:["Regex produced a zero-width match","Require a non-empty match in the numbered rule"],SCAN_MATCH_LIMIT:["Too many matches in one request","Narrow overly broad rules or reduce input"],SCAN_MAPPING_LIMIT:["Mapping limit reached","Start a new session or /reload; in-memory mappings reset"],FIXED_MAPPING_CONFLICT:["One original has conflicting replacements","Use one replacement for overlapping matchers, then /reload"],FIXED_ALIAS_REUSED:["Fixed alias already maps to another original","A fixed regex represents one original per instance; use random replacement or split literal rules"],FIXED_SENSITIVE_TARGET:["Replacement contains the matched original","Choose a target without sensitive content"],FIXED_OVERLAP:["Fixed replacement partially overlaps another match","Remove the partial overlap or use random rules; rule and otherRule show both rules"],FIXED_ALIAS_CROSSING:["Regex crosses a learned fixed alias","Narrow the regex or choose a different alias"],SCAN_COMPLEXITY:["Request exceeds depth or node limits","Reduce nested data and tool schema size"],SCAN_JSON_REWRITE:["Replacement breaks embedded JSON syntax","Avoid rules spanning JSON syntax; match field values instead"],SCAN_ATTACHMENT:["Unsupported image, audio, video or file content","Remove attachments or send reviewed text only"],SCAN_KEY_COLLISION:["Replacement creates duplicate object keys","Use distinct targets or avoid matching schema keys"],RESTORE_COMPLEXITY:["Response nesting is too deep to restore","Reduce nested structures or start a new session with /new"],RESTORE_KEY_COLLISION:["Restoring produced duplicate object keys","Use more specific rules or fixed targets that cannot collide with schema keys"],COMPACT_DENIED:["Compaction refused",'Allow it when asked, or set "compaction" to "protected" in the local config'],COMPACT_UNAVAILABLE:["No model or credentials available to summarize","Select a model and sign in to its provider, then retry"],COMPACT_FAILED:["Could not summarize the redacted conversation",'Retry; if it persists, set "compaction" to "off" and use /new'],COMPACT_EMPTY:["The model returned an empty summary","A local summary was used instead; no action is required"],CONFIG_COMPACTION:["Invalid compaction setting",'Use "ask", "protected" or "off", or an object with mode, provider, model, contextWindow and reserveTokens'],CONFIG_COMPACT_MODEL:["The compaction model cannot be resolved","Set both provider and model, check their spelling, and sign in to that provider"],AUDIT_BATCH_LIMIT:["Audit batch exceeds the size limit","Reduce matched content per request"],AUDIT_FULL:["Audit log would exceed the size limit","Review records, then run /protecter logs clear or archive locally"],AUDIT_PARTIAL:["Audit log has an incomplete last line","Inspect locally or run /protecter logs clear; never upload the log"],AUDIT_LOCKED:["Audit lock stayed busy","Wait for writers; stop all Pi processes before inspecting a stale lock"],AUDIT_UNSAFE:["Unsafe audit file","Use an owned regular file without links; checks are not bypassed"],AUDIT_IO:["Audit file operation failed","Check errno, permissions and free space; no path or content is exposed"]},$e={INTERNAL:["未预期的本地错误","重载并检查安装包完整性"],NOT_READY:["防护尚未初始化","查看初始化错误,修复后重载"],UNSUPPORTED_PI:["Pi 版本不受支持",`请使用 Pi ${J}`],MACHINE_ID:["无法获取机器标识","检查系统机器标识访问权限,不使用随机回退"],CONFIG_JSON:["配置 JSON 无效","本地修复 JSON 语法后重载"],CONFIG_SCHEMA:["配置结构或规则无效","使用版本1、敏感词数组及文档字段,rule 为规则序号"],CONFIG_REGEX:["正则或标志无效","修复指定规则,标志仅支持 gimsu"],CONFIG_TARGET:["固定替换目标无效","使用非空且少于8192字符的目标,不含保留名称"],CONFIG_CONFLICT:["规则定义冲突","为同一匹配器设置一致的替换策略"],CONFIG_SIZE:["配置超过体积或规则数上限","减少配置体积或规则数,规则不会被截断"],CONFIG_IO:["无法访问配置","检查本地权限、所有者和磁盘空间"],CONFIG_UNSAFE:["配置文件不安全","使用本人所有的普通文件,不含符号或硬链接"],CONFIG_EMPTY_MATCH:["正则匹配空字符串","修改指定规则,要求非空匹配"],CONFIG_SENSITIVE_TARGET:["固定目标与敏感规则重叠","选择与所有敏感匹配器无关的目标"],CONFIG_ALIAS_CONFLICT:["固定别名相互重叠","使用互不包含的不同别名"],CONFIG_TIMEOUT:["规则验证超时","简化正则规则后重载"],MIGRATION_LOCKED:["配置迁移锁被占用","等待其他进程,全部停止后再检查残留锁"],MIGRATION_CHANGED:["迁移期间配置发生变化","停止并发编辑后重载,源文件会保留"],MIGRATION_LIMIT:["候选配置过多","在本地整理旧配置文件"],MIGRATION_IO:["迁移文件操作失败","检查权限、空间和残留文件后重试"],PAYLOAD_JSON:["请求无法序列化为 JSON","检查提供商或其他请求改写扩展,不记录请求内容"],PAYLOAD_SIZE:["请求超过体积上限","减少历史或工具输出,或新建会话"],SCAN_TIMEOUT:["脱敏 worker 超时","简化正则规则或缩小输入"],WORKER_FAILED:["worker 启动失败或提前退出","检查 dist 文件完整性和 Node 版本,必要时重装"],SCAN_INTERNAL:["未预期的扫描错误","检查最近的规则改动并缩小输入"],SCAN_NUMBER_UNSAFE:["替换会把 JSON 数值变成无效数字","缩小规则范围,避免匹配时间戳或结构上限等数值字段"],SCAN_UNIQUE_FAILED:["无法生成唯一的同形替换值","命中内容过短或可选空间过小,请匹配更长内容或设置固定 replacement"],SCAN_ZERO_WIDTH:["正则产生零宽匹配","修改指定规则,要求非空匹配"],SCAN_MATCH_LIMIT:["单次请求匹配次数过多","缩小过宽规则或减少输入"],SCAN_MAPPING_LIMIT:["映射数量达到上限","新建会话或重载,内存映射将重置"],FIXED_MAPPING_CONFLICT:["同一原文存在冲突替换","为重叠匹配器使用同一目标后重载"],FIXED_ALIAS_REUSED:["固定别名已映射其他原文","固定正则在同一实例仅代表一种原文,改用随机替换或拆分字面规则"],FIXED_SENSITIVE_TARGET:["替换目标包含命中原文","选择不含敏感内容的目标"],FIXED_OVERLAP:["固定替换与其他匹配部分重叠","消除部分重叠或改用随机规则,rule 与 otherRule 指出两条规则"],FIXED_ALIAS_CROSSING:["正则跨越已学习的固定别名","缩小正则范围或更换别名"],SCAN_COMPLEXITY:["请求超过层级或节点上限","减少嵌套数据和工具结构体积"],SCAN_JSON_REWRITE:["替换破坏内嵌 JSON 语法","避免跨 JSON 语法的规则,改为匹配字段值"],SCAN_ATTACHMENT:["不支持图片、音频、视频或文件内容","移除附件或仅发送已审查的文本"],SCAN_KEY_COLLISION:["替换后对象键重复","使用不同目标或避免匹配结构键"],RESTORE_COMPLEXITY:["还原响应时层级过深","减少嵌套结构,或使用 /new 开始新会话"],RESTORE_KEY_COLLISION:["还原后对象键重复","使用更具体的规则或固定替换目标,避免与结构键冲突"],COMPACT_DENIED:["压缩被拒绝",'询问时选择允许,或在本地配置中将 "compaction" 设为 "protected"'],COMPACT_UNAVAILABLE:["没有可用于生成摘要的模型或凭证","选择模型并登录对应提供商后重试"],COMPACT_FAILED:["无法对脱敏后的会话生成摘要",'请重试;若持续失败,将 "compaction" 设为 "off" 并使用 /new'],COMPACT_EMPTY:["模型返回了空摘要","已改用本地摘要,无需处理"],CONFIG_COMPACTION:["compaction 配置值无效",'请使用 "ask"、"protected"、"off",或含 mode、provider、model、contextWindow、reserveTokens 的对象'],CONFIG_COMPACT_MODEL:["无法解析压缩模型","请同时填写 provider 和 model、核对拼写,并登录该提供商"],AUDIT_BATCH_LIMIT:["审计批次超过上限","减少单个请求的命中内容"],AUDIT_FULL:["审计日志将超过上限","检查记录后清空日志或本地归档"],AUDIT_PARTIAL:["审计日志末行不完整","本地检查或清空日志,不要上传日志"],AUDIT_LOCKED:["审计锁持续被占用","等待写入完成,全部停止后再检查残留锁"],AUDIT_UNSAFE:["审计文件不安全","使用本人普通文件且不含链接,不会绕过检查"],AUDIT_IO:["审计文件操作失败","检查错误码、权限和磁盘空间,不暴露路径或内容"]},ae={blockedHeader:"SPI Protecter blocked this request",reasonLabel:"Reason",actionLabel:"Action",detailsLabel:"Details",statusReady:"ready",statusNotReady:"not ready",statusLine:"SPI Protecter: {state}; {rules}; {mappings}. Memory snapshot; /reload to refresh.",statusLastBlock:"Last block @ {at}:",statusNoBlock:"No recorded block in this session.",statusBarRules:"SPI Protecter · {rules}",statusBarNotReady:"SPI Protecter · not ready",emptyRules:"SPI Protecter: no rules configured; edit the local machine config, then /reload.",commandDescription:"View local SPI protection records",usage:"Usage: /protecter [logs [1-100|clear]|status|reload]. Never type secrets as arguments.",logsTuiOnly:"Open the local TUI to view protection records.",logsNone:"No readable records.",logsSelectTitle:"Protection records",logsTruncated:" (limited tail)",logsRevealTitle:"Sensitive plaintext",logsRevealBody:"Reveal locally? Never share this view.",logsPreviewTitle:"Local preview; edits are discarded",logsPreviewTruncated:"[Preview truncated]",clearTitle:"Clear the current audit log?",clearBody:"Irreversible. Only this machine's log is cleared; configuration and in-memory mappings stay. Later requests may add new records.",cleared:"Audit log cleared: {bytes}.",toolBlocked:"SPI Protecter blocked tool access to the protected configuration or audit log; edit it locally.",compactAskTitle:"Compact this conversation?",compactAskBody:`Pi builds the summarization request internally and never routes it through this extension's redaction, so allowing Pi to summarize directly would send the conversation, including protected values, as plaintext.
2
+ import{getAgentDir as St,VERSION as _t}from"@earendil-works/pi-coding-agent";import{Worker as mt}from"node:worker_threads";import{fileURLToPath as pt}from"node:url";import*as I from"node:fs";import{join as P,resolve as ot}from"node:path";import{createHash as nt,randomUUID as rt}from"node:crypto";import{Worker as it}from"node:worker_threads";import{fileURLToPath as st}from"node:url";var J="0.84.0-0.84.x, 0.85.1-0.87.x, 0.99.x, 1.2.0-1.3.x";function se(o){let e=/^(\d+)\.(\d+)\.(\d+)/.exec(o);if(!e)return!1;let[n,t,r]=e.slice(1).map(Number);return n===1?t===2||t===3:n!==0?!1:t===84?!0:t===85?r>=1:t===86||t===87||t===99}var Y={INTERNAL:["Unexpected local failure","Reload; check the installed package integrity"],NOT_READY:["Protection is not initialized","Run /protecter status for the initialization error, fix it, then /reload"],UNSUPPORTED_PI:["Unsupported Pi version",`Use Pi ${J}`],MACHINE_ID:["Machine identifier unavailable","Check local OS machine-ID access; no random fallback is used"],CONFIG_JSON:["Invalid configuration JSON","Fix JSON syntax locally, then /reload"],CONFIG_SCHEMA:["Invalid configuration structure or rule","Use version 1, a sensitiveWords array and documented fields; rule shows the 1-based index"],CONFIG_REGEX:["Invalid regex or flags","Fix the numbered rule; flags support gimsu only"],CONFIG_TARGET:["Invalid fixed replacement target","Use a non-blank string under 8192 characters without reserved __PIP_ names"],CONFIG_CONFLICT:["Conflicting rule definitions","Give the same matcher one replacement policy across all configs"],CONFIG_SIZE:["Configuration exceeds size or rule limit","Reduce configuration size or rule count; rules are never truncated"],CONFIG_IO:["Cannot access configuration","Check local permissions, ownership and free space"],CONFIG_UNSAFE:["Unsafe configuration file","Use an owned regular file without symbolic or hard links"],CONFIG_EMPTY_MATCH:["Regex matches an empty string","Require a non-empty match in the numbered rule"],CONFIG_SENSITIVE_TARGET:["Fixed target overlaps a sensitive rule","Choose a target unrelated to every sensitive matcher"],CONFIG_ALIAS_CONFLICT:["Fixed aliases overlap each other","Use distinct aliases that are not substrings of one another"],CONFIG_TIMEOUT:["Rule validation timed out","Simplify regex rules, then /reload"],MIGRATION_LOCKED:["Configuration migration lock is busy","Wait for other Pi processes; inspect a stale lock only after stopping them"],MIGRATION_CHANGED:["Configuration changed during migration","Stop concurrent edits and /reload; source files are preserved"],MIGRATION_LIMIT:["Too many configuration candidates","Consolidate old configuration files locally"],MIGRATION_IO:["Migration file operation failed","Check permissions, free space and leftover files before retrying"],PAYLOAD_JSON:["Request is not serializable JSON","Inspect the provider or another payload-modifying extension; no payload is logged"],PAYLOAD_SIZE:["Request exceeds the size limit","Reduce history or tool output size, or start a new session"],SCAN_TIMEOUT:["Redaction worker timed out","Simplify regex rules or reduce input size"],WORKER_FAILED:["Worker failed to start or exited early","Verify all dist files and the Node version; reinstall if needed"],SCAN_INTERNAL:["Unexpected scan failure","Review recent rule changes and retry with smaller input"],SCAN_NUMBER_UNSAFE:["Replacement would turn a numeric JSON value into an invalid number","Narrow the rule so it does not match inside numeric fields such as timestamps or schema limits"],SCAN_UNIQUE_FAILED:["Could not generate a unique shape-preserving replacement","The matched value is too short; match a longer value or set an explicit replacement"],SCAN_ZERO_WIDTH:["Regex produced a zero-width match","Require a non-empty match in the numbered rule"],SCAN_MATCH_LIMIT:["Too many matches in one request","Narrow overly broad rules or reduce input"],SCAN_MAPPING_LIMIT:["Mapping limit reached","Start a new session or /reload; in-memory mappings reset"],FIXED_MAPPING_CONFLICT:["One original has conflicting replacements","Use one replacement for overlapping matchers, then /reload"],FIXED_ALIAS_REUSED:["Fixed alias already maps to another original","A fixed regex represents one original per instance; use random replacement or split literal rules"],FIXED_SENSITIVE_TARGET:["Replacement contains the matched original","Choose a target without sensitive content"],FIXED_OVERLAP:["Fixed replacement partially overlaps another match","Remove the partial overlap or use random rules; rule and otherRule show both rules"],FIXED_ALIAS_CROSSING:["Regex crosses a learned fixed alias","Narrow the regex or choose a different alias"],SCAN_COMPLEXITY:["Request exceeds depth or node limits","Reduce nested data and tool schema size"],SCAN_JSON_REWRITE:["Replacement breaks embedded JSON syntax","Avoid rules spanning JSON syntax; match field values instead"],SCAN_ATTACHMENT:["Unsupported image, audio, video or file content","Remove attachments or send reviewed text only"],SCAN_KEY_COLLISION:["Replacement creates duplicate object keys","Use distinct targets or avoid matching schema keys"],RESTORE_COMPLEXITY:["Response nesting is too deep to restore","Reduce nested structures or start a new session with /new"],RESTORE_KEY_COLLISION:["Restoring produced duplicate object keys","Use more specific rules or fixed targets that cannot collide with schema keys"],COMPACT_DENIED:["Compaction refused",'Allow it when asked, or set "compaction" to "protected" in the local config'],COMPACT_UNAVAILABLE:["No model or credentials available to summarize","Select a model and sign in to its provider, then retry"],COMPACT_FAILED:["Could not summarize the redacted conversation",'Retry; if it persists, set "compaction" to "off" and use /new'],COMPACT_EMPTY:["The model returned an empty summary","A local summary was used instead; no action is required"],CONFIG_COMPACTION:["Invalid compaction setting",'Use "ask", "protected" or "off", or an object with mode, provider, model, contextWindow and reserveTokens'],CONFIG_COMPACT_MODEL:["The compaction model cannot be resolved","Set both provider and model, check their spelling, and sign in to that provider"],AUDIT_BATCH_LIMIT:["Audit batch exceeds the size limit","Reduce matched content per request"],AUDIT_FULL:["Audit log would exceed the size limit","Review records, then run /protecter logs clear or archive locally"],AUDIT_PARTIAL:["Audit log has an incomplete last line","Inspect locally or run /protecter logs clear; never upload the log"],AUDIT_LOCKED:["Audit lock stayed busy","Wait for writers; stop all Pi processes before inspecting a stale lock"],AUDIT_UNSAFE:["Unsafe audit file","Use an owned regular file without links; checks are not bypassed"],AUDIT_IO:["Audit file operation failed","Check errno, permissions and free space; no path or content is exposed"]},$e={INTERNAL:["未预期的本地错误","重载并检查安装包完整性"],NOT_READY:["防护尚未初始化","查看初始化错误,修复后重载"],UNSUPPORTED_PI:["Pi 版本不受支持",`请使用 Pi ${J}`],MACHINE_ID:["无法获取机器标识","检查系统机器标识访问权限,不使用随机回退"],CONFIG_JSON:["配置 JSON 无效","本地修复 JSON 语法后重载"],CONFIG_SCHEMA:["配置结构或规则无效","使用版本1、敏感词数组及文档字段,rule 为规则序号"],CONFIG_REGEX:["正则或标志无效","修复指定规则,标志仅支持 gimsu"],CONFIG_TARGET:["固定替换目标无效","使用非空且少于8192字符的目标,不含保留名称"],CONFIG_CONFLICT:["规则定义冲突","为同一匹配器设置一致的替换策略"],CONFIG_SIZE:["配置超过体积或规则数上限","减少配置体积或规则数,规则不会被截断"],CONFIG_IO:["无法访问配置","检查本地权限、所有者和磁盘空间"],CONFIG_UNSAFE:["配置文件不安全","使用本人所有的普通文件,不含符号或硬链接"],CONFIG_EMPTY_MATCH:["正则匹配空字符串","修改指定规则,要求非空匹配"],CONFIG_SENSITIVE_TARGET:["固定目标与敏感规则重叠","选择与所有敏感匹配器无关的目标"],CONFIG_ALIAS_CONFLICT:["固定别名相互重叠","使用互不包含的不同别名"],CONFIG_TIMEOUT:["规则验证超时","简化正则规则后重载"],MIGRATION_LOCKED:["配置迁移锁被占用","等待其他进程,全部停止后再检查残留锁"],MIGRATION_CHANGED:["迁移期间配置发生变化","停止并发编辑后重载,源文件会保留"],MIGRATION_LIMIT:["候选配置过多","在本地整理旧配置文件"],MIGRATION_IO:["迁移文件操作失败","检查权限、空间和残留文件后重试"],PAYLOAD_JSON:["请求无法序列化为 JSON","检查提供商或其他请求改写扩展,不记录请求内容"],PAYLOAD_SIZE:["请求超过体积上限","减少历史或工具输出,或新建会话"],SCAN_TIMEOUT:["脱敏 worker 超时","简化正则规则或缩小输入"],WORKER_FAILED:["worker 启动失败或提前退出","检查 dist 文件完整性和 Node 版本,必要时重装"],SCAN_INTERNAL:["未预期的扫描错误","检查最近的规则改动并缩小输入"],SCAN_NUMBER_UNSAFE:["替换会把 JSON 数值变成无效数字","缩小规则范围,避免匹配时间戳或结构上限等数值字段"],SCAN_UNIQUE_FAILED:["无法生成唯一的同形替换值","命中内容过短或可选空间过小,请匹配更长内容或设置固定 replacement"],SCAN_ZERO_WIDTH:["正则产生零宽匹配","修改指定规则,要求非空匹配"],SCAN_MATCH_LIMIT:["单次请求匹配次数过多","缩小过宽规则或减少输入"],SCAN_MAPPING_LIMIT:["映射数量达到上限","新建会话或重载,内存映射将重置"],FIXED_MAPPING_CONFLICT:["同一原文存在冲突替换","为重叠匹配器使用同一目标后重载"],FIXED_ALIAS_REUSED:["固定别名已映射其他原文","固定正则在同一实例仅代表一种原文,改用随机替换或拆分字面规则"],FIXED_SENSITIVE_TARGET:["替换目标包含命中原文","选择不含敏感内容的目标"],FIXED_OVERLAP:["固定替换与其他匹配部分重叠","消除部分重叠或改用随机规则,rule 与 otherRule 指出两条规则"],FIXED_ALIAS_CROSSING:["正则跨越已学习的固定别名","缩小正则范围或更换别名"],SCAN_COMPLEXITY:["请求超过层级或节点上限","减少嵌套数据和工具结构体积"],SCAN_JSON_REWRITE:["替换破坏内嵌 JSON 语法","避免跨 JSON 语法的规则,改为匹配字段值"],SCAN_ATTACHMENT:["不支持图片、音频、视频或文件内容","移除附件或仅发送已审查的文本"],SCAN_KEY_COLLISION:["替换后对象键重复","使用不同目标或避免匹配结构键"],RESTORE_COMPLEXITY:["还原响应时层级过深","减少嵌套结构,或使用 /new 开始新会话"],RESTORE_KEY_COLLISION:["还原后对象键重复","使用更具体的规则或固定替换目标,避免与结构键冲突"],COMPACT_DENIED:["压缩被拒绝",'询问时选择允许,或在本地配置中将 "compaction" 设为 "protected"'],COMPACT_UNAVAILABLE:["没有可用于生成摘要的模型或凭证","选择模型并登录对应提供商后重试"],COMPACT_FAILED:["无法对脱敏后的会话生成摘要",'请重试;若持续失败,将 "compaction" 设为 "off" 并使用 /new'],COMPACT_EMPTY:["模型返回了空摘要","已改用本地摘要,无需处理"],CONFIG_COMPACTION:["compaction 配置值无效",'请使用 "ask"、"protected"、"off",或含 mode、provider、model、contextWindow、reserveTokens 的对象'],CONFIG_COMPACT_MODEL:["无法解析压缩模型","请同时填写 provider 和 model、核对拼写,并登录该提供商"],AUDIT_BATCH_LIMIT:["审计批次超过上限","减少单个请求的命中内容"],AUDIT_FULL:["审计日志将超过上限","检查记录后清空日志或本地归档"],AUDIT_PARTIAL:["审计日志末行不完整","本地检查或清空日志,不要上传日志"],AUDIT_LOCKED:["审计锁持续被占用","等待写入完成,全部停止后再检查残留锁"],AUDIT_UNSAFE:["审计文件不安全","使用本人普通文件且不含链接,不会绕过检查"],AUDIT_IO:["审计文件操作失败","检查错误码、权限和磁盘空间,不暴露路径或内容"]},ae={blockedHeader:"SPI Protecter blocked this request",reasonLabel:"Reason",actionLabel:"Action",detailsLabel:"Details",statusReady:"ready",statusNotReady:"not ready",statusLine:"SPI Protecter: {state}; {rules}; {mappings}. Memory snapshot; /reload to refresh.",statusLastBlock:"Last block @ {at}:",statusNoBlock:"No recorded block in this session.",statusBarRules:"SPI Protecter · {rules}",statusBarNotReady:"SPI Protecter · not ready",emptyRules:"SPI Protecter: no rules configured; edit the local machine config, then /reload.",commandDescription:"View local SPI protection records",usage:"Usage: /protecter [logs [1-100|clear]|status|reload]. Never type secrets as arguments.",logsTuiOnly:"Open the local TUI to view protection records.",logsNone:"No readable records.",logsSelectTitle:"Protection records",logsTruncated:" (limited tail)",logsRevealTitle:"Sensitive plaintext",logsRevealBody:"Reveal locally? Never share this view.",logsPreviewTitle:"Local preview; edits are discarded",logsPreviewTruncated:"[Preview truncated]",clearTitle:"Clear the current audit log?",clearBody:"Irreversible. Only this machine's log is cleared; configuration and in-memory mappings stay. Later requests may add new records.",cleared:"Audit log cleared: {bytes}.",toolBlocked:"SPI Protecter blocked tool access to the protected configuration or audit log; edit it locally.",compactAskTitle:"Compact this conversation?",compactAskBody:`Pi builds the summarization request internally and never routes it through this extension's redaction, so allowing Pi to summarize directly would send the conversation, including protected values, as plaintext.
3
3
 
4
4
  Allowing here does NOT do that: the text is redacted first, the summary is generated from the redacted text, and the summary is restored locally afterwards. The extra request costs tokens, and the model reasons about replacements, so the summary may describe protected values inaccurately.
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-information-protecter",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Sensitive Personal Information (SPI) Protecter — local reversible privacy redaction for Pi",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -40,10 +40,10 @@
40
40
  "node": ">=22"
41
41
  },
42
42
  "peerDependencies": {
43
- "@earendil-works/pi-coding-agent": ">=0.84.0 <0.85.0 || >=0.85.1 <0.88.0"
43
+ "@earendil-works/pi-coding-agent": ">=0.84.0 <0.85.0 || >=0.85.1 <0.88.0 || >=0.99.0 <1.0.0 || >=1.2.0 <1.4.0"
44
44
  },
45
45
  "devDependencies": {
46
- "@earendil-works/pi-coding-agent": "0.86.1",
46
+ "@earendil-works/pi-coding-agent": "1.3.0",
47
47
  "@types/node": "^22.0.0",
48
48
  "esbuild": "0.28.2",
49
49
  "tsx": "^4.20.0",