|
1 | 1 | /** |
2 | 2 | * Seller services. |
3 | 3 | * |
4 | | - * `txline` — the headline product: a verified TxLINE fair-line read for a fixture. |
5 | | - * `freelance` — the generic LLM worker (the freelancer market's baseline seller): the brief goes to |
| 4 | + * `txline` - verified TxLINE fair-line reads for a fixture. |
| 5 | + * `risk-policy` - deterministic policy guardrails for a fixture/order. |
| 6 | + * `fan-card` - deterministic fan-facing explanation card for a fixture/order. |
| 7 | + * `freelance` - the generic LLM worker (the freelancer market's baseline seller): the brief goes to |
6 | 8 | * the LLM, the deliverable comes back as JSON. Without an LLM key it returns an error payload, which |
7 | | - * the verifier fails and the buyer refuses to pay for — no-capability sellers don't get released. |
| 9 | + * the verifier fails and the buyer refuses to pay for - no-capability sellers don't get released. |
8 | 10 | */ |
9 | 11 | import { complete, parseJsonReply } from '@pay/agent-runtime' |
10 | 12 |
|
11 | 13 | const TXLINE_BASE = process.env.TXLINE_BASE_URL || 'https://txline-dev.txodds.com' |
| 14 | +const SUPPORTED_SERVICES = ['txline', 'freelance', 'risk-policy', 'fan-card'] |
12 | 15 |
|
13 | 16 | export async function deliverService(request: string): Promise<string> { |
14 | 17 | const [first, ...rest] = request.trim().split(/\s+/).filter(Boolean) |
15 | 18 | const service = (first ?? 'txline').toLowerCase() |
16 | 19 | if (service === 'freelance') return freelanceService(rest.join(' ')) |
| 20 | + if (service === 'risk-policy') return riskPolicyService(rest.join(' ')) |
| 21 | + if (service === 'fan-card') return fanCardService(rest.join(' ')) |
17 | 22 | if (service !== 'txline') { |
18 | | - return JSON.stringify({ error: 'unsupported service', service, supported: ['txline', 'freelance'] }) |
| 23 | + return JSON.stringify({ error: 'unsupported service', service, supported: SUPPORTED_SERVICES }) |
19 | 24 | } |
20 | 25 | return txlineService(rest.join(' ')) |
21 | 26 | } |
22 | 27 |
|
| 28 | +function fixtureIdFrom(request: string): string | undefined { |
| 29 | + return request.trim().split(/\s+/).find((token) => /^\d+$/.test(token)) |
| 30 | +} |
| 31 | + |
| 32 | +async function riskPolicyService(request: string): Promise<string> { |
| 33 | + const fixtureId = fixtureIdFrom(request) |
| 34 | + return JSON.stringify({ |
| 35 | + service: 'risk-policy', |
| 36 | + ...(fixtureId ? { fixtureId } : {}), |
| 37 | + request: request || 'unspecified', |
| 38 | + policy: { |
| 39 | + action: fixtureId ? 'observe' : 'no-action', |
| 40 | + maxExposureSol: 0, |
| 41 | + requires: [ |
| 42 | + 'verified TxLINE fair-line payload', |
| 43 | + 'buyer budget policy pass', |
| 44 | + 'verifier pass before escrow release', |
| 45 | + ], |
| 46 | + guardrails: [ |
| 47 | + 'devnet only', |
| 48 | + 'no real-money wagering', |
| 49 | + 'no automated sportsbook execution', |
| 50 | + ], |
| 51 | + }, |
| 52 | + rationale: fixtureId |
| 53 | + ? `Fixture ${fixtureId} can be analyzed after verified fair-line delivery.` |
| 54 | + : 'No fixture id supplied.', |
| 55 | + }) |
| 56 | +} |
| 57 | + |
| 58 | +async function fanCardService(request: string): Promise<string> { |
| 59 | + const fixtureId = fixtureIdFrom(request) |
| 60 | + const target = fixtureId ? `fixture ${fixtureId}` : 'the selected fixture' |
| 61 | + return JSON.stringify({ |
| 62 | + service: 'fan-card', |
| 63 | + ...(fixtureId ? { fixtureId } : {}), |
| 64 | + request: request || 'unspecified', |
| 65 | + card: { |
| 66 | + title: `Fair-line explainer for ${target}`, |
| 67 | + audience: 'fan', |
| 68 | + explainer: 'TxODDS provides a break-even fair line. A value claim needs an outside book price above that fair price.', |
| 69 | + sections: [ |
| 70 | + { label: 'What was bought', value: 'verified fair-line context' }, |
| 71 | + { label: 'What it is not', value: 'not a sportsbook recommendation' }, |
| 72 | + { label: 'Proof path', value: 'hash-bound delivery, verifier verdict, devnet escrow release' }, |
| 73 | + ], |
| 74 | + shareCopy: `Agent-delivered fair-line context for ${target}; verification and settlement are recorded in the run ledger.`, |
| 75 | + }, |
| 76 | + limits: ['educational summary', 'not betting advice'], |
| 77 | + }) |
| 78 | +} |
| 79 | + |
23 | 80 | async function freelanceService(brief: string): Promise<string> { |
24 | 81 | try { |
25 | 82 | const text = await complete({ |
|
0 commit comments