@plurnk/plurnk-service 0.25.0 → 0.27.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 +66 -46
- package/dist/Paths.d.ts +0 -1
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +3 -6
- package/dist/Paths.js.map +1 -1
- package/dist/content/path-mimetype.js +2 -2
- package/dist/content/path-mimetype.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +17 -2
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +11 -5
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/Engine.d.ts +4 -5
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +149 -49
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/ExecutorRegistry.d.ts +3 -0
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +8 -1
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +39 -15
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +4 -1
- package/dist/core/caps/DbNotifyCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +2 -2
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/fork.js +1 -1
- package/dist/core/fork.js.map +1 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +107 -46
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.js +2 -2
- package/dist/core/packet-wire.js +5 -5
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plugin-trust.d.ts +4 -0
- package/dist/core/plugin-trust.d.ts.map +1 -0
- package/dist/core/plugin-trust.js +23 -0
- package/dist/core/plugin-trust.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +78 -20
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +17 -14
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.js +6 -6
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Plurnk.js +4 -4
- package/dist/schemes/Plurnk.js.map +1 -1
- package/dist/schemes/_entry-chunk.d.ts +11 -0
- package/dist/schemes/_entry-chunk.d.ts.map +1 -0
- package/dist/schemes/_entry-chunk.js +100 -0
- package/dist/schemes/_entry-chunk.js.map +1 -0
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +6 -2
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-graph.js +1 -1
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +14 -6
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.js +2 -2
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-semantic.d.ts +22 -2
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +96 -9
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/exec-env.d.ts +5 -0
- package/dist/schemes/exec-env.d.ts.map +1 -0
- package/dist/schemes/exec-env.js +29 -0
- package/dist/schemes/exec-env.js.map +1 -0
- package/dist/server/Daemon.d.ts +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +11 -1
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/dsl.d.ts +2 -2
- package/dist/server/dsl.js +2 -2
- package/dist/server/envelope.d.ts +1 -0
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +5 -5
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/methods/entry_read.d.ts.map +1 -1
- package/dist/server/methods/entry_read.js +29 -7
- package/dist/server/methods/entry_read.js.map +1 -1
- package/dist/server/methods/log_read.d.ts.map +1 -1
- package/dist/server/methods/log_read.js +14 -1
- package/dist/server/methods/log_read.js.map +1 -1
- package/dist/server/methods/loop_inject.d.ts +5 -0
- package/dist/server/methods/loop_inject.d.ts.map +1 -0
- package/dist/server/methods/loop_inject.js +58 -0
- package/dist/server/methods/loop_inject.js.map +1 -0
- package/dist/server/methods/loop_run.d.ts.map +1 -1
- package/dist/server/methods/loop_run.js +6 -3
- package/dist/server/methods/loop_run.js.map +1 -1
- package/dist/server/methods/op_edit.js +1 -1
- package/dist/server/methods/op_edit.js.map +1 -1
- package/dist/server/methods/op_find.js +1 -1
- package/dist/server/methods/op_find.js.map +1 -1
- package/dist/server/methods/op_fold.js +1 -1
- package/dist/server/methods/op_open.js +1 -1
- package/dist/server/methods/run_fork.d.ts +5 -0
- package/dist/server/methods/run_fork.d.ts.map +1 -0
- package/dist/server/methods/run_fork.js +42 -0
- package/dist/server/methods/run_fork.js.map +1 -0
- package/dist/server/methods/session_constraints.js +6 -6
- package/dist/server/methods/session_constraints.js.map +1 -1
- package/dist/server/methods/session_create.d.ts +1 -0
- package/dist/server/methods/session_create.d.ts.map +1 -1
- package/dist/server/methods/session_create.js +32 -0
- package/dist/server/methods/session_create.js.map +1 -1
- package/migrations/0000-00-00.01_schema.sql +29 -15
- package/package.json +108 -111
- package/requirements.md +1 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,
|
|
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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var _a;
|
|
2
|
-
import { PlurnkParser, PlurnkParseError } from "@plurnk/plurnk-grammar";
|
|
2
|
+
import { PlurnkParser, PlurnkParseError, parsePath } from "@plurnk/plurnk-grammar";
|
|
3
3
|
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";
|
|
@@ -8,6 +8,8 @@ import GitState from "./git-state.js";
|
|
|
8
8
|
import { DEFAULT_LOOP_FLAGS } from "./scheme-types.js";
|
|
9
9
|
import { LineMarkerOps, MimetypeBinary, editedSpan } from "../content/index.js";
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
11
13
|
import Paths from "../Paths.js";
|
|
12
14
|
import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
|
|
13
15
|
// Shared module imported by both Engine and bin/digest.ts, so wire
|
|
@@ -55,6 +57,17 @@ const readMaxCommands = () => {
|
|
|
55
57
|
return DEFAULT_MAX_COMMANDS;
|
|
56
58
|
return n;
|
|
57
59
|
};
|
|
60
|
+
// PLURNK_MANIFEST_ITEMS — the turn-0 manifest preview. null = off (no foist);
|
|
61
|
+
// -1 = the full manifest; positive N = the first N items. 0 / unset = off.
|
|
62
|
+
const readManifestItems = () => {
|
|
63
|
+
const raw = process.env.PLURNK_MANIFEST_ITEMS;
|
|
64
|
+
if (raw === undefined || raw.length === 0)
|
|
65
|
+
return null;
|
|
66
|
+
const n = Number.parseInt(raw, 10);
|
|
67
|
+
if (!Number.isFinite(n) || n === 0)
|
|
68
|
+
return null;
|
|
69
|
+
return n < 0 ? -1 : n;
|
|
70
|
+
};
|
|
58
71
|
// Resolution timeout — proposed entries auto-cancel if nothing arrives
|
|
59
72
|
// within this window. SPEC.md §engine-rails (proposal lifecycle) + §methods (loop.resolve).
|
|
60
73
|
const PROPOSAL_TIMEOUT_DEFAULT_MS = 300000;
|
|
@@ -264,9 +277,20 @@ class Engine {
|
|
|
264
277
|
// attaches it iff the backend supports it and silently drops it otherwise —
|
|
265
278
|
// capability is providers' concern, not ours. Pure plumbing grammar→provider.
|
|
266
279
|
async #grammarConstraint() {
|
|
267
|
-
|
|
280
|
+
// PLURNK_PROVIDERS_GBNF SELECTS the GBNF variant to constrain sampling to (#225):
|
|
281
|
+
// a bare name (`plurnk-strict.gbnf` | `plurnk.gbnf`) is a variant shipped by
|
|
282
|
+
// @plurnk/plurnk-grammar; an absolute/relative path is a BYO grammar. Empty or "0"
|
|
283
|
+
// disables — unconstrained generation. (Was a `=== "1"` boolean; the value change
|
|
284
|
+
// would otherwise read as "off", leaving every turn unconstrained.)
|
|
285
|
+
const variant = process.env.PLURNK_PROVIDERS_GBNF;
|
|
286
|
+
if (variant === undefined || variant === "" || variant === "0")
|
|
268
287
|
return undefined;
|
|
269
|
-
this.#gbnfCache
|
|
288
|
+
if (this.#gbnfCache === null) {
|
|
289
|
+
const path = variant.startsWith("/") || variant.startsWith(".")
|
|
290
|
+
? variant
|
|
291
|
+
: fileURLToPath(import.meta.resolve(`@plurnk/plurnk-grammar/${variant}`));
|
|
292
|
+
this.#gbnfCache = await readFile(path, "utf8");
|
|
293
|
+
}
|
|
270
294
|
return this.#gbnfCache;
|
|
271
295
|
}
|
|
272
296
|
// Per-loop usage totals (#197): SUM the loop's turns (usage is stored per
|
|
@@ -365,6 +389,16 @@ class Engine {
|
|
|
365
389
|
cleanup("forceful", "max_turns");
|
|
366
390
|
return { turnIds, finalStatus: 499, hitMaxTurns: true, reason: "max_turns" };
|
|
367
391
|
}
|
|
392
|
+
// PLURNK_EXEC_WAIT_MS — a post-EXEC breath: if a spawn from the prior turn
|
|
393
|
+
// is still in flight, give it a tunable beat to land in THIS turn's packet
|
|
394
|
+
// before we assemble it. A fixed grace beat, never a wait-for-completion;
|
|
395
|
+
// 0/unset = off. Abortable with the loop signal.
|
|
396
|
+
const execWaitMs = Number(process.env.PLURNK_EXEC_WAIT_MS ?? "0");
|
|
397
|
+
if (execWaitMs > 0) {
|
|
398
|
+
const execHandler = this.#schemes.get("exec");
|
|
399
|
+
if (execHandler?.hasActiveSpawns?.(runId) === true)
|
|
400
|
+
await delay(execWaitMs, undefined, { signal });
|
|
401
|
+
}
|
|
368
402
|
const turn = await this.runTurn({
|
|
369
403
|
provider, messages, persona, requirements, sessionId, runId, loopId, origin, signal, onDispatch,
|
|
370
404
|
turnNumber: turnIds.length + 1, maxTurns,
|
|
@@ -451,7 +485,7 @@ class Engine {
|
|
|
451
485
|
throw new Error("Engine.runTurn: turn open returned no row");
|
|
452
486
|
const turnId = openRow.id;
|
|
453
487
|
// Pre-model writes. Each turn opens with a system-origin EDIT
|
|
454
|
-
// against `plurnk
|
|
488
|
+
// against `plurnk:///prompt/<loop_id>/<seq>` IF there's a prompt
|
|
455
489
|
// for THIS turn the model hasn't seen yet:
|
|
456
490
|
// - Turn 1: loop.prompt is the initial user prompt.
|
|
457
491
|
// - Turn N>1: only if Engine.inject (or wake-on-completion via
|
|
@@ -464,15 +498,15 @@ class Engine {
|
|
|
464
498
|
let nextActionIndex = 1;
|
|
465
499
|
if (seq === 1) {
|
|
466
500
|
// Operator doc READs (PLURNK_MD_<ALIAS>). The docs were materialized
|
|
467
|
-
// as plurnk
|
|
501
|
+
// as plurnk:///<entry> entries by the plurnk run (loop_run, via the
|
|
468
502
|
// §actor-boundary keystone); foist a READ of each into THIS turn-0 so the model
|
|
469
503
|
// reads them inline. It sees only the READ — the materializing EDIT
|
|
470
504
|
// lives in the plurnk run's log, never the model's.
|
|
471
505
|
for (const doc of Paths.docs()) {
|
|
472
506
|
const docTarget = {
|
|
473
|
-
kind: "url", raw: `plurnk
|
|
507
|
+
kind: "url", raw: `plurnk:///${doc.entryName}`, scheme: "plurnk",
|
|
474
508
|
username: null, password: null, hostname: null, port: null,
|
|
475
|
-
pathname: doc.entryName
|
|
509
|
+
pathname: `/${doc.entryName}`, params: {}, fragment: null,
|
|
476
510
|
};
|
|
477
511
|
const docRead = {
|
|
478
512
|
op: "READ", suffix: "", signal: null, target: docTarget,
|
|
@@ -487,10 +521,10 @@ class Engine {
|
|
|
487
521
|
const promptRow = await this.#db.engine_get_loop_prompt.get({ loop_id: loopId });
|
|
488
522
|
if (promptRow !== undefined && typeof promptRow.prompt === "string" && promptRow.prompt.length > 0) {
|
|
489
523
|
const promptPath = {
|
|
490
|
-
kind: "url", raw: `plurnk
|
|
524
|
+
kind: "url", raw: `plurnk:///prompt/${loopId}/${seq}`,
|
|
491
525
|
scheme: "plurnk", username: null, password: null,
|
|
492
526
|
hostname: null, port: null,
|
|
493
|
-
pathname:
|
|
527
|
+
pathname: `/prompt/${loopId}/${seq}`, params: {}, fragment: null,
|
|
494
528
|
};
|
|
495
529
|
const promptStmt = {
|
|
496
530
|
op: "EDIT", suffix: "", signal: null,
|
|
@@ -504,7 +538,7 @@ class Engine {
|
|
|
504
538
|
nextActionIndex++;
|
|
505
539
|
}
|
|
506
540
|
}
|
|
507
|
-
// plurnk
|
|
541
|
+
// plurnk:///manifest.json — rewritten EVERY turn (a live view of the
|
|
508
542
|
// entry set, which changes each turn). A derived view like the index,
|
|
509
543
|
// NOT an action — written directly (Engine.inject's path): no log entry,
|
|
510
544
|
// no sequence slot, not dispatched. The catalog body is built in the
|
|
@@ -529,10 +563,35 @@ class Engine {
|
|
|
529
563
|
// this turn's packet reflects them.
|
|
530
564
|
const fsDivergences = await GitMembership.indexGitMembership(systemCtx);
|
|
531
565
|
await this.#logFsFictions(sessionId, fsDivergences);
|
|
532
|
-
await EntryCrud.writeEntry("manifest.json", {
|
|
566
|
+
await EntryCrud.writeEntry("/manifest.json", {
|
|
533
567
|
channels: { body: { content: await EntryManifest.buildManifestBody(systemCtx), mimetype: "application/json" } },
|
|
534
568
|
tags: [],
|
|
535
569
|
}, systemCtx, "plurnk");
|
|
570
|
+
// Manifest preview (PLURNK_MANIFEST_ITEMS, §actor-boundary-manifest-preview):
|
|
571
|
+
// a turn-0 foisted READ of the just-built catalog so a run opens with what's
|
|
572
|
+
// available, not blank. -1 → full; N → the first N items (jsonpath slice — the
|
|
573
|
+
// manifest is JSON); off by default. AFTER the manifest write so the READ hits
|
|
574
|
+
// it, not a 404; same plurnk-origin foist as the operator docs.
|
|
575
|
+
if (seq === 1) {
|
|
576
|
+
const manifestItems = readManifestItems();
|
|
577
|
+
if (manifestItems !== null) {
|
|
578
|
+
const manifestRead = {
|
|
579
|
+
op: "READ", suffix: "", signal: null, lineMarker: null,
|
|
580
|
+
target: {
|
|
581
|
+
kind: "url", raw: "plurnk:///manifest.json", scheme: "plurnk",
|
|
582
|
+
username: null, password: null, hostname: null, port: null,
|
|
583
|
+
pathname: "/manifest.json", params: {}, fragment: null,
|
|
584
|
+
},
|
|
585
|
+
body: manifestItems < 0 ? null : { dialect: "jsonpath", raw: `$[0:${manifestItems}]` },
|
|
586
|
+
position: { line: 1, column: 1 },
|
|
587
|
+
};
|
|
588
|
+
await this.dispatch({
|
|
589
|
+
statement: manifestRead, sessionId, runId, loopId, turnId,
|
|
590
|
+
sequence: nextActionIndex, origin: "plurnk", onDispatch,
|
|
591
|
+
});
|
|
592
|
+
nextActionIndex++;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
536
595
|
// §env-delta — pre-seed environment deltas (changes since this run last
|
|
537
596
|
// reconciled) as system EDIT rows, before the packet composes; advance
|
|
538
597
|
// the action index past them so model ops continue after.
|
|
@@ -605,13 +664,17 @@ class Engine {
|
|
|
605
664
|
});
|
|
606
665
|
}
|
|
607
666
|
const opsCount = packetAssistant.ops.length;
|
|
608
|
-
|
|
667
|
+
// Informational SEND[103] broadcasts (#free-text-capture) are log rows, not
|
|
668
|
+
// actions: excluded from the real-op count so a prose-only turn still strikes
|
|
669
|
+
// as no-ops, and the terminal scan ignores 1xx so they never set turnStatus.
|
|
670
|
+
const realOpsCount = packetAssistant.ops.filter((op) => !(op.op === "SEND" && op.signal === 103 && op.target === null)).length;
|
|
671
|
+
const sendOp = packetAssistant.ops.findLast((op) => op.op === "SEND" && typeof op.signal === "number" && op.signal >= 200);
|
|
609
672
|
// Rail #41 (revised): the per-turn requirement is "emit at least one
|
|
610
673
|
// op," not "emit a terminal SEND." SEND is purely a signal verb; many
|
|
611
674
|
// turns may pass without one. An empty op list is the only strike.
|
|
612
675
|
const turnStatus = sendOp !== undefined
|
|
613
676
|
? sendOp.signal
|
|
614
|
-
:
|
|
677
|
+
: realOpsCount === 0 ? TURN_STATUS_NO_OPS : TURN_STATUS_IMPLICIT_CONTINUE;
|
|
615
678
|
// Close the turn with the final packet, status, and usage stats.
|
|
616
679
|
const packet = this.#completePacket(requestPacket, packetAssistant, response.assistantRaw, provider);
|
|
617
680
|
const { usage, finishReason, model } = callMetadata;
|
|
@@ -681,24 +744,45 @@ class Engine {
|
|
|
681
744
|
const { assistant } = response;
|
|
682
745
|
const preParsedOps = assistant.ops;
|
|
683
746
|
const ops = [];
|
|
684
|
-
|
|
747
|
+
// #plan-reasoning: PLAN bodies are the model's in-band reasoning — hoisted
|
|
748
|
+
// out of the op stream into the reasoning field, never dispatched as a log op.
|
|
749
|
+
// Free text becomes informational SEND[103] log ops (#free-text-capture) below.
|
|
750
|
+
const planFragments = [];
|
|
751
|
+
const hoistPlan = (op) => {
|
|
752
|
+
if (op.op !== "PLAN")
|
|
753
|
+
return false;
|
|
754
|
+
const raw = (op.body ?? "").trim();
|
|
755
|
+
if (raw.length > 0)
|
|
756
|
+
planFragments.push(raw);
|
|
757
|
+
return true;
|
|
758
|
+
};
|
|
685
759
|
// Full PlurnkParseError context (line/column/source) is preserved
|
|
686
760
|
// here so runTurn can build TelemetryEvent envelopes per the
|
|
687
761
|
// grammar 0.17.0 protocol — model needs position info to locate
|
|
688
762
|
// its own offending content on the next turn.
|
|
689
763
|
const parseErrors = [];
|
|
690
764
|
if (preParsedOps !== undefined) {
|
|
691
|
-
|
|
765
|
+
for (const op of preParsedOps)
|
|
766
|
+
if (!hoistPlan(op))
|
|
767
|
+
ops.push(op);
|
|
692
768
|
}
|
|
693
769
|
else {
|
|
694
770
|
const parsed = PlurnkParser.parse(assistant.content);
|
|
695
771
|
for (const item of parsed.items) {
|
|
696
|
-
if (item.kind === "statement")
|
|
697
|
-
|
|
772
|
+
if (item.kind === "statement") {
|
|
773
|
+
if (!hoistPlan(item.statement))
|
|
774
|
+
ops.push(item.statement);
|
|
775
|
+
}
|
|
698
776
|
else if (item.kind === "text") {
|
|
777
|
+
// #free-text-capture: interstitial prose → an informational
|
|
778
|
+
// SEND[103] broadcast log op, interleaved in emission order.
|
|
699
779
|
const trimmed = item.text.trim();
|
|
700
780
|
if (trimmed.length > 0)
|
|
701
|
-
|
|
781
|
+
ops.push({
|
|
782
|
+
op: "SEND", suffix: "", signal: 103, target: null,
|
|
783
|
+
lineMarker: null, body: { raw: trimmed, json: null },
|
|
784
|
+
position: item.position ?? { line: 1, column: 1 },
|
|
785
|
+
});
|
|
702
786
|
}
|
|
703
787
|
else if (item.kind === "error") {
|
|
704
788
|
const err = item.error;
|
|
@@ -723,8 +807,8 @@ class Engine {
|
|
|
723
807
|
}
|
|
724
808
|
}
|
|
725
809
|
const wireReasoning = assistant.reasoning ?? "";
|
|
726
|
-
const
|
|
727
|
-
const reasoningParts = [wireReasoning,
|
|
810
|
+
const planReasoning = planFragments.join("\n\n");
|
|
811
|
+
const reasoningParts = [wireReasoning, planReasoning].filter((s) => s.length > 0);
|
|
728
812
|
const reasoning = reasoningParts.length > 0 ? reasoningParts.join("\n\n") : null;
|
|
729
813
|
return {
|
|
730
814
|
packetAssistant: { content: assistant.content, ops, reasoning },
|
|
@@ -740,7 +824,7 @@ class Engine {
|
|
|
740
824
|
const byRole = (role) => initialMessages.filter((m) => m.role === role).map((m) => m.content).join("\n\n");
|
|
741
825
|
const system_definition = byRole("system");
|
|
742
826
|
// user.prompt sources from the loop's most recent prompt entry first
|
|
743
|
-
// (plurnk
|
|
827
|
+
// (plurnk:///prompt/<loop_id>/<N> for the highest N written to date).
|
|
744
828
|
// This is what inject + the turn-1 foist write into. Falls back to
|
|
745
829
|
// the runLoop caller's messages.user for tests that bypass the
|
|
746
830
|
// foist mechanism entirely.
|
|
@@ -825,18 +909,32 @@ class Engine {
|
|
|
825
909
|
// The # Plurnk System Tools capability sheet (SPEC §tools). A hook: each
|
|
826
910
|
// enabled capability contributes one line, rendered above Requirements so
|
|
827
911
|
// the model sees what it can do before the rules. PLAN is the first
|
|
828
|
-
// contributor (gated by PLURNK_PLAN);
|
|
829
|
-
//
|
|
912
|
+
// contributor (gated by PLURNK_PLAN); each available executor tag then
|
|
913
|
+
// contributes its self-documenting example (plurnk-execs#7), retiring the
|
|
914
|
+
// blind EXEC.
|
|
830
915
|
#collectTools() {
|
|
831
916
|
const tools = [];
|
|
832
917
|
if (process.env.PLURNK_PLAN === "1") {
|
|
833
|
-
tools.push("
|
|
918
|
+
tools.push("* Begin every response with <<PLAN:{planning and reasoning}:PLAN");
|
|
919
|
+
}
|
|
920
|
+
// Each available runtime tag contributes its self-documenting example —
|
|
921
|
+
// the example carries syntax + purpose, so there's no prose line. Tags
|
|
922
|
+
// with no example (sh/node, covered by the core prompt) contribute
|
|
923
|
+
// nothing; available-only, so the model never sees an unusable tag. `* `
|
|
924
|
+
// bullets + bare op forms match the packet's list/op rendering (no `- `,
|
|
925
|
+
// no backticks — see packet-wire.ts).
|
|
926
|
+
if (this.#executors !== undefined) {
|
|
927
|
+
for (const tag of this.#executors.availableRuntimes()) {
|
|
928
|
+
const example = this.#executors.entry(tag)?.example;
|
|
929
|
+
if (example)
|
|
930
|
+
tools.push(`* ${example}`);
|
|
931
|
+
}
|
|
834
932
|
}
|
|
835
933
|
return tools;
|
|
836
934
|
}
|
|
837
935
|
// SPEC §grinder — the budget grinder. Runs pre-LLM (in runTurn, after the packet
|
|
838
936
|
// is built, before provider.generate); fires only on actual overflow. Two
|
|
839
|
-
// passes, re-measuring between.
|
|
937
|
+
// passes, re-measuring between. Folds (never deletes) — the prior turn's logs,
|
|
840
938
|
// then the catalog except the manifest lifeline. The strike it raises and the
|
|
841
939
|
// hard-stop it can signal are returned to runLoop, which owns abandonment.
|
|
842
940
|
async #enforceBudget({ packet, provider, runId, loopId, turnId, sessionId, turnNumber, rebuild }) {
|
|
@@ -929,7 +1027,7 @@ class Engine {
|
|
|
929
1027
|
// SPEC §packet packet.system.log — chronological action-entries for the loop.
|
|
930
1028
|
// Snapshot is taken at packet build (pre-dispatch this turn), so it
|
|
931
1029
|
// reflects "what has happened before this turn." Each row carries a
|
|
932
|
-
// log
|
|
1030
|
+
// log:///<loop_seq>/<turn_seq>/<sequence> coordinate the model can READ.
|
|
933
1031
|
async #buildLog(runId) {
|
|
934
1032
|
// SPEC §packet-terms: runs own log entries — log is the run's history,
|
|
935
1033
|
// not the loop's. Span all loops in the run so the model sees
|
|
@@ -998,7 +1096,7 @@ class Engine {
|
|
|
998
1096
|
if (divergences.length === 0)
|
|
999
1097
|
return;
|
|
1000
1098
|
const run = await this.#db.envelope_get_run_by_name.get({ session_id: sessionId, name: "plurnk" })
|
|
1001
|
-
?? await this.#db.envelope_insert_run.get({ session_id: sessionId, name: "plurnk", persona: null });
|
|
1099
|
+
?? await this.#db.envelope_insert_run.get({ session_id: sessionId, name: "plurnk", persona: null, origin: "plurnk" });
|
|
1002
1100
|
if (run === undefined)
|
|
1003
1101
|
throw new Error("logFsFictions: plurnk run resolution returned no row");
|
|
1004
1102
|
const loop = await this.#db.envelope_insert_client_loop.get({ run_id: run.id });
|
|
@@ -1236,7 +1334,7 @@ class Engine {
|
|
|
1236
1334
|
return (row?.n ?? 0) > 0;
|
|
1237
1335
|
}
|
|
1238
1336
|
// Inject a prompt into the run's currently-executing loop. Writes a
|
|
1239
|
-
// plurnk
|
|
1337
|
+
// plurnk:///prompt/<loop_id>/<next-turn> entry whose body becomes
|
|
1240
1338
|
// packet.user.prompt at the next turn boundary. Last-wins: if two
|
|
1241
1339
|
// injects target the same next-turn slot, the second overwrites the
|
|
1242
1340
|
// first.
|
|
@@ -1365,7 +1463,8 @@ class Engine {
|
|
|
1365
1463
|
return this.#denyIfDisallowed("exec", origin);
|
|
1366
1464
|
}
|
|
1367
1465
|
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
1368
|
-
const
|
|
1466
|
+
const dst = statement.op === "COPY" ? (statement.body === null ? null : parsePath(statement.body)) : statement.body;
|
|
1467
|
+
const dstScheme = this.#schemeNameOf(dst);
|
|
1369
1468
|
const dstDenial = this.#denyIfDisallowed(dstScheme, origin);
|
|
1370
1469
|
if (dstDenial !== null)
|
|
1371
1470
|
return dstDenial;
|
|
@@ -1418,7 +1517,7 @@ class Engine {
|
|
|
1418
1517
|
return { status: 403, error: `scheme '${scheme}' is inactive under current loop flags` };
|
|
1419
1518
|
};
|
|
1420
1519
|
if (statement.op === "COPY" || statement.op === "MOVE") {
|
|
1421
|
-
return check(statement.target) ?? check(statement.body);
|
|
1520
|
+
return check(statement.target) ?? check(statement.op === "COPY" ? (statement.body === null ? null : parsePath(statement.body)) : statement.body);
|
|
1422
1521
|
}
|
|
1423
1522
|
return check(statement.target);
|
|
1424
1523
|
}
|
|
@@ -1426,11 +1525,14 @@ class Engine {
|
|
|
1426
1525
|
if (statement.op !== "COPY")
|
|
1427
1526
|
throw new Error("unreachable");
|
|
1428
1527
|
const srcPath = statement.target;
|
|
1429
|
-
|
|
1528
|
+
// COPY's body is an opaque raw string (grammar §COPY: a dest path OR a run-fork
|
|
1529
|
+
// prompt); parse it to the dest path. Non-path bodies (run:// fork prompts) are
|
|
1530
|
+
// not yet handled and surface as a 400.
|
|
1531
|
+
const dstPath = statement.body === null ? null : parsePath(statement.body);
|
|
1430
1532
|
if (srcPath === null)
|
|
1431
1533
|
return { status: 400, error: "COPY requires source path" };
|
|
1432
1534
|
if (dstPath === null)
|
|
1433
|
-
return { status: 400, error: "COPY
|
|
1535
|
+
return { status: 400, error: "COPY destination must be a parseable path in the body slot" };
|
|
1434
1536
|
return await this.#copyOrchestration({ statement, srcPath, dstPath, ctx });
|
|
1435
1537
|
}
|
|
1436
1538
|
async #handleMove(statement, ctx) {
|
|
@@ -1469,12 +1571,12 @@ class Engine {
|
|
|
1469
1571
|
}
|
|
1470
1572
|
// KILL — scheme-polymorphic destroy (plurnk-grammar#203 / 0.28.0). Entry-KILL
|
|
1471
1573
|
// permanently deletes the entry: the canonical delete now, MOVE→/dev/null
|
|
1472
|
-
// retired from the model's vocabulary. Process-KILL (exec
|
|
1574
|
+
// retired from the model's vocabulary. Process-KILL (exec:///) aborts the
|
|
1473
1575
|
// running spawn's controller (the same teardown loop.cancel rides), addressed
|
|
1474
1576
|
// by coordinate pathname (#203). The KILL body is an opaque
|
|
1475
1577
|
// annotation with no runtime meaning; it survives into the log row's tx for
|
|
1476
1578
|
// free via the statement serialization. Status: 200 killed · 404 unknown ·
|
|
1477
|
-
// 405 log
|
|
1579
|
+
// 405 log:/// (append-only) · 403 writableBy (the #checkWritable gate, KILL ∈
|
|
1478
1580
|
// MUTATING_OPS) · 200/410/304/404 exec (killed / killed-earlier / exited / unknown) · 501 no-kill/delete scheme.
|
|
1479
1581
|
async #handleKill(statement, ctx) {
|
|
1480
1582
|
if (statement.op !== "KILL")
|
|
@@ -1486,7 +1588,7 @@ class Engine {
|
|
|
1486
1588
|
if (schemeName === null)
|
|
1487
1589
|
return { status: 400, error: "KILL target must be a URL path with a scheme" };
|
|
1488
1590
|
if (schemeName === "log")
|
|
1489
|
-
return { status: 405, error: "log
|
|
1591
|
+
return { status: 405, error: "log:/// is append-only; KILL must bounce" };
|
|
1490
1592
|
if (schemeName === "exec") {
|
|
1491
1593
|
const execHandler = this.#schemes.get("exec");
|
|
1492
1594
|
if (execHandler === undefined || typeof execHandler.kill !== "function")
|
|
@@ -1499,14 +1601,12 @@ class Engine {
|
|
|
1499
1601
|
const delResult = await handler.deleteEntry(pathnameFromPath(path), ctx);
|
|
1500
1602
|
return { status: delResult.status };
|
|
1501
1603
|
}
|
|
1502
|
-
// PLAN —
|
|
1503
|
-
//
|
|
1504
|
-
//
|
|
1505
|
-
//
|
|
1506
|
-
//
|
|
1507
|
-
//
|
|
1508
|
-
// emits it; this handler exists only so the op resolves cleanly if we ever
|
|
1509
|
-
// deliberately reach for it (which would also require a plurnk.md override).
|
|
1604
|
+
// PLAN — the model's in-band reasoning op (plurnk-grammar 0.30.0, the 11th op).
|
|
1605
|
+
// Production HOISTS PLAN bodies into the turn's reasoning field in #parseAssistant
|
|
1606
|
+
// (#plan-reasoning) before dispatch runs, so PLAN normally never reaches here. This
|
|
1607
|
+
// handler is the op's direct-dispatch semantics (exercised by the dispatch unit
|
|
1608
|
+
// test): a pure no-op (PLAN ∉ MUTATING_OPS) whose body would serialize into the log
|
|
1609
|
+
// row's tx, no runtime effect.
|
|
1510
1610
|
#handlePlan(statement) {
|
|
1511
1611
|
if (statement.op !== "PLAN")
|
|
1512
1612
|
throw new Error("unreachable");
|
|
@@ -1621,7 +1721,7 @@ class Engine {
|
|
|
1621
1721
|
}
|
|
1622
1722
|
// Bare paths default to the file scheme per plurnk.md (grammar sysprompt):
|
|
1623
1723
|
// "Bare paths (no scheme) default to local relative project file paths."
|
|
1624
|
-
// file
|
|
1724
|
+
// file:/// remains an optional explicit form for absolute paths.
|
|
1625
1725
|
#schemeNameOf(path) {
|
|
1626
1726
|
if (path === null)
|
|
1627
1727
|
return null;
|
|
@@ -1646,11 +1746,11 @@ class Engine {
|
|
|
1646
1746
|
? { ...result.attrs }
|
|
1647
1747
|
: {};
|
|
1648
1748
|
// EXEC pathname is executor-domain + coordinate: the stream entry
|
|
1649
|
-
// lives at exec
|
|
1650
|
-
// exec
|
|
1749
|
+
// lives at exec:///<runtime>/<loop_seq>/<turn_seq>/<sequence> (e.g.
|
|
1750
|
+
// exec:///sh/1/1/2). The runtime leads — domain-aware, the executor
|
|
1651
1751
|
// as authority — and the coordinate that follows is already unique
|
|
1652
1752
|
// per statement, so no slug is injected. The log row's target points
|
|
1653
|
-
// at this same address; its log
|
|
1753
|
+
// at this same address; its log:/// coordinate shares the trailing
|
|
1654
1754
|
// <loop>/<turn>/<seq>, so the model correlates op to stream output.
|
|
1655
1755
|
// Runtime comes from statement.signal (EXEC's runtime slot) so it's
|
|
1656
1756
|
// resolvable for failed execs too; empty/absent = the default shell.
|
|
@@ -1661,7 +1761,7 @@ class Engine {
|
|
|
1661
1761
|
if (seqs === undefined)
|
|
1662
1762
|
throw new Error(`Engine.#writeLog: loop_turn_seqs returned no row for loop=${loopId} turn=${turnId}`);
|
|
1663
1763
|
const runtime = (typeof statement.signal === "string" && statement.signal.length > 0) ? statement.signal : "sh";
|
|
1664
|
-
const coordPathname =
|
|
1764
|
+
const coordPathname = `/${runtime}/${seqs.loop_seq}/${seqs.turn_seq}/${sequence}`;
|
|
1665
1765
|
target.scheme = "exec";
|
|
1666
1766
|
target.pathname = coordPathname;
|
|
1667
1767
|
attrsObj.pathname = coordPathname;
|
|
@@ -1712,7 +1812,7 @@ class Engine {
|
|
|
1712
1812
|
}
|
|
1713
1813
|
// Normalize a parsed path for storage. The `file` scheme is a routing
|
|
1714
1814
|
// internal — never stored, never rendered to the model. Both bare paths
|
|
1715
|
-
// and `file
|
|
1815
|
+
// and `file:///...` inputs collapse to scheme=null at this boundary, so
|
|
1716
1816
|
// entries.scheme / log_entries.scheme never carry the string "file".
|
|
1717
1817
|
#extractTarget(path) {
|
|
1718
1818
|
if (path === null)
|