@atbash/sdk 0.3.9-dev.2 → 0.3.9-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -60,6 +60,8 @@ __export(index_exports, {
60
60
  resolve: () => resolve,
61
61
  resolveKeyPath: () => resolveKeyPath,
62
62
  saveUserConfig: () => saveUserConfig,
63
+ setupTelemetry: () => setupTelemetry,
64
+ shutdownTelemetry: () => shutdownTelemetry,
63
65
  toPubkeyHex: () => toPubkeyHex,
64
66
  validateJudgeEndpoint: () => validateJudgeEndpoint,
65
67
  verifyJudgeResponseSignature: () => verifyJudgeResponseSignature
@@ -96,6 +98,69 @@ function verifyJudgeResponseSignature(bodyBytes, signatureHex, pubKeyHex) {
96
98
  return isValid ? { ok: true } : { ok: false, reason: "signature does not verify against configured verifyPubKey" };
97
99
  }
98
100
 
101
+ // src/opentel/telemetry.ts
102
+ var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
103
+ var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
104
+ var import_resources = require("@opentelemetry/resources");
105
+ var meterProvider = null;
106
+ var callCounter = null;
107
+ var durationHistogram = null;
108
+ var defaultSource = "sdk";
109
+ function setupTelemetry(config) {
110
+ if (!config.enabled) return;
111
+ if (meterProvider) return;
112
+ defaultSource = config.source ?? "sdk";
113
+ const ATBASH_HONEYCOMB_KEY = "YOUR_INGEST_KEY_HERE";
114
+ const apiKey = process.env.HONEYCOMB_API_KEY ?? ATBASH_HONEYCOMB_KEY;
115
+ const exporter = new import_exporter_metrics_otlp_http.OTLPMetricExporter({
116
+ url: "https://api.honeycomb.io/v1/metrics",
117
+ headers: {
118
+ "x-honeycomb-team": apiKey
119
+ }
120
+ });
121
+ const reader = new import_sdk_metrics.PeriodicExportingMetricReader({
122
+ exporter,
123
+ exportIntervalMillis: config.exportIntervalMs ?? 6e4
124
+ });
125
+ meterProvider = new import_sdk_metrics.MeterProvider({
126
+ resource: (0, import_resources.resourceFromAttributes)({
127
+ "service.name": "atbash-sdk"
128
+ }),
129
+ readers: [reader]
130
+ });
131
+ const meter = meterProvider.getMeter("atbash-sdk");
132
+ callCounter = meter.createCounter("atbash.sdk.function.calls", {
133
+ description: "Number of SDK function calls"
134
+ });
135
+ durationHistogram = meter.createHistogram("atbash.sdk.function.duration_ms", {
136
+ description: "SDK function execution duration",
137
+ unit: "ms"
138
+ });
139
+ }
140
+ function recordCall(functionName, source, agentPubkey) {
141
+ if (!callCounter) return;
142
+ callCounter.add(1, {
143
+ "function.name": functionName,
144
+ "source": source ?? defaultSource,
145
+ ...agentPubkey && { "agent.pubkey": agentPubkey }
146
+ });
147
+ }
148
+ function recordDuration(functionName, durationMs, status, source) {
149
+ if (!durationHistogram) return;
150
+ durationHistogram.record(durationMs, {
151
+ "function.name": functionName,
152
+ "status": status,
153
+ "source": source ?? defaultSource
154
+ });
155
+ }
156
+ async function shutdownTelemetry() {
157
+ if (!meterProvider) return;
158
+ await meterProvider.shutdown();
159
+ meterProvider = null;
160
+ callCounter = null;
161
+ durationHistogram = null;
162
+ }
163
+
99
164
  // src/client.ts
100
165
  var { createClient, encryption: encryption2, newSignatureProvider } = import_postchain_client2.default;
101
166
  var DEFAULT_ENDPOINT = "https://chromia-verified-ai-dev-two.vercel.app";
@@ -168,13 +233,24 @@ async function buildSignedTx(opName, args, auth, chainOpts) {
168
233
  return Buffer.from(signed).toString("hex");
169
234
  }
170
235
  async function checkAgentExists(pubkey, opts) {
171
- const url = `${baseUrl(opts)}/api/ai/exists?pubkey=${encodeURIComponent(pubkey)}`;
172
- const data = await getJson(url, opts);
173
- return Boolean(data.registered);
236
+ const start = performance.now();
237
+ recordCall("checkAgentExists", void 0, pubkey);
238
+ try {
239
+ const url = `${baseUrl(opts)}/api/ai/exists?pubkey=${encodeURIComponent(pubkey)}`;
240
+ const data = await getJson(url, opts);
241
+ recordDuration("checkAgentExists", performance.now() - start, "success");
242
+ return Boolean(data.registered);
243
+ } catch (err) {
244
+ recordDuration("checkAgentExists", performance.now() - start, "error");
245
+ throw err;
246
+ }
174
247
  }
175
248
  async function logToolCall(action, context, auth, chainOpts, extra, clientOpts) {
249
+ const start = performance.now();
250
+ recordCall("logToolCall", void 0, auth.pubkey);
176
251
  const exists = await checkAgentExists(auth.pubkey, clientOpts);
177
252
  if (!exists) {
253
+ recordDuration("logToolCall", performance.now() - start, "error");
178
254
  return {
179
255
  success: false,
180
256
  toolCallId: null,
@@ -195,8 +271,10 @@ async function logToolCall(action, context, auth, chainOpts, extra, clientOpts)
195
271
  auth,
196
272
  chainOpts
197
273
  );
274
+ recordDuration("logToolCall", performance.now() - start, "success");
198
275
  return { success: true, toolCallId, signedHex };
199
276
  } catch (err) {
277
+ recordDuration("logToolCall", performance.now() - start, "error");
200
278
  return {
201
279
  success: false,
202
280
  toolCallId: null,
@@ -290,66 +368,83 @@ async function postJudgeRequest(url, body, opts) {
290
368
  return JSON.parse(new TextDecoder().decode(buf));
291
369
  }
292
370
  async function judgeAction(action, context = "", auth, opts) {
371
+ const start = performance.now();
372
+ recordCall("judgeAction", void 0, auth.pubkey);
293
373
  if (!action || !action.trim()) {
294
374
  throw new Error("action is required and cannot be empty.");
295
375
  }
296
- const logResult = await logToolCall(
297
- action,
298
- context,
299
- auth,
300
- opts?.chainOpts,
301
- { toolName: opts?.toolName, toolArgsJson: opts?.toolArgsJson },
302
- opts
303
- );
304
- if (!logResult.success || !logResult.toolCallId || !logResult.signedHex) {
305
- throw new Error(logResult.error || "Failed to sign log_tool_call");
306
- }
307
- let signedJudgeActionHex;
308
- if (!opts?.provider) {
309
- const judgmentId = generateToolCallId();
310
- signedJudgeActionHex = await buildSignedTx(
311
- "judge_action",
312
- [judgmentId, action, context || "", ""],
376
+ try {
377
+ const logResult = await logToolCall(
378
+ action,
379
+ context,
313
380
  auth,
314
- opts?.chainOpts
381
+ opts?.chainOpts,
382
+ { toolName: opts?.toolName, toolArgsJson: opts?.toolArgsJson },
383
+ opts
315
384
  );
385
+ if (!logResult.success || !logResult.toolCallId || !logResult.signedHex) {
386
+ throw new Error(logResult.error || "Failed to sign log_tool_call");
387
+ }
388
+ let signedJudgeActionHex;
389
+ if (!opts?.provider) {
390
+ const judgmentId = generateToolCallId();
391
+ signedJudgeActionHex = await buildSignedTx(
392
+ "judge_action",
393
+ [judgmentId, action, context || "", ""],
394
+ auth,
395
+ opts?.chainOpts
396
+ );
397
+ }
398
+ const url = `${baseUrl(opts)}/api/v1/judge`;
399
+ const body = {
400
+ tool_call_id: logResult.toolCallId,
401
+ agent_pubkey: auth.pubkey,
402
+ action,
403
+ signed_log_tool_call: logResult.signedHex,
404
+ ...signedJudgeActionHex && { signed_judge_action: signedJudgeActionHex },
405
+ ...context && { context },
406
+ ...opts?.provider && { provider: opts.provider },
407
+ ...opts?.toolName && { tool_name: opts.toolName },
408
+ ...opts?.model && { model: opts.model }
409
+ };
410
+ const data = await postJudgeRequest(url, body, opts);
411
+ const result = {
412
+ verdict: normalizeVerdict(data.verdict),
413
+ action_type: String(data.action_type || ""),
414
+ reason: String(data.reason || ""),
415
+ confidence: Number(data.confidence ?? 0),
416
+ provider: String(data.provider || ""),
417
+ latency_ms: Number(data.latency_ms ?? 0),
418
+ tool_call_id: String(data.tool_call_id || logResult.toolCallId),
419
+ on_chain: Boolean(data.on_chain)
420
+ };
421
+ recordDuration("judgeAction", performance.now() - start, "success");
422
+ return result;
423
+ } catch (err) {
424
+ recordDuration("judgeAction", performance.now() - start, "error");
425
+ throw err;
316
426
  }
317
- const url = `${baseUrl(opts)}/api/v1/judge`;
318
- const body = {
319
- tool_call_id: logResult.toolCallId,
320
- agent_pubkey: auth.pubkey,
321
- action,
322
- signed_log_tool_call: logResult.signedHex,
323
- ...signedJudgeActionHex && { signed_judge_action: signedJudgeActionHex },
324
- ...context && { context },
325
- ...opts?.provider && { provider: opts.provider },
326
- ...opts?.toolName && { tool_name: opts.toolName },
327
- ...opts?.model && { model: opts.model }
328
- };
329
- const data = await postJudgeRequest(url, body, opts);
330
- return {
331
- verdict: normalizeVerdict(data.verdict),
332
- action_type: String(data.action_type || ""),
333
- reason: String(data.reason || ""),
334
- confidence: Number(data.confidence ?? 0),
335
- provider: String(data.provider || ""),
336
- latency_ms: Number(data.latency_ms ?? 0),
337
- tool_call_id: String(data.tool_call_id || logResult.toolCallId),
338
- on_chain: Boolean(data.on_chain)
339
- };
340
427
  }
341
428
  async function getJudgmentStatus(judgmentId, agentPubkey, opts) {
342
- const url = `${baseUrl(opts)}/api/v1/judge?tool_call_id=${encodeURIComponent(judgmentId)}&agent_pubkey=${encodeURIComponent(agentPubkey)}`;
343
- const data = await getJson(url, opts);
344
- return {
345
- status: normalizeStatus(data.status),
346
- verdict: normalizeVerdict(data.verdict),
347
- reason: String(data.reason || ""),
348
- judgmentId: String(data.judgmentId || judgmentId),
349
- onChain: Boolean(data.onChain),
350
- cached: Boolean(data.cached),
351
- responseTimeMs: Number(data.responseTimeMs ?? 0)
352
- };
429
+ const start = performance.now();
430
+ recordCall("getJudgmentStatus", void 0, agentPubkey);
431
+ try {
432
+ const url = `${baseUrl(opts)}/api/v1/judge?tool_call_id=${encodeURIComponent(judgmentId)}&agent_pubkey=${encodeURIComponent(agentPubkey)}`;
433
+ const data = await getJson(url, opts);
434
+ recordDuration("getJudgmentStatus", performance.now() - start, "success");
435
+ return {
436
+ status: normalizeStatus(data.status),
437
+ verdict: normalizeVerdict(data.verdict),
438
+ reason: String(data.reason || ""),
439
+ judgmentId: String(data.judgmentId || judgmentId),
440
+ onChain: Boolean(data.onChain),
441
+ cached: Boolean(data.cached),
442
+ responseTimeMs: Number(data.responseTimeMs ?? 0)
443
+ };
444
+ } catch (err) {
445
+ recordDuration("getJudgmentStatus", performance.now() - start, "error");
446
+ throw err;
447
+ }
353
448
  }
354
449
  function riskEngineUrl(action, params, opts) {
355
450
  const url = new URL(`${baseUrl(opts)}/api/risk-engine`);
@@ -360,98 +455,188 @@ function riskEngineUrl(action, params, opts) {
360
455
  return url.toString();
361
456
  }
362
457
  async function getToolCalls(maxCount, opts) {
363
- return getJson(
364
- riskEngineUrl("tool-calls", { limit: String(maxCount) }, opts),
365
- opts
366
- );
458
+ const start = performance.now();
459
+ recordCall("getToolCalls");
460
+ try {
461
+ const result = await getJson(
462
+ riskEngineUrl("tool-calls", { limit: String(maxCount) }, opts),
463
+ opts
464
+ );
465
+ recordDuration("getToolCalls", performance.now() - start, "success");
466
+ return result;
467
+ } catch (err) {
468
+ recordDuration("getToolCalls", performance.now() - start, "error");
469
+ throw err;
470
+ }
367
471
  }
368
472
  async function getOrgToolCalls(orgName, maxCount, opts) {
369
- return getJson(
370
- riskEngineUrl(
371
- "org-tool-calls",
372
- { org: orgName, limit: String(maxCount) },
473
+ const start = performance.now();
474
+ recordCall("getOrgToolCalls");
475
+ try {
476
+ const result = await getJson(
477
+ riskEngineUrl(
478
+ "org-tool-calls",
479
+ { org: orgName, limit: String(maxCount) },
480
+ opts
481
+ ),
373
482
  opts
374
- ),
375
- opts
376
- );
483
+ );
484
+ recordDuration("getOrgToolCalls", performance.now() - start, "success");
485
+ return result;
486
+ } catch (err) {
487
+ recordDuration("getOrgToolCalls", performance.now() - start, "error");
488
+ throw err;
489
+ }
377
490
  }
378
491
  async function getAgentToolCalls(agentPubkey, maxCount, opts) {
379
- return getJson(
380
- riskEngineUrl(
381
- "agent-tool-calls",
382
- { agent: agentPubkey, limit: String(maxCount) },
492
+ const start = performance.now();
493
+ recordCall("getAgentToolCalls", void 0, agentPubkey);
494
+ try {
495
+ const result = await getJson(
496
+ riskEngineUrl(
497
+ "agent-tool-calls",
498
+ { agent: agentPubkey, limit: String(maxCount) },
499
+ opts
500
+ ),
383
501
  opts
384
- ),
385
- opts
386
- );
502
+ );
503
+ recordDuration("getAgentToolCalls", performance.now() - start, "success");
504
+ return result;
505
+ } catch (err) {
506
+ recordDuration("getAgentToolCalls", performance.now() - start, "error");
507
+ throw err;
508
+ }
387
509
  }
388
510
  async function getToolCallCount(opts) {
389
- const result = await getJson(
390
- riskEngineUrl("tool-call-count", {}, opts),
391
- opts
392
- );
393
- return typeof result === "number" ? result : Number(result ?? 0);
511
+ const start = performance.now();
512
+ recordCall("getToolCallCount");
513
+ try {
514
+ const result = await getJson(
515
+ riskEngineUrl("tool-call-count", {}, opts),
516
+ opts
517
+ );
518
+ recordDuration("getToolCallCount", performance.now() - start, "success");
519
+ return typeof result === "number" ? result : Number(result ?? 0);
520
+ } catch (err) {
521
+ recordDuration("getToolCallCount", performance.now() - start, "error");
522
+ throw err;
523
+ }
394
524
  }
395
525
  async function getToolCallFull(toolCallId, opts) {
396
- return getJson(
397
- riskEngineUrl("tool-call-full", { tool_call_id: toolCallId }, opts),
398
- opts
399
- );
526
+ const start = performance.now();
527
+ recordCall("getToolCallFull");
528
+ try {
529
+ const result = await getJson(
530
+ riskEngineUrl("tool-call-full", { tool_call_id: toolCallId }, opts),
531
+ opts
532
+ );
533
+ recordDuration("getToolCallFull", performance.now() - start, "success");
534
+ return result;
535
+ } catch (err) {
536
+ recordDuration("getToolCallFull", performance.now() - start, "error");
537
+ throw err;
538
+ }
400
539
  }
401
540
  async function getOrgTierInfo(orgName, opts) {
402
- return getJson(
403
- riskEngineUrl("org-tier-info", { org: orgName }, opts),
404
- opts
405
- );
541
+ const start = performance.now();
542
+ recordCall("getOrgTierInfo");
543
+ try {
544
+ const result = await getJson(
545
+ riskEngineUrl("org-tier-info", { org: orgName }, opts),
546
+ opts
547
+ );
548
+ recordDuration("getOrgTierInfo", performance.now() - start, "success");
549
+ return result;
550
+ } catch (err) {
551
+ recordDuration("getOrgTierInfo", performance.now() - start, "error");
552
+ throw err;
553
+ }
406
554
  }
407
555
  async function getPendingHeldActions(orgName, maxCount, opts) {
408
- const raw = await getJson(
409
- riskEngineUrl(
410
- "pending-held-actions",
411
- { org: orgName, limit: String(maxCount) },
556
+ const start = performance.now();
557
+ recordCall("getPendingHeldActions");
558
+ try {
559
+ const raw = await getJson(
560
+ riskEngineUrl(
561
+ "pending-held-actions",
562
+ { org: orgName, limit: String(maxCount) },
563
+ opts
564
+ ),
412
565
  opts
413
- ),
414
- opts
415
- );
416
- return raw.map((h) => ({ ...h, verdict: normalizeVerdict(h.verdict) }));
566
+ );
567
+ recordDuration("getPendingHeldActions", performance.now() - start, "success");
568
+ return raw.map((h) => ({ ...h, verdict: normalizeVerdict(h.verdict) }));
569
+ } catch (err) {
570
+ recordDuration("getPendingHeldActions", performance.now() - start, "error");
571
+ throw err;
572
+ }
417
573
  }
418
574
  async function getHeldActionReviews(orgName, maxCount, opts) {
419
- return getJson(
420
- riskEngineUrl(
421
- "held-action-reviews",
422
- { org: orgName, limit: String(maxCount) },
575
+ const start = performance.now();
576
+ recordCall("getHeldActionReviews");
577
+ try {
578
+ const result = await getJson(
579
+ riskEngineUrl(
580
+ "held-action-reviews",
581
+ { org: orgName, limit: String(maxCount) },
582
+ opts
583
+ ),
423
584
  opts
424
- ),
425
- opts
426
- );
585
+ );
586
+ recordDuration("getHeldActionReviews", performance.now() - start, "success");
587
+ return result;
588
+ } catch (err) {
589
+ recordDuration("getHeldActionReviews", performance.now() - start, "error");
590
+ throw err;
591
+ }
427
592
  }
428
593
  function riskEnginePostUrl(opts) {
429
594
  return `${baseUrl(opts)}/api/risk-engine`;
430
595
  }
431
596
  async function getAgentDetail(agentPubkey, opts) {
432
- return postJson(
433
- riskEnginePostUrl(opts),
434
- {
435
- action: "agent-detail-batch",
436
- agent: agentPubkey
437
- },
438
- opts
439
- );
597
+ const start = performance.now();
598
+ recordCall("getAgentDetail", void 0, agentPubkey);
599
+ try {
600
+ const result = await postJson(
601
+ riskEnginePostUrl(opts),
602
+ { action: "agent-detail-batch", agent: agentPubkey },
603
+ opts
604
+ );
605
+ recordDuration("getAgentDetail", performance.now() - start, "success");
606
+ return result;
607
+ } catch (err) {
608
+ recordDuration("getAgentDetail", performance.now() - start, "error");
609
+ throw err;
610
+ }
440
611
  }
441
612
  async function getAgentPolicy(agentPubkey, opts) {
442
- return postJson(
443
- riskEnginePostUrl(opts),
444
- {
445
- action: "agent-policy-batch",
446
- agent: agentPubkey
447
- },
448
- opts
449
- );
613
+ const start = performance.now();
614
+ recordCall("getAgentPolicy", void 0, agentPubkey);
615
+ try {
616
+ const result = await postJson(
617
+ riskEnginePostUrl(opts),
618
+ { action: "agent-policy-batch", agent: agentPubkey },
619
+ opts
620
+ );
621
+ recordDuration("getAgentPolicy", performance.now() - start, "success");
622
+ return result;
623
+ } catch (err) {
624
+ recordDuration("getAgentPolicy", performance.now() - start, "error");
625
+ throw err;
626
+ }
450
627
  }
451
628
  async function getSafetyStats(opts) {
452
- const url = `${baseUrl(opts)}/api/insurance?action=safety-stats`;
453
- const result = await getJson(url, opts);
454
- return result?.data || result;
629
+ const start = performance.now();
630
+ recordCall("getSafetyStats");
631
+ try {
632
+ const url = `${baseUrl(opts)}/api/insurance?action=safety-stats`;
633
+ const result = await getJson(url, opts);
634
+ recordDuration("getSafetyStats", performance.now() - start, "success");
635
+ return result?.data || result;
636
+ } catch (err) {
637
+ recordDuration("getSafetyStats", performance.now() - start, "error");
638
+ throw err;
639
+ }
455
640
  }
456
641
 
457
642
  // src/config.ts
@@ -820,6 +1005,8 @@ function resolve(key, flagValue) {
820
1005
  resolve,
821
1006
  resolveKeyPath,
822
1007
  saveUserConfig,
1008
+ setupTelemetry,
1009
+ shutdownTelemetry,
823
1010
  toPubkeyHex,
824
1011
  validateJudgeEndpoint,
825
1012
  verifyJudgeResponseSignature
package/dist/index.d.cts CHANGED
@@ -204,6 +204,31 @@ declare function verifyJudgeResponseSignature(bodyBytes: Uint8Array, signatureHe
204
204
  reason?: string;
205
205
  };
206
206
 
207
+ /**
208
+ * Atbash SDK Telemetry — OpenTelemetry metrics for usage tracking.
209
+ *
210
+ * Tracks: function call counts, latency, source (CLI/plugin/SDK),
211
+ * and agent identity. Opt-in — no data sent unless enabled.
212
+ */
213
+ type ClientSource = "cli" | "sdk" | "plugin:openclaw" | "plugin:claude-hook" | "plugin:langchain" | "plugin:langgraph" | "plugin:hermes" | "plugin:eliza" | "plugin:crewai" | "plugin:mcp" | "plugin:autogen" | "plugin:jeenai" | (string & {});
214
+ interface TelemetryConfig {
215
+ /** Must be true to send any telemetry. Default: false */
216
+ enabled: boolean;
217
+ /** Where calls originate */
218
+ source?: ClientSource;
219
+ /** Flush interval in ms. Default: 60000 */
220
+ exportIntervalMs?: number;
221
+ }
222
+ /**
223
+ * Initialize telemetry. Call once at startup.
224
+ * Does nothing if config.enabled is false.
225
+ */
226
+ declare function setupTelemetry(config: TelemetryConfig): void;
227
+ /**
228
+ * Flush pending metrics and shut down. Call before process exits.
229
+ */
230
+ declare function shutdownTelemetry(): Promise<void>;
231
+
207
232
  interface AtbashUserConfig {
208
233
  agentKey?: string;
209
234
  orgName?: string;
@@ -218,4 +243,4 @@ declare function loadUserConfig(): AtbashUserConfig;
218
243
  declare function saveUserConfig(config: AtbashUserConfig): void;
219
244
  declare function resolve(key: keyof AtbashUserConfig, flagValue?: string): string;
220
245
 
221
- export { type ActionType, type AgentAuth, type AgentPolicy, type AtbashClient, type AtbashClientConfig, type AtbashUserConfig, type ChainOpts, type ClientOpts, DEFAULT_BLOCKCHAIN_RID, DEFAULT_CHROMIA_NODE_URLS, DEFAULT_ENDPOINT, type Decision, type DecisionVerdict, type HeldAction, type HeldActionReview, type JudgeEndpointConfig, type JudgeOptions, type JudgeResult, type JudgmentStatus, type JudgmentStatusState, type LogToolCallResult, type Provider, type PubkeyValue, type Tier, type TierInfo, type ToolCallFull, type ToolCallInput, type ToolCallRecord, type ValidatedEndpoint, type Verdict, checkAgentExists, createAtbashClient, derivePublicKey, generateKeyPair, getAgentDetail, getAgentPolicy, getAgentToolCalls, getConfigDir, getConfigPath, getHeldActionReviews, getJudgmentStatus, getOrgTierInfo, getOrgToolCalls, getPendingHeldActions, getSafetyStats, getToolCallCount, getToolCallFull, getToolCalls, isValidPrivateKey, judgeAction, loadAgent, loadAgentFromFile, loadUserConfig, logToolCall, resolve, resolveKeyPath, saveUserConfig, toPubkeyHex, validateJudgeEndpoint, verifyJudgeResponseSignature };
246
+ export { type ActionType, type AgentAuth, type AgentPolicy, type AtbashClient, type AtbashClientConfig, type AtbashUserConfig, type ChainOpts, type ClientOpts, type ClientSource, DEFAULT_BLOCKCHAIN_RID, DEFAULT_CHROMIA_NODE_URLS, DEFAULT_ENDPOINT, type Decision, type DecisionVerdict, type HeldAction, type HeldActionReview, type JudgeEndpointConfig, type JudgeOptions, type JudgeResult, type JudgmentStatus, type JudgmentStatusState, type LogToolCallResult, type Provider, type PubkeyValue, type TelemetryConfig, type Tier, type TierInfo, type ToolCallFull, type ToolCallInput, type ToolCallRecord, type ValidatedEndpoint, type Verdict, checkAgentExists, createAtbashClient, derivePublicKey, generateKeyPair, getAgentDetail, getAgentPolicy, getAgentToolCalls, getConfigDir, getConfigPath, getHeldActionReviews, getJudgmentStatus, getOrgTierInfo, getOrgToolCalls, getPendingHeldActions, getSafetyStats, getToolCallCount, getToolCallFull, getToolCalls, isValidPrivateKey, judgeAction, loadAgent, loadAgentFromFile, loadUserConfig, logToolCall, resolve, resolveKeyPath, saveUserConfig, setupTelemetry, shutdownTelemetry, toPubkeyHex, validateJudgeEndpoint, verifyJudgeResponseSignature };
package/dist/index.d.ts CHANGED
@@ -204,6 +204,31 @@ declare function verifyJudgeResponseSignature(bodyBytes: Uint8Array, signatureHe
204
204
  reason?: string;
205
205
  };
206
206
 
207
+ /**
208
+ * Atbash SDK Telemetry — OpenTelemetry metrics for usage tracking.
209
+ *
210
+ * Tracks: function call counts, latency, source (CLI/plugin/SDK),
211
+ * and agent identity. Opt-in — no data sent unless enabled.
212
+ */
213
+ type ClientSource = "cli" | "sdk" | "plugin:openclaw" | "plugin:claude-hook" | "plugin:langchain" | "plugin:langgraph" | "plugin:hermes" | "plugin:eliza" | "plugin:crewai" | "plugin:mcp" | "plugin:autogen" | "plugin:jeenai" | (string & {});
214
+ interface TelemetryConfig {
215
+ /** Must be true to send any telemetry. Default: false */
216
+ enabled: boolean;
217
+ /** Where calls originate */
218
+ source?: ClientSource;
219
+ /** Flush interval in ms. Default: 60000 */
220
+ exportIntervalMs?: number;
221
+ }
222
+ /**
223
+ * Initialize telemetry. Call once at startup.
224
+ * Does nothing if config.enabled is false.
225
+ */
226
+ declare function setupTelemetry(config: TelemetryConfig): void;
227
+ /**
228
+ * Flush pending metrics and shut down. Call before process exits.
229
+ */
230
+ declare function shutdownTelemetry(): Promise<void>;
231
+
207
232
  interface AtbashUserConfig {
208
233
  agentKey?: string;
209
234
  orgName?: string;
@@ -218,4 +243,4 @@ declare function loadUserConfig(): AtbashUserConfig;
218
243
  declare function saveUserConfig(config: AtbashUserConfig): void;
219
244
  declare function resolve(key: keyof AtbashUserConfig, flagValue?: string): string;
220
245
 
221
- export { type ActionType, type AgentAuth, type AgentPolicy, type AtbashClient, type AtbashClientConfig, type AtbashUserConfig, type ChainOpts, type ClientOpts, DEFAULT_BLOCKCHAIN_RID, DEFAULT_CHROMIA_NODE_URLS, DEFAULT_ENDPOINT, type Decision, type DecisionVerdict, type HeldAction, type HeldActionReview, type JudgeEndpointConfig, type JudgeOptions, type JudgeResult, type JudgmentStatus, type JudgmentStatusState, type LogToolCallResult, type Provider, type PubkeyValue, type Tier, type TierInfo, type ToolCallFull, type ToolCallInput, type ToolCallRecord, type ValidatedEndpoint, type Verdict, checkAgentExists, createAtbashClient, derivePublicKey, generateKeyPair, getAgentDetail, getAgentPolicy, getAgentToolCalls, getConfigDir, getConfigPath, getHeldActionReviews, getJudgmentStatus, getOrgTierInfo, getOrgToolCalls, getPendingHeldActions, getSafetyStats, getToolCallCount, getToolCallFull, getToolCalls, isValidPrivateKey, judgeAction, loadAgent, loadAgentFromFile, loadUserConfig, logToolCall, resolve, resolveKeyPath, saveUserConfig, toPubkeyHex, validateJudgeEndpoint, verifyJudgeResponseSignature };
246
+ export { type ActionType, type AgentAuth, type AgentPolicy, type AtbashClient, type AtbashClientConfig, type AtbashUserConfig, type ChainOpts, type ClientOpts, type ClientSource, DEFAULT_BLOCKCHAIN_RID, DEFAULT_CHROMIA_NODE_URLS, DEFAULT_ENDPOINT, type Decision, type DecisionVerdict, type HeldAction, type HeldActionReview, type JudgeEndpointConfig, type JudgeOptions, type JudgeResult, type JudgmentStatus, type JudgmentStatusState, type LogToolCallResult, type Provider, type PubkeyValue, type TelemetryConfig, type Tier, type TierInfo, type ToolCallFull, type ToolCallInput, type ToolCallRecord, type ValidatedEndpoint, type Verdict, checkAgentExists, createAtbashClient, derivePublicKey, generateKeyPair, getAgentDetail, getAgentPolicy, getAgentToolCalls, getConfigDir, getConfigPath, getHeldActionReviews, getJudgmentStatus, getOrgTierInfo, getOrgToolCalls, getPendingHeldActions, getSafetyStats, getToolCallCount, getToolCallFull, getToolCalls, isValidPrivateKey, judgeAction, loadAgent, loadAgentFromFile, loadUserConfig, logToolCall, resolve, resolveKeyPath, saveUserConfig, setupTelemetry, shutdownTelemetry, toPubkeyHex, validateJudgeEndpoint, verifyJudgeResponseSignature };
package/dist/index.js CHANGED
@@ -28,6 +28,69 @@ function verifyJudgeResponseSignature(bodyBytes, signatureHex, pubKeyHex) {
28
28
  return isValid ? { ok: true } : { ok: false, reason: "signature does not verify against configured verifyPubKey" };
29
29
  }
30
30
 
31
+ // src/opentel/telemetry.ts
32
+ import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
33
+ import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
34
+ import { resourceFromAttributes } from "@opentelemetry/resources";
35
+ var meterProvider = null;
36
+ var callCounter = null;
37
+ var durationHistogram = null;
38
+ var defaultSource = "sdk";
39
+ function setupTelemetry(config) {
40
+ if (!config.enabled) return;
41
+ if (meterProvider) return;
42
+ defaultSource = config.source ?? "sdk";
43
+ const ATBASH_HONEYCOMB_KEY = "YOUR_INGEST_KEY_HERE";
44
+ const apiKey = process.env.HONEYCOMB_API_KEY ?? ATBASH_HONEYCOMB_KEY;
45
+ const exporter = new OTLPMetricExporter({
46
+ url: "https://api.honeycomb.io/v1/metrics",
47
+ headers: {
48
+ "x-honeycomb-team": apiKey
49
+ }
50
+ });
51
+ const reader = new PeriodicExportingMetricReader({
52
+ exporter,
53
+ exportIntervalMillis: config.exportIntervalMs ?? 6e4
54
+ });
55
+ meterProvider = new MeterProvider({
56
+ resource: resourceFromAttributes({
57
+ "service.name": "atbash-sdk"
58
+ }),
59
+ readers: [reader]
60
+ });
61
+ const meter = meterProvider.getMeter("atbash-sdk");
62
+ callCounter = meter.createCounter("atbash.sdk.function.calls", {
63
+ description: "Number of SDK function calls"
64
+ });
65
+ durationHistogram = meter.createHistogram("atbash.sdk.function.duration_ms", {
66
+ description: "SDK function execution duration",
67
+ unit: "ms"
68
+ });
69
+ }
70
+ function recordCall(functionName, source, agentPubkey) {
71
+ if (!callCounter) return;
72
+ callCounter.add(1, {
73
+ "function.name": functionName,
74
+ "source": source ?? defaultSource,
75
+ ...agentPubkey && { "agent.pubkey": agentPubkey }
76
+ });
77
+ }
78
+ function recordDuration(functionName, durationMs, status, source) {
79
+ if (!durationHistogram) return;
80
+ durationHistogram.record(durationMs, {
81
+ "function.name": functionName,
82
+ "status": status,
83
+ "source": source ?? defaultSource
84
+ });
85
+ }
86
+ async function shutdownTelemetry() {
87
+ if (!meterProvider) return;
88
+ await meterProvider.shutdown();
89
+ meterProvider = null;
90
+ callCounter = null;
91
+ durationHistogram = null;
92
+ }
93
+
31
94
  // src/client.ts
32
95
  var { createClient, encryption: encryption2, newSignatureProvider } = postchain2;
33
96
  var DEFAULT_ENDPOINT = "https://chromia-verified-ai-dev-two.vercel.app";
@@ -100,13 +163,24 @@ async function buildSignedTx(opName, args, auth, chainOpts) {
100
163
  return Buffer.from(signed).toString("hex");
101
164
  }
102
165
  async function checkAgentExists(pubkey, opts) {
103
- const url = `${baseUrl(opts)}/api/ai/exists?pubkey=${encodeURIComponent(pubkey)}`;
104
- const data = await getJson(url, opts);
105
- return Boolean(data.registered);
166
+ const start = performance.now();
167
+ recordCall("checkAgentExists", void 0, pubkey);
168
+ try {
169
+ const url = `${baseUrl(opts)}/api/ai/exists?pubkey=${encodeURIComponent(pubkey)}`;
170
+ const data = await getJson(url, opts);
171
+ recordDuration("checkAgentExists", performance.now() - start, "success");
172
+ return Boolean(data.registered);
173
+ } catch (err) {
174
+ recordDuration("checkAgentExists", performance.now() - start, "error");
175
+ throw err;
176
+ }
106
177
  }
107
178
  async function logToolCall(action, context, auth, chainOpts, extra, clientOpts) {
179
+ const start = performance.now();
180
+ recordCall("logToolCall", void 0, auth.pubkey);
108
181
  const exists = await checkAgentExists(auth.pubkey, clientOpts);
109
182
  if (!exists) {
183
+ recordDuration("logToolCall", performance.now() - start, "error");
110
184
  return {
111
185
  success: false,
112
186
  toolCallId: null,
@@ -127,8 +201,10 @@ async function logToolCall(action, context, auth, chainOpts, extra, clientOpts)
127
201
  auth,
128
202
  chainOpts
129
203
  );
204
+ recordDuration("logToolCall", performance.now() - start, "success");
130
205
  return { success: true, toolCallId, signedHex };
131
206
  } catch (err) {
207
+ recordDuration("logToolCall", performance.now() - start, "error");
132
208
  return {
133
209
  success: false,
134
210
  toolCallId: null,
@@ -222,66 +298,83 @@ async function postJudgeRequest(url, body, opts) {
222
298
  return JSON.parse(new TextDecoder().decode(buf));
223
299
  }
224
300
  async function judgeAction(action, context = "", auth, opts) {
301
+ const start = performance.now();
302
+ recordCall("judgeAction", void 0, auth.pubkey);
225
303
  if (!action || !action.trim()) {
226
304
  throw new Error("action is required and cannot be empty.");
227
305
  }
228
- const logResult = await logToolCall(
229
- action,
230
- context,
231
- auth,
232
- opts?.chainOpts,
233
- { toolName: opts?.toolName, toolArgsJson: opts?.toolArgsJson },
234
- opts
235
- );
236
- if (!logResult.success || !logResult.toolCallId || !logResult.signedHex) {
237
- throw new Error(logResult.error || "Failed to sign log_tool_call");
238
- }
239
- let signedJudgeActionHex;
240
- if (!opts?.provider) {
241
- const judgmentId = generateToolCallId();
242
- signedJudgeActionHex = await buildSignedTx(
243
- "judge_action",
244
- [judgmentId, action, context || "", ""],
306
+ try {
307
+ const logResult = await logToolCall(
308
+ action,
309
+ context,
245
310
  auth,
246
- opts?.chainOpts
311
+ opts?.chainOpts,
312
+ { toolName: opts?.toolName, toolArgsJson: opts?.toolArgsJson },
313
+ opts
247
314
  );
315
+ if (!logResult.success || !logResult.toolCallId || !logResult.signedHex) {
316
+ throw new Error(logResult.error || "Failed to sign log_tool_call");
317
+ }
318
+ let signedJudgeActionHex;
319
+ if (!opts?.provider) {
320
+ const judgmentId = generateToolCallId();
321
+ signedJudgeActionHex = await buildSignedTx(
322
+ "judge_action",
323
+ [judgmentId, action, context || "", ""],
324
+ auth,
325
+ opts?.chainOpts
326
+ );
327
+ }
328
+ const url = `${baseUrl(opts)}/api/v1/judge`;
329
+ const body = {
330
+ tool_call_id: logResult.toolCallId,
331
+ agent_pubkey: auth.pubkey,
332
+ action,
333
+ signed_log_tool_call: logResult.signedHex,
334
+ ...signedJudgeActionHex && { signed_judge_action: signedJudgeActionHex },
335
+ ...context && { context },
336
+ ...opts?.provider && { provider: opts.provider },
337
+ ...opts?.toolName && { tool_name: opts.toolName },
338
+ ...opts?.model && { model: opts.model }
339
+ };
340
+ const data = await postJudgeRequest(url, body, opts);
341
+ const result = {
342
+ verdict: normalizeVerdict(data.verdict),
343
+ action_type: String(data.action_type || ""),
344
+ reason: String(data.reason || ""),
345
+ confidence: Number(data.confidence ?? 0),
346
+ provider: String(data.provider || ""),
347
+ latency_ms: Number(data.latency_ms ?? 0),
348
+ tool_call_id: String(data.tool_call_id || logResult.toolCallId),
349
+ on_chain: Boolean(data.on_chain)
350
+ };
351
+ recordDuration("judgeAction", performance.now() - start, "success");
352
+ return result;
353
+ } catch (err) {
354
+ recordDuration("judgeAction", performance.now() - start, "error");
355
+ throw err;
248
356
  }
249
- const url = `${baseUrl(opts)}/api/v1/judge`;
250
- const body = {
251
- tool_call_id: logResult.toolCallId,
252
- agent_pubkey: auth.pubkey,
253
- action,
254
- signed_log_tool_call: logResult.signedHex,
255
- ...signedJudgeActionHex && { signed_judge_action: signedJudgeActionHex },
256
- ...context && { context },
257
- ...opts?.provider && { provider: opts.provider },
258
- ...opts?.toolName && { tool_name: opts.toolName },
259
- ...opts?.model && { model: opts.model }
260
- };
261
- const data = await postJudgeRequest(url, body, opts);
262
- return {
263
- verdict: normalizeVerdict(data.verdict),
264
- action_type: String(data.action_type || ""),
265
- reason: String(data.reason || ""),
266
- confidence: Number(data.confidence ?? 0),
267
- provider: String(data.provider || ""),
268
- latency_ms: Number(data.latency_ms ?? 0),
269
- tool_call_id: String(data.tool_call_id || logResult.toolCallId),
270
- on_chain: Boolean(data.on_chain)
271
- };
272
357
  }
273
358
  async function getJudgmentStatus(judgmentId, agentPubkey, opts) {
274
- const url = `${baseUrl(opts)}/api/v1/judge?tool_call_id=${encodeURIComponent(judgmentId)}&agent_pubkey=${encodeURIComponent(agentPubkey)}`;
275
- const data = await getJson(url, opts);
276
- return {
277
- status: normalizeStatus(data.status),
278
- verdict: normalizeVerdict(data.verdict),
279
- reason: String(data.reason || ""),
280
- judgmentId: String(data.judgmentId || judgmentId),
281
- onChain: Boolean(data.onChain),
282
- cached: Boolean(data.cached),
283
- responseTimeMs: Number(data.responseTimeMs ?? 0)
284
- };
359
+ const start = performance.now();
360
+ recordCall("getJudgmentStatus", void 0, agentPubkey);
361
+ try {
362
+ const url = `${baseUrl(opts)}/api/v1/judge?tool_call_id=${encodeURIComponent(judgmentId)}&agent_pubkey=${encodeURIComponent(agentPubkey)}`;
363
+ const data = await getJson(url, opts);
364
+ recordDuration("getJudgmentStatus", performance.now() - start, "success");
365
+ return {
366
+ status: normalizeStatus(data.status),
367
+ verdict: normalizeVerdict(data.verdict),
368
+ reason: String(data.reason || ""),
369
+ judgmentId: String(data.judgmentId || judgmentId),
370
+ onChain: Boolean(data.onChain),
371
+ cached: Boolean(data.cached),
372
+ responseTimeMs: Number(data.responseTimeMs ?? 0)
373
+ };
374
+ } catch (err) {
375
+ recordDuration("getJudgmentStatus", performance.now() - start, "error");
376
+ throw err;
377
+ }
285
378
  }
286
379
  function riskEngineUrl(action, params, opts) {
287
380
  const url = new URL(`${baseUrl(opts)}/api/risk-engine`);
@@ -292,98 +385,188 @@ function riskEngineUrl(action, params, opts) {
292
385
  return url.toString();
293
386
  }
294
387
  async function getToolCalls(maxCount, opts) {
295
- return getJson(
296
- riskEngineUrl("tool-calls", { limit: String(maxCount) }, opts),
297
- opts
298
- );
388
+ const start = performance.now();
389
+ recordCall("getToolCalls");
390
+ try {
391
+ const result = await getJson(
392
+ riskEngineUrl("tool-calls", { limit: String(maxCount) }, opts),
393
+ opts
394
+ );
395
+ recordDuration("getToolCalls", performance.now() - start, "success");
396
+ return result;
397
+ } catch (err) {
398
+ recordDuration("getToolCalls", performance.now() - start, "error");
399
+ throw err;
400
+ }
299
401
  }
300
402
  async function getOrgToolCalls(orgName, maxCount, opts) {
301
- return getJson(
302
- riskEngineUrl(
303
- "org-tool-calls",
304
- { org: orgName, limit: String(maxCount) },
403
+ const start = performance.now();
404
+ recordCall("getOrgToolCalls");
405
+ try {
406
+ const result = await getJson(
407
+ riskEngineUrl(
408
+ "org-tool-calls",
409
+ { org: orgName, limit: String(maxCount) },
410
+ opts
411
+ ),
305
412
  opts
306
- ),
307
- opts
308
- );
413
+ );
414
+ recordDuration("getOrgToolCalls", performance.now() - start, "success");
415
+ return result;
416
+ } catch (err) {
417
+ recordDuration("getOrgToolCalls", performance.now() - start, "error");
418
+ throw err;
419
+ }
309
420
  }
310
421
  async function getAgentToolCalls(agentPubkey, maxCount, opts) {
311
- return getJson(
312
- riskEngineUrl(
313
- "agent-tool-calls",
314
- { agent: agentPubkey, limit: String(maxCount) },
422
+ const start = performance.now();
423
+ recordCall("getAgentToolCalls", void 0, agentPubkey);
424
+ try {
425
+ const result = await getJson(
426
+ riskEngineUrl(
427
+ "agent-tool-calls",
428
+ { agent: agentPubkey, limit: String(maxCount) },
429
+ opts
430
+ ),
315
431
  opts
316
- ),
317
- opts
318
- );
432
+ );
433
+ recordDuration("getAgentToolCalls", performance.now() - start, "success");
434
+ return result;
435
+ } catch (err) {
436
+ recordDuration("getAgentToolCalls", performance.now() - start, "error");
437
+ throw err;
438
+ }
319
439
  }
320
440
  async function getToolCallCount(opts) {
321
- const result = await getJson(
322
- riskEngineUrl("tool-call-count", {}, opts),
323
- opts
324
- );
325
- return typeof result === "number" ? result : Number(result ?? 0);
441
+ const start = performance.now();
442
+ recordCall("getToolCallCount");
443
+ try {
444
+ const result = await getJson(
445
+ riskEngineUrl("tool-call-count", {}, opts),
446
+ opts
447
+ );
448
+ recordDuration("getToolCallCount", performance.now() - start, "success");
449
+ return typeof result === "number" ? result : Number(result ?? 0);
450
+ } catch (err) {
451
+ recordDuration("getToolCallCount", performance.now() - start, "error");
452
+ throw err;
453
+ }
326
454
  }
327
455
  async function getToolCallFull(toolCallId, opts) {
328
- return getJson(
329
- riskEngineUrl("tool-call-full", { tool_call_id: toolCallId }, opts),
330
- opts
331
- );
456
+ const start = performance.now();
457
+ recordCall("getToolCallFull");
458
+ try {
459
+ const result = await getJson(
460
+ riskEngineUrl("tool-call-full", { tool_call_id: toolCallId }, opts),
461
+ opts
462
+ );
463
+ recordDuration("getToolCallFull", performance.now() - start, "success");
464
+ return result;
465
+ } catch (err) {
466
+ recordDuration("getToolCallFull", performance.now() - start, "error");
467
+ throw err;
468
+ }
332
469
  }
333
470
  async function getOrgTierInfo(orgName, opts) {
334
- return getJson(
335
- riskEngineUrl("org-tier-info", { org: orgName }, opts),
336
- opts
337
- );
471
+ const start = performance.now();
472
+ recordCall("getOrgTierInfo");
473
+ try {
474
+ const result = await getJson(
475
+ riskEngineUrl("org-tier-info", { org: orgName }, opts),
476
+ opts
477
+ );
478
+ recordDuration("getOrgTierInfo", performance.now() - start, "success");
479
+ return result;
480
+ } catch (err) {
481
+ recordDuration("getOrgTierInfo", performance.now() - start, "error");
482
+ throw err;
483
+ }
338
484
  }
339
485
  async function getPendingHeldActions(orgName, maxCount, opts) {
340
- const raw = await getJson(
341
- riskEngineUrl(
342
- "pending-held-actions",
343
- { org: orgName, limit: String(maxCount) },
486
+ const start = performance.now();
487
+ recordCall("getPendingHeldActions");
488
+ try {
489
+ const raw = await getJson(
490
+ riskEngineUrl(
491
+ "pending-held-actions",
492
+ { org: orgName, limit: String(maxCount) },
493
+ opts
494
+ ),
344
495
  opts
345
- ),
346
- opts
347
- );
348
- return raw.map((h) => ({ ...h, verdict: normalizeVerdict(h.verdict) }));
496
+ );
497
+ recordDuration("getPendingHeldActions", performance.now() - start, "success");
498
+ return raw.map((h) => ({ ...h, verdict: normalizeVerdict(h.verdict) }));
499
+ } catch (err) {
500
+ recordDuration("getPendingHeldActions", performance.now() - start, "error");
501
+ throw err;
502
+ }
349
503
  }
350
504
  async function getHeldActionReviews(orgName, maxCount, opts) {
351
- return getJson(
352
- riskEngineUrl(
353
- "held-action-reviews",
354
- { org: orgName, limit: String(maxCount) },
505
+ const start = performance.now();
506
+ recordCall("getHeldActionReviews");
507
+ try {
508
+ const result = await getJson(
509
+ riskEngineUrl(
510
+ "held-action-reviews",
511
+ { org: orgName, limit: String(maxCount) },
512
+ opts
513
+ ),
355
514
  opts
356
- ),
357
- opts
358
- );
515
+ );
516
+ recordDuration("getHeldActionReviews", performance.now() - start, "success");
517
+ return result;
518
+ } catch (err) {
519
+ recordDuration("getHeldActionReviews", performance.now() - start, "error");
520
+ throw err;
521
+ }
359
522
  }
360
523
  function riskEnginePostUrl(opts) {
361
524
  return `${baseUrl(opts)}/api/risk-engine`;
362
525
  }
363
526
  async function getAgentDetail(agentPubkey, opts) {
364
- return postJson(
365
- riskEnginePostUrl(opts),
366
- {
367
- action: "agent-detail-batch",
368
- agent: agentPubkey
369
- },
370
- opts
371
- );
527
+ const start = performance.now();
528
+ recordCall("getAgentDetail", void 0, agentPubkey);
529
+ try {
530
+ const result = await postJson(
531
+ riskEnginePostUrl(opts),
532
+ { action: "agent-detail-batch", agent: agentPubkey },
533
+ opts
534
+ );
535
+ recordDuration("getAgentDetail", performance.now() - start, "success");
536
+ return result;
537
+ } catch (err) {
538
+ recordDuration("getAgentDetail", performance.now() - start, "error");
539
+ throw err;
540
+ }
372
541
  }
373
542
  async function getAgentPolicy(agentPubkey, opts) {
374
- return postJson(
375
- riskEnginePostUrl(opts),
376
- {
377
- action: "agent-policy-batch",
378
- agent: agentPubkey
379
- },
380
- opts
381
- );
543
+ const start = performance.now();
544
+ recordCall("getAgentPolicy", void 0, agentPubkey);
545
+ try {
546
+ const result = await postJson(
547
+ riskEnginePostUrl(opts),
548
+ { action: "agent-policy-batch", agent: agentPubkey },
549
+ opts
550
+ );
551
+ recordDuration("getAgentPolicy", performance.now() - start, "success");
552
+ return result;
553
+ } catch (err) {
554
+ recordDuration("getAgentPolicy", performance.now() - start, "error");
555
+ throw err;
556
+ }
382
557
  }
383
558
  async function getSafetyStats(opts) {
384
- const url = `${baseUrl(opts)}/api/insurance?action=safety-stats`;
385
- const result = await getJson(url, opts);
386
- return result?.data || result;
559
+ const start = performance.now();
560
+ recordCall("getSafetyStats");
561
+ try {
562
+ const url = `${baseUrl(opts)}/api/insurance?action=safety-stats`;
563
+ const result = await getJson(url, opts);
564
+ recordDuration("getSafetyStats", performance.now() - start, "success");
565
+ return result?.data || result;
566
+ } catch (err) {
567
+ recordDuration("getSafetyStats", performance.now() - start, "error");
568
+ throw err;
569
+ }
387
570
  }
388
571
 
389
572
  // src/config.ts
@@ -751,6 +934,8 @@ export {
751
934
  resolve,
752
935
  resolveKeyPath,
753
936
  saveUserConfig,
937
+ setupTelemetry,
938
+ shutdownTelemetry,
754
939
  toPubkeyHex,
755
940
  validateJudgeEndpoint,
756
941
  verifyJudgeResponseSignature
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atbash/sdk",
3
- "version": "0.3.9-dev.2",
3
+ "version": "0.3.9-dev.4",
4
4
  "description": "Atbash SDK — control boundary before the last irreversible step in an agent workflow",
5
5
  "homepage": "https://atbash.ai",
6
6
  "author": "Atbash",
@@ -41,6 +41,10 @@
41
41
  "ai-safety"
42
42
  ],
43
43
  "dependencies": {
44
+ "@opentelemetry/api": "^1.9.1",
45
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.217.0",
46
+ "@opentelemetry/resources": "^2.7.1",
47
+ "@opentelemetry/sdk-metrics": "^2.7.1",
44
48
  "postchain-client": "^2.1.5"
45
49
  }
46
50
  }