@nanorail/sdk 0.1.1 → 0.1.2

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/dist/index.d.ts CHANGED
@@ -134,6 +134,53 @@ export declare class NanoRailEvents {
134
134
  emit(detail: Omit<NanoRailEventDetail, "at">): void;
135
135
  on(label: LifecycleLabel | "lifecycle", handler: (e: NanoRailEventDetail) => void): () => void;
136
136
  }
137
+ export interface GenerateManifestOptions {
138
+ /** DOM subtree to scan; defaults to the whole document. */
139
+ root?: ParentNode;
140
+ /** Override the article id (else derived from the chunks' attributes). */
141
+ articleId?: string;
142
+ /** Override the publisher id (else derived from the chunks' attributes). */
143
+ publisherId?: string;
144
+ /** Override the article title (else taken from <h1> or document.title). */
145
+ title?: string;
146
+ }
147
+ /**
148
+ * Build a machine-readable manifest purely from the publisher's
149
+ * `data-nanorail-*` attributes — no separately-maintained manifest file. The
150
+ * publisher marks content; this derives article metadata, chunk ids/types/prices,
151
+ * teasers, and per-chunk unlock endpoints automatically.
152
+ */
153
+ export declare function generateManifest(opts?: GenerateManifestOptions): ArticleManifest;
154
+ /** A scanned chunk plus the gated content captured from the page. */
155
+ export interface RegisteredChunkInput extends ManifestChunk {
156
+ /** The publisher's gated content, captured from the element's innerHTML. */
157
+ content: string;
158
+ }
159
+ /** Payload the SDK sends to the gateway's registration endpoint. */
160
+ export interface ArticleRegistration {
161
+ publisherId: string;
162
+ articleId: string;
163
+ title: string;
164
+ source: "sdk-scan";
165
+ chunks: RegisteredChunkInput[];
166
+ }
167
+ /**
168
+ * Like {@link generateManifest}, but ALSO captures each chunk's gated content
169
+ * (its current `innerHTML`/`textContent`) so the SDK can register the publisher's
170
+ * real content with the gateway. **Must run BEFORE the chunk is locked** — locking
171
+ * replaces the element's children. The captured `content` is sent only to the
172
+ * registration endpoint; it is never surfaced in the public manifest or discovery.
173
+ */
174
+ export declare function scanRegistration(opts?: GenerateManifestOptions): ArticleRegistration;
175
+ /** Gateway response to a publisher content registration. */
176
+ export interface RegistrationResult {
177
+ ok: boolean;
178
+ /** Total chunks registered (metadata). */
179
+ chunks?: number;
180
+ /** How many carried gated content (the rest fall back to seeded demo content). */
181
+ withContent?: number;
182
+ source?: string;
183
+ }
137
184
  export interface ChunkDescriptor {
138
185
  chunkId: string;
139
186
  type: ContentType;
@@ -199,6 +246,12 @@ export declare class NanoRailClient {
199
246
  session?: Session;
200
247
  spentMicros: number;
201
248
  receipts: Receipt[];
249
+ /** Content already unlocked this session, by chunkId — lets a re-run reuse what
250
+ * was bought instead of re-paying (the gateway rejects duplicate payments). */
251
+ unlocked: Map<string, {
252
+ content: string;
253
+ title: string;
254
+ }>;
202
255
  blocked: Array<{
203
256
  chunkId: string;
204
257
  reason: string;
@@ -224,6 +277,13 @@ export declare class NanoRailClient {
224
277
  ok: boolean;
225
278
  chunks?: number;
226
279
  }>;
280
+ /**
281
+ * Register the publisher's scanned chunks WITH their gated content. The gateway
282
+ * stores the content server-side and returns it only from a verified unlock;
283
+ * discovery/manifest expose only the metadata. This is what makes a real
284
+ * publisher's own content (not seeded demo content) the thing that unlocks.
285
+ */
286
+ registerArticle(registration: ArticleRegistration): Promise<RegistrationResult>;
227
287
  /** Open a funded session once; concurrent callers share the same promise. */
228
288
  ensureSession(): Promise<Session>;
229
289
  private openSession;
@@ -294,23 +354,6 @@ export interface ScanOptions {
294
354
  * `{ autoUnlock: true }`, policy-eligible chunks unlock themselves on scroll.
295
355
  */
296
356
  export declare function scanAndLock(client: NanoRailClient, root?: ParentNode, opts?: ScanOptions): HTMLElement[];
297
- export interface GenerateManifestOptions {
298
- /** DOM subtree to scan; defaults to the whole document. */
299
- root?: ParentNode;
300
- /** Override the article id (else derived from the chunks' attributes). */
301
- articleId?: string;
302
- /** Override the publisher id (else derived from the chunks' attributes). */
303
- publisherId?: string;
304
- /** Override the article title (else taken from <h1> or document.title). */
305
- title?: string;
306
- }
307
- /**
308
- * Build a machine-readable manifest purely from the publisher's
309
- * `data-nanorail-*` attributes — no separately-maintained manifest file. The
310
- * publisher marks content; this derives article metadata, chunk ids/types/prices,
311
- * teasers, and per-chunk unlock endpoints automatically.
312
- */
313
- export declare function generateManifest(opts?: GenerateManifestOptions): ArticleManifest;
314
357
  /**
315
358
  * The NanoRail **Agent Wallet** — the user-facing control layer for what an agent
316
359
  * is allowed to spend money on. Budget, permissions, and purchase rules are the
@@ -369,6 +412,8 @@ export interface AgentResult {
369
412
  purchased: AgentPurchase[];
370
413
  /** Total spent across all purchases, decimal USD string. */
371
414
  totalCost: string;
415
+ /** A readable answer grounded in the content the agent actually purchased. */
416
+ answer: string;
372
417
  summary: string;
373
418
  }
374
419
  /** Staged progress emitted while the agent works, so a UI can render it live. */
@@ -408,11 +453,19 @@ export interface AgentOptions {
408
453
  task?: string;
409
454
  /** Discovery query sent to the publisher (default: derived from the task). */
410
455
  query?: string;
456
+ /**
457
+ * Minimum discovery relevance (0–1) the agent will pay for. Surfaced chunks
458
+ * below this bar are skipped as "low relevance" — so the agent does not blindly
459
+ * buy everything discovery returns. Default 0.3.
460
+ */
461
+ confidenceThreshold?: number;
411
462
  /** Called at each stage so the demo can render the flow step by step. */
412
463
  onProgress?: (p: AgentProgress) => void;
413
464
  /** Pause between steps (ms) so judges can watch the lifecycle stream. */
414
465
  stepDelayMs?: number;
415
466
  }
467
+ /** The agent's default relevance bar: it won't pay for weaker discovery matches. */
468
+ export declare const DEFAULT_CONFIDENCE_THRESHOLD = 0.3;
416
469
  /**
417
470
  * Simulates an autonomous agent that **discovers before it buys** — so it never
418
471
  * pays blindly:
@@ -469,6 +522,12 @@ export interface NanoRailInstance {
469
522
  client: NanoRailClient;
470
523
  widget?: NanoRailWidget;
471
524
  manifest: ArticleManifest;
525
+ /**
526
+ * Resolves once the page's chunks + gated content have been registered with the
527
+ * gateway (undefined if there were no chunks or the call failed). Lets a
528
+ * publisher page show a "registered N chunks" confirmation.
529
+ */
530
+ registered: Promise<RegistrationResult | undefined>;
472
531
  rescan: () => void;
473
532
  regenerateManifest: () => Promise<ArticleManifest>;
474
533
  runAgent: (opts?: AgentOptions) => Promise<AgentResult>;
package/dist/index.mjs CHANGED
@@ -594,6 +594,9 @@ var NanoRailClient = class {
594
594
  __publicField(this, "session");
595
595
  __publicField(this, "spentMicros", 0);
596
596
  __publicField(this, "receipts", []);
597
+ /** Content already unlocked this session, by chunkId — lets a re-run reuse what
598
+ * was bought instead of re-paying (the gateway rejects duplicate payments). */
599
+ __publicField(this, "unlocked", /* @__PURE__ */ new Map());
597
600
  __publicField(this, "blocked", []);
598
601
  /** Chunk ids the user has manually approved (for amounts above the threshold). */
599
602
  __publicField(this, "approvals", /* @__PURE__ */ new Set());
@@ -639,6 +642,20 @@ var NanoRailClient = class {
639
642
  });
640
643
  return await res.json().catch(() => ({ ok: false }));
641
644
  }
645
+ /**
646
+ * Register the publisher's scanned chunks WITH their gated content. The gateway
647
+ * stores the content server-side and returns it only from a verified unlock;
648
+ * discovery/manifest expose only the metadata. This is what makes a real
649
+ * publisher's own content (not seeded demo content) the thing that unlocks.
650
+ */
651
+ async registerArticle(registration) {
652
+ const res = await this.fetchImpl(`${this.gatewayUrl}/api/articles/${registration.articleId}/register`, {
653
+ method: "POST",
654
+ headers: { "content-type": "application/json" },
655
+ body: JSON.stringify(registration)
656
+ });
657
+ return await res.json().catch(() => ({ ok: false }));
658
+ }
642
659
  /** Open a funded session once; concurrent callers share the same promise. */
643
660
  async ensureSession() {
644
661
  if (this.session) return this.session;
@@ -737,6 +754,7 @@ var NanoRailClient = class {
737
754
  }
738
755
  const out = await res.json();
739
756
  this.receipts.push(out.receipt);
757
+ this.unlocked.set(chunk.chunkId, { content: out.content, title: out.title });
740
758
  this.spentMicros = toMicros(out.session.spent);
741
759
  if (this.session) this.session.spentMicros = this.spentMicros;
742
760
  this.events.emit({
@@ -1096,6 +1114,33 @@ function generateManifest(opts = {}) {
1096
1114
  chunks
1097
1115
  };
1098
1116
  }
1117
+ function scanRegistration(opts = {}) {
1118
+ const root = opts.root ?? document;
1119
+ const els = Array.from(root.querySelectorAll("[data-nanorail-price]"));
1120
+ const chunks = els.map((el) => {
1121
+ const c = readChunk(el);
1122
+ const content = (el.innerHTML || el.textContent || "").trim();
1123
+ return {
1124
+ chunkId: c.chunkId,
1125
+ type: c.type,
1126
+ title: c.title,
1127
+ price: c.price,
1128
+ currency: "USD",
1129
+ recipient: c.recipient,
1130
+ teaser: c.teaser ?? c.title,
1131
+ tags: c.tags ?? [],
1132
+ endpoints: {
1133
+ challenge: `/api/chunks/${c.chunkId}/challenge`,
1134
+ unlock: `/api/chunks/${c.chunkId}/unlock`
1135
+ },
1136
+ content
1137
+ };
1138
+ });
1139
+ const articleId = opts.articleId ?? els[0]?.dataset.nanorailArticle ?? "unknown-article";
1140
+ const publisherId = opts.publisherId ?? els[0]?.dataset.nanorailPublisher ?? "unknown-publisher";
1141
+ const title = opts.title ?? (root instanceof Document ? root.querySelector("h1")?.textContent?.trim() : void 0) ?? (typeof document !== "undefined" ? document.title : void 0) ?? articleId;
1142
+ return { publisherId, articleId, title, source: "sdk-scan", chunks };
1143
+ }
1099
1144
 
1100
1145
  // src/widget.ts
1101
1146
  var nrMark = (fill, size = 15) => `<svg width="${size}" height="${size}" viewBox="0 0 64 64" style="vertical-align:-3px;flex:none"><g fill="${fill}"><circle cx="14" cy="12" r="4"/><circle cx="14" cy="22" r="4"/><circle cx="14" cy="32" r="4"/><circle cx="14" cy="42" r="4"/><circle cx="14" cy="52" r="4"/><circle cx="54" cy="12" r="4"/><circle cx="54" cy="22" r="4"/><circle cx="54" cy="42" r="4"/><circle cx="54" cy="52" r="4"/><circle cx="24" cy="22" r="4"/><circle cx="34" cy="32" r="4"/><circle cx="44" cy="42" r="4"/></g></svg>`;
@@ -1168,7 +1213,7 @@ var NanoRailWidget = class {
1168
1213
  const p = s.policy;
1169
1214
  const budgetMicros = toMicros(s.budget);
1170
1215
  const remaining = fromMicros(Math.max(0, budgetMicros - s.spent));
1171
- const pct2 = Math.min(100, Math.round(s.spent / Math.max(1, budgetMicros) * 100));
1216
+ const pct3 = Math.min(100, Math.round(s.spent / Math.max(1, budgetMicros) * 100));
1172
1217
  if (this.collapsed) {
1173
1218
  this.root.className = "nr-widget nr-collapsed";
1174
1219
  this.root.innerHTML = `<button class="nr-w-fab" title="Agent Wallet">${nrMark("#0A0A0A", 14)} Agent Wallet \xB7 $${remaining}</button>`;
@@ -1197,7 +1242,7 @@ var NanoRailWidget = class {
1197
1242
 
1198
1243
  <div class="nr-w-budget">
1199
1244
  <div class="nr-w-budget-top"><span class="nr-w-budget-label">Remaining today</span><span class="nr-w-budget-val">$${remaining}</span></div>
1200
- <div class="nr-w-bar"><i style="width:${pct2}%"></i></div>
1245
+ <div class="nr-w-bar"><i style="width:${pct3}%"></i></div>
1201
1246
  <div class="nr-w-budget-foot"><span>Daily budget $${esc(s.budget)}</span><span>Spent $${fromMicros(s.spent)}</span></div>
1202
1247
  </div>
1203
1248
 
@@ -1304,7 +1349,9 @@ function injectStyles() {
1304
1349
  // src/agent.ts
1305
1350
  var DEFAULT_TASK = "Summarize MPP payments and receipts";
1306
1351
  var DEFAULT_QUERY = "MPP payments and receipts";
1352
+ var DEFAULT_CONFIDENCE_THRESHOLD = 0.3;
1307
1353
  var sleep2 = (ms) => ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
1354
+ var pct = (x) => `${Math.round(x * 100)}%`;
1308
1355
  function descriptor(manifest, m) {
1309
1356
  return {
1310
1357
  chunkId: m.chunkId,
@@ -1324,6 +1371,7 @@ async function runAgentSummary(client, articleId = "the-agentic-chronicle", opts
1324
1371
  const delay = opts.stepDelayMs ?? 0;
1325
1372
  const task = opts.task ?? DEFAULT_TASK;
1326
1373
  const query = opts.query ?? DEFAULT_QUERY;
1374
+ const threshold = opts.confidenceThreshold ?? DEFAULT_CONFIDENCE_THRESHOLD;
1327
1375
  emit({ phase: "task", task, query });
1328
1376
  const manifest = await client.getManifest(articleId);
1329
1377
  const matches = await client.discover(articleId, query, "agent");
@@ -1334,13 +1382,17 @@ async function runAgentSummary(client, articleId = "the-agentic-chronicle", opts
1334
1382
  const chunk = descriptor(manifest, m);
1335
1383
  const decision = client.evaluate(chunk);
1336
1384
  const confidence = confidenceById.get(m.chunkId);
1385
+ const base = { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, confidence };
1337
1386
  if (!decision.allowed) {
1338
- return { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, action: "block", reason: decision.reason ?? "blocked by policy", confidence };
1387
+ return { ...base, action: "block", reason: decision.reason ?? "blocked by policy" };
1339
1388
  }
1340
- if (confidence !== void 0) {
1341
- return { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, action: "buy", reason: `discovery match (confidence ${confidence})`, confidence };
1389
+ if (confidence === void 0) {
1390
+ return { ...base, action: "skip", reason: "not relevant to the task" };
1342
1391
  }
1343
- return { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, action: "skip", reason: "not surfaced by discovery" };
1392
+ if (confidence < threshold) {
1393
+ return { ...base, action: "skip", reason: `relevance ${pct(confidence)} below ${pct(threshold)} bar` };
1394
+ }
1395
+ return { ...base, action: "buy", reason: `relevance ${pct(confidence)} \xB7 within budget & allowed` };
1344
1396
  });
1345
1397
  emit({ phase: "plan", plan });
1346
1398
  await sleep2(delay);
@@ -1365,6 +1417,18 @@ async function runAgentSummary(client, articleId = "the-agentic-chronicle", opts
1365
1417
  emit({ phase: "blocked", chunkId: item.chunkId, reason: item.reason });
1366
1418
  continue;
1367
1419
  }
1420
+ const ownedReceipt = client.receipts.find((r) => r.chunkId === item.chunkId);
1421
+ if (ownedReceipt) {
1422
+ const cached = client.unlocked.get(item.chunkId);
1423
+ receiptIds.push(ownedReceipt.receiptId);
1424
+ purchased.push({ chunkId: item.chunkId, title: item.title, type: item.type, price: item.price, receiptId: ownedReceipt.receiptId });
1425
+ totalMicros += toMicros(item.price);
1426
+ if (item.type === "text" && cached?.content) boughtText.push(cached.content);
1427
+ steps.push({ chunkId: item.chunkId, type: item.type, decision: "bought", receiptId: ownedReceipt.receiptId, content: cached?.content });
1428
+ emit({ phase: "bought", chunkId: item.chunkId, receiptId: ownedReceipt.receiptId, content: cached?.content });
1429
+ await sleep2(delay);
1430
+ continue;
1431
+ }
1368
1432
  const out = await client.unlock(chunk, { actor: "agent", viaDiscovery: true });
1369
1433
  if (out.ok && out.receipt) {
1370
1434
  receiptIds.push(out.receipt.receiptId);
@@ -1380,8 +1444,12 @@ async function runAgentSummary(client, articleId = "the-agentic-chronicle", opts
1380
1444
  }
1381
1445
  await sleep2(delay);
1382
1446
  }
1447
+ const skippedCount = plan.filter((p) => p.action === "skip").length;
1448
+ const blockedItems = plan.filter((p) => p.action === "block");
1449
+ const totalCost = fromMicros(totalMicros);
1450
+ const answer = buildAnswer({ task, purchased, boughtText, skippedCount, blockedItems, totalCost });
1383
1451
  const summary = `Task: ${task}
1384
- Discovered ${matches.length} relevant chunk(s); bought ${receiptIds.length}; skipped the rest; video blocked by policy.
1452
+ Discovered ${matches.length} relevant chunk(s); bought ${receiptIds.length}; skipped ${skippedCount}; blocked ${blockedItems.length} by policy.
1385
1453
 
1386
1454
  ` + boughtText.map((t) => `\u2022 ${t}`).join("\n\n") + `
1387
1455
 
@@ -1394,21 +1462,45 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1394
1462
  steps,
1395
1463
  receiptIds,
1396
1464
  purchased,
1397
- totalCost: fromMicros(totalMicros),
1465
+ totalCost,
1466
+ answer,
1398
1467
  summary
1399
1468
  };
1400
1469
  emit({ phase: "done", result });
1401
1470
  return result;
1402
1471
  }
1472
+ function buildAnswer(args) {
1473
+ const { task, purchased, boughtText, skippedCount, blockedItems, totalCost } = args;
1474
+ if (purchased.length === 0) {
1475
+ const why = blockedItems.length ? ` Policy blocked ${blockedItems.map((b) => b.title).join(", ")}.` : "";
1476
+ return `The agent did not spend on \u201C${task}\u201D: nothing it discovered was both relevant enough and permitted by your policy.${why}`;
1477
+ }
1478
+ const lead = `Based on the ${purchased.length} source${purchased.length === 1 ? "" : "s"} the agent purchased for \u201C${task}\u201D:`;
1479
+ const body = boughtText.length ? `
1480
+
1481
+ ${boughtText.join("\n\n")}` : "";
1482
+ const support = purchased.filter((p) => p.type !== "text");
1483
+ const supportLine = support.length ? `
1484
+
1485
+ Supporting data purchased: ${support.map((p) => `${p.title} (${p.type.replace(/_/g, " ")})`).join("; ")}.` : "";
1486
+ const decisions = [];
1487
+ if (skippedCount > 0) decisions.push(`skipped ${skippedCount} less-relevant section${skippedCount === 1 ? "" : "s"}`);
1488
+ if (blockedItems.length > 0) decisions.push(`was blocked by your policy from buying ${blockedItems.map((b) => b.title).join(", ")}`);
1489
+ const decisionLine = decisions.length ? ` The agent ${decisions.join(", and ")}.` : "";
1490
+ return `${lead}${body}${supportLine}
1491
+
1492
+ Total spend: $${totalCost} across ${purchased.length} receipt${purchased.length === 1 ? "" : "s"}.${decisionLine}`;
1493
+ }
1403
1494
 
1404
1495
  // src/agent-console.ts
1405
1496
  var DEFAULT_AGENT_TASKS = [
1406
- { label: "Find evidence supporting MPP receipts", query: "MPP receipts and payment verification" },
1407
- { label: "Explain Tempo session payments", query: "Tempo session payments and vouchers" },
1408
- { label: "Summarize machine vs human readership", query: "machine vs human read trends data chart" }
1497
+ { label: "Find evidence on MPP receipts", query: "MPP receipts citations and sources" },
1498
+ { label: "Chart the machine-vs-human shift", query: "machine vs human read share trends data chart" },
1499
+ { label: "Use the explainer video + the thesis", query: "agentic economy explainer video and the metering thesis" }
1409
1500
  ];
1501
+ var ACTION_LABEL = { buy: "BUY", skip: "SKIP", block: "BLOCKED" };
1410
1502
  var esc2 = (s) => s.replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c]);
1411
- var pct = (c) => `${Math.round(c * 100)}%`;
1503
+ var pct2 = (c) => `${Math.round(c * 100)}%`;
1412
1504
  function mountAgentConsole(instance, opts = {}) {
1413
1505
  injectStyles2();
1414
1506
  const tasks = opts.tasks ?? DEFAULT_AGENT_TASKS;
@@ -1460,7 +1552,7 @@ function mountAgentConsole(instance, opts = {}) {
1460
1552
  };
1461
1553
  });
1462
1554
  const p = instance.client.getPolicy();
1463
- policyEl.innerHTML = `<div class="nr-ac-pol-row"><span>Budget</span><strong>$${esc2(p.sessionBudget)}</strong></div><div class="nr-ac-pol-row"><span>Allowed</span><span class="nr-ac-allow">${p.allowedContentTypes.map((t) => esc2(t)).join(", ")}</span></div><div class="nr-ac-pol-row"><span>Blocked</span><span class="nr-ac-block-types">${p.blockedContentTypes.map((t) => esc2(t)).join(", ") || "\u2014"}</span></div>`;
1555
+ policyEl.innerHTML = `<div class="nr-ac-pol-row"><span>Budget</span><strong>$${esc2(p.sessionBudget)}</strong></div><div class="nr-ac-pol-row"><span>Max per purchase</span><strong>$${esc2(p.maxSpendPerChunk)}</strong></div><div class="nr-ac-pol-row"><span>Allowed</span><span class="nr-ac-allow">${p.allowedContentTypes.map((t) => esc2(t)).join(", ")}</span></div><div class="nr-ac-pol-row"><span>Blocked</span><span class="nr-ac-block-types">${p.blockedContentTypes.map((t) => esc2(t)).join(", ") || "\u2014"}</span></div><div class="nr-ac-pol-row"><span>Min. relevance to buy</span><strong>${pct2(DEFAULT_CONFIDENCE_THRESHOLD)}</strong></div>`;
1464
1556
  const open = () => overlay.classList.add("open");
1465
1557
  const close = () => overlay.classList.remove("open");
1466
1558
  fab.onclick = open;
@@ -1497,7 +1589,7 @@ function mountAgentConsole(instance, opts = {}) {
1497
1589
  priceOf.set(m.chunkId, m.price);
1498
1590
  }
1499
1591
  discoveryEl.innerHTML = `<div class="nr-ac-label">Publisher Discovery Agent \u2014 found ${ev.matches.length} relevant paid chunk${ev.matches.length === 1 ? "" : "s"} (no premium content revealed)</div>` + (ev.matches.length ? ev.matches.map(
1500
- (m, i) => `<div class="nr-ac-disc"><span class="nr-ac-disc-n">${i + 1}</span><div class="nr-ac-disc-main"><b>${esc2(m.title)}</b><span class="nr-ac-disc-teaser">${esc2(m.teaser)}</span><span class="nr-ac-disc-tags">${m.tags.map((t) => `#${esc2(t)}`).join(" ")}</span></div><div class="nr-ac-disc-meta"><span class="nr-ac-type t-${m.type}">${esc2(m.type)}</span><span class="nr-ac-price">$${esc2(m.price)}</span><span class="nr-ac-conf">${pct(m.confidence)} match</span></div></div>`
1592
+ (m, i) => `<div class="nr-ac-disc" data-cid="${esc2(m.chunkId)}"><div class="nr-ac-disc-top"><span class="nr-ac-disc-n">${i + 1}</span><div class="nr-ac-disc-main"><b>${esc2(m.title)}</b><span class="nr-ac-disc-teaser">${esc2(m.teaser)}</span><span class="nr-ac-disc-tags">${m.tags.map((t) => `#${esc2(t)}`).join(" ")}</span></div><div class="nr-ac-disc-meta"><span class="nr-ac-type t-${m.type}">${esc2(m.type)}</span><span class="nr-ac-price">$${esc2(m.price)}</span><span class="nr-ac-conf">${pct2(m.confidence)} match</span></div></div><div class="nr-ac-decision" data-decision="${esc2(m.chunkId)}"><span class="nr-ac-dec-wait">deciding\u2026</span></div></div>`
1501
1593
  ).join("") : '<div class="nr-ac-empty">No relevant paid content \u2014 the agent will not spend.</div>');
1502
1594
  row("found", `<span class="nr-ac-dot"></span> Found <strong>${ev.matches.length}</strong> purchasable chunk${ev.matches.length === 1 ? "" : "s"}`);
1503
1595
  } else if (ev.phase === "plan") {
@@ -1506,18 +1598,20 @@ function mountAgentConsole(instance, opts = {}) {
1506
1598
  priceOf.set(it.chunkId, it.price);
1507
1599
  }
1508
1600
  row("eval", `<span class="nr-ac-dot"></span> Evaluating relevance, price &amp; policy\u2026`);
1601
+ annotateDecisions(discoveryEl, ev.plan);
1509
1602
  } else if (ev.phase === "chunk-start") {
1510
- if (ev.item.action === "buy") row("buy", `<span class="nr-ac-dot"></span> Purchasing <b>${esc2(ev.item.title)}</b> <span class="nr-ac-cost">$${esc2(ev.item.price)}</span>\u2026`);
1603
+ if (ev.item.action === "buy") row("buy", `<span class="nr-ac-dot"></span> Policy \u2713 \u2014 authorizing <b>${esc2(ev.item.title)}</b> <span class="nr-ac-cost">$${esc2(ev.item.price)}</span>\u2026`);
1511
1604
  } else if (ev.phase === "bought") {
1512
- row("receipt", `<span class="nr-ac-dot ok"></span> Receipt issued \xB7 <code>${esc2(ev.receiptId)}</code>`);
1605
+ row("receipt", `<span class="nr-ac-dot ok"></span> Voucher authorized \xB7 receipt <code>${esc2(ev.receiptId)}</code>`);
1606
+ row("unlock", `<span class="nr-ac-dot ok"></span> Content unlocked`);
1513
1607
  } else if (ev.phase === "blocked") {
1514
- row("blocked", `<span class="nr-ac-dot no"></span> Skipped <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 ${esc2(ev.reason)}`);
1608
+ row("blocked", `<span class="nr-ac-dot no"></span> Blocked <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 ${esc2(ev.reason)}`);
1515
1609
  } else if (ev.phase === "skipped") {
1516
- row("skip", `<span class="nr-ac-dot"></span> Skipped <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 not relevant to the task`);
1610
+ row("skip", `<span class="nr-ac-dot"></span> Skipped <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 ${esc2(ev.reason)}`);
1517
1611
  } else if (ev.phase === "done") {
1518
- row("gen", `<span class="nr-ac-dot"></span> Generating answer\u2026`);
1612
+ row("gen", `<span class="nr-ac-dot"></span> Generating answer from purchased content\u2026`);
1519
1613
  row("done", `<span class="nr-ac-dot ok"></span> Done`);
1520
- renderOutput(outputEl, ev.result.purchased, ev.result.receiptIds, ev.result.totalCost);
1614
+ renderOutput(outputEl, ev.result);
1521
1615
  }
1522
1616
  };
1523
1617
  try {
@@ -1530,9 +1624,29 @@ function mountAgentConsole(instance, opts = {}) {
1530
1624
  };
1531
1625
  return { open, close };
1532
1626
  }
1533
- function renderOutput(el, purchased, receiptIds, totalCost) {
1534
- const sources = purchased.length ? purchased.map((s) => `<li>${esc2(s.title)} <span class="nr-ac-cost">$${esc2(s.price)}</span></li>`).join("") : '<li class="nr-ac-empty">None \u2014 nothing met the task + policy bar.</li>';
1535
- el.innerHTML = `<div class="nr-ac-out-h">Generated answer</div><div class="nr-ac-out-grid"><div><div class="nr-ac-out-label">Sources purchased</div><ul class="nr-ac-sources">${sources}</ul></div><div><div class="nr-ac-out-label">Receipts</div><div class="nr-ac-receipts">${receiptIds.map((r) => `<code>${esc2(r)}</code>`).join(" ") || "\u2014"}</div><div class="nr-ac-out-label" style="margin-top:12px">Total cost</div><div class="nr-ac-total">$${esc2(totalCost)}</div></div></div><div class="nr-ac-thesis">The agent discovered what was available, decided what was worth buying, and paid \u2014 on Tempo, with receipts.</div>`;
1627
+ function renderOutput(el, result) {
1628
+ const { answer, purchased, totalCost } = result;
1629
+ const sources = purchased.length ? purchased.map(
1630
+ (s) => `<li><span class="nr-ac-src-title">${esc2(s.title)}</span><span class="nr-ac-cost">$${esc2(s.price)}</span><code>${esc2(s.receiptId)}</code></li>`
1631
+ ).join("") : '<li class="nr-ac-empty">None \u2014 nothing met the task + policy bar.</li>';
1632
+ el.innerHTML = `<div class="nr-ac-out-h">Generated answer <em>\u2014 grounded only in what the agent paid for</em></div><div class="nr-ac-answer">${esc2(answer)}</div><div class="nr-ac-out-foot"><div class="nr-ac-out-srcs"><div class="nr-ac-out-label">Purchased sources \xB7 receipts</div><ul class="nr-ac-sources">${sources}</ul></div><div class="nr-ac-out-total"><div class="nr-ac-out-label">Total cost</div><div class="nr-ac-total">$${esc2(totalCost)}</div></div></div><div class="nr-ac-thesis">The agent discovered what was available, decided what was worth buying, and paid \u2014 on Tempo, with receipts.</div>`;
1633
+ }
1634
+ function decisionHtml(item) {
1635
+ return `<span class="nr-ac-dec-pill ${item.action}">${ACTION_LABEL[item.action]}</span><span class="nr-ac-dec-reason">${esc2(item.reason)}</span>`;
1636
+ }
1637
+ function annotateDecisions(container, plan) {
1638
+ for (const item of plan) {
1639
+ const slot = container.querySelector(`.nr-ac-decision[data-decision="${item.chunkId}"]`);
1640
+ if (slot) {
1641
+ slot.className = `nr-ac-decision act-${item.action}`;
1642
+ slot.innerHTML = decisionHtml(item);
1643
+ } else if (item.action === "block") {
1644
+ const card = document.createElement("div");
1645
+ card.className = "nr-ac-disc nr-ac-disc-extra";
1646
+ card.innerHTML = `<div class="nr-ac-disc-top"><span class="nr-ac-disc-n no">\u26D4</span><div class="nr-ac-disc-main"><b>${esc2(item.title)}</b><span class="nr-ac-disc-teaser">Not offered by discovery \u2014 your policy blocks it before any purchase.</span></div><div class="nr-ac-disc-meta"><span class="nr-ac-type t-${item.type}">${esc2(item.type)}</span><span class="nr-ac-price">$${esc2(item.price)}</span></div></div><div class="nr-ac-decision act-block">${decisionHtml(item)}</div>`;
1647
+ container.appendChild(card);
1648
+ }
1649
+ }
1536
1650
  }
1537
1651
  function markSvg(fill = "#FFC247") {
1538
1652
  return `<svg width="15" height="15" viewBox="0 0 64 64" style="vertical-align:-2px;flex:none"><g fill="${fill}"><circle cx="14" cy="12" r="4"/><circle cx="14" cy="22" r="4"/><circle cx="14" cy="32" r="4"/><circle cx="14" cy="42" r="4"/><circle cx="14" cy="52" r="4"/><circle cx="54" cy="12" r="4"/><circle cx="54" cy="22" r="4"/><circle cx="54" cy="42" r="4"/><circle cx="54" cy="52" r="4"/><circle cx="24" cy="22" r="4"/><circle cx="34" cy="32" r="4"/><circle cx="44" cy="42" r="4"/></g></svg>`;
@@ -1564,8 +1678,19 @@ function injectStyles2() {
1564
1678
  .nr-ac-run:disabled{opacity:.45;cursor:default}
1565
1679
  .nr-ac-run:not(:disabled):hover{background:#E2A12F}
1566
1680
  .nr-ac-block{margin-top:20px}
1567
- .nr-ac-disc{display:flex;gap:10px;align-items:flex-start;background:#0E0E0E;border:1px solid #1E1E1E;border-radius:10px;padding:11px 13px;margin-bottom:8px}
1681
+ .nr-ac-disc{display:flex;flex-direction:column;gap:9px;background:#0E0E0E;border:1px solid #1E1E1E;border-radius:10px;padding:11px 13px;margin-bottom:8px}
1682
+ .nr-ac-disc-top{display:flex;gap:10px;align-items:flex-start}
1683
+ .nr-ac-disc.nr-ac-disc-extra{border-style:dashed;border-color:#3b1d1d}
1568
1684
  .nr-ac-disc-n{width:20px;height:20px;flex:none;border-radius:6px;background:#161616;color:#FFC247;font-family:ui-monospace,monospace;font-size:11px;display:flex;align-items:center;justify-content:center;font-weight:700}
1685
+ .nr-ac-disc-n.no{background:#3b1d1d;color:#fca5a5}
1686
+ .nr-ac-decision{display:flex;align-items:center;gap:9px;border-top:1px dashed #1E1E1E;padding-top:8px}
1687
+ .nr-ac-dec-wait{color:#6E6E6E;font-size:11px;font-family:ui-monospace,monospace}
1688
+ .nr-ac-dec-pill{font-size:10px;font-weight:800;letter-spacing:.5px;padding:2px 8px;border-radius:6px;flex:none}
1689
+ .nr-ac-dec-pill.buy{background:#14321f;color:#86efac}
1690
+ .nr-ac-dec-pill.skip{background:#262626;color:#C4C4C4}
1691
+ .nr-ac-dec-pill.block{background:#3b1d1d;color:#fca5a5}
1692
+ .nr-ac-dec-reason{font-size:12px;color:#9A9A9A}
1693
+ .nr-ac-decision.act-buy .nr-ac-dec-reason{color:#86efac}
1569
1694
  .nr-ac-disc-main{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}
1570
1695
  .nr-ac-disc-main b{font-size:14px}
1571
1696
  .nr-ac-disc-teaser{color:#9A9A9A;font-size:12px}
@@ -1586,14 +1711,19 @@ function injectStyles2() {
1586
1711
  .nr-ac-tl.buy{color:#EDEDED}.nr-ac-tl.blocked{color:#fca5a5}
1587
1712
  .nr-ac-output{background:linear-gradient(180deg,#0E0E0E,#0B0B0B);border:1px solid #FFC247;border-radius:12px;padding:16px 18px}
1588
1713
  .nr-ac-out-h{font-weight:800;color:#FFC247;margin-bottom:12px}
1589
- .nr-ac-out-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}
1714
+ .nr-ac-out-h em{color:#8A8A8A;font-style:normal;font-weight:400;font-size:12px}
1715
+ .nr-ac-answer{white-space:pre-wrap;font-size:13.5px;line-height:1.6;color:#E4E4E4;background:#0B0B0B;border:1px solid #1E1E1E;border-radius:10px;padding:13px 15px;margin-bottom:14px}
1716
+ .nr-ac-out-foot{display:grid;grid-template-columns:1fr auto;gap:18px;align-items:start}
1590
1717
  .nr-ac-out-label{font-family:ui-monospace,monospace;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:#8A8A8A;margin-bottom:6px}
1591
- .nr-ac-sources{margin:0;padding-left:16px;font-size:13px}.nr-ac-sources li{margin:3px 0}
1592
- .nr-ac-receipts code{font-family:ui-monospace,monospace;font-size:11px;color:#FFC247;background:#161616;padding:1px 5px;border-radius:4px;margin-right:4px}
1593
- .nr-ac-total{font-family:ui-monospace,monospace;font-size:22px;font-weight:800;color:#FFC247}
1718
+ .nr-ac-sources{list-style:none;margin:0;padding:0;font-size:13px}
1719
+ .nr-ac-sources li{display:flex;align-items:center;gap:8px;margin:5px 0}
1720
+ .nr-ac-src-title{flex:1;min-width:0;color:#EDEDED}
1721
+ .nr-ac-sources code{font-family:ui-monospace,monospace;font-size:10.5px;color:#FFC247;background:#161616;padding:1px 6px;border-radius:4px}
1722
+ .nr-ac-out-total{text-align:right}
1723
+ .nr-ac-total{font-family:ui-monospace,monospace;font-size:24px;font-weight:800;color:#FFC247}
1594
1724
  .nr-ac-thesis{margin-top:14px;padding-top:12px;border-top:1px solid #1E1E1E;color:#9A9A9A;font-size:13px}
1595
1725
  .nr-ac-empty{color:#8A8A8A}
1596
- @media (max-width:560px){.nr-ac-out-grid{grid-template-columns:1fr}}
1726
+ @media (max-width:560px){.nr-ac-out-foot{grid-template-columns:1fr}.nr-ac-out-total{text-align:left}}
1597
1727
  `;
1598
1728
  const style = document.createElement("style");
1599
1729
  style.id = "nr-ac-styles";
@@ -1609,20 +1739,20 @@ function initNanoRail(config = {}) {
1609
1739
  const widget = config.mountWidget === false ? void 0 : new NanoRailWidget(client);
1610
1740
  client.ensureSession().then(() => widget?.render());
1611
1741
  client.startSettlementWatch();
1612
- const buildAndRegister = async () => {
1613
- const m = generateManifest({ articleId });
1614
- if (m.chunks.length > 0) await client.registerManifest(m).catch(() => {
1615
- });
1616
- return m;
1617
- };
1742
+ const registration = scanRegistration({ articleId });
1743
+ const registered = registration.chunks.length > 0 ? client.registerArticle(registration).catch(() => void 0) : Promise.resolve(void 0);
1618
1744
  const instance = {
1619
1745
  client,
1620
1746
  widget,
1621
1747
  manifest: generateManifest({ articleId }),
1748
+ registered,
1622
1749
  rescan: () => scanAndLock(client, document, { autoUnlock: config.autoUnlockOnScroll === true }),
1623
1750
  regenerateManifest: async () => {
1624
- instance.manifest = await buildAndRegister();
1625
- return instance.manifest;
1751
+ const m = generateManifest({ articleId });
1752
+ if (m.chunks.length > 0) await client.registerManifest(m).catch(() => {
1753
+ });
1754
+ instance.manifest = m;
1755
+ return m;
1626
1756
  },
1627
1757
  runAgent: (opts) => runAgentSummary(client, articleId, opts),
1628
1758
  forceVideoRejection: async () => {
@@ -1633,9 +1763,6 @@ function initNanoRail(config = {}) {
1633
1763
  },
1634
1764
  unlockTempoSnippet: () => client.unlockTempoSnippet()
1635
1765
  };
1636
- buildAndRegister().then((m) => {
1637
- instance.manifest = m;
1638
- });
1639
1766
  if (config.autoScan !== false) {
1640
1767
  scanAndLock(client, document, { autoUnlock: config.autoUnlockOnScroll === true });
1641
1768
  }
@@ -1647,6 +1774,7 @@ function initNanoRail(config = {}) {
1647
1774
  }
1648
1775
  export {
1649
1776
  DEFAULT_AGENT_TASKS,
1777
+ DEFAULT_CONFIDENCE_THRESHOLD,
1650
1778
  NanoRailClient,
1651
1779
  NanoRailEvents,
1652
1780
  NanoRailWidget,
@@ -1658,6 +1786,7 @@ export {
1658
1786
  renderContent,
1659
1787
  runAgentSummary,
1660
1788
  scanAndLock,
1789
+ scanRegistration,
1661
1790
  shouldAutoUnlock
1662
1791
  };
1663
1792
  /*! Bundled license information:
package/dist/nanorail.js CHANGED
@@ -596,6 +596,9 @@
596
596
  __publicField(this, "session");
597
597
  __publicField(this, "spentMicros", 0);
598
598
  __publicField(this, "receipts", []);
599
+ /** Content already unlocked this session, by chunkId — lets a re-run reuse what
600
+ * was bought instead of re-paying (the gateway rejects duplicate payments). */
601
+ __publicField(this, "unlocked", /* @__PURE__ */ new Map());
599
602
  __publicField(this, "blocked", []);
600
603
  /** Chunk ids the user has manually approved (for amounts above the threshold). */
601
604
  __publicField(this, "approvals", /* @__PURE__ */ new Set());
@@ -641,6 +644,20 @@
641
644
  });
642
645
  return await res.json().catch(() => ({ ok: false }));
643
646
  }
647
+ /**
648
+ * Register the publisher's scanned chunks WITH their gated content. The gateway
649
+ * stores the content server-side and returns it only from a verified unlock;
650
+ * discovery/manifest expose only the metadata. This is what makes a real
651
+ * publisher's own content (not seeded demo content) the thing that unlocks.
652
+ */
653
+ async registerArticle(registration) {
654
+ const res = await this.fetchImpl(`${this.gatewayUrl}/api/articles/${registration.articleId}/register`, {
655
+ method: "POST",
656
+ headers: { "content-type": "application/json" },
657
+ body: JSON.stringify(registration)
658
+ });
659
+ return await res.json().catch(() => ({ ok: false }));
660
+ }
644
661
  /** Open a funded session once; concurrent callers share the same promise. */
645
662
  async ensureSession() {
646
663
  if (this.session) return this.session;
@@ -739,6 +756,7 @@
739
756
  }
740
757
  const out = await res.json();
741
758
  this.receipts.push(out.receipt);
759
+ this.unlocked.set(chunk.chunkId, { content: out.content, title: out.title });
742
760
  this.spentMicros = toMicros(out.session.spent);
743
761
  if (this.session) this.session.spentMicros = this.spentMicros;
744
762
  this.events.emit({
@@ -941,7 +959,7 @@
941
959
  const p = s.policy;
942
960
  const budgetMicros = toMicros(s.budget);
943
961
  const remaining = fromMicros(Math.max(0, budgetMicros - s.spent));
944
- const pct2 = Math.min(100, Math.round(s.spent / Math.max(1, budgetMicros) * 100));
962
+ const pct3 = Math.min(100, Math.round(s.spent / Math.max(1, budgetMicros) * 100));
945
963
  if (this.collapsed) {
946
964
  this.root.className = "nr-widget nr-collapsed";
947
965
  this.root.innerHTML = `<button class="nr-w-fab" title="Agent Wallet">${nrMark("#0A0A0A", 14)} Agent Wallet \xB7 $${remaining}</button>`;
@@ -970,7 +988,7 @@
970
988
 
971
989
  <div class="nr-w-budget">
972
990
  <div class="nr-w-budget-top"><span class="nr-w-budget-label">Remaining today</span><span class="nr-w-budget-val">$${remaining}</span></div>
973
- <div class="nr-w-bar"><i style="width:${pct2}%"></i></div>
991
+ <div class="nr-w-bar"><i style="width:${pct3}%"></i></div>
974
992
  <div class="nr-w-budget-foot"><span>Daily budget $${esc(s.budget)}</span><span>Spent $${fromMicros(s.spent)}</span></div>
975
993
  </div>
976
994
 
@@ -1302,11 +1320,40 @@
1302
1320
  chunks
1303
1321
  };
1304
1322
  }
1323
+ function scanRegistration(opts = {}) {
1324
+ const root = opts.root ?? document;
1325
+ const els = Array.from(root.querySelectorAll("[data-nanorail-price]"));
1326
+ const chunks = els.map((el) => {
1327
+ const c = readChunk(el);
1328
+ const content = (el.innerHTML || el.textContent || "").trim();
1329
+ return {
1330
+ chunkId: c.chunkId,
1331
+ type: c.type,
1332
+ title: c.title,
1333
+ price: c.price,
1334
+ currency: "USD",
1335
+ recipient: c.recipient,
1336
+ teaser: c.teaser ?? c.title,
1337
+ tags: c.tags ?? [],
1338
+ endpoints: {
1339
+ challenge: `/api/chunks/${c.chunkId}/challenge`,
1340
+ unlock: `/api/chunks/${c.chunkId}/unlock`
1341
+ },
1342
+ content
1343
+ };
1344
+ });
1345
+ const articleId = opts.articleId ?? els[0]?.dataset.nanorailArticle ?? "unknown-article";
1346
+ const publisherId = opts.publisherId ?? els[0]?.dataset.nanorailPublisher ?? "unknown-publisher";
1347
+ const title = opts.title ?? (root instanceof Document ? root.querySelector("h1")?.textContent?.trim() : void 0) ?? (typeof document !== "undefined" ? document.title : void 0) ?? articleId;
1348
+ return { publisherId, articleId, title, source: "sdk-scan", chunks };
1349
+ }
1305
1350
 
1306
1351
  // src/agent.ts
1307
1352
  var DEFAULT_TASK = "Summarize MPP payments and receipts";
1308
1353
  var DEFAULT_QUERY = "MPP payments and receipts";
1354
+ var DEFAULT_CONFIDENCE_THRESHOLD = 0.3;
1309
1355
  var sleep2 = (ms) => ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
1356
+ var pct = (x) => `${Math.round(x * 100)}%`;
1310
1357
  function descriptor(manifest, m) {
1311
1358
  return {
1312
1359
  chunkId: m.chunkId,
@@ -1326,6 +1373,7 @@
1326
1373
  const delay = opts.stepDelayMs ?? 0;
1327
1374
  const task = opts.task ?? DEFAULT_TASK;
1328
1375
  const query = opts.query ?? DEFAULT_QUERY;
1376
+ const threshold = opts.confidenceThreshold ?? DEFAULT_CONFIDENCE_THRESHOLD;
1329
1377
  emit({ phase: "task", task, query });
1330
1378
  const manifest = await client.getManifest(articleId);
1331
1379
  const matches = await client.discover(articleId, query, "agent");
@@ -1336,13 +1384,17 @@
1336
1384
  const chunk = descriptor(manifest, m);
1337
1385
  const decision = client.evaluate(chunk);
1338
1386
  const confidence = confidenceById.get(m.chunkId);
1387
+ const base = { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, confidence };
1339
1388
  if (!decision.allowed) {
1340
- return { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, action: "block", reason: decision.reason ?? "blocked by policy", confidence };
1389
+ return { ...base, action: "block", reason: decision.reason ?? "blocked by policy" };
1341
1390
  }
1342
- if (confidence !== void 0) {
1343
- return { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, action: "buy", reason: `discovery match (confidence ${confidence})`, confidence };
1391
+ if (confidence === void 0) {
1392
+ return { ...base, action: "skip", reason: "not relevant to the task" };
1344
1393
  }
1345
- return { chunkId: m.chunkId, type: m.type, title: m.title, price: m.price, action: "skip", reason: "not surfaced by discovery" };
1394
+ if (confidence < threshold) {
1395
+ return { ...base, action: "skip", reason: `relevance ${pct(confidence)} below ${pct(threshold)} bar` };
1396
+ }
1397
+ return { ...base, action: "buy", reason: `relevance ${pct(confidence)} \xB7 within budget & allowed` };
1346
1398
  });
1347
1399
  emit({ phase: "plan", plan });
1348
1400
  await sleep2(delay);
@@ -1367,6 +1419,18 @@
1367
1419
  emit({ phase: "blocked", chunkId: item.chunkId, reason: item.reason });
1368
1420
  continue;
1369
1421
  }
1422
+ const ownedReceipt = client.receipts.find((r) => r.chunkId === item.chunkId);
1423
+ if (ownedReceipt) {
1424
+ const cached = client.unlocked.get(item.chunkId);
1425
+ receiptIds.push(ownedReceipt.receiptId);
1426
+ purchased.push({ chunkId: item.chunkId, title: item.title, type: item.type, price: item.price, receiptId: ownedReceipt.receiptId });
1427
+ totalMicros += toMicros(item.price);
1428
+ if (item.type === "text" && cached?.content) boughtText.push(cached.content);
1429
+ steps.push({ chunkId: item.chunkId, type: item.type, decision: "bought", receiptId: ownedReceipt.receiptId, content: cached?.content });
1430
+ emit({ phase: "bought", chunkId: item.chunkId, receiptId: ownedReceipt.receiptId, content: cached?.content });
1431
+ await sleep2(delay);
1432
+ continue;
1433
+ }
1370
1434
  const out = await client.unlock(chunk, { actor: "agent", viaDiscovery: true });
1371
1435
  if (out.ok && out.receipt) {
1372
1436
  receiptIds.push(out.receipt.receiptId);
@@ -1382,8 +1446,12 @@
1382
1446
  }
1383
1447
  await sleep2(delay);
1384
1448
  }
1449
+ const skippedCount = plan.filter((p) => p.action === "skip").length;
1450
+ const blockedItems = plan.filter((p) => p.action === "block");
1451
+ const totalCost = fromMicros(totalMicros);
1452
+ const answer = buildAnswer({ task, purchased, boughtText, skippedCount, blockedItems, totalCost });
1385
1453
  const summary = `Task: ${task}
1386
- Discovered ${matches.length} relevant chunk(s); bought ${receiptIds.length}; skipped the rest; video blocked by policy.
1454
+ Discovered ${matches.length} relevant chunk(s); bought ${receiptIds.length}; skipped ${skippedCount}; blocked ${blockedItems.length} by policy.
1387
1455
 
1388
1456
  ` + boughtText.map((t) => `\u2022 ${t}`).join("\n\n") + `
1389
1457
 
@@ -1396,21 +1464,45 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1396
1464
  steps,
1397
1465
  receiptIds,
1398
1466
  purchased,
1399
- totalCost: fromMicros(totalMicros),
1467
+ totalCost,
1468
+ answer,
1400
1469
  summary
1401
1470
  };
1402
1471
  emit({ phase: "done", result });
1403
1472
  return result;
1404
1473
  }
1474
+ function buildAnswer(args) {
1475
+ const { task, purchased, boughtText, skippedCount, blockedItems, totalCost } = args;
1476
+ if (purchased.length === 0) {
1477
+ const why = blockedItems.length ? ` Policy blocked ${blockedItems.map((b) => b.title).join(", ")}.` : "";
1478
+ return `The agent did not spend on \u201C${task}\u201D: nothing it discovered was both relevant enough and permitted by your policy.${why}`;
1479
+ }
1480
+ const lead = `Based on the ${purchased.length} source${purchased.length === 1 ? "" : "s"} the agent purchased for \u201C${task}\u201D:`;
1481
+ const body = boughtText.length ? `
1482
+
1483
+ ${boughtText.join("\n\n")}` : "";
1484
+ const support = purchased.filter((p) => p.type !== "text");
1485
+ const supportLine = support.length ? `
1486
+
1487
+ Supporting data purchased: ${support.map((p) => `${p.title} (${p.type.replace(/_/g, " ")})`).join("; ")}.` : "";
1488
+ const decisions = [];
1489
+ if (skippedCount > 0) decisions.push(`skipped ${skippedCount} less-relevant section${skippedCount === 1 ? "" : "s"}`);
1490
+ if (blockedItems.length > 0) decisions.push(`was blocked by your policy from buying ${blockedItems.map((b) => b.title).join(", ")}`);
1491
+ const decisionLine = decisions.length ? ` The agent ${decisions.join(", and ")}.` : "";
1492
+ return `${lead}${body}${supportLine}
1493
+
1494
+ Total spend: $${totalCost} across ${purchased.length} receipt${purchased.length === 1 ? "" : "s"}.${decisionLine}`;
1495
+ }
1405
1496
 
1406
1497
  // src/agent-console.ts
1407
1498
  var DEFAULT_AGENT_TASKS = [
1408
- { label: "Find evidence supporting MPP receipts", query: "MPP receipts and payment verification" },
1409
- { label: "Explain Tempo session payments", query: "Tempo session payments and vouchers" },
1410
- { label: "Summarize machine vs human readership", query: "machine vs human read trends data chart" }
1499
+ { label: "Find evidence on MPP receipts", query: "MPP receipts citations and sources" },
1500
+ { label: "Chart the machine-vs-human shift", query: "machine vs human read share trends data chart" },
1501
+ { label: "Use the explainer video + the thesis", query: "agentic economy explainer video and the metering thesis" }
1411
1502
  ];
1503
+ var ACTION_LABEL = { buy: "BUY", skip: "SKIP", block: "BLOCKED" };
1412
1504
  var esc2 = (s) => s.replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" })[c]);
1413
- var pct = (c) => `${Math.round(c * 100)}%`;
1505
+ var pct2 = (c) => `${Math.round(c * 100)}%`;
1414
1506
  function mountAgentConsole(instance, opts = {}) {
1415
1507
  injectStyles2();
1416
1508
  const tasks = opts.tasks ?? DEFAULT_AGENT_TASKS;
@@ -1462,7 +1554,7 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1462
1554
  };
1463
1555
  });
1464
1556
  const p = instance.client.getPolicy();
1465
- policyEl.innerHTML = `<div class="nr-ac-pol-row"><span>Budget</span><strong>$${esc2(p.sessionBudget)}</strong></div><div class="nr-ac-pol-row"><span>Allowed</span><span class="nr-ac-allow">${p.allowedContentTypes.map((t) => esc2(t)).join(", ")}</span></div><div class="nr-ac-pol-row"><span>Blocked</span><span class="nr-ac-block-types">${p.blockedContentTypes.map((t) => esc2(t)).join(", ") || "\u2014"}</span></div>`;
1557
+ policyEl.innerHTML = `<div class="nr-ac-pol-row"><span>Budget</span><strong>$${esc2(p.sessionBudget)}</strong></div><div class="nr-ac-pol-row"><span>Max per purchase</span><strong>$${esc2(p.maxSpendPerChunk)}</strong></div><div class="nr-ac-pol-row"><span>Allowed</span><span class="nr-ac-allow">${p.allowedContentTypes.map((t) => esc2(t)).join(", ")}</span></div><div class="nr-ac-pol-row"><span>Blocked</span><span class="nr-ac-block-types">${p.blockedContentTypes.map((t) => esc2(t)).join(", ") || "\u2014"}</span></div><div class="nr-ac-pol-row"><span>Min. relevance to buy</span><strong>${pct2(DEFAULT_CONFIDENCE_THRESHOLD)}</strong></div>`;
1466
1558
  const open = () => overlay.classList.add("open");
1467
1559
  const close = () => overlay.classList.remove("open");
1468
1560
  fab.onclick = open;
@@ -1499,7 +1591,7 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1499
1591
  priceOf.set(m.chunkId, m.price);
1500
1592
  }
1501
1593
  discoveryEl.innerHTML = `<div class="nr-ac-label">Publisher Discovery Agent \u2014 found ${ev.matches.length} relevant paid chunk${ev.matches.length === 1 ? "" : "s"} (no premium content revealed)</div>` + (ev.matches.length ? ev.matches.map(
1502
- (m, i) => `<div class="nr-ac-disc"><span class="nr-ac-disc-n">${i + 1}</span><div class="nr-ac-disc-main"><b>${esc2(m.title)}</b><span class="nr-ac-disc-teaser">${esc2(m.teaser)}</span><span class="nr-ac-disc-tags">${m.tags.map((t) => `#${esc2(t)}`).join(" ")}</span></div><div class="nr-ac-disc-meta"><span class="nr-ac-type t-${m.type}">${esc2(m.type)}</span><span class="nr-ac-price">$${esc2(m.price)}</span><span class="nr-ac-conf">${pct(m.confidence)} match</span></div></div>`
1594
+ (m, i) => `<div class="nr-ac-disc" data-cid="${esc2(m.chunkId)}"><div class="nr-ac-disc-top"><span class="nr-ac-disc-n">${i + 1}</span><div class="nr-ac-disc-main"><b>${esc2(m.title)}</b><span class="nr-ac-disc-teaser">${esc2(m.teaser)}</span><span class="nr-ac-disc-tags">${m.tags.map((t) => `#${esc2(t)}`).join(" ")}</span></div><div class="nr-ac-disc-meta"><span class="nr-ac-type t-${m.type}">${esc2(m.type)}</span><span class="nr-ac-price">$${esc2(m.price)}</span><span class="nr-ac-conf">${pct2(m.confidence)} match</span></div></div><div class="nr-ac-decision" data-decision="${esc2(m.chunkId)}"><span class="nr-ac-dec-wait">deciding\u2026</span></div></div>`
1503
1595
  ).join("") : '<div class="nr-ac-empty">No relevant paid content \u2014 the agent will not spend.</div>');
1504
1596
  row("found", `<span class="nr-ac-dot"></span> Found <strong>${ev.matches.length}</strong> purchasable chunk${ev.matches.length === 1 ? "" : "s"}`);
1505
1597
  } else if (ev.phase === "plan") {
@@ -1508,18 +1600,20 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1508
1600
  priceOf.set(it.chunkId, it.price);
1509
1601
  }
1510
1602
  row("eval", `<span class="nr-ac-dot"></span> Evaluating relevance, price &amp; policy\u2026`);
1603
+ annotateDecisions(discoveryEl, ev.plan);
1511
1604
  } else if (ev.phase === "chunk-start") {
1512
- if (ev.item.action === "buy") row("buy", `<span class="nr-ac-dot"></span> Purchasing <b>${esc2(ev.item.title)}</b> <span class="nr-ac-cost">$${esc2(ev.item.price)}</span>\u2026`);
1605
+ if (ev.item.action === "buy") row("buy", `<span class="nr-ac-dot"></span> Policy \u2713 \u2014 authorizing <b>${esc2(ev.item.title)}</b> <span class="nr-ac-cost">$${esc2(ev.item.price)}</span>\u2026`);
1513
1606
  } else if (ev.phase === "bought") {
1514
- row("receipt", `<span class="nr-ac-dot ok"></span> Receipt issued \xB7 <code>${esc2(ev.receiptId)}</code>`);
1607
+ row("receipt", `<span class="nr-ac-dot ok"></span> Voucher authorized \xB7 receipt <code>${esc2(ev.receiptId)}</code>`);
1608
+ row("unlock", `<span class="nr-ac-dot ok"></span> Content unlocked`);
1515
1609
  } else if (ev.phase === "blocked") {
1516
- row("blocked", `<span class="nr-ac-dot no"></span> Skipped <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 ${esc2(ev.reason)}`);
1610
+ row("blocked", `<span class="nr-ac-dot no"></span> Blocked <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 ${esc2(ev.reason)}`);
1517
1611
  } else if (ev.phase === "skipped") {
1518
- row("skip", `<span class="nr-ac-dot"></span> Skipped <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 not relevant to the task`);
1612
+ row("skip", `<span class="nr-ac-dot"></span> Skipped <b>${esc2(titleOf.get(ev.chunkId) ?? ev.chunkId)}</b> \u2014 ${esc2(ev.reason)}`);
1519
1613
  } else if (ev.phase === "done") {
1520
- row("gen", `<span class="nr-ac-dot"></span> Generating answer\u2026`);
1614
+ row("gen", `<span class="nr-ac-dot"></span> Generating answer from purchased content\u2026`);
1521
1615
  row("done", `<span class="nr-ac-dot ok"></span> Done`);
1522
- renderOutput(outputEl, ev.result.purchased, ev.result.receiptIds, ev.result.totalCost);
1616
+ renderOutput(outputEl, ev.result);
1523
1617
  }
1524
1618
  };
1525
1619
  try {
@@ -1532,9 +1626,29 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1532
1626
  };
1533
1627
  return { open, close };
1534
1628
  }
1535
- function renderOutput(el, purchased, receiptIds, totalCost) {
1536
- const sources = purchased.length ? purchased.map((s) => `<li>${esc2(s.title)} <span class="nr-ac-cost">$${esc2(s.price)}</span></li>`).join("") : '<li class="nr-ac-empty">None \u2014 nothing met the task + policy bar.</li>';
1537
- el.innerHTML = `<div class="nr-ac-out-h">Generated answer</div><div class="nr-ac-out-grid"><div><div class="nr-ac-out-label">Sources purchased</div><ul class="nr-ac-sources">${sources}</ul></div><div><div class="nr-ac-out-label">Receipts</div><div class="nr-ac-receipts">${receiptIds.map((r) => `<code>${esc2(r)}</code>`).join(" ") || "\u2014"}</div><div class="nr-ac-out-label" style="margin-top:12px">Total cost</div><div class="nr-ac-total">$${esc2(totalCost)}</div></div></div><div class="nr-ac-thesis">The agent discovered what was available, decided what was worth buying, and paid \u2014 on Tempo, with receipts.</div>`;
1629
+ function renderOutput(el, result) {
1630
+ const { answer, purchased, totalCost } = result;
1631
+ const sources = purchased.length ? purchased.map(
1632
+ (s) => `<li><span class="nr-ac-src-title">${esc2(s.title)}</span><span class="nr-ac-cost">$${esc2(s.price)}</span><code>${esc2(s.receiptId)}</code></li>`
1633
+ ).join("") : '<li class="nr-ac-empty">None \u2014 nothing met the task + policy bar.</li>';
1634
+ el.innerHTML = `<div class="nr-ac-out-h">Generated answer <em>\u2014 grounded only in what the agent paid for</em></div><div class="nr-ac-answer">${esc2(answer)}</div><div class="nr-ac-out-foot"><div class="nr-ac-out-srcs"><div class="nr-ac-out-label">Purchased sources \xB7 receipts</div><ul class="nr-ac-sources">${sources}</ul></div><div class="nr-ac-out-total"><div class="nr-ac-out-label">Total cost</div><div class="nr-ac-total">$${esc2(totalCost)}</div></div></div><div class="nr-ac-thesis">The agent discovered what was available, decided what was worth buying, and paid \u2014 on Tempo, with receipts.</div>`;
1635
+ }
1636
+ function decisionHtml(item) {
1637
+ return `<span class="nr-ac-dec-pill ${item.action}">${ACTION_LABEL[item.action]}</span><span class="nr-ac-dec-reason">${esc2(item.reason)}</span>`;
1638
+ }
1639
+ function annotateDecisions(container, plan) {
1640
+ for (const item of plan) {
1641
+ const slot = container.querySelector(`.nr-ac-decision[data-decision="${item.chunkId}"]`);
1642
+ if (slot) {
1643
+ slot.className = `nr-ac-decision act-${item.action}`;
1644
+ slot.innerHTML = decisionHtml(item);
1645
+ } else if (item.action === "block") {
1646
+ const card = document.createElement("div");
1647
+ card.className = "nr-ac-disc nr-ac-disc-extra";
1648
+ card.innerHTML = `<div class="nr-ac-disc-top"><span class="nr-ac-disc-n no">\u26D4</span><div class="nr-ac-disc-main"><b>${esc2(item.title)}</b><span class="nr-ac-disc-teaser">Not offered by discovery \u2014 your policy blocks it before any purchase.</span></div><div class="nr-ac-disc-meta"><span class="nr-ac-type t-${item.type}">${esc2(item.type)}</span><span class="nr-ac-price">$${esc2(item.price)}</span></div></div><div class="nr-ac-decision act-block">${decisionHtml(item)}</div>`;
1649
+ container.appendChild(card);
1650
+ }
1651
+ }
1538
1652
  }
1539
1653
  function markSvg(fill = "#FFC247") {
1540
1654
  return `<svg width="15" height="15" viewBox="0 0 64 64" style="vertical-align:-2px;flex:none"><g fill="${fill}"><circle cx="14" cy="12" r="4"/><circle cx="14" cy="22" r="4"/><circle cx="14" cy="32" r="4"/><circle cx="14" cy="42" r="4"/><circle cx="14" cy="52" r="4"/><circle cx="54" cy="12" r="4"/><circle cx="54" cy="22" r="4"/><circle cx="54" cy="42" r="4"/><circle cx="54" cy="52" r="4"/><circle cx="24" cy="22" r="4"/><circle cx="34" cy="32" r="4"/><circle cx="44" cy="42" r="4"/></g></svg>`;
@@ -1566,8 +1680,19 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1566
1680
  .nr-ac-run:disabled{opacity:.45;cursor:default}
1567
1681
  .nr-ac-run:not(:disabled):hover{background:#E2A12F}
1568
1682
  .nr-ac-block{margin-top:20px}
1569
- .nr-ac-disc{display:flex;gap:10px;align-items:flex-start;background:#0E0E0E;border:1px solid #1E1E1E;border-radius:10px;padding:11px 13px;margin-bottom:8px}
1683
+ .nr-ac-disc{display:flex;flex-direction:column;gap:9px;background:#0E0E0E;border:1px solid #1E1E1E;border-radius:10px;padding:11px 13px;margin-bottom:8px}
1684
+ .nr-ac-disc-top{display:flex;gap:10px;align-items:flex-start}
1685
+ .nr-ac-disc.nr-ac-disc-extra{border-style:dashed;border-color:#3b1d1d}
1570
1686
  .nr-ac-disc-n{width:20px;height:20px;flex:none;border-radius:6px;background:#161616;color:#FFC247;font-family:ui-monospace,monospace;font-size:11px;display:flex;align-items:center;justify-content:center;font-weight:700}
1687
+ .nr-ac-disc-n.no{background:#3b1d1d;color:#fca5a5}
1688
+ .nr-ac-decision{display:flex;align-items:center;gap:9px;border-top:1px dashed #1E1E1E;padding-top:8px}
1689
+ .nr-ac-dec-wait{color:#6E6E6E;font-size:11px;font-family:ui-monospace,monospace}
1690
+ .nr-ac-dec-pill{font-size:10px;font-weight:800;letter-spacing:.5px;padding:2px 8px;border-radius:6px;flex:none}
1691
+ .nr-ac-dec-pill.buy{background:#14321f;color:#86efac}
1692
+ .nr-ac-dec-pill.skip{background:#262626;color:#C4C4C4}
1693
+ .nr-ac-dec-pill.block{background:#3b1d1d;color:#fca5a5}
1694
+ .nr-ac-dec-reason{font-size:12px;color:#9A9A9A}
1695
+ .nr-ac-decision.act-buy .nr-ac-dec-reason{color:#86efac}
1571
1696
  .nr-ac-disc-main{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}
1572
1697
  .nr-ac-disc-main b{font-size:14px}
1573
1698
  .nr-ac-disc-teaser{color:#9A9A9A;font-size:12px}
@@ -1588,14 +1713,19 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1588
1713
  .nr-ac-tl.buy{color:#EDEDED}.nr-ac-tl.blocked{color:#fca5a5}
1589
1714
  .nr-ac-output{background:linear-gradient(180deg,#0E0E0E,#0B0B0B);border:1px solid #FFC247;border-radius:12px;padding:16px 18px}
1590
1715
  .nr-ac-out-h{font-weight:800;color:#FFC247;margin-bottom:12px}
1591
- .nr-ac-out-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}
1716
+ .nr-ac-out-h em{color:#8A8A8A;font-style:normal;font-weight:400;font-size:12px}
1717
+ .nr-ac-answer{white-space:pre-wrap;font-size:13.5px;line-height:1.6;color:#E4E4E4;background:#0B0B0B;border:1px solid #1E1E1E;border-radius:10px;padding:13px 15px;margin-bottom:14px}
1718
+ .nr-ac-out-foot{display:grid;grid-template-columns:1fr auto;gap:18px;align-items:start}
1592
1719
  .nr-ac-out-label{font-family:ui-monospace,monospace;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:#8A8A8A;margin-bottom:6px}
1593
- .nr-ac-sources{margin:0;padding-left:16px;font-size:13px}.nr-ac-sources li{margin:3px 0}
1594
- .nr-ac-receipts code{font-family:ui-monospace,monospace;font-size:11px;color:#FFC247;background:#161616;padding:1px 5px;border-radius:4px;margin-right:4px}
1595
- .nr-ac-total{font-family:ui-monospace,monospace;font-size:22px;font-weight:800;color:#FFC247}
1720
+ .nr-ac-sources{list-style:none;margin:0;padding:0;font-size:13px}
1721
+ .nr-ac-sources li{display:flex;align-items:center;gap:8px;margin:5px 0}
1722
+ .nr-ac-src-title{flex:1;min-width:0;color:#EDEDED}
1723
+ .nr-ac-sources code{font-family:ui-monospace,monospace;font-size:10.5px;color:#FFC247;background:#161616;padding:1px 6px;border-radius:4px}
1724
+ .nr-ac-out-total{text-align:right}
1725
+ .nr-ac-total{font-family:ui-monospace,monospace;font-size:24px;font-weight:800;color:#FFC247}
1596
1726
  .nr-ac-thesis{margin-top:14px;padding-top:12px;border-top:1px solid #1E1E1E;color:#9A9A9A;font-size:13px}
1597
1727
  .nr-ac-empty{color:#8A8A8A}
1598
- @media (max-width:560px){.nr-ac-out-grid{grid-template-columns:1fr}}
1728
+ @media (max-width:560px){.nr-ac-out-foot{grid-template-columns:1fr}.nr-ac-out-total{text-align:left}}
1599
1729
  `;
1600
1730
  const style = document.createElement("style");
1601
1731
  style.id = "nr-ac-styles";
@@ -1611,20 +1741,20 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1611
1741
  const widget = config.mountWidget === false ? void 0 : new NanoRailWidget(client);
1612
1742
  client.ensureSession().then(() => widget?.render());
1613
1743
  client.startSettlementWatch();
1614
- const buildAndRegister = async () => {
1615
- const m = generateManifest({ articleId });
1616
- if (m.chunks.length > 0) await client.registerManifest(m).catch(() => {
1617
- });
1618
- return m;
1619
- };
1744
+ const registration = scanRegistration({ articleId });
1745
+ const registered = registration.chunks.length > 0 ? client.registerArticle(registration).catch(() => void 0) : Promise.resolve(void 0);
1620
1746
  const instance = {
1621
1747
  client,
1622
1748
  widget,
1623
1749
  manifest: generateManifest({ articleId }),
1750
+ registered,
1624
1751
  rescan: () => scanAndLock(client, document, { autoUnlock: config.autoUnlockOnScroll === true }),
1625
1752
  regenerateManifest: async () => {
1626
- instance.manifest = await buildAndRegister();
1627
- return instance.manifest;
1753
+ const m = generateManifest({ articleId });
1754
+ if (m.chunks.length > 0) await client.registerManifest(m).catch(() => {
1755
+ });
1756
+ instance.manifest = m;
1757
+ return m;
1628
1758
  },
1629
1759
  runAgent: (opts) => runAgentSummary(client, articleId, opts),
1630
1760
  forceVideoRejection: async () => {
@@ -1635,9 +1765,6 @@ Receipts: ${receiptIds.join(", ") || "none"}`;
1635
1765
  },
1636
1766
  unlockTempoSnippet: () => client.unlockTempoSnippet()
1637
1767
  };
1638
- buildAndRegister().then((m) => {
1639
- instance.manifest = m;
1640
- });
1641
1768
  if (config.autoScan !== false) {
1642
1769
  scanAndLock(client, document, { autoUnlock: config.autoUnlockOnScroll === true });
1643
1770
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanorail/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "MPP-native metering SDK: turn data-nanorail-* attributes into agent-readable, policy-bounded paid content chunks with discovery, receipts, and Tempo settlement.",
5
5
  "license": "MIT",
6
6
  "author": "NanoRail",