@ergoblockchain/sage-widget 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,10 +3,21 @@
3
3
  [![npm](https://img.shields.io/npm/v/@ergoblockchain/sage-widget.svg)](https://www.npmjs.com/package/@ergoblockchain/sage-widget)
4
4
  [![license](https://img.shields.io/npm/l/@ergoblockchain/sage-widget.svg)](./LICENSE)
5
5
 
6
- Embeddable live-activity feed for **Sage** — the agent-economy concierge on [ergoblockchain.org](https://www.ergoblockchain.org). Drop a typed React component into your app, or call a vanilla DOM mount function from any framework. Every settled paid query Sage takes appears in the feed within a minute, with a clickable link to the on-chain receipt.
6
+ Embeddable widgets for **Sage** — the agent-economy concierge on [ergoblockchain.org](https://www.ergoblockchain.org). Use the read-only activity feed, or embed the paid Sage chat flow with quote, manual Note verification, streaming answer, receipt JSON, and public receipt link.
7
7
 
8
8
  > _Why this matters:_ Sage settles real paid AI queries on Ergo testnet. The feed makes the "agent-economy" thesis visibly provable wherever you embed it — not a marketing claim, a list of public on-chain receipts that update live.
9
9
 
10
+ ## What v0.2 ships
11
+
12
+ - `<SagePaymentWidget />` React component.
13
+ - `mountSagePaymentWidget(target, opts)` vanilla DOM mount.
14
+ - Typed clients for quote, verify, chat stream, receipt bundle, and activity feed.
15
+ - Tenant metadata and host-provided payment instructions.
16
+ - Payment lifecycle callbacks: quote, receipt, receipt bundle, tier, phase, status.
17
+ - Public receipt links plus machine-readable `/api/sage/receipt/<id>` links.
18
+
19
+ The canonical Sage host is **testnet live proof**. It can produce `full_receipt_bundle` receipts with Agreement JSON, Verification Receipt JSON, and Settlement Receipt JSON. Mainnet readiness remains audit-gated.
20
+
10
21
  ## Install
11
22
 
12
23
  ```bash
@@ -21,7 +32,7 @@ yarn add @ergoblockchain/sage-widget
21
32
 
22
33
  ## Use it
23
34
 
24
- ### React
35
+ ### React activity feed
25
36
 
26
37
  ```tsx
27
38
  import { SageActivityFeed } from "@ergoblockchain/sage-widget/react"
@@ -33,7 +44,30 @@ export function Footer() {
33
44
 
34
45
  The component renders into your DOM (no iframe), styles itself with inline styles to avoid host-CSS conflicts, and polls `/api/sage/activity` on the host you point it at.
35
46
 
36
- ### Vanilla / non-React
47
+ ### React paid chat
48
+
49
+ ```tsx
50
+ import { SagePaymentWidget } from "@ergoblockchain/sage-widget/react"
51
+
52
+ export function PaidSageBox() {
53
+ return (
54
+ <SagePaymentWidget
55
+ tenant={{ id: "my-site", label: "My Ergo app" }}
56
+ paymentInstructions={{
57
+ helperText: "Issue the quoted Accord Note from your testnet wallet, then paste the Note box id.",
58
+ walletUrl: "https://www.ergoblockchain.org/build/agent-payments",
59
+ }}
60
+ onQuote={(quote) => console.log("Sage quote", quote)}
61
+ onReceipt={(receipt) => console.log("Sage receipt", receipt.receiptUrl)}
62
+ onReceiptBundle={(bundle) => console.log("Receipt completeness", bundle.completeness)}
63
+ />
64
+ )
65
+ }
66
+ ```
67
+
68
+ The paid widget calls `/api/sage/quote`, shows the Accord Note payment fields, accepts a `noteBoxId`, calls `/api/sage/verify-payment`, streams `/api/sage/chat`, then surfaces the receipt link from `/api/sage/receipt/<id>`.
69
+
70
+ ### Vanilla / non-React activity feed
37
71
 
38
72
  ```ts
39
73
  import { mountSageFeed } from "@ergoblockchain/sage-widget/vanilla"
@@ -59,6 +93,22 @@ Or use the canonical hosted CDN drop-in (no install needed):
59
93
 
60
94
  That last form is an iframe variant served straight from ergoblockchain.org — total style isolation, zero install. Use the npm package when you want the markup inline (themable, accessibility-friendly, tree-shakeable).
61
95
 
96
+ ### Vanilla / non-React paid chat
97
+
98
+ ```ts
99
+ import { mountSagePaymentWidget } from "@ergoblockchain/sage-widget/vanilla"
100
+
101
+ const handle = mountSagePaymentWidget(document.getElementById("sage-chat")!, {
102
+ tenant: { id: "docs-footer", label: "Docs footer" },
103
+ })
104
+
105
+ // You can also start a question programmatically:
106
+ await handle.send("/research explain Ergo Notes")
107
+
108
+ // Inspect embed state:
109
+ console.log(handle.status().receipt?.receiptUrl)
110
+ ```
111
+
62
112
  ## Just the types
63
113
 
64
114
  If you want to render your own UI over Sage's API and skip the bundled components:
@@ -66,6 +116,9 @@ If you want to render your own UI over Sage's API and skip the bundled component
66
116
  ```ts
67
117
  import {
68
118
  fetchSageActivity,
119
+ fetchSageQuote,
120
+ streamSageChat,
121
+ verifySagePayment,
69
122
  type SageActivityEvent,
70
123
  type SageActivityResponse,
71
124
  } from "@ergoblockchain/sage-widget"
@@ -74,6 +127,18 @@ const data: SageActivityResponse = await fetchSageActivity({ limit: 10 })
74
127
  const settlements = data.events.filter((e) => e.type === "settlement")
75
128
  ```
76
129
 
130
+ Useful helpers:
131
+
132
+ ```ts
133
+ import { fetchSageReceipt, isFullSageReceiptBundle } from "@ergoblockchain/sage-widget"
134
+
135
+ const receipt = await fetchSageReceipt("f8752d10a2ece92fbc88065c3b92b94da621ec65943098f43c9e084deb763d81")
136
+
137
+ if (isFullSageReceiptBundle(receipt)) {
138
+ console.log("Agreement JSON is present", receipt.accord?.agreement_json)
139
+ }
140
+ ```
141
+
77
142
  ## Render-prop / bring-your-own-design
78
143
 
79
144
  ```tsx
@@ -97,6 +162,37 @@ const settlements = data.events.filter((e) => e.type === "settlement")
97
162
  | `style` | `CSSProperties` | — | Inline style merged onto the root container (React only). |
98
163
  | `children` | render-prop | — | Pass a function for full UI control; the component will skip the default markup. |
99
164
 
165
+ `SagePaymentWidget` also accepts:
166
+
167
+ | Prop | Type | Notes |
168
+ | ------------------- | ----------------------------------------- | ----------------------------------------------------------- |
169
+ | `tenant` | `{ id?: string; label?: string; headers?: Record<string,string> }` | Stable embed metadata and optional request headers. |
170
+ | `initialMessages` | `SageChatMessage[]` | Preloaded chat context. |
171
+ | `title` | `string` | Widget heading. |
172
+ | `placeholder` | `string` | Input placeholder. |
173
+ | `paymentInstructions` | `{ helperText?: string; walletUrl?: string; noteBoxLabel?: string }` | Host-specific payment copy and guide link. |
174
+ | `onMessage` | `(message, messages) => void` | Fired when the widget appends a chat message. |
175
+ | `onQuote` | `(quote) => void` | Fired after Sage returns a premium quote. |
176
+ | `onReceipt` | `(receipt) => void` | Fired after payment verifies and a receipt URL exists. |
177
+ | `onReceiptBundle` | `(bundle) => void` | Fired after the widget fetches `/api/sage/receipt/<id>`. |
178
+ | `onTier` | `("free" \| "premium") => void` | Fired when the stream reports model tier. |
179
+ | `onPhase` | `(phase) => void` | Fired on widget phase changes: idle, quoting, payment_required, verifying, streaming, error. |
180
+ | `onStatus` | `(status) => void` | Fired with a compact snapshot of phase, tier, quote, receipt, receipt bundle, and error. |
181
+
182
+ `onStatus` and the vanilla `handle.status()` also include `messages` and `activeQuestion`, so hosts can mirror widget state into their own telemetry or UI.
183
+
184
+ ## Payment model
185
+
186
+ v0.2 deliberately does **not** sign wallet transactions inside the widget. The widget shows the quote fields, accepts a Note box id, verifies it through Sage, then streams the answer and exposes the receipt. Hosts can pair it with their own wallet flow, Accord tooling, or a manual testnet Note issuer.
187
+
188
+ For the canonical site, a successful paid flow looks like:
189
+
190
+ ```text
191
+ question -> quote -> Ergo testnet Note -> verify-payment -> receipt bundle -> Sage answer
192
+ ```
193
+
194
+ The public receipt API is the source of truth. Articles, dashboards, and registry entries should link to it instead of duplicating receipt fields.
195
+
100
196
  ## Event shape
101
197
 
102
198
  Each event in `response.events` is a typed object:
@@ -123,10 +219,20 @@ For settlements, use `paymentNanoErg` (the value of the consumed Note) for "amou
123
219
  - **Accord Protocol**: [github.com/accord-protocol/accord-protocol](https://github.com/accord-protocol/accord-protocol)
124
220
  - **Sage in the registry**: [accord-protocol/registry/providers/sage.json](https://github.com/accord-protocol/accord-protocol/blob/main/registry/providers/sage.json)
125
221
 
222
+ ## Release checks
223
+
224
+ Before publishing:
225
+
226
+ ```bash
227
+ npm run typecheck
228
+ npm run smoke
229
+ npm pack --dry-run
230
+ ```
231
+
126
232
  ## Roadmap
127
233
 
128
- - **v0.1.x** _(current)_ — read-only activity feed (React + vanilla + types).
129
- - **v0.2** — `<SagePaymentWidget />` full chat with 402 + payment + Sonnet answer.
234
+ - **v0.1.x** — read-only activity feed (React + vanilla + types).
235
+ - **v0.2** _(current source)_ — `<SagePaymentWidget />` paid chat with quote, manual Note verify, streaming answer, receipt link, tenant config.
130
236
  - **v0.3** — generic `<AccordActivityFeed providerId="..." />` that works for any provider in the registry, not just Sage.
131
237
 
132
238
  ## License
package/dist/index.cjs CHANGED
@@ -9,13 +9,132 @@ var DEFAULT_REFRESH_MS = 6e4;
9
9
  async function fetchSageActivity(opts = {}) {
10
10
  const base = opts.apiBase ?? DEFAULT_API_BASE;
11
11
  const limit = Math.min(Math.max(opts.limit ?? DEFAULT_LIMIT, 1), 25);
12
- const url = `${base}/api/sage/activity?limit=${limit}`;
12
+ const url = `${trimSlash(base)}/api/sage/activity?limit=${limit}`;
13
13
  const res = await fetch(url, { signal: opts.signal });
14
14
  if (!res.ok) {
15
15
  throw new Error(`sage activity ${res.status}`);
16
16
  }
17
17
  return await res.json();
18
18
  }
19
+ async function fetchSageQuote(opts) {
20
+ const res = await fetch(`${apiBase(opts)}/api/sage/quote`, {
21
+ method: "POST",
22
+ headers: jsonHeaders(opts),
23
+ body: JSON.stringify({
24
+ question: opts.question,
25
+ history: opts.history ?? []
26
+ }),
27
+ signal: opts.signal
28
+ });
29
+ const body = await parseJson(res);
30
+ if (!res.ok) throw new Error(readError(body, `sage quote ${res.status}`));
31
+ return body;
32
+ }
33
+ async function verifySagePayment(opts) {
34
+ const res = await fetch(`${apiBase(opts)}/api/sage/verify-payment`, {
35
+ method: "POST",
36
+ headers: jsonHeaders(opts),
37
+ body: JSON.stringify({
38
+ quote: opts.quote,
39
+ question: opts.question,
40
+ noteBoxId: opts.noteBoxId
41
+ }),
42
+ signal: opts.signal
43
+ });
44
+ const body = await parseJson(res);
45
+ if (!res.ok) throw new Error(readError(body, `sage verify-payment ${res.status}`));
46
+ return body;
47
+ }
48
+ async function fetchSageReceipt(id, opts = {}) {
49
+ const res = await fetch(`${apiBase(opts)}/api/sage/receipt/${encodeURIComponent(id)}`, {
50
+ headers: requestHeaders(opts),
51
+ signal: opts.signal
52
+ });
53
+ const body = await parseJson(res);
54
+ if (!res.ok) throw new Error(readError(body, `sage receipt ${res.status}`));
55
+ return body;
56
+ }
57
+ function isFullSageReceiptBundle(value) {
58
+ return value?.ok === true && value.completeness === "full_receipt_bundle";
59
+ }
60
+ async function streamSageChat(opts) {
61
+ const res = await fetch(`${apiBase(opts)}/api/sage/chat`, {
62
+ method: "POST",
63
+ headers: jsonHeaders(opts),
64
+ body: JSON.stringify({
65
+ messages: opts.messages,
66
+ ...opts.paymentToken ? { paymentToken: opts.paymentToken } : {}
67
+ }),
68
+ signal: opts.signal
69
+ });
70
+ if (res.status === 402) {
71
+ const body = await parseJson(res);
72
+ return {
73
+ ok: false,
74
+ status: res.status,
75
+ text: "",
76
+ paymentRequired: body
77
+ };
78
+ }
79
+ if (!res.ok) {
80
+ const body = await parseJson(res);
81
+ return {
82
+ ok: false,
83
+ status: res.status,
84
+ text: "",
85
+ error: readError(body, `sage chat ${res.status}`)
86
+ };
87
+ }
88
+ if (!res.body) {
89
+ return { ok: false, status: res.status, text: "", error: "Sage chat stream missing body" };
90
+ }
91
+ const reader = res.body.getReader();
92
+ const decoder = new TextDecoder();
93
+ let buffer = "";
94
+ let text = "";
95
+ let tier;
96
+ while (true) {
97
+ const { value, done } = await reader.read();
98
+ if (done) break;
99
+ buffer += decoder.decode(value, { stream: true });
100
+ const parts = buffer.split("\n\n");
101
+ buffer = parts.pop() ?? "";
102
+ for (const part of parts) {
103
+ const event = parseSseEvent(part);
104
+ if (!event) continue;
105
+ if (event.type === "delta") text += event.text;
106
+ if (event.type === "tier") tier = event.tier;
107
+ opts.onEvent?.(event);
108
+ if (event.type === "error") {
109
+ return {
110
+ ok: false,
111
+ status: res.status,
112
+ text,
113
+ tier,
114
+ error: event.message
115
+ };
116
+ }
117
+ }
118
+ }
119
+ if (buffer.trim()) {
120
+ const event = parseSseEvent(buffer);
121
+ if (event) {
122
+ if (event.type === "delta") text += event.text;
123
+ if (event.type === "tier") tier = event.tier;
124
+ opts.onEvent?.(event);
125
+ if (event.type === "error") {
126
+ return {
127
+ ok: false,
128
+ status: res.status,
129
+ text,
130
+ tier,
131
+ error: event.message
132
+ };
133
+ }
134
+ }
135
+ }
136
+ return { ok: true, status: res.status, text, tier };
137
+ }
19
138
  function nanoToErg(nano) {
20
139
  if (!nano || nano <= 0) return "0";
21
140
  const erg = nano / 1e9;
@@ -32,20 +151,101 @@ function relativeTime(ms, now = Date.now()) {
32
151
  const day = Math.floor(hr / 24);
33
152
  return `${day}d ago`;
34
153
  }
35
- function receiptUrl(txId, apiBase = DEFAULT_API_BASE) {
36
- return `${apiBase}/r/sage/${txId}`;
154
+ function receiptUrl(txId, apiBase2 = DEFAULT_API_BASE) {
155
+ return `${trimSlash(apiBase2)}/r/sage/${txId}`;
37
156
  }
38
157
  function explorerUrl(txId, network = "testnet") {
39
158
  return network === "testnet" ? `https://testnet.ergoplatform.com/transactions/${txId}` : `https://explorer.ergoplatform.com/transactions/${txId}`;
40
159
  }
160
+ function apiBase(opts) {
161
+ return trimSlash(opts.apiBase ?? DEFAULT_API_BASE);
162
+ }
163
+ function trimSlash(value) {
164
+ return value.replace(/\/+$/, "");
165
+ }
166
+ function requestHeaders(opts) {
167
+ return {
168
+ ...opts.tenant?.id ? { "x-sage-tenant-id": opts.tenant.id } : {},
169
+ ...opts.tenant?.headers ?? {},
170
+ ...opts.headers ?? {}
171
+ };
172
+ }
173
+ function jsonHeaders(opts) {
174
+ return {
175
+ "content-type": "application/json",
176
+ ...requestHeaders(opts)
177
+ };
178
+ }
179
+ async function parseJson(res) {
180
+ const text = await res.text();
181
+ if (!text) return null;
182
+ try {
183
+ return JSON.parse(text);
184
+ } catch {
185
+ return { error: text };
186
+ }
187
+ }
188
+ function readError(body, fallback) {
189
+ if (body && typeof body === "object" && "error" in body) {
190
+ const error = body.error;
191
+ if (typeof error === "string") return error;
192
+ }
193
+ return fallback;
194
+ }
195
+ function parseSseEvent(raw) {
196
+ let eventName = "message";
197
+ let data = "";
198
+ for (const line of raw.split("\n")) {
199
+ if (line.startsWith("event:")) eventName = line.slice("event:".length).trim();
200
+ if (line.startsWith("data:")) data += line.slice("data:".length).trim();
201
+ }
202
+ if (!data) return null;
203
+ let parsed;
204
+ try {
205
+ parsed = JSON.parse(data);
206
+ } catch {
207
+ return null;
208
+ }
209
+ if (eventName === "tier") {
210
+ const tier = parsed.tier === "premium" ? "premium" : "free";
211
+ return {
212
+ type: "tier",
213
+ tier,
214
+ ...typeof parsed.model === "string" ? { model: parsed.model } : {}
215
+ };
216
+ }
217
+ if (eventName === "delta" && typeof parsed.text === "string") {
218
+ return { type: "delta", text: parsed.text };
219
+ }
220
+ if (eventName === "done") {
221
+ return {
222
+ type: "done",
223
+ ...typeof parsed.stopReason === "string" ? { stopReason: parsed.stopReason } : {},
224
+ ...typeof parsed.inputTokens === "number" ? { inputTokens: parsed.inputTokens } : {},
225
+ ...typeof parsed.outputTokens === "number" ? { outputTokens: parsed.outputTokens } : {}
226
+ };
227
+ }
228
+ if (eventName === "error") {
229
+ return {
230
+ type: "error",
231
+ message: typeof parsed.message === "string" ? parsed.message : "Sage stream error"
232
+ };
233
+ }
234
+ return null;
235
+ }
41
236
 
42
237
  exports.DEFAULT_API_BASE = DEFAULT_API_BASE;
43
238
  exports.DEFAULT_LIMIT = DEFAULT_LIMIT;
44
239
  exports.DEFAULT_REFRESH_MS = DEFAULT_REFRESH_MS;
45
240
  exports.explorerUrl = explorerUrl;
46
241
  exports.fetchSageActivity = fetchSageActivity;
242
+ exports.fetchSageQuote = fetchSageQuote;
243
+ exports.fetchSageReceipt = fetchSageReceipt;
244
+ exports.isFullSageReceiptBundle = isFullSageReceiptBundle;
47
245
  exports.nanoToErg = nanoToErg;
48
246
  exports.receiptUrl = receiptUrl;
49
247
  exports.relativeTime = relativeTime;
248
+ exports.streamSageChat = streamSageChat;
249
+ exports.verifySagePayment = verifySagePayment;
50
250
  //# sourceMappingURL=index.cjs.map
51
251
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/api.ts"],"names":[],"mappings":";;;AA6EO,IAAM,gBAAA,GAAmB;AACzB,IAAM,aAAA,GAAgB;AACtB,IAAM,kBAAA,GAAqB;;;AC5DlC,eAAsB,iBAAA,CACpB,IAAA,GAA6B,EAAC,EACC;AAC/B,EAAA,MAAM,IAAA,GAAO,KAAK,OAAA,IAAW,gBAAA;AAC7B,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,KAAK,KAAA,IAAS,aAAA,EAAe,CAAC,CAAA,EAAG,EAAE,CAAA;AACnE,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAI,CAAA,yBAAA,EAA4B,KAAK,CAAA,CAAA;AACpD,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,MAAA,EAAQ,IAAA,CAAK,QAAQ,CAAA;AACpD,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,cAAA,EAAiB,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAAA,EAC/C;AACA,EAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AACzB;AAGO,SAAS,UAAU,IAAA,EAAkC;AAC1D,EAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,IAAQ,CAAA,EAAG,OAAO,GAAA;AAC/B,EAAA,MAAM,MAAM,IAAA,GAAO,GAAA;AACnB,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,CAAA,CAAE,OAAA,CAAQ,UAAU,EAAE,CAAA;AAC5C;AAGO,SAAS,YAAA,CAAa,EAAA,EAAY,GAAA,GAAc,IAAA,CAAK,KAAI,EAAW;AACzE,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,MAAM,EAAE,CAAA;AACjC,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,IAAA,GAAO,GAAI,CAAA;AAClC,EAAA,IAAI,GAAA,GAAM,EAAA,EAAI,OAAO,CAAA,EAAG,GAAG,CAAA,KAAA,CAAA;AAC3B,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,EAAE,CAAA;AAC/B,EAAA,IAAI,GAAA,GAAM,EAAA,EAAI,OAAO,CAAA,EAAG,GAAG,CAAA,KAAA,CAAA;AAC3B,EAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,EAAE,CAAA;AAC9B,EAAA,IAAI,EAAA,GAAK,EAAA,EAAI,OAAO,CAAA,EAAG,EAAE,CAAA,KAAA,CAAA;AACzB,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,EAAA,GAAK,EAAE,CAAA;AAC9B,EAAA,OAAO,GAAG,GAAG,CAAA,KAAA,CAAA;AACf;AAGO,SAAS,UAAA,CAAW,IAAA,EAAc,OAAA,GAAkB,gBAAA,EAA0B;AACnF,EAAA,OAAO,CAAA,EAAG,OAAO,CAAA,QAAA,EAAW,IAAI,CAAA,CAAA;AAClC;AAGO,SAAS,WAAA,CACd,IAAA,EACA,OAAA,GAAiC,SAAA,EACzB;AACR,EAAA,OAAO,YAAY,SAAA,GACf,CAAA,8CAAA,EAAiD,IAAI,CAAA,CAAA,GACrD,kDAAkD,IAAI,CAAA,CAAA;AAC5D","file":"index.cjs","sourcesContent":["/**\n * Public types — mirror of /api/sage/activity response shape.\n *\n * The source of truth is the API at https://www.ergoblockchain.org/api/sage/activity;\n * this file re-states the schema so consumers don't have to depend on the\n * server-side fetcher.\n */\n\nexport type SageActivityType = \"settlement\" | \"issuance\" | \"transfer\"\n\nexport interface SageActivityEvent {\n /** 64-char hex transaction id. */\n txId: string\n /** Block height at inclusion. */\n blockHeight: number\n /** Block timestamp (ms epoch). */\n timestamp: number\n /** Heuristic classification of the tx. */\n type: SageActivityType\n /** nanoERG flowing into the seller wallet from this tx (sum of outputs). */\n inflowNanoErg: number\n /**\n * For settlements: value of the redeemed Note (= what the buyer paid).\n * For other event types: undefined.\n *\n * Use this — not `inflowNanoErg` — when displaying \"amount paid for a\n * settled query\". `inflowNanoErg` includes change boxes in test setups\n * where the buyer and seller share an address.\n */\n paymentNanoErg?: number\n /** First input box that carries Note-shape registers, if any. */\n noteBoxId?: string\n}\n\nexport interface SageActivityResponse {\n ok: boolean\n network: \"testnet\" | \"mainnet\"\n /** Sage seller wallet address. */\n receiver: string\n /** Total number of txs ever touching the wallet, per the explorer. */\n total: number\n events: SageActivityEvent[]\n error?: string\n}\n\n/**\n * Configuration accepted by every entry point (React component +\n * vanilla mount fn). Defaults below are sensible for the canonical\n * ergoblockchain.org deployment.\n */\nexport interface SageWidgetOptions {\n /**\n * Base URL of the Sage host. Override if you run your own Sage\n * deployment behind a custom domain. Default: ergoblockchain.org.\n */\n apiBase?: string\n /**\n * Number of events to display (max 25). Default: 5.\n */\n limit?: number\n /**\n * Polling interval in ms. Default: 60000 (60s). Set to 0 to disable\n * polling — the widget will fetch once on mount and never refresh.\n */\n refreshMs?: number\n /**\n * Optional callback fired every time a fresh response arrives. Useful\n * for analytics or for triggering host-side animations on new\n * settlements.\n */\n onUpdate?: (response: SageActivityResponse) => void\n /**\n * Optional callback fired on fetch errors. Default: console.warn.\n */\n onError?: (error: unknown) => void\n}\n\nexport const DEFAULT_API_BASE = \"https://www.ergoblockchain.org\"\nexport const DEFAULT_LIMIT = 5\nexport const DEFAULT_REFRESH_MS = 60_000\n","/**\n * Thin client over /api/sage/activity.\n *\n * Pure fetch + shape — no rendering, no DOM. React + vanilla mounts\n * both call into here so the API contract lives in one place.\n */\n\nimport {\n DEFAULT_API_BASE,\n DEFAULT_LIMIT,\n type SageActivityResponse,\n} from \"./types\"\n\nexport interface FetchActivityOptions {\n apiBase?: string\n limit?: number\n signal?: AbortSignal\n}\n\nexport async function fetchSageActivity(\n opts: FetchActivityOptions = {},\n): Promise<SageActivityResponse> {\n const base = opts.apiBase ?? DEFAULT_API_BASE\n const limit = Math.min(Math.max(opts.limit ?? DEFAULT_LIMIT, 1), 25)\n const url = `${base}/api/sage/activity?limit=${limit}`\n const res = await fetch(url, { signal: opts.signal })\n if (!res.ok) {\n throw new Error(`sage activity ${res.status}`)\n }\n return (await res.json()) as SageActivityResponse\n}\n\n/** nanoERG → \"0.001\" (trims trailing zeros, max 9 decimals). */\nexport function nanoToErg(nano: number | undefined): string {\n if (!nano || nano <= 0) return \"0\"\n const erg = nano / 1e9\n return erg.toFixed(9).replace(/\\.?0+$/, \"\")\n}\n\n/** Cheap relative-time formatter, ASCII-only, no Intl deps. */\nexport function relativeTime(ms: number, now: number = Date.now()): string {\n const diff = Math.max(0, now - ms)\n const sec = Math.floor(diff / 1000)\n if (sec < 60) return `${sec}s ago`\n const min = Math.floor(sec / 60)\n if (min < 60) return `${min}m ago`\n const hr = Math.floor(min / 60)\n if (hr < 24) return `${hr}h ago`\n const day = Math.floor(hr / 24)\n return `${day}d ago`\n}\n\n/** Receipt URL for a settled tx, given the host base. */\nexport function receiptUrl(txId: string, apiBase: string = DEFAULT_API_BASE): string {\n return `${apiBase}/r/sage/${txId}`\n}\n\n/** Explorer URL for a tx on the given network. */\nexport function explorerUrl(\n txId: string,\n network: \"testnet\" | \"mainnet\" = \"testnet\",\n): string {\n return network === \"testnet\"\n ? `https://testnet.ergoplatform.com/transactions/${txId}`\n : `https://explorer.ergoplatform.com/transactions/${txId}`\n}\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/api.ts"],"names":["apiBase"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Public types for the Sage activity feed and paid chat flow.\n *\n * The source of truth is the API at https://www.ergoblockchain.org/api/sage/activity;\n * this file re-states the schema so consumers don't have to depend on the\n * server-side fetcher.\n */\n\nexport type SageActivityType = \"settlement\" | \"issuance\" | \"transfer\"\n\nexport interface SageActivityEvent {\n /** 64-char hex transaction id. */\n txId: string\n /** Block height at inclusion. */\n blockHeight: number\n /** Block timestamp (ms epoch). */\n timestamp: number\n /** Heuristic classification of the tx. */\n type: SageActivityType\n /** nanoERG flowing into the seller wallet from this tx (sum of outputs). */\n inflowNanoErg: number\n /**\n * For settlements: value of the redeemed Note (= what the buyer paid).\n * For other event types: undefined.\n *\n * Use this — not `inflowNanoErg` — when displaying \"amount paid for a\n * settled query\". `inflowNanoErg` includes change boxes in test setups\n * where the buyer and seller share an address.\n */\n paymentNanoErg?: number\n /** First input box that carries Note-shape registers, if any. */\n noteBoxId?: string\n}\n\nexport interface SageActivityResponse {\n ok: boolean\n network: \"testnet\" | \"mainnet\"\n /** Sage seller wallet address. */\n receiver: string\n /** Total number of txs ever touching the wallet, per the explorer. */\n total: number\n events: SageActivityEvent[]\n error?: string\n}\n\n/**\n * Configuration accepted by every entry point (React component +\n * vanilla mount fn). Defaults below are sensible for the canonical\n * ergoblockchain.org deployment.\n */\nexport interface SageWidgetOptions {\n /**\n * Base URL of the Sage host. Override if you run your own Sage\n * deployment behind a custom domain. Default: ergoblockchain.org.\n */\n apiBase?: string\n /**\n * Number of events to display (max 25). Default: 5.\n */\n limit?: number\n /**\n * Polling interval in ms. Default: 60000 (60s). Set to 0 to disable\n * polling — the widget will fetch once on mount and never refresh.\n */\n refreshMs?: number\n /**\n * Optional callback fired every time a fresh response arrives. Useful\n * for analytics or for triggering host-side animations on new\n * settlements.\n */\n onUpdate?: (response: SageActivityResponse) => void\n /**\n * Optional callback fired on fetch errors. Default: console.warn.\n */\n onError?: (error: unknown) => void\n}\n\nexport type SageChatRole = \"user\" | \"assistant\"\n\nexport interface SageChatMessage {\n role: SageChatRole\n content: string\n}\n\nexport interface SageTenantConfig {\n /** Stable tenant id for analytics, logs, or future multi-tenant routing. */\n id?: string\n /** Human-facing label shown in the default widgets. */\n label?: string\n /** Extra headers attached to Sage API requests. */\n headers?: Record<string, string>\n}\n\nexport interface SageQuote {\n quoteId: string\n taskHash: string\n price: string\n issuedAt?: string\n expiresAt: string\n receiverAddress: string\n reserveBoxId: string\n deadline: `+${number} blocks`\n}\n\nexport type SagePremiumReason =\n | \"explicit_command\"\n | \"code_request\"\n | \"long_answer\"\n | \"deep_research\"\n | \"multi_turn_followup\"\n\nexport interface SageQuoteResponse {\n premium: boolean\n reason?: SagePremiumReason\n rationale?: string\n quote?: SageQuote\n}\n\nexport interface SageVerifyPaymentResponse {\n ok: true\n paymentToken: string\n receiptId: string\n receiptUrl: string\n receiptApiUrl: string\n settlementTxId?: string | null\n accordSettlementId?: string\n receiptStorage?: {\n ok: boolean\n skipped?: boolean\n reason?: string\n path?: string\n aliases?: string[]\n error?: string\n }\n}\n\nexport interface SagePremiumPaymentRequired {\n error: \"premium_payment_required\"\n reason?: SagePremiumReason\n rationale?: string\n}\n\nexport type SageChatTier = \"free\" | \"premium\"\n\nexport type SageChatStreamEvent =\n | { type: \"tier\"; tier: SageChatTier; model?: string }\n | { type: \"delta\"; text: string }\n | { type: \"done\"; stopReason?: string; inputTokens?: number; outputTokens?: number }\n | { type: \"error\"; message: string }\n\nexport interface SageChatStreamResult {\n ok: boolean\n status: number\n text: string\n tier?: SageChatTier\n paymentRequired?: SagePremiumPaymentRequired\n error?: string\n}\n\nexport interface SageReceiptBundle {\n ok: true\n type: \"sage.receipt_bundle.v1\"\n version: \"v1\"\n id: string\n status: \"settled_on_chain\" | \"verified_pending_redemption\"\n completeness: \"full_receipt_bundle\" | \"full\" | \"chain_proof_only\"\n public_receipt_url: string\n api_receipt_url: string\n explorer_url: string | null\n accord?: {\n agreement_hash?: string | null\n verification_receipt_hash?: string | null\n settlement_receipt_hash?: string | null\n agreement_json?: unknown\n verification_receipt_json?: unknown\n settlement_receipt_json?: unknown\n }\n}\n\nexport interface SagePaymentWidgetOptions {\n /**\n * Base URL of the Sage host. Default: ergoblockchain.org.\n */\n apiBase?: string\n /**\n * Optional tenant metadata. Current public Sage ignores tenant routing,\n * but the widget keeps this shape stable for multi-tenant deployments.\n */\n tenant?: SageTenantConfig\n /** Initial chat messages, useful for preloaded context. */\n initialMessages?: SageChatMessage[]\n /** Placeholder text for the default input. */\n placeholder?: string\n /** Widget heading. React also accepts this through component props. */\n title?: string\n /** Optional host-specific payment copy and links. */\n paymentInstructions?: SagePaymentInstructions\n /** Called whenever a message is appended by the widget. */\n onMessage?: (message: SageChatMessage, messages: SageChatMessage[]) => void\n /** Called after Sage returns a premium quote. */\n onQuote?: (quote: SageQuoteResponse) => void\n /** Called after a payment verifies and Sage returns a receipt link. */\n onReceipt?: (receipt: SageVerifyPaymentResponse) => void\n /** Called after the widget fetches the full machine-readable receipt bundle. */\n onReceiptBundle?: (receipt: SageReceiptBundle) => void\n /** Called when the chat stream reports free vs premium tier. */\n onTier?: (tier: SageChatTier) => void\n /** Called when the widget phase changes. */\n onPhase?: (phase: SagePaymentPhase) => void\n /** Called with a compact state snapshot after important widget events. */\n onStatus?: (status: SagePaymentWidgetStatus) => void\n /** Optional callback fired on fetch or stream errors. */\n onError?: (error: unknown) => void\n}\n\nexport type SagePaymentPhase =\n | \"idle\"\n | \"quoting\"\n | \"payment_required\"\n | \"verifying\"\n | \"streaming\"\n | \"error\"\n\nexport interface SagePaymentInstructions {\n /** Short copy displayed above the Note box input. */\n helperText?: string\n /** Optional link to host payment/wallet instructions. */\n walletUrl?: string\n /** Optional custom label for the Note box input. */\n noteBoxLabel?: string\n}\n\nexport interface SagePaymentWidgetStatus {\n phase: SagePaymentPhase\n tier: SageChatTier | null\n quote: SageQuoteResponse[\"quote\"] | null\n receipt: SageVerifyPaymentResponse | null\n receiptBundle: SageReceiptBundle | null\n error: string | null\n /** Latest chat transcript known to the widget. */\n messages: SageChatMessage[]\n /** Question currently tied to the active quote/payment cycle. */\n activeQuestion: string | null\n}\n\nexport const DEFAULT_API_BASE = \"https://www.ergoblockchain.org\"\nexport const DEFAULT_LIMIT = 5\nexport const DEFAULT_REFRESH_MS = 60_000\n","/**\n * Thin client over /api/sage/activity.\n *\n * Pure fetch + shape — no rendering, no DOM. React + vanilla mounts\n * both call into here so the API contract lives in one place.\n */\n\nimport {\n DEFAULT_API_BASE,\n DEFAULT_LIMIT,\n type SageChatMessage,\n type SageChatStreamEvent,\n type SageChatStreamResult,\n type SageQuote,\n type SageQuoteResponse,\n type SageReceiptBundle,\n type SageTenantConfig,\n type SageVerifyPaymentResponse,\n type SageActivityResponse,\n} from \"./types\"\n\nexport interface FetchActivityOptions {\n apiBase?: string\n limit?: number\n signal?: AbortSignal\n}\n\nexport interface SageRequestOptions {\n apiBase?: string\n tenant?: SageTenantConfig\n headers?: Record<string, string>\n signal?: AbortSignal\n}\n\nexport interface FetchSageQuoteOptions extends SageRequestOptions {\n question: string\n history?: SageChatMessage[]\n}\n\nexport interface VerifySagePaymentOptions extends SageRequestOptions {\n quote: SageQuote\n question: string\n noteBoxId: string\n}\n\nexport interface StreamSageChatOptions extends SageRequestOptions {\n messages: SageChatMessage[]\n paymentToken?: string\n onEvent?: (event: SageChatStreamEvent) => void\n}\n\nexport async function fetchSageActivity(\n opts: FetchActivityOptions = {},\n): Promise<SageActivityResponse> {\n const base = opts.apiBase ?? DEFAULT_API_BASE\n const limit = Math.min(Math.max(opts.limit ?? DEFAULT_LIMIT, 1), 25)\n const url = `${trimSlash(base)}/api/sage/activity?limit=${limit}`\n const res = await fetch(url, { signal: opts.signal })\n if (!res.ok) {\n throw new Error(`sage activity ${res.status}`)\n }\n return (await res.json()) as SageActivityResponse\n}\n\nexport async function fetchSageQuote(\n opts: FetchSageQuoteOptions,\n): Promise<SageQuoteResponse> {\n const res = await fetch(`${apiBase(opts)}/api/sage/quote`, {\n method: \"POST\",\n headers: jsonHeaders(opts),\n body: JSON.stringify({\n question: opts.question,\n history: opts.history ?? [],\n }),\n signal: opts.signal,\n })\n const body = await parseJson(res)\n if (!res.ok) throw new Error(readError(body, `sage quote ${res.status}`))\n return body as SageQuoteResponse\n}\n\nexport async function verifySagePayment(\n opts: VerifySagePaymentOptions,\n): Promise<SageVerifyPaymentResponse> {\n const res = await fetch(`${apiBase(opts)}/api/sage/verify-payment`, {\n method: \"POST\",\n headers: jsonHeaders(opts),\n body: JSON.stringify({\n quote: opts.quote,\n question: opts.question,\n noteBoxId: opts.noteBoxId,\n }),\n signal: opts.signal,\n })\n const body = await parseJson(res)\n if (!res.ok) throw new Error(readError(body, `sage verify-payment ${res.status}`))\n return body as SageVerifyPaymentResponse\n}\n\nexport async function fetchSageReceipt(\n id: string,\n opts: SageRequestOptions = {},\n): Promise<SageReceiptBundle> {\n const res = await fetch(`${apiBase(opts)}/api/sage/receipt/${encodeURIComponent(id)}`, {\n headers: requestHeaders(opts),\n signal: opts.signal,\n })\n const body = await parseJson(res)\n if (!res.ok) throw new Error(readError(body, `sage receipt ${res.status}`))\n return body as SageReceiptBundle\n}\n\nexport function isFullSageReceiptBundle(value: SageReceiptBundle | null | undefined): boolean {\n return value?.ok === true && value.completeness === \"full_receipt_bundle\"\n}\n\nexport async function streamSageChat(\n opts: StreamSageChatOptions,\n): Promise<SageChatStreamResult> {\n const res = await fetch(`${apiBase(opts)}/api/sage/chat`, {\n method: \"POST\",\n headers: jsonHeaders(opts),\n body: JSON.stringify({\n messages: opts.messages,\n ...(opts.paymentToken ? { paymentToken: opts.paymentToken } : {}),\n }),\n signal: opts.signal,\n })\n\n if (res.status === 402) {\n const body = await parseJson(res)\n return {\n ok: false,\n status: res.status,\n text: \"\",\n paymentRequired: body as SageChatStreamResult[\"paymentRequired\"],\n }\n }\n\n if (!res.ok) {\n const body = await parseJson(res)\n return {\n ok: false,\n status: res.status,\n text: \"\",\n error: readError(body, `sage chat ${res.status}`),\n }\n }\n\n if (!res.body) {\n return { ok: false, status: res.status, text: \"\", error: \"Sage chat stream missing body\" }\n }\n\n const reader = res.body.getReader()\n const decoder = new TextDecoder()\n let buffer = \"\"\n let text = \"\"\n let tier: SageChatStreamResult[\"tier\"]\n\n while (true) {\n const { value, done } = await reader.read()\n if (done) break\n buffer += decoder.decode(value, { stream: true })\n const parts = buffer.split(\"\\n\\n\")\n buffer = parts.pop() ?? \"\"\n for (const part of parts) {\n const event = parseSseEvent(part)\n if (!event) continue\n if (event.type === \"delta\") text += event.text\n if (event.type === \"tier\") tier = event.tier\n opts.onEvent?.(event)\n if (event.type === \"error\") {\n return {\n ok: false,\n status: res.status,\n text,\n tier,\n error: event.message,\n }\n }\n }\n }\n\n if (buffer.trim()) {\n const event = parseSseEvent(buffer)\n if (event) {\n if (event.type === \"delta\") text += event.text\n if (event.type === \"tier\") tier = event.tier\n opts.onEvent?.(event)\n if (event.type === \"error\") {\n return {\n ok: false,\n status: res.status,\n text,\n tier,\n error: event.message,\n }\n }\n }\n }\n\n return { ok: true, status: res.status, text, tier }\n}\n\n/** nanoERG → \"0.001\" (trims trailing zeros, max 9 decimals). */\nexport function nanoToErg(nano: number | undefined): string {\n if (!nano || nano <= 0) return \"0\"\n const erg = nano / 1e9\n return erg.toFixed(9).replace(/\\.?0+$/, \"\")\n}\n\n/** Cheap relative-time formatter, ASCII-only, no Intl deps. */\nexport function relativeTime(ms: number, now: number = Date.now()): string {\n const diff = Math.max(0, now - ms)\n const sec = Math.floor(diff / 1000)\n if (sec < 60) return `${sec}s ago`\n const min = Math.floor(sec / 60)\n if (min < 60) return `${min}m ago`\n const hr = Math.floor(min / 60)\n if (hr < 24) return `${hr}h ago`\n const day = Math.floor(hr / 24)\n return `${day}d ago`\n}\n\n/** Receipt URL for a settled tx, given the host base. */\nexport function receiptUrl(txId: string, apiBase: string = DEFAULT_API_BASE): string {\n return `${trimSlash(apiBase)}/r/sage/${txId}`\n}\n\n/** Explorer URL for a tx on the given network. */\nexport function explorerUrl(\n txId: string,\n network: \"testnet\" | \"mainnet\" = \"testnet\",\n): string {\n return network === \"testnet\"\n ? `https://testnet.ergoplatform.com/transactions/${txId}`\n : `https://explorer.ergoplatform.com/transactions/${txId}`\n}\n\nfunction apiBase(opts: SageRequestOptions): string {\n return trimSlash(opts.apiBase ?? DEFAULT_API_BASE)\n}\n\nfunction trimSlash(value: string): string {\n return value.replace(/\\/+$/, \"\")\n}\n\nfunction requestHeaders(opts: SageRequestOptions): Record<string, string> {\n return {\n ...(opts.tenant?.id ? { \"x-sage-tenant-id\": opts.tenant.id } : {}),\n ...(opts.tenant?.headers ?? {}),\n ...(opts.headers ?? {}),\n }\n}\n\nfunction jsonHeaders(opts: SageRequestOptions): Record<string, string> {\n return {\n \"content-type\": \"application/json\",\n ...requestHeaders(opts),\n }\n}\n\nasync function parseJson(res: Response): Promise<unknown> {\n const text = await res.text()\n if (!text) return null\n try {\n return JSON.parse(text)\n } catch {\n return { error: text }\n }\n}\n\nfunction readError(body: unknown, fallback: string): string {\n if (body && typeof body === \"object\" && \"error\" in body) {\n const error = (body as { error?: unknown }).error\n if (typeof error === \"string\") return error\n }\n return fallback\n}\n\nfunction parseSseEvent(raw: string): SageChatStreamEvent | null {\n let eventName = \"message\"\n let data = \"\"\n for (const line of raw.split(\"\\n\")) {\n if (line.startsWith(\"event:\")) eventName = line.slice(\"event:\".length).trim()\n if (line.startsWith(\"data:\")) data += line.slice(\"data:\".length).trim()\n }\n if (!data) return null\n let parsed: Record<string, unknown>\n try {\n parsed = JSON.parse(data) as Record<string, unknown>\n } catch {\n return null\n }\n if (eventName === \"tier\") {\n const tier = parsed.tier === \"premium\" ? \"premium\" : \"free\"\n return {\n type: \"tier\",\n tier,\n ...(typeof parsed.model === \"string\" ? { model: parsed.model } : {}),\n }\n }\n if (eventName === \"delta\" && typeof parsed.text === \"string\") {\n return { type: \"delta\", text: parsed.text }\n }\n if (eventName === \"done\") {\n return {\n type: \"done\",\n ...(typeof parsed.stopReason === \"string\" ? { stopReason: parsed.stopReason } : {}),\n ...(typeof parsed.inputTokens === \"number\" ? { inputTokens: parsed.inputTokens } : {}),\n ...(typeof parsed.outputTokens === \"number\" ? { outputTokens: parsed.outputTokens } : {}),\n }\n }\n if (eventName === \"error\") {\n return {\n type: \"error\",\n message: typeof parsed.message === \"string\" ? parsed.message : \"Sage stream error\",\n }\n }\n return null\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as SageActivityResponse } from './types-uzMz6_mP.cjs';
2
- export { D as DEFAULT_API_BASE, a as DEFAULT_LIMIT, b as DEFAULT_REFRESH_MS, S as SageActivityEvent, d as SageActivityType, e as SageWidgetOptions } from './types-uzMz6_mP.cjs';
1
+ import { c as SageActivityResponse, s as SageTenantConfig, e as SageChatMessage, q as SageQuoteResponse, r as SageReceiptBundle, g as SageChatStreamEvent, h as SageChatStreamResult, p as SageQuote, t as SageVerifyPaymentResponse } from './types-yv1THHVz.cjs';
2
+ export { D as DEFAULT_API_BASE, a as DEFAULT_LIMIT, b as DEFAULT_REFRESH_MS, S as SageActivityEvent, d as SageActivityType, f as SageChatRole, i as SageChatTier, j as SagePaymentInstructions, k as SagePaymentPhase, l as SagePaymentWidgetOptions, m as SagePaymentWidgetStatus, n as SagePremiumPaymentRequired, o as SagePremiumReason, u as SageWidgetOptions } from './types-yv1THHVz.cjs';
3
3
 
4
4
  /**
5
5
  * Thin client over /api/sage/activity.
@@ -13,7 +13,32 @@ interface FetchActivityOptions {
13
13
  limit?: number;
14
14
  signal?: AbortSignal;
15
15
  }
16
+ interface SageRequestOptions {
17
+ apiBase?: string;
18
+ tenant?: SageTenantConfig;
19
+ headers?: Record<string, string>;
20
+ signal?: AbortSignal;
21
+ }
22
+ interface FetchSageQuoteOptions extends SageRequestOptions {
23
+ question: string;
24
+ history?: SageChatMessage[];
25
+ }
26
+ interface VerifySagePaymentOptions extends SageRequestOptions {
27
+ quote: SageQuote;
28
+ question: string;
29
+ noteBoxId: string;
30
+ }
31
+ interface StreamSageChatOptions extends SageRequestOptions {
32
+ messages: SageChatMessage[];
33
+ paymentToken?: string;
34
+ onEvent?: (event: SageChatStreamEvent) => void;
35
+ }
16
36
  declare function fetchSageActivity(opts?: FetchActivityOptions): Promise<SageActivityResponse>;
37
+ declare function fetchSageQuote(opts: FetchSageQuoteOptions): Promise<SageQuoteResponse>;
38
+ declare function verifySagePayment(opts: VerifySagePaymentOptions): Promise<SageVerifyPaymentResponse>;
39
+ declare function fetchSageReceipt(id: string, opts?: SageRequestOptions): Promise<SageReceiptBundle>;
40
+ declare function isFullSageReceiptBundle(value: SageReceiptBundle | null | undefined): boolean;
41
+ declare function streamSageChat(opts: StreamSageChatOptions): Promise<SageChatStreamResult>;
17
42
  /** nanoERG → "0.001" (trims trailing zeros, max 9 decimals). */
18
43
  declare function nanoToErg(nano: number | undefined): string;
19
44
  /** Cheap relative-time formatter, ASCII-only, no Intl deps. */
@@ -23,4 +48,4 @@ declare function receiptUrl(txId: string, apiBase?: string): string;
23
48
  /** Explorer URL for a tx on the given network. */
24
49
  declare function explorerUrl(txId: string, network?: "testnet" | "mainnet"): string;
25
50
 
26
- export { SageActivityResponse, explorerUrl, fetchSageActivity, nanoToErg, receiptUrl, relativeTime };
51
+ export { SageActivityResponse, SageChatMessage, SageChatStreamEvent, SageChatStreamResult, SageQuote, SageQuoteResponse, SageReceiptBundle, SageTenantConfig, SageVerifyPaymentResponse, explorerUrl, fetchSageActivity, fetchSageQuote, fetchSageReceipt, isFullSageReceiptBundle, nanoToErg, receiptUrl, relativeTime, streamSageChat, verifySagePayment };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { c as SageActivityResponse } from './types-uzMz6_mP.js';
2
- export { D as DEFAULT_API_BASE, a as DEFAULT_LIMIT, b as DEFAULT_REFRESH_MS, S as SageActivityEvent, d as SageActivityType, e as SageWidgetOptions } from './types-uzMz6_mP.js';
1
+ import { c as SageActivityResponse, s as SageTenantConfig, e as SageChatMessage, q as SageQuoteResponse, r as SageReceiptBundle, g as SageChatStreamEvent, h as SageChatStreamResult, p as SageQuote, t as SageVerifyPaymentResponse } from './types-yv1THHVz.js';
2
+ export { D as DEFAULT_API_BASE, a as DEFAULT_LIMIT, b as DEFAULT_REFRESH_MS, S as SageActivityEvent, d as SageActivityType, f as SageChatRole, i as SageChatTier, j as SagePaymentInstructions, k as SagePaymentPhase, l as SagePaymentWidgetOptions, m as SagePaymentWidgetStatus, n as SagePremiumPaymentRequired, o as SagePremiumReason, u as SageWidgetOptions } from './types-yv1THHVz.js';
3
3
 
4
4
  /**
5
5
  * Thin client over /api/sage/activity.
@@ -13,7 +13,32 @@ interface FetchActivityOptions {
13
13
  limit?: number;
14
14
  signal?: AbortSignal;
15
15
  }
16
+ interface SageRequestOptions {
17
+ apiBase?: string;
18
+ tenant?: SageTenantConfig;
19
+ headers?: Record<string, string>;
20
+ signal?: AbortSignal;
21
+ }
22
+ interface FetchSageQuoteOptions extends SageRequestOptions {
23
+ question: string;
24
+ history?: SageChatMessage[];
25
+ }
26
+ interface VerifySagePaymentOptions extends SageRequestOptions {
27
+ quote: SageQuote;
28
+ question: string;
29
+ noteBoxId: string;
30
+ }
31
+ interface StreamSageChatOptions extends SageRequestOptions {
32
+ messages: SageChatMessage[];
33
+ paymentToken?: string;
34
+ onEvent?: (event: SageChatStreamEvent) => void;
35
+ }
16
36
  declare function fetchSageActivity(opts?: FetchActivityOptions): Promise<SageActivityResponse>;
37
+ declare function fetchSageQuote(opts: FetchSageQuoteOptions): Promise<SageQuoteResponse>;
38
+ declare function verifySagePayment(opts: VerifySagePaymentOptions): Promise<SageVerifyPaymentResponse>;
39
+ declare function fetchSageReceipt(id: string, opts?: SageRequestOptions): Promise<SageReceiptBundle>;
40
+ declare function isFullSageReceiptBundle(value: SageReceiptBundle | null | undefined): boolean;
41
+ declare function streamSageChat(opts: StreamSageChatOptions): Promise<SageChatStreamResult>;
17
42
  /** nanoERG → "0.001" (trims trailing zeros, max 9 decimals). */
18
43
  declare function nanoToErg(nano: number | undefined): string;
19
44
  /** Cheap relative-time formatter, ASCII-only, no Intl deps. */
@@ -23,4 +48,4 @@ declare function receiptUrl(txId: string, apiBase?: string): string;
23
48
  /** Explorer URL for a tx on the given network. */
24
49
  declare function explorerUrl(txId: string, network?: "testnet" | "mainnet"): string;
25
50
 
26
- export { SageActivityResponse, explorerUrl, fetchSageActivity, nanoToErg, receiptUrl, relativeTime };
51
+ export { SageActivityResponse, SageChatMessage, SageChatStreamEvent, SageChatStreamResult, SageQuote, SageQuoteResponse, SageReceiptBundle, SageTenantConfig, SageVerifyPaymentResponse, explorerUrl, fetchSageActivity, fetchSageQuote, fetchSageReceipt, isFullSageReceiptBundle, nanoToErg, receiptUrl, relativeTime, streamSageChat, verifySagePayment };