@tangle-network/agent-app 0.47.24 → 0.48.1

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 (47) hide show
  1. package/.claude/skills/eval-architect/SKILL.md +34 -31
  2. package/.claude/skills/improve-conductor/SKILL.md +46 -41
  3. package/.claude/skills/measurement-validation/SKILL.md +40 -34
  4. package/README.md +7 -0
  5. package/dist/DesignCanvas-MWEYD6PR.js +11 -0
  6. package/dist/DesignCanvasEditor-UZ2S6BDD.js +14 -0
  7. package/dist/app-auth/index.js +2 -2
  8. package/dist/chat-routes/index.js +2 -2
  9. package/dist/{chunk-G4CPEJNN.js → chunk-AKMQFCDL.js} +2 -2
  10. package/dist/{chunk-TXM4SS46.js → chunk-CSMJM7YT.js} +2 -2
  11. package/dist/{chunk-3YUI7WNW.js → chunk-IAY2DMKW.js} +30 -3
  12. package/dist/chunk-IAY2DMKW.js.map +1 -0
  13. package/dist/{chunk-54KJ54SY.js → chunk-JAT6SOPK.js} +3 -3
  14. package/dist/{chunk-HUYTN7RI.js → chunk-JF423ASS.js} +5 -5
  15. package/dist/{chunk-WOZSOPUZ.js → chunk-KX2GA3L6.js} +3 -3
  16. package/dist/{chunk-42NBWR4U.js → chunk-SYWEMKZR.js} +2 -2
  17. package/dist/{chunk-QEPLXYOE.js → chunk-VHMZ3KUH.js} +2 -2
  18. package/dist/{chunk-A2XMTCER.js → chunk-WYHFHTSQ.js} +2 -2
  19. package/dist/design-canvas/index.js +2 -2
  20. package/dist/design-canvas-react/engine.js +4 -4
  21. package/dist/design-canvas-react/index.js +7 -7
  22. package/dist/design-canvas-react/lazy.js +1 -1
  23. package/dist/eval-campaign/index.d.ts +4 -4
  24. package/dist/eval-campaign/index.js +3 -2
  25. package/dist/eval-campaign/index.js.map +1 -1
  26. package/dist/eval-campaign/trust-gate.d.ts +10 -33
  27. package/dist/platform/index.js +2 -2
  28. package/dist/public-consultation/index.js +2 -2
  29. package/dist/sequences/index.js +1 -1
  30. package/dist/web/core.d.ts +84 -0
  31. package/dist/web/index.d.ts +3 -84
  32. package/dist/web/index.js +3 -1
  33. package/dist/web/message-groups.d.ts +21 -0
  34. package/package.json +13 -13
  35. package/dist/DesignCanvas-UPRRA4YG.js +0 -11
  36. package/dist/DesignCanvasEditor-GVJO5SMH.js +0 -14
  37. package/dist/chunk-3YUI7WNW.js.map +0 -1
  38. /package/dist/{DesignCanvas-UPRRA4YG.js.map → DesignCanvas-MWEYD6PR.js.map} +0 -0
  39. /package/dist/{DesignCanvasEditor-GVJO5SMH.js.map → DesignCanvasEditor-UZ2S6BDD.js.map} +0 -0
  40. /package/dist/{chunk-G4CPEJNN.js.map → chunk-AKMQFCDL.js.map} +0 -0
  41. /package/dist/{chunk-TXM4SS46.js.map → chunk-CSMJM7YT.js.map} +0 -0
  42. /package/dist/{chunk-54KJ54SY.js.map → chunk-JAT6SOPK.js.map} +0 -0
  43. /package/dist/{chunk-HUYTN7RI.js.map → chunk-JF423ASS.js.map} +0 -0
  44. /package/dist/{chunk-WOZSOPUZ.js.map → chunk-KX2GA3L6.js.map} +0 -0
  45. /package/dist/{chunk-42NBWR4U.js.map → chunk-SYWEMKZR.js.map} +0 -0
  46. /package/dist/{chunk-QEPLXYOE.js.map → chunk-VHMZ3KUH.js.map} +0 -0
  47. /package/dist/{chunk-A2XMTCER.js.map → chunk-WYHFHTSQ.js.map} +0 -0
@@ -1,44 +1,47 @@
1
1
  ---
2
2
  name: eval-architect
3
- description: Build a measurement that scores an agent's REAL deliverable — not a proxy — for a product you've never seen before. Use when scaffolding or repairing the eval an Improve loop optimizes against. Get this wrong and every downstream optimization perfects a fiction.
3
+ description: Build or repair evaluations that score the agent's actual deliverable through the production path, with independent controls and visible missing evidence.
4
4
  ---
5
5
 
6
- # Eval Architect — measure the real deliverable
6
+ # Eval architect
7
7
 
8
- You are building the measurement an improvement loop will optimize against. **The loop optimizes whatever you measure.** If you measure the wrong thing, the loop perfects the wrong thing — confidently, expensively, and invisibly. The measurement is the product. Everything else in the Improve stack is downstream of getting this right.
8
+ Build the measurement around the product's required outcome and actual execution path.
9
+ Reuse maintained Eval contracts and existing product checks before creating another scorer.
9
10
 
10
- This skill is held by the agent that *builds* the eval (often a delegated coding agent). Pair it with `measurement-validation` (the gate that proves your eval is sound before anyone spends money on it).
11
+ ## Locate the deliverable
11
12
 
12
- ## The cardinal question
13
+ 1. Inspect real runs to find the output channel: replies, validated tool calls, persisted artifacts, application state, or rendered UI.
14
+ Score the channel that carries the required outcome.
15
+ 2. Define the completion boundary for the task.
16
+ For work that accumulates across turns, evaluate the completed artifact and retain intermediate evidence needed to explain failures.
17
+ 3. Trace every consumer of the score, including completion checks, optimization selection, and release decisions.
18
+ When the output channel changes, update every affected consumer.
13
19
 
14
- **Where does this agent's deliverable actually land?** Prose in the reply? Validated tool calls? Persisted artifacts (vault docs, DB rows)? A PR? A rendered UI? Find out by inspecting *real runs* — never by assuming it's the chat text.
20
+ ## Build the checks
15
21
 
16
- > Worked failure (legal-agent, this is why the skill exists): the eval scored the assistant's chat prose. A tool-migration moved the deliverable into `submit_proposal` calls + vault docs, leaving the prose empty. Every scorer reading prose silently collapsed to ~0. The loop would have optimized an empty string. The deliverable had *moved* and the measurement didn't follow it.
22
+ 1. Map each requirement to observable evidence and an explicit failure condition.
23
+ Use answer keys when available; otherwise use independently justified constraints, executable checks, or calibrated judgment.
24
+ Keep unsupported requirements and missing evidence visible.
25
+ 2. Establish a simple baseline through the same entrypoint as the candidate.
26
+ Investigate surprising scores instead of assuming either the scorer or the agent caused them.
27
+ 3. Separate training, candidate selection, and final comparison evidence where the improvement claim requires those partitions.
28
+ Preserve scenario identities and shared source-unit mappings across baseline and candidate runs.
29
+ 4. Define critical failure checks separately from aggregate quality.
30
+ A favorable composite must not erase a failure that violates the product's requirements.
31
+ 5. Preserve scorer identity, actual cost receipts, execution failures, and diagnostic artifacts.
32
+ Change `judgeVersion` when an ensemble scorer's configuration changes.
17
33
 
18
- ## Invariant (non-negotiable — violate these and the loop is a slot machine)
34
+ ## Prove the measurement
19
35
 
20
- 1. **Score the produced artifact, not the conversation.** Locate the real output channel and score *that*.
21
- 2. **For accumulating-artifact agents, score the CONVERGED multi-shot artifact, not turn 1.** Most real agents build their deliverable over several turns. Define a convergence criterion (e.g. the artifact stops growing for N shots) and score the converged state.
22
- 3. **A held-out split exists and is never trained on.** No held-out → no honest gate → no trustworthy lift.
23
- 4. **Every requirement has gold the scorer matches against, from real records — never fabricated.** A requirement with no gold means there is nothing to verify; fail loud, do not pass-by-default. A fluent hallucination that produced nothing must score 0, not 0.9.
36
+ Run known positive and negative examples through the complete scoring path.
37
+ Perturb a real deliverable so required behavior improves or regresses, then check that the score detects each change.
38
+ Confirm that absent output and evaluator failure remain distinguishable from measured low quality.
39
+ Report case coverage, detectable failures, uncertainty, and any requirements the evaluation cannot assess.
40
+ A training gain without a final-comparison gain needs diagnosis; it does not identify the cause by itself.
24
41
 
25
- ## Judgment (figure this out per product — the agentic core)
42
+ ## Then consider
26
43
 
27
- - What *is* the deliverable here, and where does it persist? Read the runtime events / tool calls / storage, not the transcript.
28
- - What is the convergence criterion for this agent's artifact? When has it stopped accumulating?
29
- - What gold defines "correct" for each requirement, and where does it come from (real records, never invented figures)?
30
- - Which dimensions matter, and what are their weights? What is the one dimension that, if it regresses, kills the deal regardless of the composite (safety, hallucination, the regulated invariant)?
31
-
32
- ## Self-test (prove the metric works before trusting it)
33
-
34
- - **Baseline sanity:** run it. Is the score non-zero and plausible for a competent agent? A near-zero baseline usually means you're scoring the wrong channel, not that the agent is terrible.
35
- - **The mutation test (the one that catches the empty-string bug):** hand-edit the produced artifact to be *obviously better* and *obviously worse*. Does the score move in the right direction and magnitude? A metric that doesn't move under obvious changes is measuring the wrong thing.
36
- - **Audit EVERY scoring surface together.** Completion, quality, and the optimizer's own scorer all read *something*. When the deliverable's channel moves, all of them that read the old channel silently zero. (Session: completion + quality were fixed; the optimizer's own scorer was missed and only found by tracing. Three surfaces — enumerate them, don't assume one.)
37
-
38
- ## Evolves-by
39
-
40
- When a later optimization shows lift on *training* but none on *held-out*, your eval was overfittable or gameable — add the gap it missed as a new judgment rule. The architect's judgment surface is itself optimized by the meta-eval *"did evals built this way yield real held-out lift, no critical regression?"* See `skill-evolution`.
41
-
42
- ## Fleet as dogfood
43
-
44
- legal / tax / gtm / creative / insurance each put their deliverable in a *different* channel — filings, forms, published copy, rendered artifacts, routed proposals. The skill is general precisely because it forces you to *locate* the channel for the product in front of you rather than hardcode "the reply text."
44
+ | Condition | Skill |
45
+ |---|---|
46
+ | The evaluation path executes and needs calibration or comparison checks | `measurement-validation` with the baseline and control results |
47
+ | The validated measurement supports a candidate search | `surface-evolution` with the target surface, evidence, and resource limits |
@@ -1,45 +1,50 @@
1
1
  ---
