@atbash/sdk 0.10.5-dev.0 → 0.10.7-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 +74 -1
- package/dist/browser.mjs +266 -30
- package/dist/browser.mjs.map +1 -1
- package/dist/index.d.mts +74 -1
- package/dist/index.d.ts +74 -1
- package/dist/index.js +156 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +155 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/dist/index.mjs
CHANGED
|
@@ -3051,19 +3051,32 @@ var HttpClient = class {
|
|
|
3051
3051
|
};
|
|
3052
3052
|
|
|
3053
3053
|
// src-ts/keyLoader.ts
|
|
3054
|
-
import { readFileSync } from "fs";
|
|
3054
|
+
import { existsSync, readFileSync } from "fs";
|
|
3055
|
+
|
|
3056
|
+
// src-ts/key-path.ts
|
|
3055
3057
|
import { homedir } from "os";
|
|
3056
3058
|
import { join } from "path";
|
|
3057
|
-
var
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
return join(home, DEFAULT_KEY_PATH_REL);
|
|
3059
|
+
var KEY_DIR_REL = ".config/atbash";
|
|
3060
|
+
var KEY_FILENAMES = ["guard-client-key", "atbash-client-key"];
|
|
3061
|
+
function home() {
|
|
3062
|
+
return process.env.HOME || homedir() || "";
|
|
3062
3063
|
}
|
|
3063
3064
|
function expandHome(p) {
|
|
3064
3065
|
if (!p.startsWith("~/")) return p;
|
|
3065
|
-
|
|
3066
|
-
|
|
3066
|
+
return join(home(), p.slice(2));
|
|
3067
|
+
}
|
|
3068
|
+
function keyPathCandidates() {
|
|
3069
|
+
return KEY_FILENAMES.map((name2) => join(home(), KEY_DIR_REL, name2));
|
|
3070
|
+
}
|
|
3071
|
+
function chooseKeyPath(input, exists) {
|
|
3072
|
+
if (input) return expandHome(input);
|
|
3073
|
+
const candidates = keyPathCandidates();
|
|
3074
|
+
return candidates.find(exists) ?? candidates[0];
|
|
3075
|
+
}
|
|
3076
|
+
|
|
3077
|
+
// src-ts/keyLoader.ts
|
|
3078
|
+
function resolveKeyPath(input) {
|
|
3079
|
+
return chooseKeyPath(input, existsSync);
|
|
3067
3080
|
}
|
|
3068
3081
|
function readKeyFile(keyPath) {
|
|
3069
3082
|
const content = String(readFileSync(keyPath, "utf8") || "").trim();
|
|
@@ -3093,6 +3106,12 @@ function readKeyFile(keyPath) {
|
|
|
3093
3106
|
}
|
|
3094
3107
|
function loadAgentFromFile(keyPath) {
|
|
3095
3108
|
const resolved = resolveKeyPath(keyPath);
|
|
3109
|
+
if (!existsSync(resolved)) {
|
|
3110
|
+
const looked = keyPath ? [resolved] : keyPathCandidates();
|
|
3111
|
+
throw new Error(
|
|
3112
|
+
`atbash key file not found. Looked for: ${looked.join(", ")}`
|
|
3113
|
+
);
|
|
3114
|
+
}
|
|
3096
3115
|
const { privKey } = readKeyFile(resolved);
|
|
3097
3116
|
return native.loadAgent(privKey);
|
|
3098
3117
|
}
|
|
@@ -3138,8 +3157,8 @@ var durationHistogram = null;
|
|
|
3138
3157
|
var defaultSource = "sdk";
|
|
3139
3158
|
function isTelemetryOptedOut() {
|
|
3140
3159
|
try {
|
|
3141
|
-
const
|
|
3142
|
-
const filePath = join2(
|
|
3160
|
+
const home2 = process.env.HOME || homedir2() || "";
|
|
3161
|
+
const filePath = join2(home2, ".config", "atbash", "telemetry.json");
|
|
3143
3162
|
const raw2 = readFileSync2(filePath, "utf-8").trim();
|
|
3144
3163
|
if (!raw2) return false;
|
|
3145
3164
|
const config2 = JSON.parse(raw2);
|
|
@@ -3217,7 +3236,7 @@ async function shutdownTelemetry() {
|
|
|
3217
3236
|
// src-ts/userConfig.ts
|
|
3218
3237
|
import {
|
|
3219
3238
|
chmodSync,
|
|
3220
|
-
existsSync,
|
|
3239
|
+
existsSync as existsSync2,
|
|
3221
3240
|
mkdirSync,
|
|
3222
3241
|
readFileSync as readFileSync3,
|
|
3223
3242
|
writeFileSync
|
|
@@ -3230,11 +3249,12 @@ var ENV_MAP = {
|
|
|
3230
3249
|
judgeEndpoint: "ATBASH_ENDPOINT",
|
|
3231
3250
|
blockchainRid: "ATBASH_BLOCKCHAIN_RID",
|
|
3232
3251
|
provider: "ATBASH_PROVIDER",
|
|
3233
|
-
providerModel: "ATBASH_PROVIDER_MODEL"
|
|
3252
|
+
providerModel: "ATBASH_PROVIDER_MODEL",
|
|
3253
|
+
debug: "ATBASH_DEBUG"
|
|
3234
3254
|
};
|
|
3235
3255
|
function getConfigDir() {
|
|
3236
|
-
const
|
|
3237
|
-
return join3(
|
|
3256
|
+
const home2 = process.env.HOME || homedir3() || "";
|
|
3257
|
+
return join3(home2, ".config", "atbash");
|
|
3238
3258
|
}
|
|
3239
3259
|
function getConfigPath() {
|
|
3240
3260
|
return join3(getConfigDir(), "config.json");
|
|
@@ -3242,7 +3262,7 @@ function getConfigPath() {
|
|
|
3242
3262
|
function loadUserConfig() {
|
|
3243
3263
|
try {
|
|
3244
3264
|
const p = getConfigPath();
|
|
3245
|
-
if (!
|
|
3265
|
+
if (!existsSync2(p)) return {};
|
|
3246
3266
|
const raw2 = readFileSync3(p, "utf-8").trim();
|
|
3247
3267
|
if (!raw2) return {};
|
|
3248
3268
|
return JSON.parse(raw2);
|
|
@@ -3253,7 +3273,7 @@ function loadUserConfig() {
|
|
|
3253
3273
|
}
|
|
3254
3274
|
function saveUserConfig(config2) {
|
|
3255
3275
|
const dir = getConfigDir();
|
|
3256
|
-
if (!
|
|
3276
|
+
if (!existsSync2(dir)) {
|
|
3257
3277
|
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
3258
3278
|
}
|
|
3259
3279
|
const filePath = getConfigPath();
|
|
@@ -3293,6 +3313,7 @@ var Atbash = class _Atbash {
|
|
|
3293
3313
|
failClosed;
|
|
3294
3314
|
/** Org key learned from the last agent-exists check, for this agent only. */
|
|
3295
3315
|
_orgKeyFromChain = null;
|
|
3316
|
+
debug;
|
|
3296
3317
|
logger;
|
|
3297
3318
|
http;
|
|
3298
3319
|
/**
|
|
@@ -3306,6 +3327,8 @@ var Atbash = class _Atbash {
|
|
|
3306
3327
|
* server-side replay protection windows never expire it mid-session.
|
|
3307
3328
|
*/
|
|
3308
3329
|
_authBearer = null;
|
|
3330
|
+
/** Guards `logEnvironmentOnce` — hosts construct several clients. */
|
|
3331
|
+
static environmentLogged = false;
|
|
3309
3332
|
constructor(privkey, options = {}) {
|
|
3310
3333
|
this.auth = native.loadAgent(privkey);
|
|
3311
3334
|
this.endpoint = (options.endpoint ?? DEFAULT_ENDPOINT).replace(/\/+$/, "") || DEFAULT_ENDPOINT;
|
|
@@ -3315,8 +3338,10 @@ var Atbash = class _Atbash {
|
|
|
3315
3338
|
this.verifyPubKey = options.verifyPubKey;
|
|
3316
3339
|
this.orgEncryptionPubKey = options.orgEncryptionPubKey;
|
|
3317
3340
|
this.failClosed = options.failClosed !== false;
|
|
3341
|
+
this.debug = options.debug === true;
|
|
3318
3342
|
this.logger = options.logger ?? {};
|
|
3319
3343
|
this.http = new HttpClient(this.endpoint, options.timeoutMs ?? 3e4);
|
|
3344
|
+
this.logEnvironmentOnce();
|
|
3320
3345
|
if (this.endpoint !== DEFAULT_ENDPOINT) {
|
|
3321
3346
|
this.logger.warn?.("[atbash] running on non-default judge endpoint", {
|
|
3322
3347
|
endpoint: this.endpoint,
|
|
@@ -3324,6 +3349,31 @@ var Atbash = class _Atbash {
|
|
|
3324
3349
|
});
|
|
3325
3350
|
}
|
|
3326
3351
|
}
|
|
3352
|
+
/**
|
|
3353
|
+
* Say which environment this build talks to, once per process.
|
|
3354
|
+
*
|
|
3355
|
+
* The endpoint and both chain RIDs are compiled in, chosen by the npm tag,
|
|
3356
|
+
* and no configuration repoints a released build. So installing the build
|
|
3357
|
+
* for the wrong environment is invisible: the plugin loads, the hook fires,
|
|
3358
|
+
* and every judge call fails because the agent does not exist on the chain
|
|
3359
|
+
* this build targets. Organisation names are not unique across environments
|
|
3360
|
+
* either, so an org resolving is not evidence the build is right.
|
|
3361
|
+
*/
|
|
3362
|
+
logEnvironmentOnce() {
|
|
3363
|
+
if (_Atbash.environmentLogged) return;
|
|
3364
|
+
_Atbash.environmentLogged = true;
|
|
3365
|
+
const brief = (rid) => rid ? `${rid.slice(0, 8)}\u2026` : "(unset)";
|
|
3366
|
+
this.logger.info?.(
|
|
3367
|
+
`[atbash] environment \u2014 judge=${this.endpoint} publicChain=${brief(native.DEFAULT_BLOCKCHAIN_RID)} privateChain=${brief(native.DEFAULT_PRIVATE_BLOCKCHAIN_RID)} activeChain=${brief(this.blockchainRid)} responseSignatureCheck=${this.verifyPubKey ? "on" : "off"}`,
|
|
3368
|
+
{
|
|
3369
|
+
judgeEndpoint: this.endpoint,
|
|
3370
|
+
publicBlockchainRid: native.DEFAULT_BLOCKCHAIN_RID,
|
|
3371
|
+
privateBlockchainRid: native.DEFAULT_PRIVATE_BLOCKCHAIN_RID,
|
|
3372
|
+
activeBlockchainRid: this.blockchainRid,
|
|
3373
|
+
responseSignatureCheck: Boolean(this.verifyPubKey)
|
|
3374
|
+
}
|
|
3375
|
+
);
|
|
3376
|
+
}
|
|
3327
3377
|
/**
|
|
3328
3378
|
* Construct from resolved config: explicit overrides → env vars → the
|
|
3329
3379
|
* `~/.config/atbash/config.json` file (see userConfig.resolve). The private
|
|
@@ -3347,6 +3397,9 @@ var Atbash = class _Atbash {
|
|
|
3347
3397
|
orgName: options.orgName,
|
|
3348
3398
|
verifyPubKey: validated.verifyPubKey ?? void 0,
|
|
3349
3399
|
failClosed: options.failClosed,
|
|
3400
|
+
// ATBASH_DEBUG lets an operator turn diagnostics on without editing a
|
|
3401
|
+
// host's plugin config, which is usually the harder half.
|
|
3402
|
+
debug: options.debug ?? /^(1|true|yes)$/i.test(resolve("debug")),
|
|
3350
3403
|
logger: options.logger
|
|
3351
3404
|
});
|
|
3352
3405
|
}
|
|
@@ -3446,11 +3499,15 @@ var Atbash = class _Atbash {
|
|
|
3446
3499
|
* response bytes via the Rust core's `verifySignature`.
|
|
3447
3500
|
*/
|
|
3448
3501
|
async judgeAction(action, context = "", options = {}) {
|
|
3449
|
-
return this.track(
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3502
|
+
return this.track("judgeAction", this.auth.pubkey, async () => {
|
|
3503
|
+
try {
|
|
3504
|
+
return await this._judgeAction(action, context, options);
|
|
3505
|
+
} catch (err) {
|
|
3506
|
+
if (!isEncryptionStateMismatch(err)) throw err;
|
|
3507
|
+
this._orgKeyFromChain = null;
|
|
3508
|
+
return await this._judgeAction(action, context, options);
|
|
3509
|
+
}
|
|
3510
|
+
});
|
|
3454
3511
|
}
|
|
3455
3512
|
async _judgeAction(action, context, options) {
|
|
3456
3513
|
if (!action?.trim()) {
|
|
@@ -3588,8 +3645,9 @@ var Atbash = class _Atbash {
|
|
|
3588
3645
|
});
|
|
3589
3646
|
if (result.verdict === "No verdict") {
|
|
3590
3647
|
if (result.status !== "logged") {
|
|
3591
|
-
return this.
|
|
3648
|
+
return this.failJudge(
|
|
3592
3649
|
`judge returned no verdict without an audit-tier marker (status: ${result.status || "absent"})`,
|
|
3650
|
+
void 0,
|
|
3593
3651
|
result.toolCallId
|
|
3594
3652
|
);
|
|
3595
3653
|
}
|
|
@@ -3641,16 +3699,38 @@ var Atbash = class _Atbash {
|
|
|
3641
3699
|
toolCallId: result.toolCallId
|
|
3642
3700
|
};
|
|
3643
3701
|
}
|
|
3644
|
-
return this.
|
|
3702
|
+
return this.failJudge(
|
|
3645
3703
|
"unrecognized action_type from judge",
|
|
3704
|
+
void 0,
|
|
3646
3705
|
result.toolCallId
|
|
3647
3706
|
);
|
|
3648
3707
|
} catch (err) {
|
|
3649
|
-
|
|
3650
|
-
this.logger.warn?.("[atbash] judge API failed", { reason: message });
|
|
3651
|
-
return this.fail(message);
|
|
3708
|
+
return this.failJudge(errorMessage(err), err);
|
|
3652
3709
|
}
|
|
3653
3710
|
}
|
|
3711
|
+
/**
|
|
3712
|
+
* One exit for every judge failure.
|
|
3713
|
+
*
|
|
3714
|
+
* Status and reason go in the *message*, not only in the meta object: hosts
|
|
3715
|
+
* print the message and drop the meta, which is why this read as a bare
|
|
3716
|
+
* "judge API failed" while the judge was answering with a precise reason.
|
|
3717
|
+
* The response body follows only under `debug`, since it can echo the action.
|
|
3718
|
+
*/
|
|
3719
|
+
failJudge(reason, cause, toolCallId) {
|
|
3720
|
+
const api2 = cause instanceof AtbashAPIError ? cause : null;
|
|
3721
|
+
const status = api2 ? ` status=${api2.status || "no-response"}` : "";
|
|
3722
|
+
const body = this.debug && api2?.body ? ` body=${truncate(api2.body, 500)}` : "";
|
|
3723
|
+
this.logger.warn?.(
|
|
3724
|
+
`[atbash] judge API failed \u2014${status} reason=${truncate(reason, 300)}${body}`,
|
|
3725
|
+
{
|
|
3726
|
+
reason,
|
|
3727
|
+
...api2 ? { status: api2.status, body: api2.body } : {},
|
|
3728
|
+
endpoint: this.endpoint,
|
|
3729
|
+
...toolCallId ? { toolCallId } : {}
|
|
3730
|
+
}
|
|
3731
|
+
);
|
|
3732
|
+
return this.fail(reason, toolCallId);
|
|
3733
|
+
}
|
|
3654
3734
|
fail(reason, toolCallId) {
|
|
3655
3735
|
return { allow: !this.failClosed, verdict: "ERROR", reason, toolCallId };
|
|
3656
3736
|
}
|
|
@@ -4124,6 +4204,10 @@ async function safeText(resp) {
|
|
|
4124
4204
|
function errorMessage(err) {
|
|
4125
4205
|
return err instanceof Error ? err.message : String(err);
|
|
4126
4206
|
}
|
|
4207
|
+
function isEncryptionStateMismatch(err) {
|
|
4208
|
+
const msg = errorMessage(err);
|
|
4209
|
+
return msg.includes("must be a valid encryption envelope") || msg.includes("must be plaintext");
|
|
4210
|
+
}
|
|
4127
4211
|
function stringifyArgs(args) {
|
|
4128
4212
|
if (args === null || args === void 0) return "";
|
|
4129
4213
|
if (typeof args === "string") return args;
|
|
@@ -4134,9 +4218,9 @@ function stringifyArgs(args) {
|
|
|
4134
4218
|
}
|
|
4135
4219
|
}
|
|
4136
4220
|
var MAX_ACTION_LEN = 4e3;
|
|
4137
|
-
function truncate(text) {
|
|
4138
|
-
if (text.length <=
|
|
4139
|
-
return text.slice(0,
|
|
4221
|
+
function truncate(text, limit = MAX_ACTION_LEN) {
|
|
4222
|
+
if (text.length <= limit) return text;
|
|
4223
|
+
return text.slice(0, limit) + "\u2026";
|
|
4140
4224
|
}
|
|
4141
4225
|
|
|
4142
4226
|
// src-ts/redact.ts
|
|
@@ -4179,6 +4263,17 @@ function verifyJudgeResponseSignature(bodyBytes, signatureHex, pubKeyHex) {
|
|
|
4179
4263
|
};
|
|
4180
4264
|
}
|
|
4181
4265
|
|
|
4266
|
+
// src-ts/memory/boot-sync-message.ts
|
|
4267
|
+
var BOOT_SYNC_HINT = "check the chain endpoint and orgName if the cause above does not explain it";
|
|
4268
|
+
function bootSyncFailureLine(cause) {
|
|
4269
|
+
const reason = cause instanceof Error ? cause.message : String(cause);
|
|
4270
|
+
const trimmed = reason.trim();
|
|
4271
|
+
return trimmed ? `[atbash] boot memory sync failed: ${trimmed}` : (
|
|
4272
|
+
// No cause to show: fall back to the advice rather than a bare colon.
|
|
4273
|
+
`[atbash] boot memory sync failed \u2014 ${BOOT_SYNC_HINT}`
|
|
4274
|
+
);
|
|
4275
|
+
}
|
|
4276
|
+
|
|
4182
4277
|
// src-ts/memory/crypto.ts
|
|
4183
4278
|
async function deriveMemoryKey(privkey) {
|
|
4184
4279
|
return native.deriveMemoryKey(privkey);
|
|
@@ -4204,6 +4299,19 @@ async function scanMemory(entry, auth, opts) {
|
|
|
4204
4299
|
toolArgsJson: opts?.toolArgsJson ?? JSON.stringify({ key: entry.key, source: entry.source }),
|
|
4205
4300
|
mode: "memory-scan"
|
|
4206
4301
|
});
|
|
4302
|
+
const knownAction = result.actionType === "allow" || result.actionType === "block" || result.actionType === "hold_for_user_confirm";
|
|
4303
|
+
const missingVerdict = result.verdict === "No verdict" && result.status !== "logged";
|
|
4304
|
+
const unknownAction = result.actionType !== "" && !knownAction;
|
|
4305
|
+
if (missingVerdict || unknownAction) {
|
|
4306
|
+
return {
|
|
4307
|
+
safe: false,
|
|
4308
|
+
verdict: "red",
|
|
4309
|
+
reason: unknownAction ? `judge returned unrecognised action_type "${result.actionType}"` : "judge returned no verdict",
|
|
4310
|
+
confidence: result.confidence,
|
|
4311
|
+
score: native.defaultScoreForVerdict("red"),
|
|
4312
|
+
toolCallId: result.toolCallId
|
|
4313
|
+
};
|
|
4314
|
+
}
|
|
4207
4315
|
const verdict = native.mapVerdict(
|
|
4208
4316
|
result.actionType,
|
|
4209
4317
|
result.confidence,
|
|
@@ -42354,6 +42462,10 @@ var index = /* @__PURE__ */ getDefaultExportFromCjs(builtExports);
|
|
|
42354
42462
|
|
|
42355
42463
|
// src-ts/memory/chain.ts
|
|
42356
42464
|
var { createClient, encryption: encryption2, newSignatureProvider: newSignatureProvider2, Buffer: PolyBuffer } = index;
|
|
42465
|
+
var FAILOVER_CONFIG = {
|
|
42466
|
+
strategy: "tryNextOnError",
|
|
42467
|
+
attemptsPerEndpoint: 1
|
|
42468
|
+
};
|
|
42357
42469
|
function toGtxBytes(bytes) {
|
|
42358
42470
|
return PolyBuffer.from(new Uint8Array(bytes));
|
|
42359
42471
|
}
|
|
@@ -42383,7 +42495,11 @@ function materializeChain(chainOpts) {
|
|
|
42383
42495
|
}
|
|
42384
42496
|
async function buildChainClient(chainOpts) {
|
|
42385
42497
|
const { nodeUrls, blockchainRid } = materializeChain(chainOpts);
|
|
42386
|
-
return createClient({
|
|
42498
|
+
return createClient({
|
|
42499
|
+
nodeUrlPool: [...nodeUrls],
|
|
42500
|
+
blockchainRid,
|
|
42501
|
+
failOverConfig: FAILOVER_CONFIG
|
|
42502
|
+
});
|
|
42387
42503
|
}
|
|
42388
42504
|
function buildSigner(auth) {
|
|
42389
42505
|
const privKeyBuf = Buffer.from(auth.privkey, "hex");
|
|
@@ -42821,7 +42937,10 @@ var MemoryGuardManager = class {
|
|
|
42821
42937
|
await this.pointerStore.set(this.agentPubkeyHex, result.pointer);
|
|
42822
42938
|
} catch (err) {
|
|
42823
42939
|
const msg = err instanceof Error ? err.message : String(err);
|
|
42824
|
-
this.logger.warn(
|
|
42940
|
+
this.logger.warn(bootSyncFailureLine(err), {
|
|
42941
|
+
error: msg,
|
|
42942
|
+
hint: BOOT_SYNC_HINT
|
|
42943
|
+
});
|
|
42825
42944
|
}
|
|
42826
42945
|
}
|
|
42827
42946
|
/**
|
|
@@ -43049,6 +43168,7 @@ function diffMemorySnapshots(before, after) {
|
|
|
43049
43168
|
export {
|
|
43050
43169
|
Atbash,
|
|
43051
43170
|
AtbashAPIError,
|
|
43171
|
+
BOOT_SYNC_HINT,
|
|
43052
43172
|
DEFAULT_BLOCKCHAIN_RID,
|
|
43053
43173
|
DEFAULT_CHROMIA_NODE_URLS,
|
|
43054
43174
|
DEFAULT_ENDPOINT,
|
|
@@ -43056,11 +43176,14 @@ export {
|
|
|
43056
43176
|
DEFAULT_MEMORY_READ_TOOL_NAMES,
|
|
43057
43177
|
DEFAULT_MEMORY_WRITE_TOOL_NAMES,
|
|
43058
43178
|
EciesDomain,
|
|
43179
|
+
KEY_FILENAMES,
|
|
43059
43180
|
MemoryGuardManager,
|
|
43060
43181
|
MemoryIntegrityError,
|
|
43061
43182
|
PointerStore,
|
|
43062
43183
|
SignatureVerificationError,
|
|
43184
|
+
bootSyncFailureLine,
|
|
43063
43185
|
buildAllowedJudgeHosts,
|
|
43186
|
+
chooseKeyPath,
|
|
43064
43187
|
claimHashHex,
|
|
43065
43188
|
classifyMemoryRead,
|
|
43066
43189
|
classifyMemoryWrite,
|
|
@@ -43095,6 +43218,7 @@ export {
|
|
|
43095
43218
|
isEnvelope,
|
|
43096
43219
|
isValidPrivateKey,
|
|
43097
43220
|
keyFingerprintOf,
|
|
43221
|
+
keyPathCandidates,
|
|
43098
43222
|
loadAgent,
|
|
43099
43223
|
loadAgentFromFile,
|
|
43100
43224
|
loadUserConfig,
|