@atbash/sdk 0.16.6-dev.0 → 0.18.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/LICENSE +202 -28
- package/NOTICE +10 -0
- package/README.md +1 -1
- package/THIRD-PARTY-NOTICES +10256 -0
- package/dist/browser.d.mts +236 -54
- package/dist/browser.mjs +377 -141
- package/dist/index.d.mts +236 -54
- package/dist/index.d.ts +236 -54
- package/dist/index.js +1107 -41571
- package/dist/index.mjs +2538 -43030
- package/index.d.ts +19 -0
- package/index.js +61 -54
- package/package.json +10 -8
package/dist/browser.mjs
CHANGED
|
@@ -43455,6 +43455,17 @@ var native = {
|
|
|
43455
43455
|
defaultChromiaNodeUrls: () => [...DEFAULT_CHROMIA_NODE_URLS_ARR],
|
|
43456
43456
|
defaultPrivateNodeUrls: () => [...DEFAULT_PRIVATE_NODE_URLS_ARR],
|
|
43457
43457
|
// Server-only surfaces — throw at runtime, keep types satisfied.
|
|
43458
|
+
// The memory signers are here and not with the other signing shims because
|
|
43459
|
+
// their args are encrypted by `encryptMemoryContent`, which this build does
|
|
43460
|
+
// not have: a browser could sign the transaction but never produce one worth
|
|
43461
|
+
// sending.
|
|
43462
|
+
signAddAgentMemory: stub("signAddAgentMemory"),
|
|
43463
|
+
signRollbackAgentMemory: stub("signRollbackAgentMemory"),
|
|
43464
|
+
extractMemoryEdits: stub("extractMemoryEdits"),
|
|
43465
|
+
applyMemoryEdits: stub("applyMemoryEdits"),
|
|
43466
|
+
memorySlotForPath: stub("memorySlotForPath"),
|
|
43467
|
+
isSafeMemorySlot: stub("isSafeMemorySlot"),
|
|
43468
|
+
defaultAgentHomeDirs: stub("defaultAgentHomeDirs"),
|
|
43458
43469
|
classifyMemoryWrite: stub("classifyMemoryWrite"),
|
|
43459
43470
|
classifyMemoryRead: stub("classifyMemoryRead"),
|
|
43460
43471
|
defaultMemoryWriteToolNames: stub("defaultMemoryWriteToolNames"),
|
|
@@ -43531,6 +43542,32 @@ function randomHex(size) {
|
|
|
43531
43542
|
return hex;
|
|
43532
43543
|
}
|
|
43533
43544
|
|
|
43545
|
+
// src-ts/auth-bearer.ts
|
|
43546
|
+
init_define_ATBASH_CHROMIA_NODE_URLS();
|
|
43547
|
+
init_define_ATBASH_PRIVATE_NODE_URLS();
|
|
43548
|
+
var BEARER_TTL_MS = 4 * 60 * 1e3;
|
|
43549
|
+
var bearers = /* @__PURE__ */ new Map();
|
|
43550
|
+
function agentAuthBearer(auth, blockchainRid) {
|
|
43551
|
+
const key3 = `${auth.pubkey}:${blockchainRid}`;
|
|
43552
|
+
const now = Date.now();
|
|
43553
|
+
const cached = bearers.get(key3);
|
|
43554
|
+
if (cached && now - cached.issuedAt < BEARER_TTL_MS) return cached.hex;
|
|
43555
|
+
const hex = native.signLogToolCall(
|
|
43556
|
+
`auth-${now.toString(36)}-${randomHex(4)}`,
|
|
43557
|
+
`auth:${now}`,
|
|
43558
|
+
"",
|
|
43559
|
+
"auth-bearer",
|
|
43560
|
+
"",
|
|
43561
|
+
auth.privkey,
|
|
43562
|
+
blockchainRid
|
|
43563
|
+
);
|
|
43564
|
+
bearers.set(key3, { hex, issuedAt: now });
|
|
43565
|
+
return hex;
|
|
43566
|
+
}
|
|
43567
|
+
function agentAuthHeaders(auth, blockchainRid) {
|
|
43568
|
+
return { Authorization: `Bearer ${agentAuthBearer(auth, blockchainRid)}` };
|
|
43569
|
+
}
|
|
43570
|
+
|
|
43534
43571
|
// src-ts/chain-config.ts
|
|
43535
43572
|
init_define_ATBASH_CHROMIA_NODE_URLS();
|
|
43536
43573
|
init_define_ATBASH_PRIVATE_NODE_URLS();
|
|
@@ -44590,7 +44627,7 @@ init_define_ATBASH_CHROMIA_NODE_URLS();
|
|
|
44590
44627
|
init_define_ATBASH_PRIVATE_NODE_URLS();
|
|
44591
44628
|
function normalizeVerdict(raw2) {
|
|
44592
44629
|
if (raw2 === null || raw2 === void 0) return "No verdict";
|
|
44593
|
-
const v = String(raw2).toUpperCase();
|
|
44630
|
+
const v = String(raw2).trim().toUpperCase();
|
|
44594
44631
|
if (v === "ALLOW" || v === "GREEN") return "ALLOW";
|
|
44595
44632
|
if (v === "HOLD" || v === "YELLOW") return "HOLD";
|
|
44596
44633
|
if (v === "BLOCK" || v === "RED") return "BLOCK";
|
|
@@ -44627,17 +44664,58 @@ function allowSignalsPermit(data) {
|
|
|
44627
44664
|
}
|
|
44628
44665
|
return signals.length === 0 || signals.every((value) => value === true);
|
|
44629
44666
|
}
|
|
44667
|
+
function explicitRefusal(data) {
|
|
44668
|
+
let hold = false;
|
|
44669
|
+
for (const source of decisionSources(data)) {
|
|
44670
|
+
const verdict = verdictOf(source);
|
|
44671
|
+
if (verdict === "BLOCK" || verdict === "RED") return "BLOCK";
|
|
44672
|
+
if (verdict === "HOLD" || verdict === "YELLOW") hold = true;
|
|
44673
|
+
const actionType = actionTypeOf(source);
|
|
44674
|
+
if (actionType === "block") return "BLOCK";
|
|
44675
|
+
if (actionType === "hold_for_user_confirm") hold = true;
|
|
44676
|
+
}
|
|
44677
|
+
if (!allowSignalsPermit(data)) return "BLOCK";
|
|
44678
|
+
return hold ? "HOLD" : "";
|
|
44679
|
+
}
|
|
44680
|
+
function decisionSources(data) {
|
|
44681
|
+
return [data, isRecord(data.decision) ? data.decision : {}];
|
|
44682
|
+
}
|
|
44683
|
+
function verdictOf(source) {
|
|
44684
|
+
return typeof source.verdict === "string" ? source.verdict.trim().toUpperCase() : "";
|
|
44685
|
+
}
|
|
44686
|
+
function actionTypeRank(actionType) {
|
|
44687
|
+
if (actionType === "block") return 4;
|
|
44688
|
+
if (actionType === "hold_for_user_confirm") return 3;
|
|
44689
|
+
if (actionType === "") return 1;
|
|
44690
|
+
if (actionType === "allow") return 0;
|
|
44691
|
+
return 2;
|
|
44692
|
+
}
|
|
44693
|
+
function actionTypeOf(source) {
|
|
44694
|
+
let winner = "";
|
|
44695
|
+
for (const key3 of ["action_type", "actionType"]) {
|
|
44696
|
+
const raw2 = source[key3];
|
|
44697
|
+
if (raw2 === void 0 || raw2 === null) continue;
|
|
44698
|
+
const actionType = normalizeActionType(raw2);
|
|
44699
|
+
if (actionTypeRank(actionType) > actionTypeRank(winner)) winner = actionType;
|
|
44700
|
+
}
|
|
44701
|
+
return winner;
|
|
44702
|
+
}
|
|
44630
44703
|
function wireActionType(data) {
|
|
44631
|
-
|
|
44632
|
-
|
|
44633
|
-
|
|
44704
|
+
for (const source of decisionSources(data)) {
|
|
44705
|
+
const actionType = actionTypeOf(source);
|
|
44706
|
+
if (actionType !== "") return actionType;
|
|
44707
|
+
}
|
|
44708
|
+
return "";
|
|
44634
44709
|
}
|
|
44635
44710
|
function isAuditTier(data) {
|
|
44636
44711
|
const absentVerdict = data.verdict === null || data.verdict === void 0;
|
|
44637
44712
|
return absentVerdict && data.status === "logged";
|
|
44638
44713
|
}
|
|
44714
|
+
function isAuditTierBody(data) {
|
|
44715
|
+
return isAuditTier(data);
|
|
44716
|
+
}
|
|
44639
44717
|
function canonicalAllow(data) {
|
|
44640
|
-
if (
|
|
44718
|
+
if (explicitRefusal(data) !== "") return false;
|
|
44641
44719
|
const actionType = wireActionType(data);
|
|
44642
44720
|
if (actionType && actionType !== "allow") return false;
|
|
44643
44721
|
if (isAuditTier(data)) return true;
|
|
@@ -44682,6 +44760,12 @@ function resolve(_key, flagValue) {
|
|
|
44682
44760
|
function forcedChainNetwork(flagValue) {
|
|
44683
44761
|
return flagValue === "public" || flagValue === "private" ? flagValue : void 0;
|
|
44684
44762
|
}
|
|
44763
|
+
function agentKeyFromEnv() {
|
|
44764
|
+
return "";
|
|
44765
|
+
}
|
|
44766
|
+
function strandedAgentKey() {
|
|
44767
|
+
return "";
|
|
44768
|
+
}
|
|
44685
44769
|
|
|
44686
44770
|
// src-ts/client.ts
|
|
44687
44771
|
function generateToolCallId() {
|
|
@@ -44720,6 +44804,10 @@ var Atbash = class _Atbash {
|
|
|
44720
44804
|
orgEncryptionPubKey;
|
|
44721
44805
|
/** When true (default), `auditToolCall` denies on any error. */
|
|
44722
44806
|
failClosed;
|
|
44807
|
+
/** When true (default), an unenforced (audit-tier) answer is refused. */
|
|
44808
|
+
requireEnforcement;
|
|
44809
|
+
/** Overall deadline for one auditToolCall decision, in ms. */
|
|
44810
|
+
decisionTimeoutMs;
|
|
44723
44811
|
/** Org key learned from the last agent-exists check, for this agent only. */
|
|
44724
44812
|
_orgKeyFromChain = null;
|
|
44725
44813
|
debug;
|
|
@@ -44761,16 +44849,6 @@ var Atbash = class _Atbash {
|
|
|
44761
44849
|
*/
|
|
44762
44850
|
_agentExistsCache = null;
|
|
44763
44851
|
static AGENT_EXISTS_TTL_MS = 5e3;
|
|
44764
|
-
/**
|
|
44765
|
-
* Cached bearer token for risk-engine / insurance read calls. Built
|
|
44766
|
-
* lazily as a signed `log_tool_call` tx and refreshed every 4 min so
|
|
44767
|
-
* server-side replay protection windows never expire it mid-session.
|
|
44768
|
-
*/
|
|
44769
|
-
// Keyed by blockchainRid so bearers for the public and private chains can
|
|
44770
|
-
// coexist. The dashboard derives `authNetwork` from the BRID inside the
|
|
44771
|
-
// signed envelope, so a bearer signed for chain A cannot authenticate a
|
|
44772
|
-
// request routed to chain B — every chain the client talks to needs its own.
|
|
44773
|
-
_authBearers = /* @__PURE__ */ new Map();
|
|
44774
44852
|
/** Guards `logEnvironmentOnce` — hosts construct several clients. */
|
|
44775
44853
|
static environmentLogged = false;
|
|
44776
44854
|
constructor(privkey, options = {}) {
|
|
@@ -44793,6 +44871,8 @@ var Atbash = class _Atbash {
|
|
|
44793
44871
|
this.verifyPubKey = validated.verifyPubKey ?? void 0;
|
|
44794
44872
|
this.orgEncryptionPubKey = options.orgEncryptionPubKey;
|
|
44795
44873
|
this.failClosed = options.failClosed !== false;
|
|
44874
|
+
this.requireEnforcement = options.requireEnforcement !== false;
|
|
44875
|
+
this.decisionTimeoutMs = options.decisionTimeoutMs ?? DEFAULT_DECISION_TIMEOUT_MS;
|
|
44796
44876
|
this.debug = options.debug === true;
|
|
44797
44877
|
this.logger = options.logger ?? {};
|
|
44798
44878
|
this.http = new HttpClient(this.endpoint, options.timeoutMs ?? 12e4);
|
|
@@ -44856,6 +44936,84 @@ var Atbash = class _Atbash {
|
|
|
44856
44936
|
* validated against the trusted allowlist / self-hosted policy; a
|
|
44857
44937
|
* self-hosted endpoint's `verifyPubKey` becomes the client default.
|
|
44858
44938
|
*/
|
|
44939
|
+
/**
|
|
44940
|
+
* Decide which key this client signs as.
|
|
44941
|
+
*
|
|
44942
|
+
* Most specific wins: a key passed in code, then a key file the host named,
|
|
44943
|
+
* then the environment, then the default key file. `config.json` is absent
|
|
44944
|
+
* from that list on purpose — it is a settings file, writable by anything
|
|
44945
|
+
* that can write the user's home and read by every Atbash integration on the
|
|
44946
|
+
* machine, so letting it name the key meant one stray value silently
|
|
44947
|
+
* re-pointed who each of them signed as.
|
|
44948
|
+
*
|
|
44949
|
+
* A named `keyPath` that does not exist throws rather than falling through.
|
|
44950
|
+
* The caller said which file holds its identity; quietly signing as a
|
|
44951
|
+
* different one is the outcome worth refusing.
|
|
44952
|
+
*
|
|
44953
|
+
* @param options - The `fromConfig` options, read for `agentKey` and `keyPath`.
|
|
44954
|
+
* @returns The resolved agent identity.
|
|
44955
|
+
* @throws When a named key file is missing, or no identity can be found.
|
|
44956
|
+
*/
|
|
44957
|
+
static resolveIdentity(options) {
|
|
44958
|
+
if (options.agentKey) return native.loadAgent(options.agentKey);
|
|
44959
|
+
if (options.keyPath) return loadAgentFromFile(options.keyPath);
|
|
44960
|
+
const fromEnv = agentKeyFromEnv();
|
|
44961
|
+
if (fromEnv) return native.loadAgent(fromEnv);
|
|
44962
|
+
try {
|
|
44963
|
+
return loadAgentFromFile();
|
|
44964
|
+
} catch (err) {
|
|
44965
|
+
throw _Atbash.explainStrandedKey(err);
|
|
44966
|
+
}
|
|
44967
|
+
}
|
|
44968
|
+
/**
|
|
44969
|
+
* Add the one thing a machine with no key file needs to hear.
|
|
44970
|
+
*
|
|
44971
|
+
* This is the only path where the operator cannot see what happened: no key
|
|
44972
|
+
* file, and the key they are looking for sits in `config.json`, which no
|
|
44973
|
+
* longer selects the identity. A bare "key file not found" sends them hunting
|
|
44974
|
+
* for a key they already have. Only this branch gets the hint — a caller who
|
|
44975
|
+
* named a `keyPath` is told about that path, where a stranded key is noise.
|
|
44976
|
+
*
|
|
44977
|
+
* @param err - The "key file not found" error from the default lookup.
|
|
44978
|
+
* @returns The same error, or one that also names the key in config.json.
|
|
44979
|
+
*/
|
|
44980
|
+
static explainStrandedKey(err) {
|
|
44981
|
+
const stranded = strandedAgentKey();
|
|
44982
|
+
if (!stranded) return err;
|
|
44983
|
+
let identity;
|
|
44984
|
+
try {
|
|
44985
|
+
identity = native.derivePublicKey(stranded);
|
|
44986
|
+
} catch {
|
|
44987
|
+
identity = "(unreadable)";
|
|
44988
|
+
}
|
|
44989
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
44990
|
+
return new Error(
|
|
44991
|
+
`${message}
|
|
44992
|
+
config.json still holds an "agentKey" (${identity}), but a settings file no longer chooses the signing key. Move it into one of the key files above, or set ATBASH_AGENT_KEY.`
|
|
44993
|
+
);
|
|
44994
|
+
}
|
|
44995
|
+
/**
|
|
44996
|
+
* Say so when `config.json` still holds an agent key.
|
|
44997
|
+
*
|
|
44998
|
+
* Nothing signs with it any more, and silence would leave an operator
|
|
44999
|
+
* believing one identity is in force while another is. The pubkey is named,
|
|
45000
|
+
* not the key, so the line is safe in a log.
|
|
45001
|
+
*
|
|
45002
|
+
* @param logger - The host logger, when one was supplied.
|
|
45003
|
+
*/
|
|
45004
|
+
static warnOnStrandedKey(logger2) {
|
|
45005
|
+
const stranded = strandedAgentKey();
|
|
45006
|
+
if (!stranded) return;
|
|
45007
|
+
let identity;
|
|
45008
|
+
try {
|
|
45009
|
+
identity = native.derivePublicKey(stranded);
|
|
45010
|
+
} catch {
|
|
45011
|
+
identity = "(unreadable)";
|
|
45012
|
+
}
|
|
45013
|
+
logger2?.warn?.(
|
|
45014
|
+
`[atbash] "agentKey" in config.json is ignored \u2014 settings do not choose the signing key. It holds ${identity}; this client signs as the key file or ATBASH_AGENT_KEY instead. Move it into the key file, or delete the field, to silence this.`
|
|
45015
|
+
);
|
|
45016
|
+
}
|
|
44859
45017
|
static fromConfig(options = {}) {
|
|
44860
45018
|
const configuredEndpoint = resolve("judgeEndpoint") || void 0;
|
|
44861
45019
|
const configuredVerifyPubKey = resolve("judgeVerifyPubKey") || void 0;
|
|
@@ -44871,8 +45029,8 @@ var Atbash = class _Atbash {
|
|
|
44871
45029
|
verifyPubKey: configuredVerifyPubKey
|
|
44872
45030
|
} : { endpoint: configuredEndpoint })
|
|
44873
45031
|
);
|
|
44874
|
-
const
|
|
44875
|
-
|
|
45032
|
+
const auth = _Atbash.resolveIdentity(options);
|
|
45033
|
+
_Atbash.warnOnStrandedKey(options.logger);
|
|
44876
45034
|
return new _Atbash(auth.privkey, {
|
|
44877
45035
|
endpoint: validated.url,
|
|
44878
45036
|
chain: options.chain,
|
|
@@ -44883,6 +45041,8 @@ var Atbash = class _Atbash {
|
|
|
44883
45041
|
orgName: options.orgName,
|
|
44884
45042
|
verifyPubKey: validated.verifyPubKey ?? void 0,
|
|
44885
45043
|
failClosed: options.failClosed,
|
|
45044
|
+
requireEnforcement: options.requireEnforcement,
|
|
45045
|
+
decisionTimeoutMs: options.decisionTimeoutMs,
|
|
44886
45046
|
// ATBASH_DEBUG lets an operator turn diagnostics on without editing a
|
|
44887
45047
|
// host's plugin config, which is usually the harder half.
|
|
44888
45048
|
debug: options.debug ?? /^(1|true|yes)$/i.test(resolve("debug")),
|
|
@@ -45081,34 +45241,16 @@ var Atbash = class _Atbash {
|
|
|
45081
45241
|
}
|
|
45082
45242
|
if (!resp.ok) throw await this.httpError(resp);
|
|
45083
45243
|
const bodyBytes = Buffer.from(await resp.arrayBuffer());
|
|
45084
|
-
const verifyPubKey = options.verifyPubKey
|
|
45244
|
+
const verifyPubKey = this.effectiveVerifyKey(options.verifyPubKey);
|
|
45085
45245
|
if (verifyPubKey !== void 0) {
|
|
45086
|
-
|
|
45087
|
-
if (!sig) {
|
|
45088
|
-
throw new SignatureVerificationError(
|
|
45089
|
-
"missing X-Atbash-Signature header"
|
|
45090
|
-
);
|
|
45091
|
-
}
|
|
45092
|
-
let ok;
|
|
45093
|
-
try {
|
|
45094
|
-
ok = native.verifySignature(bodyBytes, sig, verifyPubKey);
|
|
45095
|
-
} catch (err) {
|
|
45096
|
-
throw new SignatureVerificationError(
|
|
45097
|
-
`signature verification threw: ${errorMessage(err)}`
|
|
45098
|
-
);
|
|
45099
|
-
}
|
|
45100
|
-
if (!ok) {
|
|
45101
|
-
throw new SignatureVerificationError(
|
|
45102
|
-
"signature does not verify against configured verifyPubKey"
|
|
45103
|
-
);
|
|
45104
|
-
}
|
|
45246
|
+
verifyResponseSignature(bodyBytes, resp, verifyPubKey);
|
|
45105
45247
|
}
|
|
45106
45248
|
const data = parseJson(bodyBytes);
|
|
45107
45249
|
const rawScore = data.score;
|
|
45108
45250
|
const score = typeof rawScore === "number" && Number.isInteger(rawScore) && rawScore >= 1 && rawScore <= 10 ? rawScore : void 0;
|
|
45109
45251
|
return {
|
|
45110
45252
|
verdict: normalizeVerdict(data.verdict),
|
|
45111
|
-
allow:
|
|
45253
|
+
allow: this.executable(data),
|
|
45112
45254
|
actionType: normalizeActionType(data.action_type),
|
|
45113
45255
|
reason: String(data.reason ?? ""),
|
|
45114
45256
|
confidence: Number(data.confidence ?? 0),
|
|
@@ -45119,7 +45261,8 @@ var Atbash = class _Atbash {
|
|
|
45119
45261
|
enforced: Boolean(data.enforced),
|
|
45120
45262
|
enforcementMode: String(data.enforcement_mode ?? ""),
|
|
45121
45263
|
status: String(data.status ?? ""),
|
|
45122
|
-
score
|
|
45264
|
+
score,
|
|
45265
|
+
refusal: explicitRefusal(data)
|
|
45123
45266
|
};
|
|
45124
45267
|
}
|
|
45125
45268
|
/* ── audit_tool_call (redact → judge → decision) ───────────────────────── */
|
|
@@ -45152,77 +45295,125 @@ var Atbash = class _Atbash {
|
|
|
45152
45295
|
}
|
|
45153
45296
|
try {
|
|
45154
45297
|
this.logger.info?.("[atbash] judge API called", { tool: toolName });
|
|
45155
|
-
const result = await
|
|
45156
|
-
|
|
45157
|
-
|
|
45158
|
-
|
|
45159
|
-
|
|
45160
|
-
|
|
45161
|
-
|
|
45162
|
-
|
|
45163
|
-
|
|
45164
|
-
|
|
45165
|
-
|
|
45166
|
-
|
|
45167
|
-
|
|
45168
|
-
|
|
45169
|
-
}
|
|
45170
|
-
return {
|
|
45171
|
-
allow: true,
|
|
45172
|
-
verdict: "ALLOW",
|
|
45173
|
-
reason: result.reason || "audit tier \u2014 request logged on-chain, no AI enforcement",
|
|
45174
|
-
toolCallId: result.toolCallId
|
|
45175
|
-
};
|
|
45176
|
-
}
|
|
45177
|
-
const action = result.actionType;
|
|
45178
|
-
if (action === "block") {
|
|
45298
|
+
const result = await withDeadline(
|
|
45299
|
+
this.judgeAction(actionText, contextText, {
|
|
45300
|
+
toolName,
|
|
45301
|
+
toolArgsJson: argsJson,
|
|
45302
|
+
orgName: this.orgName,
|
|
45303
|
+
// Passed through unredacted — caller vouches for the contents.
|
|
45304
|
+
resolved: input.resolved
|
|
45305
|
+
}),
|
|
45306
|
+
this.decisionTimeoutMs
|
|
45307
|
+
);
|
|
45308
|
+
return this.decide(result);
|
|
45309
|
+
} catch (err) {
|
|
45310
|
+
if (err instanceof SignatureVerificationError) {
|
|
45311
|
+
this.failJudge(errorMessage(err), err);
|
|
45179
45312
|
return {
|
|
45180
45313
|
allow: false,
|
|
45181
|
-
verdict: "
|
|
45182
|
-
reason:
|
|
45183
|
-
toolCallId: result.toolCallId
|
|
45314
|
+
verdict: "ERROR",
|
|
45315
|
+
reason: `The judge's reply failed its signature check (${errorMessage(err)}), so it was not trusted.`
|
|
45184
45316
|
};
|
|
45185
45317
|
}
|
|
45186
|
-
|
|
45187
|
-
|
|
45188
|
-
|
|
45189
|
-
|
|
45190
|
-
|
|
45191
|
-
|
|
45192
|
-
|
|
45318
|
+
return this.failJudge(errorMessage(err), err);
|
|
45319
|
+
}
|
|
45320
|
+
}
|
|
45321
|
+
/**
|
|
45322
|
+
* The verify key for one call (R4, governor tier-2 review). A per-call key
|
|
45323
|
+
* may only narrow what the client accepts: on a client pinned to a judge
|
|
45324
|
+
* key it must be that same key, never a replacement; on an unpinned client
|
|
45325
|
+
* it adds the check.
|
|
45326
|
+
*/
|
|
45327
|
+
effectiveVerifyKey(perCall) {
|
|
45328
|
+
if (perCall === void 0) return this.verifyPubKey;
|
|
45329
|
+
const key3 = perCall.trim().toLowerCase();
|
|
45330
|
+
if (this.verifyPubKey !== void 0 && key3 !== this.verifyPubKey) {
|
|
45331
|
+
throw new SignatureVerificationError(
|
|
45332
|
+
"a per-call verifyPubKey cannot replace the judge key this client is pinned to"
|
|
45333
|
+
);
|
|
45334
|
+
}
|
|
45335
|
+
return key3;
|
|
45336
|
+
}
|
|
45337
|
+
/**
|
|
45338
|
+
* Collapse a judge result into a {@link Decision}. The most restrictive
|
|
45339
|
+
* signal wins (security review 2026-09-30, H1): `action_type`, `verdict`
|
|
45340
|
+
* and the canonical `allow` are each able to deny, and only a result where
|
|
45341
|
+
* all three agree may execute. A present `allow` / `decision.allow` that is
|
|
45342
|
+
* not exactly `true` is the judge declining to grant permission, so it
|
|
45343
|
+
* denies even under `failClosed: false` — that option relaxes *errors*, it
|
|
45344
|
+
* never overrides a refusal.
|
|
45345
|
+
*/
|
|
45346
|
+
decide(result) {
|
|
45347
|
+
const toolCallId = result.toolCallId;
|
|
45348
|
+
const deny = (verdict, reason) => ({
|
|
45349
|
+
allow: false,
|
|
45350
|
+
verdict,
|
|
45351
|
+
reason,
|
|
45352
|
+
toolCallId
|
|
45353
|
+
});
|
|
45354
|
+
const notGranted = "the judge response did not grant permission";
|
|
45355
|
+
const held = deny("HOLD", result.reason || "held for human confirmation");
|
|
45356
|
+
const blocked = deny("BLOCK", result.reason || notGranted);
|
|
45357
|
+
if (result.refusal === "BLOCK") return blocked;
|
|
45358
|
+
if (result.verdict === "No verdict") {
|
|
45359
|
+
if (result.actionType === "block") {
|
|
45360
|
+
return blocked;
|
|
45361
|
+
}
|
|
45362
|
+
if (result.actionType === "hold_for_user_confirm") {
|
|
45363
|
+
return held;
|
|
45364
|
+
}
|
|
45365
|
+
if (result.refusal === "HOLD") return held;
|
|
45366
|
+
if (result.status !== "logged") {
|
|
45367
|
+
return this.failJudge(
|
|
45368
|
+
`judge returned no verdict without an audit-tier marker (status: ${result.status || "absent"})`,
|
|
45369
|
+
void 0,
|
|
45370
|
+
toolCallId
|
|
45371
|
+
);
|
|
45193
45372
|
}
|
|
45194
|
-
if (
|
|
45195
|
-
|
|
45196
|
-
|
|
45197
|
-
|
|
45198
|
-
|
|
45199
|
-
reason: result.reason,
|
|
45200
|
-
toolCallId: result.toolCallId
|
|
45201
|
-
};
|
|
45202
|
-
}
|
|
45203
|
-
if (result.verdict === "BLOCK") {
|
|
45204
|
-
return {
|
|
45205
|
-
allow: false,
|
|
45206
|
-
verdict: "BLOCK",
|
|
45207
|
-
reason: result.reason,
|
|
45208
|
-
toolCallId: result.toolCallId
|
|
45209
|
-
};
|
|
45210
|
-
}
|
|
45373
|
+
if (this.requireEnforcement) {
|
|
45374
|
+
this.logger.warn?.(
|
|
45375
|
+
"[atbash] judge did not enforce this action (audit tier) and requireEnforcement is on: denied",
|
|
45376
|
+
{ toolCallId }
|
|
45377
|
+
);
|
|
45211
45378
|
return {
|
|
45212
|
-
allow:
|
|
45213
|
-
verdict: "
|
|
45214
|
-
reason:
|
|
45215
|
-
toolCallId
|
|
45379
|
+
allow: false,
|
|
45380
|
+
verdict: "ERROR",
|
|
45381
|
+
reason: "Atbash logged this action but did not check it (audit-tier plan), and this client requires a checked decision. Upgrade the plan, or set requireEnforcement: false for log-only mode.",
|
|
45382
|
+
toolCallId
|
|
45216
45383
|
};
|
|
45217
45384
|
}
|
|
45218
|
-
return
|
|
45219
|
-
|
|
45220
|
-
|
|
45221
|
-
|
|
45222
|
-
|
|
45223
|
-
|
|
45224
|
-
|
|
45385
|
+
if (result.allow !== true) return deny("BLOCK", notGranted);
|
|
45386
|
+
return {
|
|
45387
|
+
allow: true,
|
|
45388
|
+
verdict: "ALLOW",
|
|
45389
|
+
reason: result.reason || "audit tier \u2014 request logged on-chain, no AI enforcement",
|
|
45390
|
+
toolCallId
|
|
45391
|
+
};
|
|
45392
|
+
}
|
|
45393
|
+
const action = result.actionType;
|
|
45394
|
+
if (action === "block") {
|
|
45395
|
+
return deny("BLOCK", result.reason || "Blocked by your organisation's Atbash policy.");
|
|
45396
|
+
}
|
|
45397
|
+
if (action === "hold_for_user_confirm") {
|
|
45398
|
+
return held;
|
|
45225
45399
|
}
|
|
45400
|
+
if (result.refusal === "HOLD") return held;
|
|
45401
|
+
if (action === "allow") {
|
|
45402
|
+
if (result.verdict === "HOLD") return held;
|
|
45403
|
+
if (result.verdict === "BLOCK") return blocked;
|
|
45404
|
+
if (result.allow !== true) return deny("BLOCK", notGranted);
|
|
45405
|
+
return {
|
|
45406
|
+
allow: true,
|
|
45407
|
+
verdict: "ALLOW",
|
|
45408
|
+
reason: result.reason,
|
|
45409
|
+
toolCallId
|
|
45410
|
+
};
|
|
45411
|
+
}
|
|
45412
|
+
return this.failJudge(
|
|
45413
|
+
"unrecognized action_type from judge",
|
|
45414
|
+
void 0,
|
|
45415
|
+
toolCallId
|
|
45416
|
+
);
|
|
45226
45417
|
}
|
|
45227
45418
|
/**
|
|
45228
45419
|
* One exit for every judge failure.
|
|
@@ -45234,18 +45425,30 @@ var Atbash = class _Atbash {
|
|
|
45234
45425
|
*/
|
|
45235
45426
|
failJudge(reason, cause, toolCallId) {
|
|
45236
45427
|
const api2 = cause instanceof AtbashAPIError ? cause : null;
|
|
45428
|
+
const userReason = userFacingReason(reason, api2);
|
|
45237
45429
|
const status = api2 ? ` status=${api2.status || "no-response"}` : "";
|
|
45430
|
+
const detail = this.debug ? ` detail=${truncate(reason, 500)}` : "";
|
|
45238
45431
|
const body = this.debug && api2?.body ? ` body=${truncate(api2.body, 500)}` : "";
|
|
45239
45432
|
this.logger.warn?.(
|
|
45240
|
-
`[atbash] judge API failed \u2014${status} reason=${truncate(
|
|
45433
|
+
`[atbash] judge API failed \u2014${status} reason=${truncate(userReason, 300)}${detail}${body}`,
|
|
45241
45434
|
{
|
|
45242
|
-
reason,
|
|
45243
|
-
...api2 ? { status: api2.status
|
|
45435
|
+
reason: userReason,
|
|
45436
|
+
...api2 ? { status: api2.status } : {},
|
|
45437
|
+
...this.debug && api2 ? { body: api2.body, detail: reason } : {},
|
|
45244
45438
|
endpoint: this.endpoint,
|
|
45245
45439
|
...toolCallId ? { toolCallId } : {}
|
|
45246
45440
|
}
|
|
45247
45441
|
);
|
|
45248
|
-
return this.fail(
|
|
45442
|
+
return this.fail(userReason, toolCallId);
|
|
45443
|
+
}
|
|
45444
|
+
/**
|
|
45445
|
+
* The canonical permission for a response body under this client's
|
|
45446
|
+
* settings: {@link canonicalAllow}, minus the audit tier when
|
|
45447
|
+
* `requireEnforcement` is on. Never more permissive than `auditToolCall`.
|
|
45448
|
+
*/
|
|
45449
|
+
executable(data) {
|
|
45450
|
+
if (!canonicalAllow(data)) return false;
|
|
45451
|
+
return !(this.requireEnforcement && isAuditTierBody(data));
|
|
45249
45452
|
}
|
|
45250
45453
|
fail(reason, toolCallId) {
|
|
45251
45454
|
return { allow: !this.failClosed, verdict: "ERROR", reason, toolCallId };
|
|
@@ -45271,11 +45474,16 @@ var Atbash = class _Atbash {
|
|
|
45271
45474
|
this.authHeaders(brid)
|
|
45272
45475
|
);
|
|
45273
45476
|
await this.raiseIfError(resp);
|
|
45274
|
-
const
|
|
45477
|
+
const bytes = Buffer.from(await resp.arrayBuffer());
|
|
45478
|
+
if (this.verifyPubKey !== void 0) {
|
|
45479
|
+
verifyResponseSignature(bytes, resp, this.verifyPubKey);
|
|
45480
|
+
}
|
|
45481
|
+
const parsed = tryParseJson(bytes.toString("utf-8"));
|
|
45482
|
+
const data = isRecord2(parsed) ? parsed : {};
|
|
45275
45483
|
return {
|
|
45276
45484
|
status: normalizeStatus(data.status),
|
|
45277
45485
|
verdict: normalizeVerdict(data.verdict),
|
|
45278
|
-
allow:
|
|
45486
|
+
allow: this.executable(data),
|
|
45279
45487
|
reason: String(data.reason ?? ""),
|
|
45280
45488
|
judgmentId: String(data.judgmentId ?? judgmentId),
|
|
45281
45489
|
onChain: optBool(data.onChain),
|
|
@@ -45602,25 +45810,10 @@ var Atbash = class _Atbash {
|
|
|
45602
45810
|
* trips the server's replay window.
|
|
45603
45811
|
*/
|
|
45604
45812
|
getAuthBearer(brid) {
|
|
45605
|
-
|
|
45606
|
-
const now = Date.now();
|
|
45607
|
-
const cached = this._authBearers.get(chainBrid);
|
|
45608
|
-
if (cached && now - cached.issuedAt < 4 * 60 * 1e3) return cached.hex;
|
|
45609
|
-
const nonce = `auth-${now.toString(36)}-${randomHex(4)}`;
|
|
45610
|
-
const hex = native.signLogToolCall(
|
|
45611
|
-
nonce,
|
|
45612
|
-
`auth:${now}`,
|
|
45613
|
-
"",
|
|
45614
|
-
"auth-bearer",
|
|
45615
|
-
"",
|
|
45616
|
-
this.auth.privkey,
|
|
45617
|
-
chainBrid
|
|
45618
|
-
);
|
|
45619
|
-
this._authBearers.set(chainBrid, { hex, issuedAt: now });
|
|
45620
|
-
return hex;
|
|
45813
|
+
return agentAuthBearer(this.auth, brid ?? this.blockchainRid);
|
|
45621
45814
|
}
|
|
45622
45815
|
authHeaders(brid) {
|
|
45623
|
-
return
|
|
45816
|
+
return agentAuthHeaders(this.auth, brid ?? this.blockchainRid);
|
|
45624
45817
|
}
|
|
45625
45818
|
async riskEngineGet(action, params, brid) {
|
|
45626
45819
|
let resp;
|
|
@@ -45750,6 +45943,66 @@ var Atbash = class _Atbash {
|
|
|
45750
45943
|
return native.containsEvasionCharacters(text);
|
|
45751
45944
|
}
|
|
45752
45945
|
};
|
|
45946
|
+
function userFacingReason(reason, api2) {
|
|
45947
|
+
if (api2 && api2.status > 0) {
|
|
45948
|
+
const where = ` (HTTP ${api2.status}${api2.path ? ` from ${api2.path}` : ""})`;
|
|
45949
|
+
const b = api2.body.toLowerCase();
|
|
45950
|
+
if (b.includes("agent not registered")) {
|
|
45951
|
+
return `Agent not registered with Atbash: onboard it in the Atbash dashboard${where}.`;
|
|
45952
|
+
}
|
|
45953
|
+
if (b.includes("jailed")) {
|
|
45954
|
+
return `This agent is jailed; unjail it in the Atbash dashboard${where}.`;
|
|
45955
|
+
}
|
|
45956
|
+
if (b.includes("agent has no policy") || b.includes("no policy configured")) {
|
|
45957
|
+
return `This agent has no policy; attach one in the Atbash dashboard${where}.`;
|
|
45958
|
+
}
|
|
45959
|
+
if (b.includes("audit tier") || b.includes("verdict disabled") || b.includes("verdict not supported")) {
|
|
45960
|
+
return `The organisation's plan does not include checked decisions${where}.`;
|
|
45961
|
+
}
|
|
45962
|
+
return `The Atbash judge answered with an error${where}, so the action was not approved.`;
|
|
45963
|
+
}
|
|
45964
|
+
const text = api2 ? api2.body || reason : reason;
|
|
45965
|
+
return truncate(text.replace(/https?:\/\/[^\s)]+/g, "the judge"), 300);
|
|
45966
|
+
}
|
|
45967
|
+
var DEFAULT_DECISION_TIMEOUT_MS = 6e4;
|
|
45968
|
+
async function withDeadline(p, ms) {
|
|
45969
|
+
let timer;
|
|
45970
|
+
const deadline = new Promise((_, reject) => {
|
|
45971
|
+
timer = setTimeout(
|
|
45972
|
+
() => reject(
|
|
45973
|
+
new Error(
|
|
45974
|
+
`The judge did not give a decision within ${Math.round(ms / 1e3)} s, so the action was not approved. Check the network, or raise decisionTimeoutMs.`
|
|
45975
|
+
)
|
|
45976
|
+
),
|
|
45977
|
+
ms
|
|
45978
|
+
);
|
|
45979
|
+
});
|
|
45980
|
+
p.catch(() => void 0);
|
|
45981
|
+
try {
|
|
45982
|
+
return await Promise.race([p, deadline]);
|
|
45983
|
+
} finally {
|
|
45984
|
+
clearTimeout(timer);
|
|
45985
|
+
}
|
|
45986
|
+
}
|
|
45987
|
+
function verifyResponseSignature(bodyBytes, resp, verifyPubKey) {
|
|
45988
|
+
const sig = resp.headers.get("X-Atbash-Signature");
|
|
45989
|
+
if (!sig) {
|
|
45990
|
+
throw new SignatureVerificationError("missing X-Atbash-Signature header");
|
|
45991
|
+
}
|
|
45992
|
+
let ok;
|
|
45993
|
+
try {
|
|
45994
|
+
ok = native.verifySignature(bodyBytes, sig, verifyPubKey);
|
|
45995
|
+
} catch (err) {
|
|
45996
|
+
throw new SignatureVerificationError(
|
|
45997
|
+
`signature verification threw: ${errorMessage(err)}`
|
|
45998
|
+
);
|
|
45999
|
+
}
|
|
46000
|
+
if (!ok) {
|
|
46001
|
+
throw new SignatureVerificationError(
|
|
46002
|
+
"signature does not verify against configured verifyPubKey"
|
|
46003
|
+
);
|
|
46004
|
+
}
|
|
46005
|
+
}
|
|
45753
46006
|
function baseToolCall(raw2) {
|
|
45754
46007
|
return {
|
|
45755
46008
|
toolCallId: String(raw2.tool_call_id ?? ""),
|
|
@@ -45951,22 +46204,6 @@ function bootSyncFailureLine(cause) {
|
|
|
45951
46204
|
);
|
|
45952
46205
|
}
|
|
45953
46206
|
|
|
45954
|
-
// src-ts/memory/_gtv_bytes.ts
|
|
45955
|
-
init_define_ATBASH_CHROMIA_NODE_URLS();
|
|
45956
|
-
init_define_ATBASH_PRIVATE_NODE_URLS();
|
|
45957
|
-
function gtvToFfiSafe(value) {
|
|
45958
|
-
if (value instanceof Uint8Array) return Buffer.from(value).toString("hex");
|
|
45959
|
-
if (Array.isArray(value)) return value.map(gtvToFfiSafe);
|
|
45960
|
-
if (value !== null && typeof value === "object") {
|
|
45961
|
-
const out = {};
|
|
45962
|
-
for (const [key3, val] of Object.entries(value)) {
|
|
45963
|
-
out[key3] = gtvToFfiSafe(val);
|
|
45964
|
-
}
|
|
45965
|
-
return out;
|
|
45966
|
-
}
|
|
45967
|
-
return value;
|
|
45968
|
-
}
|
|
45969
|
-
|
|
45970
46207
|
// src-ts/memory/index.ts
|
|
45971
46208
|
init_define_ATBASH_CHROMIA_NODE_URLS();
|
|
45972
46209
|
init_define_ATBASH_PRIVATE_NODE_URLS();
|
|
@@ -46334,7 +46571,6 @@ export {
|
|
|
46334
46571
|
getMemoryHistory,
|
|
46335
46572
|
getRecentAgentMemory,
|
|
46336
46573
|
getRollbackHistory,
|
|
46337
|
-
gtvToFfiSafe,
|
|
46338
46574
|
guardMemoryWrite,
|
|
46339
46575
|
isEnvelope,
|
|
46340
46576
|
isValidPrivateKey2 as isValidPrivateKey,
|