@voltro/plugin-moderation 0.58.0 → 0.59.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.
Files changed (2) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -39,6 +39,61 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.59.0] — 2026-08-30
43
+
44
+ ### ⚠ BREAKING
45
+
46
+ - **@voltro/cli** — **`web.sourcemaps` moved to the top level of `app.config.ts`, and now covers every bundle the build minifies.**
47
+
48
+ It reached exactly one of them before: the vite CLIENT build. `voltro build` minifies four — the browser bundle, the SSR bundle, the `voltro start` bundle, and for an api app the precompiled entry plus the serve bundle that `voltro serve` actually runs — and none of the other three had a `sourcemap` option or a config path that could ask for one. `keepNames` rescues the identifiers that are read as strings and nothing else, so a frame out of the artefact production runs read `at Qo (serveEntry.js:236:76405)`.
49
+
50
+ The docs closed that hole with a sentence instead of a field: *server stack traces are un-minified already*. The built file says otherwise, and the server half is where it costs most — a schedule, a workflow step, a subscriber or a REST route has no browser caller whose frame could stand in for it.
51
+
52
+ A second field (`api.sourcemaps`) would have made the same mistake twice. "Do I want resolvable frames from this app" has one answer; how many bundles the build happens to emit is not the caller's business.
53
+
54
+ Three details that come with it:
55
+
56
+ - **The old spelling is REFUSED, not ignored.** A moved option that is silently dropped goes green, stops emitting maps, and is found by someone reading an unresolvable frame months later. - **Each output directory uploads under the prefix that matches how its files are named** — `~/assets` for the browser bundle, `~/` for the bundles a node process loaded. One prefix across both is wrong for one of them. - **The upload and sweep run at every exit of the build**, not after the client bundle. Sweeping there would have deleted the client maps before the server ones existed, leaving those neither uploaded nor deleted.
57
+
58
+ `minifiedBundlesCarryMaps.test.ts` asserts the property over the SET: every bundler options object in the CLI that turns minification on must also decide `sourcemap`. That is what was missing — not one option, three, because nothing was watching the class.
59
+
60
+ **`voltro update` carries you across this** — codemod `0.59.0/01_sourcemaps_moved_to_the_top_level`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.59.0).
61
+ - **@voltro/ai** — **Speech has a cost path, and a built-in provider name can be taken over.**
62
+
63
+ Three changes, one thread: the speech seam existed and nothing could reach it.
64
+
65
+ **`registerSpeechProvider(name, provider, { replaceBuiltin: true })`.** Refusing `mock` / `openai` / `gateway` / `anthropic` outright was wrong, and the case that shows it is one the refusal could not see: a provider name is not always an alias the app chooses. `openai` is the segment the gateway puts in its model ids, so it is what a stored catalog's rows carry and what a picker submits — an app told to pick another name cannot, without rewriting persisted rows and live sessions. It keeps its own dispatch instead, for every provider rather than the colliding one. What the refusal protected is kept by putting the intent in the call.
66
+
67
+ **Registered providers resolve BEFORE built-ins.** The dispatch decided from a hard-coded list of built-in names, so without this the flag would let you register `openai` and never route to it — declared, documented, wired at zero call sites.
68
+
69
+ **`MeteredUsage` — a tally in characters or seconds.** Every speech vendor meters one of those; none meters tokens. A ledger that only knew tokens could not hold a speech call at all, so speech spend was not un-priced, it was *absent*. The token shape is untouched (it is what the AI SDK returns); the new shape is separate rather than a unit tacked onto `AiUsage`, because writing a character count into a field called `inputTokens` puts a lie in a row someone reads later.
70
+
71
+ `_voltro_ai_usage` gains a `unit` column, and `costSource` gains `'unpriced'`: a non-token unit with no supplied price is written with the real count and a zero cost rather than a fabricated one, since a per-1M-token rate applied to a character count produces a number indistinguishable from a real one. Sum the count columns grouped by `unit`, never across it.
72
+
73
+ BREAKING for a caller that had narrowed `CostBreakdown['costSource']` to two values or reads `estimateCostUsd`'s return positionally. No call site changes SHAPE — the token path takes the same arguments and returns the same numbers — but a narrowing is user-authored code, so this carries a `manual` codemod rather than `none`: nothing here can be transformed (whether the third member belongs in your union is your decision), and the reader who needs it is exactly the one a "touches no user-authored code" line would have sent away. It also names the query that does not fail but answers wrongly: summing `_voltro_ai_usage` counts ACROSS units now adds characters to tokens.
74
+
75
+ **`voltro update` carries you across this** — codemod `0.59.0/02_costsource_gained_unpriced`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.59.0).
76
+
77
+ ### Added
78
+
79
+ - **@voltro/ai** — **`GatewayModelInfo.raw`** — the `/v1/models` entry exactly as it came over the wire, beside the normalised fields.
80
+
81
+ Normalising is the right default: the union of `tags`, `supported_parameters` and `modalities` is framework knowledge, and every app on the gateway was writing those forty lines differently. But normalising is lossy by construction, and the fields it drops are not decoration — a picker built on the vendor's own capability tags, a pricing display that shows a structure we have not normalised, `zdr` and `no_training` kept apart instead of folded into `dataPolicy`.
82
+
83
+ Without `raw`, such an app fetches the endpoint a second time and maintains a second parser of the same response. One field rather than three more normalised ones, because the alternative asks the same question again at every field the gateway adds.
84
+
85
+ Absent on the injected-provider path, for the same reason `capabilities` is: that seam never saw a response, and an empty object would turn "no wire data" into the claim that the wire carried nothing.
86
+
87
+ ### Fixed
88
+
89
+ - **@voltro/cli** — **`@sentry/cli` 3.x loads.** The loader read one export spelling — `module.exports = SentryCli`, which is 2.x. 3.x ships `exports.SentryCli = SentryCli`, so `mod.default` is a namespace object rather than the class, and a build with a source-map upload configured stopped with *could not be loaded*.
90
+
91
+ The framework cannot pin its way out of this: `@sentry/cli` is FSL-1.1-MIT, so it is not ours to depend on and the app installs it — which means `pnpm add -D @sentry/cli`, the command we ourselves print, resolves the latest. Reading one spelling turned our own documented instruction into a failing build.
92
+
93
+ All four positions are accepted now (both spellings, each of which `import()` presents twice), and the two failures are told apart: a package that resolved but had no readable constructor no longer says *install it*. That reading is what cost a pipeline — locally `require()` resolves and the binary is there, so nothing about the install looks wrong.
94
+
95
+ ---
96
+
42
97
  ## [0.58.0] — 2026-08-30
43
98
 
44
99
  ### ⚠ BREAKING
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/plugin-moderation",
3
- "version": "0.58.0",
3
+ "version": "0.59.0",
4
4
  "description": "Content moderation — run an AI / keyword / custom check on user-content fields and block or flag a write before it commits. Declarative per-rpc rules + an in-handler moderate() helper for redact/inline control.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -38,11 +38,11 @@
38
38
  "node": ">=24.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@voltro/logger": "0.58.0",
42
- "@voltro/protocol": "0.58.0"
41
+ "@voltro/logger": "0.59.0",
42
+ "@voltro/protocol": "0.59.0"
43
43
  },
44
44
  "optionalDependencies": {
45
- "@voltro/ai": "0.58.0"
45
+ "@voltro/ai": "0.59.0"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "effect": "^3.22.0"