2
2
  name: improve-conductor
3
- description: The user-facing controller for the Improve button. Decide whether a request is improvable, translate a dollar budget into a run, read the verdict honestly, and promote or refuse with a reason. Never promise a lift you cannot measure.
3
+ description: Drive a requested optimization from its target and resource limits through measured candidate selection, evidence review, and the product's promotion policy.
4
4
  ---
5
5
 
6
- # Improve Conductor — own the user's trust
7
-
8
- You are the agent the user talks to when they click **Improve**. You do not build the eval or run the loop yourself — you decide *whether* to, *how much* to spend, and *what to tell the user about the result*. The product you are protecting is **trust**, not lift. You would rather say "I could not prove an improvement — here is what another $X buys" than ship noise and call it a win.
9
-
10
- You delegate the building to an agent holding `eval-architect` + `surface-evolution`; you both share `measurement-validation` as the honesty contract.
11
-
12
- ## Invariant (non-negotiable)
13
-
14
- 1. **Never promise or report a lift you cannot measure with valid paired evidence.** Surface the honest verdict: `ship` / `hold` / `need-more-data` / `invalid`. A paired, significant lift is still not shippable until a footprint-matched placebo shows the gain comes from the CONTENT, not from added prompt/mount footprint — the substrate's `neutralizationGate` / `runImprovementLoop({ neutralize })` (`@tangle-network/agent-eval/campaign`). A lift that a neutralized twin reproduces is footprint, not improvement — refuse it. Route this check through `measurement-validation`. "Invalid" (incomplete or unpaired evidence) is a first-class outcome — say it plainly, never paper over it with a survivor-mean number.
15
- 2. **Refuse below the data threshold, and say why** — "I have N real outcomes; I won't optimize below M. Here's how to get to M." A refusal with a reason builds more trust than a fabricated win.
16
- 3. **Route correctly.** Improvable by surface-tuning → dispatch `surface-evolution`. Needs a new capability or architecture → escalate and say so; don't pretend tuning will fix a structural gap.
17
- 4. **No optimization spend before the target is confirmed and the measurement is real.** If there is no improvement infrastructure yet, you do NOT improvise a metric and start spending — you dispatch `eval-bootstrap` to BUILD a validated, externally-grounded harness first. The gate between "build the apparatus" and "spend optimizing" is yours to hold.
18
-
19
- ## Cold start — no infrastructure yet
20
-
21
- The most dangerous request is "improve this" for a product with no eval. The wrong move is to invent a metric and start a loop — you'll perfect a proxy and report a fake win. The right move is a strict two-step you orchestrate:
22
-
23
- 1. **Frame + build (no spend):** confirm with the user *what "better" means* — the thing they'd reject a draft over, tied to a product-value claim — then dispatch `eval-bootstrap` (often a delegated agent-runtime build loop) to construct a harness grounded in **external truth**, exiting only when `measurement-validation` passes. The improver is a *builder* here, not a tuner.
24
- 2. **Then optimize (spend):** only once the harness is validated, dispatch `surface-evolution` against it.
25
-
26
- Never let the user believe step 2 happened when only a toy of step 1 did. If you can't yet build a real measurement (no grounding, target unclear), say so and ask for what you need — that's the honest move, not a loop against an invented number.
27
-
28
- ## Judgment (figure this out per request)
29
-
30
- - Is this a surface-tuning problem or an architectural one? (If the agent literally cannot do the task, no prompt edit fixes it.)
31
- - Translate the user's dollars into a run: more spend = wider candidate search + more reps = tighter CI + higher chance of clearing the gate. $0.20 ≈ one quick generation on a couple scenarios; $50 ≈ multi-generation search with a held-out gate that can actually reach significance.
32
- - When to stop: threshold met, plateaued, or budget exhausted — and report which.
33
-
34
- ## Self-test
35
-
36
- - **Before spending,** you can state out loud: the metric, its variance, the threshold, the held-out set, and what this budget buys. If you can't, you're not ready to charge for the click.
37
- - **After,** you report the gated lift with its CI and the decision's *reason*. If the run came back `invalid` (a cell errored, evidence unpaired), you tell the user that and offer the re-run — you do not quote the broken number.
38
-
39
- ## Evolves-by
40
-
41
- User accept/reject of promotions; spend→lift efficiency; the rate of `invalid` runs. A rising invalid rate is a signal the measurement or the infra needs hardening — route it back to `measurement-validation` / `eval-architect`, don't absorb it silently. See `skill-evolution`.
42
-
43
- ## Why this is calibrated, not timid
44
-
45
- A naive Improve button maximizes the displayed number and tells the user "improved +47%". The disciplined one, faced with the same +47, checks the evidence, finds it unpaired, and says "I found a promising candidate but can't yet prove it beats baseline — $X more will confirm it." The second one is the one people pay for twice.
6
+ # Improve conductor
7
+
8
+ Own the requested outcome, actual spend, and the decision supported by the evidence.
9
+ Distinguish building an evaluation, searching for candidates, and proving an improvement.
10
+
11
+ ## Establish the work
12
+
13
+ 1. Recover the target, required outcome, existing evaluation, and authorization from the user's request and product context.
14
+ Ask only for consequential information that the available evidence cannot resolve.
15
+ 2. Inspect the current baseline and the system's failure cases.
16
+ Choose a surface change, capability change, or architecture experiment according to the observed limitation.
17
+ 3. Check that the evaluation can detect the required behavior through the production entrypoint.
18
+ If it cannot, build that measurement before making improvement claims.
19
+ Record measurement work as measurement work, including its actual cost.
20
+ 4. Set resource limits and a stopping rule for the selected experiment.
21
+ Estimate cost from the actual execution path and retain uncertainty about additional calls, retries, and candidate evaluations.
22
+ More spend does not guarantee a useful candidate or a conclusive result.
23
+
24
+ ## Run and decide
25
+
26
+ 1. Use the maintained optimization method and execution path already available to the product.
27
+ Preserve training, selection, and final-comparison boundaries along with the registered observation units.
28
+ 2. Retain candidate artifacts, scorer identity, paired raw evidence, failures, and complete attempt costs.
29
+ Missing usage remains an explicit accounting gap.
30
+ 3. Read the shared deciding statistic and the producer's actual verdict.
31
+ Distinguish missing evidence, a measured failure, an inconclusive comparison, and a result that meets the release policy.
32
+ 4. Investigate surprising gains and null results with controls that isolate the suspected mechanism.
33
+ Use a footprint control when the claim concerns content versus added context; it is not a universal release prerequisite.
34
+ 5. Promote only through the product's authorized decision path after its required checks pass.
35
+ A promising exploratory result can justify another scoped experiment without establishing an improvement.
36
+
37
+ ## Report
38
+
39
+ State what changed, the baseline comparison, deciding interval, independent-unit count, actual costs, and the verdict's reasons.
40
+ Explain whether the run stopped because it reached its registered criterion, exhausted its budget, or could not capture valid evidence.
41
+ A proposed follow-up must state which uncertainty it could resolve; extra budget alone does not promise confirmation.
42
+
43
+ ## Then consider
44
+
45
+ | Condition | Skill |
46
+ |---|---|
47
+ | The product has no usable evaluation path | `eval-bootstrap` with the observed deliverable and missing checks |
48
+ | A scorer or output-channel defect prevents assessment | `eval-architect` with the failed case and execution evidence |
49
+ | Existing measurements need calibration or comparison review | `measurement-validation` with the baseline and retained results |
50
+ | The supported next experiment changes an existing surface | `surface-evolution` with the target, acceptance criteria, and resource limits |
@@ -1,38 +1,44 @@
1
1
  ---
2
2
  name: measurement-validation
3
- description: Prove a measurement is sound BEFORE spending money optimizing against it. The gate that decides whether an Improve run is allowed to start, and whether its result is allowed to be believed. Refuse metrics whose noise exceeds the effect, that have no held-out split, or whose evidence is incomplete.
3
+ description: Check whether an evaluation supports an optimization or release decision through calibrated scoring, independent comparison units, and complete paired evidence.
4
4
  ---
5
5
 
