@twin3-ai/agent-id 0.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.
package/site-agent.js ADDED
@@ -0,0 +1,1010 @@
1
+ "use strict";
2
+
3
+ const { DEFAULT_ENDPOINT } = require("./runtime-config.js");
4
+
5
+ const SITE_AGENT_KEY_POLICY = Object.freeze({
6
+ serverSideOnly: true,
7
+ keepIn: ["backend", "CMS connector", "worker secret", "server-side agent runtime"],
8
+ neverExposeIn: ["browser JavaScript", "public HTML", "client-side bundle"],
9
+ privateEndpoint: "/api/site_agents/call"
10
+ });
11
+
12
+ function normalizeEndpoint(value) {
13
+ return String(value || DEFAULT_ENDPOINT).replace(/\/+$/, "");
14
+ }
15
+
16
+ function siteAgentError(code, message) {
17
+ const err = new Error(message);
18
+ err.code = code;
19
+ return err;
20
+ }
21
+
22
+ function defaultSleep(ms, signal) {
23
+ return new Promise((resolve) => {
24
+ if (signal && signal.aborted) return resolve();
25
+ const timer = setTimeout(done, ms);
26
+ function done() {
27
+ clearTimeout(timer);
28
+ if (signal) signal.removeEventListener("abort", done);
29
+ resolve();
30
+ }
31
+ if (signal) signal.addEventListener("abort", done, { once: true });
32
+ });
33
+ }
34
+
35
+ function retryDelayMs(res, attempt, baseMs) {
36
+ const retryAfter = res && res.headers && typeof res.headers.get === "function"
37
+ ? res.headers.get("retry-after")
38
+ : null;
39
+ if (retryAfter && /^\d+(\.\d+)?$/.test(String(retryAfter).trim())) {
40
+ return Math.min(30000, Math.max(0, Number(retryAfter) * 1000));
41
+ }
42
+ return Math.min(30000, baseMs * (2 ** Math.max(0, attempt - 1)));
43
+ }
44
+
45
+ const C5_REPAIR_CATALOG = Object.freeze({
46
+ content_schema_supported: ["Regenerate the Content Pack with the supported AgentX content schema.", "regenerate_content_pack"],
47
+ c1_plan_present: ["Regenerate the C1 answer-first content plan before publication.", "regenerate_content_plan"],
48
+ c4_package_present: ["Regenerate the C4 content workflow package before publication.", "regenerate_content_workflow"],
49
+ c2_prompt_sheet_present: ["Create the C2 fixed prompt-test sheet before reporting AI visibility.", "create_prompt_test_sheet"],
50
+ six_step_workflow_complete: ["Complete all six C4 workflow steps before owner review.", "complete_content_workflow"],
51
+ work_products_complete: ["Add the missing C1/C4 work products to the Content Pack.", "complete_work_products"],
52
+ deliverable_files_complete: ["Add all required deliverable files to the Content Pack manifest.", "complete_deliverable_files"],
53
+ manual_measurement_required: ["Add manual prompt-test requirements before claiming AI visibility.", "add_measurement_protocol"],
54
+ no_external_service_claim: ["Remove claims that the pack ran real AI engines unless observed evidence is supplied.", "owner_content_review"],
55
+ no_background_mutation_claim: ["Add owner-review and no-background-mutation boundaries.", "owner_content_review"],
56
+ no_credentials_required_for_review: ["Remove credential requirements from the C5 review package.", "remove_review_credentials"],
57
+ no_fabricated_statistics: ["Remove or label unsupported statistics before publication.", "owner_content_review"],
58
+ no_fabricated_competitor_scores: ["Replace competitor scores with evidence-backed rows or manual-check labels.", "owner_content_review"],
59
+ no_ai_citation_guarantee: ["Remove guaranteed AI citation/ranking claims and keep measurement as observed evidence.", "owner_content_review"]
60
+ });
61
+
62
+ function contentWorkflowRepairItems(failedChecks = []) {
63
+ return failedChecks.map((check) => {
64
+ const [nextStep, route] = C5_REPAIR_CATALOG[check] || ["Fix this failed C5 review check before publication.", "owner_content_review"];
65
+ return {
66
+ id: `repair_${check}`,
67
+ failed_check: check,
68
+ title: `Repair ${check}`,
69
+ route,
70
+ next_step: nextStep,
71
+ source: "content_workflow_review"
72
+ };
73
+ });
74
+ }
75
+
76
+ function contentWorkflowReviewGate(review = {}) {
77
+ const failedChecks = Array.isArray(review.failed_checks) ? review.failed_checks : [];
78
+ const passed = review.ok === true && failedChecks.length === 0;
79
+ const repairItems = passed ? [] : contentWorkflowRepairItems(failedChecks);
80
+ return {
81
+ schema: "agentx-c5-review-gate-v0.1",
82
+ status: passed ? "passed" : "blocked",
83
+ release_status: passed ? "ready_for_owner_review" : "blocked_by_c5_review",
84
+ review_status: review.review_status || (passed ? "reviewed" : "needs_revision"),
85
+ failed_checks: failedChecks,
86
+ repair_items: repairItems,
87
+ recommended_next_step: review.recommended_next_step || (passed ? "publish_owner_reviewed_outputs_then_run_c2_c3_a2_measurement" : "fix_failed_checks_before_publication_or_claims"),
88
+ source_hash: review.content_hash || ""
89
+ };
90
+ }
91
+
92
+ function monthlyDeliverySummary(parts = {}) {
93
+ const pack = parts.pack || {};
94
+ const review = parts.review || {};
95
+ const repairPack = parts.repair_pack || parts.repairPack || {};
96
+ const repairJobDrafts = parts.repair_job_drafts || parts.repairJobDrafts || {};
97
+ const reviewGate = contentWorkflowReviewGate(review);
98
+ const monthlyPackage = parts.package || {};
99
+ const tracker = parts.tracker || {};
100
+ const scorecard = parts.scorecard || {};
101
+ const dashboard = parts.dashboard || {};
102
+ const claimLimits = Array.isArray(dashboard.claim_limits) ? dashboard.claim_limits : [];
103
+ const decisionQueue = (Array.isArray(repairJobDrafts.drafts) ? repairJobDrafts.drafts : [])
104
+ .filter((draft) => draft && draft.draft_id)
105
+ .map((draft) => ({
106
+ id: `decide_${draft.draft_id}`,
107
+ status: "awaiting_owner_or_agent_decision",
108
+ decision_options: ["accept", "defer", "reject"],
109
+ draft_id: draft.draft_id || "",
110
+ title: draft.title || draft.check_id || draft.draft_id || "",
111
+ priority: draft.priority || "",
112
+ source: "repair_job_drafts",
113
+ source_repair_item_id: draft.source_repair_item_id || "",
114
+ check_id: draft.check_id || draft.failed_check || "",
115
+ route: draft.route || draft.repair_route || "",
116
+ decision_helper: "createSiteAgentClient().decideJobDraft",
117
+ decision_action: "decide_job_draft",
118
+ accept_helper: "createSiteAgentClient().acceptJobDraft",
119
+ accept_action: "accept_job_draft",
120
+ required_confirmation: "owner_or_authorized_website_agent"
121
+ }));
122
+ const claimBoundaries = new Set([
123
+ ...(Array.isArray(dashboard.non_claims) ? dashboard.non_claims : []),
124
+ ...(Array.isArray(scorecard.non_claims) ? scorecard.non_claims : []),
125
+ ...claimLimits.map((item) => item && item.id).filter(Boolean),
126
+ ...(reviewGate.status === "blocked" ? ["content_workflow_review_failed"] : []),
127
+ ...(decisionQueue.length ? ["does_not_accept_or_start_jobs_without_explicit_confirmation"] : []),
128
+ "does_not_modify_customer_site"
129
+ ]);
130
+ return {
131
+ schema: "agentx-monthly-delivery-summary-v0.1",
132
+ host: dashboard.host || scorecard.host || monthlyPackage.host || pack.host || "",
133
+ url: dashboard.url || scorecard.url || monthlyPackage.url || pack.url || "",
134
+ agent_id: dashboard.agent_id || scorecard.agent_id || monthlyPackage.agent_id || pack.agent_id || "",
135
+ period: dashboard.period || scorecard.period || monthlyPackage.period || {},
136
+ headline: reviewGate.status === "passed" ? (dashboard.headline || "Monthly AEO evidence package is ready for review.") : "C5 review needs revision before publication or reporting.",
137
+ review_gate: reviewGate,
138
+ deliverables: [
139
+ { id: "content_pack", label: "Content Pack", schema: pack.schema || "", source_hash: pack.content_hash || "" },
140
+ { id: "c5_content_workflow_review", label: "C5 Content Workflow Review", schema: review.schema || "", source_hash: review.content_hash || "" },
141
+ ...(repairPack.schema ? [{ id: "c6_content_repair_pack", label: "C6 Content Repair Pack", schema: repairPack.schema || "", source_hash: repairPack.content_hash || "" }] : []),
142
+ ...(repairJobDrafts.schema ? [{ id: "c6_repair_job_drafts", label: "C6 Repair Job Drafts", schema: repairJobDrafts.schema || "", source_hash: repairJobDrafts.content_hash || "" }] : []),
143
+ { id: "m2_monthly_optimization_package", label: "M2 Monthly Optimization Package", schema: monthlyPackage.schema || "", source_hash: monthlyPackage.content_hash || "" },
144
+ { id: "m3_monthly_execution_tracker", label: "M3 Monthly Execution Tracker", schema: tracker.schema || "", source_hash: tracker.content_hash || "" },
145
+ { id: "m4_monthly_outcome_scorecard", label: "M4 Monthly Outcome Scorecard", schema: scorecard.schema || "", source_hash: scorecard.content_hash || "" },
146
+ { id: "m5_monthly_dashboard", label: "M5 Monthly Dashboard", schema: dashboard.schema || "", source_hash: dashboard.content_hash || "" }
147
+ ],
148
+ source_hashes: {
149
+ pack: pack.content_hash || "",
150
+ review: review.content_hash || "",
151
+ repair_pack: repairPack.content_hash || "",
152
+ repair_job_drafts: repairJobDrafts.content_hash || "",
153
+ package: monthlyPackage.content_hash || "",
154
+ tracker: tracker.content_hash || "",
155
+ scorecard: scorecard.content_hash || "",
156
+ dashboard: dashboard.content_hash || ""
157
+ },
158
+ kpi_cards: (Array.isArray(dashboard.kpi_cards) ? dashboard.kpi_cards : []).map((item) => ({
159
+ id: item.id || "",
160
+ label: item.label || "",
161
+ value: item.value || "",
162
+ status: item.status || "",
163
+ source: item.source || "",
164
+ claim_level: item.claim_level || ""
165
+ })),
166
+ decision_queue: decisionQueue,
167
+ next_actions: [
168
+ ...(reviewGate.status === "blocked" ? [{
169
+ id: "fix_c5_content_workflow_review",
170
+ title: "Fix C5 content workflow review",
171
+ next_step: reviewGate.recommended_next_step,
172
+ source: "content_workflow_review"
173
+ }] : []),
174
+ ...(Array.isArray(dashboard.next_actions) ? dashboard.next_actions : []).map((item) => ({
175
+ id: item.id || "",
176
+ title: item.title || item.id || "",
177
+ next_step: item.next_step || item.action || "",
178
+ source: item.source || ""
179
+ }))
180
+ ],
181
+ claim_boundaries: Array.from(claimBoundaries).sort(),
182
+ non_claims: [
183
+ "summary_does_not_recalculate_evidence",
184
+ "does_not_accept_or_start_jobs_without_explicit_confirmation",
185
+ "does_not_prove_incrementality_or_causality",
186
+ "does_not_modify_customer_site"
187
+ ]
188
+ };
189
+ }
190
+
191
+ function normalizeEvidenceItems(payload, applyPlan) {
192
+ const applyItems = Array.isArray(applyPlan && applyPlan.apply_items) ? applyPlan.apply_items : [];
193
+ const firstApplyItem = applyItems[0] || {};
194
+ const firstDeliveryItem = applyItems.find((item) => item && item.route !== "owner_decision_review") || {};
195
+ const rawItems = payload.evidence_items || payload.evidenceItems || payload.evidence || [];
196
+ const items = Array.isArray(rawItems) ? rawItems : (rawItems ? [rawItems] : []);
197
+ if (items.length) {
198
+ return items.map((item) => ({
199
+ ...item,
200
+ apply_item_id: item.apply_item_id || item.applyItemId || (item.apply_item && item.apply_item.id) || firstApplyItem.id || ""
201
+ }));
202
+ }
203
+ if (payload.evidence_uri || payload.proof_url || payload.pull_request_url || payload.deployment_reference) {
204
+ const targetItem = firstDeliveryItem.id ? firstDeliveryItem : firstApplyItem;
205
+ if (targetItem.route === "owner_decision_review") return [];
206
+ return [{
207
+ apply_item_id: payload.apply_item_id || payload.applyItemId || targetItem.id || "",
208
+ evidence_uri: payload.evidence_uri,
209
+ proof_url: payload.proof_url,
210
+ pull_request_url: payload.pull_request_url,
211
+ deployment_reference: payload.deployment_reference,
212
+ verify_proof: payload.verify_proof,
213
+ verify: payload.verify
214
+ }];
215
+ }
216
+ return [];
217
+ }
218
+
219
+ function normalizePromptResultRows(payload) {
220
+ const rawRows = payload.rows || payload.results || payload.completed_rows || payload.completedRows || [];
221
+ if (!rawRows) return [];
222
+ return Array.isArray(rawRows) ? rawRows : [rawRows];
223
+ }
224
+
225
+ function requireServerSideRuntime(options) {
226
+ if (options && options.allowBrowser) return;
227
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
228
+ throw siteAgentError(
229
+ "SITE_AGENT_BROWSER_RUNTIME",
230
+ "Site Agent Key calls are server-side only. Use the browser SDK only for public page signals."
231
+ );
232
+ }
233
+ }
234
+
235
+ async function readJsonResponse(res) {
236
+ const text = await res.text();
237
+ try {
238
+ return JSON.parse(text || "{}");
239
+ } catch (_err) {
240
+ return { ok: res.ok, text };
241
+ }
242
+ }
243
+
244
+ function createSiteAgentClient(options = {}) {
245
+ requireServerSideRuntime(options);
246
+ const endpoint = normalizeEndpoint(options.endpoint || process.env.AGENT_ID_ENDPOINT || process.env.AEO_AGENT_ENDPOINT);
247
+ const agentKey = options.agentKey || options.agent_key || process.env.SITE_AGENT_KEY || process.env.AEO_AGENT_KEY || "";
248
+ const fetcher = options.fetch || globalThis.fetch;
249
+ const maxAttempts = Math.max(1, Math.min(5, Number(options.maxAttempts || 3)));
250
+ const retryBaseMs = Math.max(0, Number(options.retryBaseMs == null ? 250 : options.retryBaseMs));
251
+ const sleep = options.sleep || defaultSleep;
252
+ if (typeof fetcher !== "function") {
253
+ throw siteAgentError("FETCH_UNAVAILABLE", "A fetch implementation is required to call the Site Agent API.");
254
+ }
255
+
256
+ async function fetchWithRetry(url, request) {
257
+ let lastError;
258
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
259
+ try {
260
+ const res = await fetcher(url, request);
261
+ const retryableStateConflict = res && res.status === 409 && res.headers
262
+ && typeof res.headers.get === "function"
263
+ && res.headers.get("x-agent-id-retryable") === "state-conflict";
264
+ const retryable = res && (res.status === 429 || res.status >= 500 || retryableStateConflict);
265
+ if (!retryable || attempt >= maxAttempts) return res;
266
+ if (typeof res.text === "function") await res.text();
267
+ await sleep(retryDelayMs(res, attempt, retryBaseMs));
268
+ } catch (err) {
269
+ lastError = err;
270
+ if (attempt >= maxAttempts) break;
271
+ await sleep(Math.min(30000, retryBaseMs * (2 ** Math.max(0, attempt - 1))));
272
+ }
273
+ }
274
+ throw siteAgentError(
275
+ "SITE_AGENT_NETWORK_FAILED",
276
+ lastError && lastError.message ? lastError.message : "Site Agent network request failed"
277
+ );
278
+ }
279
+
280
+ async function call(action, payload = {}) {
281
+ const key = payload.agent_key || payload.agentKey || agentKey;
282
+ if (!key) {
283
+ throw siteAgentError(
284
+ "SITE_AGENT_KEY_REQUIRED",
285
+ "A Site Agent Key is required for private server-side Site Agent calls."
286
+ );
287
+ }
288
+ const body = {
289
+ ...payload,
290
+ agent_key: key,
291
+ action
292
+ };
293
+ delete body.agentKey;
294
+ const res = await fetchWithRetry(endpoint + SITE_AGENT_KEY_POLICY.privateEndpoint, {
295
+ method: "POST",
296
+ headers: { "content-type": "application/json" },
297
+ body: JSON.stringify(body)
298
+ });
299
+ const data = await readJsonResponse(res);
300
+ if (!res.ok || data.ok === false) {
301
+ const error = siteAgentError(
302
+ data.code || "SITE_AGENT_CALL_FAILED",
303
+ data.error || data.message || `Site Agent call failed with HTTP ${res.status}`
304
+ );
305
+ error.status = res.status;
306
+ const retryAfter = res.headers && typeof res.headers.get === "function" ? res.headers.get("retry-after") : "";
307
+ if (retryAfter && /^\d+(?:\.\d+)?$/.test(retryAfter)) error.retry_after_ms = Number(retryAfter) * 1000;
308
+ throw error;
309
+ }
310
+ return data;
311
+ }
312
+
313
+ async function publicPost(path, payload = {}) {
314
+ const res = await fetchWithRetry(endpoint + path, {
315
+ method: "POST",
316
+ headers: { "content-type": "application/json" },
317
+ body: JSON.stringify(payload || {})
318
+ });
319
+ const data = await readJsonResponse(res);
320
+ if (!res.ok || data.ok === false) {
321
+ const error = siteAgentError(
322
+ data.code || "AGENT_ID_PUBLIC_CALL_FAILED",
323
+ data.error || data.message || `Agent ID public call failed with HTTP ${res.status}`
324
+ );
325
+ error.status = res.status;
326
+ const retryAfter = res.headers && typeof res.headers.get === "function" ? res.headers.get("retry-after") : "";
327
+ if (retryAfter && /^\d+(?:\.\d+)?$/.test(retryAfter)) error.retry_after_ms = Number(retryAfter) * 1000;
328
+ throw error;
329
+ }
330
+ return data;
331
+ }
332
+
333
+ return Object.freeze({
334
+ endpoint,
335
+ policy: SITE_AGENT_KEY_POLICY,
336
+ call,
337
+ verify(payload = {}) {
338
+ const key = payload.agent_key || payload.agentKey || agentKey;
339
+ if (!key) {
340
+ throw siteAgentError(
341
+ "SITE_AGENT_KEY_REQUIRED",
342
+ "A Site Agent Key is required to verify this website agent."
343
+ );
344
+ }
345
+ const body = {
346
+ ...payload,
347
+ agent_key: key
348
+ };
349
+ delete body.agentKey;
350
+ return publicPost("/api/site_agents/verify", body);
351
+ },
352
+ insightsFeed(payload = {}) {
353
+ return call("insights_feed", payload);
354
+ },
355
+ heartbeat(payload = {}) {
356
+ return call("site_agent_events", {
357
+ ...payload,
358
+ source: payload.source || "site_agent_sync",
359
+ events: [{
360
+ event: "heartbeat",
361
+ url: payload.url || payload.website || "",
362
+ ts: payload.ts == null ? Math.floor(Date.now() / 1000) : payload.ts,
363
+ data: {
364
+ runtime: payload.runtime || "server_side_site_agent",
365
+ sdk_version: payload.sdk_version || payload.sdkVersion || "0.1.0"
366
+ }
367
+ }]
368
+ });
369
+ },
370
+ async syncOnce(payload = {}) {
371
+ const heartbeat = payload.heartbeat === false
372
+ ? null
373
+ : await call("site_agent_events", {
374
+ ...payload,
375
+ source: payload.source || "site_agent_sync",
376
+ events: [{
377
+ event: "heartbeat",
378
+ url: payload.url || payload.website || "",
379
+ ts: payload.ts == null ? Math.floor(Date.now() / 1000) : payload.ts,
380
+ data: {
381
+ runtime: payload.runtime || "server_side_site_agent",
382
+ sdk_version: payload.sdk_version || payload.sdkVersion || "0.1.0"
383
+ }
384
+ }]
385
+ });
386
+ const feedResponse = await call("insights_feed", payload);
387
+ const feed = feedResponse.feed || {};
388
+ const sync = feedResponse.sync || {
389
+ changed: (feed.content_hash || "") !== (payload.cursor || ""),
390
+ previous_cursor: payload.cursor || "",
391
+ cursor: feed.content_hash || ""
392
+ };
393
+ return {
394
+ schema: "agentx-site-agent-sync-once-v1",
395
+ changed: sync.changed === true,
396
+ previous_cursor: sync.previous_cursor || payload.cursor || "",
397
+ cursor: sync.cursor || feed.content_hash || "",
398
+ heartbeat,
399
+ feed,
400
+ sync
401
+ };
402
+ },
403
+ refreshInsightInbox(payload = {}) {
404
+ return call("refresh_insight_inbox", payload);
405
+ },
406
+ insightInbox(payload = {}) {
407
+ return call("insight_inbox", payload);
408
+ },
409
+ acknowledgeInsight(payload = {}) {
410
+ return call("acknowledge_insight", payload);
411
+ },
412
+ dismissInsight(payload = {}) {
413
+ return call("dismiss_insight", payload);
414
+ },
415
+ installDescriptor(payload = {}) {
416
+ return call("install_descriptor", payload);
417
+ },
418
+ evidencePack(payload = {}) {
419
+ return call("evidence_pack", payload);
420
+ },
421
+ auditReport(payload = {}) {
422
+ return call("audit_report", payload);
423
+ },
424
+ implementationBundle(payload = {}) {
425
+ return call("implementation_bundle", payload);
426
+ },
427
+ auditFilePackage(payload = {}) {
428
+ return call("audit_file_package", payload);
429
+ },
430
+ skillPackage(payload = {}) {
431
+ return call("skill_package", payload);
432
+ },
433
+ installationVerification(payload = {}) {
434
+ return call("installation_verification", payload);
435
+ },
436
+ contentOptimizationPlan(payload = {}) {
437
+ return call("content_optimization_plan", payload);
438
+ },
439
+ citationGapMatrix(payload = {}) {
440
+ return call("citation_gap_matrix", payload);
441
+ },
442
+ communityCitationPlan(payload = {}) {
443
+ return call("community_citation_plan", payload);
444
+ },
445
+ promptTestRunSheet(payload = {}) {
446
+ return call("prompt_test_run_sheet", payload);
447
+ },
448
+ promptTestResults(payload = {}) {
449
+ return call("prompt_test_results", payload);
450
+ },
451
+ a2CitationPackage(payload = {}) {
452
+ return call("a2_citation_evidence_package", payload);
453
+ },
454
+ async siteEvents(payload = {}) {
455
+ const eventRes = await call("site_agent_events", payload);
456
+ return {
457
+ ...eventRes,
458
+ steps: [
459
+ { action: "site_agent_events", field: "import" }
460
+ ],
461
+ non_claims: [
462
+ ...new Set([
463
+ ...(Array.isArray(eventRes.non_claims) ? eventRes.non_claims : []),
464
+ ...((eventRes.import && Array.isArray(eventRes.import.non_claims)) ? eventRes.import.non_claims : []),
465
+ "connector_observed_page_signals_only",
466
+ "does_not_prove_ai_crawler_read_or_conversion",
467
+ "site_agent_key_must_remain_server_side"
468
+ ])
469
+ ]
470
+ };
471
+ },
472
+ applyPlan(payload = {}) {
473
+ return call("apply_plan", payload);
474
+ },
475
+ async applyCycle(payload = {}) {
476
+ const feedRes = await call("insights_feed", payload);
477
+ const applyPlanRes = await call("apply_plan", {
478
+ ...payload,
479
+ feed: feedRes.feed
480
+ });
481
+ const evidenceItems = normalizeEvidenceItems(payload, applyPlanRes.apply_plan);
482
+ const evidenceResults = [];
483
+ for (const item of evidenceItems) {
484
+ if (!item.apply_item_id) continue;
485
+ evidenceResults.push(await call("apply_evidence", {
486
+ ...payload,
487
+ ...item,
488
+ apply_plan: applyPlanRes.apply_plan,
489
+ source_feed_hash: item.source_feed_hash || (applyPlanRes.apply_plan && applyPlanRes.apply_plan.source_feed_hash) || (feedRes.feed && feedRes.feed.content_hash) || ""
490
+ }));
491
+ }
492
+ return {
493
+ ok: true,
494
+ schema: "agentx-site-agent-apply-cycle-v0.1",
495
+ lane: "site_agent_key",
496
+ action: "apply_cycle",
497
+ feed: feedRes.feed,
498
+ apply_plan: applyPlanRes.apply_plan,
499
+ evidence_results: evidenceResults,
500
+ responses: {
501
+ insights_feed: feedRes,
502
+ apply_plan: applyPlanRes,
503
+ apply_evidence: evidenceResults
504
+ },
505
+ steps: [
506
+ { action: "insights_feed", field: "feed" },
507
+ { action: "apply_plan", field: "apply_plan" },
508
+ ...(evidenceResults.length ? [{ action: "apply_evidence", field: "evidence_results" }] : [])
509
+ ],
510
+ non_claims: [
511
+ "does_not_apply_changes_without_owner_or_connector",
512
+ "does_not_prove_ai_ranking_or_conversion_lift",
513
+ "site_agent_key_must_remain_server_side"
514
+ ]
515
+ };
516
+ },
517
+ contentPack(payload = {}) {
518
+ return call("content_pack", payload);
519
+ },
520
+ async contentRepairPack(payload = {}) {
521
+ const repairRes = await call("content_repair_pack", payload);
522
+ return {
523
+ ...repairRes,
524
+ steps: [
525
+ { action: "content_repair_pack", field: "pack" }
526
+ ],
527
+ non_claims: [
528
+ ...new Set([
529
+ ...(Array.isArray(repairRes.non_claims) ? repairRes.non_claims : []),
530
+ ...((repairRes.pack && Array.isArray(repairRes.pack.non_claims)) ? repairRes.pack.non_claims : []),
531
+ "site_agent_key_must_remain_server_side",
532
+ "does_not_modify_customer_site"
533
+ ])
534
+ ]
535
+ };
536
+ },
537
+ contentWorkflowReview(payload = {}) {
538
+ const body = { ...payload };
539
+ if (payload.contentPack && !body.content_pack) body.content_pack = payload.contentPack;
540
+ if (payload.package && !body.content_pack) body.content_pack = payload.package;
541
+ return publicPost("/api/agent/content_workflow_review", body);
542
+ },
543
+ async contentWorkflow(payload = {}) {
544
+ const workflowRes = await call("content_workflow_package", payload);
545
+ return {
546
+ ...workflowRes,
547
+ steps: [
548
+ { action: "content_workflow_package", field: "package" }
549
+ ],
550
+ non_claims: [
551
+ ...new Set([
552
+ ...((workflowRes.package && Array.isArray(workflowRes.package.non_claims)) ? workflowRes.package.non_claims : []),
553
+ "does_not_run_real_ai_engines",
554
+ "does_not_fabricate_statistics",
555
+ "does_not_guarantee_ai_citation_or_ranking",
556
+ "site_agent_key_must_remain_server_side"
557
+ ])
558
+ ]
559
+ };
560
+ },
561
+ async contentPublication(payload = {}) {
562
+ const publicationRes = await call("content_publication_package", payload);
563
+ return {
564
+ ...publicationRes,
565
+ steps: [
566
+ { action: "content_publication_package", field: "package" }
567
+ ],
568
+ non_claims: [
569
+ ...new Set([
570
+ ...((publicationRes.package && Array.isArray(publicationRes.package.non_claims)) ? publicationRes.package.non_claims : []),
571
+ "does_not_verify_ai_engine_visibility",
572
+ "does_not_claim_rankings_or_citations",
573
+ "does_not_prove_conversion_lift",
574
+ "site_agent_key_must_remain_server_side"
575
+ ])
576
+ ]
577
+ };
578
+ },
579
+ async promptTestCycle(payload = {}) {
580
+ const sheetRes = await call("prompt_test_run_sheet", payload);
581
+ const rows = normalizePromptResultRows(payload);
582
+ const importRes = rows.length ? await call("prompt_test_results", {
583
+ ...payload,
584
+ sheet: payload.sheet || sheetRes.sheet,
585
+ rows
586
+ }) : null;
587
+ return {
588
+ ok: true,
589
+ schema: "agentx-site-agent-prompt-test-cycle-v0.1",
590
+ lane: "site_agent_key",
591
+ action: "prompt_test_cycle",
592
+ sheet: sheetRes.sheet,
593
+ import_result: importRes,
594
+ responses: {
595
+ prompt_test_run_sheet: sheetRes,
596
+ prompt_test_results: importRes
597
+ },
598
+ steps: [
599
+ { action: "prompt_test_run_sheet", field: "sheet" },
600
+ ...(importRes ? [{ action: "prompt_test_results", field: "import_result" }] : [])
601
+ ],
602
+ non_claims: [
603
+ "manual_prompt_results_required",
604
+ "does_not_run_real_ai_engines",
605
+ "does_not_fabricate_ai_citations",
606
+ "does_not_prove_conversion_lift",
607
+ "site_agent_key_must_remain_server_side"
608
+ ]
609
+ };
610
+ },
611
+ async aiReads(payload = {}) {
612
+ const readRes = await call("a1_ai_reads", payload);
613
+ return {
614
+ ...readRes,
615
+ steps: [
616
+ { action: "a1_ai_reads", field: "import" }
617
+ ],
618
+ non_claims: [
619
+ ...new Set([
620
+ ...(Array.isArray(readRes.non_claims) ? readRes.non_claims : []),
621
+ "observed_server_logs_only",
622
+ "does_not_prove_ai_answer_citation_or_conversion",
623
+ "site_agent_key_must_remain_server_side"
624
+ ])
625
+ ]
626
+ };
627
+ },
628
+ async conversions(payload = {}) {
629
+ const conversionRes = await call("b1_conversions", payload);
630
+ return {
631
+ ...conversionRes,
632
+ steps: [
633
+ { action: "b1_conversions", field: "import" }
634
+ ],
635
+ non_claims: [
636
+ ...new Set([
637
+ ...(Array.isArray(conversionRes.non_claims) ? conversionRes.non_claims : []),
638
+ "observed_conversion_events_only",
639
+ "does_not_prove_incremental_lift_or_causality",
640
+ "site_agent_key_must_remain_server_side"
641
+ ])
642
+ ]
643
+ };
644
+ },
645
+ async a1ReadPackage(payload = {}) {
646
+ const packageRes = await call("a1_read_evidence_package", payload);
647
+ return {
648
+ ...packageRes,
649
+ steps: [
650
+ { action: "a1_read_evidence_package", field: "package" }
651
+ ],
652
+ non_claims: [
653
+ ...new Set([
654
+ ...(Array.isArray(packageRes.non_claims) ? packageRes.non_claims : []),
655
+ ...((packageRes.package && Array.isArray(packageRes.package.non_claims)) ? packageRes.package.non_claims : []),
656
+ "observed_server_logs_only",
657
+ "does_not_prove_ai_answer_citation_or_conversion",
658
+ "site_agent_key_must_remain_server_side"
659
+ ])
660
+ ]
661
+ };
662
+ },
663
+ async a1Dashboard(payload = {}) {
664
+ const dashboardRes = await call("a1_dashboard", payload);
665
+ return {
666
+ ...dashboardRes,
667
+ steps: [
668
+ { action: "a1_dashboard", field: "dashboard" }
669
+ ],
670
+ non_claims: [
671
+ ...new Set([
672
+ ...(Array.isArray(dashboardRes.non_claims) ? dashboardRes.non_claims : []),
673
+ ...((dashboardRes.dashboard && Array.isArray(dashboardRes.dashboard.claim_limits)) ? dashboardRes.dashboard.claim_limits : []),
674
+ "observed_server_logs_only",
675
+ "does_not_prove_ai_answer_citation_or_conversion",
676
+ "site_agent_key_must_remain_server_side"
677
+ ])
678
+ ]
679
+ };
680
+ },
681
+ async b1ConversionPackage(payload = {}) {
682
+ const packageRes = await call("b1_conversion_evidence_package", payload);
683
+ return {
684
+ ...packageRes,
685
+ steps: [
686
+ { action: "b1_conversion_evidence_package", field: "package" }
687
+ ],
688
+ non_claims: [
689
+ ...new Set([
690
+ ...(Array.isArray(packageRes.non_claims) ? packageRes.non_claims : []),
691
+ ...((packageRes.package && Array.isArray(packageRes.package.non_claims)) ? packageRes.package.non_claims : []),
692
+ "observed_conversion_events_only",
693
+ "does_not_prove_incremental_lift_or_causality",
694
+ "site_agent_key_must_remain_server_side"
695
+ ])
696
+ ]
697
+ };
698
+ },
699
+ async siteSignalPackage(payload = {}) {
700
+ const packageRes = await call("site_signal_package", payload);
701
+ return {
702
+ ...packageRes,
703
+ steps: [
704
+ { action: "site_signal_package", field: "package" }
705
+ ],
706
+ non_claims: [
707
+ ...new Set([
708
+ ...(Array.isArray(packageRes.non_claims) ? packageRes.non_claims : []),
709
+ ...((packageRes.package && Array.isArray(packageRes.package.non_claims)) ? packageRes.package.non_claims : []),
710
+ "site_signal_evidence_only",
711
+ "does_not_prove_ai_crawler_read_or_conversion",
712
+ "does_not_prove_ai_answer_citation",
713
+ "site_agent_key_must_remain_server_side"
714
+ ])
715
+ ]
716
+ };
717
+ },
718
+ async outcomeReport(payload = {}) {
719
+ const reportRes = await call("outcome_report", payload);
720
+ return {
721
+ ...reportRes,
722
+ steps: [
723
+ { action: "outcome_report", field: "report" }
724
+ ],
725
+ non_claims: [
726
+ ...new Set([
727
+ ...(Array.isArray(reportRes.non_claims) ? reportRes.non_claims : []),
728
+ ...((reportRes.report && Array.isArray(reportRes.report.non_claims)) ? reportRes.report.non_claims : []),
729
+ "observed_evidence_only",
730
+ "does_not_prove_incremental_lift_or_causality",
731
+ "site_agent_key_must_remain_server_side"
732
+ ])
733
+ ]
734
+ };
735
+ },
736
+ async outcomePackage(payload = {}) {
737
+ const packageRes = await call("m1_outcome_evidence_package", payload);
738
+ return {
739
+ ...packageRes,
740
+ steps: [
741
+ { action: "m1_outcome_evidence_package", field: "package" }
742
+ ],
743
+ non_claims: [
744
+ ...new Set([
745
+ ...(Array.isArray(packageRes.non_claims) ? packageRes.non_claims : []),
746
+ ...((packageRes.package && Array.isArray(packageRes.package.non_claims)) ? packageRes.package.non_claims : []),
747
+ "observed_evidence_only",
748
+ "does_not_prove_incremental_lift_or_causality",
749
+ "site_agent_key_must_remain_server_side"
750
+ ])
751
+ ]
752
+ };
753
+ },
754
+ async outcomeReview(payload = {}) {
755
+ const reportRes = await this.outcomeReport(payload);
756
+ const packageRes = await this.outcomePackage(payload);
757
+ return {
758
+ ok: true,
759
+ schema: "agentx-site-agent-outcome-review-v0.1",
760
+ lane: "site_agent_key",
761
+ action: "outcome_review",
762
+ report: reportRes.report,
763
+ package: packageRes.package,
764
+ responses: {
765
+ outcome_report: reportRes,
766
+ m1_outcome_evidence_package: packageRes
767
+ },
768
+ steps: [
769
+ { action: "outcome_report", field: "report" },
770
+ { action: "m1_outcome_evidence_package", field: "package" }
771
+ ],
772
+ non_claims: [
773
+ ...new Set([
774
+ ...(Array.isArray(reportRes.non_claims) ? reportRes.non_claims : []),
775
+ ...(Array.isArray(packageRes.non_claims) ? packageRes.non_claims : []),
776
+ "observed_evidence_only",
777
+ "does_not_prove_incremental_lift_or_causality",
778
+ "site_agent_key_must_remain_server_side"
779
+ ])
780
+ ]
781
+ };
782
+ },
783
+ monthlyPackage(payload = {}) {
784
+ return call("monthly_package", payload);
785
+ },
786
+ monthlyTracker(payload = {}) {
787
+ return call("monthly_tracker", payload);
788
+ },
789
+ monthlyScorecard(payload = {}) {
790
+ return call("monthly_scorecard", payload);
791
+ },
792
+ monthlyDashboard(payload = {}) {
793
+ return call("monthly_dashboard", payload);
794
+ },
795
+ async monthlyEvidenceLoop(payload = {}) {
796
+ const contentPackRes = await call("content_pack", payload);
797
+ const reviewRes = await publicPost("/api/agent/content_workflow_review", {
798
+ content_pack: contentPackRes.pack
799
+ });
800
+ const reviewGate = contentWorkflowReviewGate(reviewRes.review);
801
+ const repairPackRes = reviewGate.status === "blocked" ? await call("content_repair_pack", {
802
+ ...payload,
803
+ content_pack: contentPackRes.pack,
804
+ review_gate: reviewGate
805
+ }) : null;
806
+ const repairDraftsRes = reviewGate.status === "blocked" ? await call("job_drafts", {
807
+ ...payload,
808
+ review_gate: reviewGate
809
+ }) : null;
810
+ const loopPayload = {
811
+ ...payload,
812
+ content_pack: contentPackRes.pack
813
+ };
814
+ const packageRes = await call("monthly_package", loopPayload);
815
+ const trackerRes = await call("monthly_tracker", loopPayload);
816
+ const scorecardRes = await call("monthly_scorecard", loopPayload);
817
+ const dashboardRes = await call("monthly_dashboard", loopPayload);
818
+ const deliverySummary = monthlyDeliverySummary({
819
+ pack: contentPackRes.pack,
820
+ review: reviewRes.review,
821
+ repair_pack: repairPackRes && repairPackRes.pack,
822
+ repair_job_drafts: repairDraftsRes && repairDraftsRes.drafts,
823
+ package: packageRes.package,
824
+ tracker: trackerRes.tracker,
825
+ scorecard: scorecardRes.scorecard,
826
+ dashboard: dashboardRes.dashboard
827
+ });
828
+ return {
829
+ ok: true,
830
+ lane: "site_agent_key",
831
+ action: "monthly_evidence_loop",
832
+ pack: contentPackRes.pack,
833
+ review: reviewRes.review,
834
+ review_gate: reviewGate,
835
+ ...(repairPackRes ? { repair_pack: repairPackRes.pack } : {}),
836
+ ...(repairDraftsRes ? { repair_job_drafts: repairDraftsRes.drafts } : {}),
837
+ package: packageRes.package,
838
+ tracker: trackerRes.tracker,
839
+ scorecard: scorecardRes.scorecard,
840
+ dashboard: dashboardRes.dashboard,
841
+ delivery_summary: deliverySummary,
842
+ responses: {
843
+ content_pack: contentPackRes,
844
+ content_workflow_review: reviewRes,
845
+ ...(repairPackRes ? { content_repair_pack: repairPackRes } : {}),
846
+ ...(repairDraftsRes ? { job_drafts: repairDraftsRes } : {}),
847
+ monthly_package: packageRes,
848
+ monthly_tracker: trackerRes,
849
+ monthly_scorecard: scorecardRes,
850
+ monthly_dashboard: dashboardRes
851
+ },
852
+ steps: [
853
+ { action: "content_pack", field: "pack" },
854
+ { action: "content_workflow_review", field: "review" },
855
+ ...(repairPackRes ? [{ action: "content_repair_pack", field: "repair_pack" }] : []),
856
+ ...(repairDraftsRes ? [{ action: "job_drafts", field: "repair_job_drafts" }] : []),
857
+ { action: "monthly_package", field: "package" },
858
+ { action: "monthly_tracker", field: "tracker" },
859
+ { action: "monthly_scorecard", field: "scorecard" },
860
+ { action: "monthly_dashboard", field: "dashboard" }
861
+ ]
862
+ };
863
+ },
864
+ async optimizationCycle(payload = {}) {
865
+ const cycleRes = await call("optimization_cycle", payload);
866
+ return {
867
+ ...cycleRes,
868
+ steps: [
869
+ { action: "optimization_cycle", field: "cycle" }
870
+ ],
871
+ non_claims: [
872
+ ...new Set([
873
+ ...(Array.isArray(cycleRes.non_claims) ? cycleRes.non_claims : []),
874
+ ...((cycleRes.cycle && Array.isArray(cycleRes.cycle.non_claims)) ? cycleRes.cycle.non_claims : []),
875
+ "read_only_optimization_plan_not_site_mutation",
876
+ "does_not_accept_or_start_jobs_without_owner_confirmation",
877
+ "does_not_prove_incremental_lift_or_causality",
878
+ "site_agent_key_must_remain_server_side"
879
+ ])
880
+ ]
881
+ };
882
+ },
883
+ async jobDrafts(payload = {}) {
884
+ const draftRes = await call("job_drafts", payload);
885
+ return {
886
+ ...draftRes,
887
+ steps: [
888
+ { action: "job_drafts", field: "drafts" }
889
+ ],
890
+ non_claims: [
891
+ ...new Set([
892
+ ...(Array.isArray(draftRes.non_claims) ? draftRes.non_claims : []),
893
+ ...((draftRes.drafts && Array.isArray(draftRes.drafts.non_claims)) ? draftRes.drafts.non_claims : []),
894
+ "drafts_only_not_started_work",
895
+ "does_not_accept_or_start_jobs_without_owner_confirmation",
896
+ "site_agent_key_must_remain_server_side"
897
+ ])
898
+ ]
899
+ };
900
+ },
901
+ async acceptJobDraft(payload = {}) {
902
+ const acceptedRes = await call("accept_job_draft", payload);
903
+ return {
904
+ ...acceptedRes,
905
+ steps: [
906
+ { action: "accept_job_draft", field: "job" }
907
+ ],
908
+ non_claims: [
909
+ ...new Set([
910
+ ...(Array.isArray(acceptedRes.non_claims) ? acceptedRes.non_claims : []),
911
+ "explicit_owner_or_agent_confirmation_required",
912
+ "does_not_fund_or_release_payment",
913
+ "does_not_prove_delivery_or_outcome",
914
+ "site_agent_key_must_remain_server_side"
915
+ ])
916
+ ]
917
+ };
918
+ },
919
+ async decideJobDraft(payload = {}) {
920
+ const decisionRes = await call("decide_job_draft", payload);
921
+ return {
922
+ ...decisionRes,
923
+ steps: [
924
+ { action: "decide_job_draft", field: "decision" }
925
+ ],
926
+ non_claims: [
927
+ ...new Set([
928
+ ...(Array.isArray(decisionRes.non_claims) ? decisionRes.non_claims : []),
929
+ "decision_receipt_only",
930
+ "does_not_start_work_when_deferred_or_rejected",
931
+ "does_not_prove_delivery_or_outcome",
932
+ "site_agent_key_must_remain_server_side"
933
+ ])
934
+ ]
935
+ };
936
+ },
937
+ async draftDecisions(payload = {}) {
938
+ const historyRes = await call("draft_decisions", payload);
939
+ return {
940
+ ...historyRes,
941
+ steps: [
942
+ { action: "draft_decisions", field: "decisions" }
943
+ ],
944
+ non_claims: [
945
+ ...new Set([
946
+ ...(Array.isArray(historyRes.non_claims) ? historyRes.non_claims : []),
947
+ ...((historyRes.decisions && Array.isArray(historyRes.decisions.non_claims)) ? historyRes.decisions.non_claims : []),
948
+ "decision_history_not_delivery_or_outcome_proof",
949
+ "does_not_prove_ai_ranking_or_conversion_lift",
950
+ "site_agent_key_must_remain_server_side"
951
+ ])
952
+ ]
953
+ };
954
+ },
955
+ async decisionReviews(payload = {}) {
956
+ const historyRes = await call("decision_reviews", payload);
957
+ return {
958
+ ...historyRes,
959
+ steps: [
960
+ { action: "decision_reviews", field: "reviews" }
961
+ ],
962
+ non_claims: [
963
+ ...new Set([
964
+ ...(Array.isArray(historyRes.non_claims) ? historyRes.non_claims : []),
965
+ ...((historyRes.reviews && Array.isArray(historyRes.reviews.non_claims)) ? historyRes.reviews.non_claims : []),
966
+ "decision_review_history_not_delivery_or_outcome_proof",
967
+ "does_not_prove_ai_ranking_or_conversion_lift",
968
+ "site_agent_key_must_remain_server_side"
969
+ ])
970
+ ]
971
+ };
972
+ },
973
+ async c5RepairHistory(payload = {}) {
974
+ const historyRes = await call("c5_repair_history", payload);
975
+ return {
976
+ ...historyRes,
977
+ steps: [
978
+ { action: "c5_repair_history", field: "history" }
979
+ ],
980
+ non_claims: [
981
+ ...new Set([
982
+ ...(Array.isArray(historyRes.non_claims) ? historyRes.non_claims : []),
983
+ ...((historyRes.history && Array.isArray(historyRes.history.non_claims)) ? historyRes.history.non_claims : []),
984
+ "observed_repair_ledger_only",
985
+ "does_not_prove_ai_ranking_or_conversion_lift",
986
+ "site_agent_key_must_remain_server_side"
987
+ ])
988
+ ]
989
+ };
990
+ },
991
+ applyEvidence(payload = {}) {
992
+ return call("apply_evidence", payload);
993
+ },
994
+ reportJobEvidence(payload = {}) {
995
+ return call("report_job_evidence", payload);
996
+ },
997
+ insights(payload = {}) {
998
+ return call("insights", payload);
999
+ },
1000
+ jobs(payload = {}) {
1001
+ return call("jobs", payload);
1002
+ }
1003
+ });
1004
+ }
1005
+
1006
+ module.exports = {
1007
+ SITE_AGENT_KEY_POLICY,
1008
+ createSiteAgentClient,
1009
+ createTelemetryCollector: (options) => require("./telemetry-collector.js").createTelemetryCollector(options)
1010
+ };