@astrasyncai/verification-gateway 3.11.0 → 3.12.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.
Files changed (103) hide show
  1. package/dist/adapter-interface/interface.d.mts +2 -2
  2. package/dist/adapter-interface/interface.d.ts +2 -2
  3. package/dist/adapters/express.d.mts +2 -2
  4. package/dist/adapters/express.d.ts +2 -2
  5. package/dist/adapters/express.js +22 -3
  6. package/dist/adapters/express.js.map +1 -1
  7. package/dist/adapters/express.mjs +22 -3
  8. package/dist/adapters/express.mjs.map +1 -1
  9. package/dist/adapters/mcp.d.mts +1 -1
  10. package/dist/adapters/mcp.d.ts +1 -1
  11. package/dist/adapters/mcp.js +22 -3
  12. package/dist/adapters/mcp.js.map +1 -1
  13. package/dist/adapters/mcp.mjs +22 -3
  14. package/dist/adapters/mcp.mjs.map +1 -1
  15. package/dist/adapters/nextjs.d.mts +2 -2
  16. package/dist/adapters/nextjs.d.ts +2 -2
  17. package/dist/adapters/nextjs.js +22 -3
  18. package/dist/adapters/nextjs.js.map +1 -1
  19. package/dist/adapters/nextjs.mjs +22 -3
  20. package/dist/adapters/nextjs.mjs.map +1 -1
  21. package/dist/adapters/sdk.d.mts +2 -2
  22. package/dist/adapters/sdk.d.ts +2 -2
  23. package/dist/adapters/sdk.js +22 -3
  24. package/dist/adapters/sdk.js.map +1 -1
  25. package/dist/adapters/sdk.mjs +22 -3
  26. package/dist/adapters/sdk.mjs.map +1 -1
  27. package/dist/agent/index.d.mts +2 -2
  28. package/dist/agent/index.d.ts +2 -2
  29. package/dist/bin/astrasync-claude-hook.js +84 -21
  30. package/dist/bin/astrasync-codex-hook.js +84 -21
  31. package/dist/bin/astrasync-guard.js +84 -21
  32. package/dist/bin/astrasync.js +85 -21
  33. package/dist/browser/background.js +84 -21
  34. package/dist/browser/background.js.map +1 -1
  35. package/dist/browser/background.mjs +84 -21
  36. package/dist/browser/background.mjs.map +1 -1
  37. package/dist/browser/browser-adapter.d.mts +2 -2
  38. package/dist/browser/browser-adapter.d.ts +2 -2
  39. package/dist/claude-code/claude-code-adapter.d.mts +2 -2
  40. package/dist/claude-code/claude-code-adapter.d.ts +2 -2
  41. package/dist/cli/index.d.mts +2 -2
  42. package/dist/cli/index.d.ts +2 -2
  43. package/dist/codex/index.d.mts +2 -2
  44. package/dist/codex/index.d.ts +2 -2
  45. package/dist/codex/index.js +84 -21
  46. package/dist/codex/index.js.map +1 -1
  47. package/dist/codex/index.mjs +84 -21
  48. package/dist/codex/index.mjs.map +1 -1
  49. package/dist/cursor/cursor-adapter.d.mts +2 -2
  50. package/dist/cursor/cursor-adapter.d.ts +2 -2
  51. package/dist/cursor/extension.d.mts +2 -2
  52. package/dist/cursor/extension.d.ts +2 -2
  53. package/dist/cursor/extension.js +84 -21
  54. package/dist/cursor/extension.js.map +1 -1
  55. package/dist/cursor/extension.mjs +84 -21
  56. package/dist/cursor/extension.mjs.map +1 -1
  57. package/dist/edge-config.d.mts +1 -1
  58. package/dist/edge-config.d.ts +1 -1
  59. package/dist/{express-DCXa1oqj.d.mts → express-B39o89gf.d.mts} +1 -1
  60. package/dist/{express-ClNtDhhc.d.ts → express-BuxKNO_q.d.ts} +1 -1
  61. package/dist/gateway/gateway.d.mts +2 -2
  62. package/dist/gateway/gateway.d.ts +2 -2
  63. package/dist/gateway/gateway.js +84 -21
  64. package/dist/gateway/gateway.js.map +1 -1
  65. package/dist/gateway/gateway.mjs +84 -21
  66. package/dist/gateway/gateway.mjs.map +1 -1
  67. package/dist/git-trigger/git-hooks.d.mts +2 -2
  68. package/dist/git-trigger/git-hooks.d.ts +2 -2
  69. package/dist/{index-CfHurO1P.d.ts → index-BG62SXso.d.ts} +1 -1
  70. package/dist/{index-D2ztkumS.d.ts → index-CLHdqs2M.d.ts} +1 -1
  71. package/dist/{index-Dr_XiN65.d.mts → index-CqydB4ks.d.mts} +1 -1
  72. package/dist/{index-BrNKDmiR.d.mts → index-DME3w3lf.d.mts} +1 -1
  73. package/dist/index.d.mts +9 -9
  74. package/dist/index.d.ts +9 -9
  75. package/dist/index.js +42 -3
  76. package/dist/index.js.map +1 -1
  77. package/dist/index.mjs +41 -3
  78. package/dist/index.mjs.map +1 -1
  79. package/dist/local-evaluator/evaluator.d.mts +11 -2
  80. package/dist/local-evaluator/evaluator.d.ts +11 -2
  81. package/dist/local-evaluator/evaluator.js +20 -9
  82. package/dist/local-evaluator/evaluator.js.map +1 -1
  83. package/dist/local-evaluator/evaluator.mjs +20 -9
  84. package/dist/local-evaluator/evaluator.mjs.map +1 -1
  85. package/dist/{nextjs-DcQIk6Zj.d.mts → nextjs-D74W7wt-.d.mts} +1 -1
  86. package/dist/{nextjs-CEBVBsdi.d.ts → nextjs-Ddlw_J1J.d.ts} +1 -1
  87. package/dist/{sdk-CZkbSOeO.d.mts → sdk-Bn6bj7kt.d.mts} +1 -1
  88. package/dist/{sdk-H0JCLW2y.d.ts → sdk-CT7gdkfR.d.ts} +1 -1
  89. package/dist/transport/index.d.mts +2 -2
  90. package/dist/transport/index.d.ts +2 -2
  91. package/dist/{types-sy3tq8lp.d.mts → types-BntuzEEn.d.mts} +103 -1
  92. package/dist/{types-BV6abBnF.d.mts → types-DHu7m9HI.d.ts} +7 -2
  93. package/dist/{types-C23_uaY8.d.ts → types-Djw4GLtz.d.mts} +7 -2
  94. package/dist/{types-9tns4wyl.d.ts → types-SDu4Llbe.d.ts} +103 -1
  95. package/dist/ui/index.d.mts +1 -1
  96. package/dist/ui/index.d.ts +1 -1
  97. package/dist/verify.d.mts +13 -2
  98. package/dist/verify.d.ts +13 -2
  99. package/dist/verify.js +42 -3
  100. package/dist/verify.js.map +1 -1
  101. package/dist/verify.mjs +41 -3
  102. package/dist/verify.mjs.map +1 -1
  103. package/package.json +1 -1
