@adia-ai/llm 0.5.4 → 0.5.6

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/CHANGELOG.md CHANGED
@@ -3,11 +3,46 @@
3
3
  All notable changes to this package are documented here.
4
4
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
-
7
6
  ## [Unreleased]
8
7
 
9
8
  _No pending changes._
9
+ ## [0.5.6] - 2026-05-14
10
+
11
+ ### Fixed — §190 (v0.5.6) — `loadGenerators()` resolves packages-root in both dev + prod
12
+
13
+ `server.js` learns to resolve `_packagesRoot` first-existing-wins between the dev layout (repo-relative `packages/<name>/…`) and prod layout (rsync'd `/srv/adia-ui/dist/packages/<name>/…` per `scripts/build/site.mjs`). Pre-§190 the generator imports referenced the dev path absolutely, which failed at runtime on the production VM after the §188 cutover took the LLM-gateway path live but left `/api/generate` (zettel + mcp generator imports) unresolved. The fix: cache `_packagesRoot` once, fall back across layouts, then use it for every dynamic import path. Tested locally via `node packages/llm/server.js` against both layouts; production verified by operator post-deploy.
14
+
15
+ Plus README polish noting the §188 LLM-gateway routing + the dev-vs-prod packages-root resolution. No public API change.
16
+ ## [0.5.5] - 2026-05-14
17
+
18
+ ### Removed — §188 (v0.5.5) — replace `handleLlmPassthrough` with exe.dev LLM Gateway
19
+
20
+ **-155 LOC** in `packages/llm/server.js` (692 → 537 lines).
21
+
22
+ Deleted:
23
+
24
+ - `LLM_PASSTHROUGH_UPSTREAMS` const + provider-specific adapter map
25
+ - `MODEL_ALLOWLIST` + `MAX_TOKENS_CEILING` env-driven guardrails
26
+ - `handleLlmPassthrough()` function (body parsing, model gating, header scrub, fetch + SSE stream pipe)
27
+ - `handleLlmHealthz()` function
28
+ - 2 route dispatchers (`/api/llm/*` + `/api/llm/healthz`)
29
+
30
+ These existed to proxy LLM API calls from the browser to upstream providers (Anthropic/OpenAI/Google) with API key injection, model allowlist enforcement, max_tokens ceiling, origin/referer scrub. The §181 implementation predated the discovery that exe.dev (the platform hosting `ui-kit.exe.xyz`) provides this natively at `http://169.254.169.254/gateway/llm/<provider>/<rest>`.
31
+
32
+ **Production deployment** routes `/api/llm/*` via Caddy reverse_proxy to the exe.dev gateway (see `infra/caddy/llm-gateway.snippet` + `docs/guides/production-llm-proxy-deployment.md`). The browser code path (`packages/llm/llm-bridge.js`) is unchanged — relative `/api/llm/anthropic/v1/messages` resolves identically; Caddy intercepts before this server sees the request.
10
33
 
34
+ **Local dev (`npm run dev`) unchanged**: Vite proxy at `vite.config.js:38-80` continues to use the developer's own `ANTHROPIC_API_KEY`. The gateway is link-local (`169.254.169.254`), VM-internal only; localhost dev doesn't reach it.
35
+
36
+ **What stays in `server.js`**: `/api/chat`, `/api/generate`, `/api/convert-html` — AdiaUI-specific business logic, not generic LLM proxying.
37
+
38
+ ### Lost vs §181 (intentional)
39
+
40
+ - Model allowlist enforcement (Haiku-only floor at the proxy)
41
+ - `max_tokens` ceiling capping at the proxy
42
+
43
+ These were defenses against per-key API billing drain when AdiaUI owned the API key. With subscription-billed exe.dev gateway, the subscription IS the cost cap. Per user direction, accepted.
44
+
45
+ Commit `656b39dd1`. See root CHANGELOG and journal §181-§188 for the full architectural arc.
11
46
  ## [0.5.4] - 2026-05-14
12
47
 
13
48
  ### Changed — lockstep ride-along; no source changes
@@ -19,28 +54,21 @@ Tracking the v0.5.4 cycle's prop-fidelity arcs (§163 transpiler fix,
19
54
  §164 chunk re-harvest, §165 validator check #19, §166a/b/c audit-script
20
55
  family slots 12+13 + smoke shift-left): see root `CHANGELOG.md` for the
21
56
  full narrative. This package carries no source changes this cycle.
