@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.
Files changed (119) hide show
  1. package/SPEC.md +66 -46
  2. package/dist/Paths.d.ts +0 -1
  3. package/dist/Paths.d.ts.map +1 -1
  4. package/dist/Paths.js +3 -6
  5. package/dist/Paths.js.map +1 -1
  6. package/dist/content/path-mimetype.js +2 -2
  7. package/dist/content/path-mimetype.js.map +1 -1
  8. package/dist/core/ChannelWrite.d.ts +17 -2
  9. package/dist/core/ChannelWrite.d.ts.map +1 -1
  10. package/dist/core/ChannelWrite.js +11 -5
  11. package/dist/core/ChannelWrite.js.map +1 -1
  12. package/dist/core/Engine.d.ts +4 -5
  13. package/dist/core/Engine.d.ts.map +1 -1
  14. package/dist/core/Engine.js +149 -49
  15. package/dist/core/Engine.js.map +1 -1
  16. package/dist/core/ExecutorRegistry.d.ts +3 -0
  17. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  18. package/dist/core/ExecutorRegistry.js +8 -1
  19. package/dist/core/ExecutorRegistry.js.map +1 -1
  20. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  21. package/dist/core/SchemeRegistry.js +39 -15
  22. package/dist/core/SchemeRegistry.js.map +1 -1
  23. package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
  24. package/dist/core/caps/DbNotifyCaps.js +4 -1
  25. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  26. package/dist/core/caps/DbSubscriptionCaps.d.ts +1 -1
  27. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  28. package/dist/core/caps/DbSubscriptionCaps.js +2 -2
  29. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  30. package/dist/core/fork.js +1 -1
  31. package/dist/core/fork.js.map +1 -1
  32. package/dist/core/git-membership.d.ts.map +1 -1
  33. package/dist/core/git-membership.js +107 -46
  34. package/dist/core/git-membership.js.map +1 -1
  35. package/dist/core/git-state.js +2 -2
  36. package/dist/core/packet-wire.js +5 -5
  37. package/dist/core/packet-wire.js.map +1 -1
  38. package/dist/core/plugin-trust.d.ts +4 -0
  39. package/dist/core/plugin-trust.d.ts.map +1 -0
  40. package/dist/core/plugin-trust.js +23 -0
  41. package/dist/core/plugin-trust.js.map +1 -0
  42. package/dist/index.d.ts +2 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +4 -0
  45. package/dist/index.js.map +1 -1
  46. package/dist/schemes/Exec.d.ts.map +1 -1
  47. package/dist/schemes/Exec.js +78 -20
  48. package/dist/schemes/Exec.js.map +1 -1
  49. package/dist/schemes/File.d.ts.map +1 -1
  50. package/dist/schemes/File.js +17 -14
  51. package/dist/schemes/File.js.map +1 -1
  52. package/dist/schemes/Log.js +6 -6
  53. package/dist/schemes/Log.js.map +1 -1
  54. package/dist/schemes/Plurnk.js +4 -4
  55. package/dist/schemes/Plurnk.js.map +1 -1
  56. package/dist/schemes/_entry-chunk.d.ts +11 -0
  57. package/dist/schemes/_entry-chunk.d.ts.map +1 -0
  58. package/dist/schemes/_entry-chunk.js +100 -0
  59. package/dist/schemes/_entry-chunk.js.map +1 -0
  60. package/dist/schemes/_entry-find.d.ts.map +1 -1
  61. package/dist/schemes/_entry-find.js +6 -2
  62. package/dist/schemes/_entry-find.js.map +1 -1
  63. package/dist/schemes/_entry-graph.js +1 -1
  64. package/dist/schemes/_entry-graph.js.map +1 -1
  65. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  66. package/dist/schemes/_entry-manifest.js +14 -6
  67. package/dist/schemes/_entry-manifest.js.map +1 -1
  68. package/dist/schemes/_entry-ops.js +2 -2
  69. package/dist/schemes/_entry-ops.js.map +1 -1
  70. package/dist/schemes/_entry-semantic.d.ts +22 -2
  71. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  72. package/dist/schemes/_entry-semantic.js +96 -9
  73. package/dist/schemes/_entry-semantic.js.map +1 -1
  74. package/dist/schemes/exec-env.d.ts +5 -0
  75. package/dist/schemes/exec-env.d.ts.map +1 -0
  76. package/dist/schemes/exec-env.js +29 -0
  77. package/dist/schemes/exec-env.js.map +1 -0
  78. package/dist/server/Daemon.d.ts +1 -1
  79. package/dist/server/Daemon.d.ts.map +1 -1
  80. package/dist/server/Daemon.js +11 -1
  81. package/dist/server/Daemon.js.map +1 -1
  82. package/dist/server/dsl.d.ts +2 -2
  83. package/dist/server/dsl.js +2 -2
  84. package/dist/server/envelope.d.ts +1 -0
  85. package/dist/server/envelope.d.ts.map +1 -1
  86. package/dist/server/envelope.js +5 -5
  87. package/dist/server/envelope.js.map +1 -1
  88. package/dist/server/methods/entry_read.d.ts.map +1 -1
  89. package/dist/server/methods/entry_read.js +29 -7
  90. package/dist/server/methods/entry_read.js.map +1 -1
  91. package/dist/server/methods/log_read.d.ts.map +1 -1
  92. package/dist/server/methods/log_read.js +14 -1
  93. package/dist/server/methods/log_read.js.map +1 -1
  94. package/dist/server/methods/loop_inject.d.ts +5 -0
  95. package/dist/server/methods/loop_inject.d.ts.map +1 -0
  96. package/dist/server/methods/loop_inject.js +58 -0
  97. package/dist/server/methods/loop_inject.js.map +1 -0
  98. package/dist/server/methods/loop_run.d.ts.map +1 -1
  99. package/dist/server/methods/loop_run.js +6 -3
  100. package/dist/server/methods/loop_run.js.map +1 -1
  101. package/dist/server/methods/op_edit.js +1 -1
  102. package/dist/server/methods/op_edit.js.map +1 -1
  103. package/dist/server/methods/op_find.js +1 -1
  104. package/dist/server/methods/op_find.js.map +1 -1
  105. package/dist/server/methods/op_fold.js +1 -1
  106. package/dist/server/methods/op_open.js +1 -1
  107. package/dist/server/methods/run_fork.d.ts +5 -0
  108. package/dist/server/methods/run_fork.d.ts.map +1 -0
  109. package/dist/server/methods/run_fork.js +42 -0
  110. package/dist/server/methods/run_fork.js.map +1 -0
  111. package/dist/server/methods/session_constraints.js +6 -6
  112. package/dist/server/methods/session_constraints.js.map +1 -1
  113. package/dist/server/methods/session_create.d.ts +1 -0
  114. package/dist/server/methods/session_create.d.ts.map +1 -1
  115. package/dist/server/methods/session_create.js +32 -0
  116. package/dist/server/methods/session_create.js.map +1 -1
  117. package/migrations/0000-00-00.01_schema.sql +29 -15
  118. package/package.json +108 -111
  119. package/requirements.md +1 -2
