agentid-sdk 0.1.37 → 0.1.40

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
@@ -111,8 +111,100 @@ console.log(response.choices[0]?.message?.content ?? "");
111
111
  By default, official AgentID SDK integrations inherit `enable_sdk_pii_masking`
112
112
  from the dashboard/runtime config. You only need to set `piiMasking: true` in
113
113
  code if you want to force local masking on even when the dashboard policy is off.
114
-
115
- Wrapped OpenAI calls persist telemetry for both regular and streamed completions. For `stream: true`, logging happens when the stream finishes.
114
+ Starting with `agentid-sdk@0.1.40`, fail-open dependency fallback keeps local
115
+ deterministic PII and secret masking enabled when `/agent/config` or `/guard`
116
+ is unreachable. Fail-open can preserve availability, but official wrappers must
117
+ not treat it as permission to send raw sensitive text to the provider.
118
+
119
+ When SDK-side masking is enabled, the wrapper now masks both classic PII and
120
+ high-confidence secret material before the request leaves your process:
121
+
122
+ - emails, phones, card numbers, IBANs, national IDs, person names
123
+ - OpenAI / Anthropic / Google / AWS / GitHub / Slack / Stripe credentials
124
+ - bearer tokens, JWTs, `x-api-key` headers
125
+ - password / credential assignments, PEM private keys, Azure connection strings and SAS tokens
126
+
127
+ The masked form is what gets sent to `/guard`, logged to AgentID ingest, and
128
+ forwarded to the model provider. The wrapper also protects returned completion
129
+ text before it is logged or returned from the wrapped call when SDK-side masking
130
+ is enabled.
131
+
132
+ Important: this applies only to the wrapped call. If your app sends raw prompt
133
+ or raw chat history through a separate direct provider call, AgentID cannot
134
+ protect that bypass.
135
+
136
+ Correct:
137
+
138
+ ```ts
139
+ const secured = agent.wrapOpenAI(openai, {
140
+ system_id: process.env.AGENTID_SYSTEM_ID!,
141
+ });
142
+
143
+ await secured.chat.completions.create({
144
+ model: "gpt-4o-mini",
145
+ messages: fullConversationHistory,
146
+ });
147
+ ```
148
+
149
+ Incorrect:
150
+
151
+ ```ts
152
+ // Raw history reaches the provider.
153
+ await openai.chat.completions.create({
154
+ model: "gpt-4o-mini",
155
+ messages: rawConversationHistory,
156
+ });
157
+
158
+ // Logging a masked copy later does not protect the model call above.
159
+ await agent.log({ system_id: systemId, input: maskedInput, output: maskedOutput });
160
+ ```
161
+
162
+ For chat apps and agent workflows, protect the full message history, not just
163
+ the latest text field. If a previous user/assistant/tool/memory message contains
164
+ raw PII, the model can still repeat it later.
165
+
166
+ If you cannot use `wrapOpenAI()` and need a manual integration, call
167
+ `protectMessageHistory()` on the exact history that will be sent to the
168
+ provider. Then pass `protected.messages` to the provider, not the raw
169
+ `body.messages`.
170
+
171
+ ```ts
172
+ import { AgentID, protectMessageHistory } from "agentid-sdk";
173
+
174
+ const agent = new AgentID();
175
+ const protectedHistory = protectMessageHistory(body.messages, {
176
+ pii: true,
177
+ secrets: true,
178
+ });
179
+
180
+ const latestUserInput = extractLatestUserInput(protectedHistory.messages);
181
+ const verdict = await agent.guard({
182
+ system_id: process.env.AGENTID_SYSTEM_ID!,
183
+ input: latestUserInput,
184
+ model: "gpt-4o-mini",
185
+ metadata: {
186
+ runtime_surface: "manual_provider_integration",
187
+ full_history_protected: true,
188
+ messages_count: Array.isArray(protectedHistory.messages)
189
+ ? protectedHistory.messages.length
190
+ : undefined,
191
+ protected_messages_count: Array.isArray(protectedHistory.messages)
192
+ ? protectedHistory.messages.length
193
+ : undefined,
194
+ prompt_text_parts_count: protectedHistory.textPartsCount,
195
+ transformed_prompt_text_parts_count:
196
+ protectedHistory.transformedTextPartsCount,
197
+ },
198
+ });
199
+ if (!verdict.allowed) throw new Error(`Blocked: ${verdict.reason}`);
200
+
201
+ const response = await openai.chat.completions.create({
202
+ model: "gpt-4o-mini",
203
+ messages: protectedHistory.messages,
204
+ });
205
+ ```
206
+
207
+ Wrapped OpenAI calls persist telemetry for both regular and streamed completions. For `stream: true`, logging happens when the stream finishes.
116
208
 