6
- # Measurement Validation — earn the right to optimize
7
-
8
- Optimization is only as trustworthy as the measurement under it. This skill is the gate on both ends: **before** a paid run (is this metric allowed to be optimized?) and **after** (is this result allowed to be believed?). It is the difference between an Improve button that is a product and one that is a slot machine.
9
-
10
- Held by both the orchestrator (`improve-conductor`) and the builder (`eval-architect`). It is the shared honesty contract.
11
-
12
- ## Invariant (non-negotiable)
13
-
14
- 1. **Refuse to optimize if CV(metric) > the target delta.** If the run-to-run noise is bigger than the effect you're paying to move, the metric *cannot* validate the change — raise reps or fix the metric first. Do not tune against noise.
15
- 2. **Refuse to report a lift over INCOMPLETE or UNPAIRED evidence.** Every held-out scenario must have a non-errored cell on *both* the baseline and the candidate side. Below the paired-n floor (≥3), the run is **invalid**, not a verdict. A lift computed over survivors is worse than no number.
16
- > **Enforced by** `trustVerdicts` from `@tangle-network/agent-app/eval-campaign` (rater-trust dimension: IRR floor + per-item rater spread, within-item never pooled, + survivor floor, each failure named in `trustReasons`), composed with the agent-eval statistical gates from `@tangle-network/agent-eval/campaign`: `powerPreflight` (before-gate for Invariant 1 — refuse to greenlight spend when the metric is underpowered vs the target delta), `heldOutGate` / `heldoutSignificance` (paired-bootstrap CI over the held-out split — the after-gate that would have caught the +47 by refusing an unpaired lift), and `neutralizationGate` + `neutralizeText` (footprint-matched placebo gate — a held-out lift that does not survive `neutralizeText` came from added prompt/mount FOOTPRINT, not content, and must not be believed).
17
- 3. **Every metric ties to a product-value claim** — "if this number moves, *this* user-visible outcome moves with it." No claim → it's a proxy → don't optimize it.
18
- 4. **Below the data threshold of real outcomes, refuse to optimize** — state N and say why. You cannot improve what you have not yet observed enough of.
19
-
20
- > Worked failures (this is why the skill exists):
21
- > - **Noise read as signal:** ~6 optimization rounds were burned chasing ±0.15 run-to-run swings as if they were real. The metric's variance was 3× any prompt delta — every conclusion was unprovable. The bug was the *measurement*, not the model.
22
- > - **A lift that was a lie:** a GEPA run reported `heldOutLift = +47`. Reading the actual cells: 2 of 4 held-out cells had errored, so "baseline" was *delaware alone* (42) and "winner" was *saas alone* (89) — two different personas. The +47 was differencing unlike cells. The gate correctly held (0 valid pairs), but the headline number a naive promoter would have shipped was fiction.
23
-
24
- ## Judgment (figure this out per metric)
25
-
26
- - How many reps establish variance for *this* metric? (Noisy targets need 5+, converged-artifact metrics fewer.)
27
- - Is an observed "noisy" result model variance, or a measurement smell? **Default: suspect the metric** until its CI is shown tighter than the effect.
28
- - Where might this metric diverge from real value (the Goodhart risk specific to this product)?
29
-
30
- ## Self-test
31
-
32
- - Report **mean ± 95% CI over K converged rollouts.** Show CI < target delta *before* greenlighting spend. If you can't, you haven't earned the right to optimize yet.
33
- - Confirm the held-out split is disjoint from training and large enough that the paired-n floor survives an errored cell.
34
- - **Verify against ground truth, never the summary.** Read the actual cells / artifacts, not the provenance headline. (The +47 above was sitting right there in the summary; only the cells revealed it was unpaired. A green build-hook is not a successful build; a typechecking harness is not a running one; a reported lift is not a measured lift.)
35
-
36
- ## Evolves-by
37
-
38
- Track promotions that passed validation but regressed in production → that's a missed variance source or an unguarded dimension; strengthen the preflight. The validation bar itself is a surface that tightens from its own misses. See `skill-evolution`.
6
+ # Measurement validation
7
+
8
+ Check the measurement through the product's actual evaluation path before interpreting an optimization result.
9
+ Separate exploratory evidence from evidence that satisfies a release policy.
10
+
11
+ ## Establish the measurement
12
+
13
+ 1. State the user outcome, target population, independent comparison unit, and smallest useful effect.
14
+ Record the scoring revision, selection procedure, resource limits, and stopping rule before comparing candidates.
15
+ 2. Run a simple baseline and independent positive and negative controls through the scorer.
16
+ Use `auditEvaluator` from `@tangle-network/agent-eval/meta-eval` when the scorer needs an accuracy audit.
17
+ Report false acceptances, false rejections, and missing observations against the product's requirements.
18
+ 3. Check repeatability and choose enough independent units to resolve the intended effect.
19
+ Use `powerPreflight` to guide sampling and budget decisions.
20
+ An underpowered result can guide exploration; it cannot certify an improvement.
21
+ 4. Keep final comparison cases separate from training and candidate selection.
22
+ Register shared source units when several scenarios come from the same source.
23
+ Additional repetitions do not create new independent units.
24
+
25
+ ## Assess the result
26
+
27
+ 1. Inspect raw baseline and candidate cells, judge failures, pairing, and the registered unit mapping.
28
+ Missing or asymmetric evidence must remain visible and must block a promotion claim.
29
+ 2. Use Eval's `heldOutGate` or `heldoutSignificance` for the shared promotion decision.
30
+ Report its deciding interval, independent-unit count, eligibility, and vetoes.
31
+ The bootstrap diagnostic may differ from the deciding interval for binary outcomes.
32
+ 3. Use App's `trustVerdicts` to check rater agreement and surviving-judge coverage when an ensemble is present.
33
+ Agreement alone does not establish accuracy against independent controls or authorize release.
34
+ 4. Investigate null or surprising results before assigning a cause.
35
+ A control intervention supports a causal explanation only when it isolates the proposed mechanism.
36
+ 5. Retain actual costs, failures, exclusions, and uncertainty with the result.
37
+ Apply the product's release policy to the deciding evidence and record what the evidence cannot establish.
38
+
39
+ ## Then consider
40
+
41
+ | Condition | Skill |
42
+ |---|---|
43
+ | Scoring or case coverage cannot test the required outcome | `eval-architect` with the failed controls and missing coverage |
44
+ | Measurement supports a scoped optimization experiment | `improve-conductor` with the baseline, registered comparison, and resource limits |
package/README.md CHANGED
@@ -151,6 +151,13 @@ The **complete, always-current reference** — every published subpath, its expo
151
151
  - [`/runtime`](src/runtime) — the bounded tool loop; the same loop drives a sandbox agent, a Worker, or an in-browser copilot behind one `streamTurn` seam.
152
152
  - [`/trace`](src/trace) — bounded stage timing, turn waterfalls, and mission traces over an injected telemetry carrier.
153
153
 
154
+ **Evaluate changes**
155
+
156
+ - [`/eval-campaign`](docs/api/eval-campaign.md) composes Eval campaigns, optimization methods, and ensemble judges.
157
+ Change `judgeVersion` when the scorer configuration changes.
158
+ Record paid judge calls through the supplied `costLedger`, `costPhase`, and `costTags`.
159
+ `trustVerdicts` checks agreement and coverage; use Eval's `auditEvaluator` to test accuracy against independent controls.
160
+
154
161
  **The server chat vertical** ([`examples/chat-app.md`](./examples/chat-app.md))
155
162
  - [`/chat-routes`](src/chat-routes) — `createChatTurnRoutes`: auth → store → streaming turn with buffered replay → uploads → sidecar question answering, assembled. Plus `runDetachedTurn` for autonomous turns a browser can still watch live.
156
163
  - [`/chat-store`](src/chat-store) · [`/interactions`](src/interactions) · [`/plans`](src/plans) — persistence, human-in-the-loop asks, and the durable plan projection.
@@ -0,0 +1,11 @@
1
+ import {
2
+ DesignCanvas
3
+ } from "./chunk-JAT6SOPK.js";
4
+ import "./chunk-AKMQFCDL.js";
5
+ import "./chunk-WYHFHTSQ.js";
6
+ import "./chunk-IAY2DMKW.js";
7
+ import "./chunk-TXD5HXLE.js";
8
+ export {
9
+ DesignCanvas
10
+ };
11
+ //# sourceMappingURL=DesignCanvas-MWEYD6PR.js.map
@@ -0,0 +1,14 @@
1
+ import {
2
+ DesignCanvasEditor
3
+ } from "./chunk-JF423ASS.js";
4
+ import "./chunk-JAT6SOPK.js";
5
+ import "./chunk-Q2QKEV6J.js";
6
+ import "./chunk-VHMZ3KUH.js";
7
+ import "./chunk-AKMQFCDL.js";
8
+ import "./chunk-WYHFHTSQ.js";
9
+ import "./chunk-IAY2DMKW.js";
10
+ import "./chunk-TXD5HXLE.js";
11
+ export {
12
+ DesignCanvasEditor
13
+ };
14
+ //# sourceMappingURL=DesignCanvasEditor-UZ2S6BDD.js.map
@@ -2,9 +2,9 @@ import {
2
2
  createAuthGuard,
3
3
  createBetterAuthSessionCookieMinter,
4
4
  createTangleSsoHandlers
5
- } from "../chunk-TXM4SS46.js";
5
+ } from "../chunk-CSMJM7YT.js";
6
6
  import "../chunk-JML7WKWU.js";
7
- import "../chunk-3YUI7WNW.js";
7
+ import "../chunk-IAY2DMKW.js";
8
8
  import "../chunk-TXD5HXLE.js";
9
9
 
10
10
  // src/app-auth/index.ts
@@ -61,9 +61,9 @@ import {
61
61
  rowIdOf,
62
62
  storeSupportsDraftPersistence,
63
63
  streamChatRouteAsSandboxEvents
64
- } from "../chunk-42NBWR4U.js";
64
+ } from "../chunk-SYWEMKZR.js";
65
65
  import "../chunk-ZSSCVT6H.js";
66
- import "../chunk-3YUI7WNW.js";
66
+ import "../chunk-IAY2DMKW.js";
67
67
  import {
68
68
  attachmentInputToPart,
69
69
  attachmentKindForMime,
@@ -3,7 +3,7 @@ import {
3
3
  applySceneOperations,
4
4
  requireElement,
5
5
  requirePage
6
- } from "./chunk-A2XMTCER.js";
6
+ } from "./chunk-WYHFHTSQ.js";
7
7
 
8
8
  // src/design-canvas-react/engine/command-stack.ts
9
9
  var SCENE_COMMAND_HISTORY_LIMIT = 200;
@@ -601,4 +601,4 @@ export {
601
601
  bindSlotCommand,
602
602
  setDocumentTitleCommand
603
603
  };
604
- //# sourceMappingURL=chunk-G4CPEJNN.js.map
604
+ //# sourceMappingURL=chunk-AKMQFCDL.js.map
@@ -5,7 +5,7 @@ import {
5
5
  clearCookieHeader,
6
6
  readCookieValue,
7
7
  serializeCookie
8
- } from "./chunk-3YUI7WNW.js";
8
+ } from "./chunk-IAY2DMKW.js";
9
9
 