@@ -1 +1 @@
1
- {"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAwF,MAAM,wBAAwB,CAAC;AAMpJ,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAiB,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAM9C,OAAO,KAAK,EAAkB,UAAU,EAAuB,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACpG,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAC;AAE1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AA+DhG,KAAK,WAAW,GAAG;IAAE,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAG9E,OAAO,KAAK,EAAE,QAAQ,EAAsD,MAAM,0BAA0B,CAAC;AA4C7G,KAAK,eAAe,GAAG;IACnB,SAAS,EAAE,eAAe,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C,CAAC;AAEF,KAAK,cAAc,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAOjF,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAC9D,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,gBAAgB,CAAC;IAK3B,IAAI,CAAC,EAAE,MAAM,CAAC;IAKd,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD,MAAM,WAAW,oBAAoB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,CAAC;CACpB;AAuGD,MAAM,CAAC,OAAO,OAAO,MAAM;;IACvB,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUhF,MAAM,CAAC,eAAe,CAAC,GAAG,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,MAAM;IAQnE,MAAM,CAAC,WAAW,CACd,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,EAC9B,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACvB;QAAE,QAAQ,EAAE,KAAK,CAAA;KAAE,GAAG;QAAE,QAAQ,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;gBAsE/D,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,aAAa,EAAE,oBAAoB,EAAE,QAAQ,EAAE,EAAE;QACtG,EAAE,EAAE,EAAE,CAAC;QACP,OAAO,EAAE,cAAc,CAAC;QACxB,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;QACtC,aAAa,CAAC,EAAE,aAAa,CAAC;QAC9B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;QAC5C,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;KACvC;IAsBD,YAAY,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI;IA6BzC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IA+CxG,OAAO,CAAC,EACV,QAAQ,EAAE,QAAQ,EAAE,OAAY,EAAE,YAAiB,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAC7E,QAAa,EAAE,UAA6B,EAC5C,SAAoE,EACpE,cAAqF,EACrF,MAAgB,EAAE,MAAM,EAAE,UAAU,GACvC,EAAE;QACC,QAAQ,EAAE,QAAQ,CAAC;QACnB,QAAQ,EAAE,WAAW,EAAE,CAAC;QAIxB,OAAO,CAAC,EAAE,MAAM,CAAC;QAIjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,UAAU,CAAC;QACpB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;KAC7C,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,WAAW,GAAG,kBAAkB,GAAG,iBAAiB,GAAG,UAAU,GAAG,IAAI,CAAA;KAAE,CAAC;IA+HzJ,OAAO,CAAC,EACV,QAAQ,EAAE,QAAQ,EAAE,OAAY,EAAE,YAAiB,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAgB,EAAE,MAAM,EAAE,UAAU,EACnH,UAAc,EAAE,QAAa,GAChC,EAAE;QACC,QAAQ,EAAE,QAAQ,CAAC;QACnB,QAAQ,EAAE,WAAW,EAAE,CAAC;QACxB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QACjD,MAAM,CAAC,EAAE,UAAU,CAAC;QACpB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;QAK1C,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IA8sBlI,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IAuLjE,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAYzE,kBAAkB,IAAI,MAAM,EAAE;IAQxB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAiBpD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAChD;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAC7C;IAgCD,iBAAiB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,IAAI,GAAG,IAAI;CAuc3E"}
@@ -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
- if (process.env.PLURNK_PROVIDERS_GBNF !== "1")
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 ??= await readFile(Paths.grammarGbnf, "utf8");
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://prompt/<loop_id>/<seq>` IF there's a prompt
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://<entry> entries by the plurnk run (loop_run, via the
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://${doc.entryName}`, scheme: "plurnk",
507
+ kind: "url", raw: `plurnk:///${doc.entryName}`, scheme: "plurnk",
474
508
  username: null, password: null, hostname: null, port: null,
