@atbash/sdk 0.10.0-dev.0 → 0.10.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.
@@ -1,3 +1,5 @@
1
+ import { Logger } from '@opentelemetry/api-logs';
2
+
1
3
  type JudgeEndpointConfig = {
2
4
  policy?: "default";
3
5
  endpoint?: string;
@@ -11,8 +13,34 @@ interface ValidatedEndpoint {
11
13
  policy: "default" | "self-hosted";
12
14
  verifyPubKey: string | null;
13
15
  }
16
+ /**
17
+ * Builds the trusted judge host set.
18
+ *
19
+ * The compiled-in default is always trusted: a `prod` build resolves it to
20
+ * atbash.ai, a dev build to whatever DEV_ENDPOINT was set at build time. No
21
+ * dev host is spelled out in source, and dev builds still validate their own
22
+ * default.
23
+ *
24
+ * Exported for tests only. The set is a build-time value and is deliberately
25
+ * never read from the process environment — an env var would let anyone widen
26
+ * the allowlist of an already-shipped artifact, which is the silent-redirection
27
+ * attack the allowlist exists to prevent (F-003). Asserting that requires
28
+ * calling this with the environment set, so it cannot stay module-private.
29
+ *
30
+ * @internal
31
+ */
32
+ declare function buildAllowedJudgeHosts(): ReadonlySet<string>;
14
33
  declare function validateJudgeEndpoint(judge?: JudgeEndpointConfig): ValidatedEndpoint;
15
34
 
35
+ interface ChainConfig {
36
+ readonly network: Network;
37
+ readonly blockchainRid: string;
38
+ readonly nodeUrls: readonly string[];
39
+ }
40
+ declare const PUBLIC_CHAIN: ChainConfig;
41
+ declare const PRIVATE_CHAIN: ChainConfig;
42
+ declare function chainForNetwork(network: Network): ChainConfig;
43
+
16
44
  /**
17
45
  * User-facing types. Two groups:
18
46
  * - Core types — the exact shapes the Rust core emits across the NAPI
@@ -110,14 +138,24 @@ interface MemoryScanOptions {
110
138
  type Network = "public" | "private";
111
139
  /**
112
140
  * Per-call chain overrides. Pass exactly what you need to override —
113
- * `network` selects between the SDK's known chains, `blockchainRid` /
114
- * `nodeUrls` pin a fully custom chain. Pass none of these to fall back
115
- * to the SDK's defaults (or to org-resolved chain when `orgName` is set).
141
+ * `network` selects between the SDK's known chains, `blockchainRid` pins a
142
+ * custom one, and `endpoint` names the Dashboard the calls travel over. Pass
143
+ * none of these to fall back to the SDK's defaults (or to the org-resolved
144
+ * chain when `orgName` is set).
116
145
  */
117
146
  interface ChainOpts {
118
147
  network?: Network;
119
148
  blockchainRid?: string;
149
+ /**
150
+ * No longer dialled: memory reaches the chain through the Dashboard. Kept
151
+ * because callers and the other bindings still carry it as chain config.
152
+ */
120
153
  nodeUrls?: readonly string[];
154
+ /**
155
+ * Dashboard base URL. Memory reaches the chain through it, so this is where
156
+ * those requests go; defaults to the endpoint compiled into the core.
157
+ */
158
+ endpoint?: string;
121
159
  }
122
160
  /** Canonical verdict after normalization. */
123
161
  type Verdict = "ALLOW" | "HOLD" | "BLOCK" | "No verdict";
@@ -143,6 +181,11 @@ interface LogToolCallResult {
143
181
  }
144
182
  interface JudgeResult {
145
183
  verdict: Verdict;
184
+ /**
185
+ * Canonical executable permission, computed by `canonicalAllow` — see that
186
+ * function for the rule. Never more permissive than `auditToolCall`.
187
+ */
188
+ allow: boolean;
146
189
  actionType: string;
147
190
  reason: string;
148
191
  confidence: number;
@@ -167,10 +210,17 @@ interface JudgeResult {
167
210
  * missing" — never infer the former from a null verdict alone.
168
211
  */
169
212
  status: string;
213
+ /**
214
+ * The judge's explicit refusal in the raw body (`explicitRefusal`): "BLOCK",
215
+ * "HOLD", or "" when it neither refused nor is a known refusal shape.
216
+ */
217
+ refusal?: "" | "BLOCK" | "HOLD";
170
218
  }
171
219
  interface JudgmentStatus {
172
220
  status: JudgmentState;
173
221
  verdict: Verdict;
222
+ /** Same canonical executable permission as {@link JudgeResult.allow}. */
223
+ allow: boolean;
174
224
  reason: string;
175
225
  judgmentId: string;
176
226
  onChain?: boolean;
@@ -257,6 +307,13 @@ interface AgentPolicy {
257
307
  isCustom: boolean;
258
308
  defaultPolicy: string;
259
309
  }
310
+ /** Options for agent metadata and policy lookups. */
311
+ interface AgentLookupOptions {
312
+ /** Resolve the agent's network from this organization's active network. */
313
+ orgName?: string;
314
+ /** Explicit per-call chain override. `network` selects the dashboard chain. */
315
+ chainOpts?: ChainOpts;
316
+ }
260
317
  /** Optional structured logger. */
261
318
  interface AtbashLogger {
262
319
  info?(...args: unknown[]): void;
@@ -265,8 +322,32 @@ interface AtbashLogger {
265
322
  /** Options accepted by the `Atbash` constructor. */
266
323
  interface AtbashOptions {
267
324
  endpoint?: string;
325
+ /**
326
+ * Overall deadline for one `auditToolCall` decision, across every request
327
+ * it makes (org lookup, exists check, judge). Default 60 000 ms. When it
328
+ * passes the decision is an ERROR (denied unless `failClosed` is false).
329
+ * `timeoutMs` still bounds each single request.
330
+ */
331
+ decisionTimeoutMs?: number;
268
332
  timeoutMs?: number;
333
+ /**
334
+ * Full chain override — BRID + nodeUrls in one object. Wins over every
335
+ * other chain selector. Prefer this over paired `nodeUrls`/`blockchainRid`
336
+ * for anything but backwards compatibility.
337
+ */
338
+ chain?: ChainConfig;
339
+ /**
340
+ * Preset chain selector — `"public"` or `"private"`. Resolves to the
341
+ * matching `ChainConfig` via `chainForNetwork()`. Overridden by `chain`,
342
+ * overrides env `ATBASH_DEFAULT_CHAIN_NETWORK` and the config file.
343
+ */
344
+ network?: Network;
345
+ /**
346
+ * Explicit node URLs. Must be paired with `blockchainRid`. Passing one
347
+ * without the other throws — a BRID/nodes mismatch 404s every request.
348
+ */
269
349
  nodeUrls?: readonly string[];
350
+ /** Explicit BRID. Must be paired with `nodeUrls`. See {@link nodeUrls}. */
270
351
  blockchainRid?: string;
271
352
  /**
272
353
  * Default org name. When set, `judgeAction` / `auditToolCall` resolve
@@ -293,6 +374,22 @@ interface AtbashOptions {
293
374
  orgEncryptionPubKey?: string;
294
375
  /** When true (default), `auditToolCall` denies on any error. */
295
376
  failClosed?: boolean;
377
+ /**
378
+ * When true (the default), an answer that carries no enforcement decision
379
+ * (the audit tier's `{status:"logged"}` with no verdict) is refused:
380
+ * `auditToolCall` returns `{allow:false, verdict:"ERROR"}` whatever
381
+ * `failClosed` says, and `judgeAction().allow` / `getJudgmentStatus().allow`
382
+ * are false. Set it to `false` only to run deliberately in log-only mode
383
+ * (security review 2026-09-30, H6: an unsigned marker switched enforcement
384
+ * off).
385
+ */
386
+ requireEnforcement?: boolean;
387
+ /**
388
+ * Verbose diagnostics. Off by default. When on, a failed judge call also
389
+ * logs the response body, which is the difference between "judge API failed"
390
+ * and knowing why it failed. Opt-in because that body can echo the action.
391
+ */
392
+ debug?: boolean;
296
393
  logger?: AtbashLogger;
297
394
  }
298
395
  /** Canonical decision returned by `auditToolCall`. */
@@ -327,20 +424,28 @@ interface FromConfigOptions {
327
424
  keyPath?: string;
328
425
  /** Judge endpoint config — validated against the allowlist / self-hosted policy. */
329
426
  judge?: JudgeEndpointConfig;
427
+ /** See {@link AtbashOptions.chain}. */
428
+ chain?: ChainConfig;
429
+ /** See {@link AtbashOptions.network}. */
430
+ network?: Network;
330
431
  blockchainRid?: string;
331
432
  timeoutMs?: number;
332
433
  nodeUrls?: readonly string[];
333
434
  /** Default org name — see {@link AtbashOptions.orgName}. */
334
435
  orgName?: string;
335
436
  failClosed?: boolean;
437
+ /** See {@link AtbashOptions.requireEnforcement}. Default true. */
438
+ requireEnforcement?: boolean;
439
+ /** See {@link AtbashOptions.decisionTimeoutMs}. Default 60 000 ms. */
440
+ decisionTimeoutMs?: number;
441
+ /** See {@link AtbashOptions.debug}. */
442
+ debug?: boolean;
336
443
  logger?: AtbashLogger;
337
444
  }
338
445
  /** Options accepted by `judgeAction`. */
339
446
  interface JudgeOptions {
340
447
  toolName?: string;
341
448
  toolArgsJson?: string;
342
- provider?: string;
343
- model?: string;
344
449
  verifyPubKey?: string;
345
450
  /** Per-call override of {@link AtbashOptions.orgEncryptionPubKey}. */
346
451
  orgEncryptionPubKey?: string;
@@ -376,12 +481,6 @@ interface LogToolCallOptions {
376
481
  orgEncryptionPubKey?: string;
377
482
  }
378
483
 
379
- interface ChainConfig {
380
- readonly network: Network;
381
- readonly blockchainRid: string;
382
- readonly nodeUrls: readonly string[];
383
- }
384
-
385
484
  declare class Atbash {
386
485
  readonly auth: AgentAuth;
387
486
  readonly endpoint: string;
@@ -395,6 +494,13 @@ declare class Atbash {
395
494
  readonly orgEncryptionPubKey?: string;
396
495
  /** When true (default), `auditToolCall` denies on any error. */
397
496
  readonly failClosed: boolean;
497
+ /** When true (default), an unenforced (audit-tier) answer is refused. */
498
+ readonly requireEnforcement: boolean;
499
+ /** Overall deadline for one auditToolCall decision, in ms. */
500
+ readonly decisionTimeoutMs: number;
501
+ /** Org key learned from the last agent-exists check, for this agent only. */
502
+ private _orgKeyFromChain;
503
+ private readonly debug;
398
504
  private readonly logger;
399
505
  private readonly http;
400
506
  /**
@@ -403,12 +509,50 @@ declare class Atbash {
403
509
  */
404
510
  private readonly _chainCache;
405
511
  /**
406
- * Cached bearer token for risk-engine / insurance read calls. Built
407
- * lazily as a signed `log_tool_call` tx and refreshed every 4 min so
408
- * server-side replay protection windows never expire it mid-session.
512
+ * The chain the constructor settled on. Used only where a lookup returns no
513
+ * answer — see {@link resolveChainFromMap}.
409
514
  */
410
- private _authBearer;
515
+ private readonly _defaultChain;
516
+ /**
517
+ * True when the caller named a chain outright — `chain`, `network`, or the
518
+ * paired `blockchainRid` + `nodeUrls`.
519
+ *
520
+ * Such a client is never re-pointed: not by the migration switch, and not by
521
+ * where an org turns out to live. Naming a chain is the caller saying "talk
522
+ * to this one", and silently routing elsewhere would make the argument a
523
+ * suggestion. A client that names nothing is the one that follows the org.
524
+ */
525
+ private readonly _explicitChain;
526
+ /**
527
+ * The fleet-wide chain switch, read once at construction. `resolve()` hits
528
+ * the config file on disk, so re-reading it per call would put a file read
529
+ * on every judge.
530
+ */
531
+ private readonly _forcedNetwork;
532
+ /**
533
+ * Short-TTL cache for `/api/ai/exists`. The `registered` field is
534
+ * monotonic (once true, stays true), so most calls in a burst re-fetch
535
+ * data that hasn't changed. The `org_encryption_pubkey` field CAN change
536
+ * — an org toggling encryption mid-session — so the TTL is deliberately
537
+ * short (see `AGENT_EXISTS_TTL_MS`). Keyed by (pubkey, network) so
538
+ * cross-agent / cross-network calls don't collide.
539
+ */
540
+ private _agentExistsCache;
541
+ private static readonly AGENT_EXISTS_TTL_MS;
542
+ /** Guards `logEnvironmentOnce` — hosts construct several clients. */
543
+ private static environmentLogged;
411
544
  constructor(privkey: string, options?: AtbashOptions);
545
+ /**
546
+ * Say which environment this build talks to, once per process.
547
+ *
548
+ * The endpoint and both chain RIDs are compiled in, chosen by the npm tag,
549
+ * and no configuration repoints a released build. So installing the build
550
+ * for the wrong environment is invisible: the plugin loads, the hook fires,
551
+ * and every judge call fails because the agent does not exist on the chain
552
+ * this build targets. Organisation names are not unique across environments
553
+ * either, so an org resolving is not evidence the build is right.
554
+ */
555
+ private logEnvironmentOnce;
412
556
  /**
413
557
  * Construct from resolved config: explicit overrides → env vars → the
414
558
  * `~/.config/atbash/config.json` file (see userConfig.resolve). The private
@@ -417,6 +561,48 @@ declare class Atbash {
417
561
  * validated against the trusted allowlist / self-hosted policy; a
418
562
  * self-hosted endpoint's `verifyPubKey` becomes the client default.
419
563
  */
564
+ /**
565
+ * Decide which key this client signs as.
566
+ *
567
+ * Most specific wins: a key passed in code, then a key file the host named,
568
+ * then the environment, then the default key file. `config.json` is absent
569
+ * from that list on purpose — it is a settings file, writable by anything
570
+ * that can write the user's home and read by every Atbash integration on the
571
+ * machine, so letting it name the key meant one stray value silently
572
+ * re-pointed who each of them signed as.
573
+ *
574
+ * A named `keyPath` that does not exist throws rather than falling through.
575
+ * The caller said which file holds its identity; quietly signing as a
576
+ * different one is the outcome worth refusing.
577
+ *
578
+ * @param options - The `fromConfig` options, read for `agentKey` and `keyPath`.
579
+ * @returns The resolved agent identity.
580
+ * @throws When a named key file is missing, or no identity can be found.
581
+ */
582
+ private static resolveIdentity;
583
+ /**
584
+ * Add the one thing a machine with no key file needs to hear.
585
+ *
586
+ * This is the only path where the operator cannot see what happened: no key
587
+ * file, and the key they are looking for sits in `config.json`, which no
588
+ * longer selects the identity. A bare "key file not found" sends them hunting
589
+ * for a key they already have. Only this branch gets the hint — a caller who
590
+ * named a `keyPath` is told about that path, where a stranded key is noise.
591
+ *
592
+ * @param err - The "key file not found" error from the default lookup.
593
+ * @returns The same error, or one that also names the key in config.json.
594
+ */
595
+ private static explainStrandedKey;
596
+ /**
597
+ * Say so when `config.json` still holds an agent key.
598
+ *
599
+ * Nothing signs with it any more, and silence would leave an operator
600
+ * believing one identity is in force while another is. The pubkey is named,
601
+ * not the key, so the line is safe in a log.
602
+ *
603
+ * @param logger - The host logger, when one was supplied.
604
+ */
605
+ private static warnOnStrandedKey;
420
606
  static fromConfig(options?: FromConfigOptions): Atbash;
421
607
  get pubkey(): string;
422
608
  get privkey(): string;
@@ -451,8 +637,50 @@ declare class Atbash {
451
637
  * explicitly false.
452
638
  */
453
639
  auditToolCall(input: ToolCallInput): Promise<Decision>;
640
+ /**
641
+ * The verify key for one call (R4, governor tier-2 review). A per-call key
642
+ * may only narrow what the client accepts: on a client pinned to a judge
643
+ * key it must be that same key, never a replacement; on an unpinned client
644
+ * it adds the check.
645
+ */
646
+ private effectiveVerifyKey;
647
+ /**
648
+ * Collapse a judge result into a {@link Decision}. The most restrictive
649
+ * signal wins (security review 2026-09-30, H1): `action_type`, `verdict`
650
+ * and the canonical `allow` are each able to deny, and only a result where
651
+ * all three agree may execute. A present `allow` / `decision.allow` that is
652
+ * not exactly `true` is the judge declining to grant permission, so it
653
+ * denies even under `failClosed: false` — that option relaxes *errors*, it
654
+ * never overrides a refusal.
655
+ */
656
+ private decide;
657
+ /**
658
+ * One exit for every judge failure.
659
+ *
660
+ * Status and reason go in the *message*, not only in the meta object: hosts
661
+ * print the message and drop the meta, which is why this read as a bare
662
+ * "judge API failed" while the judge was answering with a precise reason.
663
+ * The response body follows only under `debug`, since it can echo the action.
664
+ */
665
+ private failJudge;
666
+ /**
667
+ * The canonical permission for a response body under this client's
668
+ * settings: {@link canonicalAllow}, minus the audit tier when
669
+ * `requireEnforcement` is on. Never more permissive than `auditToolCall`.
670
+ */
671
+ private executable;
454
672
  private fail;
455
- getJudgmentStatus(judgmentId: string, agentPubkey?: string): Promise<JudgmentStatus>;
673
+ /**
674
+ * Return the current status of a previously submitted judgment.
675
+ *
676
+ * `chainOpts` names which chain the judgment was signed against. The
677
+ * server's GET /api/v1/judge routes to that chain when the SDK sends
678
+ * a `brid` query param; without it, the server falls back to public.
679
+ * Callers on the private chain must pass a `chainOpts` (or configure
680
+ * the client on the private chain) — otherwise polling a POSTed
681
+ * judgment on the private chain 404s at the server.
682
+ */
683
+ getJudgmentStatus(judgmentId: string, agentPubkey?: string, chainOpts?: ChainOpts): Promise<JudgmentStatus>;
456
684
  getToolCalls(maxCount: number): Promise<ToolCallRecord[]>;
457
685
  getOrgToolCalls(orgName: string, maxCount: number): Promise<ToolCallRecord[]>;
458
686
  getAgentToolCalls(agentPubkey: string, maxCount: number): Promise<ToolCallRecord[]>;
@@ -461,8 +689,8 @@ declare class Atbash {
461
689
  getOrgTierInfo(orgName: string): Promise<TierInfo | null>;
462
690
  getPendingHeldActions(orgName: string, maxCount: number): Promise<HeldAction[]>;
463
691
  getHeldActionReviews(orgName: string, maxCount: number): Promise<HeldActionReview[]>;
464
- getAgentDetail(agentPubkey: string): Promise<Record<string, unknown>>;
465
- getAgentPolicy(agentPubkey: string): Promise<AgentPolicy>;
692
+ getAgentDetail(agentPubkey: string, options?: AgentLookupOptions): Promise<Record<string, unknown>>;
693
+ getAgentPolicy(agentPubkey: string, options?: AgentLookupOptions): Promise<AgentPolicy>;
466
694
  getSafetyStats(): Promise<Record<string, unknown>>;
467
695
  /**
468
696
  * Org's subscription on a specific chain. The `network` arg selects
@@ -485,7 +713,9 @@ declare class Atbash {
485
713
  * 2. Per-chain subscription fallback — public + private records
486
714
  * are fetched in parallel, with `is_private_blockchain` and
487
715
  * `assigned_at` reconciling mixed states.
488
- * Defaults to the public chain when nothing else resolves.
716
+ * A lookup that names exactly one chain wins outright. Where it names
717
+ * neither (a brand-new org) or cannot choose between them, the client's
718
+ * configured default decides.
489
719
  */
490
720
  resolveChainForOrg(orgName: string): Promise<ChainConfig>;
491
721
  /**
@@ -497,6 +727,8 @@ declare class Atbash {
497
727
  private resolveChainFromMap;
498
728
  /** Drop any cached chain resolutions. Useful in tests. */
499
729
  clearChainCache(): void;
730
+ /** Drop the short-TTL `/api/ai/exists` cache. Useful in tests. */
731
+ clearAgentExistsCache(): void;
500
732
  /**
501
733
  * Wrap an SDK method body in telemetry — records the call at start
502
734
  * and a success/error duration at end. Re-throws on failure so the
@@ -511,6 +743,14 @@ declare class Atbash {
511
743
  * chains; otherwise the client's default.
512
744
  */
513
745
  private bridFromChainOpts;
746
+ /**
747
+ * Resolve the dashboard chain used by agent metadata/policy reads.
748
+ * Explicit per-call network overrides win; otherwise use the supplied org
749
+ * or the client's configured default org. A custom BRID is intentionally
750
+ * left untouched because it cannot be represented by the dashboard's
751
+ * public/private query selector.
752
+ */
753
+ private resolveAgentLookupNetwork;
514
754
  /**
515
755
  * Get-or-create a Bearer token for dashboard reads. The token is a
516
756
  * signed `log_tool_call` op (locally signed, never submitted) — the
@@ -523,10 +763,46 @@ declare class Atbash {
523
763
  private riskEngineGet;
524
764
  private riskEnginePost;
525
765
  private riskEngineRecords;
766
+ /**
767
+ * BRID for an org — one round-trip to the map, honoring the client's chain
768
+ * cache. A "brand-new org" (nothing anywhere names its chain) is not an
769
+ * error — `resolveChainForOrg` returns the client default for that case and
770
+ * this helper returns its BRID. A transport failure or non-200 from
771
+ * `/api/org-network` IS an error and propagates: the caller cannot fall
772
+ * back to the default chain on outage, because with multi-chain live that
773
+ * silently reads from the wrong chain. Matches the Python binding's
774
+ * `_brid_for_org` semantics.
775
+ */
776
+ private bridForOrg;
777
+ /**
778
+ * BRID for the client's configured default org, if it has one.
779
+ *
780
+ * Calls that carry no `orgName` argument are not chain-less: they still
781
+ * belong to `this.orgName`, and that org lives on exactly one chain. Routing
782
+ * them by the constructor's chain instead means a client configured
783
+ * `network: "private"` reads the private chain for an org that lives on
784
+ * public, and gets an empty answer rather than an error. So where an org is
785
+ * known the org decides the chain, and the constructor's chain is what is
786
+ * left when no org is known at all — the order `resolveAgentLookupNetwork`
787
+ * already applies to agent metadata reads, and the order the dashboard
788
+ * applies in `resolveChainForWallet`.
789
+ *
790
+ * Undefined when there is no default org, so callers keep falling back to
791
+ * the client default.
792
+ */
793
+ /** The switch's chain, unless this client named one of its own. */
794
+ private forcedNetwork;
795
+ private defaultOrgBrid;
526
796
  private raiseIfError;
527
797
  /** Wrap a failed HTTP *response* (non-2xx / non-200) as an AtbashAPIError. */
528
798
  private httpError;
529
- /** Wrap a *transport* failure (fetch threw, no response) as an AtbashAPIError. */
799
+ /**
800
+ * Wrap a transport failure (fetch threw, no response) as an AtbashAPIError.
801
+ *
802
+ * `HttpTransportError.kind` names the cause; the message is already
803
+ * human-readable. `debug` echoes the original exception so operators can
804
+ * cross-reference with node / undici logs when a class doesn't match.
805
+ */
530
806
  private transportError;
531
807
  private json;
532
808
  static generateKeypair(): KeyPair;
@@ -546,15 +822,79 @@ declare class AtbashAPIError extends Error {
546
822
  readonly status: number;
547
823
  /** Raw response body text (may be empty). */
548
824
  readonly body: string;
549
- constructor(status: number, body: string, statusText?: string, endpoint?: string);
825
+ /**
826
+ * Request path this status came from, e.g. `/api/v1/judge` — absent when the
827
+ * request never reached a response (transport failures carry status 0).
828
+ *
829
+ * A status alone does not say what failed. Several routes answer one call, and
830
+ * more than one of them can 404: a caller reading only `404` cannot tell "this
831
+ * agent is not registered" from "the chain-routing lookup did not resolve", and
832
+ * has to guess from the body's prose — which a 404 raised before any handler
833
+ * ran does not carry. The path is the fact that settles it.
834
+ */
835
+ readonly path?: string;
836
+ constructor(status: number, body: string, statusText?: string, endpoint?: string, path?: string);
550
837
  }
551
838
  declare class SignatureVerificationError extends Error {
552
839
  constructor(message: string);
553
840
  }
554
841
 
842
+ /**
843
+ * Thin typed fetch wrapper.
844
+ *
845
+ * openapi-typescript emits types only (no runtime client), so this is the
846
+ * single hand-written transport — generic `get`/`post` over global `fetch`
847
+ * with a per-request timeout. The endpoint-specific request/response *shapes*
848
+ * are pulled from the generated `schema.ts` at the call sites in client.ts, so
849
+ * the wire contract still lives in spec/openapi.yaml. Methods return the raw
850
+ * `Response` so the caller can read the exact bytes the server signed before
851
+ * any decode (judge signature verification) — mirroring the Python surface's
852
+ * use of raw httpx (DECISIONS 2026-05-22).
853
+ */
854
+ type QueryValue = string | number | boolean | undefined | null;
855
+ declare class HttpClient {
856
+ readonly baseUrl: string;
857
+ readonly timeoutMs: number;
858
+ constructor(baseUrl: string, timeoutMs: number);
859
+ buildUrl(path: string, query?: Record<string, QueryValue>): string;
860
+ get(path: string, query?: Record<string, QueryValue>, headers?: Record<string, string>): Promise<Response>;
861
+ post(path: string, body: unknown, headers?: Record<string, string>): Promise<Response>;
862
+ private fetch;
863
+ }
864
+ /**
865
+ * A transport failure the SDK can act on. Every real cause the platform surfaces
866
+ * lands as one of these — the message names the cause in plain language so a
867
+ * plugin can show it to a user without decoding httpx / fetch internals.
868
+ *
869
+ * `cause` preserves the original error for debug logging; consumers that want
870
+ * the raw exception (e.g. tests) read it there.
871
+ */
872
+ declare class HttpTransportError extends Error {
873
+ readonly kind: "timeout" | "aborted" | "dns" | "connect_refused" | "connection_reset" | "unknown";
874
+ constructor(kind: HttpTransportError["kind"], message: string, options?: {
875
+ cause?: unknown;
876
+ });
877
+ }
878
+
879
+ declare function canonicalAllow(data: Record<string, unknown>): boolean;
880
+
555
881
  /** Normalize wire shapes (verdict casing, status, pubkey) to canonical forms. */
556
882
 
557
883
  declare function normalizeVerdict(raw: unknown): Verdict;
884
+ /**
885
+ * Canonicalize `action_type` at the wire boundary, the way
886
+ * {@link normalizeVerdict} already canonicalizes `verdict`.
887
+ *
888
+ * `verdict` has been normalized here since the beginning and has never
889
+ * drifted between consumers. `action_type` was passed through raw, so every
890
+ * reader invented its own folding policy — `auditToolCall` compared exactly
891
+ * while `memory/scan.ts` trimmed and case-folded, and the same `" ALLOW "`
892
+ * was therefore an error on one path and permission on the other.
893
+ *
894
+ * Trim and case-fold only. Zero-width and homoglyph variants survive
895
+ * untouched, stay outside the known set, and still fail closed.
896
+ */
897
+ declare function normalizeActionType(raw: unknown): string;
558
898
  declare function normalizeStatus(raw: unknown): JudgmentState;
559
899
  /** Wire pubkey may be a hex string, a Buffer/Uint8Array, or `{ data: [...] }`. */
560
900
  declare function pubkeyToHex(val: unknown): string;
@@ -563,9 +903,22 @@ interface AtbashUserConfig {
563
903
  agentKey?: string;
564
904
  orgName?: string;
565
905
  judgeEndpoint?: string;
566
- blockchainRid?: string;
567
- provider?: string;
568
- providerModel?: string;
906
+ /**
907
+ * Response-signing pubkey of a self-hosted judge (66 hex). Setting it makes
908
+ * `fromConfig` use the self-hosted policy for `judgeEndpoint`, which is the
909
+ * only way a non-allowlisted judge host is accepted.
910
+ */
911
+ judgeVerifyPubKey?: string;
912
+ /**
913
+ * `"private"` pins every org to the private chain regardless of where the
914
+ * dashboard says it lives — the migration switch. Leave it unset for the
915
+ * normal mode, where each org's own chain decides. There is no `"public"`
916
+ * value; a caller that wants one specific chain passes `chain` or `network`
917
+ * at construction instead.
918
+ */
919
+ defaultChainNetwork?: Network;
920
+ /** "1" / "true" turns on verbose diagnostics. See AtbashOptions.debug. */
921
+ debug?: string;
569
922
  }
570
923
  declare function getConfigDir(): string;
571
924
  declare function getConfigPath(): string;
@@ -576,6 +929,26 @@ declare function resolve(key: keyof AtbashUserConfig, flagValue?: string): strin
576
929
  declare function resolveKeyPath(input?: string): string;
577
930
  declare function loadAgentFromFile(keyPath?: string): AgentAuth;
578
931
 
932
+ /**
933
+ * Accepted key filenames, in precedence order.
934
+ *
935
+ * `guard-client-key` stays first: it is the name every existing install
936
+ * already has, and changing which file wins would silently switch agent
937
+ * identity for anyone holding both. `atbash-client-key` is accepted because
938
+ * it is the name people actually create — the old one carries retired
939
+ * branding — and hitting ENOENT on a key you just wrote, from a plugin that
940
+ * still reports itself installed, is a miserable first run.
941
+ */
942
+ declare const KEY_FILENAMES: readonly ["guard-client-key", "atbash-client-key"];
943
+ /** Every path checked when no explicit key path is given, in order. */
944
+ declare function keyPathCandidates(): string[];
945
+ /**
946
+ * Pick the key path: an explicit input wins untouched; otherwise the first
947
+ * accepted filename that exists, falling back to the preferred name so the
948
+ * error names something recognisable when nothing is there.
949
+ */
950
+ declare function chooseKeyPath(input: string | undefined, exists: (p: string) => boolean): string;
951
+
579
952
  /**
580
953
  * Canonical secret kinds reported by the Rust core in `SecretMatch.kind`.
581
954
  * Wire is permissive (modelled as a free string in {@link SecretMatch})
@@ -594,6 +967,26 @@ declare function verifyJudgeResponseSignature(bodyBytes: Uint8Array, signatureHe
594
967
  reason?: string;
595
968
  };
596
969
 
970
+ /**
971
+ * The boot memory-sync failure line.
972
+ *
973
+ * Split out of `guard-manager.ts` so it can be asserted without constructing a
974
+ * guard manager, which needs the native addon. The message is the whole point
975
+ * of AT-304: hosts print the message and drop the structured meta, so a cause
976
+ * that lives only in meta never reaches the operator.
977
+ */
978
+ /** Advice, not a diagnosis — appended only when the cause is unhelpful. */
979
+ declare const BOOT_SYNC_HINT = "check the chain endpoint and orgName if the cause above does not explain it";
980
+ /**
981
+ * Lead with the real cause.
982
+ *
983
+ * The previous wording named the chain endpoint and orgName as the things to
984
+ * check, which sent operators to verify configuration that was already correct
985
+ * while the actual cause (a node answering `404 Can't find blockchain with
986
+ * blockchainRID: …` for a chain it does not host) stayed hidden.
987
+ */
988
+ declare function bootSyncFailureLine(cause: unknown): string;
989
+
597
990
  /** Derive the 32-byte AES key from an agent's secp256k1 privkey (hex). Deterministic — same privkey always yields the same key. */
598
991
  declare function deriveMemoryKey(privkey: string): Promise<Buffer>;
599
992
  /** Encrypt UTF-8 plaintext. `ciphertext` has the 16-byte GCM auth tag appended, matching the SDK's on-chain byte layout. */
@@ -623,15 +1016,27 @@ declare function scanMemoryBatch(entries: MemoryEntry[], auth: AgentAuth, opts?:
623
1016
  interface CommitMemoryOptions {
624
1017
  /** Score in [1, 10]. Defaults to 5 (matches the Rell entity default). */
625
1018
  score?: number;
1019
+ /**
1020
+ * Which memory file this commit targets. Defaults to `""` — the
1021
+ * un-pathed slot, matching Rell's `file_path: text = ""` default.
1022
+ * Pass an explicit filename (e.g. `"AGENTS.md"`) to keep files
1023
+ * versioned independently on chain.
1024
+ */
1025
+ filePath?: string;
626
1026
  /** Org name — when set, the SDK resolves which chain the agent lives on. */
627
1027
  orgName?: string;
628
- /** Atbash service endpoint for org→chain lookup. */
1028
+ /** Atbash service endpoint — where memory reaches the chain, and the org→chain lookup. */
629
1029
  endpoint?: string;
1030
+ /** Self-hosted judge response-signing pubkey; required with a non-allowlisted `endpoint`. */
1031
+ verifyPubKey?: string;
630
1032
  chainOpts?: ChainOpts;
631
1033
  }
632
1034
  interface RollbackMemoryOptions {
633
1035
  orgName?: string;
1036
+ /** Atbash service endpoint — where memory reaches the chain, and the org→chain lookup. */
634
1037
  endpoint?: string;
1038
+ /** Self-hosted judge response-signing pubkey; required with a non-allowlisted `endpoint`. */
1039
+ verifyPubKey?: string;
635
1040
  chainOpts?: ChainOpts;
636
1041
  }
637
1042
  /**
@@ -653,6 +1058,7 @@ declare function commitMemoryVersion(plaintext: string, auth: AgentAuth, opts?:
653
1058
  */
654
1059
  interface AgentMemoryEntry {
655
1060
  id: number;
1061
+ filePath: string;
656
1062
  content: string;
657
1063
  decryptError?: string;
658
1064
  score: number;
@@ -664,6 +1070,7 @@ interface AgentMemoryEntry {
664
1070
  interface MemoryRollbackEvent {
665
1071
  fromId: number;
666
1072
  toId: number;
1073
+ filePath: string;
667
1074
  reason: string;
668
1075
  signer: string;
669
1076
  createdAt: number;
@@ -674,36 +1081,39 @@ interface MemoryRollbackEvent {
674
1081
  * response is a single integer, so this is safe to call on every
675
1082
  * memory-read hot path.
676
1083
  */
677
- declare function getActiveMemoryId(auth: AgentAuth, chainOpts?: ChainOpts): Promise<number | null>;
1084
+ declare function getActiveMemoryId(auth: AgentAuth, chainOpts?: ChainOpts, filePath?: string | null): Promise<number | null>;
678
1085
  /**
679
1086
  * Recent active memory entries — subset of active versions filtered
680
1087
  * by the chain's `MEMORY_RECENT_WINDOW_MS`. For a time-unbounded view
681
- * of every currently active version, use `getAllAgentMemory`.
1088
+ * of every currently active version, use `getActiveAgentMemory`.
682
1089
  */
683
- declare function getActiveMemory(auth: AgentAuth, chainOpts?: ChainOpts): Promise<AgentMemoryEntry[]>;
1090
+ declare function getRecentAgentMemory(auth: AgentAuth, chainOpts?: ChainOpts, filePath?: string | null): Promise<AgentMemoryEntry[]>;
684
1091
  /**
685
1092
  * All currently-active memory entries with no time cutoff. Use this
686
1093
  * when you need every active version regardless of age.
687
1094
  */
688
- declare function getAllAgentMemory(auth: AgentAuth, chainOpts?: ChainOpts): Promise<AgentMemoryEntry[]>;
1095
+ declare function getActiveAgentMemory(auth: AgentAuth, chainOpts?: ChainOpts, filePath?: string | null): Promise<AgentMemoryEntry[]>;
689
1096
  /**
690
1097
  * Full version history — active + inactive, most recent first. Used
691
1098
  * by rollback UX to choose a target version.
692
1099
  */
693
- declare function getMemoryHistory(auth: AgentAuth, chainOpts?: ChainOpts): Promise<AgentMemoryEntry[]>;
1100
+ declare function getMemoryHistory(auth: AgentAuth, chainOpts?: ChainOpts, filePath?: string | null): Promise<AgentMemoryEntry[]>;
694
1101
  /**
695
1102
  * Fetch a single memory entry by version id, including its current
696
- * `is_active` state.
1103
+ * `is_active` state. Version ids are agent-unique on chain (not
1104
+ * per-file), so `id` alone resolves the target row.
697
1105
  */
698
1106
  declare function getMemoryById(id: number, auth: AgentAuth, chainOpts?: ChainOpts): Promise<AgentMemoryEntry>;
699
1107
  /**
700
1108
  * Audit trail of rollback events for this agent, most recent first.
1109
+ * Scope by file with `filePath`; omit for a cross-file view.
701
1110
  */
702
- declare function getRollbackHistory(auth: AgentAuth, chainOpts?: ChainOpts): Promise<MemoryRollbackEvent[]>;
1111
+ declare function getRollbackHistory(auth: AgentAuth, chainOpts?: ChainOpts, filePath?: string | null): Promise<MemoryRollbackEvent[]>;
703
1112
  /**
704
1113
  * Roll back to a previously-committed memory version. The target
705
- * `toId` must exist and be currently inactive. `reason` is required
706
- * and is recorded on-chain in `memory_rollback_log`.
1114
+ * `toId` must exist and be currently inactive. The chain resolves the
1115
+ * target row's `file_path` from `toId` — no file path is passed in.
1116
+ * `reason` is required and is recorded on-chain in `memory_rollback_log`.
707
1117
  */
708
1118
  declare function rollbackMemory(toId: number, reason: string, auth: AgentAuth, opts?: RollbackMemoryOptions): Promise<void>;
709
1119
 
@@ -755,21 +1165,15 @@ interface ClassifyMemoryWriteOptions {
755
1165
  *
756
1166
  * Empty-content writes return `null`: they can't carry a poisoning
757
1167
  * payload, and the regular audit path still sees them.
758
- */
759
- declare function classifyMemoryWrite(event: unknown, ctx: unknown, opts?: ClassifyMemoryWriteOptions): MemoryEntry | null;
760
-
761
- /**
762
- * Plugin-agnostic memory-write guard.
763
- *
764
- * A single call that replaces the plugin's usual memory-write branch:
765
- * classify → scan (Layer 1 regex + Layer 2 LLM) → gate on verdict →
766
- * persist to chain (fire-and-forget when allowed) → return decision.
767
1168
  *
768
- * Plugins call this from their `before_tool_call` hook. When it returns
769
- * `{ handled: false }` the call wasn't a memory write and the plugin
770
- * should fall through to its regular tool-call audit. When
771
- * `{ handled: true }` the plugin returns `decision` directly.
1169
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1170
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1171
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1172
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1173
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1174
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
772
1175
  */
1176
+ declare function classifyMemoryWrite(event: unknown, ctx: unknown, opts?: ClassifyMemoryWriteOptions): MemoryEntry | null;
773
1177
 
774
1178
  /**
775
1179
  * Minimal logger accepted by `guardMemoryWrite`. Plugins pass their
@@ -793,6 +1197,8 @@ interface GuardMemoryWriteInput extends ClassifyMemoryWriteOptions {
793
1197
  verifyPubKey?: string;
794
1198
  /** Org name for chain resolution (public vs org-private chain). */
795
1199
  orgName?: string;
1200
+ /** Explicit chain target when the host already resolved and verified it. */
1201
+ chainOpts?: ChainOpts;
796
1202
  /** LLM confidence threshold for yellow escalation. Default 0.6. */
797
1203
  threshold?: number;
798
1204
  /** When false, red verdicts log but don't block. Default true. */
@@ -801,6 +1207,17 @@ interface GuardMemoryWriteInput extends ClassifyMemoryWriteOptions {
801
1207
  debug?: boolean;
802
1208
  /** Optional logger for debug probe + persist-failure warnings. */
803
1209
  logger?: GuardLogger;
1210
+ /** Agent home directory names to accept beyond the built-in list. */
1211
+ agentHomeDirs?: string[];
1212
+ /**
1213
+ * Root the caller keeps memory under, when it is not an agent home.
1214
+ *
1215
+ * Only pass a value the host stated outright. A root defaulted from the
1216
+ * process working directory would make whichever directory the host happened
1217
+ * to start in look like a memory store — which is how a stray MEMORY.md once
1218
+ * ended up inside a plugin checkout.
1219
+ */
1220
+ memoryRoot?: string;
804
1221
  }
805
1222
  /** Plugin-facing decision. Shape mirrors what plugins return from `before_tool_call`. */
806
1223
  interface GuardMemoryDecision {
@@ -815,22 +1232,21 @@ interface GuardMemoryWriteResult {
815
1232
  decision?: GuardMemoryDecision;
816
1233
  /** Present when handled and scan ran. Absent when scan threw. */
817
1234
  scanResult?: MemoryScanResult;
818
- /** Present when handled. True when a chain-commit was dispatched (fire-and-forget). */
1235
+ /** Present when handled. True only after the chain transaction confirms. */
819
1236
  committed?: boolean;
820
1237
  }
821
1238
  /**
822
- * Guarded memory-write flow. Returns `{ handled: false }` when the tool
823
- * call isn't a memory write, otherwise returns a `decision` the plugin
824
- * should return from its hook.
1239
+ * Guard a memory write, and record what was decided.
1240
+ *
1241
+ * Wraps the decision rather than threading telemetry through its eight exits,
1242
+ * so a later return added inside cannot quietly stop being counted.
825
1243
  *
826
- * Verdict handling:
827
- * - `red` → not persisted to chain; decision blocks (unless `enforce: false`)
828
- * - `yellow` → persisted with LLM score; decision allows
829
- * - `green` → persisted with LLM score; decision allows
1244
+ * Only a call this guard actually handled is recorded. A tool call that was
1245
+ * never a memory write is not a memory operation, and counting it would put
1246
+ * every unrelated edit into the number a memory migration reads.
830
1247
  *
831
- * Chain commit is fire-and-forget: network errors are logged via
832
- * `logger.warn` but never surfaced to the caller. This mirrors the
833
- * existing plugin behaviour — chain is for audit/recovery, not gating.
1248
+ * @param input - The tool call and the agent to judge it for.
1249
+ * @returns What the guard decided; unchanged by the recording.
834
1250
  */
835
1251
  declare function guardMemoryWrite(input: GuardMemoryWriteInput): Promise<GuardMemoryWriteResult>;
836
1252
 
@@ -860,17 +1276,26 @@ interface SyncMemoryOptions {
860
1276
  /** Skip the TTL gate and force a chain check this call. Default false. */
861
1277
  force?: boolean;
862
1278
  chainOpts?: ChainOpts;
1279
+ /** Exact chain slot for the managed file; omission keeps the cross-file API behavior. */
1280
+ filePath?: string;
863
1281
  }
864
1282
  /**
865
1283
  * `drifted: false` — pointer is still valid; caller can keep serving the local copy.
866
1284
  * `drifted: true` — active id changed on chain; `current` is the fresh decrypted row
867
1285
  * (or `null` if active memory was removed entirely).
1286
+ *
1287
+ * `checked` — whether this call actually queried chain. `false` means the TTL
1288
+ * window was still open and the pointer was trusted without contacting chain, so
1289
+ * `drifted: false` carries no evidence about the current state. Callers that
1290
+ * vouch for content to a third party must not treat an unchecked result as proof.
868
1291
  */
869
1292
  type SyncMemoryResult = {
870
1293
  drifted: false;
1294
+ checked: boolean;
871
1295
  pointer: MemoryPointer;
872
1296
  } | {
873
1297
  drifted: true;
1298
+ checked: true;
874
1299
  current: AgentMemoryEntry | null;
875
1300
  pointer: MemoryPointer;
876
1301
  };
@@ -886,10 +1311,19 @@ declare class PointerStore {
886
1311
  private cache;
887
1312
  private loading;
888
1313
  constructor(filePath: string);
889
- /** Resolves the pointer for `agentPubkeyHex`, or a zero-pointer that will force a sync on first use. */
890
- get(agentPubkeyHex: string): Promise<MemoryPointer>;
891
- /** Persists an updated pointer. Failures are swallowed to a logger callback (if provided) so sync never blocks the caller. */
892
- set(agentPubkeyHex: string, pointer: MemoryPointer, onError?: (err: Error) => void): Promise<void>;
1314
+ /**
1315
+ * The pointer for one agent's file, or a zero-pointer forcing a sync.
1316
+ *
1317
+ * @param filePath - The file's chain slot, i.e. its basename.
1318
+ */
1319
+ get(agentPubkeyHex: string, filePath: string): Promise<MemoryPointer>;
1320
+ /**
1321
+ * Persist one file's pointer, leaving the agent's other files alone.
1322
+ *
1323
+ * Write failures are reported to `onError` rather than thrown: a cache that
1324
+ * cannot be written must not take down the read it was speeding up.
1325
+ */
1326
+ set(agentPubkeyHex: string, filePath: string, pointer: MemoryPointer, onError?: (err: Error) => void): Promise<void>;
893
1327
  private ensureLoaded;
894
1328
  private loadOnce;
895
1329
  private persist;
@@ -901,6 +1335,8 @@ declare function defaultPointerPath(workspaceDir?: string): string;
901
1335
  interface WrappedLogger {
902
1336
  info(message: string, meta?: Record<string, unknown>): void;
903
1337
  warn(message: string, meta?: Record<string, unknown>): void;
1338
+ /** Wait for queued writes and surface any lost log entry. */
1339
+ flush(): Promise<void>;
904
1340
  }
905
1341
  interface UpstreamLogger {
906
1342
  info: (...args: unknown[]) => void;
@@ -929,15 +1365,52 @@ interface ClassifyMemoryReadOptions {
929
1365
  * Caller-supplied `patterns` are MERGED with the defaults (matches
930
1366
  * Node's original behavior — extending in one plugin doesn't disable
931
1367
  * standard coverage).
1368
+ *
1369
+ * THROWS on host input it cannot read without side effects: a proxy, an accessor where a data
1370
+ * property is expected, a value with no safe projection, or one past the size bounds. Refusing is
1371
+ * deliberate - a silent `null` would report "not a memory write" for a call nobody could classify,
1372
+ * and memory protection would be skipped exactly where the input was strange. Callers that sit in a
1373
+ * host hook MUST catch it and turn it into a decision; `guardMemoryWrite` and
1374
+ * `MemoryGuardManager.handleBeforeToolCall` already do.
932
1375
  */
933
1376
  declare function classifyMemoryRead(event: unknown, ctx: unknown, opts?: ClassifyMemoryReadOptions): boolean;
934
1377
 
935
- /** Decision the manager returns to the plugin's `before_tool_call` handler. */
1378
+ /**
1379
+ * Decision the manager returns to the plugin's `before_tool_call` handler.
1380
+ *
1381
+ * `allow: true` alone is NOT evidence that anything was checked. Read `audited`
1382
+ * to tell the two apart, and route un-audited calls to your own judge — see the
1383
+ * field docs below.
1384
+ */
936
1385
  interface HookDecision {
937
1386
  allow?: boolean;
938
1387
  block?: boolean;
939
1388
  blockReason?: string;
940
1389
  reason?: string;
1390
+ /**
1391
+ * Whether the guard reached an enforcement decision about *this* call.
1392
+ *
1393
+ * Note this describes whether the guard **decided**, not whether it allowed.
1394
+ * Every `block` is `audited: true` — a blocked call is the most thoroughly
1395
+ * checked outcome the guard produces (a red scan, a ciphertext integrity
1396
+ * failure, a rolled-back version), and a host must never re-judge its way past
1397
+ * one.
1398
+ *
1399
+ * Absent or false means the guard reached no decision — for example, the scan
1400
+ * never ran or this manager does not own the requested file. In enforce mode,
1401
+ * failed chain verification of the managed file is a block, never an unaudited
1402
+ * allow. In observe mode unverified calls remain unaudited.
1403
+ *
1404
+ * So the host rule is:
1405
+ * `if (d.block) deny; else if (d.audited) allow; else judge it yourself;`
1406
+ *
1407
+ * Treating a bare `allow: true` as a completed audit is what this field exists
1408
+ * to prevent. A host that ignores it and returns the decision verbatim will
1409
+ * execute unaudited tool calls.
1410
+ */
1411
+ audited?: boolean;
1412
+ /** Scan verdict when one was produced (`green` | `yellow` | `red`). Absent when no scan ran. */
1413
+ verdict?: string;
941
1414
  }
942
1415
  interface MemoryGuardManagerOptions {
943
1416
  auth: AgentAuth;
@@ -945,6 +1418,17 @@ interface MemoryGuardManagerOptions {
945
1418
  workspaceDir: string;
946
1419
  /** Absolute MEMORY.md path. Default `<workspaceDir>/MEMORY.md`. */
947
1420
  memoryFilePath?: string;
1421
+ /**
1422
+ * Root this host keeps memory under, when it is not an agent home.
1423
+ *
1424
+ * Only set this from a value the host stated outright — never from
1425
+ * `process.cwd()`. A guessed root makes whichever directory the host started
1426
+ * in look like a memory store, which is how a stray MEMORY.md once ended up
1427
+ * inside a plugin checkout.
1428
+ */
1429
+ memoryRoot?: string;
1430
+ /** Agent home directory names to accept beyond the built-in list. */
1431
+ agentHomeDirs?: string[];
948
1432
  /** Persistent pointer file. Default `<workspaceDir>/.atbash/memory-pointer.json`. */
949
1433
  pointerFilePath?: string;
950
1434
  /** File-backed log. Default `<workspaceDir>/.atbash/plugin.log`. */
@@ -960,6 +1444,13 @@ interface MemoryGuardManagerOptions {
960
1444
  rollbackMinScore?: number;
961
1445
  /** True → return `{block:true}` on defense triggers. False → log and return `null` (audit-only). Default true. */
962
1446
  enforce?: boolean;
1447
+ /**
1448
+ * Chain targeting for the pointer sync (network, blockchainRid, nodeUrls).
1449
+ * Defaults to the SDK's configured chain. Without this the manager could only
1450
+ * ever talk to the default chain, which left the whole memory-read path
1451
+ * untestable — `syncLocalMemory` already accepted these options.
1452
+ */
1453
+ chainOpts?: ChainOpts;
963
1454
  /** Host-specific tuning of what counts as a memory read. */
964
1455
  memoryReadClassifier?: ClassifyMemoryReadOptions;
965
1456
  /** Passed through to `guardMemoryWrite`. Host memory-write tool names override. */
@@ -984,11 +1475,45 @@ declare class MemoryGuardManager {
984
1475
  private readonly pointerStore;
985
1476
  private readonly logger;
986
1477
  private readonly memoryFilePath;
1478
+ /** Base for resolving the relative paths a host sends. */
1479
+ private readonly workspaceDir;
1480
+ /** Set by `verifyOneFile` when the file it checked was actually on chain. */
1481
+ private lastVerified;
987
1482
  private readonly ttlMs;
988
1483
  private readonly rollbackMinScore;
989
1484
  private readonly enforce;
990
1485
  private readonly agentPubkeyHex;
1486
+ /**
1487
+ * Memoized org→chain resolution. Reads have to hit the SAME chain
1488
+ * writes did, so an org-scoped guard must resolve `orgName` to
1489
+ * network exactly like `commitMemoryVersion` does. Without this
1490
+ * cache the read path would either (a) hit the SDK-default chain
1491
+ * every time — silently returning "no active memory on chain" when
1492
+ * writes landed on the org's actual chain, or (b) hammer
1493
+ * `/api/org-network` on every read. `undefined` means "not yet
1494
+ * resolved"; a resolved `null` means "no org / use raw chainOpts".
1495
+ */
1496
+ private _resolvedChainOpts;
1497
+ private _resolveChainInflight?;
991
1498
  constructor(opts: MemoryGuardManagerOptions);
1499
+ /**
1500
+ * Carry this manager's host into `chainOpts`.
1501
+ *
1502
+ * Memory reads reach the chain through that host, and they are handed only
1503
+ * `chainOpts` — without this a self-hosted deployment would read from the
1504
+ * compiled-in default host while writing to its own.
1505
+ */
1506
+ private withEndpoint;
1507
+ /**
1508
+ * Resolve `orgName` → chain once, cache forever. `commitMemoryVersion`
1509
+ * already does this for writes; without the same call on the read
1510
+ * path, a client on the SDK's baked default chain reads from the wrong
1511
+ * chain and reports "no active memory" for an agent whose writes did
1512
+ * land — on the org's actual chain. An explicit `chainOpts.blockchainRid`
1513
+ * still wins (caller vouched for it); everything else honors the
1514
+ * dashboard's `org_networks` map.
1515
+ */
1516
+ private resolveChainOpts;
992
1517
  /**
993
1518
  * One-shot chain probe at plugin registration. Refreshes MEMORY.md
994
1519
  * from chain when drifted and score passes threshold. Fire-and-forget
@@ -996,27 +1521,125 @@ declare class MemoryGuardManager {
996
1521
  */
997
1522
  runBootProbe(): Promise<void>;
998
1523
  /**
999
- * Returns a `HookDecision` when the event is a memory read or write
1000
- * (host returns it verbatim to its runtime). Returns `null` when the
1001
- * event isn't memory-related — host falls through to its own audit.
1524
+ * Bring one chain file into step with its local counterpart.
1525
+ *
1526
+ * Only a file that already exists locally is touched. Restoring one that is
1527
+ * absent would mean choosing where to create it, and the root is a guess the
1528
+ * moment more than one could apply — the same guess that once wrote a
1529
+ * MEMORY.md into a plugin checkout.
1530
+ *
1531
+ * @returns What happened, for the single summary line the caller logs.
1532
+ */
1533
+ private syncOneAtBoot;
1534
+ /**
1535
+ * Where a chain slot's file lives locally, or null when we do not have it.
1536
+ *
1537
+ * A stated root is tried first, then each agent home that actually exists in
1538
+ * the user's home directory — the same rule that produced the slot, run
1539
+ * backwards. Only an existing file is returned: creating one would mean
1540
+ * picking a root, and picking is guessing.
1002
1541
  */
1542
+ private locate;
1543
+ /** Roots a chain slot may be joined to, most specific first. */
1544
+ private memoryRoots;
1003
1545
  handleBeforeToolCall(event: unknown, ctx: unknown): Promise<HookDecision | null>;
1004
1546
  private mapGuardResult;
1547
+ /**
1548
+ * Whether the pointer state this manager tracks actually describes the file
1549
+ * this call is about to read.
1550
+ *
1551
+ * The classifier fires on nine patterns — including the bare tokens
1552
+ * `"memory/"`, `"CLAUDE.md"` and `"AGENTS.md"` — but the sync path only ever
1553
+ * reads, refreshes, or vouches for `this.memoryFilePath`. Without this check a
1554
+ * read of `/repo/CLAUDE.md` (or any path merely containing `memory/`) would
1555
+ * receive an `audited: true` for a file the guard never opened.
1556
+ *
1557
+ * Conservative on purpose: every path-shaped value found must resolve to the
1558
+ * managed file. If none is found, or any one differs, the answer is no. That
1559
+ * also covers events carrying two different path keys, where the classifier
1560
+ * and the host could otherwise disagree about which one is authoritative.
1561
+ */
1562
+ /**
1563
+ * The memory files this tool call reads, as absolute paths.
1564
+ *
1565
+ * Relative paths resolve against the workspace, not `process.cwd()`. The host
1566
+ * sends paths relative to the agent's workspace; resolving them against the
1567
+ * process's own directory made the guard protect whichever directory the host
1568
+ * happened to start in, which on one machine was a plugin checkout.
1569
+ *
1570
+ * @returns Every named target, de-duplicated; empty when the call names none.
1571
+ */
1572
+ private readTargets;
1573
+ /**
1574
+ * Say what the read classifier saw and what it concluded.
1575
+ *
1576
+ * The write path has `emitDebugProbe`; this is its counterpart, so a tool
1577
+ * call can be followed across the fork instead of only after it. `false` here
1578
+ * is the normal answer for a write — it means the call goes to the write
1579
+ * classifier next, not that it was ignored.
1580
+ *
1581
+ * @param isMemoryRead - What `classifyMemoryRead` returned.
1582
+ */
1583
+ private emitReadProbe;
1584
+ /**
1585
+ * The chain slot for a path, or null when the file gets no chain record.
1586
+ *
1587
+ * One helper so the boot probe and every read agree: taking a basename in one
1588
+ * place and a relative path in the other would verify a file against a record
1589
+ * that is not its own.
1590
+ */
1591
+ private slotFor;
1592
+ /**
1593
+ * Verify every memory file this call reads, each against its own chain slot.
1594
+ *
1595
+ * A call naming several files is only cleared when all of them are, so one
1596
+ * unverifiable file cannot ride along with a verified one.
1597
+ *
1598
+ * @returns A block for the first file that fails, an audited allow when all
1599
+ * were verified, or null when none of them is on chain.
1600
+ */
1005
1601
  private handleMemoryRead;
1602
+ /**
1603
+ * Check one memory file against the chain slot of the same name.
1604
+ *
1605
+ * A file with no chain version is passed through rather than blocked: there
1606
+ * is no claim to protect, so refusing would only stop an agent reading memory
1607
+ * it has never committed. A file that IS on chain must match, or the read is
1608
+ * refused — that is the whole guarantee.
1609
+ *
1610
+ * @returns A decision when the read must not proceed, else null.
1611
+ */
1612
+ private verifyOneFile;
1613
+ /** Complete queued audit-log writes before a host shuts down or removes its workspace. */
1614
+ flushLogs(): Promise<void>;
1615
+ /**
1616
+ * A read this guard could not verify.
1617
+ *
1618
+ * Enforcing, that is a block. Observing, the host audits it as usual — but
1619
+ * the reason is said out loud either way, or an unverified read looks
1620
+ * identical to a clean one in the log.
1621
+ */
1622
+ private unverifiedRead;
1006
1623
  private writeMemoryAtomic;
1624
+ /** Replace `target` in one step, so a reader never sees a half-written file. */
1625
+ private writeFileAtomic;
1007
1626
  }
1008
1627
  declare function createMemoryGuardManager(opts: MemoryGuardManagerOptions): MemoryGuardManager;
1009
1628
 
1010
1629
  /**
1011
1630
  * Atbash SDK Telemetry — OpenTelemetry metrics for usage tracking.
1012
1631
  *
1013
- * Tracks: function call counts, latency, source (CLI/plugin/SDK),
1014
- * and agent identity. ON by default.
1632
+ * Metrics are POSTed to the Atbash-owned `/api/telemetry` proxy, which
1633
+ * verifies the bearer, injects the Honeycomb ingest key server-side, and
1634
+ * forwards to Honeycomb. The ingest credential never enters the SDK.
1635
+ *
1636
+ * Environment opt-out (recommended for air-gapped deployments):
1637
+ * ATBASH_TELEMETRY_DISABLED=1
1015
1638
  *
1016
1639
  * Opt-out: create ~/.config/atbash/telemetry.json with { "enabled": false }
1017
1640
  * The file must be readable by the SDK process. If missing, corrupted, or
1018
- * unreadable → telemetry stays ON. Environment variables cannot disable
1019
- * telemetry (prevents agent bypass via env-var injection).
1641
+ * unreadable, telemetry remains eligible to start unless the environment
1642
+ * opt-out is set.
1020
1643
  */
1021
1644
  type ClientSource = "cli" | "sdk" | "plugin:openclaw" | "plugin:langchain" | "plugin:langgraph" | "plugin:hermes" | "plugin:eliza" | "plugin:crewai" | "plugin:mcp" | "plugin:autogen" | "plugin:jeenai" | (string & {});
1022
1645
  interface TelemetryConfig {
@@ -1026,6 +1649,27 @@ interface TelemetryConfig {
1026
1649
  source?: ClientSource;
1027
1650
  /** Flush interval in ms. Default: 60000 */
1028
1651
  exportIntervalMs?: number;
1652
+ /** Atbash endpoint that hosts /api/telemetry. Required to actually export. */
1653
+ endpoint?: string;
1654
+ /**
1655
+ * Called on every export to obtain fresh auth headers (typically
1656
+ * `{ Authorization: "Bearer <hex>" }`). Required to actually export.
1657
+ */
1658
+ getAuthHeaders?: () => Record<string, string>;
1659
+ /**
1660
+ * Hashes the agent key before it is emitted on a wide event.
1661
+ *
1662
+ * Defaults to an unsalted SHA-256, which is de-identification rather than
1663
+ * anonymisation: agent pubkeys are public on chain and can be enumerated, so
1664
+ * the mapping is recoverable by anyone holding that list. Supply a salted
1665
+ * hasher to make it uncorrelatable, at the cost of joining across
1666
+ * deployments.
1667
+ *
1668
+ * Injected rather than imported so the telemetry modules need nothing from
1669
+ * the native addon. Left unset, the agent field is dropped from events
1670
+ * rather than falling back to the raw key.
1671
+ */
1672
+ hashAgentKey?: (pubkeyHex: string) => string;
1029
1673
  }
1030
1674
  declare function setupTelemetry(config: TelemetryConfig): void;
1031
1675
  /**
@@ -1049,35 +1693,121 @@ declare function flushTelemetry(): Promise<void>;
1049
1693
  declare function shutdownTelemetry(): Promise<void>;
1050
1694
 
1051
1695
  /**
1052
- * Signs `log_encrypted_tool_call` — the ciphertext-only counterpart of
1053
- * `log_tool_call`. Rust core owns the encrypt + hash + GTX sign flow;
1054
- * this module is a thin wrapper. Browser callers get the pure-TS shim
1055
- * at `src-ts/browser/encrypted-toolcall.ts` (NAPI can't run in-browser).
1696
+ * Wide events: one row per SDK operation, alongside the counters.
1697
+ *
1698
+ * The counters in `telemetry.ts` are aggregated inside this process before
1699
+ * export, so Honeycomb never receives a per-operation row. That is fine for
1700
+ * "is it up" and useless for everything else: a question nobody attached an
1701
+ * attribute for in advance cannot be answered later, because the data to
1702
+ * answer it never existed. "Which org sees the most failures", "what preceded
1703
+ * a fail-closed", "is the new release slower" are all that shape.
1704
+ *
1705
+ * So each operation also emits one event carrying its whole context. The
1706
+ * counters stay — they are the right tool for health boards and alerts, and
1707
+ * they are not sampled.
1708
+ *
1709
+ * TWO RULES SHAPE THIS MODULE.
1710
+ *
1711
+ * The payload is METADATA ONLY, and the allowlist below is the whole of it:
1712
+ * function name, outcome, latency, error class, error status and path, SDK
1713
+ * version, chain, and a hash of the agent key. Action text, tool arguments, policy text, organisation
1714
+ * names and keys never appear — not as a field that is emptied, but as a field
1715
+ * that does not exist, so there is nothing to forget to strip.
1716
+ *
1717
+ * The agent key is hashed, never sent raw, and the hasher is INJECTED rather
1718
+ * than imported: this module needs nothing from the native addon. With no
1719
+ * hasher supplied the field is DROPPED rather than falling back to the raw
1720
+ * key, so the guarantee does not depend on wiring somebody could forget.
1721
+ *
1722
+ * NOTE on what hashing does and does not buy. Agent pubkeys are public on
1723
+ * chain and can be enumerated, so an UNSALTED hash is reversible by anyone who
1724
+ * can read the chain: it is de-identification, not anonymisation. A salted
1725
+ * hasher makes it uncorrelatable and costs cross-deployment joins. Which one
1726
+ * is wanted is the caller's decision; this module only guarantees the raw key
1727
+ * never leaves.
1728
+ *
1729
+ * And the richer payload has its own switch. Counters carry a function name
1730
+ * and a source. These events carry agent and org identity off a customer's
1731
+ * machine, which is a different proposition for a security product, so
1732
+ * `ATBASH_TELEMETRY_EVENTS_DISABLED=1` turns them off while leaving the health
1733
+ * counters on. Both remain subject to the existing global opt-out.
1056
1734
  */
1057
- /** Plaintext fields of a tool call, sealed into a single ECIES payload. */
1058
- interface ToolCallPlaintext {
1059
- tool_name: string;
1060
- action: string;
1061
- context: string;
1062
- tool_args_json: string;
1735
+
1736
+ /** Attributes an event may carry. Anything not listed here cannot be emitted. */
1737
+ interface OperationEvent {
1738
+ /** SDK function, matching the counter's `function.name` so the two join. */
1739
+ functionName: string;
1740
+ durationMs: number;
1741
+ status: "success" | "error";
1742
+ /**
1743
+ * Hex pubkey of the agent the call was made as. Hashed before it is emitted,
1744
+ * and omitted entirely when no hasher is configured.
1745
+ */
1746
+ agentPubkey?: string;
1747
+ /**
1748
+ * Blockchain RID of the chain this call concerned.
1749
+ *
1750
+ * The RID rather than a network label: it is public infrastructure, it is
1751
+ * unambiguous, and it identifies the deployment without naming a customer.
1752
+ */
1753
+ chain?: string;
1754
+ /** Constructor name of the thrown error, e.g. `AtbashAPIError`. */
1755
+ errorClass?: string;
1756
+ /** HTTP status, or 0 when the request never completed. */
1757
+ errorStatus?: number;
1758
+ /**
1759
+ * Request path the status came from, e.g. `/api/v1/judge`.
1760
+ *
1761
+ * The class alone does not say what failed: several routes answer one call
1762
+ * and more than one can 404, so `AtbashAPIError` cannot distinguish "this
1763
+ * agent is not registered" from "the chain-routing lookup did not resolve".
1764
+ * The path settles it.
1765
+ *
1766
+ * Carries no query string — `requestPath` strips it at the point the error
1767
+ * is built, because a query string carries org names, tool call ids and the
1768
+ * agent pubkey. Structured and already sanitised, so this stays inside the
1769
+ * metadata rule where an error message would not.
1770
+ */
1771
+ errorPath?: string;
1772
+ /**
1773
+ * Short categorical result, when "success" is too coarse — e.g. a memory
1774
+ * write that scanned clean but never reached the chain.
1775
+ *
1776
+ * Categorical by contract: a value a caller chooses from a fixed vocabulary,
1777
+ * never free text and never anything derived from user content.
1778
+ */
1779
+ outcome?: string;
1063
1780
  }
1781
+ /** Hashes an agent key for emission. Supplied by `setupTelemetry`. */
1782
+ type AgentKeyHasher = (pubkeyHex: string) => string;
1783
+ declare function configureEvents(next: Logger | null, opts?: {
1784
+ version?: string;
1785
+ source?: string;
1786
+ hashAgentKey?: AgentKeyHasher;
1787
+ }): void;
1788
+ /** True when an event would actually be emitted. Exported for tests and callers that build context lazily. */
1789
+ declare function eventsAreOn(): boolean;
1064
1790
  /**
1065
- * Canonical form of an action for the retry-cache hash. Byte-identical to
1066
- * the dashboard's `normalizeActionForHash` — diverging silently breaks
1067
- * YELLOW-hold retry resolution.
1791
+ * Emit one wide event for a completed operation.
1792
+ *
1793
+ * Never throws and never blocks: telemetry failing must not change what the
1794
+ * caller observes. Does nothing when events are off.
1795
+ *
1796
+ * @param e - The completed operation.
1068
1797
  */
1069
- declare function normalizeActionForHash(action: string): string;
1798
+ declare function recordEvent(e: OperationEvent): void;
1799
+
1070
1800
  /**
1071
- * Signed commitment to the plaintext claims the caller sends alongside the
1072
- * ciphertext. The judge reads plaintext from the request body but holds no
1073
- * org key; this hash lets it verify body-vs-ciphertext without decrypting.
1074
- * Byte-identical to the dashboard's `claimHashHex`.
1801
+ * Rust core does the work; `browser/encrypted-toolcall.ts` mirrors it for the
1802
+ * browser and must produce the same columns.
1075
1803
  */
1804
+ /** Must match `column_aad` in the core — the label binds a ciphertext to its column. */
1805
+ declare function columnAad(toolCallId: string, column: string): string;
1806
+ /** Byte-identical to the dashboard's copy — diverging breaks hold-retry resolution. */
1807
+ declare function normalizeActionForHash(action: string): string;
1808
+ /** Lets the judge check the request body against the ciphertext without an org key. */
1076
1809
  declare function claimHashHex(toolName: string, action: string, context: string, toolArgsJson: string): string;
1077
- /**
1078
- * Build + sign a `log_encrypted_tool_call` transaction. Returns hex-encoded
1079
- * signed tx, ready to POST as `signed_log_tool_call`.
1080
- */
1810
+ /** @returns hex-encoded signed tx, ready to POST as `signed_log_tool_call`. */
1081
1811
  declare function signEncryptedToolCall(toolCallId: string, action: string, context: string, toolName: string, toolArgsJson: string, orgEncryptionPubKey: string, privkeyHex: string, blockchainRidHex: string): string;
1082
1812
 
1083
1813
  /**
@@ -1126,6 +1856,29 @@ declare function decryptForOrg(payload: Uint8Array, orgPrivKeyHex: string, aad:
1126
1856
  */
1127
1857
  declare function encryptedLength(plaintextByteLength: number): number;
1128
1858
 
1859
+ /**
1860
+ * atb1.<key-fingerprint>.<claim-hash>.<base64 ciphertext>
1861
+ *
1862
+ * Normative spec: `core/src/crypto_envelope.rs`. This mirrors it for the browser.
1863
+ */
1864
+ interface Envelope {
1865
+ /** First 8 bytes of the recipient public key, hex. May be empty. */
1866
+ keyFingerprint: string;
1867
+ /** Commitment to the accompanying plaintext claims. May be empty. */
1868
+ claimHash: string;
1869
+ /** Raw ECIES payload. */
1870
+ payload: Uint8Array;
1871
+ }
1872
+ declare function packEnvelope(payload: Uint8Array, keyFingerprint?: string, claimHash?: string): string;
1873
+ /**
1874
+ * Stays true for a truncated envelope that `parseEnvelope` rejects — a severed
1875
+ * ciphertext is not plaintext, so callers must show a placeholder.
1876
+ */
1877
+ declare function isEnvelope(value: string): boolean;
1878
+ /** Null, not a throw — pre-encryption records are plaintext. */
1879
+ declare function parseEnvelope(value: string): Envelope | null;
1880
+ declare function keyFingerprintOf(pubKeyHex: string): string;
1881
+
1129
1882
  declare function isValidPrivateKey(hex: string): boolean;
1130
1883
  declare function derivePublicKey(privkey: string): string;
1131
1884
  declare function generateKeypair(): KeyPair;
@@ -1141,4 +1894,4 @@ declare function containsSecret(text: string): boolean;
1141
1894
  declare function createMemorySnapshot(entries: MemoryEntry[], takenAt: number): MemorySnapshot;
1142
1895
  declare function diffMemorySnapshots(before: MemorySnapshot, after: MemorySnapshot): MemoryDiffResult;
1143
1896
 
1144
- export { type ActionType, type AgentAuth, type AgentMemoryEntry, type AgentPolicy, type AnomalySeverity, type AnomalyType, Atbash, AtbashAPIError, type AtbashLogger, type AtbashOptions, type AtbashUserConfig, type ChainOpts, type ClassifierToolContext, type ClassifierToolEvent, type ClassifyMemoryReadOptions, type ClassifyMemoryWriteOptions, type ClientSource, type CommitMemoryOptions, DEFAULT_BLOCKCHAIN_RID, DEFAULT_CHROMIA_NODE_URLS, DEFAULT_ENDPOINT, DEFAULT_MEMORY_PATH_PATTERNS, DEFAULT_MEMORY_READ_TOOL_NAMES, DEFAULT_MEMORY_WRITE_TOOL_NAMES, type Decision, type DecisionVerdict, EciesDomain, type EncryptedMemory, type FromConfigOptions, type GuardLogger, type GuardMemoryDecision, type GuardMemoryWriteInput, type GuardMemoryWriteResult, type HeldAction, type HeldActionReview, type HookDecision, type JudgeEndpointConfig, type JudgeOptions, type JudgeResult, type JudgmentState, type JudgmentStatus, type KeyPair, type LogToolCallOptions, type LogToolCallResult, type MemoryAnomaly, type MemoryDiffResult, type MemoryEntry, MemoryGuardManager, type MemoryGuardManagerOptions, MemoryIntegrityError, type MemoryPointer, type MemoryRollbackEvent, type MemoryScanOptions, type MemoryScanResult, type MemoryScanVerdict, type MemorySnapshot, type ModifiedEntry, type Network, type OrgSubscription, PointerStore, type Provider, type PubkeyValue, type RedactResult, type RollbackMemoryOptions, type SecretKind, type SecretMatch, SignatureVerificationError, type Subscription, type SyncMemoryOptions, type SyncMemoryResult, type TelemetryConfig, type TierInfo, type ToolCallFull, type ToolCallInput, type ToolCallPlaintext, type ToolCallRecord, type ValidatedEndpoint, type Verdict, type WrappedLogger, claimHashHex, classifyMemoryRead, classifyMemoryWrite, commitMemoryVersion, containsEvasionCharacters, containsSecret, createFileLogger, createMemoryGuardManager, createMemorySnapshot, decryptForOrg, decryptMemoryContent, defaultPluginLogPath, defaultPointerPath, deriveMemoryKey, derivePublicKey, diffMemorySnapshots, encryptForOrg, encryptMemoryContent, encryptedLength, flushTelemetry, generateKeypair, getActiveMemory, getActiveMemoryId, getAllAgentMemory, getConfigDir, getConfigPath, getMemoryById, getMemoryHistory, getRollbackHistory, guardMemoryWrite, isValidPrivateKey, loadAgent, loadAgentFromFile, loadUserConfig, normalizeActionForHash, normalizeForMatching, normalizeStatus, normalizeVerdict, pubkeyToHex, recordCall, recordDuration, redactJsonStrings, redactSecrets, resolve, resolveKeyPath, rollbackMemory, saveUserConfig, scanMemory, scanMemoryBatch, setupTelemetry, shutdownTelemetry, signEncryptedToolCall, signJudgeAction, signLogToolCall, syncLocalMemory, validateJudgeEndpoint, verifyJudgeResponseSignature, verifySignature };
1897
+ export { type ActionType, type AgentAuth, type AgentLookupOptions, type AgentMemoryEntry, type AgentPolicy, type AnomalySeverity, type AnomalyType, Atbash, AtbashAPIError, type AtbashLogger, type AtbashOptions, type AtbashUserConfig, BOOT_SYNC_HINT, type ChainConfig, type ChainOpts, type ClassifierToolContext, type ClassifierToolEvent, type ClassifyMemoryReadOptions, type ClassifyMemoryWriteOptions, type ClientSource, type CommitMemoryOptions, DEFAULT_BLOCKCHAIN_RID, DEFAULT_CHROMIA_NODE_URLS, DEFAULT_ENDPOINT, DEFAULT_MEMORY_PATH_PATTERNS, DEFAULT_MEMORY_READ_TOOL_NAMES, DEFAULT_MEMORY_WRITE_TOOL_NAMES, type Decision, type DecisionVerdict, EciesDomain, type EncryptedMemory, type Envelope, type FromConfigOptions, type GuardLogger, type GuardMemoryDecision, type GuardMemoryWriteInput, type GuardMemoryWriteResult, type HeldAction, type HeldActionReview, type HookDecision, HttpClient, HttpTransportError, type JudgeEndpointConfig, type JudgeOptions, type JudgeResult, type JudgmentState, type JudgmentStatus, KEY_FILENAMES, type KeyPair, type LogToolCallOptions, type LogToolCallResult, type MemoryAnomaly, type MemoryDiffResult, type MemoryEntry, MemoryGuardManager, type MemoryGuardManagerOptions, MemoryIntegrityError, type MemoryPointer, type MemoryRollbackEvent, type MemoryScanOptions, type MemoryScanResult, type MemoryScanVerdict, type MemorySnapshot, type ModifiedEntry, type Network, type OperationEvent, type OrgSubscription, PRIVATE_CHAIN, PUBLIC_CHAIN, PointerStore, type Provider, type PubkeyValue, type RedactResult, type RollbackMemoryOptions, type SecretKind, type SecretMatch, SignatureVerificationError, type Subscription, type SyncMemoryOptions, type SyncMemoryResult, type TelemetryConfig, type TierInfo, type ToolCallFull, type ToolCallInput, type ToolCallRecord, type ValidatedEndpoint, type Verdict, type WrappedLogger, bootSyncFailureLine, buildAllowedJudgeHosts, canonicalAllow, chainForNetwork, chooseKeyPath, claimHashHex, classifyMemoryRead, classifyMemoryWrite, columnAad, commitMemoryVersion, configureEvents, containsEvasionCharacters, containsSecret, createFileLogger, createMemoryGuardManager, createMemorySnapshot, decryptForOrg, decryptMemoryContent, defaultPluginLogPath, defaultPointerPath, deriveMemoryKey, derivePublicKey, diffMemorySnapshots, encryptForOrg, encryptMemoryContent, encryptedLength, eventsAreOn, flushTelemetry, generateKeypair, getActiveAgentMemory, getActiveMemoryId, getConfigDir, getConfigPath, getMemoryById, getMemoryHistory, getRecentAgentMemory, getRollbackHistory, guardMemoryWrite, isEnvelope, isValidPrivateKey, keyFingerprintOf, keyPathCandidates, loadAgent, loadAgentFromFile, loadUserConfig, normalizeActionForHash, normalizeActionType, normalizeForMatching, normalizeStatus, normalizeVerdict, packEnvelope, parseEnvelope, pubkeyToHex, recordCall, recordDuration, recordEvent, redactJsonStrings, redactSecrets, resolve, resolveKeyPath, rollbackMemory, saveUserConfig, scanMemory, scanMemoryBatch, setupTelemetry, shutdownTelemetry, signEncryptedToolCall, signJudgeAction, signLogToolCall, syncLocalMemory, validateJudgeEndpoint, verifyJudgeResponseSignature, verifySignature };