@plurnk/plurnk-service 0.34.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SPEC.md +51 -15
- package/dist/content/matcher.d.ts +1 -0
- package/dist/content/matcher.d.ts.map +1 -1
- package/dist/content/matcher.js +1 -1
- package/dist/content/matcher.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +1 -0
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/Engine.d.ts +8 -2
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +99 -28
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/ExecutorRegistry.d.ts +2 -0
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +1 -0
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +4 -0
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +18 -1
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/fork.js +1 -1
- package/dist/core/fork.js.map +1 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +44 -22
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +3 -1
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/packet-wire.js +1 -1
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/path-decode.d.ts +2 -0
- package/dist/core/path-decode.d.ts.map +1 -0
- package/dist/core/path-decode.js +8 -0
- package/dist/core/path-decode.js.map +1 -0
- package/dist/core/run-cap.d.ts +8 -0
- package/dist/core/run-cap.d.ts.map +1 -0
- package/dist/core/run-cap.js +20 -0
- package/dist/core/run-cap.js.map +1 -0
- package/dist/core/session-settings.d.ts +19 -0
- package/dist/core/session-settings.d.ts.map +1 -0
- package/dist/core/session-settings.js +44 -0
- package/dist/core/session-settings.js.map +1 -0
- package/dist/schemes/File.d.ts +2 -0
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +72 -11
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.js +7 -7
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Run.d.ts.map +1 -1
- package/dist/schemes/Run.js +5 -1
- package/dist/schemes/Run.js.map +1 -1
- package/dist/schemes/_entry-find.d.ts +14 -3
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +59 -27
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-graph.d.ts +6 -0
- package/dist/schemes/_entry-graph.d.ts.map +1 -1
- package/dist/schemes/_entry-graph.js +8 -0
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +13 -0
- 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 +4 -3
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-semantic.d.ts +5 -1
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +10 -8
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/_entry-send.d.ts.map +1 -1
- package/dist/schemes/_entry-send.js +4 -3
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +26 -1
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/envelope.d.ts +2 -0
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +8 -1
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/methods/discover.d.ts.map +1 -1
- package/dist/server/methods/discover.js +6 -3
- package/dist/server/methods/discover.js.map +1 -1
- package/dist/server/methods/loop_run.d.ts.map +1 -1
- package/dist/server/methods/loop_run.js +14 -10
- package/dist/server/methods/loop_run.js.map +1 -1
- package/dist/server/methods/session_constraints.js +6 -6
- package/dist/server/methods/session_constraints.js.map +1 -1
- package/dist/server/methods/session_create.d.ts.map +1 -1
- package/dist/server/methods/session_create.js +55 -5
- package/dist/server/methods/session_create.js.map +1 -1
- package/dist/server/methods/session_prompts.d.ts +5 -0
- package/dist/server/methods/session_prompts.d.ts.map +1 -0
- package/dist/server/methods/session_prompts.js +29 -0
- package/dist/server/methods/session_prompts.js.map +1 -0
- package/dist/server/version-info.d.ts +14 -0
- package/dist/server/version-info.d.ts.map +1 -0
- package/dist/server/version-info.js +69 -0
- package/dist/server/version-info.js.map +1 -0
- package/dist/server/yolo.d.ts.map +1 -1
- package/dist/server/yolo.js +8 -0
- package/dist/server/yolo.js.map +1 -1
- package/migrations/0000-00-00.01_schema.sql +29 -8
- package/package.json +22 -22
package/dist/core/Engine.js
CHANGED
|
@@ -6,6 +6,9 @@ import EntryManifest from "../schemes/_entry-manifest.js";
|
|
|
6
6
|
import GitMembership from "./git-membership.js";
|
|
7
7
|
import GitState from "./git-state.js";
|
|
8
8
|
import Fork from "./fork.js";
|
|
9
|
+
import RunCap from "./run-cap.js";
|
|
10
|
+
import SessionSettings from "./session-settings.js";
|
|
11
|
+
import { decodePathParens } from "./path-decode.js";
|
|
9
12
|
import { DEFAULT_LOOP_FLAGS } from "./scheme-types.js";
|
|
10
13
|
import { LineMarkerOps, MimetypeBinary, editedSpan } from "../content/index.js";
|
|
11
14
|
import { readFile } from "node:fs/promises";
|
|
@@ -60,14 +63,12 @@ const readMaxCommands = () => {
|
|
|
60
63
|
};
|
|
61
64
|
// PLURNK_MANIFEST_ITEMS — the turn-0 manifest preview. null = off (no foist);
|
|
62
65
|
// -1 = the full manifest; positive N = the first N items. 0 / unset = off.
|
|
66
|
+
const normalizeManifestItems = (n) => (!Number.isFinite(n) || n === 0 ? null : n < 0 ? -1 : n);
|
|
63
67
|
const readManifestItems = () => {
|
|
64
68
|
const raw = process.env.PLURNK_MANIFEST_ITEMS;
|
|
65
69
|
if (raw === undefined || raw.length === 0)
|
|
66
70
|
return null;
|
|
67
|
-
|
|
68
|
-
if (!Number.isFinite(n) || n === 0)
|
|
69
|
-
return null;
|
|
70
|
-
return n < 0 ? -1 : n;
|
|
71
|
+
return normalizeManifestItems(Number.parseInt(raw, 10));
|
|
71
72
|
};
|
|
72
73
|
// Resolution timeout — proposed entries auto-cancel if nothing arrives
|
|
73
74
|
// within this window. SPEC.md §engine-rails (proposal lifecycle) + §methods (loop.resolve).
|
|
@@ -82,9 +83,9 @@ const readProposalTimeoutMs = () => {
|
|
|
82
83
|
return n;
|
|
83
84
|
};
|
|
84
85
|
const pathnameFromPath = (path) => {
|
|
85
|
-
if (path.kind === "
|
|
86
|
-
return path.
|
|
87
|
-
return path.raw;
|
|
86
|
+
if (path.kind === "regex")
|
|
87
|
+
return path.raw; // regex source — parens are syntax, never encoded
|
|
88
|
+
return decodePathParens(path.kind === "url" ? path.pathname : path.raw); // #239 item 4
|
|
88
89
|
};
|
|
89
90
|
// Default turn.status when ops were emitted but no SEND. Model is implicitly
|
|
90
91
|
// continuing; loop.status stays 102 either way (only SEND broadcast advances
|
|
@@ -241,6 +242,7 @@ class Engine {
|
|
|
241
242
|
#streamEventNotify;
|
|
242
243
|
#wakeRunNotify;
|
|
243
244
|
#injectRun;
|
|
245
|
+
#cancelRun;
|
|
244
246
|
// Telemetry event fan-out: every TelemetryEvent pushed to the loop's
|
|
245
247
|
// buffer is also broadcast live to the connected client(s) on the
|
|
246
248
|
// session. Without this, the client sees `loop/terminated` with a
|
|
@@ -249,12 +251,13 @@ class Engine {
|
|
|
249
251
|
#telemetryEventNotify;
|
|
250
252
|
// Cached plurnk GBNF — read once on the first constrained generate (#189).
|
|
251
253
|
#gbnfCache = null;
|
|
252
|
-
constructor({ db, schemes, mimetypes, streamEventNotify, wakeRunNotify, injectRun, telemetryEventNotify, tokenize }) {
|
|
254
|
+
constructor({ db, schemes, mimetypes, streamEventNotify, wakeRunNotify, injectRun, cancelRun, telemetryEventNotify, tokenize }) {
|
|
253
255
|
this.#db = db;
|
|
254
256
|
this.#schemes = schemes;
|
|
255
257
|
this.#streamEventNotify = streamEventNotify;
|
|
256
258
|
this.#wakeRunNotify = wakeRunNotify;
|
|
257
259
|
this.#injectRun = injectRun;
|
|
260
|
+
this.#cancelRun = cancelRun;
|
|
258
261
|
this.#telemetryEventNotify = telemetryEventNotify;
|
|
259
262
|
// Default to empty discovery — standalone Engine construction (in
|
|
260
263
|
// tests) gets no handlers, and content flows through the framework's
|
|
@@ -389,7 +392,7 @@ class Engine {
|
|
|
389
392
|
return { turnIds, finalStatus: row.status, hitMaxTurns: false, reason: "external" };
|
|
390
393
|
}
|
|
391
394
|
if (maxTurns >= 0 && turnIds.length >= maxTurns) {
|
|
392
|
-
await this.#db.engine_loop_cancel.run({ loop_id: loopId });
|
|
395
|
+
await this.#db.engine_loop_cancel.run({ loop_id: loopId, message: "max_turns" });
|
|
393
396
|
cleanup("forceful", "max_turns");
|
|
394
397
|
return { turnIds, finalStatus: 499, hitMaxTurns: true, reason: "max_turns" };
|
|
395
398
|
}
|
|
@@ -410,7 +413,7 @@ class Engine {
|
|
|
410
413
|
turnIds.push(turn.turnId);
|
|
411
414
|
// SPEC §grinder: budget hard-stop — packet won't fit even collapsed → abandon.
|
|
412
415
|
if (turn.budgetHardStop) {
|
|
413
|
-
await this.#db.engine_loop_cancel.run({ loop_id: loopId });
|
|
416
|
+
await this.#db.engine_loop_cancel.run({ loop_id: loopId, message: "budget_overflow" });
|
|
414
417
|
cleanup("forceful", "budget_overflow");
|
|
415
418
|
return { turnIds, finalStatus: 499, hitMaxTurns: false, reason: "budget_overflow" };
|
|
416
419
|
}
|
|
@@ -451,7 +454,7 @@ class Engine {
|
|
|
451
454
|
if (struck) {
|
|
452
455
|
state.streak++;
|
|
453
456
|
if (state.streak >= maxStrikes) {
|
|
454
|
-
await this.#db.engine_loop_cancel.run({ loop_id: loopId });
|
|
457
|
+
await this.#db.engine_loop_cancel.run({ loop_id: loopId, message: "strike_threshold" });
|
|
455
458
|
cleanup("forceful", "strike_threshold");
|
|
456
459
|
return { turnIds, finalStatus: 499, hitMaxTurns: false, reason: "strike_threshold" };
|
|
457
460
|
}
|
|
@@ -506,7 +509,10 @@ class Engine {
|
|
|
506
509
|
// §actor-boundary keystone); foist a READ of each into THIS turn-0 so the model
|
|
507
510
|
// reads them inline. It sees only the READ — the materializing EDIT
|
|
508
511
|
// lives in the plurnk run's log, never the model's.
|
|
509
|
-
|
|
512
|
+
// #231 — env docs (PLURNK_MD_*) UNION the session's client docs; foist a READ of
|
|
513
|
+
// each materialized plurnk:///<alias>.md (loop_run materialized the same set).
|
|
514
|
+
const { mdDocs } = await SessionSettings.read(this.#db, sessionId);
|
|
515
|
+
for (const doc of await SessionSettings.resolveDocs(mdDocs)) {
|
|
510
516
|
const docTarget = {
|
|
511
517
|
kind: "url", raw: `plurnk:///${doc.entryName}`, scheme: "plurnk",
|
|
512
518
|
username: null, password: null, hostname: null, port: null,
|
|
@@ -550,8 +556,8 @@ class Engine {
|
|
|
550
556
|
}
|
|
551
557
|
}
|
|
552
558
|
// plurnk:///manifest.json — rewritten EVERY turn (a live view of the
|
|
553
|
-
// entry set, which changes each turn). A derived view
|
|
554
|
-
// NOT an action — written directly (Engine.inject's path): no log entry,
|
|
559
|
+
// entry set, which changes each turn). A derived view (computed each
|
|
560
|
+
// turn), NOT an action — written directly (Engine.inject's path): no log entry,
|
|
555
561
|
// no sequence slot, not dispatched. The catalog body is built in the
|
|
556
562
|
// schemes layer (_entry-manifest); the engine only orchestrates the
|
|
557
563
|
// per-turn write. Does not list itself.
|
|
@@ -584,7 +590,9 @@ class Engine {
|
|
|
584
590
|
// manifest is JSON); off by default. AFTER the manifest write so the READ hits
|
|
585
591
|
// it, not a 404; same plurnk-origin foist as the operator docs.
|
|
586
592
|
if (seq === 1) {
|
|
587
|
-
|
|
593
|
+
// #231 — a session's client-chosen manifestItems REPLACES the env default outright.
|
|
594
|
+
const { manifestItems: sessionMI } = await SessionSettings.read(this.#db, sessionId);
|
|
595
|
+
const manifestItems = sessionMI !== null ? normalizeManifestItems(sessionMI) : readManifestItems();
|
|
588
596
|
if (manifestItems !== null) {
|
|
589
597
|
const manifestRead = {
|
|
590
598
|
op: "READ", suffix: "", signal: null, lineMarker: null,
|
|
@@ -710,7 +718,8 @@ class Engine {
|
|
|
710
718
|
// entries (avoids bloating forensics with hundreds of identical refusals)
|
|
711
719
|
// and the model gets a single telemetry signal next packet so it knows
|
|
712
720
|
// its emission was truncated.
|
|
713
|
-
|
|
721
|
+
// #232 — a session's maxCommands is a tighten-only ceiling: min() the env ceiling.
|
|
722
|
+
const maxCommands = Math.min(readMaxCommands(), (await SessionSettings.read(this.#db, sessionId)).maxCommands ?? Number.POSITIVE_INFINITY);
|
|
714
723
|
const opsToDispatch = packetAssistant.ops.slice(0, maxCommands);
|
|
715
724
|
const droppedCount = opsCount - opsToDispatch.length;
|
|
716
725
|
const statuses = [];
|
|
@@ -905,10 +914,12 @@ class Engine {
|
|
|
905
914
|
for (const t of sections.log.byTurn)
|
|
906
915
|
lines.push(`| ${t.turn} | ${t.tokens} |`);
|
|
907
916
|
}
|
|
908
|
-
// The heaviest individual
|
|
909
|
-
//
|
|
917
|
+
// The heaviest individual log items — the FOLD targets behind the weight
|
|
918
|
+
// (§tokenomics {§tokenomics-largest-entries}). "items", not "entries": the readout
|
|
919
|
+
// lists log:/// rows (log items), distinct from catalog entries (plurnk.md: "EDIT
|
|
920
|
+
// is only for entries. Do not attempt to edit log items.").
|
|
910
921
|
if (sections.log.largest.length > 0) {
|
|
911
|
-
lines.push("Heaviest
|
|
922
|
+
lines.push("Heaviest items:", "| item | tokens |", "|---|--:|");
|
|
912
923
|
for (const e of sections.log.largest)
|
|
913
924
|
lines.push(`| ${e.path} | ${e.tokens} |`);
|
|
914
925
|
}
|
|
@@ -935,13 +946,31 @@ class Engine {
|
|
|
935
946
|
// no backticks — see packet-wire.ts).
|
|
936
947
|
if (this.#executors !== undefined) {
|
|
937
948
|
for (const tag of this.#executors.availableRuntimes()) {
|
|
938
|
-
const
|
|
939
|
-
if (example)
|
|
940
|
-
tools.push(`* ${example}`);
|
|
949
|
+
const entry = this.#executors.entry(tag);
|
|
950
|
+
if (entry?.example)
|
|
951
|
+
tools.push(`* ${entry.example}`);
|
|
952
|
+
// #note12 — link the executor's fuller doc (materialized at plurnk:///docs/<tag>);
|
|
953
|
+
// its token cost rides that manifest entry, so no inline recount here.
|
|
954
|
+
if (entry?.documentation)
|
|
955
|
+
tools.push(`* docs for ${tag}: plurnk:///docs/${tag}`);
|
|
941
956
|
}
|
|
942
957
|
}
|
|
943
958
|
return tools;
|
|
944
959
|
}
|
|
960
|
+
// #note12 — the daughter-provided reference docs (schemes' + execs' `documentation`),
|
|
961
|
+
// materialized at plurnk:///docs/<name> by loop_run (like operator docs) so the
|
|
962
|
+
// catalogue's doc-links READ and the manifest carries each doc's token cost.
|
|
963
|
+
docEntries() {
|
|
964
|
+
const out = this.#schemes.docs();
|
|
965
|
+
if (this.#executors !== undefined) {
|
|
966
|
+
for (const tag of this.#executors.availableRuntimes()) {
|
|
967
|
+
const doc = this.#executors.entry(tag)?.documentation;
|
|
968
|
+
if (doc !== undefined && doc.length > 0)
|
|
969
|
+
out.push({ name: tag, content: doc });
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return out;
|
|
973
|
+
}
|
|
945
974
|
// SPEC §grinder — the budget grinder. Runs pre-LLM (in runTurn, after the packet
|
|
946
975
|
// is built, before provider.generate); fires only on actual overflow. Two
|
|
947
976
|
// passes, re-measuring between. Folds (never deletes) — the prior turn's logs,
|
|
@@ -1008,7 +1037,7 @@ class Engine {
|
|
|
1008
1037
|
}
|
|
1009
1038
|
// Render-time mimetype invocation (SPEC §mimetype {§mimetype-handlers-fire-render-time},
|
|
1010
1039
|
// §per-entry-channels {§per-entry-channels-preview-is-handler-output}). For each (run, entry, channel)
|
|
1011
|
-
// with
|
|
1040
|
+
// with expanded=1, pass the channel's current content through
|
|
1012
1041
|
// mimetype.preview(content, budget). State is included verbatim — engine
|
|
1013
1042
|
// does NOT branch on it (§channel-state {§channel-state-engine-does-not-branch-on-state}).
|
|
1014
1043
|
// SPEC §telemetry: model-facing alert surface.
|
|
@@ -1069,7 +1098,7 @@ class Engine {
|
|
|
1069
1098
|
mimetype_rx: r.mimetype_rx,
|
|
1070
1099
|
tx: r.mimetype_tx === "application/json" ? JSON.parse(r.tx) : r.tx,
|
|
1071
1100
|
mimetype_tx: r.mimetype_tx,
|
|
1072
|
-
folded: r.
|
|
1101
|
+
folded: r.expanded === 0,
|
|
1073
1102
|
source: r.source,
|
|
1074
1103
|
}));
|
|
1075
1104
|
}
|
|
@@ -1096,6 +1125,19 @@ class Engine {
|
|
|
1096
1125
|
});
|
|
1097
1126
|
written++;
|
|
1098
1127
|
}
|
|
1128
|
+
// §run-scheme — loop-terminations: a sibling's loop reaching terminal surfaces the
|
|
1129
|
+
// same way an entry-change does, carrying its deliverable (the SEND body) or the
|
|
1130
|
+
// abandonment reason. Folded, attributed to the terminated run.
|
|
1131
|
+
const terms = await this.#db.engine_pull_loop_terminations.all({ session_id: sessionId, run_id: runId, since });
|
|
1132
|
+
for (const t of terms) {
|
|
1133
|
+
await this.#db.engine_insert_loop_termination_delta.run({
|
|
1134
|
+
run_id: runId, loop_id: loopId, turn_id: turnId, sequence: fromSequence + written,
|
|
1135
|
+
source: String(t.run_id), pathname: `/${t.run_name}`,
|
|
1136
|
+
rx: t.terminal_message ?? `loop "${t.prompt}" ended (${t.status})`,
|
|
1137
|
+
status: t.status,
|
|
1138
|
+
});
|
|
1139
|
+
written++;
|
|
1140
|
+
}
|
|
1099
1141
|
return written;
|
|
1100
1142
|
}
|
|
1101
1143
|
// §env-delta — the filesystem as an actor. Ambient disk divergences detected at
|
|
@@ -1223,6 +1265,9 @@ class Engine {
|
|
|
1223
1265
|
// resolve synchronously inside their handlers.
|
|
1224
1266
|
const target = this.#extractTarget(statement.target);
|
|
1225
1267
|
const flags = await this.#loadLoopFlags(loopId); // the loop/proposal notification carries flags (yolo) — §dual-yolo-proposal-carries-flags
|
|
1268
|
+
// #note10 — if the target diverged on disk this turn, the model's EDIT is based
|
|
1269
|
+
// on a stale read; flag it so a YOLO auto-accept rejects instead of clobbering.
|
|
1270
|
+
const diverged = await this.#db.engine_target_diverged_this_turn.get({ run_id: runId, turn_id: turnId, scheme: target.scheme, pathname: target.pathname });
|
|
1226
1271
|
const event = {
|
|
1227
1272
|
logEntryId, sessionId, runId, loopId, turnId,
|
|
1228
1273
|
op: statement.op,
|
|
@@ -1230,6 +1275,7 @@ class Engine {
|
|
|
1230
1275
|
body: typeof result.body === "string" ? result.body : "",
|
|
1231
1276
|
attrs: (result.attrs ?? {}),
|
|
1232
1277
|
flags,
|
|
1278
|
+
staleClobberRisk: diverged !== undefined,
|
|
1233
1279
|
};
|
|
1234
1280
|
for (const listener of this.#proposalPendingListeners) {
|
|
1235
1281
|
try {
|
|
@@ -1567,6 +1613,9 @@ class Engine {
|
|
|
1567
1613
|
}
|
|
1568
1614
|
if (ctx.injectRun === undefined)
|
|
1569
1615
|
throw new Error("run fork: injectRun capability absent");
|
|
1616
|
+
const denied = await RunCap.deny(this.#db, ctx.sessionId);
|
|
1617
|
+
if (denied !== null)
|
|
1618
|
+
return denied;
|
|
1570
1619
|
const branchRunId = await Fork.fork(this.#db, srcRunId);
|
|
1571
1620
|
const branch = await this.#db.fork_get_run.get({ id: branchRunId });
|
|
1572
1621
|
await ctx.injectRun({ sessionId: ctx.sessionId, runId: branchRunId, prompt: typeof statement.body === "string" ? statement.body : "" });
|
|
@@ -1648,6 +1697,23 @@ class Engine {
|
|
|
1648
1697
|
return { status: 501 };
|
|
1649
1698
|
return await execHandler.kill(pathnameFromPath(path), ctx);
|
|
1650
1699
|
}
|
|
1700
|
+
if (schemeName === "run") {
|
|
1701
|
+
// terminate — abort any run by address; whoever holds it may end it.
|
|
1702
|
+
// `.`/"" = self. cancelRun (→ Daemon.cancelDrain) aborts the run's signal
|
|
1703
|
+
// (its loop closes 499); an idle run is a no-op-200, a missing run 404.
|
|
1704
|
+
const name = pathnameFromPath(path).replace(/^\/+/, "");
|
|
1705
|
+
let runId = ctx.runId;
|
|
1706
|
+
if (name !== "" && name !== ".") {
|
|
1707
|
+
const row = await this.#db.run_resolve_by_name.get({ session_id: ctx.sessionId, name });
|
|
1708
|
+
if (row === undefined)
|
|
1709
|
+
return { status: 404, error: `run:///${name} not found in this session` };
|
|
1710
|
+
runId = row.id;
|
|
1711
|
+
}
|
|
1712
|
+
if (this.#cancelRun === undefined)
|
|
1713
|
+
throw new Error("run kill: cancelRun capability absent");
|
|
1714
|
+
this.#cancelRun(runId);
|
|
1715
|
+
return { status: 200 };
|
|
1716
|
+
}
|
|
1651
1717
|
const handler = this.#schemes.get(schemeName);
|
|
1652
1718
|
if (handler === undefined || typeof handler.deleteEntry !== "function")
|
|
1653
1719
|
return { status: 501 };
|
|
@@ -1750,7 +1816,10 @@ class Engine {
|
|
|
1750
1816
|
if (status === null)
|
|
1751
1817
|
return { status: 400 };
|
|
1752
1818
|
if (status === 200 || status === 499) {
|
|
1753
|
-
|
|
1819
|
+
// the loop's terminal message — its deliverable — rides the termination delta.
|
|
1820
|
+
const body = statement.body;
|
|
1821
|
+
const message = body === null ? null : typeof body === "string" ? body : body.raw;
|
|
1822
|
+
await this.#db.engine_loop_set_status.run({ status, loop_id: loopId, message });
|
|
1754
1823
|
}
|
|
1755
1824
|
return { status };
|
|
1756
1825
|
}
|
|
@@ -1872,12 +1941,14 @@ class Engine {
|
|
|
1872
1941
|
if (path === null)
|
|
1873
1942
|
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: null, params: null, fragment: null };
|
|
1874
1943
|
// `local` (bare path) and `regex` (grammar 0.46 `#pattern#flags` target) carry no URL parts — store the raw text as the pathname for the log record, scheme=null.
|
|
1875
|
-
if (path.kind === "
|
|
1876
|
-
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: path.raw, params: null, fragment: null };
|
|
1944
|
+
if (path.kind === "regex")
|
|
1945
|
+
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: path.raw, params: null, fragment: null }; // regex source — no decode
|
|
1946
|
+
if (path.kind === "local")
|
|
1947
|
+
return { scheme: null, username: null, password: null, hostname: null, port: null, pathname: decodePathParens(path.raw), params: null, fragment: null }; // #239 item 4
|
|
1877
1948
|
const scheme = path.scheme === "file" ? null : path.scheme;
|
|
1878
1949
|
return {
|
|
1879
1950
|
scheme, username: path.username, password: path.password,
|
|
1880
|
-
hostname: path.hostname, port: path.port, pathname: path.pathname,
|
|
1951
|
+
hostname: path.hostname, port: path.port, pathname: decodePathParens(path.pathname), // #239 item 4
|
|
1881
1952
|
params: JSON.stringify(path.params), fragment: path.fragment,
|
|
1882
1953
|
};
|
|
1883
1954
|
}
|