117
209
  > Scope note: AgentID compliance/risk controls apply to the specific SDK-wrapped LLM calls (`guard()`, `wrapOpenAI()`, LangChain callback-wrapped flows). They do not automatically classify unrelated code paths in your whole monolithic application.
118
210
 
@@ -133,17 +225,30 @@ npm install agentid-sdk openai @langchain/core @langchain/openai
133
225
  ```
134
226
 
135
227
  ```ts
136
- import { AgentID } from "agentid-sdk";
137
- import { AgentIDCallbackHandler } from "agentid-sdk/langchain";
138
- import { ChatOpenAI } from "@langchain/openai";
139
- import { ChatPromptTemplate } from "@langchain/core/prompts";
140
- import { StringOutputParser } from "@langchain/core/output_parsers";
141
-
142
- const agent = new AgentID();
143
- const handler = new AgentIDCallbackHandler(agent, {
144
- system_id: process.env.AGENTID_SYSTEM_ID!,
145
- expected_languages: ["en"],
146
- });
228
+ import {
229
+ AgentID,
230
+ createAgentIdCorrelationId,
231
+ createAgentIdTelemetryContext,
232
+ } from "agentid-sdk";
233
+ import { AgentIDCallbackHandler } from "agentid-sdk/langchain";
234
+ import { ChatOpenAI } from "@langchain/openai";
235
+ import { ChatPromptTemplate } from "@langchain/core/prompts";
236
+ import { StringOutputParser } from "@langchain/core/output_parsers";
237
+
238
+ const agent = new AgentID();
239
+ const workflowRunId = createAgentIdCorrelationId();
240
+ const handler = new AgentIDCallbackHandler(agent, {
241
+ system_id: process.env.AGENTID_SYSTEM_ID!,
242
+ expected_languages: ["en"],
243
+ telemetry: createAgentIdTelemetryContext({
244
+ workflowRunId,
245
+ workflowStepName: "answer_question",
246
+ toolName: "langchain.chat",
247
+ toolTargetType: "conversation",
248
+ eventCategory: "ai",
249
+ eventSubtype: "answer_generated",
250
+ }),
251
+ });
147
252
 
148
253
  const prompt = ChatPromptTemplate.fromTemplate("Answer in one sentence: {question}");
