@astrasyncai/verification-gateway 3.12.0 → 4.1.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 (134) hide show
  1. package/README.md +106 -29
  2. package/dist/adapter-interface/interface.d.mts +2 -2
  3. package/dist/adapter-interface/interface.d.ts +2 -2
  4. package/dist/adapters/express.d.mts +2 -2
  5. package/dist/adapters/express.d.ts +2 -2
  6. package/dist/adapters/express.js +482 -22
  7. package/dist/adapters/express.js.map +1 -1
  8. package/dist/adapters/express.mjs +482 -22
  9. package/dist/adapters/express.mjs.map +1 -1
  10. package/dist/adapters/http-pdlss.d.mts +3 -3
  11. package/dist/adapters/http-pdlss.d.ts +3 -3
  12. package/dist/adapters/http-pdlss.js.map +1 -1
  13. package/dist/adapters/http-pdlss.mjs.map +1 -1
  14. package/dist/adapters/mcp.d.mts +56 -36
  15. package/dist/adapters/mcp.d.ts +56 -36
  16. package/dist/adapters/mcp.js +309 -10
  17. package/dist/adapters/mcp.js.map +1 -1
  18. package/dist/adapters/mcp.mjs +309 -10
  19. package/dist/adapters/mcp.mjs.map +1 -1
  20. package/dist/adapters/nextjs.d.mts +2 -2
  21. package/dist/adapters/nextjs.d.ts +2 -2
  22. package/dist/adapters/nextjs.js +518 -25
  23. package/dist/adapters/nextjs.js.map +1 -1
  24. package/dist/adapters/nextjs.mjs +518 -25
  25. package/dist/adapters/nextjs.mjs.map +1 -1
  26. package/dist/adapters/sdk.d.mts +2 -2
  27. package/dist/adapters/sdk.d.ts +2 -2
  28. package/dist/adapters/sdk.js +72 -9
  29. package/dist/adapters/sdk.js.map +1 -1
  30. package/dist/adapters/sdk.mjs +72 -9
  31. package/dist/adapters/sdk.mjs.map +1 -1
  32. package/dist/agent/index.d.mts +2 -2
  33. package/dist/agent/index.d.ts +2 -2
  34. package/dist/agent/index.js.map +1 -1
  35. package/dist/agent/index.mjs.map +1 -1
  36. package/dist/bin/astrasync-claude-hook.js +83 -20
  37. package/dist/bin/astrasync-codex-hook.js +83 -20
  38. package/dist/bin/astrasync-guard.js +83 -20
  39. package/dist/bin/astrasync.js +150 -33
  40. package/dist/browser/background.js +83 -20
  41. package/dist/browser/background.js.map +1 -1
  42. package/dist/browser/background.mjs +83 -20
  43. package/dist/browser/background.mjs.map +1 -1
  44. package/dist/browser/browser-adapter.d.mts +2 -2
  45. package/dist/browser/browser-adapter.d.ts +2 -2
  46. package/dist/claude-code/claude-code-adapter.d.mts +2 -2
  47. package/dist/claude-code/claude-code-adapter.d.ts +2 -2
  48. package/dist/cli/index.d.mts +2 -2
  49. package/dist/cli/index.d.ts +2 -2
  50. package/dist/codex/index.d.mts +3 -3
  51. package/dist/codex/index.d.ts +3 -3
  52. package/dist/codex/index.js +83 -20
  53. package/dist/codex/index.js.map +1 -1
  54. package/dist/codex/index.mjs +83 -20
  55. package/dist/codex/index.mjs.map +1 -1
  56. package/dist/cursor/cursor-adapter.d.mts +2 -2
  57. package/dist/cursor/cursor-adapter.d.ts +2 -2
  58. package/dist/cursor/extension.d.mts +2 -2
  59. package/dist/cursor/extension.d.ts +2 -2
  60. package/dist/cursor/extension.js +83 -20
  61. package/dist/cursor/extension.js.map +1 -1
  62. package/dist/cursor/extension.mjs +83 -20
  63. package/dist/cursor/extension.mjs.map +1 -1
  64. package/dist/edge-config.d.mts +22 -7
  65. package/dist/edge-config.d.ts +22 -7
  66. package/dist/edge-config.js.map +1 -1
  67. package/dist/edge-config.mjs.map +1 -1
  68. package/dist/edge-core/index.d.mts +425 -0
  69. package/dist/edge-core/index.d.ts +425 -0
  70. package/dist/edge-core/index.js +1482 -0
  71. package/dist/edge-core/index.js.map +1 -0
  72. package/dist/edge-core/index.mjs +1437 -0
  73. package/dist/edge-core/index.mjs.map +1 -0
  74. package/dist/{express-B39o89gf.d.mts → express-BVd1_3FE.d.ts} +9 -7
  75. package/dist/{express-BuxKNO_q.d.ts → express-D_4hTn5Z.d.mts} +9 -7
  76. package/dist/gateway/gateway.d.mts +2 -2
  77. package/dist/gateway/gateway.d.ts +2 -2
  78. package/dist/gateway/gateway.js +83 -20
  79. package/dist/gateway/gateway.js.map +1 -1
  80. package/dist/gateway/gateway.mjs +83 -20
  81. package/dist/gateway/gateway.mjs.map +1 -1
  82. package/dist/git-trigger/git-hooks.d.mts +2 -2
  83. package/dist/git-trigger/git-hooks.d.ts +2 -2
  84. package/dist/{index-CLHdqs2M.d.ts → index-B0YHu_SP.d.ts} +30 -27
  85. package/dist/{index-DME3w3lf.d.mts → index-BPEBlOsE.d.mts} +30 -27
  86. package/dist/{index-CqydB4ks.d.mts → index-DQb5-_1X.d.mts} +1 -1
  87. package/dist/{index-BG62SXso.d.ts → index-T1aBoUcc.d.ts} +1 -1
  88. package/dist/index.d.mts +11 -11
  89. package/dist/index.d.ts +11 -11
  90. package/dist/index.js +595 -272
  91. package/dist/index.js.map +1 -1
  92. package/dist/index.mjs +592 -272
  93. package/dist/index.mjs.map +1 -1
  94. package/dist/local-evaluator/evaluator.d.mts +2 -2
  95. package/dist/local-evaluator/evaluator.d.ts +2 -2
  96. package/dist/metadata-capture.d.mts +63 -4
  97. package/dist/metadata-capture.d.ts +63 -4
  98. package/dist/metadata-capture.js +67 -0
  99. package/dist/metadata-capture.js.map +1 -1
  100. package/dist/metadata-capture.mjs +64 -0
  101. package/dist/metadata-capture.mjs.map +1 -1
  102. package/dist/{nextjs-D74W7wt-.d.mts → nextjs-WyeVr2Kp.d.mts} +3 -3
  103. package/dist/{nextjs-Ddlw_J1J.d.ts → nextjs-w7RTbP1P.d.ts} +3 -3
  104. package/dist/platform-signatures.d.mts +5 -6
  105. package/dist/platform-signatures.d.ts +5 -6
  106. package/dist/platform-signatures.js.map +1 -1
  107. package/dist/platform-signatures.mjs.map +1 -1
  108. package/dist/registration/index.d.mts +10 -11
  109. package/dist/registration/index.d.ts +10 -11
  110. package/dist/registration/index.js.map +1 -1
  111. package/dist/registration/index.mjs.map +1 -1
  112. package/dist/{sdk-CT7gdkfR.d.ts → sdk-CbTNIkAa.d.mts} +4 -4
  113. package/dist/{sdk-Bn6bj7kt.d.mts → sdk-QeX0Z8Ho.d.ts} +4 -4
  114. package/dist/transport/index.d.mts +2 -2
  115. package/dist/transport/index.d.ts +2 -2
  116. package/dist/transport/index.js.map +1 -1
  117. package/dist/transport/index.mjs.map +1 -1
  118. package/dist/{types-DHu7m9HI.d.ts → types-BLUx92FJ.d.ts} +1 -1
  119. package/dist/{types-Djw4GLtz.d.mts → types-DqfPU5Bl.d.mts} +1 -1
  120. package/dist/{types-BntuzEEn.d.mts → types-r850cOt0.d.mts} +121 -88
  121. package/dist/{types-SDu4Llbe.d.ts → types-sMxBT-nM.d.ts} +121 -88
  122. package/dist/ui/index.d.mts +11 -7
  123. package/dist/ui/index.d.ts +11 -7
  124. package/dist/ui/index.js +13 -9
  125. package/dist/ui/index.js.map +1 -1
  126. package/dist/ui/index.mjs +10 -8
  127. package/dist/ui/index.mjs.map +1 -1
  128. package/dist/verify.d.mts +11 -8
  129. package/dist/verify.d.ts +11 -8
  130. package/dist/verify.js +76 -8
  131. package/dist/verify.js.map +1 -1
  132. package/dist/verify.mjs +75 -8
  133. package/dist/verify.mjs.map +1 -1
  134. package/package.json +8 -2
@@ -10,8 +10,7 @@
10
10
  * the three-tier visibility numbers drift between capture points.
11
11
  *
12
12
  * Pure regex — no Node built-ins, edge-safe. Detection is regex-only for
13
- * v2.9 — TLS fingerprinting / behavioural signatures are deferred
14
- * (recorded in adapter requirements doc).
13
+ * v2.9 — TLS fingerprinting / behavioural signatures are deferred.
15
14
  */
16
15
  type PlatformAgentVendor = 'claude' | 'chatgpt' | 'gemini' | 'cursor' | 'goose' | 'perplexity' | 'unknown';
