@agentproto/runtime 2.7.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -1
- package/dist/catalog-models.d.ts +62 -2
- package/dist/catalog-models.mjs +53 -5
- package/dist/catalog-models.mjs.map +1 -1
- package/dist/config.d.ts +47 -2
- package/dist/config.mjs.map +1 -1
- package/dist/index.d.ts +438 -19
- package/dist/index.mjs +2956 -961
- package/dist/index.mjs.map +1 -1
- package/dist/pr-provenance.d.ts +29 -1
- package/dist/pr-provenance.mjs +18 -3
- package/dist/pr-provenance.mjs.map +1 -1
- package/dist/release-check.d.ts +144 -0
- package/dist/release-check.mjs +149 -0
- package/dist/release-check.mjs.map +1 -0
- package/dist/resume-strategies.d.ts +48 -8
- package/dist/resume-strategies.mjs +37 -18
- package/dist/resume-strategies.mjs.map +1 -1
- package/dist/session-presence.d.ts +115 -0
- package/dist/session-presence.mjs +103 -0
- package/dist/session-presence.mjs.map +1 -0
- package/dist/session-story.mjs.map +1 -1
- package/dist/{spawn-defaults-DVgmfxWo.d.ts → spawn-defaults-DUnTfwVK.d.ts} +29 -10
- package/package.json +37 -25
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ A per-boot bearer token is generated automatically and written into `<workspace>
|
|
|
37
37
|
|
|
38
38
|
| Surface | URL | Notes |
|
|
39
39
|
|-------------------|------------------------------------------|--------------------------------------------------------|
|
|
40
|
-
| Health | `GET /health` |
|
|
40
|
+
| Health | `GET /health` | Daemon status: workspace, uptime, `startedAt`, version, build identity (`sha`, `builtAt`, `source`), pid, node path, entry point — always public |
|
|
41
41
|
| Events (SSE) | `GET /events` | RuntimeEvents stream |
|
|
42
42
|
| MCP | `POST /mcp` (Streamable HTTP) | Stateless mode; per-request transport |
|
|
43
43
|
| Conversations | `GET /conversations` / `GET /conversations/<id>` | Markdown bodies |
|
|
@@ -51,6 +51,8 @@ A per-boot bearer token is generated automatically and written into `<workspace>
|
|
|
51
51
|
| **PTY spawn** | **`POST /sessions/terminal`** | Needs `spawnPty` factory |
|
|
52
52
|
| **PTY attach** | **`WS /sessions/:id/pty`** | JSON frames `{kind:data|input|resize|exit|ping|pong}`; multi-subscriber, min-size resize, ring-buffer replay |
|
|
53
53
|
| SSE attach | `GET /sessions/:id/stream` | Line-by-line text events |
|
|
54
|
+
| **Chat stream** | `POST /sessions/:id/chat` | Send a follow-up prompt + fan RAW transcript into the `ai` v6 UI message stream |
|
|
55
|
+
| **Chat (create)** | `POST /sessions/chat` | Spawn a session + stream its first turn as chat (needs `resolveAgentAdapter`) |
|
|
54
56
|
| Kill / forget / gc | `POST /sessions/:id/kill`, `DELETE /sessions/:id`, `POST /sessions/gc` | SIGTERM, drop from registry, bulk archive terminal sessions |
|
|
55
57
|
|
|
56
58
|
### MCP tool surface
|
|
@@ -77,6 +79,44 @@ The `/mcp` endpoint exposes the core toolset plus several opt-in / feature-gated
|
|
|
77
79
|
- The optional `auth?: AuthOptions` field on `createGateway` is for the *tunnel* bearer (Cloudflare-fronted public surface), independent of the per-boot token. It gates `/mcp`, `/events`, `/conversations*`, and the heartbeat tick route, with a loopback bypass for requests that never crossed a tunnel (127.0.0.1/::1 with no `X-Forwarded-For`).
|
|
78
80
|
- `agentproto serve` wires this from `daemon.authToken` in `~/.agentproto/config.json` (or `--auth-token`) when set, so the gateway can boot already gated with a stable token — no `remote_enable` call, and it survives restarts since it isn't held in memory. `RemoteController`'s `remote_enable` MCP tool is a separate, complementary mechanism: it always mints a fresh in-memory token and opens a Cloudflare quick tunnel, and takes precedence over `daemon.authToken` while active.
|
|
79
81
|
|
|
82
|
+
## Worktree isolation policy
|
|
83
|
+
|
|
84
|
+
`agent_start.worktree` isolation is decided by `worktrees.isolation` in
|
|
85
|
+
`~/.agentproto/config.json` (or the `AGENTPROTO_WORKTREES_ISOLATION` env,
|
|
86
|
+
which wins). Three modes — see `WorktreeIsolationMode` in
|
|
87
|
+
[`config.ts`](./src/config.ts) and the decision matrix in
|
|
88
|
+
[`worktree-isolation.ts`](./src/worktree-isolation.ts):
|
|
89
|
+
|
|
90
|
+
- `"on-request"` (default) — isolates only when the caller explicitly passes
|
|
91
|
+
`worktree`.
|
|
92
|
+
- `"always"` — every **root** (depth-0) spawn is provisioned into a fresh
|
|
93
|
+
`<worktrees.root>/<repo>/<slug>` worktree on branch `wt/<slug>` cut from
|
|
94
|
+
`origin/main`, whether or not the caller asked. A `cwd` outside any git
|
|
95
|
+
repo has nothing to isolate, so it spawns plain.
|
|
96
|
+
- `"never"` — isolation is off; an explicit `worktree` request is rejected
|
|
97
|
+
loudly rather than silently ignored.
|
|
98
|
+
|
|
99
|
+
**Depth-0 only.** A spawn made *through* an orchestrator's scoped sub-gateway
|
|
100
|
+
(depth > 0 — including any `agent_start` a supervisor session issues itself,
|
|
101
|
+
even with `attach: false`) always inherits its parent's working tree; the
|
|
102
|
+
`always` policy never provisions a second worktree for it, and an explicit
|
|
103
|
+
`worktree` request at depth > 0 is rejected (use `sandbox` for child
|
|
104
|
+
isolation instead). To exercise `always` end-to-end you need a genuine root
|
|
105
|
+
spawn — e.g. `agentproto sessions start <adapter> --cwd <repo>` from a shell,
|
|
106
|
+
not an `agent_start` call made from inside another session.
|
|
107
|
+
|
|
108
|
+
**Config key gotcha:** the field is nested — `{"worktrees": {"isolation":
|
|
109
|
+
"always"}}` — NOT a top-level `worktreeIsolation` key. The loader
|
|
110
|
+
(`loadWorktreeIsolation` / `loadConfig`) silently ignores unknown top-level
|
|
111
|
+
keys, so a hand-edit that adds `worktreeIsolation` at the top level parses
|
|
112
|
+
fine, `agentproto config show` will happily print it back, and the policy
|
|
113
|
+
still silently resolves to the `"on-request"` default — no error, no spawn
|
|
114
|
+
behaviour change. Verify with `agentproto sessions --json` after a root spawn
|
|
115
|
+
and check the descriptor for `worktreePath`/`worktreeId`, not just the config
|
|
116
|
+
file's contents. Config is re-read from disk on every spawn (no caching), so
|
|
117
|
+
a fix to the key takes effect on the next `agent_start`/`sessions start` —
|
|
118
|
+
no daemon restart needed.
|
|
119
|
+
|
|
80
120
|
## SessionsRegistry
|
|
81
121
|
|
|
82
122
|
Exposed via `gateway.sessions`. Useful when you want to register externally-spawned children (e.g. tunnel-driven spawns) or programmatically attach without going through HTTP.
|
|
@@ -104,6 +144,52 @@ handle?.detach()
|
|
|
104
144
|
|
|
105
145
|
Other methods: `spawn` (raw `child_process.spawn`), `spawnAgent` (ACP), `register` (adopt an external `ChildProcess`), `attach` (SSE-style line subscription), `kill`, `forget`, `findByIdOrName`, `writeTerminalInput`, `readTerminalOutput`, `shutdown`. See [`sessions.ts`](./src/sessions.ts) for the typed surface.
|
|
106
146
|
|
|
147
|
+
## Chat routes — `POST /sessions/:id/chat` and `POST /sessions/chat`
|
|
148
|
+
|
|
149
|
+
The chat routes turn a daemon session into an AI-SDK v6 "UI message stream": they enqueue a user prompt on the session and fan the daemon's RAW transcript records (`events.jsonl`, the shapes `@agentproto/transcript-fixtures` documents) into a stream of `UIMessageChunk` objects — the protocol `ai`'s `createUIMessageStreamResponse` emits (`data: <JSON chunk>\n\n` SSE frames + `x-vercel-ai-ui-message-stream: v1`). The mapping is the pure `createTranscriptToUiMapper(sessionId)` from [`chat-stream.ts`](./src/chat-stream.ts); the background replay + live subscribe use the same `deliverRecordsExactlyOnce` machinery as `/sessions/:id/events/stream`, so a client sees no duplicate and no hole.
|
|
150
|
+
|
|
151
|
+
### Route contract
|
|
152
|
+
|
|
153
|
+
**`POST /sessions/:id/chat`** — enqueue a follow-up turn on an EXISTING session and stream its output.
|
|
154
|
+
|
|
155
|
+
- Method / path: `POST /sessions/:id/chat` (id-or-name, like `/sessions/:id/events`).
|
|
156
|
+
- Body (JSON): `{ "prompt": string, "interrupt"?: boolean, "source"?: string }`. `prompt` (non-empty) is required; `interrupt: true` cancels a mid-turn and redirects the same session (mirrors the MCP `agent_prompt` tool); `source` overrides the recorded provenance (default `http:chat`).
|
|
157
|
+
- Response: 200 with `Content-Type: text/event-stream`, header `x-vercel-ai-ui-message-stream: v1`, body a sequence of `data: <UIMessageChunk JSON>` frames ending in `data: [DONE]`. Adverse outcomes fail before any stream byte: `400` for a missing/invalid `prompt`, `404` for an unresolvable session, `409 {error:"session_not_alive"}` for a dead session, `409 {error:"prompt_rejected"}` when a busy session refused the prompt without `interrupt` (this is exactly the admission error `enqueuePrompt` throws).
|
|
158
|
+
|
|
159
|
+
**`POST /sessions/chat`** — create-and-chat variant. It REUSES the exact `/sessions/agent` spawn core: the same `spawnAgentSession` function and the same shared body→args mapper (`buildSpawnSessionHttpArgs`, kept in `http-server.ts` so the two surfaces can't drift from each other or from the MCP `agent_start` tool). Body is the `/sessions/agent` spawn fields (e.g. `adapter`, `cwd`, `workspaceSlug`, `orchestrator`, …) **plus a required `prompt`** (the opening user message). On success it does NOT return a 201 descriptor like `/sessions/agent` — it immediately bleeds the session's first turn into the chat stream above. Requires the host-injected `resolveAgentAdapter` (otherwise `501`).
|
|
160
|
+
|
|
161
|
+
Why not a second, lighter spawn path? Because the spawn surface (orchestrator / mcpServers / worktree / access / presets / …) is genuinely large and already validated in one place; duplicating a slice of it here would create two divergent spawn contracts. Sharing `spawnAgentSession` + `buildSpawnSessionHttpArgs` means `/sessions/chat` inherits every fix to either by construction. The cost is that `/sessions/chat` accepts the same (wide) body as `/sessions/agent`; callers wanting a minimal contract should use `/sessions/agent` (to create) then `/sessions/:id/chat` (to stream follow-ups).
|
|
162
|
+
|
|
163
|
+
### Record → `UIMessageChunk` mapping
|
|
164
|
+
|
|
165
|
+
Replayed in `seq` order, each RAW record maps as follows (see `chat-stream.ts` for the authoritative switch, under conformance test):
|
|
166
|
+
|
|
167
|
+
| RAW kind | Emitted chunk(s) |
|
|
168
|
+
|---|---|
|
|
169
|
+
| `user-prompt` | none (echo of the user's input, not assistant output) |
|
|
170
|
+
| `thought` | `reasoning-start` (once per run) + `reasoning-delta` (per fragment) + `reasoning-end` at the segment boundary |
|
|
171
|
+
| `text-delta` | `text-start` (once per run) + `text-delta` (per fragment) + `text-end` at the boundary |
|
|
172
|
+
| `tool-call` | `tool-input-available {toolCallId, toolName, input: arguments}` (`isUpdate` re-emits with the fresh snapshot) |
|
|
173
|
+
| `tool-result` | `tool-output-available {toolCallId, output: result}` (RAW `result`, unwrapped) — or `tool-output-error {toolCallId, errorText}` when `isError` |
|
|
174
|
+
| `tool-call-record` | none — DELIBERATE skip (bookkeeping that duplicates `tool-call` + `tool-result`) |
|
|
175
|
+
| `permission-resolved` | custom data part `data-tool-call-approval` (CONDITION 4, below) |
|
|
176
|
+
| `turn-end` | `finish` (`finishReason: "stop"` for `reason: "turn-complete"`; omitted when nothing maps cleanly) |
|
|
177
|
+
| any other kind | `{type:"error", errorText:"unhandled transcript record kind: <kind>"}` + a server-side `console.error` (CONDITION 2 — never a silent swallow) |
|
|
178
|
+
|
|
179
|
+
### Custom data part: `data-tool-call-approval`
|
|
180
|
+
|
|
181
|
+
A resolved permission decision (`permission-resolved` records) travels as a custom data part:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{"type":"data-tool-call-approval","data":{"toolCallId":"call_fixture_01","decision":"allow","optionId":"once"}}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
We use a `data-*` custom part rather than the `ai` v6 native `tool-approval-request` chunk (which has `approvalId`/`signature`) because the latter models a PENDING approval request (before execution), whereas our transcript only ever contains the already-RESOLVED final decision (`permission-resolved`). The `tool-call-approval` name is aligned with Mastra's `tool-call-approval` vocabulary for future consistency, not with the native chunk, which does not match our data. The generic data-part type in `ai` v6 is `DataUIMessageChunk` = `{ type: \`data-${NAME}\`, id?, data, transient? }`, so `data-tool-call-approval` is a first-class protocol citizen.
|
|
188
|
+
|
|
189
|
+
### Known gap — pending approval requests do NOT flow through this route yet
|
|
190
|
+
|
|
191
|
+
Only `permission-resolved` (the final decision) transits the chat stream today. PENDING permission requests (the same class of thing `ai` models as `tool-approval-request`, and Mastra as `tool-call-approval`) are **not** yet surfaced by this route — there is no per-turn "request is waiting for a human" event in this stream. That is a known gap, out of scope for this work package, not an oversight; closing it (emitting a pending-approval data part when the daemon holds on a permission) is future work.
|
|
192
|
+
|
|
107
193
|
## License
|
|
108
194
|
|
|
109
195
|
MIT — see [LICENSE](../../LICENSE).
|
package/dist/catalog-models.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AuthProfile } from '@agentproto/auth';
|
|
2
|
-
import { A as AdapterAuthDescriptor } from './spawn-defaults-
|
|
2
|
+
import { A as AdapterAuthDescriptor } from './spawn-defaults-DUnTfwVK.js';
|
|
3
3
|
import { R as RouteSpec } from './session-config-DbWP9RRj.js';
|
|
4
4
|
import '@agentproto/model-catalog';
|
|
5
5
|
import './context-continuity-ib9_bVYM.js';
|
|
@@ -90,6 +90,14 @@ interface CatalogRoute {
|
|
|
90
90
|
ref: string;
|
|
91
91
|
baseUrl: string | null;
|
|
92
92
|
pricing: CatalogPricing | null;
|
|
93
|
+
/** Human-readable max input tokens (e.g. `"1M"`, `"200k"`), from the
|
|
94
|
+
* live-synced CONTEXT_WINDOWS table (`resolveContextWindow`); null when
|
|
95
|
+
* no synced provider carries this id. Consumers wanting the raw integer
|
|
96
|
+
* can re-resolve via `resolveContextWindow(ref product)` or parse. */
|
|
97
|
+
contextWindow: string | null;
|
|
98
|
+
/** Human-readable max output tokens (same source/format), null when the
|
|
99
|
+
* source doesn't publish a completion cap for this id. */
|
|
100
|
+
maxOutput: string | null;
|
|
93
101
|
runnable: boolean;
|
|
94
102
|
eligibleProfiles: string[];
|
|
95
103
|
adapterModes: string[];
|
|
@@ -237,6 +245,58 @@ interface ModelWalletEligibility {
|
|
|
237
245
|
* guard only REJECTS; it never substitutes a wallet the operator didn't name.
|
|
238
246
|
*/
|
|
239
247
|
declare function checkModelWalletEligibility(model: string, walletRoute: string): ModelWalletEligibility;
|
|
248
|
+
/** Verdict of the spawn-time adapter-capability guard
|
|
249
|
+
* ({@link checkModelAdapterEligibility}). */
|
|
250
|
+
interface ModelAdapterEligibility {
|
|
251
|
+
ok: boolean;
|
|
252
|
+
/** Other installed adapters whose catalog row already curates this exact
|
|
253
|
+
* model on this exact route — the actionable set to re-spawn onto. Empty
|
|
254
|
+
* when `ok`, or when NO installed adapter (this one included) curates the
|
|
255
|
+
* combination — nobody has proven it reachable at all, so this guard has
|
|
256
|
+
* nothing to reject on. */
|
|
257
|
+
compatibleAdapters: string[];
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Adapter-capability spawn guard: the money-safety guard above
|
|
261
|
+
* ({@link checkModelWalletEligibility}) proves the resolved ROUTE can bill
|
|
262
|
+
* `model`; it says nothing about whether THIS adapter's own manifest can
|
|
263
|
+
* actually reach it there. A fixed hand-curated client (claude-code's ACP
|
|
264
|
+
* wrapper validates every model id against its own live selector and 404s on
|
|
265
|
+
* anything it doesn't recognize) can be routeSelection:"free" — genuinely able
|
|
266
|
+
* to reach several gateways — while still only supporting a small, explicitly
|
|
267
|
+
* vetted model list on each one. A pass-through client (opencode/mastracode/
|
|
268
|
+
* hermes/jcode, routeSelection:"derived-from-model") instead auto-derives a
|
|
269
|
+
* broad curated list straight from the pricing catalog, so it ends up
|
|
270
|
+
* supporting far more of a gateway's models without needing a per-model
|
|
271
|
+
* allowlist maintained by hand.
|
|
272
|
+
*
|
|
273
|
+
* Takes the SAME `CatalogModelsResponse` shape `buildCatalogModels` (and
|
|
274
|
+
* therefore `catalog_models`) produces — reusing that exact join, never a
|
|
275
|
+
* parallel per-adapter table — and looks up whether `adapterSlug` is among
|
|
276
|
+
* the resolved (vendor, product, route) row's `adapters`.
|
|
277
|
+
*
|
|
278
|
+
* `ok:true` when EITHER no installed adapter's catalog row covers this exact
|
|
279
|
+
* model+route (nobody has proven it servable at all — the same never-reject-
|
|
280
|
+
* an-unknown-combination stance {@link checkModelWalletEligibility} takes), OR
|
|
281
|
+
* `adapterSlug` is already among the row's adapters. `ok:false` only when the
|
|
282
|
+
* row exists and excludes `adapterSlug` — a proven "wrong client for this
|
|
283
|
+
* model" mismatch, with the row's other adapters (if any) as the actionable
|
|
284
|
+
* alternative.
|
|
285
|
+
*/
|
|
286
|
+
declare function checkModelAdapterEligibility(catalog: CatalogModelsResponse, adapterSlug: string, model: string, route: string): ModelAdapterEligibility;
|
|
287
|
+
/**
|
|
288
|
+
* The actionable fail-fast message for {@link checkModelAdapterEligibility} —
|
|
289
|
+
* names the adapters that DO already curate the model on this route (when any
|
|
290
|
+
* do) so the operator can re-spawn without opening the catalog by hand. Never
|
|
291
|
+
* auto-switches adapters for the operator; only rejects.
|
|
292
|
+
*/
|
|
293
|
+
declare function modelAdapterIncompatibleMessage(opts: {
|
|
294
|
+
prefix: string;
|
|
295
|
+
adapter: string;
|
|
296
|
+
model: string;
|
|
297
|
+
route: string;
|
|
298
|
+
compatibleAdapters: string[];
|
|
299
|
+
}): string;
|
|
240
300
|
/**
|
|
241
301
|
* The actionable fail-fast message shared by both spawn paths (session-spawn +
|
|
242
302
|
* session-restart-core) so they never drift. Names the wallet that couldn't
|
|
@@ -252,4 +312,4 @@ declare function modelWalletIneligibleMessage(opts: {
|
|
|
252
312
|
suggestedRoutes: string[];
|
|
253
313
|
}): string;
|
|
254
314
|
|
|
255
|
-
export { type BuildCatalogModelsInput, type CatalogAdapterInput, type CatalogAdapterModelInput, type CatalogModelsQuery, type CatalogModelsResponse, type CatalogPricing, type CatalogProduct, type CatalogRoute, type CatalogRouteSummary, type CatalogVendor, type ModelWalletEligibility, RouteSpec, buildCatalogModels, checkModelWalletEligibility, modelWalletIneligibleMessage, modelWithRoute, reconcileModelRoute, resolveEffectiveRoute, serviceableModelRoutes, suggestModelSlugs };
|
|
315
|
+
export { type BuildCatalogModelsInput, type CatalogAdapterInput, type CatalogAdapterModelInput, type CatalogModelsQuery, type CatalogModelsResponse, type CatalogPricing, type CatalogProduct, type CatalogRoute, type CatalogRouteSummary, type CatalogVendor, type ModelAdapterEligibility, type ModelWalletEligibility, RouteSpec, buildCatalogModels, checkModelAdapterEligibility, checkModelWalletEligibility, modelAdapterIncompatibleMessage, modelWalletIneligibleMessage, modelWithRoute, reconcileModelRoute, resolveEffectiveRoute, serviceableModelRoutes, suggestModelSlugs };
|
package/dist/catalog-models.mjs
CHANGED
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
import { eligibleProfiles } from '@agentproto/auth';
|
|
2
2
|
import { tryParseModelRef, formatModelRef, resolveLlmModelRoute } from '@agentproto/model-catalog/route-identity';
|
|
3
|
-
import { getModelProvider, resolvePricingExact, LLM_PRICING_CATALOG, MODEL_ALIASES } from '@agentproto/model-catalog/llm';
|
|
3
|
+
import { resolveContextWindow, formatTokens, getModelProvider, resolvePricingExact, LLM_PRICING_CATALOG, MODEL_ALIASES } from '@agentproto/model-catalog/llm';
|
|
4
4
|
import { getAnthropicGatewayPreset } from '@agentproto/provider-presets';
|
|
5
|
+
import * as providers_store_star from '@agentproto/providers-store';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @agentproto/runtime v0.1.0-alpha
|
|
8
9
|
* Long-running gateway: MCP server + HTTP transport + HEARTBEAT autonomy + conversation persistence over a workspace dir.
|
|
9
10
|
*/
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget);
|
|
24
|
+
|
|
25
|
+
// src/providers-store.ts
|
|
26
|
+
var providers_store_exports = {};
|
|
27
|
+
__reExport(providers_store_exports, providers_store_star);
|
|
10
28
|
|
|
29
|
+
// src/spawn-defaults.ts
|
|
30
|
+
function subscriptionSurfaceFor(sub, endpoint) {
|
|
31
|
+
if (sub === void 0) return void 0;
|
|
32
|
+
if (!Array.isArray(sub)) {
|
|
33
|
+
return sub.provider === void 0 || endpoint === void 0 || sub.provider === endpoint ? sub : void 0;
|
|
34
|
+
}
|
|
35
|
+
return sub.find((s) => s.provider !== void 0 && s.provider === endpoint) ?? sub.find((s) => s.provider === void 0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/catalog-models.ts
|
|
11
39
|
var WIDENING_ROUTES = ["openrouter", "requesty", "huggingface"];
|
|
12
40
|
var VENDOR_COMPATIBILITY_ROUTES = {
|
|
13
41
|
xai: ["xai", "xai-anthropic"]
|
|
@@ -73,10 +101,11 @@ function resolveModelId(id) {
|
|
|
73
101
|
pricing: null
|
|
74
102
|
};
|
|
75
103
|
}
|
|
76
|
-
function methodsForDirect(descriptor) {
|
|
104
|
+
function methodsForDirect(descriptor, endpoint) {
|
|
77
105
|
const methods = [];
|
|
78
|
-
if (descriptor?.authSubscription
|
|
106
|
+
if (subscriptionSurfaceFor(descriptor?.authSubscription, endpoint) !== void 0) {
|
|
79
107
|
methods.push("oauth-bearer");
|
|
108
|
+
}
|
|
80
109
|
if (descriptor?.provider || descriptor?.modelDerivedApiKey) methods.push("api-key");
|
|
81
110
|
return methods;
|
|
82
111
|
}
|
|
@@ -89,7 +118,7 @@ function curatedContributions(adapters) {
|
|
|
89
118
|
for (const model of adapter.models) {
|
|
90
119
|
const resolved = resolveModelId(model.id);
|
|
91
120
|
const route = model.mode ?? model.provider ?? resolved.directRoute;
|
|
92
|
-
const methods = isDirectRoute(route, model.mode, resolved) ? methodsForDirect(adapter.authDescriptor) : ["api-key"];
|
|
121
|
+
const methods = isDirectRoute(route, model.mode, resolved) ? methodsForDirect(adapter.authDescriptor, route) : ["api-key"];
|
|
93
122
|
out.push({
|
|
94
123
|
vendor: resolved.vendor,
|
|
95
124
|
product: resolved.product,
|
|
@@ -259,11 +288,19 @@ function buildCatalogModels(input) {
|
|
|
259
288
|
) : [];
|
|
260
289
|
const runnable = eligible.length > 0;
|
|
261
290
|
if (query.runnableOnly && !runnable) continue;
|
|
291
|
+
const ctx = resolveContextWindow(row.product);
|
|
262
292
|
const route = {
|
|
263
293
|
route: row.route,
|
|
264
294
|
ref: row.ref,
|
|
265
295
|
baseUrl: row.baseUrl,
|
|
266
296
|
pricing: row.pricing,
|
|
297
|
+
// Live-synced context window (max input) + max output, when a synced
|
|
298
|
+
// provider (Anthropic/Groq/xAI/Moonshot/Mistral/Google) carries this
|
|
299
|
+
// id — null otherwise. All CONTEXT_WINDOWS providers get this, not
|
|
300
|
+
// only Anthropic. Formatted for display (`1M`/`200k`); consumers
|
|
301
|
+
// needing the raw integer resolve it themselves.
|
|
302
|
+
contextWindow: formatTokens(ctx?.contextWindow),
|
|
303
|
+
maxOutput: formatTokens(ctx?.maxOutput),
|
|
267
304
|
runnable,
|
|
268
305
|
eligibleProfiles: eligible.map((p) => p.id),
|
|
269
306
|
adapterModes: row.adapterModes,
|
|
@@ -375,6 +412,17 @@ function checkModelWalletEligibility(model, walletRoute) {
|
|
|
375
412
|
}
|
|
376
413
|
return { ok: false, suggestedRoutes: serviceable.filter((r) => r !== walletRoute) };
|
|
377
414
|
}
|
|
415
|
+
function checkModelAdapterEligibility(catalog, adapterSlug, model, route) {
|
|
416
|
+
const target = resolveModelId(model);
|
|
417
|
+
const routeEntry = catalog.vendors.find((v) => v.vendor === target.vendor)?.products.find((p) => p.product === target.product)?.routes.find((r) => r.route === route);
|
|
418
|
+
if (!routeEntry) return { ok: true, compatibleAdapters: [] };
|
|
419
|
+
if (routeEntry.adapters.includes(adapterSlug)) return { ok: true, compatibleAdapters: [] };
|
|
420
|
+
return { ok: false, compatibleAdapters: routeEntry.adapters };
|
|
421
|
+
}
|
|
422
|
+
function modelAdapterIncompatibleMessage(opts) {
|
|
423
|
+
const alternative = opts.compatibleAdapters.length > 0 ? `Adapters that already support it on "${opts.route}": ${opts.compatibleAdapters.map((a) => `"${a}"`).join(", ")} \u2014 re-spawn with one of those instead.` : `No installed adapter currently supports it on "${opts.route}" either \u2014 check \`catalog_models\` for a route this model IS servable on.`;
|
|
424
|
+
return `${opts.prefix}: adapter "${opts.adapter}" does not declare support for model "${opts.model}" on route "${opts.route}" and would 404/reject upstream even though that route can bill it. ${alternative} This guard only rejects; it never switches adapters for you.`;
|
|
425
|
+
}
|
|
378
426
|
function modelWalletIneligibleMessage(opts) {
|
|
379
427
|
const wallet = opts.walletMode ? `"${opts.walletRoute}" ${opts.walletMode} wallet` : `"${opts.walletRoute}" wallet`;
|
|
380
428
|
const primary = opts.suggestedRoutes[0] ?? "a gateway route";
|
|
@@ -382,6 +430,6 @@ function modelWalletIneligibleMessage(opts) {
|
|
|
382
430
|
return `${opts.prefix}: model "${opts.model}" is not serviceable on the resolved ${wallet} (adapter "${opts.adapter}") and would 404 upstream. This model bills route "${primary}"${also} \u2014 re-spawn on it: set route.gateway="${primary}" with an eligible "${primary}" api-key profile (access.profileRef). This guard only rejects; it never switches wallets for you.`;
|
|
383
431
|
}
|
|
384
432
|
|
|
385
|
-
export { buildCatalogModels, checkModelWalletEligibility, modelWalletIneligibleMessage, modelWithRoute, reconcileModelRoute, resolveEffectiveRoute, serviceableModelRoutes, suggestModelSlugs };
|
|
433
|
+
export { buildCatalogModels, checkModelAdapterEligibility, checkModelWalletEligibility, modelAdapterIncompatibleMessage, modelWalletIneligibleMessage, modelWithRoute, reconcileModelRoute, resolveEffectiveRoute, serviceableModelRoutes, suggestModelSlugs };
|
|
386
434
|
//# sourceMappingURL=catalog-models.mjs.map
|
|
387
435
|
//# sourceMappingURL=catalog-models.mjs.map
|