@plurnk/plurnk-service 1.3.4 → 1.3.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/INSTALL.md CHANGED
@@ -63,7 +63,9 @@ Each mirrors a `# --- section ---` in the floor; consult the floor for exact def
63
63
  - **Loop control / Engine rails** — `PLURNK_SERVICE_MAX_TURNS` (−1 = uncapped), `_MAX_COMMANDS`, `_MAX_STRIKES`, `_MIN_CYCLES`, `_MAX_CYCLE_PERIOD`, `_RPC_TIMEOUT`, `_LOOP_TIMEOUT`, `_PROPOSAL_TIMEOUT_MS`, `_EXEC_WAIT_MS`, `_EXEC_KILL_GRACE_MS`, `_SESSION_RUNS_MAX_ACTIVE`. Guardrails; the shipped values are sane.
64
64
  - **Git** — `PLURNK_SERVICE_GIT_ALLOWED` (0 = hard sandbox lockout), `_GIT_AUTO`.
65
65
  - **Packet / reference docs** — `PLURNK_SERVICE_FILES_ITEMS` (turn-1 file catalog cap), `_PROMPT_PREVIEW_CHARS`, `_DOCS_EXCLUDE`, `_PACKET_INJECT` (operator markdown section), `_POLICY`/`_PROJECT`/`_REQUIREMENTS` (policy + footer overrides; unset = the seeded/packaged defaults), `_MD_<alias>` (inject a markdown doc as a turn-0 entry).
66
- - **Providers** — `PLURNK_PROVIDERS_THINKING`/`_THINKING_CAPACITY`, `_TEMPERATURE`, `_REPEAT_PENALTY`, `_FETCH_TIMEOUT`, `_RETRY_ATTEMPTS`/`_RETRY_DELAY`, `_PROBE_ATTEMPTS`/`_PROBE_DELAY`, `_GBNF` (grammar variant), `_LLAMA_SERVER_<alias>`, `_CONTEXT_SIZE`, `_GBNF_DEBUG`. Alias-scopable: any knob takes a `_<alias>` suffix that wins over the bare fallback.
66
+ - **Providers** — the portable provider knobs and defaults are defined by
67
+ `@plurnk/plurnk-providers/.env.defaults`; any provider knob may take an alias
68
+ suffix that wins over the bare fallback.
67
69
  - **The window partition** — `PLURNK_SERVICE_CONTEXT_WINDOW`/`_REASONING`/`_ASSISTANT`/`_SAFETY` (see Couplings).
68
70
  - **Plugins** — bare `PLURNK_PLUGINS_TRUSTED_ONLY` (0/unset = load all installed; a value = `@plurnk/*` plus an allowlist).
