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.
Files changed (2) hide show
  1. package/dist/index.js +43 -5
  2. 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 ?? "unknown-run";
111
- receipt = createHash("sha256").update(`${runId}:${candidates[0].id}:${Date.now()}:${randomUUID()}`).digest("hex").slice(0, 16);
112
- store().set(runId, { id: receipt, pack: candidates[0].pack, at: Date.now(), expires: Date.now() + ttl });
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 = store().get(runId);
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.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",