@atbash/sdk 0.19.2-dev.0 → 0.20.0-dev.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/dist/browser.d.mts +105 -8
- package/dist/browser.mjs +283 -122
- package/dist/index.d.mts +105 -8
- package/dist/index.d.ts +105 -8
- package/dist/index.js +237 -61
- package/dist/index.mjs +246 -75
- package/index.js +58 -54
- package/package.json +8 -6
package/dist/index.mjs
CHANGED
|
@@ -166,12 +166,12 @@ var AtbashAPIError = class extends Error {
|
|
|
166
166
|
* ran does not carry. The path is the fact that settles it.
|
|
167
167
|
*/
|
|
168
168
|
path;
|
|
169
|
-
constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT,
|
|
170
|
-
super(enrich(status, body, statusText, endpoint,
|
|
169
|
+
constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path4) {
|
|
170
|
+
super(enrich(status, body, statusText, endpoint, path4));
|
|
171
171
|
this.name = "AtbashAPIError";
|
|
172
172
|
this.status = status;
|
|
173
173
|
this.body = body;
|
|
174
|
-
this.path =
|
|
174
|
+
this.path = path4;
|
|
175
175
|
}
|
|
176
176
|
};
|
|
177
177
|
var SignatureVerificationError = class extends Error {
|
|
@@ -180,9 +180,18 @@ var SignatureVerificationError = class extends Error {
|
|
|
180
180
|
this.name = "SignatureVerificationError";
|
|
181
181
|
}
|
|
182
182
|
};
|
|
183
|
-
|
|
183
|
+
var JudgeRefusalError = class extends Error {
|
|
184
|
+
constructor(message) {
|
|
185
|
+
super(message);
|
|
186
|
+
this.name = "JudgeRefusalError";
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
function isRefusalStatus(status) {
|
|
190
|
+
return status >= 400 && status < 500 && status !== 408;
|
|
191
|
+
}
|
|
192
|
+
function enrich(status, body, statusText, endpoint, path4) {
|
|
184
193
|
const dashboard = endpoint.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
|
|
185
|
-
let msg = `API error ${status}${
|
|
194
|
+
let msg = `API error ${status}${path4 ? ` from ${path4}` : ""}: ${body || statusText}`;
|
|
186
195
|
const lowered = body.toLowerCase();
|
|
187
196
|
if (lowered.includes("agent not registered")) {
|
|
188
197
|
msg += `
|
|
@@ -203,6 +212,61 @@ function enrich(status, body, statusText, endpoint, path5) {
|
|
|
203
212
|
return msg;
|
|
204
213
|
}
|
|
205
214
|
|
|
215
|
+
// src-ts/jail-hold.ts
|
|
216
|
+
var DEFAULTS = { ceilingMs: 3e4, pollMs: 1e3 };
|
|
217
|
+
var holds = /* @__PURE__ */ new Map();
|
|
218
|
+
var blockedAt = /* @__PURE__ */ new Map();
|
|
219
|
+
var BLOCK_MEMORY_MS = 10 * 6e4;
|
|
220
|
+
var config = { ...DEFAULTS };
|
|
221
|
+
function isJailHeld(pubkey) {
|
|
222
|
+
return holds.has(pubkey);
|
|
223
|
+
}
|
|
224
|
+
function recordBlock(pubkey) {
|
|
225
|
+
const now = Date.now();
|
|
226
|
+
for (const [key, at] of blockedAt) if (now - at > BLOCK_MEMORY_MS) blockedAt.delete(key);
|
|
227
|
+
blockedAt.set(pubkey, now);
|
|
228
|
+
}
|
|
229
|
+
function blockedSince(pubkey, startedAt) {
|
|
230
|
+
const at = blockedAt.get(pubkey);
|
|
231
|
+
return at !== void 0 && startedAt <= at;
|
|
232
|
+
}
|
|
233
|
+
function holdJail(pubkey, confirmJailed, onGiveUp) {
|
|
234
|
+
if (holds.has(pubkey)) return;
|
|
235
|
+
const hold = { timer: null };
|
|
236
|
+
holds.set(pubkey, hold);
|
|
237
|
+
const deadline = Date.now() + config.ceilingMs;
|
|
238
|
+
const tick = async () => {
|
|
239
|
+
if (holds.get(pubkey) !== hold) return;
|
|
240
|
+
let jailed = false;
|
|
241
|
+
try {
|
|
242
|
+
jailed = await confirmJailed();
|
|
243
|
+
} catch {
|
|
244
|
+
}
|
|
245
|
+
if (holds.get(pubkey) !== hold) return;
|
|
246
|
+
if (jailed) {
|
|
247
|
+
holds.delete(pubkey);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (Date.now() >= deadline) {
|
|
251
|
+
holds.delete(pubkey);
|
|
252
|
+
onGiveUp?.();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const timer = setTimeout(() => void tick(), config.pollMs);
|
|
256
|
+
timer.unref?.();
|
|
257
|
+
hold.timer = timer;
|
|
258
|
+
};
|
|
259
|
+
void tick();
|
|
260
|
+
}
|
|
261
|
+
function releaseJailHolds() {
|
|
262
|
+
for (const hold of holds.values()) if (hold.timer) clearTimeout(hold.timer);
|
|
263
|
+
holds.clear();
|
|
264
|
+
blockedAt.clear();
|
|
265
|
+
}
|
|
266
|
+
function configureJailHold(next) {
|
|
267
|
+
config = { ...DEFAULTS, ...next };
|
|
268
|
+
}
|
|
269
|
+
|
|
206
270
|
// src-ts/http/client.ts
|
|
207
271
|
var HttpClient = class {
|
|
208
272
|
baseUrl;
|
|
@@ -211,8 +275,8 @@ var HttpClient = class {
|
|
|
211
275
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
212
276
|
this.timeoutMs = timeoutMs;
|
|
213
277
|
}
|
|
214
|
-
buildUrl(
|
|
215
|
-
const url = new URL(this.baseUrl +
|
|
278
|
+
buildUrl(path4, query) {
|
|
279
|
+
const url = new URL(this.baseUrl + path4);
|
|
216
280
|
if (query) {
|
|
217
281
|
for (const [k, v] of Object.entries(query)) {
|
|
218
282
|
if (v !== void 0 && v !== null && v !== "") {
|
|
@@ -222,14 +286,14 @@ var HttpClient = class {
|
|
|
222
286
|
}
|
|
223
287
|
return url.toString();
|
|
224
288
|
}
|
|
225
|
-
async get(
|
|
226
|
-
return this.fetch(this.buildUrl(
|
|
289
|
+
async get(path4, query, headers) {
|
|
290
|
+
return this.fetch(this.buildUrl(path4, query), {
|
|
227
291
|
method: "GET",
|
|
228
292
|
...headers && { headers }
|
|
229
293
|
});
|
|
230
294
|
}
|
|
231
|
-
async post(
|
|
232
|
-
return this.fetch(this.buildUrl(
|
|
295
|
+
async post(path4, body, headers) {
|
|
296
|
+
return this.fetch(this.buildUrl(path4), {
|
|
233
297
|
method: "POST",
|
|
234
298
|
headers: { "Content-Type": "application/json", ...headers },
|
|
235
299
|
body: JSON.stringify(body)
|
|
@@ -553,7 +617,7 @@ import {
|
|
|
553
617
|
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
|
|
554
618
|
import { BatchLogRecordProcessor, LoggerProvider } from "@opentelemetry/sdk-logs";
|
|
555
619
|
import { createHash } from "crypto";
|
|
556
|
-
var SDK_VERSION = true ? "0.
|
|
620
|
+
var SDK_VERSION = true ? "0.20.0-dev.0" : "";
|
|
557
621
|
var SDK_LANGUAGE = "node";
|
|
558
622
|
function sdkResourceAttributes() {
|
|
559
623
|
const attrs = {
|
|
@@ -584,8 +648,8 @@ function isTelemetryOptedOut() {
|
|
|
584
648
|
const filePath = join2(home2, ".config", "atbash", "telemetry.json");
|
|
585
649
|
const raw = readFileSync2(filePath, "utf-8").trim();
|
|
586
650
|
if (!raw) return false;
|
|
587
|
-
const
|
|
588
|
-
return
|
|
651
|
+
const config2 = JSON.parse(raw);
|
|
652
|
+
return config2.enabled === false;
|
|
589
653
|
} catch {
|
|
590
654
|
return false;
|
|
591
655
|
}
|
|
@@ -630,26 +694,26 @@ function customCollector() {
|
|
|
630
694
|
}
|
|
631
695
|
return { url: url.replace(/\/+$/, ""), headers: () => headers };
|
|
632
696
|
}
|
|
633
|
-
function setupTelemetry(
|
|
634
|
-
if (!
|
|
697
|
+
function setupTelemetry(config2) {
|
|
698
|
+
if (!config2.enabled) return;
|
|
635
699
|
if (isTelemetryOptedOut()) return;
|
|
636
|
-
if (
|
|
637
|
-
defaultSource =
|
|
700
|
+
if (config2.source && !sourceExplicitlySet && SOURCE_SHAPE.test(config2.source)) {
|
|
701
|
+
defaultSource = config2.source;
|
|
638
702
|
sourceExplicitlySet = true;
|
|
639
703
|
}
|
|
640
|
-
if (
|
|
641
|
-
requestedExportIntervalMs =
|
|
704
|
+
if (config2.exportIntervalMs !== void 0 && requestedExportIntervalMs === void 0) {
|
|
705
|
+
requestedExportIntervalMs = config2.exportIntervalMs;
|
|
642
706
|
}
|
|
643
707
|
if (meterProvider) return;
|
|
644
708
|
const collector = customCollector();
|
|
645
|
-
if (!collector && (!
|
|
709
|
+
if (!collector && (!config2.endpoint || !config2.getAuthHeaders)) return;
|
|
646
710
|
if (!collector && /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
|
|
647
|
-
|
|
711
|
+
config2.endpoint
|
|
648
712
|
))
|
|
649
713
|
return;
|
|
650
714
|
const destination = collector ?? {
|
|
651
|
-
url: `${
|
|
652
|
-
headers: () =>
|
|
715
|
+
url: `${config2.endpoint.replace(/\/+$/, "")}/api/telemetry`,
|
|
716
|
+
headers: () => config2.getAuthHeaders()
|
|
653
717
|
};
|
|
654
718
|
const proxyUrl = destination.url;
|
|
655
719
|
const exporter = new OTLPMetricExporter({
|
|
@@ -684,7 +748,7 @@ function setupTelemetry(config) {
|
|
|
684
748
|
description: "SDK function execution duration",
|
|
685
749
|
unit: "ms"
|
|
686
750
|
});
|
|
687
|
-
counterHasher =
|
|
751
|
+
counterHasher = config2.hashAgentKey ?? defaultAgentKeyHasher;
|
|
688
752
|
const logExporter = new OTLPLogExporter({
|
|
689
753
|
// A real collector takes OTLP at its own signal paths; the Atbash proxy
|
|
690
754
|
// takes a query parameter, because it is one authenticated route.
|
|
@@ -703,8 +767,8 @@ function setupTelemetry(config) {
|
|
|
703
767
|
});
|
|
704
768
|
}
|
|
705
769
|
configureEvents(loggerProvider.getLogger("atbash-sdk"), {
|
|
706
|
-
source: sourceOrDefault(
|
|
707
|
-
hashAgentKey:
|
|
770
|
+
source: sourceOrDefault(config2.source),
|
|
771
|
+
hashAgentKey: config2.hashAgentKey ?? defaultAgentKeyHasher,
|
|
708
772
|
version: SDK_VERSION
|
|
709
773
|
});
|
|
710
774
|
}
|
|
@@ -782,13 +846,13 @@ function loadUserConfig() {
|
|
|
782
846
|
return {};
|
|
783
847
|
}
|
|
784
848
|
}
|
|
785
|
-
function saveUserConfig(
|
|
849
|
+
function saveUserConfig(config2) {
|
|
786
850
|
const dir = getConfigDir();
|
|
787
851
|
if (!existsSync2(dir)) {
|
|
788
852
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
789
853
|
}
|
|
790
854
|
const filePath = getConfigPath();
|
|
791
|
-
writeFileSync(filePath, JSON.stringify(
|
|
855
|
+
writeFileSync(filePath, JSON.stringify(config2, null, 2) + "\n", {
|
|
792
856
|
mode: 384
|
|
793
857
|
});
|
|
794
858
|
chmodSync(filePath, 384);
|
|
@@ -823,6 +887,7 @@ function forcedChainNetwork(flagValue) {
|
|
|
823
887
|
}
|
|
824
888
|
|
|
825
889
|
// src-ts/client.ts
|
|
890
|
+
var AGENT_NOT_REGISTERED = "Agent not registered. Onboard the agent at the dashboard before submitting actions.";
|
|
826
891
|
function generateToolCallId() {
|
|
827
892
|
return `tc-${Date.now()}-${randomHex(4)}`;
|
|
828
893
|
}
|
|
@@ -1171,6 +1236,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1171
1236
|
* return the signed tx hex. The server broadcasts to chain.
|
|
1172
1237
|
*/
|
|
1173
1238
|
async logToolCall(action, context = "", options = {}) {
|
|
1239
|
+
return this._logToolCall(action, context, options, false);
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* `raisePreflightErrors` (the judge path, like Python's
|
|
1243
|
+
* `raise_preflight_errors`): a failed registration check throws instead of
|
|
1244
|
+
* becoming `{ success: false }`, so the caller can tell Atbash's "no"
|
|
1245
|
+
* (not registered, or a 4xx) from an outage.
|
|
1246
|
+
*/
|
|
1247
|
+
async _logToolCall(action, context, options, raisePreflightErrors) {
|
|
1174
1248
|
const start = performance.now();
|
|
1175
1249
|
recordCall("logToolCall", void 0, this.auth.pubkey);
|
|
1176
1250
|
let exists;
|
|
@@ -1180,18 +1254,20 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1180
1254
|
});
|
|
1181
1255
|
} catch (err) {
|
|
1182
1256
|
recordDuration("logToolCall", performance.now() - start, "error");
|
|
1257
|
+
if (raisePreflightErrors) throw err;
|
|
1183
1258
|
return { success: false, toolCallId: null, error: errorMessage(err) };
|
|
1184
1259
|
}
|
|
1185
1260
|
if (!exists) {
|
|
1186
1261
|
recordDuration("logToolCall", performance.now() - start, "error");
|
|
1262
|
+
if (raisePreflightErrors) throw new JudgeRefusalError(AGENT_NOT_REGISTERED);
|
|
1187
1263
|
return {
|
|
1188
1264
|
success: false,
|
|
1189
1265
|
toolCallId: null,
|
|
1190
|
-
error:
|
|
1266
|
+
error: AGENT_NOT_REGISTERED
|
|
1191
1267
|
};
|
|
1192
1268
|
}
|
|
1193
1269
|
const toolCallId = generateToolCallId();
|
|
1194
|
-
const brid =
|
|
1270
|
+
const brid = await this.callBrid(options.chainOpts);
|
|
1195
1271
|
const orgKey = options.orgEncryptionPubKey ?? this.orgEncryptionPubKey ?? this._orgKeyFromChain;
|
|
1196
1272
|
try {
|
|
1197
1273
|
const signedHex = orgKey ? signEncryptedToolCall(
|
|
@@ -1241,6 +1317,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1241
1317
|
if (!action?.trim()) {
|
|
1242
1318
|
throw new Error("action is required and cannot be empty.");
|
|
1243
1319
|
}
|
|
1320
|
+
if (isJailHeld(this.auth.pubkey)) return this.localJailBlock();
|
|
1321
|
+
const startedAt = Date.now();
|
|
1244
1322
|
let chainOpts = options.chainOpts;
|
|
1245
1323
|
if (options.orgName && this._explicitChain) {
|
|
1246
1324
|
chainOpts = options.chainOpts ?? { network: this._defaultChain.network };
|
|
@@ -1265,12 +1343,13 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1265
1343
|
}
|
|
1266
1344
|
}
|
|
1267
1345
|
}
|
|
1268
|
-
const brid = this.
|
|
1269
|
-
const logResult = await this.
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
chainOpts
|
|
1273
|
-
|
|
1346
|
+
const brid = await this.callBrid(chainOpts);
|
|
1347
|
+
const logResult = await this._logToolCall(
|
|
1348
|
+
action,
|
|
1349
|
+
context,
|
|
1350
|
+
{ toolName: options.toolName, toolArgsJson: options.toolArgsJson, chainOpts },
|
|
1351
|
+
true
|
|
1352
|
+
);
|
|
1274
1353
|
if (!logResult.success || !logResult.toolCallId || !logResult.signedHex) {
|
|
1275
1354
|
throw new Error(logResult.error || "Failed to sign log_tool_call");
|
|
1276
1355
|
}
|
|
@@ -1307,10 +1386,16 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1307
1386
|
if (verifyPubKey !== void 0) {
|
|
1308
1387
|
verifyResponseSignature(bodyBytes, resp, verifyPubKey);
|
|
1309
1388
|
}
|
|
1310
|
-
const
|
|
1389
|
+
const parsed = tryParseJson(bodyBytes.toString("utf-8"));
|
|
1390
|
+
if (!isRecord2(parsed)) {
|
|
1391
|
+
throw new JudgeRefusalError(
|
|
1392
|
+
"The judge's reply could not be read, so the action was not approved."
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
const data = parsed;
|
|
1311
1396
|
const rawScore = data.score;
|
|
1312
1397
|
const score = typeof rawScore === "number" && Number.isInteger(rawScore) && rawScore >= 1 && rawScore <= 10 ? rawScore : void 0;
|
|
1313
|
-
|
|
1398
|
+
const result = {
|
|
1314
1399
|
verdict: normalizeVerdict(data.verdict),
|
|
1315
1400
|
allow: this.executable(data),
|
|
1316
1401
|
actionType: normalizeActionType(data.action_type),
|
|
@@ -1326,6 +1411,45 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1326
1411
|
score,
|
|
1327
1412
|
refusal: explicitRefusal(data)
|
|
1328
1413
|
};
|
|
1414
|
+
return this.applyJailHold(result, startedAt);
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Start a local hold on an enforced, on-chain BLOCK (the verdict that jails).
|
|
1418
|
+
* Refuse any other result that lands while the agent is held, or whose
|
|
1419
|
+
* request was already in flight when the block arrived: the server judged it
|
|
1420
|
+
* on pre-jail state.
|
|
1421
|
+
*/
|
|
1422
|
+
applyJailHold(result, startedAt) {
|
|
1423
|
+
const pubkey = this.auth.pubkey;
|
|
1424
|
+
if (result.verdict === "BLOCK" && result.enforced && result.onChain) {
|
|
1425
|
+
recordBlock(pubkey);
|
|
1426
|
+
holdJail(
|
|
1427
|
+
pubkey,
|
|
1428
|
+
() => this.getAgentPolicy(pubkey).then((p) => p.isJailed),
|
|
1429
|
+
() => this.logger.warn?.("[atbash] local jail hold released: the chain did not confirm the jail in time")
|
|
1430
|
+
);
|
|
1431
|
+
return result;
|
|
1432
|
+
}
|
|
1433
|
+
return isJailHeld(pubkey) || blockedSince(pubkey, startedAt) ? this.localJailBlock(result.toolCallId) : result;
|
|
1434
|
+
}
|
|
1435
|
+
/** The server's jailed refusal, produced locally while the agent is held. */
|
|
1436
|
+
localJailBlock(toolCallId = generateToolCallId()) {
|
|
1437
|
+
return {
|
|
1438
|
+
verdict: "BLOCK",
|
|
1439
|
+
allow: false,
|
|
1440
|
+
actionType: "block",
|
|
1441
|
+
reason: "Agent is jailed. All actions are blocked until the organization owner unjails this agent.",
|
|
1442
|
+
confidence: 1,
|
|
1443
|
+
provider: "policy",
|
|
1444
|
+
latencyMs: 0,
|
|
1445
|
+
toolCallId,
|
|
1446
|
+
onChain: false,
|
|
1447
|
+
enforced: true,
|
|
1448
|
+
enforcementMode: "enforce",
|
|
1449
|
+
status: "",
|
|
1450
|
+
score: void 0,
|
|
1451
|
+
refusal: "BLOCK"
|
|
1452
|
+
};
|
|
1329
1453
|
}
|
|
1330
1454
|
/* ── audit_tool_call (redact → judge → decision) ───────────────────────── */
|
|
1331
1455
|
/**
|
|
@@ -1377,6 +1501,9 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1377
1501
|
reason: `The judge's reply failed its signature check (${errorMessage(err)}), so it was not trusted.`
|
|
1378
1502
|
};
|
|
1379
1503
|
}
|
|
1504
|
+
if (err instanceof JudgeRefusalError || err instanceof AtbashAPIError && isRefusalStatus(err.status)) {
|
|
1505
|
+
return { ...this.failJudge(errorMessage(err), err), allow: false };
|
|
1506
|
+
}
|
|
1380
1507
|
return this.failJudge(errorMessage(err), err);
|
|
1381
1508
|
}
|
|
1382
1509
|
}
|
|
@@ -1406,6 +1533,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1406
1533
|
* never overrides a refusal.
|
|
1407
1534
|
*/
|
|
1408
1535
|
decide(result) {
|
|
1536
|
+
const inner = this.decideInner(result);
|
|
1537
|
+
if (!result.enforcementMode) return inner;
|
|
1538
|
+
return {
|
|
1539
|
+
...inner,
|
|
1540
|
+
enforced: result.enforced,
|
|
1541
|
+
enforcementMode: result.enforcementMode
|
|
1542
|
+
};
|
|
1543
|
+
}
|
|
1544
|
+
decideInner(result) {
|
|
1409
1545
|
const toolCallId = result.toolCallId;
|
|
1410
1546
|
const deny = (verdict, reason) => ({
|
|
1411
1547
|
allow: false,
|
|
@@ -1519,17 +1655,17 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1519
1655
|
/**
|
|
1520
1656
|
* Return the current status of a previously submitted judgment.
|
|
1521
1657
|
*
|
|
1522
|
-
* `chainOpts` names which chain the judgment was signed against
|
|
1523
|
-
*
|
|
1524
|
-
*
|
|
1525
|
-
*
|
|
1526
|
-
*
|
|
1527
|
-
*
|
|
1658
|
+
* `chainOpts` names which chain the judgment was signed against; the SDK
|
|
1659
|
+
* sends it as the `brid` query param the server routes by. Without it the
|
|
1660
|
+
* read uses the same chain `judgeAction` signs for (see `callBrid`): the
|
|
1661
|
+
* client's org chain when it has an `orgName`, else the client default.
|
|
1662
|
+
* A judgment signed with an explicit per-call `chainOpts` (or a per-call
|
|
1663
|
+
* `orgName` on another chain) must be polled with that same `chainOpts`.
|
|
1528
1664
|
*/
|
|
1529
1665
|
async getJudgmentStatus(judgmentId, agentPubkey, chainOpts) {
|
|
1530
1666
|
const pk = agentPubkey ?? this.auth.pubkey;
|
|
1531
|
-
const brid = this.bridFromChainOpts(chainOpts);
|
|
1532
1667
|
return this.track("getJudgmentStatus", pk, async () => {
|
|
1668
|
+
const brid = await this.callBrid(chainOpts);
|
|
1533
1669
|
const resp = await this.http.get(
|
|
1534
1670
|
"/api/v1/judge",
|
|
1535
1671
|
{ tool_call_id: judgmentId, agent_pubkey: pk, brid },
|
|
@@ -1867,6 +2003,18 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1867
2003
|
if (chainOpts?.network === "public") return PUBLIC_CHAIN.blockchainRid;
|
|
1868
2004
|
return this.blockchainRid;
|
|
1869
2005
|
}
|
|
2006
|
+
/**
|
|
2007
|
+
* The chain one call's transactions are signed for: the per-call override
|
|
2008
|
+
* when given, otherwise the client's org, otherwise the client default.
|
|
2009
|
+
* log_tool_call, judge_action and the judgment-status read all use this, so
|
|
2010
|
+
* a request never pairs transactions signed for two different chains.
|
|
2011
|
+
*/
|
|
2012
|
+
async callBrid(chainOpts) {
|
|
2013
|
+
if (chainOpts?.blockchainRid || chainOpts?.network) {
|
|
2014
|
+
return this.bridFromChainOpts(chainOpts);
|
|
2015
|
+
}
|
|
2016
|
+
return await this.defaultOrgBrid() ?? this.blockchainRid;
|
|
2017
|
+
}
|
|
1870
2018
|
/**
|
|
1871
2019
|
* Resolve the dashboard chain used by agent metadata/policy reads.
|
|
1872
2020
|
* Explicit per-call network overrides win; otherwise use the supplied org
|
|
@@ -1912,8 +2060,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1912
2060
|
async riskEnginePost(body, network, brid) {
|
|
1913
2061
|
let resp;
|
|
1914
2062
|
try {
|
|
1915
|
-
const
|
|
1916
|
-
resp = await this.http.post(
|
|
2063
|
+
const path4 = network ? `/api/risk-engine?network=${encodeURIComponent(network)}` : "/api/risk-engine";
|
|
2064
|
+
resp = await this.http.post(path4, body, this.authHeaders(brid));
|
|
1917
2065
|
} catch (err) {
|
|
1918
2066
|
throw this.transportError(err);
|
|
1919
2067
|
}
|
|
@@ -2174,10 +2322,6 @@ function tryParseJson(text) {
|
|
|
2174
2322
|
return void 0;
|
|
2175
2323
|
}
|
|
2176
2324
|
}
|
|
2177
|
-
function parseJson(bytes) {
|
|
2178
|
-
const parsed = tryParseJson(bytes.toString("utf-8"));
|
|
2179
|
-
return parsed === void 0 ? {} : parsed;
|
|
2180
|
-
}
|
|
2181
2325
|
function requestPath(url) {
|
|
2182
2326
|
if (!url) return void 0;
|
|
2183
2327
|
try {
|
|
@@ -2456,10 +2600,10 @@ function resolveBlockchainRid(chainOpts) {
|
|
|
2456
2600
|
'chainOpts.nodeUrls no longer selects a chain on its own \u2014 memory reaches the chain through the Atbash service, not a node. Set `blockchainRid`, or `network: "public" | "private"`.'
|
|
2457
2601
|
);
|
|
2458
2602
|
}
|
|
2459
|
-
const
|
|
2603
|
+
const config2 = chainForNetwork(
|
|
2460
2604
|
chainOpts?.network ?? forcedChainNetwork() ?? "private"
|
|
2461
2605
|
);
|
|
2462
|
-
return
|
|
2606
|
+
return config2.blockchainRid;
|
|
2463
2607
|
}
|
|
2464
2608
|
async function commitMemoryVersion(plaintext, auth, opts) {
|
|
2465
2609
|
const key = await deriveMemoryKey(auth.privkey);
|
|
@@ -2685,7 +2829,34 @@ function classifyMemoryWrite(event, ctx, opts = {}) {
|
|
|
2685
2829
|
// src-ts/memory/guard.ts
|
|
2686
2830
|
import { promises as fs } from "fs";
|
|
2687
2831
|
import os from "os";
|
|
2688
|
-
|
|
2832
|
+
|
|
2833
|
+
// src-ts/memory/slot.ts
|
|
2834
|
+
import nodePath from "path";
|
|
2835
|
+
function asciiLower(s) {
|
|
2836
|
+
return s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) | 32));
|
|
2837
|
+
}
|
|
2838
|
+
function spelledAs(target, base, p = nodePath) {
|
|
2839
|
+
const resolved = p.resolve(base);
|
|
2840
|
+
if (p.sep !== "\\") return resolved;
|
|
2841
|
+
const prefix = resolved.replace(/[\\/]+$/, "");
|
|
2842
|
+
const t = asciiLower(target);
|
|
2843
|
+
const b = asciiLower(prefix);
|
|
2844
|
+
if (t === b || t.startsWith(b + "\\") || t.startsWith(b + "/")) {
|
|
2845
|
+
return target.slice(0, prefix.length);
|
|
2846
|
+
}
|
|
2847
|
+
return resolved;
|
|
2848
|
+
}
|
|
2849
|
+
function memorySlotOf(target, home2, root, extraHomes, p = nodePath) {
|
|
2850
|
+
const resolved = p.resolve(target);
|
|
2851
|
+
return native.memorySlotForPath(
|
|
2852
|
+
resolved,
|
|
2853
|
+
home2 ? spelledAs(resolved, home2, p) : "",
|
|
2854
|
+
root ? spelledAs(resolved, root, p) : void 0,
|
|
2855
|
+
extraHomes
|
|
2856
|
+
);
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
// src-ts/memory/guard.ts
|
|
2689
2860
|
function emitDebugProbe(event, ctx, memEntry, logger2) {
|
|
2690
2861
|
if (!logger2?.info) return;
|
|
2691
2862
|
const ev = event ?? {};
|
|
@@ -2711,7 +2882,7 @@ function emitDebugProbe(event, ctx, memEntry, logger2) {
|
|
|
2711
2882
|
}
|
|
2712
2883
|
function memorySlotFor(key, root, extraHomes) {
|
|
2713
2884
|
const home2 = process.env.HOME || os.homedir() || "";
|
|
2714
|
-
return
|
|
2885
|
+
return memorySlotOf(key, home2, root, extraHomes);
|
|
2715
2886
|
}
|
|
2716
2887
|
async function resultingDocument(memEntry, event, ctx) {
|
|
2717
2888
|
const edits = native.extractMemoryEdits(event, ctx);
|
|
@@ -2914,7 +3085,7 @@ async function syncLocalMemory(auth, pointer, opts = {}) {
|
|
|
2914
3085
|
|
|
2915
3086
|
// src-ts/memory/pointer-store.ts
|
|
2916
3087
|
import { promises as fs2 } from "fs";
|
|
2917
|
-
import
|
|
3088
|
+
import path from "path";
|
|
2918
3089
|
var VERSION = 2;
|
|
2919
3090
|
var ZERO = { activeId: null, checkedAt: 0 };
|
|
2920
3091
|
var PointerStore = class {
|
|
@@ -2967,19 +3138,19 @@ var PointerStore = class {
|
|
|
2967
3138
|
this.cache = { version: VERSION, agents: {} };
|
|
2968
3139
|
}
|
|
2969
3140
|
async persist(file) {
|
|
2970
|
-
await fs2.mkdir(
|
|
3141
|
+
await fs2.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
2971
3142
|
const tmp = `${this.filePath}.${process.pid}.tmp`;
|
|
2972
3143
|
await fs2.writeFile(tmp, JSON.stringify(file, null, 2), "utf8");
|
|
2973
3144
|
await fs2.rename(tmp, this.filePath);
|
|
2974
3145
|
}
|
|
2975
3146
|
};
|
|
2976
3147
|
function defaultPointerPath(workspaceDir = process.cwd()) {
|
|
2977
|
-
return
|
|
3148
|
+
return path.join(workspaceDir, ".atbash", "memory-pointer.json");
|
|
2978
3149
|
}
|
|
2979
3150
|
|
|
2980
3151
|
// src-ts/memory/file-logger.ts
|
|
2981
3152
|
import { promises as fs3 } from "fs";
|
|
2982
|
-
import
|
|
3153
|
+
import path2 from "path";
|
|
2983
3154
|
function formatMeta(meta) {
|
|
2984
3155
|
if (!meta || Object.keys(meta).length === 0) return "";
|
|
2985
3156
|
try {
|
|
@@ -2992,7 +3163,7 @@ function createFileLogger(filePath, upstream) {
|
|
|
2992
3163
|
let queue = Promise.resolve();
|
|
2993
3164
|
let writeError;
|
|
2994
3165
|
async function ensureDir() {
|
|
2995
|
-
await fs3.mkdir(
|
|
3166
|
+
await fs3.mkdir(path2.dirname(filePath), { recursive: true });
|
|
2996
3167
|
}
|
|
2997
3168
|
function append(level, message, meta) {
|
|
2998
3169
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${message}${formatMeta(meta)}
|
|
@@ -3018,7 +3189,7 @@ function createFileLogger(filePath, upstream) {
|
|
|
3018
3189
|
};
|
|
3019
3190
|
}
|
|
3020
3191
|
function defaultPluginLogPath(workspaceDir = process.cwd()) {
|
|
3021
|
-
return
|
|
3192
|
+
return path2.join(workspaceDir, ".atbash", "plugin.log");
|
|
3022
3193
|
}
|
|
3023
3194
|
|
|
3024
3195
|
// src-ts/memory/read-classifier.ts
|
|
@@ -3034,14 +3205,14 @@ function classifyMemoryRead(event, ctx, opts = {}) {
|
|
|
3034
3205
|
// src-ts/memory/guard-manager.ts
|
|
3035
3206
|
import { promises as fs4 } from "fs";
|
|
3036
3207
|
import os2 from "os";
|
|
3037
|
-
import
|
|
3208
|
+
import path3 from "path";
|
|
3038
3209
|
var DEFAULT_SYNC_TTL_MS = 3e4;
|
|
3039
3210
|
var MemoryGuardManager = class {
|
|
3040
3211
|
constructor(opts) {
|
|
3041
3212
|
this.opts = opts;
|
|
3042
3213
|
const workspaceDir = opts.workspaceDir;
|
|
3043
3214
|
this.workspaceDir = workspaceDir;
|
|
3044
|
-
this.memoryFilePath = opts.memoryFilePath ??
|
|
3215
|
+
this.memoryFilePath = opts.memoryFilePath ?? path3.join(workspaceDir, "MEMORY.md");
|
|
3045
3216
|
this.pointerStore = new PointerStore(opts.pointerFilePath ?? defaultPointerPath(workspaceDir));
|
|
3046
3217
|
this.logger = createFileLogger(
|
|
3047
3218
|
opts.logFilePath ?? defaultPluginLogPath(workspaceDir),
|
|
@@ -3256,7 +3427,7 @@ var MemoryGuardManager = class {
|
|
|
3256
3427
|
*/
|
|
3257
3428
|
async locate(slot, roots) {
|
|
3258
3429
|
for (const root of roots) {
|
|
3259
|
-
const candidate =
|
|
3430
|
+
const candidate = path3.join(root, slot);
|
|
3260
3431
|
try {
|
|
3261
3432
|
await fs4.access(candidate);
|
|
3262
3433
|
return candidate;
|
|
@@ -3271,7 +3442,7 @@ var MemoryGuardManager = class {
|
|
|
3271
3442
|
/** Roots a chain slot may be joined to, most specific first. */
|
|
3272
3443
|
async memoryRoots() {
|
|
3273
3444
|
const roots = [];
|
|
3274
|
-
if (this.opts.memoryRoot) roots.push(
|
|
3445
|
+
if (this.opts.memoryRoot) roots.push(path3.resolve(this.opts.memoryRoot));
|
|
3275
3446
|
const home2 = process.env.HOME || os2.homedir() || "";
|
|
3276
3447
|
if (home2) {
|
|
3277
3448
|
const names = [
|
|
@@ -3279,7 +3450,7 @@ var MemoryGuardManager = class {
|
|
|
3279
3450
|
...this.opts.agentHomeDirs ?? []
|
|
3280
3451
|
];
|
|
3281
3452
|
for (const name of names) {
|
|
3282
|
-
const candidate =
|
|
3453
|
+
const candidate = path3.join(home2, name);
|
|
3283
3454
|
try {
|
|
3284
3455
|
if ((await fs4.stat(candidate)).isDirectory()) roots.push(candidate);
|
|
3285
3456
|
} catch {
|
|
@@ -3417,7 +3588,7 @@ var MemoryGuardManager = class {
|
|
|
3417
3588
|
const rec = bag;
|
|
3418
3589
|
for (const k of KEYS) {
|
|
3419
3590
|
const v = rec[k];
|
|
3420
|
-
if (typeof v === "string" && v) found.add(
|
|
3591
|
+
if (typeof v === "string" && v) found.add(path3.resolve(this.workspaceDir, v));
|
|
3421
3592
|
}
|
|
3422
3593
|
}
|
|
3423
3594
|
}
|
|
@@ -3464,12 +3635,7 @@ var MemoryGuardManager = class {
|
|
|
3464
3635
|
*/
|
|
3465
3636
|
slotFor(target) {
|
|
3466
3637
|
const home2 = process.env.HOME || os2.homedir() || "";
|
|
3467
|
-
return
|
|
3468
|
-
path4.resolve(target),
|
|
3469
|
-
home2,
|
|
3470
|
-
this.opts.memoryRoot,
|
|
3471
|
-
this.opts.agentHomeDirs
|
|
3472
|
-
);
|
|
3638
|
+
return memorySlotOf(target, home2, this.opts.memoryRoot, this.opts.agentHomeDirs);
|
|
3473
3639
|
}
|
|
3474
3640
|
/**
|
|
3475
3641
|
* Verify every memory file this call reads, each against its own chain slot.
|
|
@@ -3618,7 +3784,7 @@ var MemoryGuardManager = class {
|
|
|
3618
3784
|
}
|
|
3619
3785
|
/** Replace `target` in one step, so a reader never sees a half-written file. */
|
|
3620
3786
|
async writeFileAtomic(target, content) {
|
|
3621
|
-
await fs4.mkdir(
|
|
3787
|
+
await fs4.mkdir(path3.dirname(target), { recursive: true });
|
|
3622
3788
|
const tmp = `${target}.${process.pid}.tmp`;
|
|
3623
3789
|
await fs4.writeFile(tmp, content, "utf8");
|
|
3624
3790
|
await fs4.rename(tmp, target);
|
|
@@ -3758,6 +3924,7 @@ export {
|
|
|
3758
3924
|
EciesDomain,
|
|
3759
3925
|
HttpClient,
|
|
3760
3926
|
HttpTransportError,
|
|
3927
|
+
JudgeRefusalError,
|
|
3761
3928
|
KEY_FILENAMES,
|
|
3762
3929
|
MemoryGuardManager,
|
|
3763
3930
|
MemoryIntegrityError,
|
|
@@ -3776,6 +3943,7 @@ export {
|
|
|
3776
3943
|
columnAad,
|
|
3777
3944
|
commitMemoryVersion,
|
|
3778
3945
|
configureEvents,
|
|
3946
|
+
configureJailHold,
|
|
3779
3947
|
containsEvasionCharacters,
|
|
3780
3948
|
containsSecret,
|
|
3781
3949
|
createFileLogger,
|
|
@@ -3804,12 +3972,14 @@ export {
|
|
|
3804
3972
|
getRollbackHistory,
|
|
3805
3973
|
guardMemoryWrite,
|
|
3806
3974
|
isEnvelope,
|
|
3975
|
+
isJailHeld,
|
|
3807
3976
|
isValidPrivateKey,
|
|
3808
3977
|
keyFingerprintOf,
|
|
3809
3978
|
keyPathCandidates,
|
|
3810
3979
|
loadAgent,
|
|
3811
3980
|
loadAgentFromFile,
|
|
3812
3981
|
loadUserConfig,
|
|
3982
|
+
memorySlotOf,
|
|
3813
3983
|
normalizeActionForHash,
|
|
3814
3984
|
normalizeActionType,
|
|
3815
3985
|
normalizeForMatching,
|
|
@@ -3823,6 +3993,7 @@ export {
|
|
|
3823
3993
|
recordEvent,
|
|
3824
3994
|
redactJsonStrings,
|
|
3825
3995
|
redactSecrets,
|
|
3996
|
+
releaseJailHolds,
|
|
3826
3997
|
resolve,
|
|
3827
3998
|
resolveKeyPath,
|
|
3828
3999
|
rollbackMemory,
|