@twin3-ai/agent-id 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@twin3-ai/agent-id",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Domain-bound Agent identity, AEO evidence, and website-Agent integration SDK.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cis2042/agent-id.git"
10
+ },
7
11
  "files": [
8
12
  "bin",
9
13
  "*.js"
@@ -16,7 +20,7 @@
16
20
  "npm": ">=10"
17
21
  },
18
22
  "scripts": {
19
- "prepublishOnly": "node --check bin/agent-id.js && node --check installer.js && node --check enterprise-identity.js && node --check release-verifier.js && node --check production-preflight.js && node --check static-edge-adapter.js && node --check optimization-loop.js && node --check artifact-bundle.js"
23
+ "prepublishOnly": "node --check bin/agent-id.js && node --check bin/agent-id-cloudflare-a1.js && node --check bin/agent-id-b1-sync.js && node --check installer.js && node --check enterprise-identity.js && node --check domain-proof.js && node --check release-verifier.js && node --check production-preflight.js && node --check static-edge-adapter.js && node --check edge-html-injection.js && node --check optimization-loop.js && node --check artifact-bundle.js && node --check cloudflare-a1-sync.js && node --check b1-sync.js"
20
24
  },
21
25
  "main": "site-agent.js",
22
26
  "exports": {
@@ -31,10 +35,13 @@
31
35
  "./local-policy": "./local-policy.js",
32
36
  "./task-executor": "./task-executor.js",
33
37
  "./telemetry-collector": "./telemetry-collector.js",
38
+ "./cloudflare-a1-sync": "./cloudflare-a1-sync.js",
39
+ "./b1-sync": "./b1-sync.js",
34
40
  "./trust-verifier": "./trust-verifier.js",
35
41
  "./repository-connector": "./repository-connector.js",
36
42
  "./cloudflare-worker-adapter": "./cloudflare-worker-adapter.js",
37
43
  "./static-edge-adapter": "./static-edge-adapter.js",
44
+ "./edge-html-injection": "./edge-html-injection.js",
38
45
  "./optimization-loop": "./optimization-loop.js",
39
46
  "./artifact-bundle": "./artifact-bundle.js",
40
47
  "./release-verifier": "./release-verifier.js",
@@ -42,6 +49,8 @@
42
49
  },
43
50
  "bin": {
44
51
  "agent-id": "bin/agent-id.js",
45
- "agent-id-sync": "bin/agent-id-sync.js"
52
+ "agent-id-sync": "bin/agent-id-sync.js",
53
+ "agent-id-cloudflare-a1": "bin/agent-id-cloudflare-a1.js",
54
+ "agent-id-b1-sync": "bin/agent-id-b1-sync.js"
46
55
  }
47
56
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  const net = require("node:net");
4
- const { verifyReleaseManifest } = require("./release-verifier.js");
4
+ const { verifyReleaseManifest, verifyProductionReleaseReceipt, sha256 } = require("./release-verifier.js");
5
5
 
6
6
  const PACKAGE_NAME = "@twin3-ai/agent-id";
7
7
 