10
10
  // src/platform/sso.ts
11
11
  var DEFAULT_STATE_TTL_SECONDS = 600;
@@ -374,4 +374,4 @@ export {
374
374
  createAdminGuard,
375
375
  assertBillableBalance
376
376
  };
377
- //# sourceMappingURL=chunk-TXM4SS46.js.map
377
+ //# sourceMappingURL=chunk-CSMJM7YT.js.map
@@ -176,7 +176,7 @@ function withFreeRouteLimit(options, handler) {
176
176
  };
177
177
  }
178
178
 
179
- // src/web/index.ts
179
+ // src/web/core.ts
180
180
  async function parseJsonObjectBody(request) {
181
181
  let raw;
182
182
  try {
@@ -262,6 +262,32 @@ function assertMediaUrl(url, what = "media url") {
262
262
  throw new Error(`${what} must be http(s) or a rooted /api/ path (${shown})`);
263
263
  }
264
264
 
265
+ // src/web/message-groups.ts
266
+ function groupConversationMessages(items = []) {
267
+ let speaker = null;
268
+ let conversation;
269
+ let groupId;
270
+ return items.map((item, index) => {
271
+ if (item.conversationId !== void 0 && item.conversationId !== conversation) {
272
+ conversation = item.conversationId;
273
+ speaker = null;
274
+ groupId = void 0;
275
+ }
276
+ const role = item.kind === "thinking" ? "assistant" : !item.kind || item.kind === "message" ? item.role : void 0;
277
+ if (role === "user") {
278
+ speaker = null;
279
+ groupId = void 0;
280
+ return { ...item, isContinuation: false };
281
+ }
282
+ if (role !== "assistant") return item;
283
+ const current = item.speakerId ?? "assistant";
284
+ const isContinuation = speaker === current;
285
+ if (!isContinuation) groupId = String(item.id ?? `assistant-${index}`);
286
+ speaker = current;
287
+ return { ...item, isContinuation, groupId };
288
+ });
289
+ }
290
+
265
291
  export {
266
292
  checkRateLimit,
267
293
  FREE_ROUTE_BUDGETS,
@@ -278,6 +304,7 @@ export {
278
304
  readCookieValue,
279
305
  STANDARD_SECURITY_HEADERS,
280
306
  addSecurityHeaders,
281
- assertMediaUrl
307
+ assertMediaUrl,
308
+ groupConversationMessages
282
309
  };
283
- //# sourceMappingURL=chunk-3YUI7WNW.js.map
310
+ //# sourceMappingURL=chunk-IAY2DMKW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web/rate-limit.ts","../src/web/free-route-limit.ts","../src/web/core.ts","../src/web/message-groups.ts"],"sourcesContent":["/**\n * The KV-backed sliding-window rate limiter. Lives in its own leaf so policy\n * layers (`./free-route-limit`) can build on it without importing the web\n * barrel and creating an import cycle. Re-exported from `./index`, which is the\n * published surface.\n */\n\n/** Minimal KV contract (Cloudflare `KVNamespace` satisfies it structurally). */\nexport interface KvLike {\n get(key: string): Promise<string | null>\n put(key: string, value: string, options?: { expirationTtl?: number }): Promise<void>\n}\n\n/** Describe the outcome of a rate limit check including allowance, remaining count, and reset time */\nexport interface RateLimitResult {\n allowed: boolean\n remaining: number\n resetAt: number\n}\n\n/** KV-backed sliding-window rate limit. Stores recent timestamps per key,\n * prunes the window, allows until `limit` is hit.\n *\n * Read-modify-write is best-effort, NOT atomic: KV has no compare-and-swap, so\n * two requests racing on the same key can each read the same pre-state and both\n * write — a concurrent burst can momentarily admit up to one extra request per\n * racing writer. This is acceptable for coarse abuse limiting; it is NOT a hard\n * quota gate.\n *\n * Fail-CLOSED on unreadable state: corrupt/non-array KV (a poisoned or\n * truncated value) is treated as a full window, so a tampered key cannot reset\n * the count and bypass the limiter. A bare `JSON.parse` here would throw and\n * abort the request handler, silently disabling the limit (fail-open). */\nexport async function checkRateLimit(kv: KvLike, key: string, limit: number, windowSeconds: number): Promise<RateLimitResult> {\n const now = Math.floor(Date.now() / 1000)\n const windowStart = now - windowSeconds\n const kvKey = `rl:${key}`\n const raw = await kv.get(kvKey)\n const parsed = parseRateLimitState(raw)\n if (parsed === POISONED_STATE) {\n // Unreadable state (parse threw or value is not a JSON array): deny rather\n // than reset the window to empty. A poisoned key must not become a bypass.\n return { allowed: false, remaining: 0, resetAt: now + windowSeconds }\n }\n const valid = parsed.filter((t) => t > windowStart)\n if (valid.length >= limit) return { allowed: false, remaining: 0, resetAt: (valid[0] ?? now) + windowSeconds }\n valid.push(now)\n await kv.put(kvKey, JSON.stringify(valid), { expirationTtl: windowSeconds * 2 })\n return { allowed: true, remaining: limit - valid.length, resetAt: now + windowSeconds }\n}\n\n/** Sentinel returned by `parseRateLimitState` when the stored value cannot be\n * read as a timestamp array — distinct from an empty window so the limiter can\n * fail closed instead of treating corruption as a fresh window. */\nconst POISONED_STATE = Symbol('rate-limit-poisoned-state')\n\n/** Parse stored rate-limit state into a timestamp array. Absent state is a\n * fresh (empty) window. A value that fails to parse, or parses to a non-array,\n * returns `POISONED_STATE`; numeric junk inside a valid array is dropped. */\nfunction parseRateLimitState(raw: string | null): number[] | typeof POISONED_STATE {\n if (raw === null) return []\n let parsed: unknown\n try {\n parsed = JSON.parse(raw)\n } catch {\n return POISONED_STATE\n }\n if (!Array.isArray(parsed)) return POISONED_STATE\n return parsed.filter((t): t is number => typeof t === 'number' && Number.isFinite(t))\n}\n","/**\n * Rate-limit policy for FREE routes: authenticated, unmetered, compute-bearing\n * endpoints.\n *\n * Billing meters the chat turn. Everything deterministic a product adds beside\n * it — a planning recompute fired on every slider drag, a redline diff, a\n * deadline computation — runs unmetered, so nothing bounds how much worker CPU\n * one authenticated caller can spend. `checkRateLimit` is the primitive; this\n * is the policy around it, so a route declares a cost class instead of each one\n * inventing a key format, a budget, and a denial response.\n *\n * Three rules this enforces that a hand-rolled call site usually does not:\n *\n * 1. **Identity is required.** A missing subject is denied, never pooled into a\n * shared bucket and never quietly keyed by IP — a shared bucket makes one\n * heavy user throttle everyone, and an IP key makes one NAT do the same.\n * 2. **A limiter that throws denies.** KV `get`/`put` failures are transport\n * failures; treating them as \"allowed\" converts a KV outage into an\n * unbounded-compute hole, which is exactly when the limiter matters most.\n * 3. **The denial is a typed, correctable outcome** — reason, HTTP status and\n * `Retry-After` — so the caller answers the client rather than failing\n * opaquely or, worse, continuing.\n *\n * ```ts\n * export const action = withFreeRouteLimit(\n * {\n * route: 'tax.plan.recompute',\n * costClass: 'interactive',\n * kv: ({ context }) => context.cloudflare.env.RATE_LIMIT_KV,\n * identify: async ({ request }) => {\n * const session = await requireSession(request)\n * return { subject: session.userId, workspace: session.workspaceId }\n * },\n * },\n * async ({ request }) => Response.json(recompute(await request.json())),\n * )\n * ```\n */\n\nimport { checkRateLimit, type KvLike, type RateLimitResult } from './rate-limit'\n\n/** Requests allowed per identity inside a sliding window. */\nexport interface RateLimitBudget {\n limit: number\n windowSeconds: number\n}\n\n/**\n * How expensive one call is, which is what picks the default budget.\n *\n * - `interactive` — a control the user drags or types into, recomputed per\n * tick. Sized for sustained two-per-second interaction, so a real slider\n * never trips it and a script still stops.\n * - `compute` — a derivation the user triggers deliberately (recalculate a\n * plan, diff a document, compute a deadline set). The default.\n * - `heavy` — whole-document or whole-matter work measured in seconds of CPU.\n */\nexport type FreeRouteClass = 'interactive' | 'compute' | 'heavy'\n\n/** Default budget per cost class. A product overrides with an explicit\n * `budget` when it has measured its own route. */\nexport const FREE_ROUTE_BUDGETS: Readonly<Record<FreeRouteClass, RateLimitBudget>> = Object.freeze({\n interactive: Object.freeze({ limit: 120, windowSeconds: 60 }),\n compute: Object.freeze({ limit: 30, windowSeconds: 60 }),\n heavy: Object.freeze({ limit: 10, windowSeconds: 300 }),\n})\n\n/** A workspace's shared ceiling defaults to this many times one member's, so a\n * multi-seat tenant is bounded without a single active member tripping it. */\nexport const WORKSPACE_BUDGET_MULTIPLIER = 5\n\n/** Why a call was refused. Every value is a REFUSAL — there is no reason code\n * that lets the handler run. */\nexport type FreeRouteDenialReason =\n /** No authenticated subject. Not correctable by waiting. */\n | 'unidentified'\n /** The window is full for this subject or workspace. */\n | 'rate-limited'\n /** The limiter itself failed (KV read/write threw). Denied, not passed. */\n | 'limiter-unavailable'\n\n/** Which window refused the call. `null` when no window was consulted. */\nexport type FreeRouteDimension = 'subject' | 'workspace' | null\n\n/**\n * A correctable refusal: the caller maps `status` + `retryAfterSeconds` onto a\n * response (see {@link freeRouteLimitResponse}) so the client learns what to do\n * next. Carrying the limiter's own failure in `cause` keeps a KV outage\n * diagnosable instead of collapsing into a generic 429.\n */\nexport class FreeRouteLimitError extends Error {\n readonly reason: FreeRouteDenialReason\n readonly status: 401 | 429 | 503\n readonly retryAfterSeconds: number\n readonly dimension: FreeRouteDimension\n readonly route: string\n\n constructor(init: {\n reason: FreeRouteDenialReason\n status: 401 | 429 | 503\n retryAfterSeconds: number\n dimension: FreeRouteDimension\n route: string\n message: string\n cause?: unknown\n }) {\n super(init.message, init.cause === undefined ? undefined : { cause: init.cause })\n this.name = 'FreeRouteLimitError'\n this.reason = init.reason\n this.status = init.status\n this.retryAfterSeconds = init.retryAfterSeconds\n this.dimension = init.dimension\n this.route = init.route\n }\n}\n\n/** What remains of the binding window after an allowed call. */\nexport interface FreeRouteAllowance {\n /** Remaining calls in whichever window is tightest. */\n remaining: number\n /** Unix seconds at which that window resets. */\n resetAt: number\n /** The budget that window enforces. */\n budget: RateLimitBudget\n /** Which window is tightest — `subject` unless a workspace ceiling binds. */\n dimension: Exclude<FreeRouteDimension, null>\n}\n\n/** Typed outcome at the route boundary: an allowance, or a refusal that says\n * how to correct it. There is no third state and no silent pass. */\nexport type FreeRouteLimitOutcome =\n | { succeeded: true; value: FreeRouteAllowance }\n | { succeeded: false; error: FreeRouteLimitError }\n\nexport interface FreeRouteLimitInput {\n kv: KvLike\n /** Stable route name. Part of the key, so two routes never share a window. */\n route: string\n /** The authenticated subject (user id). Absent or blank ⇒ denied. */\n subject: string | null | undefined\n /** Tenant the call belongs to. When present, a second, wider window bounds\n * the workspace as a whole. */\n workspace?: string | null\n /** Cost class picking the default budget. Ignored when `budget` is given.\n * Default `'compute'`. */\n costClass?: FreeRouteClass\n budget?: RateLimitBudget\n /** Workspace ceiling. Defaults to the subject budget's limit multiplied by\n * {@link WORKSPACE_BUDGET_MULTIPLIER} over the same window. */\n workspaceBudget?: RateLimitBudget\n}\n\n/** Key components are percent-encoded so a route name or subject containing the\n * separator cannot be crafted to collide with another caller's window. */\nfunction limitKey(route: string, dimension: string, id: string): string {\n return `free:${encodeURIComponent(route)}:${dimension}:${encodeURIComponent(id)}`\n}\n\nfunction assertBudget(budget: RateLimitBudget, label: string): void {\n const valid =\n Number.isInteger(budget.limit) &&\n budget.limit >= 1 &&\n Number.isInteger(budget.windowSeconds) &&\n budget.windowSeconds >= 1\n if (!valid) {\n throw new Error(\n `${label} must be positive integers (got limit=${budget.limit}, windowSeconds=${budget.windowSeconds})`,\n )\n }\n}\n\nfunction secondsUntil(resetAt: number): number {\n return Math.max(1, resetAt - Math.floor(Date.now() / 1000))\n}\n\n/**\n * Run one window. A throw from KV becomes a `limiter-unavailable` refusal\n * rather than propagating — the caller must be able to answer 503 with a\n * `Retry-After` instead of the handler running or the route 500ing opaquely.\n */\nasync function runWindow(\n kv: KvLike,\n route: string,\n dimension: Exclude<FreeRouteDimension, null>,\n id: string,\n budget: RateLimitBudget,\n): Promise<{ succeeded: true; value: RateLimitResult } | { succeeded: false; error: FreeRouteLimitError }> {\n try {\n const value = await checkRateLimit(kv, limitKey(route, dimension, id), budget.limit, budget.windowSeconds)\n return { succeeded: true, value }\n } catch (cause) {\n return {\n succeeded: false,\n error: new FreeRouteLimitError({\n reason: 'limiter-unavailable',\n status: 503,\n retryAfterSeconds: Math.min(budget.windowSeconds, 30),\n dimension,\n route,\n message: `Rate limiter unavailable for route '${route}' — the request is refused rather than admitted unmetered`,\n cause,\n }),\n }\n }\n}\n\n/**\n * Decide whether one authenticated, unmetered call may run.\n *\n * Checks the subject's window first, then the workspace ceiling when a\n * workspace is supplied; the tighter of the two is what the allowance reports.\n * Every failure path returns a refusal — none returns an allowance.\n */\nexport async function checkFreeRouteLimit(input: FreeRouteLimitInput): Promise<FreeRouteLimitOutcome> {\n const budget = input.budget ?? FREE_ROUTE_BUDGETS[input.costClass ?? 'compute']\n assertBudget(budget, `Free-route budget for '${input.route}'`)\n const workspaceBudget =\n input.workspaceBudget ?? { limit: budget.limit * WORKSPACE_BUDGET_MULTIPLIER, windowSeconds: budget.windowSeconds }\n if (input.workspace) assertBudget(workspaceBudget, `Free-route workspace budget for '${input.route}'`)\n\n const subject = input.subject?.trim()\n if (!subject) {\n return {\n succeeded: false,\n error: new FreeRouteLimitError({\n reason: 'unidentified',\n status: 401,\n // Not correctable by waiting: the caller must authenticate.\n retryAfterSeconds: 0,\n dimension: null,\n route: input.route,\n message: `Route '${input.route}' is authenticated-only — an unidentified caller cannot be rate limited, so it is refused`,\n }),\n }\n }\n\n const subjectWindow = await runWindow(input.kv, input.route, 'subject', subject, budget)\n if (!subjectWindow.succeeded) return subjectWindow\n if (!subjectWindow.value.allowed) {\n return { succeeded: false, error: rateLimited(input.route, 'subject', subjectWindow.value) }\n }\n\n let tightest: { result: RateLimitResult; budget: RateLimitBudget; dimension: Exclude<FreeRouteDimension, null> } = {\n result: subjectWindow.value,\n budget,\n dimension: 'subject',\n }\n\n if (input.workspace) {\n const workspaceWindow = await runWindow(input.kv, input.route, 'workspace', input.workspace, workspaceBudget)\n if (!workspaceWindow.succeeded) return workspaceWindow\n if (!workspaceWindow.value.allowed) {\n return { succeeded: false, error: rateLimited(input.route, 'workspace', workspaceWindow.value) }\n }\n if (workspaceWindow.value.remaining < tightest.result.remaining) {\n tightest = { result: workspaceWindow.value, budget: workspaceBudget, dimension: 'workspace' }\n }\n }\n\n return {\n succeeded: true,\n value: {\n remaining: tightest.result.remaining,\n resetAt: tightest.result.resetAt,\n budget: tightest.budget,\n dimension: tightest.dimension,\n },\n }\n}\n\nfunction rateLimited(\n route: string,\n dimension: Exclude<FreeRouteDimension, null>,\n result: RateLimitResult,\n): FreeRouteLimitError {\n const retryAfterSeconds = secondsUntil(result.resetAt)\n return new FreeRouteLimitError({\n reason: 'rate-limited',\n status: 429,\n retryAfterSeconds,\n dimension,\n route,\n message: `Route '${route}' rate limit reached for this ${dimension}; retry in ${retryAfterSeconds}s`,\n })\n}\n\nexport interface FreeRouteLimitResponseOptions {\n /** Extra response headers (a product's security headers, CORS). */\n headers?: Record<string, string>\n /** Client-facing message. Defaults to the error's own message. */\n message?: string\n}\n\n/** The refusal as an HTTP response: the error's status, a JSON body carrying\n * the machine-readable `reason`, and `Retry-After` when waiting can fix it. */\nexport function freeRouteLimitResponse(\n error: FreeRouteLimitError,\n options: FreeRouteLimitResponseOptions = {},\n): Response {\n const headers = new Headers(options.headers)\n headers.set('Content-Type', 'application/json')\n if (error.retryAfterSeconds > 0) headers.set('Retry-After', String(error.retryAfterSeconds))\n return new Response(\n JSON.stringify({\n error: options.message ?? error.message,\n reason: error.reason,\n retryAfterSeconds: error.retryAfterSeconds,\n }),\n { status: error.status, headers },\n )\n}\n\n/** The authenticated caller behind one request. */\nexport interface FreeRouteIdentity {\n subject: string\n workspace?: string | null\n}\n\nexport interface WithFreeRouteLimitOptions<TArgs> {\n /** Stable route name; part of the key. */\n route: string\n costClass?: FreeRouteClass\n budget?: RateLimitBudget\n workspaceBudget?: RateLimitBudget\n /** The KV binding for this request. */\n kv: (args: TArgs) => KvLike\n /**\n * Resolve the authenticated caller. Return `null` for an unauthenticated\n * request — the wrapper then refuses with 401 and the handler never runs.\n *\n * A THROW here propagates: an auth lookup that failed is not the same as an\n * absent session, and the wrapper will not invent a status for it. The\n * handler still does not run, so the failure stays closed.\n */\n identify: (args: TArgs) => Promise<FreeRouteIdentity | null> | FreeRouteIdentity | null\n /** Build the refusal response. Defaults to {@link freeRouteLimitResponse}. */\n onDenied?: (error: FreeRouteLimitError, args: TArgs) => Response | Promise<Response>\n}\n\n/**\n * Wrap an authenticated, unmetered, compute-bearing route handler in its\n * budget. The limit is applied BEFORE the handler is entered — including\n * before the request body is read — so a refused call costs a KV read, not the\n * compute the route exists to do.\n *\n * The handler's own response is returned untouched: this never rewrites status,\n * body or headers on the success path, so it cannot break a streaming or\n * pre-built response.\n *\n * `TArgs` is the router's handler-argument object (react-router's\n * `ActionFunctionArgs`, or any single-object shape a product uses).\n */\nexport function withFreeRouteLimit<TArgs>(\n options: WithFreeRouteLimitOptions<TArgs>,\n handler: (args: TArgs) => Response | Promise<Response>,\n): (args: TArgs) => Promise<Response> {\n return async (args: TArgs): Promise<Response> => {\n const identity = await options.identify(args)\n const outcome = await checkFreeRouteLimit({\n kv: options.kv(args),\n route: options.route,\n subject: identity?.subject,\n workspace: identity?.workspace,\n costClass: options.costClass,\n budget: options.budget,\n workspaceBudget: options.workspaceBudget,\n })\n if (!outcome.succeeded) {\n return options.onDenied ? await options.onDenied(outcome.error, args) : freeRouteLimitResponse(outcome.error)\n }\n return await handler(args)\n }\n}\n","/**\n * Web-boundary utilities every agent app's routes hand-roll: JSON body parsing\n * + narrowing, request-context extraction (real client IP behind Cloudflare),\n * a KV-backed sliding-window rate limiter, the free-route budget policy built\n * on it, and security response headers. Pure mechanism — no DB, no domain. The\n * KV is a structural interface so this needs no `@cloudflare/workers-types`\n * dependency.\n */\n\nexport * from './rate-limit'\nexport * from './free-route-limit'\n\nexport type JsonObject = Record<string, unknown>\n\n/** Parse + object-narrow a Request body. `[body, null]` on success, `[null,\n * errorResponse]` on a non-object body (callers `if (err) return err`). */\nexport async function parseJsonObjectBody(request: Request): Promise<[JsonObject, null] | [null, Response]> {\n let raw: unknown\n try {\n raw = await request.json()\n } catch {\n return [null, Response.json({ error: 'Invalid JSON body' }, { status: 400 })]\n }\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n return [null, Response.json({ error: 'Body must be a JSON object' }, { status: 400 })]\n }\n return [raw as JsonObject, null]\n}\n\n/** Narrow one required string field, 400 if missing/empty. */\nexport function requireString(body: JsonObject, field: string): string | Response {\n const v = body[field]\n if (typeof v !== 'string' || v.length === 0) {\n return Response.json({ error: `Missing or non-string field: ${field}` }, { status: 400 })\n }\n return v\n}\n\n/** Define the context of a request including IP address, user agent, timestamp, and request ID */\nexport interface RequestContext {\n ipAddress: string\n userAgent: string\n timestamp: string\n requestId: string\n}\n\n/** Extract request context for audit trails. Uses `CF-Connecting-IP` for the\n * real client IP behind Cloudflare. */\nexport function extractRequestContext(request: Request): RequestContext {\n const ipAddress =\n request.headers.get('CF-Connecting-IP') ??\n request.headers.get('X-Forwarded-For')?.split(',')[0]?.trim() ??\n '0.0.0.0'\n return {\n ipAddress,\n userAgent: request.headers.get('User-Agent') ?? '',\n timestamp: new Date().toISOString(),\n requestId: crypto.randomUUID(),\n }\n}\n\n/** Define options for configuring cookie attributes and behavior */\nexport interface CookieOptions {\n name: string\n /** Default '/'. */\n path?: string\n /** Default true. */\n httpOnly?: boolean\n /** Adds the `Secure` attribute. Default false. */\n secure?: boolean\n /** Default 'Lax'. */\n sameSite?: 'Lax' | 'Strict' | 'None'\n maxAgeSeconds?: number\n}\n\n/** Serialize a Set-Cookie header value: `name=encodeURIComponent(value)` plus\n * attributes in Path / HttpOnly / SameSite / Max-Age / Secure order.\n * Throws on `SameSite=None` without `secure` — browsers silently drop that\n * combination, which would otherwise fail invisibly. */\nexport function serializeCookie(value: string, opts: CookieOptions): string {\n if (opts.sameSite === 'None' && !opts.secure) {\n throw new Error('SameSite=None cookies require secure: true (browsers reject them otherwise)')\n }\n const parts = [`${opts.name}=${encodeURIComponent(value)}`, `Path=${opts.path ?? '/'}`]\n if (opts.httpOnly !== false) parts.push('HttpOnly')\n parts.push(`SameSite=${opts.sameSite ?? 'Lax'}`)\n if (opts.maxAgeSeconds !== undefined) parts.push(`Max-Age=${opts.maxAgeSeconds}`)\n if (opts.secure) parts.push('Secure')\n return parts.join('; ')\n}\n\n/** Set-Cookie header value that deletes the cookie (empty value, Max-Age=0). */\nexport function clearCookieHeader(opts: Omit<CookieOptions, 'maxAgeSeconds'>): string {\n return serializeCookie('', { ...opts, maxAgeSeconds: 0 })\n}\n\n/** Read + decode one cookie from a Cookie request header; null when absent. */\nexport function readCookieValue(cookieHeader: string | null, name: string): string | null {\n if (!cookieHeader) return null\n for (const part of cookieHeader.split(/;\\s*/)) {\n const [cookieName, ...rest] = part.split('=')\n if (cookieName === name) {\n try {\n return decodeURIComponent(rest.join('='))\n } catch {\n return null\n }\n }\n }\n return null\n}\n\n/** Define options for configuring security-related HTTP headers including disclaimers and retention labels */\nexport interface SecurityHeaderOptions {\n /** Product disclaimer (e.g. \"AI-powered tool. Not legal advice.\"). Omitted if absent. */\n disclaimer?: string\n /** Data-retention label (e.g. \"7-years\"). Omitted if absent. */\n retention?: string\n /** Extra headers to set. */\n extra?: Record<string, string>\n}\n\n/** Canonical generic response headers used by {@link addSecurityHeaders}.\n * Exported so static-asset hosts can apply the same policy without copying\n * values that silently drift from Worker/API responses. */\nexport const STANDARD_SECURITY_HEADERS = Object.freeze({\n 'Strict-Transport-Security':\n 'max-age=31536000; includeSubDomains; preload',\n 'X-Content-Type-Options': 'nosniff',\n 'X-Frame-Options': 'SAMEORIGIN',\n 'Referrer-Policy': 'same-origin',\n 'X-XSS-Protection': '1; mode=block',\n} as const)\n\n/** Set standard security headers on a response (HSTS, nosniff, frame-options,\n * referrer-policy, XSS) + optional product disclaimer/retention. The security\n * set is generic; the disclaimer/retention are the product's. */\nexport function addSecurityHeaders(response: Response, opts: SecurityHeaderOptions = {}): Response {\n for (const [name, value] of Object.entries(STANDARD_SECURITY_HEADERS)) {\n response.headers.set(name, value)\n }\n if (opts.disclaimer) response.headers.set('X-AI-Disclaimer', opts.disclaimer)\n if (opts.retention) response.headers.set('X-Data-Retention', opts.retention)\n for (const [k, v] of Object.entries(opts.extra ?? {})) response.headers.set(k, v)\n return response\n}\n\n/** Local-sandbox / inline schemes a stored media reference must never use.\n * Reachable from neither a browser nor the product worker, and a `file:`/`data:`\n * url is the tell of an agent substituting local ffmpeg output for a real\n * provider artifact. `blob:` and `javascript:` are inert/active client schemes\n * with no server reachability. */\nconst REJECTED_MEDIA_SCHEMES = ['file:', 'data:', 'blob:', 'javascript:', 'vbscript:'] as const\n\n/**\n * Canonical media-reference boundary shared by every surface that persists a\n * media url (sequences clips, design-canvas image/video src). The ONE rule:\n * remote `http(s)` or a rooted `/api/` path are allowed; everything else is\n * rejected, with a named reason for known-bad local/inline schemes so the\n * thrown message is actionable for an LLM planner. The url is trimmed before\n * the scheme check so leading whitespace cannot smuggle a rejected scheme past\n * a naive `startsWith`.\n *\n * @param what - noun for the error message (e.g. 'media url', 'src').\n */\nexport function assertMediaUrl(url: string, what = 'media url'): void {\n const trimmed = url.trim()\n if (/^https?:\\/\\//i.test(trimmed)) return\n if (trimmed.startsWith('/api/')) return\n const shown = trimmed.length > 96 ? `${trimmed.slice(0, 96)}…` : trimmed\n const lower = trimmed.toLowerCase()\n if (\n REJECTED_MEDIA_SCHEMES.some((scheme) => lower.startsWith(scheme)) ||\n lower.startsWith('/tmp/') ||\n lower.startsWith('/home/')\n ) {\n throw new Error(`${what} must reference a provider http(s) URL or a rooted /api/ path, not a local sandbox file (${shown})`)\n }\n throw new Error(`${what} must be http(s) or a rooted /api/ path (${shown})`)\n}\n\nexport { isWorkspaceFileExportable } from './file-export'\n","/** Renderer-neutral attribution. Not a transcript merger or an execution state machine. */\nexport interface ConversationGroupItem {\n id?: string | number\n kind?: string\n role?: string\n /** A different assistant/persona must never inherit the previous speaker's label. */\n speakerId?: string\n /** Distinct threads must not be grouped if their rows share a viewport. */\n conversationId?: string\n}\nexport type GroupedConversationItem<T> = T & { isContinuation?: boolean; groupId?: string }\n\n/**\n * Annotate assistant rows until an actual user message, speaker or conversation change.\n * Tool/progress notices do not interrupt the group. The first visible assistant is\n * always labeled, including when a renderer has paged earlier history away.\n * Pass display order; stored content, IDs, timestamps and input objects are untouched.\n */\nexport function groupConversationMessages<T extends ConversationGroupItem>(items: readonly T[] = []): GroupedConversationItem<T>[] {\n let speaker: string | null = null\n let conversation: string | undefined\n let groupId: string | undefined\n return items.map((item, index) => {\n if (item.conversationId !== undefined && item.conversationId !== conversation) {\n conversation = item.conversationId\n speaker = null\n groupId = undefined\n }\n const role = item.kind === 'thinking' ? 'assistant' : (!item.kind || item.kind === 'message' ? item.role : undefined)\n if (role === 'user') { speaker = null; groupId = undefined; return { ...item, isContinuation: false } }\n if (role !== 'assistant') return item\n const current = item.speakerId ?? 'assistant'\n const isContinuation = speaker === current\n if (!isContinuation) groupId = String(item.id ?? `assistant-${index}`)\n speaker = current\n return { ...item, isContinuation, groupId }\n })\n}\n"],"mappings":";AAiCA,eAAsB,eAAe,IAAY,KAAa,OAAe,eAAiD;AAC5H,QAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AACxC,QAAM,cAAc,MAAM;AAC1B,QAAM,QAAQ,MAAM,GAAG;AACvB,QAAM,MAAM,MAAM,GAAG,IAAI,KAAK;AAC9B,QAAM,SAAS,oBAAoB,GAAG;AACtC,MAAI,WAAW,gBAAgB;AAG7B,WAAO,EAAE,SAAS,OAAO,WAAW,GAAG,SAAS,MAAM,cAAc;AAAA,EACtE;AACA,QAAM,QAAQ,OAAO,OAAO,CAAC,MAAM,IAAI,WAAW;AAClD,MAAI,MAAM,UAAU,MAAO,QAAO,EAAE,SAAS,OAAO,WAAW,GAAG,UAAU,MAAM,CAAC,KAAK,OAAO,cAAc;AAC7G,QAAM,KAAK,GAAG;AACd,QAAM,GAAG,IAAI,OAAO,KAAK,UAAU,KAAK,GAAG,EAAE,eAAe,gBAAgB,EAAE,CAAC;AAC/E,SAAO,EAAE,SAAS,MAAM,WAAW,QAAQ,MAAM,QAAQ,SAAS,MAAM,cAAc;AACxF;AAKA,IAAM,iBAAiB,uBAAO,2BAA2B;AAKzD,SAAS,oBAAoB,KAAsD;AACjF,MAAI,QAAQ,KAAM,QAAO,CAAC;AAC1B,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO;AACnC,SAAO,OAAO,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,CAAC;AACtF;;;ACRO,IAAM,qBAAwE,OAAO,OAAO;AAAA,EACjG,aAAa,OAAO,OAAO,EAAE,OAAO,KAAK,eAAe,GAAG,CAAC;AAAA,EAC5D,SAAS,OAAO,OAAO,EAAE,OAAO,IAAI,eAAe,GAAG,CAAC;AAAA,EACvD,OAAO,OAAO,OAAO,EAAE,OAAO,IAAI,eAAe,IAAI,CAAC;AACxD,CAAC;AAIM,IAAM,8BAA8B;AAqBpC,IAAM,sBAAN,cAAkC,MAAM;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YAAY,MAQT;AACD,UAAM,KAAK,SAAS,KAAK,UAAU,SAAY,SAAY,EAAE,OAAO,KAAK,MAAM,CAAC;AAChF,SAAK,OAAO;AACZ,SAAK,SAAS,KAAK;AACnB,SAAK,SAAS,KAAK;AACnB,SAAK,oBAAoB,KAAK;AAC9B,SAAK,YAAY,KAAK;AACtB,SAAK,QAAQ,KAAK;AAAA,EACpB;AACF;AAwCA,SAAS,SAAS,OAAe,WAAmB,IAAoB;AACtE,SAAO,QAAQ,mBAAmB,KAAK,CAAC,IAAI,SAAS,IAAI,mBAAmB,EAAE,CAAC;AACjF;AAEA,SAAS,aAAa,QAAyB,OAAqB;AAClE,QAAM,QACJ,OAAO,UAAU,OAAO,KAAK,KAC7B,OAAO,SAAS,KAChB,OAAO,UAAU,OAAO,aAAa,KACrC,OAAO,iBAAiB;AAC1B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,yCAAyC,OAAO,KAAK,mBAAmB,OAAO,aAAa;AAAA,IACtG;AAAA,EACF;AACF;AAEA,SAAS,aAAa,SAAyB;AAC7C,SAAO,KAAK,IAAI,GAAG,UAAU,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,CAAC;AAC5D;AAOA,eAAe,UACb,IACA,OACA,WACA,IACA,QACyG;AACzG,MAAI;AACF,UAAM,QAAQ,MAAM,eAAe,IAAI,SAAS,OAAO,WAAW,EAAE,GAAG,OAAO,OAAO,OAAO,aAAa;AACzG,WAAO,EAAE,WAAW,MAAM,MAAM;AAAA,EAClC,SAAS,OAAO;AACd,WAAO;AAAA,MACL,WAAW;AAAA,MACX,OAAO,IAAI,oBAAoB;AAAA,QAC7B,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,mBAAmB,KAAK,IAAI,OAAO,eAAe,EAAE;AAAA,QACpD;AAAA,QACA;AAAA,QACA,SAAS,uCAAuC,KAAK;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AASA,eAAsB,oBAAoB,OAA4D;AACpG,QAAM,SAAS,MAAM,UAAU,mBAAmB,MAAM,aAAa,SAAS;AAC9E,eAAa,QAAQ,0BAA0B,MAAM,KAAK,GAAG;AAC7D,QAAM,kBACJ,MAAM,mBAAmB,EAAE,OAAO,OAAO,QAAQ,6BAA6B,eAAe,OAAO,cAAc;AACpH,MAAI,MAAM,UAAW,cAAa,iBAAiB,oCAAoC,MAAM,KAAK,GAAG;AAErG,QAAM,UAAU,MAAM,SAAS,KAAK;AACpC,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,MACL,WAAW;AAAA,MACX,OAAO,IAAI,oBAAoB;AAAA,QAC7B,QAAQ;AAAA,QACR,QAAQ;AAAA;AAAA,QAER,mBAAmB;AAAA,QACnB,WAAW;AAAA,QACX,OAAO,MAAM;AAAA,QACb,SAAS,UAAU,MAAM,KAAK;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,gBAAgB,MAAM,UAAU,MAAM,IAAI,MAAM,OAAO,WAAW,SAAS,MAAM;AACvF,MAAI,CAAC,cAAc,UAAW,QAAO;AACrC,MAAI,CAAC,cAAc,MAAM,SAAS;AAChC,WAAO,EAAE,WAAW,OAAO,OAAO,YAAY,MAAM,OAAO,WAAW,cAAc,KAAK,EAAE;AAAA,EAC7F;AAEA,MAAI,WAA+G;AAAA,IACjH,QAAQ,cAAc;AAAA,IACtB;AAAA,IACA,WAAW;AAAA,EACb;AAEA,MAAI,MAAM,WAAW;AACnB,UAAM,kBAAkB,MAAM,UAAU,MAAM,IAAI,MAAM,OAAO,aAAa,MAAM,WAAW,eAAe;AAC5G,QAAI,CAAC,gBAAgB,UAAW,QAAO;AACvC,QAAI,CAAC,gBAAgB,MAAM,SAAS;AAClC,aAAO,EAAE,WAAW,OAAO,OAAO,YAAY,MAAM,OAAO,aAAa,gBAAgB,KAAK,EAAE;AAAA,IACjG;AACA,QAAI,gBAAgB,MAAM,YAAY,SAAS,OAAO,WAAW;AAC/D,iBAAW,EAAE,QAAQ,gBAAgB,OAAO,QAAQ,iBAAiB,WAAW,YAAY;AAAA,IAC9F;AAAA,EACF;AAEA,SAAO;AAAA,IACL,WAAW;AAAA,IACX,OAAO;AAAA,MACL,WAAW,SAAS,OAAO;AAAA,MAC3B,SAAS,SAAS,OAAO;AAAA,MACzB,QAAQ,SAAS;AAAA,MACjB,WAAW,SAAS;AAAA,IACtB;AAAA,EACF;AACF;AAEA,SAAS,YACP,OACA,WACA,QACqB;AACrB,QAAM,oBAAoB,aAAa,OAAO,OAAO;AACrD,SAAO,IAAI,oBAAoB;AAAA,IAC7B,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,UAAU,KAAK,iCAAiC,SAAS,cAAc,iBAAiB;AAAA,EACnG,CAAC;AACH;AAWO,SAAS,uBACd,OACA,UAAyC,CAAC,GAChC;AACV,QAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAC3C,UAAQ,IAAI,gBAAgB,kBAAkB;AAC9C,MAAI,MAAM,oBAAoB,EAAG,SAAQ,IAAI,eAAe,OAAO,MAAM,iBAAiB,CAAC;AAC3F,SAAO,IAAI;AAAA,IACT,KAAK,UAAU;AAAA,MACb,OAAO,QAAQ,WAAW,MAAM;AAAA,MAChC,QAAQ,MAAM;AAAA,MACd,mBAAmB,MAAM;AAAA,IAC3B,CAAC;AAAA,IACD,EAAE,QAAQ,MAAM,QAAQ,QAAQ;AAAA,EAClC;AACF;AA0CO,SAAS,mBACd,SACA,SACoC;AACpC,SAAO,OAAO,SAAmC;AAC/C,UAAM,WAAW,MAAM,QAAQ,SAAS,IAAI;AAC5C,UAAM,UAAU,MAAM,oBAAoB;AAAA,MACxC,IAAI,QAAQ,GAAG,IAAI;AAAA,MACnB,OAAO,QAAQ;AAAA,MACf,SAAS,UAAU;AAAA,MACnB,WAAW,UAAU;AAAA,MACrB,WAAW,QAAQ;AAAA,MACnB,QAAQ,QAAQ;AAAA,MAChB,iBAAiB,QAAQ;AAAA,IAC3B,CAAC;AACD,QAAI,CAAC,QAAQ,WAAW;AACtB,aAAO,QAAQ,WAAW,MAAM,QAAQ,SAAS,QAAQ,OAAO,IAAI,IAAI,uBAAuB,QAAQ,KAAK;AAAA,IAC9G;AACA,WAAO,MAAM,QAAQ,IAAI;AAAA,EAC3B;AACF;;;ACpWA,eAAsB,oBAAoB,SAAkE;AAC1G,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,QAAQ,KAAK;AAAA,EAC3B,QAAQ;AACN,WAAO,CAAC,MAAM,SAAS,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC,CAAC;AAAA,EAC9E;AACA,MAAI,QAAQ,QAAQ,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,GAAG;AACjE,WAAO,CAAC,MAAM,SAAS,KAAK,EAAE,OAAO,6BAA6B,GAAG,EAAE,QAAQ,IAAI,CAAC,CAAC;AAAA,EACvF;AACA,SAAO,CAAC,KAAmB,IAAI;AACjC;AAGO,SAAS,cAAc,MAAkB,OAAkC;AAChF,QAAM,IAAI,KAAK,KAAK;AACpB,MAAI,OAAO,MAAM,YAAY,EAAE,WAAW,GAAG;AAC3C,WAAO,SAAS,KAAK,EAAE,OAAO,gCAAgC,KAAK,GAAG,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC1F;AACA,SAAO;AACT;AAYO,SAAS,sBAAsB,SAAkC;AACtE,QAAM,YACJ,QAAQ,QAAQ,IAAI,kBAAkB,KACtC,QAAQ,QAAQ,IAAI,iBAAiB,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK,KAC5D;AACF,SAAO;AAAA,IACL;AAAA,IACA,WAAW,QAAQ,QAAQ,IAAI,YAAY,KAAK;AAAA,IAChD,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,WAAW,OAAO,WAAW;AAAA,EAC/B;AACF;AAoBO,SAAS,gBAAgB,OAAe,MAA6B;AAC1E,MAAI,KAAK,aAAa,UAAU,CAAC,KAAK,QAAQ;AAC5C,UAAM,IAAI,MAAM,6EAA6E;AAAA,EAC/F;AACA,QAAM,QAAQ,CAAC,GAAG,KAAK,IAAI,IAAI,mBAAmB,KAAK,CAAC,IAAI,QAAQ,KAAK,QAAQ,GAAG,EAAE;AACtF,MAAI,KAAK,aAAa,MAAO,OAAM,KAAK,UAAU;AAClD,QAAM,KAAK,YAAY,KAAK,YAAY,KAAK,EAAE;AAC/C,MAAI,KAAK,kBAAkB,OAAW,OAAM,KAAK,WAAW,KAAK,aAAa,EAAE;AAChF,MAAI,KAAK,OAAQ,OAAM,KAAK,QAAQ;AACpC,SAAO,MAAM,KAAK,IAAI;AACxB;AAGO,SAAS,kBAAkB,MAAoD;AACpF,SAAO,gBAAgB,IAAI,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;AAC1D;AAGO,SAAS,gBAAgB,cAA6B,MAA6B;AACxF,MAAI,CAAC,aAAc,QAAO;AAC1B,aAAW,QAAQ,aAAa,MAAM,MAAM,GAAG;AAC7C,UAAM,CAAC,YAAY,GAAG,IAAI,IAAI,KAAK,MAAM,GAAG;AAC5C,QAAI,eAAe,MAAM;AACvB,UAAI;AACF,eAAO,mBAAmB,KAAK,KAAK,GAAG,CAAC;AAAA,MAC1C,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAeO,IAAM,4BAA4B,OAAO,OAAO;AAAA,EACrD,6BACE;AAAA,EACF,0BAA0B;AAAA,EAC1B,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,oBAAoB;AACtB,CAAU;AAKH,SAAS,mBAAmB,UAAoB,OAA8B,CAAC,GAAa;AACjG,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,yBAAyB,GAAG;AACrE,aAAS,QAAQ,IAAI,MAAM,KAAK;AAAA,EAClC;AACA,MAAI,KAAK,WAAY,UAAS,QAAQ,IAAI,mBAAmB,KAAK,UAAU;AAC5E,MAAI,KAAK,UAAW,UAAS,QAAQ,IAAI,oBAAoB,KAAK,SAAS;AAC3E,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,SAAS,CAAC,CAAC,EAAG,UAAS,QAAQ,IAAI,GAAG,CAAC;AAChF,SAAO;AACT;AAOA,IAAM,yBAAyB,CAAC,SAAS,SAAS,SAAS,eAAe,WAAW;AAa9E,SAAS,eAAe,KAAa,OAAO,aAAmB;AACpE,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,gBAAgB,KAAK,OAAO,EAAG;AACnC,MAAI,QAAQ,WAAW,OAAO,EAAG;AACjC,QAAM,QAAQ,QAAQ,SAAS,KAAK,GAAG,QAAQ,MAAM,GAAG,EAAE,CAAC,WAAM;AACjE,QAAM,QAAQ,QAAQ,YAAY;AAClC,MACE,uBAAuB,KAAK,CAAC,WAAW,MAAM,WAAW,MAAM,CAAC,KAChE,MAAM,WAAW,OAAO,KACxB,MAAM,WAAW,QAAQ,GACzB;AACA,UAAM,IAAI,MAAM,GAAG,IAAI,4FAA4F,KAAK,GAAG;AAAA,EAC7H;AACA,QAAM,IAAI,MAAM,GAAG,IAAI,4CAA4C,KAAK,GAAG;AAC7E;;;ACjKO,SAAS,0BAA2D,QAAsB,CAAC,GAAiC;AACjI,MAAI,UAAyB;AAC7B,MAAI;AACJ,MAAI;AACJ,SAAO,MAAM,IAAI,CAAC,MAAM,UAAU;AAChC,QAAI,KAAK,mBAAmB,UAAa,KAAK,mBAAmB,cAAc;AAC7E,qBAAe,KAAK;AACpB,gBAAU;AACV,gBAAU;AAAA,IACZ;AACA,UAAM,OAAO,KAAK,SAAS,aAAa,cAAe,CAAC,KAAK,QAAQ,KAAK,SAAS,YAAY,KAAK,OAAO;AAC3G,QAAI,SAAS,QAAQ;AAAE,gBAAU;AAAM,gBAAU;AAAW,aAAO,EAAE,GAAG,MAAM,gBAAgB,MAAM;AAAA,IAAE;AACtG,QAAI,SAAS,YAAa,QAAO;AACjC,UAAM,UAAU,KAAK,aAAa;AAClC,UAAM,iBAAiB,YAAY;AACnC,QAAI,CAAC,eAAgB,WAAU,OAAO,KAAK,MAAM,aAAa,KAAK,EAAE;AACrE,cAAU;AACV,WAAO,EAAE,GAAG,MAAM,gBAAgB,QAAQ;AAAA,EAC5C,CAAC;AACH;","names":[]}
@@ -13,13 +13,13 @@ import {
13
13
  setPageGuidesCommand,
14
14
  setPagePropsCommand,
15
15
  ungroupElementCommand
16
- } from "./chunk-G4CPEJNN.js";
16
+ } from "./chunk-AKMQFCDL.js";
17
17
  import {
18
18
  SIZE_PRESETS,
19
19
  findElement,
20
20
  matchPreset,
21
21
  requirePage
22
- } from "./chunk-A2XMTCER.js";
22
+ } from "./chunk-WYHFHTSQ.js";
23
23
 
24
24
  // src/design-canvas-react/components/DesignCanvas.tsx
25
25
  import { useCallback as useCallback2, useEffect as useEffect3, useMemo, useRef as useRef5, useState as useState5, useSyncExternalStore } from "react";
@@ -2261,4 +2261,4 @@ export {
2261
2261
  ExportControl,
2262
2262
  DesignCanvas
2263
2263
  };
2264
- //# sourceMappingURL=chunk-54KJ54SY.js.map
2264
+ //# sourceMappingURL=chunk-JAT6SOPK.js.map
@@ -4,7 +4,7 @@ import {
4
4
  DesignCanvas,
5
5
  PlusGlyph,
6
6
  ShapesGlyph
7
- } from "./chunk-54KJ54SY.js";
7
+ } from "./chunk-JAT6SOPK.js";
8
8
  import {
9
9
  lightTheme
10
10
  } from "./chunk-Q2QKEV6J.js";