@@ -150,8 +150,9 @@ function extractBacktickSubstitutions(text, collect) {
150
150
  return kept.join(" ");
151
151
  }
152
152
  function basename(token) {
153
- const slash = token.lastIndexOf("/");
154
- return slash === -1 ? token : token.slice(slash + 1);
153
+ const slash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
154
+ const name = slash === -1 ? token : token.slice(slash + 1);
155
+ return name.replace(/\.(exe|cmd|bat|ps1)$/i, "");
155
156
  }
156
157
  function matchSegment(segment, list) {
157
158
  const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
@@ -284,7 +285,9 @@ var LocalEvaluator = class {
284
285
  }
285
286
  return {
286
287
  recommendation: "ALLOW",
287
- reason: "All PDLSS checks passed",
288
+ // "evaluated": dimensions absent from the policy are skipped, not
289
+ // failed — evaluatedDimensions below records which ones ran.
290
+ reason: "All evaluated PDLSS checks passed",
288
291
  evaluatedDimensions: {
289
292
  purpose: true,
290
293
  scope: !!this.policy.scope,
@@ -383,7 +386,7 @@ var LocalEvaluator = class {
383
386
  }
384
387
  }
385
388
  if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
386
- const targetLower = context.target.toLowerCase();
389
+ const targetLower = context.target.toLowerCase().replace(/\\/g, "/");
387
390
  for (const sensitivePath of SENSITIVE_PATHS) {
388
391
  if (targetLower.includes(sensitivePath.toLowerCase())) {
389
392
  score = Math.max(score, 50);
@@ -412,12 +415,20 @@ var LocalEvaluator = class {
412
415
  matchesAnyPattern(value, patterns) {
413
416
  return patterns.some((pattern) => this.matchGlob(value, pattern));
414
417
  }
415
- /**
416
- * Simple glob pattern matching.
417
- * Supports * (matches any characters including / and spaces) and ? (single char).
418
- * Uses the same approach as the backend's matchGlobPattern.
419
- */
418
+ homePrefix() {
419
+ if (this.cachedHomePrefix !== void 0) return this.cachedHomePrefix;
420
+ const env = typeof process !== "undefined" ? process.env : void 0;
421
+ const home = env?.HOME || env?.USERPROFILE;
422
+ this.cachedHomePrefix = home ? home.replace(/\\/g, "/") : null;
423
+ return this.cachedHomePrefix;
424
+ }
420
425
  matchGlob(value, pattern) {
426
+ value = value.replace(/\\/g, "/");
427
+ pattern = pattern.replace(/\\/g, "/");
428
+ if (pattern === "~" || pattern.startsWith("~/")) {
429
+ const home = this.homePrefix();
430
+ if (home) pattern = home + pattern.slice(1);
431
+ }
421
432
  if (pattern === value) return true;
422
433
  const starCount = (pattern.match(/\*/g) ?? []).length;
423
434
  if (starCount > 8) {
@@ -3690,7 +3701,7 @@ function getTrustLevel(score) {
3690
3701
  }
3691
3702
 
3692
3703
  // src/version.ts
3693
- var SDK_VERSION = "3.11.0";
3704
+ var SDK_VERSION = "3.12.0";
3694
3705
 
3695
3706
  // src/well-known.ts
3696
3707
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -3857,6 +3868,7 @@ function createGuidanceResponse(_config, reason, options = {}) {
3857
3868
  }
3858
3869
  ] : void 0,
3859
3870
  correlationId: options.correlationId,
3871
+ attemptId: options.attemptId,
3860
3872
  verifiedAt: /* @__PURE__ */ new Date()
3861
3873
  };
3862
3874
  }
@@ -3886,6 +3898,8 @@ async function callVerifyAccessAPI(config, request) {
3886
3898
  body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
3887
3899
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
3888
3900
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
3901
+ if (requestData.attemptId) body.attemptId = requestData.attemptId;
3902
+ if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
3889
3903
  body.sdkVersion = SDK_VERSION;
3890
3904
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
3891
3905
  const meta = {
@@ -3913,6 +3927,9 @@ async function callVerifyAccessAPI(config, request) {
3913
3927
  if (response.status === 410) {
3914
3928
  return {
3915
3929
  success: true,
3930
+ // 3.12.0 (Trust Record): keep the server-echoed attempt handle even on
3931
+ // the synthesised 410 deny shape (echoed on every branch).
3932
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
3916
3933
  access: {
3917
3934
  allowed: false,
3918
3935
  accessLevel: "none",
@@ -3931,7 +3948,8 @@ async function callVerifyAccessAPI(config, request) {
3931
3948
  return {
3932
3949
  success: false,
3933
3950
  error: data.message || data.error || `API returned ${response.status}`,
3934
- correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0
3951
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
3952
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
3935
3953
  };
3936
3954
  }
3937
3955
  return data;
@@ -3965,7 +3983,13 @@ async function verify(config, request, options) {
3965
3983
  if (mergedConfig.debug) {
3966
3984
  console.log("[VerificationGateway] Returning cached result");
3967
3985
  }
3968
- return cached;
3986
+ const hit = { ...cached };
3987
+ if (request.attemptId) {
3988
+ hit.attemptId = request.attemptId;
3989
+ } else {
3990
+ delete hit.attemptId;
3991
+ }
3992
+ return hit;
3969
3993
  }
3970
3994
  }
3971
3995
  const enrichedRequest = { ...request };
@@ -3985,6 +4009,7 @@ async function verify(config, request, options) {
3985
4009
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
3986
4010
  source: "api_error",
3987
4011
  correlationId: apiResponse.correlationId,
4012
+ attemptId: apiResponse.attemptId,
3988
4013
  urls
3989
4014
  });
3990
4015
  }
@@ -4020,6 +4045,9 @@ async function verify(config, request, options) {
4020
4045
  // Anonymous traffic has no session → correlationId is the per-attempt
4021
4046
  // linking key (the sessionId-equivalent for anonymous callers).
4022
4047
  correlationId: apiResponse.correlationId,
4048
+ // 3.12.0 (Trust Record): server echoes the attempt-chain handle on
4049
+ // every branch — denials included (the funnel outlives one deny).
4050
+ attemptId: apiResponse.attemptId,
4023
4051
  recommendation: apiResponse.recommendation,
4024
4052
  recommendationReasons: apiResponse.recommendationReasons,
4025
4053
  stepUpApproval: apiResponse.stepUpApproval,
@@ -4071,6 +4099,8 @@ async function verify(config, request, options) {
4071
4099
  // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId
4072
4100
  // (no session row exists for unverified callers).
4073
4101
  correlationId: apiResponse.correlationId,
4102
+ // 3.12.0 (Trust Record): server-echoed attempt-chain handle.
4103
+ attemptId: apiResponse.attemptId,
4074
4104
  runtimeChallenge: apiResponse.runtimeChallenge,
4075
4105
  tokenGuidance: apiResponse.tokenGuidance,
4076
4106
  recommendation: apiResponse.recommendation,
@@ -4454,13 +4484,29 @@ var TraceLogger = class {
4454
4484
  */
4455
4485
  logEvaluation(context, decision) {
4456
4486
  if (!this.enabled) return;
4457
- this.write({
4458
- id: this.generateId(),
4459
- timestamp: /* @__PURE__ */ new Date(),
4460
- type: "evaluation",
4461
- context: redactDeep(context),
4462
- decision: redactDeep(decision)
4463
- });
4487
+ let event;
4488
+ try {
4489
+ event = {
4490
+ id: this.generateId(),
4491
+ timestamp: /* @__PURE__ */ new Date(),
4492
+ type: "evaluation",
4493
+ context: redactDeep(context),
4494
+ decision: redactDeep(decision)
4495
+ };
4496
+ } catch (err) {
4497
+ event = {
4498
+ id: this.generateId(),
4499
+ timestamp: /* @__PURE__ */ new Date(),
4500
+ type: "error",
4501
+ metadata: {
4502
+ error: `trace redaction failed: ${err instanceof Error ? err.message : String(err)}`,
4503
+ recommendation: decision.recommendation,
4504
+ purpose: context.purpose,
4505
+ action: context.action
4506
+ }
4507
+ };
4508
+ }
4509
+ this.write(event);
4464
4510
  }
4465
4511
  /**
4466
4512
  * Log a tool call the guard saw but did not govern. One line per skip
@@ -4502,17 +4548,34 @@ var TraceLogger = class {
4502
4548
  });
4503
4549
  }
4504
4550
  write(event) {
4551
+ let line;
4552
+ try {
4553
+ line = JSON.stringify(event) + "\n";
4554
+ } catch {
4555
+ return;
4556
+ }
4505
4557
  try {
4506
4558
  const filePath = this.getCurrentFilePath();
4507
- const line = JSON.stringify(event) + "\n";
4508
4559
  if ((0, import_fs3.existsSync)(filePath)) {
4509
4560
  const stats = (0, import_fs3.statSync)(filePath);
4510
4561
  if (stats.size > MAX_FILE_SIZE) {
4511
4562
  this.rotate();
4512
4563
  }
4513
4564
  }
4514
- (0, import_fs3.appendFileSync)(filePath, line);
4565
+ for (let attempt = 0; attempt < 3; attempt++) {
4566
+ try {
4567
+ (0, import_fs3.appendFileSync)(filePath, line);
4568
+ return;
4569
+ } catch {
4570
+ }
4571
+ }
4572
+ const fallbackPath = filePath.replace(/\.jsonl$/, `-r${process.pid}.jsonl`);
4573
+ (0, import_fs3.appendFileSync)(fallbackPath, line);
4515
4574
  } catch {
4575
+ try {
4576
+ console.error(`[AstraSync TraceLogger] failed to persist trace line: ${line.trim()}`);
4577
+ } catch {
4578
+ }
4516
4579
  }
4517
4580
  }
4518
4581
  getCurrentFilePath() {
@@ -149,8 +149,9 @@ function extractBacktickSubstitutions(text, collect) {
149
149
  return kept.join(" ");
150
150
  }
151
151
  function basename(token) {
152
- const slash = token.lastIndexOf("/");
153
- return slash === -1 ? token : token.slice(slash + 1);
152
+ const slash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
153
+ const name = slash === -1 ? token : token.slice(slash + 1);
154
+ return name.replace(/\.(exe|cmd|bat|ps1)$/i, "");
154
155
  }
155
156
  function matchSegment(segment, list) {
156
157
  const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
@@ -283,7 +284,9 @@ var LocalEvaluator = class {
283
284
  }
284
285
  return {
285
286
  recommendation: "ALLOW",
286
- reason: "All PDLSS checks passed",
287
+ // "evaluated": dimensions absent from the policy are skipped, not
288
+ // failed — evaluatedDimensions below records which ones ran.
289
+ reason: "All evaluated PDLSS checks passed",
287
290
  evaluatedDimensions: {
288
291
  purpose: true,
289
292
  scope: !!this.policy.scope,
@@ -382,7 +385,7 @@ var LocalEvaluator = class {
382
385
  }
383
386
  }
384
387
  if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
385
- const targetLower = context.target.toLowerCase();
388
+ const targetLower = context.target.toLowerCase().replace(/\\/g, "/");
386
389
  for (const sensitivePath of SENSITIVE_PATHS) {
387
390
  if (targetLower.includes(sensitivePath.toLowerCase())) {
388
391
  score = Math.max(score, 50);
@@ -411,12 +414,20 @@ var LocalEvaluator = class {
411
414
  matchesAnyPattern(value, patterns) {
412
415
  return patterns.some((pattern) => this.matchGlob(value, pattern));
413
416
  }
414
- /**
415
- * Simple glob pattern matching.
416
- * Supports * (matches any characters including / and spaces) and ? (single char).
417
- * Uses the same approach as the backend's matchGlobPattern.
418
- */
417
+ homePrefix() {
418
+ if (this.cachedHomePrefix !== void 0) return this.cachedHomePrefix;
419
+ const env = typeof process !== "undefined" ? process.env : void 0;
420
+ const home = env?.HOME || env?.USERPROFILE;
421
+ this.cachedHomePrefix = home ? home.replace(/\\/g, "/") : null;
422
+ return this.cachedHomePrefix;
423
+ }
419
424
  matchGlob(value, pattern) {
425
+ value = value.replace(/\\/g, "/");
426
+ pattern = pattern.replace(/\\/g, "/");
427
+ if (pattern === "~" || pattern.startsWith("~/")) {
428
+ const home = this.homePrefix();
429
+ if (home) pattern = home + pattern.slice(1);
430
+ }
420
431
  if (pattern === value) return true;
421
432
  const starCount = (pattern.match(/\*/g) ?? []).length;
422
433
  if (starCount > 8) {
@@ -3689,7 +3700,7 @@ function getTrustLevel(score) {
3689
3700
  }
3690
3701
 
3691
3702
  // src/version.ts
3692
- var SDK_VERSION = "3.11.0";
3703
+ var SDK_VERSION = "3.12.0";
3693
3704
 
3694
3705
  // src/well-known.ts
3695
3706
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -3856,6 +3867,7 @@ function createGuidanceResponse(_config, reason, options = {}) {
3856
3867
  }
3857
3868
  ] : void 0,
3858
3869
  correlationId: options.correlationId,
3870
+ attemptId: options.attemptId,
3859
3871
  verifiedAt: /* @__PURE__ */ new Date()
3860
3872
  };
3861
3873
  }
@@ -3885,6 +3897,8 @@ async function callVerifyAccessAPI(config, request) {
3885
3897
  body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
3886
3898
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
3887
3899
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
3900
+ if (requestData.attemptId) body.attemptId = requestData.attemptId;
3901
+ if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
3888
3902
  body.sdkVersion = SDK_VERSION;
3889
3903
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
3890
3904
  const meta = {
@@ -3912,6 +3926,9 @@ async function callVerifyAccessAPI(config, request) {
3912
3926
  if (response.status === 410) {
3913
3927
  return {
3914
3928
  success: true,
3929
+ // 3.12.0 (Trust Record): keep the server-echoed attempt handle even on
3930
+ // the synthesised 410 deny shape (echoed on every branch).
3931
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
3915
3932
  access: {
3916
3933
  allowed: false,
3917
3934
  accessLevel: "none",
@@ -3930,7 +3947,8 @@ async function callVerifyAccessAPI(config, request) {
3930
3947
  return {
3931
3948
  success: false,
3932
3949
  error: data.message || data.error || `API returned ${response.status}`,
3933
- correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0
3950
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
3951
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
3934
3952
  };
3935
3953
  }
3936
3954
  return data;
@@ -3964,7 +3982,13 @@ async function verify(config, request, options) {
3964
3982
  if (mergedConfig.debug) {
3965
3983
  console.log("[VerificationGateway] Returning cached result");
3966
3984
  }
3967
- return cached;
3985
+ const hit = { ...cached };
3986
+ if (request.attemptId) {
3987
+ hit.attemptId = request.attemptId;
3988
+ } else {
3989
+ delete hit.attemptId;
3990
+ }
3991
+ return hit;
3968
3992
  }
3969
3993
  }
3970
3994
  const enrichedRequest = { ...request };
@@ -3984,6 +4008,7 @@ async function verify(config, request, options) {
3984
4008
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
3985
4009
  source: "api_error",
3986
4010
  correlationId: apiResponse.correlationId,
4011
+ attemptId: apiResponse.attemptId,
3987
4012
  urls
3988
4013
  });
3989
4014
  }
@@ -4019,6 +4044,9 @@ async function verify(config, request, options) {
4019
4044
  // Anonymous traffic has no session → correlationId is the per-attempt
4020
4045
  // linking key (the sessionId-equivalent for anonymous callers).
4021
4046
  correlationId: apiResponse.correlationId,
4047
+ // 3.12.0 (Trust Record): server echoes the attempt-chain handle on
4048
+ // every branch — denials included (the funnel outlives one deny).
4049
+ attemptId: apiResponse.attemptId,
4022
4050
  recommendation: apiResponse.recommendation,
4023
4051
  recommendationReasons: apiResponse.recommendationReasons,
4024
4052
  stepUpApproval: apiResponse.stepUpApproval,
@@ -4070,6 +4098,8 @@ async function verify(config, request, options) {
4070
4098
  // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId
4071
4099
  // (no session row exists for unverified callers).
4072
4100
  correlationId: apiResponse.correlationId,
4101
+ // 3.12.0 (Trust Record): server-echoed attempt-chain handle.
4102
+ attemptId: apiResponse.attemptId,
4073
4103
  runtimeChallenge: apiResponse.runtimeChallenge,
4074
4104
  tokenGuidance: apiResponse.tokenGuidance,
4075
4105
  recommendation: apiResponse.recommendation,
@@ -4453,13 +4483,29 @@ var TraceLogger = class {
4453
4483
  */
4454
4484
  logEvaluation(context, decision) {
4455
4485
  if (!this.enabled) return;
4456
- this.write({
4457
- id: this.generateId(),
4458
- timestamp: /* @__PURE__ */ new Date(),
4459
- type: "evaluation",
4460
- context: redactDeep(context),
4461
- decision: redactDeep(decision)
4462
- });
4486
+ let event;
4487
+ try {
4488
+ event = {
4489
+ id: this.generateId(),
4490
+ timestamp: /* @__PURE__ */ new Date(),
4491
+ type: "evaluation",
4492
+ context: redactDeep(context),
4493
+ decision: redactDeep(decision)
4494
+ };
4495
+ } catch (err) {
4496
+ event = {
4497
+ id: this.generateId(),
4498
+ timestamp: /* @__PURE__ */ new Date(),
4499
+ type: "error",
4500
+ metadata: {
4501
+ error: `trace redaction failed: ${err instanceof Error ? err.message : String(err)}`,
4502
+ recommendation: decision.recommendation,
4503
+ purpose: context.purpose,
4504
+ action: context.action
4505
+ }
4506
+ };
4507
+ }
4508
+ this.write(event);
4463
4509
  }
4464
4510
  /**
4465
4511
  * Log a tool call the guard saw but did not govern. One line per skip
@@ -4501,17 +4547,34 @@ var TraceLogger = class {
4501
4547
  });
4502
4548
  }
4503
4549
  write(event) {
4550
+ let line;
4551
+ try {
4552
+ line = JSON.stringify(event) + "\n";
4553
+ } catch {
4554
+ return;
4555
+ }
4504
4556
  try {
4505
4557
  const filePath = this.getCurrentFilePath();
4506
- const line = JSON.stringify(event) + "\n";
4507
4558
  if ((0, import_fs3.existsSync)(filePath)) {
4508
4559
  const stats = (0, import_fs3.statSync)(filePath);
4509
4560
  if (stats.size > MAX_FILE_SIZE) {
4510
4561
  this.rotate();
4511
4562
  }
4512
4563
  }
4513
- (0, import_fs3.appendFileSync)(filePath, line);
4564
+ for (let attempt = 0; attempt < 3; attempt++) {
4565
+ try {
4566
+ (0, import_fs3.appendFileSync)(filePath, line);
4567
+ return;
4568
+ } catch {
4569
+ }
4570
+ }
4571
+ const fallbackPath = filePath.replace(/\.jsonl$/, `-r${process.pid}.jsonl`);
4572
+ (0, import_fs3.appendFileSync)(fallbackPath, line);
4514
4573
  } catch {
4574
+ try {
4575
+ console.error(`[AstraSync TraceLogger] failed to persist trace line: ${line.trim()}`);
4576
+ } catch {
4577
+ }
4515
4578
  }
4516
4579
  }
4517
4580
  getCurrentFilePath() {
@@ -4288,8 +4288,9 @@ function extractBacktickSubstitutions(text, collect) {
4288
4288
  return kept.join(" ");
4289
4289
  }
4290
4290
  function basename(token) {
4291
- const slash = token.lastIndexOf("/");
4292
- return slash === -1 ? token : token.slice(slash + 1);
4291
+ const slash = Math.max(token.lastIndexOf("/"), token.lastIndexOf("\\"));
4292
+ const name = slash === -1 ? token : token.slice(slash + 1);
4293
+ return name.replace(/\.(exe|cmd|bat|ps1)$/i, "");
4293
4294
  }
4294
4295
  function matchSegment(segment, list) {
4295
4296
  const tokens = segment.split(/\s+/).filter((t) => /[a-z0-9]/.test(t));
@@ -4422,7 +4423,9 @@ var LocalEvaluator = class {
4422
4423
  }
4423
4424
  return {
4424
4425
  recommendation: "ALLOW",
4425
- reason: "All PDLSS checks passed",
4426
+ // "evaluated": dimensions absent from the policy are skipped, not
4427
+ // failed — evaluatedDimensions below records which ones ran.
4428
+ reason: "All evaluated PDLSS checks passed",
4426
4429
  evaluatedDimensions: {
4427
4430
  purpose: true,
4428
4431
  scope: !!this.policy.scope,
@@ -4521,7 +4524,7 @@ var LocalEvaluator = class {
4521
4524
  }
4522
4525
  }
4523
4526
  if ((context.purpose === "file.read" || context.purpose === "file.write" || context.purpose === "file.delete") && context.target) {
4524
- const targetLower = context.target.toLowerCase();
4527
+ const targetLower = context.target.toLowerCase().replace(/\\/g, "/");
4525
4528
  for (const sensitivePath of SENSITIVE_PATHS) {
4526
4529
  if (targetLower.includes(sensitivePath.toLowerCase())) {
4527
4530
  score = Math.max(score, 50);
@@ -4550,12 +4553,20 @@ var LocalEvaluator = class {
4550
4553
  matchesAnyPattern(value, patterns) {
4551
4554
  return patterns.some((pattern) => this.matchGlob(value, pattern));
4552
4555
  }
4553
- /**
4554
- * Simple glob pattern matching.
4555
- * Supports * (matches any characters including / and spaces) and ? (single char).
4556
- * Uses the same approach as the backend's matchGlobPattern.
4557
- */
4556
+ homePrefix() {
4557
+ if (this.cachedHomePrefix !== void 0) return this.cachedHomePrefix;
4558
+ const env = typeof process !== "undefined" ? process.env : void 0;
4559
+ const home = env?.HOME || env?.USERPROFILE;
4560
+ this.cachedHomePrefix = home ? home.replace(/\\/g, "/") : null;
4561
+ return this.cachedHomePrefix;
4562
+ }
4558
4563
  matchGlob(value, pattern) {
4564
+ value = value.replace(/\\/g, "/");
4565
+ pattern = pattern.replace(/\\/g, "/");
4566
+ if (pattern === "~" || pattern.startsWith("~/")) {
4567
+ const home = this.homePrefix();
4568
+ if (home) pattern = home + pattern.slice(1);
4569
+ }
4559
4570
  if (pattern === value) return true;
4560
4571
  const starCount = (pattern.match(/\*/g) ?? []).length;
4561
4572
  if (starCount > 8) {
@@ -4598,7 +4609,7 @@ function getTrustLevel(score) {
4598
4609
  }
4599
4610
 
4600
4611
  // src/version.ts
4601
- var SDK_VERSION = "3.11.0";
4612
+ var SDK_VERSION = "3.12.0";
4602
4613
 
4603
4614
  // src/well-known.ts
4604
4615
  var CACHE_TTL_MS = 60 * 60 * 1e3;
@@ -4765,6 +4776,7 @@ function createGuidanceResponse(_config, reason, options = {}) {
4765
4776
  }
4766
4777
  ] : void 0,
4767
4778
  correlationId: options.correlationId,
4779
+ attemptId: options.attemptId,
4768
4780
  verifiedAt: /* @__PURE__ */ new Date()
4769
4781
  };
4770
4782
  }
@@ -4794,6 +4806,8 @@ async function callVerifyAccessAPI(config, request2) {
4794
4806
  body.runtimeChallengeOptions = requestData.runtimeChallengeOptions;
4795
4807
  if (requestData.invocationProtocol) body.invocationProtocol = requestData.invocationProtocol;
4796
4808
  if (requestData.commerceArtifacts) body.commerceArtifacts = requestData.commerceArtifacts;
4809
+ if (requestData.attemptId) body.attemptId = requestData.attemptId;
4810
+ if (requestData.considerationSet) body.considerationSet = requestData.considerationSet;
4797
4811
  body.sdkVersion = SDK_VERSION;
4798
4812
  if (requestData.callerMetadata || requestData.clientIp || requestData.userAgent) {
4799
4813
  const meta = {
@@ -4821,6 +4835,9 @@ async function callVerifyAccessAPI(config, request2) {
4821
4835
  if (response.status === 410) {
4822
4836
  return {
4823
4837
  success: true,
4838
+ // 3.12.0 (Trust Record): keep the server-echoed attempt handle even on
4839
+ // the synthesised 410 deny shape (echoed on every branch).
4840
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0,
4824
4841
  access: {
4825
4842
  allowed: false,
4826
4843
  accessLevel: "none",
@@ -4839,7 +4856,8 @@ async function callVerifyAccessAPI(config, request2) {
4839
4856
  return {
4840
4857
  success: false,
4841
4858
  error: data.message || data.error || `API returned ${response.status}`,
4842
- correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0
4859
+ correlationId: typeof data?.correlationId === "string" ? data.correlationId : void 0,
4860
+ attemptId: typeof data?.attemptId === "string" ? data.attemptId : void 0
4843
4861
  };
4844
4862
  }
4845
4863
  return data;
@@ -4873,7 +4891,13 @@ async function verify(config, request2, options) {
4873
4891
  if (mergedConfig.debug) {
4874
4892
  console.log("[VerificationGateway] Returning cached result");
4875
4893
  }
4876
- return cached;
4894
+ const hit = { ...cached };
4895
+ if (request2.attemptId) {
4896
+ hit.attemptId = request2.attemptId;
4897
+ } else {
4898
+ delete hit.attemptId;
4899
+ }
4900
+ return hit;
4877
4901
  }
4878
4902
  }
4879
4903
  const enrichedRequest = { ...request2 };
@@ -4893,6 +4917,7 @@ async function verify(config, request2, options) {
4893
4917
  return createGuidanceResponse(mergedConfig, apiResponse.error, {
4894
4918
  source: "api_error",
4895
4919
  correlationId: apiResponse.correlationId,
4920
+ attemptId: apiResponse.attemptId,
4896
4921
  urls
4897
4922
  });
4898
4923
  }
@@ -4928,6 +4953,9 @@ async function verify(config, request2, options) {
4928
4953
  // Anonymous traffic has no session → correlationId is the per-attempt
4929
4954
  // linking key (the sessionId-equivalent for anonymous callers).
4930
4955
  correlationId: apiResponse.correlationId,
4956
+ // 3.12.0 (Trust Record): server echoes the attempt-chain handle on
4957
+ // every branch — denials included (the funnel outlives one deny).
4958
+ attemptId: apiResponse.attemptId,
4931
4959
  recommendation: apiResponse.recommendation,
4932
4960
  recommendationReasons: apiResponse.recommendationReasons,
4933
4961
  stepUpApproval: apiResponse.stepUpApproval,
@@ -4979,6 +5007,8 @@ async function verify(config, request2, options) {
4979
5007
  // v2.3.10 (defect #34, round-4): anonymous responses surface correlationId
4980
5008
  // (no session row exists for unverified callers).
4981
5009
  correlationId: apiResponse.correlationId,
5010
+ // 3.12.0 (Trust Record): server-echoed attempt-chain handle.
5011
+ attemptId: apiResponse.attemptId,
4982
5012
  runtimeChallenge: apiResponse.runtimeChallenge,
4983
5013
  tokenGuidance: apiResponse.tokenGuidance,
4984
5014
  recommendation: apiResponse.recommendation,
@@ -5362,13 +5392,29 @@ var TraceLogger = class {
5362
5392
  */
5363
5393
  logEvaluation(context, decision) {
5364
5394
  if (!this.enabled) return;
5365
- this.write({
5366
- id: this.generateId(),
5367
- timestamp: /* @__PURE__ */ new Date(),
5368
- type: "evaluation",
5369
- context: redactDeep(context),
5370
- decision: redactDeep(decision)
5371
- });
5395
+ let event;
5396
+ try {
5397
+ event = {
5398
+ id: this.generateId(),
5399
+ timestamp: /* @__PURE__ */ new Date(),
5400
+ type: "evaluation",
5401
+ context: redactDeep(context),
5402
+ decision: redactDeep(decision)
5403
+ };
5404
+ } catch (err) {
5405
+ event = {
5406
+ id: this.generateId(),
5407
+ timestamp: /* @__PURE__ */ new Date(),
5408
+ type: "error",
5409
+ metadata: {
5410
+ error: `trace redaction failed: ${err instanceof Error ? err.message : String(err)}`,
5411
+ recommendation: decision.recommendation,
5412
+ purpose: context.purpose,
5413
+ action: context.action
5414
+ }
5415
+ };
5416
+ }
5417
+ this.write(event);
5372
5418
  }
5373
5419
  /**
5374
5420
  * Log a tool call the guard saw but did not govern. One line per skip
@@ -5410,17 +5456,34 @@ var TraceLogger = class {
5410
5456
  });
5411
5457
  }
5412
5458
  write(event) {
5459
+ let line;
5460
+ try {
5461
+ line = JSON.stringify(event) + "\n";
5462
+ } catch {
5463
+ return;
5464
+ }
5413
5465
  try {
5414
5466
  const filePath = this.getCurrentFilePath();
5415
- const line = JSON.stringify(event) + "\n";
5416
5467
  if ((0, import_fs3.existsSync)(filePath)) {
5417
5468
  const stats = (0, import_fs3.statSync)(filePath);
5418
5469
  if (stats.size > MAX_FILE_SIZE) {
5419
5470
  this.rotate();
5420
5471
  }
5421
5472
  }
5422
- (0, import_fs3.appendFileSync)(filePath, line);
5473
+ for (let attempt = 0; attempt < 3; attempt++) {
5474
+ try {
5475
+ (0, import_fs3.appendFileSync)(filePath, line);
5476
+ return;
5477
+ } catch {
5478
+ }
5479
+ }
5480
+ const fallbackPath = filePath.replace(/\.jsonl$/, `-r${process.pid}.jsonl`);
5481
+ (0, import_fs3.appendFileSync)(fallbackPath, line);
5423
5482
  } catch {
5483
+ try {
5484
+ console.error(`[AstraSync TraceLogger] failed to persist trace line: ${line.trim()}`);
5485
+ } catch {
5486
+ }
5424
5487
  }
5425
5488
  }
5426
5489
  getCurrentFilePath() {