475
- pathname: doc.entryName, params: {}, fragment: null,
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://prompt/${loopId}/${seq}`,
524
+ kind: "url", raw: `plurnk:///prompt/${loopId}/${seq}`,
491
525
  scheme: "plurnk", username: null, password: null,
492
526
  hostname: null, port: null,
493
- pathname: `prompt/${loopId}/${seq}`, params: {}, fragment: null,
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://manifest.json — rewritten EVERY turn (a live view of the
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
- const sendOp = packetAssistant.ops.findLast((op) => op.op === "SEND" && typeof op.signal === "number");
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
- : opsCount === 0 ? TURN_STATUS_NO_OPS : TURN_STATUS_IMPLICIT_CONTINUE;
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
- const textFragments = [];
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
- ops.push(...preParsedOps);
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
- ops.push(item.statement);
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
- textFragments.push(trimmed);
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 scrapedReasoning = textFragments.join("\n");
727
- const reasoningParts = [wireReasoning, scrapedReasoning].filter((s) => s.length > 0);
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://prompt/<loop_id>/<N> for the highest N written to date).
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); wired executor tags inject their own
829
- // lines here later (the ExecutorRegistry surface), retiring the blind EXEC.
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("- `<<PLAN:...:PLAN` think or plan in-band before acting; the body is your reasoning, not an op.");
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. Hides (never deletes) — the prior turn's logs,
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://<loop_seq>/<turn_seq>/<sequence> coordinate the model can READ.
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://prompt/<loop_id>/<next-turn> entry whose body becomes
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 dstScheme = this.#schemeNameOf(statement.body);
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
- const dstPath = statement.body;
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 requires destination path (in body slot)" };
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://) aborts the
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:// (append-only) · 403 writableBy (the #checkWritable gate, KILL ∈
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:// is append-only; KILL must bounce" };
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 — undocumented reasoning op (plurnk-grammar 0.30.0, the 11th op). A pure
1503
- // no-op: the body is the model's in-content reasoning, which lands in the log
1504
- // row's tx for free via statement serialization, no runtime effect (PLAN ∉
1505
- // MUTATING_OPS). NOT taught in plurnk.md by design reserved for a future model
1506
- // that must reason in the content field (no separate reasoning channel). gemma
1507
- // reasons on its own channel via the relaxed root, is never shown PLAN, and never
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:// remains an optional explicit form for absolute paths.
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://<runtime>/<loop_seq>/<turn_seq>/<sequence> (e.g.
1650
- // exec://sh/1/1/2). The runtime leads — domain-aware, the executor
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:// coordinate shares the trailing
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 = `${runtime}/${seqs.loop_seq}/${seqs.turn_seq}/${sequence}`;
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://...` inputs collapse to scheme=null at this boundary, so
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)