@plurnk/plurnk-service 3.2.0 → 3.3.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.defaults +2 -2
- package/SPEC.md +122 -40
- package/dist/build-info.json +1 -1
- package/dist/core/BudgetReadout.d.ts +1 -3
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +43 -46
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +1 -0
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +7 -5
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +3 -2
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +16 -13
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/PacketBuilder.sql +14 -0
- package/dist/core/ProposalLifecycle.d.ts +1 -0
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +1 -0
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/TurnDispositionHandler.d.ts +1 -1
- package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
- package/dist/core/TurnDispositionHandler.js +5 -3
- package/dist/core/TurnDispositionHandler.js.map +1 -1
- package/dist/core/WorkerControlHandler.d.ts.map +1 -1
- package/dist/core/WorkerControlHandler.js +38 -2
- package/dist/core/WorkerControlHandler.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +1 -0
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +2 -0
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/fork.sql +2 -2
- package/dist/core/mutation-types.d.ts +1 -0
- package/dist/core/mutation-types.d.ts.map +1 -1
- package/dist/core/packet-wire.d.ts +1 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +6 -2
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +1 -1
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +1 -1
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/scheme-types.d.ts +1 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/man/plurnk-service.1 +4 -4
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +5 -1
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecutionInput.d.ts +2 -1
- package/dist/schemes/ExecutionInput.d.ts.map +1 -1
- package/dist/schemes/ExecutionInput.js +44 -6
- package/dist/schemes/ExecutionInput.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +17 -5
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +4 -1
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +24 -1
- package/dist/server/Daemon.d.ts +7 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +50 -1
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +3 -1
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +17 -1
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/Functionality.d.ts.map +1 -1
- package/dist/server/Functionality.js +86 -49
- package/dist/server/Functionality.js.map +1 -1
- package/dist/server/FunctionalityManager.d.ts +1 -4
- package/dist/server/FunctionalityManager.d.ts.map +1 -1
- package/dist/server/FunctionalityManager.js +5 -5
- package/dist/server/FunctionalityManager.js.map +1 -1
- package/dist/server/MembersFunctionality.d.ts +7 -8
- package/dist/server/MembersFunctionality.d.ts.map +1 -1
- package/dist/server/MembersFunctionality.js +86 -54
- package/dist/server/MembersFunctionality.js.map +1 -1
- package/dist/server/MessageAbandonment.d.ts +7 -0
- package/dist/server/MessageAbandonment.d.ts.map +1 -0
- package/dist/server/MessageAbandonment.js +57 -0
- package/dist/server/MessageAbandonment.js.map +1 -0
- package/dist/server/Rollback.d.ts +29 -0
- package/dist/server/Rollback.d.ts.map +1 -0
- package/dist/server/Rollback.js +111 -0
- package/dist/server/Rollback.js.map +1 -0
- package/dist/server/drain.sql +9 -0
- package/docs/members.md +14 -9
- package/migrations/026_message_obligations.sql +32 -0
- package/migrations/027_members_identity.sql +20 -0
- package/migrations/028_mutation_patch.sql +20 -0
- package/package.json +10 -11
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// {§rollback} — undo a worker's settled file mutations after one of its log coordinates. Each row's
|
|
2
|
+
// patch ({§log-mutation-patch}) is reversed against the file as it is now and written back as an
|
|
3
|
+
// ordinary operation of the acting client, so every step is a proposal, a row and a patch of its
|
|
4
|
+
// own; the first file that no longer holds what its mutation wrote stops the rollback where it is.
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { applyPatch, parsePatch, reversePatch } from "diff";
|
|
8
|
+
import { PlurnkParser } from "@plurnk/plurnk-parser";
|
|
9
|
+
import { entryAddress } from "../core/plurnk-uri.js";
|
|
10
|
+
import { daemonFailure } from "./daemon-results.js";
|
|
11
|
+
export default class Rollback {
|
|
12
|
+
// `<loop>/<turn>/<item>`, with or without the `log:///` prefix and a trailing `/<OP>`.
|
|
13
|
+
static coordinate(to) {
|
|
14
|
+
const match = /^(?:log:\/\/\/)?(\d+)\/(\d+)\/(\d+)(?:\/[A-Za-z]+)?$/u.exec(to.trim());
|
|
15
|
+
if (match === null)
|
|
16
|
+
return null;
|
|
17
|
+
const [loopSeq, turnSeq, sequence] = match.slice(1).map(Number);
|
|
18
|
+
return loopSeq > 0 && turnSeq > 0 && sequence > 0 ? { loopSeq: loopSeq, turnSeq: turnSeq, sequence: sequence } : null;
|
|
19
|
+
}
|
|
20
|
+
static async run(seam, args) {
|
|
21
|
+
const coordinate = Rollback.coordinate(args.to);
|
|
22
|
+
if (coordinate === null) {
|
|
23
|
+
throw daemonFailure("daemon:rollback", "coordinate-invalid", 400, `'${args.to}' is not a log coordinate.`, {
|
|
24
|
+
to: args.to, recovery: "Name the coordinate as <loop>/<turn>/<item>, as the log shows it.",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
const at = `${coordinate.loopSeq}/${coordinate.turnSeq}/${coordinate.sequence}`;
|
|
28
|
+
const anchor = await seam.db.log_id_by_coordinate.get({
|
|
29
|
+
worker_id: args.workerId, loop_seq: coordinate.loopSeq, turn_seq: coordinate.turnSeq, sequence: coordinate.sequence,
|
|
30
|
+
});
|
|
31
|
+
if (anchor === undefined) {
|
|
32
|
+
throw daemonFailure("daemon:rollback", "coordinate-not-found", 404, `No log entry exists at log:///${at}.`, { to: at });
|
|
33
|
+
}
|
|
34
|
+
const root = await seam.projectRoot(args.workspaceId);
|
|
35
|
+
if (root === null) {
|
|
36
|
+
throw daemonFailure("daemon:rollback", "project-root-required", 400, "Rollback restores files, and this workspace has no project root.", {});
|
|
37
|
+
}
|
|
38
|
+
const rows = await seam.db.log_file_mutations_after.all({
|
|
39
|
+
worker_id: args.workerId, loop_seq: coordinate.loopSeq, turn_seq: coordinate.turnSeq, sequence: coordinate.sequence,
|
|
40
|
+
});
|
|
41
|
+
const restored = [];
|
|
42
|
+
const skipped = [];
|
|
43
|
+
const executors = seam.executorTags(args.workspaceId);
|
|
44
|
+
for (const row of rows) {
|
|
45
|
+
const rowCoordinate = `${row.loop_seq}/${row.turn_seq}/${row.sequence}`;
|
|
46
|
+
if (row.patch === null) {
|
|
47
|
+
// A file mutation that kept no patch: nothing is invented for it ({§log-mutation-patch}).
|
|
48
|
+
if (row.scheme === null || row.scheme === "file")
|
|
49
|
+
skipped.push({ coordinate: rowCoordinate, reason: "no-patch" });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// A row that wrote several files is reversed last file first.
|
|
53
|
+
for (const file of parsePatch(row.patch).toReversed()) {
|
|
54
|
+
const outcome = await Rollback.#reverse(seam, { root, executors, file, coordinate: rowCoordinate }, restored);
|
|
55
|
+
if (outcome === "already-reversed") {
|
|
56
|
+
skipped.push({ coordinate: rowCoordinate, reason: "already-reversed" });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (outcome !== null)
|
|
60
|
+
return { to: at, restored, skipped, stopped: outcome };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return { to: at, restored, skipped, stopped: null };
|
|
64
|
+
}
|
|
65
|
+
static async #reverse(seam, { root, executors, file, coordinate }, restored) {
|
|
66
|
+
const path = file.oldFileName;
|
|
67
|
+
if (typeof path !== "string" || path.length === 0)
|
|
68
|
+
throw new Error(`log:///${coordinate} keeps a patch that names no file.`);
|
|
69
|
+
let current;
|
|
70
|
+
try {
|
|
71
|
+
current = await readFile(join(root, path), "utf8");
|
|
72
|
+
}
|
|
73
|
+
catch (cause) {
|
|
74
|
+
if (cause.code !== "ENOENT")
|
|
75
|
+
throw cause;
|
|
76
|
+
current = null;
|
|
77
|
+
}
|
|
78
|
+
const created = file.hunks.every((hunk) => hunk.oldLines === 0);
|
|
79
|
+
const removed = !created && file.hunks.every((hunk) => hunk.newLines === 0);
|
|
80
|
+
// A row whose forward patch still applies was reversed already, by an earlier rollback whose
|
|
81
|
+
// own rows live in the client's log: nothing to do for it, and nothing invented.
|
|
82
|
+
const alreadyReversed = () => applyPatch(current ?? "", file) !== false;
|
|
83
|
+
if (removed ? current !== null : current === null) {
|
|
84
|
+
if (alreadyReversed())
|
|
85
|
+
return "already-reversed";
|
|
86
|
+
return { coordinate, path, reason: "drift", detail: `'${path}' ${current === null ? "no longer exists" : "exists again"}; log:///${coordinate} ${removed ? "removed" : "wrote"} it.` };
|
|
87
|
+
}
|
|
88
|
+
const previous = applyPatch(current ?? "", reversePatch(file));
|
|
89
|
+
if (previous === false) {
|
|
90
|
+
if (alreadyReversed())
|
|
91
|
+
return "already-reversed";
|
|
92
|
+
return { coordinate, path, reason: "drift", detail: `'${path}' no longer holds what log:///${coordinate} wrote.` };
|
|
93
|
+
}
|
|
94
|
+
const action = created ? "delete" : removed ? "create" : "update";
|
|
95
|
+
const target = entryAddress("file", "", path, root);
|
|
96
|
+
const source = action === "delete"
|
|
97
|
+
? `\`\`\`\`KILL (${target})\`\`\`\``
|
|
98
|
+
: `\`\`\`\`EDIT (${target})${action === "update" ? " <1,-1>" : ""}\n${previous}\n\`\`\`\``;
|
|
99
|
+
const { items } = PlurnkParser.parseClient(source, { executors: [...executors] });
|
|
100
|
+
const item = items[0];
|
|
101
|
+
if (items.length !== 1 || item?.kind !== "statement")
|
|
102
|
+
throw new Error(`rollback of log:///${coordinate} built an operation the parser refused: ${source.slice(0, 120)}`);
|
|
103
|
+
const result = await seam.apply(item.statement);
|
|
104
|
+
if (result.status >= 300) {
|
|
105
|
+
return { coordinate, path, reason: "refused", detail: result.problem?.detail ?? `The reversing operation answered ${result.status}.` };
|
|
106
|
+
}
|
|
107
|
+
restored.push({ coordinate, path, action });
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=Rollback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Rollback.js","sourceRoot":"","sources":["../../src/server/Rollback.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,iGAAiG;AACjG,iGAAiG;AACjG,mGAAmG;AACnG,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAwB,MAAM,MAAM,CAAC;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAmBpD,MAAM,CAAC,OAAO,OAAO,QAAQ;IACzB,uFAAuF;IACvF,MAAM,CAAC,UAAU,CAAC,EAAU;QACxB,MAAM,KAAK,GAAG,uDAAuD,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACtF,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAChC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChE,OAAO,OAAQ,GAAG,CAAC,IAAI,OAAQ,GAAG,CAAC,IAAI,QAAS,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAQ,EAAE,OAAO,EAAE,OAAQ,EAAE,QAAQ,EAAE,QAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAChI,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAkB,EAAE,IAA2D;QAC5F,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,aAAa,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,EAAE,4BAA4B,EAAE;gBACvG,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,mEAAmE;aAC7F,CAAC,CAAC;QACP,CAAC;QACD,MAAM,EAAE,GAAG,GAAG,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;QAChF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAiB;YAClE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ;SACtH,CAAC,CAAC;QACH,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,EAAE,GAAG,EAAE,iCAAiC,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5H,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAChB,MAAM,aAAa,CAAC,iBAAiB,EAAE,uBAAuB,EAAE,GAAG,EAAE,kEAAkE,EAAE,EAAE,CAAC,CAAC;QACjJ,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,wBAAwB,CAAC,GAAG,CAAc;YACjE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ;SACtH,CAAC,CAAC;QACH,MAAM,QAAQ,GAAe,EAAE,CAAC;QAChC,MAAM,OAAO,GAAwC,EAAE,CAAC;QACxD,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,aAAa,GAAG,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACxE,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;gBACrB,0FAA0F;gBAC1F,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;oBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;gBAClH,SAAS;YACb,CAAC;YACD,8DAA8D;YAC9D,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,QAAQ,CAAC,CAAC;gBAC9G,IAAI,OAAO,KAAK,kBAAkB,EAAE,CAAC;oBAAC,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBAC1H,IAAI,OAAO,KAAK,IAAI;oBAAE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YACjF,CAAC;QACL,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,QAAQ,CACjB,IAAkB,EAClB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAA6F,EAChI,QAAoB;QAEpB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,UAAU,oCAAoC,CAAC,CAAC;QAC7H,IAAI,OAAsB,CAAC;QAC3B,IAAI,CAAC;YACD,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACpE,OAAO,GAAG,IAAI,CAAC;QACnB,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC;QAC5E,6FAA6F;QAC7F,iFAAiF;QACjF,MAAM,eAAe,GAAG,GAAY,EAAE,CAAC,UAAU,CAAC,OAAO,IAAI,EAAE,EAAE,IAAI,CAAC,KAAK,KAAK,CAAC;QACjF,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAChD,IAAI,eAAe,EAAE;gBAAE,OAAO,kBAAkB,CAAC;YACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,cAAc,YAAY,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC;QAC3L,CAAC;QACD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,IAAI,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACrB,IAAI,eAAe,EAAE;gBAAE,OAAO,kBAAkB,CAAC;YACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,IAAI,iCAAiC,UAAU,SAAS,EAAE,CAAC;QACvH,CAAC;QACD,MAAM,MAAM,GAAuB,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,KAAK,QAAQ;YAC9B,CAAC,CAAC,iBAAiB,MAAM,WAAW;YACpC,CAAC,CAAC,iBAAiB,MAAM,IAAI,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,YAAY,CAAC;QAC/F,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,2CAA2C,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACzK,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAA4B,CAAC,CAAC;QACnE,IAAI,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACvB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,oCAAoC,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC;QAC3I,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
|
package/dist/server/drain.sql
CHANGED
|
@@ -52,6 +52,15 @@ WHERE worker_id = $worker_id
|
|
|
52
52
|
ORDER BY (status = 102) DESC, sequence ASC
|
|
53
53
|
LIMIT 1;
|
|
54
54
|
|
|
55
|
+
-- PREP: drain_abandoned_messages
|
|
56
|
+
-- {§message-abandonment}: the messages other workers sent that this ended loop still holds without a completion.
|
|
57
|
+
SELECT m.path
|
|
58
|
+
FROM message_sources m
|
|
59
|
+
WHERE m.loop_id = $loop_id
|
|
60
|
+
AND m.source LIKE 'worker://%'
|
|
61
|
+
AND NOT EXISTS (SELECT 1 FROM message_completions c WHERE c.message_id = m.id)
|
|
62
|
+
ORDER BY m.ordinal ASC;
|
|
63
|
+
|
|
55
64
|
-- PREP: drain_message_source
|
|
56
65
|
SELECT l.worker_id, w.workspace_id, l.status
|
|
57
66
|
FROM loops l JOIN workers w ON w.id = l.worker_id
|
package/docs/members.md
CHANGED
|
@@ -50,8 +50,8 @@ candidate: its `provenance.kind` is the verdict and its `summary` the reason.
|
|
|
50
50
|
- **Untracked, scope `root` or `namespace`**: a definition makes it a member
|
|
51
51
|
from the next turn.
|
|
52
52
|
|
|
53
|
-
```members (add) <!-- include the generated reports -->
|
|
54
|
-
{"
|
|
53
|
+
```members (add) <!-- include the generated reports; each glob is one definition -->
|
|
54
|
+
{"files": ["build/*.json"]}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
- **Untracked, scope `none`** (an operator narrowed it): the refusal names the
|
|
@@ -95,10 +95,15 @@ cleanup conflicts; its receipt identifies that completed effect.
|
|
|
95
95
|
|
|
96
96
|
## Definitions belong to the workspace
|
|
97
97
|
|
|
98
|
-
`list` shows every definition with its origin (`service` from the
|
|
99
|
-
environment, `workspace` from an `add`) and whether it is enabled. `enable` and
|
|
100
|
-
`disable` flip one
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
A definition is its glob: `list` shows every definition with its origin (`service` from the
|
|
99
|
+
operator's environment, `workspace` from an `add`) and whether it is enabled. `enable` and
|
|
100
|
+
`disable` flip one definition, and `remove` forgets a workspace definition and restores
|
|
101
|
+
inheritance, each named by the glob as `list` shows it:
|
|
102
|
+
|
|
103
|
+
```members (disable)
|
|
104
|
+
{"file": "build/*.json"}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Inherited definitions cannot be removed here. Changes apply to every worker. Each enabled
|
|
108
|
+
definition is projected as `worker:///_plurnk/members/<glob>.md`, the glob percent-encoded,
|
|
109
|
+
showing what it resolved to.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
-- MIGRATE: 26 message obligations
|
|
2
|
+
-- Released in 3.3.0 and frozen ({§db-migrations}): a shape change is the next MIGRATE version, never an edit here.
|
|
3
|
+
-- {§worker-obligations}: a message a worker sent to another worker holds the sender, beside its live children
|
|
4
|
+
-- and open streams, until the recipient completes it ({§message-completion}) or the recipient's loop ends
|
|
5
|
+
-- ({§message-abandonment}). The two liveness views are recreated with that third column; no row changes.
|
|
6
|
+
DROP VIEW IF EXISTS loop_obligations;
|
|
7
|
+
DROP VIEW IF EXISTS worker_obligations;
|
|
8
|
+
|
|
9
|
+
CREATE VIEW worker_obligations AS
|
|
10
|
+
SELECT w.id AS worker_id,
|
|
11
|
+
EXISTS (
|
|
12
|
+
SELECT 1 FROM subscriptions s
|
|
13
|
+
WHERE s.worker_id = w.id AND s.closed_at IS NULL AND s.detached = 0
|
|
14
|
+
) AS streams,
|
|
15
|
+
EXISTS (
|
|
16
|
+
SELECT 1 FROM workers c JOIN loops l ON l.worker_id = c.id
|
|
17
|
+
WHERE c.parent_worker_id = w.id AND l.status IN (100, 102, 202)
|
|
18
|
+
) AS workers,
|
|
19
|
+
EXISTS (
|
|
20
|
+
SELECT 1 FROM loop_messages m
|
|
21
|
+
JOIN loops l ON l.id = m.loop_id
|
|
22
|
+
JOIN workers r ON r.id = l.worker_id
|
|
23
|
+
WHERE r.workspace_id = w.workspace_id
|
|
24
|
+
AND m.source = 'worker://' || w.name
|
|
25
|
+
AND l.status IN (100, 102, 202)
|
|
26
|
+
AND NOT EXISTS (SELECT 1 FROM message_completions c WHERE c.message_id = m.id)
|
|
27
|
+
) AS messages
|
|
28
|
+
FROM workers w;
|
|
29
|
+
|
|
30
|
+
CREATE VIEW loop_obligations AS
|
|
31
|
+
SELECT l.id AS loop_id, held.streams, held.workers, held.messages
|
|
32
|
+
FROM loops l JOIN worker_obligations held ON held.worker_id = l.worker_id;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- MIGRATE: 27 members identity
|
|
2
|
+
-- Released in 3.3.0 and frozen ({§db-migrations}): a shape change is the next MIGRATE version, never an edit here.
|
|
3
|
+
-- {§members-functionality} {§functionality-addressing}: a members definition is its glob. Workspace
|
|
4
|
+
-- definitions are re-keyed by their glob. A service-origin row held only an enabledness override
|
|
5
|
+
-- keyed by the operator's environment alias, which no longer names a definition, so it is dropped;
|
|
6
|
+
-- a state left with no definition is removed, as the coordinator persists none.
|
|
7
|
+
UPDATE workspace_module_state
|
|
8
|
+
SET state = (
|
|
9
|
+
SELECT json_object(
|
|
10
|
+
'version', 1,
|
|
11
|
+
'definitions', COALESCE(json_group_object(json_extract(value, '$.definition.glob'), json(value)), json('{}'))
|
|
12
|
+
)
|
|
13
|
+
FROM json_each(workspace_module_state.state, '$.definitions')
|
|
14
|
+
WHERE json_extract(value, '$.origin') = 'workspace'
|
|
15
|
+
)
|
|
16
|
+
WHERE namespace_owner = '@plurnk/plurnk-core/members';
|
|
17
|
+
|
|
18
|
+
DELETE FROM workspace_module_state
|
|
19
|
+
WHERE namespace_owner = '@plurnk/plurnk-core/members'
|
|
20
|
+
AND (SELECT COUNT(*) FROM json_each(workspace_module_state.state, '$.definitions')) = 0;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- MIGRATE: 28 mutation patch
|
|
2
|
+
-- Released in 3.3.0 and frozen ({§db-migrations}): a shape change is the next MIGRATE version, never an edit here.
|
|
3
|
+
-- {§log-mutation-patch}: a settled file mutation's row keeps the unified diff of what it did, for the
|
|
4
|
+
-- life of the row; it is served as the row's `patch` channel and never enters a receipt or a packet.
|
|
5
|
+
-- Rows settled before this version, byte writes and operations that changed no text hold NULL.
|
|
6
|
+
ALTER TABLE log_entries ADD COLUMN patch TEXT;
|
|
7
|
+
|
|
8
|
+
DROP VIEW active_log_entries;
|
|
9
|
+
CREATE VIEW active_log_entries AS
|
|
10
|
+
SELECT le.id, le.version, le.worker_id, le.loop_id, le.turn_id, le.sequence,
|
|
11
|
+
le.at, le.origin, le.source, le.ambient_event_id, le.inherited_history,
|
|
12
|
+
le.deep_hash, le.model_call_id, le.subscription_publication_id,
|
|
13
|
+
le.op, le.signal, le.scheme, le.username, le.password,
|
|
14
|
+
le.hostname, le.port, le.pathname, le.query, le.fragment, le.lineMarker,
|
|
15
|
+
le.tx, le.mimetype_tx,
|
|
16
|
+
le.rx, le.mimetype_rx, le.status_rx, le.weight,
|
|
17
|
+
le.state, le.outcome, le.attrs, le.initial_folded, le.patch, projection.folded
|
|
18
|
+
FROM log_entries le
|
|
19
|
+
JOIN log_entry_projections projection ON projection.log_entry_id = le.id
|
|
20
|
+
WHERE projection.active = 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plurnk/plurnk-service",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Plurnk agent runtime — the server / engine / core. Implements the @plurnk/plurnk-contracts contract.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"plurnk",
|
|
@@ -50,14 +50,13 @@
|
|
|
50
50
|
"test:lint": "npm run types:sqlrite:check && tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
|
|
51
51
|
"test:unit": "node --conditions=plurnk-dev --import=./test/setup.ts --env-file-if-exists=.env.defaults --test --test-timeout=30000 scripts/*.test.mjs src/**/*.test.ts",
|
|
52
52
|
"test:intg": "export PLURNK_TEST_RUN=$(date -u +%Y%m%dT%H%M%SZ); PLURNK_SERVICE_FILES_ITEMS=0 node --conditions=plurnk-dev --import=./test/setup.ts --env-file-if-exists=.env.defaults --test --test-concurrency=8 --test-timeout=30000 test/intg/*.test.ts && node ../scripts/reclaim-green-run.mjs core",
|
|
53
|
-
"test:mcp:dogfood": "PLURNK_TEST_MCP_DOGFOOD=1 node --conditions=plurnk-dev --import=./test/setup.ts --env-file-if-exists=.env.defaults --test --test-concurrency=1 --test-timeout=120000 test/intg/mcp-agui-composition.test.ts",
|
|
54
|
-
"test:a2a:tck": "node scripts/a2a-tck.mjs",
|
|
55
53
|
"test:live": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/live.mjs",
|
|
56
54
|
"test:live:specimen": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/live.mjs --specimen",
|
|
57
55
|
"test:live:zeropin": "PLURNK_ZERO_PIN=1 npm run test:live",
|
|
58
56
|
"test:demo": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/demo.mjs",
|
|
59
57
|
"test:demo:specimen": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/demo.mjs --specimen",
|
|
60
58
|
"test:demo:zeropin": "PLURNK_ZERO_PIN=1 npm run test:demo",
|
|
59
|
+
"tier:history": "node --conditions=plurnk-dev scripts/tier-history.mjs",
|
|
61
60
|
"test:benchlet": "bash ../scripts/operator-environment.sh node scripts/benchlet.mjs",
|
|
62
61
|
"fixture:demo": "node --conditions=plurnk-dev test/demo/_fixture.ts",
|
|
63
62
|
"test": "npm run test:lint && npm run test:unit && npm run test:intg",
|
|
@@ -90,17 +89,17 @@
|
|
|
90
89
|
"@opentelemetry/sdk-metrics": "^2.12.0",
|
|
91
90
|
"@opentelemetry/sdk-trace-base": "^2.12.0",
|
|
92
91
|
"@plurnk/plurnk-a2a": "3.1.1",
|
|
93
|
-
"@plurnk/plurnk-agui": "3.
|
|
94
|
-
"@plurnk/plurnk-contracts": "3.
|
|
92
|
+
"@plurnk/plurnk-agui": "3.1.0",
|
|
93
|
+
"@plurnk/plurnk-contracts": "3.3.0",
|
|
95
94
|
"@plurnk/plurnk-parser": "3.0.3",
|
|
96
|
-
"@plurnk/plurnk-schedule": "3.0.
|
|
95
|
+
"@plurnk/plurnk-schedule": "3.0.2",
|
|
97
96
|
"@plurnk/plurnk-skills": "3.0.1",
|
|
98
|
-
"@plurnk/plurnk-execs": "3.
|
|
97
|
+
"@plurnk/plurnk-execs": "3.2.0",
|
|
99
98
|
"@plurnk/plurnk-execs-common": "2.0.2",
|
|
100
99
|
"@plurnk/plurnk-execs-jq": "2.0.2",
|
|
101
100
|
"@plurnk/plurnk-execs-sqlite": "2.0.2",
|
|
102
101
|
"@plurnk/plurnk-hooks": "2.0.1",
|
|
103
|
-
"@plurnk/plurnk-mcp": "3.0
|
|
102
|
+
"@plurnk/plurnk-mcp": "3.1.0",
|
|
104
103
|
"@plurnk/plurnk-meta": "2.1.1",
|
|
105
104
|
"@plurnk/plurnk-agent-plugins": "2.0.0",
|
|
106
105
|
"@plurnk/plurnk-agent-skills": "2.0.1",
|
|
@@ -146,9 +145,9 @@
|
|
|
146
145
|
"@plurnk/plurnk-mimetypes-text-markdown": "2.0.1",
|
|
147
146
|
"@plurnk/plurnk-mimetypes-text-plain": "2.0.1",
|
|
148
147
|
"@plurnk/plurnk-models": "3.0.1",
|
|
149
|
-
"@plurnk/plurnk-modules": "3.
|
|
148
|
+
"@plurnk/plurnk-modules": "3.2.0",
|
|
150
149
|
"@plurnk/plurnk-providers": "3.0.2",
|
|
151
|
-
"@plurnk/plurnk-schemes": "3.
|
|
150
|
+
"@plurnk/plurnk-schemes": "3.2.0",
|
|
152
151
|
"@plurnk/plurnk-schemes-http": "2.0.1",
|
|
153
152
|
"@possumtech/sqlrite": "^6.6.1",
|
|
154
153
|
"diff": "^9.0.0",
|
|
@@ -172,5 +171,5 @@
|
|
|
172
171
|
"url": "git+https://github.com/plurnk/plurnk-service.git",
|
|
173
172
|
"directory": "plurnk-core"
|
|
174
173
|
},
|
|
175
|
-
"gitHead": "
|
|
174
|
+
"gitHead": "1dcf640b6bad0f5972443dd59354a5fc094acf46"
|
|
176
175
|
}
|