@astrasyncai/verification-gateway 3.9.0 → 3.10.1

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.
Files changed (108) hide show
  1. package/dist/adapter-interface/interface.d.mts +3 -2
  2. package/dist/adapter-interface/interface.d.ts +3 -2
  3. package/dist/adapters/express.d.mts +3 -2
  4. package/dist/adapters/express.d.ts +3 -2
  5. package/dist/adapters/express.js +151 -2
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +151 -2
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/mcp.d.mts +2 -1
  10. package/dist/adapters/mcp.d.ts +2 -1
  11. package/dist/adapters/mcp.js +1 -1
  12. package/dist/adapters/mcp.js.map +1 -1
  13. package/dist/adapters/mcp.mjs +1 -1
  14. package/dist/adapters/mcp.mjs.map +1 -1
  15. package/dist/adapters/nextjs.d.mts +3 -2
  16. package/dist/adapters/nextjs.d.ts +3 -2
  17. package/dist/adapters/nextjs.js +151 -2
  18. package/dist/adapters/nextjs.js.map +1 -1
  19. package/dist/adapters/nextjs.mjs +151 -2
  20. package/dist/adapters/nextjs.mjs.map +1 -1
  21. package/dist/adapters/sdk.d.mts +3 -2
  22. package/dist/adapters/sdk.d.ts +3 -2
  23. package/dist/adapters/sdk.js +1 -1
  24. package/dist/adapters/sdk.js.map +1 -1
  25. package/dist/adapters/sdk.mjs +1 -1
  26. package/dist/adapters/sdk.mjs.map +1 -1
  27. package/dist/agent/index.d.mts +3 -2
  28. package/dist/agent/index.d.ts +3 -2
  29. package/dist/bin/astrasync-claude-hook.js +276 -25
  30. package/dist/bin/astrasync-guard.js +258 -23
  31. package/dist/bin/astrasync.js +282 -24
  32. package/dist/browser/background.js +257 -22
  33. package/dist/browser/background.js.map +1 -1
  34. package/dist/browser/background.mjs +257 -22
  35. package/dist/browser/background.mjs.map +1 -1
  36. package/dist/browser/browser-adapter.d.mts +3 -2
  37. package/dist/browser/browser-adapter.d.ts +3 -2
  38. package/dist/claude-code/claude-code-adapter.d.mts +3 -2
  39. package/dist/claude-code/claude-code-adapter.d.ts +3 -2
  40. package/dist/cli/index.d.mts +3 -2
  41. package/dist/cli/index.d.ts +3 -2
  42. package/dist/cursor/cursor-adapter.d.mts +3 -2
  43. package/dist/cursor/cursor-adapter.d.ts +3 -2
  44. package/dist/cursor/extension.d.mts +3 -2
  45. package/dist/cursor/extension.d.ts +3 -2
  46. package/dist/cursor/extension.js +257 -22
  47. package/dist/cursor/extension.js.map +1 -1
  48. package/dist/cursor/extension.mjs +257 -22
  49. package/dist/cursor/extension.mjs.map +1 -1
  50. package/dist/edge-config.d.mts +2 -1
  51. package/dist/edge-config.d.ts +2 -1
  52. package/dist/{express-sGXhDdd0.d.ts → express-ClNtDhhc.d.ts} +1 -1
  53. package/dist/{express-DXUyvv7t.d.mts → express-DCXa1oqj.d.mts} +1 -1
  54. package/dist/gateway/gateway.d.mts +29 -3
  55. package/dist/gateway/gateway.d.ts +29 -3
  56. package/dist/gateway/gateway.js +257 -22
  57. package/dist/gateway/gateway.js.map +1 -1
  58. package/dist/gateway/gateway.mjs +257 -22
  59. package/dist/gateway/gateway.mjs.map +1 -1
  60. package/dist/git-trigger/git-hooks.d.mts +3 -2
  61. package/dist/git-trigger/git-hooks.d.ts +3 -2
  62. package/dist/{index-Dalih3EB.d.mts → index-BrNKDmiR.d.mts} +1 -1
  63. package/dist/{index-C61VL_OW.d.ts → index-CfHurO1P.d.ts} +1 -1
  64. package/dist/{index-CMg-vjEK.d.ts → index-D2ztkumS.d.ts} +1 -1
  65. package/dist/{index-DmmdehNu.d.mts → index-Dr_XiN65.d.mts} +1 -1
  66. package/dist/index.d.mts +10 -9
  67. package/dist/index.d.ts +10 -9
  68. package/dist/index.js +218 -11
  69. package/dist/index.js.map +1 -1
  70. package/dist/index.mjs +211 -11
  71. package/dist/index.mjs.map +1 -1
  72. package/dist/local-evaluator/evaluator.d.mts +3 -2
  73. package/dist/local-evaluator/evaluator.d.ts +3 -2
  74. package/dist/local-evaluator/evaluator.js +165 -7
  75. package/dist/local-evaluator/evaluator.js.map +1 -1
  76. package/dist/local-evaluator/evaluator.mjs +165 -7
  77. package/dist/local-evaluator/evaluator.mjs.map +1 -1
  78. package/dist/metadata-capture.d.mts +87 -0
  79. package/dist/metadata-capture.d.ts +87 -0
  80. package/dist/metadata-capture.js +184 -0
  81. package/dist/metadata-capture.js.map +1 -0
  82. package/dist/metadata-capture.mjs +154 -0
  83. package/dist/metadata-capture.mjs.map +1 -0
  84. package/dist/{nextjs-DwUW-Nm1.d.ts → nextjs-CEBVBsdi.d.ts} +1 -1
  85. package/dist/{nextjs-F_2pbPIA.d.mts → nextjs-DcQIk6Zj.d.mts} +1 -1
  86. package/dist/platform-signatures.d.mts +60 -10
  87. package/dist/platform-signatures.d.ts +60 -10
  88. package/dist/platform-signatures.js +37 -10
  89. package/dist/platform-signatures.js.map +1 -1
  90. package/dist/platform-signatures.mjs +35 -9
  91. package/dist/platform-signatures.mjs.map +1 -1
  92. package/dist/{sdk-Clob2-0A.d.mts → sdk-CZkbSOeO.d.mts} +1 -1
  93. package/dist/{sdk-CfHANs4A.d.ts → sdk-H0JCLW2y.d.ts} +1 -1
  94. package/dist/transport/index.d.mts +3 -2
  95. package/dist/transport/index.d.ts +3 -2
  96. package/dist/{types-ACMsV07B.d.mts → types-9tns4wyl.d.ts} +12 -0
  97. package/dist/{types-DmSDgPpG.d.ts → types-BV6abBnF.d.mts} +7 -1
  98. package/dist/{types-BtqUvK8v.d.mts → types-C23_uaY8.d.ts} +7 -1
  99. package/dist/{types-ACMsV07B.d.ts → types-sy3tq8lp.d.mts} +12 -0
  100. package/dist/ui/index.d.mts +2 -1
  101. package/dist/ui/index.d.ts +2 -1
  102. package/dist/verify.d.mts +8 -1
  103. package/dist/verify.d.ts +8 -1
  104. package/dist/verify.js +1 -1
  105. package/dist/verify.js.map +1 -1
  106. package/dist/verify.mjs +1 -1
  107. package/dist/verify.mjs.map +1 -1
  108. package/package.json +6 -1
