@plurnk/plurnk-service 0.41.0 → 0.43.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 +21 -3
- package/SPEC.md +5 -4
- package/bin/plurnk-service.ts +50 -7
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +2 -1
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +97 -66
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +2 -0
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +16 -2
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/packet-wire.d.ts +30 -31
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +81 -80
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +3 -0
- package/dist/core/plurnk-uri.d.ts.map +1 -0
- package/dist/core/plurnk-uri.js +23 -0
- package/dist/core/plurnk-uri.js.map +1 -0
- package/dist/core/scheme-types.d.ts +4 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js.map +1 -1
- package/dist/schemes/Plurnk.d.ts.map +1 -1
- package/dist/schemes/Plurnk.js +8 -4
- package/dist/schemes/Plurnk.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +2 -1
- 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 +6 -1
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +3 -1
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/clientTurn.d.ts.map +1 -1
- package/dist/server/clientTurn.js +1 -2
- package/dist/server/clientTurn.js.map +1 -1
- package/dist/server/envelope.d.ts +1 -0
- 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/loop_run.js +2 -2
- package/dist/server/methods/loop_run.js.map +1 -1
- package/dist/server/methods/session_rename.d.ts +5 -0
- package/dist/server/methods/session_rename.d.ts.map +1 -0
- package/dist/server/methods/session_rename.js +32 -0
- package/dist/server/methods/session_rename.js.map +1 -0
- package/migrations/0000-00-00.01_schema.sql +11 -12
- package/package.json +9 -34
- package/requirements.md +2 -8
package/.env.example
CHANGED
|
@@ -6,6 +6,18 @@
|
|
|
6
6
|
# --- Storage ---
|
|
7
7
|
PLURNK_DB_PATH=./plurnk.db
|
|
8
8
|
|
|
9
|
+
# --- Sqlite tuning (curated knobs passed through to sqlrite; optional) ---
|
|
10
|
+
# sqlrite already sets the safe posture (WAL + synchronous=NORMAL + busy_timeout=5000ms).
|
|
11
|
+
# These are operator overrides — integers; unset = sqlrite/sqlite default. Uncomment to tune.
|
|
12
|
+
# busy_timeout (ms): how long a writer waits on a lock before SQLITE_BUSY. 0 = immediate.
|
|
13
|
+
# PLURNK_SQLITE_TIMEOUT=5000
|
|
14
|
+
# cache_size: positive = pages, negative = KiB of memory. Bigger = fewer disk reads on a hot db.
|
|
15
|
+
# PLURNK_SQLITE_CACHE_SIZE=-16000
|
|
16
|
+
# mmap_size: bytes of memory-mapped I/O (read perf on large file-backed dbs). 0 disables.
|
|
17
|
+
# PLURNK_SQLITE_MMAP_SIZE=268435456
|
|
18
|
+
# max_page_count: hard db-size ceiling in PAGES — a write past it errors (disk-fill guard).
|
|
19
|
+
# PLURNK_SQLITE_MAX_PAGE_COUNT=524288
|
|
20
|
+
|
|
9
21
|
# --- Daemon transport ---
|
|
10
22
|
PLURNK_HOST=127.0.0.1
|
|
11
23
|
PLURNK_PORT=3044
|
|
@@ -89,6 +101,12 @@ PLURNK_GIT_AUTO=1
|
|
|
89
101
|
# session.create settings.manifestItems, which replaces this value (#231).
|
|
90
102
|
PLURNK_MANIFEST_ITEMS=-1
|
|
91
103
|
|
|
104
|
+
# Prompt-preview cap: the loop's prompt renders in user.prompt every turn, and a fat prompt
|
|
105
|
+
# replays each turn (bloat). Show the first N CHARS of the body + a pointer to the full
|
|
106
|
+
# prompt (always READable at its plurnk://prompt/<loop>/<seq> entry — nothing is lost).
|
|
107
|
+
# -1 = no cap (render the full prompt every turn). Default 512.
|
|
108
|
+
PLURNK_PROMPT_PREVIEW_CHARS=512
|
|
109
|
+
|
|
92
110
|
# Session-tier ceiling on CONCURRENT active runs (a run with a non-terminal loop)
|
|
93
111
|
# — the fork-bomb / destabilization brake. -1 = no cap (default); only concurrency
|
|
94
112
|
# is bounded, never lifetime, since sessions persist for months. A spawn/fork past
|
|
@@ -100,12 +118,12 @@ PLURNK_SESSION_RUNS_MAX_ACTIVE=-1
|
|
|
100
118
|
# fail-hard if unset — 0 = native reasoning off (in-DSL PLAN does the reasoning), -1 =
|
|
101
119
|
# adaptive / no cap, N = capped at N tokens. Provider modules translate it per model
|
|
102
120
|
# family (reasoning_effort tiers, budget_tokens). Floor (gemma): 0.
|
|
103
|
-
PLURNK_PROVIDERS_REASONING_BUDGET
|
|
121
|
+
PLURNK_PROVIDERS_REASONING_BUDGET=-1
|
|
104
122
|
#
|
|
105
123
|
# Planning: enable the grammar's <<PLAN:...:PLAN op so the model reasons in-band before
|
|
106
124
|
# acting. A shared universal knob (like PLURNK_FETCH_TIMEOUT below): the service advertises
|
|
107
125
|
# the op in # Plurnk System Tools; the provider reads it too. =1 on, =0 off.
|
|
108
|
-
PLURNK_PLAN=
|
|
126
|
+
PLURNK_PLAN=0
|
|
109
127
|
# Service-wide fetch timeout in ms — the universal upper bound on any
|
|
110
128
|
# single outbound request (provider calls, future http:// scheme reads,
|
|
111
129
|
# anything that does network IO). Streamed completions can run long;
|
|
@@ -122,7 +140,7 @@ PLURNK_VERSION_POLL_TTL=3600000
|
|
|
122
140
|
# /v1/models fingerprint) can only sample valid plurnk DSL; others drop it silently.
|
|
123
141
|
# A bare name (plurnk.gbnf) is a variant shipped by
|
|
124
142
|
# @plurnk/plurnk-grammar; an absolute/relative path is your own. =0 (or empty) disables.
|
|
125
|
-
PLURNK_PROVIDERS_GBNF=plurnk.gbnf
|
|
143
|
+
# PLURNK_PROVIDERS_GBNF=plurnk.gbnf
|
|
126
144
|
# Provider retry attempts (providers 0.7+): how many times generate() retries a
|
|
127
145
|
# TRANSIENT failure (429 rate-limit, 5xx/network) with exponential backoff (base 2s
|
|
128
146
|
# is a provider constant — the COUNT is the operator knob). REQUIRED, fail-hard if
|
package/SPEC.md
CHANGED
|
@@ -17,7 +17,7 @@ Canonical meanings. When a doc, comment, test name, or commit message uses one o
|
|
|
17
17
|
| **agent** | The plurnk runtime singleton. Owns agent-scoped state (default scheme registry, agent-wide entries). One per process. |
|
|
18
18
|
| **session** | Durable user-named workspace. Persists across runs and process restarts. Identity: `sessions.id` + unique `sessions.name`. |
|
|
19
19
|
| **run** | A stretch of work within a session. Multiple runs per session. May fork from another run via `parent_run_id`. Owns the log entries. |
|
|
20
|
-
| **loop** | One model-driven or client-driven iteration within a run. Status ∈ {102
|
|
20
|
+
| **loop** | One model-driven or client-driven iteration within a run. Status ∈ {100 pending · 102 running · 200 done · 413 budget-overflow · 429 turn-ceiling · 499 cancelled · 500 failed · 508 runaway}. Many loops per run. The model runs inside a loop; each client RPC has its own loop. |
|
|
21
21
|
| **turn** | One round-trip with the LLM (or one client RPC dispatch). One assembled prompt sent, one parsed response handled. Many turns per loop. Identity: `(loop_id, sequence)`. |
|
|
22
22
|
| **op** | One DSL operation the model emits. Parsed into a `PlurnkStatement`. Examples: `EDIT`, `READ`, `SEND`, `FIND`, `COPY`, `MOVE`, `OPEN`, `FOLD`, `EXEC`. One turn produces zero or more ops. |
|
|
23
23
|
| **statement** | Synonym for parsed op. The AST shape `PlurnkStatement` from `@plurnk/plurnk-grammar`. |
|
|
@@ -59,7 +59,7 @@ Independent axes on entries and channels. Confusion across them is a recurring s
|
|
|
59
59
|
| Term | Meaning |
|
|
60
60
|
|---|---|
|
|
61
61
|
| **verdict** | End-of-turn ruling computed directly in `Engine.runLoop` from strike/cycle/sudden-death rail state. Decides whether the loop terminates or another turn fires. No filter chain — rails are inline. |
|
|
62
|
-
| **strike** | A turn whose verdict counts toward `MAX_STRIKES`. Fires when `turnErrors > 0` or cycle detection trips. The streak counter resets on clean turn; reaches `MAX_STRIKES` → loop abandons at
|
|
62
|
+
| **strike** | A turn whose verdict counts toward `MAX_STRIKES`. Fires when `turnErrors > 0` or cycle detection trips. The streak counter resets on clean turn; reaches `MAX_STRIKES` → loop abandons at 500 (failed), or 508 (Loop Detected) when the crossing strike was cycle-driven. |
|
|
63
63
|
| **cycle** | A repeated turn fingerprint across consecutive turns. Detected silently; model never sees the trigger. Strike accumulates internally. |
|
|
64
64
|
| **sudden death** | The last `MAX_STRIKES` turns of a loop's `MAX_LOOP_TURNS` window emit soft 429 warnings so the model can wrap up cleanly. `soft=true`: no strike, no streak increment. |
|
|
65
65
|
| **mode** | `"ask" \| "act"`. Per-loop. Ask = read-only (no side-effecting ops); act = full surface. |
|
|
@@ -856,6 +856,7 @@ registry.register("loop.run", {
|
|
|
856
856
|
| `session.runs` | `id?: number` | `{ runs: Run[] }` | Lists runs in a session (defaults to attached session); most-recent first. |
|
|
857
857
|
| `session.prompts` | `id?: number`, `limit?: number` | `{ prompts: string[] }` | A session's prior user prompts (the conversation run's loop seeds), newest-first, capped by `limit` (default 100); defaults to attached session. Lets a client seed up/down recall without log archaeology. |
|
|
858
858
|
| `session.set_root` | `projectRoot: string \| null` | `{ projectRoot }` | Update the workspace pointer on the attached session. Null reverts to headless. |
|
|
859
|
+
| `session.rename` | `name: string` | `{ id, name }` | Rename the attached session — its name is a **mutable handle** on the world (unlike a run, whose name is frozen at instantiation, §machine-processes). Mutates `sessions.name` only; runs, log, and membership untouched. A name another session holds is rejected (`sessions.name` is unique). {§methods-session-rename} |
|
|
859
860
|
| `session.constrain` | `effect: "pick" \| "hide" \| "view" \| "repo"`, `glob: string` | `{ effect, glob }` | Add a workspace membership constraint (§membership overlay): `pick` admits a file git misses (the sole source when git is absent), `hide` drops a tracked match, `view` admits a member read-only (refused at the edit gate), `repo` declares a git repo folder anywhere so its members join the manifest. Immediate. |
|
|
860
861
|
| `session.unconstrain` | `effect: "pick" \| "hide" \| "view" \| "repo"`, `glob: string` | `{ effect, glob }` | Remove a membership constraint (the `drop` verb) — the inverse of `session.constrain`. Immediate. |
|
|
861
862
|
| `session.constraints` | none | `{ constraints }` | List the attached session's membership constraints. |
|
|
@@ -1083,9 +1084,9 @@ The CAS is the **hard backstop**, at the moment of writing, on every accept path
|
|
|
1083
1084
|
**Decision — a pre-LLM grinder, fired only on actual overflow.** In `Engine.runTurn`, after the packet is assembled (`#buildRequestPacket`) and before `provider.generate`, the assembled render-weight (§tokenomics) is measured against the ceiling. At or under → the packet ships untouched; the grinder never trims speculatively or "helpfully." {§grinder-overflow-only} On overflow it reverts the prior turn, then hard-stops if that isn't enough:
|
|
1084
1085
|
|
|
1085
1086
|
- **Prior-turn rollback.** The immediately-prior turn's log entries — the latest emissions, the ones that pushed the packet over — are folded (`expanded=0`, the same flag the model's own FOLD uses); the prior turn fit by induction, so reverting it usually lands back under. Folded, not deleted: rows and bodies persist and are re-OPENable, so log *history* is preserved while the render collapses to coordinates. {§grinder-layer1-rollback}
|
|
1086
|
-
- **Hard stop.** If the packet still overflows after the prior-turn rollback, the loop abandons at
|
|
1087
|
+
- **Hard stop.** If the packet still overflows after the prior-turn rollback, the loop abandons at **413 Content Too Large** (`engine_loop_set_status`) — the content genuinely won't fit, and the anchor's name is finally its status. Its sibling engine-imposed terminals are HTTP-precise too: `maxTurns` → 429, a strike-out → 500 (508 when cycle-driven) — no longer the old catch-all 499. No further passes. {§grinder-hard-413-abort}
|
|
1087
1088
|
|
|
1088
|
-
**Strike coupling.** A grinder fire bumps the engine's `turnErrors` — the same internal counter cycle detection feeds — so an overflow counts toward the strike streak that ends a runaway loop at
|
|
1089
|
+
**Strike coupling.** A grinder fire bumps the engine's `turnErrors` — the same internal counter cycle detection feeds — so an overflow counts toward the strike streak that ends a runaway loop at 500 (or 508 if the crossing strike was a detected cycle). This is the pressure that keeps self-curation the path of least resistance. {§grinder-strike-coupling} **Turn 0/1 is exempt:** the first turn's overflow precedes any model action — it's the environment, not the model — so it never strikes. {§grinder-soft-turn-0-1}
|
|
1089
1090
|
|
|
1090
1091
|
**What the model sees.** A `budget_overflow` telemetry event (§telemetry), in the model's own terms: which of its entries left the window, by scheme. No mechanism vocabulary — no "layer," no "grinder," no "reclaim" — and no advice. The engine reports *what happened to the model's world*; the budget readout (§tokenomics) — its turn and entry weights — is the diagnostic surface, and the model — which can see what changed in its repo, its reads, its turn — diagnoses the cause the engine can't attribute. {§grinder-event-model-terms} Per the gamification policy (§telemetry), the *strike* the overflow triggers stays engine-internal; the model sees the hidden entries, never the accounting.
|
|
1091
1092
|
|
package/bin/plurnk-service.ts
CHANGED
|
@@ -24,10 +24,25 @@ export default class Cli {
|
|
|
24
24
|
try { process.loadEnvFile(path); }
|
|
25
25
|
catch (cause) { Cli.#die(64, `failed to load ${path}: ${cause instanceof Error ? cause.message : String(cause)}`); }
|
|
26
26
|
} else if (required) {
|
|
27
|
-
Cli.#die(64,
|
|
27
|
+
Cli.#die(64, `${path} does not exist`);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// node-style env-file flags: --env-file=<path> (required) / --env-file-if-exists=<path>
|
|
32
|
+
// (skip if missing), repeatable, in command-line order. They layer extra files ABOVE
|
|
33
|
+
// the .env cascade but BELOW shell env (loadEnvFile is set-if-unset) and the --<knob>
|
|
34
|
+
// CLI flags (assigned last). The `=` form only — node's canonical syntax (so it never
|
|
35
|
+
// leaks a positional). NB: node validates these paths from the full argv (and exits on
|
|
36
|
+
// a missing *required* one), but only LOADS pre-script files — the post-script loading
|
|
37
|
+
// a published `plurnk-service --env-file=…` needs is this.
|
|
38
|
+
static #envFileArgs(): Array<{ path: string; required: boolean }> {
|
|
39
|
+
return process.argv.flatMap((a): Array<{ path: string; required: boolean }> => {
|
|
40
|
+
if (a.startsWith("--env-file-if-exists=")) return [{ path: a.slice(a.indexOf("=") + 1), required: false }];
|
|
41
|
+
if (a.startsWith("--env-file=")) return [{ path: a.slice(a.indexOf("=") + 1), required: true }];
|
|
42
|
+
return [];
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
31
46
|
// The .env cascade always populates these from .env.example, so absence is
|
|
32
47
|
// a broken config, not a runtime branch — fail hard rather than `?? ""`.
|
|
33
48
|
static #requireEnv(name: string): string {
|
|
@@ -36,11 +51,34 @@ export default class Cli {
|
|
|
36
51
|
return value;
|
|
37
52
|
}
|
|
38
53
|
|
|
54
|
+
// Optional integer sqlite tuning knob — undefined when unset (so it never clobbers
|
|
55
|
+
// sqlrite's default by spreading an explicit `undefined`); fail-hard on a non-integer.
|
|
56
|
+
static #sqliteKnob(name: string): number | undefined {
|
|
57
|
+
const raw = process.env[name];
|
|
58
|
+
if (raw === undefined || raw.trim() === "") return undefined;
|
|
59
|
+
const n = Number(raw);
|
|
60
|
+
if (!Number.isInteger(n)) Cli.#die(78, `${name} must be an integer, got ${JSON.stringify(raw)}`);
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
|
|
39
64
|
static async #openDb(dbPath: string): Promise<Db> {
|
|
65
|
+
// Curated sqlite tuning (sqlrite 5.2.0, #7) — pass through ONLY the knobs the
|
|
66
|
+
// operator set, so an unset one keeps sqlrite's default (e.g. busy_timeout=5000).
|
|
67
|
+
const tuning: Record<string, number> = {};
|
|
68
|
+
for (const [env, opt] of [
|
|
69
|
+
["PLURNK_SQLITE_TIMEOUT", "timeout"],
|
|
70
|
+
["PLURNK_SQLITE_CACHE_SIZE", "cacheSize"],
|
|
71
|
+
["PLURNK_SQLITE_MMAP_SIZE", "mmapSize"],
|
|
72
|
+
["PLURNK_SQLITE_MAX_PAGE_COUNT", "maxPageCount"],
|
|
73
|
+
] as const) {
|
|
74
|
+
const v = Cli.#sqliteKnob(env);
|
|
75
|
+
if (v !== undefined) tuning[opt] = v;
|
|
76
|
+
}
|
|
40
77
|
const db = await SqlRite.open({
|
|
41
78
|
path: dbPath,
|
|
42
79
|
dir: [resolve(Cli.#projectRoot, "migrations"), resolve(Cli.#projectRoot, "src")],
|
|
43
80
|
functions: [resolve(Cli.#projectRoot, "src/schemes/cosine.ts")],
|
|
81
|
+
...tuning,
|
|
44
82
|
});
|
|
45
83
|
return db as unknown as Db;
|
|
46
84
|
}
|
|
@@ -71,10 +109,13 @@ export default class Cli {
|
|
|
71
109
|
}
|
|
72
110
|
|
|
73
111
|
static async main(): Promise<void> {
|
|
74
|
-
// Env cascade
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
112
|
+
// Env cascade, highest precedence loads FIRST (loadEnvFile is set-if-unset,
|
|
113
|
+
// first write wins): --env-file(s) < --config < .env < .env.example, all of them
|
|
114
|
+
// OUTRANKED by pre-set shell env, then by the --<knob> CLI flags (assigned last,
|
|
115
|
+
// below). So --env-file overrides the .env files but never a shell var or a CLI
|
|
116
|
+
// arg — node-idiomatic layering.
|
|
117
|
+
for (const { path: envFile, required } of Cli.#envFileArgs()) Cli.#loadEnv(envFile, required);
|
|
118
|
+
|
|
78
119
|
const configFlagIndex = process.argv.findIndex((a) => a === "--config" || a.startsWith("--config="));
|
|
79
120
|
const configFile = ((): string | null => {
|
|
80
121
|
if (configFlagIndex === -1) return null;
|
|
@@ -97,8 +138,10 @@ export default class Cli {
|
|
|
97
138
|
|
|
98
139
|
${EnvFlags.formatFlagsHelp(flagDescriptors)}
|
|
99
140
|
|
|
100
|
-
--
|
|
101
|
-
-
|
|
141
|
+
--env-file=<path> layer env from <path> (repeatable; errors if missing)
|
|
142
|
+
--env-file-if-exists=<path> layer env from <path> if present (repeatable)
|
|
143
|
+
--config=<path> layer additional env from <path>
|
|
144
|
+
-h, --help show this help
|
|
102
145
|
`;
|
|
103
146
|
|
|
104
147
|
const { positionals, values } = parseArgs({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// Helpers update entry_channels (content / state) and subscriptions, and emit
|
|
7
7
|
// stream/event notifications scoped to the entry's session via an optional
|
|
8
8
|
// callback the daemon wires in.
|
|
9
|
+
import { renderAddress } from "./plurnk-uri.js";
|
|
9
10
|
export default class ChannelWrite {
|
|
10
11
|
static #channelMeta(db) { return db.channel_meta; }
|
|
11
12
|
static #appendStmt(db) { return db.append_to_channel; }
|
|
@@ -19,7 +20,7 @@ export default class ChannelWrite {
|
|
|
19
20
|
// a filesystem entry (the file scheme stores scheme=NULL), so it decodes to
|
|
20
21
|
// file:///.
|
|
21
22
|
static #targetUri(scheme, pathname) {
|
|
22
|
-
return
|
|
23
|
+
return renderAddress(scheme === null ? "file" : scheme, pathname);
|
|
23
24
|
}
|
|
24
25
|
// A stream chunk accumulates into the channel's content (§chunk-accumulation-chunks-accumulate)
|
|
25
26
|
// and fires a stream/event (§live-updates-stream-event-fires-on-chunk); the log carries the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,sFAAsF;AACtF,EAAE;AACF,mFAAmF;AACnF,4FAA4F;AAC5F,8EAA8E;AAC9E,2EAA2E;AAC3E,gCAAgC;
|
|
1
|
+
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"Engine.d.ts","sourceRoot":"","sources":["../../src/core/Engine.ts"],"names":[],"mappings":"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"}
|
package/dist/core/Engine.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Mimetypes, emptyRegistry } from "@plurnk/plurnk-mimetypes";
|
|
|
4
4
|
import EntryCrud from "../schemes/_entry-crud.js";
|
|
5
5
|
import EntryManifest from "../schemes/_entry-manifest.js";
|
|
6
6
|
import GitMembership from "./git-membership.js";
|
|
7
|
+
import { foldAuthorityIntoPath, renderAddress } from "./plurnk-uri.js";
|
|
7
8
|
import GitState from "./git-state.js";
|
|
8
9
|
import Fork from "./fork.js";
|
|
9
10
|
import RunCap from "./run-cap.js";
|
|
@@ -392,9 +393,11 @@ class Engine {
|
|
|
392
393
|
return { turnIds, finalStatus: row.status, hitMaxTurns: false, reason: "external" };
|
|
393
394
|
}
|
|
394
395
|
if (maxTurns >= 0 && turnIds.length >= maxTurns) {
|
|
395
|
-
|
|
396
|
+
// §loop-terminals — the turn ceiling is exhausted: 429 Too Many Requests
|
|
397
|
+
// (kin to the soft sudden-death 429 warnings that precede it).
|
|
398
|
+
await this.#db.engine_loop_set_status.run({ loop_id: loopId, status: 429, message: "max_turns" });
|
|
396
399
|
cleanup("forceful", "max_turns");
|
|
397
|
-
return { turnIds, finalStatus:
|
|
400
|
+
return { turnIds, finalStatus: 429, hitMaxTurns: true, reason: "max_turns" };
|
|
398
401
|
}
|
|
399
402
|
// PLURNK_EXEC_WAIT_MS — a post-EXEC breath: if a spawn from the prior turn
|
|
400
403
|
// is still in flight, give it a tunable beat to land in THIS turn's packet
|
|
@@ -413,9 +416,10 @@ class Engine {
|
|
|
413
416
|
turnIds.push(turn.turnId);
|
|
414
417
|
// SPEC §grinder: budget hard-stop — packet won't fit even collapsed → abandon.
|
|
415
418
|
if (turn.budgetHardStop) {
|
|
416
|
-
|
|
419
|
+
// §loop-terminals — the packet won't fit even collapsed: 413 Content Too Large.
|
|
420
|
+
await this.#db.engine_loop_set_status.run({ loop_id: loopId, status: 413, message: "budget_overflow" });
|
|
417
421
|
cleanup("forceful", "budget_overflow");
|
|
418
|
-
return { turnIds, finalStatus:
|
|
422
|
+
return { turnIds, finalStatus: 413, hitMaxTurns: false, reason: "budget_overflow" };
|
|
419
423
|
}
|
|
420
424
|
// Rail #39: cycle detection. Push this turn's fingerprint to
|
|
421
425
|
// history, scan for repetition patterns. Detection bumps
|
|
@@ -454,9 +458,13 @@ class Engine {
|
|
|
454
458
|
if (struck) {
|
|
455
459
|
state.streak++;
|
|
456
460
|
if (state.streak >= maxStrikes) {
|
|
457
|
-
|
|
461
|
+
// §loop-terminals — a cycle-driven strike is the model spinning in place
|
|
462
|
+
// (508 Loop Detected); a failure/no-op strike is the model failing (500
|
|
463
|
+
// Internal Server Error). The straw that crossed the threshold picks it.
|
|
464
|
+
const status = cycle.detected ? 508 : 500;
|
|
465
|
+
await this.#db.engine_loop_set_status.run({ loop_id: loopId, status, message: "strike_threshold" });
|
|
458
466
|
cleanup("forceful", "strike_threshold");
|
|
459
|
-
return { turnIds, finalStatus:
|
|
467
|
+
return { turnIds, finalStatus: status, hitMaxTurns: false, reason: "strike_threshold" };
|
|
460
468
|
}
|
|
461
469
|
}
|
|
462
470
|
else {
|
|
@@ -531,10 +539,10 @@ class Engine {
|
|
|
531
539
|
const promptRow = await this.#db.engine_get_loop_prompt.get({ loop_id: loopId });
|
|
532
540
|
if (promptRow !== undefined && typeof promptRow.prompt === "string" && promptRow.prompt.length > 0) {
|
|
533
541
|
const promptPath = {
|
|
534
|
-
kind: "url", raw: `plurnk
|
|
542
|
+
kind: "url", raw: `plurnk://prompt/${loopId}/${seq}`,
|
|
535
543
|
scheme: "plurnk", username: null, password: null,
|
|
536
|
-
hostname:
|
|
537
|
-
pathname:
|
|
544
|
+
hostname: "prompt", port: null,
|
|
545
|
+
pathname: `/${loopId}/${seq}`, params: {}, fragment: null,
|
|
538
546
|
};
|
|
539
547
|
const promptStmt = {
|
|
540
548
|
op: "EDIT", suffix: "", signal: null,
|
|
@@ -547,9 +555,9 @@ class Engine {
|
|
|
547
555
|
sequence: nextActionIndex, origin: "plurnk",
|
|
548
556
|
onDispatch: (id) => { promptLogId = id; onDispatch?.(id); },
|
|
549
557
|
});
|
|
550
|
-
// §prompt-fold (User Note 6): the prompt EDIT duplicates
|
|
551
|
-
//
|
|
552
|
-
//
|
|
558
|
+
// §prompt-fold (User Note 6): the prompt EDIT duplicates the
|
|
559
|
+
// prompt section, so fold it — logged for forensics, collapsed
|
|
560
|
+
// in the model's log, re-OPENable.
|
|
553
561
|
if (promptLogId !== undefined)
|
|
554
562
|
await this.#db.engine_fold_log_entry.run({ id: promptLogId });
|
|
555
563
|
nextActionIndex++;
|
|
@@ -652,7 +660,7 @@ class Engine {
|
|
|
652
660
|
// grammar can't bound degeneration *inside* a statement body — this caps the
|
|
653
661
|
// decode at the free window so a runaway can't reach the context wall.
|
|
654
662
|
const genCeiling = _a.computeCeiling(provider.contextSize, this.#budgetCeiling); // provider.contextSize, the immutable identity, read by the budget — §provider-surface-identity
|
|
655
|
-
const maxTokens = genCeiling === null ? undefined : Math.max(1, genCeiling - requestPacket.
|
|
663
|
+
const maxTokens = genCeiling === null ? undefined : Math.max(1, genCeiling - requestPacket.tokens);
|
|
656
664
|
const response = await provider.generate({ messages: modelMessages, runId: String(runId), signal, grammar: await this.#grammarConstraint(), maxTokens }); // §provider-surface-generate §provider-guarantees-single-call §provider-guarantees-signal-wired
|
|
657
665
|
// Engine splits wire-level response: emission (content, reasoning,
|
|
658
666
|
// parsed ops) → packet.assistant per Packet.json §assistant;
|
|
@@ -846,14 +854,15 @@ class Engine {
|
|
|
846
854
|
// scheme-agnostic, so the service teaches what schemes exist + what they do
|
|
847
855
|
// at packet-time (grammar#239 item 7). SchemeRegistry.teach() assembles it.
|
|
848
856
|
const system_definition = `${byRole("system")}\n\n${this.#schemes.teach()}`;
|
|
849
|
-
//
|
|
857
|
+
// the prompt section sources from the loop's most recent prompt entry first
|
|
850
858
|
// (plurnk:///prompt/<loop_id>/<N> for the highest N written to date).
|
|
851
859
|
// This is what inject + the turn-1 foist write into. Falls back to
|
|
852
860
|
// the runLoop caller's messages.user for tests that bypass the
|
|
853
861
|
// foist mechanism entirely.
|
|
854
|
-
const latestPromptRow = await this.#db.drain_get_latest_prompt_body_for_loop.get({ pattern:
|
|
862
|
+
const latestPromptRow = await this.#db.drain_get_latest_prompt_body_for_loop.get({ pattern: `/prompt/${loopId}/%` });
|
|
863
|
+
const promptCap = Number.parseInt(process.env.PLURNK_PROMPT_PREVIEW_CHARS ?? "", 10);
|
|
855
864
|
const prompt = (latestPromptRow !== undefined && typeof latestPromptRow.content === "string" && latestPromptRow.content.length > 0)
|
|
856
|
-
? latestPromptRow.content
|
|
865
|
+
? PacketWire.previewPrompt(latestPromptRow.content, renderAddress("plurnk", latestPromptRow.pathname), Number.isInteger(promptCap) ? promptCap : -1)
|
|
857
866
|
: byRole("user");
|
|
858
867
|
// Requirements is engine-sourced, NOT threaded from callers — that threading is
|
|
859
868
|
// exactly how it went missing (callers read the sysprompt but never the
|
|
@@ -867,65 +876,82 @@ class Engine {
|
|
|
867
876
|
const requirementsText = `Syntax: <<OPsuffix[signal]?(target)?<Line/Result>?:body?:OPsuffix\n\n${planDirective}${baseRequirements}`;
|
|
868
877
|
const log = await this.#buildLog(runId);
|
|
869
878
|
const telemetryErrors = presetTelemetry ?? await this.#buildTelemetryErrors(loopId, currentTurnSeq);
|
|
870
|
-
// Per-section render-cost subtotals via provider's tokenizer.
|
|
871
|
-
// Engine approximates each section by tokenizing its serialized
|
|
872
|
-
// form — wire-payload tokens may differ slightly because chat-
|
|
873
|
-
// template scaffolding adds bytes, but the subtotal tracks "what
|
|
874
|
-
// the model has to process" closely enough for budget diagnostics.
|
|
875
879
|
const countTokens = (t) => provider.countTokens(t); // §provider-surface-counttokens
|
|
876
|
-
|
|
877
|
-
//
|
|
878
|
-
//
|
|
879
|
-
//
|
|
880
|
-
//
|
|
881
|
-
//
|
|
882
|
-
//
|
|
880
|
+
const tools = this.#collectTools();
|
|
881
|
+
// Budget readout (SPEC.md §tokenomics). Two-pass: render the budget from
|
|
882
|
+
// the structured log's subtotals with a {{tokensFree}} placeholder, build
|
|
883
|
+
// the section list, measure the assembled total, resolve free, substitute.
|
|
884
|
+
// Subtotals come from the real log render — meta and fences included — not
|
|
885
|
+
// a serialized approximation. ceiling is the provider's window ×
|
|
886
|
+
// PLURNK_BUDGET_CEILING (null when no window is reported → headline
|
|
887
|
+
// omitted, section lines still shown). §tokenomics-render-weight-budget
|
|
883
888
|
const ceiling = _a.computeCeiling(provider.contextSize, this.#budgetCeiling);
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
889
|
+
const budgetReadout = this.#renderBudget(PacketWire.measureLogBudget(log, countTokens), ceiling);
|
|
890
|
+
// The default packet: an ordered list of sections, each addressable state
|
|
891
|
+
// (§packet-construction). `slot` is the prompt-cache boundary; order within
|
|
892
|
+
// a slot is the render order (requirements last — the contract closest to
|
|
893
|
+
// the assistant turn). budget/errors/git are peer sections (unbundled —
|
|
894
|
+
// each independently overridable). The budget section still carries its
|
|
895
|
+
// {{tokensFree}} placeholders here; they resolve below once the assembled
|
|
896
|
+
// total is known.
|
|
897
|
+
const defaults = [
|
|
898
|
+
{ name: "definition", slot: "system", header: null, content: system_definition, tokens: 0 },
|
|
899
|
+
{ name: "log", slot: "system", header: "Plurnk System Log", content: PacketWire.renderLog(log), tokens: 0 },
|
|
900
|
+
{ name: "prompt", slot: "user", header: "Plurnk System User Prompt", content: prompt, tokens: 0 },
|
|
901
|
+
{ name: "budget", slot: "user", header: "Plurnk System Budget", content: budgetReadout, tokens: 0 },
|
|
902
|
+
{ name: "errors", slot: "user", header: "Plurnk System Errors", content: PacketWire.renderErrors(telemetryErrors), tokens: 0 },
|
|
903
|
+
{ name: "git", slot: "user", header: "Plurnk System Git Status", content: PacketWire.renderGit(gitStatus), tokens: 0 },
|
|
904
|
+
{ name: "tools", slot: "user", header: "Plurnk System Tools", content: tools.join("\n"), tokens: 0 },
|
|
905
|
+
{ name: "requirements", slot: "user", header: "Plurnk System Requirements", content: requirementsText, tokens: 0 },
|
|
906
|
+
];
|
|
907
|
+
// Plugin packet control (§packet-construction): trusted schemes rewrite the
|
|
908
|
+
// default list — add, remove, reorder — in-process, before measurement.
|
|
909
|
+
const sections = await this.#schemes.transformSections(defaults);
|
|
910
|
+
// Pass 1: measure the assembled total with the placeholder budget in
|
|
911
|
+
// place, resolve free/percent, substitute into the budget section.
|
|
912
|
+
const total = countTokens(PacketWire.renderSlot(sections, "system")) + countTokens(PacketWire.renderSlot(sections, "user"));
|
|
891
913
|
const tokensFree = ceiling === null ? null : Math.max(0, ceiling - total); // free floors at 0 on overshoot — §tokenomics-over-budget-floor
|
|
892
914
|
const percent = ceiling === null ? null : Math.round((total / ceiling) * 100); // usage as % of the ceiling — §tokenomics-context-percent
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
.
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
915
|
+
if (tokensFree !== null) {
|
|
916
|
+
const budgetSec = sections.find((s) => s.name === "budget"); // a plugin may have removed it
|
|
917
|
+
if (budgetSec) {
|
|
918
|
+
budgetSec.content = budgetSec.content
|
|
919
|
+
.replace(TOKEN_USAGE_PLACEHOLDER, String(total))
|
|
920
|
+
.replace(TOKEN_PERCENT_PLACEHOLDER, percent === 0 && total > 0 ? "<1" : String(percent))
|
|
921
|
+
.replace(TOKENS_FREE_PLACEHOLDER, String(tokensFree));
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
// Pass 2: per-section render-weight + the assembled packet total (post
|
|
925
|
+
// substitution — the placeholder/number length delta is negligible).
|
|
926
|
+
for (const s of sections)
|
|
927
|
+
s.tokens = countTokens(PacketWire.renderSection(s));
|
|
928
|
+
const packetTokens = countTokens(PacketWire.renderSlot(sections, "system")) + countTokens(PacketWire.renderSlot(sections, "user"));
|
|
929
|
+
return { tokens: packetTokens, sections, telemetryErrors };
|
|
904
930
|
}
|
|
905
931
|
// Budget readout body, rendered into the `# Plurnk System Budget` section.
|
|
906
932
|
// Headline `ceiling/free` only when a ceiling exists; section lines for the
|
|
907
933
|
// curatable index/log weight the model can FOLD back. tokensFree is a
|
|
908
934
|
// placeholder here — buildSystem substitutes it after measuring the packet.
|
|
909
|
-
#renderBudget(
|
|
935
|
+
#renderBudget(log, ceiling) {
|
|
910
936
|
const lines = [];
|
|
911
937
|
if (ceiling !== null)
|
|
912
938
|
lines.push(`ceiling ${ceiling} · usage ${TOKEN_USAGE_PLACEHOLDER} (${TOKEN_PERCENT_PLACEHOLDER}%) · free ${TOKENS_FREE_PLACEHOLDER}`);
|
|
913
|
-
if (
|
|
914
|
-
lines.push(`Log entries: ${
|
|
939
|
+
if (log.entries > 0) {
|
|
940
|
+
lines.push(`Log entries: ${log.entries} entries, ${log.tokens} tokens`);
|
|
915
941
|
// Per-turn weight — the grinder's rollback unit, oldest first: the
|
|
916
942
|
// model sees what's first to go (§tokenomics {§tokenomics-turn-totals}).
|
|
917
|
-
if (
|
|
943
|
+
if (log.byTurn.length > 0) {
|
|
918
944
|
lines.push("Turns:", "| turn | tokens |", "|---|--:|");
|
|
919
|
-
for (const t of
|
|
945
|
+
for (const t of log.byTurn)
|
|
920
946
|
lines.push(`| ${t.turn} | ${t.tokens} |`);
|
|
921
947
|
}
|
|
922
948
|
// The heaviest individual log items — the FOLD targets behind the weight
|
|
923
949
|
// (§tokenomics {§tokenomics-largest-entries}). "items", not "entries": the readout
|
|
924
950
|
// lists log:/// rows (log items), distinct from catalog entries (plurnk.md: "EDIT
|
|
925
951
|
// is only for entries. Do not attempt to edit log items.").
|
|
926
|
-
if (
|
|
952
|
+
if (log.largest.length > 0) {
|
|
927
953
|
lines.push("Heaviest items:", "| item | tokens |", "|---|--:|");
|
|
928
|
-
for (const e of
|
|
954
|
+
for (const e of log.largest)
|
|
929
955
|
lines.push(`| ${e.path} | ${e.tokens} |`);
|
|
930
956
|
}
|
|
931
957
|
}
|
|
@@ -951,16 +977,16 @@ class Engine {
|
|
|
951
977
|
const entry = this.#executors.entry(tag);
|
|
952
978
|
if (entry?.example)
|
|
953
979
|
tools.push(`* ${entry.example}`);
|
|
954
|
-
// #note12 — link the executor's fuller doc (materialized at plurnk:///docs/<tag
|
|
980
|
+
// #note12 — link the executor's fuller doc (materialized at plurnk:///docs/<tag>.md);
|
|
955
981
|
// its token cost rides that manifest entry, so no inline recount here.
|
|
956
982
|
if (entry?.documentation)
|
|
957
|
-
tools.push(`* docs for ${tag}: plurnk
|
|
983
|
+
tools.push(`* docs for ${tag}: plurnk://docs/${tag}.md`);
|
|
958
984
|
}
|
|
959
985
|
}
|
|
960
986
|
return tools;
|
|
961
987
|
}
|
|
962
988
|
// #note12 — the daughter-provided reference docs (schemes' + execs' `documentation`),
|
|
963
|
-
// materialized at plurnk:///docs/<name
|
|
989
|
+
// materialized at plurnk:///docs/<name>.md by loop_run (like operator docs) so the
|
|
964
990
|
// catalogue's doc-links READ and the manifest carries each doc's token cost.
|
|
965
991
|
docEntries() {
|
|
966
992
|
const out = this.#schemes.docs();
|
|
@@ -981,7 +1007,7 @@ class Engine {
|
|
|
981
1007
|
// §grinder-overflow-only — fires only on actual overflow, never speculatively
|
|
982
1008
|
async #enforceBudget({ packet, provider, runId, loopId, turnId, sessionId, turnNumber, rebuild }) {
|
|
983
1009
|
const ceiling = _a.computeCeiling(provider.contextSize, this.#budgetCeiling);
|
|
984
|
-
const measure = (p) => p.
|
|
1010
|
+
const measure = (p) => p.tokens;
|
|
985
1011
|
if (ceiling === null || measure(packet) <= ceiling)
|
|
986
1012
|
return { packet, fit: true, struck: false };
|
|
987
1013
|
const folded = new Map();
|
|
@@ -993,7 +1019,7 @@ class Engine {
|
|
|
993
1019
|
note(le.scheme ?? "log");
|
|
994
1020
|
if (priorLogs.length > 0)
|
|
995
1021
|
await this.#db.engine_grinder_fold_prior_turn_logs.run({ loop_id: loopId, turn_id: turnId });
|
|
996
|
-
const errors = packet.
|
|
1022
|
+
const errors = packet.telemetryErrors;
|
|
997
1023
|
let current = priorLogs.length > 0 ? await rebuild(errors) : packet;
|
|
998
1024
|
if (measure(current) <= ceiling) {
|
|
999
1025
|
this.#emitBudgetOverflow(sessionId, loopId, folded);
|
|
@@ -1030,9 +1056,9 @@ class Engine {
|
|
|
1030
1056
|
#completePacket(requestPacket, assistant, assistantRaw, provider) {
|
|
1031
1057
|
const assistantTokens = provider.countTokens(assistant.content);
|
|
1032
1058
|
return {
|
|
1033
|
-
tokens: requestPacket.
|
|
1034
|
-
|
|
1035
|
-
|
|
1059
|
+
tokens: requestPacket.tokens + assistantTokens,
|
|
1060
|
+
sections: requestPacket.sections,
|
|
1061
|
+
telemetryErrors: requestPacket.telemetryErrors,
|
|
1036
1062
|
assistant,
|
|
1037
1063
|
assistantRaw,
|
|
1038
1064
|
};
|
|
@@ -1067,7 +1093,7 @@ class Engine {
|
|
|
1067
1093
|
});
|
|
1068
1094
|
return [...this.#drainTelemetry(loopId), ...actionFailures];
|
|
1069
1095
|
}
|
|
1070
|
-
// SPEC §packet
|
|
1096
|
+
// SPEC §packet the log section — chronological action-entries for the loop.
|
|
1071
1097
|
// Snapshot is taken at packet build (pre-dispatch this turn), so it
|
|
1072
1098
|
// reflects "what has happened before this turn." Each row carries a
|
|
1073
1099
|
// log:///<loop_seq>/<turn_seq>/<sequence> coordinate the model can READ.
|
|
@@ -1397,8 +1423,8 @@ class Engine {
|
|
|
1397
1423
|
return (row?.n ?? 0) > 0;
|
|
1398
1424
|
}
|
|
1399
1425
|
// Inject a prompt into the run's currently-executing loop. Writes a
|
|
1400
|
-
// plurnk:///prompt/<loop_id>/<next-turn> entry whose body becomes
|
|
1401
|
-
//
|
|
1426
|
+
// plurnk:///prompt/<loop_id>/<next-turn> entry whose body becomes the
|
|
1427
|
+
// prompt section at the next turn boundary. Last-wins: if two
|
|
1402
1428
|
// injects target the same next-turn slot, the second overwrites the
|
|
1403
1429
|
// first.
|
|
1404
1430
|
//
|
|
@@ -1418,7 +1444,7 @@ class Engine {
|
|
|
1418
1444
|
const sessionRow = await this.#db.drain_get_run_session.get({ run_id: runId });
|
|
1419
1445
|
if (sessionRow === undefined)
|
|
1420
1446
|
throw new Error(`Engine.inject: run ${runId} not found`);
|
|
1421
|
-
const pathname =
|
|
1447
|
+
const pathname = `/prompt/${loopId}/${turnSeq}`; // canonical storage form (leading slash), matching the foist via #pathnameOf
|
|
1422
1448
|
const ctx = {
|
|
1423
1449
|
db: this.#db, sessionId: sessionRow.session_id, runId, loopId,
|
|
1424
1450
|
turnId: 0, // no turn open at inject time; entries don't pin turnId
|
|
@@ -1948,9 +1974,14 @@ class Engine {
|
|
|
1948
1974
|
if (path.kind === "local")
|
|
1949
1975
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: decodePathParens(path.raw), params: null, fragment: null }; // #239 item 4
|
|
1950
1976
|
const scheme = path.scheme === "file" ? null : path.scheme;
|
|
1977
|
+
// plurnk uses its authority as a namespace — fold it into the canonical pathname so the
|
|
1978
|
+
// log keys identically to the entry (/prompt/<loop>, /docs/x.md). A web host (http://) is
|
|
1979
|
+
// NOT a namespace: keep it in hostname.
|
|
1980
|
+
const foldNs = scheme === "plurnk";
|
|
1951
1981
|
return {
|
|
1952
1982
|
scheme, username: path.username, password: path.password,
|
|
1953
|
-
hostname: path.hostname, port: path.port,
|
|
1983
|
+
hostname: foldNs ? null : path.hostname, port: path.port,
|
|
1984
|
+
pathname: decodePathParens(foldNs ? foldAuthorityIntoPath(path.hostname, path.pathname) : path.pathname), // #239 item 4
|
|
1954
1985
|
params: JSON.stringify(path.params), fragment: path.fragment,
|
|
1955
1986
|
};
|
|
1956
1987
|
}
|