@trazum/core 1.61.0 → 1.61.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D,2DAA2D;AAC3D,MAAM,MAAM,sBAAsB;AAChC,yEAAyE;AACvE,sBAAsB;AACxB,kEAAkE;GAChE,kBAAkB;AACpB,uEAAuE;GACrE,gBAAgB,CAAC;AAErB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,UAAU,EAAE,WAAW,CAAC;IACxB;;;;;;OAMG;IACH,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,UAAU,EAAE,UAAU,CAAC;IACvB,6CAA6C;IAC7C,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,CAAC,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,aAAa,CAAC;IACvB;;;;;;;OAOG;IACH,OAAO,EAAE,UAAU,GAAG,oBAAoB,GAAG,IAAI,CAAC;IAClD,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;KACvD,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,4CAA4C;IAC5C,KAAK,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;IAAE,SAAS,EAAE,gBAAgB,CAAC;IAAC,EAAE,CAAC,EAAE,IAAI,CAAA;CAAE,GAClD,UAAU,CA8GZ"}
1
+ {"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAC;AAE9D,2DAA2D;AAC3D,MAAM,MAAM,sBAAsB;AAChC,yEAAyE;AACvE,sBAAsB;AACxB,kEAAkE;GAChE,kBAAkB;AACpB,uEAAuE;GACrE,gBAAgB,CAAC;AAErB,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,UAAU,EAAE,WAAW,CAAC;IACxB;;;;;;OAMG;IACH,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC;CACpC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,UAAU,EAAE,UAAU,CAAC;IACvB,6CAA6C;IAC7C,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjD;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,CAAC,CAAC;IACjB,2EAA2E;IAC3E,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,aAAa,CAAC;IACvB;;;;;;;OAOG;IACH,OAAO,EAAE,UAAU,GAAG,oBAAoB,GAAG,IAAI,CAAC;IAClD,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE;YAAE,WAAW,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,CAAC;KACvD,GAAG,IAAI,CAAC;CACV;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjD,4CAA4C;IAC5C,KAAK,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;CACrC;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE;IAAE,SAAS,EAAE,gBAAgB,CAAC;IAAC,EAAE,CAAC,EAAE,IAAI,CAAA;CAAE,GAClD,UAAU,CAkIZ"}
package/dist/answer.js CHANGED
@@ -30,6 +30,25 @@ import { effectivePricing } from './pricing.js';
30
30
  */
