@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.js
CHANGED
|
@@ -42,6 +42,7 @@ __export(src_ts_exports, {
|
|
|
42
42
|
EciesDomain: () => EciesDomain,
|
|
43
43
|
HttpClient: () => HttpClient,
|
|
44
44
|
HttpTransportError: () => HttpTransportError,
|
|
45
|
+
JudgeRefusalError: () => JudgeRefusalError,
|
|
45
46
|
KEY_FILENAMES: () => KEY_FILENAMES,
|
|
46
47
|
MemoryGuardManager: () => MemoryGuardManager,
|
|
47
48
|
MemoryIntegrityError: () => MemoryIntegrityError,
|
|
@@ -60,6 +61,7 @@ __export(src_ts_exports, {
|
|
|
60
61
|
columnAad: () => columnAad,
|
|
61
62
|
commitMemoryVersion: () => commitMemoryVersion,
|
|
62
63
|
configureEvents: () => configureEvents,
|
|
64
|
+
configureJailHold: () => configureJailHold,
|
|
63
65
|
containsEvasionCharacters: () => containsEvasionCharacters,
|
|
64
66
|
containsSecret: () => containsSecret,
|
|
65
67
|
createFileLogger: () => createFileLogger,
|
|
@@ -88,12 +90,14 @@ __export(src_ts_exports, {
|
|
|
88
90
|
getRollbackHistory: () => getRollbackHistory,
|
|
89
91
|
guardMemoryWrite: () => guardMemoryWrite,
|
|
90
92
|
isEnvelope: () => isEnvelope,
|
|
93
|
+
isJailHeld: () => isJailHeld,
|
|
91
94
|
isValidPrivateKey: () => isValidPrivateKey,
|
|
92
95
|
keyFingerprintOf: () => keyFingerprintOf,
|
|
93
96
|
keyPathCandidates: () => keyPathCandidates,
|
|
94
97
|
loadAgent: () => loadAgent,
|
|
95
98
|
loadAgentFromFile: () => loadAgentFromFile,
|
|
96
99
|
loadUserConfig: () => loadUserConfig,
|
|
100
|
+
memorySlotOf: () => memorySlotOf,
|
|
97
101
|
normalizeActionForHash: () => normalizeActionForHash,
|
|
98
102
|
normalizeActionType: () => normalizeActionType,
|
|
99
103
|
normalizeForMatching: () => normalizeForMatching,
|
|
@@ -107,6 +111,7 @@ __export(src_ts_exports, {
|
|
|
107
111
|
recordEvent: () => recordEvent,
|
|
108
112
|
redactJsonStrings: () => redactJsonStrings,
|
|
109
113
|
redactSecrets: () => redactSecrets,
|
|
114
|
+
releaseJailHolds: () => releaseJailHolds,
|
|
110
115
|
resolve: () => resolve,
|
|
111
116
|
resolveKeyPath: () => resolveKeyPath,
|
|
112
117
|
rollbackMemory: () => rollbackMemory,
|
|
@@ -293,12 +298,12 @@ var AtbashAPIError = class extends Error {
|
|
|
293
298
|
* ran does not carry. The path is the fact that settles it.
|
|
294
299
|
*/
|
|
295
300
|
path;
|
|
296
|
-
constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT,
|
|
297
|
-
super(enrich(status, body, statusText, endpoint,
|
|
301
|
+
constructor(status, body, statusText = "", endpoint = DEFAULT_ENDPOINT, path4) {
|
|
302
|
+
super(enrich(status, body, statusText, endpoint, path4));
|
|
298
303
|
this.name = "AtbashAPIError";
|
|
299
304
|
this.status = status;
|
|
300
305
|
this.body = body;
|
|
301
|
-
this.path =
|
|
306
|
+
this.path = path4;
|
|
302
307
|
}
|
|
303
308
|
};
|
|
304
309
|
var SignatureVerificationError = class extends Error {
|
|
@@ -307,9 +312,18 @@ var SignatureVerificationError = class extends Error {
|
|
|
307
312
|
this.name = "SignatureVerificationError";
|
|
308
313
|
}
|
|
309
314
|
};
|
|
310
|
-
|
|
315
|
+
var JudgeRefusalError = class extends Error {
|
|
316
|
+
constructor(message) {
|
|
317
|
+
super(message);
|
|
318
|
+
this.name = "JudgeRefusalError";
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
function isRefusalStatus(status) {
|
|
322
|
+
return status >= 400 && status < 500 && status !== 408;
|
|
323
|
+
}
|
|
324
|
+
function enrich(status, body, statusText, endpoint, path4) {
|
|
311
325
|
const dashboard = endpoint.replace(/\/+$/, "") || DEFAULT_ENDPOINT;
|
|
312
|
-
let msg = `API error ${status}${
|
|
326
|
+
let msg = `API error ${status}${path4 ? ` from ${path4}` : ""}: ${body || statusText}`;
|
|
313
327
|
const lowered = body.toLowerCase();
|
|
314
328
|
if (lowered.includes("agent not registered")) {
|
|
315
329
|
msg += `
|
|
@@ -330,6 +344,61 @@ function enrich(status, body, statusText, endpoint, path5) {
|
|
|
330
344
|
return msg;
|
|
331
345
|
}
|
|
332
346
|
|
|
347
|
+
// src-ts/jail-hold.ts
|
|
348
|
+
var DEFAULTS = { ceilingMs: 3e4, pollMs: 1e3 };
|
|
349
|
+
var holds = /* @__PURE__ */ new Map();
|
|
350
|
+
var blockedAt = /* @__PURE__ */ new Map();
|
|
351
|
+
var BLOCK_MEMORY_MS = 10 * 6e4;
|
|
352
|
+
var config = { ...DEFAULTS };
|
|
353
|
+
function isJailHeld(pubkey) {
|
|
354
|
+
return holds.has(pubkey);
|
|
355
|
+
}
|
|
356
|
+
function recordBlock(pubkey) {
|
|
357
|
+
const now = Date.now();
|
|
358
|
+
for (const [key, at] of blockedAt) if (now - at > BLOCK_MEMORY_MS) blockedAt.delete(key);
|
|
359
|
+
blockedAt.set(pubkey, now);
|
|
360
|
+
}
|
|
361
|
+
function blockedSince(pubkey, startedAt) {
|
|
362
|
+
const at = blockedAt.get(pubkey);
|
|
363
|
+
return at !== void 0 && startedAt <= at;
|
|
364
|
+
}
|
|
365
|
+
function holdJail(pubkey, confirmJailed, onGiveUp) {
|
|
366
|
+
if (holds.has(pubkey)) return;
|
|
367
|
+
const hold = { timer: null };
|
|
368
|
+
holds.set(pubkey, hold);
|
|
369
|
+
const deadline = Date.now() + config.ceilingMs;
|
|
370
|
+
const tick = async () => {
|
|
371
|
+
if (holds.get(pubkey) !== hold) return;
|
|
372
|
+
let jailed = false;
|
|
373
|
+
try {
|
|
374
|
+
jailed = await confirmJailed();
|
|
375
|
+
} catch {
|
|
376
|
+
}
|
|
377
|
+
if (holds.get(pubkey) !== hold) return;
|
|
378
|
+
if (jailed) {
|
|
379
|
+
holds.delete(pubkey);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (Date.now() >= deadline) {
|
|
383
|
+
holds.delete(pubkey);
|
|
384
|
+
onGiveUp?.();
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
const timer = setTimeout(() => void tick(), config.pollMs);
|
|
388
|
+
timer.unref?.();
|
|
389
|
+
hold.timer = timer;
|
|
390
|
+
};
|
|
391
|
+
void tick();
|
|
392
|
+
}
|
|
393
|
+
function releaseJailHolds() {
|
|
394
|
+
for (const hold of holds.values()) if (hold.timer) clearTimeout(hold.timer);
|
|
395
|
+
holds.clear();
|
|
396
|
+
blockedAt.clear();
|
|
397
|
+
}
|
|
398
|
+
function configureJailHold(next) {
|
|
399
|
+
config = { ...DEFAULTS, ...next };
|
|
400
|
+
}
|
|
401
|
+
|
|
333
402
|
// src-ts/http/client.ts
|
|
334
403
|
var HttpClient = class {
|
|
335
404
|
baseUrl;
|
|
@@ -338,8 +407,8 @@ var HttpClient = class {
|
|
|
338
407
|
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
339
408
|
this.timeoutMs = timeoutMs;
|
|
340
409
|
}
|
|
341
|
-
buildUrl(
|
|
342
|
-
const url = new URL(this.baseUrl +
|
|
410
|
+
buildUrl(path4, query) {
|
|
411
|
+
const url = new URL(this.baseUrl + path4);
|
|
343
412
|
if (query) {
|
|
344
413
|
for (const [k, v] of Object.entries(query)) {
|
|
345
414
|
if (v !== void 0 && v !== null && v !== "") {
|
|
@@ -349,14 +418,14 @@ var HttpClient = class {
|
|
|
349
418
|
}
|
|
350
419
|
return url.toString();
|
|
351
420
|
}
|
|
352
|
-
async get(
|
|
353
|
-
return this.fetch(this.buildUrl(
|
|
421
|
+
async get(path4, query, headers) {
|
|
422
|
+
return this.fetch(this.buildUrl(path4, query), {
|
|
354
423
|
method: "GET",
|
|
355
424
|
...headers && { headers }
|
|
356
425
|
});
|
|
357
426
|
}
|
|
358
|
-
async post(
|
|
359
|
-
return this.fetch(this.buildUrl(
|
|
427
|
+
async post(path4, body, headers) {
|
|
428
|
+
return this.fetch(this.buildUrl(path4), {
|
|
360
429
|
method: "POST",
|
|
361
430
|
headers: { "Content-Type": "application/json", ...headers },
|
|
362
431
|
body: JSON.stringify(body)
|
|
@@ -674,7 +743,7 @@ var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
|
674
743
|
var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
|
|
675
744
|
var import_sdk_logs = require("@opentelemetry/sdk-logs");
|
|
676
745
|
var import_node_crypto = require("crypto");
|
|
677
|
-
var SDK_VERSION = true ? "0.
|
|
746
|
+
var SDK_VERSION = true ? "0.20.0-dev.0" : "";
|
|
678
747
|
var SDK_LANGUAGE = "node";
|
|
679
748
|
function sdkResourceAttributes() {
|
|
680
749
|
const attrs = {
|
|
@@ -705,8 +774,8 @@ function isTelemetryOptedOut() {
|
|
|
705
774
|
const filePath = (0, import_node_path2.join)(home2, ".config", "atbash", "telemetry.json");
|
|
706
775
|
const raw = (0, import_node_fs2.readFileSync)(filePath, "utf-8").trim();
|
|
707
776
|
if (!raw) return false;
|
|
708
|
-
const
|
|
709
|
-
return
|
|
777
|
+
const config2 = JSON.parse(raw);
|
|
778
|
+
return config2.enabled === false;
|
|
710
779
|
} catch {
|
|
711
780
|
return false;
|
|
712
781
|
}
|
|
@@ -751,26 +820,26 @@ function customCollector() {
|
|
|
751
820
|
}
|
|
752
821
|
return { url: url.replace(/\/+$/, ""), headers: () => headers };
|
|
753
822
|
}
|
|
754
|
-
function setupTelemetry(
|
|
755
|
-
if (!
|
|
823
|
+
function setupTelemetry(config2) {
|
|
824
|
+
if (!config2.enabled) return;
|
|
756
825
|
if (isTelemetryOptedOut()) return;
|
|
757
|
-
if (
|
|
758
|
-
defaultSource =
|
|
826
|
+
if (config2.source && !sourceExplicitlySet && SOURCE_SHAPE.test(config2.source)) {
|
|
827
|
+
defaultSource = config2.source;
|
|
759
828
|
sourceExplicitlySet = true;
|
|
760
829
|
}
|
|
761
|
-
if (
|
|
762
|
-
requestedExportIntervalMs =
|
|
830
|
+
if (config2.exportIntervalMs !== void 0 && requestedExportIntervalMs === void 0) {
|
|
831
|
+
requestedExportIntervalMs = config2.exportIntervalMs;
|
|
763
832
|
}
|
|
764
833
|
if (meterProvider) return;
|
|
765
834
|
const collector = customCollector();
|
|
766
|
-
if (!collector && (!
|
|
835
|
+
if (!collector && (!config2.endpoint || !config2.getAuthHeaders)) return;
|
|
767
836
|
if (!collector && /^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:|\/|$)/i.test(
|
|
768
|
-
|
|
837
|
+
config2.endpoint
|
|
769
838
|
))
|
|
770
839
|
return;
|
|
771
840
|
const destination = collector ?? {
|
|
772
|
-
url: `${
|
|
773
|
-
headers: () =>
|
|
841
|
+
url: `${config2.endpoint.replace(/\/+$/, "")}/api/telemetry`,
|
|
842
|
+
headers: () => config2.getAuthHeaders()
|
|
774
843
|
};
|
|
775
844
|
const proxyUrl = destination.url;
|
|
776
845
|
const exporter = new import_exporter_metrics_otlp_http.OTLPMetricExporter({
|
|
@@ -805,7 +874,7 @@ function setupTelemetry(config) {
|
|
|
805
874
|
description: "SDK function execution duration",
|
|
806
875
|
unit: "ms"
|
|
807
876
|
});
|
|
808
|
-
counterHasher =
|
|
877
|
+
counterHasher = config2.hashAgentKey ?? defaultAgentKeyHasher;
|
|
809
878
|
const logExporter = new import_exporter_logs_otlp_http.OTLPLogExporter({
|
|
810
879
|
// A real collector takes OTLP at its own signal paths; the Atbash proxy
|
|
811
880
|
// takes a query parameter, because it is one authenticated route.
|
|
@@ -824,8 +893,8 @@ function setupTelemetry(config) {
|
|
|
824
893
|
});
|
|
825
894
|
}
|
|
826
895
|
configureEvents(loggerProvider.getLogger("atbash-sdk"), {
|
|
827
|
-
source: sourceOrDefault(
|
|
828
|
-
hashAgentKey:
|
|
896
|
+
source: sourceOrDefault(config2.source),
|
|
897
|
+
hashAgentKey: config2.hashAgentKey ?? defaultAgentKeyHasher,
|
|
829
898
|
version: SDK_VERSION
|
|
830
899
|
});
|
|
831
900
|
}
|
|
@@ -897,13 +966,13 @@ function loadUserConfig() {
|
|
|
897
966
|
return {};
|
|
898
967
|
}
|
|
899
968
|
}
|
|
900
|
-
function saveUserConfig(
|
|
969
|
+
function saveUserConfig(config2) {
|
|
901
970
|
const dir = getConfigDir();
|
|
902
971
|
if (!(0, import_node_fs3.existsSync)(dir)) {
|
|
903
972
|
(0, import_node_fs3.mkdirSync)(dir, { recursive: true, mode: 448 });
|
|
904
973
|
}
|
|
905
974
|
const filePath = getConfigPath();
|
|
906
|
-
(0, import_node_fs3.writeFileSync)(filePath, JSON.stringify(
|
|
975
|
+
(0, import_node_fs3.writeFileSync)(filePath, JSON.stringify(config2, null, 2) + "\n", {
|
|
907
976
|
mode: 384
|
|
908
977
|
});
|
|
909
978
|
(0, import_node_fs3.chmodSync)(filePath, 384);
|
|
@@ -938,6 +1007,7 @@ function forcedChainNetwork(flagValue) {
|
|
|
938
1007
|
}
|
|
939
1008
|
|
|
940
1009
|
// src-ts/client.ts
|
|
1010
|
+
var AGENT_NOT_REGISTERED = "Agent not registered. Onboard the agent at the dashboard before submitting actions.";
|
|
941
1011
|
function generateToolCallId() {
|
|
942
1012
|
return `tc-${Date.now()}-${randomHex(4)}`;
|
|
943
1013
|
}
|
|
@@ -1286,6 +1356,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1286
1356
|
* return the signed tx hex. The server broadcasts to chain.
|
|
1287
1357
|
*/
|
|
1288
1358
|
async logToolCall(action, context = "", options = {}) {
|
|
1359
|
+
return this._logToolCall(action, context, options, false);
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* `raisePreflightErrors` (the judge path, like Python's
|
|
1363
|
+
* `raise_preflight_errors`): a failed registration check throws instead of
|
|
1364
|
+
* becoming `{ success: false }`, so the caller can tell Atbash's "no"
|
|
1365
|
+
* (not registered, or a 4xx) from an outage.
|
|
1366
|
+
*/
|
|
1367
|
+
async _logToolCall(action, context, options, raisePreflightErrors) {
|
|
1289
1368
|
const start = performance.now();
|
|
1290
1369
|
recordCall("logToolCall", void 0, this.auth.pubkey);
|
|
1291
1370
|
let exists;
|
|
@@ -1295,18 +1374,20 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1295
1374
|
});
|
|
1296
1375
|
} catch (err) {
|
|
1297
1376
|
recordDuration("logToolCall", performance.now() - start, "error");
|
|
1377
|
+
if (raisePreflightErrors) throw err;
|
|
1298
1378
|
return { success: false, toolCallId: null, error: errorMessage(err) };
|
|
1299
1379
|
}
|
|
1300
1380
|
if (!exists) {
|
|
1301
1381
|
recordDuration("logToolCall", performance.now() - start, "error");
|
|
1382
|
+
if (raisePreflightErrors) throw new JudgeRefusalError(AGENT_NOT_REGISTERED);
|
|
1302
1383
|
return {
|
|
1303
1384
|
success: false,
|
|
1304
1385
|
toolCallId: null,
|
|
1305
|
-
error:
|
|
1386
|
+
error: AGENT_NOT_REGISTERED
|
|
1306
1387
|
};
|
|
1307
1388
|
}
|
|
1308
1389
|
const toolCallId = generateToolCallId();
|
|
1309
|
-
const brid =
|
|
1390
|
+
const brid = await this.callBrid(options.chainOpts);
|
|
1310
1391
|
const orgKey = options.orgEncryptionPubKey ?? this.orgEncryptionPubKey ?? this._orgKeyFromChain;
|
|
1311
1392
|
try {
|
|
1312
1393
|
const signedHex = orgKey ? signEncryptedToolCall(
|
|
@@ -1356,6 +1437,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1356
1437
|
if (!action?.trim()) {
|
|
1357
1438
|
throw new Error("action is required and cannot be empty.");
|
|
1358
1439
|
}
|
|
1440
|
+
if (isJailHeld(this.auth.pubkey)) return this.localJailBlock();
|
|
1441
|
+
const startedAt = Date.now();
|
|
1359
1442
|
let chainOpts = options.chainOpts;
|
|
1360
1443
|
if (options.orgName && this._explicitChain) {
|
|
1361
1444
|
chainOpts = options.chainOpts ?? { network: this._defaultChain.network };
|
|
@@ -1380,12 +1463,13 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1380
1463
|
}
|
|
1381
1464
|
}
|
|
1382
1465
|
}
|
|
1383
|
-
const brid = this.
|
|
1384
|
-
const logResult = await this.
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
chainOpts
|
|
1388
|
-
|
|
1466
|
+
const brid = await this.callBrid(chainOpts);
|
|
1467
|
+
const logResult = await this._logToolCall(
|
|
1468
|
+
action,
|
|
1469
|
+
context,
|
|
1470
|
+
{ toolName: options.toolName, toolArgsJson: options.toolArgsJson, chainOpts },
|
|
1471
|
+
true
|
|
1472
|
+
);
|
|
1389
1473
|
if (!logResult.success || !logResult.toolCallId || !logResult.signedHex) {
|
|
1390
1474
|
throw new Error(logResult.error || "Failed to sign log_tool_call");
|
|
1391
1475
|
}
|
|
@@ -1422,10 +1506,16 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1422
1506
|
if (verifyPubKey !== void 0) {
|
|
1423
1507
|
verifyResponseSignature(bodyBytes, resp, verifyPubKey);
|
|
1424
1508
|
}
|
|
1425
|
-
const
|
|
1509
|
+
const parsed = tryParseJson(bodyBytes.toString("utf-8"));
|
|
1510
|
+
if (!isRecord2(parsed)) {
|
|
1511
|
+
throw new JudgeRefusalError(
|
|
1512
|
+
"The judge's reply could not be read, so the action was not approved."
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
const data = parsed;
|
|
1426
1516
|
const rawScore = data.score;
|
|
1427
1517
|
const score = typeof rawScore === "number" && Number.isInteger(rawScore) && rawScore >= 1 && rawScore <= 10 ? rawScore : void 0;
|
|
1428
|
-
|
|
1518
|
+
const result = {
|
|
1429
1519
|
verdict: normalizeVerdict(data.verdict),
|
|
1430
1520
|
allow: this.executable(data),
|
|
1431
1521
|
actionType: normalizeActionType(data.action_type),
|
|
@@ -1441,6 +1531,45 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1441
1531
|
score,
|
|
1442
1532
|
refusal: explicitRefusal(data)
|
|
1443
1533
|
};
|
|
1534
|
+
return this.applyJailHold(result, startedAt);
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Start a local hold on an enforced, on-chain BLOCK (the verdict that jails).
|
|
1538
|
+
* Refuse any other result that lands while the agent is held, or whose
|
|
1539
|
+
* request was already in flight when the block arrived: the server judged it
|
|
1540
|
+
* on pre-jail state.
|
|
1541
|
+
*/
|
|
1542
|
+
applyJailHold(result, startedAt) {
|
|
1543
|
+
const pubkey = this.auth.pubkey;
|
|
1544
|
+
if (result.verdict === "BLOCK" && result.enforced && result.onChain) {
|
|
1545
|
+
recordBlock(pubkey);
|
|
1546
|
+
holdJail(
|
|
1547
|
+
pubkey,
|
|
1548
|
+
() => this.getAgentPolicy(pubkey).then((p) => p.isJailed),
|
|
1549
|
+
() => this.logger.warn?.("[atbash] local jail hold released: the chain did not confirm the jail in time")
|
|
1550
|
+
);
|
|
1551
|
+
return result;
|
|
1552
|
+
}
|
|
1553
|
+
return isJailHeld(pubkey) || blockedSince(pubkey, startedAt) ? this.localJailBlock(result.toolCallId) : result;
|
|
1554
|
+
}
|
|
1555
|
+
/** The server's jailed refusal, produced locally while the agent is held. */
|
|
1556
|
+
localJailBlock(toolCallId = generateToolCallId()) {
|
|
1557
|
+
return {
|
|
1558
|
+
verdict: "BLOCK",
|
|
1559
|
+
allow: false,
|
|
1560
|
+
actionType: "block",
|
|
1561
|
+
reason: "Agent is jailed. All actions are blocked until the organization owner unjails this agent.",
|
|
1562
|
+
confidence: 1,
|
|
1563
|
+
provider: "policy",
|
|
1564
|
+
latencyMs: 0,
|
|
1565
|
+
toolCallId,
|
|
1566
|
+
onChain: false,
|
|
1567
|
+
enforced: true,
|
|
1568
|
+
enforcementMode: "enforce",
|
|
1569
|
+
status: "",
|
|
1570
|
+
score: void 0,
|
|
1571
|
+
refusal: "BLOCK"
|
|
1572
|
+
};
|
|
1444
1573
|
}
|
|
1445
1574
|
/* ── audit_tool_call (redact → judge → decision) ───────────────────────── */
|
|
1446
1575
|
/**
|
|
@@ -1492,6 +1621,9 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1492
1621
|
reason: `The judge's reply failed its signature check (${errorMessage(err)}), so it was not trusted.`
|
|
1493
1622
|
};
|
|
1494
1623
|
}
|
|
1624
|
+
if (err instanceof JudgeRefusalError || err instanceof AtbashAPIError && isRefusalStatus(err.status)) {
|
|
1625
|
+
return { ...this.failJudge(errorMessage(err), err), allow: false };
|
|
1626
|
+
}
|
|
1495
1627
|
return this.failJudge(errorMessage(err), err);
|
|
1496
1628
|
}
|
|
1497
1629
|
}
|
|
@@ -1521,6 +1653,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1521
1653
|
* never overrides a refusal.
|
|
1522
1654
|
*/
|
|
1523
1655
|
decide(result) {
|
|
1656
|
+
const inner = this.decideInner(result);
|
|
1657
|
+
if (!result.enforcementMode) return inner;
|
|
1658
|
+
return {
|
|
1659
|
+
...inner,
|
|
1660
|
+
enforced: result.enforced,
|
|
1661
|
+
enforcementMode: result.enforcementMode
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
decideInner(result) {
|
|
1524
1665
|
const toolCallId = result.toolCallId;
|
|
1525
1666
|
const deny = (verdict, reason) => ({
|
|
1526
1667
|
allow: false,
|
|
@@ -1634,17 +1775,17 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1634
1775
|
/**
|
|
1635
1776
|
* Return the current status of a previously submitted judgment.
|
|
1636
1777
|
*
|
|
1637
|
-
* `chainOpts` names which chain the judgment was signed against
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
1640
|
-
*
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1778
|
+
* `chainOpts` names which chain the judgment was signed against; the SDK
|
|
1779
|
+
* sends it as the `brid` query param the server routes by. Without it the
|
|
1780
|
+
* read uses the same chain `judgeAction` signs for (see `callBrid`): the
|
|
1781
|
+
* client's org chain when it has an `orgName`, else the client default.
|
|
1782
|
+
* A judgment signed with an explicit per-call `chainOpts` (or a per-call
|
|
1783
|
+
* `orgName` on another chain) must be polled with that same `chainOpts`.
|
|
1643
1784
|
*/
|
|
1644
1785
|
async getJudgmentStatus(judgmentId, agentPubkey, chainOpts) {
|
|
1645
1786
|
const pk = agentPubkey ?? this.auth.pubkey;
|
|
1646
|
-
const brid = this.bridFromChainOpts(chainOpts);
|
|
1647
1787
|
return this.track("getJudgmentStatus", pk, async () => {
|
|
1788
|
+
const brid = await this.callBrid(chainOpts);
|
|
1648
1789
|
const resp = await this.http.get(
|
|
1649
1790
|
"/api/v1/judge",
|
|
1650
1791
|
{ tool_call_id: judgmentId, agent_pubkey: pk, brid },
|
|
@@ -1982,6 +2123,18 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
1982
2123
|
if (chainOpts?.network === "public") return PUBLIC_CHAIN.blockchainRid;
|
|
1983
2124
|
return this.blockchainRid;
|
|
1984
2125
|
}
|
|
2126
|
+
/**
|
|
2127
|
+
* The chain one call's transactions are signed for: the per-call override
|
|
2128
|
+
* when given, otherwise the client's org, otherwise the client default.
|
|
2129
|
+
* log_tool_call, judge_action and the judgment-status read all use this, so
|
|
2130
|
+
* a request never pairs transactions signed for two different chains.
|
|
2131
|
+
*/
|
|
2132
|
+
async callBrid(chainOpts) {
|
|
2133
|
+
if (chainOpts?.blockchainRid || chainOpts?.network) {
|
|
2134
|
+
return this.bridFromChainOpts(chainOpts);
|
|
2135
|
+
}
|
|
2136
|
+
return await this.defaultOrgBrid() ?? this.blockchainRid;
|
|
2137
|
+
}
|
|
1985
2138
|
/**
|
|
1986
2139
|
* Resolve the dashboard chain used by agent metadata/policy reads.
|
|
1987
2140
|
* Explicit per-call network overrides win; otherwise use the supplied org
|
|
@@ -2027,8 +2180,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
|
|
|
2027
2180
|
async riskEnginePost(body, network, brid) {
|
|
2028
2181
|
let resp;
|
|
2029
2182
|
try {
|
|
2030
|
-
const
|
|
2031
|
-
resp = await this.http.post(
|
|
2183
|
+
const path4 = network ? `/api/risk-engine?network=${encodeURIComponent(network)}` : "/api/risk-engine";
|
|
2184
|
+
resp = await this.http.post(path4, body, this.authHeaders(brid));
|
|
2032
2185
|
} catch (err) {
|
|
2033
2186
|
throw this.transportError(err);
|
|
2034
2187
|
}
|
|
@@ -2289,10 +2442,6 @@ function tryParseJson(text) {
|
|
|
2289
2442
|
return void 0;
|
|
2290
2443
|
}
|
|
2291
2444
|
}
|
|
2292
|
-
function parseJson(bytes) {
|
|
2293
|
-
const parsed = tryParseJson(bytes.toString("utf-8"));
|
|
2294
|
-
return parsed === void 0 ? {} : parsed;
|
|
2295
|
-
}
|
|
2296
2445
|
function requestPath(url) {
|
|
2297
2446
|
if (!url) return void 0;
|
|
2298
2447
|
try {
|
|
@@ -2571,10 +2720,10 @@ function resolveBlockchainRid(chainOpts) {
|
|
|
2571
2720
|
'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"`.'
|
|
2572
2721
|
);
|
|
2573
2722
|
}
|
|
2574
|
-
const
|
|
2723
|
+
const config2 = chainForNetwork(
|
|
2575
2724
|
chainOpts?.network ?? forcedChainNetwork() ?? "private"
|
|
2576
2725
|
);
|
|
2577
|
-
return
|
|
2726
|
+
return config2.blockchainRid;
|
|
2578
2727
|
}
|
|
2579
2728
|
async function commitMemoryVersion(plaintext, auth, opts) {
|
|
2580
2729
|
const key = await deriveMemoryKey(auth.privkey);
|
|
@@ -2800,7 +2949,34 @@ function classifyMemoryWrite(event, ctx, opts = {}) {
|
|
|
2800
2949
|
// src-ts/memory/guard.ts
|
|
2801
2950
|
var import_node_fs4 = require("fs");
|
|
2802
2951
|
var import_node_os4 = __toESM(require("os"));
|
|
2952
|
+
|
|
2953
|
+
// src-ts/memory/slot.ts
|
|
2803
2954
|
var import_node_path4 = __toESM(require("path"));
|
|
2955
|
+
function asciiLower(s) {
|
|
2956
|
+
return s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) | 32));
|
|
2957
|
+
}
|
|
2958
|
+
function spelledAs(target, base, p = import_node_path4.default) {
|
|
2959
|
+
const resolved = p.resolve(base);
|
|
2960
|
+
if (p.sep !== "\\") return resolved;
|
|
2961
|
+
const prefix = resolved.replace(/[\\/]+$/, "");
|
|
2962
|
+
const t = asciiLower(target);
|
|
2963
|
+
const b = asciiLower(prefix);
|
|
2964
|
+
if (t === b || t.startsWith(b + "\\") || t.startsWith(b + "/")) {
|
|
2965
|
+
return target.slice(0, prefix.length);
|
|
2966
|
+
}
|
|
2967
|
+
return resolved;
|
|
2968
|
+
}
|
|
2969
|
+
function memorySlotOf(target, home2, root, extraHomes, p = import_node_path4.default) {
|
|
2970
|
+
const resolved = p.resolve(target);
|
|
2971
|
+
return native.memorySlotForPath(
|
|
2972
|
+
resolved,
|
|
2973
|
+
home2 ? spelledAs(resolved, home2, p) : "",
|
|
2974
|
+
root ? spelledAs(resolved, root, p) : void 0,
|
|
2975
|
+
extraHomes
|
|
2976
|
+
);
|
|
2977
|
+
}
|
|
2978
|
+
|
|
2979
|
+
// src-ts/memory/guard.ts
|
|
2804
2980
|
function emitDebugProbe(event, ctx, memEntry, logger2) {
|
|
2805
2981
|
if (!logger2?.info) return;
|
|
2806
2982
|
const ev = event ?? {};
|
|
@@ -2826,7 +3002,7 @@ function emitDebugProbe(event, ctx, memEntry, logger2) {
|
|
|
2826
3002
|
}
|
|
2827
3003
|
function memorySlotFor(key, root, extraHomes) {
|
|
2828
3004
|
const home2 = process.env.HOME || import_node_os4.default.homedir() || "";
|
|
2829
|
-
return
|
|
3005
|
+
return memorySlotOf(key, home2, root, extraHomes);
|
|
2830
3006
|
}
|
|
2831
3007
|
async function resultingDocument(memEntry, event, ctx) {
|
|
2832
3008
|
const edits = native.extractMemoryEdits(event, ctx);
|
|
@@ -3579,12 +3755,7 @@ var MemoryGuardManager = class {
|
|
|
3579
3755
|
*/
|
|
3580
3756
|
slotFor(target) {
|
|
3581
3757
|
const home2 = process.env.HOME || import_node_os5.default.homedir() || "";
|
|
3582
|
-
return
|
|
3583
|
-
import_node_path7.default.resolve(target),
|
|
3584
|
-
home2,
|
|
3585
|
-
this.opts.memoryRoot,
|
|
3586
|
-
this.opts.agentHomeDirs
|
|
3587
|
-
);
|
|
3758
|
+
return memorySlotOf(target, home2, this.opts.memoryRoot, this.opts.agentHomeDirs);
|
|
3588
3759
|
}
|
|
3589
3760
|
/**
|
|
3590
3761
|
* Verify every memory file this call reads, each against its own chain slot.
|
|
@@ -3874,6 +4045,7 @@ function diffMemorySnapshots(before, after) {
|
|
|
3874
4045
|
EciesDomain,
|
|
3875
4046
|
HttpClient,
|
|
3876
4047
|
HttpTransportError,
|
|
4048
|
+
JudgeRefusalError,
|
|
3877
4049
|
KEY_FILENAMES,
|
|
3878
4050
|
MemoryGuardManager,
|
|
3879
4051
|
MemoryIntegrityError,
|
|
@@ -3892,6 +4064,7 @@ function diffMemorySnapshots(before, after) {
|
|
|
3892
4064
|
columnAad,
|
|
3893
4065
|
commitMemoryVersion,
|
|
3894
4066
|
configureEvents,
|
|
4067
|
+
configureJailHold,
|
|
3895
4068
|
containsEvasionCharacters,
|
|
3896
4069
|
containsSecret,
|
|
3897
4070
|
createFileLogger,
|
|
@@ -3920,12 +4093,14 @@ function diffMemorySnapshots(before, after) {
|
|
|
3920
4093
|
getRollbackHistory,
|
|
3921
4094
|
guardMemoryWrite,
|
|
3922
4095
|
isEnvelope,
|
|
4096
|
+
isJailHeld,
|
|
3923
4097
|
isValidPrivateKey,
|
|
3924
4098
|
keyFingerprintOf,
|
|
3925
4099
|
keyPathCandidates,
|
|
3926
4100
|
loadAgent,
|
|
3927
4101
|
loadAgentFromFile,
|
|
3928
4102
|
loadUserConfig,
|
|
4103
|
+
memorySlotOf,
|
|
3929
4104
|
normalizeActionForHash,
|
|
3930
4105
|
normalizeActionType,
|
|
3931
4106
|
normalizeForMatching,
|
|
@@ -3939,6 +4114,7 @@ function diffMemorySnapshots(before, after) {
|
|
|
3939
4114
|
recordEvent,
|
|
3940
4115
|
redactJsonStrings,
|
|
3941
4116
|
redactSecrets,
|
|
4117
|
+
releaseJailHolds,
|
|
3942
4118
|
resolve,
|
|
3943
4119
|
resolveKeyPath,
|
|
3944
4120
|
rollbackMemory,
|