69
71
  - **Semantic search** — `PLURNK_SERVICE_SEMANTIC_TOP_K` (markerless result count), `_SEMANTIC_CHUNK_TOKENS`/`_CHUNK_OVERLAP` (service-side chunking), `PLURNK_SERVICE_EMBED_DISABLE` (FTS-only), `PLURNK_MIMETYPES_EMBED_WORKERS` (the embedder's pool — mimetypes-owned).
package/SPEC.md CHANGED
@@ -96,11 +96,11 @@ Dependency direction (from root to leaf):
96
96
 
97
97
  - **`plurnk-grammar`** — root. Owns the JSON-Schema contracts (Packet, TelemetryEvent, AST shapes), the ANTLR parser that turns model output into `PlurnkStatement[]`, and `PlurnkParseError` with its `toTelemetryEvent()` helper. Nothing in the ecosystem can speak the DSL without it; everything else pins it exactly.
98
98
  - **Framework siblings** consume grammar and define their own author-facing contracts:
99
- - `plurnk-providers` — Provider/Alias types, `parseAliasesFromEnv`, `resolveActiveAlias`, `Mock`, `ProviderUsage` (currency-aware, includes `reasoning`). Vendor-specific implementations are children: `plurnk-providers-openai`, `-google`, `-ollama`, `-openrouter`, `-cloudflare`, `-xai`.
99
+ - `plurnk-providers` — Provider/Alias types, AI SDK adaptation, Models.dev-backed provider resolution, local endpoint probes, `Mock`, and normalized usage. Ordinary vendor protocols belong to official AI SDK providers; only genuinely new protocol bindings require a provider plugin.
100
100
  - `plurnk-mimetypes` — handler base classes, discovery, the fitting algorithm, and the match primitives (`queryGlob`/`queryRegex`/`queryJsonpathObject`/`queryXpathString`) the service's matcher dispatches over (§matcher-dispatch). Handler children are per-mimetype: `plurnk-mimetypes-text-{python,typescript,markdown,html,csv,plain}`, `plurnk-mimetypes-application-{json,yaml,toml,pdf}`, …
101
101
  - `plurnk-schemes` — scheme-author types (`SchemeManifest`, `WriterTier`, `LoopFlags`), result-shape contracts (`EntryResult` / `ProposalResult` / `PassthroughResult`), slicing primitives, matcher helpers, `schemeError(...)` constructor. Future scheme children: `plurnk-schemes-http`, `plurnk-schemes-git`, …
102
102
  - `plurnk-execs` — `BaseExecutor`, `SubprocessExecutor`, runtime resolver, discovery. Children declare runtimes: `plurnk-execs-sh`, future `plurnk-execs-search`, `plurnk-execs-node`, …
103
- - **`plurnk-service`** (this repo) — consumes all of the above. Implements the engine, dispatches ops through scheme handlers, hosts the in-tree set of schemes (`plurnk`, `log`, `exec`, `known`, `unknown`, `skill`, `file`), discovers installed mimetype handlers + provider vendors + executor siblings at boot, and exposes an in-process seam to client-interface modules. Packet assembly is service-owned (§packet).
103
+ - **`plurnk-service`** (this repo) — consumes all of the above. Implements the engine, dispatches ops through scheme handlers, hosts the in-tree set of schemes (`plurnk`, `log`, `exec`, `known`, `unknown`, `skill`, `file`), resolves model providers, discovers installed mimetype handlers and executor siblings at boot, and exposes an in-process seam to client-interface modules. Packet assembly is service-owned (§packet).
104
104
  - **`plurnk`** (client) — terminal UI consuming the AG-UI+ client surface. Renders daemon events and contains no engine logic.
105
105
 
106
106
  The grammar is the contract. The frameworks consume the contract and add author-facing surfaces. The service consumes the frameworks and runs the engine. The client consumes the service and renders to humans. Each tier is its own published package; each tier's evolution happens in its own repo.
@@ -217,6 +217,102 @@ stateDiagram-v2
217
217
 
218
218
  The lifecycle store admits only the guarded transitions shown above: `100 → 102`, `102 → 202`, `202 → 100`, and any unresolved state (`100`, `102`, `202`) to a terminal status. Terminal state is immutable. The queue drain owns claim, the dispatcher owns model-requested park/conclusion, the daemon owns wake/cancellation/restart reconciliation, and the engine owns policy terminals. A racing transition that loses observes the durable winner; it does not overwrite it or report the requested state as fact. {§worker-lifecycle-state-machine}
219
219
 
220
+ Streams are independently durable subscriptions owned by a worker. Payload and
221
+ lifecycle are orthogonal: zero bytes is a valid payload for both success and
222
+ failure, while the closed subscription and its status are the terminal fact.
223
+
224
+ ```mermaid
225
+ stateDiagram-v2
226
+ [*] --> Open: executor registers subscription
227
+ Open --> Open: append chunk
228
+ Open --> Closed: driver succeeds
229
+ Open --> Failed: driver fails
230
+ Open --> Cancelled: worker or stream is cancelled
231
+ Closed --> Observed: terminal delta enters a packet
232
+ Failed --> Observed: terminal delta enters a packet
233
+ Cancelled --> Observed: terminal delta enters a packet
234
+ Observed --> [*]
235
+ ```
236
+
237
+ | Subscription state at `SEND[202]` | Terminal observation already in a packet | Result |
238
+ |---|---:|---|
239
+ | open | no | park; polling or closure may wake it |
240
+ | closed, any status, empty or non-empty | no | continue directly to the observation turn |
241
+ | closed, any status, empty or non-empty | yes | no stream obligation remains |
242
+ | cancelled as part of worker cancellation | irrelevant | terminate the cancelled worker; never resurrect it |
243
+
244
+ {§worker-lifecycle-subscription-matrix}
245
+
246
+ Polling observes a still-open stream; it never changes ownership or manufactures
247
+ completion. Closure is always a wake edge regardless of polling mode.
248
+
249
+ | EXEC poll marker | While open | On closure |
250
+ |---|---|---|
251
+ | omitted | exponential-backoff observation wakes | resume once with terminal observation |
252
+ | positive `P` | fixed-cadence observation wakes every `P` seconds | cancel cadence; resume once |
253
+ | zero | no observation wakes | resume once |
254
+ | turn-scoped `<0>` | reap at the next pre-turn boundary | surface the terminal outcome |
255
+
256
+ {§worker-lifecycle-poll-matrix}
257
+
258
+ The structured-concurrency sequence is identical whether a child performs an
259
+ EXEC, retrieval, or pure inference. Intermediate child status is private to the
260
+ child. Only the child's terminal loop result crosses the parent edge.
261
+
262
+ ```mermaid
263
+ sequenceDiagram
264
+ participant P as Parent loop
265
+ participant C as Child loop
266
+ participant S as Child stream
267
+ P->>C: WORK or FORK
268
+ P->>P: SEND[202] parks on live child
269
+ C->>S: EXEC opens subscription
270
+ C->>C: SEND[202] parks on live stream
271
+ loop backoff, fixed cadence, or explicit arrival
272
+ S-->>C: optional progress observation
273
+ C->>C: continue or park
274
+ end
275
+ S-->>C: terminal transition
276
+ C->>C: terminal delta enters packet
277
+ C->>P: child SEND terminal becomes collect delta
278
+ P->>P: resume same parked loop
279
+ P->>P: observe child result and continue
280
+ ```
281
+
282
+ | Child state at parent wait | Child result delivered to parent | Result |
283
+ |---|---:|---|
284
+ | queued, running, or parked on its own live obligation | no | parent parks |
285
+ | terminal during the parent's turn | no | owed wake; parent continues to the result packet |
286
+ | terminal before the parent's wait | no | parent continues directly to the result packet |
287
+ | terminal | yes | child obligation is drained |
288
+ | cancelled or failed terminal | no | same wake/delivery path as success; outcome remains non-2xx |
289
+
290
+ {§worker-lifecycle-child-matrix}
291
+
292
+ A stream's close status and a loop's terminal status are separate layers. A
293
+ stream may close 4xx/5xx and wake its worker to recover. Model `SEND[4xx/5xx]`
294
+ reports a failed action and continues; `SEND[200]` concludes successfully and
295
+ `SEND[499]` explicitly abandons the worker. Only a concluded loop crosses the
296
+ parent edge as the child's result.
297
+
298
+ ```mermaid
299
+ flowchart TD
300
+ W[Worker cancellation] --> L[Terminalize unresolved loops]
301
+ W --> C[Cancel descendants]
302
+ W --> S[Enumerate durable open subscriptions]
303
+ S --> H[Invoke each live cancellation handle]
304
+ H --> T[Persist terminal channel and subscription state]
305
+ T --> N[Publish conclusion without resurrection]
306
+ C --> L
307
+ ```
308
+
309
+ Restart applies the same ownership rule: accepted queued loops are reclaimable;
310
+ in-flight provider calls and subscriptions belonged to the vanished process and
311
+ become explicit failures. A park whose child remains live stays parked; a park
312
+ whose obligations settled during reconciliation is requeued in place so it can
313
+ observe their terminal results. No effect is replayed across an unknown
314
+ boundary.
315
+
220
316
  - **One drain advances a worker.** At most one drain is registered for a worker at any instant: a `loop.run` or wake on a worker with a live drain folds in (active→next-turn) or enqueues a loop that drain claims, never a second parallel drain. A drain's start and its empty-queue teardown relinquish the worker under one per-worker lock, so the teardown's re-claim cannot race a concurrent start into a double-drain. {§worker-lifecycle-single-drain}
221
317
  - **Cancellation is recursive and reaps every held stream.** `loop.cancel`, worker `KILL`, shutdown, and a worker's `SEND[499]` terminalize every unresolved loop in the cancelled worker subtree and iterate each worker's durable open-subscription rows, invoking each exact callable owner from the process-local live registry. The durable rows answer *what is held*; the live registry answers *how this process tears it down*; the abort signal is a fast-path optimization. There is no implicit detachment. A stream that is running, mid-spawn (its row written before it is killable), or spawned after the cancel is reaped alike. The teardown abort is bounded: the executor sends a polite signal then SIGKILL after a consumer-set grace (`PLURNK_SERVICE_EXEC_KILL_GRACE_MS`). A model `KILL[code]` on one live stream instead delivers exactly that signal once (bare `KILL` → the executor's SIGHUP default, `KILL[9]` → SIGKILL). {§worker-lifecycle-total-reap}
222
318
  - **A stream's kill binds to the scope it captured at spawn.** A stream captures the worker's cancellation scope as it registers and wires its kill to it, re-checking `aborted` AFTER wiring — no check-then-listen gap can drop an abort that lands mid-registration. Because the scope is replaced only once aborted, a captured-then-replaced scope is necessarily already aborted, so replacement never strands a live stream. {§worker-lifecycle-exec-epoch-bound}
@@ -239,11 +335,11 @@ Three entry points:
239
335
 
240
336
  - `provider.generate({messages, signal})` — once per turn; returns `{ assistant: { content, reasoning, usage, finishReason, model }, assistantRaw, meta? }`. **Engine parses `assistant.content`** into `PlurnkStatement[]` via `@plurnk/plurnk-grammar`. {§provider-surface-generate}
241
337
  - `provider.countTokens(text)` — synchronous, called at write-time (§tokenomics) and render-time. Non-negative integer. {§provider-surface-counttokens}
242
- - `provider.costFor(usage)` — once per completed turn; pico-USD. Engine writes to `turns.usage_cost_pico`; triggers cascade to `workers.cost_pico` / `workspaces.cost_pico`. {§provider-surface-costfor}
338
+ - `provider.calculateCost(usage)` — once per completed turn; estimated USD. Engine writes to `turns.usage_cost_usd`; triggers cascade to `workers.cost_usd` / `workspaces.cost_usd`. {§provider-surface-calculate-cost}
243
339
 
244
340
  Plus immutable identity: `provider.contextWindow` (token total, or `null` → "no budget info"), read by the budget {§provider-surface-identity}; and `provider.model` — the instance identity the deferred model-switch recompute compares (§tokenomics), exposed but not yet consumed here.
245
341
 
246
- **Metadata passthrough (provider → client).** `generate` may return an open `meta: Record<string, unknown>` bag — e.g. a hosted provider's running `balancePico`. The service stores it **unenforced** per turn (`turns.meta`, `json_valid` only — no schema) and forwards the latest turn's blob to the client on the loop usage payload (`loop.run` result / `loop/terminated`, §methods). **The service never reads a field within it.** The canonical-field contract which fields exist and their shapes is the *provider framework's* (it normalizes raw vendor data into the agreed set) and the *client's* (it renders that set); a provider and client can ship a feature with **zero service change** as long as the blob flows. Absent → `{}` (the client renders nothing; never fabricated). The mirror direction (client → provider, the self-identified `client` id) rides `generate({client})` (§attribution). {§meta-passthrough}
342
+ **Metadata passthrough (provider → client).** `generate` may return an open `meta: Record<string, unknown>` bag. The service stores it unenforced per turn (`turns.meta`, `json_valid` only — no schema) and forwards the latest turn's blob to the client on the loop usage payload (`loop.run` result / `loop/terminated`, §methods). The service never reads a field within it. Providers own their metadata shapes; monetary values carry an explicit amount and currency rather than an implied unit. Absent → `{}`. The mirror direction (client → provider, the self-identified `client` id) rides `generate({client})` (§attribution). {§meta-passthrough}
247
343
 
248
344
  ### §provider-guarantees Engine → provider guarantees
249
345
 
@@ -273,7 +369,13 @@ Deferred (#249): grounding the attribution value in real per-turn value flow rat
273
369
 
274
370
  ### §provider-instantiation Provider instantiation
275
371
 
276
- Model alias parsing (`parseAliasesFromEnv` / `resolveActiveAlias`) lives in [`@plurnk/plurnk-providers`](https://github.com/plurnk/plurnk-providers). {§provider-instantiation-alias-resolution} Dynamic provider instantiation (`instantiateProvider` / `loadActiveProvider`) lives in `src/core/ProviderInstantiate.ts` here — `import()` resolves package specifiers relative to the calling module, so the dynamic-import path stays in the consumer where the `@plurnk/plurnk-providers-<vendor>` packages are installed.
372
+ Model alias parsing and provider construction live in
373
+ [`@plurnk/plurnk-providers`](https://github.com/plurnk/plurnk-providers).
374
+ `src/core/ProviderInstantiate.ts` delegates to that owner and adds only
375
+ service-side caching, per-loop selection, context-cap handling, and local GBNF
376
+ verification. Cataloged providers use Models.dev metadata and official AI SDK
377
+ bindings; an operator declaration covers an uncataloged compatible endpoint;
378
+ plugin discovery is the last protocol-extension seam. {§provider-instantiation-alias-resolution}
277
379
 
278
380
  **Optional local GBNF is verified at boot.** {§grammar-enforcement-verified-at-boot}
279
381
  The ANTLR grammar always defines and validates the PLURNK language. Separately,
@@ -296,7 +398,7 @@ PLURNK_MODEL_opus=openrouter/anthropic/claude-opus-latest
296
398
  PLURNK_MODEL=gemma
297
399
  ```
298
400
 
299
- First path segment = provider plugin; rest = provider's own model id.
401
+ First path segment = provider name; rest = provider-native model id.
300
402
 
301
403
  ### §mock-provider Mock provider (sibling fixture)
302
404
 
@@ -812,7 +914,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
812
914
  **Lives in SQL:**
813
915
  - Render queries — log assembly + the manifest catalog.
814
916
  - Cross-scope path collision (CHECK/trigger → 409).
815
- - Cost rollups (denormalized pico-units; atomic on turn close).
917
+ - Cost rollups (denormalized USD; atomic on turn close).
816
918
  - Sequence number issuance (1-based per grammar).
817
919
  - Entry-vs-log integrity.
818
920
 
@@ -837,10 +939,10 @@ Scoped-package scan with manifest field:
837
939
 
838
940
  1. Each package declares its kind:
839
941
  ```json
840
- { "name": "@plurnk/plurnk-providers-openrouter",
841
- "plurnk": { "kind": "provider", "name": "openrouter" } }
942
+ { "name": "@acme/ai-provider",
943
+ "plurnk": { "kind": "provider", "name": "acme" } }
842
944
  ```
843
- 2. Boot scans `node_modules/@plurnk/*/package.json`, filters by `plurnk` field, dynamic-imports matches.
945
+ 2. Boot scans installed scoped and unscoped packages, filters by `plurnk` field, and dynamic-imports matches.
844
946
  3. Load order: deterministic alphabetical.
845
947
  4. Collision on `(kind, name)` is fail-hard.
846
948
  5. Operator flow: `npm i @plurnk/plurnk-<kind>-<name> && plurnk start`. Zero config.
@@ -855,7 +957,9 @@ Env vars configure installed plugins; never declare existence. Filesystem is the
855
957
 
856
958
  Plugin discovery (§plugin-discovery) registers whatever's in `node_modules/@plurnk/*`.
857
959
 
858
- **Providers in-tree:** none. `Mock` (the intg-only test fixture + worked example) is a sibling, `@plurnk/plurnk-providers` (§mock-provider); `ProviderInstantiate.ts` dynamically imports the selected provider package.
960
+ **Providers:** `@plurnk/plurnk-providers` resolves the Models.dev catalog,
961
+ operator declarations, local adapters, and finally installed AI SDK provider
962
+ plugins. `Mock` is its integration fixture. Core contains no vendor protocol.
859
963
 
860
964
  **Mimetypes in-tree:** none. Framework + handlers are all siblings.
861
965
 
@@ -1322,7 +1426,7 @@ type Packet = {
1322
1426
 
1323
1427
  The wire projection (`PacketWire.renderSlot`) groups sections by slot into the system + user ChatMessages; the digest re-renders the same stored sections byte-for-byte.
1324
1428
 
1325
- **The lexicon has two standards, split by audience.** {§lexicon} The OPERATOR/WIRE/STORAGE layers follow the industry lexicon — where a concept is standard, its standard name (OpenAI's for provider quantities: `contextWindow`, `reasoning`, `completion`, `finish_reason`, usage nouns). The MODEL-FACING packet follows the training distribution — ops mirror HTTP/shell, `display` mirrors CSS, jsonplurnk is JSON — because that is the standard the model has actually read; renaming packet vocabulary to API-speak trades load-bearing resonance for a standard the model never sees at that layer. A standard name on a NON-standard concept is the worst case (a label that lies with credentials): `promptBudget` is deliberately not `contextWindow` because it holds window-minus-reserves. **Deliberate keeps** (each a plurnk-native concept with no honest standard name): `worker`/`loop`/`turn` — the OS process paradigm; the industry's countable ("run") was retired EMPTY and the client seam translates `loop` → AG-UI RUN events; `packet` — the assembled address space, a kernel concept, not a "request"; `costPico` — no standard cost field exists; picodollar integers avoid float cents; `PLURNK_SERVICE_SAFETY` — the ruler's packing margin, a service fact no provider owns; the `chars/2` ruler — model-agnostic BY DESIGN (§tokenomics-agnostic-ruler). Retired terms stay retired: the lexicon guard fails CI on `thinking`, the `session` noun, `contextSize`, `decodeBudget`, and the moved partition knob names — drift is caught at commit time, not the next audit. <!-- lexicon-allow: this section QUOTES the retired terms it bans -->
1429
+ **The lexicon has two standards, split by audience.** {§lexicon} The OPERATOR/WIRE/STORAGE layers follow the industry lexicon — where a concept is standard, its standard name (OpenAI's for provider quantities: `contextWindow`, `reasoning`, `completion`, `finish_reason`, usage nouns). The MODEL-FACING packet follows the training distribution — ops mirror HTTP/shell, `display` mirrors CSS, jsonplurnk is JSON — because that is the standard the model has actually read; renaming packet vocabulary to API-speak trades load-bearing resonance for a standard the model never sees at that layer. A standard name on a NON-standard concept is the worst case (a label that lies with credentials): `promptBudget` is deliberately not `contextWindow` because it holds window-minus-reserves. **Deliberate keeps** (each a plurnk-native concept with no honest standard name): `worker`/`loop`/`turn` — the OS process paradigm; the industry's countable ("run") was retired EMPTY and the client seam translates `loop` → AG-UI RUN events; `packet` — the assembled address space, a kernel concept, not a "request"; `costUsd` — no standard cost field exists; US dollar integers avoid float cents; `PLURNK_SERVICE_SAFETY` — the ruler's packing margin, a service fact no provider owns; the `chars/2` ruler — model-agnostic BY DESIGN (§tokenomics-agnostic-ruler). Retired terms stay retired: the lexicon guard fails CI on `thinking`, the `session` noun, `contextSize`, `decodeBudget`, and the moved partition knob names — drift is caught at commit time, not the next audit. <!-- lexicon-allow: this section QUOTES the retired terms it bans -->
1326
1430
 
1327
1431
  **Sealed reasoning rides the mirror row.** {§sealed-reasoning-carrier} A provider's ENCRYPTED reasoning — the OpenAI Responses reasoning-item LIST (`[{id, subtype, encrypted: [{data, format}]}]` — a turn can carry N distinct-id items; normalized by the provider tier: id + subtype from the wire, never synthesized) — lands VERBATIM in the model mirror row's `attrs.reasoning` — per turn, on the same `log/entry` broadcast and `readLog` read the client seam already consumes; agui projects one correlated `REASONING_ENCRYPTED_VALUE` span per item (#482). A cross-lane conformance test drives core's real write through agui's real Translator, so a future non-meeting seam is a red pre-push gate, not a shipped-broken main. The blobs are never decoded, never synthesized, and never rendered into a packet — the packet renderer reads no foreign attrs keys, so the model never pays tokens for ciphertext it cannot read. Readable text keeps riding `assistant.reasoning`; the two never mix.
1328
1432
 
@@ -1 +1 @@
1
- {"package":"@plurnk/plurnk-service","version":"1.3.3","revision":"3352dea7a51575d4a1e7804428397e4d649f5c57","dirty":false}
1
+ {"package":"@plurnk/plurnk-service","version":"1.3.6","revision":"19e299ab4fd23ba077100e0426667f9f6404897c","dirty":false}
@@ -54,7 +54,7 @@ export default class Engine {
54
54
  loopUsage(loopId: number): Promise<{
55
55
  promptTokens: number;
56
56
  completionTokens: number;
57
- costPico: number;
57
+ costUsd: number;
58
58
  contextTokens: number;
59
59
  promptBudget: number | null;
60
60
  meta: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"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"}
@@ -365,7 +365,7 @@ export default class Engine {
365
365
  }
366
366
  // Per-loop usage totals (#197): SUM the loop's turns (usage is stored per
367
367
  // turn, §tokenomics). Surfaced on loop.run + loop/terminated so clients render real
368
- // token/cost numbers. costPico is the stored pico-dollar unit.
368
+ // token/cost numbers. costUsd is the stored USD unit.
369
369
  // #345 — the client-facing budget denominator, ONE meaning on every surface: the prompt
370
370
  // budget the packet actually lives under (effective window minus the partition reserves),
371
371
  // the same number loop-usage stores per turn. providers.list advertised the raw KV and the
@@ -390,7 +390,7 @@ export default class Engine {
390
390
  return {
391
391
  promptTokens: row?.prompt ?? 0,
392
392
  completionTokens: row?.completion ?? 0,
393
- costPico: row?.cost_pico ?? 0,
393
+ costUsd: row?.cost_usd ?? 0,
394
394
  // #263 — the last turn's prompt tokens = current window occupancy (gauge numerator), NOT the
395
395
  // summed promptTokens above, which overcounts a context that grows across turns.
396
396
  contextTokens: row?.context ?? 0,
@@ -974,7 +974,7 @@ export default class Engine {
974
974
  const hardPacket = this.#packets.completePacket(requestPacket, { content: "", ops: [], reasoning: null }, null, provider);
975
975
  await this.#db.engine_close_turn.run({
976
976
  id: turnId, status: 413, packet: JSON.stringify(hardPacket),
977
- usage_prompt: 0, usage_completion: 0, usage_reasoning: 0, usage_cached: 0, usage_cost_pico: 0,
977
+ usage_prompt: 0, usage_completion: 0, usage_reasoning: 0, usage_cached: 0, usage_cost_usd: 0,
978
978
  usage_prompt_budget: this.#packets.promptBudgetFor(provider), // #274 — the PROMPT BUDGET (window − reserves), even on a hard-413 turn: the gauge denominator the packet actually lives under
979
979
  finish_reason: "budget_hard_stop", model: provider.model, meta: "{}",
980
980
  });
@@ -1189,11 +1189,11 @@ export default class Engine {
1189
1189
  usage_completion: usage.completion,
1190
1190
  usage_reasoning: usage.reasoning,
1191
1191
  usage_cached: usage.cached,
1192
- usage_cost_pico: provider.costFor(usage), // §provider-surface-costfor
1192
+ usage_cost_usd: provider.calculateCost(usage), // §provider-surface-calculate-cost
1193
1193
  usage_prompt_budget: this.#packets.promptBudgetFor(provider), // #274 — the PROMPT BUDGET (window − reserves): the raw n_ctx overstated usable room by the reserve total
1194
1194
  finish_reason: finishReason,
1195
1195
  model,
1196
- // #252 — opaque provider→client metadata passthrough (e.g. balancePico the
1196
+ // #252 — opaque provider→client metadata passthrough (for example, the
1197
1197
  // provider normalized), plus the ONE service-authored carve-out: the engine's rail
1198
1198
  // keys ({§rail-truth-engine-verdict}) merge over any transitional provider railsMeta.
1199
1199
  meta: JSON.stringify({ ...(response.meta ?? {}), ...(railKeys ?? {}) }),
@@ -1491,16 +1491,20 @@ export default class Engine {
1491
1491
  // The cursor-terminal race (owner's dogfood find): a channel written in one final
1492
1492
  // burst gets fully shown FOLDED while still active; the close then has zero new
1493
1493
  // bytes and the auto-OPEN terminal delta never fired — the model was never shown
1494
- // the conclusion of a stream whose result it already holds folded. Emit the
1495
- // terminal marker ONCE: open, terse, carrying the close status; the content is a
1496
- // pointer to the already-delivered bytes, never a re-send (§tokenomics-fetch-fits-free).
1497
- if (closed && priorAttrs.terminal !== true && cursor > 0) {
1494
+ // the conclusion of a stream whose result it already holds folded. The same
1495
+ // observation is required when the stream produced zero bytes: completion is
1496
+ // information independently of payload. Emit the terminal marker ONCE: open,
1497
+ // terse, carrying the close status (§tokenomics-fetch-fits-free).
1498
+ if (closed && priorAttrs.terminal !== true) {
1499
+ const pointer = cursor > 0
1500
+ ? `full output already delivered above; READ ${ch.runtime}://${ch.coord}${visibleFragment === null ? "" : `#${visibleFragment}`} to revisit`
1501
+ : "stream produced no output";
1498
1502
  await this.#db.engine_insert_stream_delta.run({
1499
1503
  worker_id: workerId, loop_id: loopId, turn_id: turnId, sequence: fromSequence + written,
1500
1504
  scheme: ch.runtime, pathname: ch.coord, fragment: visibleFragment,
1501
1505
  rx: JSON.stringify({
1502
1506
  status: ch.close_status ?? 200,
1503
- content: `[ stream closed (${ch.close_status ?? 200}) — full output already delivered above; READ ${ch.runtime}://${ch.coord}${visibleFragment === null ? "" : `#${visibleFragment}`} to revisit ]`,
1507
+ content: `[ stream closed (${ch.close_status ?? 200}) — ${pointer} ]`,
1504
1508
  mimetype: "text/stream",
1505
1509
  }),
1506
1510
  attrs: JSON.stringify({ streamEnd: ch.content.length, terminal: true }),