mancode 0.6.4 → 0.6.5

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.
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,181 @@
1
+ import {
2
+ GatewayError,
3
+ MappingStore,
4
+ ProtocolStream,
5
+ gatewayErrorCode,
6
+ parseStrictJson,
7
+ property,
8
+ stringValue,
9
+ transformRequest,
10
+ transformResponse
11
+ } from "../chunk-GI24QVXE.js";
12
+ import {
13
+ scanSensitiveText
14
+ } from "../chunk-IRZQYMHD.js";
15
+
16
+ // src/gateway/worker.ts
17
+ import { parentPort, workerData } from "worker_threads";
18
+
19
+ // src/gateway/engine.ts
20
+ var GatewayEngine = class {
21
+ constructor(scope, rules, host) {
22
+ this.rules = rules;
23
+ this.host = host;
24
+ this.store = new MappingStore(scope);
25
+ }
26
+ store;
27
+ requests = /* @__PURE__ */ new Map();
28
+ execute(operation) {
29
+ if (operation.kind === "begin") {
30
+ if (this.requests.size >= 8 || this.requests.has(operation.id))
31
+ throw new GatewayError("MANCODE_GATEWAY_CONCURRENCY_LIMIT", 429);
32
+ const tree = parseStrictJson(operation.body);
33
+ const previous = stringValue(property(tree, "previous_response_id"));
34
+ const mapping = this.store.begin(previous);
35
+ try {
36
+ const result = transformRequest(
37
+ operation.body,
38
+ operation.protocol,
39
+ mapping,
40
+ this.rules,
41
+ this.host
42
+ );
43
+ this.requests.set(operation.id, {
44
+ mapping,
45
+ stream: new ProtocolStream(operation.protocol, mapping),
46
+ protocol: operation.protocol,
47
+ finished: false,
48
+ audit: /* @__PURE__ */ new Map(),
49
+ truncated: false
50
+ });
51
+ return result;
52
+ } catch (error) {
53
+ mapping.release();
54
+ throw error;
55
+ }
56
+ }
57
+ const active = this.requests.get(operation.id);
58
+ if (!active) {
59
+ if (operation.kind === "release") return null;
60
+ throw new GatewayError("MANCODE_GATEWAY_REQUEST_UNKNOWN");
61
+ }
62
+ if (operation.kind === "release") {
63
+ active.mapping.release();
64
+ this.requests.delete(operation.id);
65
+ return null;
66
+ }
67
+ if (operation.kind === "frame") {
68
+ const result = active.stream.accept(operation.frame);
69
+ this.observeFrame(active, operation.frame);
70
+ if (active.stream.completed && !active.finished) {
71
+ active.mapping.finish(
72
+ active.protocol === "responses" ? active.stream.responseId : void 0
73
+ );
74
+ active.finished = true;
75
+ }
76
+ return { frames: result, opaqueBlocks: active.stream.opaqueBlocks };
77
+ }
78
+ if (operation.kind === "response") {
79
+ const result = transformResponse(
80
+ operation.body,
81
+ active.protocol,
82
+ active.mapping
83
+ );
84
+ const responseId = stringValue(
85
+ property(parseStrictJson(operation.body), "id")
86
+ );
87
+ if (!responseId)
88
+ throw new GatewayError("MANCODE_GATEWAY_RESPONSE_ID_MISSING");
89
+ active.mapping.finish(
90
+ active.protocol === "responses" ? responseId : void 0
91
+ );
92
+ active.finished = true;
93
+ return {
94
+ ...result,
95
+ audit: {
96
+ phase: "after_emit",
97
+ action: "observe",
98
+ scanStatus: "partial",
99
+ counts: {},
100
+ truncated: false
101
+ }
102
+ };
103
+ }
104
+ active.stream.finish();
105
+ return {
106
+ opaqueBlocks: active.stream.opaqueBlocks,
107
+ audit: this.audit(active)
108
+ };
109
+ }
110
+ observeFrame(active, frame) {
111
+ if (frame.data === "[DONE]") return;
112
+ const event = JSON.parse(frame.data);
113
+ let text;
114
+ let key;
115
+ if (event.type === "response.output_text.delta" || event.type === "response.refusal.delta") {
116
+ text = typeof event.delta === "string" ? event.delta : void 0;
117
+ key = `${event.output_index}:${event.content_index}:${event.item_id}`;
118
+ }
119
+ if (event.type === "content_block_delta") {
120
+ const delta = event.delta;
121
+ if (delta?.type === "text_delta" && typeof delta.text === "string") {
122
+ text = delta.text;
123
+ key = `messages:${event.index}`;
124
+ }
125
+ }
126
+ if (text === void 0 || key === void 0) return;
127
+ const previous = active.audit.get(key) ?? "";
128
+ if (Buffer.byteLength(previous) + Buffer.byteLength(text) > 64 * 1024) {
129
+ active.truncated = true;
130
+ return;
131
+ }
132
+ active.audit.set(key, previous + text);
133
+ }
134
+ audit(active) {
135
+ const summary = {
136
+ phase: "after_emit",
137
+ action: "observe",
138
+ scanStatus: active.truncated ? "partial" : "complete",
139
+ counts: {},
140
+ truncated: active.truncated
141
+ };
142
+ for (const text of active.audit.values()) {
143
+ const result = scanSensitiveText(
144
+ active.mapping.withoutKnownEchoes(text),
145
+ this.rules
146
+ );
147
+ if (result.status !== "complete") {
148
+ summary.scanStatus = "failed";
149
+ continue;
150
+ }
151
+ for (const finding of result.findings)
152
+ summary.counts[finding.category] = (summary.counts[finding.category] ?? 0) + 1;
153
+ }
154
+ return summary;
155
+ }
156
+ };
157
+
158
+ // src/gateway/worker.ts
159
+ var engine = new GatewayEngine(
160
+ workerData.scope,
161
+ workerData.rules,
162
+ workerData.host
163
+ );
164
+ parentPort?.on(
165
+ "message",
166
+ (message) => {
167
+ try {
168
+ parentPort?.postMessage({
169
+ sequence: message.sequence,
170
+ value: engine.execute(message.operation)
171
+ });
172
+ } catch (error) {
173
+ parentPort?.postMessage({
174
+ sequence: message.sequence,
175
+ error: gatewayErrorCode(error),
176
+ status: error instanceof GatewayError ? error.status : 500
177
+ });
178
+ }
179
+ }
180
+ );
181
+ //# sourceMappingURL=worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/gateway/worker.ts","../../src/gateway/engine.ts"],"sourcesContent":["import { parentPort, workerData } from 'node:worker_threads';\nimport { type EngineOperation, GatewayEngine } from './engine.js';\nimport { GatewayError, gatewayErrorCode } from './errors.js';\nconst engine = new GatewayEngine(\n workerData.scope,\n workerData.rules,\n workerData.host,\n);\nparentPort?.on(\n 'message',\n (message: { sequence: number; operation: EngineOperation }) => {\n try {\n parentPort?.postMessage({\n sequence: message.sequence,\n value: engine.execute(message.operation),\n });\n } catch (error) {\n parentPort?.postMessage({\n sequence: message.sequence,\n error: gatewayErrorCode(error),\n status: error instanceof GatewayError ? error.status : 500,\n });\n }\n },\n);\n","import { scanSensitiveText } from '../privacy/detect.js';\nimport { GatewayError } from './errors.js';\nimport { parseStrictJson, property, stringValue } from './json.js';\nimport { MappingStore, type RequestMapping } from './mapping.js';\nimport {\n type GatewayProtocol,\n transformRequest,\n transformResponse,\n} from './protocol.js';\nimport { ProtocolStream, type SseFrame } from './sse.js';\n\nexport interface AuditSummary {\n phase: 'after_emit';\n action: 'observe';\n scanStatus: 'complete' | 'partial' | 'failed';\n counts: Record<string, number>;\n truncated: boolean;\n}\ninterface ActiveRequest {\n mapping: RequestMapping;\n stream: ProtocolStream;\n protocol: GatewayProtocol;\n finished: boolean;\n audit: Map<string, string>;\n truncated: boolean;\n}\nexport type EngineOperation =\n | { kind: 'begin'; id: string; body: string; protocol: GatewayProtocol }\n | { kind: 'frame'; id: string; frame: SseFrame }\n | { kind: 'response'; id: string; body: string }\n | { kind: 'finish'; id: string }\n | { kind: 'release'; id: string };\n\n/** Entire content processing and reversible state stay in one bounded worker. */\nexport class GatewayEngine {\n private store: MappingStore;\n private requests = new Map<string, ActiveRequest>();\n constructor(\n scope: string,\n private readonly rules: readonly string[],\n private readonly host: string,\n ) {\n this.store = new MappingStore(scope);\n }\n execute(operation: EngineOperation): unknown {\n if (operation.kind === 'begin') {\n if (this.requests.size >= 8 || this.requests.has(operation.id))\n throw new GatewayError('MANCODE_GATEWAY_CONCURRENCY_LIMIT', 429);\n const tree = parseStrictJson(operation.body);\n const previous = stringValue(property(tree, 'previous_response_id'));\n const mapping = this.store.begin(previous);\n try {\n const result = transformRequest(\n operation.body,\n operation.protocol,\n mapping,\n this.rules,\n this.host,\n );\n this.requests.set(operation.id, {\n mapping,\n stream: new ProtocolStream(operation.protocol, mapping),\n protocol: operation.protocol,\n finished: false,\n audit: new Map(),\n truncated: false,\n });\n return result;\n } catch (error) {\n mapping.release();\n throw error;\n }\n }\n const active = this.requests.get(operation.id);\n if (!active) {\n if (operation.kind === 'release') return null;\n throw new GatewayError('MANCODE_GATEWAY_REQUEST_UNKNOWN');\n }\n if (operation.kind === 'release') {\n active.mapping.release();\n this.requests.delete(operation.id);\n return null;\n }\n if (operation.kind === 'frame') {\n const result = active.stream.accept(operation.frame);\n this.observeFrame(active, operation.frame);\n if (active.stream.completed && !active.finished) {\n active.mapping.finish(\n active.protocol === 'responses'\n ? active.stream.responseId\n : undefined,\n );\n active.finished = true;\n }\n return { frames: result, opaqueBlocks: active.stream.opaqueBlocks };\n }\n if (operation.kind === 'response') {\n const result = transformResponse(\n operation.body,\n active.protocol,\n active.mapping,\n );\n const responseId = stringValue(\n property(parseStrictJson(operation.body), 'id'),\n );\n if (!responseId)\n throw new GatewayError('MANCODE_GATEWAY_RESPONSE_ID_MISSING');\n active.mapping.finish(\n active.protocol === 'responses' ? responseId : undefined,\n );\n active.finished = true;\n // Buffered HTTP output can be observed before emission; we conservatively report observation only.\n return {\n ...result,\n audit: {\n phase: 'after_emit',\n action: 'observe',\n scanStatus: 'partial',\n counts: {},\n truncated: false,\n } satisfies AuditSummary,\n };\n }\n active.stream.finish();\n return {\n opaqueBlocks: active.stream.opaqueBlocks,\n audit: this.audit(active),\n };\n }\n\n private observeFrame(active: ActiveRequest, frame: SseFrame): void {\n if (frame.data === '[DONE]') return;\n const event = JSON.parse(frame.data) as Record<string, unknown>;\n let text: string | undefined;\n let key: string | undefined;\n if (\n event.type === 'response.output_text.delta' ||\n event.type === 'response.refusal.delta'\n ) {\n text = typeof event.delta === 'string' ? event.delta : undefined;\n key = `${event.output_index}:${event.content_index}:${event.item_id}`;\n }\n if (event.type === 'content_block_delta') {\n const delta = event.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && typeof delta.text === 'string') {\n text = delta.text;\n key = `messages:${event.index}`;\n }\n }\n if (text === undefined || key === undefined) return;\n const previous = active.audit.get(key) ?? '';\n if (Buffer.byteLength(previous) + Buffer.byteLength(text) > 64 * 1024) {\n active.truncated = true;\n return;\n }\n active.audit.set(key, previous + text);\n }\n\n private audit(active: ActiveRequest): AuditSummary {\n const summary: AuditSummary = {\n phase: 'after_emit',\n action: 'observe',\n scanStatus: active.truncated ? 'partial' : 'complete',\n counts: {},\n truncated: active.truncated,\n };\n for (const text of active.audit.values()) {\n const result = scanSensitiveText(\n active.mapping.withoutKnownEchoes(text),\n this.rules,\n );\n if (result.status !== 'complete') {\n summary.scanStatus = 'failed';\n continue;\n }\n for (const finding of result.findings)\n summary.counts[finding.category] =\n (summary.counts[finding.category] ?? 0) + 1;\n }\n return summary;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,kBAAkB;;;ACkChC,IAAM,gBAAN,MAAoB;AAAA,EAGzB,YACE,OACiB,OACA,MACjB;AAFiB;AACA;AAEjB,SAAK,QAAQ,IAAI,aAAa,KAAK;AAAA,EACrC;AAAA,EARQ;AAAA,EACA,WAAW,oBAAI,IAA2B;AAAA,EAQlD,QAAQ,WAAqC;AAC3C,QAAI,UAAU,SAAS,SAAS;AAC9B,UAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,UAAU,EAAE;AAC3D,cAAM,IAAI,aAAa,qCAAqC,GAAG;AACjE,YAAM,OAAO,gBAAgB,UAAU,IAAI;AAC3C,YAAM,WAAW,YAAY,SAAS,MAAM,sBAAsB,CAAC;AACnE,YAAM,UAAU,KAAK,MAAM,MAAM,QAAQ;AACzC,UAAI;AACF,cAAM,SAAS;AAAA,UACb,UAAU;AAAA,UACV,UAAU;AAAA,UACV;AAAA,UACA,KAAK;AAAA,UACL,KAAK;AAAA,QACP;AACA,aAAK,SAAS,IAAI,UAAU,IAAI;AAAA,UAC9B;AAAA,UACA,QAAQ,IAAI,eAAe,UAAU,UAAU,OAAO;AAAA,UACtD,UAAU,UAAU;AAAA,UACpB,UAAU;AAAA,UACV,OAAO,oBAAI,IAAI;AAAA,UACf,WAAW;AAAA,QACb,CAAC;AACD,eAAO;AAAA,MACT,SAAS,OAAO;AACd,gBAAQ,QAAQ;AAChB,cAAM;AAAA,MACR;AAAA,IACF;AACA,UAAM,SAAS,KAAK,SAAS,IAAI,UAAU,EAAE;AAC7C,QAAI,CAAC,QAAQ;AACX,UAAI,UAAU,SAAS,UAAW,QAAO;AACzC,YAAM,IAAI,aAAa,iCAAiC;AAAA,IAC1D;AACA,QAAI,UAAU,SAAS,WAAW;AAChC,aAAO,QAAQ,QAAQ;AACvB,WAAK,SAAS,OAAO,UAAU,EAAE;AACjC,aAAO;AAAA,IACT;AACA,QAAI,UAAU,SAAS,SAAS;AAC9B,YAAM,SAAS,OAAO,OAAO,OAAO,UAAU,KAAK;AACnD,WAAK,aAAa,QAAQ,UAAU,KAAK;AACzC,UAAI,OAAO,OAAO,aAAa,CAAC,OAAO,UAAU;AAC/C,eAAO,QAAQ;AAAA,UACb,OAAO,aAAa,cAChB,OAAO,OAAO,aACd;AAAA,QACN;AACA,eAAO,WAAW;AAAA,MACpB;AACA,aAAO,EAAE,QAAQ,QAAQ,cAAc,OAAO,OAAO,aAAa;AAAA,IACpE;AACA,QAAI,UAAU,SAAS,YAAY;AACjC,YAAM,SAAS;AAAA,QACb,UAAU;AAAA,QACV,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AACA,YAAM,aAAa;AAAA,QACjB,SAAS,gBAAgB,UAAU,IAAI,GAAG,IAAI;AAAA,MAChD;AACA,UAAI,CAAC;AACH,cAAM,IAAI,aAAa,qCAAqC;AAC9D,aAAO,QAAQ;AAAA,QACb,OAAO,aAAa,cAAc,aAAa;AAAA,MACjD;AACA,aAAO,WAAW;AAElB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,OAAO;AAAA,UACL,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,QAAQ,CAAC;AAAA,UACT,WAAW;AAAA,QACb;AAAA,MACF;AAAA,IACF;AACA,WAAO,OAAO,OAAO;AACrB,WAAO;AAAA,MACL,cAAc,OAAO,OAAO;AAAA,MAC5B,OAAO,KAAK,MAAM,MAAM;AAAA,IAC1B;AAAA,EACF;AAAA,EAEQ,aAAa,QAAuB,OAAuB;AACjE,QAAI,MAAM,SAAS,SAAU;AAC7B,UAAM,QAAQ,KAAK,MAAM,MAAM,IAAI;AACnC,QAAI;AACJ,QAAI;AACJ,QACE,MAAM,SAAS,gCACf,MAAM,SAAS,0BACf;AACA,aAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AACvD,YAAM,GAAG,MAAM,YAAY,IAAI,MAAM,aAAa,IAAI,MAAM,OAAO;AAAA,IACrE;AACA,QAAI,MAAM,SAAS,uBAAuB;AACxC,YAAM,QAAQ,MAAM;AACpB,UAAI,OAAO,SAAS,gBAAgB,OAAO,MAAM,SAAS,UAAU;AAClE,eAAO,MAAM;AACb,cAAM,YAAY,MAAM,KAAK;AAAA,MAC/B;AAAA,IACF;AACA,QAAI,SAAS,UAAa,QAAQ,OAAW;AAC7C,UAAM,WAAW,OAAO,MAAM,IAAI,GAAG,KAAK;AAC1C,QAAI,OAAO,WAAW,QAAQ,IAAI,OAAO,WAAW,IAAI,IAAI,KAAK,MAAM;AACrE,aAAO,YAAY;AACnB;AAAA,IACF;AACA,WAAO,MAAM,IAAI,KAAK,WAAW,IAAI;AAAA,EACvC;AAAA,EAEQ,MAAM,QAAqC;AACjD,UAAM,UAAwB;AAAA,MAC5B,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,YAAY,OAAO,YAAY,YAAY;AAAA,MAC3C,QAAQ,CAAC;AAAA,MACT,WAAW,OAAO;AAAA,IACpB;AACA,eAAW,QAAQ,OAAO,MAAM,OAAO,GAAG;AACxC,YAAM,SAAS;AAAA,QACb,OAAO,QAAQ,mBAAmB,IAAI;AAAA,QACtC,KAAK;AAAA,MACP;AACA,UAAI,OAAO,WAAW,YAAY;AAChC,gBAAQ,aAAa;AACrB;AAAA,MACF;AACA,iBAAW,WAAW,OAAO;AAC3B,gBAAQ,OAAO,QAAQ,QAAQ,KAC5B,QAAQ,OAAO,QAAQ,QAAQ,KAAK,KAAK;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AACF;;;ADlLA,IAAM,SAAS,IAAI;AAAA,EACjB,WAAW;AAAA,EACX,WAAW;AAAA,EACX,WAAW;AACb;AACA,YAAY;AAAA,EACV;AAAA,EACA,CAAC,YAA8D;AAC7D,QAAI;AACF,kBAAY,YAAY;AAAA,QACtB,UAAU,QAAQ;AAAA,QAClB,OAAO,OAAO,QAAQ,QAAQ,SAAS;AAAA,MACzC,CAAC;AAAA,IACH,SAAS,OAAO;AACd,kBAAY,YAAY;AAAA,QACtB,UAAU,QAAQ;AAAA,QAClB,OAAO,iBAAiB,KAAK;AAAA,QAC7B,QAAQ,iBAAiB,eAAe,MAAM,SAAS;AAAA,MACzD,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,20 @@
1
+ import {
2
+ configurePrivacyGateway,
3
+ disablePrivacyGateway,
4
+ printPrivacyGatewayConfig,
5
+ readPrivacyGatewayStatus,
6
+ registerPrivacyGatewayCommands,
7
+ runPrivacyGateway
8
+ } from "./chunk-THOE33LU.js";
9
+ import "./chunk-GI24QVXE.js";
10
+ import "./chunk-WRBNOPFA.js";
11
+ import "./chunk-IRZQYMHD.js";
12
+ export {
13
+ configurePrivacyGateway,
14
+ disablePrivacyGateway,
15
+ printPrivacyGatewayConfig,
16
+ readPrivacyGatewayStatus,
17
+ registerPrivacyGatewayCommands,
18
+ runPrivacyGateway
19
+ };
20
+ //# sourceMappingURL=privacy-gateway-Q4GD2JXF.js.map
@@ -0,0 +1,11 @@
1
+ import {
2
+ V3ContextStore,
3
+ storedTaskAggregateDigest
4
+ } from "./chunk-E2K22WYH.js";
5
+ import "./chunk-WRBNOPFA.js";
6
+ import "./chunk-IRZQYMHD.js";
7
+ export {
8
+ V3ContextStore,
9
+ storedTaskAggregateDigest
10
+ };
11
+ //# sourceMappingURL=store-GSLSLZ7D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/docs/README.md ADDED
@@ -0,0 +1,34 @@
1
+ # mancode 开发文档
2
+
3
+ 这里记录当前实现的稳定契约。历史 MVP 计划、一次性审核报告和已完成的网站计划不再保留在仓库中。
4
+
5
+ ## 信息来源
6
+
7
+ 发生冲突时,按以下优先级判断:
8
+
9
+ 1. `src/` 中的 schema、CLI 注册和运行时代码。
10
+ 2. 对应的自动化测试。
11
+ 3. `README.md` 与 `README.en.md` 的公开使用说明。
12
+ 4. 本目录中的开发说明。
13
+
14
+ 文档不能把计划中的功能写成已支持,也不能用旧测试数量或版本号证明当前状态。
15
+
16
+ ## 文档索引
17
+
18
+ | 文档 | 内容 |
19
+ |---|---|
20
+ | [architecture.md](./architecture.md) | Continuity 权威模型、目录、Task Aggregate 和一致性 |
21
+ | [workflows.md](./workflows.md) | 模式、工作流状态、治理门禁与团队协作 |
22
+ | [project-intelligence.md](./project-intelligence.md) | 项目检测、设计资产扫描和 preseason |
23
+ | [platform-adapters.md](./platform-adapters.md) | 八个平台的 bootstrap、能力差异与边界 |
24
+ | [12-lifecycle.md](./12-lifecycle.md) | 初始化、会话、任务、恢复和迁移生命周期 |
25
+ | [engineering.md](./engineering.md) | 开发原则、验证要求和代码地图 |
26
+
27
+ 许可证文本保存在仓库根目录的 [`LICENSE`](../LICENSE)。
28
+
29
+ ## 维护规则
30
+
31
+ - 新文档优先更新现有专题,不创建新的阶段计划副本。
32
+ - 已完成计划应删除;仍有长期价值的决策应写成当前约束。
33
+ - 仅在真实宿主验证完成后更新平台能力声明。
34
+ - 命令示例必须能在当前 `src/cli.ts` 中找到对应入口。
@@ -0,0 +1,76 @@
1
+ # Privacy protection
2
+
3
+ mancode offers three separate surfaces: text scanning, project-shared content protection, and an optional local model gateway. New capabilities are opt-in. Existing basic shared-content checks continue to apply.
4
+
5
+ ## Scan and preview
6
+
7
+ ```sh
8
+ mancode privacy scan --file notes.txt --profile shared --json
9
+ cat notes.txt | mancode privacy scan --json
10
+ mancode privacy preview --file notes.txt --output notes.redacted.txt --json
11
+ ```
12
+
13
+ Pass sensitive text in a UTF-8 file or stdin, never as a command-line argument. The scanner accepts up to 1 MiB and reports only rule/category, UTF-16 offsets, finding count and completion state. Scan exits with 0 for no findings, 1 for findings and 2 for an input/scan error. Preview exits with 0 when the new copy is fully written; findings are expected and do not make a successful preview fail.
14
+
15
+ Preview never overwrites an existing target or source. It writes a separate irreversible copy, restricts POSIX permissions to 0600, and publishes only after a complete scan and successful write. It does not rewrite task authority. If the input is too large, malformed UTF-8, contains invalid text, exceeds the finding/time budget or cannot be read, no preview is published. Error messages contain safe reason codes and selected system error codes, without paths or original text.
16
+
17
+ Detection uses explicit shapes plus selected checksums. A clean result does not prove that all credentials or personal data are absent. Unsupported identifier formats, encoded content and application-specific secrets may require additional rules. Rule sources and limitations are documented in [privacy-rule-sources.md](privacy-rule-sources.md).
18
+
19
+ Named credentials include assignments such as `client_password="synthetic phrase"` and `DB_PASSWORD=synthetic-value`. Supported quoted values are protected through the matching closing quote, including spaces and escaped quotes; an unfinished quote protects the remaining input. Metrics such as `token_count` and `password_length` are not credential names. This same scanner protects previews, enhanced shared writes and supported gateway prose fields.
20
+
21
+ ## First initialization
22
+
23
+ ```sh
24
+ mancode init --platform codex --shared-privacy --gateway-privacy
25
+ mancode init --platform codex --no-shared-privacy --no-gateway-privacy --yes
26
+ ```
27
+
28
+ Interactive first initialization asks separately about shared enhanced protection and this user's gateway preference for the current checkout. Choose `q` to cancel before the new project is written. Explicit flags answer only their corresponding question. Supplying both positive and negative forms is a parameter error.
29
+
30
+ Non-interactive first initialization leaves unspecified enhancements disabled. `--yes` skips questions and does not enable privacy features or redirect network traffic. Repeating `init`, including with opposite flags, preserves existing choices. Legacy initialization rejects these options instead of implicitly migrating authority.
31
+
32
+ Shared policy participates in the journaled project initialization. Gateway settings are written afterward to user-local storage; if that step fails, the project remains initialized and the command reports the local-settings failure. Retry the gateway command directly. Enabling a gateway preference does not start a server or change a client provider.
33
+
34
+ ## Activate shared policy
35
+
36
+ ```sh
37
+ mancode privacy status --json
38
+ mancode privacy enable --dry-run --json
39
+ mancode privacy enable --expected-revision 0 --session <session-id> --client codex --json
40
+ mancode privacy disable --expected-revision 1 --session <session-id> --client codex --json
41
+ mancode privacy policy apply --file candidate.json --expected-revision 2 --session <session-id> --client codex --json
42
+ ```
43
+
44
+ Dry-run scans current shared files and, for git-ref coordination, the current remote authority. It performs no authority mutation and returns 2 when activation is blocked. Results report safe counts and reasons without sensitive values. Candidate JSON contains exactly `schemaVersion`, `enabled`, `rulesetVersion`, and `enabledRuleIds`; the current ruleset is `mancode-sensitive-text:1`, and rule IDs are listed in `src/privacy/rules.ts`. Updates require an active maintenance session, the expected policy revision (zero before activation), and a clean operation baseline. The same `--operation-id` can resume an interrupted operation without creating a competing policy.
45
+
46
+ Mutable sensitive shared content must be cleaned through its owning workflow before enabling. Existing sensitive confirmed decisions and checkpoint files remain unchanged; their digests enter a permanent exclusion ledger so future context output, shared writes, sync and recovery cannot re-export those entities, including after disabling enhanced rules. A mutable `summary.md` copy of a historical checkpoint is separately scanned and must also be cleaned.
47
+
48
+ Remote actor profiles, claims and handoffs are immutable coordination history. If they match the proposed rules, dry-run returns `retain_basic_or_new_workspace`: retain basic protection, or explicitly create a new workspace and migrate cleaned content through the supported workflow. Activation never rewrites these objects or deletes ownership history. A sensitive checkpoint still present in an active remote task bundle requires a new safe checkpoint and an explicit sync before activation; its remediation is `replace_checkpoint_and_sync`. Other mutable bundle content must be cleaned and synced. A clean existing git-ref workspace can upgrade in place.
49
+
50
+ The project manifest, policy and exclusions are revision/digest bound. Git-ref policy changes first use the remote manifest's CAS, then commit local authority through a recoverable journal. Concurrent changes and stale clones fail closed. Other clones must receive the tracked `.mancode/schema.json` and `.mancode/shared/context/privacy-*.json` authority files through the repository's normal checkout before their next sync. Old local caches are discarded when their policy differs. Interrupted policy writes block ordinary writes until operation recovery establishes the committed state. Disabling keeps the upgraded local/remote schema and minimum client version 0.6.5.
51
+
52
+ ## Configure the optional gateway
53
+
54
+ Run these commands inside a project already initialized by `mancode init`. Gateway configuration and execution require its real workspace and checkout identity. Reading gateway status in an uninitialized directory reports missing configuration and creates no project authority.
55
+
56
+ ```sh
57
+ mancode privacy gateway enable --upstream openai --env-key OPENAI_API_KEY --client-host codex-cli/0.153.4 --json
58
+ mancode privacy gateway print-config --host codex
59
+ mancode privacy gateway run
60
+ mancode privacy gateway doctor --json
61
+ mancode privacy gateway disable --json
62
+ ```
63
+
64
+ Use `--upstream anthropic --env-key ANTHROPIC_API_KEY --client-host claude-code/2.1.142` and `print-config --host claude` for the corresponding Claude Code API-key flow. `run` stays in the foreground. Configuration fragments are explicit instructions for the user; mancode does not edit host provider/login settings. Host versions are checked when starting a bound gateway; an unknown host remains unverified. Keep gateway/client secrets in the documented local environment or private settings, never in command arguments or shared project files.
65
+
66
+ The gateway handles supported OpenAI Responses and Anthropic Messages HTTP/SSE text fields. Strict JSON rejects duplicate keys, invalid escapes and limit violations; unknown supported-endpoint payload shapes and processing failures fail closed. Incremental SSE restoration preserves event boundaries, typed identities, completion and usage fields. In-memory mappings have a 15-minute TTL, 4 MiB/4,096-entry bounds, and isolation by checkout, user, upstream and gateway instance; request limits are 1 MiB and eight active requests. Scanning runs outside the main event loop. Disabling allows a bounded five-second drain and then cancels remaining requests.
67
+
68
+ Supported prose fields in tool schemas are scanned; schema constraints and opaque protocol blocks are not rewritten. Executable token restoration is limited to the exact captured Claude Code 2.1.142 Read schema. Its real-host test used a synthetic temporary file under the host's ordinary permissions; other executable tools are blocked when restoration would be required. SSE audit observation follows semantic channels; `after_emit` observation is not a blocking filter. HTTP audit coverage is reported as partial and does not claim complete semantic observation.
69
+
70
+ ## Shared policy and gateway boundaries
71
+
72
+ Project-shared policy is versioned authority and must be changed through the policy commands. Editing a live policy file is not a supported configuration shortcut. Disabling enhanced protection does not downgrade project format, remove history, restore redacted copies or turn off the pre-existing basic checks.
73
+
74
+ Gateway settings and mappings belong to the local user and checkout. They must not be committed with task content. A running port or `enabled: true` setting does not prove the client routes its requests through the intended instance. `routeVerified` remains false; `routeObservedAt` and the host binding describe observations by the current instance and do not establish coverage of all traffic. Unsupported protocols and processing failures must fail closed; disabling the gateway does not create a plaintext proxy or silently restore a provider configuration.
75
+
76
+ Protection covers content handled by the corresponding mancode boundary. It does not imply coverage of arbitrary Git operations, direct file access, tool-originated network traffic, images, encrypted blocks or every host connection. Real-host compatibility and production concurrency require separate evidence; see [privacy-implementation-plan.md](privacy-implementation-plan.md) for the current verified scope.
@@ -0,0 +1,101 @@
1
+ # 隐私保护实施计划与验收记录
2
+
3
+ 用户于 2026-09-11 批准按 `research/maskit-integration-2026-09-11/report.md` 与 `review.md` 开发,并明确要求在 `develop` 分支由子 Agent 开发、主 Agent 统筹。原研究文档保留当时的授权状态和源码行号;本文件记录批准后的实现进展,适用于 0.6.5。
4
+
5
+ ## 已批准的交付边界
6
+
7
+ 1. P1:TypeScript 共用检测核心、只读扫描和不可逆副本预览;保留旧共享数据解析契约与原文,完整扫描失败不产出副本。
8
+ 2. P2:共享策略由命令事务提交,版本/摘要绑定;首次初始化可明确启用,旧项目显式升级;历史原件不可变,激活范围和隔离/迁移路径必须可执行。
9
+ 3. P3:可选、本机单用户单 checkout 的前台模型网关;启用意愿与进程/路由证据分开;故障不明文透传;未验证的可执行工具参数不能无条件回填。
10
+ 4. 初始化首次询问两个独立选项;非交互缺省关闭,`--yes` 不代表启用;重复初始化保留选择。状态只报告可验证事实。
11
+ 5. P4 的真实宿主/模型验证和生产性能声明需要实际证据;本机假上游及协议测试不能替代真实宿主验收。首版不自动改用户 provider、登录或计费配置。
12
+
13
+ ## 并行分工
14
+
15
+ - 核心/CLI Agent:`src/privacy/`、scan/preview、统一根命令、init/status 聚合、用户文档和打包来源。
16
+ - 共享策略 Agent:manifest、策略事务、共享写入/输出、transport 兼容与历史处理。
17
+ - 网关 Agent:严格 JSON、占位映射、HTTP/SSE 协议、前台进程、本地设置与诊断。
18
+ - 主 Agent:接口协调、独立审查、整体验证和用户沟通;不直接承包实现。
19
+
20
+ ## P1 当前实现
21
+
22
+ - `scanSensitiveText`:1 MiB UTF-8 预算、4,096 条 finding 预算、500 ms 完成预算;严格文本有效性;规则版本和安全元数据;合并重叠区间时覆盖尾部。
23
+ - `privacy scan --file <path> --profile shared --json` 或 stdin:读取字节有界、严格 UTF-8 解码,文件输入只接受普通文件。返回码 0=无命中、1=有命中、2=失败。
24
+ - `privacy preview --output <new-file>`:完整扫描后写 0600 临时文件、sync、独占发布新副本;不覆盖源、既有目标或符号链接。成功返回 0。
25
+ - 输出不含正文、输入路径、原值 hash 或 token。错误保留内部 cause,仅输出安全原因和白名单系统错误码。
26
+ - 上游固定提交、版权声明、AGPL 许可副本已纳入 npm 包来源记录。
27
+
28
+ ## 已运行的验证(仅对应当时实现)
29
+
30
+ | 命令/探针 | 结果 | 证据意义 |
31
+ |---|---|---|
32
+ | `npx vitest run tests/privacy-detect.test.ts tests/privacy-contracts.test.ts` | 30/30 通过 | 核心首轮与旧解析契约 |
33
+ | `npx vitest run tests/privacy-command.test.ts tests/privacy-detect.test.ts tests/cli-v3-surface-contracts.test.ts` | 37/37 通过 | 输入/输出命令与公开注册首轮 |
34
+ | `npx vitest run tests/privacy-command.test.ts tests/privacy-detect.test.ts` | 37/37 通过 | PEM 大小写/Unicode修正与完整副本发布实现 |
35
+ | `npx vitest run tests/privacy-detect.test.ts tests/privacy-command.test.ts tests/privacy-contracts.test.ts` | 46/46 通过 | 增补多类别标记幂等、冒号前缀邮件、跨行凭据与 ENOSPC 失败不发布;长负例独立子进程回归 |
36
+ | `npx vitest run tests/init-onboarding.test.ts tests/v3-init-command.test.ts tests/cli-v3-surface-contracts.test.ts` | 51/51 通过 | 原初始化行为兼容(首次接口接入后) |
37
+ | `npx vitest run tests/init-privacy.test.ts tests/cli-v3-surface-contracts.test.ts` | 7/7 通过 | 共享启用/关闭、重复保持、--yes、取消、部分显式选择、相反 flags 冲突 |
38
+ | 限时子进程 1 MiB 探针,每场景 9 次,3 秒硬超时 | 6 场景全部完成 | 不是生产并发保证;消除重叠空白量词的失控回溯 |
39
+
40
+ 1 MiB 子进程实测 p95:普通文本 28.82 ms,`password` 后超长空白 8.01 ms,重复 secret 近似匹配 12.63 ms,email 近似匹配 13.02 ms,PEM 近似匹配 14.50 ms,JWT 近似匹配 11.44 ms。输入均为合成内容。500 ms 预算只能检测已返回的扫描工作,不能中断一次正则执行;因此保留这些长负例回归,网关仍须使用有界 worker 执行模型。
41
+
42
+ ## P2 已实现边界
43
+
44
+ - 本地 manifest 升级至格式 3,策略和历史排除表各自 revision CAS、摘要绑定、同一 operation ID;启用、关闭、apply 和重试统一通过 `privacy_policy_update`。关闭保留格式、排除表、最低 0.6.5 客户端要求和原 basic checks。
45
+ - 策略更新持项目 schema barrier,提交前后校验基线与 pending operations;共享写入和恢复目标在持锁时读取当前策略。直接编辑 live policy 不构成合法更新。
46
+ - git-ref manifest 格式 2 保存完整策略/排除快照,通过远端 CAS 先提交,再完成本地 journal 的三个权威文件。远端成功但本地中断必须 repair;不会宣称仅本地启用已完成。旧 clone 和旧版本不能继续写;接收共享权威文件后可恢复普通同步。
47
+ - 历史 confirmed decision/checkpoint 不修改原件,永久排除后不能通过 Context Pack、共享写入或恢复重新导出。当前远端 task bundle 含敏感 checkpoint 时必须先产生安全 checkpoint 并同步。
48
+ - 不扩展 actor/claim/handoff 的历史修改语义。远端这些不可变对象命中时,dry-run 按实体类型/数量/原因返回 `retain_basic_or_new_workspace`,允许保留 basic protection,或由用户明确新建 workspace 并迁移清理后的内容;不删除旧 ownership 历史。
49
+ - 独立 materialization 及其旧 journal 恢复也持 schema barrier,并检查当前策略和永久排除;旧缓存与当前策略不同则不可复用。增强规则关闭后,旧 applying journal 仍不能恢复 excluded checkpoint。
50
+ - local→git-ref 迁移带入完整策略快照;迁移准备和配置 CAS 与策略升级共用 barrier,既有迁移恢复路径保持可用。
51
+
52
+ ## P3 已实现边界
53
+
54
+ 网关配置、token 与运行状态保存在用户/checkout 的私有位置;公开配置与运行命令要求已有 workspaceId 和 checkoutId。`init` 只在新项目提交后保存可选偏好;不启动进程、不改宿主 provider 或登录设置。`run` 前台运行并绑定 loopback;状态分开报告配置有效性、进程确认、loaded/configured digest 和路由观察,`routeVerified` 不以端口可达替代为 true。
55
+
56
+ OpenAI Responses 和 Anthropic Messages 的支持范围包括严格 JSON 与增量 SSE 文本往返;对象键值语义扫描、工具 schema 的 prose 字段扫描、opaque block 排除、未知/危险可执行参数回填阻断、生命周期隔离与容量限制、故障关闭、关闭活动请求、线程外有界扫描与心跳。真实宿主版本绑定、上游类型和配置 digest 分别校验。协议内工具描述可能脱敏,但 `enum`/`const` 等约束保持不改。
57
+
58
+ ## 最新验证补充
59
+
60
+ | 命令/探针 | 结果 | 证据意义 |
61
+ |---|---|---|
62
+ | `privacy-detect`、`privacy-command`、`init-privacy`、`cli-v3-surface-contracts`、`git-ref-cache-contracts`、`git-ref-materialization-contracts`、`transport-migration-adapters-contracts`、`transport-migration-contracts` 八组 Vitest | 99/99 通过 | 含核心58、命令11、init8、materialization6、adapter4及既有迁移8;四种初始化组合、gateway配置写失败保留项目、扫描句尾标点、2-series卡、schema barrier和禁用后排除恢复 |
63
+ | 核心句末标点回归 | 19种全通过 | 真宿主canary发现英文句号漏检后修正规则;保留mailto、域名内部点号和原1MiB近似email限时负例 |
64
+ | owned 24个源/测试文件 `biome check` | 通过,无诊断 | 未格式化全仓或无关用户改动 |
65
+ | `npm pack --dry-run --ignore-scripts` | 来源文档和AGPL副本在包内,research目录不入包 | 完整使用指南和验收记录亦纳入显式files列表 |
66
+ | 主线程性能探针,合成1,020,024 bytes请求×1与×8 | 均HTTP 200;单请求51.26 ms,8并发各约233.65–234.66 ms;主事件循环延迟p95 1.42 ms、max 5.66 ms | 有界worker版本本机样本;不构成生产并发承诺 |
67
+ | 30个delta:假上游实际发出→本机客户端接收 | p95 1.15 ms、max 1.20 ms | 该测量没有直连A/B对照,不称为纯网关增量开销 |
68
+ | `node scripts/privacy-gateway-spike.mjs --hosts-only` | Codex CLI 0.153.4 Responses、Claude Code 2.1.142 Messages 均exit 0、canary受保护并还原 | 真实CLI→网关→本机假上游→CLI;非真实付费模型 |
69
+ | 同一探针的 Claude Read 两轮往返 | 2次上游调用、pathMasked/resultMasked/canaryRestored均true,hostPermissionBypass=false | 精确已捕获Read schema;真实宿主默认权限读取本次创建的临时合成文件,未读取用户真实文件 |
70
+ | Gateway九组契约测试 | 44/44通过 | lifecycle、config、JSON、mapping、protocol、SSE、server等;完整结果在下述证据文件 |
71
+ | `website-docs` 与最新 `git-ref-materialization-contracts` | 15/15通过 | 两语言完整公开CLI索引、0.6.5版本标签及materialization策略保护;网站只补文档,沿用原结构 |
72
+ | 原跨clone workflow scope/update 两个失败案例定向重验 | 2/2通过 | originating operation→repair→materialize传递实际schema lock owner,并核对持久operationId与当前processId,避免二次拿锁 |
73
+ | 最新 `privacy-policy-transport` | 13/13通过 | 远端策略CAS并发、六处journal崩溃边界恢复、禁用/重启用、双clone接收、tamper/旧writer拒绝、安全dry-run、失败无本地/远端变化 |
74
+ | 完整 `git-ref-cross-clone-e2e` | 7/7通过 | 加入schema锁复用及独立actor写入有界等待后的全路径回归 |
75
+ | shared privacy/actor/glossary 相关回归与当前 `npm run typecheck` | 31项通过;类型检查通过 | schema竞争只重试拿锁,不重试实体CAS,等待后重读新策略 |
76
+
77
+ ## 最终全仓验收(2026-09-12)
78
+
79
+ 主 Agent 在首轮收尾源码上完成以下检查。这一快照早于独立复测发现的命名凭据漏检;该修复的最终验收须重新运行,不能复用此表宣布修复后全仓通过。
80
+
81
+ | 命令/探针 | 最终结果 | 证据意义 |
82
+ |---|---|---|
83
+ | `npm run lint` | 348个文件全部通过 | 全仓源代码与测试通过Biome检查 |
84
+ | `npm run typecheck` | 通过 | 全仓TypeScript类型检查 |
85
+ | `npm run build` | ESM与DTS构建通过 | CLI、模块和独立gateway worker均生成成功 |
86
+ | `npm run test:dist` | 16个adapter全部通过 | 验证实际构建产物的适配器内容 |
87
+ | 允许loopback的 `npx vitest run` | 147个测试文件、1,278项测试全部通过;100.55秒 | 含真实本机HTTP服务的完整契约回归;日志为 `/private/tmp/mancode-privacy-final-tests-20260912.log` |
88
+ | 实际 `dist/gateway/worker.js` Worker启动探针 | `builtWorker: true` | 向构建产物发送begin及合成句末email canary,验证独立worker真实执行路径 |
89
+ | `npm pack --dry-run --ignore-scripts`,使用任务临时cache | v0.6.5,39个entries;worker、规则来源、AGPL副本和使用指南均包含;research目录不包含 | 核对最终npm包文件清单,未发布软件包 |
90
+
91
+ 网关证据保存在 `tests/fixtures/privacy-protocols/host-roundtrip-evidence.json`、`implementation-evidence.json` 和 `performance-evidence.json`。支持范围限上述CLI的API-provider路径,不包括桌面、订阅登录、云路由和未列出的宿主版本。除精确Read schema外,需恢复占位符的可执行工具参数仍被阻断;opaque思考/签名/加密块不改写。`routeVerified`始终false,只报告本实例路由观察。SSE的after_emit审计不负责拦截,HTTP审计当前仅partial。
92
+
93
+ 以上性能和协议样本使用合成数据及本机假上游。真实付费模型、桌面客户端、订阅与云路由、真实上游网络抖动及生产规模性能仍是未验证边界;本次全仓通过不扩大这些支持声明。
94
+
95
+ ## 独立复测后的命名凭据修复
96
+
97
+ 独立安装包复测发现 `client_password` / `DB_PASSWORD` 等带前缀的字段漏检,以及带引号多词密码只覆盖首词。修复集中在 `src/privacy/` 的公共扫描路径:完整匹配有界字段名,再用单向游标读取值,正确处理同一行多个赋值、转义引号与反斜杠、空值、未闭合引号,以及占位标记后追加敏感内容。原 UTF-16 偏移、失败关闭、1 MiB / 4,096 findings / 500 ms 预算保持不变。
98
+
99
+ 回归覆盖 raw JSON 与普通文本、CLI scan/preview、真实 shared workflow 写入前拒绝、两个协议的 gateway prose 及实际 HTTP 发往假上游的请求。保留 gateway 原有 metadata/嵌套 JSON 键语义路径;不改旧 `src/context/privacy.ts` 六分类契约、不迁移 live policy、不变更用户 provider 或本地开关。这些修复在首个 ruleset 发布前完成,因此保持 `mancode-sensitive-text:1`,已发布规则变更仍要求显式版本和兼容性审查。
100
+
101
+ 修复冻结前的定向验证:10 个测试文件、138 项全部通过,其中命名凭据 14、scanner 58、CLI 12、shared policy 22、旧 privacy 契约 2、gateway protocol/server/engine/mapping/SSE 共 30 项。包含 0/1/2 个前导短横线兼容、metric 负例与 1 MiB 长值/未闭合引号子进程硬期限回归;实际 HTTP 测试在允许 loopback 的本机环境运行。`npm run typecheck`、相关 9 个源/测试文件的 `biome check` 与 `git diff --check` 均通过。全量覆盖率及重新打包后的独立验收由主 Agent 另行记录,此处不宣称完成。
@@ -0,0 +1,22 @@
1
+ # Privacy scanner rule sources
2
+
3
+ The TypeScript scanner in `src/privacy/` adapts detection ideas, regular-expression shapes, and checksum validation from **数据面具 Maskit — 本地 LLM 敏感信息脱敏代理**, Copyright (C) 2026 TMW. The audited upstream baseline is [Maskit v0.2.7, commit 19ee66463dc11432feec7afe69dabfe24bc997e6](https://github.com/xiaYuTian11/maskit/tree/19ee66463dc11432feec7afe69dabfe24bc997e6). This notice does not imply endorsement or affiliation.
4
+
5
+ Maskit's upstream license is GNU Affero General Public License version 3. A complete copy is retained in [privacy-upstream-license.txt](privacy-upstream-license.txt). mancode remains AGPL-3.0-only; its root `LICENSE` applies to this distribution. Both this attribution and the upstream license are included in the npm package's explicit file list.
6
+
7
+ The source reference is the upstream [`engine/transparent.py`](https://github.com/xiaYuTian11/maskit/blob/19ee66463dc11432feec7afe69dabfe24bc997e6/engine/transparent.py), with the locally audited rule catalogue recorded in `research/maskit-integration-2026-09-11/`. The upstream proxy, event store, audit logging, token table, configuration loader, and UI are not copied into the scanner.
8
+
9
+ | Rule family | mancode adaptation |
10
+ |---|---|
11
+ | API/vendor/cloud keys, connection credentials, bearer/JWT | Explicit bounded shapes; offsets select the password capture where appropriate; JWT header/payload decoding validates structure only |
12
+ | Chinese mobile numbers | Explicit supported prefixes and consistent spaces/hyphens; identifier boundaries reduce build/version false positives |
13
+ | Chinese identity cards | Province prefix, real date and 18-digit checksum; legacy 15-digit values have no checksum claim |
14
+ | Payment cards | Supported digit/group shapes and Luhn validation, including 16-digit Mastercard 2-series prefixes 222100–272099; a checksum alone is insufficient to accept arbitrary grouped numbers |
15
+ | Named secrets | Complete ASCII field names of 1–128 characters, beginning with a letter, optionally preceded by `-` or `--`; credential names and `_`/`-` separated prefixes such as `client_password`, `DB_PASSWORD` and `service-api-key`; quoted values preserve spaces and escaped quotes/backslashes, and unfinished quotes protect the remaining input |
16
+ | Authorization, cookies, local paths, email, PEM | Retains mancode's existing categories as new scanner rules; PEM covers unfinished blocks and case variants; email matches preserve sentence punctuation and support `mailto:` prefixes |
17
+
18
+ Ruleset identity: `mancode-sensitive-text:1`. Findings contain a rule ID, category, original UTF-16 code-unit offsets, and `shape` or `checksum` validation. They contain no input, input path, secret-derived hash, or reversible identifier. Checksum success does not establish authenticity, ownership, or current validity. Patterns deliberately have finite boundaries and may miss unsupported formats; the tool does not claim complete PII or credential detection.
19
+
20
+ Named-secret matching requires `:` or `=` and a terminal credential name (`password`, `passwd`, `secret`, `token`, `api_key`/`api-key`/`apikey`, or the corresponding `access_key` variants), case-insensitively. Metrics such as `password_length` and `token_count` do not match this rule. Unquoted values end at whitespace, quotes, semicolons or commas; quote values that contain these characters. Empty quoted values and exact recognized redaction markers are already safe; a marker followed by more value text is scanned as a credential. Other rules still apply independently.
21
+
22
+ The old `src/context/privacy.ts` parser contract and its six persisted categories remain independent. New scanner categories must never be written into old schemas merely because their names are similar. Once a ruleset is released, changes require a new explicit version and compatibility review. The prefixed-name and complete-quoted-value corrections are part of the initial `mancode-sensitive-text:1` implementation in mancode 0.6.5; they do not introduce a policy migration.