openclaw-zero-agency 0.2.3 → 0.2.5
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 +46 -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 { rename, writeFile } from "node:fs/promises";
|
|
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,45 @@ 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
|
+
async function readReceipts() {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(await readFile(receiptPath, "utf8"));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function writeReceipt(runId, rec) {
|
|
38
|
+
const now = Date.now();
|
|
39
|
+
const all = await 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
|
+
await mkdir(dirname(receiptPath), { recursive: true });
|
|
50
|
+
const tmp = `${receiptPath}.tmp`;
|
|
51
|
+
await writeFile(tmp, JSON.stringify(all));
|
|
52
|
+
await rename(tmp, receiptPath);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function findReceipt(runId) {
|
|
60
|
+
const all = await readReceipts();
|
|
61
|
+
const now = Date.now();
|
|
62
|
+
const r = (runId ? all[runId] : undefined) ?? all.global;
|
|
63
|
+
return r && r.expires > now ? r : undefined;
|
|
64
|
+
}
|
|
25
65
|
const norm = (s) => String(s ?? "").toLowerCase().replace(/[^a-z0-9\s-]/g, " ").replace(/\s+/g, " ").trim();
|
|
26
66
|
async function loadRegistry(path) {
|
|
27
67
|
const st = statSync(path);
|
|
@@ -71,6 +111,8 @@ export default definePluginEntry({
|
|
|
71
111
|
register(api) {
|
|
72
112
|
// api.config is the GLOBAL gateway config; plugin-scoped config lives on api.pluginConfig.
|
|
73
113
|
const cfg = api.pluginConfig ?? {};
|
|
114
|
+
if (cfg.telemetryPath)
|
|
115
|
+
receiptPath = `${dirname(cfg.telemetryPath)}/receipts.json`;
|
|
74
116
|
const registryPath = cfg.registryPath ?? "";
|
|
75
117
|
const telemetryPath = cfg.telemetryPath;
|
|
76
118
|
const gateMode = cfg.gateMode ?? "advisory";
|
|
@@ -109,13 +151,9 @@ export default definePluginEntry({
|
|
|
109
151
|
if (candidates.length) {
|
|
110
152
|
const runId = ctx?.runId ?? ctx?.sessionId ?? null;
|
|
111
153
|
receipt = createHash("sha256").update(`${runId ?? "global"}:${candidates[0].id}:${Date.now()}:${randomUUID()}`).digest("hex").slice(0, 16);
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (runId)
|
|
116
|
-
store().set(runId, rec);
|
|
117
|
-
store().set("global", rec);
|
|
118
|
-
prune();
|
|
154
|
+
const wrote = await writeReceipt(runId, { id: receipt, pack: candidates[0].pack, at: Date.now(), expires: Date.now() + ttl });
|
|
155
|
+
if (!wrote)
|
|
156
|
+
await telemetry(telemetryPath, { kind: "receipt-write-failed", path: receiptPath, receipt });
|
|
119
157
|
}
|
|
120
158
|
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
159
|
return { content: [{ type: "text", text: JSON.stringify({ candidates, receipt, note }) }], details: { candidates, receipt, note } };
|
|
@@ -142,7 +180,7 @@ export default definePluginEntry({
|
|
|
142
180
|
return undefined;
|
|
143
181
|
const runId = event?.runId ?? ctx?.runId ?? "unknown-run";
|
|
144
182
|
prune();
|
|
145
|
-
const r =
|
|
183
|
+
const r = await findReceipt(runId);
|
|
146
184
|
if (r) {
|
|
147
185
|
await telemetry(telemetryPath, { kind: "gate", decision: "receipt-ok", tool: event?.toolName, runId, receipt: r.id });
|
|
148
186
|
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.5",
|
|
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",
|