@@ -20,7 +20,7 @@ import {
20
20
  marqueeSelect,
21
21
  resolveExportParams,
22
22
  resolveNodeCachePixelRatio
23
- } from "./chunk-QEPLXYOE.js";
23
+ } from "./chunk-VHMZ3KUH.js";
24
24
  import {
25
25
  addElementCommand,
26
26
  createSceneCommandStack,
@@ -29,13 +29,13 @@ import {
29
29
  multiSetAttrsCommand,
30
30
  setAttrsCommand,
31
31
  ungroupElementCommand
32
- } from "./chunk-G4CPEJNN.js";
32
+ } from "./chunk-AKMQFCDL.js";
33
33
  import {
34
34
  SCENE_SCHEMA_VERSION,
35
35
  boundsIntersect,
36
36
  elementAabb,
37
37
  findElement
38
- } from "./chunk-A2XMTCER.js";
38
+ } from "./chunk-WYHFHTSQ.js";
39
39
 
40
40
  // src/design-canvas-react/components/Workspace.tsx
41
41
  import {
@@ -1948,4 +1948,4 @@ export {
1948
1948
  Workspace,
1949
1949
  DesignCanvasEditor
1950
1950
  };
1951
- //# sourceMappingURL=chunk-HUYTN7RI.js.map
1951
+ //# sourceMappingURL=chunk-JF423ASS.js.map
@@ -1,14 +1,14 @@
1
1
  // src/design-canvas-react/lazy.tsx
2
2
  import { lazy } from "react";
3
3
  var DesignCanvasLazy = lazy(
4
- () => import("./DesignCanvasEditor-GVJO5SMH.js").then((m) => ({ default: m.DesignCanvasEditor }))
4
+ () => import("./DesignCanvasEditor-UZ2S6BDD.js").then((m) => ({ default: m.DesignCanvasEditor }))
5
5
  );
6
6
  var DesignCanvasChromeLazy = lazy(
7
- () => import("./DesignCanvas-UPRRA4YG.js").then((m) => ({ default: m.DesignCanvas }))
7
+ () => import("./DesignCanvas-MWEYD6PR.js").then((m) => ({ default: m.DesignCanvas }))
8
8
  );
9
9
 
10
10
  export {
11
11
  DesignCanvasLazy,
12
12
  DesignCanvasChromeLazy
13
13
  };
14
- //# sourceMappingURL=chunk-WOZSOPUZ.js.map
14
+ //# sourceMappingURL=chunk-KX2GA3L6.js.map