@plurnk/plurnk-service 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.defaults +7 -21
- package/INSTALL.md +18 -17
- package/README.md +1 -1
- package/SPEC.md +125 -115
- package/dist/build-info.json +1 -1
- package/dist/core/ChannelWrite.sql +3 -3
- package/dist/core/CoreSchemeServices.d.ts +1 -1
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +1 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +2 -2
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +79 -1115
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +9 -33
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +58 -1656
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +46 -32
- package/dist/core/LogBody.d.ts +1 -0
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +4 -0
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/ModelCall.d.ts +2 -2
- package/dist/core/ModelCall.d.ts.map +1 -1
- package/dist/core/ModelCall.js +4 -2
- package/dist/core/ModelCall.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +11 -22
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +66 -168
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProblemLog.d.ts +1 -1
- package/dist/core/ProblemLog.d.ts.map +1 -1
- package/dist/core/ProblemLog.js +14 -3
- package/dist/core/ProblemLog.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts +2 -2
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +14 -5
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.js +2 -2
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/ResourceMutations.d.ts +73 -0
- package/dist/core/ResourceMutations.d.ts.map +1 -0
- package/dist/core/ResourceMutations.js +1142 -0
- package/dist/core/ResourceMutations.js.map +1 -0
- package/dist/core/SchemeRegistry.js +3 -3
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/StoredPacket.d.ts +2 -2
- package/dist/core/StoredPacket.js +7 -7
- package/dist/core/TurnRunner.d.ts +72 -0
- package/dist/core/TurnRunner.d.ts.map +1 -0
- package/dist/core/TurnRunner.js +1730 -0
- package/dist/core/TurnRunner.js.map +1 -0
- package/dist/core/caps/DbChannelCaps.js +4 -4
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/content-hash.js +2 -2
- package/dist/core/content-hash.js.map +1 -1
- package/dist/core/content-weight.d.ts +2 -0
- package/dist/core/content-weight.d.ts.map +1 -0
- package/dist/core/content-weight.js +4 -0
- package/dist/core/content-weight.js.map +1 -0
- package/dist/core/content_weight.d.ts +4 -0
- package/dist/core/content_weight.d.ts.map +1 -0
- package/dist/core/content_weight.js +6 -0
- package/dist/core/content_weight.js.map +1 -0
- package/dist/core/fork.sql +5 -5
- package/dist/core/packet-wire.d.ts +4 -4
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +21 -21
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/scheme-types.d.ts +1 -1
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/digest/Digest.js +1 -1
- package/dist/digest/Digest.js.map +1 -1
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +26 -10
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +1 -1
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +1 -1
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +2 -2
- package/dist/schemes/Log.sql +2 -2
- package/dist/schemes/Worker.js +2 -2
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.js +4 -4
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +2 -2
- package/dist/schemes/_entry-find.d.ts +5 -5
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +29 -28
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-manifest.js +4 -4
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +3 -3
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +3 -3
- package/dist/server/Daemon.d.ts +7 -42
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +124 -887
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +117 -0
- package/dist/server/DrainSupervisor.d.ts.map +1 -0
- package/dist/server/DrainSupervisor.js +802 -0
- package/dist/server/DrainSupervisor.js.map +1 -0
- package/dist/server/lifecycle-recovery.sql +3 -0
- package/dist/server/logEntry.d.ts +1 -1
- package/dist/server/logEntry.js +1 -1
- package/dist/server/seam-entry-read.sql +2 -2
- package/dist/service.js +1 -1
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +20 -10
- package/package.json +33 -33
- package/dist/core/ruler_count.d.ts +0 -4
- package/dist/core/ruler_count.d.ts.map +0 -1
- package/dist/core/ruler_count.js +0 -7
- package/dist/core/ruler_count.js.map +0 -1
- package/dist/core/token-ruler.d.ts +0 -2
- package/dist/core/token-ruler.d.ts.map +0 -1
- package/dist/core/token-ruler.js +0 -5
- package/dist/core/token-ruler.js.map +0 -1
package/SPEC.md
CHANGED
|
@@ -26,7 +26,7 @@ flowchart LR
|
|
|
26
26
|
| **agent** | PLURNK | The plurnk runtime. Acts in-workspace as the reserved `plurnk` worker ({§actor-boundary} self-hosting), never a privileged singleton owning its own entries ({§entry-owner}, {§machine-processes}). |
|
|
27
27
|
| **workspace** | Core | Durable user-named shared world. Persists across workers and process restarts. Identity: `workspaces.id` + unique `workspaces.name`. |
|
|
28
28
|
| **worker** | Core | Durable actor and private history over one workspace. Owns its loops and log rows, may carry a `parent_worker_id`, and has one process-local cancellation scope while active. |
|
|
29
|
-
| **loop** | Core | Queued-to-terminal unit of model or client work within a worker. Status ∈ {100 pending · 102 running · 200 done · 202 waiting (blocked on a live obligation, {§send}) · 413
|
|
29
|
+
| **loop** | Core | Queued-to-terminal unit of model or client work within a worker. Status ∈ {100 pending · 102 running · 200 done · 202 waiting (blocked on a live obligation, {§send}) · 413 provider input-capacity failure · 429 turn-ceiling · 499 cancelled · 500 failed · 504 wall-clock timeout ({§operator-config-loop-timeout}) · 508 runaway}. Many loops may belong to one worker. |
|
|
30
30
|
| **turn** | Core | One engine scheduling unit (or one client-op scheduling unit). A model turn sends one assembled prompt through one or more emission attempts and admits at most one response. Many turns may belong to one loop. Identity: `(loop_id, sequence)`. |
|
|
31
31
|
| **model call** | Core/provider | One logical `provider.generate` invocation. Emission attempts and BARE inferences share this durable accounting owner; provider retries remain cardinal physical requests beneath it. Identity: `(turn_id, sequence)`. |
|
|
32
32
|
| **op** | Model/core | One DSL operation the model emits, parsed into a `PlurnkStatement`. One admitted turn produces zero or more ops. |
|
|
@@ -36,14 +36,14 @@ flowchart LR
|
|
|
36
36
|
| **AG-UI Run** | AG-UI protocol | A client request/stream envelope identified by the client's `runId`. A message or resume AG-UI Run binds to one core loop; a management-action AG-UI Run may complete without creating a core loop. |
|
|
37
37
|
| **AG-UI thread** | AG-UI protocol | Conversation identity. Within an explicitly selected workspace, `threadId` resolves to one conversation worker. |
|
|
38
38
|
| **`--run`** | Client compatibility | A compatibility-sensitive client spelling, not an internal entity. |
|
|
39
|
-
| **session** | Retired/unqualified | Not a PLURNK lifecycle noun. Use the actual core noun; a third-party standard may use only its explicitly qualified protocol term.
|
|
39
|
+
| **session** | Retired/unqualified | Not a PLURNK lifecycle noun. Use the actual core noun; a third-party standard may use only its explicitly qualified protocol term. <!-- lexicon-allow: this row defines the retired noun --> |
|
|
40
40
|
|
|
41
41
|
### §storage-terms Storage terms
|
|
42
42
|
|
|
43
43
|
| Term | Meaning |
|
|
44
44
|
|---|---|
|
|
45
45
|
| **entry** | The unit of canonical state. Identity: `(workspace, owner, scheme, pathname)` ({§entry-identity-no-null}). Holds one or more `channels` of content plus private `attributes`. |
|
|
46
|
-
| **channel** | A named content buffer on an entry. Examples: `body`, `stdout`, `stderr`, `headers`, `symbols`. Each channel has `content`, `mimetype`, `
|
|
46
|
+
| **channel** | A named content buffer on an entry. Examples: `body`, `stdout`, `stderr`, `headers`, `symbols`. Each channel has `content`, `mimetype`, curation `weight`, and lifecycle `state`. |
|
|
47
47
|
| **scope** | A scheme-manifest declaration ignored by core; registrations are discovered at boot and are not persisted. Entry sharing and privacy are owner-based; #80 owns retiring this residual axis. |
|
|
48
48
|
| **scheme** | An addressed capability family + handler. Built-ins include `worker`, `prompt`, `log`, and bare/file paths; discovered schemes and executor-runtime tags extend that set. Internal `exec` routes the EXEC op but is not an addressable model namespace. Consumption surface {§scheme-surface}; author contract: [plurnk-schemes](../plurnk-schemes/SPEC.md). |
|
|
49
49
|
| **mimetype** | A channel's content type. Drives the handler that produces the structural projections (`symbols`, `deepJson`, `deepXml`). Consumption surface {§mimetype-surface}; author contract: [plurnk-mimetypes](../plurnk-mimetypes/SPEC.md). |
|
|
@@ -192,7 +192,18 @@ Composed daemon internals + admin CLI. Four plug points:
|
|
|
192
192
|
- **Mimetypes** ({§mimetype}) — content interpretation. Render-time handlers consume channel content; framework owns the dispatch.
|
|
193
193
|
- **Executors** ({§exec} / {§bundled-set}) — EXEC runtime dispatch for subprocess, search, data, and pure-computation runtimes.
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
Core's internal owners compose without becoming new package or public seams:
|
|
196
|
+
|
|
197
|
+
| Owner | Machine |
|
|
198
|
+
|-------|---------|
|
|
199
|
+
| `Daemon` | Process/module lifecycle, dependency composition, provider policy, notifications, and the external client façade. |
|
|
200
|
+
| `DrainSupervisor` | One worker's queue consumer, drain identity, wake obligations, cancellation scope, poll/park timers, and terminal cleanup. |
|
|
201
|
+
| `Engine` | Loop lifecycle and the public turn, dispatch, derivation, and proposal façades. |
|
|
202
|
+
| `TurnRunner` | One durable model turn from materialization through provider admission, operation scheduling, and settlement. |
|
|
203
|
+
| `Dispatcher` | Operation admission/routing, scheme execution, proposal waiting, curation, and durable log writes. |
|
|
204
|
+
| `ResourceMutations` | EDIT/COPY/MOVE selection, anchor preconditions, cross-scheme effects, and mutation settlement. |
|
|
205
|
+
|
|
206
|
+
Capability-specific behavior remains with the owning plug point.
|
|
196
207
|
|
|
197
208
|
The contracts package (`@plurnk/plurnk-contracts`) owns the parser and AST contract. Schemes receive parsed statement fragments via dispatch.
|
|
198
209
|
|
|
@@ -538,7 +549,7 @@ stateDiagram-v2
|
|
|
538
549
|
Terminal --> [*]
|
|
539
550
|
```
|
|
540
551
|
|
|
541
|
-
§worker-lifecycle-state-machine 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.
|
|
552
|
+
§worker-lifecycle-state-machine 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. `DrainSupervisor` owns claim, wake, and cancellation; the dispatcher owns model-requested park/conclusion; the daemon owns boot-recovery orchestration; 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.
|
|
542
553
|
|
|
543
554
|
§stream-catalog-lifecycle Streams are independently durable subscriptions owned by a worker. Payload and
|
|
544
555
|
lifecycle are orthogonal: zero bytes is a valid payload for both success and
|
|
@@ -549,7 +560,6 @@ their exact `status` and derive `closed` (status below 400), `killed` (499), or
|
|
|
549
560
|
`failed` (other failure status). An entry with no subscription has no `stream`
|
|
550
561
|
member. This is historical state, not merely a live-process hint.
|
|
551
562
|
|
|
552
|
-
|
|
553
563
|
```mermaid
|
|
554
564
|
stateDiagram-v2
|
|
555
565
|
[*] --> Open: executor registers subscription
|
|
@@ -631,7 +641,6 @@ it is the parked lifecycle state, not a terminal. No product surface may infer o
|
|
|
631
641
|
reconstruct a result from that projection. Active rows have no terminal result;
|
|
632
642
|
terminal rows must have one, and database triggers enforce both directions.
|
|
633
643
|
|
|
634
|
-
|
|
635
644
|
```mermaid
|
|
636
645
|
flowchart TD
|
|
637
646
|
W[Worker cancellation] --> L[Terminalize unresolved loops]
|
|
@@ -654,8 +663,8 @@ boundary.
|
|
|
654
663
|
- §worker-lifecycle-total-reap **Cancellation is recursive and reaps every held stream.** `loop.cancel`, worker `KILL`, shutdown, and a worker's SEND signal `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. Before shutdown awaits drains, it cancels every process-local proposal waiter through {§proposal-cancel-aborts} with outcome `daemon_stopping`, so a stopped-world dispatch cannot hold teardown open. 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 `## KILL0 [code]` on one live stream instead delivers exactly that signal once (bare KILL uses the executor's SIGHUP default; `## KILL0 [9]` uses SIGKILL).
|
|
655
664
|
- §worker-lifecycle-exec-epoch-bound **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.
|
|
656
665
|
- §worker-lifecycle-no-resurrection **A cancelled worker is not resurrected by its own torn-down work.** A stream conclusion delivered to a cancelled, idle worker starts no fresh drain: an aborted (499) conclusion is skipped, and a straggler that concluded cleanly surfaces its deliverable as an environment delta ({§env-delta}), never a revived loop. The cancel was deliberate; only an explicit `runLoop` request resumes the worker.
|
|
657
|
-
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** When a backgrounded stream concludes,
|
|
658
|
-
- §worker-lifecycle-child-wake **A child worker concluding wakes a parent blocked on it — the topology join.** `worker://` spawn/fork records `parent_worker_id` ({§lifecycle-terms}). When a worker's drain exits having **concluded** — no `202`-blocked loop, no open stream —
|
|
666
|
+
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** When a backgrounded stream concludes, `DrainSupervisor` routes it through the same inject seam as any loop source ({§actor-boundary-passive-wake}): an active worker folds the conclusion into its next turn; a worker **blocked on a 202 wait** for that stream ({§wait-obligation-matrix}) **awakens that loop in place** — the blocked loop *is* the continuation, so there is no fresh loop and no summary-as-prompt fiction. The result is never lost: a blocked loop sleeps rather than ending, and the stream's status-transition is the arrival ({§actor-boundary-passive-wake}) that wakes it; on resume it reads the concluded stream's own state, not a synthetic prompt.
|
|
667
|
+
- §worker-lifecycle-child-wake **A child worker concluding wakes a parent blocked on it — the topology join.** `worker://` spawn/fork records `parent_worker_id` ({§lifecycle-terms}). When a worker's drain exits having **concluded** — no `202`-blocked loop, no open stream — `DrainSupervisor.#onDrainExit` resumes its parent **in place** through the shared `#wakeParkedWorker`, the same 202→100 resume a stream conclusion uses. So a parent that spawns work and blocks with SEND signal `202` is woken the moment its child finishes; on resume it reads the child's deliverable from the {§worker-scheme-collect} delta in its own log — a control edge, **never an injected prompt**. The wake recurses upward via the parent's own drain-exit. A child still running — or itself blocked at 202 — is not *concluded*, so it does not wake the parent (it's still a live thing the subtree holds). This is the structured-concurrency join: streams and child workers are the same kind of "live thing a worker holds," driving premature-terminate ({§send-premature-terminate}), the wake edge, and the collect delta identically. A worker conclusion is a **bounded, un-loseable** wake: if the conclusion fires while the parent is mid-turn (before its block commits), `#wakeParkedWorker` finds it not-yet-slept and records an **owed wake**, which the drain honors when the parent blocks — so a wait awaiting workers **always returns**, never dead-blocks on a conclude-before-block race. (Only a live exec stream, unbounded absent a timeout, may legitimately hold a wait open.)
|
|
659
668
|
- §worker-optimistic-settlement **Asynchronous settlement receives one bounded worker-local opportunity before model dispatch.** An initiating turn lets only the streams it started settle before its terminal SEND; separately, a stream or direct-child conclusion persists and publishes immediately but holds the parked worker's single `202→100` requeue while another stream or direct child remains live. Both use `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS`, shipped at five seconds; zero disables the opportunity. The wake hold ends as soon as no sibling obligation remains, never extends its original deadline, and coalesces every conclusion that lands within it into one requeue. With no sibling obligation the wake is immediate; at the deadline, surviving work follows the ordinary monitored lifecycle. A conclusion that lands after provider dispatch begins retains its next wake, while poll, park-deadline, prompt, and operator wakes never open this hold. Only packet/provider dispatch waits: terminal state, client events, cancellation, and child execution do not. One redaction-safe span records elapsed time, quiescence versus deadline, and conclusion count without entering the packet.
|
|
660
669
|
- §worker-lifecycle-idle-is-concluded **An idle worker concludes; it does not park.** A loop is idle only when it has neither live obligations nor completed results awaiting their first packet. A live child or stream blocks a SEND signal `202` join; a completed stream, child result, or same-turn retrieval continues directly to the next packet where it is observed. Only after those sets are drained does signal `202` resolve like signal `200`. There is no held-open idle loop and no `loop/quiesced` soft signal. A concluded worker is durable working history and an addressed arrival reawakens it as a new loop.
|
|
661
670
|
- §worker-lifecycle-no-lost-loop **A loop is never stranded by a drain's exit.** A drain relinquishes its registry slot only after a lock-held re-claim confirms the queue is empty; a loop enqueued during that teardown is either re-claimed by the exiting drain or claimed by a fresh drain that a later inject starts. The relinquish and the start are serialized, so neither the lost-loop hang nor a transient double-drain can occur.
|
|
@@ -670,12 +679,13 @@ Author-facing contract: [`@plurnk/plurnk-providers`](../plurnk-providers/SPEC.md
|
|
|
670
679
|
|
|
671
680
|
### §provider-surface Consumption surface
|
|
672
681
|
|
|
673
|
-
|
|
682
|
+
Three current entry points:
|
|
674
683
|
|
|
675
684
|
- §provider-surface-generate `provider.generate(args)` — once per logical model call. An emission attempt supplies the complete packet messages, worker/turn coordinates, generation envelope, optional local grammar, first-party metadata, and `callKind: "emission"`. A BARE inference supplies only one user message containing its body plus non-prompt call identity and accounting metadata, including `callKind: "bare"` ({§bare-inference} {§provider-call-kind}). Both receive a durable physical-request observer; provider-owned retry and failover may issue several ordered requests beneath either call. A successful `ProviderResponse` reaches its call-specific consumer; a `ProviderError.attempt` remains failed response evidence under {§provider-interrupted-attempt}. Core persists normalized response evidence separately from physical accounting and relays encrypted reasoning only from an admitted emission ({§encrypted-reasoning-carrier}).
|
|
676
|
-
- §provider-surface-
|
|
685
|
+
- §provider-surface-capacity `provider.assessRequestCapacity(messages, maxOutputTokens?, signal?)` — provider-owned intersection of request-shaped token evidence and every known physical input limit. It admits, rejects only a proven exact overflow, or defers ambiguity to upstream ({§tokenomics-context-envelope-admission}). `generate` performs this assessment for its exact request and preserves the evidence on success and capacity failure.
|
|
686
|
+
- §provider-surface-prompt-measurement `provider.countPromptTokens(messages, signal)` — the cancellable complete-request measurement primitive used by provider capacity assessment, with `exact`, `upper_bound`, `estimate`, or `unavailable` provenance. Core never substitutes this physical fact for its curation ruler.
|
|
677
687
|
|
|
678
|
-
§provider-surface-identity Provider capacity and identity are immutable for one instance. `contextWindow`
|
|
688
|
+
§provider-surface-identity Provider capacity and identity are immutable for one instance. `contextWindow`, `maxInputTokens`, and `maxOutputTokens` carry known model limits; `outputBudget` is the total generation envelope, optional `reasoningBudget` is its strict subset, and `inputCapacity` is the stable intersection of known input constraints ({§tokenomics}). Unknown facts remain `null`. `model` identifies persisted turn/provider evidence. Local GBNF boot verification also consumes `constrainsOutput` ({§grammar-enforcement-verified-at-boot}).
|
|
679
689
|
|
|
680
690
|
§meta-passthrough **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 in `loop/terminated.usage` ({§notifications}). 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}).
|
|
681
691
|
|
|
@@ -687,13 +697,13 @@ Two current entry points:
|
|
|
687
697
|
- BARE calls admitted by one turn launch as one parallel batch; each call retains independent observer and failure state, and the engine awaits the complete batch before committing results in authored order ({§bare-inference}).
|
|
688
698
|
- §provider-guarantees-request-observer Immediately before each physical provider I/O, the provider opens its provider/model identity through `observeRequest` and settles the returned handle exactly once as response or error. Core durably records that occurrence before I/O and rejects a returned response or `ProviderError` whose ordered accounting differs from the observed records. Persistence failure is an internal contract failure, never optional telemetry.
|
|
689
699
|
- §provider-guarantees-assistantraw-opaque `assistantRaw` is opaque to the engine (forensics-only).
|
|
690
|
-
-
|
|
700
|
+
- Capacity assessment receives the exact `PacketWire` messages supplied to `generate`, its effective total-output tightening, and the loop cancellation signal. It may perform provider I/O. A curation-weight comparison never authorizes or rejects physical I/O.
|
|
691
701
|
|
|
692
702
|
### §emission-admission Provider emission admission
|
|
693
703
|
|
|
694
704
|
A completed provider exchange is an **emission attempt**, not necessarily an engine turn. The provider transports and observes the model's bytes; ANTLR is the admission authority only after provider completion. Admission asks whether the exchange has a trustworthy frame: its first parsed operation is PLAN, its last parsed operation is a terminal SEND, every hard parse error is bounded between those anchors, and no `unparsedTail` exists. Missing anchors, an error outside the frame, or a boundary-destroying tail rejects the entire exchange regardless of `finishReason`; no recovered prefix dispatches. Parser warnings remain admissible. `finish=length` is forensic evidence of likely truncation, not an independent rejection rule. A provider-declared resource interruption never reaches admission, even when its partial bytes form a complete-looking frame ({§provider-interrupted-attempt}).
|
|
695
705
|
|
|
696
|
-
Core retries a rejected emission against the exact same packet beneath the same engine turn, up to `PLURNK_SERVICE_EMISSION_ATTEMPTS`. Rejected bytes never dispatch or reach the engine strike rail. Before each `generate`, Core opens one durable logical `model_calls` row and its emission-specific `turn_attempts` admission row. Beneath the model call, every provider observer invocation opens one cardinal `provider_requests` occurrence immediately before physical I/O and settles it as response or error. Adapter retries and capacity failover append requests in issue order; a response-less failure therefore remains an accounted occurrence rather than disappearing. Normalized response evidence is durable before parser classification and does not duplicate the separately owned accounting. The accepted exchange alone completes `turns.packet`; every physical request remains in turn and loop accounting, while the context gauge reads the latest settled emission request on the latest turn. Digest exposes rejected response evidence as `packetNNN.attemptNNN.rejected.*` and every physical request in its machine-readable ledger.
|
|
706
|
+
Core retries a rejected emission against the exact same packet beneath the same engine turn, up to `PLURNK_SERVICE_EMISSION_ATTEMPTS`. Rejected bytes never dispatch or reach the engine strike rail. Before each `generate`, Core opens one durable logical `model_calls` row and its emission-specific `turn_attempts` admission row. A call that ends without response evidence leaves that admission row unclassified (`accepted IS NULL`) and does not consume the emission-attempt ceiling. Beneath the model call, every provider observer invocation opens one cardinal `provider_requests` occurrence immediately before physical I/O and settles it as response or error. Adapter retries and capacity failover append requests in issue order; a response-less failure therefore remains an accounted occurrence rather than disappearing. Normalized response evidence is durable before parser classification and does not duplicate the separately owned accounting. The accepted exchange alone completes `turns.packet`; every physical request remains in turn and loop accounting, while the context gauge reads the latest settled emission request on the latest turn. Digest exposes rejected response evidence as `packetNNN.attemptNNN.rejected.*` and every physical request in its machine-readable ledger.
|
|
697
707
|
|
|
698
708
|
The first exhaustion in a consecutive sequence closes that unadmitted turn as a continue and opens exactly one ordinary recovery turn. Its packet projects the latest rejected response OPEN from a durably FOLDED model item under {§model-entry-log-curation} and carries one transient `invalid_emission` Notice whose complete message is: `Your previous response contained an unrecoverable syntax error. No operations were performed. Try again.` No parser diagnostic, attempt count, or rail state becomes model-facing. The recovery turn has its own honestly stored packet and its configured private same-packet attempts. The packet-local projection never changes the row's curation state, so no later packet repeats the malformed body unless the model explicitly OPENs it. Admission clears the recovery state; exhausting the informed turn terminates instead of opening another.
|
|
699
709
|
|
|
@@ -721,7 +731,7 @@ an assistant or a zero-valued observation.
|
|
|
721
731
|
A plugin may declare opaque attribution tags statically or at runtime under the
|
|
722
732
|
shared contract {§plugin-attribution}:
|
|
723
733
|
|
|
724
|
-
```
|
|
734
|
+
```jsonc
|
|
725
735
|
{ "plurnk": { "attribution": "@acme/widgets" } } // always-on string or string[]
|
|
726
736
|
```
|
|
727
737
|
|
|
@@ -787,7 +797,7 @@ inside the projected reasoning prefix has no false content pointer. With no
|
|
|
787
797
|
local GBNF, core adds no rail state and makes no claim about endpoint-owned
|
|
788
798
|
settings.
|
|
789
799
|
|
|
790
|
-
```
|
|
800
|
+
```dotenv
|
|
791
801
|
PLURNK_MODEL_gemma=openai/macher.gguf
|
|
792
802
|
PLURNK_MODEL_opus=openrouter/anthropic/claude-opus-latest
|
|
793
803
|
PLURNK_MODEL=gemma
|
|
@@ -1013,9 +1023,9 @@ Engine → scheme guarantees:
|
|
|
1013
1023
|
- `ctx.signal` is wired to the worker's AbortController ({§provider-guarantees-signal-wired}).
|
|
1014
1024
|
- §scheme-surface-exception-500 Scheme exceptions are contract violations. Core records their complete cause in daemon diagnostics, closes the action with a generic core-owned 500 Problem, and surfaces that durable row in the next turn's `errors` section ({§operation-results}). Implementation exception text is not repurposed as a model recovery instruction.
|
|
1015
1025
|
|
|
1016
|
-
**
|
|
1017
|
-
populates `entry_channels.
|
|
1018
|
-
({§tokenomics-
|
|
1026
|
+
**Curation-weight participation.** Core's shared `_entry-crud.ts` write helper
|
|
1027
|
+
populates `entry_channels.weight` at write time through `ctx.weigh`
|
|
1028
|
+
({§tokenomics-weight-stored-at-write}). Scheme handlers reach that path through
|
|
1019
1029
|
the public `ctx.entries` capability. Raw database writes are outside the scheme
|
|
1020
1030
|
API and receive no implicit token accounting.
|
|
1021
1031
|
|
|
@@ -1106,9 +1116,9 @@ discovery ({§mimetype-discovery}).
|
|
|
1106
1116
|
|
|
1107
1117
|
**Token accounting.** The daemon injects no tokenizer into `Mimetypes`; content
|
|
1108
1118
|
projection is independent of packet budgeting. Core uses the stable
|
|
1109
|
-
model-independent ruler for stored/catalog weights and the model-facing budget
|
|
1119
|
+
model-independent ruler for stored/catalog weights and the model-facing curation budget
|
|
1110
1120
|
({§tokenomics-agnostic-ruler}). The provider's request-shaped measurement is
|
|
1111
|
-
confined to
|
|
1121
|
+
confined to provider-owned physical capacity assessment
|
|
1112
1122
|
({§tokenomics-context-envelope-admission}).
|
|
1113
1123
|
|
|
1114
1124
|
§persistent-search-index **Persistent search index.** `SearchIndex.maintain` is the pre-model engine pass. Each searchable resource supplies an address and the exact readable body its READ exposes. Entries supply their default body; `LogBody` resolves each log row's canonical full body from its durable tx/rx envelope. Acquisition schemes project remote source material before storing that body; search never introduces a second hidden text projection. The readable body, mimetype, resolved text/binary classification, mimetype projection identity, embedder configuration, and applicable search exclusion form a content hash. Complete artifacts own FTS, vectors, symbol definitions, and references; resource rows hold only the attachment hash. Binary, empty, and excluded derivations do not invoke handler projections and therefore use one fixed no-projection identity.
|
|
@@ -1341,7 +1351,7 @@ OPEN/FOLD operate on the **log** (`log:///`) - the model's context-curation surf
|
|
|
1341
1351
|
|
|
1342
1352
|
### §jsonplurnk The Log's wire format
|
|
1343
1353
|
|
|
1344
|
-
The `## Log` section renders as a fixed three-backtick `jsonplurnk` fence - a JSON array of entry objects, otherwise-valid JSON with **exactly one** deviation: an open, nonempty `body` is a raw multiline string. Its opening JSON quote is followed by a physical newline, every content line begins with a numeric `N:` or anchored `@hash N:` coordinate prefix whose separator is one or more ASCII spaces, and its closing quote appears at column zero before either the object close or a following member. Source quotes, braces, fences, and headings cannot collide with either boundary because source text never occupies column zero after projection; source backticks therefore cannot form a CommonMark closing fence. The fixed opener keeps the packet prefix stable across content changes. The carve-out is localized to `body`, so the strip-parser recognizes `"body":"` followed by a newline, consumes one or more coordinate-prefixed lines, and replaces the raw multiline value with an escaped JSON string while preserving following members to recover strict JSON. The body shape is a strict three-state invariant: `"display":"none","body":""` for no body, `"display":"folded"` with the ordinary projection withheld, and `"display":"open","body":"\n<coordinate>...\n"` with it shown. `path` is the complete model-facing log identity: when a projected operation exists it ends in `/OP`, and no separate `op` field duplicates it. Nonempty `tags` is the row's complete deduplicated, sorted folksonomy; an untagged row omits it. When an OPEN bounded projection differs from the canonical body, it appends `"chunk":"showing <selected> of <complete>"` after `body`; otherwise it omits `chunk`. Complete-line extents use inclusive two-coordinate line regions. A cut inside a line uses four-coordinate, start-inclusive and end-exclusive regions with 1-based Unicode code-point columns. The row's `path` remains the canonical READ target. The block is data only - no prose leads the fence. `tokens` is the
|
|
1354
|
+
The `## Log` section renders as a fixed three-backtick `jsonplurnk` fence - a JSON array of entry objects, otherwise-valid JSON with **exactly one** deviation: an open, nonempty `body` is a raw multiline string. Its opening JSON quote is followed by a physical newline, every content line begins with a numeric `N:` or anchored `@hash N:` coordinate prefix whose separator is one or more ASCII spaces, and its closing quote appears at column zero before either the object close or a following member. Source quotes, braces, fences, and headings cannot collide with either boundary because source text never occupies column zero after projection; source backticks therefore cannot form a CommonMark closing fence. The fixed opener keeps the packet prefix stable across content changes. The carve-out is localized to `body`, so the strip-parser recognizes `"body":"` followed by a newline, consumes one or more coordinate-prefixed lines, and replaces the raw multiline value with an escaped JSON string while preserving following members to recover strict JSON. The body shape is a strict three-state invariant: `"display":"none","body":""` for no body, `"display":"folded"` with the ordinary projection withheld, and `"display":"open","body":"\n<coordinate>...\n"` with it shown. `path` is the complete model-facing log identity: when a projected operation exists it ends in `/OP`, and no separate `op` field duplicates it. Nonempty `tags` is the row's complete deduplicated, sorted folksonomy; an untagged row omits it. When an OPEN bounded projection differs from the canonical body, it appends `"chunk":"showing <selected> of <complete>"` after `body`; otherwise it omits `chunk`. Complete-line extents use inclusive two-coordinate line regions. A cut inside a line uses four-coordinate, start-inclusive and end-exclusive regions with 1-based Unicode code-point columns. The row's `path` remains the canonical READ target. The block is data only - no prose leads the fence. `tokens` is the model-facing projection of the row's curation weight: the room OPEN adds and FOLD saves. A FIND's nonzero `itemsTokenTotal` weighs the complete matched set; a nonzero `returnedItemsTokenTotal` appears only when the returned page has a different weight. These are curation weights, not dollars. The invariants bind regardless of shape ({§packet}): addressability (`path`/`target`/`#channel`/coordinate-prefixed bodies), weighability (per-item `tokens`), honesty (every 4xx/5xx row and the exact body/display state). {§jsonplurnk} {§packet-jsonplurnk-exception}
|
|
1345
1355
|
|
|
1346
1356
|
### §retrieval-packet-metadata READ/FIND packet metadata
|
|
1347
1357
|
|
|
@@ -1464,7 +1474,7 @@ AST: `{ op: "FIND", target (scope), body: MatcherBody | null (predicate), signal
|
|
|
1464
1474
|
- §find-glob-filter-on-content `body` matcher operates on entry content (glob/regex/jsonpath/xpath), per `plurnk.md` "Pattern Filtering"; the path-glob lives in the (target), not the body.
|
|
1465
1475
|
- §find-semantic-selection Every matcher operates only over the candidate set selected by `(target)`; relation matchers do not bypass that selection. Semantic ranking is exhaustive within that candidate set, then applies the ordinary FIND result scope. Markerless semantic FIND therefore uses the same `<1,16>` default as every other matcher. Integers retain FIND's positional contract: `<N>` selects result N and `<N,M>` selects the inclusive range. A leading decimal first applies a minimum cosine-similarity threshold; following integers select positions within that ranked threshold set. Thus `<0.7,10,20>` means threshold 0.7 followed by results 10 through 20, while `<0.7>` applies the threshold and the ordinary first-16 page.
|
|
1466
1476
|
- §find-scoped-isolation Workspace + scheme scoped — no cross-workspace/cross-scheme leakage.
|
|
1467
|
-
- §find-result-projection **The authored target shape determines the result unit; result cardinality never changes it** ({§find-result-unit}). Returns `FindResult { status, content, mimetype, results, range, matchingPathCount, matchLocationCount,
|
|
1477
|
+
- §find-result-projection **The authored target shape determines the result unit; result cardinality never changes it** ({§find-result-unit}). Returns `FindResult { status, content, mimetype, results, range, matchingPathCount, matchLocationCount, itemsWeightTotal, returnedItemsWeightTotal }`:
|
|
1468
1478
|
|
|
1469
1479
|
| Target | Matcher body | `range.unit` | Result rows |
|
|
1470
1480
|
|---|---|---|---|
|
|
@@ -1488,25 +1498,26 @@ AST: `{ op: "FIND", target (scope), body: MatcherBody | null (predicate), signal
|
|
|
1488
1498
|
contains one nonempty, flat channel array per resource. Element `[0]` is always
|
|
1489
1499
|
the default channel and carries the bare resource path; later elements carry
|
|
1490
1500
|
their complete `path#channel` addresses. Each channel is
|
|
1491
|
-
`{ path, mimetype,
|
|
1501
|
+
`{ path, mimetype, weight, lines, parseIssues? }`; `parseIssues` is the
|
|
1492
1502
|
positive-only advisory projection of `{§mimetype-parse-issues}` for the exact
|
|
1493
1503
|
body derivation under `{§scheme-catalog-parse-issues}`. Resource-level `stream` and broad-match
|
|
1494
1504
|
`matchLocationCount` live only on `[0]`. A single-channel resource is therefore
|
|
1495
1505
|
a one-element array, with no path-owning wrapper or duplicated channel map.
|
|
1496
1506
|
A terminal single-star path scope is a one-level map: direct entries retain
|
|
1497
1507
|
that shape, while deeper first-segment directories collapse to the one-element
|
|
1498
|
-
group `[{ path: "dir/**", items,
|
|
1508
|
+
group `[{ path: "dir/**", items, weight }]`, where the selector and both aggregates
|
|
1499
1509
|
describe the exact recursive subtree. Scope summaries are navigation
|
|
1500
1510
|
metadata, not resources. Markerless FIND returns positions 1–16 in the
|
|
1501
1511
|
selected unit; `<N,M>` selects an inclusive page and `<1,-1>` explicitly
|
|
1502
1512
|
selects all. `range` reports the unit, complete result total, normalized
|
|
1503
|
-
request, and returned positions ({§range-extent}). `
|
|
1504
|
-
`
|
|
1513
|
+
request, and returned positions ({§range-extent}). `itemsWeightTotal` weighs the complete matched set while
|
|
1514
|
+
`returnedItemsWeightTotal` weighs the returned resource page; in exact
|
|
1505
1515
|
location mode both weigh the one selected resource once. Resource order is
|
|
1506
1516
|
rank for `~`semantic and candidate order otherwise; location order is dialect
|
|
1507
1517
|
order and exact duplicates deduplicate. The intended drill-down is broad FIND
|
|
1508
1518
|
to choose paths, exact-target FIND to choose locations, then exact READ.
|
|
1509
|
-
`content` uses the shared generated-JSON projection
|
|
1519
|
+
`content` uses the shared generated-JSON projection and translates only this
|
|
1520
|
+
final model-facing representation from `weight` to `tokens`
|
|
1510
1521
|
({§json-result-rendering}), so universal packet numbering makes result
|
|
1511
1522
|
ordinal N addressable as line N, matching `<N>` pagination without a second
|
|
1512
1523
|
coordinate system. Pagination is the only FIND materialization bound; no
|
|
@@ -1717,8 +1728,9 @@ The runtime scheme participates in the durable lookup; a completed `sh:///`
|
|
|
1717
1728
|
stream cannot fall through an internal `exec`-only query. {§stream-control}
|
|
1718
1729
|
|
|
1719
1730
|
§exec-env-scoped **Scoped environment.** An EXEC subprocess inherits the *project's* environment — its `.env`, the standard shell vars — so the model's commands run as the project expects; but never plurnk's own secrets: the provider API keys and `PLURNK_*` config are stripped before the spawn, so a model-executed command can't `printenv` the engine's keys. The service owns the scoping policy (the denylist); the executor spawns with the env it is handed.
|
|
1731
|
+
|
|
1720
1732
|
- §exec-hold-until-concluded **The turn-hold exception** — for runtimes in `PLURNK_SERVICE_EXEC_HOLD` (a decision-table env, shipped listing the search family), an in-flight stream **pauses the cycle**: the next packet does not assemble until the stream concludes, so the model never burns a turn asking "are we there yet" about a result the engine controls end-to-end. This exception is limited to seconds-bounded runtimes whose final result the engine controls end-to-end. Bounded by `PLURNK_SERVICE_EXEC_HOLD_MS` and **fail-open**: at the cap the standard cycle resumes untouched (waits, wakes, polls). Zero grammar or teaching surface — the model emits EXEC followed by SEND signal `102` as ever; the wake-shaped world simply arrives one packet sooner. It extends selected runtimes beyond the ordinary {§worker-optimistic-settlement} cap before the next packet assembles. A bare entry holds ALL of a runtime's spawns; a `<runtime>:<effect>` suffix (`github:read`) holds only that effect-class — an MCP server is one runtime whose tools split (a `read` `get_issue` is instant; a `host` `run_migration` is a slow mutation), so an operator opts the known-fast read-class in without parking on the mutation. Conservative stays default: an arbitrary third-party server's latency never parks the engine unless a suffix opts a class in.
|
|
1721
|
-
- §exec-entry-sink **The entry() sink** — an executor may *request* entry materialization (execs SPEC §2.6: every sink is a consumer-implemented callback; the executor owns zero substrate). The service implements it in exec dispatch: `entry(path, content: string | null, {tags, mimetype?})` upserts the entry, then journals ONE typed `EDIT` row in the reserved `plurnk` worker's log — the fs-fiction pattern, `source` = the calling worker, `
|
|
1733
|
+
- §exec-entry-sink **The entry() sink** — an executor may *request* entry materialization (execs SPEC §2.6: every sink is a consumer-implemented callback; the executor owns zero substrate). The service implements it in exec dispatch: `entry(path, content: string | null, {tags, mimetype?})` upserts the entry, then journals ONE typed `EDIT` row in the reserved `plurnk` worker's log — the fs-fiction pattern, `source` = the calling worker, `weight` = the canonical resulting span's curation weight, and `attrs.kind="entry_materialized"`. The requested tags classify that journal row under {§log-item-tags}; they never become resource metadata or duplicate into attrs. Durable replay and clients retain that exact creation event. The model packet projects the typed event as a folded system `READ` of the resulting ordinary resource: its relevant truth is readable state now available in the environment, not an agent-authored mutation. **The executor owns no fetcher:** a `content: null` is a *declaration* — the service acquires the page through schemes-http's checked WebFetcher and accepts its model-facing body and available source/evidence channels {§html-materialization}. Generic public HTML follows the same origin-Markdown, configured Tavily, and local-projection routes as exact HTTP acquisition. A failed acquisition, body-production failure, materialization exception, or absent final projection rejects the sink and produces no HTTP entry, but does not invalidate a search runtime's upstream discovery row; materialization exceptions retain their cause in daemon diagnostics. A non-null `content` is the materialize-given-body path (the caller already holds the bytes and states their mimetype) and grants no provider authority. **No page body ever rides a packet**; the announcement is the folded row's path, weight, and log classifications, and the model READs/~queries what it chooses. Parallel `entry()` calls serialize on a per-spawn chain; a rejected call leaves the chain healthy. The spawn tail settles that complete chain before unregistering, so executor idleness and shutdown are barriers over its materialization writes. The narration context (one plurnk-worker turn) is lazy per spawn, not per entry.
|
|
1722
1734
|
|
|
1723
1735
|
### §proposal The proposal lifecycle
|
|
1724
1736
|
|
|
@@ -1795,7 +1807,7 @@ pretends to reconstruct an opaque plugin connection.
|
|
|
1795
1807
|
|
|
1796
1808
|
### §chunk-accumulation Chunk accumulation
|
|
1797
1809
|
|
|
1798
|
-
§chunk-accumulation-chunks-accumulate SSE event types, WS message types, exec stdout/stderr each map to a named channel. Each stored channel carries `content`, `mimetype`, `
|
|
1810
|
+
§chunk-accumulation-chunks-accumulate SSE event types, WS message types, exec stdout/stderr each map to a named channel. Each stored channel carries `content`, `mimetype`, curation `weight`, and lifecycle `state` ({§channel-state}). The subscription registry owns durable subscription identity and process-local cancellation routing, not a second channel-state representation. Chunks accumulate into the channel as they arrive — not buffered until close.
|
|
1799
1811
|
|
|
1800
1812
|
### §no-chunk-rows No per-chunk log rows
|
|
1801
1813
|
|
|
@@ -1850,6 +1862,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
1850
1862
|
### §sql-ts-boundary SQL/TS responsibility boundary
|
|
1851
1863
|
|
|
1852
1864
|
**Lives in SQL:**
|
|
1865
|
+
|
|
1853
1866
|
- Render queries — log assembly + the manifest catalog.
|
|
1854
1867
|
- Cross-scope path collision (CHECK/trigger → 409).
|
|
1855
1868
|
- Logical model-call identity plus cardinal physical provider-request lifecycle and immutable settlement constraints.
|
|
@@ -1857,6 +1870,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
1857
1870
|
- Entry-vs-log integrity.
|
|
1858
1871
|
|
|
1859
1872
|
**Lives in TS:**
|
|
1873
|
+
|
|
1860
1874
|
- Status-bubble rules (`turn.status` → `loop.status` → `worker.status` → `workspace.status`). Engine UPDATEs explicitly; CHECK constraints enforce; triggers fight branching state machines.
|
|
1861
1875
|
- Tokenization (provider-bound; hot-swap re-tokenizes per {§tokenomics}).
|
|
1862
1876
|
- Provider dispatch, request-accounting validation, and exact-decimal aggregate projection through the shared contracts-owned path.
|
|
@@ -2005,7 +2019,7 @@ Model selection: separate alias cascade in `ProviderRegistry` ({§provider-insta
|
|
|
2005
2019
|
| `PLURNK_SERVICE_EMISSION_ATTEMPTS` | `3` | Completed provider responses allowed beneath one engine turn before an untrustworthy model-turn frame exhausts admission. Bounded interior operation errors are admitted and do not spend this budget. Consecutive exhaustion after the one informed recovery turn terminates independently of strikes. |
|
|
2006
2020
|
| `PLURNK_SERVICE_PREVIEW_LINES` | `16` | Maximum lines in an ordinary bounded log-body projection ({§body-projection}). |
|
|
2007
2021
|
| `PLURNK_SERVICE_PREVIEW_CHARS` | `2560` | Maximum Unicode code points in an ordinary bounded log-body projection, with CRLF treated as one indivisible separator; independently contains single-line bodies ({§body-projection}). |
|
|
2008
|
-
| `PLURNK_SERVICE_PROMPT_PROJECTION` | `25%` | Aggregate
|
|
2022
|
+
| `PLURNK_SERVICE_PROMPT_PROJECTION` | `25%` | Aggregate curation-weight share of the provider-derived input capacity available to automatic prompt-body projection ({§prompt-projection}); alias-scoped overrides are supported. |
|
|
2009
2023
|
| `PLURNK_SERVICE_LINE_ANCHOR_CONTEXT_LINES` | `2` | Complete neighboring lines hashed on each side of a model-facing line anchor ({§line-anchors}). |
|
|
2010
2024
|
| `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES` | `2` | Surrounding and landed lines shown at each EDIT result boundary ({§edit-result-receipt-projection}). |
|
|
2011
2025
|
| `PLURNK_SERVICE_MIN_CYCLES` | `3` | Min repetitions before cycle detection fires ({§engine-rails}). |
|
|
@@ -2019,6 +2033,7 @@ Model selection: separate alias cascade in `ProviderRegistry` ({§provider-insta
|
|
|
2019
2033
|
Every core knob listed is enforced at its owning read site; `.env.defaults` is the authoritative default ({§operator-config-env-defaults}). Provider, scheme, executor, mimetype, and client-interface knobs are documented by their owning packages and appear in the assembled catalog.
|
|
2020
2034
|
|
|
2021
2035
|
**Two override semantics — ceiling vs default.** Which kind a var is determines what "override" means across the cascade:
|
|
2036
|
+
|
|
2022
2037
|
- **Ceiling** (most-restrictive-wins) — an operator-set hard bound nothing downstream may exceed: not a lower-precedence file, not a per-workspace constraint, not a per-call seam argument. `PLURNK_SERVICE_GIT_ALLOWED` ({§operator-config-git-ceiling}), `PLURNK_SERVICE_MAX_COMMANDS`, `PLURNK_SERVICE_MAX_STRIKES`, and `PLURNK_SERVICE_MAX_TURNS` (`-1` ships it off; a positive value caps the per-call request). The sandbox/cost guarantee: the operator caps it; no client widens it.
|
|
2023
2038
|
- **Default** (explicit-wins) — a fallback the most-specific setter replaces freely: `PLURNK_MODEL` (a `runLoop({alias})` request overrides it) and the config-time vars (`HOST` / `PORT` / `DB_PATH`).
|
|
2024
2039
|
|
|
@@ -2055,14 +2070,13 @@ before assembled package defaults fill unset values:
|
|
|
2055
2070
|
| Configuration family | Zero-pin treatment |
|
|
2056
2071
|
|-----------------------------------------------------------|--------------------|
|
|
2057
2072
|
| Any `PLURNK_PROVIDERS_CONTEXT_WINDOW` | Remove |
|
|
2058
|
-
| Alias-specific
|
|
2059
|
-
| Any `PLURNK_SERVICE_PROMPT_BUDGET` | Remove |
|
|
2073
|
+
| Alias-specific output and reasoning budgets | Remove |
|
|
2060
2074
|
| Model selection, routes, and credentials | Retain |
|
|
2061
|
-
| Bare shipped
|
|
2075
|
+
| Bare shipped generation-envelope defaults | Retain |
|
|
2062
2076
|
| Unrelated environment | Retain |
|
|
2063
2077
|
|
|
2064
2078
|
The floor reports every removed key. A gate that succeeds only with those pins
|
|
2065
|
-
is red because provider capacity
|
|
2079
|
+
is red because provider capacity did not derive for
|
|
2066
2080
|
a fresh-user configuration.
|
|
2067
2081
|
|
|
2068
2082
|
§operator-config-max-turns-ceiling Enforcement is per-use-site — no central most-restrictive pass; each ceiling is checked where it bites. `PLURNK_SERVICE_MAX_TURNS` ships **off** (`-1` = no cap; the loop ends via SEND, budget, strikes, or cycle detection) and, when an operator sets a positive value, the per-call request is `min()`-capped against it.
|
|
@@ -2087,10 +2101,12 @@ The composition families remain distinct so one setting's semantics never
|
|
|
2087
2101
|
leak into another.
|
|
2088
2102
|
|
|
2089
2103
|
*Defaults — explicit-wins (the client replaces/merges freely):*
|
|
2104
|
+
|
|
2090
2105
|
- §operator-config-workspace-files-items `settings.filesItems` (number) **replaces** `PLURNK_SERVICE_FILES_ITEMS` for the workspace: a one-shot opens clean (`0`, no preview), with ordinary markerless pages (`-1`), or with the file list explicitly capped (`N`, other surveys remain markerless). A single scalar — the client value wins outright.
|
|
2091
2106
|
- §operator-config-workspace-md-docs `settings.mdDocs` (`[{alias, content}]`) **unions** with the server's `PLURNK_SERVICE_MD_*` docs, keyed by alias — a client adds its own repo docs atop the operator's systemwide policy doc. On alias collision the client wins before I/O (a deliberate shadow), so the unselected operator path is not read; every selected non-empty operator path is required, and absence or another read failure rejects materialization with its cause. The client sends content (it owns the file), not a path.
|
|
2092
2107
|
|
|
2093
2108
|
*Ceilings — most-restrictive-wins (the client may only narrow, never widen):*
|
|
2109
|
+
|
|
2094
2110
|
- §operator-config-workspace-max-commands `settings.maxCommands` (number)
|
|
2095
2111
|
**min()s** the `PLURNK_SERVICE_MAX_COMMANDS` per-emission cap for the
|
|
2096
2112
|
workspace: a client tightens the runaway-op guard and never raises it past
|
|
@@ -2148,6 +2164,22 @@ recovers durable lifecycle, and starts modules in registration order. Shutdown
|
|
|
2148
2164
|
closes started and self-closing modules in reverse order and surfaces aggregated
|
|
2149
2165
|
close failures.
|
|
2150
2166
|
|
|
2167
|
+
§module-shutdown-order `Daemon.stop()` returns only after active worker drains,
|
|
2168
|
+
streaming producers, derivations, mimetypes, schemes, and every accepted
|
|
2169
|
+
conclusion-wake task have settled in dependency order. The supervisor owns each
|
|
2170
|
+
asynchronous wake task from acceptance through settlement; a task failure
|
|
2171
|
+
participates in the shutdown aggregate. The database may be released only after
|
|
2172
|
+
the final wake barrier resolves.
|
|
2173
|
+
|
|
2174
|
+
```mermaid
|
|
2175
|
+
flowchart LR
|
|
2176
|
+
stop[Stop accepting work] --> drains[Settle worker drains]
|
|
2177
|
+
drains --> producers[Settle streaming producers]
|
|
2178
|
+
producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
|
|
2179
|
+
resources --> wakes[Settle conclusion wakes]
|
|
2180
|
+
wakes --> database[Release database]
|
|
2181
|
+
```
|
|
2182
|
+
|
|
2151
2183
|
| Setup function | Contract |
|
|
2152
2184
|
|------------------------------------------------------------------------|----------|
|
|
2153
2185
|
| `registerRuntime({ decl, executor, availability, scheme? })` | Admits one canonical tag under {§executor-runtime-declaration}, then adds its executor and optional claimed scheme facet atomically. |
|
|
@@ -2171,7 +2203,7 @@ Its function names are transport-neutral library calls, not public wire names.
|
|
|
2171
2203
|
| Client observation | `look({ workspaceId, workerId, statement })` | Runs an already-parsed READ through the full resolver without a log row. A non-READ statement is rejected ({§op-look}). |
|
|
2172
2204
|
| §methods-log-read Reads | `readLog({ workspaceId, workerId, ...coordinate })` | Ownership-checks the worker, then reads by ids, recency, or the complete `loopSeq`/`turnSeq`/`sequence` display coordinate. `limit` defaults to 100 and is capped at 1000. |
|
|
2173
2205
|
| §methods-entry-read Reads | `readEntry({ workspaceId, workerId, target, channel?, offset? })` | Resolves the selector from that worker's perspective and returns {§entry-read-result}, either complete or as one channel suffix, without creating action evidence. |
|
|
2174
|
-
| Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective `
|
|
2206
|
+
| Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective provider-derived `inputCapacity` when known. |
|
|
2175
2207
|
| Client capabilities | `listClientDisplayCapabilities()` | Composes sorted scheme declarations ({§manifest-client-display}) followed by sorted MIME declarations ({§mimetype-client-display}) into the validated shared wire ({§client-display-capabilities}). The internal `exec` operation handler is excluded; its addressable runtime-tag scheme faces remain included. |
|
|
2176
2208
|
| §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings?, constraints? })` | Validates `settings` through {§operator-config-workspace-settings}, creates the world and its client envelope, materializes current docs and constraints, starts derivation warming, and emits global `workspace/created`. `projectRoot` is established here or the workspace remains headless. |
|
|
2177
2209
|
| §methods-workspace-attach Workspace lifecycle | `attachWorkspace({ workspaceId, workerId?, workerName? })` | Validates ownership and returns a client envelope for an existing world. It does not retain caller or transport binding state in core. |
|
|
@@ -2271,7 +2303,7 @@ active lifecycle behind. LOOK text anchors resolve through the same
|
|
|
2271
2303
|
| Event | Payload | When fired |
|
|
2272
2304
|
|--------------------------------------------------------------|---------|------------|
|
|
2273
2305
|
| §notifications-log-entry-notify `log/entry` | `{ entry: LogEntry }` | A `log_entries` row is committed. |
|
|
2274
|
-
| §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage: { accounting, contextTokens,
|
|
2306
|
+
| §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage: { accounting, curationWeight, curationBudget, contextTokens, contextCapacity, meta }, attributions }` | One loop reaches a terminal state. `result` is the exact universal operation result, including its RFC 9457 Problem Details on failure. `accounting` is the loop's contracts-owned {§provider-accounting}; the two curation facts and two physical-context facts follow {§tokenomics-client-gauge}; `meta` is that turn's opaque provider bag. `attributions` is the sorted union of exact provider-request evidence ({§attribution}), separate from accounting. Worker and loop are an inseparable owning coordinate. |
|
|
2275
2307
|
| §notifications-loop-proposal `loop/proposal` | contracts-owned `ProposalProjection` | Dispatch pauses on a durable 202 proposal. `disposition` is the sole authority for whether a client presents review UI; live and reconnect share {§proposal-projection}. |
|
|
2276
2308
|
| §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
|
|
2277
2309
|
| §notifications-workspace-branch-batch `workspace/branch-batch` | Branch-batch lifecycle payload | A branch batch enters queued, running, completed, failed, or recovery-required state. |
|
|
@@ -2384,63 +2416,40 @@ trust gate; an external client action cannot invoke it. Whole-list transformatio
|
|
|
2384
2416
|
the fork-avoidance valve for alternate packet shapes ({§ecosystem}), while
|
|
2385
2417
|
grinding and folding remain closed engine concerns.
|
|
2386
2418
|
|
|
2387
|
-
### §tokenomics Tokenomics: four
|
|
2419
|
+
### §tokenomics Tokenomics: four facts, one curation ruler
|
|
2388
2420
|
|
|
2389
2421
|
Token accounting distinguishes the artifact being measured, the unit, and the
|
|
2390
2422
|
time of measurement.
|
|
2391
2423
|
|
|
2392
|
-
|
|
|
2393
|
-
|
|
2394
|
-
|
|
|
2395
|
-
|
|
|
2396
|
-
|
|
|
2397
|
-
|
|
|
2398
|
-
|
|
2399
|
-
- §tokenomics-
|
|
2400
|
-
- §tokenomics-render-weight-budget **
|
|
2401
|
-
- §tokenomics-context-percent **
|
|
2402
|
-
- §tokenomics-window-partition **
|
|
2403
|
-
separate.** The provider owns the effective total context envelope and
|
|
2404
|
-
generation settings. The envelope is already the minimum of model capacity
|
|
2405
|
-
and any stricter operator context cap ({§model-fact-resolution}); that cap is
|
|
2406
|
-
final. Core derives the natural prompt gauge from the envelope and its packing-safety
|
|
2407
|
-
margin. `PLURNK_SERVICE_PROMPT_BUDGET` is an optional alias-scoped virtual
|
|
2408
|
-
ceiling:
|
|
2409
|
-
`promptBudget = min(configuredPromptBudget, naturalPromptCapacity)`. Unset
|
|
2410
|
-
uses the natural capacity. The virtual ceiling controls only the packet gauge
|
|
2411
|
-
and grinder; it never changes the hard context envelope, reasoning, completion, or
|
|
2412
|
-
`maxTokens`.
|
|
2424
|
+
| Fact | Owner and unit | Time | Contract |
|
|
2425
|
+
|:-----|:---------------|:-----|:---------|
|
|
2426
|
+
| Core curation weight | `contentWeight = ceil(chars/2)` over channel content, canonical log bodies, and rendered packet slots | Write/build | Stable, model-independent pressure and OPEN/FOLD savings; never a tokenizer claim. |
|
|
2427
|
+
| §tokenomics-context-envelope-admission Provider input capacity | Provider model limits and configured total output envelope, in provider tokens | Before every logical request | `min(maxInputTokens, contextWindow - outputBudget)` over the known terms. The provider alone measures the complete request and admits, defers, or rejects it. |
|
|
2428
|
+
| Provider generation envelope | Provider total output budget and optional reasoning subset, in provider tokens | Before every logical request | One total output budget includes hidden reasoning. A reasoning budget is a strict subset, never an additive reserve. |
|
|
2429
|
+
| Provider usage and cost | Provider-reported input/output/cache/reasoning tokens and monetary evidence | After every physical request | Durable physical-request forensics under {§provider-usage}; never curation state or a preflight estimate. |
|
|
2430
|
+
|
|
2431
|
+
- §tokenomics-weight-stored-at-write **Curation weight, stored at write.** `entry_channels.weight` weighs the complete channel content. `log_entries.weight` weighs the complete canonical `LogBody` content before coordinate and packet presentation; persistence `tx`/`rx` envelopes contribute nothing merely by existing, and proposal settlement recomputes the value when the canonical result changes. Bodyless rows therefore weigh zero. The stored number is a stable content-depth measurement, not a provider-token prediction.
|
|
2432
|
+
- §tokenomics-render-weight-budget **Packet curation budget.** The Curation headline measures the *assembled packet* after section transforms and readout substitution. Core measures minimum-width probes, monotonically expands fields that do not fit, then right-aligns final values into those widths; final substitution is length-invariant and displayed weight equals the stored request weight. A `SUM` of stored content weights measures a different artifact and cannot substitute for packet render weight.
|
|
2433
|
+
- §tokenomics-context-percent **Curation percent.** The headline carries packet weight as a percentage of its curation budget beside the absolutes. It reads the capacity already resolved by the provider; no extra provider call.
|
|
2434
|
+
- §tokenomics-window-partition **One capacity derivation; no service-side token budget.** The provider owns model limits and the configured total output envelope. Its resolved `inputCapacity` is the numeric curation-budget calibration as well as the physical denominator exposed to clients. That reuse is policy, not a unit conversion: Core compares stable curation weight with it only to shape context, while provider request-shaped evidence alone admits or rejects I/O. `PLURNK_SERVICE_PROMPT_BUDGET`, `PLURNK_SERVICE_SAFETY`, and the additive reasoning/completion reserve knobs are retired; local and custom deployments tune context window, total output budget, optional reasoning subset, and prompt-projection percentage at their owning layers.
|
|
2413
2435
|
- §tokenomics-prompt-projection-share **Prompt projection is stable packet policy.**
|
|
2414
2436
|
`PLURNK_SERVICE_PROMPT_PROJECTION` is a required alias-scoped percentage in
|
|
2415
|
-
`(0, 100)`. It allocates that share of the
|
|
2437
|
+
`(0, 100)`. It allocates that share of the provider-derived curation budget to the
|
|
2416
2438
|
aggregate automatic prompt-body projection. It does not bound stored prompt
|
|
2417
2439
|
size, provider capacity, an explicit READ/FIND result, or the complete packet.
|
|
2418
2440
|
Basing the share on the stable ceiling rather than current free weight keeps
|
|
2419
2441
|
one prompt's projection byte-stable as the worker log evolves.
|
|
2420
|
-
- §tokenomics-window-unpollable-deliberate **Unknown provider capacity stays
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
configured virtual ceiling still supplies PLURNK's curation gauge without
|
|
2424
|
-
pretending to describe hard capacity. A negative-gauge request cannot be
|
|
2425
|
-
admitted without a known effective context and generation envelope.
|
|
2426
|
-
|
|
2427
|
-
§tokenomics-client-gauge **The client gauge pairs current occupancy with its
|
|
2428
|
-
effective ceiling.** A model switch changes both latest-turn values together;
|
|
2429
|
-
the loop's aggregate provider accounting remains monetary evidence, not a gauge
|
|
2430
|
-
input.
|
|
2431
|
-
|
|
2432
|
-
| Surface | `contextTokens` | `promptBudget` |
|
|
2433
|
-
| -------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
2434
|
-
| `loop/terminated.usage` | `inputTokens` from the latest settled physical request on the latest turn; `null` when absent or unknown | Latest turn's enforced packet allowance; `null` when uncapped or unknown |
|
|
2435
|
-
| `providers.list` alias row | not present | Active alias's current enforced packet allowance; `null` when not established |
|
|
2442
|
+
- §tokenomics-window-unpollable-deliberate **Unknown provider capacity stays unknown.** When the provider cannot derive `inputCapacity`, Core omits denominator-dependent curation telemetry and uses the ordinary bounded prompt projection. The provider still sends requests whose measurement or limits are estimates or unavailable: ambiguity defers to the upstream capacity oracle rather than becoming a local rejection.
|
|
2443
|
+
|
|
2444
|
+
§tokenomics-client-gauge **Clients receive curation and physical occupancy as separate pairs.** `loop/terminated.usage` carries latest-turn `curationWeight`/`curationBudget` and latest-emission-call `contextTokens`/`contextCapacity`; each unknown fact is `null`. Both physical facts bind to that same call: a preflight rejection may report capacity while its absent physical request leaves `contextTokens=null`, never borrowed from an earlier call. Clients never divide provider-reported physical tokens by Core curation weight. `providers.list` exposes each instantiated alias's `inputCapacity`. A model switch replaces the latest-turn facts together; aggregate provider accounting remains cardinal monetary evidence, not a gauge input.
|
|
2436
2445
|
|
|
2437
2446
|
- **Derivation is eager and exhaustive.** Workspace creation and searchable-resource changes start one coalesced warm. The first model turn joins that warm; later turns derive intervening changes before dispatch. No model operation observes partial graph or vector coverage. A semantic query ranks every eligible candidate in scope, so lexical overlap never gates vector recall. With no embedder, readable-content FTS is the explicit keyword fallback. Progress notices make the wait visible; latency is never hidden by partial semantics. {§derivation-exhaustive}
|
|
2438
2447
|
- §membership-binary-sniff **Binary truth beats the label; no entry dominates the corpus.** A tracked member whose HEAD bytes contain NUL enters {§membership-source-projection} as `application/octet-stream` **regardless of what extension-based detection claims**; byte-level evidence outranks a default label. Every eligible text is tiled losslessly to the embedder window and every tile is embedded before its derivation attaches; semantic ranking max-pools the best chunk per candidate.
|
|
2439
|
-
- §tokenomics-agnostic-ruler **One model-agnostic ruler.** The daemon runs workers on different models in one workspace concurrently, while catalog and log accounting are workspace-wide.
|
|
2440
|
-
- §tokenomics-neutral-telemetry **
|
|
2441
|
-
- §tokenomics-content-hash-identity **Content identity, not per-tokenizer counts.** Static channel writes stamp `content_hash` (SHA-256) as
|
|
2448
|
+
- §tokenomics-agnostic-ruler **One model-agnostic curation ruler.** The daemon runs workers on different models in one workspace concurrently, while catalog and log accounting are workspace-wide. `contentWeight = ceil(chars/2)` therefore gives one content one stable number without per-model workspace state or recount passes. It controls curation only; every provider call independently measures the complete request as well as it can.
|
|
2449
|
+
- §tokenomics-neutral-telemetry **Curation telemetry is state, with one pressure alarm.** The model-facing Curation section ordinarily has one line: budget, current packet weight and percentage, and free weight. When free weight is negative, exactly one second line directs the model to FOLD or KILL less-relevant log items. Per-entry weights remain on log rows where they describe OPEN cost and FOLD savings. Packet-level composition, rankings, and physical token speculation are absent.
|
|
2450
|
+
- §tokenomics-content-hash-identity **Content identity, not per-tokenizer counts.** Static channel writes stamp `content_hash` (SHA-256) as stable content identity. `weight` is stored beside that content and is never keyed or recomputed by model.
|
|
2442
2451
|
- §tokenomics-provider-usage **Provider accounting is physical-request evidence, not curation state.** Every issued physical request has one durable pre-I/O `provider_requests` identity and settles once as response or error. Each record preserves conventional {§provider-usage} quantities and required {§provider-cost} evidence; an unreported quantity remains absent, including on response-less failures, and is never replaced by zero. `model_calls` own logical response/failure evidence, `turn_attempts` specialize emission admission, and `provider_requests` are the sole durable accounting representation. Emissions, BARE calls, rejected responses, retries, failovers, and errors therefore remain cardinal and ordered. Turn, loop, worker, workspace, digest, and protocol accounting are derived from those records through the shared {§provider-accounting} projection; only emission calls contribute the latest-packet context gauge. The baseline stores no floating-point money, denormalized totals, or rollup triggers. A documented direct charge becomes `charged`; otherwise the provider may compute an exact-decimal USD `estimated` amount from complete usage and the exact model's Models.dev rates; insufficient evidence becomes `unknown`. Derived `costUsd` is an exact decimal string only when every request is expressible in USD and is `null` otherwise. This is operational request accounting, not invoice reconciliation. Output and reasoning are quantities the model cannot FOLD, so they never alter the model-facing Budget ledger.
|
|
2443
|
-
- §tokenomics-negative-pressure **Negative
|
|
2452
|
+
- §tokenomics-negative-pressure **Negative curation pressure is honest and nonterminal.** The readout describes the packet the model actually receives: weight and percent may exceed the curation budget, and free weight equals `budget - weight` without flooring. Crossing the budget records {§grinder-overflow-problem}, then the grinder ({§grinder}) folds only the newest boundary. Remaining curation debt never decides physical admission, creates a strike, or establishes a one-turn quota.
|
|
2444
2453
|
|
|
2445
2454
|
### §membership Workspace identity, membership, disk co-location
|
|
2446
2455
|
|
|
@@ -2517,6 +2526,7 @@ materialization verdicts and folded ambient rows for every acquired page.
|
|
|
2517
2526
|
- §membership-create-parents **Parent-complete creation.** An accepted File creation—whether authored as EDIT or as a COPY/MOVE destination—recursively creates missing parent directories before writing and registering the new member.
|
|
2518
2527
|
|
|
2519
2528
|
**The overlay — `pick | view | hide`, removed by `drop`.** A `workspace_constraints` table is the client's supersede over Git. Resolved membership is `(project repository files ∪ pick) − hide`, with `view` enforced at the edit gate.
|
|
2529
|
+
|
|
2520
2530
|
- §membership-auto-add **Auto-add** — the project repository's membership is its tracked `ls-files` plus untracked-but-not-ignored files (`git ls-files --others --exclude-standard`), with `git` origin. A model-created file is a member the moment it exists—no `git add`—while `.gitignore` still filters it.
|
|
2521
2531
|
- §membership-overlay-pick **`pick`** — admit an untracked file git misses: a targeted client-dictated `node:fs` glob scan over untracked matches (files only), 'constraint' origin, reconciled like git members. Enumerated, so the manifest stays exhaustive. git-absent, `pick` is the *sole* membership source.
|
|
2522
2532
|
- §membership-overlay-hide **`hide`** — exclude a tracked file: resolution drops matches (`node:path.matchesGlob`) and reconciles so the entry set *equals* the member set. The lever to exclude a committed-but-sensitive tracked file; `entries.membership_origin` keeps reconciliation off model-created members.
|
|
@@ -2584,46 +2594,53 @@ explicit.
|
|
|
2584
2594
|
|
|
2585
2595
|
### §grinder Budget enforcement: the grinder
|
|
2586
2596
|
|
|
2587
|
-
The grinder is the one pre-provider curation path for the model-facing
|
|
2588
|
-
|
|
2589
|
-
|
|
2597
|
+
The grinder is the one pre-provider curation path for the model-facing gauge.
|
|
2598
|
+
It never decides physical admission. Every request then crosses the provider's
|
|
2599
|
+
request-shaped capacity boundary, where proven exact overflow may reject
|
|
2600
|
+
pre-I/O and ambiguous evidence defers to upstream:
|
|
2590
2601
|
|
|
2591
2602
|
```mermaid
|
|
2592
2603
|
flowchart TD
|
|
2593
|
-
assemble["Assemble and measure<br/>request packet"] --> policy{"
|
|
2594
|
-
policy -->|yes|
|
|
2604
|
+
assemble["Assemble and measure<br/>request packet"] --> policy{"Packet weight ≤<br/>curation budget?"}
|
|
2605
|
+
policy -->|yes| generate["Provider generate"]
|
|
2595
2606
|
policy -->|no| problem["Record nonterminal 413<br/>Token Budget Overflow"]
|
|
2596
2607
|
problem --> fold["FOLD newest boundary<br/>and its OPEN effects<br/>tag overflow"]
|
|
2597
2608
|
fold --> rebuild["Rebuild and remeasure"]
|
|
2598
|
-
rebuild -->
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2609
|
+
rebuild --> generate
|
|
2610
|
+
generate --> capacity{"Provider capacity<br/>failure?"}
|
|
2611
|
+
capacity -->|no| response["Classify completed response"]
|
|
2612
|
+
capacity -->|yes| prompt{"Withholding automatic<br/>prompt bodies changes request?"}
|
|
2613
|
+
prompt -->|yes| retryPrompt["Persist failure;<br/>rebuild and retry"]
|
|
2614
|
+
retryPrompt --> generate
|
|
2615
|
+
prompt -->|no| boundary{"Newest boundary not yet<br/>rolled back, and rollback<br/>changes request?"}
|
|
2616
|
+
boundary -->|yes| retryBoundary["Persist failure;<br/>roll back, rebuild, retry"]
|
|
2617
|
+
retryBoundary --> generate
|
|
2618
|
+
boundary -->|no| stop["Persist terminal 413"]
|
|
2603
2619
|
```
|
|
2604
2620
|
|
|
2605
2621
|
§grinder-overflow-only **The grinder fires only on actual overflow.** In
|
|
2606
2622
|
`Engine.runTurn`, after `PacketBuilder.buildRequestPacket` assembles the request
|
|
2607
2623
|
and before `provider.generate`, it compares the packet's render-weight
|
|
2608
|
-
({§tokenomics}) with the
|
|
2624
|
+
({§tokenomics}) with the provider-derived curation budget. At or under the budget, the packet
|
|
2609
2625
|
ships untouched. The grinder never trims
|
|
2610
2626
|
speculatively or "helpfully."
|
|
2611
2627
|
|
|
2612
2628
|
- §grinder-overflow-problem **Token Budget Overflow is a nonterminal 413 Problem.** Every over-ceiling assembly records exactly one `engine/context/token-budget-overflow` Problem before automatic recovery, with pre-recovery `usage`, `ceiling`, and `deficit` evidence. Its exact `detail` is `Token Budget Overflow: Token Usage exceeded Token Ceiling. Newest log items were automatically FOLDed to fit within token budget. Curate the log and/or perform more conservatively scoped or chunked retrieval operations to recover.` The Problem remains durable and model-visible even when folding restores room and the turn later concludes successfully; it does not strike or replace the turn's terminal disposition.
|
|
2613
2629
|
- §grinder-layer1-rollback **One rule, every turn: roll back context introduced by the newest boundary.** On overflow the grinder folds, in one set operation, still-open rows born in the immediately prior turn or current pre-model turn plus exact older rows that a successful OPEN in the immediately prior turn transitioned from folded to open. It reads those landed effects from {§fold-open-meta-operations}; it never re-runs the selector, treats an already-open target as newly introduced, or chooses other older history by relevance. Turn 1 has no prior turn, so only its pre-model rows qualify. The same set operation additively applies the `overflow` tag to every row it folds; rows and bodies remain re-OPENable.
|
|
2614
2630
|
- §grinder-errors-exempt **Errors, the prompt, AND the plan are exempt.** The grinder never folds an `op='error'` row: errors are the model's durable, curatable record of what went wrong. Nor does it fold the actionless **user prompt** row (`prompt:///<loop>/<N>`): the task frame is not ordinary model-authored memory. Nor a **PLAN row**: the checklist is the model's orientation surface when the grinder fires. All three stay OPEN until the model itself FOLDs or KILLs them.
|
|
2615
|
-
- §grinder-hard-413 **
|
|
2631
|
+
- §grinder-hard-413 **Only a provider capacity failure can terminalize at 413.** Every `provider.generate` assesses its exact request under {§provider-surface-capacity}. A proven exact preflight overflow issues no physical request; estimate, unavailable evidence, and a non-fitting upper bound defer to upstream, whose context rejection normalizes to the same `capacity_exceeded` failure. Core retries only after changing the request: first by withholding automatic prompt-body projection when that changes the packet, then by rolling back the newest boundary once when the grinder has not already done so and that changes the packet. Each failed logical call, any issued physical request, capacity evidence, and model-visible provider Problem remain durable; these recovery calls do not consume the completed-emission attempt budget. Core never resends identical bytes as capacity recovery and never chooses arbitrary older history. If neither recovery changes the request, or the changed request is still rejected, the request-only turn and exact provider Problem terminalize the loop at **413 Content Too Large**.
|
|
2616
2632
|
|
|
2617
|
-
- §tokenomics-fetch-fits-free **A retrieval larger than the available packet room arrives folded with a 413 explanation.** The result lands in the next build; if that build exceeds the
|
|
2633
|
+
- §tokenomics-fetch-fits-free **A retrieval larger than the available packet room arrives folded with a 413 explanation.** The result lands in the next build; if that build exceeds the curation budget, Core records {§grinder-overflow-problem} and folds the newest boundary, which contains the result. Its row and exact body remain durable and re-OPENable, and its `overflow` tag identifies every row selected by the automatic fold. Remaining curation debt follows {§tokenomics-negative-pressure}.
|
|
2618
2634
|
|
|
2619
|
-
- §loop-terminals **Engine-imposed terminals are HTTP-precise** — the loop-status vocabulary, one meaning each: `200` concluded (the model's SEND signal `200`) · `499` model-abandoned (signal `499`, or a cancel) · `429` maxTurns exhausted · `413`
|
|
2635
|
+
- §loop-terminals **Engine-imposed terminals are HTTP-precise** — the loop-status vocabulary, one meaning each: `200` concluded (the model's SEND signal `200`) · `499` model-abandoned (signal `499`, or a cancel) · `429` maxTurns exhausted · `413` provider input-capacity failure after changed-request recovery · `500` strike threshold or invalid-emission exhaustion (distinct Problem types; `508` when the crossing strike was a detected cycle) · `504` loop timeout / exec-timeout restamp · `202` the bounded wait — a loop blocked on a live obligation (the model's `## SEND0 [202] <T,P>`, {§wait-obligation-matrix}); a wait on nothing resolves to `200` unless a successful same-turn FOLD requires the curated next packet · `100`/`102` queued/running. Never a catch-all, never a new value without changing the owning schema.
|
|
2620
2636
|
|
|
2621
|
-
§grinder-pressure-surface **What the model sees.** A fold-to-fit packet carries the open {§grinder-overflow-problem} row, its terse `## Errors` pointer, and ordinary folded rows whose complete sorted `tags` include `overflow`. If
|
|
2637
|
+
§grinder-pressure-surface **What the model sees.** A fold-to-fit packet carries the open {§grinder-overflow-problem} row, its terse `## Errors` pointer, and ordinary folded rows whose complete sorted `tags` include `overflow`. If curation pressure remains negative but the provider accepts the request, the Budget section also reports the negative free-weight value and its one panic line ({§tokenomics-neutral-telemetry}). The curation 413 diagnoses the overflow without striking, terminalizing the turn, or changing the ordinary operation contract.
|
|
2622
2638
|
|
|
2623
|
-
The model controls its context; the
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
speculative or non-overflow
|
|
2639
|
+
The model controls its context; the provider owns physical admission, and Core
|
|
2640
|
+
performs only bounded changed-request recovery without choosing what older
|
|
2641
|
+
history matters. The same boundary applies on turn 1 and turn 101. The grinder
|
|
2642
|
+
folds reversibly, never deletes, and never performs speculative or non-overflow
|
|
2643
|
+
trimming.
|
|
2627
2644
|
|
|
2628
2645
|
### §env-delta The environment delta: what changed since the model last looked
|
|
2629
2646
|
|
|
@@ -2833,14 +2850,14 @@ leaves the request-only record, while rejected exchanges remain in
|
|
|
2833
2850
|
| Turn state | `turns.packet` |
|
|
2834
2851
|
| ----------------------------- | ----------------------------------------------- |
|
|
2835
2852
|
| No model request assembled | SQL `NULL` |
|
|
2836
|
-
| Request assembled | `{
|
|
2837
|
-
| Response admitted | `{
|
|
2853
|
+
| Request assembled | `{ weight, sections }` |
|
|
2854
|
+
| Response admitted | `{ weight, sections, assistant, assistantRaw }` |
|
|
2838
2855
|
|
|
2839
2856
|
| Field | Presence | Contract |
|
|
2840
2857
|
| ----------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2841
|
-
| `
|
|
2858
|
+
| `weight` | Every assembled model request | Curation weight of both rendered request slots. Admission does not change its meaning; it is never response weight or provider usage. |
|
|
2842
2859
|
| `sections` | Every assembled model request | Ordered post-transform request sections. `PacketWire.renderSlot` groups them into system and user messages, and the digest re-renders those stored sections byte-for-byte. |
|
|
2843
|
-
| `sections[].
|
|
2860
|
+
| `sections[].weight` | Every stored section | Independently measured curation weight of that section. Their sum is not the rendered request weight because slot separators and independent rounding remain outside each row. |
|
|
2844
2861
|
| `assistant.content` | Admitted response only | Accepted model content from which operations were parsed. |
|
|
2845
2862
|
| `assistant.ops` | Admitted response only | Parsed operations admitted from that content. |
|
|
2846
2863
|
| `assistant.reasoning` | Admitted response only | Normalized readable reasoning text, or `null`. |
|
|
@@ -2855,7 +2872,7 @@ or request-only note instead of fabricating response files.
|
|
|
2855
2872
|
|
|
2856
2873
|
The external tokenless draft and transformation boundary is owned by
|
|
2857
2874
|
{§scheme-packet-transform}. Core alone extends each validated draft with its
|
|
2858
|
-
measured `
|
|
2875
|
+
measured `weight` field for storage. #74 tracks coverage that mistakes the sum
|
|
2859
2876
|
of section weights for the rendered request weight.
|
|
2860
2877
|
|
|
2861
2878
|
§definition-table-projection The authored `plurnk.md` remains human-aligned. Its `definition` section deterministically removes Markdown table-cell padding and shortens separator cells to three dashes before plugin transforms, measurement, storage, and wire rendering; alignment colons survive, while fenced blocks and all non-table whitespace remain exact.
|
|
@@ -2868,17 +2885,13 @@ of section weights for the rendered request weight.
|
|
|
2868
2885
|
| Core lifecycle | Use the exact Workspace → Worker → Loop → Turn → Op hierarchy in {§lifecycle-terms}. An AG-UI Run or thread is always protocol-qualified. |
|
|
2869
2886
|
| Model-facing packet | Use the model's training distribution: operations mirror HTTP and shell, `display` mirrors CSS, and jsonplurnk remains JSON. Renaming this vocabulary to internal API terminology would discard useful resonance for a standard the model never sees. |
|
|
2870
2887
|
|
|
2871
|
-
`promptBudget` is deliberately distinct from `contextWindow`: it is PLURNK's
|
|
2872
|
-
enforced packet allowance, bounded by provider capacity and optionally tightened
|
|
2873
|
-
by service policy.
|
|
2874
|
-
|
|
2875
2888
|
| PLURNK-native term | Why it remains |
|
|
2876
2889
|
|--------------------------------|----------------|
|
|
2877
2890
|
| `worker` / `loop` / `turn` | The process hierarchy in {§lifecycle-terms}; unqualified `run` names no internal entity. |
|
|
2878
2891
|
| `packet` | The assembled address space, a kernel concept rather than merely a provider request. |
|
|
2879
2892
|
| `costUsd` | No standard cost field exists; the explicit currency avoids implied units, and the value remains an exact decimal string. |
|
|
2880
|
-
| `
|
|
2881
|
-
| the `chars/2` ruler
|
|
2893
|
+
| `curationWeight` / `curationBudget` | Explicitly distinguish Core's model-independent context-shaping facts from physical provider tokens. |
|
|
2894
|
+
| the `chars/2` curation ruler | Model-agnostic by design ({§tokenomics-agnostic-ruler}); it is never presented as a tokenizer. |
|
|
2882
2895
|
|
|
2883
2896
|
Retired terms stay retired: the lexicon guard rejects `thinking`, the unqualified `session` noun, `contextSize`, `decodeBudget`, and moved partition-knob names. <!-- lexicon-allow: this sentence enumerates the retired terms -->
|
|
2884
2897
|
|
|
@@ -2908,7 +2921,7 @@ READ and FIND own their range or pagination before packet rendering; the packet
|
|
|
2908
2921
|
|
|
2909
2922
|
§prompt-entry **Prompt as a first-class entry and log row.** Each prompt is stored once at `prompt:///<loop>/<N>` as an owner-keyed text/markdown entry, then published to its turn as one actionless lowercase `prompt` log row. No synthetic EDIT or READ operation is invented. The row is born OPEN and obeys {§body-projection}. The **Active User Prompts** section closes the user-slot status clump as a paths-only list (`* prompt:///<loop>/<N>`), so every frame remains directly READable even after its log row is folded or killed.
|
|
2910
2923
|
|
|
2911
|
-
§prompt-projection **Prompt storage is unbounded by model context; automatic materialization is not.** Core persists every accepted prompt completely before packet assembly. The selected provider's
|
|
2924
|
+
§prompt-projection **Prompt storage is unbounded by model context; automatic materialization is not.** Core persists every accepted prompt completely before packet assembly. The selected provider's derived `inputCapacity` and the alias-resolved percentage from `PLURNK_SERVICE_PROMPT_PROJECTION` derive one aggregate curation-weight allowance for OPEN prompt bodies. Complete prompt bodies render when their aggregate weight fits. Otherwise all OPEN prompt rows share the allowance: full bodies consume only their required share, unused shares are redistributed, and partial bodies render the largest leading complete-line region that fits their share or an exact character-bound prefix when the first physical line alone is larger. The sum of their rendered body weights never exceeds the allowance. Every partial body carries its exact `chunk` after `body`; the canonical `prompt:///` entry and `log:///` body remain complete and READ/FIND-addressable. When provider input capacity is unknown the percentage is underivable, so prompt rows retain the ordinary bounded projection rather than inventing capacity. This policy never rejects, summarizes, or discards a prompt because it exceeds a context window.
|
|
2912
2925
|
|
|
2913
2926
|
§prompt-self-only The frame is self-only and owner-keyed:
|
|
2914
2927
|
`entries.owner_id` carries worker identity while the address carries only the
|
|
@@ -2971,7 +2984,7 @@ retain distinct contracts and lifetimes.
|
|
|
2971
2984
|
| failure | row | status |
|
|
2972
2985
|
|---|---|---|
|
|
2973
2986
|
| action failure | the failed op's own row; the owning scheme supplies Problem Details | 4xx/5xx |
|
|
2974
|
-
|
|
|
2987
|
+
| provider input capacity | `op='error'`, origin `plurnk`, source `provider`; exact provider-owned `capacity-exceeded` Problem Details | 413 |
|
|
2975
2988
|
| max commands exceeded | `op='error'`, origin `plurnk`, source `rail`; `engine/rail/max-commands-exceeded` Problem Details | 429 |
|
|
2976
2989
|
| idle turn | `op='error'`, origin `plurnk`, source `rail`; `engine/rail/idle-turn` Problem Details | 409 |
|
|
2977
2990
|
|
|
@@ -3117,7 +3130,6 @@ container identity.
|
|
|
3117
3130
|
§matcher-dispatch-203-soft-fallback On parse failure, 203 returns raw content as the text primitive with `reason`
|
|
3118
3131
|
so the model can use ordinary text retrieval or repair the source.
|
|
3119
3132
|
|
|
3120
|
-
|
|
3121
3133
|
`Matcher.matchCandidates` searches heterogeneous resource sets. A candidate
|
|
3122
3134
|
whose handler returns 415 is omitted when another candidate supports the
|
|
3123
3135
|
dialect; if every candidate is unsupported, the first exact 415 Problem is the
|
|
@@ -3192,7 +3204,6 @@ scope shape. A follow-up `## READ0 (resource) <SL,SC,EL,EC>` retrieves that exac
|
|
|
3192
3204
|
region. JSONPath/XPath remain locators and matchers; they do not introduce a
|
|
3193
3205
|
second structural scope or structural EDIT language.
|
|
3194
3206
|
|
|
3195
|
-
|
|
3196
3207
|
### §ext-mimetype Path-extension declares mimetype
|
|
3197
3208
|
|
|
3198
3209
|
`resolveEntryMimetype` (exported from `@plurnk/plurnk-schemes`): pathname extension → `Mimetypes.detect({ ext })` (with `text/plain` normalized to `text/markdown` per the text-primitive rule {§markdown-primitive}); falls back to scheme manifest channel default when no extension.
|
|
@@ -3206,7 +3217,6 @@ second structural scope or structural EDIT language.
|
|
|
3206
3217
|
mimetype is stored in `entry_channels.mimetype` on write and drives matcher,
|
|
3207
3218
|
projection, and binary handling. Text scope meaning does not vary by mimetype.
|
|
3208
3219
|
|
|
3209
|
-
|
|
3210
3220
|
### §render-rule Render rule
|
|
3211
3221
|
|
|
3212
3222
|
§render-rule-line-navigable-prefix Every textual content body with a source
|