22
-
23
57
  ## [0.5.3] - 2026-05-14
24
58
 
25
59
  _No pending changes._
26
-
27
60
  ## [0.5.2] - 2026-05-13
28
61
 
29
62
  _Lockstep ride-along (no source change)._
30
-
31
-
32
63
  ## [0.5.1] - 2026-05-13
33
64
 
34
65
  _Lockstep ride-along (no source change)._
35
-
36
66
  ## [0.5.0] - 2026-05-13
37
67
 
38
68
  _Lockstep ride-along (no source change)._
39
-
40
69
  ## [0.4.9] - 2026-05-13
41
70
 
42
71
  _No pending changes._
43
-
44
72
  ## [0.4.8] - 2026-05-12
45
73
 
46
74
  ### Ride-along (no source changes)
@@ -48,7 +76,6 @@ _No pending changes._
48
76
  Lockstep PATCH cut alongside `@adia-ai/web-components@0.4.8` (§80 event-types codegen for 38 non-form primitives + §85 class-subpath script fix + §89 icons.js split + B+C+D consumer-friction infra), `@adia-ai/web-modules@0.4.8` (FEEDBACK-02 #1 `.d.ts` shipment + `composes:` ADR-0027 wiring), `@adia-ai/a2ui-compose@0.4.8` (§87 zettel `ensureBooted()` race fix + §88 free-form composition mode), `@adia-ai/a2ui-corpus@0.4.8` (catalog regen ride-along), `@adia-ai/a2ui-mcp@0.4.8` (§87 eval threshold rebaseline). Source byte-identical to v0.4.7.
49
77
 
50
78
  Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). See root [CHANGELOG.md `## [0.4.8]`](../../../CHANGELOG.md) for the cut narrative.
51
-
52
79
  ## [0.4.7] - 2026-05-12
53
80
 
54
81
  ### Ride-along (no source changes)
@@ -56,19 +83,16 @@ Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4
56
83
  Lockstep PATCH cut alongside `@adia-ai/web-components@0.4.7` (§74 `.d.ts` codegen for 73 non-form primitives + §77 non-side-effect `class` subpath per component + class.js helper extractions in agent + toolbar primitives), `@adia-ai/web-modules@0.4.7`, `@adia-ai/a2ui-compose@0.4.7` (§72 `getComponentCatalog()` reads canonical catalog), `@adia-ai/a2ui-corpus@0.4.7` (§72 `corpus/patterns/` + `corpus/compositions/` retired from disk + tarball — v0.4.6 §65 carry-over closed), `@adia-ai/a2ui-mcp@0.4.7`, `@adia-ai/a2ui-retrieval@0.4.7` (§72 vocab reads canonical catalog + dead pattern-embeddings retired). Source byte-identical to v0.4.6.
57
84
 
58
85
  Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). See root [CHANGELOG.md `## [0.4.7]`](../../../CHANGELOG.md) for the cut narrative.
59
-
60
86
  ## [0.4.6] - 2026-05-12
61
87
 
62
88
  ### Changed — README currency sweep
63
89
 
64
90
  - **`README.md`** — currency + consumer-guide sweep aligned with v0.4.6 [Unreleased] (commit `c94374c9`). Doc-only; no API change.
65
-
66
91
  ## [0.4.5] - 2026-05-12
67
92
 
68
93
  ### Changed — doc-comment refresh (§54)
69
94
 
70
95
  - **`models.js`** — header docstring path-rename `apps/genui/gen-ui-ux/` → `apps/genui/app/factory-chat/`. Cross-doc sweep from the §54 GenUI overhaul rename arc. No API change.
71
-
72
96
  ## [0.4.4] - 2026-05-12
73
97
 
74
98
  ### Ride-along (no source changes)
@@ -76,7 +100,6 @@ Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4
76
100
  Lockstep PATCH cut alongside `@adia-ai/web-components@0.4.4` (14 yaml gap closures from corpus simplification arc), `@adia-ai/a2ui-runtime@0.4.4` (registry expansion +27 web-modules + renderer kebab-case fix), `@adia-ai/a2ui-compose@0.4.4` (composition-library rename + transpiler `<a>` → Link), `@adia-ai/a2ui-corpus@0.4.4` (corpus simplification arc §36–§51), `@adia-ai/a2ui-mcp@0.4.4` (`get_fragment` tool retirement). Source byte-identical to v0.4.3.
