@plurnk/plurnk-service 0.50.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -2
- package/SPEC.md +15 -5
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +73 -54
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +4 -6
- package/dist/core/git-membership.js +1 -1
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/run-ops.sql +5 -0
- package/dist/core/session-settings.d.ts +4 -0
- package/dist/core/session-settings.d.ts.map +1 -1
- package/dist/core/session-settings.js +8 -0
- package/dist/core/session-settings.js.map +1 -1
- package/dist/schemes/Plurnk.js +1 -1
- package/dist/schemes/Plurnk.js.map +1 -1
- package/dist/schemes/Run.d.ts +4 -2
- package/dist/schemes/Run.d.ts.map +1 -1
- package/dist/schemes/Run.js +86 -51
- package/dist/schemes/Run.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +12 -0
- package/dist/schemes/_entry-find.d.ts +3 -17
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +67 -65
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-graph.d.ts +0 -6
- package/dist/schemes/_entry-graph.d.ts.map +1 -1
- package/dist/schemes/_entry-graph.js +0 -8
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-graph.sql +0 -10
- package/dist/schemes/_entry-manifest.d.ts +13 -2
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +84 -89
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +14 -6
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +11 -0
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +6 -5
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +9 -0
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/methods/session_create.js +4 -4
- package/dist/server/methods/session_create.js.map +1 -1
- package/migrations/0000-00-00.01_schema.sql +3 -1
- package/package.json +4 -4
- package/requirements.md +11 -3
package/.env.example
CHANGED
|
@@ -124,8 +124,8 @@ PLURNK_MANIFEST_ITEMS=-1
|
|
|
124
124
|
# Prompt-preview cap: the loop's prompt renders in user.prompt every turn, and a fat prompt
|
|
125
125
|
# replays each turn (bloat). Show the first N CHARS of the body + a pointer to the full
|
|
126
126
|
# prompt (always READable at its plurnk://prompt/<loop>/<seq> entry — nothing is lost).
|
|
127
|
-
# -1 = no cap (render the full prompt every turn).
|
|
128
|
-
PLURNK_PROMPT_PREVIEW_CHARS=
|
|
127
|
+
# -1 = no cap (render the full prompt every turn).
|
|
128
|
+
PLURNK_PROMPT_PREVIEW_CHARS=1024
|
|
129
129
|
|
|
130
130
|
# Session-tier ceiling on CONCURRENT active runs (a run with a non-terminal loop)
|
|
131
131
|
# — the fork-bomb / destabilization brake. -1 = no cap (default); only concurrency
|
package/SPEC.md
CHANGED
|
@@ -146,13 +146,13 @@ Server posture: this package is the runtime. User-facing CLI lives in `plurnk` a
|
|
|
146
146
|
|
|
147
147
|
**The keystone's first use: operator reference docs.** `PLURNK_MD_<ALIAS>=<path>` (§operator-config) materializes `<path>` as a `plurnk:///<ALIAS>.md` entry — a `dispatchAsPlurnk` EDIT in the plurnk run, **not** the model's — and the model's turn-0 foists a READ of it. The model reads the doc inline while the materializing EDIT stays out of its log: idiomatic context injection, an ordinary entry + READ rather than a bespoke packet section. The same `PLURNK_MD_*` convention cascades to clients. {§actor-boundary-doc-injection}
|
|
148
148
|
|
|
149
|
-
**
|
|
149
|
+
**Catalog preview.** `PLURNK_MANIFEST_ITEMS` foists a turn-0 `FIND(scheme:///**)` — one per scheme that holds entries — into the run's first turn, the same plurnk-origin foist as the docs, so a run opens with its catalog instead of blank. `-1` foists each scheme's whole catalog; a positive `N` caps each to its first `N` rows (FIND's `<L>`, clamped to the scheme's count so the strict marker never 416s); unset / `0` foists nothing. `log://` is absent — it is present-mode (the `# Log` section), not a catalog scheme. {§actor-boundary-manifest-preview}
|
|
150
150
|
|
|
151
151
|
### §machine-processes The machine and its processes: session, run, fork
|
|
152
152
|
|
|
153
153
|
**Question.** §actor-boundary isolates runs and lets the runtime self-host, but it stands on an ownership model it never states: what does a *session* own versus a *run*; what is shared versus private; and what does a fork carry? Unstated, the downstream questions — which run `log.read` reads, what a fork copies, where a per-client window onto the workspace would live — grow subtle, then metastasize. Drawn once, they vanish.
|
|
154
154
|
|
|
155
|
-
**Decision — the session is the world; a run is a log on it.** A **session** is the world: one shared filesystem — the `session`-scoped entries, surfaced as `plurnk:///manifest.json` (§packet) — under one membership overlay (§membership). Exactly one filesystem and one overlay per session; neither is per-run. A **run** is a process whose
|
|
155
|
+
**Decision — the session is the world; a run is a log on it.** A **session** is the world: one shared filesystem — the `session`-scoped entries, surfaced as `plurnk:///manifest.json` (§packet) — under one membership overlay (§membership). Exactly one filesystem and one overlay per session; neither is per-run. A **run** is a process whose private memory is its **log** (§lifecycle-terms) — its loops, turns, and rows, each row carrying its own content, attribution (`origin`/`source`, §env-delta), and fold-state (`expanded`). A run owns **no membership**; even its visibility is not a possession but a bit on its own rows. It is a *history over the shared world, not a world*.
|
|
156
156
|
|
|
157
157
|
**One filesystem.** The entries are the session's: `entries.session_id`, never a run. A write by any run is a write to the one filesystem every run reads; there is no per-run entry set. {§machine-processes-one-filesystem}
|
|
158
158
|
|
|
@@ -261,6 +261,16 @@ First path segment = provider plugin; rest = provider's own model id.
|
|
|
261
261
|
|
|
262
262
|
Author-facing contract: [plurnk-schemes#1](https://github.com/plurnk/plurnk-schemes/issues/1). Below: what plurnk-service exposes to schemes and orchestrates over them.
|
|
263
263
|
|
|
264
|
+
### §scheme-address Address resolution (RFC 3986)
|
|
265
|
+
|
|
266
|
+
Every op targets a URI; the entry key is `(scope, scheme, pathname)`. The URI parses per RFC 3986 (`scheme://[authority]/path`) and maps to that key by one rule — no per-scheme carve-out:
|
|
267
|
+
|
|
268
|
+
- A **registered** scheme is a plurnk namespace: its authority is a leading path segment, folded into the pathname (`Engine.#extractTarget` → `foldAuthorityIntoPath`). So `known://x`, `known:///x`, and pathname `/x` are the same entry — the authority is never a host, and the two-slash and three-slash forms are not distinct resources. {§scheme-address-namespace-fold}
|
|
269
|
+
- A **foreign** scheme (unregistered — `http`/`https`) is a real web host: its authority stays in `hostname`, never folded.
|
|
270
|
+
- `file` persists `scheme = NULL`; a relative path resolves against the workspace root to the namespace-absolute `/rel` key (RFC §5 reference resolution), and a path escaping the root is 403 (§membership).
|
|
271
|
+
|
|
272
|
+
Storage keys on the resolved `(scheme, pathname)` **verbatim** — the leading slash is the namespace origin, not a filesystem absolute, and is never re-normalized at the storage boundary.
|
|
273
|
+
|
|
264
274
|
### §scheme-manifest Manifest
|
|
265
275
|
|
|
266
276
|
Per author contract. Each scheme declares a `static manifest: SchemeManifest` with `name`, `channels`, `defaultChannel`, `category`, `scope`, `writableBy`, `volatile`, `modelVisible`, optional `flags`. {§scheme-manifest-manifest} Identity match enforced at plugin load: `manifest.name` must equal `package.json#plurnk.name`.
|
|
@@ -546,7 +556,7 @@ AST: `{ op: "FIND", target (scope), body: MatcherBody | null (predicate), signal
|
|
|
546
556
|
- `body` matcher operates on entry content (glob/regex/jsonpath/xpath), per grammar plurnk.md §"Body matcher dispatch"; the path-glob lives in the (target), not the body. {§find-glob-filter-on-content}
|
|
547
557
|
- `signal` is a tag filter; entries match if they have ALL listed tags. {§find-tag-filter-and-semantics}
|
|
548
558
|
- Session + scheme scoped — no cross-session/cross-scheme leakage. {§find-scoped-isolation}
|
|
549
|
-
- Returns `FindResult { status, content, mimetype, results:
|
|
559
|
+
- Returns `FindResult { status, content, mimetype, results: CatalogEntry[] }`. FIND resolves to the scheme's **catalog rows** — the very rows the manifest catalogs — filtered to the statement's matches and kept in match order. A **catalog row** is `{ path, seconds?, tags?, channels: { <uri>: { mimetype, tokens, lines } } }`: the addressable entry path, its per-channel `{mimetype, tokens, lines}` keyed by addressable URI (default channel → the bare path, non-default → `path#channel`), plus the entry's `tags` and a live `seconds` stream age. The matcher (glob/regex/jsonpath/xpath, `~`semantic, `@`graph) decides WHICH entries appear and in what order — a content hit **includes** the entry, a miss **excludes** it; it never reshapes the row. There is no per-match extent: the match LOCATION (which line or symbol) is a `READ` concern, not a FIND field. `content` is the rows as a JSON array (`application/json`); a body-less `FIND(scheme:///**)` yields the scheme's whole catalog — the manifest's per-scheme slice. {§find-result-catalog-rows}
|
|
550
560
|
|
|
551
561
|
### §send SEND
|
|
552
562
|
|
|
@@ -1192,7 +1202,7 @@ The wire projection (`PacketWire.renderSlot`) groups sections by slot into the s
|
|
|
1192
1202
|
|
|
1193
1203
|
**Prompt as a first-class entry.** Each loop's prompt is written on loop start as a plurnk-origin `EDIT` against `plurnk:///prompt/<loop_id>` (indexable, body channel, text/markdown). At render time the current loop's prompt body materializes into the `prompt` section; the entry itself stays READ/FOLD-able like any other. The foisted `EDIT`'s **log row is folded by default** (`expanded=0`): the prompt body already lives in the `prompt` section, so the log keeps the action for forensics while collapsing the duplicate body, re-OPENable like any fold (§open-fold). {§prompt-fold}
|
|
1194
1204
|
|
|
1195
|
-
**The entry catalog.**
|
|
1205
|
+
**The entry catalog.** The catalog is the **complete, unranked directory** of what a session holds, served by `FIND(scheme:///**)` — one per-scheme array, queried on demand, not a single materialized entry (there is no `plurnk:///manifest.json`; the per-scheme arrays replaced it). Built in the schemes layer (`_entry-manifest.catalogRowsFor`); a per-turn derivation pump (`maintainDerivations`) refreshes the deep channels the rows report. A scheme's array is **every entry it holds, in no relevance order**, each `{ path, seconds?, tags?, channels: { <uri>: { mimetype, tokens, lines } } }` — every channel keyed by the URI the model READs (the default channel by the bare path, a non-default by `path#channel`), so it reaches a channel without guessing. `tags` is present only when the entry carries `entry_tags` — its own categorization, surfaced so the model can `FIND` by tag. The model ranks and filters the catalog itself by querying it (task-aware); the catalog never ranks for it — the instant it did, it would be an index again. `tokens` is the provider's live count recounted at render, `lines` the content extent from `Mimetypes.process().totalLines`. The catalog never lists itself. {§packet-manifest-catalog}
|
|
1196
1206
|
|
|
1197
1207
|
### §telemetry user.telemetry — model-facing runtime telemetry
|
|
1198
1208
|
|
|
@@ -1230,7 +1240,7 @@ Strike accounting, cycle detection, sudden-death thresholds, and no-ops bookkeep
|
|
|
1230
1240
|
|
|
1231
1241
|
### §tools user.tools — the capability sheet
|
|
1232
1242
|
|
|
1233
|
-
|
|
1243
|
+
The tools capability lines render **titleless**, directly under the `definition` (plurnk.md) section — the examples flow on from plurnk.md with no separate header — and **above** `# Plurnk System Requirements`, so the model sees what it can *do* before the rules it must follow. Each enabled capability contributes one line via `Engine.#collectTools`; the section is omitted when nothing is enabled. {§tools-capability-sheet}
|
|
1234
1244
|
|
|
1235
1245
|
**Contributors: the wired executor tags.** Each available executor tag *with an example* contributes ONE line — its canonical usage — plus a `(docs: plurnk://docs/<tag>.md)` pointer when its package ships documentation, via the shared `teachingLine` (identical shape to the scheme directory, §schemes). A tag with no example contributes nothing; `PLURNK_DOCS_EXCLUDE` drops a named tag's line + doc. The boot `ExecutorRegistry` probes availability per tag, retiring the model's blind `<<EXEC[sh]…`.
|
|
1236
1246
|
|
|
@@ -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
|
@@ -498,6 +498,13 @@ class Engine {
|
|
|
498
498
|
// writes consume low indices; model ops continue from there.
|
|
499
499
|
const seqRow = await this.#db.engine_next_turn_sequence.get({ loop_id: loopId });
|
|
500
500
|
const seq = seqRow.next;
|
|
501
|
+
// #269 — loops.sequence is the loop's ordinal WITHIN the run. Turn-0 foists that belong to the
|
|
502
|
+
// RUN (manifest preview, AGENTS, operator docs) gate on the run's FIRST loop, not every loop's
|
|
503
|
+
// first turn; per-loop foists (the prompt, @file) still fire each loop. Read once, turn-1 only.
|
|
504
|
+
const loopRow = seq === 1
|
|
505
|
+
? await this.#db.engine_get_loop_prompt.get({ loop_id: loopId })
|
|
506
|
+
: undefined;
|
|
507
|
+
const runFirstLoop = (loopRow?.sequence ?? 0) === 1;
|
|
501
508
|
const openRow = await this.#db.engine_open_turn.get({
|
|
502
509
|
loop_id: loopId, sequence: seq,
|
|
503
510
|
});
|
|
@@ -525,7 +532,8 @@ class Engine {
|
|
|
525
532
|
// #231 — env docs (PLURNK_MD_*) UNION the session's client docs; foist a READ of
|
|
526
533
|
// each materialized plurnk:///<alias>.md (loop_run materialized the same set).
|
|
527
534
|
const { mdDocs } = await SessionSettings.read(this.#db, sessionId);
|
|
528
|
-
|
|
535
|
+
// #269 — operator docs are run-once; foist them only on the run's first loop.
|
|
536
|
+
for (const doc of runFirstLoop ? await SessionSettings.resolveDocs(mdDocs) : []) {
|
|
529
537
|
const docTarget = {
|
|
530
538
|
kind: "url", raw: `plurnk:///${doc.entryName}`, scheme: "plurnk",
|
|
531
539
|
username: null, password: null, hostname: null, port: null,
|
|
@@ -541,7 +549,7 @@ class Engine {
|
|
|
541
549
|
});
|
|
542
550
|
nextActionIndex++;
|
|
543
551
|
}
|
|
544
|
-
const promptRow =
|
|
552
|
+
const promptRow = loopRow; // #269 — already read above (per-loop; fires every loop's turn 1)
|
|
545
553
|
if (promptRow !== undefined && typeof promptRow.prompt === "string" && promptRow.prompt.length > 0) {
|
|
546
554
|
const promptPath = {
|
|
547
555
|
kind: "url", raw: `plurnk://prompt/${loopId}/${seq}`,
|
|
@@ -568,12 +576,11 @@ class Engine {
|
|
|
568
576
|
nextActionIndex++;
|
|
569
577
|
}
|
|
570
578
|
}
|
|
571
|
-
//
|
|
572
|
-
// entry
|
|
573
|
-
//
|
|
574
|
-
//
|
|
575
|
-
//
|
|
576
|
-
// per-turn write. Does not list itself.
|
|
579
|
+
// The per-turn derivation pump (_entry-manifest.maintainDerivations) — refreshes
|
|
580
|
+
// every entry's deep channels (symbols/refs/embeddings/FTS, deep_hash-gated) so the
|
|
581
|
+
// catalog and FIND read current data. NOT an action: no log entry, no sequence slot,
|
|
582
|
+
// not dispatched. There is no plurnk:///manifest.json entry — the catalog is served
|
|
583
|
+
// on demand by FIND(scheme:///**), foisted into the run's first turn below.
|
|
577
584
|
const systemCtx = {
|
|
578
585
|
db: this.#db, sessionId, runId, loopId, turnId,
|
|
579
586
|
writer: "plurnk",
|
|
@@ -589,55 +596,64 @@ class Engine {
|
|
|
589
596
|
// prompt-composition (EMI is eager + exhaustive — git is the only bound). When the
|
|
590
597
|
// session's project_root is a git working tree, tracked files are
|
|
591
598
|
// members without a client `add`; active members are materialized
|
|
592
|
-
// (disk → body channel) so they appear in the
|
|
593
|
-
// on headless / non-git sessions. Runs BEFORE the
|
|
599
|
+
// (disk → body channel) so they appear in the catalog. No-ops
|
|
600
|
+
// on headless / non-git sessions. Runs BEFORE the derivation pump so
|
|
594
601
|
// this turn's packet reflects them.
|
|
595
602
|
const fsDivergences = await GitMembership.indexGitMembership(systemCtx);
|
|
596
603
|
await this.#logFsFictions(sessionId, fsDivergences);
|
|
597
|
-
await
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
// available, not blank. -1 → full; N → the first N items (jsonpath slice — the
|
|
604
|
-
// manifest is JSON); off by default. AFTER the manifest write so the READ hits
|
|
605
|
-
// it, not a 404; same plurnk-origin foist as the operator docs.
|
|
604
|
+
await EntryManifest.maintainDerivations(systemCtx);
|
|
605
|
+
// Turn-0 catalog preview (PLURNK_MANIFEST_ITEMS, §actor-boundary-manifest-preview):
|
|
606
|
+
// one FIND(scheme:///**) per scheme that holds entries, foisted into the run's first
|
|
607
|
+
// model turn so it opens with its catalog (the per-scheme arrays that replaced the
|
|
608
|
+
// single manifest.json). -1 → each scheme's whole catalog; N → its first N rows
|
|
609
|
+
// (clamped to the scheme's count so FIND's strict <L> never 416s); off by default.
|
|
606
610
|
if (seq === 1) {
|
|
607
611
|
// #231 — a session's client-chosen manifestItems REPLACES the env default outright.
|
|
608
612
|
const { manifestItems: sessionMI, autoReadAgents } = await SessionSettings.read(this.#db, sessionId);
|
|
609
613
|
const manifestItems = sessionMI !== null ? normalizeManifestItems(sessionMI) : readManifestItems();
|
|
610
|
-
if (manifestItems !== null) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
614
|
+
if (manifestItems !== null && runFirstLoop) { // #269 — catalog preview is run-once
|
|
615
|
+
// engine_scheme_catalog_summary is the scheme source: session-scoped, ordered,
|
|
616
|
+
// one row per scheme that has entries (scheme=null → file). log:// is absent —
|
|
617
|
+
// it lives in log_entries, not the catalog (present-mode, the # Log section).
|
|
618
|
+
const catalogSchemes = await this.#db.engine_scheme_catalog_summary.all({ session_id: sessionId });
|
|
619
|
+
for (const { scheme, entries } of catalogSchemes) {
|
|
620
|
+
const schemeName = scheme ?? "file";
|
|
621
|
+
const cap = manifestItems < 0 ? null : Math.min(manifestItems, entries);
|
|
622
|
+
const catalogFind = {
|
|
623
|
+
op: "FIND", suffix: "", signal: null,
|
|
624
|
+
target: {
|
|
625
|
+
kind: "url", raw: `${schemeName}:///**`, scheme: schemeName,
|
|
626
|
+
username: null, password: null, hostname: null, port: null,
|
|
627
|
+
pathname: "", params: {}, fragment: null,
|
|
628
|
+
},
|
|
629
|
+
body: null,
|
|
630
|
+
lineMarker: cap === null ? null : { marks: [1, cap] },
|
|
631
|
+
position: { line: 1, column: 1 },
|
|
632
|
+
};
|
|
633
|
+
await this.dispatch({
|
|
634
|
+
statement: catalogFind, sessionId, runId, loopId, turnId,
|
|
635
|
+
sequence: nextActionIndex, origin: "plurnk", onDispatch,
|
|
636
|
+
});
|
|
637
|
+
nextActionIndex++;
|
|
638
|
+
}
|
|
626
639
|
}
|
|
627
|
-
// #
|
|
628
|
-
//
|
|
629
|
-
//
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
if (
|
|
633
|
-
const
|
|
634
|
-
|
|
640
|
+
// #268 — auto-READ the configured AGENTS file(s) into THIS first model turn. Env-driven
|
|
641
|
+
// (PLURNK_AGENTS_AUTO / PLURNK_AGENTS_FILES), overridable per-session by autoReadAgents; the
|
|
642
|
+
// matching files are auto-PICKed into membership at session setup (envelope). The model sees
|
|
643
|
+
// only the READ — a normal file:/// member READ, read-write so it edits the scratchpad back.
|
|
644
|
+
const { auto: agentsAuto, files: agentsFiles } = SessionSettings.resolveAgentsAutoload(autoReadAgents);
|
|
645
|
+
if (agentsAuto && runFirstLoop) { // #269 — run-once, the run's first loop
|
|
646
|
+
for (const file of agentsFiles) {
|
|
647
|
+
const pathname = file.startsWith("/") ? file : `/${file}`;
|
|
648
|
+
const member = await this.#db.crud_get_member_sig.get({ session_id: sessionId, scheme: null, pathname });
|
|
649
|
+
if (member === undefined)
|
|
650
|
+
continue; // absent / non-git session → not a member → skip
|
|
635
651
|
const agentsRead = {
|
|
636
652
|
op: "READ", suffix: "", signal: null, lineMarker: null,
|
|
637
653
|
target: {
|
|
638
|
-
kind: "url", raw:
|
|
654
|
+
kind: "url", raw: `file://${pathname}`, scheme: "file",
|
|
639
655
|
username: null, password: null, hostname: null, port: null,
|
|
640
|
-
pathname
|
|
656
|
+
pathname, params: {}, fragment: null,
|
|
641
657
|
},
|
|
642
658
|
body: null, position: { line: 1, column: 1 },
|
|
643
659
|
};
|
|
@@ -967,7 +983,7 @@ class Engine {
|
|
|
967
983
|
const inject = await readPacketInject(); // #240 — operator section, per-turn, fail-hard on a broken path
|
|
968
984
|
const defaults = [
|
|
969
985
|
{ name: "definition", slot: "system", header: null, content: system_definition, tokens: 0 },
|
|
970
|
-
{ name: "tools", slot: "system", header:
|
|
986
|
+
{ name: "tools", slot: "system", header: null, content: tools.join("\n"), tokens: 0 }, // titleless — the examples flow on from plurnk.md (definition) directly above
|
|
971
987
|
{ name: "schemes", slot: "system", header: "Plurnk System Schemes", content: this.#schemes.teach(), tokens: 0 },
|
|
972
988
|
...(inject !== null ? [{ name: "inject", slot: "system", header: "Plurnk Operator Notes", content: inject, tokens: 0 }] : []),
|
|
973
989
|
{ name: "log", slot: "system", header: "Plurnk System Log", content: PacketWire.renderLog(log), tokens: 0 },
|
|
@@ -1708,12 +1724,12 @@ class Engine {
|
|
|
1708
1724
|
const target = statement.target;
|
|
1709
1725
|
if (target === null)
|
|
1710
1726
|
return { status: 400, error: "run:// fork requires a source run" };
|
|
1711
|
-
const name =
|
|
1727
|
+
const name = target.kind === "url" ? (target.hostname ?? "") : ""; // §run-scheme — run is the AUTHORITY (run://<name>), not the path
|
|
1712
1728
|
let srcRunId = ctx.runId;
|
|
1713
1729
|
if (name !== "" && name !== ".") {
|
|
1714
1730
|
const row = await this.#db.run_resolve_by_name.get({ session_id: ctx.sessionId, name });
|
|
1715
1731
|
if (row === undefined)
|
|
1716
|
-
return { status: 404, error: `run
|
|
1732
|
+
return { status: 404, error: `run://${name} not found in this session` };
|
|
1717
1733
|
srcRunId = row.id;
|
|
1718
1734
|
}
|
|
1719
1735
|
if (ctx.injectRun === undefined)
|
|
@@ -1807,12 +1823,12 @@ class Engine {
|
|
|
1807
1823
|
// terminate — abort any run by address; whoever holds it may end it.
|
|
1808
1824
|
// `.`/"" = self. cancelRun (→ Daemon.cancelDrain) aborts the run's signal
|
|
1809
1825
|
// (its loop closes 499); an idle run is a no-op-200, a missing run 404.
|
|
1810
|
-
const name =
|
|
1826
|
+
const name = path.kind === "url" ? (path.hostname ?? "") : ""; // §run-scheme — run is the AUTHORITY
|
|
1811
1827
|
let runId = ctx.runId;
|
|
1812
1828
|
if (name !== "" && name !== ".") {
|
|
1813
1829
|
const row = await this.#db.run_resolve_by_name.get({ session_id: ctx.sessionId, name });
|
|
1814
1830
|
if (row === undefined)
|
|
1815
|
-
return { status: 404, error: `run
|
|
1831
|
+
return { status: 404, error: `run://${name} not found in this session` };
|
|
1816
1832
|
runId = row.id;
|
|
1817
1833
|
}
|
|
1818
1834
|
if (this.#cancelRun === undefined)
|
|
@@ -2056,10 +2072,13 @@ class Engine {
|
|
|
2056
2072
|
if (path.kind === "local")
|
|
2057
2073
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: decodePathParens(path.raw), params: null, fragment: null }; // #239 item 4
|
|
2058
2074
|
const scheme = path.scheme === "file" ? null : path.scheme;
|
|
2059
|
-
// plurnk uses its authority as a namespace — fold
|
|
2060
|
-
//
|
|
2061
|
-
//
|
|
2062
|
-
|
|
2075
|
+
// Every registered (plurnk-namespace) scheme uses its authority as a namespace segment — fold
|
|
2076
|
+
// it into the canonical pathname so known://x ≡ known:///x ≡ /x and the log keys identically to
|
|
2077
|
+
// the entry (/prompt/<loop>, /docs/x.md). A foreign web host (http://, unregistered) is NOT a
|
|
2078
|
+
// namespace: keep it in hostname. run:// is the one registered EXCEPTION — its authority IS the
|
|
2079
|
+
// run selector (§run-scheme), and run:/// (self) must stay distinct from run://name, so Run.ts
|
|
2080
|
+
// folds the owner into the storage path itself, never here.
|
|
2081
|
+
const foldNs = scheme !== null && scheme !== "run" && this.#schemes.has(scheme);
|
|
2063
2082
|
return {
|
|
2064
2083
|
scheme, username: path.username, password: path.password,
|
|
2065
2084
|
hostname: foldNs ? null : path.hostname, port: path.port,
|