@plurnk/plurnk-service 1.24.0 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.defaults +17 -15
- package/INSTALL.md +96 -20
- package/README.md +2 -2
- package/SPEC.md +517 -193
- package/dist/Paths.d.ts +1 -0
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +1 -0
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/content/read-projector.d.ts.map +1 -1
- package/dist/content/read-projector.js +19 -0
- package/dist/content/read-projector.js.map +1 -1
- package/dist/core/AdministrativeLoop.d.ts +1 -1
- package/dist/core/AdministrativeLoop.d.ts.map +1 -1
- package/dist/core/AdministrativeLoop.js +5 -5
- package/dist/core/AdministrativeLoop.js.map +1 -1
- package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
- package/dist/core/AdmittedTurnExecutor.js +1 -0
- package/dist/core/AdmittedTurnExecutor.js.map +1 -1
- package/dist/core/CapabilityResolver.js +1 -1
- package/dist/core/CapabilityResolver.js.map +1 -1
- package/dist/core/DataStatementRunner.d.ts.map +1 -1
- package/dist/core/DataStatementRunner.js +1 -3
- package/dist/core/DataStatementRunner.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +1 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +76 -21
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Dispatcher.sql +0 -7
- package/dist/core/Engine.d.ts +2 -2
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.d.ts +16 -4
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +31 -31
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/HostPaths.d.ts +7 -1
- package/dist/core/HostPaths.d.ts.map +1 -1
- package/dist/core/HostPaths.js +25 -12
- package/dist/core/HostPaths.js.map +1 -1
- package/dist/core/LoopDriver.d.ts.map +1 -1
- package/dist/core/LoopDriver.js +11 -2
- package/dist/core/LoopDriver.js.map +1 -1
- package/dist/core/LoopLifecycle.d.ts +1 -1
- package/dist/core/LoopLifecycle.js +1 -1
- package/dist/core/LoopLifecycle.sql +2 -2
- package/dist/core/LoopPolicies.d.ts.map +1 -1
- package/dist/core/LoopPolicies.js +12 -7
- package/dist/core/LoopPolicies.js.map +1 -1
- package/dist/core/MembershipMaterialization.js +1 -1
- package/dist/core/MembershipMaterialization.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +97 -51
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +1 -0
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +27 -25
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ReasoningView.d.ts +1 -1
- package/dist/core/ReasoningView.d.ts.map +1 -1
- package/dist/core/ReasoningView.js +4 -5
- package/dist/core/ReasoningView.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +3 -2
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/Turn.d.ts +1 -1
- package/dist/core/Turn.d.ts.map +1 -1
- package/dist/core/Turn.js +2 -2
- package/dist/core/Turn.js.map +1 -1
- package/dist/core/Turn.sql +1 -1
- package/dist/core/TurnDispositionHandler.d.ts +1 -1
- package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
- package/dist/core/TurnDispositionHandler.js +6 -15
- package/dist/core/TurnDispositionHandler.js.map +1 -1
- package/dist/core/TurnMaterialization.js +1 -1
- package/dist/core/TurnMaterialization.js.map +1 -1
- package/dist/core/TurnOps.d.ts +1 -0
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +18 -0
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +53 -32
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/env-defaults.d.ts +12 -1
- package/dist/core/env-defaults.d.ts.map +1 -1
- package/dist/core/env-defaults.js +50 -7
- package/dist/core/env-defaults.js.map +1 -1
- package/dist/core/file-creation-policy.d.ts.map +1 -1
- package/dist/core/file-creation-policy.js +2 -1
- package/dist/core/file-creation-policy.js.map +1 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +0 -3
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/namespace.d.ts +1 -0
- package/dist/core/namespace.d.ts.map +1 -1
- package/dist/core/namespace.js +21 -75
- package/dist/core/namespace.js.map +1 -1
- package/dist/core/notifications.d.ts +2 -0
- package/dist/core/notifications.d.ts.map +1 -1
- package/dist/core/packet-wire.d.ts +1 -0
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +28 -7
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/results.d.ts +2 -0
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +7 -0
- package/dist/core/results.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +1 -2
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/DigestEvidence.d.ts +1 -0
- package/dist/digest/DigestEvidence.d.ts.map +1 -1
- package/dist/digest/DigestEvidence.js +8 -0
- package/dist/digest/DigestEvidence.js.map +1 -1
- package/dist/digest/DigestRender.d.ts.map +1 -1
- package/dist/digest/DigestRender.js +23 -21
- package/dist/digest/DigestRender.js.map +1 -1
- package/dist/digest/digest-rows.d.ts +2 -1
- package/dist/digest/digest-rows.d.ts.map +1 -1
- package/dist/digest/digest.sql +4 -0
- package/dist/observe/init.d.ts +1 -0
- package/dist/observe/init.d.ts.map +1 -1
- package/dist/observe/init.js +5 -1
- package/dist/observe/init.js.map +1 -1
- package/dist/schemes/EffectPolicy.d.ts.map +1 -1
- package/dist/schemes/EffectPolicy.js +4 -4
- package/dist/schemes/EffectPolicy.js.map +1 -1
- package/dist/schemes/Exec.d.ts +1 -0
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +30 -14
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecScheduler.d.ts.map +1 -1
- package/dist/schemes/ExecScheduler.js +4 -3
- package/dist/schemes/ExecScheduler.js.map +1 -1
- package/dist/schemes/ExecScratch.d.ts.map +1 -1
- package/dist/schemes/ExecScratch.js +2 -1
- package/dist/schemes/ExecScratch.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +21 -22
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/_entry-find.d.ts +1 -0
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +4 -3
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_path-scope.d.ts +2 -2
- package/dist/schemes/_path-scope.d.ts.map +1 -1
- package/dist/schemes/_path-scope.js +23 -2
- package/dist/schemes/_path-scope.js.map +1 -1
- package/dist/server/AgentRoots.d.ts +6 -0
- package/dist/server/AgentRoots.d.ts.map +1 -0
- package/dist/server/AgentRoots.js +25 -0
- package/dist/server/AgentRoots.js.map +1 -0
- package/dist/server/ConfigurationDiagnostics.d.ts +12 -0
- package/dist/server/ConfigurationDiagnostics.d.ts.map +1 -0
- package/dist/server/ConfigurationDiagnostics.js +40 -0
- package/dist/server/ConfigurationDiagnostics.js.map +1 -0
- package/dist/server/Daemon.d.ts +11 -5
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +125 -48
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +11 -4
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/EnvFunctionality.d.ts.map +1 -1
- package/dist/server/EnvFunctionality.js +5 -2
- package/dist/server/EnvFunctionality.js.map +1 -1
- package/dist/server/Functionality.d.ts +6 -0
- package/dist/server/Functionality.d.ts.map +1 -1
- package/dist/server/Functionality.js +173 -37
- package/dist/server/Functionality.js.map +1 -1
- package/dist/server/FunctionalityManager.js +6 -6
- package/dist/server/FunctionalityManager.js.map +1 -1
- package/dist/server/MembersFunctionality.d.ts.map +1 -1
- package/dist/server/MembersFunctionality.js +13 -41
- package/dist/server/MembersFunctionality.js.map +1 -1
- package/dist/server/PluginSources.d.ts +20 -0
- package/dist/server/PluginSources.d.ts.map +1 -0
- package/dist/server/PluginSources.js +52 -0
- package/dist/server/PluginSources.js.map +1 -0
- package/dist/server/PlurnkSkill.js +1 -1
- package/dist/server/PlurnkSkill.js.map +1 -1
- package/dist/server/Retention.d.ts.map +1 -1
- package/dist/server/Retention.js +11 -30
- package/dist/server/Retention.js.map +1 -1
- package/dist/server/ServiceModules.d.ts +1 -0
- package/dist/server/ServiceModules.d.ts.map +1 -1
- package/dist/server/ServiceModules.js +9 -3
- package/dist/server/ServiceModules.js.map +1 -1
- package/dist/server/SkillSource.d.ts +37 -0
- package/dist/server/SkillSource.d.ts.map +1 -0
- package/dist/server/SkillSource.js +267 -0
- package/dist/server/SkillSource.js.map +1 -0
- package/dist/server/SkillsFunctionality.d.ts +8 -27
- package/dist/server/SkillsFunctionality.d.ts.map +1 -1
- package/dist/server/SkillsFunctionality.js +218 -270
- package/dist/server/SkillsFunctionality.js.map +1 -1
- package/dist/server/WorkerModelResolver.d.ts +1 -2
- package/dist/server/WorkerModelResolver.d.ts.map +1 -1
- package/dist/server/WorkerModelResolver.js +18 -19
- package/dist/server/WorkerModelResolver.js.map +1 -1
- package/dist/server/WorkspacePlugins.d.ts +25 -0
- package/dist/server/WorkspacePlugins.d.ts.map +1 -0
- package/dist/server/WorkspacePlugins.js +43 -0
- package/dist/server/WorkspacePlugins.js.map +1 -0
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +2 -1
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/envelope.js +1 -1
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/loop-model.d.ts.map +1 -1
- package/dist/server/loop-model.js +1 -2
- package/dist/server/loop-model.js.map +1 -1
- package/dist/server/module-discovery.d.ts +6 -0
- package/dist/server/module-discovery.d.ts.map +1 -1
- package/dist/server/module-discovery.js +46 -24
- package/dist/server/module-discovery.js.map +1 -1
- package/dist/server/skills-problems.d.ts +8 -0
- package/dist/server/skills-problems.d.ts.map +1 -0
- package/dist/server/skills-problems.js +18 -0
- package/dist/server/skills-problems.js.map +1 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +55 -45
- package/dist/service.js.map +1 -1
- package/docs/env.md +7 -8
- package/docs/members.md +4 -3
- package/docs/skills.md +45 -22
- package/package.json +37 -35
- package/plurnk.service +29 -0
package/SPEC.md
CHANGED
|
@@ -133,7 +133,7 @@ package map. The default installed composition is specified in {§bundled-set}.
|
|
|
133
133
|
|
|
134
134
|
OpenTelemetry may observe PLURNK; it never becomes product state, failure transport, scheduler input, model teaching, or client protocol. Domain and client activity remain on AG-UI. Reusable packages depend on the OTel API only; the daemon constructs only the explicitly configured trace and metric providers. An unconfigured or standards-valid disabled process loads no SDK or exporter implementation and keeps the API's no-op behavior with bounded overhead. OTel Logs have no provider or initialization path.
|
|
135
135
|
|
|
136
|
-
Configuration uses the standard `OTEL_*` environment: `OTEL_TRACES_EXPORTER` / `OTEL_METRICS_EXPORTER` select `otlp` or `console` per signal (a missing or `none` value keeps that signal off; no SDK default selects an exporter), `OTEL_SERVICE_NAME` names the service (default `plurnk-service`), case-insensitive `true` in `OTEL_SDK_DISABLED` turns the boundary off, and OTLP exporters honor `OTEL_EXPORTER_OTLP_*`. An unknown exporter name
|
|
136
|
+
Configuration uses the standard `OTEL_*` environment: `OTEL_TRACES_EXPORTER` / `OTEL_METRICS_EXPORTER` select `otlp` or `console` per signal (a missing or `none` value keeps that signal off; no SDK default selects an exporter), `OTEL_SERVICE_NAME` names the service (default `plurnk-service`), case-insensitive `true` in `OTEL_SDK_DISABLED` turns the boundary off, and OTLP exporters honor `OTEL_EXPORTER_OTLP_*`. An unknown exporter name makes observability unavailable with a configuration diagnostic ({§configuration-repair-path}); offline checking rejects the same input before loading an SDK. OTel Logs and direct draft semantic-convention use are excluded. HTTP spans carry only an AG-UI-owned bounded route class, never an input pathname or query. Spans otherwise carry high-cardinality identifiers; metric labels stay low-cardinality. Prompts, reasoning, file bodies, arbitrary URLs, secrets, and plugin payloads are never recorded as attributes or metric values by default. Exporter failure cannot change product results or client lifecycle. Daemon, telemetry, and database teardown are independent reverse-ownership phases; every phase runs and aggregate failure preserves every cause.
|
|
137
137
|
|
|
138
138
|
§observability-genai-conventions **GenAI convention projection.** Provider
|
|
139
139
|
request spans follow the [GenAI registry at `c88d504`](https://github.com/open-telemetry/semantic-conventions-genai/tree/c88d504ab3d9879f8e50d3cc87e69775e11db234),
|
|
@@ -188,12 +188,13 @@ an explicitly specified subpath, not in the frozen root barrel.
|
|
|
188
188
|
```mermaid
|
|
189
189
|
flowchart LR
|
|
190
190
|
LISTENER["Bind client listener<br/>unready: HTTP 503"] --> DB["Acquire daemon lock<br/>and admit SQLite schema"]
|
|
191
|
-
DB -->
|
|
192
|
-
PROVIDER --> DAEMON["Construct and start<br/>daemon composition"]
|
|
191
|
+
DB --> DAEMON["Construct and start<br/>daemon composition"]
|
|
193
192
|
DAEMON --> CLIENT["Activate client transport"]
|
|
193
|
+
CLIENT --> SELECT["Worker selects or first uses a model"]
|
|
194
|
+
SELECT --> PROVIDER["Construct and verify<br/>selected provider"]
|
|
194
195
|
LISTENER -. failure .-> FAIL["Fail startup<br/>durable state untouched"]
|
|
195
196
|
DB -. failure .-> CLOSE_LISTENER["Close listener"] --> FAIL
|
|
196
|
-
PROVIDER -. failure .->
|
|
197
|
+
PROVIDER -. failure .-> REPAIR["Return selection failure<br/>client remains available"]
|
|
197
198
|
DAEMON -. failure .-> TEARDOWN["Close every started owner"] --> FAIL
|
|
198
199
|
```
|
|
199
200
|
|
|
@@ -220,14 +221,17 @@ address by URL, never by port (#641): AG-UI mounts `/` and `/agui`, A2A the
|
|
|
220
221
|
well-known card and its endpoint path, on the same address.
|
|
221
222
|
|
|
222
223
|
§startup-admission-order After listener ownership, database admission completes
|
|
223
|
-
before
|
|
224
|
+
before capability initialization can perform external work. Provider construction
|
|
225
|
+
and endpoint verification occur on worker selection or first use, never merely
|
|
226
|
+
because a default selector is configured. Startup validates selectors without
|
|
227
|
+
provider I/O and retains their configuration diagnostics. Every
|
|
224
228
|
later startup failure closes resources in reverse ownership order while
|
|
225
229
|
preserving the originating failure: daemon, observability, database, listener.
|
|
226
230
|
|
|
227
231
|
§startup-readiness-line **Readiness is one stdout line.** After the client interface is mounted
|
|
228
232
|
the service prints exactly one line, `plurnk-service agui=<url> db=<json string> route=<json string>`:
|
|
229
233
|
the URL brackets an IPv6 host, and the database path and the route (the active model route or
|
|
230
|
-
`no model`) are JSON strings, so a path or route containing spaces is exact and a consumer parses
|
|
234
|
+
`no model`, or `invalid model configuration`) are JSON strings, so a path or route containing spaces is exact and a consumer parses
|
|
231
235
|
the URL as a URL and the strings as JSON; nothing else the service prints on stdout before it has
|
|
232
236
|
that prefix. Before the line the listener answers `503 service-starting`; after it,
|
|
233
237
|
`discover` is the identity check a launcher uses to tell this daemon from any other listener. A bind
|
|
@@ -378,7 +382,7 @@ direct-entry-plus-directory count; `-1` enables the ordinary markerless page;
|
|
|
378
382
|
unset / `0` disables previews. `log://` is absent because the current worker's
|
|
379
383
|
log already renders in present mode.
|
|
380
384
|
|
|
381
|
-
§worker-initialization-entry **Model-worker initialization is a real `_plurnk` turn.** A model worker's first loop begins with one packetless `{ producer="_plurnk", kind="initialization" }` turn submitted through {§turn-ops-admission-path}. Its reasoning
|
|
385
|
+
§worker-initialization-entry **Model-worker initialization is a real reasoning-only `_plurnk` turn.** A model worker's first loop begins with one packetless `{ producer="_plurnk", kind="initialization" }` turn submitted through {§turn-ops-admission-path}. Its authored reasoning contains the orientation NOTE, READ/FIND surveys and its own reasoning READ ({§reasoning-initial-read}); it is stored before those operations execute through {§reasoning-operations} and {§op-execution-order}. No content program or emission row is fabricated ({§emission-row}). The first request sees ordinary results and the reasoning READ, not an assistant-content copy of the surveys. Every orienting row is structurally classified `_plurnk` and `init`. The namespace surveys and their asides follow {§actor-boundary-catalog-preview}.
|
|
382
386
|
|
|
383
387
|
Incoming messages publish once as inbound SEND rows in the first model turn
|
|
384
388
|
({§message-arrival}); initialization neither READs nor archives them. The turn
|
|
@@ -1049,7 +1053,7 @@ boundary.
|
|
|
1049
1053
|
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** The stream first persists its terminal state. A worker **blocked on a 202 wait** for that stream ({§wait-obligation-matrix}) then **awakens that loop in place** — the blocked loop *is* the continuation, so there is no fresh loop and no summary-as-prompt fiction. An already-active worker needs no injected prompt or second wake because its next packet reads the durable terminal state. A concluded worker receives no synthetic loop from ambient stream closure. The result remains available in the stream's own state under every case.
|
|
1050
1054
|
- §worker-lifecycle-child-wake **Each child task completion notifies its parent.** Terminal-task publication, including failure and cancellation of a parked task, notifies the direct parent without injecting a prompt. Other unfinished tasks or streams in that child remain independent obligations; they cannot suppress notification. The parent's eligible waits requeue in place under {§loop-wake-identity} and the bounded {§worker-optimistic-settlement} opportunity. Durable revisioning covers completion-before-park and restart; drain teardown and whole-worker quiescence are not completion identities.
|
|
1051
1055
|
- §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 program completion; separately, a stream conclusion, direct-child conclusion or addressed reply persists and publishes immediately but holds eligible parked loops' `202→100` requeues 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 arrivals within that window into at most one requeue per eligible loop. With no sibling obligation the wake is immediate; at the deadline, surviving work follows the ordinary monitored lifecycle. An arrival after provider dispatch begins retains its next wake, while poll, new-request and operator wakes never open this hold. Only packet/provider dispatch waits: durable state, client events, cancellation and the replying program do not. One redaction-safe span records elapsed time, quiescence versus deadline, and arrival count without entering the packet.
|
|
1052
|
-
- §worker-lifecycle-idle-is-concluded **Idle is not
|
|
1056
|
+
- §worker-lifecycle-idle-is-concluded **Idle is not completion.** An empty WAIT continues; an eligible parameterless KILL with observed arrivals and results and no held work concludes under {§wait-obligation-matrix}, with or without an answer body. A concluded worker retains durable history; a later addressed arrival starts a new loop.
|
|
1053
1057
|
- §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.
|
|
1054
1058
|
- §worker-lifecycle-durable-disposition **Durable disposition wins cancellation races.** At a turn boundary, the engine reads the loop's durable status before interpreting a process-local abort. A committed `202` park survives a later daemon-shutdown signal; only a loop still durably running at `102` can be terminalized by that cancellation. Wake selection rechecks shutdown and worker cancellation before requeuing each parked loop.
|
|
1055
1059
|
- §worker-lifecycle-restart-recovery **Restart is owner-loss reconciliation, not replay.** Before opening client transports, the service holds an exclusive database-adjacent daemon lock; a second live owner fails before touching SQLite, while a dead-PID crash claim is replaced atomically without a timeout lease. Boot preserves accepted `100` loops and restores their drains. A `102` loop belonged to a vanished drain/provider call, so it settles `500` with the interruption on its durable row—never replayed across an unknown effect boundary. Every pending physical provider request first settles as an error with absent usage and explicitly unknown cost; then its logical model call closes. Recovery never fabricates zero evidence. Every durable proposed operation likewise lost its process-local resolution waiter and settles as a visible `500 owner_vanished` occurrence rather than an unresolvable interrupt ({§proposal-list}). A pending client interaction also lost its exact awaiting operation, so boot removes the orphan instead of replaying work or inventing a response ({§client-interactions}). Every durable-open subscription belonged to a vanished callable: active channels become errored and its row closes `500`. A `202` continuation requeues on an unseen completion or when no live obligation remains. Otherwise it stays parked on surviving children; the drain restores inherited stream observation through the same guarded scheduler ({§worker-wait-timing}). Child terminalization wakes its parked parent on every outcome, including provider exceptions, cancellation, and restart interruption, recursively through the durable parent edges. These operations are idempotent, so an interrupted recovery safely repeats.
|
|
@@ -1078,6 +1082,16 @@ plugin-authored operation turns; exposing that path must not introduce a
|
|
|
1078
1082
|
parallel record or lifecycle. Producer and kind never change. Process-restart
|
|
1079
1083
|
recovery completes any turn whose producer vanished.
|
|
1080
1084
|
|
|
1085
|
+
§turn-exception-outcome An exceptional inference exit completes every still-open
|
|
1086
|
+
turn it acquired, without overwriting completed turns or inventing provider evidence.
|
|
1087
|
+
The turn owner propagates the original exception unchanged.
|
|
1088
|
+
|
|
1089
|
+
| Exception cause | Turn status |
|
|
1090
|
+
|---|---|
|
|
1091
|
+
| The owning aborted signal's reason, directly or through an `Error.cause` chain | 499 for cancellation; 504 for the loop execution deadline. |
|
|
1092
|
+
| An unrelated exception, including an unrelated `AbortError` concurrent with cancellation | 500. An aborted signal alone does not prove causation. |
|
|
1093
|
+
| An `AggregateError` combining cancellation with other failures | 500; cancellation does not conceal another failure. |
|
|
1094
|
+
|
|
1081
1095
|
§turn-ops-admission-path **Source acquisition varies; admitted-turn execution does not.**
|
|
1082
1096
|
A provider response, deterministic `_plurnk` program, or future client/plugin
|
|
1083
1097
|
program crosses one admission boundary into the same executor. That executor
|
|
@@ -1214,7 +1228,8 @@ A crossing terminal names the source that struck the crossing turn — `repetiti
|
|
|
1214
1228
|
`no_operation`, then `operation` — in its detail, in that order when a turn matches more
|
|
1215
1229
|
than one. The three are not interchangeable: a turn that authored no operation did not *fail*
|
|
1216
1230
|
one, and reporting it as a failed turn misreads a model answering without the fence as a model
|
|
1217
|
-
whose operations broke.
|
|
1231
|
+
whose operations broke. A cycle of empty turns names repeated responses without operations,
|
|
1232
|
+
not repeated operations or results. This is the crossing turn's source, not the streak's composition; the
|
|
1218
1233
|
rail rules on the crossing and does not retain the kinds behind it. What the crossing turn
|
|
1219
1234
|
actually said is cited, not discarded ({§terminal-evidence}). Naming the source is not the
|
|
1220
1235
|
private accounting {§rail-accounting-private} withholds: the streak, the cycle verdict and
|
|
@@ -1277,7 +1292,7 @@ The parser owns its boundaries; core admits determinate work and exposes its fai
|
|
|
1277
1292
|
| Bounded program, including malformed operations | Admit valid operations and record parser failures; with no authored operation, apply {§empty-turn}. |
|
|
1278
1293
|
| Outside response text | Store it as the turn's `outside` source under {§outside-text}; never a row, never delivered, never completion. |
|
|
1279
1294
|
| Lost boundary after a closed operation | Admit the closed operations and record the boundary diagnostic under {§unparsed-tail-boundary}. |
|
|
1280
|
-
| Lost boundary before any closed operation | Reject the attempt; neither outside text nor a reasoning NOTE substitutes for a closed
|
|
1295
|
+
| Lost boundary before any closed operation | Reject the attempt; neither outside text nor a reasoning NOTE substitutes for a closed operational statement. Reasoning FIND/READ count as such statements ({§reasoning-operations}). |
|
|
1281
1296
|
| Outside text carrying a log-entry heading, other than an emission row's | Reject the attempt ({§fabricated-log-entry}). |
|
|
1282
1297
|
| A response the provider stopped at a repeated line | Reject the attempt with the provider's sentence as its diagnostic ({§repetition-stop}); no provider recovery, notice, or problem row. |
|
|
1283
1298
|
|
|
@@ -1460,9 +1475,23 @@ sequences; native messages and workspace outputs use opaque identifiers rather t
|
|
|
1460
1475
|
pretending to be turn coordinates. `worker://<name>` addresses the actor, not a
|
|
1461
1476
|
historical execution. No address grants ownership or access restrictions.
|
|
1462
1477
|
|
|
1463
|
-
§fs-namespace **
|
|
1478
|
+
§fs-namespace **Filesystem coordinates are ordinary paths; internal addresses are project-relative.** `project_root` is the base directory, not a replacement for the operating-system `/`. It is fixed at workspace creation; a headless workspace has no implicit filesystem base. Absolute paths and paths emitted by executors name the same filesystem locations as they do on the host. Resolution never grants membership or creation authority ({§fs-visibility-grantors}, {§fs-write-surface}); an outside-root member retains its `../`-prefixed key. Plurnk is not a sandbox: confinement belongs to the host.
|
|
1479
|
+
|
|
1480
|
+
§fs-namei **Resolve, then relativize, through one pathname resolver.** Resolve relative input against `project_root` and absolute input from the operating-system root, normalize lexical `.`/`..` segments, then translate the result to a `project_root`-relative key before storage, comparison or canonical rendering. Physical membership and symlink checks remain separate. No failed absolute lookup retries as a project-relative spelling.
|
|
1481
|
+
|
|
1482
|
+
| Input with `project_root=/work/project` | Canonical address |
|
|
1483
|
+
|---|---|
|
|
1484
|
+
| `src/x.md`, `./src/x.md`, `/work/project/src/x.md` | `src/x.md` |
|
|
1485
|
+
| `/src/x.md` | `../../src/x.md` |
|
|
1486
|
+
| `../policy.md`, `/work/policy.md` | `../policy.md` |
|
|
1487
|
+
| `.`, `/work/project/` | The project collection, not a file entry |
|
|
1488
|
+
| `/` | `../../`, the operating-system root collection |
|
|
1489
|
+
|
|
1490
|
+
At `project_root=/`, `/src/x.md` and `src/x.md` resolve to the same key. Without a project root, absolute paths cannot be translated; no process CWD or home directory is substituted.
|
|
1491
|
+
|
|
1492
|
+
Folders and globs select members in those same filesystem coordinates. Parent-directory selectors may include in-root and outside-root members; they never scan or admit unrelated disk contents. Catalog paths and grouped subtree selectors remain project-relative.
|
|
1464
1493
|
|
|
1465
|
-
§
|
|
1494
|
+
§file-path-normalization An authored model file operation using an absolute path receives a `scheme:file/path_normalized` warning Notice naming its project-relative address. COPY/MOVE cover each absolute operand. The Notice neither changes the operation result nor causes a strike, and does not repeat for automatic observations or already-relative paths. Root-mounted workspaces need no such notice. It never suggests that an unadmitted path has become a member.
|
|
1466
1495
|
|
|
1467
1496
|
§fs-canonical-name **One canonical name, storage ≡ wire: the git pathspec.** Member keys follow gitformat-index(5) verbatim (reference edition: git 2.47.3): relative to the workspace `project_root`, without leading slash, `/`-separated, no trailing slash or NUL. Directories are never entries and the root needs no name. When `project_root` is below the containing repository's top level, Git members above it naturally use the same `../`-prefixed CWD-relative names that `git ls-files` emits without `--full-name`; these are not outside-repository mounts. The database stores that root-relative key directly because workspace identity is rooted at the access point. Every model spelling canonicalizes before storage or comparison.
|
|
1468
1497
|
|
|
@@ -1529,6 +1558,8 @@ invalid range, read-only authority, and occupied hidden state without guessing.
|
|
|
1529
1558
|
|
|
1530
1559
|
§file-directory-target **A directory is named as a directory.** Inside the root, a READ (or other exact-path read), KILL or EDIT whose target is a directory on disk — with or without a trailing slash — is refused `path-is-directory`, never as a missing or non-member file, since admitting it is not what the model needs: READ and KILL answer 404, EDIT 403. The detail is `'<key>' is a directory, not a file; <OP> reads/removes/writes one file.` and the recovery names the listing that reaches its files, `` List its files with `FIND (<key>/)`, then READ one by its path. `` (KILL: `then KILL each by its path`; EDIT: `` Name a file inside it, as `EDIT (<key>/<file>)`; list its files with `FIND (<key>/)`. ``). Beyond the root the disk stays dark and {§membership-read-refusal} holds unchanged.
|
|
1531
1560
|
|
|
1561
|
+
§file-find-directory **FIND recognizes existing directories without requiring a trailing slash.** An exact target naming a directory inside the workspace root resolves to the same recursive collection as its slash-suffixed spelling, including content matching and result pagination. Only workspace members appear; an empty directory or one containing only non-members yields a successful empty survey. Exact files stay exact, genuinely absent paths retain their missing-entry error, and this resolution does not inspect disk paths beyond the root. Shared glob and non-file URI semantics are unchanged.
|
|
1562
|
+
|
|
1532
1563
|
### §scheme-manifest Manifest
|
|
1533
1564
|
|
|
1534
1565
|
§scheme-manifest-manifest Per the framework-owned author contract ({§manifest}), each registered scheme exposes one closed `SchemeManifest`. `Manifest.of` validates the complete declaration and enforces that `manifest.name` matches `package.json#plurnk.name` before registration.
|
|
@@ -2171,11 +2202,12 @@ same transitions the dispatcher's atomic curation event makes, without the row.
|
|
|
2171
2202
|
|
|
2172
2203
|
### §reasoning-initial-read Initial reasoning observation
|
|
2173
2204
|
|
|
2174
|
-
The initialization turn records
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2205
|
+
The initialization turn records its `_plurnk`-authored rationale and complete
|
|
2206
|
+
NOTE/FIND/READ program as one reasoning source before dispatch. The shared
|
|
2207
|
+
reasoning extractor ({§reasoning-operations}) admits every operation once, in
|
|
2208
|
+
source order. Its final READ observes that same reasoning source, including
|
|
2209
|
+
the READ itself; this is an ordinary read of already stored text, not recursion
|
|
2210
|
+
or a future-source subscription. The initial message arrives separately as an
|
|
2179
2211
|
inbound SEND ({§message-arrival}). Neither initialization nor later turns
|
|
2180
2212
|
manufacture a task inventory.
|
|
2181
2213
|
`PLURNK_REASONING_VIEW_LINES` (alias-scoped, default in `.env.defaults`) selects this one READ's
|
|
@@ -2184,13 +2216,14 @@ bounds it to the first N lines. Source retention, deliberate READs, and client
|
|
|
2184
2216
|
streaming are independent. The only other automatic reasoning READ follows an empty
|
|
2185
2217
|
turn ({§reasoning-empty-turn-read}).
|
|
2186
2218
|
|
|
2187
|
-
§reasoning-empty-turn-read **
|
|
2188
|
-
turn admitted under {§empty-turn}, one runtime turn of the same loop
|
|
2219
|
+
§reasoning-empty-turn-read **Operation-free reasoning is read back for recovery.** After a
|
|
2220
|
+
turn admitted under {§empty-turn} with no admitted reasoning operations, one runtime turn of the same loop
|
|
2189
2221
|
(`{ producer="_plurnk", kind="operation" }`) dispatches
|
|
2190
2222
|
`READ (reasoning://<worker>/<loop>/<turn>) <!-- turn N emitted no OP -->` over that turn's stored
|
|
2191
2223
|
reasoning source; its receipt renders in the next packet like any other log row.
|
|
2192
2224
|
`PLURNK_REASONING_EMPTY_TURN_LINES` (alias-scoped, default in `.env.defaults`) selects the scope on the same
|
|
2193
|
-
scale as `PLURNK_REASONING_VIEW_LINES`.
|
|
2225
|
+
scale as `PLURNK_REASONING_VIEW_LINES`. Any admitted reasoning NOTE, FIND or READ suppresses
|
|
2226
|
+
this recovery readback. No read follows a turn without reasoning, and none follows
|
|
2194
2227
|
a turn whose emission or reasoning carries a foreign tool-call grammar or leaked template token
|
|
2195
2228
|
(`KnownToxins` names them); the strike and its error row are unchanged.
|
|
2196
2229
|
|
|
@@ -2370,7 +2403,7 @@ single line past the end, a reversed range, empty content, a command's log row
|
|
|
2370
2403
|
|
|
2371
2404
|
### §turn-ops-entry The admitted turn program
|
|
2372
2405
|
|
|
2373
|
-
§turn-ops-log-curation A source-backed turn preserves its **exact
|
|
2406
|
+
§turn-ops-log-curation A source-backed turn preserves its **exact content emission**, including ignored interstitial text, before dispatch. `turn_sources` records that source once, separately from reasoning, the curatable log and optional provider evidence. Retention does not manufacture a log row; the one row an admitted emission gains is its announcement ({§emission-row}). Ordinary READ creates a receipt governed by {§log-readable-projection}; curation of either never changes the source.
|
|
2374
2407
|
|
|
2375
2408
|
### §emission-row The emission row
|
|
2376
2409
|
|
|
@@ -2380,22 +2413,24 @@ like any other row.
|
|
|
2380
2413
|
|
|
2381
2414
|
| Surface | Contract |
|
|
2382
2415
|
|---|---|
|
|
2383
|
-
| When | An inference turn that admitted at least one statement
|
|
2416
|
+
| When | An inference turn that admitted at least one content statement. Reasoning-only turns, including initialization ({§worker-initialization-entry}), a programmatic batch, an empty turn ({§empty-turn}), a client operation and a rejected attempt ({§rejected-emission-entry}) announce nothing. |
|
|
2384
2417
|
| Place | After the turn's inputs (arrivals, deltas, open-path READs) and before its reasoning NOTEs and operations, written after the selection snapshot ({§turn-ops-selection-snapshot}): the emission sits between what the worker had seen and what it caused. |
|
|
2385
2418
|
| Row | A `_plurnk` READ of the turn's own source, `ops://<worker>/L/T`, with `attrs.kind="emission"` and the canonical leaf `/emission`: `### log:///L/T/S/emission → ops://<worker>/L/T · N`. It renders its author, the turn's producer, as `origin`, so a model's row carries none. It is no operation: no receipt, tool call or strike, and outside the op mix. |
|
|
2386
|
-
| Body | Frozen: the canonical rendering ({§statement-rendering}) of
|
|
2419
|
+
| Body | Frozen at announcement: the canonical rendering ({§statement-rendering}) of admitted content statements, each in a closed fence. Each body appears within the shared preview bound ({§body-projection}), `PLURNK_SERVICE_PREVIEW_LINES` and `PLURNK_SERVICE_PREVIEW_CHARS`. A longer body keeps its head, and its closing fence carries `<!-- Automatically truncated op body: READ (ops://<worker>/L/T) to retrieve in full -->`; nothing the harness writes enters a fence. Absent and empty bodies remain empty. All heading operands, scopes, metadata, patterns and asides remain. Free text and unadmitted forms are absent; a recovered native call ({§native-tool-calls}) appears as the operation it was read as; an operation whose receipt failed stays. Reasoning operations retain their own source and normal receipts, never an assistant-content copy. No body text is inspected for nested operations. |
|
|
2420
|
+
| Sources and memory | Dispatch and immutable `ops://` sources retain complete bodies ({§turn-source-resources}); an explicit source READ returns them normally. The wire omits NOTE and WAIT blocks, whose own rows show them whole ({§body-projection}), so curating a NOTE row removes its text; an emission of only NOTE and WAIT delivers nothing, its row stands, and no assistant message follows it ({§packet-wire-envelope}); reasoning-only NOTEs never enter this projection. |
|
|
2421
|
+
| Stability | The projection is fixed from its first appearance, never aged or resized under budget pressure. Already frozen announcements and historical request captures are not rewritten. |
|
|
2387
2422
|
| Presentation | Born folded: the record shows its header, and its body follows the record as the worker's assistant message. |
|
|
2388
|
-
| Accounting | `logTokens` charges the record and the emission
|
|
2423
|
+
| Accounting | `logTokens` charges the record and the emission the wire delivers, truncation asides included, never the omitted NOTE and WAIT blocks or the cut remainder ({§packet-token-accounting}). An explicit source READ has its own ordinary charge. |
|
|
2389
2424
|
| Curation | Curated whole ({§log-kill-scope}): KILL retires it, and so does a scope covering every line (`<1,-1>`); on its exact coordinate a narrower scope is 422 `emission-curated-whole`, and a sweep whose scope would only trim it leaves it intact. |
|
|
2390
2425
|
| Schema | Migration 12 admits `kind="emission"` only on this shape: one per turn, the turn's newest row when written, frozen, and curated whole. A database from before version 12 keeps its rows and gains no announcement. FORK copies it with the inherited turns, still naming its writer. |
|
|
2391
|
-
| Echoes | A worker that repeats the heading in its own text is tolerated ({§fabricated-log-entry}); the digest counts the echoes. |
|
|
2426
|
+
| Echoes | A worker that repeats the heading in its own text is tolerated ({§fabricated-log-entry}); the digest counts the echoes. A worker that copies the truncation aside onto its own closer keeps its body intact; the aside is outside text ({§outside-text}). |
|
|
2392
2427
|
|
|
2393
2428
|
### §turn-source-resources Immutable turn-source resources
|
|
2394
2429
|
|
|
2395
2430
|
| Surface | Contract |
|
|
2396
2431
|
|---|---|
|
|
2397
2432
|
| Identity | `ops://<worker>/<loop>/<turn>`, `reasoning://<worker>/<loop>/<turn>`, and `note://<worker>/<loop>/<turn>/<item>` name a worker in the current workspace and its durable coordinates. A note's item is its dispatched NOTE ordinal. The `outside` source ({§outside-text}) has no address: no `outside://` scheme exists, and it is reached only through `outside/event`, FORK and the digest. The worker authority is required and case-sensitive; userinfo, ports, and queries are invalid. Source identity never depends on the reading worker. `log:///` remains local; READ, FIND and KILL reject log authorities, userinfo, ports and queries with 400, never substitute the caller's log. |
|
|
2398
|
-
| Source | `ops` is exact admitted `text/vnd.plurnk`, and its turn's emission row carries the
|
|
2433
|
+
| Source | `ops` is exact admitted `text/vnd.plurnk`, and its turn's emission row carries the preview-bounded projection ({§emission-row}); `reasoning` is `text/plain` containing the selected original provider reasoning or a non-model producer's authored rationale. Producer identity comes from the owning turn; a harness rationale is not provider evidence. The turn decides existence and the source decides content: a turn that exists but has no source of that kind reads as the ordinary empty resource (204, empty body), never a fabricated one; a worker or turn that does not exist is 404. |
|
|
2399
2434
|
| Notes | Each dispatched NOTE stores its exact literal body as an immutable `text/plain` source and returns its worker-qualified address. There may be multiple notes in a turn, from reasoning, content, or another producer. A missing note is 404, not an empty invented note. Sharing its URI uses ordinary SEND; the receiver deliberately READs it. NOTE itself sends no ambient update. |
|
|
2400
2435
|
| Retention | One ops source, one reasoning source and one outside source per turn; one note source per NOTE ordinal. An optional inference-call link records provenance. Source removal follows deletion of its owning turn, never log curation. |
|
|
2401
2436
|
| Operations | Ordinary scoped READ, FIND, content search and COPY from any named worker's source within the workspace. FIND accepts authority and path patterns, retaining complete worker-qualified identities in results and folder selectors. READ returns data and never executes it. Sources are read-only for every actor and have no edit hashes. |
|
|
@@ -2631,7 +2666,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
2631
2666
|
- §find-line-anchors **A FIND regex anchors each line**, as READ, EDIT and KILL do ({§read-pattern}, {§edit-pattern}): `^` and `$` are a line's ends in every FIND content match — over entries, log rows, turn sources and a binary channel's bytes — so ```` ```FIND (django/urls/resolvers.py) /^from|^import/ ```` locates the same import lines a READ with that pattern shows, never a false 204.
|
|
2632
2667
|
- §find-candidate-containment **One candidate's crash is that candidate's problem** — arbitrary member content can crash a mimetype handler mid-match (an unbalanced template partial crashed Readability and killed a 1,916-file FIND as a blank 500, #449). `Matcher.matchCandidates` contains a per-candidate handler throw: the candidate drops out exactly like unsupported content, the cause goes to daemon stderr, and only a FIND whose every candidate crashed reports a 415 whose Problem names the first crashing member and handler. The operation's other candidates always answer.
|
|
2633
2668
|
|
|
2634
|
-
- §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does — in every spelling: a `**` glued to a name (`**.go`, `src/**.ts`) is matched as `**/*.go` / `src/**/*.ts`, never demoted to a one-level `*` the way a native matcher reads it. Terminal `*` and `**` are structural catalog selectors and include dot-prefixed entries, so a complete map does not hide `.env.defaults` or `.github`; richer patterns retain native shell behavior. SQLite prefix queries may reduce the candidate set but never decide the match. A trailing slash is a recursive FIND scope only for a scheme whose manifest declares `folderScopes: true`; otherwise it is ordinary resource syntax. This is an explicit plugin contract, never inferred from URL punctuation.
|
|
2669
|
+
- §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry unless the file scheme resolves a directory under {§file-find-directory}; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does — in every spelling: a `**` glued to a name (`**.go`, `src/**.ts`) is matched as `**/*.go` / `src/**/*.ts`, never demoted to a one-level `*` the way a native matcher reads it. Terminal `*` and `**` are structural catalog selectors and include dot-prefixed entries, so a complete map does not hide `.env.defaults` or `.github`; richer patterns retain native shell behavior. SQLite prefix queries may reduce the candidate set but never decide the match. A trailing slash is a recursive FIND scope only for a scheme whose manifest declares `folderScopes: true`; otherwise it is ordinary resource syntax. This is an explicit plugin contract, never inferred from URL punctuation.
|
|
2635
2670
|
|
|
2636
2671
|
Resource-authority globs select authorities independently of the path scope.
|
|
2637
2672
|
Matching resources retain their full addresses through pattern matching,
|
|
@@ -2648,7 +2683,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
2648
2683
|
- §find-fulltext-selection Every matcher operates only over the candidate set selected by `(target)`; indexed matchers do not bypass that selection. `~query` passes the native FTS5 expression to SQLite and ranks matching candidates by ascending BM25, with resource identity breaking ties. Native BM25 uses the shared index's term statistics; candidate visibility, owner, channel and target filters determine which resources can be returned. The ordinary FIND pager selects resources for broad targets or match locations for exact targets: markerless search uses {§markerless-first-page}, `<N>` selects position N and `<N,M>` selects an inclusive range. Fractions are invalid result coordinates, not similarity thresholds. Results expose addressable matched text regions; neither cosine scores nor percentage similarity is invented. Native query-syntax failures return 400 with SQLite's diagnostic; database and implementation failures propagate.
|
|
2649
2684
|
- §fts-word-phrase **A word with inner punctuation is the phrase of its tokens.** FTS5 barewords hold only letters, digits, `_` and non-ASCII, so before the query reaches SQLite each word outside a quoted string or `NEAR(…)` group that is not a bareword (with optional leading `^` and trailing `*`) is quoted as a phrase: `~inherited-members` searches `"inherited-members"` — the adjacent tokens `inherited members` — instead of failing as `no such column: members`, and `c++`, `x.y`, `a/b` likewise. FTS5's own syntax passes untouched: `AND`/`OR`/`NOT`/`NEAR`, `+`, quoted phrases, parentheses, and column filters (a word containing `:`, `{` or `}`, or opening with `-`). A column-filter failure keeps SQLite's diagnostic and its recovery says what the filter is and gives the bare-word and `NOT` forms: `` `members:` and `-members` are FTS5 column filters, and the index has one column; to search for a word write it bare, as `~members`, and to exclude one write `NOT` between terms, as `~a NOT members`. ``
|
|
2650
2685
|
- §find-scoped-isolation Workspace + scheme scoped — no cross-workspace/cross-scheme leakage.
|
|
2651
|
-
- §find-result-projection **The
|
|
2686
|
+
- §find-result-projection **The resolved target selection determines the result unit; result cardinality never changes it** ({§find-result-unit}). Returns `FindResult { status, content, mimetype, results, range, matchingPathCount, matchLocationCount, itemsWeightTotal, returnedItemsWeightTotal }`:
|
|
2652
2687
|
|
|
2653
2688
|
| Target | Matcher | `range.unit` | Result rows |
|
|
2654
2689
|
|---|---|---|---|
|
|
@@ -2734,10 +2769,9 @@ same durable liveness.
|
|
|
2734
2769
|
| Fresh operation/parser failure, without an authored WAIT | Continue before any automatic parking. |
|
|
2735
2770
|
| Neither an authored WAIT nor an eligible completion request ({§kill-conclusion}) | Continue, regardless of earlier replies or live work. |
|
|
2736
2771
|
| Live work and either WAIT or an eligible completion request | Park the same loop; message arrival, child or stream settlement, or stream cadence wakes it. No final-answer body is delivered while joining. |
|
|
2737
|
-
| WAIT without live work | Continue; never invent a future wake.
|
|
2738
|
-
| Unanswered messages | Continue. |
|
|
2772
|
+
| WAIT without live work | Continue; never invent a future wake. Its row says only `Nothing is in flight. Continuing.`, however often the loop yields this way. |
|
|
2739
2773
|
| Unobserved operation results, failures, child results or stream conclusions | Continue; the next packet presents them. |
|
|
2740
|
-
| Eligible completion request with no
|
|
2774
|
+
| Eligible completion request with no unpublished arrivals, live work or unobserved results | Conclude successfully, whether or not it delivers an answer. |
|
|
2741
2775
|
|
|
2742
2776
|
An empty emission is handled by {§empty-turn}. Ordinary strikes, cycles and
|
|
2743
2777
|
execution limits remain independent. NOTE and successful targeted KILL do not themselves
|
|
@@ -2797,11 +2831,12 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2797
2831
|
outstanding condition. Valid sibling operations always execute. Only an admitted
|
|
2798
2832
|
KILL delivers its literal body through {§send-response-receipt}; a deferred body
|
|
2799
2833
|
remains forensic evidence, never a stored draft to replay automatically. An empty
|
|
2800
|
-
KILL concludes
|
|
2801
|
-
|
|
2834
|
+
KILL concludes silently even when an observed message has no delivered answer. It neither
|
|
2835
|
+
invents delivery nor replaces an earlier answer; immutable input and reply history remain intact.
|
|
2836
|
+
SEND, NOTE and targeted KILL never request successful
|
|
2802
2837
|
completion. New arrivals still guard the terminal transition atomically
|
|
2803
|
-
({§completion-defers-to-messages}); an arrival concurrent with
|
|
2804
|
-
|
|
2838
|
+
({§completion-defers-to-messages}); an unobserved arrival concurrent with completion
|
|
2839
|
+
keeps the loop running. No implicit successful exit exists.
|
|
2805
2840
|
- §outside-text **Text outside the operations is the turn's `outside` source: stored, weighed, never a row.**
|
|
2806
2841
|
The spans {§response-text} supplies are stored verbatim as one immutable `outside` turn source
|
|
2807
2842
|
per admitted emission, in source order joined by a blank line ({§turn-source-resources}); no
|
|
@@ -2821,15 +2856,16 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2821
2856
|
{§response-text} alone owns which bytes are operations, quotations or outside text.
|
|
2822
2857
|
- §loop-answer **A loop's address is what it said.** READ `ops://<worker>/<loop>` resolves to
|
|
2823
2858
|
the latest reply the loop gave to the message that started it: the body of a SEND
|
|
2824
|
-
or accepted final KILL that answered that message. A
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2859
|
+
or accepted final KILL that answered that message. A failed terminal takes precedence over
|
|
2860
|
+
an earlier reply and retains its exact Problem, including {§terminal-evidence}. A running loop
|
|
2861
|
+
without a reply is 425; a concluded loop without one returns its terminal outcome, including
|
|
2862
|
+
successful silence. Completion never fabricates an answer or a missing-resource failure.
|
|
2863
|
+
`ops://<worker>/<loop>/<turn>`
|
|
2828
2864
|
remains that turn's emission. A concluded child's `loop_termination` row to its parent
|
|
2829
2865
|
READs this same loop resource. Witness: `test/intg/loop-answer.test.ts`.
|
|
2830
2866
|
- §empty-turn **No authored response operation is a recoverable turn, never completion.**
|
|
2831
|
-
Count parsed
|
|
2832
|
-
text ({§outside-text}) enter the count. When none exist and no boundary was lost, retain the turn
|
|
2867
|
+
Count parsed content operations and reasoning FIND/READs ({§reasoning-operations}); neither
|
|
2868
|
+
reasoning NOTEs nor outside text ({§outside-text}) enter the count. When none exist and no boundary was lost, retain the turn
|
|
2833
2869
|
and its raw sources and count one progress-contract strike, whether or not the turn carried text. The strike sends no notice of its own: the turn records one `_plurnk`
|
|
2834
2870
|
error row, `422` `The turn performed no operation.`, which rides the next packet's errors like
|
|
2835
2871
|
any failure ({§operation-result-uniform-error-channel}), and its reasoning is read back to the
|
|
@@ -2938,8 +2974,9 @@ target that cannot be read keeps the owning READ's failure identity (#163) and s
|
|
|
2938
2974
|
the slot contract in its recovery — the resource is the program and the body its stdin;
|
|
2939
2975
|
a command belongs beneath a targetless heading — without guessing which was meant (#425). The started receipt always
|
|
2940
2976
|
names the working directory only when it is not the project root, and then in the
|
|
2941
|
-
|
|
2942
|
-
|
|
2977
|
+
project-relative form ({§fs-namespace}); the default directory is omitted rather
|
|
2978
|
+
than repeated in every receipt. Native file addresses resolve from that same project
|
|
2979
|
+
directory, while absolute input retains its filesystem meaning. The `(path)` is a program — a script for an interpreter, a tool name for a tool
|
|
2943
2980
|
family — and neither a command nor a working directory is ever a target. The default
|
|
2944
2981
|
shell is written as its own fence, ```` ```sh ````; no runtime-less form exists.
|
|
2945
2982
|
|
|
@@ -3047,13 +3084,15 @@ the workspace snapshot. Installed siblings form the immutable base:
|
|
|
3047
3084
|
they are discovered and probed at startup, and availability is cached.
|
|
3048
3085
|
Workspace Functionality providers may atomically overlay additional names under
|
|
3049
3086
|
{§module-workspace-capabilities}; a name has one owner within a workspace, while
|
|
3050
|
-
independent workspaces may use the same name.
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
`
|
|
3087
|
+
independent workspaces may use the same name. The fence name selects exactly
|
|
3088
|
+
that registered executable tool. Unknown tags are refused 400 with the
|
|
3089
|
+
advertised catalogue and are never reinterpreted as shell command words.
|
|
3090
|
+
A runtime unavailable after an ordinary probe is 501 with the probe `detail`.
|
|
3091
|
+
Typed configuration failures preserve the declaration, with no executor instance
|
|
3092
|
+
or output scheme; invocation returns the exact 503 configuration Problem
|
|
3093
|
+
({§configuration-repair-path}). No executor, including `sh`, is required for
|
|
3094
|
+
daemon startup. Internal constructor defects remain failures, not unavailable
|
|
3095
|
+
configuration verdicts.
|
|
3057
3096
|
|
|
3058
3097
|
For a family runtime, `ExecutorRegistry.toolRegistry(tag, workspaceId)`
|
|
3059
3098
|
validates the one executor-owned snapshot used by packet presentation,
|
|
@@ -3230,8 +3269,8 @@ Each layer uses the same value and masking rules. A worker's list includes works
|
|
|
3230
3269
|
defaults by reference with `origin: "workspace"`; worker overrides and masks remain
|
|
3231
3270
|
worker-owned. Enabling an inherited entry clears this layer's mask, not a mask in a
|
|
3232
3271
|
lower layer; an explicit local value can override that lower layer. A mask follows
|
|
3233
|
-
the name even when its lower-layer origin changes. Removing an override
|
|
3234
|
-
|
|
3272
|
+
the name even when its lower-layer origin changes. Removing an override restores the
|
|
3273
|
+
lower entry and its enabledness ({§configuration-definition-resolution}). Forking copies only worker state, not the workspace defaults. Workspace edits
|
|
3235
3274
|
affect subsequent launches, not existing processes or other workspaces. A shared
|
|
3236
3275
|
capability never acquires an invoking worker's overrides or ownership.
|
|
3237
3276
|
|
|
@@ -3297,7 +3336,7 @@ body prefixes.
|
|
|
3297
3336
|
|
|
3298
3337
|
## §proposal Proposals and client interactions
|
|
3299
3338
|
|
|
3300
|
-
§proposal-202-pauses A side-effecting op does not execute on dispatch — it **proposes**. The scheme returns **202** (an execution on a `host` runtime {§exec}, an EDIT to a member file {§membership}); the engine writes the log row `state='proposed'`, registers a waiter keyed by `logEntryId`, and **pauses `dispatch`** awaiting a resolution. The provider exchange and emitted operation are already durable, while the turn remains open until dispatch settles; {§engine-rails} therefore sees the *resolved* status, never the provisional 202.
|
|
3339
|
+
§proposal-202-pauses A side-effecting op does not execute on dispatch — it **proposes**. The scheme returns **202** (an execution on a `host` runtime {§exec}, an EDIT to a member file {§membership}); the engine writes the log row `state='proposed'`, registers a waiter keyed by `logEntryId`, and **pauses `dispatch`** awaiting a resolution. The provider exchange and emitted operation are already durable, while the turn remains open until dispatch settles; {§engine-rails} therefore sees the *resolved* status, never the provisional 202. Acceptance runs the scheme's effect and settles with its result ({§proposal-accept-applies}).
|
|
3301
3340
|
|
|
3302
3341
|
**Resolution arrives through one lifecycle:**
|
|
3303
3342
|
|
|
@@ -3309,7 +3348,7 @@ body prefixes.
|
|
|
3309
3348
|
|
|
3310
3349
|
| decision | state | `status_rx` | default outcome | effect |
|
|
3311
3350
|
|---------------------------------|---|---|---|---|
|
|
3312
|
-
| §proposal-accept-applies accept | `resolved` | 200 | — | runs the scheme's **`applyResolution`** — the real side effect (disk write, exec spawn). An unavailable handler returns `410 handler-unavailable`, never success. A failing apply (≥400)
|
|
3351
|
+
| §proposal-accept-applies accept | `resolved`, or `failed` when application fails | applied result's status, otherwise 200 | — | runs the scheme's **`applyResolution`** — the real side effect (disk write, exec spawn). An unavailable handler returns `410 handler-unavailable`, never success. A failing apply (≥400) preserves its result and marks the row failed without changing the client's decision; its outcome is retained, or `apply_failed` when it names none. |
|
|
3313
3352
|
| §proposal-reject-fails reject | `failed` | 400 | `rejected` | none — the action did not occur. |
|
|
3314
3353
|
| §proposal-cancel-aborts cancel | `cancelled` | 499 | `loop_aborted` | none — the loop is abandoning. |
|
|
3315
3354
|
|
|
@@ -3431,11 +3470,19 @@ was said ({§methods-loop-run-fold-consistency}). `LoopPolicies.compose` then ma
|
|
|
3431
3470
|
once. `PLURNK_SERVICE_ATTENDED` answers an unstated attendance, and the attendance picks which
|
|
3432
3471
|
knob answers an unstated disposition: `PLURNK_SERVICE_PROPOSALS` for an attended loop,
|
|
3433
3472
|
`PLURNK_SERVICE_UNATTENDED_PROPOSALS` for an unattended one, whose vocabulary has no `review`.
|
|
3434
|
-
Every panel state is therefore lawful
|
|
3473
|
+
Every valid panel state is therefore lawful; an invalid knob is diagnosed at startup
|
|
3474
|
+
and refuses a loop that needs it ({§configuration-repair-path}). No code,
|
|
3435
3475
|
schema or column holds a default ({§operator-config-only-home}): `loops.policy` and
|
|
3436
|
-
`loops.max_turns` carry none, so every insert states both
|
|
3437
|
-
|
|
3438
|
-
|
|
3476
|
+
`loops.max_turns` carry none, so every insert states both. Client-authored administrative
|
|
3477
|
+
loops use the same composition.
|
|
3478
|
+
|
|
3479
|
+
§runtime-bookkeeping-policy **Runtime bookkeeping has no reviewer and cannot acquire
|
|
3480
|
+
new authority.** Its administrative loops explicitly state
|
|
3481
|
+
`{ attended: false, proposals: "reject" }`; this is a runtime invariant, not an
|
|
3482
|
+
interactive default. Generated reference publication and audit narration therefore
|
|
3483
|
+
do not depend on client policy configuration. Runtime-authored proposals do not
|
|
3484
|
+
use effect-policy auto-admission; bookkeeping proposals settle as failures through
|
|
3485
|
+
the ordinary proposal lifecycle, never wait for a client or auto-accept.
|
|
3439
3486
|
|
|
3440
3487
|
§loop-policy-effective-read `loops.policy` persists one complete immutable
|
|
3441
3488
|
`LoopPolicy`; every runtime policy read validates that snapshot before use.
|
|
@@ -3538,7 +3585,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
3538
3585
|
| §db-maintenance-optimize Statistics at shutdown | The daemon's last database step before the caller closes SQLite is `PRAGMA optimize` on the writer (`maintenance_optimize`), so `sqlite_stat1` reflects tables the connection planned against, bounded by SQLite's own analysis limit; a failure there is a reported shutdown error, never silent. Retention runs before it under the operator's policy ({§retention-policy}) and ends with a WAL truncation ({§db-space-reclamation}); no periodic `ANALYZE` runs. |
|
|
3539
3586
|
| §db-space-reclamation The daemon keeps its own file healthy | `PLURNK_SERVICE_AUTO_VACUUM` (`incremental` or `none`) names the mode the daemon keeps its file in. At start, before any drain, a database in another mode is converted (set the mode, one `VACUUM`, which rewrites the file and needs free disk about its size) and the journal says so with page counts before and after. Under `incremental`, every retention pass ends by stepping `PRAGMA incremental_vacuum` to completion once free pages reach `PLURNK_SERVICE_RECLAIM_MIN_FREE_BYTES` (0 = every pass), and reports `reclaimedPages`; below the floor, free pages stay for SQLite to reuse. Under `none` the file never shrinks and freed pages are reused. No operator step is involved beyond the knobs. The WAL stays bounded by SQLite's automatic checkpoint (#764). |
|
|
3540
3587
|
| §content-store Every body is stored once | `contents` holds each settled body once, addressed by its SHA-256, however many channels, workspaces, forks or derivations carry it; rows are immutable. `entry_channel_rows` points a settled channel at its body and keeps an active stream's body as a private buffer until it settles, when it is interned. Every reader and writer uses the `entry_channels` view, whose `INSTEAD OF` triggers intern bodies, refuse a bound `content_hash` that is not the content's, and write each column group only when it changed, so a search attachment is never a representation write. SQLite counts no changes for a view, so a write that must know whether its channel exists returns the channel's name; an outer join cannot flatten the view, so the two statements that need one read `entry_channel_rows` and `contents` directly. `derivation_fts` is an external-content index over `derivation_texts` (a derivation joined to its body); `derivations.content_id` names the indexed text, and the triggers in `_entry-fts.sql` move the index with it and forget it on delete. A body no channel holds and no derivation indexes is collected by retention under `PLURNK_SERVICE_COLLECT_CONTENTS`. Witnesses: `test/intg/retention.test.ts`, `test/intg/entries.test.ts`, `test/intg/fulltext-index.test.ts`. |
|
|
3541
|
-
| §retention-policy Retention is the operator's policy; information is kept by default | `Retention` (`src/server/Retention.ts`, statements in `Retention.sql`) reads ten knobs from `.env.defaults` once at daemon
|
|
3588
|
+
| §retention-policy Retention is the operator's policy; information is kept by default | `Retention` (`src/server/Retention.ts`, statements in `Retention.sql`) reads ten knobs from `.env.defaults` once at daemon start (the two storage knobs are {§db-space-reclamation}) and runs four set statements in dependency order — on `PLURNK_SERVICE_RETENTION_INTERVAL_MS` cadence while the daemon runs (0 = shutdown only) and once more at shutdown before `PRAGMA optimize`. `PLURNK_SERVICE_RETAIN_PACKET_TURNS` (-1 = every packet) and `PLURNK_SERVICE_RETAIN_PACKET_MS` (-1 = no age limit) retire a completed turn's packet composition (`turn_sections`, {§packet-items}) once it is beyond the newest N packet-bearing turns of its loop or older than the age; the turn, its bag, its log rows and its accounting stay, and an open turn is never retired. `PLURNK_SERVICE_COLLECT_PACKET_ITEMS` collects items no composition references. `PLURNK_SERVICE_COLLECT_CONTENTS` collects stored bodies nothing holds ({§content-store}), after the collectors that release them. `PLURNK_SERVICE_COLLECT_DERIVATIONS` collects derivations no channel, turn source, or log row cites — superseded editions — with their symbols (cascade) and their full-text shadow (`derivations_delete_fts`, a process trigger beside the FTS statements, on every delete path). `PLURNK_SERVICE_RETAIN_RESPONSE_TURNS` and `PLURNK_SERVICE_RETAIN_RESPONSE_MS` retire a settled call's response body (`model_call_responses`) once it is beyond the newest N body-bearing calls of its loop or its turn is older than the age; the call's identity, failure, capacity, admission and accounting stay, and the digest renders such a call request-only. The durable record is kept; packets and response bodies — transient evidence — age out, so a daemon left running for months stops growing (#788). An invalid policy withholds retention, including storage conversion and shutdown collection, and reports its configuration diagnostic without blocking the client ({§configuration-repair-path}). Storage I/O failures remain ordinary startup or shutdown failures. Witness: `test/intg/retention.test.ts`. |
|
|
3542
3589
|
|
|
3543
3590
|
- DDL = storage truth; JSON Schemas = wire truth. They are allowed to differ where ergonomics demand.
|
|
3544
3591
|
- §entry-identity-no-null **Identity components are never NULL.** `(workspace_id, scheme, authority, pathname)` is a unique key. `workspace_id` references the workspace directly with cascading deletion. Namespace schemes use empty authority; resource schemes retain their canonical authority. File members use nonempty `scheme="file"` and render as bare paths. Registration refuses `storedScheme: null`.
|
|
@@ -3684,11 +3731,14 @@ and is ignored rather than resolved against the working directory.
|
|
|
3684
3731
|
|
|
3685
3732
|
| Class | Base | Plurnk member |
|
|
3686
3733
|
|---|---|---|
|
|
3687
|
-
| Configuration | `$XDG_CONFIG_HOME` (default `~/.config`) | `plurnk/.env`, `plurnk/AGENTS.md` |
|
|
3734
|
+
| Configuration | `$XDG_CONFIG_HOME` (default `~/.config`) | `plurnk/.env`, `plurnk/AGENTS.md`, plurnk-only MCP definitions `plurnk/mcp.json`, Agent Skills `plurnk/skills/<name>/SKILL.md`, and Agent Plugins `plurnk/plugins/<plugin>/` |
|
|
3688
3735
|
| Durable user data | `$XDG_DATA_HOME` (default `~/.local/share`) | `plurnk/plurnk.db` and SQLite sidecars |
|
|
3689
3736
|
| Persistent operational state | `$XDG_STATE_HOME` (default `~/.local/state`) | On-demand workspace/module directories ({§module-workspace-directory}). |
|
|
3690
3737
|
| Reproducible cache | `$XDG_CACHE_HOME` (default `~/.cache`) | Reserved; no directory is created without an owned artifact. |
|
|
3691
3738
|
| Shared global Agent Skills | User home | `.agents/skills/<name>/SKILL.md` |
|
|
3739
|
+
| Shared global MCP definitions | User home | `.agents/mcp.json` |
|
|
3740
|
+
| Shared global Agent Plugins | User home | `.agents/plugins/<plugin>/` ({§agent-plugins-hosting}) |
|
|
3741
|
+
| A plugin's `PLUGIN_DATA` | `$XDG_DATA_HOME` | `plurnk/plugins/<plugin>/` |
|
|
3692
3742
|
|
|
3693
3743
|
§state-root **A private daemon has one root.** `PLURNK_SERVICE_STATE_ROOT` (absolute; a leading
|
|
3694
3744
|
`~/` expands; a relative value fails hard by name) replaces the data, state, cache and runtime homes
|
|
@@ -3710,15 +3760,34 @@ ordinary precedence; XDG variables themselves require absolute paths.
|
|
|
3710
3760
|
|---------:|------------------------------------|-----------------------------------------------------------|
|
|
3711
3761
|
| 1 | Assembled package `.env.defaults` | Set-if-unset floor; one owner per key. |
|
|
3712
3762
|
| 2 | `$XDG_CONFIG_HOME/plurnk/.env` | User-level ambient configuration. |
|
|
3713
|
-
| 3 |
|
|
3714
|
-
| 4 | `--
|
|
3715
|
-
| 5 |
|
|
3716
|
-
| 6 |
|
|
3717
|
-
|
|
3763
|
+
| 3 | `--config=<path>` | Singular service-owned explicit file. |
|
|
3764
|
+
| 4 | `--env-file*` | Repeatable explicit files; later selected files win. |
|
|
3765
|
+
| 5 | Initial shell environment | Preserved over every file layer. |
|
|
3766
|
+
| 6 | Derived service CLI flags | Assigned last. |
|
|
3767
|
+
|
|
3768
|
+
A working directory's `.env` configures that directory's application, never plurnk (#926); a
|
|
3769
|
+
project's variables reach its commands through the workspace environment ({§workspace-env}).
|
|
3718
3770
|
|
|
3719
3771
|
Node's pre-script env-file form and the executable's post-script form share the same later-file-wins ordering. `--env-file-if-exists` skips an absent file without changing the order of selected files.
|
|
3720
3772
|
|
|
3721
|
-
§operator-config-env-defaults **Every package owns its knobs —
|
|
3773
|
+
§operator-config-env-defaults **Every package owns its knobs — one assembled floor.**
|
|
3774
|
+
|
|
3775
|
+
| Source | Panel | Admission |
|
|
3776
|
+
|---|---|---|
|
|
3777
|
+
| Platform capability package | `.env.defaults` at the package root | `@plurnk/*` or a `plurnk` package field; {§plugin-trust-boundary} |
|
|
3778
|
+
| Agent Plugin native extension | `ai.plurnk/.env.defaults` | The winning daemon-wide plugin in {§agent-plugins-hosting}, a valid native declaration, and the same trust gate |
|
|
3779
|
+
|
|
3780
|
+
Root and trust flags apply before collection. A project plugin contributes no native panel.
|
|
3781
|
+
Non-module native panels follow their npm-only family discovery ({§plugin-manifest-read});
|
|
3782
|
+
a plain-folder declaration does not suppress an installed capability's panel.
|
|
3783
|
+
Linked packages resolve panels against the same canonical root as native code;
|
|
3784
|
+
an absent panel is optional, but a panel escaping that root is rejected.
|
|
3785
|
+
The file travels with its code and is its configuration reference. All admitted files compose
|
|
3786
|
+
one floor, applied set-if-unset beneath operator sources. `plurnk-service config defaults`
|
|
3787
|
+
renders those same owner-labelled files, preserving comments and optional declarations without
|
|
3788
|
+
persisting another copy or exposing effective values. Duplicate key ownership fails naming both
|
|
3789
|
+
owners. Invalid optional native panels are diagnosed and prevent that extension from loading;
|
|
3790
|
+
they do not block the remaining floor or the repair path ({§configuration-repair-path}).
|
|
3722
3791
|
|
|
3723
3792
|
§operator-config-only-home **The cascading environment is the only home for a choice.** The principle and its reasons are ARCHITECTURE.md's (*Configuration authority*); this is what `scripts/env-surface-policy.mjs` enforces in `root:lint`, over the source Git tracks:
|
|
3724
3793
|
|
|
@@ -3750,6 +3819,34 @@ or provider request. The seeded `.env`, first-run diagnostic, service help, and
|
|
|
3750
3819
|
missing-model recovery all signpost `plurnk-service config defaults` as the
|
|
3751
3820
|
complete installed option catalog.
|
|
3752
3821
|
|
|
3822
|
+
§operator-config-offline-validation **`config check` and runtime use the same
|
|
3823
|
+
owning configuration readers, with different failure boundaries.** An offline
|
|
3824
|
+
check rejects invalid configuration with a nonzero exit. Runtime contains
|
|
3825
|
+
optional-family errors according to {§configuration-repair-path}. Failure
|
|
3826
|
+
names the offending variable or file/entry and retains its cause.
|
|
3827
|
+
|
|
3828
|
+
| Owner | Offline validation |
|
|
3829
|
+
|---|---|
|
|
3830
|
+
| Core | Model selection, file-creation/effect/loop policy, members definitions and controls, skill-fetch settings and root selection |
|
|
3831
|
+
| MCP | Whole definitions from the environment and selected files (cwd is the project for this check), future-alias controls, catalog settings, timeouts, retry pacing and registry URL |
|
|
3832
|
+
| A2A | Whole outbound definitions and controls, timeout/diagnostic bounds, configured inbound exposure |
|
|
3833
|
+
| Schedule | Whole definitions and controls, recurrence syntax, time zone and preview count |
|
|
3834
|
+
| Hooks | Command/argument/event configuration and delivery bounds |
|
|
3835
|
+
|
|
3836
|
+
Disabled definitions and controls without a resource are validated, not skipped.
|
|
3837
|
+
Checking creates no database, starts no process or listener, arms no schedule,
|
|
3838
|
+
and contacts no provider or endpoint. Symbolic credential references remain
|
|
3839
|
+
symbolic: availability belongs to workspace preparation, not offline validation.
|
|
3840
|
+
|
|
3841
|
+
First-run seeding publishes the complete private configuration directory atomically.
|
|
3842
|
+
Concurrent initializers adopt the winning seed; a failed initializer removes only
|
|
3843
|
+
its own staging directory. An existing operator directory is never reseeded.
|
|
3844
|
+
|
|
3845
|
+
§systemd-user-unit The service package ships `plurnk.service` as an example
|
|
3846
|
+
systemd user unit. Installation and enablement are explicit operator actions;
|
|
3847
|
+
package installation performs neither. The template documents executable-path
|
|
3848
|
+
and environment adjustments instead of introducing a service-management command.
|
|
3849
|
+
|
|
3753
3850
|
Model selection uses one selector vocabulary in `ProviderRegistry` ({§provider-instantiation}). `PLURNK_MODEL_<alias>=<provider>/<model-id>` optionally declares a friendly route and tuning scope; `PLURNK_MODEL=<selector>` selects either that alias or an exact provider/model route. `PLURNK_MODEL_CHILD=<selector>` uses the same vocabulary for the default child provider; unset means inherit the spawning loop's provider. Operator selections and alias declarations live in `.env`, not `.env.defaults`.
|
|
3754
3851
|
|
|
3755
3852
|
Each knob's value lives on its panel and nowhere else (`plurnk-service config defaults` prints them all); this table says what the service's knobs mean.
|
|
@@ -3870,7 +3967,7 @@ the policy renders in exactly one packet section. Every other tier runs the
|
|
|
3870
3967
|
test cascade, so shipped-default regressions are otherwise invisible by
|
|
3871
3968
|
construction.
|
|
3872
3969
|
|
|
3873
|
-
§operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to gate-wide service posture that is identical on every machine: complete catalog orientation, automatic Git membership when the operator ceiling permits Git, ambient operator-file docs/packet notes cleared, ambient MCP
|
|
3970
|
+
§operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to gate-wide service posture that is identical on every machine: complete catalog orientation, automatic Git membership when the operator ceiling permits Git, ambient operator-file docs/packet notes cleared, the operator's installed skills and plugins left unread ({§agent-roots}), ambient MCP/A2A and schedules default disabled, and `PLURNK_EXECS_QUESTION=0` for unattended runs. The ordinary executor switch removes the question tool and its teaching; an explicit override can opt into an attended drill. The drivers also project per-alias `ENABLED=0` overrides for named MCP, A2A, schedule, membership, and skill resources in the operator's config file. Definitions remain inspectable; explicit shell/benchmark controls win. Mock-tier bootstrap clears these ambient families before loading its fixture floor. No operator file is rewritten. Configuration with a narrower or variable owner stays outside it:
|
|
3874
3971
|
|
|
3875
3972
|
| Owner | Configuration |
|
|
3876
3973
|
|---|---|
|
|
@@ -3953,36 +4050,55 @@ proceeds. `setup` is the readiness boundary for every capability registered
|
|
|
3953
4050
|
with Core: recovery may demand a workspace provider before `start`. For a
|
|
3954
4051
|
pre-bound client interface, requests remain unavailable until `start`; every
|
|
3955
4052
|
other module opens its module-owned exterior ingress only after recovery. No
|
|
3956
|
-
registered capability may depend on exterior ingress.
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
discovery
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
4053
|
+
registered capability may depend on exterior ingress. A module, and any distinct
|
|
4054
|
+
lifetime object returned by `start`, may implement the following phases:
|
|
4055
|
+
|
|
4056
|
+
| Phase | Obligation |
|
|
4057
|
+
|---|---|
|
|
4058
|
+
| `stop()` | Reject new ingress, stop timers and other producers, and settle owned work that can emit core events. Keep event subscriptions and resources needed for settlement alive. |
|
|
4059
|
+
| `close()` | Unsubscribe observers and release remaining resources after producer settlement; await admitted notification deliveries. Do not start new core work. |
|
|
4060
|
+
|
|
4061
|
+
Both phases are optional and idempotent; repeated calls join the same work.
|
|
4062
|
+
Core tracks a module before `setup` so partially acquired resources are released
|
|
4063
|
+
even if setup fails. A returned object identical to its module is tracked once.
|
|
4064
|
+
|
|
4065
|
+
§module-discovery **Daemon modules compose through their shared lifecycle.**
|
|
4066
|
+
|
|
4067
|
+
| Source | Declaration | Lifetime |
|
|
4068
|
+
|---|---|---|
|
|
4069
|
+
| Platform capability package | `package.json#plurnk` with `kind: "module"` and `module` | Daemon-wide |
|
|
4070
|
+
| Agent Plugin | `plugin.json#extensions.ai.plurnk` with `kind: "module"` and a `module` path under `ai.plurnk/` | Daemon-wide; npm and selected user roots only |
|
|
4071
|
+
| Project Agent Plugin | Portable components only | Workspace-scoped; native code is not imported |
|
|
4072
|
+
|
|
4073
|
+
The export is one DaemonModule object or no-argument factory. Standard bundles follow
|
|
4074
|
+
{§agent-plugins-hosting} source order, then other installed module packages load in package-name
|
|
4075
|
+
order. All trusted modules register before setup. The service's explicit AG-UI, hooks and MCP
|
|
4076
|
+
composition is never duplicated. Untrusted modules are reported and not imported. Invalid
|
|
4077
|
+
declarations, unavailable module files and configuration errors during construction are diagnosed
|
|
4078
|
+
at the affected native extension; healthy siblings remain available. A factory validates startup
|
|
4079
|
+
configuration before `setup` acquires resources. Failures after registration begins follow
|
|
4080
|
+
{§module-lifecycle} cleanup, not a partial-registration fallback.
|
|
4081
|
+
An invalid module object, factory result or lifecycle member is an implementation contract failure,
|
|
4082
|
+
not configuration, and fails loudly. Native capabilities register through their owning public
|
|
4083
|
+
interfaces and release registrations during {§module-lifecycle} resource teardown.
|
|
3972
4084
|
|
|
3973
4085
|
§module-shutdown-order `Daemon.stop()` first rejects new capability demand and
|
|
3974
|
-
aborts proposals, branches, derivations, and worker scopes. It
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
4086
|
+
aborts proposals, branches, derivations, and worker scopes. It begins module
|
|
4087
|
+
`stop()` calls in reverse registration order without serially awaiting them,
|
|
4088
|
+
so every producer is asked to stop even if another stalls. Core settles drains,
|
|
4089
|
+
capability publications, module producers, streaming producers, derivations,
|
|
4090
|
+
mimetypes, schemes, and the final worker-settlement barrier while observers
|
|
4091
|
+
remain subscribed. Only then does it begin and join module `close()` calls in
|
|
4092
|
+
reverse order. Failures do not skip later phases and join one shutdown aggregate.
|
|
4093
|
+
The supervisor owns each asynchronous cancellation and wake task from
|
|
3980
4094
|
acceptance through settlement, including immediately acknowledged and explicitly
|
|
3981
4095
|
awaited cancellation; a task failure participates in the shutdown aggregate.
|
|
3982
4096
|
After asynchronous selection, the supervisor rechecks shutdown before creating
|
|
3983
4097
|
a drain or installing a timer; parked-loop wake mutations also recheck worker
|
|
3984
4098
|
cancellation under {§worker-lifecycle-durable-disposition}.
|
|
3985
|
-
The database
|
|
4099
|
+
The database remains available through observer closure and final maintenance.
|
|
4100
|
+
The shared deadline bounds every phase, including observer delivery; forced
|
|
4101
|
+
shutdown may therefore lose notifications and reports the unfinished phase.
|
|
3986
4102
|
|
|
3987
4103
|
§crash-only-stop The settle sequence is deadline-bounded
|
|
3988
4104
|
(`PLURNK_SERVICE_STOP_TIMEOUT_MS`, default 30000): past the deadline each wait
|
|
@@ -3996,21 +4112,22 @@ backstop, not the exit.
|
|
|
3996
4112
|
```mermaid
|
|
3997
4113
|
flowchart LR
|
|
3998
4114
|
stop[Begin stop] --> abort[Abort core producers]
|
|
3999
|
-
stop -->
|
|
4115
|
+
stop --> moduleStop[Begin reverse module stop]
|
|
4000
4116
|
abort --> drains[Settle worker drains]
|
|
4001
|
-
drains --> joined[Settle module
|
|
4002
|
-
|
|
4117
|
+
drains --> joined[Settle module producers]
|
|
4118
|
+
moduleStop --> joined
|
|
4003
4119
|
joined --> producers[Settle streaming producers]
|
|
4004
4120
|
producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
|
|
4005
4121
|
resources --> settlement[Settle cancellations and wakes]
|
|
4006
|
-
settlement -->
|
|
4122
|
+
settlement --> observers[Close observers and resources]
|
|
4123
|
+
observers --> database[Maintain and release database]
|
|
4007
4124
|
```
|
|
4008
4125
|
|
|
4009
4126
|
| Setup function | Contract |
|
|
4010
4127
|
|---|---|
|
|
4011
4128
|
| `registerRuntimes([{ decl, executor, availability, scheme? }, ...])` | Validates the complete canonical tag set under {§executor-runtime-declaration}, then publishes every process-wide executor and optional claimed scheme facet atomically. |
|
|
4012
4129
|
| `registerScheme(name, handler)` | Adds one process-wide addressable scheme handler; scheme readiness and model-facing capability publication remain core-owned. |
|
|
4013
|
-
| §module-action-registration `registerModuleAction({ name, scope, inputSchema, outputSchema, handler })` | Adds one non-empty, extension-unique action with resolvable JSON Schemas. `scope` is exactly `worldless`, `workspace`, or `worker`; the handler receives schema-validated params and a separate matching context. Scoped contexts contain trusted bound identifiers, never client parameters. A client-interface module decides whether and how the name becomes public, validates successful output, and owns collisions with its built-ins. |
|
|
4130
|
+
| §module-action-registration `registerModuleAction({ name, scope, residency, inputSchema, outputSchema, handler })` | Adds one non-empty, extension-unique action with resolvable JSON Schemas. `scope` is exactly `worldless`, `workspace`, or `worker`; the handler receives schema-validated params and a separate matching context. `residency` is explicitly `required` or `none`: only the former acquires workspace capabilities and reconciles worker documents. Worldless actions require `none`. Scoped contexts contain trusted bound identifiers, never client parameters. A client-interface module decides whether and how the name becomes public, validates successful output, and owns collisions with its built-ins. |
|
|
4014
4131
|
| §module-workspace-provider `registerWorkspaceCapabilityProvider(namespaceOwner, provider)` | Registers one extension-unique Functionality provider. `activate({ workspaceId, retain })` reconstructs the workspace snapshot; idempotent `deactivate({ workspaceId })` releases process resources. Core coalesces demand and supplies residency leases for work that outlives its caller. |
|
|
4015
4132
|
| §module-workspace-state `readWorkspaceModuleState(workspaceId, namespaceOwner)` | Reads one nullable JSON state value per workspace and provider. Core owns storage and lifecycle; the provider owns its schema. Store symbolic credential references, not copied secrets. A worker-scoped family's coordinator reads and replaces the same shape per worker in `worker_module_state` ({§functionality-scope}). |
|
|
4016
4133
|
| `readWorkspaceEnvironment(workspaceId)` | Captures the workspace env layer ({§workspace-env}) and returns its composer. No argument uses admitted host values; a supplied environment supplies a module's reference-resolution context. Both apply the same captured values and masks, without worker overrides. |
|
|
@@ -4060,7 +4177,7 @@ A conflicting alias is rejected explicitly; it never produces a hidden second
|
|
|
4060
4177
|
definition for the submitting client or worker.
|
|
4061
4178
|
|
|
4062
4179
|
§module-workspace-residency **Persistence is not residency.** Model execution,
|
|
4063
|
-
capability-aware operations,
|
|
4180
|
+
capability-aware operations, module actions declaring required residency, and retained provider work
|
|
4064
4181
|
lease the workspace's Functionality. Boot, workspace or worker creation,
|
|
4065
4182
|
attachment, listing, naming, idle clients, and parked state alone do not.
|
|
4066
4183
|
After the last lease releases, `PLURNK_SERVICE_WORKSPACE_WARM_MS` and
|
|
@@ -4090,10 +4207,10 @@ registry. Deleting the workspace cascades its state; worker lifecycle does not.
|
|
|
4090
4207
|
## Workspace Functionality
|
|
4091
4208
|
|
|
4092
4209
|
§functionality-coordinator **One coordinator owns the common lifecycle.**
|
|
4093
|
-
|
|
4210
|
+
Functionality families are adapters beneath
|
|
4094
4211
|
`list | discover | add | enable | disable | remove`. State and mutations
|
|
4095
|
-
serialize per workspace and family.
|
|
4096
|
-
`workspace.<family>.<verb>` and model manager executors invoke the same
|
|
4212
|
+
serialize per workspace and family. Scope-bound client actions
|
|
4213
|
+
`workspace.<family>.<verb>` / `worker.<family>.<verb>` and model manager executors invoke the same
|
|
4097
4214
|
coordinator. Families do not invent another management grammar, proposal
|
|
4098
4215
|
policy, or hotload path.
|
|
4099
4216
|
|
|
@@ -4101,12 +4218,98 @@ Retryability describes the actual failed condition, not its numeric status.
|
|
|
4101
4218
|
|
|
4102
4219
|
| Verb | Common contract |
|
|
4103
4220
|
|---|---|
|
|
4104
|
-
| `list` | Project definitions, origin, enabledness, and preparation outcome: disabled, active, unavailable with its Problem, or authorization-required. No credential values. |
|
|
4221
|
+
| `list` | Project definitions, ownership (`origin`), winning configuration source (`provenance`), enabledness, and published preparation outcome: disabled, dormant, active, unavailable with its Problem, or authorization-required. No credential values. |
|
|
4105
4222
|
| `discover` | Return inert candidates. Never install, persist, enable, or execute them. |
|
|
4106
|
-
| `add` | Admit and persist a
|
|
4223
|
+
| `add` | Admit and persist a local definition, prepare it, and enable it atomically. It may override an inherited definition. Reapplying the same local definition enables it idempotently (200); a different local definition for that alias fails 409 without replacing it. |
|
|
4107
4224
|
| `enable` | Publish an available definition; retry preparation if unavailable. |
|
|
4108
4225
|
| `disable` | Withdraw live capability; retain its definition and saved results. |
|
|
4109
|
-
| `remove` |
|
|
4226
|
+
| `remove` | Forget the locally owned definition and its enabledness override. Restore any inherited definition with its inherited enabledness. Inherited definitions cannot be removed at this scope. Saved results remain. |
|
|
4227
|
+
|
|
4228
|
+
§configuration-definition-resolution **Named resource definitions replace whole;
|
|
4229
|
+
independent behavior controls remain independent.** Source readers and scope
|
|
4230
|
+
overlays apply the same boundary:
|
|
4231
|
+
|
|
4232
|
+
| Value | Resolution |
|
|
4233
|
+
|---|---|
|
|
4234
|
+
| Named definition | Select the complete definition from the highest-precedence source or scope declaring that alias. Omitted fields, arrays and nested objects never inherit from a lower definition. |
|
|
4235
|
+
| Environment resource declaration | Use {§resource-environment}: absence inherits, an empty definition is invalid, and an explicit enabledness switch disables without erasing the definition. |
|
|
4236
|
+
| Definition validity | Validate the selected definition against its family's schema. Missing required fields are errors, not requests to fill from a lower definition; rejected live changes preserve the previous publication. |
|
|
4237
|
+
| Independently declared behavior control | Resolve its own value through its cascade. An enabledness override does not copy or patch the definition it controls. |
|
|
4238
|
+
| Local definition removal | Remove this scope's definition and enabledness override. Restore the current inherited definition and enabledness, or leave no entry if none exists. Do not persist a replacement or disabling mask; subsequent inherited changes remain effective. Restoration follows the same preparation and publication failure policy as other mutations ({§functionality-publication}). |
|
|
4239
|
+
|
|
4240
|
+
§configuration-provenance **Inspection names the winning definition's input, not
|
|
4241
|
+
its owner or runtime.** `provenance` uses the same `{kind, source, reference?}`
|
|
4242
|
+
shape as discovery candidates. Source readers contribute it; the coordinator
|
|
4243
|
+
preserves it through inheritance, enabledness changes, and every readiness state.
|
|
4244
|
+
|
|
4245
|
+
| Definition source | Inspection |
|
|
4246
|
+
|---|---|
|
|
4247
|
+
| Assembled environment | `kind: environment`, `source`: exact definition key; never its value or an inferred dotenv filename. |
|
|
4248
|
+
| Discovered skill root | `kind: file`, `source`: the winning `SKILL.md` path. |
|
|
4249
|
+
| Standalone MCP file | `kind: file`, `source`: the winning `mcp.json` path; `reference`: its entry's JSON Pointer. |
|
|
4250
|
+
| Local workspace/worker definition or host-provided tree with no configuration input | No fabricated provenance; `origin` identifies ownership and the family definition describes the resource. |
|
|
4251
|
+
| Local override | Replaces inherited provenance with the local definition; removal restores the current inherited provenance. |
|
|
4252
|
+
|
|
4253
|
+
Only the winning definition's source is reported. Shadowed definitions, secrets,
|
|
4254
|
+
and environment-file loading history are not tracked. Source metadata is derived
|
|
4255
|
+
on inspection, not persisted in the local overlay or used as runtime identity.
|
|
4256
|
+
|
|
4257
|
+
§configuration-repair-path **Invalid optional configuration cannot remove the
|
|
4258
|
+
agent's repair environment.** Capability owners reject typed operator input
|
|
4259
|
+
errors. The launcher and shared coordinator contain them at their respective
|
|
4260
|
+
composition boundaries, not arbitrary exceptions:
|
|
4261
|
+
|
|
4262
|
+
| Boundary | Outcome |
|
|
4263
|
+
|---|---|
|
|
4264
|
+
| Optional startup integration (hooks, hosted A2A, observability) cannot be configured | Withhold that integration and retain its exact configuration diagnostic. Activate the client interface and unrelated capabilities; never invent a replacement setting. |
|
|
4265
|
+
| Invalid default model or child selector | Retain its diagnostic at startup; keep client inspection and explicit selection available. A request relying on the invalid selector fails with `daemon:configuration/configuration-invalid` (503), naming its key. Never substitute another model or silently inherit a child model. |
|
|
4266
|
+
| Model construction or endpoint verification fails | Reject selection/use before inference or committing the selection. Keep the client available; a later selection can retry or choose another route. |
|
|
4267
|
+
| Invalid effect, file-creation, or loop-default policy | Retain the startup diagnostic. Reject the affected operation or unresolved loop policy as `daemon:configuration/configuration-invalid` (503); no guessed admission policy, external effect, or orphan approval wait. Independent operations and explicitly supplied valid loop policies remain usable. |
|
|
4268
|
+
| Invalid retention configuration | Withhold collection and automatic storage conversion, including shutdown collection. Preserve stored evidence and expose the diagnostic; do not substitute a deletion policy. |
|
|
4269
|
+
| Invalid packet configuration or retired capacity knobs | Retain the startup diagnostic and keep client inspection available. Reject affected packet construction before inference with `daemon:configuration/configuration-invalid` (503). Never guess a capacity or projection setting. |
|
|
4270
|
+
| Invalid executor construction/probe configuration | Keep the installed declaration and its diagnostic, but no executable instance or output scheme. Other executors and ordinary READ/EDIT remain usable. Invoking the unavailable tag returns its exact 503 configuration Problem. |
|
|
4271
|
+
| Invalid execution scheduling, input, or scratch configuration | Diagnose at startup and validate on the affected execution path before admission, stream creation, or external effects. Scratch configuration applies only to resource-backed execution; inline programs remain independent. Never substitute concurrency, timeout, or directory settings. |
|
|
4272
|
+
| Invalid model-alias catalog | Fail catalog inspection explicitly; omit its unavailable snapshot rather than report an empty catalog. Exact provider/model selection does not depend on aliases. |
|
|
4273
|
+
| Offline `config check` | Validate the same inputs without activating integrations; an invalid setting remains a nonzero failure. |
|
|
4274
|
+
| Family configuration cannot be resolved | Keep its manager available, identify the configuration failure in its generated documentation, and preserve durable definitions. Do not publish the family's operational capabilities or pretend its catalog is empty. Other families and ordinary model work remain usable. |
|
|
4275
|
+
| Inspection or mutation of an unresolved family | Return the exact configuration Problem, naming the key and required correction, through both client and model paths. No silent source fallback or change to stored settings. |
|
|
4276
|
+
| Invalid live mutation | Reject atomically and preserve the preceding publication. |
|
|
4277
|
+
| Previously valid family becomes invalid | Withdraw its operational capabilities at normal publication, then release the old snapshot. Keep the manager and diagnostic. |
|
|
4278
|
+
| Configuration source is corrected or removed | Inspection resolves the current source immediately, even in the repairing turn; a previous source-resolution error cannot override a successful read. Not-yet-published definitions remain dormant. Normal publication restores capabilities; inspection does not activate them. |
|
|
4279
|
+
| Preparation configuration is corrected | The published preparation failure remains until ordinary preparation succeeds. Successful source inspection alone does not establish runtime readiness. Environment-file edits follow their ordinary process lifetime, not an implicit reload. |
|
|
4280
|
+
| Internal invariant, state, or implementation failure | Preserve the exception; never reclassify it as an operator configuration error. |
|
|
4281
|
+
|
|
4282
|
+
Client discovery and passive synchronization report startup diagnostics through the
|
|
4283
|
+
existing Notice channel, even without a usable model. The first turn of a drain, and a changed diagnostic
|
|
4284
|
+
thereafter, reports unresolved configuration to both client and model. An
|
|
4285
|
+
unchanged diagnostic is not repeated every turn. Operation failures remain Problems.
|
|
4286
|
+
|
|
4287
|
+
§functionality-inspection **Inspection is not demand.** `list` and `discover` do not
|
|
4288
|
+
acquire residency, join preparation, reconcile worker documents, or extend warm
|
|
4289
|
+
retention. An enabled definition no resident publication has prepared is `dormant`: every one while
|
|
4290
|
+
the family is cold, and one that arrived or changed out of band until the next turn publishes it
|
|
4291
|
+
({§functionality-hotload}). During replacement the preceding publication remains authoritative;
|
|
4292
|
+
the candidate is never presented as active. A published outcome belongs to the
|
|
4293
|
+
complete definition prepared, not merely its alias. Cooling leaves durable definitions
|
|
4294
|
+
inspectable. Mutations and protocol continuations retain their residency rules.
|
|
4295
|
+
|
|
4296
|
+
§functionality-preparation-visibility **Preparation is workspace activity, not
|
|
4297
|
+
model context.** The coordinator owns a current `FunctionalityPreparationActivity`
|
|
4298
|
+
per preparing family. `workspacePreparationStatus(workspaceId)` returns that same
|
|
4299
|
+
state without demand; `workspace/preparation` broadcasts
|
|
4300
|
+
`{ workspaceId, preparation: [...] }` whenever it changes.
|
|
4301
|
+
|
|
4302
|
+
| Boundary | Visible state |
|
|
4303
|
+
|---|---|
|
|
4304
|
+
| Preparation begins | Family, `phase: preparing`, `alias: null`, UTC `since` |
|
|
4305
|
+
| Adapter calls `progress(alias)` | Enabled alias being prepared; a fresh `since` |
|
|
4306
|
+
| Prepared candidate enters publication | `phase: publishing`, `alias: null` |
|
|
4307
|
+
| Commit, rejection, or rollback settles | Family removed; empty array means no preparation |
|
|
4308
|
+
|
|
4309
|
+
Preparation reports neither definitions nor credentials, does not alter the
|
|
4310
|
+
publication contract, and creates no log entries or model Notices. Published
|
|
4311
|
+
failures retain their exact Problems in `list`. Concurrent consumers share the
|
|
4312
|
+
workspace activity; a client disconnect does not clear another consumer's work.
|
|
4110
4313
|
|
|
4111
4314
|
§functionality-adapter **An adapter owns protocol truth.** It declares its
|
|
4112
4315
|
family, namespace owner, definition schema, contributed defaults, discovery,
|
|
@@ -4116,16 +4319,66 @@ family declares, at admission, in the service projection, and on persisted
|
|
|
4116
4319
|
state, so an environment variable's name is an alias exactly as a skill name
|
|
4117
4320
|
is. Admission distinguishes explicit client
|
|
4118
4321
|
actions from model operations where the family contract requires it
|
|
4119
|
-
({§members-model-scope}). Preparation
|
|
4322
|
+
({§members-model-scope}). Preparation receives each complete definition with optional
|
|
4323
|
+
adapter-owned interpretation context. Context is source semantics, not policy or provenance;
|
|
4324
|
+
it participates in runtime identity and hot-load comparisons, is never projected as configuration
|
|
4325
|
+
or persisted into a workspace override, and cannot survive replacement by a local definition.
|
|
4326
|
+
Removing that override restores the current inherited definition and context together.
|
|
4327
|
+
Descriptive provenance alone does not change runtime identity. Preparation returns runtimes, documents, per-alias
|
|
4120
4328
|
outcomes, and a snapshot with `commit`/`abort`. Successful publication commits;
|
|
4121
4329
|
failure aborts; cooling tears down. Protocol continuations remain ordinary
|
|
4122
4330
|
module actions. Optional `forget` releases an installed or provisioned
|
|
4123
|
-
definition before removal; failure rejects removal
|
|
4331
|
+
definition before removal; failure rejects removal. The
|
|
4124
4332
|
seam's shapes — the identity a verb acts under, its options, definition
|
|
4125
4333
|
sources, outcomes, preparation, the prepared result and the family handle —
|
|
4126
4334
|
are declared once in `plurnk-contracts` and imported by core and every
|
|
4127
4335
|
module; core adds only its own face of the seam, the runtime registration a
|
|
4128
4336
|
resident family prepares and the scheme facet it may expose.
|
|
4337
|
+
An adapter may expose current partial-source `configurationNotices`; these join the ordinary
|
|
4338
|
+
workspace diagnostics without preventing independently valid definitions from preparing.
|
|
4339
|
+
|
|
4340
|
+
§functionality-hotload **Out-of-band state is admitted before the next turn.** An adapter whose
|
|
4341
|
+
`available` reads state that changes outside the daemon, such as skill roots ({§skills-hotload}) or
|
|
4342
|
+
installed plugins ({§agent-plugins-hosting}), implements `refreshIfChanged`. Turn admission calls it
|
|
4343
|
+
for every family under the workspace gate before packet assembly. The family handle's `refresh` with
|
|
4344
|
+
`ifChanged` republishes a resident family only when the enabled definitions it would prepare differ
|
|
4345
|
+
from the ones its publication prepared. The coordinator makes that comparison because it alone knows
|
|
4346
|
+
what it published, so a change `list` saw first is still published at the next turn. A family whose
|
|
4347
|
+
definitions do not capture its published content, such as a skill's files, republishes
|
|
4348
|
+
unconditionally when that content changed. An unchanged family dispatches nothing.
|
|
4349
|
+
|
|
4350
|
+
§agent-plugins-hosting **Installed Agent Plugins are found like skills.** A workspace's plugins are
|
|
4351
|
+
the immediate child directories of its project's `.agents/plugins`, then
|
|
4352
|
+
`$XDG_CONFIG_HOME/plurnk/plugins` (plurnk alone), then `~/.agents/plugins` (every agent), loaded and
|
|
4353
|
+
validated by `@plurnk/plurnk-agent-plugins` ({§agent-plugins-roots}), followed by standard plugin
|
|
4354
|
+
bundles in the installed npm graph. An earlier source shadows a later plugin of the same manifest
|
|
4355
|
+
name, regardless of distribution or directory name. Native discovery uses that same cascade with
|
|
4356
|
+
the project root omitted; workspace discovery includes it. A plugin's `PLUGIN_DATA` is
|
|
4357
|
+
`$XDG_DATA_HOME/plurnk/plugins/<name>/<sha256(canonical-root)>`, kept across in-place updates and
|
|
4358
|
+
moved with a state root ({§state-root}). Distinct installations never share data by name alone;
|
|
4359
|
+
workspaces referencing the same canonical installation share its data. Modules receive a workspace's plugins,
|
|
4360
|
+
in precedence order, through the setup seam's `readWorkspacePlugins`, with one signature that changes
|
|
4361
|
+
exactly when a plugin, its manifest, its MCP configuration, or its skills change
|
|
4362
|
+
({§functionality-hotload}), and the roots the workspace has. This read-only source loader does
|
|
4363
|
+
not install or delete plugins. MCP's own lifecycle is independent ({§mcp-definitions}).
|
|
4364
|
+
|
|
4365
|
+
Portable components use each family's existing management and publication path. Within a source
|
|
4366
|
+
scope, standalone definitions precede bundled components; nearer scopes precede farther scopes,
|
|
4367
|
+
with npm last. Complete environment definitions override those inputs, followed by workspace
|
|
4368
|
+
definitions and enabledness. Inspection names the winning component file as plugin provenance.
|
|
4369
|
+
Removing a workspace override restores inheritance; it never deletes the installed bundle.
|
|
4370
|
+
Current plugin-source diagnostics join the workspace's configuration notices before inference.
|
|
4371
|
+
|
|
4372
|
+
§agent-roots **A daemon reads the roots `PLURNK_SERVICE_ROOTS` names.** A comma list drawn from
|
|
4373
|
+
`project`, `plurnk` and `global`, nearest first, selects which Agent Skills, Agent Plugins and standalone MCP roots a
|
|
4374
|
+
daemon reads; the default names all three. The real-model gate profile selects
|
|
4375
|
+
its discovery roots explicitly ({§operator-config-real-model-profile}), so
|
|
4376
|
+
operator roots do not shape a gate. Root selection does not restrict explicit
|
|
4377
|
+
source definitions. Skills mutations change workspace bindings, not these roots
|
|
4378
|
+
({§skills-functionality}); MCP mutations likewise remain workspace-owned. The module setup seam's
|
|
4379
|
+
`workspaceConfigurationDirectories` supplies selected `<project>/.agents`,
|
|
4380
|
+
`$XDG_CONFIG_HOME/plurnk`, and `~/.agents` directories in precedence order, omitting
|
|
4381
|
+
project when no project is bound. Modules own their file formats; core owns discovery roots.
|
|
4129
4382
|
|
|
4130
4383
|
An adapter may expose a `scheme` facet beneath its family's runtime namespace
|
|
4131
4384
|
({§runtime-resource-binding}). A facet claims a path subtree and is the scheme's
|
|
@@ -4145,9 +4398,10 @@ operator's ceiling ({§exec-env-scoped}, service origin) precede workspace defau
|
|
|
4145
4398
|
worker overrides ({§workspace-env}). `add` takes
|
|
4146
4399
|
the name as the alias and `{ "value": "…" }` as the definition, used verbatim with no
|
|
4147
4400
|
interpolation. `disable` withholds a name in the selected scope while retaining it;
|
|
4148
|
-
`remove` forgets a locally-owned entry and
|
|
4149
|
-
|
|
4150
|
-
|
|
4401
|
+
`remove` forgets a locally-owned entry and restores any same-name inherited value
|
|
4402
|
+
and enabledness ({§configuration-definition-resolution}). Use `disable` to keep
|
|
4403
|
+
an inherited name out of subsequent launches. Definitions from a lower layer
|
|
4404
|
+
cannot be removed in the current scope.
|
|
4151
4405
|
|
|
4152
4406
|
`list` projects effective values with their origin. Values are shown: the ceiling is the security
|
|
4153
4407
|
boundary, not the projection, and any admitted name is already readable by every command the
|
|
@@ -4550,12 +4804,14 @@ adding a loop to it. LOOK text anchors resolve through the same
|
|
|
4550
4804
|
|
|
4551
4805
|
| Event | Payload | When fired |
|
|
4552
4806
|
|--------------------------------------------------------------|---------|------------|
|
|
4807
|
+
| §notifications-operation-event `operation/event` | `ApplicationOperationEvent`: `{ workerId, loopId, turnId, sequence, origin, projectRoot, statement, phase, result? }` | One admitted dispatch starts before capability/proposal admission and settles after its durable receipt(s). Only the settled phase has `result`, the exact returned operation result. READ fan-out is one dispatch; BARE starts before prompt preparation and settles after ordered receipt persistence. Automatic stream observations and log writes are not dispatches. An asynchronous executor's successful dispatch does not assert process exit. An internal exception that prevents settlement has no invented result event. |
|
|
4553
4808
|
| §notifications-log-entry-notify `log/entry` | `{ entry: LogEntry }` | A non-proposed `log_entries` row is committed, or a proposed row reaches terminal settlement under {§proposal-proposed-hidden}. Delivery completes before a later event may terminate the owning Loop. |
|
|
4554
4809
|
| §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. |
|
|
4555
4810
|
| §notifications-loop-packet `loop/packet` | `{ workerId, loopId, packetCount }` | One provider packet becomes durable. `packetCount` is the exact count of packet-bearing turns in that Loop; packetless producer turns and physical provider retries never contribute. |
|
|
4556
4811
|
| §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}. |
|
|
4557
4812
|
| §notifications-loop-interaction `loop/interaction` | contracts-owned `ClientInteractionProjection` | An operation is paused on client input. Live delivery and reconnect discovery share {§client-interactions}; workspace scope remains the event envelope. |
|
|
4558
4813
|
| §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
|
|
4814
|
+
| `workspace/preparation` | `{ workspaceId, preparation: FunctionalityPreparationActivity[] }` | Workspace capability preparation changes; snapshot and clearing semantics follow {§functionality-preparation-visibility}. |
|
|
4559
4815
|
| §notifications-stream-event-on-channel-change `stream/event` | `{ entryId, workerId, target, channel, state, contentLength, mimetype?, loop_seq?, turn_seq?, sequence? }` | Channel content grows or channel state transitions. `workerId` is the initiating actor used for conversation routing, never entry ownership or access control. `target` is the canonical resource URI. Optional numeric coordinates identify the causal log item, independently of that URI. Core-managed channel writes include the current stored `mimetype`, which may change per call ({§channel-mimetype}); the generic plugin notification capability does not require it. It carries metadata, not content; consumers read bytes by canonical workspace address. |
|
|
4560
4816
|
| §notifications-stream-concluded `stream/concluded` | `{ entryId, workerId, target, subscriptionId, scheme, result, summary, wakeAction, loop_seq?, turn_seq?, sequence? }` | A subscription closes. `workerId` identifies the initiating actor; `target` is the canonical resource URI. Optional numeric fields identify the causal log item, never parsed from `target`. Exact result truth is preserved. `wakeAction` reports `wake-pending` before settlement, `no-op-active-loop` when work is already executing, `no-loop`, or `skipped-aborted`/`skipped-cancelled` for an aborted worker scope. A pending wake predicts neither execution nor recipient count; subsequent ordinary loop events report actual progress and completion. |
|
|
4561
4817
|
| §notifications-notice-event `notice/event` | `{ workerId, loopId, notice: Notice }` | A transient observation or progress notice occurs. `workerId` owns loop activity; only workspace derivation progress uses `null` with `loopId=0`. It cannot alter durable history, scheduling, recovery, or model-visible failure truth. |
|
|
@@ -4618,16 +4874,17 @@ The packet reaches the provider as a transcript, under the roles the model was t
|
|
|
4618
4874
|
| Message | Role | Content |
|
|
4619
4875
|
|:--|:--|:--|
|
|
4620
4876
|
| 1 | `system` | the system slot, as rendered |
|
|
4621
|
-
| 2, 4, … | `user` | the user slot's text up to and including the next placed emission
|
|
4622
|
-
| 3, 5, … | `assistant` | that row's emission, as the worker's own message |
|
|
4623
|
-
| last | `user` | the records after the last
|
|
4877
|
+
| 2, 4, … | `user` | the user slot's text up to and including the record of the next placed emission that delivers text ({§emission-row}); the first opens with `## Log` |
|
|
4878
|
+
| 3, 5, … | `assistant` | that row's frozen emission projection less its NOTE and WAIT blocks, as the worker's own message ({§emission-row}) |
|
|
4879
|
+
| last | `user` | the records after the last emission delivered, then the remaining user sections in {§packet-cache-monotone} order; native parts ride here ({§packet-attachment-parts}) |
|
|
4624
4880
|
|
|
4625
4881
|
Only role boundaries are added: joined by blank lines, the user messages are the user slot's
|
|
4626
4882
|
bytes, in record order. An emission is placed exactly when its row is present in the final log
|
|
4627
4883
|
section, so curation governs the transcript: a KILLed emission row, or one a trusted transform
|
|
4628
4884
|
removed ({§packet-plugin-transform}), takes its emission with it, and a log without emission rows
|
|
4629
|
-
is one user message.
|
|
4630
|
-
|
|
4885
|
+
is one user message. An emission of only NOTE and WAIT delivers nothing, so its record runs on
|
|
4886
|
+
into the next user message. The Worker block and the status clump always follow the log, so a
|
|
4887
|
+
request never ends on an emission, and the projection refuses one that would. The digest's packet
|
|
4631
4888
|
artifacts record the sections, and `.wire.json` the messages ({§share-packet-names}).
|
|
4632
4889
|
|
|
4633
4890
|
### §packet-cache-monotone Default order and cache locality
|
|
@@ -4936,7 +5193,7 @@ source independently from this optional model-exchange record; a request-only
|
|
|
4936
5193
|
turn receives a note instead of a fabricated response.
|
|
4937
5194
|
|
|
4938
5195
|
§digest-turn-artifact-identity **Digest packet artifacts project durable turns.**
|
|
4939
|
-
After selectors are applied, digest retains every turn with exact
|
|
5196
|
+
After selectors are applied, digest retains every turn with exact content or reasoning source, a
|
|
4940
5197
|
valid stored provider request, or malformed stored packet evidence; orders those
|
|
4941
5198
|
turns by durable chronology; and names each by its log coordinate ({§share-packet-names}). The
|
|
4942
5199
|
producer does not affect projection.
|
|
@@ -4951,6 +5208,7 @@ consumer reconstructs a name. A name that cannot be a file name, or two turns sh
|
|
|
4951
5208
|
| Artifact | Present when | Authority |
|
|
4952
5209
|
|----------|--------------|-----------|
|
|
4953
5210
|
| `<stem>.assistant.md` | The turn has an `ops` source | Exact `turn_sources.content`, independent of log rows |
|
|
5211
|
+
| `<stem>.reasoning.md` | The turn has a `reasoning` source | Exact `turn_sources.content`, without relabeling it as content |
|
|
4954
5212
|
| `<stem>.system.md`, `<stem>.user.md` | The turn stored a provider request | Stored text sections projected through `PacketWire`; native parts are not Markdown |
|
|
4955
5213
|
| `<stem>.wire.json` | The turn stored a provider request | The request's text messages in order, its worker's emission rows placed ({§packet-wire-envelope}); `<stem>.wire.invalid.json` names a stored log that cannot be projected |
|
|
4956
5214
|
| `digest.json` turn `attachments` | Every turn | Stored native attachment descriptors; `[]` means a request without attachments, `null` means no valid stored request. Selection is not proof of provider acceptance. |
|
|
@@ -4959,8 +5217,8 @@ consumer reconstructs a name. A name that cannot be a file name, or two turns sh
|
|
|
4959
5217
|
| `<stem>.packet.raw.txt` | The stored packet fails typed validation | Exact stored packet text |
|
|
4960
5218
|
| `<stem>.packet.invalid.json` | The stored packet fails typed validation | Turn identity and complete validation error chain |
|
|
4961
5219
|
|
|
4962
|
-
A source-backed turn without provider participation
|
|
4963
|
-
|
|
5220
|
+
A source-backed turn without provider participation produces only its source-channel
|
|
5221
|
+
artifacts; a request-only turn produces no fabricated assistant. A
|
|
4964
5222
|
source-less programmatic turn with no provider request has no forensic payload
|
|
4965
5223
|
to project and writes no files.
|
|
4966
5224
|
|
|
@@ -5074,7 +5332,7 @@ ordered set exactly once. Recovery retries complete the same queued loop and nev
|
|
|
5074
5332
|
mint duplicate work. Output withholding preserves readable arrival rows; explicit
|
|
5075
5333
|
KILL follows the ordinary log contract.
|
|
5076
5334
|
|
|
5077
|
-
§completion-defers-to-messages **Conclusion does not cross an
|
|
5335
|
+
§completion-defers-to-messages **Conclusion does not cross an unobserved arrival.** The end-of-program check includes messages that arrived during inference. The final database transition atomically requires every inbox message to be published and the loop's observed wake revision to equal its worker's current revision. Answer delivery is not this barrier. An arrival that wins the race continues the current loop; one admitted after conclusion belongs to a new loop. Orphan recovery preserves messages accepted before an independently forced termination.
|
|
5078
5336
|
|
|
5079
5337
|
§packet-catalog **Catalogs are query results, not packet state.** The packet
|
|
5080
5338
|
stores no materialized manifest. Complete and one-level entry directories,
|
|
@@ -5172,7 +5430,21 @@ retain distinct contracts and lifetimes.
|
|
|
5172
5430
|
|
|
5173
5431
|
§digest-wire-line **Wire health aggregated.** Each worker summary renders a `Wire:` line — total physical provider requests, error-outcome count, and the error percentage when nonzero. Provider-level failures are absorbed by retries below the packet stream, so without this aggregate a rate-limit storm is invisible in every summary while the model's experience stays clean.
|
|
5174
5432
|
|
|
5175
|
-
§digest-cache-ledger **
|
|
5433
|
+
§digest-cache-ledger **Measured cache reuse and estimated prompt overlap are separate.**
|
|
5434
|
+
|
|
5435
|
+
| Projection | Meaning |
|
|
5436
|
+
|---|---|
|
|
5437
|
+
| `digest.json` provider-request `cachedTokens`, `inputTokens` | Exact provider-reported cache reads and input tokens; absent quantities remain `null`, reported zero remains zero. Every physical request counts, including retries and first requests of new loops. Stored packet availability is irrelevant to these counters. |
|
|
5438
|
+
| Turn `cache=<cached>/<input>` | Sum each measured quantity over that turn's requests. If any request omits a quantity, that sum is `?`. |
|
|
5439
|
+
| Workspace `Cache: <cached> of <input> reported input tokens read from cache (<pct>%) over <n> requests` | Sum only requests reporting both counters. Percentage is cache reads / input, rounded to one decimal; zero input is `n/a`. Requests missing either counter are counted separately as `missing input or cache usage (excluded)`. |
|
|
5440
|
+
| `digest.json` provider-request `adjacentPrefixTokensEstimate` | Optional loop-local diagnostic: the longest common character prefix with the preceding request, weighted under {§tokenomics-agnostic-ruler} as a share of the current stored prompt, multiplied by reported input tokens. First request: `0`; missing current/preceding packet or current input: `null`. Empty prompts have zero overlap. |
|
|
5441
|
+
|
|
5442
|
+
The prefix estimate uses the stored emission packet's wire message order, roles
|
|
5443
|
+
and content. A BARE request's input is not that packet; its prefix estimate and
|
|
5444
|
+
the following request's comparison are unknown. The estimate is
|
|
5445
|
+
neither provider tokenization nor a cache ceiling, and never supplies a cache-ratio
|
|
5446
|
+
denominator. Caching across loops or against other provider-resident prefixes
|
|
5447
|
+
does not make the measured counters inconsistent.
|
|
5176
5448
|
|
|
5177
5449
|
§digest-edit-census **Every model EDIT by the form it authored, how it landed, and whether it came back.** For each worker the digest reads every model-authored EDIT row and classifies the form from the row's stored marker and the durable statement's pattern: `hash` (one anchor), `line` (one line number), `range` (two marks), `insert` (the zero-width `<L,1,L,1>` form, {§zero-width-column-one-insert}), `column` (any other four-mark region), `prepend` / `append` (`<0>` / `<-1>`), `offset` (a tolerated anchor offset, {§anchor-offset}), `pattern` (a selection matcher), `whole` (no marker: a creation when it lands 201). It counts the EDITs, those refused (status ≥ 400), and the *revisits*: an EDIT of a path the same worker had edited within its previous two model turns — the shape of a repair without the claim of one. Each worker summary renders `EDITs: <n> · <form>=<count>… · refused=<k> · revisits=<r>` (`(no edits)` for none); `digest.json` carries the census as `edit_census` on every worker and stamps every EDIT log entry with its `edit_form` and `edit_revisit`. A form is a fact about what was written, never about intent; the bench sheet reads the counts as friction and leaves the judgement to the reader.
|
|
5178
5450
|
|
|
@@ -5293,8 +5565,10 @@ enable | disable | remove`, `workspace.members.<verb>` for the client,
|
|
|
5293
5565
|
```` ```members (<verb>) ```` for the model — for what the model may see, exactly as they do for skills and
|
|
5294
5566
|
MCP servers. A definition is one gitignore-style glob, `{ glob }`, relative to the project
|
|
5295
5567
|
root; a leading `!` excludes matching members, and an exclusion wins over every inclusion.
|
|
5296
|
-
|
|
5297
|
-
rides the definition
|
|
5568
|
+
Admission provenance (`service-configuration`, `client-action`, `model-proposal`)
|
|
5569
|
+
rides the definition to enforce {§members-model-scope}; configuration-source
|
|
5570
|
+
provenance belongs to the shared inspection projection ({§configuration-provenance}).
|
|
5571
|
+
The alias is a short name, suggested from the glob. `list` shows each
|
|
5298
5572
|
definition with what it resolved to — `include` or `exclude`, the pattern, the members it
|
|
5299
5573
|
admits or removes (count and a bounded sample), and for a model's inclusion the matches the
|
|
5300
5574
|
repository's ignore rules refused — so the model sees what its glob did and adapts.
|
|
@@ -5303,10 +5577,12 @@ repository's ignore rules refused — so the model sees what its glob did and ad
|
|
|
5303
5577
|
untracked, absent); a glob previews what `add` would include or exclude. Names only, never
|
|
5304
5578
|
content; nothing is added.
|
|
5305
5579
|
|
|
5306
|
-
§members-configuration *Available definitions.*
|
|
5307
|
-
(`!glob` excludes)
|
|
5308
|
-
|
|
5309
|
-
|
|
5580
|
+
§members-configuration *Available definitions.* `PLURNK_MEMBERS_<alias>=<glob>`
|
|
5581
|
+
declares one service-origin rule (`!glob` excludes). The shared naming and
|
|
5582
|
+
enabledness dialect is {§resource-environment}; `PLURNK_MEMBERS_ENABLED` supplies
|
|
5583
|
+
the panel default and `PLURNK_MEMBERS_<alias>_ENABLED` overrides one rule.
|
|
5584
|
+
An empty glob or a bare `!` fails validation. Controls may precede their rule;
|
|
5585
|
+
they are validated without creating a definition ({§resource-environment}).
|
|
5310
5586
|
|
|
5311
5587
|
§members-model-scope *The model's authority.* A model's `add` is admitted against
|
|
5312
5588
|
`PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` in the file-creation lattice `none < root <
|
|
@@ -5338,53 +5614,98 @@ verbs are these verbs.
|
|
|
5338
5614
|
§skills-functionality **Agent Skills are one workspace Functionality family.**
|
|
5339
5615
|
Core registers the `skills` family with the coordinator ({§functionality-coordinator});
|
|
5340
5616
|
its adapter owns protocol truth for standard Agent Skills and nothing else. A
|
|
5341
|
-
definition is `SkillDefinition
|
|
5342
|
-
`
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5617
|
+
definition is `SkillDefinition`: the standard skill `name`, its `source`, and
|
|
5618
|
+
optional Git `ref`/resolved `commit` ({§skills-sources}). Only a host-provided
|
|
5619
|
+
resource tree omits `source`. Source location is not mutation ownership: standard
|
|
5620
|
+
project/global roots are read-only configuration inputs; live changes belong to
|
|
5621
|
+
the workspace. Plurnk neither installs into nor deletes from those roots.
|
|
5346
5622
|
|
|
5347
5623
|
*Available definitions.* The filesystem is the only truth about installation:
|
|
5348
|
-
every `<root>/<name>/SKILL.md` directory under the project then
|
|
5349
|
-
root is one service-origin definition,
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
({§functionality-state}); a disabled skill stays client-visible and
|
|
5353
|
-
model-facing trace.
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
skill
|
|
5367
|
-
|
|
5624
|
+
every `<root>/<name>/SKILL.md` directory under the project, plurnk, then global
|
|
5625
|
+
root is one service-origin definition, a nearer root shadowing a farther one and
|
|
5626
|
+
all of them shadowing host-provided trees by name. Each filesystem definition
|
|
5627
|
+
names its actual source directory. The workspace's durable state owns
|
|
5628
|
+
enablement ({§functionality-state}); a disabled skill stays client-visible and
|
|
5629
|
+
leaves no model-facing trace.
|
|
5630
|
+
|
|
5631
|
+
§skills-configuration **Skills use the shared definition cascade.**
|
|
5632
|
+
|
|
5633
|
+
| Layer, low to high | Definition source |
|
|
5634
|
+
|---|---|
|
|
5635
|
+
| Service | Host-provided trees |
|
|
5636
|
+
| Standard locations | Global, plurnk, then project roots selected by {§agent-roots} |
|
|
5637
|
+
| Cascading environment | `PLURNK_SKILLS_<name>={"name":"<name>","source":"…","ref"?:"…"}` replaces the complete definition |
|
|
5638
|
+
| Live workspace | `skills (add)` creates a workspace definition through {§functionality-coordinator} |
|
|
5639
|
+
|
|
5640
|
+
`PLURNK_SKILLS_ENABLED` supplies default enabledness; `<name>_ENABLED` overrides
|
|
5641
|
+
it independently ({§resource-environment}). The decoded environment alias must
|
|
5642
|
+
equal the standard skill name, including digit-leading and Unicode names.
|
|
5643
|
+
Blank definitions are invalid; controls may precede a definition. Environment
|
|
5644
|
+
validation checks shape, names, remote URL rules, and Git-only `ref` without
|
|
5645
|
+
fetching or opening a source; `commit` is service-recorded, not an input.
|
|
5646
|
+
|
|
5647
|
+
*Discovery is inert.* `discover {source}` lists the standard skills one source
|
|
5648
|
+
carries, each a candidate with `source` provenance and the exact definition to
|
|
5649
|
+
add; it never installs, persists, or enables. Agent Skills have no standard
|
|
5650
|
+
registry, so `discover {query}` is 400 `query-unsupported`, naming the source
|
|
5651
|
+
forms. Client `configuration` contributes nothing and is refused with 400.
|
|
5652
|
+
|
|
5653
|
+
*Admission.* `add {alias, definition}` requires `alias = name` and a `source`;
|
|
5654
|
+
the workspace definition may shadow a service skill of the same name. Relative
|
|
5655
|
+
sources require a project root; absolute sources work in headless workspaces.
|
|
5656
|
+
A local source is recorded as its absolute path.
|
|
5657
|
+
A git source records the `commit` its `ref` names at admission, or its default
|
|
5658
|
+
branch's when no `ref` is given; `ref` belongs to git sources, and a supplied
|
|
5659
|
+
`commit` is refused because the service records it. The family's aliases use
|
|
5660
|
+
the standard skill-name grammar ({§agent-skills-name}), including digit-leading
|
|
5661
|
+
and Unicode names, rather than the coordinator's generic default.
|
|
5368
5662
|
|
|
5369
5663
|
*Preparation.* For each enabled alias the adapter selects the host-provided
|
|
5370
|
-
tree
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5664
|
+
tree or resolves the complete source definition ({§skills-sources}). Local
|
|
5665
|
+
folders and their `SKILL.md` files remain live references, including supporting
|
|
5666
|
+
resources and symlink retargeting. Git/archive sources are materialized only
|
|
5667
|
+
inside {§module-workspace-directory}; different workspaces and complete source
|
|
5668
|
+
definitions cannot reuse each other's materializations accidentally.
|
|
5669
|
+
The first fetched Git/archive copy remains stable across enable, cooling, and
|
|
5670
|
+
restart until the complete source definition changes. In particular, an
|
|
5671
|
+
operator-configured symbolic Git ref is not an implicit update subscription.
|
|
5377
5672
|
Each admitted skill requires standard `name` and `description` frontmatter
|
|
5378
5673
|
with `name` matching its directory. A missing, uninstallable, or invalid skill
|
|
5379
|
-
is `unavailable` with its exact Problem (
|
|
5380
|
-
|
|
5381
|
-
|
|
5674
|
+
is `unavailable` with its exact Problem ({§problems-functionality}) under the
|
|
5675
|
+
coordinator's failure policy ({§functionality-model-mutation}); one bad skill
|
|
5676
|
+
never fails the family.
|
|
5677
|
+
|
|
5678
|
+
Removal follows {§skills-remove}.
|
|
5382
5679
|
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5680
|
+
§skills-sources **A source is a git remote, a folder, or a file, read with standard tools.**
|
|
5681
|
+
Fetching runs nothing it fetched. Materialized copies cannot contain references
|
|
5682
|
+
outside their skill; live resources retain {§agent-skills-directory} containment.
|
|
5683
|
+
|
|
5684
|
+
| Source | How it is read |
|
|
5685
|
+
|---|---|
|
|
5686
|
+
| Git remote: a full `https://` or `ssh://` URL, or `user@host:path` | `git ls-remote` resolves the ref at admission; preparation shallow-clones it with hooks and submodules off, and a checkout at any other commit is 409 `source-moved` |
|
|
5687
|
+
| Folder: absolute, `~/`, or relative to the project root | Live reference; standard directory/name matching applies |
|
|
5688
|
+
| A file named `SKILL.md` | Live reference to its skill directory and supporting resources |
|
|
5689
|
+
| A `.zip`, `.tar`, `.tgz`, `.tar.gz`, `.tar.bz2`, `.tar.xz`, or `.tar.zst` archive | Unpacked into private staging with `unzip` or `tar`; a lone top-level directory is the source's root |
|
|
5690
|
+
|
|
5691
|
+
Any other scheme, plain `http`, `owner/repo` shorthand, and an https URL carrying
|
|
5692
|
+
credentials are refused with `source-invalid` or `source-missing`: shorthand names no
|
|
5693
|
+
forge, and a recorded source is listed to every client. Git runs with the
|
|
5694
|
+
operator's configuration, credentials, and SSH agent, never plurnk's secrets, and
|
|
5695
|
+
never prompts; `PLURNK_SERVICE_SKILLS_FETCH_TIMEOUT_MS` bounds each fetch. The
|
|
5696
|
+
retired vendor-installer knobs (`PLURNK_SERVICE_SKILLS_CLI`, `_CLI_TIMEOUT_MS`,
|
|
5697
|
+
`_REGISTRY_URL`, `_REGISTRY_LIMIT`, `_REGISTRY_TIMEOUT_MS`) make the skills family
|
|
5698
|
+
unavailable when set, each naming what replaced it ({§configuration-repair-path}).
|
|
5699
|
+
|
|
5700
|
+
A source's skills are the directories holding a `SKILL.md`, found by walking
|
|
5701
|
+
from its root without entering `.git` or a skill already found. A fetched skill at
|
|
5702
|
+
the root is named by its frontmatter ({§agent-skills-name}); local references and
|
|
5703
|
+
directories below the root use the standard folder rule. A source with `plugin.json` at its root is an
|
|
5704
|
+
Agent Plugin and is refused with 422 `source-is-plugin`, so its skills keep the
|
|
5705
|
+
plugin's identity. Materialization copies the named skill beside its workspace-owned destination
|
|
5706
|
+
and renames it to `<root>/<name>`; a copy that holds a link out of the skill, or
|
|
5707
|
+
anything but files, directories, and inward links, is refused with 422
|
|
5708
|
+
`source-unsafe` and leaves nothing behind.
|
|
5388
5709
|
|
|
5389
5710
|
§skills-resources **A skill is a resource tree, not a rewritten document.**
|
|
5390
5711
|
The family exposes enabled, available {§agent-skills-tree} sources through
|
|
@@ -5401,13 +5722,13 @@ serialized URI address the same resource, not separate skill identities.
|
|
|
5401
5722
|
| `READ (skill://<name>/SKILL.md)` | Original frontmatter and Markdown, unchanged; relative links remain relative to the source layout. |
|
|
5402
5723
|
| `READ` / `FIND` below the authority | References, scripts, and assets retain their source paths and ordinary pattern, channel, byte, and multimodal semantics. Acquisition observes current source contents, including disappearance. |
|
|
5403
5724
|
| ```` ```runtime (skill://<name>/scripts/program.ext) ```` | Ordinary resource execution and proposal policy; preserve the native file and its siblings under {§exec-source-temporary}. Discovery and READ never execute scripts. |
|
|
5404
|
-
| Model mutation | Read-only; no EDIT, SEND, or KILL of
|
|
5725
|
+
| Model mutation | Read-only; no EDIT, SEND, or KILL of skill resources. Manage definitions and enablement through ```` ```skills ````. |
|
|
5405
5726
|
| Disable / unavailable / remove | Withdraw the authority from new resource access and discovery. Existing log receipts remain historical evidence. |
|
|
5406
5727
|
| WORK / FORK | Use the same workspace Functionality, not copied definitions or resource caches. |
|
|
5407
5728
|
|
|
5408
5729
|
Explicit skill URIs address these resources; bare operation paths still address
|
|
5409
5730
|
project files, with no implicit current-skill directory. Source resolution follows
|
|
5410
|
-
{§agent-skills-directory}, including
|
|
5731
|
+
{§agent-skills-directory}, including symlinked skill directories and containment of references.
|
|
5411
5732
|
An uninstalled Git skill is not manufactured by repository detection.
|
|
5412
5733
|
|
|
5413
5734
|
§plurnk-skill **Plurnk's own reference is an ordinary service-provided skill.**
|
|
@@ -5420,24 +5741,22 @@ same {§operator-config-env-defaults} renderer as the operator command, never th
|
|
|
5420
5741
|
effective environment. Native chapter files retain their owners and locations;
|
|
5421
5742
|
runtime-generated bytes have no invented disk location. Disable/enable,
|
|
5422
5743
|
shared workspace visibility, and project/global shadowing use the ordinary Skills lifecycle.
|
|
5423
|
-
Service-provided skills are not installer targets;
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
coordinator
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
installed or removed by any other tool is discoverable in the first subsequent
|
|
5440
|
-
model turn while an unchanged set dispatches nothing. The model manages skills
|
|
5744
|
+
Service-provided skills are not installer targets; removal follows {§skills-remove}.
|
|
5745
|
+
|
|
5746
|
+
§skills-remove **`remove` forgets the workspace binding, not the source.**
|
|
5747
|
+
It withdraws that definition and restores any inherited definition and enabledness
|
|
5748
|
+
({§functionality-coordinator}). External folders are never deleted. Fetched
|
|
5749
|
+
materializations remain workspace-owned operational state, reusable only for the
|
|
5750
|
+
same complete definition; they confer no availability without a definition.
|
|
5751
|
+
Inherited definitions cannot be removed here; their enabledness can be overridden.
|
|
5752
|
+
|
|
5753
|
+
§skills-hotload **Skills placed out of band are admitted at the next turn** ({§functionality-hotload}). The
|
|
5754
|
+
family keeps one signature of the discovered roots, configured definitions, source locations, and `SKILL.md` sources
|
|
5755
|
+
per resident workspace, read before a publication loads the skills it describes. Turn admission
|
|
5756
|
+
recomputes it under the workspace gate before packet assembly: a changed signature republishes the
|
|
5757
|
+
family, and an unchanged one republishes only when the skills the coordinator would publish differ
|
|
5758
|
+
from the published ones. A skill installed, edited or removed by any other tool is therefore
|
|
5759
|
+
discoverable in the first subsequent model turn, while an unchanged set dispatches nothing. The model manages skills
|
|
5441
5760
|
only through the generated ```` ```skills ```` family
|
|
5442
5761
|
({§functionality-model-projection}); it is never taught a package manager.
|
|
5443
5762
|
|
|
@@ -5896,6 +6215,7 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5896
6215
|
|---|---:|---|
|
|
5897
6216
|
| `service-starting` | 503 | The PLURNK service owns this listener but has not admitted its client interface yet. |
|
|
5898
6217
|
| `configuration-unsupported` | 400 | Environment discovery reads this installation's declared configuration; client configuration contributes nothing. |
|
|
6218
|
+
| `configuration-invalid` | 503 | The owning configuration reader's diagnostic, naming the invalid key. Recovery: Correct the named configuration input. Other capabilities remain available. |
|
|
5899
6219
|
| `name-reserved` | 400 | '*alias*' is plurnk's own: PLURNK_* configuration and provider credential names never reach a subprocess. |
|
|
5900
6220
|
| `value-invalid` | 400 | '*alias*' needs a string value. |
|
|
5901
6221
|
| `env-invalid` | 400 | `env` must be an object of string values; '*name*' is not a name a shell can export. |
|
|
@@ -5903,14 +6223,19 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5903
6223
|
| `headless` | 409 | The workspace has no project root, so there are no file members. Recovery: Open the workspace on a project root. |
|
|
5904
6224
|
| `definition-invalid` | 400 | A members definition is { glob }: a gitignore-style pattern, `!glob` to exclude; a skill definition names an installable skill. |
|
|
5905
6225
|
| `model-scope` | 403 | The model may not change membership here: the members scope is none. Recovery: `git add` the file so git tracks it, or ask the operator to add it (/members add) or raise PLURNK_SERVICE_MEMBERS_MODEL_SCOPE. |
|
|
5906
|
-
| `
|
|
5907
|
-
| `
|
|
5908
|
-
| `
|
|
5909
|
-
| `
|
|
6226
|
+
| `query-unsupported` | 400 | Agent Skills have no standard registry to search; discover takes a source: a git remote as a full https or ssh URL, a folder, a lone SKILL.md, or a zip or tar archive. |
|
|
6227
|
+
| `source-invalid` | 400 | '*source*' is not a valid git remote URL; an https source carries no credentials (git's credential helper supplies them); is not a source: a git remote is a full https or ssh URL; is relative, and this workspace has no project root to resolve it against; or is neither a folder, a SKILL.md, nor a zip or tar archive. |
|
|
6228
|
+
| `source-missing` | 404 | No folder or file is at '*source*' (; owner/repo shorthand names no forge, so give the repository's full https or ssh URL). |
|
|
6229
|
+
| `source-unreadable` | 422 | '*source*' cannot be read: *cause*; or '*path*' could not be unpacked: *reason*. |
|
|
6230
|
+
| `source-unreachable` | 502 | git could not reach '*remote*', or fetch it (at '*ref*'): *reason*. |
|
|
6231
|
+
| `ref-missing` | 404 | '*remote*' has no branch or tag '*ref*', or names no default branch. |
|
|
6232
|
+
| `source-moved` | 409 | '*source*' *ref* now names *current*; this skill was added at *commit*. Recovery: Remove the skill and add it again to take the current commit. |
|
|
6233
|
+
| `source-is-plugin` | 422 | '*source*' is an Agent Plugin; install it as a plugin, so its skills keep the plugin's identity and servers. |
|
|
6234
|
+
| `source-unsafe` | 422 | '*path*' links outside its skill, or is neither a file, a directory, nor an inward link. |
|
|
6235
|
+
| `skill-not-found` | 404 | '*source*' carries no Agent Skill named '*name*'. |
|
|
6236
|
+
| `skill-ambiguous` | 409 | '*source*' carries *count* skills named '*name*'. |
|
|
5910
6237
|
| `alias-mismatch` | 400 | Alias '*alias*' must equal the skill name '*name*'. |
|
|
5911
|
-
| `
|
|
5912
|
-
| `source-required` | 400 | Adding '*alias*' requires the standard installer source that provides it. |
|
|
5913
|
-
| `uninstall-failed` | 502 | Agent Skill '*name*' could not be removed from its *scope* root: *cause*. |
|
|
6238
|
+
| `source-required` | 400 | Adding '*alias*' requires the source that provides it. |
|
|
5914
6239
|
| `workspace-not-found` | 404 | Workspace *id* does not exist. |
|
|
5915
6240
|
| `state-not-json` | 400 | Worker module state is not JSON-serializable. |
|
|
5916
6241
|
| `workspace-busy` | 409 | Workspace *id* is running an operation or another capability change. Recovery: Settle the current operation and retry the capability change. |
|
|
@@ -5923,9 +6248,8 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5923
6248
|
| `loop-policy-invalid` | 400 | An unattended loop cannot hold a proposal for review: nobody is present to answer. Recovery: State proposals accept or reject, or attend the loop. |
|
|
5924
6249
|
| `scope-cancelled` | 499 | The worker scope was cancelled: *reason*. |
|
|
5925
6250
|
| `range-not-satisfiable` | 416 | `Range <0,-1>` starts at 0, which is not a line; lines are numbered from 1. Recovery: Write `<1,-1>` to trim every line of the body; `KILL (log:///…/READ)` with no scope retires the item. |
|
|
5926
|
-
| `
|
|
5927
|
-
| `
|
|
5928
|
-
| `skill-missing` | 404 | Agent Skill '*alias*' is not installed under its *scope* root, or is not provided by this service. |
|
|
6251
|
+
| `install-failed` | 500 | Agent Skill '*name*' could not be placed under *root*: *cause*. |
|
|
6252
|
+
| `skill-missing` | 404 | Agent Skill '*alias*' is not provided by this service. |
|
|
5929
6253
|
| `skill-invalid` | 422 | Agent Skill '*alias*' is not a valid standard skill: *cause*. |
|
|
5930
6254
|
|
|
5931
6255
|
§pinned-wording-core **Pinned wording.** Verbatim sentences tests pin: each is contract, and a change here is a change of contract.
|