contexel 0.1.16 → 0.1.17

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/README.md CHANGED
@@ -72,7 +72,7 @@ const searchCode = shaped([
72
72
  | `select` | keep only the listed fields |
73
73
  | `dedupe` | drop duplicates by key or whole-record fingerprint (type-aware: `1`, `"1"`, `true` stay distinct) |
74
74
  | `allowlist` | fail-closed provenance gate — keep records whose field is in the allowed set (a missing field reads as `null`, kept only if `null` is explicitly allowed) |
75
- | `quarantine` | drop or flag records matching injection patterns ("ignore previous instructions", …) |
75
+ | `quarantine` | drop or flag records matching injection patterns ("ignore previous instructions", …); custom `patterns` extend the built-ins — `replacePatterns: true` is the explicit opt-out |
76
76
  | `rescore` | batch BM25 relevance with word-boundary matching and in-order proximity bonus |
77
77
  | `rank` | stable sort by a field; records missing the field sort last |
78
78
  | `truncateField` | cut one field to a token budget, `…` suffix |
package/dist/index.d.ts CHANGED
@@ -8,4 +8,4 @@ export { pipeline, stage, type StageFn } from "./pipeline.js";
8
8
  export { shaped } from "./shaped.js";
9
9
  export { trace, currentTrace, Trace, type Entry } from "./trace.js";
10
10
  export * as tokens from "./tokens.js";
11
- export declare const VERSION = "0.1.16";
11
+ export declare const VERSION = "0.1.17";
package/dist/index.js CHANGED
@@ -8,4 +8,4 @@ export { pipeline, stage } from "./pipeline.js";
8
8
  export { shaped } from "./shaped.js";
9
9
  export { trace, currentTrace, Trace } from "./trace.js";
10
10
  export * as tokens from "./tokens.js";
11
- export const VERSION = "0.1.16";
11
+ export const VERSION = "0.1.17";
package/dist/stages.d.ts CHANGED
@@ -34,9 +34,10 @@ export declare const quarantine: (records: {
34
34
  [x: string]: unknown;
35
35
  }[], params?: {
36
36
  fields?: string | string[];
37
- patterns?: string[];
37
+ patterns?: string | string[];
38
38
  action?: "drop" | "flag";
39
39
  into?: string;
40
+ replacePatterns?: boolean;
40
41
  } | undefined) => {
41
42
  [x: string]: unknown;
42
43
  }[];
package/dist/stages.js CHANGED
@@ -105,7 +105,32 @@ export const quarantine = traced("quarantine", (records, params = {}) => {
105
105
  if (action !== "drop" && action !== "flag") {
106
106
  throw new Error(`action must be 'drop' or 'flag', got ${String(action)}`);
107
107
  }
108
- const matcher = new RegExp((params.patterns ?? INJECTION_PATTERNS).join("|"), "i");
108
+ // custom patterns EXTEND the built-ins: passing domain markers must
109
+ // never silently disable "ignore all previous instructions" detection;
110
+ // replacePatterns is the explicit opt-out
111
+ let patternList;
112
+ if (params.patterns === undefined) {
113
+ if (params.replacePatterns) {
114
+ throw new Error("replacePatterns without patterns is contradictory; pass the replacement patterns");
115
+ }
116
+ patternList = INJECTION_PATTERNS;
117
+ }
118
+ else {
119
+ const userPatterns = fieldList(params.patterns);
120
+ if (userPatterns.some((p) => !p)) {
121
+ // an empty fragment becomes an empty regex alternative, which
122
+ // matches EVERY record — the opposite of configuring a screen
123
+ throw new Error("empty pattern fragments match everything; remove them");
124
+ }
125
+ patternList = params.replacePatterns
126
+ ? userPatterns
127
+ : [...INJECTION_PATTERNS, ...userPatterns];
128
+ }
129
+ if (!patternList.length) {
130
+ throw new Error("replacePatterns with no patterns would disable the control entirely; " +
131
+ "pass at least one pattern");
132
+ }
133
+ const matcher = new RegExp(patternList.join("|"), "i");
109
134
  const out = [];
110
135
  for (const r of records) {
111
136
  // Python str(r.get(f, "")): None -> "None", nested containers via
@@ -175,7 +200,9 @@ export const rescore = traced("rescore", (records, params) => {
175
200
  const firstIdx = idx;
176
201
  while (idx !== -1) {
177
202
  cnt++;
178
- idx = text.indexOf(t, idx + 1);
203
+ // Python str.count is NON-overlapping: advance past the whole
204
+ // match ("aaaa".count("aa") === 2)
205
+ idx = text.indexOf(t, idx + t.length);
179
206
  }
180
207
  if (cnt) {
181
208
  row.set(t, cnt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contexel",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Deterministic, dependency-free context shaping for code-writing agents — TypeScript parity of the Python package, enforced by shared golden vectors.",
5
5
  "keywords": ["llm", "agents", "context-engineering", "tokens", "tool-use", "rag"],
6
6
  "license": "MIT",