@naulon/wayfarer 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/agent.d.ts +77 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +235 -0
- package/dist/agent.js.map +1 -0
- package/dist/allocation.d.ts +36 -0
- package/dist/allocation.d.ts.map +1 -0
- package/dist/allocation.js +45 -0
- package/dist/allocation.js.map +1 -0
- package/dist/appraise.d.ts +3 -0
- package/dist/appraise.d.ts.map +1 -0
- package/dist/appraise.js +60 -0
- package/dist/appraise.js.map +1 -0
- package/dist/buyer.d.ts +195 -0
- package/dist/buyer.d.ts.map +1 -0
- package/dist/buyer.js +254 -0
- package/dist/buyer.js.map +1 -0
- package/dist/decide.d.ts +115 -0
- package/dist/decide.d.ts.map +1 -0
- package/dist/decide.js +206 -0
- package/dist/decide.js.map +1 -0
- package/dist/discover.d.ts +10 -0
- package/dist/discover.d.ts.map +1 -0
- package/dist/discover.js +5 -0
- package/dist/discover.js.map +1 -0
- package/dist/discovery.d.ts +26 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +93 -0
- package/dist/discovery.js.map +1 -0
- package/dist/gateway.d.ts +117 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +187 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +40 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +44 -0
- package/dist/lib.js.map +1 -0
- package/dist/licenseStore.d.ts +56 -0
- package/dist/licenseStore.d.ts.map +1 -0
- package/dist/licenseStore.js +79 -0
- package/dist/licenseStore.js.map +1 -0
- package/dist/memo.d.ts +43 -0
- package/dist/memo.d.ts.map +1 -0
- package/dist/memo.js +102 -0
- package/dist/memo.js.map +1 -0
- package/dist/origin-policy.d.ts +74 -0
- package/dist/origin-policy.d.ts.map +1 -0
- package/dist/origin-policy.js +88 -0
- package/dist/origin-policy.js.map +1 -0
- package/dist/paidFetch.d.ts +14 -0
- package/dist/paidFetch.d.ts.map +1 -0
- package/dist/paidFetch.js +93 -0
- package/dist/paidFetch.js.map +1 -0
- package/dist/pay.d.ts +3 -0
- package/dist/pay.d.ts.map +1 -0
- package/dist/pay.js +82 -0
- package/dist/pay.js.map +1 -0
- package/dist/pop.d.ts +8 -0
- package/dist/pop.d.ts.map +1 -0
- package/dist/pop.js +25 -0
- package/dist/pop.js.map +1 -0
- package/dist/rail.d.ts +18 -0
- package/dist/rail.d.ts.map +1 -0
- package/dist/rail.js +73 -0
- package/dist/rail.js.map +1 -0
- package/dist/rss.d.ts +38 -0
- package/dist/rss.d.ts.map +1 -0
- package/dist/rss.js +110 -0
- package/dist/rss.js.map +1 -0
- package/dist/sign.d.ts +13 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +52 -0
- package/dist/sign.js.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wallet.d.ts +12 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +46 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @naulon/wayfarer
|
|
2
|
+
|
|
3
|
+
The paying agent. Give it a topic and a budget; it discovers tolled articles,
|
|
4
|
+
decides which are worth citing, pays only those, and answers from what it bought.
|
|
5
|
+
|
|
6
|
+
The point isn't automation for its own sake — it's that the agent makes a real
|
|
7
|
+
budget decision at each step instead of paying for everything. It ranks candidates
|
|
8
|
+
by relevance-per-dollar, buys greedily under the budget down to a relevance floor,
|
|
9
|
+
reuses anything already paid for, and logs *why* for every candidate. The
|
|
10
|
+
reasoning is the artifact.
|
|
11
|
+
|
|
12
|
+
## Run
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm run -w @naulon/tollgate dev & # the gate to pay
|
|
16
|
+
npm run -w @naulon/wayfarer start -- "payment and passage" # the agent
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
decisions:
|
|
21
|
+
[PAY] the-naulon — relevance 1.00 @ $0.005; $0.095 left
|
|
22
|
+
[SKIP] on-stillness — relevance 0.00 below floor 0.35
|
|
23
|
+
✓ paid $0.005000 for the-naulon
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
It runs offline against mock settlement. Set `OPENAI_API_KEY` for LLM appraisal
|
|
27
|
+
and answer synthesis instead of the keyword heuristic; set `PAYMENT_MODE=gateway`
|
|
28
|
+
with a funded buyer wallet to pay for real over Circle Gateway on Arc Network.
|
|
29
|
+
|
|
30
|
+
## What's inside
|
|
31
|
+
|
|
32
|
+
- **`discover.ts` / `discovery.ts`** — find tollable candidates from a gate or feed.
|
|
33
|
+
- **`appraise.ts` / `decide.ts` / `allocation.ts`** — relevance, the pay/skip call, budget allocation.
|
|
34
|
+
- **`buyer.ts` / `gateway.ts`** — the x402 payment loop (mock and Circle Gateway).
|
|
35
|
+
- **`agent.ts` / `lib.ts`** — the end-to-end research run, also importable as a library.
|
|
36
|
+
|
|
37
|
+
Exposed as an MCP server by [`@naulon/wayfarer-mcp`](../wayfarer-mcp).
|
|
38
|
+
|
|
39
|
+
MIT.
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Wayfarer pipeline: discover → price → appraise → decide → pay → ground.
|
|
3
|
+
*
|
|
4
|
+
* Each stage is explicit and logged, so the agent's reasoning is auditable — the
|
|
5
|
+
* decision log is the artifact, not a black box. Paying runs against the
|
|
6
|
+
* tollgate over the real x402 contract; the Buyer is mock (offline) or Circle
|
|
7
|
+
* Gateway (PAYMENT_MODE=gateway), chosen at startup.
|
|
8
|
+
*/
|
|
9
|
+
import { type JwkSet } from "@naulon/shared";
|
|
10
|
+
import { type GatewaySigner } from "./gateway.ts";
|
|
11
|
+
import { type MemoSigner } from "./memo.ts";
|
|
12
|
+
import { type RailSigners } from "./rail.ts";
|
|
13
|
+
import type { DecideContext, DecisionPolicy } from "./decide.ts";
|
|
14
|
+
import type { HeldStore } from "./licenseStore.ts";
|
|
15
|
+
import type { RunResult } from "./types.ts";
|
|
16
|
+
import type { AgentWallet } from "./wallet.ts";
|
|
17
|
+
export type Logger = (line: string) => void;
|
|
18
|
+
export declare function tollgateBase(): string;
|
|
19
|
+
export declare function articleUrl(base: string, slug: string): string;
|
|
20
|
+
/** Fetch the gate's public key set so the agent can verify the receipts it holds. */
|
|
21
|
+
export declare function fetchJwks(base: string): Promise<JwkSet | null>;
|
|
22
|
+
/** Verify a captured license against the gate's JWKS. null when JWKS unavailable. */
|
|
23
|
+
export declare function verifyAgainst(jws: string, jwks: JwkSet): boolean;
|
|
24
|
+
/** Optional overrides for a single run. `budgetUsdc` lets a caller spend LESS than
|
|
25
|
+
* the configured ceiling for this run (e.g. the MCP clamping the model's requested
|
|
26
|
+
* budget to what remains in the session envelope) — it can only narrow, never widen,
|
|
27
|
+
* because the caller is expected to pass `min(requested, configCeiling)`. */
|
|
28
|
+
export interface RunOptions {
|
|
29
|
+
budgetUsdc?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Operator policy for this run (allow/deny domains, per-domain cap, approval
|
|
32
|
+
* threshold, kill-switch). Server/config-supplied, never LLM-controlled — same
|
|
33
|
+
* principle as `budgetUsdc`. Omitted ⇒ `DEFAULT_POLICY` (the original behavior).
|
|
34
|
+
*/
|
|
35
|
+
policy?: DecisionPolicy;
|
|
36
|
+
/** Window state for per-domain rate caps across runs (prior pays per host). */
|
|
37
|
+
decideContext?: DecideContext;
|
|
38
|
+
/**
|
|
39
|
+
* Gate this run settles into (base URL). Server/config-supplied, never
|
|
40
|
+
* LLM-controlled — same principle as `budgetUsdc`/`policy`. Lets a hosted caller
|
|
41
|
+
* point one run at a specific fleet tenant's gate. Omitted ⇒ `tollgateBase()`
|
|
42
|
+
* (the env default), so the OSS single-gate path is unchanged.
|
|
43
|
+
*/
|
|
44
|
+
tollgateUrl?: string;
|
|
45
|
+
/**
|
|
46
|
+
* This run's custody-free session signer (BUY-4 hosted path). When present, tolls
|
|
47
|
+
* are signed by the injected `MemoSigner` (naulon's grant-checked `/sign-memo` BFF
|
|
48
|
+
* over an encrypted session key) instead of the env `BUYER_PRIVATE_KEY` — so the
|
|
49
|
+
* hosted `naulon_research` pays from the buyer's managed session wallet, mirroring
|
|
50
|
+
* `naulon_pay_and_read`. Omitted ⇒ `selectBuyer()` picks the env buyer (the OSS
|
|
51
|
+
* self-host path). Server/config-supplied, never LLM-controlled. A `MemoSigner`
|
|
52
|
+
* (memo/Arc rail) or a `GatewaySigner` (memo-less Circle rails); `run` routes it to
|
|
53
|
+
* the matching buyer via `supportsMemo(activeNetwork())`, like `selectBuyer()`.
|
|
54
|
+
*/
|
|
55
|
+
signer?: MemoSigner | GatewaySigner;
|
|
56
|
+
/**
|
|
57
|
+
* BOTH rail signers over the same sealed session key (RAS-B mixed fleet). When present, the run
|
|
58
|
+
* pays through `railBuyer`, which picks memo vs gateway PER-402 from each tenant's advertised
|
|
59
|
+
* network — exactly like `naulon_pay_and_read` — instead of the fleet-global `supportsMemo`
|
|
60
|
+
* routing a single `signer` gets. Wins over `signer`. Server/config-supplied, never LLM-controlled.
|
|
61
|
+
*/
|
|
62
|
+
railSigners?: RailSigners;
|
|
63
|
+
/**
|
|
64
|
+
* This run's held-license backend (BUY-4 hosted path). The cloud injects a
|
|
65
|
+
* per-session store so the composite loop's free re-reads never cross buyer
|
|
66
|
+
* boundaries in a shared process. Omitted ⇒ the process-global file (OSS path).
|
|
67
|
+
*/
|
|
68
|
+
heldStore?: HeldStore;
|
|
69
|
+
/**
|
|
70
|
+
* The wallet that signs proof-of-possession for this run's cnf-bound held
|
|
71
|
+
* re-reads. Hosted path injects a signer over the session key (`/sign-pop`);
|
|
72
|
+
* omitted ⇒ `getWallet()` (the env/dev wallet, unchanged OSS path).
|
|
73
|
+
*/
|
|
74
|
+
popWallet?: AgentWallet;
|
|
75
|
+
}
|
|
76
|
+
export declare function run(topic: string, log?: Logger, opts?: RunOptions): Promise<RunResult>;
|
|
77
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAA+D,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAG1G,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,OAAO,KAAK,EAAuC,SAAS,EAAU,MAAM,YAAY,CAAC;AAEzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAE5C,wBAAgB,YAAY,IAAI,MAAM,CAQrC;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,qFAAqF;AACrF,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASpE;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAgBhE;AAED;;;8EAG8E;AAC9E,MAAM,WAAW,UAAU;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC;IACpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB;AAED,wBAAsB,GAAG,CACvB,KAAK,EAAE,MAAM,EACb,GAAG,GAAE,MAAiB,EACtB,IAAI,GAAE,UAAe,GACpB,OAAO,CAAC,SAAS,CAAC,CAwJpB"}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Wayfarer pipeline: discover → price → appraise → decide → pay → ground.
|
|
3
|
+
*
|
|
4
|
+
* Each stage is explicit and logged, so the agent's reasoning is auditable — the
|
|
5
|
+
* decision log is the artifact, not a black box. Paying runs against the
|
|
6
|
+
* tollgate over the real x402 contract; the Buyer is mock (offline) or Circle
|
|
7
|
+
* Gateway (PAYMENT_MODE=gateway), chosen at startup.
|
|
8
|
+
*/
|
|
9
|
+
import { activeNetwork, getConfig, supportsMemo, usdc, verifyLicense } from "@naulon/shared";
|
|
10
|
+
import { appraise } from "./appraise.js";
|
|
11
|
+
import { quotedTotalAtomic, rereadWithLicense, selectBuyer } from "./buyer.js";
|
|
12
|
+
import { gatewayBuyer } from "./gateway.js";
|
|
13
|
+
import { memoBuyer } from "./memo.js";
|
|
14
|
+
import { railBuyer } from "./rail.js";
|
|
15
|
+
import { decide, DEFAULT_POLICY } from "./decide.js";
|
|
16
|
+
import { discover } from "./discover.js";
|
|
17
|
+
import { decodeHeld, fileHeldStore, isLive } from "./licenseStore.js";
|
|
18
|
+
import { buildPopProof } from "./pop.js";
|
|
19
|
+
import { agentFetch } from "./sign.js";
|
|
20
|
+
import { getWallet } from "./wallet.js";
|
|
21
|
+
export function tollgateBase() {
|
|
22
|
+
const url = getConfig().TOLLGATE_URL;
|
|
23
|
+
// No fail-open: an unset gate is a config error surfaced here, not a fabricated
|
|
24
|
+
// http://localhost target that refuses later with a confusing "unreachable host".
|
|
25
|
+
// Callers prefer an injected per-session `opts.tollgateUrl`; this is only reached
|
|
26
|
+
// when neither is set.
|
|
27
|
+
if (!url)
|
|
28
|
+
throw new Error("TOLLGATE_URL is not configured");
|
|
29
|
+
return url;
|
|
30
|
+
}
|
|
31
|
+
export function articleUrl(base, slug) {
|
|
32
|
+
return `${base.replace(/\/$/, "")}/essays/${encodeURIComponent(slug)}`;
|
|
33
|
+
}
|
|
34
|
+
/** Fetch the gate's public key set so the agent can verify the receipts it holds. */
|
|
35
|
+
export async function fetchJwks(base) {
|
|
36
|
+
try {
|
|
37
|
+
const res = await agentFetch(`${base.replace(/\/$/, "")}/.well-known/naulon-jwks.json`);
|
|
38
|
+
if (!res.ok)
|
|
39
|
+
return null;
|
|
40
|
+
const set = (await res.json());
|
|
41
|
+
return set.keys?.length ? set : null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Verify a captured license against the gate's JWKS. null when JWKS unavailable. */
|
|
48
|
+
export function verifyAgainst(jws, jwks) {
|
|
49
|
+
try {
|
|
50
|
+
const claims = JSON.parse(Buffer.from(jws.split(".")[1] ?? "", "base64url").toString("utf8"));
|
|
51
|
+
const r = verifyLicense(jws, {
|
|
52
|
+
now: Date.now(),
|
|
53
|
+
expectedIssuer: claims.iss ?? "",
|
|
54
|
+
expectedAudience: claims.aud ?? "",
|
|
55
|
+
jwks,
|
|
56
|
+
});
|
|
57
|
+
return r.ok;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export async function run(topic, log = () => { }, opts = {}) {
|
|
64
|
+
const cfg = getConfig();
|
|
65
|
+
const budget = opts.budgetUsdc ?? cfg.WAYFARER_BUDGET_USDC;
|
|
66
|
+
const base = opts.tollgateUrl ?? tollgateBase();
|
|
67
|
+
// Hosted path: sign each leg via the injected cloud session key (custody-free); the
|
|
68
|
+
// env BUYER_PRIVATE_KEY is never read. Route the injected signer to the active network's
|
|
69
|
+
// rail — memo-LESS networks (Base + every Gateway chain) settle via gatewayBuyer (the Circle
|
|
70
|
+
// envelope), memo networks via memoBuyer — mirroring selectBuyer() and naulon_pay_and_read so
|
|
71
|
+
// hosted research pays from the same wallet on whatever rail the tenant settles on. The cloud
|
|
72
|
+
// injects the signer matching the network's rail. Default: the BYO-key buyer selectBuyer() picks.
|
|
73
|
+
const buyer = opts.railSigners
|
|
74
|
+
? railBuyer(opts.railSigners)
|
|
75
|
+
: opts.signer
|
|
76
|
+
? supportsMemo(activeNetwork())
|
|
77
|
+
? memoBuyer(opts.signer)
|
|
78
|
+
: gatewayBuyer(opts.signer)
|
|
79
|
+
: await selectBuyer();
|
|
80
|
+
// Per-session held store + PoP signer (BUY-4): the injected pair keeps the composite loop's
|
|
81
|
+
// free re-reads isolated per buyer and signed by the paying session EOA. Omitted ⇒ OSS defaults.
|
|
82
|
+
const heldStore = opts.heldStore ?? fileHeldStore;
|
|
83
|
+
const popWallet = opts.popWallet ?? getWallet();
|
|
84
|
+
log(`topic: "${topic}"`);
|
|
85
|
+
log(`budget: $${budget} · mode ${cfg.PAYMENT_MODE} · wallet ${buyer.address} · gate ${base}`);
|
|
86
|
+
await buyer.init();
|
|
87
|
+
// 1. discover (free teasers)
|
|
88
|
+
const candidates = await discover(topic);
|
|
89
|
+
log(`\ndiscovered ${candidates.length} candidate essays`);
|
|
90
|
+
// 2. price (free x402 probes — no payment yet)
|
|
91
|
+
const priced = [];
|
|
92
|
+
// The TRUE total (all legs, atomic micro-USDC) each candidate was quoted at here. The pay step
|
|
93
|
+
// uses it as the toll-moved ceiling, so a gate that quotes low at appraisal and charges more at
|
|
94
|
+
// pay time is aborted — the BUY-1.4 guarantee. Budget-remaining alone cannot detect that.
|
|
95
|
+
const quotedTotals = new Map();
|
|
96
|
+
for (const c of candidates) {
|
|
97
|
+
const quoted = await buyer.price(c.url ?? articleUrl(base, c.slug), "citation");
|
|
98
|
+
if (quoted) {
|
|
99
|
+
priced.push({ ...c, price: usdc(quoted.priceUsdc) });
|
|
100
|
+
quotedTotals.set(c.slug, quotedTotalAtomic(quoted));
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
log(` · ${c.slug}: not gated — skipping`);
|
|
104
|
+
}
|
|
105
|
+
log(`priced ${priced.length} gated essays`);
|
|
106
|
+
// 3. appraise
|
|
107
|
+
const appraised = await appraise(topic, priced);
|
|
108
|
+
log(`\nappraisal:`);
|
|
109
|
+
for (const a of appraised)
|
|
110
|
+
log(` · ${a.slug}: relevance ${a.relevance.toFixed(2)} — ${a.rationale}`);
|
|
111
|
+
// 4. decide (the agency). A live license already held for an essay makes it a
|
|
112
|
+
// zero-cost "cache" — pay once, re-read free.
|
|
113
|
+
const held = await heldStore.load();
|
|
114
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
115
|
+
const licensed = new Set([...held.values()].filter((h) => isLive(h, nowSec)).map((h) => h.slug));
|
|
116
|
+
if (licensed.size)
|
|
117
|
+
log(`\nholding ${licensed.size} live license(s) — those re-read free`);
|
|
118
|
+
// gateBase lets decide() resolve a slug-only candidate to the SAME url the pay step below uses,
|
|
119
|
+
// so domain policy is evaluated against the host that actually gets paid (never Candidate.host).
|
|
120
|
+
const decisions = decide(appraised, budget, licensed, opts.policy ?? DEFAULT_POLICY, {
|
|
121
|
+
...opts.decideContext,
|
|
122
|
+
gateBase: opts.decideContext?.gateBase ?? base,
|
|
123
|
+
});
|
|
124
|
+
log(`\ndecisions:`);
|
|
125
|
+
for (const d of decisions)
|
|
126
|
+
log(` [${d.action.toUpperCase()}] ${d.slug} — ${d.reason}`);
|
|
127
|
+
// 5. obtain: pay for new sources (capturing + verifying the license), or re-read
|
|
128
|
+
// already-licensed ones for free.
|
|
129
|
+
const jwks = await fetchJwks(base);
|
|
130
|
+
const sources = [];
|
|
131
|
+
let spent = 0;
|
|
132
|
+
for (const d of decisions) {
|
|
133
|
+
const url = d.url ?? articleUrl(base, d.slug);
|
|
134
|
+
if (d.action === "cache") {
|
|
135
|
+
const h = held.get(d.slug);
|
|
136
|
+
if (!h)
|
|
137
|
+
continue;
|
|
138
|
+
// Holder-of-key license: sign a fresh proof-of-possession so the gate knows
|
|
139
|
+
// we still hold the payer wallet, not just a captured token.
|
|
140
|
+
let proof;
|
|
141
|
+
if (h.pop) {
|
|
142
|
+
proof = (await buildPopProof(h, popWallet, Date.now())) ?? undefined;
|
|
143
|
+
if (!proof) {
|
|
144
|
+
log(` ✗ re-read ${d.slug}: license is holder-of-key but wallet can't sign — paying instead`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const reread = await rereadWithLicense(url, "citation", h.jws, buyer.address, proof);
|
|
149
|
+
if (reread.ok) {
|
|
150
|
+
sources.push({ slug: d.slug, title: d.title, content: reread.content ?? "", paidUsdc: 0, licenseId: h.jti });
|
|
151
|
+
log(` 🎫 re-read ${d.slug} FREE with held license (${h.jti.slice(0, 8)})${h.pop ? " 🔑 proof-of-possession" : ""}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
log(` ✗ re-read failed for ${d.slug}: ${reread.error}`);
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (d.action !== "pay")
|
|
159
|
+
continue;
|
|
160
|
+
// The pay-time ceiling is the TIGHTER of two independent bounds:
|
|
161
|
+
// · toll-moved — the total this candidate was quoted at in step 2, plus the configured
|
|
162
|
+
// tolerance. Catches a gate that quotes low at appraisal and charges more at pay time
|
|
163
|
+
// (a budget check alone cannot: the inflated price still "fits").
|
|
164
|
+
// · budget — what is actually left. decide() plans against the AUTHOR-leg price, which
|
|
165
|
+
// under-counts a fee'd toll, so its plan can exceed budget.
|
|
166
|
+
// Whichever is smaller wins; exceeding it aborts the pay having spent nothing.
|
|
167
|
+
const remainingAtomic = BigInt(Math.max(0, Math.round((budget - spent) * 1_000_000)));
|
|
168
|
+
const quotedAtomic = quotedTotals.get(d.slug);
|
|
169
|
+
const bps = BigInt(cfg.WAYFARER_TOLL_TOLERANCE_BPS);
|
|
170
|
+
const tollCeiling = quotedAtomic === undefined ? undefined : quotedAtomic + (quotedAtomic * bps) / 10000n;
|
|
171
|
+
const ceiling = tollCeiling === undefined ? remainingAtomic : (tollCeiling < remainingAtomic ? tollCeiling : remainingAtomic);
|
|
172
|
+
const result = await buyer.fetch(url, "citation", { maxTotalAtomic: ceiling.toString() });
|
|
173
|
+
if (!result.ok) {
|
|
174
|
+
log(` ✗ payment failed for ${d.slug}: ${result.error}`);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
// Debit the TRUE total across all legs (costUsdc), not the author leg (paidUsdc) — else a
|
|
178
|
+
// fee'd toll under-counts spend and the loop keeps buying past budget.
|
|
179
|
+
spent += result.costUsdc ?? result.paidUsdc ?? d.price;
|
|
180
|
+
let licenseId;
|
|
181
|
+
if (result.license) {
|
|
182
|
+
const decoded = decodeHeld(result.license);
|
|
183
|
+
const verified = jwks ? verifyAgainst(result.license, jwks) : null;
|
|
184
|
+
if (decoded) {
|
|
185
|
+
// Persist the url actually paid alongside the license, so it travels with the
|
|
186
|
+
// held record (a slug-only re-read can then target the real link, not a template).
|
|
187
|
+
held.set(d.slug, { ...decoded, jws: result.license, url });
|
|
188
|
+
licenseId = decoded.jti;
|
|
189
|
+
}
|
|
190
|
+
const mark = verified === true ? " → 🎫 license verified" : verified === false ? " → ⚠ license UNVERIFIED" : " → 🎫 license";
|
|
191
|
+
log(` ✓ paid $${(result.paidUsdc ?? d.price).toFixed(6)} for ${d.slug} (ref ${result.settlementRef})${mark} ${licenseId?.slice(0, 8) ?? ""}`);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
log(` ✓ paid $${(result.paidUsdc ?? d.price).toFixed(6)} for ${d.slug} (ref ${result.settlementRef})`);
|
|
195
|
+
}
|
|
196
|
+
sources.push({
|
|
197
|
+
slug: d.slug,
|
|
198
|
+
title: d.title,
|
|
199
|
+
content: result.content ?? "",
|
|
200
|
+
paidUsdc: result.paidUsdc ?? d.price,
|
|
201
|
+
settlementRef: result.settlementRef,
|
|
202
|
+
licenseId,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
await heldStore.save(held);
|
|
206
|
+
// 6. ground
|
|
207
|
+
const answer = await ground(topic, sources);
|
|
208
|
+
log(`\nspent $${spent.toFixed(6)} of $${budget} across ${sources.length} cited source(s)`);
|
|
209
|
+
return { topic, budget, spent: usdc(spent), decisions, sources, answer };
|
|
210
|
+
}
|
|
211
|
+
async function ground(topic, sources) {
|
|
212
|
+
if (sources.length === 0) {
|
|
213
|
+
return `No sources were worth paying for under budget for "${topic}".`;
|
|
214
|
+
}
|
|
215
|
+
const citations = sources
|
|
216
|
+
.map((s, i) => `[${i + 1}] ${s.title} (${s.slug})${s.licenseId ? ` · licensed 🎫 ${s.licenseId.slice(0, 8)}` : ""}`)
|
|
217
|
+
.join("\n");
|
|
218
|
+
if (getConfig().OPENAI_API_KEY) {
|
|
219
|
+
try {
|
|
220
|
+
const { ChatOpenAI } = await import("@langchain/openai");
|
|
221
|
+
const model = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0.2 });
|
|
222
|
+
const corpus = sources
|
|
223
|
+
.map((s, i) => `[${i + 1}] ${s.title}\n${s.content.slice(0, 4000)}`)
|
|
224
|
+
.join("\n\n");
|
|
225
|
+
const res = await model.invoke(`Answer the topic "${topic}" grounded ONLY in these sources, citing with [n].\n\n${corpus}`);
|
|
226
|
+
return `${String(res.content)}\n\nSources:\n${citations}`;
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
// fall through to template
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return (`Grounded answer for "${topic}" (template; set OPENAI_API_KEY for synthesis):\n` +
|
|
233
|
+
`Paid for and cited ${sources.length} source(s).\n\nSources:\n${citations}`);
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-source proportional allocation (BUY-3.2) — a BUYER-side policy.
|
|
3
|
+
*
|
|
4
|
+
* Keryx splits a citation-reward pool across the sources it actually cited,
|
|
5
|
+
* weighted by each source's contribution. Wayfarer pays each source a flat
|
|
6
|
+
* per-article toll; this closes the parity gap as a *policy* on top of those
|
|
7
|
+
* tolls: given a fixed answer-budget, decide how much of it each cited source
|
|
8
|
+
* earned, proportional to its appraised contribution.
|
|
9
|
+
*
|
|
10
|
+
* It computes numbers — it does NOT pay. There is no new settlement primitive;
|
|
11
|
+
* a caller that wants to act on the split reuses the existing per-article toll
|
|
12
|
+
* rail. Money is integer micro-USDC and the split is dust-free (largest-
|
|
13
|
+
* remainder apportionment, same money-truth discipline as shared `splitMicro`,
|
|
14
|
+
* here keyed by source slug rather than payee wallet).
|
|
15
|
+
*/
|
|
16
|
+
/** One source's share of the answer-budget. */
|
|
17
|
+
export interface SourceAllocation {
|
|
18
|
+
slug: string;
|
|
19
|
+
/** Integer micro-USDC of the pool attributed to this source. */
|
|
20
|
+
micro: number;
|
|
21
|
+
/** Normalized 0..1 contribution weight (for display / a citation-reward call). */
|
|
22
|
+
weight: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Allocate `poolMicro` (integer micro-USDC) across `sources` proportional to each
|
|
26
|
+
* source's `contribution`. Negative contributions clamp to zero. When the total
|
|
27
|
+
* contribution is positive, `Σ micro === floor(poolMicro)` exactly — no dust
|
|
28
|
+
* created or lost; the rounding remainder goes to the largest fractional shares.
|
|
29
|
+
* When every contribution is zero (or the list is empty), nothing is allocated
|
|
30
|
+
* (all `micro === 0`) — there is no basis to weight the pool.
|
|
31
|
+
*/
|
|
32
|
+
export declare function allocateByContribution(sources: readonly {
|
|
33
|
+
slug: string;
|
|
34
|
+
contribution: number;
|
|
35
|
+
}[], poolMicro: number): SourceAllocation[];
|
|
36
|
+
//# sourceMappingURL=allocation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocation.d.ts","sourceRoot":"","sources":["../src/allocation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,+CAA+C;AAC/C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAAE,EAC1D,SAAS,EAAE,MAAM,GAChB,gBAAgB,EAAE,CAsBpB"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-source proportional allocation (BUY-3.2) — a BUYER-side policy.
|
|
3
|
+
*
|
|
4
|
+
* Keryx splits a citation-reward pool across the sources it actually cited,
|
|
5
|
+
* weighted by each source's contribution. Wayfarer pays each source a flat
|
|
6
|
+
* per-article toll; this closes the parity gap as a *policy* on top of those
|
|
7
|
+
* tolls: given a fixed answer-budget, decide how much of it each cited source
|
|
8
|
+
* earned, proportional to its appraised contribution.
|
|
9
|
+
*
|
|
10
|
+
* It computes numbers — it does NOT pay. There is no new settlement primitive;
|
|
11
|
+
* a caller that wants to act on the split reuses the existing per-article toll
|
|
12
|
+
* rail. Money is integer micro-USDC and the split is dust-free (largest-
|
|
13
|
+
* remainder apportionment, same money-truth discipline as shared `splitMicro`,
|
|
14
|
+
* here keyed by source slug rather than payee wallet).
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Allocate `poolMicro` (integer micro-USDC) across `sources` proportional to each
|
|
18
|
+
* source's `contribution`. Negative contributions clamp to zero. When the total
|
|
19
|
+
* contribution is positive, `Σ micro === floor(poolMicro)` exactly — no dust
|
|
20
|
+
* created or lost; the rounding remainder goes to the largest fractional shares.
|
|
21
|
+
* When every contribution is zero (or the list is empty), nothing is allocated
|
|
22
|
+
* (all `micro === 0`) — there is no basis to weight the pool.
|
|
23
|
+
*/
|
|
24
|
+
export function allocateByContribution(sources, poolMicro) {
|
|
25
|
+
const weights = sources.map((s) => (s.contribution > 0 ? s.contribution : 0));
|
|
26
|
+
const total = weights.reduce((a, b) => a + b, 0);
|
|
27
|
+
if (total <= 0)
|
|
28
|
+
return sources.map((s) => ({ slug: s.slug, micro: 0, weight: 0 }));
|
|
29
|
+
const pool = Math.max(0, Math.floor(poolMicro));
|
|
30
|
+
const exact = weights.map((w) => (pool * w) / total);
|
|
31
|
+
const micro = exact.map((e) => Math.floor(e));
|
|
32
|
+
let remainder = pool - micro.reduce((a, b) => a + b, 0);
|
|
33
|
+
// Largest-remainder apportionment: hand each leftover micro-unit to the source
|
|
34
|
+
// with the biggest fractional part (tie → larger weight, then lower index, for
|
|
35
|
+
// determinism). remainder is an integer in [0, sources.length], so this closes
|
|
36
|
+
// the gap exactly without ever over-distributing.
|
|
37
|
+
const byFraction = exact
|
|
38
|
+
.map((e, i) => ({ i, frac: e - Math.floor(e), weight: weights[i] }))
|
|
39
|
+
.sort((a, b) => b.frac - a.frac || b.weight - a.weight || a.i - b.i);
|
|
40
|
+
for (let k = 0; remainder > 0 && k < byFraction.length; k++, remainder--) {
|
|
41
|
+
micro[byFraction[k].i]++;
|
|
42
|
+
}
|
|
43
|
+
return sources.map((s, i) => ({ slug: s.slug, micro: micro[i], weight: weights[i] / total }));
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=allocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allocation.js","sourceRoot":"","sources":["../src/allocation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAWH;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,OAA0D,EAC1D,SAAiB;IAEjB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACjD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAEnF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9C,IAAI,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAExD,+EAA+E;IAC/E,+EAA+E;IAC/E,+EAA+E;IAC/E,kDAAkD;IAClD,MAAM,UAAU,GAAG,KAAK;SACrB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC;SACpE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC;QACzE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC,CAAC,CAAE,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAE,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;AAClG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appraise.d.ts","sourceRoot":"","sources":["../src/appraise.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAa,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAqDjF,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,eAAe,EAAE,GAC5B,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAQ/B"}
|
package/dist/appraise.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Appraisal — estimate how relevant each candidate is to the research topic,
|
|
3
|
+
* from its free teaser alone. Returns a 0..1 score plus a one-line rationale
|
|
4
|
+
* that surfaces in the decision log.
|
|
5
|
+
*
|
|
6
|
+
* With OPENAI_API_KEY set, an LLM judges relevance. Without it, a keyword-overlap
|
|
7
|
+
* heuristic stands in, so the agent still makes a real (if blunter) judgement
|
|
8
|
+
* offline — no network, no key, still decides.
|
|
9
|
+
*/
|
|
10
|
+
import { getConfig } from "@naulon/shared";
|
|
11
|
+
const STOP = new Set([
|
|
12
|
+
"the", "a", "an", "and", "or", "of", "to", "in", "on", "for", "with", "is",
|
|
13
|
+
"are", "what", "how", "why", "about", "into", "we", "i", "it", "its", "that",
|
|
14
|
+
]);
|
|
15
|
+
function tokens(s) {
|
|
16
|
+
return new Set(s
|
|
17
|
+
.toLowerCase()
|
|
18
|
+
.replace(/[^a-z0-9\s]/g, " ")
|
|
19
|
+
.split(/\s+/)
|
|
20
|
+
.filter((w) => w.length > 2 && !STOP.has(w)));
|
|
21
|
+
}
|
|
22
|
+
/** Keyword-overlap relevance in [0,1]: matched topic terms / topic terms. */
|
|
23
|
+
function heuristicScore(topic, c) {
|
|
24
|
+
const topicTerms = tokens(topic);
|
|
25
|
+
const docTerms = tokens(`${c.title} ${c.summary}`);
|
|
26
|
+
if (topicTerms.size === 0)
|
|
27
|
+
return { relevance: 0, rationale: "empty topic" };
|
|
28
|
+
const hits = [...topicTerms].filter((t) => docTerms.has(t));
|
|
29
|
+
const relevance = hits.length / topicTerms.size;
|
|
30
|
+
const rationale = hits.length
|
|
31
|
+
? `shares ${hits.length}/${topicTerms.size} topic terms (${hits.join(", ")})`
|
|
32
|
+
: "no topic-term overlap in the teaser";
|
|
33
|
+
return { relevance, rationale };
|
|
34
|
+
}
|
|
35
|
+
async function llmScore(topic, c) {
|
|
36
|
+
try {
|
|
37
|
+
const { ChatOpenAI } = await import("@langchain/openai");
|
|
38
|
+
const model = new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 });
|
|
39
|
+
const res = await model.invoke(`Topic: "${topic}"\nEssay: "${c.title}" — ${c.summary}\n` +
|
|
40
|
+
`Rate 0-100 how useful this essay is for the topic, then one short reason. ` +
|
|
41
|
+
`Reply exactly as: SCORE|reason`);
|
|
42
|
+
const text = String(res.content);
|
|
43
|
+
const [scoreStr, ...rest] = text.split("|");
|
|
44
|
+
const score = Number(scoreStr?.trim());
|
|
45
|
+
if (!Number.isFinite(score))
|
|
46
|
+
return null;
|
|
47
|
+
return { relevance: Math.max(0, Math.min(1, score / 100)), rationale: rest.join("|").trim() };
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null; // any failure → caller falls back to heuristic
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export async function appraise(topic, candidates) {
|
|
54
|
+
const useLlm = Boolean(getConfig().OPENAI_API_KEY);
|
|
55
|
+
return Promise.all(candidates.map(async (c) => {
|
|
56
|
+
const scored = (useLlm ? await llmScore(topic, c) : null) ?? heuristicScore(topic, c);
|
|
57
|
+
return { ...c, relevance: scored.relevance, rationale: scored.rationale };
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=appraise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appraise.js","sourceRoot":"","sources":["../src/appraise.ts"],"names":[],"mappings":"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"}
|