openclaw-zero-agency 0.2.3 → 0.2.4
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/dist/index.js +41 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { Type } from "typebox";
|
|
9
9
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
10
10
|
import { readFile, appendFile, mkdir } from "node:fs/promises";
|
|
11
|
+
import { readFileSync, writeFileSync, renameSync, mkdirSync } from "node:fs";
|
|
11
12
|
import { statSync } from "node:fs";
|
|
12
13
|
import { createHash, randomUUID } from "node:crypto";
|
|
13
14
|
import { dirname } from "node:path";
|
|
@@ -22,6 +23,42 @@ const prune = () => { const t = Date.now(); for (const [k, v] of store())
|
|
|
22
23
|
if (v.expires < t)
|
|
23
24
|
store().delete(k); };
|
|
24
25
|
const cache = new Map();
|
|
26
|
+
// Receipts are shared through a file, not memory: the tool and the hook run in
|
|
27
|
+
// different execution contexts inside the plugin host, so a Map is not shared.
|
|
28
|
+
let receiptPath = "/tmp/zero-agency-receipts.json";
|
|
29
|
+
function readReceipts() {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(readFileSync(receiptPath, "utf8"));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function writeReceipt(runId, rec) {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const all = readReceipts();
|
|
40
|
+
for (const k of Object.keys(all)) {
|
|
41
|
+
const v = all[k];
|
|
42
|
+
if (!v || v.expires < now)
|
|
43
|
+
delete all[k];
|
|
44
|
+
}
|
|
45
|
+
all.global = rec;
|
|
46
|
+
if (runId)
|
|
47
|
+
all[runId] = rec;
|
|
48
|
+
try {
|
|
49
|
+
mkdirSync(dirname(receiptPath), { recursive: true });
|
|
50
|
+
const tmp = `${receiptPath}.tmp`;
|
|
51
|
+
writeFileSync(tmp, JSON.stringify(all));
|
|
52
|
+
renameSync(tmp, receiptPath);
|
|
53
|
+
}
|
|
54
|
+
catch { /* a missing receipt fails safe */ }
|
|
55
|
+
}
|
|
56
|
+
function findReceipt(runId) {
|
|
57
|
+
const all = readReceipts();
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
const r = (runId ? all[runId] : undefined) ?? all.global;
|
|
60
|
+
return r && r.expires > now ? r : undefined;
|
|
61
|
+
}
|
|
25
62
|
const norm = (s) => String(s ?? "").toLowerCase().replace(/[^a-z0-9\s-]/g, " ").replace(/\s+/g, " ").trim();
|
|
26
63
|
async function loadRegistry(path) {
|
|
27
64
|
const st = statSync(path);
|
|
@@ -71,6 +108,8 @@ export default definePluginEntry({
|
|
|
71
108
|
register(api) {
|
|
72
109
|
// api.config is the GLOBAL gateway config; plugin-scoped config lives on api.pluginConfig.
|
|
73
110
|
const cfg = api.pluginConfig ?? {};
|
|
111
|
+
if (cfg.telemetryPath)
|
|
112
|
+
receiptPath = `${dirname(cfg.telemetryPath)}/receipts.json`;
|
|
74
113
|
const registryPath = cfg.registryPath ?? "";
|
|
75
114
|
const telemetryPath = cfg.telemetryPath;
|
|
76
115
|
const gateMode = cfg.gateMode ?? "advisory";
|
|
@@ -109,13 +148,7 @@ export default definePluginEntry({
|
|
|
109
148
|
if (candidates.length) {
|
|
110
149
|
const runId = ctx?.runId ?? ctx?.sessionId ?? null;
|
|
111
150
|
receipt = createHash("sha256").update(`${runId ?? "global"}:${candidates[0].id}:${Date.now()}:${randomUUID()}`).digest("hex").slice(0, 16);
|
|
112
|
-
|
|
113
|
-
// Tool context does not always carry runId; always publish a global
|
|
114
|
-
// receipt so the gate can match when the tool cannot see the run.
|
|
115
|
-
if (runId)
|
|
116
|
-
store().set(runId, rec);
|
|
117
|
-
store().set("global", rec);
|
|
118
|
-
prune();
|
|
151
|
+
writeReceipt(runId, { id: receipt, pack: candidates[0].pack, at: Date.now(), expires: Date.now() + ttl });
|
|
119
152
|
}
|
|
120
153
|
await telemetry(telemetryPath, { kind: "route", agent: ctx?.agentId ?? null, session: ctx?.sessionKey ?? null, query: params.task.slice(0, 200), selected: candidates[0]?.id ?? null, candidates: candidates.length, receipt });
|
|
121
154
|
return { content: [{ type: "text", text: JSON.stringify({ candidates, receipt, note }) }], details: { candidates, receipt, note } };
|
|
@@ -142,7 +175,7 @@ export default definePluginEntry({
|
|
|
142
175
|
return undefined;
|
|
143
176
|
const runId = event?.runId ?? ctx?.runId ?? "unknown-run";
|
|
144
177
|
prune();
|
|
145
|
-
const r = (runId
|
|
178
|
+
const r = findReceipt(runId);
|
|
146
179
|
if (r) {
|
|
147
180
|
await telemetry(telemetryPath, { kind: "gate", decision: "receipt-ok", tool: event?.toolName, runId, receipt: r.id });
|
|
148
181
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-zero-agency",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Zero Agency: capability packs, programmatic routing and a receipt gate for OpenClaw. Packs inform judgement; they never confer authority.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|