@atbash/sdk 0.18.0-dev.0 → 0.19.1-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/README.md CHANGED
@@ -29,18 +29,15 @@ const result = await atbash.judgeAction(
29
29
  "Outbound AML check — new recipient, over threshold",
30
30
  );
31
31
 
32
- // 3. Enforce the verdict
33
- switch (result.verdict) {
34
- case "ALLOW":
35
- // Proceed with the action
36
- break;
37
- case "HOLD":
38
- // Held — operator must approve in the dashboard
39
- console.log("Held for review:", result.toolCallId);
40
- break;
41
- case "BLOCK":
42
- // Refused — agent is jailed in Enforcement tier
43
- throw new Error(`Blocked: ${result.reason}`);
32
+ // 3. Act on the decision. `allow` is the gate; `verdict` says why.
33
+ if (result.allow) {
34
+ // Proceed with the action
35
+ } else if (result.verdict === "HOLD") {
36
+ // Held — operator must approve in the dashboard
37
+ console.log("Held for review:", result.toolCallId);
38
+ } else {
39
+ // Refused — agent is jailed in Enforcement tier
40
+ throw new Error(`Blocked: ${result.reason}`);
44
41
  }
45
42
  ```
46
43
 
@@ -85,6 +82,8 @@ Every `judgeAction` call returns one of three verdicts:
85
82
  | `HOLD` | Requires operator review | Pause — poll `getJudgmentStatus` until resolved |
86
83
  | `BLOCK` | Violates a red line | Abort — agent is jailed in Enforcement tier |
87
84
 
85
+ > **Gate on `allow`, not on `verdict`.** `allow` is the canonical permission the SDK computes from every signal in the response; `verdict` is one of those signals, and reads as the reason. The two differ when an organisation does not enforce an agent's verdicts — Shadow Mode on an agent, or org-wide Monitor mode — where the action is judged and recorded on chain but deliberately not applied. Branching on `verdict` re-derives a denial the organisation chose not to make.
86
+
88
87
  > **NB:** If your org has no active subscription, the judge returns `"No verdict"` — actions are logged on-chain for the audit trail but not evaluated by an AI provider. Assign a plan at [atbash.ai/risk-engine/settings](https://atbash.ai/risk-engine/settings) for active verdicts.
89
88
 
90
89
  ## API
@@ -195,3 +194,67 @@ Policy authoring, operator reviews, and agent management happen at [atbash.ai](h
195
194
  ## License
196
195
 
197
196
  Licensed under the [Apache License, Version 2.0](LICENSE). See [NOTICE](NOTICE) for attribution. Versions published before this change remain available under the license they shipped with.
197
+
198
+ ## Telemetry — what the SDK sends
199
+
200
+ The SDK reports how its own functions perform. It runs inside your agents and
201
+ the data leaves your network, so the payload is **metadata only** and the list
202
+ below is the whole of it.
203
+
204
+ **Sent**
205
+
206
+ | Field | Example |
207
+ | --- | --- |
208
+ | `function.name` | `judgeAction`, `memory.memory-add` |
209
+ | `status` / `outcome` | `success`, `error` |
210
+ | `duration_ms` | `142` |
211
+ | `error.class` | `AtbashAPIError` |
212
+ | `error.status` / `error.path` | `404`, `/api/v1/judge` — the route, never its query string |
213
+ | `sdk.version` | `0.18.0` |
214
+ | `chain` | the blockchain RID — public infrastructure, and it names no customer |
215
+ | `agent.hash` | SHA-256 of the agent's **public** key, never the key itself |
216
+ | `source` | `sdk`, `cli`, `plugin:openclaw` |
217
+
218
+ **Never sent.** Action text, tool arguments, policy text, organisation names,
219
+ memory file paths, memory contents, response bodies, and keys of any kind.
220
+ `error.path` is the route alone — the query string is stripped where the error
221
+ is built, because it carries org names, tool call ids and the agent pubkey. These are not fields
222
+ that get emptied before sending — there is no field for them to go in.
223
+
224
+ `agent.hash` is de-identification, not anonymisation: agent public keys are
225
+ readable from the chain, so anyone holding that list can match a hash back to
226
+ an agent. Supply your own salted hasher to `setupTelemetry({ hashAgentKey })`
227
+ if you need it to be uncorrelatable.
228
+
229
+ ### Turning it off
230
+
231
+ ```bash
232
+ export ATBASH_TELEMETRY_DISABLED=1
233
+ ```
234
+
235
+ or create `~/.config/atbash/telemetry.json` containing `{ "enabled": false }`.
236
+
237
+ **On a Private deployment, telemetry may already be off for everyone.** The operator disables collection by leaving `HONEYCOMB_API_KEY` unset on their Atbash service; the telemetry route then accepts the request and discards the payload, so nothing is forwarded and agents never see an error or retry. That is an operator setting on the deployment, not one you control from the agent — the switches above are yours.
238
+
239
+ To keep the operational counters but drop the richer per-operation events:
240
+
241
+ ```bash
242
+ export ATBASH_TELEMETRY_EVENTS_DISABLED=1
243
+ ```
244
+
245
+ ### Sending it to your own collector
246
+
247
+ Point the SDK at any OTLP endpoint you control. Atbash is then bypassed
248
+ entirely and **no Atbash credential is created or transmitted** — your headers
249
+ are the only ones sent.
250
+
251
+ ```bash
252
+ export ATBASH_TELEMETRY_COLLECTOR_URL=https://otel.yourcompany.example
253
+ export ATBASH_TELEMETRY_COLLECTOR_HEADERS='{"x-api-key":"your-key"}'
254
+ ```
255
+
256
+ Metrics go to `/v1/metrics` and events to `/v1/logs` beneath that URL. `https`
257
+ is required; a collector on `localhost` additionally needs
258
+ `ATBASH_TELEMETRY_ALLOW_LOOPBACK=1`, so a mistyped URL cannot silently turn
259
+ exporting off. A URL that is refused raises at setup rather than quietly
260
+ falling back to Atbash.
@@ -1,3 +1,5 @@
1
+ import { Logger } from '@opentelemetry/api-logs';
2
+
1
3
  type JudgeEndpointConfig = {
2
4
  policy?: "default";
3
5
  endpoint?: string;
@@ -1233,6 +1235,19 @@ interface GuardMemoryWriteResult {
1233
1235
  /** Present when handled. True only after the chain transaction confirms. */
1234
1236
  committed?: boolean;
1235
1237
  }
1238
+ /**
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.
1243
+ *
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.
1247
+ *
1248
+ * @param input - The tool call and the agent to judge it for.
1249
+ * @returns What the guard decided; unchanged by the recording.
1250
+ */
1236
1251
  declare function guardMemoryWrite(input: GuardMemoryWriteInput): Promise<GuardMemoryWriteResult>;
1237
1252
 
1238
1253
  /**
@@ -1641,6 +1656,20 @@ interface TelemetryConfig {
1641
1656
  * `{ Authorization: "Bearer <hex>" }`). Required to actually export.
1642
1657
  */
1643
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;
1644
1673
  }
1645
1674
  declare function setupTelemetry(config: TelemetryConfig): void;
1646
1675
  /**
@@ -1663,6 +1692,121 @@ declare function flushTelemetry(): Promise<void>;
1663
1692
  */
1664
1693
  declare function shutdownTelemetry(): Promise<void>;
1665
1694
 
1695
+ /**
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.
1734
+ */
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;
1780
+ }
1781
+ /** Hashes an agent key for emission. Supplied by `setupTelemetry`. */
1782
+ type AgentKeyHasher = (pubkeyHex: string) => string;
1783
+ /**
1784
+ * Point this module at a logger, or at nothing.
1785
+ *
1786
+ * Called by `setupTelemetry` once the shared exporter exists, so events and
1787
+ * counters travel the same authenticated route and share one opt-out decision.
1788
+ *
1789
+ * @param next - Logger to emit through, or null to disable.
1790
+ * @param opts - Version and source stamped on every event, and the hasher
1791
+ * applied to the agent key. Without a hasher, the agent field is dropped.
1792
+ */
1793
+ declare function configureEvents(next: Logger | null, opts?: {
1794
+ version?: string;
1795
+ source?: string;
1796
+ hashAgentKey?: AgentKeyHasher;
1797
+ }): void;
1798
+ /** True when an event would actually be emitted. Exported for tests and callers that build context lazily. */
1799
+ declare function eventsAreOn(): boolean;
1800
+ /**
1801
+ * Emit one wide event for a completed operation.
1802
+ *
1803
+ * Never throws and never blocks: telemetry failing must not change what the
1804
+ * caller observes. Does nothing when events are off.
1805
+ *
1806
+ * @param e - The completed operation.
1807
+ */
1808
+ declare function recordEvent(e: OperationEvent): void;
1809
+
1666
1810
  /**
1667
1811
  * Rust core does the work; `browser/encrypted-toolcall.ts` mirrors it for the
1668
1812
  * browser and must produce the same columns.
@@ -1760,4 +1904,4 @@ declare function containsSecret(text: string): boolean;
1760
1904
  declare function createMemorySnapshot(entries: MemoryEntry[], takenAt: number): MemorySnapshot;
1761
1905
  declare function diffMemorySnapshots(before: MemorySnapshot, after: MemorySnapshot): MemoryDiffResult;
1762
1906
 
1763
- 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 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, containsEvasionCharacters, containsSecret, createFileLogger, createMemoryGuardManager, createMemorySnapshot, decryptForOrg, decryptMemoryContent, defaultPluginLogPath, defaultPointerPath, deriveMemoryKey, derivePublicKey, diffMemorySnapshots, encryptForOrg, encryptMemoryContent, encryptedLength, 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, redactJsonStrings, redactSecrets, resolve, resolveKeyPath, rollbackMemory, saveUserConfig, scanMemory, scanMemoryBatch, setupTelemetry, shutdownTelemetry, signEncryptedToolCall, signJudgeAction, signLogToolCall, syncLocalMemory, validateJudgeEndpoint, verifyJudgeResponseSignature, verifySignature };
1907
+ 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 };
package/dist/browser.mjs CHANGED
@@ -40585,7 +40585,7 @@ function requireFailoverStrategies() {
40585
40585
  exports.createTimeoutPromise = createTimeoutPromise;
40586
40586
  const utils_12 = requireUtils$1();
40587
40587
  const httpUtil_1 = httpUtil;
40588
- const logger2 = __importStar2(logger$3);
40588
+ const logger3 = __importStar2(logger$3);
40589
40589
  const http_utils_1 = httpUtils;
40590
40590
  function getUnreachableDuration(statusCode, error4) {
40591
40591
  if ((0, http_utils_1.isNetworkError)(error4)) {
@@ -40686,7 +40686,7 @@ function requireFailoverStrategies() {
40686
40686
  while (true) {
40687
40687
  if (groupedSuccessfulResponses.maxNumberOfEqualResponses() >= bftMajorityThreshold) {
40688
40688
  if (groupedSuccessfulResponses.numberOfDistinctResponses() > 1) {
40689
- logger2.warning(`Got disagreeing responses, but could still reach BFT majority`);
40689
+ logger3.warning(`Got disagreeing responses, but could still reach BFT majority`);
40690
40690
  }
40691
40691
  return groupedSuccessfulResponses.majorityResponse();
40692
40692
  }
@@ -40724,7 +40724,7 @@ function requireFailoverStrategies() {
40724
40724
  if (!isError4 && !error4) {
40725
40725
  return { error: error4, statusCode, rspBody, transactionTimestamp };
40726
40726
  }
40727
- logger2.info(`${method} request failed on ${endpoint.url}. Attempt: ${attempt + 1} / ${config2.attemptsPerEndpoint}`);
40727
+ logger3.info(`${method} request failed on ${endpoint.url}. Attempt: ${attempt + 1} / ${config2.attemptsPerEndpoint}`);
40728
40728
  yield (0, utils_12.sleep)(config2.attemptInterval);
40729
40729
  }
40730
40730
  return { error: error4, statusCode, rspBody, transactionTimestamp };
@@ -40760,7 +40760,7 @@ function requireFailoverStrategies() {
40760
40760
  if (isServerError) {
40761
40761
  const duration = getUnreachableDuration(statusCode !== null && statusCode !== void 0 ? statusCode : void 0, response.error);
40762
40762
  nodeManager2.makeNodeUnavailable(node2.url, duration);
40763
- logger2.info(`request failed with server error on ${node2.url} trying next node`);
40763
+ logger3.info(`request failed with server error on ${node2.url} trying next node`);
40764
40764
  break;
40765
40765
  }
40766
40766
  if (isClientError) {
@@ -40768,11 +40768,11 @@ function requireFailoverStrategies() {
40768
40768
  return { error: error4, statusCode, rspBody, transactionTimestamp };
40769
40769
  }
40770
40770
  if (!nodeManager2.useStickyNode) {
40771
- logger2.info(`request failed client error on ${node2.url} trying next node`);
40771
+ logger3.info(`request failed client error on ${node2.url} trying next node`);
40772
40772
  break;
40773
40773
  }
40774
40774
  }
40775
- logger2.info(`${method} request failed on ${node2.url}. Attempt: ${attempt + 1} / ${config2.attemptsPerEndpoint}`);
40775
+ logger3.info(`${method} request failed on ${node2.url}. Attempt: ${attempt + 1} / ${config2.attemptsPerEndpoint}`);
40776
40776
  if (config2.attemptInterval > 0) {
40777
40777
  yield (0, utils_12.sleep)(config2.attemptInterval);
40778
40778
  }
@@ -41001,7 +41001,7 @@ function requireUtils$1() {
41001
41001
  exports.getBlockchainRidFromIid = getBlockchainRidFromIid;
41002
41002
  exports.convertToPrintable = convertToPrintable;
41003
41003
  const buffer_12 = buffer$3;
41004
- const logger2 = __importStar2(logger$3);
41004
+ const logger3 = __importStar2(logger$3);
41005
41005
  const errors_12 = errors;
41006
41006
  const gtxTool = __importStar2(gtx$1);
41007
41007
  const errors_2 = errors;
@@ -41084,7 +41084,7 @@ function requireUtils$1() {
41084
41084
  function validTxRid(txRID) {
41085
41085
  if (txRID.length != 32) {
41086
41086
  const error4 = new errors_12.InvalidTxRidException(txRID);
41087
- logger2.error(error4.toString());
41087
+ logger3.error(error4.toString());
41088
41088
  return false;
41089
41089
  }
41090
41090
  return true;
@@ -41487,7 +41487,7 @@ function requireUtils$1() {
41487
41487
  function handleGetResponse(error4, statusCode, responseObject, callback) {
41488
41488
  try {
41489
41489
  const responseObjectPrintable = convertToPrintable(responseObject);
41490
- logger2.debug(`error: ${error4}, status code: ${statusCode}, response body: ${responseObjectPrintable}`);
41490
+ logger3.debug(`error: ${error4}, status code: ${statusCode}, response body: ${responseObjectPrintable}`);
41491
41491
  if (error4) {
41492
41492
  return callback(error4, null);
41493
41493
  }
@@ -41497,16 +41497,16 @@ function requireUtils$1() {
41497
41497
  return callback(null, responseObject);
41498
41498
  } catch (error5) {
41499
41499
  callback(error5, null);
41500
- logger2.error(`client.handleGetResponse(): Failed to call the callback function. ${error5}`);
41500
+ logger3.error(`client.handleGetResponse(): Failed to call the callback function. ${error5}`);
41501
41501
  }
41502
41502
  }
41503
41503
  function handlePostResponse(error4, statusCode, responseObject, callback) {
41504
41504
  var _a2;
41505
41505
  const responseObjectPrintable = convertToPrintable(responseObject);
41506
- logger2.debug(`error: ${error4}, status code: ${statusCode}, response body: ${responseObjectPrintable}`);
41506
+ logger3.debug(`error: ${error4}, status code: ${statusCode}, response body: ${responseObjectPrintable}`);
41507
41507
  try {
41508
41508
  if (error4) {
41509
- logger2.error(`In client post(). ${error4}`);
41509
+ logger3.error(`In client post(). ${error4}`);
41510
41510
  callback(error4, null);
41511
41511
  } else if (!statusCode || !(0, http_utils_1.isSuccessfulStatusCode)(statusCode)) {
41512
41512
  let errorMessage2 = `Unexpected status code from server. Code: ${statusCode}.`;
@@ -41514,14 +41514,14 @@ function requireUtils$1() {
41514
41514
  if (responseObjectPrintable) {
41515
41515
  errorMessage2 += ` Message: ${responseObjectPrintable}.`;
41516
41516
  }
41517
- logger2.error(errorMessage2);
41517
+ logger3.error(errorMessage2);
41518
41518
  callback(new errors_12.UnexpectedStatusError(statusCode !== null && statusCode !== void 0 ? statusCode : 400, errorMessageInResponseBody || responseObjectPrintable), responseObject);
41519
41519
  } else {
41520
- logger2.info(`Calling responseCallback with responseObject: ${responseObjectPrintable}`);
41520
+ logger3.info(`Calling responseCallback with responseObject: ${responseObjectPrintable}`);
41521
41521
  callback(null, responseObject);
41522
41522
  }
41523
41523
  } catch (error5) {
41524
- logger2.error(`client.handlePostResponse(): Failed to call callback function ${error5}`);
41524
+ logger3.error(`client.handlePostResponse(): Failed to call callback function ${error5}`);
41525
41525
  }
41526
41526
  }
41527
41527
  function getBlockchainRidFromIid(_a2) {
@@ -42259,7 +42259,7 @@ function requireBlockchainClient() {
42259
42259
  const crypto_12 = requireCryptoBrowserify();
42260
42260
  const cloneDeep_1$1 = __importDefault2(cloneDeep_1);
42261
42261
  const IccfProofTxMaterialBuilder_1 = requireIccfProofTxMaterialBuilder();
42262
- const logger2 = __importStar2(logger$3);
42262
+ const logger3 = __importStar2(logger$3);
42263
42263
  const formatter_12 = formatter;
42264
42264
  const serialization_12 = serialization$2;
42265
42265
  const gtxTool = __importStar2(gtx$1);
@@ -42301,7 +42301,7 @@ function requireBlockchainClient() {
42301
42301
  },
42302
42302
  signTransaction(transaction, signMethod2, callback) {
42303
42303
  return __awaiter2(this, void 0, void 0, function* () {
42304
- logger2.debug(`Signing transaction with ${!(0, utils_12.isKeyPair)(signMethod2) ? "signature provider containing " : ""}pubKey: ${(0, formatter_12.toString)(signMethod2.pubKey)}`);
42304
+ logger3.debug(`Signing transaction with ${!(0, utils_12.isKeyPair)(signMethod2) ? "signature provider containing " : ""}pubKey: ${(0, formatter_12.toString)(signMethod2.pubKey)}`);
42305
42305
  const gtx2 = (0, utils_12.getGTXFromBufferOrTransactionOrOperation)(transaction, config2.blockchainRid);
42306
42306
  try {
42307
42307
  const signedTx = yield (0, utils_12.isKeyPair)(signMethod2) ? gtxTool.sign(gtx2, signMethod2.privKey, config2.merkleHashVersion, signMethod2.pubKey) : gtxTool.sign(gtx2, signMethod2, config2.merkleHashVersion);
@@ -42326,7 +42326,7 @@ function requireBlockchainClient() {
42326
42326
  const promiEvent = new promiEvents_12.Web3PromiEvent((resolve2, reject) => __awaiter2(this, void 0, void 0, function* () {
42327
42327
  let error4 = null;
42328
42328
  for (let i = 0; i < config2.retryTransactionPolling.count; i++) {
42329
- logger2.info(`sendTransactionWithRetries attempt ${i + 1} of ${config2.retryTransactionPolling.count}`);
42329
+ logger3.info(`sendTransactionWithRetries attempt ${i + 1} of ${config2.retryTransactionPolling.count}`);
42330
42330
  const tmpPromiEvent = sendOnce().on(enums_12.TransactionEvent.ClusterAnchoringConfirmation, (receipt) => {
42331
42331
  promiEvent.emit(enums_12.TransactionEvent.ClusterAnchoringConfirmation, receipt);
42332
42332
  }).on(enums_12.TransactionEvent.SystemAnchoringConfirmation, (receipt) => {
@@ -42338,7 +42338,7 @@ function requireBlockchainClient() {
42338
42338
  }).on(enums_12.TransactionEvent.DappConfirmed, (receipt) => {
42339
42339
  promiEvent.emit(enums_12.TransactionEvent.DappConfirmed, receipt);
42340
42340
  }).catch((tmpError) => {
42341
- logger2.warning(`sendTransactionWithRetries error on attempt ${i + 1} of ${config2.retryTransactionPolling.count}: ${tmpError}`);
42341
+ logger3.warning(`sendTransactionWithRetries error on attempt ${i + 1} of ${config2.retryTransactionPolling.count}: ${tmpError}`);
42342
42342
  promiEvent.emit(enums_12.TransactionEvent.Rejected, tmpError);
42343
42343
  error4 = tmpError;
42344
42344
  });
@@ -43073,11 +43073,11 @@ function requireDecodeTransactionToGtx() {
43073
43073
  Object.defineProperty(decodeTransactionToGtx, "__esModule", { value: true });
43074
43074
  decodeTransactionToGtx.decodeTransactionToGtx = decodeTransactionToGtx$1;
43075
43075
  const gtxTool = __importStar2(gtx$1);
43076
- const logger2 = __importStar2(logger$3);
43076
+ const logger3 = __importStar2(logger$3);
43077
43077
  const dump_1 = requireDump();
43078
43078
  function decodeTransactionToGtx$1(encodedTransaction) {
43079
43079
  const gtx2 = gtxTool.deserialize(encodedTransaction);
43080
- logger2.debug(`Output from deserializing a raw transaction: ${(0, dump_1.dumpGtv)(gtx2)}`);
43080
+ logger3.debug(`Output from deserializing a raw transaction: ${(0, dump_1.dumpGtv)(gtx2)}`);
43081
43081
  return gtx2;
43082
43082
  }
43083
43083
  return decodeTransactionToGtx;
@@ -44625,9 +44625,19 @@ init_define_ATBASH_PRIVATE_NODE_URLS();
44625
44625
  // src-ts/normalize.ts
44626
44626
  init_define_ATBASH_CHROMIA_NODE_URLS();
44627
44627
  init_define_ATBASH_PRIVATE_NODE_URLS();
44628
+ function wireText(raw2, fallback = "") {
44629
+ if (raw2 === null || raw2 === void 0) return fallback;
44630
+ if (typeof raw2 === "object" || typeof raw2 === "function") return fallback;
44631
+ return String(raw2);
44632
+ }
44633
+ function wireNumber(raw2, fallback = 0) {
44634
+ if (raw2 === null || raw2 === void 0) return fallback;
44635
+ if (typeof raw2 === "object" || typeof raw2 === "function") return fallback;
44636
+ return Number(raw2);
44637
+ }
44628
44638
  function normalizeVerdict(raw2) {
44629
44639
  if (raw2 === null || raw2 === void 0) return "No verdict";
44630
- const v = String(raw2).trim().toUpperCase();
44640
+ const v = wireText(raw2).trim().toUpperCase();
44631
44641
  if (v === "ALLOW" || v === "GREEN") return "ALLOW";
44632
44642
  if (v === "HOLD" || v === "YELLOW") return "HOLD";
44633
44643
  if (v === "BLOCK" || v === "RED") return "BLOCK";
@@ -44637,7 +44647,7 @@ function normalizeActionType(raw2) {
44637
44647
  return typeof raw2 === "string" ? raw2.trim().toLowerCase() : "";
44638
44648
  }
44639
44649
  function normalizeStatus(raw2) {
44640
- const s2 = String(raw2 ?? "").toLowerCase();
44650
+ const s2 = wireText(raw2).toLowerCase();
44641
44651
  if (s2 === "pending" || s2 === "answered" || s2 === "error") return s2;
44642
44652
  return "error";
44643
44653
  }
@@ -44724,6 +44734,61 @@ function canonicalAllow(data) {
44724
44734
  return verdict === "ALLOW" || verdict === "GREEN";
44725
44735
  }
44726
44736
 
44737
+ // src-ts/opentel/events.ts
44738
+ init_define_ATBASH_CHROMIA_NODE_URLS();
44739
+ init_define_ATBASH_PRIVATE_NODE_URLS();
44740
+ var SLOW_MS = 1500;
44741
+ var SUCCESS_SAMPLE_RATE = 10;
44742
+ var logger2 = null;
44743
+ var eventsEnabled = false;
44744
+ var sdkVersion = "";
44745
+ var eventSource = "sdk";
44746
+ var hashAgentKey = null;
44747
+ function eventsOptedOut() {
44748
+ const flag = process.env.ATBASH_TELEMETRY_EVENTS_DISABLED?.trim().toLowerCase();
44749
+ return Boolean(flag && ["1", "true", "yes", "on"].includes(flag));
44750
+ }
44751
+ function configureEvents(next, opts = {}) {
44752
+ logger2 = next;
44753
+ eventsEnabled = next !== null && !eventsOptedOut();
44754
+ if (opts.version) sdkVersion = opts.version;
44755
+ if (opts.source) eventSource = opts.source;
44756
+ hashAgentKey = opts.hashAgentKey ?? null;
44757
+ }
44758
+ function eventsAreOn() {
44759
+ return eventsEnabled && logger2 !== null;
44760
+ }
44761
+ function sampleRateFor(e) {
44762
+ if (e.status === "error") return 1;
44763
+ if (e.durationMs >= SLOW_MS) return 1;
44764
+ return Math.random() < 1 / SUCCESS_SAMPLE_RATE ? SUCCESS_SAMPLE_RATE : 0;
44765
+ }
44766
+ function recordEvent(e) {
44767
+ if (!eventsAreOn()) return;
44768
+ const sampleRate = sampleRateFor(e);
44769
+ if (sampleRate === 0) return;
44770
+ try {
44771
+ const attributes = {
44772
+ "function.name": e.functionName,
44773
+ "duration_ms": Math.round(e.durationMs),
44774
+ status: e.status,
44775
+ source: eventSource,
44776
+ // Honeycomb reweights counts and percentiles by this. It is per-event
44777
+ // because the sampling is biased by outcome and latency.
44778
+ sampleRate
44779
+ };
44780
+ if (sdkVersion) attributes["sdk.version"] = sdkVersion;
44781
+ if (e.agentPubkey && hashAgentKey) attributes["agent.hash"] = hashAgentKey(e.agentPubkey);
44782
+ if (e.chain) attributes["chain"] = e.chain;
44783
+ if (e.errorClass) attributes["error.class"] = e.errorClass;
44784
+ if (e.errorStatus !== void 0) attributes["error.status"] = e.errorStatus;
44785
+ if (e.errorPath) attributes["error.path"] = e.errorPath;
44786
+ if (e.outcome) attributes["outcome"] = e.outcome;
44787
+ logger2.emit({ body: e.functionName, attributes });
44788
+ } catch {
44789
+ }
44790
+ }
44791
+
44727
44792
  // src-ts/browser/opentel/telemetry.ts
44728
44793
  init_define_ATBASH_CHROMIA_NODE_URLS();
44729
44794
  init_define_ATBASH_PRIVATE_NODE_URLS();
@@ -44791,6 +44856,13 @@ function resolveConstructorChain(options) {
44791
44856
  }
44792
44857
  return chainForNetwork(options.network ?? forcedChainNetwork() ?? "private");
44793
44858
  }
44859
+ function errorDetail(err) {
44860
+ if (!(err instanceof AtbashAPIError)) return {};
44861
+ return {
44862
+ errorStatus: err.status,
44863
+ ...err.path ? { errorPath: err.path } : {}
44864
+ };
44865
+ }
44794
44866
  var Atbash = class _Atbash {
44795
44867
  auth;
44796
44868
  endpoint;
@@ -45001,7 +45073,7 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
45001
45073
  *
45002
45074
  * @param logger - The host logger, when one was supplied.
45003
45075
  */
45004
- static warnOnStrandedKey(logger2) {
45076
+ static warnOnStrandedKey(logger3) {
45005
45077
  const stranded = strandedAgentKey();
45006
45078
  if (!stranded) return;
45007
45079
  let identity;
@@ -45010,7 +45082,7 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
45010
45082
  } catch {
45011
45083
  identity = "(unreadable)";
45012
45084
  }
45013
- logger2?.warn?.(
45085
+ logger3?.warn?.(
45014
45086
  `[atbash] "agentKey" in config.json is ignored \u2014 settings do not choose the signing key. It holds ${identity}; this client signs as the key file or ATBASH_AGENT_KEY instead. Move it into the key file, or delete the field, to silence this.`
45015
45087
  );
45016
45088
  }
@@ -45252,15 +45324,15 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
45252
45324
  verdict: normalizeVerdict(data.verdict),
45253
45325
  allow: this.executable(data),
45254
45326
  actionType: normalizeActionType(data.action_type),
45255
- reason: String(data.reason ?? ""),
45256
- confidence: Number(data.confidence ?? 0),
45257
- provider: String(data.provider ?? ""),
45258
- latencyMs: Number(data.latency_ms ?? 0),
45259
- toolCallId: String(data.tool_call_id ?? logResult.toolCallId),
45327
+ reason: wireText(data.reason),
45328
+ confidence: wireNumber(data.confidence),
45329
+ provider: wireText(data.provider),
45330
+ latencyMs: wireNumber(data.latency_ms),
45331
+ toolCallId: wireText(data.tool_call_id, logResult.toolCallId),
45260
45332
  onChain: Boolean(data.on_chain),
45261
45333
  enforced: Boolean(data.enforced),
45262
- enforcementMode: String(data.enforcement_mode ?? ""),
45263
- status: String(data.status ?? ""),
45334
+ enforcementMode: wireText(data.enforcement_mode),
45335
+ status: wireText(data.status),
45264
45336
  score,
45265
45337
  refusal: explicitRefusal(data)
45266
45338
  };
@@ -45484,8 +45556,8 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
45484
45556
  status: normalizeStatus(data.status),
45485
45557
  verdict: normalizeVerdict(data.verdict),
45486
45558
  allow: this.executable(data),
45487
- reason: String(data.reason ?? ""),
45488
- judgmentId: String(data.judgmentId ?? judgmentId),
45559
+ reason: wireText(data.reason),
45560
+ judgmentId: wireText(data.judgmentId, judgmentId),
45489
45561
  onChain: optBool(data.onChain),
45490
45562
  cached: optBool(data.cached),
45491
45563
  responseTimeMs: optNumber(data.responseTimeMs)
@@ -45769,10 +45841,28 @@ config.json still holds an "agentKey" (${identity}), but a settings file no long
45769
45841
  recordCall(name2, void 0, agentPubkey);
45770
45842
  try {
45771
45843
  const result = await fn();
45772
- recordDuration(name2, performance.now() - start, "success");
45844
+ const durationMs = performance.now() - start;
45845
+ recordDuration(name2, durationMs, "success");
45846
+ recordEvent({
45847
+ functionName: name2,
45848
+ durationMs,
45849
+ status: "success",
45850
+ agentPubkey,
45851
+ chain: this.blockchainRid
45852
+ });
45773
45853
  return result;
45774
45854
  } catch (err) {
45775
- recordDuration(name2, performance.now() - start, "error");
45855
+ const durationMs = performance.now() - start;
45856
+ recordDuration(name2, durationMs, "error");
45857
+ recordEvent({
45858
+ functionName: name2,
45859
+ durationMs,
45860
+ status: "error",
45861
+ agentPubkey,
45862
+ chain: this.blockchainRid,
45863
+ errorClass: err instanceof Error ? err.constructor.name : typeof err,
45864
+ ...errorDetail(err)
45865
+ });
45776
45866
  throw err;
45777
45867
  }
45778
45868
  }
@@ -46074,10 +46164,12 @@ function isRecord2(v) {
46074
46164
  }
46075
46165
  function optString(v) {
46076
46166
  if (v === null || v === void 0) return void 0;
46167
+ if (typeof v === "object" || typeof v === "function") return void 0;
46077
46168
  return typeof v === "string" ? v : String(v);
46078
46169
  }
46079
46170
  function optNumber(v) {
46080
46171
  if (v === null || v === void 0) return void 0;
46172
+ if (typeof v === "object" || typeof v === "function") return void 0;
46081
46173
  const n = Number(v);
46082
46174
  return Number.isFinite(n) ? n : void 0;
46083
46175
  }
@@ -46546,6 +46638,7 @@ export {
46546
46638
  classifyMemoryWrite,
46547
46639
  columnAad,
46548
46640
  commitMemoryVersion,
46641
+ configureEvents,
46549
46642
  containsEvasionCharacters2 as containsEvasionCharacters,
46550
46643
  containsSecret2 as containsSecret,
46551
46644
  createFileLogger,
@@ -46561,6 +46654,7 @@ export {
46561
46654
  encryptForOrg,
46562
46655
  encryptMemoryContent2 as encryptMemoryContent,
46563
46656
  encryptedLength,
46657
+ eventsAreOn,
46564
46658
  flushTelemetry,
46565
46659
  generateKeypair2 as generateKeypair,
46566
46660
  getActiveAgentMemory,
@@ -46589,6 +46683,7 @@ export {
46589
46683
  pubkeyToHex,
46590
46684
  recordCall,
46591
46685
  recordDuration,
46686
+ recordEvent,
46592
46687
  redactJsonStrings,
46593
46688
  redactSecrets2 as redactSecrets,
46594
46689
  resolve,