77
101
 
78
102
  Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). See root [CHANGELOG.md `## [0.4.4]`](../../../CHANGELOG.md) for the cut narrative.
79
-
80
103
  ## [0.4.3] - 2026-05-11
81
104
 
82
105
  ### Added
@@ -88,7 +111,6 @@ Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4
88
111
  Lockstep PATCH cut alongside `@adia-ai/web-components@0.4.3` (input-ui type=number locale + thousands grouping + hold-to-repeat) + `@adia-ai/a2ui-compose@0.4.3` + `@adia-ai/a2ui-retrieval@0.4.3` (process.env browser-compat fix) + `@adia-ai/a2ui-corpus@0.4.3` (catalog regen + chunks re-harvest with new settings-appearance pattern). Apart from the `llm-bridge.js` addition above, source byte-identical to v0.4.2.
89
112
 
90
113
  Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). See root [CHANGELOG.md `## [0.4.3]`](../../CHANGELOG.md) for the cut narrative.
91
-
92
114
  ## [0.4.2] - 2026-05-11
93
115
 
94
116
  ### Ride-along (no source changes)
@@ -96,7 +118,6 @@ Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4
96
118
  Lockstep PATCH cut alongside `@adia-ai/web-components@0.4.2` (`<input-ui type="number">` rewrite drops native `<input type=number>` wrapping) + `@adia-ai/web-modules@0.4.2` (`<editor-sidebar>` grid-track width-mirror fix). Source byte-identical to v0.4.1.
97
119
 
98
120
  Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4.0` covers `0.4.x` under semver). See root [CHANGELOG.md `## [0.4.2]`](../../../CHANGELOG.md) for the cut narrative.
99
-
100
121
  ## [0.4.1] - 2026-05-10
101
122
 
102
123
  ### Ride-along (no source changes)