@@ -282,7 +282,9 @@ var HIGH_RISK_COMMANDS = [
282
282
  "git push",
283
283
  "npm publish",
284
284
  "docker",
285
- "kubectl"
285
+ "kubectl",
286
+ "gh pr create",
287
+ "gh pr merge"
286
288
  ];
287
289
  var SENSITIVE_PATHS = [
288
290
  ".ssh",
@@ -301,6 +303,164 @@ var SENSITIVE_PATHS = [
301
303
  ".pypirc"
302
304
  ];
303
305
 
306
+ // src/local-evaluator/command-matcher.ts
307
+ var WRAPPERS = /* @__PURE__ */ new Set([
308
+ "sudo",
309
+ "doas",
310
+ "env",
311
+ "nohup",
312
+ "time",
313
+ "timeout",
314
+ "nice",
315
+ "ionice",
316
+ "stdbuf",
317
+ "xargs",
318
+ "command",
319
+ "exec",
320
+ "builtin"
321
+ ]);
322
+ var ASSIGN_RE = /^[a-z_][a-z0-9_]*=/;
323
+ var MAX_WRAPPER_HOPS = 4;
324
+ var MAX_RECURSION_DEPTH = 3;
325
+ var MAX_COMMAND_LENGTH = 1e5;
326
+ var LOCAL_HOST_RE = /^(localhost|127\.0\.0\.1|::1|[a-z0-9.-]*\.localhost)$/;
327
+ function buildRiskList(entries) {
328
+ const singleWord = /* @__PURE__ */ new Set();
329
+ const multiWord = [];
330
+ for (const entry of entries) {
331
+ const words = entry.toLowerCase().trim().split(/\s+/);
332
+ if (words.length === 1) singleWord.add(words[0]);
333
+ else multiWord.push(words);
334
+ }
335
+ multiWord.sort((a, b) => b.length - a.length);
336
+ return { singleWord, multiWord };
337
+ }
338
+ function stripHeredocBodies(text) {
339
+ const lines = text.split("\n");
340
+ const kept = [];
341
+ let terminator = null;
342
+ for (const line of lines) {
343
+ if (terminator !== null) {
344
+ if (line.trim() === terminator) terminator = null;
345
+ continue;
346
+ }
347
+ kept.push(line);
348
+ const heredoc = line.match(/<<-?\s*['"]?(\w+)['"]?/);
349
+ if (heredoc) terminator = heredoc[1];
350
+ }
351
+ return kept.join("\n");
352
+ }
353
+ function extractDollarSubstitutions(text, collect) {
354
+ let result = text;
355
+ let start = result.indexOf("$(");
356
+ let iterations = 0;
357
+ while (start !== -1 && iterations < 50) {
358
+ iterations++;
359
+ let depth = 0;
360
+ let end = result.length;
361
+ for (let i = start + 1; i < result.length; i++) {
362
+ if (result[i] === "(") depth++;
363
+ else if (result[i] === ")") {
364
+ depth--;
365
+ if (depth === 0) {
366
+ end = i;
367
+ break;
368
+ }
369
+ }
370
+ }
371
+ collect(result.slice(start + 2, end));
372
+ result = `${result.slice(0, start)} ${result.slice(Math.min(end + 1, result.length))}`;
373
+ start = result.indexOf("$(");
374
+ }
375
+ return result;
376
+ }
377
+ function extractBacktickSubstitutions(text, collect) {
378
+ if (!text.includes("`")) return text;
379
+ const chunks = text.split("`");
380
+ const kept = [];
381
+ for (let i = 0; i < chunks.length; i++) {
382
+ if (i % 2 === 1) collect(chunks[i]);
383
+ else kept.push(chunks[i]);
384
+ }
385
+ return kept.join(" ");
386
+ }
387
+ function basename(token) {
388
+ const slash = token.lastIndexOf("/");
389
+ return slash === -1 ? token : token.slice(slash + 1);
390
+ }
391
+ function matchSegment(segment, list) {
392
+ const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
393
+ let i = 0;
394
+ let hops = 0;
395
+ while (i < tokens.length) {
396
+ while (i < tokens.length && (tokens[i] === "export" || ASSIGN_RE.test(tokens[i]))) i++;
397
+ if (i >= tokens.length) return null;
398
+ const cmd = basename(tokens[i]);
399
+ const significant = [cmd, ...tokens.slice(i + 1).filter((t) => !t.startsWith("-"))];
400
+ for (const entry of list.multiWord) {
401
+ if (entry.length <= significant.length && entry.every((w, j) => significant[j] === w)) {
402
+ return entry.join(" ");
403
+ }
404
+ }
405
+ if (list.singleWord.has(cmd)) return cmd;
406
+ if (WRAPPERS.has(cmd) && hops < MAX_WRAPPER_HOPS) {
407
+ hops++;
408
+ i++;
409
+ while (i < tokens.length && (tokens[i].startsWith("-") || ASSIGN_RE.test(tokens[i]))) i++;
410
+ continue;
411
+ }
412
+ return null;
413
+ }
414
+ return null;
415
+ }
416
+ function collectMatches(command, list, matches, depth) {
417
+ if (depth > MAX_RECURSION_DEPTH || command.length > MAX_COMMAND_LENGTH) return;
418
+ let text = command.toLowerCase();
419
+ text = stripHeredocBodies(text);
420
+ const recurse = (inner) => collectMatches(inner, list, matches, depth + 1);
421
+ text = extractDollarSubstitutions(text, recurse);
422
+ text = extractBacktickSubstitutions(text, recurse);
423
+ text = text.replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
424
+ text = text.replace(/(^|\s)#[^\n]*/g, " ");
425
+ const segments = text.split(/\|\||&&|[;|&\n]|[({]/);
426
+ for (const segment of segments) {
427
+ const matched = matchSegment(segment, list);
428
+ if (matched) matches.add(matched);
429
+ }
430
+ }
431
+ function matchHighRiskCommands(command, highRiskCommands = HIGH_RISK_COMMANDS) {
432
+ if (!command) return [];
433
+ const matches = /* @__PURE__ */ new Set();
434
+ collectMatches(command, buildRiskList(highRiskCommands), matches, 0);
435
+ return [...matches];
436
+ }
437
+ function isLocalhostOnlyNetworkCommand(command) {
438
+ const text = command.toLowerCase();
439
+ const tokens = text.split(/\s+/).map((t) => t.replace(/^["'`]+|["'`,;)]+$/g, ""));
440
+ let localCount = 0;
441
+ for (const token of tokens) {
442
+ let host = null;
443
+ if (token.includes("://")) {
444
+ let rest = token.slice(token.indexOf("://") + 3);
445
+ const at = rest.indexOf("@");
446
+ if (at !== -1) rest = rest.slice(at + 1);
447
+ host = rest.split(/[/?#]/)[0];
448
+ } else if (/^[a-z0-9.[\]:-]+:\d+(\/.*)?$/.test(token)) {
449
+ host = token.split("/")[0];
450
+ } else if (/^(localhost|127\.0\.0\.1|\[?::1\]?)(\/.*)?$/.test(token)) {
451
+ host = token.split("/")[0];
452
+ }
453
+ if (host === null) continue;
454
+ const bare = host.replace(/:\d+$/, "").replace(/^\[|\]$/g, "");
455
+ if (LOCAL_HOST_RE.test(bare)) {
456
+ localCount++;
457
+ } else {
458
+ return false;
459
+ }
460
+ }
461
+ return localCount > 0;
462
+ }
463
+
304
464
  // src/local-evaluator/evaluator.ts
305
465
  var LocalEvaluator = class {
306
466
  constructor(policy) {
@@ -451,12 +611,10 @@ var LocalEvaluator = class {
451
611
  return score;
452
612
  }
453
613
  if (context.purpose === "shell.exec" && context.target) {
454
- const targetLower = context.target.toLowerCase();
455
- for (const cmd of HIGH_RISK_COMMANDS) {
456
- if (targetLower.startsWith(cmd) || targetLower.includes(` ${cmd} `) || targetLower.includes(` ${cmd}`)) {
457
- score = Math.max(score, 80);
458
- break;
459
- }
614
+ const matched = matchHighRiskCommands(context.target);
615
+ const localProbeOnly = matched.length > 0 && matched.every((cmd) => cmd === "curl" || cmd === "wget") && isLocalhostOnlyNetworkCommand(context.target);
616
+ if (matched.length > 0 && !localProbeOnly) {
617
+ score = Math.max(score, 80);
460
618
  }
461
619
  }
462
620
  if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
@@ -3767,7 +3925,7 @@ function getTrustLevel(score) {
3767
3925
  }
3768
3926
 
3769
3927
  // src/version.ts
3770
- var SDK_VERSION = "3.9.0";
3928
+ var SDK_VERSION = "3.10.1";
3771
3929
 
3772
3930
  // src/well-known.ts
3773
3931
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -4183,6 +4341,9 @@ async function verify(config, request, options) {
4183
4341
  // src/adapters/stepup-poll.ts
4184
4342
  var FALLBACK_MAX_WAIT_MS = 5 * 60 * 1e3;
4185
4343
 
4344
+ // src/metadata-capture.ts
4345
+ var MAX_TOTAL_BYTES = 16 * 1024;
4346
+
4186
4347
  // src/adapters/express.ts
4187
4348
  var DEFAULT_ROUTES_REFRESH_MS = 5 * 60 * 1e3;
4188
4349
 
@@ -4325,8 +4486,75 @@ async function verifyOnline(config, context, astraId) {
4325
4486
  // src/gateway/modes/hybrid.ts
4326
4487
  import { existsSync, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "fs";
4327
4488
  import { dirname } from "path";
4489
+
4490
+ // src/gateway/redact.ts
4491
+ var marker = (type2) => `[REDACTED:${type2}]`;
4492
+ var REDACTION_PATTERNS = [
4493
+ // Multi-line blocks first so later single-line patterns don't shred them.
4494
+ {
4495
+ type: "private-key",
4496
+ re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?(?:-----END [A-Z ]*PRIVATE KEY-----|$)/g
4497
+ },
4498
+ // URL userinfo credentials (https://user:pass@, x-access-token:ghp_…@).
4499
+ {
4500
+ type: "url-credentials",
4501
+ re: /([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi,
4502
+ replacement: `$1${marker("url-credentials")}@`
4503
+ },
4504
+ // Known token formats.
4505
+ { type: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,255}\b/g },
4506
+ { type: "github-token", re: /\bgithub_pat_[A-Za-z0-9_]{20,255}\b/g },
4507
+ { type: "astrasync-key", re: /\bkya_[A-Fa-f0-9]{32,128}\b/g },
4508
+ { type: "openai-key", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g },
4509
+ { type: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/g },
4510
+ { type: "jwt", re: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/g },
4511
+ // Bearer/token headers.
4512
+ {
4513
+ type: "bearer-token",
4514
+ re: /\b(bearer\s+)[a-z0-9._~+/=-]{16,}/gi,
4515
+ replacement: `$1${marker("bearer-token")}`
4516
+ },
4517
+ // Generic KEY=value / key: value assignments with secret-ish names.
4518
+ {
4519
+ type: "assigned-secret",
4520
+ re: /\b((?:password|passwd|secret|token|api[_-]?key|apikey|access[_-]?key|client[_-]?secret|private[_-]?key)\s*[=:]\s*)(['"]?)[^\s'"&;]{8,}\2/gi,
4521
+ replacement: `$1$2${marker("assigned-secret")}$2`
4522
+ }
4523
+ ];
4524
+ function redactSecrets(text) {
4525
+ let result = text;
4526
+ for (const { type: type2, re, replacement } of REDACTION_PATTERNS) {
4527
+ result = result.replace(re, replacement ?? marker(type2));
4528
+ }
4529
+ return result;
4530
+ }
4531
+ function redactDeep(value) {
4532
+ if (typeof value === "string") {
4533
+ return redactSecrets(value);
4534
+ }
4535
+ if (Array.isArray(value)) {
4536
+ return value.map((item) => redactDeep(item));
4537
+ }
4538
+ if (value !== null && typeof value === "object" && !(value instanceof Date)) {
4539
+ const out = {};
4540
+ for (const [k, v] of Object.entries(value)) {
4541
+ out[k] = redactDeep(v);
4542
+ }
4543
+ return out;
4544
+ }
4545
+ return value;
4546
+ }
4547
+
4548
+ // src/gateway/modes/hybrid.ts
4328
4549
  var MAX_QUEUE_SIZE = 1e3;
4329
4550
  var MAX_RETRIES = 3;
4551
+ var FLUSH_TIMEOUT = /* @__PURE__ */ Symbol("flush-timeout");
4552
+ function delay(ms) {
4553
+ return new Promise((resolve) => {
4554
+ const timer = setTimeout(() => resolve(FLUSH_TIMEOUT), ms);
4555
+ if (typeof timer.unref === "function") timer.unref();
4556
+ });
4557
+ }
4330
4558
  var HybridSyncQueue = class {
4331
4559
  constructor(config, persistPath) {
4332
4560
  this.queue = [];
@@ -4347,12 +4575,12 @@ var HybridSyncQueue = class {
4347
4575
  /**
4348
4576
  * Stop periodic sync and flush remaining entries.
4349
4577
  */
4350
- async stop() {
4578
+ async stop(flushOptions = {}) {
4351
4579
  if (this.timer) {
4352
4580
  clearInterval(this.timer);
4353
4581
  this.timer = null;
4354
4582
  }
4355
- await this.flush();
4583
+ await this.flush(flushOptions);
4356
4584
  this.saveToDisk();
4357
4585
  }
4358
4586
  /**
@@ -4364,8 +4592,8 @@ var HybridSyncQueue = class {
4364
4592
  }
4365
4593
  this.queue.push({
4366
4594
  id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
4367
- context,
4368
- decision,
4595
+ context: redactDeep(context),
4596
+ decision: redactDeep(decision),
4369
4597
  timestamp: /* @__PURE__ */ new Date(),
4370
4598
  retryCount: 0,
4371
4599
  status: "pending"
@@ -4375,16 +4603,21 @@ var HybridSyncQueue = class {
4375
4603
  /**
4376
4604
  * Flush pending entries to the API.
4377
4605
  */
4378
- async flush() {
4606
+ async flush(options = {}) {
4379
4607
  if (this.processing) return;
4380
4608
  this.processing = true;
4381
4609
  try {
4382
4610
  const pending = this.queue.filter((e) => e.status === "pending");
4383
- for (const entry of pending) {
4384
- try {
4385
- await verifyOnline(this.config, entry.context);
4611
+ const batch = typeof options.limit === "number" ? pending.slice(0, options.limit) : pending;
4612
+ for (const entry of batch) {
4613
+ const attempt = verifyOnline(this.config, entry.context, this.config.agentId).then(() => "synced").catch(() => "error");
4614
+ const result = options.perEntryTimeoutMs ? await Promise.race([attempt, delay(options.perEntryTimeoutMs)]) : await attempt;
4615
+ if (result === FLUSH_TIMEOUT) {
4616
+ break;
4617
+ }
4618
+ if (result === "synced") {
4386
4619
  entry.status = "synced";
4387
- } catch {
4620
+ } else {
4388
4621
  entry.retryCount++;
4389
4622
  if (entry.retryCount >= MAX_RETRIES) {
4390
4623
  entry.status = "failed";
@@ -4461,8 +4694,8 @@ var TraceLogger = class {
4461
4694
  id: this.generateId(),
4462
4695
  timestamp: /* @__PURE__ */ new Date(),
4463
4696
  type: "evaluation",
4464
- context,
4465
- decision
4697
+ context: redactDeep(context),
4698
+ decision: redactDeep(decision)
4466
4699
  });
4467
4700
  }
4468
4701
  /**
@@ -4579,10 +4812,12 @@ var AstraSyncGateway = class {
4579
4812
  }
4580
4813
  /**
4581
4814
  * Graceful shutdown. Flushes sync queue and cleans up resources.
4815
+ * Stateless callers (hooks) pass bounded flush options so sync never
4816
+ * sits on a tool call's critical path.
4582
4817
  */
4583
- async shutdown() {
4818
+ async shutdown(options) {
4584
4819
  if (this.syncQueue) {
4585
- await this.syncQueue.stop();
4820
+ await this.syncQueue.stop(options?.syncFlush ?? {});
4586
4821
  this.syncQueue = null;
4587
4822
  }
4588
4823
  this.evaluator = null;
@@ -4605,7 +4840,7 @@ var AstraSyncGateway = class {
4605
4840
  let decision;
4606
4841
  switch (this.config.mode) {
4607
4842
  case "online":
4608
- decision = await verifyOnline(this.config, context, astraId);
4843
+ decision = await verifyOnline(this.config, context, astraId ?? this.config.agentId);
4609
4844
  break;
4610
4845
  case "local":
4611
4846
  if (!this.evaluator) throw new Error("Local evaluator not initialized");