jev-devtools 0.5.2 → 0.5.4

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
@@ -1,10 +1,10 @@
1
1
  # Jev DevTools
2
2
 
3
- [![npm version 0.5.2](https://img.shields.io/badge/npm-v0.5.2-E551BA?labelColor=0D0A0F&logo=npm)](https://www.npmjs.com/package/jev-devtools/v/0.5.2)
3
+ [![npm version 0.5.4](https://img.shields.io/badge/npm-v0.5.4-E551BA?labelColor=0D0A0F&logo=npm)](https://www.npmjs.com/package/jev-devtools/v/0.5.4)
4
4
 
5
5
  A React drawer and Node adapter for inspecting TypeSafe Jev requests with animated diagrams. Includes request history, context, questions, typed answers, transport attempts, and raw snapshots.
6
6
 
7
- **Source version: 0.5.2 — pending publication.** npm currently provides 0.5.1. Check the [release history](https://github.com/diegoag64/jev-devtools/blob/main/CHANGELOG.md) for compatibility notes. To try this release candidate, install the local archive below. **Experimental (pre-1.0).** React 18/19 and Node 22.12+ are supported. APIs may change before 1.0. Verification uses sample responses through the real TypeSafe SDK, not live inference.
7
+ **Package version: 0.5.4.** [See release notes for verified publication status.](https://github.com/diegoag64/jev-devtools/releases/tag/v0.5.4) Check the [release history](https://github.com/diegoag64/jev-devtools/blob/main/CHANGELOG.md) for compatibility notes. To try newer source changes, install the local archive below. **Experimental (pre-1.0).** React 18/19 and Node 22.12+ are supported. APIs may change before 1.0. Verification uses sample responses through the real TypeSafe SDK, not live inference.
8
8
 
9
9
  [Website](https://jevtools.dev/) · [Documentation](https://docs.jevtools.dev/) · [Quick start](https://docs.jevtools.dev/getting-started/) · [API reference](https://docs.jevtools.dev/api/reference/) · [Release history](https://docs.jevtools.dev/changelog/)
10
10
 
@@ -48,7 +48,7 @@ To try local changes before a future release:
48
48
  npm ci
49
49
  npm run pack:devtools
50
50
  # From the consuming app:
51
- npm install /absolute/path/to/jev-devtools-0.5.2.tgz
51
+ npm install /absolute/path/to/jev-devtools-0.5.4.tgz
52
52
  ```
53
53
 
54
54
  Mount once. The drawer loads its own styles; no CSS import is needed:
@@ -73,6 +73,60 @@ export function createJevDevtools(options) {
73
73
  scheduled = undefined;
74
74
  }
75
75
  }
76
+ const pendingFlows = new Set();
77
+ let scheduledFlows;
78
+ const encodeFlowUpdate = (session, cursor, knownDefinitions) => {
79
+ const update = flows.changes(session, cursor, knownDefinitions);
80
+ return {
81
+ bytes: encoder.encode(`event: flows\ndata: ${JSON.stringify(update.data)}\n\n`),
82
+ revision: update.revision,
83
+ retainedDefinitionIds: update.retainedDefinitionIds,
84
+ };
85
+ };
86
+ function scheduleFlow(flush) {
87
+ pendingFlows.add(flush);
88
+ if (scheduledFlows)
89
+ return;
90
+ scheduledFlows = setImmediate(() => {
91
+ scheduledFlows = undefined;
92
+ const batch = [...pendingFlows];
93
+ pendingFlows.clear();
94
+ // A lone reader has nothing to share; keep its direct encoding path and
95
+ // avoid allocating a cache or copying an otherwise unshared buffer.
96
+ if (batch.length === 1) {
97
+ batch[0](encodeFlowUpdate);
98
+ return;
99
+ }
100
+ // Scope sharing to this synchronous turn: no snapshots survive a later
101
+ // update/clear. Different cursors or definition knowledge need their own
102
+ // projection, even when the authorized session matches.
103
+ const encoded = new Map();
104
+ const updateFor = (session, cursor, knownDefinitions) => {
105
+ const key = JSON.stringify([
106
+ session,
107
+ cursor,
108
+ [...knownDefinitions].sort(),
109
+ ]);
110
+ let update = encoded.get(key);
111
+ if (!update) {
112
+ update = encodeFlowUpdate(session, cursor, knownDefinitions);
113
+ encoded.set(key, update);
114
+ }
115
+ // Response consumers can mutate/transfer their chunk. Never expose the
116
+ // cached backing buffer to a reader.
117
+ return { ...update, bytes: update.bytes.slice() };
118
+ };
119
+ for (const deliver of batch)
120
+ deliver(updateFor);
121
+ });
122
+ }
123
+ function unscheduleFlow(flush) {
124
+ pendingFlows.delete(flush);
125
+ if (!pendingFlows.size && scheduledFlows) {
126
+ clearImmediate(scheduledFlows);
127
+ scheduledFlows = undefined;
128
+ }
129
+ }
76
130
  let disposed = false;
77
131
  const enabled = () => options.enabled === true && !disposed;
78
132
  const sessionFor = async (request) => {
@@ -112,15 +166,14 @@ export function createJevDevtools(options) {
112
166
  let stop = () => { };
113
167
  const body = new ReadableStream({
114
168
  start(controller) {
115
- let closed = false, dirty = true, cursor, timer;
169
+ let closed = false, dirty = true, cursor;
116
170
  let knownDefinitions = new Set();
117
- const flush = () => {
118
- timer = undefined;
171
+ const flush = (updateFor) => {
119
172
  if (closed || !dirty || (controller.desiredSize ?? 0) <= 0)
120
173
  return;
121
174
  try {
122
- const update = flows.changes(session, cursor, knownDefinitions);
123
- controller.enqueue(encoder.encode(`event: flows\ndata: ${JSON.stringify(update.data)}\n\n`));
175
+ const update = updateFor(session, cursor, knownDefinitions);
176
+ controller.enqueue(update.bytes);
124
177
  // Match the browser's pruning after this delivered order. A clear
125
178
  // coalesced away under backpressure must not discard reader state.
126
179
  knownDefinitions = update.retainedDefinitionIds;
@@ -134,8 +187,8 @@ export function createJevDevtools(options) {
134
187
  }
135
188
  };
136
189
  const wake = () => {
137
- if (!closed && dirty && !timer)
138
- timer = setImmediate(flush);
190
+ if (!closed && dirty && (controller.desiredSize ?? 0) > 0)
191
+ scheduleFlow(flush);
139
192
  };
140
193
  const unsubscribe = flows.subscribe((sessions) => {
141
194
  if (sessions.has(session)) {
@@ -151,9 +204,9 @@ export function createJevDevtools(options) {
151
204
  if (closed)
152
205
  return;
153
206
  closed = true;
154
- knownDefinitions.clear();
207
+ knownDefinitions = new Set();
155
208
  unsubscribe();
156
- clearImmediate(timer);
209
+ unscheduleFlow(flush);
157
210
  clearInterval(heartbeat);
158
211
  request.signal.removeEventListener("abort", stop);
159
212
  streams.delete(stop);
@@ -168,7 +221,7 @@ export function createJevDevtools(options) {
168
221
  if (request.signal.aborted)
169
222
  stop();
170
223
  else
171
- flush();
224
+ flush(encodeFlowUpdate);
172
225
  },
173
226
  pull() {
174
227
  flowPull();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jev-devtools",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.12"
@@ -19,7 +19,7 @@ Inspect the app's package manager/lockfile, React and Node versions, root compon
19
19
 
20
20
  Install `jev-devtools` using the app's existing package manager (npm: `npm install jev-devtools @typesafe-ai/sdk`; pnpm: `pnpm add jev-devtools @typesafe-ai/sdk`; Yarn: `yarn add jev-devtools @typesafe-ai/sdk`; Bun: `bun add jev-devtools @typesafe-ai/sdk`). `@typesafe-ai/sdk` is a peer dependency: keep the app's existing SDK version when it satisfies the peer range. Install in the workspace(s) that actually import it. It is a regular dependency when application modules import it, even though capture/display are development-only. Do not replace the app's framework or upgrade unrelated dependencies to force compatibility.
21
21
 
22
- ### Consumer compatibility (0.5.2)
22
+ ### Consumer compatibility (0.5.4)
23
23
 
24
24
  | Requirement | Contract |
25
25
  | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
@@ -35,7 +35,7 @@ Import application APIs from `jev-devtools` (React and browser-safe capture help
35
35
 
36
36
  ## Optional connected flow API
37
37
 
38
- When asked to build or inspect connected calls, read [references/flows.md](references/flows.md) after the framework recipe. For 0.5.2 it covers requests, server function tasks, runtime request factories, parallel checks, terminal branches, returning conditionals, bounded collections, stateful loops, captured-answer replay, and fixture comparison. It includes sample verification, scope/execution rules, and privacy limits. For an older installation, use its bundled recipe and declarations rather than assuming these operations exist. Preserve existing application policies; do not convert unrelated calls just to install the drawer. Flow execution is server-only and continues even with the inspector disabled.
38
+ When asked to build or inspect connected calls, read [references/flows.md](references/flows.md) after the framework recipe. For 0.5.4 it covers requests, server function tasks, runtime request factories, parallel checks, terminal branches, returning conditionals, bounded collections, stateful loops, captured-answer replay, and fixture comparison. It includes sample verification, scope/execution rules, and privacy limits. For an older installation, use its bundled recipe and declarations rather than assuming these operations exist. Preserve existing application policies; do not convert unrelated calls just to install the drawer. Flow execution is server-only and continues even with the inspector disabled.
39
39
 
40
40
  ## Integration invariants
41
41
 
@@ -1,6 +1,6 @@
1
1
  # Build and inspect a Jev flow
2
2
 
3
- For `jev-devtools` **0.5.2**. Check the repository changelog for publication status when installing from source. Read the installed package version and `lib/flow/index.d.ts` first; online `main` may describe newer APIs. Use this recipe when the user asks to connect multiple Jev requests, inspect branches, run server function steps, or visualize parallel checks, collections, and loops. Adding the inspector alone does not require converting existing calls to flows.
3
+ For `jev-devtools` **0.5.4**. Check the [release notes](https://github.com/diegoag64/jev-devtools/releases/tag/v0.5.4) for verified publication status when installing from source. Read the installed package version and `lib/flow/index.d.ts` first; online `main` may describe newer APIs. Use this recipe when the user asks to connect multiple Jev requests, inspect branches, run server function steps, or visualize parallel checks, collections, and loops. Adding the inspector alone does not require converting existing calls to flows.
4
4
 
5
5
  ## Plan the integration
6
6
 
@@ -9,7 +9,7 @@ For `jev-devtools` **0.5.2**. Check the repository changelog for publication sta
9
9
  3. Import `defineJevFlow` (and `all`, `any`, `not` when needed) from **server-only** `jev-devtools/flow`. Define the graph once at module scope and call `run()` from the existing Node handler/action. In Next.js, mark the module `server-only` and keep the executing route/segment on the Node runtime. No new client provider or root component is needed.
10
10
  4. A named output returns data and does not execute an action. Existing business actions can remain in the application after the returned outcome; use `task()` when the user wants application-owned server work included in the flow. Preserve authorization, idempotency, input validation, and error handling. Cancellation cannot undo side effects.
11
11
 
12
- If importing question helpers as below, install `@typesafe-ai/sdk` as a direct dependency in the server workspace using its package manager. Version 0.5.2 requires SDK `>=0.7.0 <0.8.0` and is verified with 0.7.0; preserve a compatible existing version rather than silently changing SDK semantics. The flow also accepts ordinary SDK question objects.
12
+ If importing question helpers as below, install `@typesafe-ai/sdk` as a direct dependency in the server workspace using its package manager. Version 0.5.4 requires SDK `>=0.7.0 <0.8.0` and is verified with 0.7.0; preserve a compatible existing version rather than silently changing SDK semantics. The flow also accepts ordinary SDK question objects.
13
13
 
14
14
  ## Complete example: parallel checks, one decision, two outcomes
15
15
 
@@ -248,6 +248,16 @@ Use `task()` for application-owned server work and the request-factory overload
248
248
 
249
249
  Choose an item in the collection card, then **View Flow** to expand its full request/function diagram directly underneath on the same canvas. Changing the selector replaces the inline item flow; **Hide Flow** collapses it. Item values use bounded/redacted capture; don't put runtime data in structural titles. For an executable sample, see `cookbooks/rag-passages/README.md` in the repository checkout. Do not promise embedding search or LLM generation in that sample: it uses fixed retrieved passages, synthetic Jev scores and a deterministic excerpt preview.
250
250
 
251
+ For a finite chat feed, consult `cookbooks/twitch-chat-filter/README.md` in the repository. Preserve its 29 messages, concurrency-eight input-order map, one category Choice plus toxic/spoiler Nouls per request, and precedence: toxic ≥ 0.70 hides; category hide wins next; spoiler ≥ 0.70 blurs; remaining category action follows. Quiet reapplication reuses answers without requests. The port uses synthetic fixtures and excludes ongoing Twitch ingestion.
252
+
253
+ For independent candidate scoring followed by sorting, use the repository's `cookbooks/reranking` sample at `/?cookbook=reranking`: one query, a 30-candidate authored retrieval fixture, a bounded map with 12 concurrent Noul requests, and a stable descending sorting task. Preserve retrieval order for ties; these independent probabilities are not Choice confidence or a normalized distribution. BM25, CLERC evaluation and live inference are excluded.
254
+
255
+ The checkout also contains `cookbooks/pre-parsed-extraction` at `/?cookbook=pre-parsed-extraction`: preserve sequential helper requests with map concurrency 1, runtime candidate choices, amount-dependent Noul instructions, strict credit `> 0.5`, and empty/none parsing bypasses. Its phone and decimal-string normalization tasks execute locally against fixed documents and synthetic answers.
256
+
257
+ For selected-argument routing, read `cookbooks/function-calling/README.md` in the repository checkout. The sample at `/?cookbook=function-calling` uses one batched request and deterministic omit/fill tasks. Preserve the distinction between selected-label probability, argument decision products, call minimum decision mass and SDK Choice confidence. Its output is a proposed call; executing a tool requires a separate application action.
258
+
259
+ For sequential repeated assessments, the repository’s `cookbooks/consistency-choices/README.md` uses `map` with concurrency one and 15 requests. Keep its original eight-question rubric, fresh uid per repeat, ordered argmax and maximum-probability gate: below 0.60 abstains; exactly 0.60 accepts. Choice confidence is separate. Fixtures are authored demonstrations, not measured consistency; provider comparisons and live inference are excluded.
260
+
251
261
  ## Returning conditionals: `flow.if()` (0.4.0)
252
262
 
253
263
  Version 0.4.0 includes `flow.if()` and strict numeric `.gt()` comparisons. Use `flow.if(id, { when, then, else }, display?)` for a conditional whose value feeds shared downstream steps. Each arm returns a `FlowExpression`; only the selected arm executes. The result type is the union of both values. An arm may return an earlier expression without adding a task. `output()` and terminal `branch()` arms are rejected. Scope-local refs must not leak; consume the returned conditional reference. Conditionals may nest and run inside parallel lanes or map items.
@@ -276,6 +286,8 @@ The drawer generates a Selected result merge, highlights only its selected incom
276
286
 
277
287
  The repository's `cookbooks/sde-cascade` provides a complete local example at `/?cookbook=sde-cascade`: cheap extraction → runtime field verification → **any per-field probability > 0.70** → stronger extractor or keep cheap → shared comparison. The overall judge is displayed but excluded from the gate. Empty fields receive only `absence_wrong`. All four cases use synthetic probabilities and local extractor fixtures; no real provider performance or original evaluation is reproduced. Do not turn on live extraction just by adding an API key.
278
288
 
289
+ The repository’s `cookbooks/confidence-fallback` is a sample at `/?cookbook=confidence-fallback`. Preserve its 75 SIC groups, API `confidence >= 0.90` gate and parent-division lookup task. Largest class probability is a separate field, not a replacement for confidence or a Score. Verify below/equal/above boundary cases and one request on both routes; fixtures are synthetic and do not evaluate SEC filings.
290
+
279
291
  ## Bounded loops: `flow.loop()` (0.4.0)
280
292
 
281
293
  Use `flow.loop()` to carry JSON-compatible state across bounded iterations. Literal initial state, whole-state references and nested reference templates are supported. Whole-state references and scalar initial values preserve their `S` payload, including generic types. For literal/template state, the gate, iteration, body return and final result use `Resolved<S>`, preserving branded primitives: for `initial: { count: flow.input.get("count") }`, task callbacks receive numeric `state.count` and can return `{ count: state.count + 1 }`. Expression methods belong on builder references, not resolved task values. Syntax:
@@ -307,6 +319,8 @@ Loops may contain one level of map, plus tasks, requests, conditions and paralle
307
319
 
308
320
  `cookbooks/hierarchical-classification` is an executable sample at `/?cookbook=hierarchical-classification`. It follows the official executable algorithm’s **separate concurrent requests per frontier node**, despite introductory prose describing batching. Preserve geometric-mean decision-edge scores, epsilon 1e-9, single-child inference bypass, completed-leaf retention and top-k pruning. Samples are a small authored taxonomy and synthetic probabilities, not a replay of official benchmark data. Verify recovery, no-request paths, an initial leaf and intentional depth exhaustion before considering live execution.
309
321
 
322
+ For guardrail routing, see the repository [LLM guardrails example](https://github.com/diegoag64/jev-devtools/tree/main/cookbooks/guardrails) at `/?cookbook=guardrails`. It screens one input or output message with a single Noul/Score battery, then reuses that assessment in strict/permissive policy tasks. Preserve inclusive action thresholds 0.70/0.85, review 0.35, severity ≥ 2 upgrades of triggered reviews only, and support > block > review > pass precedence. Synthetic fixtures are sample-only; there is no LLM generation or live transport.
323
+
310
324
  ## Replay captures and compare runs (new in 0.5.0)
311
325
 
312
326
  For human examples, see [the flow reference](https://docs.jevtools.dev/api/flow/) and [API glossary](https://docs.jevtools.dev/api/reference/#fixtures-and-comparison). These operations require 0.5.0 or later; inspect the installed declarations before using them with older packages.
@@ -315,3 +329,23 @@ For human examples, see [the flow reference](https://docs.jevtools.dev/api/flow/
315
329
  2. On Node, import `replayClient` from `jev-devtools/flow` and call `await program.run(originalInput, { client: replayClient(fixture) })`. Use the same recipe, input, and stable step IDs. Jev steps use stored answers without a key/network call; tasks and runtime request factories still execute. Avoid uncontrolled effects in test tasks.
316
330
  3. For an explicitly requested live comparison, import `compareWithFixture` and `formatComparison` from `jev-devtools/flow`. Call `await compareWithFixture(program, originalInput, fixture, { client: liveClient, devtools: inspector, tolerance: 0.01 })`, then use `formatComparison(result)` for text. Do not run live comparison merely to verify installation; use stored SDK responses for ordinary verification.
317
331
  4. Tasks execute on both sides. `changed` tracks outcomes, values, decisions, and executed Jev steps; answer drift alone does not set it. Inspect `answers` separately, and apply numeric tolerance only to answer fields. The result's `liveFixture` contains unredacted live responses: review it before saving or sharing. Replay is not rollback, and incomplete captures cannot recreate missing inference results.
332
+
333
+ For the repository sample at `/?cookbook=parallel-questions`, preserve the five batched requests followed by five 13-item maps with `concurrency: 1`. Choice comparisons use maximum probability, independently of confidence; Score is divided by its top level. Keep the authored document and synthetic answers labeled. The sample provides no usage, cost or model timing data; local drawer durations are not inference benchmarks. See `cookbooks/parallel-questions/README.md` in the checkout for all-answer inspection and fixture limits.
334
+
335
+ ## Entity-alignment repository example
336
+
337
+ For a Score-driven matching example, see `cookbooks/entity-alignment/README.md` in the repository checkout and `/?cookbook=entity-alignment` in the local playground. Preserve the source nearest-level rule: Score < 0.5 leaves unlinked, 0.5 ≤ Score < 1.5 requests curator review, and Score ≥ 1.5 proposes sameAs. The three companion Nouls explain field agreement; Score confidence and probabilities are separate evidence, not additional routing gates. The sample uses four attributed pairs and authored synthetic responses, returns proposals without database writes, and is not included in the npm package.
338
+
339
+ ## Citation-checking example
340
+
341
+ When adapting citation checks, preserve the distinction between an absent quoted string (fabricated, zero Jev calls, null confidence) and a null quote (assess the named source section). Normalize and locate quotes in a task, then use `if()` to conditionally assess support with Choice. Route on returned API confidence `>= 0.80`, independently of option probabilities. The repository-only `cookbooks/citation-checking` example demonstrates these boundaries at `/?cookbook=citation-checking` with pinned RFC/citation assets and synthetic responses; its code and fixtures are not shipped in the package.
342
+
343
+ ### Line-search cookbook in the repository
344
+
345
+ For a complete tagging → runtime request → ranking/lookup → branch example, use `cookbooks/line-search/README.md` at `/?cookbook=line-search`. Preserve one Choice + Noul request per search and document-local line IDs. Route only on Noul existence: `>= 0.70` answered, `< 0.35` absent, otherwise partial. Choice probability and API confidence have separate meanings. The pinned document is complete; responses are synthetic and the server accepts fixed sample queries only. The application result exposes every option beyond bounded DevTools previews.
346
+
347
+ ### Date extraction sample
348
+
349
+ The repository’s `cookbooks/date-extraction` runs at `/?cookbook=date-extraction` with synthetic responses only. Preserve the seven Choice questions per role, fixed 2026-07-30 reference date, strict earlier-than-today-minus-31-days missing-year rollover, and Monday-based current/next week conventions. Assemble calendar dates in a task and return ISO strings or `null` across flow boundaries. Route on the minimum available confidence among consumed parts, separately flagging unavailable used confidence for review; label probabilities do not substitute for Choice confidence. The README documents the stricter missing-confidence policy and invalid leap-day handling relative to the source. Verify calendar boundaries, `< 0.60` routing and one request per role before adapting the server transport.
350
+
351
+ For the repository's `cookbooks/consistency-nouls` sample (`/?cookbook=consistency-nouls`), preserve fifteen sequential requests with fourteen Nouls each: `< 0.30` means no, `> 0.70` yes, and both boundaries belong to uncertain. Its tasks compute decisions and descriptive statistics from authored fixture values. Keep deterministic UID variation distinct from independent model draws; no measured consistency or provider comparison is reproduced. The per-run transport records returned models separately from the requested alias; unavailable metadata stays null.