@@ -104,7 +125,6 @@ Internal `@adia-ai/*` dep ranges stay at `^0.4.0` (patch-cut asymmetry — `^0.4
104
125
  Lockstep PATCH cut alongside `@adia-ai/web-modules@0.4.1` (simple cluster) + `@adia-ai/a2ui-validator@0.4.1` (Phase 3 foundation) + `@adia-ai/a2ui-corpus@0.4.1` (fragment metrics reconciliation). Source byte-identical to v0.4.0.
105
126
 
106
127
  Internal `@adia-ai/*` dep ranges bumped from `^0.4.0` to `^0.4.1`. See root [CHANGELOG.md `## [0.4.1]`](../../CHANGELOG.md) for the cut narrative.
107
-
108
128
  ## [0.4.0] - 2026-05-10
109
129
 
110
130
  ### Ride-along (no source changes)
@@ -112,25 +132,21 @@ Internal `@adia-ai/*` dep ranges bumped from `^0.4.0` to `^0.4.1`. See root [CHA
112
132
  Lockstep MINOR cut alongside `@adia-ai/web-modules@0.4.0` (ADR-0024 legacy shell shapes retired). Source byte-identical to v0.3.6.
113
133
 
114
134
  Internal `@adia-ai/*` dep ranges bumped from `^0.3.0` to `^0.4.0`. See root [CHANGELOG.md `## [0.4.0]`](../../CHANGELOG.md) for the cut narrative.
115
-
116
135
  ## [0.3.6] - 2026-05-10
117
136
 
118
137
  ### Ride-along (no source changes)
119
138
 
120
139
  Lockstep version bump only — source byte-identical to v0.3.5. Internal `@adia-ai/*` dep ranges remain at `^0.3.0`. See root [CHANGELOG.md `## [0.3.6]`](../../CHANGELOG.md) for the cut narrative.
121
-
122
140
  ## [0.3.5] - 2026-05-07
123
141
 
124
142
  ### Ride-along (no source changes)
125
143
 
126
144
  Lockstep version bump only — source byte-identical to v0.3.4. Internal `@adia-ai/*` dep ranges remain at `^0.3.0`. See root [CHANGELOG.md `## [0.3.5]`](../../CHANGELOG.md) for the cut narrative.
127
-
128
145
  ## [0.3.4] - 2026-05-07
129
146
 
130
147
  ### Ride-along (no source changes)
131
148
 
132
149
  Lockstep version bump only — source byte-identical to v0.3.3. Internal `@adia-ai/*` dep ranges remain at `^0.3.0`. See root [CHANGELOG.md `## [0.3.4]`](../../CHANGELOG.md) for the cut narrative.
133
-
134
150
  ## [0.3.3] - 2026-05-07
135
151
 
136
152
  **Lockstep cut.** All 9 published `@adia-ai/*` packages now share version `0.3.3`, governed by [`docs/specs/package-architecture.md` § 15](../../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` ranges stay at `^0.3.0` (patch-cut asymmetry — caret floats `0.3.x`).
@@ -171,7 +187,6 @@ Lockstep version bump only — source byte-identical to v0.3.3. Internal `@adia-
171
187
  - `adapters/client.test.js` (4) — createClient defaults + override
172
188
  - `llm-stub.test.js` (8) — StubLLMAdapter contract for free-tier evals
173
189
  - New `npm run test:llm` script for package-scoped runs.
174
-
175
190
  ## [0.3.2] - 2026-05-06
176
191
 
177
192
  **9-package lockstep patch cut to v0.3.2.** All lockstep members share
@@ -186,7 +201,6 @@ version only.
186
201
  ### Changed
187
202
 
188
203
  - `version`: `0.3.1` → `0.3.2`.
189
-
190
204
  ## [0.3.1] - 2026-05-06
191
205
 
192
206
  **9-package lockstep patch cut.** All 9 published `@adia-ai/*` packages bump 0.3.0 → 0.3.1 per [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy). Internal `@adia-ai/*` dep ranges remain at `^0.3.0` (covers `0.3.1` under semver — patch-cut asymmetry).
@@ -197,7 +211,6 @@ This package itself ships **no source changes** in v0.3.1. The cut bumps version
197
211
 
198
212
  - `version`: `0.3.0` → `0.3.1`.
199
213
  - Internal `@adia-ai/*` dep ranges: unchanged at `^0.3.0` (covers `0.3.1` under semver — patch-cut asymmetry).
200
-
201
214
  ## [0.3.0] - 2026-05-05
202
215
 
203
216
  **Initial release as the 9th `@adia-ai/*` lockstep package.** Joins the lockstep at the cut version. All 9 published `@adia-ai/*` packages now share one version, governed by [`docs/specs/package-architecture.md` § 15](../../docs/specs/package-architecture.md#15-versioning-policy).
package/README.md CHANGED
@@ -133,6 +133,31 @@ Either contract works — the client auto-detects which one your proxy
133
133
  implements by URL shape. Pick the one that matches your existing
134
134
  infrastructure.
135
135
 
136
+ #### Platform-native gateways (no proxy code needed)
137
+
138
+ If your VM runs on a managed platform that provides an **LLM Gateway**
139
+ natively (e.g. **exe.dev** at `http://169.254.169.254/gateway/llm/<provider>/<rest>`
140
+ on every VM, included in subscription), the cleanest production deploy
141
+ skips both contracts above and just **reverse-proxies `/api/llm/*`
142
+ through Caddy/nginx directly to the platform endpoint**. No API key
143
+ in app config, no hand-rolled proxy code, no per-cycle maintenance —
144
+ the platform handles auth, billing, model selection.
145
+
146
+ The AdiaUI repo demonstrates this pattern. `infra/caddy/llm-gateway.snippet`
147
+ holds a drop-in Caddyfile fragment for exe.dev VMs. The §188 (v0.5.5)
148
+ arc deleted ~155 LOC of hand-rolled passthrough from `server.js` in
149
+ favor of this approach. See `docs/specs/exe-dev-platform-reference.md`
150
+ + `docs/guides/production-llm-proxy-deployment.md` for the full
151
+ recipe and `docs/journal/2026/05/2026-05-14.md §185-§188` for the
152
+ architectural migration arc.
153
+
154
+ **Stage 0b recon checklist**: before hand-rolling a proxy, check whether
155
+ your platform provides one natively. Look for `LLM Gateway` / `AI` /
156
+ `Inference` in the platform's docs. Other platforms with similar
157
+ gateways: Cloudflare AI Gateway, AWS Bedrock proxy, Azure OpenAI
158
+ private endpoints. Match shape; check pricing; prefer platform-native
159
+ over hand-rolled when available.
160
+
136
161
  ## Subpath exports
137
162
 
138
163
  | Subpath | Purpose |
package/llm-bridge.js CHANGED
@@ -55,6 +55,51 @@ function resolveBaseUrl(provider) {
55
55
  return proxyMap[provider];
56
56
  }
57
57
 
58
+ /**
59
+ * §181 (v0.5.5) — Is the browser running on a production host (not a
60
+ * local Vite dev server)? Used by createAdapter() to decide whether
61
+ * to construct a real bridge that routes through the same-origin proxy
62
+ * even when no API key is visible to the browser.
63
+ */
64
+ function isProductionHost() {
65
+ if (!IS_BROWSER) return false;
66
+ const host = window.location?.hostname || '';
67
+ if (!host) return false;
68
+ if (host === 'localhost' || host === '127.0.0.1' || host === '0.0.0.0') return false;
69
+ if (host.endsWith('.local')) return false;
70
+ if (/^10\./.test(host)) return false;
71
+ if (/^192\.168\./.test(host)) return false;
72
+ if (/^172\.(1[6-9]|2\d|3[01])\./.test(host)) return false;
73
+ return true;
74
+ }
75
+
76
+ /**
77
+ * §181 (v0.5.5) — Build a bridge for the production-browser case
78
+ * where there's no client-side API key but the page can reach a
79
+ * same-origin proxy at /api/llm/<provider>/<rest>. The proxy strips
80
+ * the incoming auth header and injects its own server-side key.
81
+ *
82
+ * Uses a sentinel "browser-uses-proxy" string for the apiKey so the
83
+ * underlying client passes its non-empty check. The sentinel never
84
+ * reaches the upstream provider — the proxy discards it.
85
+ */
86
+ async function createBrowserProxyBridge(provider, modelOpt) {
87
+ const createClient = await getCreateClient();
88
+ if (!createClient) {
89
+ console.warn('LLM Bridge: LLM module not available. Using stub adapter.');
90
+ return new StubLLMAdapter();
91
+ }
92
+ const model = modelOpt || DEFAULT_MODELS[provider] || 'claude-haiku-4-5-20251001';
93
+ const proxyUrl = resolveBaseUrl(provider);
94
+ const client = createClient({
95
+ provider,
96
+ apiKey: 'browser-uses-server-side-proxy-key', // sentinel; proxy ignores it
97
+ model,
98
+ ...(proxyUrl ? { proxyUrl } : {}),
99
+ });
100
+ return new AdiaUILLMBridge(client, model, provider);
101
+ }
102
+
58
103
  // ── Factory ──────────────────────────────────────────────────────────────
59
104
 
60
105
  /**
@@ -73,7 +118,20 @@ export async function createAdapter(opts = {}) {
73
118
  const provider = opts.provider || getEnv('LLM_PROVIDER') || detectProvider();
74
119
  const model = opts.model || getEnv('LLM_MODEL') || undefined;
75
120
 
76
- if (provider === 'stub') return new StubLLMAdapter();
121
+ if (provider === 'stub') {
122
+ // §181 (v0.5.5) — browser on a production host: even though
123
+ // detectProvider() returned 'stub' (no env vars in the browser),
124
+ // the page can still make real LLM calls via the same-origin
125
+ // proxy at /api/llm/<provider>/<rest>. The proxy strips the
126
+ // incoming x-api-key / Authorization header and re-injects its
127
+ // own server-side key. The sentinel below is a non-empty
128
+ // placeholder so the bridge passes the !apiKey gate; it never
129
+ // reaches the upstream provider.
130
+ if (IS_BROWSER && isProductionHost()) {
131
+ return createBrowserProxyBridge('anthropic', opts.model);
132
+ }
133
+ return new StubLLMAdapter();
134
+ }
77
135
 
78
136
  // Resolve API key for the detected provider
79
137
  const apiKey = opts.apiKey || getEnv(`${provider.toUpperCase()}_API_KEY`) || getEnv('ANTHROPIC_API_KEY') || getEnv('OPENAI_API_KEY') || getEnv('GOOGLE_API_KEY');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adia-ai/llm",
3
- "version": "0.5.4",
4
- "description": "Provider-agnostic LLM client anthropic / openai / gemini adapters with a unified chat() + streamChat() facade. Used by AdiaUI's chat-shell and the A2UI generation pipeline; works in browser (with proxyUrl) and Node.",
3
+ "version": "0.5.6",
4
+ "description": "Provider-agnostic LLM client \u2014 anthropic / openai / gemini adapters with a unified chat() + streamChat() facade. Used by AdiaUI's chat-shell and the A2UI generation pipeline; works in browser (with proxyUrl) and Node.",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./index.js",