openclaw-zero-agency 0.2.2 → 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 +43 -5
- 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";
|
|
@@ -107,10 +146,9 @@ export default definePluginEntry({
|
|
|
107
146
|
}
|
|
108
147
|
let receipt = null;
|
|
109
148
|
if (candidates.length) {
|
|
110
|
-
const runId = ctx?.runId ?? ctx?.sessionId ??
|
|
111
|
-
receipt = createHash("sha256").update(`${runId}:${candidates[0].id}:${Date.now()}:${randomUUID()}`).digest("hex").slice(0, 16);
|
|
112
|
-
|
|
113
|
-
prune();
|
|
149
|
+
const runId = ctx?.runId ?? ctx?.sessionId ?? null;
|
|
150
|
+
receipt = createHash("sha256").update(`${runId ?? "global"}:${candidates[0].id}:${Date.now()}:${randomUUID()}`).digest("hex").slice(0, 16);
|
|
151
|
+
writeReceipt(runId, { id: receipt, pack: candidates[0].pack, at: Date.now(), expires: Date.now() + ttl });
|
|
114
152
|
}
|
|
115
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 });
|
|
116
154
|
return { content: [{ type: "text", text: JSON.stringify({ candidates, receipt, note }) }], details: { candidates, receipt, note } };
|
|
@@ -137,7 +175,7 @@ export default definePluginEntry({
|
|
|
137
175
|
return undefined;
|
|
138
176
|
const runId = event?.runId ?? ctx?.runId ?? "unknown-run";
|
|
139
177
|
prune();
|
|
140
|
-
const r =
|
|
178
|
+
const r = findReceipt(runId);
|
|
141
179
|
if (r) {
|
|
142
180
|
await telemetry(telemetryPath, { kind: "gate", decision: "receipt-ok", tool: event?.toolName, runId, receipt: r.id });
|
|
143
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",
|