@@ -50,12 +50,15 @@ async function runProductionPreflight({ endpoint, packageVersion, expectedIssuer
50
50
 
51
51
  const packageId = encodeURIComponent(PACKAGE_NAME);
52
52
  const version = String(packageVersion || "");
53
- const [manifestResponse, issuerResponse, registryResponse, healthResponse, cardResponse] = await Promise.all([
53
+ const [manifestResponse, receiptResponse, issuerResponse, registryResponse, healthResponse, cardResponse, installResponse, openapiResponse] = await Promise.all([
54
54
  fetchJson(fetchImpl, `${origin}/.well-known/agent-id-release.json`),
55
+ fetchJson(fetchImpl, `${origin}/.well-known/agent-id-release-receipt.json`),
55
56
  fetchJson(fetchImpl, `${origin}/.well-known/agentx-issuer-key.json`),
56
57
  fetchJson(fetchImpl, `https://registry.npmjs.org/${packageId}/${encodeURIComponent(version)}`),
57
58
  fetchJson(fetchImpl, `${origin}/agent/health.json`),
58
59
  fetchJson(fetchImpl, `${origin}/.well-known/agent-card.json`),
60
+ fetchJson(fetchImpl, `${origin}/.well-known/xagent-install.json`),
61
+ fetchJson(fetchImpl, `${origin}/agent/openapi.json`),
59
62
  ]);
60
63
 
61
64
  const manifest = manifestResponse.body;
@@ -79,13 +82,32 @@ async function runProductionPreflight({ endpoint, packageVersion, expectedIssuer
79
82
  && registry.dist.integrity === manifest?.package?.integrity;
80
83
  const health = healthResponse.ok && healthResponse.body && healthResponse.body.ok !== false;
81
84
  const agentCard = cardResponse.ok && typeof cardResponse.body?.name === "string" && cardResponse.body.name.length > 0;
82
- const apiReachable = [manifestResponse, issuerResponse, healthResponse, cardResponse]
85
+ const installManifest = installResponse.ok && installResponse.body?.schema === "xagent-install-manifest-v1";
86
+ const openapi = openapiResponse.ok && openapiResponse.body?.openapi === "3.1.0";
87
+ const productionReceipt = receiptResponse.ok && receiptResponse.body
88
+ ? verifyProductionReleaseReceipt({
89
+ receipt: receiptResponse.body,
90
+ issuerDescriptor: issuerResponse.body,
91
+ releaseManifest: manifest,
92
+ expectedOrigin: origin,
93
+ packageVersion: version,
94
+ expectedIssuerKeySha256,
95
+ })
96
+ : { ok: false, signature_verified: false, blockers: Array.isArray(receiptResponse.body?.blockers) ? receiptResponse.body.blockers : [] };
97
+ const artifactBindings = productionReceipt.ok
98
+ && receiptResponse.body?.artifacts?.agent_card?.sha256 === sha256(cardResponse.body)
99
+ && receiptResponse.body?.artifacts?.install_manifest?.sha256 === sha256(installResponse.body)
100
+ && receiptResponse.body?.artifacts?.openapi?.sha256 === sha256(openapiResponse.body);
101
+ const apiReachable = [manifestResponse, receiptResponse, issuerResponse, healthResponse, cardResponse, installResponse, openapiResponse]
83
102
  .some(response => Number(response.status) > 0);
84
103
  const checks = {
85
104
  stable_api_origin_format: true,
86
105
  api_https_reachable: apiReachable,
87
106
  stable_api_origin: apiReachable,
88
107
  release_manifest_http: manifestResponse.ok,
108
+ production_receipt_http: receiptResponse.ok,
109
+ production_receipt_signature: productionReceipt.signature_verified === true,
110
+ production_release_binding: productionReceipt.ok === true && artifactBindings,
89
111
  issuer_descriptor_http: issuerResponse.ok,
90
112
  release_signature: release.signature_verified === true,
91
113
  release_contract: release.ok === true,
@@ -93,15 +115,21 @@ async function runProductionPreflight({ endpoint, packageVersion, expectedIssuer
93
115
  registry_integrity: registryIntegrity,
94
116
  health_endpoint: health,
95
117
  agent_card: agentCard,
118
+ install_manifest: installManifest,
119
+ openapi,
96
120
  };
97
- const blockers = [...release.blockers];
121
+ const blockers = [...release.blockers, ...productionReceipt.blockers];
98
122
  if (!apiReachable) blockers.push("stable_api_unreachable");
99
123
  if (apiReachable && !manifestResponse.ok) blockers.push("release_manifest_unavailable");
124
+ if (apiReachable && !receiptResponse.ok) blockers.push("production_release_receipt_unavailable");
100
125
  if (apiReachable && !issuerResponse.ok) blockers.push("issuer_descriptor_unavailable");
101
126
  if (!registryIdentity) blockers.push("npm_package_unpublished_or_version_mismatch");
102
127
  if (manifestResponse.ok && registryIdentity && !registryIntegrity) blockers.push("npm_integrity_mismatch");
103
128
  if (apiReachable && !health) blockers.push("health_endpoint_unavailable");
104
129
  if (apiReachable && !agentCard) blockers.push("agent_card_unavailable");
130
+ if (apiReachable && !installManifest) blockers.push("install_manifest_unavailable");
131
+ if (apiReachable && !openapi) blockers.push("openapi_unavailable");
132
+ if (productionReceipt.ok && !artifactBindings) blockers.push("production_artifact_hash_mismatch");
105
133
 
106
134
  return {
107
135
  ok: blockers.length === 0,
@@ -116,6 +144,9 @@ async function runProductionPreflight({ endpoint, packageVersion, expectedIssuer
116
144
  published_at: manifest?.published_at || "",
117
145
  integrity: manifest?.package?.integrity || "",
118
146
  signature_verified: release.signature_verified === true,
147
+ production_receipt_signature_verified: productionReceipt.signature_verified === true,
148
+ production_revision: receiptResponse.body?.deployment?.revision || "",
149
+ production_image_digest: receiptResponse.body?.deployment?.image_digest || "",
119
150
  },
120
151
  };
121
152
  }
@@ -3,6 +3,7 @@
3
3
  const crypto = require("node:crypto");
4
4
 
5
5
  const SCHEMA = "agentx-release-manifest-v1";
6
+ const PRODUCTION_RECEIPT_SCHEMA = "agentx-production-release-receipt-v1";
6
7
  const PACKAGE_NAME = "@twin3-ai/agent-id";
7
8
 
8
9
  function stableValue(value) {
@@ -80,4 +81,48 @@ function verifyReleaseManifest({ manifest, issuerDescriptor, expectedOrigin, pac
80
81
  };
81
82
  }
82
83
 
83
- module.exports = { verifyReleaseManifest };
84
+ function verifyProductionReleaseReceipt({ receipt, issuerDescriptor, releaseManifest, expectedOrigin, packageVersion, expectedIssuerKeySha256 }) {
85
+ const blockers = [];
86
+ const expected = normalizedOrigin(expectedOrigin);
87
+ if (!receipt || receipt.schema !== PRODUCTION_RECEIPT_SCHEMA) blockers.push("invalid_production_receipt_schema");
88
+ if (!receipt || receipt.ready !== true) blockers.push(...(Array.isArray(receipt?.blockers) ? receipt.blockers : ["production_receipt_not_ready"]));
89
+ if (!expected || normalizedOrigin(receipt?.api_origin) !== expected) blockers.push("production_receipt_origin_mismatch");
90
+ if (receipt?.package?.name !== PACKAGE_NAME || receipt?.package?.version !== String(packageVersion || "")) blockers.push("production_receipt_package_mismatch");
91
+ if (receipt?.package?.integrity !== releaseManifest?.package?.integrity) blockers.push("production_receipt_integrity_mismatch");
92
+ if (receipt?.source?.release_commit !== releaseManifest?.release_commit) blockers.push("production_receipt_commit_mismatch");
93
+ if (receipt?.source?.release_manifest_sha256 !== releaseManifest?.content_hash) blockers.push("production_receipt_manifest_hash_mismatch");
94
+ if (!/^sha256:[0-9a-f]{64}$/.test(String(receipt?.deployment?.image_digest || ""))) blockers.push("production_receipt_image_digest_missing");
95
+ if (!String(receipt?.deployment?.revision || "").startsWith(`${String(receipt?.deployment?.service || "")}-`)) blockers.push("production_receipt_revision_mismatch");
96
+ for (const name of ["agent_card", "install_manifest", "openapi"]) {
97
+ if (!/^sha256:[0-9a-f]{64}$/.test(String(receipt?.artifacts?.[name]?.sha256 || ""))) blockers.push(`production_receipt_${name}_hash_missing`);
98
+ }
99
+
100
+ const proof = receipt && typeof receipt.proof === "object" ? receipt.proof : {};
101
+ const unsigned = receipt && typeof receipt === "object"
102
+ ? Object.fromEntries(Object.entries(receipt).filter(([key]) => !["proof", "content_hash"].includes(key)))
103
+ : {};
104
+ if (receipt?.content_hash !== sha256(unsigned)) blockers.push("production_receipt_content_hash_mismatch");
105
+ if (proof.verification_method !== `${expected}/.well-known/agentx-issuer-key.json#ed25519-2026`) blockers.push("production_receipt_verification_method_mismatch");
106
+
107
+ let signatureVerified = false;
108
+ const publicKey = String(issuerDescriptor?.public_key_pem || "");
109
+ if (!publicKey) {
110
+ blockers.push("issuer_public_key_missing");
111
+ } else {
112
+ const issuerKeySha256 = `sha256:${crypto.createHash("sha256").update(publicKey).digest("hex")}`;
113
+ if (!expectedIssuerKeySha256 || issuerKeySha256 !== expectedIssuerKeySha256) blockers.push("issuer_key_pin_mismatch");
114
+ try {
115
+ signatureVerified = crypto.verify(null, Buffer.from(canonical(unsigned)), crypto.createPublicKey(publicKey), decodeProof(proof.proof_value));
116
+ } catch (_error) {
117
+ signatureVerified = false;
118
+ }
119
+ if (!signatureVerified) blockers.push("production_receipt_signature_invalid");
120
+ }
121
+ return {
122
+ ok: blockers.length === 0,
123
+ signature_verified: signatureVerified,
124
+ blockers: [...new Set(blockers)],
125
+ };
126
+ }
127
+
128
+ module.exports = { verifyReleaseManifest, verifyProductionReleaseReceipt, canonical, sha256 };
@@ -11,8 +11,22 @@ const AUTO_MANAGED_PATHS = new Set([
11
11
  ".well-known/aeo-agent.json",
12
12
  ".well-known/agent-knowledge.json"
13
13
  ]);
14
+ // A missing sitemap may be created, but an existing customer sitemap is never
15
+ // replaced by the generic connector. Replacing one could silently remove product,
16
+ // locale, image, or news URLs that this service has not observed.
17
+ const CREATE_ONLY_PATHS = new Set(["sitemap.xml"]);
18
+ // A full robots.txt rewrite stays owner-gated: a bad directive can delist an entire
19
+ // site. `buildRobotsAdditivePatch` below is the only supported automated path, and it
20
+ // can append AI crawler directives without ever touching an existing line.
14
21
  const REVIEW_ONLY_PATHS = new Set(["robots.txt"]);
15
- const ALLOWED_PATHS = AUTO_MANAGED_PATHS;
22
+ const ALLOWED_PATHS = new Set([...AUTO_MANAGED_PATHS, ...CREATE_ONLY_PATHS]);
23
+
24
+ const AI_CRAWLER_BLOCK_MARKER = "# agent-id: AI crawler directives (additive, generated)";
25
+ const KNOWN_AI_AGENTS = new Set([
26
+ "GPTBot", "OAI-SearchBot", "ChatGPT-User", "ClaudeBot", "Claude-Web",
27
+ "PerplexityBot", "Google-Extended", "Applebot-Extended", "CCBot",
28
+ "Bytespider", "meta-externalagent", "Amazonbot", "cohere-ai"
29
+ ]);
16
30
  const SECRET_PATTERNS = [
17
31
  /\bak_aeo_[A-Za-z0-9_-]{6,}\b/,
18
32
  /\bav_[A-Za-z0-9_-]{8,}\b/,
@@ -94,6 +108,9 @@ async function buildRepositoryPatch({ repositoryRoot = process.cwd(), changes =
94
108
  const absolute = await safeAbsolute(root, relative);
95
109
  const before = await readText(absolute);
96
110
  const after = change.content;
111
+ if (CREATE_ONLY_PATHS.has(relative) && before != null && before !== after) {
112
+ throw connectorError("CONNECTOR_CREATE_ONLY_PATH_EXISTS", `Existing customer file cannot be replaced automatically: ${relative}`);
113
+ }
97
114
  const beforeHash = before == null ? "" : hashText(before);
98
115
  const afterHash = hashText(after);
99
116
  normalized.push({
@@ -153,10 +170,27 @@ function deploymentChanges({ implementationBundle, knowledgePack } = {}) {
153
170
  }
154
171
 
155
172
  async function buildImplementationPatch({ repositoryRoot = process.cwd(), implementationBundle, knowledgePack } = {}) {
156
- return buildRepositoryPatch({
157
- repositoryRoot,
158
- changes: deploymentChanges({ implementationBundle, knowledgePack })
159
- });
173
+ const root = rootPath(repositoryRoot);
174
+ const changes = deploymentChanges({ implementationBundle, knowledgePack });
175
+ const safeChanges = [];
176
+ const skippedChanges = [];
177
+ for (const change of changes) {
178
+ if (CREATE_ONLY_PATHS.has(change.path)) {
179
+ const current = await readText(await safeAbsolute(root, change.path));
180
+ if (current != null && current !== change.content) {
181
+ skippedChanges.push({
182
+ path: change.path,
183
+ status: "requires_customer_review",
184
+ reason: "existing_customer_file_preserved",
185
+ current_sha256: hashText(current)
186
+ });
187
+ continue;
188
+ }
189
+ }
190
+ safeChanges.push(change);
191
+ }
192
+ const plan = await buildRepositoryPatch({ repositoryRoot: root, changes: safeChanges });
193
+ return { ...plan, skipped_changes: skippedChanges };
160
194
  }
161
195
 
162
196
  async function buildVerifiedArtifactPatch({ repositoryRoot = process.cwd(), artifactBundle, issuerPublicKey, expectedSubject, now } = {}) {
@@ -276,10 +310,94 @@ async function rollbackRepositoryPatch(receiptOrRoot, { approved = false } = {})
276
310
  return { schema: "agentx-repository-rollback-receipt-v1", connector: "repository_patch", plan_id: receipt.plan_id || "", artifact_bundle_hash: String(receipt.artifact_bundle_hash || ""), rolled_back: true, files: restored };
277
311
  }
278
312
 
313
+ /**
314
+ * Append AI crawler directives to robots.txt without ever removing or rewriting an
315
+ * existing line.
316
+ *
317
+ * A full robots.txt rewrite remains review-only because a single bad `Disallow: /`
318
+ * can delist an entire site. This additive-only mode is narrow enough to automate:
319
+ * it appends a clearly marked block, refuses any removal, refuses a global disallow,
320
+ * and is idempotent so repeated runs converge on the same file.
321
+ */
322
+ function buildRobotsAdditivePatch({ current = "", allowAgents = [], removeLines = [], disallowAll = false } = {}) {
323
+ if (Array.isArray(removeLines) && removeLines.length) {
324
+ throw connectorError("ROBOTS_REMOVAL_FORBIDDEN", "Additive robots mode cannot remove or rewrite existing directives.");
325
+ }
326
+ if (disallowAll === true) {
327
+ throw connectorError("ROBOTS_GLOBAL_DISALLOW_FORBIDDEN", "Additive robots mode refuses a global disallow directive.");
328
+ }
329
+ const before = typeof current === "string" ? current : "";
330
+ assertSafeContent(before);
331
+
332
+ const agents = [];
333
+ const seen = new Set();
334
+ for (const value of Array.isArray(allowAgents) ? allowAgents : [allowAgents]) {
335
+ const agent = String(value || "").trim();
336
+ if (!agent || seen.has(agent)) continue;
337
+ if (agent === "*") {
338
+ throw connectorError("ROBOTS_GLOBAL_DISALLOW_FORBIDDEN", "Additive robots mode refuses wildcard agent directives.");
339
+ }
340
+ if (!KNOWN_AI_AGENTS.has(agent)) {
341
+ throw connectorError("ROBOTS_AGENT_NOT_RECOGNIZED", `Agent is not a recognized AI crawler: ${agent}`);
342
+ }
343
+ seen.add(agent);
344
+ agents.push(agent);
345
+ }
346
+ if (!agents.length) throw connectorError("ROBOTS_AGENTS_REQUIRED", "At least one recognized AI crawler is required.");
347
+
348
+ const existingAgents = new Set(
349
+ (before.match(/^\s*User-agent:\s*(.+)$/gim) || []).map((line) => line.split(":").slice(1).join(":").trim())
350
+ );
351
+ const pending = agents.filter((agent) => !existingAgents.has(agent));
352
+
353
+ let after = before;
354
+ const appendedLines = [];
355
+ if (pending.length) {
356
+ const block = [before.endsWith("\n") || before === "" ? "" : "\n", "\n", `${AI_CRAWLER_BLOCK_MARKER}\n`];
357
+ for (const agent of pending) {
358
+ block.push(`User-agent: ${agent}\n`, "Allow: /\n");
359
+ appendedLines.push(`User-agent: ${agent}`, "Allow: /");
360
+ }
361
+ after = before + block.join("");
362
+ assertSafeContent(after);
363
+ }
364
+
365
+ const beforeLines = before.split("\n");
366
+ const afterLines = after.split("\n");
367
+ const removed = beforeLines.filter((line) => line.trim() && !afterLines.includes(line));
368
+ if (removed.length) {
369
+ throw connectorError("ROBOTS_REMOVAL_FORBIDDEN", "Additive robots mode must preserve every existing line.");
370
+ }
371
+
372
+ return {
373
+ schema: "agentx-robots-additive-patch-v1",
374
+ path: "robots.txt",
375
+ mode: "additive_only",
376
+ approval_required: true,
377
+ before,
378
+ after,
379
+ before_sha256: before === "" ? "" : hashText(before),
380
+ after_sha256: hashText(after),
381
+ appended_lines: appendedLines,
382
+ removed_lines: [],
383
+ agents: pending,
384
+ closes_checks: ["aeo_ai_crawler_policy", "seo_robots"],
385
+ non_claims: [
386
+ "never_removes_or_rewrites_existing_directives",
387
+ "never_emits_a_global_disallow",
388
+ "does_not_guarantee_ai_crawler_compliance"
389
+ ]
390
+ };
391
+ }
392
+
279
393
  module.exports = {
280
394
  ALLOWED_PATHS,
281
395
  AUTO_MANAGED_PATHS,
396
+ CREATE_ONLY_PATHS,
282
397
  REVIEW_ONLY_PATHS,
398
+ AI_CRAWLER_BLOCK_MARKER,
399
+ KNOWN_AI_AGENTS,
400
+ buildRobotsAdditivePatch,
283
401
  buildRepositoryPatch,
284
402
  buildImplementationPatch,
285
403
  buildVerifiedArtifactPatch,
package/site-agent.js CHANGED
@@ -330,6 +330,40 @@ function createSiteAgentClient(options = {}) {
330
330
  return data;
331
331
  }
332
332
 
333
+ async function permissionsRequest(method, payload = {}) {
334
+ const key = payload.agent_key || payload.agentKey || agentKey;
335
+ const target = payload.url || payload.website || payload.host || "";
336
+ if (!key) {
337
+ throw siteAgentError(
338
+ "SITE_AGENT_KEY_REQUIRED",
339
+ "A Site Agent Key is required to manage this website agent's permissions."
340
+ );
341
+ }
342
+ const body = { ...payload };
343
+ delete body.agent_key;
344
+ delete body.agentKey;
345
+ const suffix = method === "GET" ? `?url=${encodeURIComponent(target)}` : "";
346
+ const request = {
347
+ method,
348
+ headers: {
349
+ "authorization": `Bearer ${key}`,
350
+ ...(method === "POST" ? { "content-type": "application/json" } : {})
351
+ },
352
+ ...(method === "POST" ? { body: JSON.stringify(body) } : {})
353
+ };
354
+ const res = await fetchWithRetry(endpoint + "/api/site_agents/permissions" + suffix, request);
355
+ const data = await readJsonResponse(res);
356
+ if (!res.ok || data.ok === false) {
357
+ const error = siteAgentError(
358
+ data.error || "SITE_AGENT_PERMISSION_FAILED",
359
+ data.message || data.error || `Site Agent permission request failed with HTTP ${res.status}`
360
+ );
361
+ error.status = res.status;
362
+ throw error;
363
+ }
364
+ return data;
365
+ }
366
+
333
367
  return Object.freeze({
334
368
  endpoint,
335
369
  policy: SITE_AGENT_KEY_POLICY,
@@ -349,6 +383,12 @@ function createSiteAgentClient(options = {}) {
349
383
  delete body.agentKey;
350
384
  return publicPost("/api/site_agents/verify", body);
351
385
  },
386
+ permissions(payload = {}) {
387
+ return permissionsRequest("GET", payload);
388
+ },
389
+ updatePermissions(payload = {}) {
390
+ return permissionsRequest("POST", payload);
391
+ },
352
392
  insightsFeed(payload = {}) {
353
393
  return call("insights_feed", payload);
354
394
  },
@@ -362,7 +402,7 @@ function createSiteAgentClient(options = {}) {
362
402
  ts: payload.ts == null ? Math.floor(Date.now() / 1000) : payload.ts,
363
403
  data: {
364
404
  runtime: payload.runtime || "server_side_site_agent",
365
- sdk_version: payload.sdk_version || payload.sdkVersion || "0.2.0"
405
+ sdk_version: payload.sdk_version || payload.sdkVersion || "0.3.0"
366
406
  }
367
407
  }]
368
408
  });
@@ -379,7 +419,7 @@ function createSiteAgentClient(options = {}) {
379
419
  ts: payload.ts == null ? Math.floor(Date.now() / 1000) : payload.ts,
380
420
  data: {
381
421
  runtime: payload.runtime || "server_side_site_agent",
382
- sdk_version: payload.sdk_version || payload.sdkVersion || "0.2.0"
422
+ sdk_version: payload.sdk_version || payload.sdkVersion || "0.3.0"
383
423
  }
384
424
  }]
385
425
  });
@@ -792,6 +832,15 @@ function createSiteAgentClient(options = {}) {
792
832
  monthlyDashboard(payload = {}) {
793
833
  return call("monthly_dashboard", payload);
794
834
  },
835
+ measurementSnapshot(payload = {}) {
836
+ return call("measurement_snapshot", payload);
837
+ },
838
+ measurementHistory(payload = {}) {
839
+ return call("measurement_history", payload);
840
+ },
841
+ measurementComparison(payload = {}) {
842
+ return call("measurement_comparison", payload);
843
+ },
795
844
  async monthlyEvidenceLoop(payload = {}) {
796
845
  const contentPackRes = await call("content_pack", payload);
797
846
  const reviewRes = await publicPost("/api/agent/content_workflow_review", {
@@ -1006,5 +1055,8 @@ function createSiteAgentClient(options = {}) {
1006
1055
  module.exports = {
1007
1056
  SITE_AGENT_KEY_POLICY,
1008
1057
  createSiteAgentClient,
1009
- createTelemetryCollector: (options) => require("./telemetry-collector.js").createTelemetryCollector(options)
1058
+ createTelemetryCollector: (options) => require("./telemetry-collector.js").createTelemetryCollector(options),
1059
+ createCloudflareA1Sync: (options) => require("./cloudflare-a1-sync.js").createCloudflareA1Sync(options),
1060
+ createGa4B1Sync: (options) => require("./b1-sync.js").createGa4B1Sync(options),
1061
+ createOrderB1Sync: (options) => require("./b1-sync.js").createOrderB1Sync(options)
1010
1062
  };
package/sync-service.js CHANGED
@@ -125,7 +125,7 @@ function createSiteAgentSyncService(options = {}) {
125
125
  const intervalMs = Math.max(1000, Number(options.intervalMs || process.env.AGENT_ID_SYNC_INTERVAL_MS || 900000));
126
126
  const heartbeatIntervalMs = Math.max(300000, Number(options.heartbeatIntervalMs || process.env.AGENT_ID_HEARTBEAT_INTERVAL_MS || options.intervalMs || 900000));
127
127
  const collector = options.collector || null;
128
- const runtimeVersion = String(options.runtimeVersion || process.env.AGENT_ID_RUNTIME_VERSION || "0.2.0");
128
+ const runtimeVersion = String(options.runtimeVersion || process.env.AGENT_ID_RUNTIME_VERSION || "0.3.0");
129
129
  const capabilities = Array.from(new Set((Array.isArray(options.capabilities) ? options.capabilities : ["heartbeat", "insights_feed"]).map((item) => String(item).trim()).filter(Boolean))).sort();
130
130
  const now = options.now || Date.now;
131
131
  const sleep = options.sleep || ((ms, signal) => new Promise((resolve) => {