@plurnk/plurnk-service 1.24.0 → 1.25.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 +461 -169
- 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/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/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.map +1 -1
- package/dist/core/Dispatcher.js +28 -19
- 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 +8 -1
- 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/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 +20 -24
- package/dist/core/PacketBuilder.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/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 +7 -6
- 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 +46 -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/packet-wire.d.ts +1 -0
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +27 -5
- 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 +0 -2
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/DigestRender.d.ts.map +1 -1
- package/dist/digest/DigestRender.js +15 -19
- package/dist/digest/DigestRender.js.map +1 -1
- package/dist/digest/digest-rows.d.ts +1 -1
- package/dist/digest/digest-rows.d.ts.map +1 -1
- 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 +15 -10
- package/dist/schemes/File.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 +124 -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
|
|
@@ -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.
|
|
@@ -1529,6 +1533,8 @@ invalid range, read-only authority, and occupied hidden state without guessing.
|
|
|
1529
1533
|
|
|
1530
1534
|
§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
1535
|
|
|
1536
|
+
§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.
|
|
1537
|
+
|
|
1532
1538
|
### §scheme-manifest Manifest
|
|
1533
1539
|
|
|
1534
1540
|
§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.
|
|
@@ -2383,19 +2389,21 @@ like any other row.
|
|
|
2383
2389
|
| When | An inference turn that admitted at least one statement from its provider content, and turn zero's survey ({§worker-initialization-entry}). A programmatic batch, an empty turn ({§empty-turn}), a client operation and a rejected attempt ({§rejected-emission-entry}) announce nothing. |
|
|
2384
2390
|
| 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
2391
|
| 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 every statement the
|
|
2392
|
+
| Body | Frozen at announcement: the canonical rendering ({§statement-rendering}) of every admitted content statement, in order, 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. Turn zero uses the same projection of its survey. No body text is inspected for nested operations. |
|
|
2393
|
+
| 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. |
|
|
2394
|
+
| 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
2395
|
| 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
|
|
2396
|
+
| 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
2397
|
| 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
2398
|
| 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. |
|
|
2399
|
+
| 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
2400
|
|
|
2393
2401
|
### §turn-source-resources Immutable turn-source resources
|
|
2394
2402
|
|
|
2395
2403
|
| Surface | Contract |
|
|
2396
2404
|
|---|---|
|
|
2397
2405
|
| 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
|
|
2406
|
+
| 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
2407
|
| 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
2408
|
| 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
2409
|
| 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 +2639,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
2631
2639
|
- §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
2640
|
- §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
2641
|
|
|
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.
|
|
2642
|
+
- §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
2643
|
|
|
2636
2644
|
Resource-authority globs select authorities independently of the path scope.
|
|
2637
2645
|
Matching resources retain their full addresses through pattern matching,
|
|
@@ -2648,7 +2656,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
2648
2656
|
- §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
2657
|
- §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
2658
|
- §find-scoped-isolation Workspace + scheme scoped — no cross-workspace/cross-scheme leakage.
|
|
2651
|
-
- §find-result-projection **The
|
|
2659
|
+
- §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
2660
|
|
|
2653
2661
|
| Target | Matcher | `range.unit` | Result rows |
|
|
2654
2662
|
|---|---|---|---|
|
|
@@ -2734,10 +2742,9 @@ same durable liveness.
|
|
|
2734
2742
|
| Fresh operation/parser failure, without an authored WAIT | Continue before any automatic parking. |
|
|
2735
2743
|
| Neither an authored WAIT nor an eligible completion request ({§kill-conclusion}) | Continue, regardless of earlier replies or live work. |
|
|
2736
2744
|
| 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. |
|
|
2745
|
+
| 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
2746
|
| Unobserved operation results, failures, child results or stream conclusions | Continue; the next packet presents them. |
|
|
2740
|
-
| Eligible completion request with no
|
|
2747
|
+
| Eligible completion request with no unpublished arrivals, live work or unobserved results | Conclude successfully, whether or not it delivers an answer. |
|
|
2741
2748
|
|
|
2742
2749
|
An empty emission is handled by {§empty-turn}. Ordinary strikes, cycles and
|
|
2743
2750
|
execution limits remain independent. NOTE and successful targeted KILL do not themselves
|
|
@@ -2797,11 +2804,12 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2797
2804
|
outstanding condition. Valid sibling operations always execute. Only an admitted
|
|
2798
2805
|
KILL delivers its literal body through {§send-response-receipt}; a deferred body
|
|
2799
2806
|
remains forensic evidence, never a stored draft to replay automatically. An empty
|
|
2800
|
-
KILL concludes
|
|
2801
|
-
|
|
2807
|
+
KILL concludes silently even when an observed message has no delivered answer. It neither
|
|
2808
|
+
invents delivery nor replaces an earlier answer; immutable input and reply history remain intact.
|
|
2809
|
+
SEND, NOTE and targeted KILL never request successful
|
|
2802
2810
|
completion. New arrivals still guard the terminal transition atomically
|
|
2803
|
-
({§completion-defers-to-messages}); an arrival concurrent with
|
|
2804
|
-
|
|
2811
|
+
({§completion-defers-to-messages}); an unobserved arrival concurrent with completion
|
|
2812
|
+
keeps the loop running. No implicit successful exit exists.
|
|
2805
2813
|
- §outside-text **Text outside the operations is the turn's `outside` source: stored, weighed, never a row.**
|
|
2806
2814
|
The spans {§response-text} supplies are stored verbatim as one immutable `outside` turn source
|
|
2807
2815
|
per admitted emission, in source order joined by a blank line ({§turn-source-resources}); no
|
|
@@ -2821,10 +2829,11 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2821
2829
|
{§response-text} alone owns which bytes are operations, quotations or outside text.
|
|
2822
2830
|
- §loop-answer **A loop's address is what it said.** READ `ops://<worker>/<loop>` resolves to
|
|
2823
2831
|
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
|
-
|
|
2832
|
+
or accepted final KILL that answered that message. A failed terminal takes precedence over
|
|
2833
|
+
an earlier reply and retains its exact Problem, including {§terminal-evidence}. A running loop
|
|
2834
|
+
without a reply is 425; a concluded loop without one returns its terminal outcome, including
|
|
2835
|
+
successful silence. Completion never fabricates an answer or a missing-resource failure.
|
|
2836
|
+
`ops://<worker>/<loop>/<turn>`
|
|
2828
2837
|
remains that turn's emission. A concluded child's `loop_termination` row to its parent
|
|
2829
2838
|
READs this same loop resource. Witness: `test/intg/loop-answer.test.ts`.
|
|
2830
2839
|
- §empty-turn **No authored response operation is a recoverable turn, never completion.**
|
|
@@ -3047,13 +3056,15 @@ the workspace snapshot. Installed siblings form the immutable base:
|
|
|
3047
3056
|
they are discovered and probed at startup, and availability is cached.
|
|
3048
3057
|
Workspace Functionality providers may atomically overlay additional names under
|
|
3049
3058
|
{§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
|
-
`
|
|
3059
|
+
independent workspaces may use the same name. The fence name selects exactly
|
|
3060
|
+
that registered executable tool. Unknown tags are refused 400 with the
|
|
3061
|
+
advertised catalogue and are never reinterpreted as shell command words.
|
|
3062
|
+
A runtime unavailable after an ordinary probe is 501 with the probe `detail`.
|
|
3063
|
+
Typed configuration failures preserve the declaration, with no executor instance
|
|
3064
|
+
or output scheme; invocation returns the exact 503 configuration Problem
|
|
3065
|
+
({§configuration-repair-path}). No executor, including `sh`, is required for
|
|
3066
|
+
daemon startup. Internal constructor defects remain failures, not unavailable
|
|
3067
|
+
configuration verdicts.
|
|
3057
3068
|
|
|
3058
3069
|
For a family runtime, `ExecutorRegistry.toolRegistry(tag, workspaceId)`
|
|
3059
3070
|
validates the one executor-owned snapshot used by packet presentation,
|
|
@@ -3230,8 +3241,8 @@ Each layer uses the same value and masking rules. A worker's list includes works
|
|
|
3230
3241
|
defaults by reference with `origin: "workspace"`; worker overrides and masks remain
|
|
3231
3242
|
worker-owned. Enabling an inherited entry clears this layer's mask, not a mask in a
|
|
3232
3243
|
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
|
-
|
|
3244
|
+
the name even when its lower-layer origin changes. Removing an override restores the
|
|
3245
|
+
lower entry and its enabledness ({§configuration-definition-resolution}). Forking copies only worker state, not the workspace defaults. Workspace edits
|
|
3235
3246
|
affect subsequent launches, not existing processes or other workspaces. A shared
|
|
3236
3247
|
capability never acquires an invoking worker's overrides or ownership.
|
|
3237
3248
|
|
|
@@ -3431,11 +3442,19 @@ was said ({§methods-loop-run-fold-consistency}). `LoopPolicies.compose` then ma
|
|
|
3431
3442
|
once. `PLURNK_SERVICE_ATTENDED` answers an unstated attendance, and the attendance picks which
|
|
3432
3443
|
knob answers an unstated disposition: `PLURNK_SERVICE_PROPOSALS` for an attended loop,
|
|
3433
3444
|
`PLURNK_SERVICE_UNATTENDED_PROPOSALS` for an unattended one, whose vocabulary has no `review`.
|
|
3434
|
-
Every panel state is therefore lawful
|
|
3445
|
+
Every valid panel state is therefore lawful; an invalid knob is diagnosed at startup
|
|
3446
|
+
and refuses a loop that needs it ({§configuration-repair-path}). No code,
|
|
3435
3447
|
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
|
-
|
|
3448
|
+
`loops.max_turns` carry none, so every insert states both. Client-authored administrative
|
|
3449
|
+
loops use the same composition.
|
|
3450
|
+
|
|
3451
|
+
§runtime-bookkeeping-policy **Runtime bookkeeping has no reviewer and cannot acquire
|
|
3452
|
+
new authority.** Its administrative loops explicitly state
|
|
3453
|
+
`{ attended: false, proposals: "reject" }`; this is a runtime invariant, not an
|
|
3454
|
+
interactive default. Generated reference publication and audit narration therefore
|
|
3455
|
+
do not depend on client policy configuration. Runtime-authored proposals do not
|
|
3456
|
+
use effect-policy auto-admission; bookkeeping proposals settle as failures through
|
|
3457
|
+
the ordinary proposal lifecycle, never wait for a client or auto-accept.
|
|
3439
3458
|
|
|
3440
3459
|
§loop-policy-effective-read `loops.policy` persists one complete immutable
|
|
3441
3460
|
`LoopPolicy`; every runtime policy read validates that snapshot before use.
|
|
@@ -3538,7 +3557,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
3538
3557
|
| §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
3558
|
| §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
3559
|
| §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
|
|
3560
|
+
| §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
3561
|
|
|
3543
3562
|
- DDL = storage truth; JSON Schemas = wire truth. They are allowed to differ where ergonomics demand.
|
|
3544
3563
|
- §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 +3703,14 @@ and is ignored rather than resolved against the working directory.
|
|
|
3684
3703
|
|
|
3685
3704
|
| Class | Base | Plurnk member |
|
|
3686
3705
|
|---|---|---|
|
|
3687
|
-
| Configuration | `$XDG_CONFIG_HOME` (default `~/.config`) | `plurnk/.env`, `plurnk/AGENTS.md` |
|
|
3706
|
+
| 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
3707
|
| Durable user data | `$XDG_DATA_HOME` (default `~/.local/share`) | `plurnk/plurnk.db` and SQLite sidecars |
|
|
3689
3708
|
| Persistent operational state | `$XDG_STATE_HOME` (default `~/.local/state`) | On-demand workspace/module directories ({§module-workspace-directory}). |
|
|
3690
3709
|
| Reproducible cache | `$XDG_CACHE_HOME` (default `~/.cache`) | Reserved; no directory is created without an owned artifact. |
|
|
3691
3710
|
| Shared global Agent Skills | User home | `.agents/skills/<name>/SKILL.md` |
|
|
3711
|
+
| Shared global MCP definitions | User home | `.agents/mcp.json` |
|
|
3712
|
+
| Shared global Agent Plugins | User home | `.agents/plugins/<plugin>/` ({§agent-plugins-hosting}) |
|
|
3713
|
+
| A plugin's `PLUGIN_DATA` | `$XDG_DATA_HOME` | `plurnk/plugins/<plugin>/` |
|
|
3692
3714
|
|
|
3693
3715
|
§state-root **A private daemon has one root.** `PLURNK_SERVICE_STATE_ROOT` (absolute; a leading
|
|
3694
3716
|
`~/` expands; a relative value fails hard by name) replaces the data, state, cache and runtime homes
|
|
@@ -3710,15 +3732,32 @@ ordinary precedence; XDG variables themselves require absolute paths.
|
|
|
3710
3732
|
|---------:|------------------------------------|-----------------------------------------------------------|
|
|
3711
3733
|
| 1 | Assembled package `.env.defaults` | Set-if-unset floor; one owner per key. |
|
|
3712
3734
|
| 2 | `$XDG_CONFIG_HOME/plurnk/.env` | User-level ambient configuration. |
|
|
3713
|
-
| 3 |
|
|
3714
|
-
| 4 | `--
|
|
3715
|
-
| 5 |
|
|
3716
|
-
| 6 |
|
|
3717
|
-
|
|
3735
|
+
| 3 | `--config=<path>` | Singular service-owned explicit file. |
|
|
3736
|
+
| 4 | `--env-file*` | Repeatable explicit files; later selected files win. |
|
|
3737
|
+
| 5 | Initial shell environment | Preserved over every file layer. |
|
|
3738
|
+
| 6 | Derived service CLI flags | Assigned last. |
|
|
3739
|
+
|
|
3740
|
+
A working directory's `.env` configures that directory's application, never plurnk (#926); a
|
|
3741
|
+
project's variables reach its commands through the workspace environment ({§workspace-env}).
|
|
3718
3742
|
|
|
3719
3743
|
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
3744
|
|
|
3721
|
-
§operator-config-env-defaults **Every package owns its knobs —
|
|
3745
|
+
§operator-config-env-defaults **Every package owns its knobs — one assembled floor.**
|
|
3746
|
+
|
|
3747
|
+
| Source | Panel | Admission |
|
|
3748
|
+
|---|---|---|
|
|
3749
|
+
| Platform capability package | `.env.defaults` at the package root | `@plurnk/*` or a `plurnk` package field; {§plugin-trust-boundary} |
|
|
3750
|
+
| 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 |
|
|
3751
|
+
|
|
3752
|
+
Root and trust flags apply before collection. A project plugin contributes no native panel.
|
|
3753
|
+
Non-module native panels follow their npm-only family discovery ({§plugin-manifest-read});
|
|
3754
|
+
a plain-folder declaration does not suppress an installed capability's panel.
|
|
3755
|
+
The file travels with its code and is its configuration reference. All admitted files compose
|
|
3756
|
+
one floor, applied set-if-unset beneath operator sources. `plurnk-service config defaults`
|
|
3757
|
+
renders those same owner-labelled files, preserving comments and optional declarations without
|
|
3758
|
+
persisting another copy or exposing effective values. Duplicate key ownership fails naming both
|
|
3759
|
+
owners. Invalid optional native panels are diagnosed and prevent that extension from loading;
|
|
3760
|
+
they do not block the remaining floor or the repair path ({§configuration-repair-path}).
|
|
3722
3761
|
|
|
3723
3762
|
§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
3763
|
|
|
@@ -3750,6 +3789,34 @@ or provider request. The seeded `.env`, first-run diagnostic, service help, and
|
|
|
3750
3789
|
missing-model recovery all signpost `plurnk-service config defaults` as the
|
|
3751
3790
|
complete installed option catalog.
|
|
3752
3791
|
|
|
3792
|
+
§operator-config-offline-validation **`config check` and runtime use the same
|
|
3793
|
+
owning configuration readers, with different failure boundaries.** An offline
|
|
3794
|
+
check rejects invalid configuration with a nonzero exit. Runtime contains
|
|
3795
|
+
optional-family errors according to {§configuration-repair-path}. Failure
|
|
3796
|
+
names the offending variable or file/entry and retains its cause.
|
|
3797
|
+
|
|
3798
|
+
| Owner | Offline validation |
|
|
3799
|
+
|---|---|
|
|
3800
|
+
| Core | Model selection, file-creation/effect/loop policy, members definitions and controls, skill-fetch settings and root selection |
|
|
3801
|
+
| 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 |
|
|
3802
|
+
| A2A | Whole outbound definitions and controls, timeout/diagnostic bounds, configured inbound exposure |
|
|
3803
|
+
| Schedule | Whole definitions and controls, recurrence syntax, time zone and preview count |
|
|
3804
|
+
| Hooks | Command/argument/event configuration and delivery bounds |
|
|
3805
|
+
|
|
3806
|
+
Disabled definitions and controls without a resource are validated, not skipped.
|
|
3807
|
+
Checking creates no database, starts no process or listener, arms no schedule,
|
|
3808
|
+
and contacts no provider or endpoint. Symbolic credential references remain
|
|
3809
|
+
symbolic: availability belongs to workspace preparation, not offline validation.
|
|
3810
|
+
|
|
3811
|
+
First-run seeding publishes the complete private configuration directory atomically.
|
|
3812
|
+
Concurrent initializers adopt the winning seed; a failed initializer removes only
|
|
3813
|
+
its own staging directory. An existing operator directory is never reseeded.
|
|
3814
|
+
|
|
3815
|
+
§systemd-user-unit The service package ships `plurnk.service` as an example
|
|
3816
|
+
systemd user unit. Installation and enablement are explicit operator actions;
|
|
3817
|
+
package installation performs neither. The template documents executable-path
|
|
3818
|
+
and environment adjustments instead of introducing a service-management command.
|
|
3819
|
+
|
|
3753
3820
|
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
3821
|
|
|
3755
3822
|
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 +3937,7 @@ the policy renders in exactly one packet section. Every other tier runs the
|
|
|
3870
3937
|
test cascade, so shipped-default regressions are otherwise invisible by
|
|
3871
3938
|
construction.
|
|
3872
3939
|
|
|
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
|
|
3940
|
+
§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
3941
|
|
|
3875
3942
|
| Owner | Configuration |
|
|
3876
3943
|
|---|---|
|
|
@@ -3953,36 +4020,55 @@ proceeds. `setup` is the readiness boundary for every capability registered
|
|
|
3953
4020
|
with Core: recovery may demand a workspace provider before `start`. For a
|
|
3954
4021
|
pre-bound client interface, requests remain unavailable until `start`; every
|
|
3955
4022
|
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
|
-
|
|
4023
|
+
registered capability may depend on exterior ingress. A module, and any distinct
|
|
4024
|
+
lifetime object returned by `start`, may implement the following phases:
|
|
4025
|
+
|
|
4026
|
+
| Phase | Obligation |
|
|
4027
|
+
|---|---|
|
|
4028
|
+
| `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. |
|
|
4029
|
+
| `close()` | Unsubscribe observers and release remaining resources after producer settlement; await admitted notification deliveries. Do not start new core work. |
|
|
4030
|
+
|
|
4031
|
+
Both phases are optional and idempotent; repeated calls join the same work.
|
|
4032
|
+
Core tracks a module before `setup` so partially acquired resources are released
|
|
4033
|
+
even if setup fails. A returned object identical to its module is tracked once.
|
|
4034
|
+
|
|
4035
|
+
§module-discovery **Daemon modules compose through their shared lifecycle.**
|
|
4036
|
+
|
|
4037
|
+
| Source | Declaration | Lifetime |
|
|
4038
|
+
|---|---|---|
|
|
4039
|
+
| Platform capability package | `package.json#plurnk` with `kind: "module"` and `module` | Daemon-wide |
|
|
4040
|
+
| 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 |
|
|
4041
|
+
| Project Agent Plugin | Portable components only | Workspace-scoped; native code is not imported |
|
|
4042
|
+
|
|
4043
|
+
The export is one DaemonModule object or no-argument factory. Standard bundles follow
|
|
4044
|
+
{§agent-plugins-hosting} source order, then other installed module packages load in package-name
|
|
4045
|
+
order. All trusted modules register before setup. The service's explicit AG-UI, hooks and MCP
|
|
4046
|
+
composition is never duplicated. Untrusted modules are reported and not imported. Invalid
|
|
4047
|
+
declarations, unavailable module files and configuration errors during construction are diagnosed
|
|
4048
|
+
at the affected native extension; healthy siblings remain available. A factory validates startup
|
|
4049
|
+
configuration before `setup` acquires resources. Failures after registration begins follow
|
|
4050
|
+
{§module-lifecycle} cleanup, not a partial-registration fallback.
|
|
4051
|
+
An invalid module object, factory result or lifecycle member is an implementation contract failure,
|
|
4052
|
+
not configuration, and fails loudly. Native capabilities register through their owning public
|
|
4053
|
+
interfaces and release registrations during {§module-lifecycle} resource teardown.
|
|
3972
4054
|
|
|
3973
4055
|
§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
|
-
|
|
4056
|
+
aborts proposals, branches, derivations, and worker scopes. It begins module
|
|
4057
|
+
`stop()` calls in reverse registration order without serially awaiting them,
|
|
4058
|
+
so every producer is asked to stop even if another stalls. Core settles drains,
|
|
4059
|
+
capability publications, module producers, streaming producers, derivations,
|
|
4060
|
+
mimetypes, schemes, and the final worker-settlement barrier while observers
|
|
4061
|
+
remain subscribed. Only then does it begin and join module `close()` calls in
|
|
4062
|
+
reverse order. Failures do not skip later phases and join one shutdown aggregate.
|
|
4063
|
+
The supervisor owns each asynchronous cancellation and wake task from
|
|
3980
4064
|
acceptance through settlement, including immediately acknowledged and explicitly
|
|
3981
4065
|
awaited cancellation; a task failure participates in the shutdown aggregate.
|
|
3982
4066
|
After asynchronous selection, the supervisor rechecks shutdown before creating
|
|
3983
4067
|
a drain or installing a timer; parked-loop wake mutations also recheck worker
|
|
3984
4068
|
cancellation under {§worker-lifecycle-durable-disposition}.
|
|
3985
|
-
The database
|
|
4069
|
+
The database remains available through observer closure and final maintenance.
|
|
4070
|
+
The shared deadline bounds every phase, including observer delivery; forced
|
|
4071
|
+
shutdown may therefore lose notifications and reports the unfinished phase.
|
|
3986
4072
|
|
|
3987
4073
|
§crash-only-stop The settle sequence is deadline-bounded
|
|
3988
4074
|
(`PLURNK_SERVICE_STOP_TIMEOUT_MS`, default 30000): past the deadline each wait
|
|
@@ -3996,21 +4082,22 @@ backstop, not the exit.
|
|
|
3996
4082
|
```mermaid
|
|
3997
4083
|
flowchart LR
|
|
3998
4084
|
stop[Begin stop] --> abort[Abort core producers]
|
|
3999
|
-
stop -->
|
|
4085
|
+
stop --> moduleStop[Begin reverse module stop]
|
|
4000
4086
|
abort --> drains[Settle worker drains]
|
|
4001
|
-
drains --> joined[Settle module
|
|
4002
|
-
|
|
4087
|
+
drains --> joined[Settle module producers]
|
|
4088
|
+
moduleStop --> joined
|
|
4003
4089
|
joined --> producers[Settle streaming producers]
|
|
4004
4090
|
producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
|
|
4005
4091
|
resources --> settlement[Settle cancellations and wakes]
|
|
4006
|
-
settlement -->
|
|
4092
|
+
settlement --> observers[Close observers and resources]
|
|
4093
|
+
observers --> database[Maintain and release database]
|
|
4007
4094
|
```
|
|
4008
4095
|
|
|
4009
4096
|
| Setup function | Contract |
|
|
4010
4097
|
|---|---|
|
|
4011
4098
|
| `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
4099
|
| `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. |
|
|
4100
|
+
| §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
4101
|
| §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
4102
|
| §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
4103
|
| `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 +4147,7 @@ A conflicting alias is rejected explicitly; it never produces a hidden second
|
|
|
4060
4147
|
definition for the submitting client or worker.
|
|
4061
4148
|
|
|
4062
4149
|
§module-workspace-residency **Persistence is not residency.** Model execution,
|
|
4063
|
-
capability-aware operations,
|
|
4150
|
+
capability-aware operations, module actions declaring required residency, and retained provider work
|
|
4064
4151
|
lease the workspace's Functionality. Boot, workspace or worker creation,
|
|
4065
4152
|
attachment, listing, naming, idle clients, and parked state alone do not.
|
|
4066
4153
|
After the last lease releases, `PLURNK_SERVICE_WORKSPACE_WARM_MS` and
|
|
@@ -4090,10 +4177,10 @@ registry. Deleting the workspace cascades its state; worker lifecycle does not.
|
|
|
4090
4177
|
## Workspace Functionality
|
|
4091
4178
|
|
|
4092
4179
|
§functionality-coordinator **One coordinator owns the common lifecycle.**
|
|
4093
|
-
|
|
4180
|
+
Functionality families are adapters beneath
|
|
4094
4181
|
`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
|
|
4182
|
+
serialize per workspace and family. Scope-bound client actions
|
|
4183
|
+
`workspace.<family>.<verb>` / `worker.<family>.<verb>` and model manager executors invoke the same
|
|
4097
4184
|
coordinator. Families do not invent another management grammar, proposal
|
|
4098
4185
|
policy, or hotload path.
|
|
4099
4186
|
|
|
@@ -4101,12 +4188,98 @@ Retryability describes the actual failed condition, not its numeric status.
|
|
|
4101
4188
|
|
|
4102
4189
|
| Verb | Common contract |
|
|
4103
4190
|
|---|---|
|
|
4104
|
-
| `list` | Project definitions, origin, enabledness, and preparation outcome: disabled, active, unavailable with its Problem, or authorization-required. No credential values. |
|
|
4191
|
+
| `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
4192
|
| `discover` | Return inert candidates. Never install, persist, enable, or execute them. |
|
|
4106
|
-
| `add` | Admit and persist a
|
|
4193
|
+
| `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
4194
|
| `enable` | Publish an available definition; retry preparation if unavailable. |
|
|
4108
4195
|
| `disable` | Withdraw live capability; retain its definition and saved results. |
|
|
4109
|
-
| `remove` |
|
|
4196
|
+
| `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. |
|
|
4197
|
+
|
|
4198
|
+
§configuration-definition-resolution **Named resource definitions replace whole;
|
|
4199
|
+
independent behavior controls remain independent.** Source readers and scope
|
|
4200
|
+
overlays apply the same boundary:
|
|
4201
|
+
|
|
4202
|
+
| Value | Resolution |
|
|
4203
|
+
|---|---|
|
|
4204
|
+
| 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. |
|
|
4205
|
+
| Environment resource declaration | Use {§resource-environment}: absence inherits, an empty definition is invalid, and an explicit enabledness switch disables without erasing the definition. |
|
|
4206
|
+
| 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. |
|
|
4207
|
+
| Independently declared behavior control | Resolve its own value through its cascade. An enabledness override does not copy or patch the definition it controls. |
|
|
4208
|
+
| 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}). |
|
|
4209
|
+
|
|
4210
|
+
§configuration-provenance **Inspection names the winning definition's input, not
|
|
4211
|
+
its owner or runtime.** `provenance` uses the same `{kind, source, reference?}`
|
|
4212
|
+
shape as discovery candidates. Source readers contribute it; the coordinator
|
|
4213
|
+
preserves it through inheritance, enabledness changes, and every readiness state.
|
|
4214
|
+
|
|
4215
|
+
| Definition source | Inspection |
|
|
4216
|
+
|---|---|
|
|
4217
|
+
| Assembled environment | `kind: environment`, `source`: exact definition key; never its value or an inferred dotenv filename. |
|
|
4218
|
+
| Discovered skill root | `kind: file`, `source`: the winning `SKILL.md` path. |
|
|
4219
|
+
| Standalone MCP file | `kind: file`, `source`: the winning `mcp.json` path; `reference`: its entry's JSON Pointer. |
|
|
4220
|
+
| 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. |
|
|
4221
|
+
| Local override | Replaces inherited provenance with the local definition; removal restores the current inherited provenance. |
|
|
4222
|
+
|
|
4223
|
+
Only the winning definition's source is reported. Shadowed definitions, secrets,
|
|
4224
|
+
and environment-file loading history are not tracked. Source metadata is derived
|
|
4225
|
+
on inspection, not persisted in the local overlay or used as runtime identity.
|
|
4226
|
+
|
|
4227
|
+
§configuration-repair-path **Invalid optional configuration cannot remove the
|
|
4228
|
+
agent's repair environment.** Capability owners reject typed operator input
|
|
4229
|
+
errors. The launcher and shared coordinator contain them at their respective
|
|
4230
|
+
composition boundaries, not arbitrary exceptions:
|
|
4231
|
+
|
|
4232
|
+
| Boundary | Outcome |
|
|
4233
|
+
|---|---|
|
|
4234
|
+
| 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. |
|
|
4235
|
+
| 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. |
|
|
4236
|
+
| 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. |
|
|
4237
|
+
| 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. |
|
|
4238
|
+
| 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. |
|
|
4239
|
+
| 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. |
|
|
4240
|
+
| 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. |
|
|
4241
|
+
| 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. |
|
|
4242
|
+
| 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. |
|
|
4243
|
+
| Offline `config check` | Validate the same inputs without activating integrations; an invalid setting remains a nonzero failure. |
|
|
4244
|
+
| 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. |
|
|
4245
|
+
| 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. |
|
|
4246
|
+
| Invalid live mutation | Reject atomically and preserve the preceding publication. |
|
|
4247
|
+
| Previously valid family becomes invalid | Withdraw its operational capabilities at normal publication, then release the old snapshot. Keep the manager and diagnostic. |
|
|
4248
|
+
| 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. |
|
|
4249
|
+
| 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. |
|
|
4250
|
+
| Internal invariant, state, or implementation failure | Preserve the exception; never reclassify it as an operator configuration error. |
|
|
4251
|
+
|
|
4252
|
+
Client discovery and passive synchronization report startup diagnostics through the
|
|
4253
|
+
existing Notice channel, even without a usable model. The first turn of a drain, and a changed diagnostic
|
|
4254
|
+
thereafter, reports unresolved configuration to both client and model. An
|
|
4255
|
+
unchanged diagnostic is not repeated every turn. Operation failures remain Problems.
|
|
4256
|
+
|
|
4257
|
+
§functionality-inspection **Inspection is not demand.** `list` and `discover` do not
|
|
4258
|
+
acquire residency, join preparation, reconcile worker documents, or extend warm
|
|
4259
|
+
retention. An enabled definition no resident publication has prepared is `dormant`: every one while
|
|
4260
|
+
the family is cold, and one that arrived or changed out of band until the next turn publishes it
|
|
4261
|
+
({§functionality-hotload}). During replacement the preceding publication remains authoritative;
|
|
4262
|
+
the candidate is never presented as active. A published outcome belongs to the
|
|
4263
|
+
complete definition prepared, not merely its alias. Cooling leaves durable definitions
|
|
4264
|
+
inspectable. Mutations and protocol continuations retain their residency rules.
|
|
4265
|
+
|
|
4266
|
+
§functionality-preparation-visibility **Preparation is workspace activity, not
|
|
4267
|
+
model context.** The coordinator owns a current `FunctionalityPreparationActivity`
|
|
4268
|
+
per preparing family. `workspacePreparationStatus(workspaceId)` returns that same
|
|
4269
|
+
state without demand; `workspace/preparation` broadcasts
|
|
4270
|
+
`{ workspaceId, preparation: [...] }` whenever it changes.
|
|
4271
|
+
|
|
4272
|
+
| Boundary | Visible state |
|
|
4273
|
+
|---|---|
|
|
4274
|
+
| Preparation begins | Family, `phase: preparing`, `alias: null`, UTC `since` |
|
|
4275
|
+
| Adapter calls `progress(alias)` | Enabled alias being prepared; a fresh `since` |
|
|
4276
|
+
| Prepared candidate enters publication | `phase: publishing`, `alias: null` |
|
|
4277
|
+
| Commit, rejection, or rollback settles | Family removed; empty array means no preparation |
|
|
4278
|
+
|
|
4279
|
+
Preparation reports neither definitions nor credentials, does not alter the
|
|
4280
|
+
publication contract, and creates no log entries or model Notices. Published
|
|
4281
|
+
failures retain their exact Problems in `list`. Concurrent consumers share the
|
|
4282
|
+
workspace activity; a client disconnect does not clear another consumer's work.
|
|
4110
4283
|
|
|
4111
4284
|
§functionality-adapter **An adapter owns protocol truth.** It declares its
|
|
4112
4285
|
family, namespace owner, definition schema, contributed defaults, discovery,
|
|
@@ -4116,16 +4289,66 @@ family declares, at admission, in the service projection, and on persisted
|
|
|
4116
4289
|
state, so an environment variable's name is an alias exactly as a skill name
|
|
4117
4290
|
is. Admission distinguishes explicit client
|
|
4118
4291
|
actions from model operations where the family contract requires it
|
|
4119
|
-
({§members-model-scope}). Preparation
|
|
4292
|
+
({§members-model-scope}). Preparation receives each complete definition with optional
|
|
4293
|
+
adapter-owned interpretation context. Context is source semantics, not policy or provenance;
|
|
4294
|
+
it participates in runtime identity and hot-load comparisons, is never projected as configuration
|
|
4295
|
+
or persisted into a workspace override, and cannot survive replacement by a local definition.
|
|
4296
|
+
Removing that override restores the current inherited definition and context together.
|
|
4297
|
+
Descriptive provenance alone does not change runtime identity. Preparation returns runtimes, documents, per-alias
|
|
4120
4298
|
outcomes, and a snapshot with `commit`/`abort`. Successful publication commits;
|
|
4121
4299
|
failure aborts; cooling tears down. Protocol continuations remain ordinary
|
|
4122
4300
|
module actions. Optional `forget` releases an installed or provisioned
|
|
4123
|
-
definition before removal; failure rejects removal
|
|
4301
|
+
definition before removal; failure rejects removal. The
|
|
4124
4302
|
seam's shapes — the identity a verb acts under, its options, definition
|
|
4125
4303
|
sources, outcomes, preparation, the prepared result and the family handle —
|
|
4126
4304
|
are declared once in `plurnk-contracts` and imported by core and every
|
|
4127
4305
|
module; core adds only its own face of the seam, the runtime registration a
|
|
4128
4306
|
resident family prepares and the scheme facet it may expose.
|
|
4307
|
+
An adapter may expose current partial-source `configurationNotices`; these join the ordinary
|
|
4308
|
+
workspace diagnostics without preventing independently valid definitions from preparing.
|
|
4309
|
+
|
|
4310
|
+
§functionality-hotload **Out-of-band state is admitted before the next turn.** An adapter whose
|
|
4311
|
+
`available` reads state that changes outside the daemon, such as skill roots ({§skills-hotload}) or
|
|
4312
|
+
installed plugins ({§agent-plugins-hosting}), implements `refreshIfChanged`. Turn admission calls it
|
|
4313
|
+
for every family under the workspace gate before packet assembly. The family handle's `refresh` with
|
|
4314
|
+
`ifChanged` republishes a resident family only when the enabled definitions it would prepare differ
|
|
4315
|
+
from the ones its publication prepared. The coordinator makes that comparison because it alone knows
|
|
4316
|
+
what it published, so a change `list` saw first is still published at the next turn. A family whose
|
|
4317
|
+
definitions do not capture its published content, such as a skill's files, republishes
|
|
4318
|
+
unconditionally when that content changed. An unchanged family dispatches nothing.
|
|
4319
|
+
|
|
4320
|
+
§agent-plugins-hosting **Installed Agent Plugins are found like skills.** A workspace's plugins are
|
|
4321
|
+
the immediate child directories of its project's `.agents/plugins`, then
|
|
4322
|
+
`$XDG_CONFIG_HOME/plurnk/plugins` (plurnk alone), then `~/.agents/plugins` (every agent), loaded and
|
|
4323
|
+
validated by `@plurnk/plurnk-agent-plugins` ({§agent-plugins-roots}), followed by standard plugin
|
|
4324
|
+
bundles in the installed npm graph. An earlier source shadows a later plugin of the same manifest
|
|
4325
|
+
name, regardless of distribution or directory name. Native discovery uses that same cascade with
|
|
4326
|
+
the project root omitted; workspace discovery includes it. A plugin's `PLUGIN_DATA` is
|
|
4327
|
+
`$XDG_DATA_HOME/plurnk/plugins/<name>/<sha256(canonical-root)>`, kept across in-place updates and
|
|
4328
|
+
moved with a state root ({§state-root}). Distinct installations never share data by name alone;
|
|
4329
|
+
workspaces referencing the same canonical installation share its data. Modules receive a workspace's plugins,
|
|
4330
|
+
in precedence order, through the setup seam's `readWorkspacePlugins`, with one signature that changes
|
|
4331
|
+
exactly when a plugin, its manifest, its MCP configuration, or its skills change
|
|
4332
|
+
({§functionality-hotload}), and the roots the workspace has. This read-only source loader does
|
|
4333
|
+
not install or delete plugins. MCP's own lifecycle is independent ({§mcp-definitions}).
|
|
4334
|
+
|
|
4335
|
+
Portable components use each family's existing management and publication path. Within a source
|
|
4336
|
+
scope, standalone definitions precede bundled components; nearer scopes precede farther scopes,
|
|
4337
|
+
with npm last. Complete environment definitions override those inputs, followed by workspace
|
|
4338
|
+
definitions and enabledness. Inspection names the winning component file as plugin provenance.
|
|
4339
|
+
Removing a workspace override restores inheritance; it never deletes the installed bundle.
|
|
4340
|
+
Current plugin-source diagnostics join the workspace's configuration notices before inference.
|
|
4341
|
+
|
|
4342
|
+
§agent-roots **A daemon reads the roots `PLURNK_SERVICE_ROOTS` names.** A comma list drawn from
|
|
4343
|
+
`project`, `plurnk` and `global`, nearest first, selects which Agent Skills, Agent Plugins and standalone MCP roots a
|
|
4344
|
+
daemon reads; the default names all three. The real-model gate profile selects
|
|
4345
|
+
its discovery roots explicitly ({§operator-config-real-model-profile}), so
|
|
4346
|
+
operator roots do not shape a gate. Root selection does not restrict explicit
|
|
4347
|
+
source definitions. Skills mutations change workspace bindings, not these roots
|
|
4348
|
+
({§skills-functionality}); MCP mutations likewise remain workspace-owned. The module setup seam's
|
|
4349
|
+
`workspaceConfigurationDirectories` supplies selected `<project>/.agents`,
|
|
4350
|
+
`$XDG_CONFIG_HOME/plurnk`, and `~/.agents` directories in precedence order, omitting
|
|
4351
|
+
project when no project is bound. Modules own their file formats; core owns discovery roots.
|
|
4129
4352
|
|
|
4130
4353
|
An adapter may expose a `scheme` facet beneath its family's runtime namespace
|
|
4131
4354
|
({§runtime-resource-binding}). A facet claims a path subtree and is the scheme's
|
|
@@ -4145,9 +4368,10 @@ operator's ceiling ({§exec-env-scoped}, service origin) precede workspace defau
|
|
|
4145
4368
|
worker overrides ({§workspace-env}). `add` takes
|
|
4146
4369
|
the name as the alias and `{ "value": "…" }` as the definition, used verbatim with no
|
|
4147
4370
|
interpolation. `disable` withholds a name in the selected scope while retaining it;
|
|
4148
|
-
`remove` forgets a locally-owned entry and
|
|
4149
|
-
|
|
4150
|
-
|
|
4371
|
+
`remove` forgets a locally-owned entry and restores any same-name inherited value
|
|
4372
|
+
and enabledness ({§configuration-definition-resolution}). Use `disable` to keep
|
|
4373
|
+
an inherited name out of subsequent launches. Definitions from a lower layer
|
|
4374
|
+
cannot be removed in the current scope.
|
|
4151
4375
|
|
|
4152
4376
|
`list` projects effective values with their origin. Values are shown: the ceiling is the security
|
|
4153
4377
|
boundary, not the projection, and any admitted name is already readable by every command the
|
|
@@ -4556,6 +4780,7 @@ adding a loop to it. LOOK text anchors resolve through the same
|
|
|
4556
4780
|
| §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
4781
|
| §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
4782
|
| §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
|
|
4783
|
+
| `workspace/preparation` | `{ workspaceId, preparation: FunctionalityPreparationActivity[] }` | Workspace capability preparation changes; snapshot and clearing semantics follow {§functionality-preparation-visibility}. |
|
|
4559
4784
|
| §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
4785
|
| §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
4786
|
| §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 +4843,17 @@ The packet reaches the provider as a transcript, under the roles the model was t
|
|
|
4618
4843
|
| Message | Role | Content |
|
|
4619
4844
|
|:--|:--|:--|
|
|
4620
4845
|
| 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
|
|
4846
|
+
| 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` |
|
|
4847
|
+
| 3, 5, … | `assistant` | that row's frozen emission projection less its NOTE and WAIT blocks, as the worker's own message ({§emission-row}) |
|
|
4848
|
+
| 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
4849
|
|
|
4625
4850
|
Only role boundaries are added: joined by blank lines, the user messages are the user slot's
|
|
4626
4851
|
bytes, in record order. An emission is placed exactly when its row is present in the final log
|
|
4627
4852
|
section, so curation governs the transcript: a KILLed emission row, or one a trusted transform
|
|
4628
4853
|
removed ({§packet-plugin-transform}), takes its emission with it, and a log without emission rows
|
|
4629
|
-
is one user message.
|
|
4630
|
-
|
|
4854
|
+
is one user message. An emission of only NOTE and WAIT delivers nothing, so its record runs on
|
|
4855
|
+
into the next user message. The Worker block and the status clump always follow the log, so a
|
|
4856
|
+
request never ends on an emission, and the projection refuses one that would. The digest's packet
|
|
4631
4857
|
artifacts record the sections, and `.wire.json` the messages ({§share-packet-names}).
|
|
4632
4858
|
|
|
4633
4859
|
### §packet-cache-monotone Default order and cache locality
|
|
@@ -5074,7 +5300,7 @@ ordered set exactly once. Recovery retries complete the same queued loop and nev
|
|
|
5074
5300
|
mint duplicate work. Output withholding preserves readable arrival rows; explicit
|
|
5075
5301
|
KILL follows the ordinary log contract.
|
|
5076
5302
|
|
|
5077
|
-
§completion-defers-to-messages **Conclusion does not cross an
|
|
5303
|
+
§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
5304
|
|
|
5079
5305
|
§packet-catalog **Catalogs are query results, not packet state.** The packet
|
|
5080
5306
|
stores no materialized manifest. Complete and one-level entry directories,
|
|
@@ -5172,7 +5398,21 @@ retain distinct contracts and lifetimes.
|
|
|
5172
5398
|
|
|
5173
5399
|
§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
5400
|
|
|
5175
|
-
§digest-cache-ledger **
|
|
5401
|
+
§digest-cache-ledger **Measured cache reuse and estimated prompt overlap are separate.**
|
|
5402
|
+
|
|
5403
|
+
| Projection | Meaning |
|
|
5404
|
+
|---|---|
|
|
5405
|
+
| `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. |
|
|
5406
|
+
| Turn `cache=<cached>/<input>` | Sum each measured quantity over that turn's requests. If any request omits a quantity, that sum is `?`. |
|
|
5407
|
+
| 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)`. |
|
|
5408
|
+
| `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. |
|
|
5409
|
+
|
|
5410
|
+
The prefix estimate uses the stored emission packet's wire message order, roles
|
|
5411
|
+
and content. A BARE request's input is not that packet; its prefix estimate and
|
|
5412
|
+
the following request's comparison are unknown. The estimate is
|
|
5413
|
+
neither provider tokenization nor a cache ceiling, and never supplies a cache-ratio
|
|
5414
|
+
denominator. Caching across loops or against other provider-resident prefixes
|
|
5415
|
+
does not make the measured counters inconsistent.
|
|
5176
5416
|
|
|
5177
5417
|
§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
5418
|
|
|
@@ -5293,8 +5533,10 @@ enable | disable | remove`, `workspace.members.<verb>` for the client,
|
|
|
5293
5533
|
```` ```members (<verb>) ```` for the model — for what the model may see, exactly as they do for skills and
|
|
5294
5534
|
MCP servers. A definition is one gitignore-style glob, `{ glob }`, relative to the project
|
|
5295
5535
|
root; a leading `!` excludes matching members, and an exclusion wins over every inclusion.
|
|
5296
|
-
|
|
5297
|
-
rides the definition
|
|
5536
|
+
Admission provenance (`service-configuration`, `client-action`, `model-proposal`)
|
|
5537
|
+
rides the definition to enforce {§members-model-scope}; configuration-source
|
|
5538
|
+
provenance belongs to the shared inspection projection ({§configuration-provenance}).
|
|
5539
|
+
The alias is a short name, suggested from the glob. `list` shows each
|
|
5298
5540
|
definition with what it resolved to — `include` or `exclude`, the pattern, the members it
|
|
5299
5541
|
admits or removes (count and a bounded sample), and for a model's inclusion the matches the
|
|
5300
5542
|
repository's ignore rules refused — so the model sees what its glob did and adapts.
|
|
@@ -5303,10 +5545,12 @@ repository's ignore rules refused — so the model sees what its glob did and ad
|
|
|
5303
5545
|
untracked, absent); a glob previews what `add` would include or exclude. Names only, never
|
|
5304
5546
|
content; nothing is added.
|
|
5305
5547
|
|
|
5306
|
-
§members-configuration *Available definitions.*
|
|
5307
|
-
(`!glob` excludes)
|
|
5308
|
-
|
|
5309
|
-
|
|
5548
|
+
§members-configuration *Available definitions.* `PLURNK_MEMBERS_<alias>=<glob>`
|
|
5549
|
+
declares one service-origin rule (`!glob` excludes). The shared naming and
|
|
5550
|
+
enabledness dialect is {§resource-environment}; `PLURNK_MEMBERS_ENABLED` supplies
|
|
5551
|
+
the panel default and `PLURNK_MEMBERS_<alias>_ENABLED` overrides one rule.
|
|
5552
|
+
An empty glob or a bare `!` fails validation. Controls may precede their rule;
|
|
5553
|
+
they are validated without creating a definition ({§resource-environment}).
|
|
5310
5554
|
|
|
5311
5555
|
§members-model-scope *The model's authority.* A model's `add` is admitted against
|
|
5312
5556
|
`PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` in the file-creation lattice `none < root <
|
|
@@ -5338,53 +5582,98 @@ verbs are these verbs.
|
|
|
5338
5582
|
§skills-functionality **Agent Skills are one workspace Functionality family.**
|
|
5339
5583
|
Core registers the `skills` family with the coordinator ({§functionality-coordinator});
|
|
5340
5584
|
its adapter owns protocol truth for standard Agent Skills and nothing else. A
|
|
5341
|
-
definition is `SkillDefinition
|
|
5342
|
-
`
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5585
|
+
definition is `SkillDefinition`: the standard skill `name`, its `source`, and
|
|
5586
|
+
optional Git `ref`/resolved `commit` ({§skills-sources}). Only a host-provided
|
|
5587
|
+
resource tree omits `source`. Source location is not mutation ownership: standard
|
|
5588
|
+
project/global roots are read-only configuration inputs; live changes belong to
|
|
5589
|
+
the workspace. Plurnk neither installs into nor deletes from those roots.
|
|
5346
5590
|
|
|
5347
5591
|
*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
|
-
|
|
5592
|
+
every `<root>/<name>/SKILL.md` directory under the project, plurnk, then global
|
|
5593
|
+
root is one service-origin definition, a nearer root shadowing a farther one and
|
|
5594
|
+
all of them shadowing host-provided trees by name. Each filesystem definition
|
|
5595
|
+
names its actual source directory. The workspace's durable state owns
|
|
5596
|
+
enablement ({§functionality-state}); a disabled skill stays client-visible and
|
|
5597
|
+
leaves no model-facing trace.
|
|
5598
|
+
|
|
5599
|
+
§skills-configuration **Skills use the shared definition cascade.**
|
|
5600
|
+
|
|
5601
|
+
| Layer, low to high | Definition source |
|
|
5602
|
+
|---|---|
|
|
5603
|
+
| Service | Host-provided trees |
|
|
5604
|
+
| Standard locations | Global, plurnk, then project roots selected by {§agent-roots} |
|
|
5605
|
+
| Cascading environment | `PLURNK_SKILLS_<name>={"name":"<name>","source":"…","ref"?:"…"}` replaces the complete definition |
|
|
5606
|
+
| Live workspace | `skills (add)` creates a workspace definition through {§functionality-coordinator} |
|
|
5607
|
+
|
|
5608
|
+
`PLURNK_SKILLS_ENABLED` supplies default enabledness; `<name>_ENABLED` overrides
|
|
5609
|
+
it independently ({§resource-environment}). The decoded environment alias must
|
|
5610
|
+
equal the standard skill name, including digit-leading and Unicode names.
|
|
5611
|
+
Blank definitions are invalid; controls may precede a definition. Environment
|
|
5612
|
+
validation checks shape, names, remote URL rules, and Git-only `ref` without
|
|
5613
|
+
fetching or opening a source; `commit` is service-recorded, not an input.
|
|
5614
|
+
|
|
5615
|
+
*Discovery is inert.* `discover {source}` lists the standard skills one source
|
|
5616
|
+
carries, each a candidate with `source` provenance and the exact definition to
|
|
5617
|
+
add; it never installs, persists, or enables. Agent Skills have no standard
|
|
5618
|
+
registry, so `discover {query}` is 400 `query-unsupported`, naming the source
|
|
5619
|
+
forms. Client `configuration` contributes nothing and is refused with 400.
|
|
5620
|
+
|
|
5621
|
+
*Admission.* `add {alias, definition}` requires `alias = name` and a `source`;
|
|
5622
|
+
the workspace definition may shadow a service skill of the same name. Relative
|
|
5623
|
+
sources require a project root; absolute sources work in headless workspaces.
|
|
5624
|
+
A local source is recorded as its absolute path.
|
|
5625
|
+
A git source records the `commit` its `ref` names at admission, or its default
|
|
5626
|
+
branch's when no `ref` is given; `ref` belongs to git sources, and a supplied
|
|
5627
|
+
`commit` is refused because the service records it. The family's aliases use
|
|
5628
|
+
the standard skill-name grammar ({§agent-skills-name}), including digit-leading
|
|
5629
|
+
and Unicode names, rather than the coordinator's generic default.
|
|
5368
5630
|
|
|
5369
5631
|
*Preparation.* For each enabled alias the adapter selects the host-provided
|
|
5370
|
-
tree
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5632
|
+
tree or resolves the complete source definition ({§skills-sources}). Local
|
|
5633
|
+
folders and their `SKILL.md` files remain live references, including supporting
|
|
5634
|
+
resources and symlink retargeting. Git/archive sources are materialized only
|
|
5635
|
+
inside {§module-workspace-directory}; different workspaces and complete source
|
|
5636
|
+
definitions cannot reuse each other's materializations accidentally.
|
|
5637
|
+
The first fetched Git/archive copy remains stable across enable, cooling, and
|
|
5638
|
+
restart until the complete source definition changes. In particular, an
|
|
5639
|
+
operator-configured symbolic Git ref is not an implicit update subscription.
|
|
5377
5640
|
Each admitted skill requires standard `name` and `description` frontmatter
|
|
5378
5641
|
with `name` matching its directory. A missing, uninstallable, or invalid skill
|
|
5379
|
-
is `unavailable` with its exact Problem (
|
|
5380
|
-
|
|
5381
|
-
|
|
5642
|
+
is `unavailable` with its exact Problem ({§problems-functionality}) under the
|
|
5643
|
+
coordinator's failure policy ({§functionality-model-mutation}); one bad skill
|
|
5644
|
+
never fails the family.
|
|
5382
5645
|
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5646
|
+
Removal follows {§skills-remove}.
|
|
5647
|
+
|
|
5648
|
+
§skills-sources **A source is a git remote, a folder, or a file, read with standard tools.**
|
|
5649
|
+
Fetching runs nothing it fetched. Materialized copies cannot contain references
|
|
5650
|
+
outside their skill; live resources retain {§agent-skills-directory} containment.
|
|
5651
|
+
|
|
5652
|
+
| Source | How it is read |
|
|
5653
|
+
|---|---|
|
|
5654
|
+
| 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` |
|
|
5655
|
+
| Folder: absolute, `~/`, or relative to the project root | Live reference; standard directory/name matching applies |
|
|
5656
|
+
| A file named `SKILL.md` | Live reference to its skill directory and supporting resources |
|
|
5657
|
+
| 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 |
|
|
5658
|
+
|
|
5659
|
+
Any other scheme, plain `http`, `owner/repo` shorthand, and an https URL carrying
|
|
5660
|
+
credentials are refused with `source-invalid` or `source-missing`: shorthand names no
|
|
5661
|
+
forge, and a recorded source is listed to every client. Git runs with the
|
|
5662
|
+
operator's configuration, credentials, and SSH agent, never plurnk's secrets, and
|
|
5663
|
+
never prompts; `PLURNK_SERVICE_SKILLS_FETCH_TIMEOUT_MS` bounds each fetch. The
|
|
5664
|
+
retired vendor-installer knobs (`PLURNK_SERVICE_SKILLS_CLI`, `_CLI_TIMEOUT_MS`,
|
|
5665
|
+
`_REGISTRY_URL`, `_REGISTRY_LIMIT`, `_REGISTRY_TIMEOUT_MS`) make the skills family
|
|
5666
|
+
unavailable when set, each naming what replaced it ({§configuration-repair-path}).
|
|
5667
|
+
|
|
5668
|
+
A source's skills are the directories holding a `SKILL.md`, found by walking
|
|
5669
|
+
from its root without entering `.git` or a skill already found. A fetched skill at
|
|
5670
|
+
the root is named by its frontmatter ({§agent-skills-name}); local references and
|
|
5671
|
+
directories below the root use the standard folder rule. A source with `plugin.json` at its root is an
|
|
5672
|
+
Agent Plugin and is refused with 422 `source-is-plugin`, so its skills keep the
|
|
5673
|
+
plugin's identity. Materialization copies the named skill beside its workspace-owned destination
|
|
5674
|
+
and renames it to `<root>/<name>`; a copy that holds a link out of the skill, or
|
|
5675
|
+
anything but files, directories, and inward links, is refused with 422
|
|
5676
|
+
`source-unsafe` and leaves nothing behind.
|
|
5388
5677
|
|
|
5389
5678
|
§skills-resources **A skill is a resource tree, not a rewritten document.**
|
|
5390
5679
|
The family exposes enabled, available {§agent-skills-tree} sources through
|
|
@@ -5401,13 +5690,13 @@ serialized URI address the same resource, not separate skill identities.
|
|
|
5401
5690
|
| `READ (skill://<name>/SKILL.md)` | Original frontmatter and Markdown, unchanged; relative links remain relative to the source layout. |
|
|
5402
5691
|
| `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
5692
|
| ```` ```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
|
|
5693
|
+
| Model mutation | Read-only; no EDIT, SEND, or KILL of skill resources. Manage definitions and enablement through ```` ```skills ````. |
|
|
5405
5694
|
| Disable / unavailable / remove | Withdraw the authority from new resource access and discovery. Existing log receipts remain historical evidence. |
|
|
5406
5695
|
| WORK / FORK | Use the same workspace Functionality, not copied definitions or resource caches. |
|
|
5407
5696
|
|
|
5408
5697
|
Explicit skill URIs address these resources; bare operation paths still address
|
|
5409
5698
|
project files, with no implicit current-skill directory. Source resolution follows
|
|
5410
|
-
{§agent-skills-directory}, including
|
|
5699
|
+
{§agent-skills-directory}, including symlinked skill directories and containment of references.
|
|
5411
5700
|
An uninstalled Git skill is not manufactured by repository detection.
|
|
5412
5701
|
|
|
5413
5702
|
§plurnk-skill **Plurnk's own reference is an ordinary service-provided skill.**
|
|
@@ -5420,24 +5709,22 @@ same {§operator-config-env-defaults} renderer as the operator command, never th
|
|
|
5420
5709
|
effective environment. Native chapter files retain their owners and locations;
|
|
5421
5710
|
runtime-generated bytes have no invented disk location. Disable/enable,
|
|
5422
5711
|
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
|
|
5712
|
+
Service-provided skills are not installer targets; removal follows {§skills-remove}.
|
|
5713
|
+
|
|
5714
|
+
§skills-remove **`remove` forgets the workspace binding, not the source.**
|
|
5715
|
+
It withdraws that definition and restores any inherited definition and enabledness
|
|
5716
|
+
({§functionality-coordinator}). External folders are never deleted. Fetched
|
|
5717
|
+
materializations remain workspace-owned operational state, reusable only for the
|
|
5718
|
+
same complete definition; they confer no availability without a definition.
|
|
5719
|
+
Inherited definitions cannot be removed here; their enabledness can be overridden.
|
|
5720
|
+
|
|
5721
|
+
§skills-hotload **Skills placed out of band are admitted at the next turn** ({§functionality-hotload}). The
|
|
5722
|
+
family keeps one signature of the discovered roots, configured definitions, source locations, and `SKILL.md` sources
|
|
5723
|
+
per resident workspace, read before a publication loads the skills it describes. Turn admission
|
|
5724
|
+
recomputes it under the workspace gate before packet assembly: a changed signature republishes the
|
|
5725
|
+
family, and an unchanged one republishes only when the skills the coordinator would publish differ
|
|
5726
|
+
from the published ones. A skill installed, edited or removed by any other tool is therefore
|
|
5727
|
+
discoverable in the first subsequent model turn, while an unchanged set dispatches nothing. The model manages skills
|
|
5441
5728
|
only through the generated ```` ```skills ```` family
|
|
5442
5729
|
({§functionality-model-projection}); it is never taught a package manager.
|
|
5443
5730
|
|
|
@@ -5896,6 +6183,7 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5896
6183
|
|---|---:|---|
|
|
5897
6184
|
| `service-starting` | 503 | The PLURNK service owns this listener but has not admitted its client interface yet. |
|
|
5898
6185
|
| `configuration-unsupported` | 400 | Environment discovery reads this installation's declared configuration; client configuration contributes nothing. |
|
|
6186
|
+
| `configuration-invalid` | 503 | The owning configuration reader's diagnostic, naming the invalid key. Recovery: Correct the named configuration input. Other capabilities remain available. |
|
|
5899
6187
|
| `name-reserved` | 400 | '*alias*' is plurnk's own: PLURNK_* configuration and provider credential names never reach a subprocess. |
|
|
5900
6188
|
| `value-invalid` | 400 | '*alias*' needs a string value. |
|
|
5901
6189
|
| `env-invalid` | 400 | `env` must be an object of string values; '*name*' is not a name a shell can export. |
|
|
@@ -5903,14 +6191,19 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5903
6191
|
| `headless` | 409 | The workspace has no project root, so there are no file members. Recovery: Open the workspace on a project root. |
|
|
5904
6192
|
| `definition-invalid` | 400 | A members definition is { glob }: a gitignore-style pattern, `!glob` to exclude; a skill definition names an installable skill. |
|
|
5905
6193
|
| `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
|
-
| `
|
|
6194
|
+
| `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. |
|
|
6195
|
+
| `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. |
|
|
6196
|
+
| `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). |
|
|
6197
|
+
| `source-unreadable` | 422 | '*source*' cannot be read: *cause*; or '*path*' could not be unpacked: *reason*. |
|
|
6198
|
+
| `source-unreachable` | 502 | git could not reach '*remote*', or fetch it (at '*ref*'): *reason*. |
|
|
6199
|
+
| `ref-missing` | 404 | '*remote*' has no branch or tag '*ref*', or names no default branch. |
|
|
6200
|
+
| `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. |
|
|
6201
|
+
| `source-is-plugin` | 422 | '*source*' is an Agent Plugin; install it as a plugin, so its skills keep the plugin's identity and servers. |
|
|
6202
|
+
| `source-unsafe` | 422 | '*path*' links outside its skill, or is neither a file, a directory, nor an inward link. |
|
|
6203
|
+
| `skill-not-found` | 404 | '*source*' carries no Agent Skill named '*name*'. |
|
|
6204
|
+
| `skill-ambiguous` | 409 | '*source*' carries *count* skills named '*name*'. |
|
|
5910
6205
|
| `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*. |
|
|
6206
|
+
| `source-required` | 400 | Adding '*alias*' requires the source that provides it. |
|
|
5914
6207
|
| `workspace-not-found` | 404 | Workspace *id* does not exist. |
|
|
5915
6208
|
| `state-not-json` | 400 | Worker module state is not JSON-serializable. |
|
|
5916
6209
|
| `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 +6216,8 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5923
6216
|
| `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
6217
|
| `scope-cancelled` | 499 | The worker scope was cancelled: *reason*. |
|
|
5925
6218
|
| `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. |
|
|
6219
|
+
| `install-failed` | 500 | Agent Skill '*name*' could not be placed under *root*: *cause*. |
|
|
6220
|
+
| `skill-missing` | 404 | Agent Skill '*alias*' is not provided by this service. |
|
|
5929
6221
|
| `skill-invalid` | 422 | Agent Skill '*alias*' is not a valid standard skill: *cause*. |
|
|
5930
6222
|
|
|
5931
6223
|
§pinned-wording-core **Pinned wording.** Verbatim sentences tests pin: each is contract, and a change here is a change of contract.
|