@plurnk/plurnk-service 0.16.0 → 0.17.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/SPEC.md +10 -2
- package/dist/Paths.d.ts +1 -0
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +5 -0
- package/dist/Paths.js.map +1 -1
- package/dist/core/Engine.d.ts +5 -0
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +106 -9
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/ExecutorRegistry.d.ts +9 -1
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +21 -28
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +2 -0
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +52 -0
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/git-state.d.ts +15 -0
- package/dist/core/git-state.d.ts.map +1 -0
- package/dist/core/git-state.js +65 -0
- package/dist/core/git-state.js.map +1 -0
- 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 +8 -1
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/scheme-types.d.ts +9 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/schemes/Exec.d.ts +4 -0
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +17 -1
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/File.d.ts +2 -11
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +2 -10
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +5 -1
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/server/ClientConnection.d.ts.map +1 -1
- package/dist/server/ClientConnection.js +5 -2
- package/dist/server/ClientConnection.js.map +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +8 -0
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/MethodRegistry.d.ts +5 -0
- package/dist/server/MethodRegistry.d.ts.map +1 -1
- package/dist/server/MethodRegistry.js.map +1 -1
- package/dist/server/methods/loop_run.d.ts.map +1 -1
- package/dist/server/methods/loop_run.js +1 -0
- package/dist/server/methods/loop_run.js.map +1 -1
- package/dist/server/methods/session_attach.d.ts.map +1 -1
- package/dist/server/methods/session_attach.js +0 -3
- package/dist/server/methods/session_attach.js.map +1 -1
- package/dist/server/methods/session_create.d.ts.map +1 -1
- package/dist/server/methods/session_create.js +1 -3
- package/dist/server/methods/session_create.js.map +1 -1
- package/migrations/0000-00-00.01_schema.sql +1 -1
- package/package.json +11 -15
package/SPEC.md
CHANGED
|
@@ -338,7 +338,7 @@ Every entry is uniformly listed in `plurnk://manifest.json` (§15) and READable
|
|
|
338
338
|
|
|
339
339
|
Mimetype is declared by scheme manifest (§3.1) or supplied per-call for dynamic schemes. Writing a channel without a declared mimetype throws. No default mimetype anywhere.
|
|
340
340
|
|
|
341
|
-
- Cross-mimetype COPY/MOVE → 415, never coerces (§6.4).
|
|
341
|
+
- Cross-mimetype COPY/MOVE → 415, never coerces (§6.4). {§5.3-cross-mimetype-415}
|
|
342
342
|
|
|
343
343
|
### §5.5 Channel selection in the DSL
|
|
344
344
|
|
|
@@ -730,12 +730,17 @@ registry.register("loop.run", {
|
|
|
730
730
|
|
|
731
731
|
| Method | Params | Result | Notes |
|
|
732
732
|
|------------------------|---------------------|-------------------|-------|
|
|
733
|
-
| `session.create` | `name?: string`, `projectRoot?: string`, `persona?: string` | `{ id, name, projectRoot, persona }` | Creates new session; auto-name if unprovided. Optional `projectRoot` pins the workspace (null/omitted = headless). Optional `persona` sets the session-level persona override. |
|
|
733
|
+
| `session.create` | `name?: string`, `projectRoot?: string`, `persona?: string` | `{ id, name, runId, runName, projectRoot, persona }` | Creates new session + its first run; auto-name if unprovided. Returns the auto-created run's identity so clients skip the pending-dance ({§13.5-session-create}). Optional `projectRoot` pins the workspace (null/omitted = headless). Optional `persona` sets the session-level persona override. |
|
|
734
734
|
| `session.list` | none | `{ sessions: Session[] }` | Lists all sessions. |
|
|
735
735
|
| `session.attach` | `id: number`, `runId?: number`, `runName?: string`, `persona?: string` | `{ id, name, runId, runName }` | Binds this connection to an existing session. Optional `runId` resumes that specific run (must belong to the session). Optional `runName` reuses-or-creates by name within the session. Both omitted → new auto-named run. Optional `persona` sets run-level persona only when a NEW run is created. {§13.5-session-attach} |
|
|
736
736
|
| `session.runs` | `id?: number` | `{ runs: Run[] }` | Lists runs in a session (defaults to attached session); most-recent first. |
|
|
737
737
|
| `session.set_root` | `projectRoot: string \| null` | `{ projectRoot }` | Update the workspace pointer on the attached session. Null reverts to headless. |
|
|
738
738
|
| `session.set_persona` | `persona: string \| null` | `{ persona }` | Update the session-level persona. Null clears the override (falls through to PLURNK_PERSONA file). |
|
|
739
|
+
| `session.constrain` | `effect: "add" \| "ignore" \| "read-only"`, `glob: string` | `{ effect, glob }` | Add a workspace membership constraint (§14.3 overlay): `add` admits files git misses, `ignore` drops tracked matches, `read-only` admits for read but refuses edits. Immediate. |
|
|
740
|
+
| `session.unconstrain` | `effect: "add" \| "ignore" \| "read-only"`, `glob: string` | `{ effect, glob }` | Remove a membership constraint — the inverse of `session.constrain`. Immediate. |
|
|
741
|
+
| `session.constraints` | none | `{ constraints }` | List the attached session's membership constraints. |
|
|
742
|
+
|
|
743
|
+
**Re-binding.** `session.create` and `session.attach` may be called on a connection that already has a session attached — the connection switches in place, releasing the prior client loop (closed at 200). No reconnect needed to change session or run. {§13.5-rebind}
|
|
739
744
|
|
|
740
745
|
**Auto-envelope.** Clients calling a `requiresInit: true` method without first attaching get auto-created session → run → client loop. Records persist normally; auto-created ≠ auto-deleted. Cleanup is a future `session.delete` / `session.archive` endpoint. {§13.5-auto-envelope}
|
|
741
746
|
|
|
@@ -745,6 +750,7 @@ registry.register("loop.run", {
|
|
|
745
750
|
|-------------------|-------------------------------------|------------------------|-------|
|
|
746
751
|
| `loop.run` | `prompt: string`, `maxTurns?: number`, `alias?: string`, `flags?: LoopFlags`, `persona?: string` | `{ loopId, turnIds, finalStatus, hitMaxTurns, reason }` | Model-driven loop. Optional `alias` overrides the boot-time `PLURNK_MODEL`. Optional `flags` carries per-loop flags (currently `{yolo?: boolean}`; more arrive as wired — see §0.5). Optional `persona` sets the loop-level persona (highest precedence in the cascade). Streams `log/entry` and `loop/proposal` notifications during. `longRunning: true`. {§13.5-loop-run} |
|
|
747
752
|
| `loop.resolve` | `logEntryId: number`, `decision: "accept" \| "reject" \| "cancel"`, `body?: string`, `outcome?: string` | `{ status, logEntryId }` | Resolve a pending proposal (status=202 log entry). Engine.dispatch unpauses on resolution. |
|
|
753
|
+
| `loop.cancel` | `reason?: string` | `{ cancelled, runId, reason }` | Abort the attached run's active drain. `{cancelled: true}` if a drain was running, `{false}` if idle. Cancelled loops close at 499; queued-but-unclaimed loops stay enqueued. Default reason `user_cancelled`. {§13.5-loop-cancel} |
|
|
748
754
|
| `providers.list` | none | `{ aliases: ProviderAlias[] }` | Lists configured `PLURNK_MODEL_<alias>` entries with `{alias, provider, model, active}`. Clients use to populate model-selection UI. |
|
|
749
755
|
|
|
750
756
|
**Reads**
|
|
@@ -791,6 +797,8 @@ Server-initiated events on the same WebSocket.
|
|
|
791
797
|
| `loop/proposal` | `{ logEntryId, sessionId, runId, loopId, turnId, op, target, body, attrs, flags }` | Dispatch pauses on status=202. Carries `flags` so server-YOLO clients can suppress review UI. Client responds with `loop.resolve` (or `PLURNK_PROPOSAL_TIMEOUT_MS` fires). |
|
|
792
798
|
| `session/created` | `{ id, name, projectRoot, persona }` | Any client creates a session. |
|
|
793
799
|
| `stream/event` | `{ entryId, channel, state, contentLength }` | Channel content grows or state transitions. {§13.6-stream-event-on-channel-change} |
|
|
800
|
+
| `stream/concluded` | `{ entryId, target, subscriptionId, scheme, closeStatus, summary, wakeAction, wakeLoopId? }` | A streaming subscription closed (subprocess finished / errored / cancelled). `wakeAction` says whether the daemon opened a fresh loop to surface the conclusion to the model. {§13.6-stream-concluded} |
|
|
801
|
+
| `telemetry/event` | `{ loopId, event: TelemetryEvent }` | A TelemetryEvent (parse error, engine-rail strike/cycle/sudden-death, scheme/provider failure) was buffered — the same envelope the model sees on the next packet, delivered live for client surfacing. {§13.6-telemetry-event} |
|
|
794
802
|
|
|
795
803
|
`stream/event` carries metadata only, never content. Clients fetch via `entry.read({target})`. **Every notification envelope carries its `sessionId`** (and `runId` where the emitter has it) so a multi-session client — one connection, many sessions — can route it ({§13.6-envelope-carries-sessionid}); the broadcast stays session-scoped too.
|
|
796
804
|
|
package/dist/Paths.d.ts
CHANGED
package/dist/Paths.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Paths.d.ts","sourceRoot":"","sources":["../src/Paths.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,OAAO,OAAO,KAAK;;IAItB,MAAM,CAAC,UAAU,SAA8C;IAC/D,MAAM,CAAC,kBAAkB,SAA6C;IAKtE,MAAM,CAAC,cAAc,SAAkC;IAIvD,MAAM,CAAC,mBAAmB,SAAuC;CAuBpE"}
|
|
1
|
+
{"version":3,"file":"Paths.d.ts","sourceRoot":"","sources":["../src/Paths.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,OAAO,OAAO,KAAK;;IAItB,MAAM,CAAC,UAAU,SAA8C;IAC/D,MAAM,CAAC,kBAAkB,SAA6C;IAKtE,MAAM,CAAC,WAAW,SAAoD;IAKtE,MAAM,CAAC,cAAc,SAAkC;IAIvD,MAAM,CAAC,mBAAmB,SAAuC;CAuBpE"}
|
package/dist/Paths.js
CHANGED
|
@@ -14,6 +14,11 @@ export default class Paths {
|
|
|
14
14
|
static #GRAMMAR_ROOT = dirname(fileURLToPath(import.meta.resolve("@plurnk/plurnk-grammar/package.json")));
|
|
15
15
|
static migrations = resolve(Paths.#PACKAGE_ROOT, "migrations");
|
|
16
16
|
static instructionsSystem = resolve(Paths.#GRAMMAR_ROOT, "plurnk.md");
|
|
17
|
+
// The GBNF artifact (the full multi-op root) for grammar-constrained
|
|
18
|
+
// sampling, resolved from the grammar package like the sysprompt above.
|
|
19
|
+
// Plumbed to the provider per generate() when PLURNK_PROVIDERS_GBNF is
|
|
20
|
+
// enabled; the service holds no opinion about its content or root.
|
|
21
|
+
static grammarGbnf = resolve(Paths.#GRAMMAR_ROOT, "dist/plurnk.gbnf");
|
|
17
22
|
// packet.system.persona DEFAULT. Cascade at packet-build time is
|
|
18
23
|
// loops.persona > runs.persona > sessions.persona > this file
|
|
19
24
|
// RPC overrides on loop.run / session.attach / session.create populate
|
package/dist/Paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Paths.js","sourceRoot":"","sources":["../src/Paths.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,gDAAgD;AAChD,+EAA+E;AAC/E,6EAA6E;AAC7E,oCAAoC;AACpC,EAAE;AACF,8EAA8E;AAC9E,mDAAmD;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,KAAK;IACtB,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9E,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,CAAC,CAAC;IAE1G,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,CAAC,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACtE,iEAAiE;IACjE,gEAAgE;IAChE,uEAAuE;IACvE,iEAAiE;IACjE,MAAM,CAAC,cAAc,GAAG,KAAK,CAAC,sBAAsB,EAAE,CAAC;IACvD,uEAAuE;IACvE,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,CAAC,mBAAmB,GAAG,KAAK,CAAC,2BAA2B,EAAE,CAAC;IAEjE,yEAAyE;IACzE,kEAAkE;IAClE,mEAAmE;IACnE,oDAAoD;IACpD,MAAM,CAAC,sBAAsB;QACzB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QACvC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IACtD,CAAC;IAED,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,CAAC,2BAA2B;QAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAC5C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;IAC3D,CAAC"}
|
|
1
|
+
{"version":3,"file":"Paths.js","sourceRoot":"","sources":["../src/Paths.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,gDAAgD;AAChD,+EAA+E;AAC/E,6EAA6E;AAC7E,oCAAoC;AACpC,EAAE;AACF,8EAA8E;AAC9E,mDAAmD;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,OAAO,OAAO,KAAK;IACtB,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9E,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,CAAC,CAAC;IAE1G,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,CAAC,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACtE,qEAAqE;IACrE,wEAAwE;IACxE,uEAAuE;IACvE,mEAAmE;IACnE,MAAM,CAAC,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;IACtE,iEAAiE;IACjE,gEAAgE;IAChE,uEAAuE;IACvE,iEAAiE;IACjE,MAAM,CAAC,cAAc,GAAG,KAAK,CAAC,sBAAsB,EAAE,CAAC;IACvD,uEAAuE;IACvE,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,CAAC,mBAAmB,GAAG,KAAK,CAAC,2BAA2B,EAAE,CAAC;IAEjE,yEAAyE;IACzE,kEAAkE;IAClE,mEAAmE;IACnE,oDAAoD;IACpD,MAAM,CAAC,sBAAsB;QACzB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QACvC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IACtD,CAAC;IAED,4EAA4E;IAC5E,2EAA2E;IAC3E,MAAM,CAAC,2BAA2B;QAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAC5C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;IAC3D,CAAC"}
|
package/dist/core/Engine.d.ts
CHANGED
|
@@ -68,6 +68,11 @@ export default class Engine {
|
|
|
68
68
|
tokenize?: (text: string) => number;
|
|
69
69
|
});
|
|
70
70
|
setExecutors(executors: ExecutorRegistry): void;
|
|
71
|
+
loopUsage(loopId: number): Promise<{
|
|
72
|
+
promptTokens: number;
|
|
73
|
+
completionTokens: number;
|
|
74
|
+
costPico: number;
|
|
75
|
+
}>;
|
|
71
76
|
runLoop({ provider, messages, persona, requirements, sessionId, runId, loopId, maxTurns, maxStrikes, minCycles, maxCyclePeriod, origin, signal, onDispatch, }: {
|
|
72
77
|
provider: Provider;
|
|
73
78
|
messages: ChatMessage[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAA4D,MAAM,wBAAwB,CAAC;AAMxH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAiB,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"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"}
|
package/dist/core/Engine.js
CHANGED
|
@@ -4,15 +4,19 @@ import { Mimetypes, emptyRegistry } from "@plurnk/plurnk-mimetypes";
|
|
|
4
4
|
import EntryCrud from "../schemes/_entry-crud.js";
|
|
5
5
|
import EntryManifest from "../schemes/_entry-manifest.js";
|
|
6
6
|
import GitMembership from "./git-membership.js";
|
|
7
|
+
import GitState from "./git-state.js";
|
|
7
8
|
import { DEFAULT_LOOP_FLAGS } from "./scheme-types.js";
|
|
8
9
|
import { LineMarkerOps, MimetypeBinary, editedSpan } from "../content/index.js";
|
|
10
|
+
import { readFile } from "node:fs/promises";
|
|
11
|
+
import Paths from "../Paths.js";
|
|
12
|
+
import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
|
|
9
13
|
// Shared module imported by both Engine and bin/digest.ts, so wire
|
|
10
14
|
// projection and digest projection are structurally one function — no
|
|
11
15
|
// drift between wire and digest possible.
|
|
12
16
|
import PacketWire from "./packet-wire.js";
|
|
13
17
|
// SPEC §3.6: writer must be in target scheme's manifest.writableBy.
|
|
14
18
|
// OPEN/FOLD/READ/FIND are not gated — they curate the log or read, never mutating an entry.
|
|
15
|
-
const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC"]);
|
|
19
|
+
const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC", "KILL"]);
|
|
16
20
|
const DEFAULT_MAX_STRIKES = 3;
|
|
17
21
|
const DEFAULT_MAX_COMMANDS = 99;
|
|
18
22
|
const DEFAULT_BUDGET_CEILING = 0.9;
|
|
@@ -228,6 +232,8 @@ class Engine {
|
|
|
228
232
|
// status code but has no way to surface why the loop degraded.
|
|
229
233
|
// Per-grammar 0.17.0 protocol — see SPEC §15.1.
|
|
230
234
|
#telemetryEventNotify;
|
|
235
|
+
// Cached plurnk GBNF — read once on the first constrained generate (#189).
|
|
236
|
+
#gbnfCache = null;
|
|
231
237
|
constructor({ db, schemes, mimetypes, streamEventNotify, wakeRunNotify, telemetryEventNotify, tokenize }) {
|
|
232
238
|
this.#db = db;
|
|
233
239
|
this.#schemes = schemes;
|
|
@@ -252,6 +258,28 @@ class Engine {
|
|
|
252
258
|
setExecutors(executors) {
|
|
253
259
|
this.#executors = executors;
|
|
254
260
|
}
|
|
261
|
+
// Grammar-constrained sampling (#189): when PLURNK_PROVIDERS_GBNF is enabled
|
|
262
|
+
// (the only knob — default-on in .env.example), hand the provider the plurnk
|
|
263
|
+
// GBNF (the full shipped multi-op root, read once + cached). The provider
|
|
264
|
+
// attaches it iff the backend supports it and silently drops it otherwise —
|
|
265
|
+
// capability is providers' concern, not ours. Pure plumbing grammar→provider.
|
|
266
|
+
async #grammarConstraint() {
|
|
267
|
+
if (process.env.PLURNK_PROVIDERS_GBNF !== "1")
|
|
268
|
+
return undefined;
|
|
269
|
+
this.#gbnfCache ??= await readFile(Paths.grammarGbnf, "utf8");
|
|
270
|
+
return this.#gbnfCache;
|
|
271
|
+
}
|
|
272
|
+
// Per-loop usage totals (#197): SUM the loop's turns (usage is stored per
|
|
273
|
+
// turn, §14.2). Surfaced on loop.run + loop/terminated so clients render real
|
|
274
|
+
// token/cost numbers. costPico is the stored pico-dollar unit.
|
|
275
|
+
async loopUsage(loopId) {
|
|
276
|
+
const row = await this.#db.engine_loop_usage.get({ loop_id: loopId });
|
|
277
|
+
return {
|
|
278
|
+
promptTokens: row?.prompt ?? 0,
|
|
279
|
+
completionTokens: row?.completion ?? 0,
|
|
280
|
+
costPico: row?.cost_pico ?? 0,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
255
283
|
#pushTelemetry(sessionId, loopId, event) {
|
|
256
284
|
const existing = this.#telemetryBuffer.get(loopId);
|
|
257
285
|
if (existing === undefined)
|
|
@@ -450,7 +478,7 @@ class Engine {
|
|
|
450
478
|
};
|
|
451
479
|
await this.dispatch({
|
|
452
480
|
statement: promptStmt, sessionId, runId, loopId, turnId,
|
|
453
|
-
sequence: nextActionIndex, origin: "system",
|
|
481
|
+
sequence: nextActionIndex, origin: "system", onDispatch,
|
|
454
482
|
});
|
|
455
483
|
nextActionIndex++;
|
|
456
484
|
}
|
|
@@ -487,19 +515,23 @@ class Engine {
|
|
|
487
515
|
// reconciled) as system EDIT rows, before the packet composes; advance
|
|
488
516
|
// the action index past them so model ops continue after.
|
|
489
517
|
nextActionIndex += await this.#materializeEnvironmentDeltas({ sessionId, runId, loopId, turnId, fromSequence: nextActionIndex });
|
|
518
|
+
// SPEC §15.1 — git working-tree state for the telemetry section, read once
|
|
519
|
+
// (a service-side `git status` shell-out) and threaded into the budget
|
|
520
|
+
// rebuild too so it isn't re-shelled on overflow.
|
|
521
|
+
const gitStatus = await GitState.status(this.#db, sessionId, this.#loopAborts.get(loopId)?.signal);
|
|
490
522
|
// Build the spec'd packet (Packet.json) request half. #buildLog
|
|
491
523
|
// queries log_entries scoped to the run — the prompt entry just
|
|
492
524
|
// written (if turn 1) is part of that query result.
|
|
493
525
|
let requestPacket = await this.#buildRequestPacket({
|
|
494
526
|
initialMessages: messages, persona, requirements, runId, loopId,
|
|
495
|
-
currentTurnSeq: seq, provider,
|
|
527
|
+
currentTurnSeq: seq, provider, gitStatus,
|
|
496
528
|
});
|
|
497
529
|
// SPEC §14.4 — budget grinder, pre-LLM: reclaim window on actual overflow.
|
|
498
530
|
const enforced = await this.#enforceBudget({
|
|
499
531
|
packet: requestPacket, provider, runId, loopId, turnId, sessionId, turnNumber,
|
|
500
532
|
rebuild: (telemetryErrors) => this.#buildRequestPacket({
|
|
501
533
|
initialMessages: messages, persona, requirements, runId, loopId,
|
|
502
|
-
currentTurnSeq: seq, provider, telemetryErrors,
|
|
534
|
+
currentTurnSeq: seq, provider, telemetryErrors, gitStatus,
|
|
503
535
|
}),
|
|
504
536
|
});
|
|
505
537
|
requestPacket = enforced.packet;
|
|
@@ -515,7 +547,13 @@ class Engine {
|
|
|
515
547
|
return { turnId, status: 413, statuses: [], fingerprint: "", budgetStruck: enforced.struck, budgetHardStop: true };
|
|
516
548
|
}
|
|
517
549
|
const modelMessages = this.#packetToWireMessages(requestPacket);
|
|
518
|
-
|
|
550
|
+
// maxTokens = remaining context window (loop policy, plurnk-providers#10).
|
|
551
|
+
// The 0.28.0 EOS-forcing root terminates the turn at the status SEND, but a
|
|
552
|
+
// grammar can't bound degeneration *inside* a statement body — this caps the
|
|
553
|
+
// decode at the free window so a runaway can't reach the context wall.
|
|
554
|
+
const genCeiling = _a.computeCeiling(provider.contextSize, this.#budgetCeiling);
|
|
555
|
+
const maxTokens = genCeiling === null ? undefined : Math.max(1, genCeiling - requestPacket.system.tokens - requestPacket.user.tokens);
|
|
556
|
+
const response = await provider.generate({ messages: modelMessages, signal, grammar: await this.#grammarConstraint(), maxTokens });
|
|
519
557
|
// Engine splits wire-level response: emission (content, reasoning,
|
|
520
558
|
// parsed ops) → packet.assistant per Packet.json §assistant;
|
|
521
559
|
// call-metadata (usage, finishReason, model) → Turn columns per
|
|
@@ -676,7 +714,7 @@ class Engine {
|
|
|
676
714
|
// and §user) BEFORE the provider call. The same packet object is then
|
|
677
715
|
// completed with assistant + assistantRaw after the model responds, so
|
|
678
716
|
// the stored packet and the wire payload share one source of truth.
|
|
679
|
-
async #buildRequestPacket({ initialMessages, persona: defaultPersona, requirements, runId, loopId, currentTurnSeq, provider, telemetryErrors: presetTelemetry, }) {
|
|
717
|
+
async #buildRequestPacket({ initialMessages, persona: defaultPersona, requirements, runId, loopId, currentTurnSeq, provider, gitStatus, telemetryErrors: presetTelemetry, }) {
|
|
680
718
|
const byRole = (role) => initialMessages.filter((m) => m.role === role).map((m) => m.content).join("\n\n");
|
|
681
719
|
const system_definition = byRole("system");
|
|
682
720
|
// user.prompt sources from the loop's most recent prompt entry first
|
|
@@ -711,7 +749,7 @@ class Engine {
|
|
|
711
749
|
const ceiling = _a.computeCeiling(provider.contextSize, this.#budgetCeiling);
|
|
712
750
|
const scratch = {
|
|
713
751
|
system: { system_definition, persona, log },
|
|
714
|
-
user: { prompt, telemetry: { budget: "", errors: telemetryErrors }, system_requirements: requirements },
|
|
752
|
+
user: { prompt, telemetry: { budget: "", errors: telemetryErrors, git: gitStatus }, system_requirements: requirements },
|
|
715
753
|
};
|
|
716
754
|
const sections = PacketWire.measureBudgetSections(scratch, countTokens);
|
|
717
755
|
scratch.user.telemetry.budget = this.#renderBudget(sections, ceiling);
|
|
@@ -725,7 +763,7 @@ class Engine {
|
|
|
725
763
|
.replace(TOKEN_PERCENT_PLACEHOLDER, String(percent))
|
|
726
764
|
.replace(TOKENS_FREE_PLACEHOLDER, String(tokensFree));
|
|
727
765
|
const system = { tokens: 0, system_definition, persona, log };
|
|
728
|
-
const user = { tokens: 0, prompt, telemetry: { budget, errors: telemetryErrors }, system_requirements: requirements };
|
|
766
|
+
const user = { tokens: 0, prompt, telemetry: { budget, errors: telemetryErrors, git: gitStatus }, system_requirements: requirements };
|
|
729
767
|
system.tokens = countTokens(PacketWire.renderSystemContent(system));
|
|
730
768
|
user.tokens = countTokens(PacketWire.renderUserContent(user));
|
|
731
769
|
return { system, user };
|
|
@@ -955,6 +993,12 @@ class Engine {
|
|
|
955
993
|
else if (statement.op === "MOVE") {
|
|
956
994
|
result = await this.#handleMove(statement, schemeCtx);
|
|
957
995
|
}
|
|
996
|
+
else if (statement.op === "KILL") {
|
|
997
|
+
result = await this.#handleKill(statement, schemeCtx);
|
|
998
|
+
}
|
|
999
|
+
else if (statement.op === "PLAN") {
|
|
1000
|
+
result = this.#handlePlan(statement);
|
|
1001
|
+
}
|
|
958
1002
|
else if (statement.op === "EXEC") {
|
|
959
1003
|
// EXEC's target slot is `cwd`, not a scheme address.
|
|
960
1004
|
// Per plurnk.md the op routes unconditionally to the
|
|
@@ -1339,6 +1383,51 @@ class Engine {
|
|
|
1339
1383
|
return { status: delResult.status };
|
|
1340
1384
|
return copyResult;
|
|
1341
1385
|
}
|
|
1386
|
+
// KILL — scheme-polymorphic destroy (plurnk-grammar#203 / 0.28.0). Entry-KILL
|
|
1387
|
+
// permanently deletes the entry: the canonical delete now, MOVE→/dev/null
|
|
1388
|
+
// retired from the model's vocabulary. Process-KILL (exec://) aborts the
|
|
1389
|
+
// running spawn's controller (the same teardown loop.cancel rides), addressed
|
|
1390
|
+
// by coordinate pathname (#203). The KILL body is an opaque
|
|
1391
|
+
// annotation with no runtime meaning; it survives into the log row's tx for
|
|
1392
|
+
// free via the statement serialization. Status: 200 killed · 404 unknown ·
|
|
1393
|
+
// 405 log:// (append-only) · 403 writableBy (the #checkWritable gate, KILL ∈
|
|
1394
|
+
// MUTATING_OPS) · 200/404 exec (killed / not running) · 501 no-kill/delete scheme.
|
|
1395
|
+
async #handleKill(statement, ctx) {
|
|
1396
|
+
if (statement.op !== "KILL")
|
|
1397
|
+
throw new Error("unreachable");
|
|
1398
|
+
const path = statement.target;
|
|
1399
|
+
if (path === null)
|
|
1400
|
+
return { status: 400, error: "KILL requires a target path" };
|
|
1401
|
+
const schemeName = this.#schemeNameOf(path);
|
|
1402
|
+
if (schemeName === null)
|
|
1403
|
+
return { status: 400, error: "KILL target must be a URL path with a scheme" };
|
|
1404
|
+
if (schemeName === "log")
|
|
1405
|
+
return { status: 405, error: "log:// is append-only; KILL must bounce" };
|
|
1406
|
+
if (schemeName === "exec") {
|
|
1407
|
+
const execHandler = this.#schemes.get("exec");
|
|
1408
|
+
if (execHandler === undefined || typeof execHandler.kill !== "function")
|
|
1409
|
+
return { status: 501 };
|
|
1410
|
+
return execHandler.kill(pathnameFromPath(path));
|
|
1411
|
+
}
|
|
1412
|
+
const handler = this.#schemes.get(schemeName);
|
|
1413
|
+
if (handler === undefined || typeof handler.deleteEntry !== "function")
|
|
1414
|
+
return { status: 501 };
|
|
1415
|
+
const delResult = await handler.deleteEntry(pathnameFromPath(path), ctx);
|
|
1416
|
+
return { status: delResult.status };
|
|
1417
|
+
}
|
|
1418
|
+
// PLAN — undocumented reasoning op (plurnk-grammar 0.30.0, the 11th op). A pure
|
|
1419
|
+
// no-op: the body is the model's in-content reasoning, which lands in the log
|
|
1420
|
+
// row's tx for free via statement serialization, no runtime effect (PLAN ∉
|
|
1421
|
+
// MUTATING_OPS). NOT taught in plurnk.md by design — reserved for a future model
|
|
1422
|
+
// that must reason in the content field (no separate reasoning channel). gemma
|
|
1423
|
+
// reasons on its own channel via the relaxed root, is never shown PLAN, and never
|
|
1424
|
+
// emits it; this handler exists only so the op resolves cleanly if we ever
|
|
1425
|
+
// deliberately reach for it (which would also require a plurnk.md override).
|
|
1426
|
+
#handlePlan(statement) {
|
|
1427
|
+
if (statement.op !== "PLAN")
|
|
1428
|
+
throw new Error("unreachable");
|
|
1429
|
+
return { status: 200 };
|
|
1430
|
+
}
|
|
1342
1431
|
async #copyOrchestration({ statement, srcPath, dstPath, ctx }) {
|
|
1343
1432
|
const srcSchemeName = this.#schemeNameOf(srcPath);
|
|
1344
1433
|
const dstSchemeName = this.#schemeNameOf(dstPath);
|
|
@@ -1433,6 +1522,13 @@ class Engine {
|
|
|
1433
1522
|
const method = handler[methodName];
|
|
1434
1523
|
if (typeof method !== "function")
|
|
1435
1524
|
return { status: 501 };
|
|
1525
|
+
// External @plurnk/plurnk-schemes-* siblings receive the DB-free SchemeCtx
|
|
1526
|
+
// (caps), never the raw PlurnkSchemeContext (schemes SPEC §5). The dynamic
|
|
1527
|
+
// dispatch is typed for in-tree schemes; the cast bridges the ctx shapes —
|
|
1528
|
+
// the sibling reads caps, the in-tree handler reads db.
|
|
1529
|
+
if (this.#schemes.isExternal(schemeName)) {
|
|
1530
|
+
return method.call(handler, statement, new SchemeCtxImpl(ctx, schemeName));
|
|
1531
|
+
}
|
|
1436
1532
|
return method.call(handler, statement, ctx);
|
|
1437
1533
|
}
|
|
1438
1534
|
// Bare paths default to the file scheme per plurnk.md (grammar sysprompt):
|
|
@@ -1441,8 +1537,9 @@ class Engine {
|
|
|
1441
1537
|
#schemeNameOf(path) {
|
|
1442
1538
|
if (path === null)
|
|
1443
1539
|
return null;
|
|
1540
|
+
// http + https are one scheme — the http sibling owns both prefixes (#195).
|
|
1444
1541
|
if (path.kind === "url")
|
|
1445
|
-
return path.scheme;
|
|
1542
|
+
return path.scheme === "https" ? "http" : path.scheme;
|
|
1446
1543
|
return "file"; // local (bare) → file
|
|
1447
1544
|
}
|
|
1448
1545
|
async #writeLog({ statement, result, runId, loopId, turnId, sequence, origin, }) {
|