@ultimat3/ai 23.0.0 → 25.0.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/CLAUDE.md +96 -32
- package/README.md +249 -75
- package/package.json +12 -11
- package/src/agent-facts.ts +9 -2
- package/src/agent-transcript.ts +1 -1
- package/src/agent.ts +43 -25
- package/src/budget.ts +178 -51
- package/src/content-blocks.ts +227 -0
- package/src/content-errors.ts +67 -0
- package/src/echo-provider.ts +102 -0
- package/src/embeddings.ts +40 -21
- package/src/error-body.ts +38 -3
- package/src/errors.ts +19 -11
- package/src/eval-baseline.ts +5 -1
- package/src/eval-errors.ts +3 -1
- package/src/evals.ts +1 -1
- package/src/failure-disclosure.ts +90 -0
- package/src/gateway.ts +73 -37
- package/src/hive-pool.ts +24 -16
- package/src/hive-result.ts +4 -1
- package/src/hive.ts +15 -5
- package/src/index.ts +66 -25
- package/src/llm-cache.ts +4 -3
- package/src/llm.ts +27 -65
- package/src/model-resolve.ts +79 -0
- package/src/models.ts +125 -111
- package/src/openai-body.ts +13 -2
- package/src/openai-messages.ts +86 -15
- package/src/openai-provider.ts +28 -31
- package/src/openai-wire.ts +11 -3
- package/src/pg-vector-sql.ts +11 -7
- package/src/pg-vector.ts +21 -10
- package/src/prompt-fence.ts +128 -0
- package/src/prompt.ts +43 -13
- package/src/provider.ts +82 -117
- package/src/rag.ts +17 -9
- package/src/remote-embedder.ts +15 -41
- package/src/respond.ts +86 -0
- package/src/runtime.ts +12 -14
- package/src/scorers.ts +14 -3
- package/src/tool-idempotency.ts +79 -0
- package/src/tools.ts +52 -39
- package/src/vector-scope.ts +46 -4
- package/src/vector.ts +117 -41
- package/src/wire.ts +7 -2
- package/src/fix-line.evals.ts +0 -35
- package/src/fix-line.ts +0 -27
- package/src/openai-models.ts +0 -84
package/CLAUDE.md
CHANGED
|
@@ -4,7 +4,8 @@ Tier 4. May import tier 0–3: `core schema i18n money time cache seo entity pol
|
|
|
4
4
|
query jobs realtime`. **Never** `mcp manifest render pwa ui admin testing cli`.
|
|
5
5
|
|
|
6
6
|
Declared today: `action` (the primitive `llm()` returns), `cache` (semantic cache), `core`,
|
|
7
|
-
`db` (pgvector), `
|
|
7
|
+
`db` (pgvector), `http` (`statusFor`, so a tool result redacts a 5xx cause as a problem document
|
|
8
|
+
does), `jobs` (`agentJob()`), `money`, `policy`, `schema`, `time`.
|
|
8
9
|
|
|
9
10
|
`jobs` is declared for `agentJob()`: the action→job bridge has to live at tier 4+.
|
|
10
11
|
|
|
@@ -14,14 +15,24 @@ rather than imported. Same contract, two wire formats — and the same *decision
|
|
|
14
15
|
local `=== true`. An in-app agent and an external one must be offered exactly the same tools.
|
|
15
16
|
|
|
16
17
|
**And the same NAME**: `toLlmTool` passes `action.name` through untouched (the export name
|
|
17
|
-
`@ultimat3/mcp` serves); `llm()` and `agent()`
|
|
18
|
+
`@ultimat3/mcp` serves); `llm()` and `agent()` tool names are verbatim too. Never derive one.
|
|
19
|
+
|
|
20
|
+
**And the same TOOL**: `asProjectableAction` and mcp's `primitiveFromAction` are two copies the
|
|
21
|
+
tier table forces; `scripts/ai-mcp-tool-parity.test.ts` holds them equal — name, description,
|
|
22
|
+
exposure, the whole input schema (an idempotent action's reserved `idempotencyKey` argument,
|
|
23
|
+
`tool-idempotency.ts`), the policy verdict and the replay. A field one adds, the other adds.
|
|
18
24
|
|
|
19
25
|
## Owns
|
|
20
26
|
|
|
21
27
|
| File | Job |
|
|
22
28
|
|---|---|
|
|
23
|
-
| `models.ts` | the model REGISTRY: `registerModel`, limits, prices, the reasoning controls each one accepts |
|
|
24
|
-
| `
|
|
29
|
+
| `models.ts` | the model REGISTRY: `registerModel`, limits, prices, the reasoning controls each one accepts — and NO row of its own |
|
|
30
|
+
| `model-resolve.ts` | `resolveModel` / `modelForCall` — declaration → prompt → gateway `defaultModel`, none is `X_AI_MODEL_UNRESOLVED` naming all three; `describedModel` for a published fact |
|
|
31
|
+
| `model-fixture.ts` | the rows this package's own suites register (`useFixtureModels()`), as an app's `models.ts` would. Not shipped (`!src/**/*-fixture.ts`) |
|
|
32
|
+
| `provider.ts` | `Provider` interface, the request half, the money arithmetic, `anthropicProvider()` |
|
|
33
|
+
| `echo-provider.ts` | `echoProvider()`, the deterministic double — split from `provider.ts` at its line ceiling |
|
|
34
|
+
| `content-blocks.ts` | the `image` / `document` block shapes, the screen every request runs them through, their token estimate |
|
|
35
|
+
| `content-errors.ts` | the `X_AI_CONTENT_UNSUPPORTED` class; its code and title belong in `errors.ts` |
|
|
25
36
|
| `wire.ts` | the response half: `usage` / `stop_reason` shapes, and the SSE `MessageStream` |
|
|
26
37
|
| `error-body.ts` | what a failure body SAYS (`detailOf`) and what must never survive into it (`withoutKey`) — one copy, both transports |
|
|
27
38
|
| `sse.ts` | Server-Sent Events framing — protocol only, knows nothing about Anthropic |
|
|
@@ -29,12 +40,12 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
29
40
|
| `openai-messages.ts` | the format mapping's request half: `AiMessage` blocks → OpenAI messages, `LlmTool` → functions, `tool_choice` |
|
|
30
41
|
| `openai-body.ts` | one chat-completions body, and the per-model reasoning mapping |
|
|
31
42
|
| `openai-wire.ts` | its response half: one completion, and `ChatCompletionStream` (fragmented tool calls, trailing usage, `[DONE]`) |
|
|
32
|
-
| `openai-models.ts` | the OpenAI-format price rows, registered through the same `registerModel` |
|
|
33
43
|
| `gateway.ts` | routing, retries, cache, budget wiring |
|
|
34
44
|
| `budget.ts` | token ledgers per request/actor/org, ALS carrier |
|
|
35
|
-
| `prompt.ts` | `definePrompt`,
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
45
|
+
| `prompt.ts` | `definePrompt`, `promptHash`, version registry |
|
|
46
|
+
| `prompt-fence.ts` | a template's data fences: which tag pairs enclose each slot (`promptFences`), and the closer-breaking `render` applies inside one |
|
|
47
|
+
| `embeddings.ts` | `Embedder`, `hashEmbedder()`, cosine helpers |
|
|
48
|
+
| `remote-embedder.ts` | `remoteEmbedder()` — the production `/v1/embeddings` client |
|
|
38
49
|
| `evals.ts` | `defineEval`, the run, the baseline gate, prompt coverage |
|
|
39
50
|
| `eval-baseline.ts` | the recorded scores: path, read/write, what counts as a regression |
|
|
40
51
|
| `scorers.ts` | what a `Scorer` is, the built-in ones, and `llmJudge` |
|
|
@@ -42,10 +53,13 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
42
53
|
| `pg-vector.live.test.ts` | the same store against a real pgvector — DDL, fusion, scope, plan |
|
|
43
54
|
| `vector-scope.ts` | the tenant + policy envelope, and the tighten-only derive rule |
|
|
44
55
|
| `pg-vector-sql.ts` | every pgvector statement: DDL, upsert, cosine, FTS, RRF fusion |
|
|
45
|
-
| `pg-vector.ts` | `
|
|
56
|
+
| `pg-vector.ts` | `postgresVectorStore()` — the production store |
|
|
46
57
|
| `rag.ts` | chunker, retriever, reranker, budgeted context assembler |
|
|
47
58
|
| `tools.ts` | action → LLM tool definition; the `AgentTool` union and `asProjectableAction`; `runLlmToolCall` |
|
|
59
|
+
| `tool-idempotency.ts` | an idempotent action's reserved `idempotencyKey` tool argument — MCP's, restated (same tier), held equal by the parity test |
|
|
60
|
+
| `failure-disclosure.ts` | `discloseFailure` — the ONE rule for what of a throw a remote reader sees (tool result, hive member); the withheld half is logged as code, name and stack frames — never its message or cause |
|
|
48
61
|
| `llm.ts` | `llm()` — the model call, declared as an `action`; and what a streamed answer must satisfy |
|
|
62
|
+
| `respond.ts` | the `respond` tool for one `output` (a non-object one wrapped in `{ value }`), its reader, and `parseJsonish` |
|
|
49
63
|
| `llm-stream.ts` | `.stream()`'s plumbing: the sink, the ambient mark, the one-turn drive |
|
|
50
64
|
| `agent.ts` | `agent()` — the tool loop, declared as an `action` |
|
|
51
65
|
| `agent-transcript.ts` | what one turn leaves in the transcript: the assistant replay, the tool results, the correction |
|
|
@@ -72,12 +86,24 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
72
86
|
off. `Gateway.generate`/`stream` screen it at the one seam, `registerModel` screens `maxOutput`, and
|
|
73
87
|
`llm()`/`agent()` screen theirs at DECLARATION.
|
|
74
88
|
- **A bound is screened under the key the DECLARATION uses** (`budget.tokensPerRun` in `limitsOf`
|
|
75
|
-
before the ledger's `request`; `retrieve()`'s own `k`; `
|
|
89
|
+
before the ledger's `request`; `retrieve()`'s own `k`; `remoteEmbedder()`'s `maxResponseBytes`).
|
|
76
90
|
- A per-call budget `derive`s from the ambient ledger and only TIGHTENS. **A derived ledger reports
|
|
77
91
|
back up the chain** (every debit on every ancestor; `reserve` checks each `request` scope); the
|
|
78
|
-
STORE is written once, by the ledger the call was made on
|
|
92
|
+
STORE is written once, by the ledger the call was made on.
|
|
93
|
+
- **`reserve` debits, then checks — never a read-then-write across an `await`.** The in-memory scopes
|
|
94
|
+
check and debit synchronously; the store's go through `BudgetStore.take(key, tokens, limit)`, which
|
|
95
|
+
is REQUIRED and must be atomic (no default over `spent` + `add` — that pair is the race). A refusal
|
|
96
|
+
at a later scope gives back what the earlier ones took.
|
|
97
|
+
- **The store is touched only for a scope whose ceiling is DECLARED** (`takeScope`, `meteredKeys`):
|
|
98
|
+
every call carries keys, so a write per key grew the default store by one entry per caller.
|
|
99
|
+
`memoryBudgetStore()` has no window and no eviction (eviction is a ceiling bypass); a counter back
|
|
100
|
+
at zero is deleted.
|
|
101
|
+
- **`llm()`, `agent()`, `hive()` root as `currentBudget() ?? gateway.callLedger(budgetKeysFor(ctx.actor))`**
|
|
102
|
+
— the gateway's ceilings, keyed `actor:<kind>:<id>` / `org:<orgId>` (none for an `undefined`/`null`/`''`
|
|
103
|
+
org — the `orgless` test `query`'s cache uses). `callLedger` is required on
|
|
104
|
+
`Gateway`; `hive()` asks `installedGateway()` so a hive of plain actions needs none.
|
|
79
105
|
- **`BudgetStore` is where `actor` and `org` live; the default is per PROCESS**
|
|
80
|
-
(`
|
|
106
|
+
(`providerGateway({ budgetStore })`). `add` takes a negative `tokens` for a release — a store that
|
|
81
107
|
clamps at zero leaks the ceiling.
|
|
82
108
|
- Cost is `Money` (integer minor units), rounded **up**. A budget throws `X_AI_BUDGET_EXCEEDED`
|
|
83
109
|
**before** the provider call. Never truncate. List price over-reserves (no introductory prices).
|
|
@@ -88,6 +114,9 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
88
114
|
|
|
89
115
|
- **`llm()` returns an `action`**, never a ninth primitive; it adds only the model half: render the
|
|
90
116
|
prompt, project `output` into the one `respond` tool, reserve the budget, consult the semantic cache.
|
|
117
|
+
- **A non-object `output` is wrapped in `{ value }` and unwrapped** (`respond.ts`) — a tool's input is
|
|
118
|
+
an object; never refused, because `.stream()` serves `output: t.string`. `parseJsonish` is two
|
|
119
|
+
`indexOf` scans, never a backtracking pattern.
|
|
91
120
|
- `src/index.ts` re-exports `t` from `@ultimat3/schema` **verbatim** (`index.test.ts`).
|
|
92
121
|
- One repair turn, then `X_LLM_OUTPUT_INVALID`. A refusal (a 200 with no answer) is `X_LLM_REFUSED`
|
|
93
122
|
before the parse, carrying `stopDetails.category`; a truncated invalid answer is `X_LLM_TRUNCATED`.
|
|
@@ -96,7 +125,7 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
96
125
|
`ctx.locale` (in the unconditional half). **The default scope is the calling ACTOR**
|
|
97
126
|
(`JSON.stringify([actor.kind, actor.id, actor.orgId ?? null])`); `scope` receives
|
|
98
127
|
`{ input, ctx }`; widening is `scope: () => 'global'`. The instance map is bounded
|
|
99
|
-
(`
|
|
128
|
+
(core's `MAX_CACHED_FORMATTERS`).
|
|
100
129
|
- `cache.invalidates` is not on `llm()`: a `SemanticCache` is not a `CacheTier`. Version bump + `ttl`
|
|
101
130
|
is the invalidation.
|
|
102
131
|
- The gateway is ambient (`configureAi`); absent at call time is `X_AI_GATEWAY_MISSING`, never a
|
|
@@ -111,11 +140,20 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
111
140
|
- **Fallback is across PROVIDERS serving one model, never across models**; `GenerateResult.provider`
|
|
112
141
|
is stamped by the gateway and put on the span as `llm.provider`.
|
|
113
142
|
- Server-side `fallbacks` (beta) are never sent — the stable `2023-06-01` surface only.
|
|
143
|
+
- **A slot inside a tag pair is DATA** (`prompt-fence.ts`, #689): `render` breaks, in the ASSEMBLED
|
|
144
|
+
prompt, every fence closer a fenced value wrote any character of (never per value: two slots can
|
|
145
|
+
each hold half) — never deletes, never touches a template-only closer. The
|
|
146
|
+
fences are read once at `definePrompt`; no hash moves.
|
|
114
147
|
- `definePrompt` refuses a re-registered version whose hash moved; the hash is over core's
|
|
115
148
|
`canonicalJson` (`prompt.test.ts` pins one hash literally); an absent schema hashes as `''`.
|
|
116
149
|
|
|
117
150
|
## Invariants — providers and the gateway
|
|
118
151
|
|
|
152
|
+
- **The response cache key is core's `fingerprint` over the request, each tool WHOLE** (every `llm()`
|
|
153
|
+
tool is `respond`); a throwing `cache.set` is logged (`ai.cache.write_failed`), never a failure.
|
|
154
|
+
- **An unknown stop/finish reason FAILS CLOSED as `max_tokens` on both formats**, streamed or not.
|
|
155
|
+
- **One failure-body reader** (`error-body.ts`'s `detailOf`, bounded at `READ_LIMIT`) and one
|
|
156
|
+
`withoutKey` scrub — the embedder included.
|
|
119
157
|
- **`attempt` collects TRANSPORT failures only**: an `UltimateError` other than
|
|
120
158
|
`X_AI_PROVIDER_UNAVAILABLE` is rethrown on the spot (the same misconfiguration everywhere). Read
|
|
121
159
|
with `stringField(error, 'code')`.
|
|
@@ -126,7 +164,7 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
126
164
|
stays UNREGISTERED on purpose.
|
|
127
165
|
- **The retry SCHEDULE is core's (`backoffDelay`, `isRetryableStatus`); the LOOP is this package's** —
|
|
128
166
|
core's `retry()` retries unclassified throws, and every value here is an app `Provider`'s plain
|
|
129
|
-
object, so it would retry a 400. `gateway-backoff.test.ts`. `RetryPolicy`/`
|
|
167
|
+
object, so it would retry a 400. `gateway-backoff.test.ts`. `RetryPolicy`/`DEFAULT_GATEWAY_RETRY` keep the
|
|
130
168
|
field names an app writes.
|
|
131
169
|
- Every non-2xx and every in-band `error` frame is `AiTransportError` with a real `status`.
|
|
132
170
|
- **Both wire formats answer the same question the same way** (`provider-parity.test.ts`): an in-band
|
|
@@ -139,15 +177,33 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
139
177
|
- A tool call is emitted whole. Thinking chunks are never appended to `text`.
|
|
140
178
|
- Anthropic body: no `temperature`/`top_p`/`top_k`, no `budget_tokens`, `effort` inside
|
|
141
179
|
`output_config`. Model IDs are exact aliases, never date-suffixed.
|
|
180
|
+
- **Apps bring their own models and providers (M12, 25.0.0): no vendor choice, no vendor data.** No
|
|
181
|
+
default model, no catalogue row, no id list ships; the registry starts EMPTY and `resetModels()`
|
|
182
|
+
empties it. A model resolves declaration → prompt → `Gateway.defaultModel` through `resolveModel`
|
|
183
|
+
/ `modelForCall` only; none is `X_AI_MODEL_UNRESOLVED` naming all three (with no gateway and nothing
|
|
184
|
+
declared, `X_AI_GATEWAY_MISSING` — the third place does not exist yet). `vendor-neutral.test.ts`
|
|
185
|
+
proves it in a fresh process; no fix line names a vendor id.
|
|
142
186
|
- **`ModelId` is `string`; the catalogue is an open registry** — `modelSpec(id)` refuses an
|
|
143
|
-
unregistered id (`X_AI_MODEL_UNKNOWN`)
|
|
144
|
-
**Re-registering REPLACES the spec and keeps
|
|
145
|
-
the ladder within a `family`**, read only by
|
|
146
|
-
ABOVE or nothing.
|
|
147
|
-
-
|
|
187
|
+
unregistered id (`X_AI_MODEL_UNKNOWN`, fix `registerModel(…)`) at `costOf`, the budget estimate and
|
|
188
|
+
the body builders, before a socket or a reservation. **Re-registering REPLACES the spec and keeps
|
|
189
|
+
its rung** (negotiated rates). **Registration order is the ladder within a `family`**, read only by
|
|
190
|
+
`moreCapableThan`, over the APP's rows; `X_LLM_REFUSED`'s fix names a rung ABOVE or nothing.
|
|
191
|
+
- **Every provider serves the app's list**: `anthropicProvider({ models })` and
|
|
192
|
+
`openAiProvider({ models })` require a non-empty one (`X_AI_REQUEST_INVALID`, a missing config
|
|
193
|
+
too); `echoProvider().models` reads the registry. **No provider has a model of its own**: a direct
|
|
194
|
+
call naming none is `X_AI_MODEL_UNRESOLVED` from all three — the list is what it serves, never a
|
|
195
|
+
fourth place (`vendor-neutral.test.ts`, one parity case).
|
|
148
196
|
- **The reasoning controls are PER MODEL (`models.ts`)**: an unasked control is omitted; an asked
|
|
149
|
-
control the model lacks is `X_AI_REQUEST_INVALID`, never dropped. Adding a model is a row
|
|
197
|
+
control the model lacks is `X_AI_REQUEST_INVALID`, never dropped. Adding a model is a row the app
|
|
198
|
+
registers. `disableThinkingUpTo: 'never'` refuses `thinking: 'disabled'` at every effort;
|
|
199
|
+
`disabledThinking: 'between_tools'` is the wire spelling of the same declaration
|
|
200
|
+
(`model-rules.test.ts`, over the fixture rows).
|
|
201
|
+
- **Media blocks are screened once, by `assertMediaContent`, at the top of BOTH body builders** —
|
|
202
|
+
malformed is `X_AI_REQUEST_INVALID`, untakeable (role, the row's `input`, the wire) is
|
|
203
|
+
`X_AI_CONTENT_UNSUPPORTED`; never dropped, never approximated (`provider-parity-content.test.ts`).
|
|
150
204
|
- `generate()` above `STREAM_ONLY_MAX_TOKENS` runs the streaming transport.
|
|
205
|
+
- **Every provider and embedder is built by its factory** (`anthropicProvider()`, `echoProvider()`,
|
|
206
|
+
`hashEmbedder()`, `remoteEmbedder()`); the classes are type exports only (`bun run factory-names`).
|
|
151
207
|
- **`openAiProvider()` is a FORMAT, not a vendor** — `baseUrl`, `auth`, `headers` are the
|
|
152
208
|
differences; never a second class per vendor:
|
|
153
209
|
- structured output is the `respond` tool, never `response_format`; `tool_choice` names the tool
|
|
@@ -157,17 +213,16 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
157
213
|
ESTIMATE when usage never arrives; subtract `cached_tokens` from `prompt_tokens`;
|
|
158
214
|
- tool-call deltas merge by `tool_calls[].index`; `isComplete()` accepts `[DONE]` or a finish reason,
|
|
159
215
|
except `[DONE]` with pending tool fragments, which is refused;
|
|
160
|
-
-
|
|
161
|
-
`
|
|
162
|
-
- no new `X_*` code; `AiTransportError` takes the provider's `envVar`; the key is revealed late,
|
|
216
|
+
- no code of its own (a media refusal is the shared `X_AI_CONTENT_UNSUPPORTED`);
|
|
217
|
+
`AiTransportError` takes the provider's `envVar`; the key is revealed late,
|
|
163
218
|
never stored, scrubbed from `detail`.
|
|
164
219
|
- **`embedBatched` enforces the `Embedder` arity per batch** (`X_AI_EMBEDDER_INVALID` with both
|
|
165
|
-
counts). One `
|
|
220
|
+
counts). One `remoteEmbedder()` for every vendor; vectors L2-normalised; a wrong width is
|
|
166
221
|
`X_VECTOR_DIM_MISMATCH`.
|
|
167
222
|
- **A caught value is read with `renderThrowable`** (`remote-embedder.ts`, `wire.ts`,
|
|
168
223
|
`openai-wire.ts`) — `scripts/error-render.ts` cannot see `catch` bindings.
|
|
169
224
|
- **A caller's string is never an object KEY**: the wire tables are `Map`s; `prompt.render` and
|
|
170
|
-
`
|
|
225
|
+
`echoProvider()`'s `replies` use `Object.hasOwn`.
|
|
171
226
|
|
|
172
227
|
## Invariants — `agent()`, `hive()`, `agentJob()`
|
|
173
228
|
|
|
@@ -188,10 +243,15 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
188
243
|
("superseded").
|
|
189
244
|
- A tool result is rendered totally (`'null'` for `undefined`; a non-JSON result reported as such,
|
|
190
245
|
never as a failure); the throw is read with `stringField`.
|
|
246
|
+
- **A 5xx code's cause reaches the model only when core's `hasPublicCause` allows it** (status from
|
|
247
|
+
`@ultimat3/http`'s `statusFor`) — else `CODE: HIDDEN_TOOL_CAUSE`, plus a branded `callerFix`. The
|
|
248
|
+
same verdict a problem document and `@ultimat3/mcp` give, from `failure-disclosure.ts` — the
|
|
249
|
+
rule a hive member's `reason` follows too. Never a second copy.
|
|
191
250
|
- **`hive()` is a fan-out action** (`PRIMITIVE_FACTORIES`): `HiveResult` is a SCHEMA from the
|
|
192
251
|
member's `output`; three arms (`ok`/`failed`/`skipped`) and three counters; `members` in SPLIT
|
|
193
|
-
order with `index`; the hive never names an actor; no hive budget code (the
|
|
194
|
-
it); a member's throw is recorded
|
|
252
|
+
order with `index`; the hive never names an actor; no hive budget code (the reservation holds
|
|
253
|
+
it); a member's throw is recorded through `discloseFailure` — a 5xx cause or an uncoded throw's
|
|
254
|
+
message becomes `HIDDEN_MEMBER_CAUSE`, logged as facts only; `skipped` reasons
|
|
195
255
|
`SKIPPED_ABORTED` / `SKIPPED_NO_INPUT`; `onMemberError` is required; `concurrency` 4, `minMembers`
|
|
196
256
|
2 (a below-floor split still runs serially); an empty split is `X_HIVE_EMPTY`; an aborted `ctx`
|
|
197
257
|
unwinds with `X_ABORTED`.
|
|
@@ -206,15 +266,19 @@ local `=== true`. An in-app agent and an external one must be offered exactly th
|
|
|
206
266
|
- **No fix line may name `x ai`** (planned). An eval is selected with `x test eval --filter <name>`;
|
|
207
267
|
`eval-errors.test.ts` asserts every `x test <word>` is one of the six types.
|
|
208
268
|
- Every eval result carries the prompt hash. An eval gates on the DROP from its recorded baseline,
|
|
209
|
-
mean AND per case. Never-recorded is `X_EVAL_BASELINE_MISSING`, corrupt is `X_EVAL_BASELINE_INVALID`.
|
|
269
|
+
mean AND per case, both sides rounded as the baseline is written. Never-recorded is `X_EVAL_BASELINE_MISSING`, corrupt is `X_EVAL_BASELINE_INVALID`.
|
|
210
270
|
`baseline` is `import.meta.resolve('./…')`. Every prompt is named by an eval (`X_EVAL_MISSING`, by
|
|
211
271
|
prompt ID). `ULTIMATE_EVAL_RECORD=1` records; a deliberately-worse test calls `run`, never
|
|
212
272
|
`assert`; `x verify` with the flag is `X_EVAL_RECORDING` and runs no eval. The gate asks for a
|
|
213
273
|
recorded baseline, not only a declaration.
|
|
214
|
-
- Retrieval is hybrid by default; no vector-only path. **`chunk()` hard-wraps**,
|
|
215
|
-
carry stops at `buffer.length - 1
|
|
216
|
-
|
|
217
|
-
|
|
274
|
+
- Retrieval is hybrid by default; no vector-only path. **`chunk()` hard-wraps**, the overlap
|
|
275
|
+
carry stops at `buffer.length - 1` and yields to a next unit it does not fit beside (no chunk over
|
|
276
|
+
`size`); `metadata.source` is written AFTER the caller's spread. `VectorStore.prune` is required.
|
|
277
|
+
- **A store with no tenant bound, read inside a request whose actor has an `orgId`, is
|
|
278
|
+
`X_VECTOR_UNSCOPED`** (`assertTenantRead`, both stores, every read). `scope: UNSCOPED`
|
|
279
|
+
(`crossTenant: true`) is the named opt-in; an omitted scope is `UNBOUND`.
|
|
280
|
+
- `postgresVectorStore()` is the only production vector path (pgvector + FTS in the app's Postgres);
|
|
281
|
+
`memoryVectorStore()` enforces the same envelope. **`pg-vector.live.test.ts` refuses to skip** on a
|
|
218
282
|
Postgres without the extension (CI uses `pgvector/pgvector:pg17`).
|
|
219
283
|
- The distance ordering lives in an ascending raw subquery (the only shape hnsw answers), pinned by a
|
|
220
284
|
plan assertion. hnsw scopes after the scan — assert rows, never the node; `analyze` after a backfill.
|