149
254
  const model = new ChatOpenAI({
@@ -157,29 +262,134 @@ const result = await chain.invoke(
157
262
  { callbacks: [handler] }
158
263
  );
159
264
  console.log(result);
160
- ```
161
-
162
- LangChain callbacks log on run completion. Token/cost telemetry for streamed chains depends on the provider exposing usage in the final LangChain result.
163
-
164
- ### Raw Ingest API (Telemetry Only)
165
-
166
- ```ts
167
- import { AgentID } from "agentid-sdk";
265
+ ```
266
+
267
+ LangChain callbacks log on run completion. Constructor-level `telemetry` is copied
268
+ to the guard request, local policy telemetry, and final ingest log. You can
269
+ override or extend it per invocation with LangChain metadata:
270
+ `{ metadata: { agentid_telemetry: { workflowStepName: "..." } } }`.
271
+ Token/cost telemetry for streamed chains depends on the provider exposing usage
272
+ in the final LangChain result.
273
+
274
+ ### Raw Ingest API (Telemetry Only)
275
+
276
+ ```ts
277
+ import { AgentID } from "agentid-sdk";
168
278
 
169
279
  const agent = new AgentID();
170
280
 
171
- await agent.log({
172
- system_id: process.env.AGENTID_SYSTEM_ID!,
173
- event_type: "complete",
174
- severity: "info",
175
- model: "gpt-4o-mini",
176
- input: "Raw telemetry prompt",
177
- output: '{"ok": true}',
178
- metadata: { agent_role: "batch-worker", channel: "manual_ingest" },
179
- });
180
- ```
181
-
182
- ### Transparency Badge (Article 50 UI Evidence)
281
+ await agent.log({
282
+ system_id: process.env.AGENTID_SYSTEM_ID!,
283
+ event_type: "complete",
284
+ severity: "info",
285
+ model: "gpt-4o-mini",
286
+ input: "Raw telemetry prompt",
287
+ output: '{"ok": true}',
288
+ usage: {
289
+ prompt_tokens: 33,
290
+ completion_tokens: 9,
291
+ total_tokens: 42,
292
+ },
293
+ latency: 1450,
294
+ metadata: { agent_role: "batch-worker", channel: "manual_ingest" },
295
+ });
296
+ ```
297
+
298
+ For manual integrations, preserve provider usage. Without `usage` or
299
+ normalized `tokens`, AgentID can store Activity but cannot compute token totals,
300
+ `cost_usd`, Total Spend, or ROI. ROI also requires the system business context
301
+ fields `human_hourly_rate` and `human_time_per_task_min`.
302
+
303
+ ### Agent workflow and tool events
304
+
305
+ Use `logOperation()` when an agent calls tools or performs operational work outside the wrapped LLM call. Reuse the same `workflowRunId` across steps.
306
+
307
+ ```ts
308
+ import {
309
+ AgentID,
310
+ createAgentIdCorrelationId,
311
+ createAgentIdTelemetryContext,
312
+ } from "agentid-sdk";
313
+
314
+ const agent = new AgentID();
315
+ const workflowRunId = createAgentIdCorrelationId();
316
+
317
+ await agent.logOperation({
318
+ system_id: process.env.AGENTID_SYSTEM_ID!,
319
+ telemetry: createAgentIdTelemetryContext({
320
+ workflowRunId,
321
+ workflowStepName: "screen_candidate",
322
+ toolName: "hr.cv_screen",
323
+ toolTargetType: "candidate",
324
+ }),
325
+ event_category: "tool",
326
+ event_status: "completed",
327
+ });
328
+
329
+ await agent.logOperation({
330
+ system_id: process.env.AGENTID_SYSTEM_ID!,
331
+ telemetry: createAgentIdTelemetryContext({
332
+ workflowRunId,
333
+ workflowStepName: "send_followup",
334
+ toolName: "email.send",
335
+ toolTargetType: "email",
336
+ }),
337
+ event_category: "delivery",
338
+ event_status: "completed",
339
+ });
340
+ ```
341
+
342
+ Tool, delivery, inbox, workflow, guard, and operational events are logged as separate audit rows. They are grouped in the dashboard by `workflow_run_id` and do not count as model-used or spend-bearing unless you explicitly provide model/usage data. Do not reuse one `client_event_id` for the whole workflow; use `workflowRunId` for grouping and let each event keep its own idempotency key.
343
+
344
+ Dashboard behavior:
345
+
346
+ - prompt/guard checks remain visible as standalone Activity rows with `View Details` and `View Prompt`
347
+ - workflow summary rows open the grouped timeline with tools, delivery, inbox, workflow lifecycle, guard checks, and LLM steps
348
+ - the workflow timeline is operational context; the standalone prompt row is the forensic prompt inspection surface
349
+ - non-model workflow/tool/delivery rows show `Model: Not applicable` and are not spend-bearing unless model/cost metadata is explicitly present
350
+
351
+ For full agent runs, prefer the workflow trail helper so each step gets a shared
352
+ `workflow_step_id`, plus automatic `started/completed/failed` rows:
353
+
354
+ ```ts
355
+ import {
356
+ AgentID,
357
+ createAgentIdCorrelationId,
358
+ createAgentIdTelemetryContext,
359
+ createAgentIdWorkflowTrail,
360
+ } from "agentid-sdk";
361
+
362
+ const agent = new AgentID({ apiKey: process.env.AGENTID_API_KEY! });
363
+ const workflowRunId = createAgentIdCorrelationId();
364
+
365
+ const trail = createAgentIdWorkflowTrail({
366
+ agent,
367
+ system_id: process.env.AGENTID_SYSTEM_ID!,
368
+ telemetry: createAgentIdTelemetryContext({
369
+ workflowRunId,
370
+ workflowName: "Candidate intake",
371
+ }),
372
+ });
373
+
374
+ await trail.runStep(
375
+ {
376
+ telemetry: createAgentIdTelemetryContext({
377
+ workflowStepName: "screen_candidate",
378
+ toolName: "hr.cv_screen",
379
+ toolTargetType: "candidate",
380
+ eventCategory: "tool",
381
+ }),
382
+ },
383
+ async () => screenCandidate(),
384
+ {
385
+ complete: {
386
+ metadata: { result_count: 4 },
387
+ },
388
+ }
389
+ );
390
+ ```
391
+
392
+ ### Transparency Badge (Article 50 UI Evidence)
183
393
 
184
394
  When rendering disclosure UI, log proof-of-render telemetry so you can demonstrate the end-user actually saw the badge.
185
395
 
@@ -251,11 +461,25 @@ By default, AgentID is designed to keep your application running if the AgentID
251
461
 
252
462
  - `guard()` returns a verdict (`allowed`, `reason`); handle deny paths explicitly.
253
463
  - `wrapOpenAI()` and LangChain handlers throw `SecurityBlockError` when a prompt is blocked.
254
- - Backend `/guard` is the default authority for prompt injection, DB access, code execution, and PII leakage in SDK-wrapped flows.
255
- - `clientFastFail` / `client_fast_fail` is optional and disabled by default. Enable it only when you explicitly want local preflight before the backend call.
256
- - If backend guard is unreachable and the effective failure mode is `fail_close`, wrapped OpenAI/LangChain flows can run local fallback enforcement. Local hits still block; otherwise the request can continue with fallback telemetry attached.
257
- - If `strictMode` is not explicitly set in SDK code, runtime behavior follows the system configuration from AgentID (`strict_security_mode` / `failure_mode`).
258
- - Ingest retries transient failures (5xx/429) and logs warnings if persistence fails.
464
+ - Backend `/guard` is the default authority for prompt injection, DB access, code execution, and PII leakage in SDK-wrapped flows.
465
+ - `clientFastFail` / `client_fast_fail` is optional and disabled by default. Enable it only when you explicitly want local preflight before the backend call.
466
+ - If backend guard is unreachable and the effective failure mode is `fail_close`, wrapped OpenAI/LangChain flows can run local fallback enforcement. Local hits still block; otherwise the request can continue with fallback telemetry attached.
467
+ - If `strictMode` is not explicitly set in SDK code, runtime behavior follows the system configuration from AgentID (`strict_security_mode` / `failure_mode`).
468
+ - Ingest retries transient failures (5xx/429) and logs warnings if persistence fails.
469
+
470
+ ### SDK-side masking scope
471
+
472
+ If `enable_sdk_pii_masking=true` in AgentID runtime config, or if you force
473
+ `piiMasking: true` in code, masking happens locally before `/guard` and before
474
+ provider dispatch.
475
+
476
+ - Default mode: backend-first enforcement, optional local masking
477
+ - `clientFastFail=false`: no local prompt/code/db blocker, but local masking can still rewrite prompt text before network dispatch
478
+ - `clientFastFail=true`: local prompt-injection scan and strict local enforcement can run before `/guard`
479
+
480
+ This means SDK masking is useful even when you keep backend guard as the main
481
+ policy authority: it reduces raw data exposure on the wire without changing the
482
+ server-side decision model.
259
483
 
260
484
  ### Event Identity Model
261
485
 
@@ -263,7 +487,7 @@ For consistent lifecycle correlation in Activity/Prompts, use this model:
263
487
 
264
488
  - `client_event_id`: external correlation ID for one end-to-end action.
265
489
  - `guard_event_id`: ID of the preflight guard event returned by `guard()`.
266
- - `event_id` on `log()`: idempotency key for ingest. In the JS SDK it is canonicalized to `client_event_id` for stable one-row lifecycle updates.
490
+ - `event_id` on `log()`: idempotency key for ingest. In `agentid-sdk` it is canonicalized to `client_event_id` for stable one-row lifecycle updates.
267
491
 
268
492
  SDK behavior:
269
493
 
@@ -289,7 +513,7 @@ SDK-managed metadata can include:
289
513
 
290
514
  ### Policy-Pack Runtime Telemetry
291
515
 
292
- When the backend uses compiled policy packs, runtime metadata includes:
516
+ When the backend uses compiled policy packs, runtime metadata includes:
293
517
 
294
518
  - `policy_pack_version`: active compiled artifact version.
295
519
  - `policy_pack_fallback`: `true` means fallback detector path was used.
@@ -299,7 +523,23 @@ Latency interpretation:
299
523
 
300
524
  - Activity `Latency (ms)` maps to synchronous processing (`processing_time_ms`).
301
525
  - Async AI audit time is separate (`ai_audit_duration_ms`) and can be higher.
302
- - First request after warm-up boundaries can be slower than steady-state requests.
526
+ - First request after warm-up boundaries can be slower than steady-state requests.
527
+
528
+ ### Secret and PII Masking Edge Cases
529
+
530
+ SDK-side masking and the backend scanner include regression coverage for common
531
+ boundary failures:
532
+
533
+ - multiline PEM, certificate, and PGP private key blocks
534
+ - natural-language password disclosures such as `my Password is Passwordk123`
535
+ - environment-style assignments such as `DB_PASSWORD=...`
536
+ - secret values with suffix punctuation such as `#`
537
+ - high-entropy base64-like values with `=` / `==` padding
538
+ - security-question answers where the value appears after `answer is`, `is`, or localized equivalents
539
+
540
+ When local masking is enabled, these values are replaced before provider
541
+ dispatch and before AgentID ingest. Placeholder mappings stay local to the SDK
542
+ for reversible deanonymization.
303
543
 
304
544
  ### Monorepo QA Commands (Maintainers)
305
545
 
@@ -319,21 +559,27 @@ powershell -ExecutionPolicy Bypass -File .\scripts\qa\run-ai-label-audit-check.p
319
559
 
320
560
  ## 7. Security & Compliance
321
561
 
322
- - Backend `/guard` remains the primary enforcement authority by default.
323
- - Optional local PII masking and opt-in `clientFastFail` are available for edge cases.
324
- - Guard checks run pre-execution; ingest + finalize telemetry captures prompt/output lifecycle and SDK timing breakdowns.
325
- - Safe for server and serverless runtimes (including async completion flows).
326
- - Supports compliance and forensics workflows with durable event records.
327
-
328
- ## 8. Support
562
+ - Backend `/guard` remains the primary enforcement authority by default.
563
+ - Optional local masking and opt-in `clientFastFail` are available for edge cases.
564
+ - SDK-side masking can now cover both structured PII and high-confidence leaked secrets before provider dispatch.
565
+ - Guard checks run pre-execution; ingest + finalize telemetry captures prompt/output lifecycle and SDK timing breakdowns.
566
+ - Safe for server and serverless runtimes (including async completion flows).
567
+ - Supports compliance and forensics workflows with durable event records.
329
568
 
330
- - Dashboard: `https://app.getagentid.com`
331
- - Repository: `https://github.com/ondrejsukac-rgb/agentid/tree/main/js-sdk`
332
- - Issues: `https://github.com/ondrejsukac-rgb/agentid/issues`
333
-
334
- ## 9. Publishing Notes (NPM)
335
-
336
- NPM automatically renders `README.md` from the package root during `npm publish`.
569
+ ## 8. Support
570
+
571
+ - Dashboard: `https://app.getagentid.com`
572
+ - Documentation: `https://docs.getagentid.com/docs/node-typescript-sdk`
573
+ - Repository: `https://github.com/ondrejsukac-rgb/agentid/tree/main/agentid-sdk`
574
+ - Issues: `https://github.com/ondrejsukac-rgb/agentid/issues`
337
575
 
338
- - File location: next to `package.json` in `js-sdk/`.
339
- - No additional NPM config is required for README rendering.
576
+ ## 9. Publishing Notes (NPM)
577
+
578
+ NPM automatically renders `README.md` from the package root during `npm publish`.
579
+
580
+ - File location: next to `package.json` in `agentid-sdk/`.
581
+ - No additional NPM config is required for README rendering.
582
+ - Before publishing from the monorepo, run `npm run audit:all` and
583
+ `npm run qa:production-gate` from the repository root.
584
+ - The production gate audits the root app, `agentid-sdk`, `agentid-vercel-sdk`, and
585
+ the browser extension package so package-local lockfile issues are not missed.