31
31
  export function answerCost(request, options) {
32
32
  const { catalogue, on = new Date() } = options;
33
+ /*
34
+ A token count below zero, or not a number at all, is refused before it can
35
+ become money.
36
+
37
+ `spend_guard` took `outputTokens: -500`, priced the call at −$0.0075, and
38
+ said **yes** — a negative estimate lowers the projected spend, so an agent
39
+ that lies about its output tokens buys itself an approval. The refusal
40
+ lives here rather than in the MCP wrapper because every door — serve's
41
+ `POST /cost`, the gateway, the guard — routes through this function, and a
42
+ fix in one wrapper is a hole left in the other three.
43
+ */
44
+ for (const [name, value] of [
45
+ ['inputTokens', request.inputTokens],
46
+ ['outputTokens', request.outputTokens],
47
+ ]) {
48
+ if (value !== undefined && (!Number.isFinite(value) || value < 0)) {
49
+ throw new Error(`${name} must be a non-negative finite number (received: ${String(value)})`);
50
+ }
51
+ }
33
52
  let call = null;
34
53
  let unpriced = false;
35
54
  if (request.model !== undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"answer.js","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"answer.js","sourceRoot":"","sources":["../src/answer.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"optimize.d.ts","sourceRoot":"","sources":["../src/optimize.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EAEf,OAAO,EAEP,YAAY,EACb,MAAM,YAAY,CAAC;AAQpB,eAAO,MAAM,aAAa,EAAE,YAM3B,CAAC;AAEF,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,iBAAS,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAqBzC;AAED,uCAAuC;AACvC,iBAAS,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CASrD;AAOD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,kBAAkB,CA2F1F;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,iBAAiB,EAC1B,OAAO,GAAE,gBAAoC,GAC5C,OAAO,CAAC,kBAAkB,CAAC,CA2C7B;AAED,sEAAsE;AACtE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,aAAa,EAAE,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"optimize.d.ts","sourceRoot":"","sources":["../src/optimize.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EACV,kBAAkB,EAClB,eAAe,EAEf,OAAO,EAEP,YAAY,EACb,MAAM,YAAY,CAAC;AAQpB,eAAO,MAAM,aAAa,EAAE,YAM3B,CAAC;AAEF,UAAU,MAAM;IACd,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,iBAAS,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAqBzC;AAED,uCAAuC;AACvC,iBAAS,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CASrD;AAOD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,kBAAkB,CA6H1F;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,kBAAkB,EAC1B,OAAO,EAAE,iBAAiB,EAC1B,OAAO,GAAE,gBAAoC,GAC5C,OAAO,CAAC,kBAAkB,CAAC,CA2C7B;AAED,sEAAsE;AACtE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,aAAa,EAAE,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC"}
package/dist/optimize.js CHANGED
@@ -87,39 +87,72 @@ export function optimize(prompt, options = {}) {
87
87
  const mustSurvive = distinctProtected(segments);
88
88
  const { text: maskedOriginal, vault } = mask(segments);
89
89
  let current = maskedOriginal;
90
- const ruleResults = [];
91
- for (const rule of RULES) {
92
- if (disabled.has(rule.id))
93
- continue;
94
- if (rule.level === 'aggressive' && level !== 'aggressive')
95
- continue;
96
- const currentUnmasked = unmask(current, vault);
97
- const before = count(currentUnmasked);
98
- const { text: candidate, hits } = rule.apply(current);
99
- if (hits === 0 || candidate === current)
100
- continue;
101
- const candidateUnmasked = unmask(candidate, vault);
102
- // Safety net: if a rule made protected content disappear, that rule is
103
- // dropped rather than returning a broken prompt.
104
- const lostProtected = mustSurvive.some((text) => !candidateUnmasked.includes(text));
105
- if (lostProtected)
106
- continue;
107
- const after = count(candidateUnmasked);
108
- // Captured against the unmasked text on both sides, so the snippets read
109
- // as the author wrote them rather than showing private-use markers.
110
- const changes = extractChanges(currentUnmasked, candidateUnmasked);
111
- current = candidate;
112
- const copy = t.rules[rule.id];
113
- ruleResults.push({
114
- id: rule.id,
115
- title: copy.title,
116
- rationale: copy.rationale,
117
- level: rule.level,
118
- hits,
119
- tokensSaved: Math.max(0, before - after),
120
- changes,
121
- });
90
+ const byRule = new Map();
91
+ /*
92
+ The pipeline runs to a fixed point, because one pass is not one.
93
+
94
+ A single pass missed its own cascades: `emphasis` strips `IMPORTANT:` and
95
+ leaves two lines equal but for a space, and `whitespace` — which would
96
+ have collapsed that space — has already run, so `duplicate-lines` never
97
+ sees the pair. The result was a tool that saves more when run on its own
98
+ output, which is the writer's acceptance test failed by the tool that
99
+ grades it. Found by a fuzzer, on 1 input in 4,000.
100
+
101
+ The bound is a cap against oscillation, not a budget anybody expects to
102
+ reach: pass two exists for the cascades and pass three is almost always
103
+ the one that confirms nothing changed. A run that is still moving at the
104
+ cap returns what it has — valid, merely short of the fixed point — and
105
+ the idempotence property in the fuzz suite is what would make that loud.
106
+ */
107
+ const FIXED_POINT_PASSES = 5;
108
+ for (let pass = 0; pass < FIXED_POINT_PASSES; pass += 1) {
109
+ const atPassStart = current;
110
+ for (const rule of RULES) {
111
+ if (disabled.has(rule.id))
112
+ continue;
113
+ if (rule.level === 'aggressive' && level !== 'aggressive')
114
+ continue;
115
+ const currentUnmasked = unmask(current, vault);
116
+ const before = count(currentUnmasked);
117
+ const { text: candidate, hits } = rule.apply(current);
118
+ if (hits === 0 || candidate === current)
119
+ continue;
120
+ const candidateUnmasked = unmask(candidate, vault);
121
+ // Safety net: if a rule made protected content disappear, that rule is
122
+ // dropped rather than returning a broken prompt.
123
+ const lostProtected = mustSurvive.some((text) => !candidateUnmasked.includes(text));
124
+ if (lostProtected)
125
+ continue;
126
+ const after = count(candidateUnmasked);
127
+ // Captured against the unmasked text on both sides, so the snippets read
128
+ // as the author wrote them rather than showing private-use markers.
129
+ const changes = extractChanges(currentUnmasked, candidateUnmasked);
130
+ current = candidate;
131
+ const copy = t.rules[rule.id];
132
+ const existing = byRule.get(rule.id);
133
+ if (existing === undefined) {
134
+ byRule.set(rule.id, {
135
+ id: rule.id,
136
+ title: copy.title,
137
+ rationale: copy.rationale,
138
+ level: rule.level,
139
+ hits,
140
+ tokensSaved: Math.max(0, before - after),
141
+ changes,
142
+ });
143
+ }
144
+ else {
145
+ // A rule that fires again in a later pass is one finding, continued:
146
+ // its hits and saving accumulate rather than appearing as a second row.
147
+ existing.hits += hits;
148
+ existing.tokensSaved += Math.max(0, before - after);
149
+ existing.changes = [...existing.changes, ...changes];
150
+ }
151
+ }
152
+ if (current === atPassStart)
153
+ break;
122
154
  }
155
+ const ruleResults = [...byRule.values()];
123
156
  const optimized = unmask(current, vault).trim();
124
157
  const tokensBefore = count(prompt);
125
158
  const tokensAfter = count(optimized);
@@ -1 +1 @@
1
- {"version":3,"file":"optimize.js","sourceRoot":"","sources":["../src/optimize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"optimize.js","sourceRoot":"","sources":["../src/optimize.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"segment.d.ts","sourceRoot":"","sources":["../src/segment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,OAAO,EAAE,MAAM,YAAY,CAAC;AA6C1D,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAiD/C;AAED,gDAAgD;AAChD,wBAAgB,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAEhD;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAE5D"}
1
+ {"version":3,"file":"segment.d.ts","sourceRoot":"","sources":["../src/segment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,OAAO,EAAE,MAAM,YAAY,CAAC;AA6C1D,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAqE/C;AAED,gDAAgD;AAChD,wBAAgB,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAEhD;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,EAAE,CAE5D"}
package/dist/segment.js CHANGED
@@ -16,8 +16,25 @@ const PATTERNS = [
16
16
  ];
17
17
  /** Splits the text into segments, marking what must not be modified. */
18
18
  export function segment(text) {
19
- const matches = [];
19
+ /*
20
+ Each pattern scans with the earlier patterns' ranges already reserved, and
21
+ a match that overlaps a reservation restarts the scan **after the
22
+ reservation** rather than after itself.
23
+
24
+ The old version scanned every pattern over the whole text and dropped the
25
+ overlaps afterwards, which loses more than the overlap: on
26
+ ``` ``` `span` ``` ``` shapes, inline-code matched from the *third
27
+ backtick of a closing fence* to the opening of the real span — an
28
+ illegitimate match, later dropped — but its `lastIndex` had already
29
+ advanced past the real span's opening backtick, so the legitimate match
30
+ was never seen and the span was left mutable. A fuzzed corpus caught it:
31
+ bait text inside a code span came out rewritten, with every mask believed
32
+ to be on.
33
+ */
34
+ const kept = [];
20
35
  for (const { kind, regex, trimTrailing } of PATTERNS) {
36
+ const reserved = [...kept].sort((a, b) => a.start - b.start);
37
+ const overlapping = (start, end) => reserved.find((range) => start < range.end && end > range.start);
21
38
  // Copy the regex so lastIndex is not shared between calls.
22
39
  const re = new RegExp(regex.source, regex.flags);
23
40
  let m;
@@ -31,20 +48,21 @@ export function segment(text) {
31
48
  matched = matched.replace(trimTrailing, '');
32
49
  if (matched.length === 0)
33
50
  continue;
34
- matches.push({ start: m.index, end: m.index + matched.length, kind });
51
+ const start = m.index;
52
+ const end = start + matched.length;
53
+ const hit = overlapping(start, end);
54
+ if (hit !== undefined) {
55
+ // Not simply skipped: the scan resumes where the reservation ends, so
56
+ // a legitimate match sitting just past it is still found.
57
+ re.lastIndex = Math.max(hit.end, start + 1);
58
+ continue;
59
+ }
60
+ kept.push({ start, end, kind });
35
61
  }
36
62
  }
37
- // Sort by start and, on a tie, longest span first.
38
- matches.sort((a, b) => a.start - b.start || b.end - a.end);
39
- // Drop overlaps, keeping the first match at each position.
40
- const kept = [];
41
- let cursor = 0;
42
- for (const match of matches) {
43
- if (match.start < cursor)
44
- continue;
45
- kept.push(match);
46
- cursor = match.end;
47
- }
63
+ // Order for emission; within one pattern matches never overlap, and across
64
+ // patterns the reservation check above already guaranteed it.
65
+ kept.sort((a, b) => a.start - b.start);
48
66
  const segments = [];
49
67
  let pos = 0;
50
68
  for (const match of kept) {
@@ -1 +1 @@
1
- {"version":3,"file":"segment.js","sourceRoot":"","sources":["../src/segment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"segment.js","sourceRoot":"","sources":["../src/segment.ts"],"names":[],"mappings":"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"}
package/dist/write.js CHANGED
@@ -180,8 +180,16 @@ export function assemble(answers, options = {}) {
180
180
  }
181
181
  const measure = (prompt, answers, state, options) => {
182
182
  const model = typeof answers['model'] === 'string' ? answers['model'].trim() : null;
183
+ /*
184
+ A budget is a positive finite number or it is no budget.
185
+
186
+ `Number.isFinite` alone let `-5` through, and a negative ceiling produced
187
+ the verdict `over` — a judgement against a limit that cannot exist. `-5`,
188
+ `Infinity` and `NaN` all land in the same place: nothing to check against,
189
+ which the verdict already knows how to say.
190
+ */
183
191
  const budgetAnswer = typeof answers['budget'] === 'string' ? Number(answers['budget']) : NaN;
184
- const budgetUsd = Number.isFinite(budgetAnswer) ? budgetAnswer : null;
192
+ const budgetUsd = Number.isFinite(budgetAnswer) && budgetAnswer > 0 ? budgetAnswer : null;
185
193
  /*
186
194
  Priced only when the catalogue knows the model.
187
195
 
@@ -192,13 +200,22 @@ const measure = (prompt, answers, state, options) => {
192
200
  is asked for separately and only when there is something to ask.
193
201
  */
194
202
  const known = model !== null && (options.pricing ?? BUNDLED_CATALOGUE).byId.has(model);
203
+ /*
204
+ A negative volume is not a volume. `callsPerMonth: -100` priced a prompt at
205
+ −$1.26 a month — a number no bill ever had — so anything that is not a
206
+ positive finite count is treated as not stated, the same door the budget
207
+ goes through.
208
+ */
209
+ const positive = (value) => value !== undefined && Number.isFinite(value) && value > 0 ? value : undefined;
195
210
  const usage = {};
196
211
  if (known)
197
212
  usage.model = model;
198
- if (options.callsPerMonth !== undefined)
199
- usage.callsPerMonth = options.callsPerMonth;
200
- if (options.avgOutputTokens !== undefined)
201
- usage.avgOutputTokens = options.avgOutputTokens;
213
+ const callsPerMonth = positive(options.callsPerMonth);
214
+ if (callsPerMonth !== undefined)
215
+ usage.callsPerMonth = callsPerMonth;
216
+ const avgOutputTokens = positive(options.avgOutputTokens);
217
+ if (avgOutputTokens !== undefined)
218
+ usage.avgOutputTokens = avgOutputTokens;
202
219
  const report = optimize(prompt, {
203
220
  level: options.level ?? 'safe',
204
221
  pricing: options.pricing,
package/dist/write.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../src/write.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trazum/core",
3
- "version": "1.61.0",
3
+ "version": "1.61.2",
4
4
  "description": "Trazum core: priced advisories for LLM prompts (caching, model tier, batching, schemas), plus deterministic trimming, token counting and pricing.",
5
5
  "license": "MIT",
6
6
  "author": "David Mu\u00f1oz Rey",
package/src/answer.ts CHANGED
@@ -116,6 +116,26 @@ export function answerCost(
116
116
  ): CostAnswer {
117
117
  const { catalogue, on = new Date() } = options;
118
118
 
119
+ /*
120
+ A token count below zero, or not a number at all, is refused before it can
121
+ become money.
122
+
123
+ `spend_guard` took `outputTokens: -500`, priced the call at −$0.0075, and
124
+ said **yes** — a negative estimate lowers the projected spend, so an agent
125
+ that lies about its output tokens buys itself an approval. The refusal
126
+ lives here rather than in the MCP wrapper because every door — serve's
127
+ `POST /cost`, the gateway, the guard — routes through this function, and a
128
+ fix in one wrapper is a hole left in the other three.
129
+ */
130
+ for (const [name, value] of [
131
+ ['inputTokens', request.inputTokens],
132
+ ['outputTokens', request.outputTokens],
133
+ ] as const) {
134
+ if (value !== undefined && (!Number.isFinite(value) || value < 0)) {
135
+ throw new Error(`${name} must be a non-negative finite number (received: ${String(value)})`);
136
+ }
137
+ }
138
+
119
139
  let call: CallEstimate | null = null;
120
140
  let unpriced = false;
121
141
  if (request.model !== undefined) {
package/src/optimize.ts CHANGED
@@ -119,41 +119,75 @@ export function optimize(prompt: string, options: OptimizeOptions = {}): Optimiz
119
119
  const { text: maskedOriginal, vault } = mask(segments);
120
120
 
121
121
  let current = maskedOriginal;
122
- const ruleResults: RuleResult[] = [];
123
-
124
- for (const rule of RULES) {
125
- if (disabled.has(rule.id)) continue;
126
- if (rule.level === 'aggressive' && level !== 'aggressive') continue;
127
-
128
- const currentUnmasked = unmask(current, vault);
129
- const before = count(currentUnmasked);
130
- const { text: candidate, hits } = rule.apply(current);
131
- if (hits === 0 || candidate === current) continue;
132
-
133
- const candidateUnmasked = unmask(candidate, vault);
134
-
135
- // Safety net: if a rule made protected content disappear, that rule is
136
- // dropped rather than returning a broken prompt.
137
- const lostProtected = mustSurvive.some((text) => !candidateUnmasked.includes(text));
138
- if (lostProtected) continue;
139
-
140
- const after = count(candidateUnmasked);
141
- // Captured against the unmasked text on both sides, so the snippets read
142
- // as the author wrote them rather than showing private-use markers.
143
- const changes = extractChanges(currentUnmasked, candidateUnmasked);
144
- current = candidate;
145
- const copy = t.rules[rule.id];
146
- ruleResults.push({
147
- id: rule.id,
148
- title: copy.title,
149
- rationale: copy.rationale,
150
- level: rule.level,
151
- hits,
152
- tokensSaved: Math.max(0, before - after),
153
- changes,
154
- });
122
+ const byRule = new Map<RuleId, RuleResult>();
123
+
124
+ /*
125
+ The pipeline runs to a fixed point, because one pass is not one.
126
+
127
+ A single pass missed its own cascades: `emphasis` strips `IMPORTANT:` and
128
+ leaves two lines equal but for a space, and `whitespace` — which would
129
+ have collapsed that space — has already run, so `duplicate-lines` never
130
+ sees the pair. The result was a tool that saves more when run on its own
131
+ output, which is the writer's acceptance test failed by the tool that
132
+ grades it. Found by a fuzzer, on 1 input in 4,000.
133
+
134
+ The bound is a cap against oscillation, not a budget anybody expects to
135
+ reach: pass two exists for the cascades and pass three is almost always
136
+ the one that confirms nothing changed. A run that is still moving at the
137
+ cap returns what it has — valid, merely short of the fixed point — and
138
+ the idempotence property in the fuzz suite is what would make that loud.
139
+ */
140
+ const FIXED_POINT_PASSES = 5;
141
+ for (let pass = 0; pass < FIXED_POINT_PASSES; pass += 1) {
142
+ const atPassStart = current;
143
+
144
+ for (const rule of RULES) {
145
+ if (disabled.has(rule.id)) continue;
146
+ if (rule.level === 'aggressive' && level !== 'aggressive') continue;
147
+
148
+ const currentUnmasked = unmask(current, vault);
149
+ const before = count(currentUnmasked);
150
+ const { text: candidate, hits } = rule.apply(current);
151
+ if (hits === 0 || candidate === current) continue;
152
+
153
+ const candidateUnmasked = unmask(candidate, vault);
154
+
155
+ // Safety net: if a rule made protected content disappear, that rule is
156
+ // dropped rather than returning a broken prompt.
157
+ const lostProtected = mustSurvive.some((text) => !candidateUnmasked.includes(text));
158
+ if (lostProtected) continue;
159
+
160
+ const after = count(candidateUnmasked);
161
+ // Captured against the unmasked text on both sides, so the snippets read
162
+ // as the author wrote them rather than showing private-use markers.
163
+ const changes = extractChanges(currentUnmasked, candidateUnmasked);
164
+ current = candidate;
165
+ const copy = t.rules[rule.id];
166
+ const existing = byRule.get(rule.id);
167
+ if (existing === undefined) {
168
+ byRule.set(rule.id, {
169
+ id: rule.id,
170
+ title: copy.title,
171
+ rationale: copy.rationale,
172
+ level: rule.level,
173
+ hits,
174
+ tokensSaved: Math.max(0, before - after),
175
+ changes,
176
+ });
177
+ } else {
178
+ // A rule that fires again in a later pass is one finding, continued:
179
+ // its hits and saving accumulate rather than appearing as a second row.
180
+ existing.hits += hits;
181
+ existing.tokensSaved += Math.max(0, before - after);
182
+ existing.changes = [...existing.changes, ...changes];
183
+ }
184
+ }
185
+
186
+ if (current === atPassStart) break;
155
187
  }
156
188
 
189
+ const ruleResults: RuleResult[] = [...byRule.values()];
190
+
157
191
  const optimized = unmask(current, vault).trim();
158
192
  const tokensBefore = count(prompt);
159
193
  const tokensAfter = count(optimized);
package/src/segment.ts CHANGED
@@ -45,9 +45,28 @@ interface Match {
45
45
 
46
46
  /** Splits the text into segments, marking what must not be modified. */
47
47
  export function segment(text: string): Segment[] {
48
- const matches: Match[] = [];
48
+ /*
49
+ Each pattern scans with the earlier patterns' ranges already reserved, and
50
+ a match that overlaps a reservation restarts the scan **after the
51
+ reservation** rather than after itself.
52
+
53
+ The old version scanned every pattern over the whole text and dropped the
54
+ overlaps afterwards, which loses more than the overlap: on
55
+ ``` ``` `span` ``` ``` shapes, inline-code matched from the *third
56
+ backtick of a closing fence* to the opening of the real span — an
57
+ illegitimate match, later dropped — but its `lastIndex` had already
58
+ advanced past the real span's opening backtick, so the legitimate match
59
+ was never seen and the span was left mutable. A fuzzed corpus caught it:
60
+ bait text inside a code span came out rewritten, with every mask believed
61
+ to be on.
62
+ */
63
+ const kept: Match[] = [];
49
64
 
50
65
  for (const { kind, regex, trimTrailing } of PATTERNS) {
66
+ const reserved = [...kept].sort((a, b) => a.start - b.start);
67
+ const overlapping = (start: number, end: number): Match | undefined =>
68
+ reserved.find((range) => start < range.end && end > range.start);
69
+
51
70
  // Copy the regex so lastIndex is not shared between calls.
52
71
  const re = new RegExp(regex.source, regex.flags);
53
72
  let m: RegExpExecArray | null;
@@ -59,21 +78,22 @@ export function segment(text: string): Segment[] {
59
78
  }
60
79
  if (trimTrailing) matched = matched.replace(trimTrailing, '');
61
80
  if (matched.length === 0) continue;
62
- matches.push({ start: m.index, end: m.index + matched.length, kind });
81
+ const start = m.index;
82
+ const end = start + matched.length;
83
+ const hit = overlapping(start, end);
84
+ if (hit !== undefined) {
85
+ // Not simply skipped: the scan resumes where the reservation ends, so
86
+ // a legitimate match sitting just past it is still found.
87
+ re.lastIndex = Math.max(hit.end, start + 1);
88
+ continue;
89
+ }
90
+ kept.push({ start, end, kind });
63
91
  }
64
92
  }
65
93
 
66
- // Sort by start and, on a tie, longest span first.
67
- matches.sort((a, b) => a.start - b.start || b.end - a.end);
68
-
69
- // Drop overlaps, keeping the first match at each position.
70
- const kept: Match[] = [];
71
- let cursor = 0;
72
- for (const match of matches) {
73
- if (match.start < cursor) continue;
74
- kept.push(match);
75
- cursor = match.end;
76
- }
94
+ // Order for emission; within one pattern matches never overlap, and across
95
+ // patterns the reservation check above already guaranteed it.
96
+ kept.sort((a, b) => a.start - b.start);
77
97
 
78
98
  const segments: Segment[] = [];
79
99
  let pos = 0;
package/src/write.ts CHANGED
@@ -350,8 +350,16 @@ const measure = (
350
350
  options: AssembleOptions,
351
351
  ): DraftMeasurement => {
352
352
  const model = typeof answers['model'] === 'string' ? answers['model'].trim() : null;
353
+ /*
354
+ A budget is a positive finite number or it is no budget.
355
+
356
+ `Number.isFinite` alone let `-5` through, and a negative ceiling produced
357
+ the verdict `over` — a judgement against a limit that cannot exist. `-5`,
358
+ `Infinity` and `NaN` all land in the same place: nothing to check against,
359
+ which the verdict already knows how to say.
360
+ */
353
361
  const budgetAnswer = typeof answers['budget'] === 'string' ? Number(answers['budget']) : NaN;
354
- const budgetUsd = Number.isFinite(budgetAnswer) ? budgetAnswer : null;
362
+ const budgetUsd = Number.isFinite(budgetAnswer) && budgetAnswer > 0 ? budgetAnswer : null;
355
363
 
356
364
  /*
357
365
  Priced only when the catalogue knows the model.
@@ -365,10 +373,21 @@ const measure = (
365
373
  const known =
366
374
  model !== null && (options.pricing ?? BUNDLED_CATALOGUE).byId.has(model);
367
375
 
376
+ /*
377
+ A negative volume is not a volume. `callsPerMonth: -100` priced a prompt at
378
+ −$1.26 a month — a number no bill ever had — so anything that is not a
379
+ positive finite count is treated as not stated, the same door the budget
380
+ goes through.
381
+ */
382
+ const positive = (value: number | undefined): number | undefined =>
383
+ value !== undefined && Number.isFinite(value) && value > 0 ? value : undefined;
384
+
368
385
  const usage: Partial<UsageProfile> = {};
369
386
  if (known) usage.model = model as string;
370
- if (options.callsPerMonth !== undefined) usage.callsPerMonth = options.callsPerMonth;
371
- if (options.avgOutputTokens !== undefined) usage.avgOutputTokens = options.avgOutputTokens;
387
+ const callsPerMonth = positive(options.callsPerMonth);
388
+ if (callsPerMonth !== undefined) usage.callsPerMonth = callsPerMonth;
389
+ const avgOutputTokens = positive(options.avgOutputTokens);
390
+ if (avgOutputTokens !== undefined) usage.avgOutputTokens = avgOutputTokens;
372
391
 
373
392
  const report = optimize(prompt, {
374
393
  level: options.level ?? 'safe',