17
16
  interface PlatformFingerprint {
@@ -25,13 +24,13 @@ interface PlatformDetectionInput {
25
24
  userAgent?: string;
26
25
  agentCardUrl?: string;
27
26
  /**
28
- * Workstream D — a safe API-key format prefix observed on the caller's
27
+ * A safe API-key format prefix observed on the caller's
29
28
  * credential header (e.g. `sk-ant-api03`, `sk-proj`, `aiza`). A strong
30
29
  * platform signal; never the secret. Matched against `apiKeyFormatPatterns`.
31
30
  */
32
31
  apiKeyFormat?: string;
33
32
  /**
34
- * Workstream D — the sanitised platform-signal headers (lowercased names →
33
+ * The sanitised platform-signal headers (lowercased names →
35
34
  * values), e.g. `openai-organization`, `x-goog-user-project`. Matched
36
35
  * against `headerPatterns` (which test on `name` and `name: value`).
37
36
  */
@@ -49,13 +48,13 @@ interface PlatformSignatureDef<V extends string = string> {
49
48
  uaPatterns?: RegExp[];
50
49
  agentCardPatterns?: RegExp[];
51
50
  /**
52
- * Workstream D — patterns tested against an observed API-key format prefix
51
+ * Patterns tested against an observed API-key format prefix
53
52
  * (e.g. `/^sk-ant/i` → claude). A credential-header format is a stronger
54
53
  * platform signal than a spoofable User-Agent.
55
54
  */
56
55
  apiKeyFormatPatterns?: RegExp[];
57
56
  /**
58
- * Workstream D — patterns tested against each observed platform-signal
57
+ * Patterns tested against each observed platform-signal
59
58
  * header, as both `name` and `name: value` (so `/openai-organization/i` and
60
59
  * `/x-goog-user-project/i` match on presence).
61
60
  */
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/platform-signatures.ts"],"sourcesContent":["/**\n * Platform-agent fingerprint signatures — the shared registry of known\n * platform-agent UA / agent-card patterns (Claude / ChatGPT / Gemini /\n * Cursor / Goose).\n *\n * Single source of truth, lifted verbatim from the backend's\n * `platform-agent-detector.service` (which now imports detection from\n * here): the backend's verify-access anonymous handler and the edge\n * adapter (`@astrasyncai/adapter-lambda`) must classify identically or\n * the three-tier visibility numbers drift between capture points.\n *\n * Pure regex — no Node built-ins, edge-safe. Detection is regex-only for\n * v2.9 — TLS fingerprinting / behavioural signatures are deferred\n * (recorded in adapter requirements doc).\n */\n\nexport type PlatformAgentVendor =\n | 'claude'\n | 'chatgpt'\n | 'gemini'\n | 'cursor'\n | 'goose'\n | 'perplexity'\n | 'unknown';\n\nexport interface PlatformFingerprint {\n vendor: PlatformAgentVendor;\n /** A stable identifier used as the orphan-agent dedup key. */\n fingerprintKey: string;\n /** Human-readable name surfaced in the admin panel. */\n displayName: string;\n}\n\nexport interface PlatformDetectionInput {\n userAgent?: string;\n agentCardUrl?: string;\n /**\n * Workstream D — a safe API-key format prefix observed on the caller's\n * credential header (e.g. `sk-ant-api03`, `sk-proj`, `aiza`). A strong\n * platform signal; never the secret. Matched against `apiKeyFormatPatterns`.\n */\n apiKeyFormat?: string;\n /**\n * Workstream D — the sanitised platform-signal headers (lowercased names →\n * values), e.g. `openai-organization`, `x-goog-user-project`. Matched\n * against `headerPatterns` (which test on `name` and `name: value`).\n */\n platformHeaders?: Record<string, string>;\n}\n\n/**\n * A signature definition, generic over the vendor key. The seed constant\n * below uses the closed `PlatformAgentVendor` union; DB-promoted signatures\n * (backend dynamic registry, console round) carry arbitrary string vendors —\n * `matchPlatformSignature` accepts both.\n */\nexport interface PlatformSignatureDef<V extends string = string> {\n vendor: V;\n displayName: string;\n uaPatterns?: RegExp[];\n agentCardPatterns?: RegExp[];\n /**\n * Workstream D — patterns tested against an observed API-key format prefix\n * (e.g. `/^sk-ant/i` → claude). A credential-header format is a stronger\n * platform signal than a spoofable User-Agent.\n */\n apiKeyFormatPatterns?: RegExp[];\n /**\n * Workstream D — patterns tested against each observed platform-signal\n * header, as both `name` and `name: value` (so `/openai-organization/i` and\n * `/x-goog-user-project/i` match on presence).\n */\n headerPatterns?: RegExp[];\n}\n\n/**\n * A fingerprint whose vendor may be a dynamically-promoted (non-seed) key.\n * Structurally a superset of `PlatformFingerprint`.\n */\nexport interface DynamicPlatformFingerprint {\n vendor: string;\n fingerprintKey: string;\n displayName: string;\n}\n\n/**\n * Registry of known platform-agent signatures. Order matters — first match wins.\n *\n * Patterns are intentionally permissive (case-insensitive substring match) since\n * platform UA strings drift between versions. False positives are tolerable —\n * the orphan record is informational, not enforcement.\n */\nexport const PLATFORM_AGENT_SIGNATURES: ReadonlyArray<PlatformSignatureDef<PlatformAgentVendor>> = [\n {\n vendor: 'claude',\n displayName: 'Anthropic Claude',\n uaPatterns: [/claude/i, /anthropic/i],\n agentCardPatterns: [/anthropic\\.com/i, /claude\\.ai/i],\n apiKeyFormatPatterns: [/^sk-ant/i],\n headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i],\n },\n {\n vendor: 'chatgpt',\n displayName: 'OpenAI ChatGPT',\n uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\\d/i],\n agentCardPatterns: [/openai\\.com/i, /chatgpt\\.com/i],\n apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],\n headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i],\n },\n {\n vendor: 'gemini',\n displayName: 'Google Gemini',\n uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],\n agentCardPatterns: [/gemini\\.google\\.com/i, /ai\\.google\\.dev/i],\n apiKeyFormatPatterns: [/^aiza/i],\n headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i],\n },\n {\n vendor: 'cursor',\n displayName: 'Cursor',\n uaPatterns: [/cursor/i],\n agentCardPatterns: [/cursor\\.sh/i, /cursor\\.com/i],\n },\n {\n vendor: 'goose',\n displayName: 'Block Goose',\n uaPatterns: [/goose/i, /block-goose/i],\n agentCardPatterns: [/block\\.xyz\\/goose/i, /goose\\.tools/i],\n },\n {\n vendor: 'perplexity',\n displayName: 'Perplexity',\n uaPatterns: [/perplexity/i],\n agentCardPatterns: [/perplexity\\.ai/i],\n apiKeyFormatPatterns: [/^pplx-/i],\n },\n];\n\n/**\n * Generic first-match-wins matcher over an arbitrary signature set. The\n * backend's dynamic registry merges the seed constant with DB-promoted\n * signatures and calls this — ONE matching implementation everywhere\n * (fingerprint-key rule included: vendor + lowercased UA basename before\n * the first space/slash, falling back to the agent-card host).\n */\nexport function matchPlatformSignature(\n signatures: ReadonlyArray<PlatformSignatureDef>,\n input: PlatformDetectionInput\n): DynamicPlatformFingerprint | undefined {\n const ua = input.userAgent ?? '';\n const cardUrl = input.agentCardUrl ?? '';\n const apiKeyFormat = input.apiKeyFormat ?? '';\n // Flatten platform headers to `name` and `name: value` lines for matching.\n const headerLines: string[] = [];\n if (input.platformHeaders) {\n for (const [name, value] of Object.entries(input.platformHeaders)) {\n headerLines.push(name, `${name}: ${value}`);\n }\n }\n\n for (const sig of signatures) {\n const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));\n const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));\n const keyMatch =\n apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));\n const headerMatch =\n headerLines.length > 0 &&\n sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));\n if (uaMatch || cardMatch || keyMatch || headerMatch) {\n const uaBase = ua.split(/[\\s/]/)[0]?.toLowerCase() ?? '';\n const cardHost = (() => {\n try {\n return cardUrl ? new URL(cardUrl).host : '';\n } catch {\n return '';\n }\n })();\n // Prefer a UA/card basename for the dedup key; fall back to the credential\n // format (still a stable per-vendor key) when only a key/header matched.\n const keyBasis = uaBase || cardHost || apiKeyFormat || 'unknown';\n return {\n vendor: sig.vendor,\n fingerprintKey: `${sig.vendor}:${keyBasis}`,\n displayName: sig.displayName,\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Inspect caller metadata for a known SEED platform-agent fingerprint.\n * Returns undefined if no signature matches. (Backends with the dynamic\n * registry should call `matchPlatformSignature` over their merged set\n * instead — same semantics, live signature additions included.)\n */\nexport function detectPlatformFingerprint(\n input: PlatformDetectionInput\n): PlatformFingerprint | undefined {\n // Seed vendors are all members of the PlatformAgentVendor union, so the\n // narrowing cast is sound by construction.\n return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input) as\n | PlatformFingerprint\n | undefined;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/platform-signatures.ts"],"sourcesContent":["/**\n * Platform-agent fingerprint signatures — the shared registry of known\n * platform-agent UA / agent-card patterns (Claude / ChatGPT / Gemini /\n * Cursor / Goose).\n *\n * Single source of truth, lifted verbatim from the backend's\n * `platform-agent-detector.service` (which now imports detection from\n * here): the backend's verify-access anonymous handler and the edge\n * adapter (`@astrasyncai/adapter-lambda`) must classify identically or\n * the three-tier visibility numbers drift between capture points.\n *\n * Pure regex — no Node built-ins, edge-safe. Detection is regex-only for\n * v2.9 — TLS fingerprinting / behavioural signatures are deferred.\n */\n\nexport type PlatformAgentVendor =\n | 'claude'\n | 'chatgpt'\n | 'gemini'\n | 'cursor'\n | 'goose'\n | 'perplexity'\n | 'unknown';\n\nexport interface PlatformFingerprint {\n vendor: PlatformAgentVendor;\n /** A stable identifier used as the orphan-agent dedup key. */\n fingerprintKey: string;\n /** Human-readable name surfaced in the admin panel. */\n displayName: string;\n}\n\nexport interface PlatformDetectionInput {\n userAgent?: string;\n agentCardUrl?: string;\n /**\n * A safe API-key format prefix observed on the caller's\n * credential header (e.g. `sk-ant-api03`, `sk-proj`, `aiza`). A strong\n * platform signal; never the secret. Matched against `apiKeyFormatPatterns`.\n */\n apiKeyFormat?: string;\n /**\n * The sanitised platform-signal headers (lowercased names →\n * values), e.g. `openai-organization`, `x-goog-user-project`. Matched\n * against `headerPatterns` (which test on `name` and `name: value`).\n */\n platformHeaders?: Record<string, string>;\n}\n\n/**\n * A signature definition, generic over the vendor key. The seed constant\n * below uses the closed `PlatformAgentVendor` union; DB-promoted signatures\n * (backend dynamic registry, console round) carry arbitrary string vendors —\n * `matchPlatformSignature` accepts both.\n */\nexport interface PlatformSignatureDef<V extends string = string> {\n vendor: V;\n displayName: string;\n uaPatterns?: RegExp[];\n agentCardPatterns?: RegExp[];\n /**\n * Patterns tested against an observed API-key format prefix\n * (e.g. `/^sk-ant/i` → claude). A credential-header format is a stronger\n * platform signal than a spoofable User-Agent.\n */\n apiKeyFormatPatterns?: RegExp[];\n /**\n * Patterns tested against each observed platform-signal\n * header, as both `name` and `name: value` (so `/openai-organization/i` and\n * `/x-goog-user-project/i` match on presence).\n */\n headerPatterns?: RegExp[];\n}\n\n/**\n * A fingerprint whose vendor may be a dynamically-promoted (non-seed) key.\n * Structurally a superset of `PlatformFingerprint`.\n */\nexport interface DynamicPlatformFingerprint {\n vendor: string;\n fingerprintKey: string;\n displayName: string;\n}\n\n/**\n * Registry of known platform-agent signatures. Order matters — first match wins.\n *\n * Patterns are intentionally permissive (case-insensitive substring match) since\n * platform UA strings drift between versions. False positives are tolerable —\n * the orphan record is informational, not enforcement.\n */\nexport const PLATFORM_AGENT_SIGNATURES: ReadonlyArray<PlatformSignatureDef<PlatformAgentVendor>> = [\n {\n vendor: 'claude',\n displayName: 'Anthropic Claude',\n uaPatterns: [/claude/i, /anthropic/i],\n agentCardPatterns: [/anthropic\\.com/i, /claude\\.ai/i],\n apiKeyFormatPatterns: [/^sk-ant/i],\n headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i],\n },\n {\n vendor: 'chatgpt',\n displayName: 'OpenAI ChatGPT',\n uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\\d/i],\n agentCardPatterns: [/openai\\.com/i, /chatgpt\\.com/i],\n apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],\n headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i],\n },\n {\n vendor: 'gemini',\n displayName: 'Google Gemini',\n uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],\n agentCardPatterns: [/gemini\\.google\\.com/i, /ai\\.google\\.dev/i],\n apiKeyFormatPatterns: [/^aiza/i],\n headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i],\n },\n {\n vendor: 'cursor',\n displayName: 'Cursor',\n uaPatterns: [/cursor/i],\n agentCardPatterns: [/cursor\\.sh/i, /cursor\\.com/i],\n },\n {\n vendor: 'goose',\n displayName: 'Block Goose',\n uaPatterns: [/goose/i, /block-goose/i],\n agentCardPatterns: [/block\\.xyz\\/goose/i, /goose\\.tools/i],\n },\n {\n vendor: 'perplexity',\n displayName: 'Perplexity',\n uaPatterns: [/perplexity/i],\n agentCardPatterns: [/perplexity\\.ai/i],\n apiKeyFormatPatterns: [/^pplx-/i],\n },\n];\n\n/**\n * Generic first-match-wins matcher over an arbitrary signature set. The\n * backend's dynamic registry merges the seed constant with DB-promoted\n * signatures and calls this — ONE matching implementation everywhere\n * (fingerprint-key rule included: vendor + lowercased UA basename before\n * the first space/slash, falling back to the agent-card host).\n */\nexport function matchPlatformSignature(\n signatures: ReadonlyArray<PlatformSignatureDef>,\n input: PlatformDetectionInput\n): DynamicPlatformFingerprint | undefined {\n const ua = input.userAgent ?? '';\n const cardUrl = input.agentCardUrl ?? '';\n const apiKeyFormat = input.apiKeyFormat ?? '';\n // Flatten platform headers to `name` and `name: value` lines for matching.\n const headerLines: string[] = [];\n if (input.platformHeaders) {\n for (const [name, value] of Object.entries(input.platformHeaders)) {\n headerLines.push(name, `${name}: ${value}`);\n }\n }\n\n for (const sig of signatures) {\n const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));\n const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));\n const keyMatch =\n apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));\n const headerMatch =\n headerLines.length > 0 &&\n sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));\n if (uaMatch || cardMatch || keyMatch || headerMatch) {\n const uaBase = ua.split(/[\\s/]/)[0]?.toLowerCase() ?? '';\n const cardHost = (() => {\n try {\n return cardUrl ? new URL(cardUrl).host : '';\n } catch {\n return '';\n }\n })();\n // Prefer a UA/card basename for the dedup key; fall back to the credential\n // format (still a stable per-vendor key) when only a key/header matched.\n const keyBasis = uaBase || cardHost || apiKeyFormat || 'unknown';\n return {\n vendor: sig.vendor,\n fingerprintKey: `${sig.vendor}:${keyBasis}`,\n displayName: sig.displayName,\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Inspect caller metadata for a known SEED platform-agent fingerprint.\n * Returns undefined if no signature matches. (Backends with the dynamic\n * registry should call `matchPlatformSignature` over their merged set\n * instead — same semantics, live signature additions included.)\n */\nexport function detectPlatformFingerprint(\n input: PlatformDetectionInput\n): PlatformFingerprint | undefined {\n // Seed vendors are all members of the PlatformAgentVendor union, so the\n // narrowing cast is sound by construction.\n return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input) as\n | PlatformFingerprint\n | undefined;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/platform-signatures.ts"],"sourcesContent":["/**\n * Platform-agent fingerprint signatures — the shared registry of known\n * platform-agent UA / agent-card patterns (Claude / ChatGPT / Gemini /\n * Cursor / Goose).\n *\n * Single source of truth, lifted verbatim from the backend's\n * `platform-agent-detector.service` (which now imports detection from\n * here): the backend's verify-access anonymous handler and the edge\n * adapter (`@astrasyncai/adapter-lambda`) must classify identically or\n * the three-tier visibility numbers drift between capture points.\n *\n * Pure regex — no Node built-ins, edge-safe. Detection is regex-only for\n * v2.9 — TLS fingerprinting / behavioural signatures are deferred\n * (recorded in adapter requirements doc).\n */\n\nexport type PlatformAgentVendor =\n | 'claude'\n | 'chatgpt'\n | 'gemini'\n | 'cursor'\n | 'goose'\n | 'perplexity'\n | 'unknown';\n\nexport interface PlatformFingerprint {\n vendor: PlatformAgentVendor;\n /** A stable identifier used as the orphan-agent dedup key. */\n fingerprintKey: string;\n /** Human-readable name surfaced in the admin panel. */\n displayName: string;\n}\n\nexport interface PlatformDetectionInput {\n userAgent?: string;\n agentCardUrl?: string;\n /**\n * Workstream D — a safe API-key format prefix observed on the caller's\n * credential header (e.g. `sk-ant-api03`, `sk-proj`, `aiza`). A strong\n * platform signal; never the secret. Matched against `apiKeyFormatPatterns`.\n */\n apiKeyFormat?: string;\n /**\n * Workstream D — the sanitised platform-signal headers (lowercased names →\n * values), e.g. `openai-organization`, `x-goog-user-project`. Matched\n * against `headerPatterns` (which test on `name` and `name: value`).\n */\n platformHeaders?: Record<string, string>;\n}\n\n/**\n * A signature definition, generic over the vendor key. The seed constant\n * below uses the closed `PlatformAgentVendor` union; DB-promoted signatures\n * (backend dynamic registry, console round) carry arbitrary string vendors —\n * `matchPlatformSignature` accepts both.\n */\nexport interface PlatformSignatureDef<V extends string = string> {\n vendor: V;\n displayName: string;\n uaPatterns?: RegExp[];\n agentCardPatterns?: RegExp[];\n /**\n * Workstream D — patterns tested against an observed API-key format prefix\n * (e.g. `/^sk-ant/i` → claude). A credential-header format is a stronger\n * platform signal than a spoofable User-Agent.\n */\n apiKeyFormatPatterns?: RegExp[];\n /**\n * Workstream D — patterns tested against each observed platform-signal\n * header, as both `name` and `name: value` (so `/openai-organization/i` and\n * `/x-goog-user-project/i` match on presence).\n */\n headerPatterns?: RegExp[];\n}\n\n/**\n * A fingerprint whose vendor may be a dynamically-promoted (non-seed) key.\n * Structurally a superset of `PlatformFingerprint`.\n */\nexport interface DynamicPlatformFingerprint {\n vendor: string;\n fingerprintKey: string;\n displayName: string;\n}\n\n/**\n * Registry of known platform-agent signatures. Order matters — first match wins.\n *\n * Patterns are intentionally permissive (case-insensitive substring match) since\n * platform UA strings drift between versions. False positives are tolerable —\n * the orphan record is informational, not enforcement.\n */\nexport const PLATFORM_AGENT_SIGNATURES: ReadonlyArray<PlatformSignatureDef<PlatformAgentVendor>> = [\n {\n vendor: 'claude',\n displayName: 'Anthropic Claude',\n uaPatterns: [/claude/i, /anthropic/i],\n agentCardPatterns: [/anthropic\\.com/i, /claude\\.ai/i],\n apiKeyFormatPatterns: [/^sk-ant/i],\n headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i],\n },\n {\n vendor: 'chatgpt',\n displayName: 'OpenAI ChatGPT',\n uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\\d/i],\n agentCardPatterns: [/openai\\.com/i, /chatgpt\\.com/i],\n apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],\n headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i],\n },\n {\n vendor: 'gemini',\n displayName: 'Google Gemini',\n uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],\n agentCardPatterns: [/gemini\\.google\\.com/i, /ai\\.google\\.dev/i],\n apiKeyFormatPatterns: [/^aiza/i],\n headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i],\n },\n {\n vendor: 'cursor',\n displayName: 'Cursor',\n uaPatterns: [/cursor/i],\n agentCardPatterns: [/cursor\\.sh/i, /cursor\\.com/i],\n },\n {\n vendor: 'goose',\n displayName: 'Block Goose',\n uaPatterns: [/goose/i, /block-goose/i],\n agentCardPatterns: [/block\\.xyz\\/goose/i, /goose\\.tools/i],\n },\n {\n vendor: 'perplexity',\n displayName: 'Perplexity',\n uaPatterns: [/perplexity/i],\n agentCardPatterns: [/perplexity\\.ai/i],\n apiKeyFormatPatterns: [/^pplx-/i],\n },\n];\n\n/**\n * Generic first-match-wins matcher over an arbitrary signature set. The\n * backend's dynamic registry merges the seed constant with DB-promoted\n * signatures and calls this — ONE matching implementation everywhere\n * (fingerprint-key rule included: vendor + lowercased UA basename before\n * the first space/slash, falling back to the agent-card host).\n */\nexport function matchPlatformSignature(\n signatures: ReadonlyArray<PlatformSignatureDef>,\n input: PlatformDetectionInput\n): DynamicPlatformFingerprint | undefined {\n const ua = input.userAgent ?? '';\n const cardUrl = input.agentCardUrl ?? '';\n const apiKeyFormat = input.apiKeyFormat ?? '';\n // Flatten platform headers to `name` and `name: value` lines for matching.\n const headerLines: string[] = [];\n if (input.platformHeaders) {\n for (const [name, value] of Object.entries(input.platformHeaders)) {\n headerLines.push(name, `${name}: ${value}`);\n }\n }\n\n for (const sig of signatures) {\n const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));\n const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));\n const keyMatch =\n apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));\n const headerMatch =\n headerLines.length > 0 &&\n sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));\n if (uaMatch || cardMatch || keyMatch || headerMatch) {\n const uaBase = ua.split(/[\\s/]/)[0]?.toLowerCase() ?? '';\n const cardHost = (() => {\n try {\n return cardUrl ? new URL(cardUrl).host : '';\n } catch {\n return '';\n }\n })();\n // Prefer a UA/card basename for the dedup key; fall back to the credential\n // format (still a stable per-vendor key) when only a key/header matched.\n const keyBasis = uaBase || cardHost || apiKeyFormat || 'unknown';\n return {\n vendor: sig.vendor,\n fingerprintKey: `${sig.vendor}:${keyBasis}`,\n displayName: sig.displayName,\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Inspect caller metadata for a known SEED platform-agent fingerprint.\n * Returns undefined if no signature matches. (Backends with the dynamic\n * registry should call `matchPlatformSignature` over their merged set\n * instead — same semantics, live signature additions included.)\n */\nexport function detectPlatformFingerprint(\n input: PlatformDetectionInput\n): PlatformFingerprint | undefined {\n // Seed vendors are all members of the PlatformAgentVendor union, so the\n // narrowing cast is sound by construction.\n return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input) as\n | PlatformFingerprint\n | undefined;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/platform-signatures.ts"],"sourcesContent":["/**\n * Platform-agent fingerprint signatures — the shared registry of known\n * platform-agent UA / agent-card patterns (Claude / ChatGPT / Gemini /\n * Cursor / Goose).\n *\n * Single source of truth, lifted verbatim from the backend's\n * `platform-agent-detector.service` (which now imports detection from\n * here): the backend's verify-access anonymous handler and the edge\n * adapter (`@astrasyncai/adapter-lambda`) must classify identically or\n * the three-tier visibility numbers drift between capture points.\n *\n * Pure regex — no Node built-ins, edge-safe. Detection is regex-only for\n * v2.9 — TLS fingerprinting / behavioural signatures are deferred.\n */\n\nexport type PlatformAgentVendor =\n | 'claude'\n | 'chatgpt'\n | 'gemini'\n | 'cursor'\n | 'goose'\n | 'perplexity'\n | 'unknown';\n\nexport interface PlatformFingerprint {\n vendor: PlatformAgentVendor;\n /** A stable identifier used as the orphan-agent dedup key. */\n fingerprintKey: string;\n /** Human-readable name surfaced in the admin panel. */\n displayName: string;\n}\n\nexport interface PlatformDetectionInput {\n userAgent?: string;\n agentCardUrl?: string;\n /**\n * A safe API-key format prefix observed on the caller's\n * credential header (e.g. `sk-ant-api03`, `sk-proj`, `aiza`). A strong\n * platform signal; never the secret. Matched against `apiKeyFormatPatterns`.\n */\n apiKeyFormat?: string;\n /**\n * The sanitised platform-signal headers (lowercased names →\n * values), e.g. `openai-organization`, `x-goog-user-project`. Matched\n * against `headerPatterns` (which test on `name` and `name: value`).\n */\n platformHeaders?: Record<string, string>;\n}\n\n/**\n * A signature definition, generic over the vendor key. The seed constant\n * below uses the closed `PlatformAgentVendor` union; DB-promoted signatures\n * (backend dynamic registry, console round) carry arbitrary string vendors —\n * `matchPlatformSignature` accepts both.\n */\nexport interface PlatformSignatureDef<V extends string = string> {\n vendor: V;\n displayName: string;\n uaPatterns?: RegExp[];\n agentCardPatterns?: RegExp[];\n /**\n * Patterns tested against an observed API-key format prefix\n * (e.g. `/^sk-ant/i` → claude). A credential-header format is a stronger\n * platform signal than a spoofable User-Agent.\n */\n apiKeyFormatPatterns?: RegExp[];\n /**\n * Patterns tested against each observed platform-signal\n * header, as both `name` and `name: value` (so `/openai-organization/i` and\n * `/x-goog-user-project/i` match on presence).\n */\n headerPatterns?: RegExp[];\n}\n\n/**\n * A fingerprint whose vendor may be a dynamically-promoted (non-seed) key.\n * Structurally a superset of `PlatformFingerprint`.\n */\nexport interface DynamicPlatformFingerprint {\n vendor: string;\n fingerprintKey: string;\n displayName: string;\n}\n\n/**\n * Registry of known platform-agent signatures. Order matters — first match wins.\n *\n * Patterns are intentionally permissive (case-insensitive substring match) since\n * platform UA strings drift between versions. False positives are tolerable —\n * the orphan record is informational, not enforcement.\n */\nexport const PLATFORM_AGENT_SIGNATURES: ReadonlyArray<PlatformSignatureDef<PlatformAgentVendor>> = [\n {\n vendor: 'claude',\n displayName: 'Anthropic Claude',\n uaPatterns: [/claude/i, /anthropic/i],\n agentCardPatterns: [/anthropic\\.com/i, /claude\\.ai/i],\n apiKeyFormatPatterns: [/^sk-ant/i],\n headerPatterns: [/^anthropic-version/i, /^anthropic-beta/i, /^x-anthropic-/i],\n },\n {\n vendor: 'chatgpt',\n displayName: 'OpenAI ChatGPT',\n uaPatterns: [/chatgpt/i, /openai/i, /gpt-?\\d/i],\n agentCardPatterns: [/openai\\.com/i, /chatgpt\\.com/i],\n apiKeyFormatPatterns: [/^sk-proj/i, /^sk-svcacct/i],\n headerPatterns: [/^openai-organization/i, /^openai-project/i, /^openai-version/i],\n },\n {\n vendor: 'gemini',\n displayName: 'Google Gemini',\n uaPatterns: [/gemini/i, /google-gemini/i, /bard/i],\n agentCardPatterns: [/gemini\\.google\\.com/i, /ai\\.google\\.dev/i],\n apiKeyFormatPatterns: [/^aiza/i],\n headerPatterns: [/^x-goog-user-project/i, /^x-goog-api-client/i],\n },\n {\n vendor: 'cursor',\n displayName: 'Cursor',\n uaPatterns: [/cursor/i],\n agentCardPatterns: [/cursor\\.sh/i, /cursor\\.com/i],\n },\n {\n vendor: 'goose',\n displayName: 'Block Goose',\n uaPatterns: [/goose/i, /block-goose/i],\n agentCardPatterns: [/block\\.xyz\\/goose/i, /goose\\.tools/i],\n },\n {\n vendor: 'perplexity',\n displayName: 'Perplexity',\n uaPatterns: [/perplexity/i],\n agentCardPatterns: [/perplexity\\.ai/i],\n apiKeyFormatPatterns: [/^pplx-/i],\n },\n];\n\n/**\n * Generic first-match-wins matcher over an arbitrary signature set. The\n * backend's dynamic registry merges the seed constant with DB-promoted\n * signatures and calls this — ONE matching implementation everywhere\n * (fingerprint-key rule included: vendor + lowercased UA basename before\n * the first space/slash, falling back to the agent-card host).\n */\nexport function matchPlatformSignature(\n signatures: ReadonlyArray<PlatformSignatureDef>,\n input: PlatformDetectionInput\n): DynamicPlatformFingerprint | undefined {\n const ua = input.userAgent ?? '';\n const cardUrl = input.agentCardUrl ?? '';\n const apiKeyFormat = input.apiKeyFormat ?? '';\n // Flatten platform headers to `name` and `name: value` lines for matching.\n const headerLines: string[] = [];\n if (input.platformHeaders) {\n for (const [name, value] of Object.entries(input.platformHeaders)) {\n headerLines.push(name, `${name}: ${value}`);\n }\n }\n\n for (const sig of signatures) {\n const uaMatch = sig.uaPatterns?.some((p) => p.test(ua));\n const cardMatch = sig.agentCardPatterns?.some((p) => p.test(cardUrl));\n const keyMatch =\n apiKeyFormat.length > 0 && sig.apiKeyFormatPatterns?.some((p) => p.test(apiKeyFormat));\n const headerMatch =\n headerLines.length > 0 &&\n sig.headerPatterns?.some((p) => headerLines.some((line) => p.test(line)));\n if (uaMatch || cardMatch || keyMatch || headerMatch) {\n const uaBase = ua.split(/[\\s/]/)[0]?.toLowerCase() ?? '';\n const cardHost = (() => {\n try {\n return cardUrl ? new URL(cardUrl).host : '';\n } catch {\n return '';\n }\n })();\n // Prefer a UA/card basename for the dedup key; fall back to the credential\n // format (still a stable per-vendor key) when only a key/header matched.\n const keyBasis = uaBase || cardHost || apiKeyFormat || 'unknown';\n return {\n vendor: sig.vendor,\n fingerprintKey: `${sig.vendor}:${keyBasis}`,\n displayName: sig.displayName,\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Inspect caller metadata for a known SEED platform-agent fingerprint.\n * Returns undefined if no signature matches. (Backends with the dynamic\n * registry should call `matchPlatformSignature` over their merged set\n * instead — same semantics, live signature additions included.)\n */\nexport function detectPlatformFingerprint(\n input: PlatformDetectionInput\n): PlatformFingerprint | undefined {\n // Seed vendors are all members of the PlatformAgentVendor union, so the\n // narrowing cast is sound by construction.\n return matchPlatformSignature(PLATFORM_AGENT_SIGNATURES, input) as\n | PlatformFingerprint\n | undefined;\n}\n"],"mappings":"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","names":[]}
@@ -170,7 +170,7 @@ interface WaitForApprovalOptions {
170
170
  type RegisterResult = {
171
171
  status: 'active';
172
172
  agent: AgentRecord;
173
- /** Round-12 (F16): backend advisories surfaced verbatim. */
173
+ /** Backend advisories surfaced verbatim. */
174
174
  warnings?: Array<{
175
175
  code: string;
176
176
  message: string;
@@ -181,7 +181,7 @@ type RegisterResult = {
181
181
  expiresAt: string;
182
182
  pollUrl: string;
183
183
  message?: string;
184
- /** Round-12 (F16): backend advisories surfaced verbatim. */
184
+ /** Backend advisories surfaced verbatim. */
185
185
  warnings?: Array<{
186
186
  code: string;
187
187
  message: string;
@@ -191,7 +191,7 @@ type RegisterResult = {
191
191
  * Response shape from `pollRegistration(requestId)` and the internal poll
192
192
  * loop. State `pending` means still awaiting owner; the others are terminal.
193
193
  *
194
- * Round-11 (F3): on `state: 'approved'`, the response also carries the
194
+ * On `state: 'approved'`, the response also carries the
195
195
  * canonical `astraId` — the partner-facing public agent identity that all
196
196
  * verify-access / verify/{id} surfaces accept. The `agent` (UUID) field
197
197
  * stays owner-private and is kept for backward compat.
@@ -219,10 +219,10 @@ interface RegistrationResponse {
219
219
  agent: AgentRecord;
220
220
  };
221
221
  /**
222
- * Round-12 (F16): backend-emitted advisories (e.g. `no_callback_endpoint`
223
- * when apiEndpoint is omitted). Pre-fix the SDK whitelisted five fields
224
- * on the way out and dropped this. Surface verbatim on both 201 and 202
225
- * register paths.
222
+ * Backend-emitted advisories (e.g. `no_callback_endpoint`
223
+ * when apiEndpoint is omitted). Previously the SDK whitelisted five
224
+ * fields on the way out and dropped this. Surface verbatim on both 201
225
+ * and 202 register paths.
226
226
  */
227
227
  warnings?: Array<{
228
228
  code: string;
@@ -237,7 +237,7 @@ interface PendingRegistrationResponse {
237
237
  expiresAt: string;
238
238
  pollUrl: string;
239
239
  message?: string;
240
- /** Round-12 (F16): same advisories as 201 path. */
240
+ /** Same advisories as 201 path. */
241
241
  warnings?: Array<{
242
242
  code: string;
243
243
  message: string;
@@ -436,8 +436,7 @@ declare class RegistrationTimeoutError extends AstraSyncError {
436
436
  /**
437
437
  * Guidance envelope for credentials-required cases.
438
438
  *
439
- * Round-18 B3b: lifted from `apps/mcp-server/src/tools/agent-registration.ts`
440
- * (which had inlined the shape) so partners writing their own wrappers
439
+ * A single shared source of truth so partners writing their own wrappers
441
440
  * (custom MCP servers, Express middleware around registration, gateway
442
441
  * adapters) consume a single source of truth instead of re-implementing the
443
442
  * five-step boilerplate.
@@ -490,7 +489,7 @@ interface BuildGuidanceParams {
490
489
  * Express middleware in custom integrations, and any other partner-side
491
490
  * wrapper that needs to surface "you need to register" should call this
492
491
  * rather than reconstructing the shape inline. Inline reconstruction
493
- * pre-round-18 led to drift between wrappers; this consolidates it.
492
+ * historically led to drift between wrappers; this consolidates it.
494
493
  *
495
494
  * @example
496
495
  * ```ts
@@ -170,7 +170,7 @@ interface WaitForApprovalOptions {
170
170
  type RegisterResult = {
171
171
  status: 'active';
172
172
  agent: AgentRecord;
173
- /** Round-12 (F16): backend advisories surfaced verbatim. */
173
+ /** Backend advisories surfaced verbatim. */
174
174
  warnings?: Array<{
175
175
  code: string;
176
176
  message: string;
@@ -181,7 +181,7 @@ type RegisterResult = {
181
181
  expiresAt: string;
182
182
  pollUrl: string;
183
183
  message?: string;
184
- /** Round-12 (F16): backend advisories surfaced verbatim. */
184
+ /** Backend advisories surfaced verbatim. */
185
185
  warnings?: Array<{
186
186
  code: string;
187
187
  message: string;
@@ -191,7 +191,7 @@ type RegisterResult = {
191
191
  * Response shape from `pollRegistration(requestId)` and the internal poll
192
192
  * loop. State `pending` means still awaiting owner; the others are terminal.
193
193
  *
194
- * Round-11 (F3): on `state: 'approved'`, the response also carries the
194
+ * On `state: 'approved'`, the response also carries the
195
195
  * canonical `astraId` — the partner-facing public agent identity that all
196
196
  * verify-access / verify/{id} surfaces accept. The `agent` (UUID) field
197
197
  * stays owner-private and is kept for backward compat.
@@ -219,10 +219,10 @@ interface RegistrationResponse {
219
219
  agent: AgentRecord;
220
220
  };
221
221
  /**
222
- * Round-12 (F16): backend-emitted advisories (e.g. `no_callback_endpoint`
223
- * when apiEndpoint is omitted). Pre-fix the SDK whitelisted five fields
224
- * on the way out and dropped this. Surface verbatim on both 201 and 202
225
- * register paths.
222
+ * Backend-emitted advisories (e.g. `no_callback_endpoint`
223
+ * when apiEndpoint is omitted). Previously the SDK whitelisted five
224
+ * fields on the way out and dropped this. Surface verbatim on both 201
225
+ * and 202 register paths.
226
226
  */
227
227
  warnings?: Array<{
228
228
  code: string;
@@ -237,7 +237,7 @@ interface PendingRegistrationResponse {
237
237
  expiresAt: string;
238
238
  pollUrl: string;
239
239
  message?: string;
240
- /** Round-12 (F16): same advisories as 201 path. */
240
+ /** Same advisories as 201 path. */
241
241
  warnings?: Array<{
242
242
  code: string;
243
243
  message: string;
@@ -436,8 +436,7 @@ declare class RegistrationTimeoutError extends AstraSyncError {
436
436
  /**
437
437
  * Guidance envelope for credentials-required cases.
438
438
  *
439
- * Round-18 B3b: lifted from `apps/mcp-server/src/tools/agent-registration.ts`
440
- * (which had inlined the shape) so partners writing their own wrappers
439
+ * A single shared source of truth so partners writing their own wrappers
441
440
  * (custom MCP servers, Express middleware around registration, gateway
442
441
  * adapters) consume a single source of truth instead of re-implementing the
443
442
  * five-step boilerplate.
@@ -490,7 +489,7 @@ interface BuildGuidanceParams {
490
489
  * Express middleware in custom integrations, and any other partner-side
491
490
  * wrapper that needs to surface "you need to register" should call this
492
491
  * rather than reconstructing the shape inline. Inline reconstruction
493
- * pre-round-18 led to drift between wrappers; this consolidates it.
492
+ * historically led to drift between wrappers; this consolidates it.
494
493
  *
495
494
  * @example
496
495
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/registration/index.ts","../../src/registration/errors.ts","../../src/registration/api.ts","../../src/registration/guidance.ts"],"sourcesContent":["export { AstraSync } from './api';\nexport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\nexport { buildGuidance } from './guidance';\nexport type { GuidanceEnvelope, BuildGuidanceParams } from './guidance';\nexport type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n AgentRecord,\n VerifyResponse,\n HealthResponse,\n PDLSSConfig,\n PDLSSPurpose,\n PDLSSDuration,\n PDLSSLimits,\n PDLSSScope,\n PDLSSSelfInstantiation,\n ModelConfig,\n FrameworkConfig,\n AgentProtocol,\n} from './types';\n","import type { ApiErrorResponse } from './types';\n\n/** Base error class for AstraSync SDK errors. */\nexport class AstraSyncError extends Error {\n public readonly code?: string;\n public readonly statusCode: number;\n\n constructor(message: string, statusCode: number, code?: string) {\n super(message);\n this.name = 'AstraSyncError';\n this.statusCode = statusCode;\n this.code = code;\n }\n}\n\n/** Thrown when KYD verification is required before agent registration. */\nexport class KYDRequiredError extends AstraSyncError {\n public readonly kydUrl: string;\n public readonly ownerNotified: boolean;\n\n constructor(response: ApiErrorResponse) {\n const kydUrl = response.kydUrl || 'https://astrasync.ai/developer-profile';\n super(\n `KYD verification required before registering agents.\\nComplete your KYD profile at: ${kydUrl}`,\n 403,\n 'KYD_REQUIRED'\n );\n this.name = 'KYDRequiredError';\n this.kydUrl = kydUrl;\n this.ownerNotified = response.ownerNotified || false;\n }\n}\n\n/** Thrown when authentication fails. */\nexport class AuthenticationError extends AstraSyncError {\n constructor(message: string) {\n super(message, 401, 'AUTH_FAILED');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the owner denies the\n * pending registration request. The `reason` field, when present, mirrors the\n * deny note the owner left in the dashboard.\n */\nexport class RegistrationDeniedError extends AstraSyncError {\n public readonly requestId: string;\n public readonly reason?: string;\n\n constructor(requestId: string, reason?: string) {\n super(\n `Registration request ${requestId} was denied by the account owner.${reason ? ` Reason: ${reason}` : ''}`,\n 403,\n 'REGISTRATION_DENIED'\n );\n this.name = 'RegistrationDeniedError';\n this.requestId = requestId;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the pending request\n * passes its 14-day TTL with no owner decision. The agent must re-submit.\n */\nexport class RegistrationExpiredError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Registration request ${requestId} expired before the owner approved it. Submit a new registration request.`,\n 410,\n 'REGISTRATION_EXPIRED'\n );\n this.name = 'RegistrationExpiredError';\n this.requestId = requestId;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the caller's local\n * `timeoutMs` elapses before the owner makes a decision. The request is still\n * live server-side — poll `pollRegistration(requestId)` to resume waiting, or\n * call `waitForApproval` again with a longer timeout.\n */\nexport class RegistrationTimeoutError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Timed out waiting for owner approval of registration request ${requestId}. The request is still active server-side; poll the request to resume waiting.`,\n 408,\n 'REGISTRATION_TIMEOUT'\n );\n this.name = 'RegistrationTimeoutError';\n this.requestId = requestId;\n }\n}\n","import type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n VerifyResponse,\n HealthResponse,\n ApiErrorResponse,\n AgentRecord,\n} from './types';\nimport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://astrasync.ai';\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/**\n * AstraSync SDK client for registering and managing AI agents.\n *\n * @example\n * ```typescript\n * const client = new AstraSync({ apiKey: 'kya_your_api_key' });\n * const result = await client.register({\n * name: 'My Agent',\n * model: { modelName: 'gpt-4o', modelProvider: 'openai', modelType: 'llm' },\n * });\n * ```\n *\n * For staging, pass `baseUrl: 'https://staging.astrasync.ai'`.\n */\nexport class AstraSync {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly email?: string;\n private readonly password?: string;\n private readonly privateKey?: string;\n private cachedJwt?: string;\n private jwtExpiresAt?: number;\n\n constructor(config: AstraSyncConfig = {}) {\n let raw = (config.baseUrl || process.env.ASTRASYNC_API_URL || DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n );\n // Round-10 (O2): tolerate the verify-side convention. `GatewayConfig.apiBaseUrl`\n // is documented as `https://astrasync.ai/api` (with /api), but\n // `AstraSyncConfig.baseUrl` is documented as the bare origin. Partners\n // passing the verify-style URL to the registration client hit a 404\n // because we'd then append `/api/agents/register` → double /api. Strip\n // a trailing `/api` and warn once so the partner can fix the source.\n if (raw.toLowerCase().endsWith('/api')) {\n raw = raw.slice(0, -'/api'.length);\n if (config.baseUrl && !config.silent) {\n // eslint-disable-next-line no-console\n console.warn(\n `[AstraSync] baseUrl '${config.baseUrl}' had a trailing /api — stripped to '${raw}'. ` +\n `Pass the bare origin (e.g. 'https://astrasync.ai' or 'https://staging.astrasync.ai') ` +\n `to AstraSync(). The /api suffix is the verify-gateway (GatewayConfig.apiBaseUrl) convention.`\n );\n }\n }\n this.baseUrl = raw;\n\n // Env fallback is opt-OUT: server-side wrappers (MCP tool handlers,\n // gateway adapters) pass `disableEnvFallback: true` so a no-credentials\n // call from a user-facing flow cannot silently authenticate as the host\n // process's platform-attribution key. CLIs / scripts that legitimately\n // rely on ASTRASYNC_API_KEY keep working under the default.\n this.apiKey = config.disableEnvFallback\n ? config.apiKey\n : config.apiKey || process.env.ASTRASYNC_API_KEY;\n this.email = config.email;\n this.password = config.password;\n this.privateKey = config.privateKey;\n\n // Defense-in-depth: warn when env fallback actually fires under the\n // default (non-strict) config. Surfaces the \"I'm running as platform\"\n // foot-gun before it becomes a security bug. Gated by !silent.\n if (\n !config.apiKey &&\n !config.disableEnvFallback &&\n process.env.ASTRASYNC_API_KEY &&\n !config.silent\n ) {\n // eslint-disable-next-line no-console\n console.warn(\n '[AstraSync] No apiKey passed to constructor; using process.env.ASTRASYNC_API_KEY. ' +\n 'If this code wraps user-facing flows (e.g. MCP tool handlers), pass ' +\n 'disableEnvFallback: true to prevent ambient credentials from impersonating callers. ' +\n 'See https://astrasync.ai/docs/agent-access#disableenvfallback for details.'\n );\n }\n\n if (!this.apiKey && !this.email) {\n throw new AuthenticationError(\n 'Authentication required. Provide apiKey, or email+password. ' +\n 'Set ASTRASYNC_API_KEY env var or pass config to constructor.'\n );\n }\n\n if (this.email && !this.password) {\n throw new AuthenticationError('Password is required when using email authentication.');\n }\n }\n\n /**\n * Register a new AI agent on the AstraSync KYA Platform.\n *\n * The backend response depends on auth context:\n * - **Crypto-keypair signed** (`privateKey` configured): synchronous 201,\n * returns `{ status: 'active', agent }`.\n * - **API-key only** (no signature): 202 pending, returns\n * `{ status: 'pending_approval', requestId, pollUrl, expiresAt }`. The\n * owner is notified by email and a dashboard alert is emitted; the agent\n * becomes active only after the owner approves.\n *\n * Blocking mode: pass `{ waitForApproval: true }` to have the SDK poll the\n * request until it resolves, then return the live agent record. The promise\n * rejects with `RegistrationDeniedError`, `RegistrationExpiredError`, or\n * `RegistrationTimeoutError` on the corresponding terminal states.\n *\n * @example Non-blocking (default — best for serverless / scheduled agents):\n * ```typescript\n * const result = await sdk.register({ name, pdlss });\n * if (result.status === 'pending_approval') {\n * storeRequestId(result.requestId);\n * return; // function exits; resume later via pollRegistration()\n * }\n * ```\n *\n * @example Blocking (best for long-running services + CLI):\n * ```typescript\n * const agent = await sdk.register({\n * name, pdlss, waitForApproval: true, timeoutMs: 600_000,\n * onPending: ({ ageMs }) => console.log(`waiting ${ageMs}ms`),\n * });\n * ```\n */\n async register(\n options: RegisterOptions & WaitForApprovalOptions\n ): Promise<RegisterResult | AgentRecord> {\n const body: Record<string, unknown> = {\n name: options.name,\n ...(options.description && { description: options.description }),\n ...(options.agentType && { agentType: options.agentType }),\n ...(options.apiEndpoint && { apiEndpoint: options.apiEndpoint }),\n ...(options.model && { model: options.model }),\n ...(options.framework && { framework: options.framework }),\n ...(options.protocols && { protocols: options.protocols }),\n ...(options.metadata && { metadata: options.metadata }),\n ...(options.pdlss && { pdlss: options.pdlss }),\n };\n\n const { status, body: raw } = await this.requestWithStatus<\n RegistrationResponse | PendingRegistrationResponse\n >('POST', '/api/agents/register', body);\n\n if (status === 201) {\n const activeBody = raw as RegistrationResponse;\n const active: RegisterResult = {\n status: 'active',\n agent: activeBody.data.agent,\n // Round-12 (F16): pass backend advisories through verbatim.\n // Pre-fix the SDK whitelisted five fields and silently dropped\n // `warnings`, which left partners with no signal that\n // `no_callback_endpoint` (or future advisories) had fired.\n ...(activeBody.warnings && { warnings: activeBody.warnings }),\n };\n return active;\n }\n\n // 202 Accepted — owner approval required.\n const pendingBody = raw as PendingRegistrationResponse;\n const pending: RegisterResult = {\n status: 'pending_approval',\n requestId: pendingBody.requestId,\n expiresAt: pendingBody.expiresAt,\n pollUrl: pendingBody.pollUrl,\n message: pendingBody.message,\n // Round-12 (F16): same pass-through on the pending path.\n ...(pendingBody.warnings && { warnings: pendingBody.warnings }),\n };\n\n if (!options.waitForApproval) return pending;\n\n return this.waitForApproval(pendingBody.requestId, options);\n }\n\n /**\n * Poll the current state of a pending-approval registration request.\n *\n * Useful for caller-driven polling when `waitForApproval: false` (the\n * default). The endpoint is unauthenticated — pass the `requestId` that\n * was returned from the 202 response.\n *\n * @returns `state: 'pending'` while awaiting; `'approved'` carries the\n * minted agent in `agent`; `'denied'` may carry the owner's\n * `reason`; `'expired'` is terminal after 14 days.\n */\n async pollRegistration(requestId: string): Promise<PollRegistrationResult> {\n const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;\n const res = await fetch(url, { headers: { Accept: 'application/json' } });\n if (!res.ok) {\n const errBody = (await res.json().catch(() => ({}))) as ApiErrorResponse;\n throw new AstraSyncError(\n errBody.error || `pollRegistration failed: ${res.status}`,\n res.status,\n errBody.code\n );\n }\n return (await res.json()) as PollRegistrationResult;\n }\n\n /**\n * Block until a pending registration request resolves to a terminal state.\n * Resolves to the live `AgentRecord` on approval; rejects with the matching\n * Registration*Error on deny/expire/timeout. Usually called via\n * `register({ waitForApproval: true })`, but exposed for callers that want\n * to fire-and-forget the initial register call and resume waiting later\n * (e.g. after restoring a stored `requestId` on cold start).\n */\n async waitForApproval(\n requestId: string,\n options: WaitForApprovalOptions = {}\n ): Promise<AgentRecord> {\n const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;\n const pollIntervalMs = options.pollIntervalMs ?? 5_000;\n const start = Date.now();\n const deadline = start + timeoutMs;\n\n while (Date.now() < deadline) {\n const result = await this.pollRegistration(requestId);\n const ageMs = Date.now() - start;\n options.onPending?.({ requestId, ageMs });\n\n if (result.state === 'approved') {\n if (!result.agent) {\n throw new AstraSyncError(\n `Registration ${requestId} reported approved but no agent payload returned.`,\n 500\n );\n }\n return result.agent;\n }\n if (result.state === 'denied') {\n throw new RegistrationDeniedError(requestId, result.reason);\n }\n if (result.state === 'expired') {\n throw new RegistrationExpiredError(requestId);\n }\n await sleep(pollIntervalMs);\n }\n throw new RegistrationTimeoutError(requestId);\n }\n\n /**\n * Look up an agent's public profile by ASTRA ID or UUID.\n */\n async verify(agentId: string): Promise<VerifyResponse> {\n return this.request<VerifyResponse>('GET', `/api/agents/verify/${agentId}`);\n }\n\n /**\n * Check API health.\n */\n async health(): Promise<HealthResponse> {\n const res = await fetch(`${this.baseUrl}/api/health/`);\n if (!res.ok) {\n throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);\n }\n return res.json() as Promise<HealthResponse>;\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private async request<T>(method: string, endpoint: string, body?: unknown): Promise<T> {\n const { body: parsed } = await this.requestWithStatus<T>(method, endpoint, body);\n return parsed;\n }\n\n /**\n * Variant of {@link request} that also returns the HTTP status code, so\n * callers can branch on 201 vs 202 (or other success codes) without losing\n * type information about the response body.\n */\n private async requestWithStatus<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<{ status: number; body: T }> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Set auth header\n const token = await this.getAuthToken();\n headers['Authorization'] = `Bearer ${token}`;\n\n // Sign request if private key is configured\n if (this.privateKey) {\n const signature = await this.signRequest(method, endpoint, body || {});\n headers['X-AstraSync-Signature'] = signature;\n }\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!res.ok) {\n const errorBody = (await res\n .json()\n .catch(() => ({ error: res.statusText }))) as ApiErrorResponse;\n\n // Handle KYD_REQUIRED specifically\n if (res.status === 403 && errorBody.code === 'KYD_REQUIRED') {\n throw new KYDRequiredError(errorBody);\n }\n\n throw new AstraSyncError(\n errorBody.error || `Request failed: ${res.status}`,\n res.status,\n errorBody.code\n );\n }\n\n return { status: res.status, body: (await res.json()) as T };\n }\n\n private async getAuthToken(): Promise<string> {\n // API key auth — use directly\n if (this.apiKey) {\n return this.apiKey;\n }\n\n // Email+password — login and cache JWT\n if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {\n return this.cachedJwt;\n }\n\n const res = await fetch(`${this.baseUrl}/api/auth/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: this.email, password: this.password }),\n });\n\n if (!res.ok) {\n const errorBody = (await res.json().catch(() => ({}))) as Record<string, unknown>;\n throw new AuthenticationError(\n (errorBody.message as string) || (errorBody.error as string) || 'Login failed'\n );\n }\n\n const data = (await res.json()) as { data: { token: string } };\n this.cachedJwt = data.data.token;\n // Cache for 6 days (tokens expire in 7)\n this.jwtExpiresAt = Date.now() + 6 * 24 * 60 * 60 * 1000;\n\n return this.cachedJwt;\n }\n\n /**\n * Sign a request using secp256k1 (ethers.js).\n * Canonical message format: METHOD:ENDPOINT:SORTED_JSON_BODY\n * Must match apps/backend/src/services/signature-verify.service.ts exactly.\n */\n private async signRequest(method: string, endpoint: string, body: unknown): Promise<string> {\n const { Wallet } = await import('ethers');\n const sorted = this.sortObjectKeys(body);\n const canonical = `${method}:${endpoint}:${JSON.stringify(sorted)}`;\n const wallet = new Wallet(this.privateKey!);\n return wallet.signMessage(canonical);\n }\n\n /** Recursively sort object keys for canonical JSON representation. */\n private sortObjectKeys(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map((item) => this.sortObjectKeys(item));\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = this.sortObjectKeys((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n }\n}\n","/**\n * Guidance envelope for credentials-required cases.\n *\n * Round-18 B3b: lifted from `apps/mcp-server/src/tools/agent-registration.ts`\n * (which had inlined the shape) so partners writing their own wrappers\n * (custom MCP servers, Express middleware around registration, gateway\n * adapters) consume a single source of truth instead of re-implementing the\n * five-step boilerplate.\n *\n * The envelope is what an agent (or wrapping MCP client) sees when it calls\n * `register_agent` with no AstraSync credentials. It tells the calling agent\n * (a) what failed (`status: 'credentials_required'`), (b) the keyless\n * registration ACTION endpoint (`registrationUrl` — POST-able, not the human\n * dashboard), (c) where the relevant docs are (`documentationUrl`), and (d) the\n * ordered next-actions (`steps`).\n */\n\nexport interface GuidanceEnvelope {\n /**\n * Single-literal today. Expand to a union (e.g.\n * `'credentials_required' | 'kyd_required' | …`) when a second guidance\n * status emerges. Don't pre-emptively widen — the literal pins the shape.\n */\n status: 'credentials_required';\n message: string;\n guidance: {\n message: string;\n registrationUrl: string;\n documentationUrl: string;\n steps: string[];\n };\n}\n\nexport interface BuildGuidanceParams {\n /**\n * Bare origin of the AstraSync deployment the caller should register at,\n * e.g. `https://astrasync.ai` or `https://staging.astrasync.ai`. No\n * trailing slash, no `/api` suffix — registrationUrl and documentationUrl\n * are templated relative to this origin.\n */\n origin: string;\n /**\n * Overrides the top-level `message` (the short summary the agent sees).\n * Defaults to the error message from the SDK's `AuthenticationError`\n * when present, else a generic \"credentials required\" line.\n */\n message?: string;\n /**\n * Overrides the documentation path. Defaults to `/docs/agent-access`.\n */\n documentationPath?: string;\n}\n\n/**\n * Build the credentials-required guidance envelope.\n *\n * This is the canonical builder — MCP wrappers (`agent-registration.ts`),\n * Express middleware in custom integrations, and any other partner-side\n * wrapper that needs to surface \"you need to register\" should call this\n * rather than reconstructing the shape inline. Inline reconstruction\n * pre-round-18 led to drift between wrappers; this consolidates it.\n *\n * @example\n * ```ts\n * import { buildGuidance } from '@astrasyncai/verification-gateway';\n *\n * try {\n * const sdk = new AstraSync({ apiKey: callerApiKey, disableEnvFallback: true });\n * // ...\n * } catch (err) {\n * if (err instanceof AuthenticationError) {\n * return buildGuidance({ origin: 'https://astrasync.ai', message: err.message });\n * }\n * throw err;\n * }\n * ```\n */\nexport function buildGuidance(params: BuildGuidanceParams): GuidanceEnvelope {\n const origin = params.origin.replace(/\\/+$/, '');\n const docsPath = params.documentationPath ?? '/docs/agent-access';\n const message = params.message ?? 'AstraSync registration requires credentials.';\n\n return {\n status: 'credentials_required',\n message,\n guidance: {\n message:\n \"Register either with an agent-scoped API key (the recommended credentialed path) OR, if you hold no credentials, via the owner's email using request_registration. Never send a password or private key over the wire.\",\n // 3.0.0: registrationUrl is the agent-actionable KEYLESS endpoint, not the\n // human dashboard `/agents/register` (an auth-walled page an agent can't\n // use). documentationUrl carries the machine-readable contract.\n registrationUrl: `${origin}/api/agents/request-registration`,\n documentationUrl: `${origin}${docsPath.startsWith('/') ? docsPath : `/${docsPath}`}`,\n steps: [\n 'Credentialed path (recommended): have your human mint an agent-scoped API key at Settings → API Keys, then re-call register_agent with apiKey set. An agent holding its own scoped kya_ key is fine and intended.',\n \"No-credentials path: call request_registration({ name, ownerEmail, ... }) with the human owner's email — the only bootstrap credential. No API key is involved.\",\n 'The owner approves via an emailed link (first time, they create an account and complete a quick verification).',\n 'Use poll_registration({ requestId }) once the owner confirms approval to retrieve the astraId.',\n 'Never transmit a password or private key over the MCP wire — those are never required.',\n ],\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EAIxC,YAAY,SAAiB,YAAoB,MAAe;AAC9D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,aAAa;AAClB,SAAK,OAAO;AAAA,EACd;AACF;AAGO,IAAM,mBAAN,cAA+B,eAAe;AAAA,EAInD,YAAY,UAA4B;AACtC,UAAM,SAAS,SAAS,UAAU;AAClC;AAAA,MACE;AAAA,gCAAuF,MAAM;AAAA,MAC7F;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,gBAAgB,SAAS,iBAAiB;AAAA,EACjD;AACF;AAGO,IAAM,sBAAN,cAAkC,eAAe;AAAA,EACtD,YAAY,SAAiB;AAC3B,UAAM,SAAS,KAAK,aAAa;AACjC,SAAK,OAAO;AAAA,EACd;AACF;AAOO,IAAM,0BAAN,cAAsC,eAAe;AAAA,EAI1D,YAAY,WAAmB,QAAiB;AAC9C;AAAA,MACE,wBAAwB,SAAS,oCAAoC,SAAS,YAAY,MAAM,KAAK,EAAE;AAAA,MACvG;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,SAAK,SAAS;AAAA,EAChB;AACF;AAMO,IAAM,2BAAN,cAAuC,eAAe;AAAA,EAG3D,YAAY,WAAmB;AAC7B;AAAA,MACE,wBAAwB,SAAS;AAAA,MACjC;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AACZ,SAAK,YAAY;AAAA,EACnB;AACF;AAQO,IAAM,2BAAN,cAAuC,eAAe;AAAA,EAG3D,YAAY,WAAmB;AAC7B;AAAA,MACE,gEAAgE,SAAS;AAAA,MACzE;AAAA,MACA;AAAA,IACF;AACA,SAAK,OAAO;AACZ,SAAK,YAAY;AAAA,EACnB;AACF;;;AC5EA,IAAM,mBAAmB;AAEzB,IAAM,QAAQ,CAAC,OAA8B,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAgB1E,IAAM,YAAN,MAAgB;AAAA,EASrB,YAAY,SAA0B,CAAC,GAAG;AACxC,QAAI,OAAO,OAAO,WAAW,QAAQ,IAAI,qBAAqB,kBAAkB;AAAA,MAC9E;AAAA,MACA;AAAA,IACF;AAOA,QAAI,IAAI,YAAY,EAAE,SAAS,MAAM,GAAG;AACtC,YAAM,IAAI,MAAM,GAAG,CAAC,OAAO,MAAM;AACjC,UAAI,OAAO,WAAW,CAAC,OAAO,QAAQ;AAEpC,gBAAQ;AAAA,UACN,wBAAwB,OAAO,OAAO,6CAAwC,GAAG;AAAA,QAGnF;AAAA,MACF;AAAA,IACF;AACA,SAAK,UAAU;AAOf,SAAK,SAAS,OAAO,qBACjB,OAAO,SACP,OAAO,UAAU,QAAQ,IAAI;AACjC,SAAK,QAAQ,OAAO;AACpB,SAAK,WAAW,OAAO;AACvB,SAAK,aAAa,OAAO;AAKzB,QACE,CAAC,OAAO,UACR,CAAC,OAAO,sBACR,QAAQ,IAAI,qBACZ,CAAC,OAAO,QACR;AAEA,cAAQ;AAAA,QACN;AAAA,MAIF;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,OAAO;AAC/B,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAEA,QAAI,KAAK,SAAS,CAAC,KAAK,UAAU;AAChC,YAAM,IAAI,oBAAoB,uDAAuD;AAAA,IACvF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCA,MAAM,SACJ,SACuC;AACvC,UAAM,OAAgC;AAAA,MACpC,MAAM,QAAQ;AAAA,MACd,GAAI,QAAQ,eAAe,EAAE,aAAa,QAAQ,YAAY;AAAA,MAC9D,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,UAAU;AAAA,MACxD,GAAI,QAAQ,eAAe,EAAE,aAAa,QAAQ,YAAY;AAAA,MAC9D,GAAI,QAAQ,SAAS,EAAE,OAAO,QAAQ,MAAM;AAAA,MAC5C,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,UAAU;AAAA,MACxD,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,UAAU;AAAA,MACxD,GAAI,QAAQ,YAAY,EAAE,UAAU,QAAQ,SAAS;AAAA,MACrD,GAAI,QAAQ,SAAS,EAAE,OAAO,QAAQ,MAAM;AAAA,IAC9C;AAEA,UAAM,EAAE,QAAQ,MAAM,IAAI,IAAI,MAAM,KAAK,kBAEvC,QAAQ,wBAAwB,IAAI;AAEtC,QAAI,WAAW,KAAK;AAClB,YAAM,aAAa;AACnB,YAAM,SAAyB;AAAA,QAC7B,QAAQ;AAAA,QACR,OAAO,WAAW,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,QAKvB,GAAI,WAAW,YAAY,EAAE,UAAU,WAAW,SAAS;AAAA,MAC7D;AACA,aAAO;AAAA,IACT;AAGA,UAAM,cAAc;AACpB,UAAM,UAA0B;AAAA,MAC9B,QAAQ;AAAA,MACR,WAAW,YAAY;AAAA,MACvB,WAAW,YAAY;AAAA,MACvB,SAAS,YAAY;AAAA,MACrB,SAAS,YAAY;AAAA;AAAA,MAErB,GAAI,YAAY,YAAY,EAAE,UAAU,YAAY,SAAS;AAAA,IAC/D;AAEA,QAAI,CAAC,QAAQ,gBAAiB,QAAO;AAErC,WAAO,KAAK,gBAAgB,YAAY,WAAW,OAAO;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,iBAAiB,WAAoD;AACzE,UAAM,MAAM,GAAG,KAAK,OAAO,oCAAoC,SAAS;AACxE,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,QAAQ,mBAAmB,EAAE,CAAC;AACxE,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,UAAW,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAClD,YAAM,IAAI;AAAA,QACR,QAAQ,SAAS,4BAA4B,IAAI,MAAM;AAAA,QACvD,IAAI;AAAA,QACJ,QAAQ;AAAA,MACV;AAAA,IACF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,gBACJ,WACA,UAAkC,CAAC,GACb;AACtB,UAAM,YAAY,QAAQ,aAAa,KAAK,KAAK;AACjD,UAAM,iBAAiB,QAAQ,kBAAkB;AACjD,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,WAAW,QAAQ;AAEzB,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,YAAM,SAAS,MAAM,KAAK,iBAAiB,SAAS;AACpD,YAAM,QAAQ,KAAK,IAAI,IAAI;AAC3B,cAAQ,YAAY,EAAE,WAAW,MAAM,CAAC;AAExC,UAAI,OAAO,UAAU,YAAY;AAC/B,YAAI,CAAC,OAAO,OAAO;AACjB,gBAAM,IAAI;AAAA,YACR,gBAAgB,SAAS;AAAA,YACzB;AAAA,UACF;AAAA,QACF;AACA,eAAO,OAAO;AAAA,MAChB;AACA,UAAI,OAAO,UAAU,UAAU;AAC7B,cAAM,IAAI,wBAAwB,WAAW,OAAO,MAAM;AAAA,MAC5D;AACA,UAAI,OAAO,UAAU,WAAW;AAC9B,cAAM,IAAI,yBAAyB,SAAS;AAAA,MAC9C;AACA,YAAM,MAAM,cAAc;AAAA,IAC5B;AACA,UAAM,IAAI,yBAAyB,SAAS;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO,SAA0C;AACrD,WAAO,KAAK,QAAwB,OAAO,sBAAsB,OAAO,EAAE;AAAA,EAC5E;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAkC;AACtC,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,cAAc;AACrD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,eAAe,wBAAwB,IAAI,MAAM,IAAI,IAAI,MAAM;AAAA,IAC3E;AACA,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA,EAIA,MAAc,QAAW,QAAgB,UAAkB,MAA4B;AACrF,UAAM,EAAE,MAAM,OAAO,IAAI,MAAM,KAAK,kBAAqB,QAAQ,UAAU,IAAI;AAC/E,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,kBACZ,QACA,UACA,MACsC;AACtC,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,QAAQ;AACtC,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,IAClB;AAGA,UAAM,QAAQ,MAAM,KAAK,aAAa;AACtC,YAAQ,eAAe,IAAI,UAAU,KAAK;AAG1C,QAAI,KAAK,YAAY;AACnB,YAAM,YAAY,MAAM,KAAK,YAAY,QAAQ,UAAU,QAAQ,CAAC,CAAC;AACrE,cAAQ,uBAAuB,IAAI;AAAA,IACrC;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,GAAI,OAAO,EAAE,MAAM,KAAK,UAAU,IAAI,EAAE,IAAI,CAAC;AAAA,IAC/C,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,YAAa,MAAM,IACtB,KAAK,EACL,MAAM,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE;AAG1C,UAAI,IAAI,WAAW,OAAO,UAAU,SAAS,gBAAgB;AAC3D,cAAM,IAAI,iBAAiB,SAAS;AAAA,MACtC;AAEA,YAAM,IAAI;AAAA,QACR,UAAU,SAAS,mBAAmB,IAAI,MAAM;AAAA,QAChD,IAAI;AAAA,QACJ,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,WAAO,EAAE,QAAQ,IAAI,QAAQ,MAAO,MAAM,IAAI,KAAK,EAAQ;AAAA,EAC7D;AAAA,EAEA,MAAc,eAAgC;AAE5C,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK;AAAA,IACd;AAGA,QAAI,KAAK,aAAa,KAAK,gBAAgB,KAAK,IAAI,IAAI,KAAK,cAAc;AACzE,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,mBAAmB;AAAA,MACxD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,KAAK,OAAO,UAAU,KAAK,SAAS,CAAC;AAAA,IACrE,CAAC;AAED,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,YAAa,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACpD,YAAM,IAAI;AAAA,QACP,UAAU,WAAuB,UAAU,SAAoB;AAAA,MAClE;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,SAAK,YAAY,KAAK,KAAK;AAE3B,SAAK,eAAe,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,KAAK;AAEpD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,YAAY,QAAgB,UAAkB,MAAgC;AAC1F,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,QAAQ;AACxC,UAAM,SAAS,KAAK,eAAe,IAAI;AACvC,UAAM,YAAY,GAAG,MAAM,IAAI,QAAQ,IAAI,KAAK,UAAU,MAAM,CAAC;AACjE,UAAM,SAAS,IAAI,OAAO,KAAK,UAAW;AAC1C,WAAO,OAAO,YAAY,SAAS;AAAA,EACrC;AAAA;AAAA,EAGQ,eAAe,KAAuB;AAC5C,QAAI,QAAQ,QAAQ,OAAO,QAAQ,UAAU;AAC3C,aAAO;AAAA,IACT;AACA,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,aAAO,IAAI,IAAI,CAAC,SAAS,KAAK,eAAe,IAAI,CAAC;AAAA,IACpD;AACA,UAAM,SAAkC,CAAC;AACzC,eAAW,OAAO,OAAO,KAAK,GAAG,EAAE,KAAK,GAAG;AACzC,aAAO,GAAG,IAAI,KAAK,eAAgB,IAAgC,GAAG,CAAC;AAAA,IACzE;AACA,WAAO;AAAA,EACT;AACF;;;ACnUO,SAAS,cAAc,QAA+C;AAC3E,QAAM,SAAS,OAAO,OAAO,QAAQ,QAAQ,EAAE;AAC/C,QAAM,WAAW,OAAO,qBAAqB;AAC7C,QAAM,UAAU,OAAO,WAAW;AAElC,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,UAAU;AAAA,MACR,SACE;AAAA;AAAA;AAAA;AAAA,MAIF,iBAAiB,GAAG,MAAM;AAAA,MAC1B,kBAAkB,GAAG,MAAM,GAAG,SAAS,WAAW,GAAG,IAAI,WAAW,IAAI,QAAQ,EAAE;AAAA,MAClF,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/registration/index.ts","../../src/registration/errors.ts","../../src/registration/api.ts","../../src/registration/guidance.ts"],"sourcesContent":["export { AstraSync } from './api';\nexport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\nexport { buildGuidance } from './guidance';\nexport type { GuidanceEnvelope, BuildGuidanceParams } from './guidance';\nexport type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n AgentRecord,\n VerifyResponse,\n HealthResponse,\n PDLSSConfig,\n PDLSSPurpose,\n PDLSSDuration,\n PDLSSLimits,\n PDLSSScope,\n PDLSSSelfInstantiation,\n ModelConfig,\n FrameworkConfig,\n AgentProtocol,\n} from './types';\n","import type { ApiErrorResponse } from './types';\n\n/** Base error class for AstraSync SDK errors. */\nexport class AstraSyncError extends Error {\n public readonly code?: string;\n public readonly statusCode: number;\n\n constructor(message: string, statusCode: number, code?: string) {\n super(message);\n this.name = 'AstraSyncError';\n this.statusCode = statusCode;\n this.code = code;\n }\n}\n\n/** Thrown when KYD verification is required before agent registration. */\nexport class KYDRequiredError extends AstraSyncError {\n public readonly kydUrl: string;\n public readonly ownerNotified: boolean;\n\n constructor(response: ApiErrorResponse) {\n const kydUrl = response.kydUrl || 'https://astrasync.ai/developer-profile';\n super(\n `KYD verification required before registering agents.\\nComplete your KYD profile at: ${kydUrl}`,\n 403,\n 'KYD_REQUIRED'\n );\n this.name = 'KYDRequiredError';\n this.kydUrl = kydUrl;\n this.ownerNotified = response.ownerNotified || false;\n }\n}\n\n/** Thrown when authentication fails. */\nexport class AuthenticationError extends AstraSyncError {\n constructor(message: string) {\n super(message, 401, 'AUTH_FAILED');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the owner denies the\n * pending registration request. The `reason` field, when present, mirrors the\n * deny note the owner left in the dashboard.\n */\nexport class RegistrationDeniedError extends AstraSyncError {\n public readonly requestId: string;\n public readonly reason?: string;\n\n constructor(requestId: string, reason?: string) {\n super(\n `Registration request ${requestId} was denied by the account owner.${reason ? ` Reason: ${reason}` : ''}`,\n 403,\n 'REGISTRATION_DENIED'\n );\n this.name = 'RegistrationDeniedError';\n this.requestId = requestId;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the pending request\n * passes its 14-day TTL with no owner decision. The agent must re-submit.\n */\nexport class RegistrationExpiredError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Registration request ${requestId} expired before the owner approved it. Submit a new registration request.`,\n 410,\n 'REGISTRATION_EXPIRED'\n );\n this.name = 'RegistrationExpiredError';\n this.requestId = requestId;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the caller's local\n * `timeoutMs` elapses before the owner makes a decision. The request is still\n * live server-side — poll `pollRegistration(requestId)` to resume waiting, or\n * call `waitForApproval` again with a longer timeout.\n */\nexport class RegistrationTimeoutError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Timed out waiting for owner approval of registration request ${requestId}. The request is still active server-side; poll the request to resume waiting.`,\n 408,\n 'REGISTRATION_TIMEOUT'\n );\n this.name = 'RegistrationTimeoutError';\n this.requestId = requestId;\n }\n}\n","import type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n VerifyResponse,\n HealthResponse,\n ApiErrorResponse,\n AgentRecord,\n} from './types';\nimport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://astrasync.ai';\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/**\n * AstraSync SDK client for registering and managing AI agents.\n *\n * @example\n * ```typescript\n * const client = new AstraSync({ apiKey: 'kya_your_api_key' });\n * const result = await client.register({\n * name: 'My Agent',\n * model: { modelName: 'gpt-4o', modelProvider: 'openai', modelType: 'llm' },\n * });\n * ```\n *\n * For staging, pass `baseUrl: 'https://staging.astrasync.ai'`.\n */\nexport class AstraSync {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly email?: string;\n private readonly password?: string;\n private readonly privateKey?: string;\n private cachedJwt?: string;\n private jwtExpiresAt?: number;\n\n constructor(config: AstraSyncConfig = {}) {\n let raw = (config.baseUrl || process.env.ASTRASYNC_API_URL || DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n );\n // Round-10 (O2): tolerate the verify-side convention. `GatewayConfig.apiBaseUrl`\n // is documented as `https://astrasync.ai/api` (with /api), but\n // `AstraSyncConfig.baseUrl` is documented as the bare origin. Partners\n // passing the verify-style URL to the registration client hit a 404\n // because we'd then append `/api/agents/register` → double /api. Strip\n // a trailing `/api` and warn once so the partner can fix the source.\n if (raw.toLowerCase().endsWith('/api')) {\n raw = raw.slice(0, -'/api'.length);\n if (config.baseUrl && !config.silent) {\n // eslint-disable-next-line no-console\n console.warn(\n `[AstraSync] baseUrl '${config.baseUrl}' had a trailing /api — stripped to '${raw}'. ` +\n `Pass the bare origin (e.g. 'https://astrasync.ai' or 'https://staging.astrasync.ai') ` +\n `to AstraSync(). The /api suffix is the verify-gateway (GatewayConfig.apiBaseUrl) convention.`\n );\n }\n }\n this.baseUrl = raw;\n\n // Env fallback is opt-OUT: server-side wrappers (MCP tool handlers,\n // gateway adapters) pass `disableEnvFallback: true` so a no-credentials\n // call from a user-facing flow cannot silently authenticate as the host\n // process's platform-attribution key. CLIs / scripts that legitimately\n // rely on ASTRASYNC_API_KEY keep working under the default.\n this.apiKey = config.disableEnvFallback\n ? config.apiKey\n : config.apiKey || process.env.ASTRASYNC_API_KEY;\n this.email = config.email;\n this.password = config.password;\n this.privateKey = config.privateKey;\n\n // Defense-in-depth: warn when env fallback actually fires under the\n // default (non-strict) config. Surfaces the \"I'm running as platform\"\n // foot-gun before it becomes a security bug. Gated by !silent.\n if (\n !config.apiKey &&\n !config.disableEnvFallback &&\n process.env.ASTRASYNC_API_KEY &&\n !config.silent\n ) {\n // eslint-disable-next-line no-console\n console.warn(\n '[AstraSync] No apiKey passed to constructor; using process.env.ASTRASYNC_API_KEY. ' +\n 'If this code wraps user-facing flows (e.g. MCP tool handlers), pass ' +\n 'disableEnvFallback: true to prevent ambient credentials from impersonating callers. ' +\n 'See https://astrasync.ai/docs/agent-access#disableenvfallback for details.'\n );\n }\n\n if (!this.apiKey && !this.email) {\n throw new AuthenticationError(\n 'Authentication required. Provide apiKey, or email+password. ' +\n 'Set ASTRASYNC_API_KEY env var or pass config to constructor.'\n );\n }\n\n if (this.email && !this.password) {\n throw new AuthenticationError('Password is required when using email authentication.');\n }\n }\n\n /**\n * Register a new AI agent on the AstraSync KYA Platform.\n *\n * The backend response depends on auth context:\n * - **Crypto-keypair signed** (`privateKey` configured): synchronous 201,\n * returns `{ status: 'active', agent }`.\n * - **API-key only** (no signature): 202 pending, returns\n * `{ status: 'pending_approval', requestId, pollUrl, expiresAt }`. The\n * owner is notified by email and a dashboard alert is emitted; the agent\n * becomes active only after the owner approves.\n *\n * Blocking mode: pass `{ waitForApproval: true }` to have the SDK poll the\n * request until it resolves, then return the live agent record. The promise\n * rejects with `RegistrationDeniedError`, `RegistrationExpiredError`, or\n * `RegistrationTimeoutError` on the corresponding terminal states.\n *\n * @example Non-blocking (default — best for serverless / scheduled agents):\n * ```typescript\n * const result = await sdk.register({ name, pdlss });\n * if (result.status === 'pending_approval') {\n * storeRequestId(result.requestId);\n * return; // function exits; resume later via pollRegistration()\n * }\n * ```\n *\n * @example Blocking (best for long-running services + CLI):\n * ```typescript\n * const agent = await sdk.register({\n * name, pdlss, waitForApproval: true, timeoutMs: 600_000,\n * onPending: ({ ageMs }) => console.log(`waiting ${ageMs}ms`),\n * });\n * ```\n */\n async register(\n options: RegisterOptions & WaitForApprovalOptions\n ): Promise<RegisterResult | AgentRecord> {\n const body: Record<string, unknown> = {\n name: options.name,\n ...(options.description && { description: options.description }),\n ...(options.agentType && { agentType: options.agentType }),\n ...(options.apiEndpoint && { apiEndpoint: options.apiEndpoint }),\n ...(options.model && { model: options.model }),\n ...(options.framework && { framework: options.framework }),\n ...(options.protocols && { protocols: options.protocols }),\n ...(options.metadata && { metadata: options.metadata }),\n ...(options.pdlss && { pdlss: options.pdlss }),\n };\n\n const { status, body: raw } = await this.requestWithStatus<\n RegistrationResponse | PendingRegistrationResponse\n >('POST', '/api/agents/register', body);\n\n if (status === 201) {\n const activeBody = raw as RegistrationResponse;\n const active: RegisterResult = {\n status: 'active',\n agent: activeBody.data.agent,\n // Round-12 (F16): pass backend advisories through verbatim.\n // Pre-fix the SDK whitelisted five fields and silently dropped\n // `warnings`, which left partners with no signal that\n // `no_callback_endpoint` (or future advisories) had fired.\n ...(activeBody.warnings && { warnings: activeBody.warnings }),\n };\n return active;\n }\n\n // 202 Accepted — owner approval required.\n const pendingBody = raw as PendingRegistrationResponse;\n const pending: RegisterResult = {\n status: 'pending_approval',\n requestId: pendingBody.requestId,\n expiresAt: pendingBody.expiresAt,\n pollUrl: pendingBody.pollUrl,\n message: pendingBody.message,\n // Round-12 (F16): same pass-through on the pending path.\n ...(pendingBody.warnings && { warnings: pendingBody.warnings }),\n };\n\n if (!options.waitForApproval) return pending;\n\n return this.waitForApproval(pendingBody.requestId, options);\n }\n\n /**\n * Poll the current state of a pending-approval registration request.\n *\n * Useful for caller-driven polling when `waitForApproval: false` (the\n * default). The endpoint is unauthenticated — pass the `requestId` that\n * was returned from the 202 response.\n *\n * @returns `state: 'pending'` while awaiting; `'approved'` carries the\n * minted agent in `agent`; `'denied'` may carry the owner's\n * `reason`; `'expired'` is terminal after 14 days.\n */\n async pollRegistration(requestId: string): Promise<PollRegistrationResult> {\n const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;\n const res = await fetch(url, { headers: { Accept: 'application/json' } });\n if (!res.ok) {\n const errBody = (await res.json().catch(() => ({}))) as ApiErrorResponse;\n throw new AstraSyncError(\n errBody.error || `pollRegistration failed: ${res.status}`,\n res.status,\n errBody.code\n );\n }\n return (await res.json()) as PollRegistrationResult;\n }\n\n /**\n * Block until a pending registration request resolves to a terminal state.\n * Resolves to the live `AgentRecord` on approval; rejects with the matching\n * Registration*Error on deny/expire/timeout. Usually called via\n * `register({ waitForApproval: true })`, but exposed for callers that want\n * to fire-and-forget the initial register call and resume waiting later\n * (e.g. after restoring a stored `requestId` on cold start).\n */\n async waitForApproval(\n requestId: string,\n options: WaitForApprovalOptions = {}\n ): Promise<AgentRecord> {\n const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;\n const pollIntervalMs = options.pollIntervalMs ?? 5_000;\n const start = Date.now();\n const deadline = start + timeoutMs;\n\n while (Date.now() < deadline) {\n const result = await this.pollRegistration(requestId);\n const ageMs = Date.now() - start;\n options.onPending?.({ requestId, ageMs });\n\n if (result.state === 'approved') {\n if (!result.agent) {\n throw new AstraSyncError(\n `Registration ${requestId} reported approved but no agent payload returned.`,\n 500\n );\n }\n return result.agent;\n }\n if (result.state === 'denied') {\n throw new RegistrationDeniedError(requestId, result.reason);\n }\n if (result.state === 'expired') {\n throw new RegistrationExpiredError(requestId);\n }\n await sleep(pollIntervalMs);\n }\n throw new RegistrationTimeoutError(requestId);\n }\n\n /**\n * Look up an agent's public profile by ASTRA ID or UUID.\n */\n async verify(agentId: string): Promise<VerifyResponse> {\n return this.request<VerifyResponse>('GET', `/api/agents/verify/${agentId}`);\n }\n\n /**\n * Check API health.\n */\n async health(): Promise<HealthResponse> {\n const res = await fetch(`${this.baseUrl}/api/health/`);\n if (!res.ok) {\n throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);\n }\n return res.json() as Promise<HealthResponse>;\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private async request<T>(method: string, endpoint: string, body?: unknown): Promise<T> {\n const { body: parsed } = await this.requestWithStatus<T>(method, endpoint, body);\n return parsed;\n }\n\n /**\n * Variant of {@link request} that also returns the HTTP status code, so\n * callers can branch on 201 vs 202 (or other success codes) without losing\n * type information about the response body.\n */\n private async requestWithStatus<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<{ status: number; body: T }> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Set auth header\n const token = await this.getAuthToken();\n headers['Authorization'] = `Bearer ${token}`;\n\n // Sign request if private key is configured\n if (this.privateKey) {\n const signature = await this.signRequest(method, endpoint, body || {});\n headers['X-AstraSync-Signature'] = signature;\n }\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!res.ok) {\n const errorBody = (await res\n .json()\n .catch(() => ({ error: res.statusText }))) as ApiErrorResponse;\n\n // Handle KYD_REQUIRED specifically\n if (res.status === 403 && errorBody.code === 'KYD_REQUIRED') {\n throw new KYDRequiredError(errorBody);\n }\n\n throw new AstraSyncError(\n errorBody.error || `Request failed: ${res.status}`,\n res.status,\n errorBody.code\n );\n }\n\n return { status: res.status, body: (await res.json()) as T };\n }\n\n private async getAuthToken(): Promise<string> {\n // API key auth — use directly\n if (this.apiKey) {\n return this.apiKey;\n }\n\n // Email+password — login and cache JWT\n if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {\n return this.cachedJwt;\n }\n\n const res = await fetch(`${this.baseUrl}/api/auth/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: this.email, password: this.password }),\n });\n\n if (!res.ok) {\n const errorBody = (await res.json().catch(() => ({}))) as Record<string, unknown>;\n throw new AuthenticationError(\n (errorBody.message as string) || (errorBody.error as string) || 'Login failed'\n );\n }\n\n const data = (await res.json()) as { data: { token: string } };\n this.cachedJwt = data.data.token;\n // Cache for 6 days (tokens expire in 7)\n this.jwtExpiresAt = Date.now() + 6 * 24 * 60 * 60 * 1000;\n\n return this.cachedJwt;\n }\n\n /**\n * Sign a request using secp256k1 (ethers.js).\n * Canonical message format: METHOD:ENDPOINT:SORTED_JSON_BODY\n * Must match apps/backend/src/services/signature-verify.service.ts exactly.\n */\n private async signRequest(method: string, endpoint: string, body: unknown): Promise<string> {\n const { Wallet } = await import('ethers');\n const sorted = this.sortObjectKeys(body);\n const canonical = `${method}:${endpoint}:${JSON.stringify(sorted)}`;\n const wallet = new Wallet(this.privateKey!);\n return wallet.signMessage(canonical);\n }\n\n /** Recursively sort object keys for canonical JSON representation. */\n private sortObjectKeys(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map((item) => this.sortObjectKeys(item));\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = this.sortObjectKeys((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n }\n}\n","/**\n * Guidance envelope for credentials-required cases.\n *\n * A single shared source of truth so partners writing their own wrappers\n * (custom MCP servers, Express middleware around registration, gateway\n * adapters) consume a single source of truth instead of re-implementing the\n * five-step boilerplate.\n *\n * The envelope is what an agent (or wrapping MCP client) sees when it calls\n * `register_agent` with no AstraSync credentials. It tells the calling agent\n * (a) what failed (`status: 'credentials_required'`), (b) the keyless\n * registration ACTION endpoint (`registrationUrl` — POST-able, not the human\n * dashboard), (c) where the relevant docs are (`documentationUrl`), and (d) the\n * ordered next-actions (`steps`).\n */\n\nexport interface GuidanceEnvelope {\n /**\n * Single-literal today. Expand to a union (e.g.\n * `'credentials_required' | 'kyd_required' | …`) when a second guidance\n * status emerges. Don't pre-emptively widen — the literal pins the shape.\n */\n status: 'credentials_required';\n message: string;\n guidance: {\n message: string;\n registrationUrl: string;\n documentationUrl: string;\n steps: string[];\n };\n}\n\nexport interface BuildGuidanceParams {\n /**\n * Bare origin of the AstraSync deployment the caller should register at,\n * e.g. `https://astrasync.ai` or `https://staging.astrasync.ai`. No\n * trailing slash, no `/api` suffix — registrationUrl and documentationUrl\n * are templated relative to this origin.\n */\n origin: string;\n /**\n * Overrides the top-level `message` (the short summary the agent sees).\n * Defaults to the error message from the SDK's `AuthenticationError`\n * when present, else a generic \"credentials required\" line.\n */\n message?: string;\n /**\n * Overrides the documentation path. Defaults to `/docs/agent-access`.\n */\n documentationPath?: string;\n}\n\n/**\n * Build the credentials-required guidance envelope.\n *\n * This is the canonical builder — MCP wrappers (`agent-registration.ts`),\n * Express middleware in custom integrations, and any other partner-side\n * wrapper that needs to surface \"you need to register\" should call this\n * rather than reconstructing the shape inline. Inline reconstruction\n * historically led to drift between wrappers; this consolidates it.\n *\n * @example\n * ```ts\n * import { buildGuidance } from '@astrasyncai/verification-gateway';\n *\n * try {\n * const sdk = new AstraSync({ apiKey: callerApiKey, disableEnvFallback: true });\n * // ...\n * } catch (err) {\n * if (err instanceof AuthenticationError) {\n * return buildGuidance({ origin: 'https://astrasync.ai', message: err.message });\n * }\n * throw err;\n * }\n * ```\n */\nexport function buildGuidance(params: BuildGuidanceParams): GuidanceEnvelope {\n const origin = params.origin.replace(/\\/+$/, '');\n const docsPath = params.documentationPath ?? '/docs/agent-access';\n const message = params.message ?? 'AstraSync registration requires credentials.';\n\n return {\n status: 'credentials_required',\n message,\n guidance: {\n message:\n \"Register either with an agent-scoped API key (the recommended credentialed path) OR, if you hold no credentials, via the owner's email using request_registration. Never send a password or private key over the wire.\",\n // 3.0.0: registrationUrl is the agent-actionable KEYLESS endpoint, not the\n // human dashboard `/agents/register` (an auth-walled page an agent can't\n // use). documentationUrl carries the machine-readable contract.\n registrationUrl: `${origin}/api/agents/request-registration`,\n documentationUrl: `${origin}${docsPath.startsWith('/') ? docsPath : `/${docsPath}`}`,\n steps: [\n 'Credentialed path (recommended): have your human mint an agent-scoped API key at Settings → API Keys, then re-call register_agent with apiKey set. An agent holding its own scoped kya_ key is fine and intended.',\n \"No-credentials path: call request_registration({ name, ownerEmail, ... }) with the human owner's email — the only bootstrap credential. No API key is involved.\",\n 'The owner approves via an emailed link (first time, they create an account and complete a quick verification).',\n 'Use poll_registration({ requestId }) once the owner confirms approval to retrieve the astraId.',\n 'Never transmit a password or private key over the MCP wire — those are never required.',\n ],\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/registration/errors.ts","../../src/registration/api.ts","../../src/registration/guidance.ts"],"sourcesContent":["import type { ApiErrorResponse } from './types';\n\n/** Base error class for AstraSync SDK errors. */\nexport class AstraSyncError extends Error {\n public readonly code?: string;\n public readonly statusCode: number;\n\n constructor(message: string, statusCode: number, code?: string) {\n super(message);\n this.name = 'AstraSyncError';\n this.statusCode = statusCode;\n this.code = code;\n }\n}\n\n/** Thrown when KYD verification is required before agent registration. */\nexport class KYDRequiredError extends AstraSyncError {\n public readonly kydUrl: string;\n public readonly ownerNotified: boolean;\n\n constructor(response: ApiErrorResponse) {\n const kydUrl = response.kydUrl || 'https://astrasync.ai/developer-profile';\n super(\n `KYD verification required before registering agents.\\nComplete your KYD profile at: ${kydUrl}`,\n 403,\n 'KYD_REQUIRED'\n );\n this.name = 'KYDRequiredError';\n this.kydUrl = kydUrl;\n this.ownerNotified = response.ownerNotified || false;\n }\n}\n\n/** Thrown when authentication fails. */\nexport class AuthenticationError extends AstraSyncError {\n constructor(message: string) {\n super(message, 401, 'AUTH_FAILED');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the owner denies the\n * pending registration request. The `reason` field, when present, mirrors the\n * deny note the owner left in the dashboard.\n */\nexport class RegistrationDeniedError extends AstraSyncError {\n public readonly requestId: string;\n public readonly reason?: string;\n\n constructor(requestId: string, reason?: string) {\n super(\n `Registration request ${requestId} was denied by the account owner.${reason ? ` Reason: ${reason}` : ''}`,\n 403,\n 'REGISTRATION_DENIED'\n );\n this.name = 'RegistrationDeniedError';\n this.requestId = requestId;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the pending request\n * passes its 14-day TTL with no owner decision. The agent must re-submit.\n */\nexport class RegistrationExpiredError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Registration request ${requestId} expired before the owner approved it. Submit a new registration request.`,\n 410,\n 'REGISTRATION_EXPIRED'\n );\n this.name = 'RegistrationExpiredError';\n this.requestId = requestId;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the caller's local\n * `timeoutMs` elapses before the owner makes a decision. The request is still\n * live server-side — poll `pollRegistration(requestId)` to resume waiting, or\n * call `waitForApproval` again with a longer timeout.\n */\nexport class RegistrationTimeoutError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Timed out waiting for owner approval of registration request ${requestId}. The request is still active server-side; poll the request to resume waiting.`,\n 408,\n 'REGISTRATION_TIMEOUT'\n );\n this.name = 'RegistrationTimeoutError';\n this.requestId = requestId;\n }\n}\n","import type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n VerifyResponse,\n HealthResponse,\n ApiErrorResponse,\n AgentRecord,\n} from './types';\nimport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://astrasync.ai';\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/**\n * AstraSync SDK client for registering and managing AI agents.\n *\n * @example\n * ```typescript\n * const client = new AstraSync({ apiKey: 'kya_your_api_key' });\n * const result = await client.register({\n * name: 'My Agent',\n * model: { modelName: 'gpt-4o', modelProvider: 'openai', modelType: 'llm' },\n * });\n * ```\n *\n * For staging, pass `baseUrl: 'https://staging.astrasync.ai'`.\n */\nexport class AstraSync {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly email?: string;\n private readonly password?: string;\n private readonly privateKey?: string;\n private cachedJwt?: string;\n private jwtExpiresAt?: number;\n\n constructor(config: AstraSyncConfig = {}) {\n let raw = (config.baseUrl || process.env.ASTRASYNC_API_URL || DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n );\n // Round-10 (O2): tolerate the verify-side convention. `GatewayConfig.apiBaseUrl`\n // is documented as `https://astrasync.ai/api` (with /api), but\n // `AstraSyncConfig.baseUrl` is documented as the bare origin. Partners\n // passing the verify-style URL to the registration client hit a 404\n // because we'd then append `/api/agents/register` → double /api. Strip\n // a trailing `/api` and warn once so the partner can fix the source.\n if (raw.toLowerCase().endsWith('/api')) {\n raw = raw.slice(0, -'/api'.length);\n if (config.baseUrl && !config.silent) {\n // eslint-disable-next-line no-console\n console.warn(\n `[AstraSync] baseUrl '${config.baseUrl}' had a trailing /api — stripped to '${raw}'. ` +\n `Pass the bare origin (e.g. 'https://astrasync.ai' or 'https://staging.astrasync.ai') ` +\n `to AstraSync(). The /api suffix is the verify-gateway (GatewayConfig.apiBaseUrl) convention.`\n );\n }\n }\n this.baseUrl = raw;\n\n // Env fallback is opt-OUT: server-side wrappers (MCP tool handlers,\n // gateway adapters) pass `disableEnvFallback: true` so a no-credentials\n // call from a user-facing flow cannot silently authenticate as the host\n // process's platform-attribution key. CLIs / scripts that legitimately\n // rely on ASTRASYNC_API_KEY keep working under the default.\n this.apiKey = config.disableEnvFallback\n ? config.apiKey\n : config.apiKey || process.env.ASTRASYNC_API_KEY;\n this.email = config.email;\n this.password = config.password;\n this.privateKey = config.privateKey;\n\n // Defense-in-depth: warn when env fallback actually fires under the\n // default (non-strict) config. Surfaces the \"I'm running as platform\"\n // foot-gun before it becomes a security bug. Gated by !silent.\n if (\n !config.apiKey &&\n !config.disableEnvFallback &&\n process.env.ASTRASYNC_API_KEY &&\n !config.silent\n ) {\n // eslint-disable-next-line no-console\n console.warn(\n '[AstraSync] No apiKey passed to constructor; using process.env.ASTRASYNC_API_KEY. ' +\n 'If this code wraps user-facing flows (e.g. MCP tool handlers), pass ' +\n 'disableEnvFallback: true to prevent ambient credentials from impersonating callers. ' +\n 'See https://astrasync.ai/docs/agent-access#disableenvfallback for details.'\n );\n }\n\n if (!this.apiKey && !this.email) {\n throw new AuthenticationError(\n 'Authentication required. Provide apiKey, or email+password. ' +\n 'Set ASTRASYNC_API_KEY env var or pass config to constructor.'\n );\n }\n\n if (this.email && !this.password) {\n throw new AuthenticationError('Password is required when using email authentication.');\n }\n }\n\n /**\n * Register a new AI agent on the AstraSync KYA Platform.\n *\n * The backend response depends on auth context:\n * - **Crypto-keypair signed** (`privateKey` configured): synchronous 201,\n * returns `{ status: 'active', agent }`.\n * - **API-key only** (no signature): 202 pending, returns\n * `{ status: 'pending_approval', requestId, pollUrl, expiresAt }`. The\n * owner is notified by email and a dashboard alert is emitted; the agent\n * becomes active only after the owner approves.\n *\n * Blocking mode: pass `{ waitForApproval: true }` to have the SDK poll the\n * request until it resolves, then return the live agent record. The promise\n * rejects with `RegistrationDeniedError`, `RegistrationExpiredError`, or\n * `RegistrationTimeoutError` on the corresponding terminal states.\n *\n * @example Non-blocking (default — best for serverless / scheduled agents):\n * ```typescript\n * const result = await sdk.register({ name, pdlss });\n * if (result.status === 'pending_approval') {\n * storeRequestId(result.requestId);\n * return; // function exits; resume later via pollRegistration()\n * }\n * ```\n *\n * @example Blocking (best for long-running services + CLI):\n * ```typescript\n * const agent = await sdk.register({\n * name, pdlss, waitForApproval: true, timeoutMs: 600_000,\n * onPending: ({ ageMs }) => console.log(`waiting ${ageMs}ms`),\n * });\n * ```\n */\n async register(\n options: RegisterOptions & WaitForApprovalOptions\n ): Promise<RegisterResult | AgentRecord> {\n const body: Record<string, unknown> = {\n name: options.name,\n ...(options.description && { description: options.description }),\n ...(options.agentType && { agentType: options.agentType }),\n ...(options.apiEndpoint && { apiEndpoint: options.apiEndpoint }),\n ...(options.model && { model: options.model }),\n ...(options.framework && { framework: options.framework }),\n ...(options.protocols && { protocols: options.protocols }),\n ...(options.metadata && { metadata: options.metadata }),\n ...(options.pdlss && { pdlss: options.pdlss }),\n };\n\n const { status, body: raw } = await this.requestWithStatus<\n RegistrationResponse | PendingRegistrationResponse\n >('POST', '/api/agents/register', body);\n\n if (status === 201) {\n const activeBody = raw as RegistrationResponse;\n const active: RegisterResult = {\n status: 'active',\n agent: activeBody.data.agent,\n // Round-12 (F16): pass backend advisories through verbatim.\n // Pre-fix the SDK whitelisted five fields and silently dropped\n // `warnings`, which left partners with no signal that\n // `no_callback_endpoint` (or future advisories) had fired.\n ...(activeBody.warnings && { warnings: activeBody.warnings }),\n };\n return active;\n }\n\n // 202 Accepted — owner approval required.\n const pendingBody = raw as PendingRegistrationResponse;\n const pending: RegisterResult = {\n status: 'pending_approval',\n requestId: pendingBody.requestId,\n expiresAt: pendingBody.expiresAt,\n pollUrl: pendingBody.pollUrl,\n message: pendingBody.message,\n // Round-12 (F16): same pass-through on the pending path.\n ...(pendingBody.warnings && { warnings: pendingBody.warnings }),\n };\n\n if (!options.waitForApproval) return pending;\n\n return this.waitForApproval(pendingBody.requestId, options);\n }\n\n /**\n * Poll the current state of a pending-approval registration request.\n *\n * Useful for caller-driven polling when `waitForApproval: false` (the\n * default). The endpoint is unauthenticated — pass the `requestId` that\n * was returned from the 202 response.\n *\n * @returns `state: 'pending'` while awaiting; `'approved'` carries the\n * minted agent in `agent`; `'denied'` may carry the owner's\n * `reason`; `'expired'` is terminal after 14 days.\n */\n async pollRegistration(requestId: string): Promise<PollRegistrationResult> {\n const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;\n const res = await fetch(url, { headers: { Accept: 'application/json' } });\n if (!res.ok) {\n const errBody = (await res.json().catch(() => ({}))) as ApiErrorResponse;\n throw new AstraSyncError(\n errBody.error || `pollRegistration failed: ${res.status}`,\n res.status,\n errBody.code\n );\n }\n return (await res.json()) as PollRegistrationResult;\n }\n\n /**\n * Block until a pending registration request resolves to a terminal state.\n * Resolves to the live `AgentRecord` on approval; rejects with the matching\n * Registration*Error on deny/expire/timeout. Usually called via\n * `register({ waitForApproval: true })`, but exposed for callers that want\n * to fire-and-forget the initial register call and resume waiting later\n * (e.g. after restoring a stored `requestId` on cold start).\n */\n async waitForApproval(\n requestId: string,\n options: WaitForApprovalOptions = {}\n ): Promise<AgentRecord> {\n const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;\n const pollIntervalMs = options.pollIntervalMs ?? 5_000;\n const start = Date.now();\n const deadline = start + timeoutMs;\n\n while (Date.now() < deadline) {\n const result = await this.pollRegistration(requestId);\n const ageMs = Date.now() - start;\n options.onPending?.({ requestId, ageMs });\n\n if (result.state === 'approved') {\n if (!result.agent) {\n throw new AstraSyncError(\n `Registration ${requestId} reported approved but no agent payload returned.`,\n 500\n );\n }\n return result.agent;\n }\n if (result.state === 'denied') {\n throw new RegistrationDeniedError(requestId, result.reason);\n }\n if (result.state === 'expired') {\n throw new RegistrationExpiredError(requestId);\n }\n await sleep(pollIntervalMs);\n }\n throw new RegistrationTimeoutError(requestId);\n }\n\n /**\n * Look up an agent's public profile by ASTRA ID or UUID.\n */\n async verify(agentId: string): Promise<VerifyResponse> {\n return this.request<VerifyResponse>('GET', `/api/agents/verify/${agentId}`);\n }\n\n /**\n * Check API health.\n */\n async health(): Promise<HealthResponse> {\n const res = await fetch(`${this.baseUrl}/api/health/`);\n if (!res.ok) {\n throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);\n }\n return res.json() as Promise<HealthResponse>;\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private async request<T>(method: string, endpoint: string, body?: unknown): Promise<T> {\n const { body: parsed } = await this.requestWithStatus<T>(method, endpoint, body);\n return parsed;\n }\n\n /**\n * Variant of {@link request} that also returns the HTTP status code, so\n * callers can branch on 201 vs 202 (or other success codes) without losing\n * type information about the response body.\n */\n private async requestWithStatus<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<{ status: number; body: T }> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Set auth header\n const token = await this.getAuthToken();\n headers['Authorization'] = `Bearer ${token}`;\n\n // Sign request if private key is configured\n if (this.privateKey) {\n const signature = await this.signRequest(method, endpoint, body || {});\n headers['X-AstraSync-Signature'] = signature;\n }\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!res.ok) {\n const errorBody = (await res\n .json()\n .catch(() => ({ error: res.statusText }))) as ApiErrorResponse;\n\n // Handle KYD_REQUIRED specifically\n if (res.status === 403 && errorBody.code === 'KYD_REQUIRED') {\n throw new KYDRequiredError(errorBody);\n }\n\n throw new AstraSyncError(\n errorBody.error || `Request failed: ${res.status}`,\n res.status,\n errorBody.code\n );\n }\n\n return { status: res.status, body: (await res.json()) as T };\n }\n\n private async getAuthToken(): Promise<string> {\n // API key auth — use directly\n if (this.apiKey) {\n return this.apiKey;\n }\n\n // Email+password — login and cache JWT\n if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {\n return this.cachedJwt;\n }\n\n const res = await fetch(`${this.baseUrl}/api/auth/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: this.email, password: this.password }),\n });\n\n if (!res.ok) {\n const errorBody = (await res.json().catch(() => ({}))) as Record<string, unknown>;\n throw new AuthenticationError(\n (errorBody.message as string) || (errorBody.error as string) || 'Login failed'\n );\n }\n\n const data = (await res.json()) as { data: { token: string } };\n this.cachedJwt = data.data.token;\n // Cache for 6 days (tokens expire in 7)\n this.jwtExpiresAt = Date.now() + 6 * 24 * 60 * 60 * 1000;\n\n return this.cachedJwt;\n }\n\n /**\n * Sign a request using secp256k1 (ethers.js).\n * Canonical message format: METHOD:ENDPOINT:SORTED_JSON_BODY\n * Must match apps/backend/src/services/signature-verify.service.ts exactly.\n */\n private async signRequest(method: string, endpoint: string, body: unknown): Promise<string> {\n const { Wallet } = await import('ethers');\n const sorted = this.sortObjectKeys(body);\n const canonical = `${method}:${endpoint}:${JSON.stringify(sorted)}`;\n const wallet = new Wallet(this.privateKey!);\n return wallet.signMessage(canonical);\n }\n\n /** Recursively sort object keys for canonical JSON representation. */\n private sortObjectKeys(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map((item) => this.sortObjectKeys(item));\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = this.sortObjectKeys((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n }\n}\n","/**\n * Guidance envelope for credentials-required cases.\n *\n * Round-18 B3b: lifted from `apps/mcp-server/src/tools/agent-registration.ts`\n * (which had inlined the shape) so partners writing their own wrappers\n * (custom MCP servers, Express middleware around registration, gateway\n * adapters) consume a single source of truth instead of re-implementing the\n * five-step boilerplate.\n *\n * The envelope is what an agent (or wrapping MCP client) sees when it calls\n * `register_agent` with no AstraSync credentials. It tells the calling agent\n * (a) what failed (`status: 'credentials_required'`), (b) the keyless\n * registration ACTION endpoint (`registrationUrl` — POST-able, not the human\n * dashboard), (c) where the relevant docs are (`documentationUrl`), and (d) the\n * ordered next-actions (`steps`).\n */\n\nexport interface GuidanceEnvelope {\n /**\n * Single-literal today. Expand to a union (e.g.\n * `'credentials_required' | 'kyd_required' | …`) when a second guidance\n * status emerges. Don't pre-emptively widen — the literal pins the shape.\n */\n status: 'credentials_required';\n message: string;\n guidance: {\n message: string;\n registrationUrl: string;\n documentationUrl: string;\n steps: string[];\n };\n}\n\nexport interface BuildGuidanceParams {\n /**\n * Bare origin of the AstraSync deployment the caller should register at,\n * e.g. `https://astrasync.ai` or `https://staging.astrasync.ai`. No\n * trailing slash, no `/api` suffix — registrationUrl and documentationUrl\n * are templated relative to this origin.\n */\n origin: string;\n /**\n * Overrides the top-level `message` (the short summary the agent sees).\n * Defaults to the error message from the SDK's `AuthenticationError`\n * when present, else a generic \"credentials required\" line.\n */\n message?: string;\n /**\n * Overrides the documentation path. Defaults to `/docs/agent-access`.\n */\n documentationPath?: string;\n}\n\n/**\n * Build the credentials-required guidance envelope.\n *\n * This is the canonical builder — MCP wrappers (`agent-registration.ts`),\n * Express middleware in custom integrations, and any other partner-side\n * wrapper that needs to surface \"you need to register\" should call this\n * rather than reconstructing the shape inline. Inline reconstruction\n * pre-round-18 led to drift between wrappers; this consolidates it.\n *\n * @example\n * ```ts\n * import { buildGuidance } from '@astrasyncai/verification-gateway';\n *\n * try {\n * const sdk = new AstraSync({ apiKey: callerApiKey, disableEnvFallback: true });\n * // ...\n * } catch (err) {\n * if (err instanceof AuthenticationError) {\n * return buildGuidance({ origin: 'https://astrasync.ai', message: err.message });\n * }\n * throw err;\n * }\n * ```\n */\nexport function buildGuidance(params: BuildGuidanceParams): GuidanceEnvelope {\n const origin = params.origin.replace(/\\/+$/, '');\n const docsPath = params.documentationPath ?? '/docs/agent-access';\n const message = params.message ?? 'AstraSync registration requires credentials.';\n\n return {\n status: 'credentials_required',\n message,\n guidance: {\n message:\n \"Register either with an agent-scoped API key (the recommended credentialed path) OR, if you hold no credentials, via the owner's email using request_registration. Never send a password or private key over the wire.\",\n // 3.0.0: registrationUrl is the agent-actionable KEYLESS endpoint, not the\n // human dashboard `/agents/register` (an auth-walled page an agent can't\n // use). documentationUrl carries the machine-readable contract.\n registrationUrl: `${origin}/api/agents/request-registration`,\n documentationUrl: `${origin}${docsPath.startsWith('/') ? docsPath : `/${docsPath}`}`,\n steps: [\n 'Credentialed path (recommended): have your human mint an agent-scoped API key at Settings → API Keys, then re-call register_agent with apiKey set. An agent holding its own scoped kya_ key is fine and intended.',\n \"No-credentials path: call request_registration({ name, ownerEmail, ... }) with the human owner's email — the only bootstrap credential. No API key is involved.\",\n 'The owner approves via an emailed link (first time, they create an account and complete a quick verification).',\n 'Use poll_registration({ requestId }) once the owner confirms approval to retrieve the astraId.',\n 'Never transmit a password or private key over the MCP wire — those are never required.',\n ],\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/registration/errors.ts","../../src/registration/api.ts","../../src/registration/guidance.ts"],"sourcesContent":["import type { ApiErrorResponse } from './types';\n\n/** Base error class for AstraSync SDK errors. */\nexport class AstraSyncError extends Error {\n public readonly code?: string;\n public readonly statusCode: number;\n\n constructor(message: string, statusCode: number, code?: string) {\n super(message);\n this.name = 'AstraSyncError';\n this.statusCode = statusCode;\n this.code = code;\n }\n}\n\n/** Thrown when KYD verification is required before agent registration. */\nexport class KYDRequiredError extends AstraSyncError {\n public readonly kydUrl: string;\n public readonly ownerNotified: boolean;\n\n constructor(response: ApiErrorResponse) {\n const kydUrl = response.kydUrl || 'https://astrasync.ai/developer-profile';\n super(\n `KYD verification required before registering agents.\\nComplete your KYD profile at: ${kydUrl}`,\n 403,\n 'KYD_REQUIRED'\n );\n this.name = 'KYDRequiredError';\n this.kydUrl = kydUrl;\n this.ownerNotified = response.ownerNotified || false;\n }\n}\n\n/** Thrown when authentication fails. */\nexport class AuthenticationError extends AstraSyncError {\n constructor(message: string) {\n super(message, 401, 'AUTH_FAILED');\n this.name = 'AuthenticationError';\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the owner denies the\n * pending registration request. The `reason` field, when present, mirrors the\n * deny note the owner left in the dashboard.\n */\nexport class RegistrationDeniedError extends AstraSyncError {\n public readonly requestId: string;\n public readonly reason?: string;\n\n constructor(requestId: string, reason?: string) {\n super(\n `Registration request ${requestId} was denied by the account owner.${reason ? ` Reason: ${reason}` : ''}`,\n 403,\n 'REGISTRATION_DENIED'\n );\n this.name = 'RegistrationDeniedError';\n this.requestId = requestId;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the pending request\n * passes its 14-day TTL with no owner decision. The agent must re-submit.\n */\nexport class RegistrationExpiredError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Registration request ${requestId} expired before the owner approved it. Submit a new registration request.`,\n 410,\n 'REGISTRATION_EXPIRED'\n );\n this.name = 'RegistrationExpiredError';\n this.requestId = requestId;\n }\n}\n\n/**\n * Thrown by `register({ waitForApproval: true })` when the caller's local\n * `timeoutMs` elapses before the owner makes a decision. The request is still\n * live server-side — poll `pollRegistration(requestId)` to resume waiting, or\n * call `waitForApproval` again with a longer timeout.\n */\nexport class RegistrationTimeoutError extends AstraSyncError {\n public readonly requestId: string;\n\n constructor(requestId: string) {\n super(\n `Timed out waiting for owner approval of registration request ${requestId}. The request is still active server-side; poll the request to resume waiting.`,\n 408,\n 'REGISTRATION_TIMEOUT'\n );\n this.name = 'RegistrationTimeoutError';\n this.requestId = requestId;\n }\n}\n","import type {\n AstraSyncConfig,\n RegisterOptions,\n RegisterResult,\n WaitForApprovalOptions,\n PendingRegistrationResponse,\n PollRegistrationResult,\n RegistrationResponse,\n VerifyResponse,\n HealthResponse,\n ApiErrorResponse,\n AgentRecord,\n} from './types';\nimport {\n AstraSyncError,\n KYDRequiredError,\n AuthenticationError,\n RegistrationDeniedError,\n RegistrationExpiredError,\n RegistrationTimeoutError,\n} from './errors';\n\nconst DEFAULT_BASE_URL = 'https://astrasync.ai';\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));\n\n/**\n * AstraSync SDK client for registering and managing AI agents.\n *\n * @example\n * ```typescript\n * const client = new AstraSync({ apiKey: 'kya_your_api_key' });\n * const result = await client.register({\n * name: 'My Agent',\n * model: { modelName: 'gpt-4o', modelProvider: 'openai', modelType: 'llm' },\n * });\n * ```\n *\n * For staging, pass `baseUrl: 'https://staging.astrasync.ai'`.\n */\nexport class AstraSync {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly email?: string;\n private readonly password?: string;\n private readonly privateKey?: string;\n private cachedJwt?: string;\n private jwtExpiresAt?: number;\n\n constructor(config: AstraSyncConfig = {}) {\n let raw = (config.baseUrl || process.env.ASTRASYNC_API_URL || DEFAULT_BASE_URL).replace(\n /\\/+$/,\n ''\n );\n // Round-10 (O2): tolerate the verify-side convention. `GatewayConfig.apiBaseUrl`\n // is documented as `https://astrasync.ai/api` (with /api), but\n // `AstraSyncConfig.baseUrl` is documented as the bare origin. Partners\n // passing the verify-style URL to the registration client hit a 404\n // because we'd then append `/api/agents/register` → double /api. Strip\n // a trailing `/api` and warn once so the partner can fix the source.\n if (raw.toLowerCase().endsWith('/api')) {\n raw = raw.slice(0, -'/api'.length);\n if (config.baseUrl && !config.silent) {\n // eslint-disable-next-line no-console\n console.warn(\n `[AstraSync] baseUrl '${config.baseUrl}' had a trailing /api — stripped to '${raw}'. ` +\n `Pass the bare origin (e.g. 'https://astrasync.ai' or 'https://staging.astrasync.ai') ` +\n `to AstraSync(). The /api suffix is the verify-gateway (GatewayConfig.apiBaseUrl) convention.`\n );\n }\n }\n this.baseUrl = raw;\n\n // Env fallback is opt-OUT: server-side wrappers (MCP tool handlers,\n // gateway adapters) pass `disableEnvFallback: true` so a no-credentials\n // call from a user-facing flow cannot silently authenticate as the host\n // process's platform-attribution key. CLIs / scripts that legitimately\n // rely on ASTRASYNC_API_KEY keep working under the default.\n this.apiKey = config.disableEnvFallback\n ? config.apiKey\n : config.apiKey || process.env.ASTRASYNC_API_KEY;\n this.email = config.email;\n this.password = config.password;\n this.privateKey = config.privateKey;\n\n // Defense-in-depth: warn when env fallback actually fires under the\n // default (non-strict) config. Surfaces the \"I'm running as platform\"\n // foot-gun before it becomes a security bug. Gated by !silent.\n if (\n !config.apiKey &&\n !config.disableEnvFallback &&\n process.env.ASTRASYNC_API_KEY &&\n !config.silent\n ) {\n // eslint-disable-next-line no-console\n console.warn(\n '[AstraSync] No apiKey passed to constructor; using process.env.ASTRASYNC_API_KEY. ' +\n 'If this code wraps user-facing flows (e.g. MCP tool handlers), pass ' +\n 'disableEnvFallback: true to prevent ambient credentials from impersonating callers. ' +\n 'See https://astrasync.ai/docs/agent-access#disableenvfallback for details.'\n );\n }\n\n if (!this.apiKey && !this.email) {\n throw new AuthenticationError(\n 'Authentication required. Provide apiKey, or email+password. ' +\n 'Set ASTRASYNC_API_KEY env var or pass config to constructor.'\n );\n }\n\n if (this.email && !this.password) {\n throw new AuthenticationError('Password is required when using email authentication.');\n }\n }\n\n /**\n * Register a new AI agent on the AstraSync KYA Platform.\n *\n * The backend response depends on auth context:\n * - **Crypto-keypair signed** (`privateKey` configured): synchronous 201,\n * returns `{ status: 'active', agent }`.\n * - **API-key only** (no signature): 202 pending, returns\n * `{ status: 'pending_approval', requestId, pollUrl, expiresAt }`. The\n * owner is notified by email and a dashboard alert is emitted; the agent\n * becomes active only after the owner approves.\n *\n * Blocking mode: pass `{ waitForApproval: true }` to have the SDK poll the\n * request until it resolves, then return the live agent record. The promise\n * rejects with `RegistrationDeniedError`, `RegistrationExpiredError`, or\n * `RegistrationTimeoutError` on the corresponding terminal states.\n *\n * @example Non-blocking (default — best for serverless / scheduled agents):\n * ```typescript\n * const result = await sdk.register({ name, pdlss });\n * if (result.status === 'pending_approval') {\n * storeRequestId(result.requestId);\n * return; // function exits; resume later via pollRegistration()\n * }\n * ```\n *\n * @example Blocking (best for long-running services + CLI):\n * ```typescript\n * const agent = await sdk.register({\n * name, pdlss, waitForApproval: true, timeoutMs: 600_000,\n * onPending: ({ ageMs }) => console.log(`waiting ${ageMs}ms`),\n * });\n * ```\n */\n async register(\n options: RegisterOptions & WaitForApprovalOptions\n ): Promise<RegisterResult | AgentRecord> {\n const body: Record<string, unknown> = {\n name: options.name,\n ...(options.description && { description: options.description }),\n ...(options.agentType && { agentType: options.agentType }),\n ...(options.apiEndpoint && { apiEndpoint: options.apiEndpoint }),\n ...(options.model && { model: options.model }),\n ...(options.framework && { framework: options.framework }),\n ...(options.protocols && { protocols: options.protocols }),\n ...(options.metadata && { metadata: options.metadata }),\n ...(options.pdlss && { pdlss: options.pdlss }),\n };\n\n const { status, body: raw } = await this.requestWithStatus<\n RegistrationResponse | PendingRegistrationResponse\n >('POST', '/api/agents/register', body);\n\n if (status === 201) {\n const activeBody = raw as RegistrationResponse;\n const active: RegisterResult = {\n status: 'active',\n agent: activeBody.data.agent,\n // Round-12 (F16): pass backend advisories through verbatim.\n // Pre-fix the SDK whitelisted five fields and silently dropped\n // `warnings`, which left partners with no signal that\n // `no_callback_endpoint` (or future advisories) had fired.\n ...(activeBody.warnings && { warnings: activeBody.warnings }),\n };\n return active;\n }\n\n // 202 Accepted — owner approval required.\n const pendingBody = raw as PendingRegistrationResponse;\n const pending: RegisterResult = {\n status: 'pending_approval',\n requestId: pendingBody.requestId,\n expiresAt: pendingBody.expiresAt,\n pollUrl: pendingBody.pollUrl,\n message: pendingBody.message,\n // Round-12 (F16): same pass-through on the pending path.\n ...(pendingBody.warnings && { warnings: pendingBody.warnings }),\n };\n\n if (!options.waitForApproval) return pending;\n\n return this.waitForApproval(pendingBody.requestId, options);\n }\n\n /**\n * Poll the current state of a pending-approval registration request.\n *\n * Useful for caller-driven polling when `waitForApproval: false` (the\n * default). The endpoint is unauthenticated — pass the `requestId` that\n * was returned from the 202 response.\n *\n * @returns `state: 'pending'` while awaiting; `'approved'` carries the\n * minted agent in `agent`; `'denied'` may carry the owner's\n * `reason`; `'expired'` is terminal after 14 days.\n */\n async pollRegistration(requestId: string): Promise<PollRegistrationResult> {\n const url = `${this.baseUrl}/api/agents/request-registration/${requestId}`;\n const res = await fetch(url, { headers: { Accept: 'application/json' } });\n if (!res.ok) {\n const errBody = (await res.json().catch(() => ({}))) as ApiErrorResponse;\n throw new AstraSyncError(\n errBody.error || `pollRegistration failed: ${res.status}`,\n res.status,\n errBody.code\n );\n }\n return (await res.json()) as PollRegistrationResult;\n }\n\n /**\n * Block until a pending registration request resolves to a terminal state.\n * Resolves to the live `AgentRecord` on approval; rejects with the matching\n * Registration*Error on deny/expire/timeout. Usually called via\n * `register({ waitForApproval: true })`, but exposed for callers that want\n * to fire-and-forget the initial register call and resume waiting later\n * (e.g. after restoring a stored `requestId` on cold start).\n */\n async waitForApproval(\n requestId: string,\n options: WaitForApprovalOptions = {}\n ): Promise<AgentRecord> {\n const timeoutMs = options.timeoutMs ?? 10 * 60 * 1000;\n const pollIntervalMs = options.pollIntervalMs ?? 5_000;\n const start = Date.now();\n const deadline = start + timeoutMs;\n\n while (Date.now() < deadline) {\n const result = await this.pollRegistration(requestId);\n const ageMs = Date.now() - start;\n options.onPending?.({ requestId, ageMs });\n\n if (result.state === 'approved') {\n if (!result.agent) {\n throw new AstraSyncError(\n `Registration ${requestId} reported approved but no agent payload returned.`,\n 500\n );\n }\n return result.agent;\n }\n if (result.state === 'denied') {\n throw new RegistrationDeniedError(requestId, result.reason);\n }\n if (result.state === 'expired') {\n throw new RegistrationExpiredError(requestId);\n }\n await sleep(pollIntervalMs);\n }\n throw new RegistrationTimeoutError(requestId);\n }\n\n /**\n * Look up an agent's public profile by ASTRA ID or UUID.\n */\n async verify(agentId: string): Promise<VerifyResponse> {\n return this.request<VerifyResponse>('GET', `/api/agents/verify/${agentId}`);\n }\n\n /**\n * Check API health.\n */\n async health(): Promise<HealthResponse> {\n const res = await fetch(`${this.baseUrl}/api/health/`);\n if (!res.ok) {\n throw new AstraSyncError(`Health check failed: ${res.status}`, res.status);\n }\n return res.json() as Promise<HealthResponse>;\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private async request<T>(method: string, endpoint: string, body?: unknown): Promise<T> {\n const { body: parsed } = await this.requestWithStatus<T>(method, endpoint, body);\n return parsed;\n }\n\n /**\n * Variant of {@link request} that also returns the HTTP status code, so\n * callers can branch on 201 vs 202 (or other success codes) without losing\n * type information about the response body.\n */\n private async requestWithStatus<T>(\n method: string,\n endpoint: string,\n body?: unknown\n ): Promise<{ status: number; body: T }> {\n const url = `${this.baseUrl}${endpoint}`;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // Set auth header\n const token = await this.getAuthToken();\n headers['Authorization'] = `Bearer ${token}`;\n\n // Sign request if private key is configured\n if (this.privateKey) {\n const signature = await this.signRequest(method, endpoint, body || {});\n headers['X-AstraSync-Signature'] = signature;\n }\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!res.ok) {\n const errorBody = (await res\n .json()\n .catch(() => ({ error: res.statusText }))) as ApiErrorResponse;\n\n // Handle KYD_REQUIRED specifically\n if (res.status === 403 && errorBody.code === 'KYD_REQUIRED') {\n throw new KYDRequiredError(errorBody);\n }\n\n throw new AstraSyncError(\n errorBody.error || `Request failed: ${res.status}`,\n res.status,\n errorBody.code\n );\n }\n\n return { status: res.status, body: (await res.json()) as T };\n }\n\n private async getAuthToken(): Promise<string> {\n // API key auth — use directly\n if (this.apiKey) {\n return this.apiKey;\n }\n\n // Email+password — login and cache JWT\n if (this.cachedJwt && this.jwtExpiresAt && Date.now() < this.jwtExpiresAt) {\n return this.cachedJwt;\n }\n\n const res = await fetch(`${this.baseUrl}/api/auth/login`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: this.email, password: this.password }),\n });\n\n if (!res.ok) {\n const errorBody = (await res.json().catch(() => ({}))) as Record<string, unknown>;\n throw new AuthenticationError(\n (errorBody.message as string) || (errorBody.error as string) || 'Login failed'\n );\n }\n\n const data = (await res.json()) as { data: { token: string } };\n this.cachedJwt = data.data.token;\n // Cache for 6 days (tokens expire in 7)\n this.jwtExpiresAt = Date.now() + 6 * 24 * 60 * 60 * 1000;\n\n return this.cachedJwt;\n }\n\n /**\n * Sign a request using secp256k1 (ethers.js).\n * Canonical message format: METHOD:ENDPOINT:SORTED_JSON_BODY\n * Must match apps/backend/src/services/signature-verify.service.ts exactly.\n */\n private async signRequest(method: string, endpoint: string, body: unknown): Promise<string> {\n const { Wallet } = await import('ethers');\n const sorted = this.sortObjectKeys(body);\n const canonical = `${method}:${endpoint}:${JSON.stringify(sorted)}`;\n const wallet = new Wallet(this.privateKey!);\n return wallet.signMessage(canonical);\n }\n\n /** Recursively sort object keys for canonical JSON representation. */\n private sortObjectKeys(obj: unknown): unknown {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n if (Array.isArray(obj)) {\n return obj.map((item) => this.sortObjectKeys(item));\n }\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = this.sortObjectKeys((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n }\n}\n","/**\n * Guidance envelope for credentials-required cases.\n *\n * A single shared source of truth so partners writing their own wrappers\n * (custom MCP servers, Express middleware around registration, gateway\n * adapters) consume a single source of truth instead of re-implementing the\n * five-step boilerplate.\n *\n * The envelope is what an agent (or wrapping MCP client) sees when it calls\n * `register_agent` with no AstraSync credentials. It tells the calling agent\n * (a) what failed (`status: 'credentials_required'`), (b) the keyless\n * registration ACTION endpoint (`registrationUrl` — POST-able, not the human\n * dashboard), (c) where the relevant docs are (`documentationUrl`), and (d) the\n * ordered next-actions (`steps`).\n */\n\nexport interface GuidanceEnvelope {\n /**\n * Single-literal today. Expand to a union (e.g.\n * `'credentials_required' | 'kyd_required' | …`) when a second guidance\n * status emerges. Don't pre-emptively widen — the literal pins the shape.\n */\n status: 'credentials_required';\n message: string;\n guidance: {\n message: string;\n registrationUrl: string;\n documentationUrl: string;\n steps: string[];\n };\n}\n\nexport interface BuildGuidanceParams {\n /**\n * Bare origin of the AstraSync deployment the caller should register at,\n * e.g. `https://astrasync.ai` or `https://staging.astrasync.ai`. No\n * trailing slash, no `/api` suffix — registrationUrl and documentationUrl\n * are templated relative to this origin.\n */\n origin: string;\n /**\n * Overrides the top-level `message` (the short summary the agent sees).\n * Defaults to the error message from the SDK's `AuthenticationError`\n * when present, else a generic \"credentials required\" line.\n */\n message?: string;\n /**\n * Overrides the documentation path. Defaults to `/docs/agent-access`.\n */\n documentationPath?: string;\n}\n\n/**\n * Build the credentials-required guidance envelope.\n *\n * This is the canonical builder — MCP wrappers (`agent-registration.ts`),\n * Express middleware in custom integrations, and any other partner-side\n * wrapper that needs to surface \"you need to register\" should call this\n * rather than reconstructing the shape inline. Inline reconstruction\n * historically led to drift between wrappers; this consolidates it.\n *\n * @example\n * ```ts\n * import { buildGuidance } from '@astrasyncai/verification-gateway';\n *\n * try {\n * const sdk = new AstraSync({ apiKey: callerApiKey, disableEnvFallback: true });\n * // ...\n * } catch (err) {\n * if (err instanceof AuthenticationError) {\n * return buildGuidance({ origin: 'https://astrasync.ai', message: err.message });\n * }\n * throw err;\n * }\n * ```\n */\nexport function buildGuidance(params: BuildGuidanceParams): GuidanceEnvelope {\n const origin = params.origin.replace(/\\/+$/, '');\n const docsPath = params.documentationPath ?? '/docs/agent-access';\n const message = params.message ?? 'AstraSync registration requires credentials.';\n\n return {\n status: 'credentials_required',\n message,\n guidance: {\n message:\n \"Register either with an agent-scoped API key (the recommended credentialed path) OR, if you hold no credentials, via the owner's email using request_registration. Never send a password or private key over the wire.\",\n // 3.0.0: registrationUrl is the agent-actionable KEYLESS endpoint, not the\n // human dashboard `/agents/register` (an auth-walled page an agent can't\n // use). documentationUrl carries the machine-readable contract.\n registrationUrl: `${origin}/api/agents/request-registration`,\n documentationUrl: `${origin}${docsPath.startsWith('/') ? docsPath : `/${docsPath}`}`,\n steps: [\n 'Credentialed path (recommended): have your human mint an agent-scoped API key at Settings → API Keys, then re-call register_agent with apiKey set. An agent holding its own scoped kya_ key is fine and intended.',\n \"No-credentials path: call request_registration({ name, ownerEmail, ... }) with the human owner's email — the only bootstrap credential. No API key is involved.\",\n 'The owner approves via an emailed link (first time, they create an account and complete a quick verification).',\n 'Use poll_registration({ requestId }) once the owner confirms approval to retrieve the astraId.',\n 'Never transmit a password or private key over the MCP wire — those are never required.',\n ],\n },\n };\n}\n"],"mappings":"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","names":[]}