@chatpanel/pii 0.2.2 → 0.2.3
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/index.js +1 -0
- package/package.json +4 -2
- package/tool-harness.js +79 -0
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/pii",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "The canonical ChatPanel privacy engine — reversible PII redaction + pseudonymization with local entity detection. Pure, dependency-free ESM shared by the ChatPanel extension, gateway, and bridge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"./pii-redact.js": "./pii-redact.js",
|
|
10
10
|
"./pii-detect.js": "./pii-detect.js",
|
|
11
11
|
"./pipeline.js": "./pipeline.js",
|
|
12
|
-
"./tool-rank.js": "./tool-rank.js"
|
|
12
|
+
"./tool-rank.js": "./tool-rank.js",
|
|
13
|
+
"./tool-harness.js": "./tool-harness.js"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"index.js",
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
"pii-detect.js",
|
|
18
19
|
"pipeline.js",
|
|
19
20
|
"tool-rank.js",
|
|
21
|
+
"tool-harness.js",
|
|
20
22
|
"LICENSE",
|
|
21
23
|
"README.md"
|
|
22
24
|
],
|
package/tool-harness.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// THE tool harness — one interception layer shared by every orchestrator
|
|
2
|
+
// (ChatPanel API + agent, gateway API + relay) so tool handling can't drift. It
|
|
3
|
+
// owns the boundaries around a model turn:
|
|
4
|
+
//
|
|
5
|
+
// ⓪ selectTools — inject + narrow the tools the model is offered (MCP-auto).
|
|
6
|
+
// ② toTool — what a tool RECEIVES: real values (so on-device / remote
|
|
7
|
+
// lookups work), or the redacted token for remote MCP tools
|
|
8
|
+
// when the user chose "redact remote".
|
|
9
|
+
// ③ toModelResult— what the MODEL sees back: the tool result re-redacted so it
|
|
10
|
+
// stays blinded.
|
|
11
|
+
// ④ toUser — the final reply: reversible tokens restored (pseudonyms stay).
|
|
12
|
+
//
|
|
13
|
+
// PRIVACY IS OPTIONAL. With no `vault` (redaction off), ②③④ are pass-throughs —
|
|
14
|
+
// no latency, no placeholder confusion — but ⓪ selectTools STILL narrows, because
|
|
15
|
+
// not every turn is privacy-sensitive yet every turn benefits from fewer tools.
|
|
16
|
+
//
|
|
17
|
+
// Self-contained on the SYNCED engine files (pii-redact.js, tool-rank.js), so the
|
|
18
|
+
// extension (browser ESM) and the gateway (npm) run the exact same code. The caller
|
|
19
|
+
// passes the already-gated `redactOpts` ({tier, entities, dictionary}) it computed
|
|
20
|
+
// from cfg+isPro — keeping tier/dictionary Pro-gating out of the harness.
|
|
21
|
+
|
|
22
|
+
import { restoreText, restoreWithAliases, redactText } from './pii-redact.js';
|
|
23
|
+
import { narrowSpecs } from './tool-rank.js';
|
|
24
|
+
|
|
25
|
+
// MCP / remote tools are server-prefixed (mcp_server__tool). Local tools
|
|
26
|
+
// (history/meeting/page, or a client's core bash/read) are not — they always get
|
|
27
|
+
// real values and are never narrowed away.
|
|
28
|
+
export const isRemoteToolName = (name) => /^mcp[_-]/i.test(String(name || ''));
|
|
29
|
+
|
|
30
|
+
// Deep restore of a tool-call argument value, undoing reversible tokens AND
|
|
31
|
+
// pseudonyms (tools run locally / on real data; only the model stays blinded).
|
|
32
|
+
export function restoreToolArgs(value, vault) {
|
|
33
|
+
if (!vault) return value;
|
|
34
|
+
if (typeof value === 'string') return restoreWithAliases(value, vault);
|
|
35
|
+
if (Array.isArray(value)) return value.map((v) => restoreToolArgs(v, vault));
|
|
36
|
+
if (value && typeof value === 'object') {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const k of Object.keys(value)) out[k] = restoreToolArgs(value[k], vault);
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function makeToolHarness({ vault = null, toolData = 'real', redactOpts = null, redactResults = true } = {}) {
|
|
45
|
+
const on = !!vault; // privacy enabled for this turn?
|
|
46
|
+
const redactRemote = toolData === 'redactRemote';
|
|
47
|
+
return {
|
|
48
|
+
enabled: on,
|
|
49
|
+
isRemoteTool: isRemoteToolName,
|
|
50
|
+
|
|
51
|
+
// ⓪ Always-on tool selection (privacy-independent). `available` is any spec
|
|
52
|
+
// list; `opts` forwards { cap, keep, name, description } to the shared ranker.
|
|
53
|
+
selectTools(available, query, opts = {}) {
|
|
54
|
+
return narrowSpecs(available, query, opts);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// ② What the tool receives.
|
|
58
|
+
toTool(name, args) {
|
|
59
|
+
if (!on) return args; // privacy off → already real
|
|
60
|
+
if (redactRemote && isRemoteToolName(name)) return args; // keep PII off remote MCP
|
|
61
|
+
return restoreToolArgs(args, vault); // real values for the tool
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// ③ What the model sees back (re-redacted). Handles a string or a { text } shape.
|
|
65
|
+
toModelResult(name, raw) {
|
|
66
|
+
if (!on || !redactResults || !redactOpts) return raw;
|
|
67
|
+
if (typeof raw === 'string') return redactText(raw, vault, redactOpts);
|
|
68
|
+
if (raw && typeof raw === 'object' && typeof raw.text === 'string') {
|
|
69
|
+
return { ...raw, text: redactText(raw.text, vault, redactOpts) };
|
|
70
|
+
}
|
|
71
|
+
return raw;
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// ④ The final reply the user sees.
|
|
75
|
+
toUser(text) {
|
|
76
|
+
return on ? restoreText(text, vault) : text;
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|