@nanorail/sdk 0.1.0 → 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 +76 -17
- package/dist/index.mjs +169 -40
- package/dist/nanorail.js +167 -40
- package/package.json +1 -1
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
|
|
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:${
|
|
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 {
|
|
1387
|
+
return { ...base, action: "block", reason: decision.reason ?? "blocked by policy" };
|
|
1339
1388
|
}
|
|
1340
|
-
if (confidence
|
|
1341
|
-
return {
|
|
1389
|
+
if (confidence === void 0) {
|
|
1390
|
+
return { ...base, action: "skip", reason: "not relevant to the task" };
|
|
1342
1391
|
}
|
|
1343
|
-
|
|
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
|
|
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
|
|
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
|
|
1407
|
-
{ label: "
|
|
1408
|
-
{ label: "
|
|
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) => ({ "&": "&", "<": "<", ">": ">" })[c]);
|
|
1411
|
-
var
|
|
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">${
|
|
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 & 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>
|
|
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>
|
|
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>
|
|
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
|
|
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
|
|
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,
|
|
1534
|
-
const
|
|
1535
|
-
|
|
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;
|
|
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-
|
|
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
|
|
1592
|
-
.nr-ac-
|
|
1593
|
-
.nr-ac-
|
|
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-
|
|
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
|
|
1613
|
-
|
|
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
|
-
|
|
1625
|
-
|
|
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
|
|
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:${
|
|
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 {
|
|
1389
|
+
return { ...base, action: "block", reason: decision.reason ?? "blocked by policy" };
|
|
1341
1390
|
}
|
|
1342
|
-
if (confidence
|
|
1343
|
-
return {
|
|
1391
|
+
if (confidence === void 0) {
|
|
1392
|
+
return { ...base, action: "skip", reason: "not relevant to the task" };
|
|
1344
1393
|
}
|
|
1345
|
-
|
|
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
|
|
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
|
|
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
|
|
1409
|
-
{ label: "
|
|
1410
|
-
{ label: "
|
|
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) => ({ "&": "&", "<": "<", ">": ">" })[c]);
|
|
1413
|
-
var
|
|
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">${
|
|
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 & 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>
|
|
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>
|
|
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>
|
|
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
|
|
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
|
|
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,
|
|
1536
|
-
const
|
|
1537
|
-
|
|
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;
|
|
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-
|
|
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
|
|
1594
|
-
.nr-ac-
|
|
1595
|
-
.nr-ac-
|
|
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-
|
|
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
|
|
1615
|
-
|
|
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
|
-
|
|
1627
|
-
|
|
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.
|
|
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",
|