@aithos/sdk 0.1.0-alpha.6 → 0.1.0-alpha.60

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.
Files changed (105) hide show
  1. package/README.md +202 -7
  2. package/dist/src/agent-dispatch.d.ts +18 -0
  3. package/dist/src/agent-dispatch.js +178 -0
  4. package/dist/src/agent-loop.d.ts +94 -0
  5. package/dist/src/agent-loop.js +95 -0
  6. package/dist/src/agent-tools.d.ts +24 -0
  7. package/dist/src/agent-tools.js +147 -0
  8. package/dist/src/apps.d.ts +224 -0
  9. package/dist/src/apps.js +432 -0
  10. package/dist/src/assets.d.ts +225 -0
  11. package/dist/src/assets.js +534 -0
  12. package/dist/src/auth-api.d.ts +219 -0
  13. package/dist/src/auth-api.js +248 -0
  14. package/dist/src/auth.d.ts +591 -0
  15. package/dist/src/auth.js +947 -31
  16. package/dist/src/compute.d.ts +674 -6
  17. package/dist/src/compute.js +968 -20
  18. package/dist/src/data-schema-contacts-v1.d.ts +14 -0
  19. package/dist/src/data-schema-contacts-v1.js +28 -0
  20. package/dist/src/data.d.ts +368 -0
  21. package/dist/src/data.js +1124 -0
  22. package/dist/src/endpoints.d.ts +43 -0
  23. package/dist/src/endpoints.js +23 -0
  24. package/dist/src/ethos.d.ts +85 -0
  25. package/dist/src/ethos.js +463 -7
  26. package/dist/src/index.d.ts +22 -4
  27. package/dist/src/index.js +47 -2
  28. package/dist/src/internal/cmk-wrap.d.ts +41 -0
  29. package/dist/src/internal/cmk-wrap.js +132 -0
  30. package/dist/src/internal/delegate-bundle.js +7 -2
  31. package/dist/src/internal/envelope.d.ts +93 -0
  32. package/dist/src/internal/envelope.js +59 -0
  33. package/dist/src/internal/owner-signers.d.ts +5 -2
  34. package/dist/src/internal/owner-signers.js +22 -1
  35. package/dist/src/internal/recovery-file.d.ts +2 -0
  36. package/dist/src/internal/recovery-file.js +7 -0
  37. package/dist/src/key-store.d.ts +10 -0
  38. package/dist/src/key-store.js +6 -0
  39. package/dist/src/mandates.d.ts +58 -1
  40. package/dist/src/mandates.js +46 -3
  41. package/dist/src/migrate.d.ts +105 -0
  42. package/dist/src/migrate.js +367 -0
  43. package/dist/src/react/AithosAsset.d.ts +66 -0
  44. package/dist/src/react/AithosAsset.js +67 -0
  45. package/dist/src/react/context.d.ts +29 -0
  46. package/dist/src/react/context.js +31 -0
  47. package/dist/src/react/index.d.ts +29 -0
  48. package/dist/src/react/index.js +31 -0
  49. package/dist/src/react/use-aithos-asset.d.ts +39 -0
  50. package/dist/src/react/use-aithos-asset.js +118 -0
  51. package/dist/src/react/use-transcribe-pending.d.ts +21 -0
  52. package/dist/src/react/use-transcribe-pending.js +47 -0
  53. package/dist/src/rotate.d.ts +94 -0
  54. package/dist/src/rotate.js +298 -0
  55. package/dist/src/sdk.d.ts +36 -2
  56. package/dist/src/sdk.js +72 -1
  57. package/dist/src/transcribe-resilience.d.ts +57 -0
  58. package/dist/src/transcribe-resilience.js +203 -0
  59. package/dist/src/web.d.ts +279 -0
  60. package/dist/src/web.js +186 -0
  61. package/dist/test/agent-dispatch.test.d.ts +2 -0
  62. package/dist/test/agent-dispatch.test.js +222 -0
  63. package/dist/test/agent-loop.test.d.ts +2 -0
  64. package/dist/test/agent-loop.test.js +117 -0
  65. package/dist/test/agent-tools.test.d.ts +2 -0
  66. package/dist/test/agent-tools.test.js +50 -0
  67. package/dist/test/auth-j3.test.js +32 -1
  68. package/dist/test/canonical-conformance.test.d.ts +2 -0
  69. package/dist/test/canonical-conformance.test.js +86 -0
  70. package/dist/test/compute-delegate-path.test.d.ts +2 -0
  71. package/dist/test/compute-delegate-path.test.js +183 -0
  72. package/dist/test/compute.test.js +4 -0
  73. package/dist/test/converse.test.d.ts +2 -0
  74. package/dist/test/converse.test.js +162 -0
  75. package/dist/test/data-sphere.test.d.ts +2 -0
  76. package/dist/test/data-sphere.test.js +57 -0
  77. package/dist/test/endpoints.test.js +40 -1
  78. package/dist/test/envelope-core-conformance.test.d.ts +2 -0
  79. package/dist/test/envelope-core-conformance.test.js +75 -0
  80. package/dist/test/envelope.test.d.ts +2 -0
  81. package/dist/test/envelope.test.js +318 -0
  82. package/dist/test/ethos-first-edition.test.d.ts +2 -0
  83. package/dist/test/ethos-first-edition.test.js +371 -0
  84. package/dist/test/invoke-turn-sdk.test.d.ts +2 -0
  85. package/dist/test/invoke-turn-sdk.test.js +177 -0
  86. package/dist/test/migrate.test.d.ts +2 -0
  87. package/dist/test/migrate.test.js +340 -0
  88. package/dist/test/owner-data-client.test.d.ts +2 -0
  89. package/dist/test/owner-data-client.test.js +88 -0
  90. package/dist/test/rotate-ethos.test.d.ts +2 -0
  91. package/dist/test/rotate-ethos.test.js +151 -0
  92. package/dist/test/rotate.test.d.ts +2 -0
  93. package/dist/test/rotate.test.js +63 -0
  94. package/dist/test/schema-autoresolve.test.d.ts +2 -0
  95. package/dist/test/schema-autoresolve.test.js +146 -0
  96. package/dist/test/sdk.test.js +11 -2
  97. package/dist/test/signup-bootstrap.test.d.ts +2 -0
  98. package/dist/test/signup-bootstrap.test.js +311 -0
  99. package/dist/test/transcribe-invoke.test.d.ts +2 -0
  100. package/dist/test/transcribe-invoke.test.js +204 -0
  101. package/dist/test/transcribe.test.d.ts +2 -0
  102. package/dist/test/transcribe.test.js +186 -0
  103. package/dist/test/web.test.d.ts +2 -0
  104. package/dist/test/web.test.js +270 -0
  105. package/package.json +20 -3
@@ -0,0 +1,203 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Mathieu Colla
3
+ const LS_KEY = "aithos:transcribe:pending";
4
+ /**
5
+ * Framework-agnostic observable registry of in-flight transcription jobs.
6
+ * Persisted to localStorage when available (so it survives reloads), with
7
+ * an in-memory fallback otherwise. Subscribe with `subscribe(listener)`;
8
+ * read with `getSnapshot()` (stable reference between mutations, so it
9
+ * plugs directly into React's `useSyncExternalStore`).
10
+ */
11
+ export class LocalPendingTranscribeTracker {
12
+ #listeners = new Set();
13
+ #mem = [];
14
+ #snapshot = [];
15
+ constructor() {
16
+ this.#snapshot = this.#read();
17
+ // Cross-tab sync: when another tab updates the key, re-emit.
18
+ try {
19
+ if (typeof window !== "undefined" && typeof window.addEventListener === "function") {
20
+ window.addEventListener("storage", (e) => {
21
+ if (e.key === LS_KEY) {
22
+ this.#snapshot = this.#read();
23
+ this.#emit();
24
+ }
25
+ });
26
+ }
27
+ }
28
+ catch {
29
+ /* no window — fine */
30
+ }
31
+ }
32
+ #ls() {
33
+ try {
34
+ return typeof localStorage !== "undefined" ? localStorage : null;
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ #read() {
41
+ const ls = this.#ls();
42
+ if (!ls)
43
+ return [...this.#mem];
44
+ try {
45
+ const raw = ls.getItem(LS_KEY);
46
+ return raw ? JSON.parse(raw) : [];
47
+ }
48
+ catch {
49
+ return [];
50
+ }
51
+ }
52
+ #write(list) {
53
+ const ls = this.#ls();
54
+ if (ls) {
55
+ try {
56
+ ls.setItem(LS_KEY, JSON.stringify(list));
57
+ }
58
+ catch {
59
+ /* quota / private mode — keep in-memory copy authoritative */
60
+ this.#mem = list;
61
+ }
62
+ }
63
+ else {
64
+ this.#mem = list;
65
+ }
66
+ this.#snapshot = list;
67
+ this.#emit();
68
+ }
69
+ #emit() {
70
+ for (const l of this.#listeners)
71
+ l();
72
+ }
73
+ /** Current entries. Stable reference until the next mutation. */
74
+ getSnapshot() {
75
+ return this.#snapshot;
76
+ }
77
+ list() {
78
+ this.#snapshot = this.#read();
79
+ return this.#snapshot;
80
+ }
81
+ /** Subscribe to changes. Returns an unsubscribe function. */
82
+ subscribe(listener) {
83
+ this.#listeners.add(listener);
84
+ return () => {
85
+ this.#listeners.delete(listener);
86
+ };
87
+ }
88
+ upsert(jobId, status, meta) {
89
+ const now = Date.now();
90
+ const list = this.#read();
91
+ const idx = list.findIndex((e) => e.jobId === jobId);
92
+ if (idx >= 0) {
93
+ const prev = list[idx];
94
+ list[idx] = {
95
+ ...prev,
96
+ status,
97
+ updatedAt: now,
98
+ ...(meta ? { meta: { ...prev.meta, ...meta } } : {}),
99
+ };
100
+ }
101
+ else {
102
+ list.push({
103
+ jobId,
104
+ status,
105
+ createdAt: now,
106
+ updatedAt: now,
107
+ ...(meta ? { meta } : {}),
108
+ });
109
+ }
110
+ this.#write(list);
111
+ }
112
+ remove(jobId) {
113
+ const list = this.#read().filter((e) => e.jobId !== jobId);
114
+ this.#write(list);
115
+ }
116
+ clear() {
117
+ this.#write([]);
118
+ }
119
+ }
120
+ const DB_NAME = "aithos-transcribe";
121
+ const DB_VERSION = 1;
122
+ const STORE = "drafts";
123
+ export class TranscribeDraftUnavailableError extends Error {
124
+ constructor() {
125
+ super("IndexedDB is not available in this environment — the transcription draft queue is browser-only.");
126
+ this.name = "TranscribeDraftUnavailableError";
127
+ }
128
+ }
129
+ function getIndexedDB() {
130
+ try {
131
+ if (typeof indexedDB !== "undefined")
132
+ return indexedDB;
133
+ }
134
+ catch {
135
+ /* fall through */
136
+ }
137
+ throw new TranscribeDraftUnavailableError();
138
+ }
139
+ function openDb() {
140
+ const idb = getIndexedDB();
141
+ return new Promise((resolve, reject) => {
142
+ const req = idb.open(DB_NAME, DB_VERSION);
143
+ req.onupgradeneeded = () => {
144
+ const db = req.result;
145
+ if (!db.objectStoreNames.contains(STORE)) {
146
+ db.createObjectStore(STORE, { keyPath: "draftId" });
147
+ }
148
+ };
149
+ req.onsuccess = () => resolve(req.result);
150
+ req.onerror = () => reject(req.error ?? new Error("indexedDB open failed"));
151
+ });
152
+ }
153
+ function tx(db, mode, fn) {
154
+ return new Promise((resolve, reject) => {
155
+ const t = db.transaction(STORE, mode);
156
+ const req = fn(t.objectStore(STORE));
157
+ req.onsuccess = () => resolve(req.result);
158
+ req.onerror = () => reject(req.error ?? new Error("indexedDB request failed"));
159
+ });
160
+ }
161
+ /**
162
+ * IndexedDB-backed queue of recorded audio Blobs. Save a recording the
163
+ * instant it finishes (before any network), then `upload` it when the
164
+ * user confirms — so a flaky network or a closed tab never loses audio.
165
+ * Browser-only: methods reject with {@link TranscribeDraftUnavailableError}
166
+ * when IndexedDB is absent.
167
+ */
168
+ export class TranscribeDraftStore {
169
+ async save(blob, meta) {
170
+ const db = await openDb();
171
+ const draftId = `draft_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
172
+ const record = {
173
+ draftId,
174
+ blob,
175
+ metadata: {
176
+ ...(meta ?? {}),
177
+ ...(meta?.contentType ? {} : { contentType: blob.type || "audio/webm" }),
178
+ },
179
+ createdAt: Date.now(),
180
+ };
181
+ await tx(db, "readwrite", (s) => s.put(record));
182
+ db.close();
183
+ return { draftId };
184
+ }
185
+ async list() {
186
+ const db = await openDb();
187
+ const out = await tx(db, "readonly", (s) => s.getAll());
188
+ db.close();
189
+ return out.sort((a, b) => a.createdAt - b.createdAt);
190
+ }
191
+ async get(draftId) {
192
+ const db = await openDb();
193
+ const rec = await tx(db, "readonly", (s) => s.get(draftId));
194
+ db.close();
195
+ return rec ?? null;
196
+ }
197
+ async delete(draftId) {
198
+ const db = await openDb();
199
+ await tx(db, "readwrite", (s) => s.delete(draftId));
200
+ db.close();
201
+ }
202
+ }
203
+ //# sourceMappingURL=transcribe-resilience.js.map
@@ -0,0 +1,279 @@
1
+ import type { AithosAuth } from "./auth.js";
2
+ import { type AithosSdkEndpoints } from "./endpoints.js";
3
+ /** Opt-in scope a mandate must carry to invoke `aithos.web_extract`. */
4
+ export declare const WEB_EXTRACT_SCOPE: "web.extract";
5
+ export interface ExtractArgs {
6
+ /**
7
+ * Mandate ID under which this call should be attributed.
8
+ *
9
+ * - **Owner sessions**: optional. The SDK uses the owner's own DID
10
+ * as a sentinel "self" mandate id — the proxy skips mandate checks
11
+ * when the envelope is owner-signed.
12
+ * - **Delegate sessions**: required. Must reference the imported
13
+ * mandate bundle the SDK signs with; the proxy enforces the
14
+ * `web.extract` scope.
15
+ */
16
+ readonly mandateId?: string;
17
+ /** Absolute http(s) URL to extract. */
18
+ readonly url: string;
19
+ /**
20
+ * Playwright `waitUntil` strategy passed straight through to the
21
+ * server-side navigation. Defaults to `"networkidle"` server-side
22
+ * if omitted.
23
+ */
24
+ readonly waitUntil?: "load" | "domcontentloaded" | "networkidle";
25
+ /** Navigation timeout in ms. Server validates [1000, 60000]. */
26
+ readonly timeoutMs?: number;
27
+ /** Reserved for audit-level deduplication; the proxy currently does not
28
+ * enforce idempotency keys for extractions. */
29
+ readonly idempotencyKey?: string;
30
+ /** Abort signal to cancel the request. */
31
+ readonly signal?: AbortSignal;
32
+ }
33
+ export interface ExtractMeta {
34
+ readonly title: string | null;
35
+ readonly description: string | null;
36
+ readonly lang: string | null;
37
+ readonly charset: string | null;
38
+ readonly viewport: string | null;
39
+ readonly canonical: string | null;
40
+ readonly og: Readonly<Record<string, string>>;
41
+ }
42
+ export interface ExtractHeading {
43
+ readonly level: 1 | 2 | 3 | 4 | 5 | 6;
44
+ readonly text: string;
45
+ readonly id: string | null;
46
+ }
47
+ export interface ExtractSection {
48
+ readonly tag: string;
49
+ readonly role: string | null;
50
+ readonly html: string;
51
+ readonly text_len: number;
52
+ }
53
+ export interface ExtractLink {
54
+ readonly label: string;
55
+ readonly href: string;
56
+ readonly internal: boolean;
57
+ }
58
+ export interface ExtractImage {
59
+ readonly src: string;
60
+ readonly alt: string | null;
61
+ readonly role: string | null;
62
+ }
63
+ export interface ExtractFormField {
64
+ readonly type: string;
65
+ readonly name: string | null;
66
+ readonly required: boolean;
67
+ }
68
+ export interface ExtractForm {
69
+ readonly action: string | null;
70
+ readonly method: string;
71
+ readonly fields: readonly ExtractFormField[];
72
+ }
73
+ export interface ExtractStructure {
74
+ readonly headings: readonly ExtractHeading[];
75
+ readonly sections: readonly ExtractSection[];
76
+ readonly nav_links: readonly ExtractLink[];
77
+ readonly forms: readonly ExtractForm[];
78
+ }
79
+ export interface ExtractContent {
80
+ readonly main_html: string;
81
+ readonly main_text: string;
82
+ readonly images: readonly ExtractImage[];
83
+ readonly links: {
84
+ readonly internal: readonly ExtractLink[];
85
+ readonly external: readonly ExtractLink[];
86
+ };
87
+ }
88
+ export interface ExtractStyles {
89
+ readonly css: string;
90
+ readonly inline_styles_count: number;
91
+ }
92
+ export interface PaletteEntry {
93
+ readonly hex: string;
94
+ readonly weight: number;
95
+ readonly role: "background" | "text" | "accent" | "other";
96
+ }
97
+ export interface ComponentStyle {
98
+ readonly count: number;
99
+ readonly bg: string | null;
100
+ readonly fg: string | null;
101
+ readonly border: string | null;
102
+ readonly radius: string | null;
103
+ readonly padding: string | null;
104
+ readonly font_size: string | null;
105
+ readonly font_weight: string | null;
106
+ }
107
+ export interface VisualSignature {
108
+ readonly colors: {
109
+ readonly palette: readonly PaletteEntry[];
110
+ readonly background: string | null;
111
+ readonly text: string | null;
112
+ readonly primary: string | null;
113
+ readonly link: string | null;
114
+ };
115
+ readonly typography: {
116
+ readonly heading_font: string | null;
117
+ readonly body_font: string | null;
118
+ readonly size_scale: readonly number[];
119
+ readonly base_size_px: number | null;
120
+ readonly base_line_height: number | null;
121
+ };
122
+ readonly radii: {
123
+ readonly button: string | null;
124
+ readonly input: string | null;
125
+ readonly card: string | null;
126
+ };
127
+ readonly spacing: {
128
+ readonly base_unit_px: number | null;
129
+ readonly common_gaps_px: readonly number[];
130
+ };
131
+ readonly layout: {
132
+ readonly max_content_width_px: number | null;
133
+ readonly mode: "flex" | "grid" | "block" | null;
134
+ };
135
+ readonly components: {
136
+ readonly buttons: readonly ComponentStyle[];
137
+ readonly inputs: readonly ComponentStyle[];
138
+ readonly cards: readonly ComponentStyle[];
139
+ };
140
+ }
141
+ export interface ExtractIconDeclaration {
142
+ /** href as written in the HTML (relative or absolute). */
143
+ readonly href: string;
144
+ /** rel value, lowercased: "icon", "apple-touch-icon", "shortcut icon", ... */
145
+ readonly rel: string;
146
+ /** Declared `sizes` attribute, e.g. "180x180" or "any" or null. */
147
+ readonly sizes: string | null;
148
+ /** Declared mime type, e.g. "image/svg+xml" or null. */
149
+ readonly type: string | null;
150
+ }
151
+ /**
152
+ * Logo asset resolved server-side. The Lambda picks the best
153
+ * symbol-only asset available on the page — declared <link rel="icon"|
154
+ * "apple-touch-icon"> declarations + conventional well-known paths
155
+ * (/apple-touch-icon.png, /favicon.svg, /favicon.ico) — in that
156
+ * order of expected quality. Null when nothing resolves.
157
+ *
158
+ * Favicons are symbol-only by construction (no designer ships a
159
+ * wordmark inside a 16-180 px icon), which sidesteps the
160
+ * lockup-vs-symbol problem callers used to handle client-side with
161
+ * a vision model.
162
+ */
163
+ export interface ExtractLogo {
164
+ /** Absolute URL of the asset that was successfully fetched. */
165
+ readonly url: string;
166
+ /** Which source produced the winner. */
167
+ readonly source: "link-icon-svg" | "link-apple-touch-icon" | "link-icon-large" | "link-icon" | "link-shortcut-icon" | "well-known-apple-180" | "well-known-apple" | "well-known-svg" | "well-known-png-large" | "well-known-ico";
168
+ readonly content_type: string;
169
+ readonly size_bytes: number;
170
+ /** Base64-encoded asset bytes (no `data:` prefix). Build a data
171
+ * URI with `data:${content_type};base64,${base64}`. */
172
+ readonly base64: string;
173
+ }
174
+ export interface ExtractData {
175
+ readonly url: string;
176
+ readonly final_url: string;
177
+ readonly fetched_at: string;
178
+ readonly render_ms: number;
179
+ readonly meta: ExtractMeta;
180
+ readonly structure: ExtractStructure;
181
+ readonly content: ExtractContent;
182
+ readonly styles: ExtractStyles;
183
+ readonly visual_signature: VisualSignature;
184
+ /**
185
+ * Best logo asset resolved by the lambda — null when no
186
+ * <link rel="icon"> declaration and no conventional favicon
187
+ * path produced a usable image. Callers should then let the
188
+ * operator upload the logo manually rather than treat this as
189
+ * a fatal error.
190
+ */
191
+ readonly logo: ExtractLogo | null;
192
+ }
193
+ export interface ExtractResult {
194
+ /** Cleaned extraction payload. */
195
+ readonly data: ExtractData;
196
+ /** Microcredits charged for this call (1 on success, 0 on refunded failures). */
197
+ readonly creditsCharged: number;
198
+ /** Wallet balance after the (possibly refunded) debit. */
199
+ readonly walletBalance: number;
200
+ /** Audit log id for traceability. */
201
+ readonly auditId: string;
202
+ }
203
+ export interface FetchAssetArgs {
204
+ /** Absolute http(s) URL of the asset to fetch. */
205
+ readonly url: string;
206
+ /** Mandate id under which this call should be attributed. */
207
+ readonly mandateId?: string;
208
+ /** Abort signal. */
209
+ readonly signal?: AbortSignal;
210
+ }
211
+ export interface FetchAssetResult {
212
+ /** Asset payload. */
213
+ readonly data: {
214
+ /** URL we asked the proxy to fetch. */
215
+ readonly url: string;
216
+ /** URL after the proxy followed any redirects. */
217
+ readonly final_url: string;
218
+ /** Content-Type reported by the upstream server. */
219
+ readonly content_type: string;
220
+ /** Size of the fetched body in bytes. */
221
+ readonly size_bytes: number;
222
+ /** Base64-encoded body (no `data:` prefix). Build a data URI
223
+ * with `data:${content_type};base64,${base64}`. */
224
+ readonly base64: string;
225
+ };
226
+ /** Microcredits charged for this call. */
227
+ readonly creditsCharged: number;
228
+ readonly walletBalance: number;
229
+ readonly auditId: string;
230
+ }
231
+ export interface WebNamespaceDeps {
232
+ readonly auth: AithosAuth;
233
+ readonly appDid: string;
234
+ readonly endpoints: AithosSdkEndpoints;
235
+ readonly fetch: typeof fetch;
236
+ }
237
+ /**
238
+ * `sdk.web` namespace — Aithos's web extraction primitive.
239
+ *
240
+ * Designed so a downstream agent can read the static content of any
241
+ * public page (HTML, purged CSS, computed visual signature) without
242
+ * involving an LLM — saving ~30× over a Bedrock-based extraction in
243
+ * both latency and cost.
244
+ *
245
+ * @throws {AithosSDKError} — same error taxonomy as `sdk.compute`,
246
+ * including `-32071` (insufficient balance with `{required, available}`
247
+ * in `data`) and `-32042` (mandate scope mismatch).
248
+ */
249
+ export declare class WebNamespace {
250
+ #private;
251
+ constructor(deps: WebNamespaceDeps);
252
+ /**
253
+ * Extract a public webpage. Returns the cleaned HTML, purged CSS and
254
+ * a deterministic visual signature (palette, typography, dominant
255
+ * radii, spacing, layout mode, component digests).
256
+ */
257
+ extract(args: ExtractArgs): Promise<ExtractResult>;
258
+ /**
259
+ * Fetch a single asset (image / font / css / json …) server-side,
260
+ * bypassing browser CORS. Returns the bytes as base64 + content-type.
261
+ *
262
+ * Use when `fetch(url, {mode: "cors"})` and `<img crossOrigin>`
263
+ * canvas readback both fail because the asset server doesn't return
264
+ * Access-Control-Allow-Origin headers — typical for production
265
+ * sites' logos hosted on the main domain.
266
+ *
267
+ * For the common "logo of a webpage" case the lambda already
268
+ * resolves and embeds the best symbol-only logo in
269
+ * {@link extract}'s `data.logo` field; you only need fetchAsset
270
+ * when extract's logo doesn't fit, when picking up secondary
271
+ * assets (og:image, hero image, document download), or when
272
+ * fetching an asset on a page you haven't extracted.
273
+ *
274
+ * Costs 1 mc per successful fetch, full refund on failure. Server
275
+ * caps: 15 s timeout, 10 MB body, http/https only.
276
+ */
277
+ fetchAsset(args: FetchAssetArgs): Promise<FetchAssetResult>;
278
+ }
279
+ //# sourceMappingURL=web.d.ts.map
@@ -0,0 +1,186 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright 2026 Mathieu Colla
3
+ // Web namespace — `aithos.web_extract` through the web-extractor proxy.
4
+ //
5
+ // Same JSON-RPC + signed-envelope protocol as the compute namespace, but
6
+ // against a separate Aithos service (`extract.aithos.be`). Pricing is a
7
+ // flat 1 microcredit per successful extraction (refunded on failure).
8
+ //
9
+ // The mandate scope is `web.extract` (exported as {@link WEB_EXTRACT_SCOPE}
10
+ // for owner mint-time use). A delegate that holds only this scope can read
11
+ // pages on the owner's behalf without gaining LLM-spend authority.
12
+ //
13
+ // Signing follows the same owner-vs-delegate logic as the compute namespace
14
+ // (see ComputeNamespace.#resolveSigner). The duplication is bounded — both
15
+ // namespaces' `#signAndPost` helpers can later move into a shared internal
16
+ // once a third primitive arrives.
17
+ import { buildSignedEnvelope, } from "@aithos/protocol-client";
18
+ import { webInvokeUrl, } from "./endpoints.js";
19
+ import { delegateKeyPair, ownerKeyPair, } from "./internal/protocol-client-bridge.js";
20
+ import { AithosSDKError } from "./types.js";
21
+ /** Opt-in scope a mandate must carry to invoke `aithos.web_extract`. */
22
+ export const WEB_EXTRACT_SCOPE = "web.extract";
23
+ /**
24
+ * `sdk.web` namespace — Aithos's web extraction primitive.
25
+ *
26
+ * Designed so a downstream agent can read the static content of any
27
+ * public page (HTML, purged CSS, computed visual signature) without
28
+ * involving an LLM — saving ~30× over a Bedrock-based extraction in
29
+ * both latency and cost.
30
+ *
31
+ * @throws {AithosSDKError} — same error taxonomy as `sdk.compute`,
32
+ * including `-32071` (insufficient balance with `{required, available}`
33
+ * in `data`) and `-32042` (mandate scope mismatch).
34
+ */
35
+ export class WebNamespace {
36
+ #deps;
37
+ constructor(deps) {
38
+ this.#deps = deps;
39
+ }
40
+ /**
41
+ * Extract a public webpage. Returns the cleaned HTML, purged CSS and
42
+ * a deterministic visual signature (palette, typography, dominant
43
+ * radii, spacing, layout mode, component digests).
44
+ */
45
+ async extract(args) {
46
+ const { endpoints, fetch: fetchImpl } = this.#deps;
47
+ const choice = this.#resolveSigner(args.mandateId);
48
+ const url = webInvokeUrl(endpoints);
49
+ const params = {
50
+ app_did: this.#deps.appDid,
51
+ mandate_id: this.#resolveMandateIdForWire(args.mandateId, choice),
52
+ url: args.url,
53
+ };
54
+ if (args.waitUntil !== undefined)
55
+ params.waitUntil = args.waitUntil;
56
+ if (args.timeoutMs !== undefined)
57
+ params.timeoutMs = args.timeoutMs;
58
+ if (args.idempotencyKey !== undefined) {
59
+ params.idempotencyKey = args.idempotencyKey;
60
+ }
61
+ return await this.#signAndPost({
62
+ url,
63
+ method: "aithos.web_extract",
64
+ params,
65
+ choice,
66
+ fetchImpl,
67
+ signal: args.signal,
68
+ });
69
+ }
70
+ /**
71
+ * Fetch a single asset (image / font / css / json …) server-side,
72
+ * bypassing browser CORS. Returns the bytes as base64 + content-type.
73
+ *
74
+ * Use when `fetch(url, {mode: "cors"})` and `<img crossOrigin>`
75
+ * canvas readback both fail because the asset server doesn't return
76
+ * Access-Control-Allow-Origin headers — typical for production
77
+ * sites' logos hosted on the main domain.
78
+ *
79
+ * For the common "logo of a webpage" case the lambda already
80
+ * resolves and embeds the best symbol-only logo in
81
+ * {@link extract}'s `data.logo` field; you only need fetchAsset
82
+ * when extract's logo doesn't fit, when picking up secondary
83
+ * assets (og:image, hero image, document download), or when
84
+ * fetching an asset on a page you haven't extracted.
85
+ *
86
+ * Costs 1 mc per successful fetch, full refund on failure. Server
87
+ * caps: 15 s timeout, 10 MB body, http/https only.
88
+ */
89
+ async fetchAsset(args) {
90
+ const { endpoints, fetch: fetchImpl } = this.#deps;
91
+ const choice = this.#resolveSigner(args.mandateId);
92
+ const url = webInvokeUrl(endpoints);
93
+ const params = {
94
+ app_did: this.#deps.appDid,
95
+ mandate_id: this.#resolveMandateIdForWire(args.mandateId, choice),
96
+ url: args.url,
97
+ };
98
+ return await this.#signAndPost({
99
+ url,
100
+ method: "aithos.web_fetch_asset",
101
+ params,
102
+ choice,
103
+ fetchImpl,
104
+ signal: args.signal,
105
+ });
106
+ }
107
+ /* ----------------------------- internals ----------------------------- */
108
+ #resolveSigner(mandateId) {
109
+ const { auth } = this.#deps;
110
+ const owner = auth._getOwnerSigners();
111
+ const ownerLoaded = owner !== null && !owner.destroyed;
112
+ if (ownerLoaded) {
113
+ const publicKp = ownerKeyPair(owner, "public");
114
+ return {
115
+ kind: "owner",
116
+ iss: owner.did,
117
+ verificationMethod: `${owner.did}#public`,
118
+ signer: publicKp,
119
+ mandate: undefined,
120
+ };
121
+ }
122
+ if (mandateId === undefined || mandateId.length === 0) {
123
+ throw new AithosSDKError("sdk_no_signer", "no owner signed in and no mandateId provided — pass a mandateId for a delegate session, or sign in as an owner first.");
124
+ }
125
+ const actor = auth._getDelegateActor(mandateId);
126
+ if (!actor || actor.destroyed) {
127
+ throw new AithosSDKError("sdk_no_delegate_for_mandate", `no owner signed in and no imported delegate mandate matches '${mandateId}'. Sign in as an owner, or import a delegate bundle for that mandate via auth.importMandate.`);
128
+ }
129
+ const kp = delegateKeyPair(actor);
130
+ return {
131
+ kind: "delegate",
132
+ iss: actor.subjectDid,
133
+ verificationMethod: actor.granteePubkeyMultibase,
134
+ signer: kp,
135
+ mandate: actor.mandate,
136
+ };
137
+ }
138
+ #resolveMandateIdForWire(explicit, choice) {
139
+ if (explicit && explicit.length > 0)
140
+ return explicit;
141
+ if (choice.kind === "delegate")
142
+ return choice.mandate.id;
143
+ return `${choice.iss}#self`;
144
+ }
145
+ async #signAndPost(opts) {
146
+ const { url, method, params, choice, fetchImpl, signal } = opts;
147
+ const envelope = buildSignedEnvelope({
148
+ iss: choice.iss,
149
+ aud: url,
150
+ method,
151
+ verificationMethod: choice.verificationMethod,
152
+ params,
153
+ signer: choice.signer,
154
+ ...(choice.kind === "delegate" ? { mandate: choice.mandate } : {}),
155
+ });
156
+ let res;
157
+ try {
158
+ res = await fetchImpl(url, {
159
+ method: "POST",
160
+ headers: { "content-type": "application/json" },
161
+ body: JSON.stringify({
162
+ jsonrpc: "2.0",
163
+ id: method,
164
+ method,
165
+ params: { ...params, _envelope: envelope },
166
+ }),
167
+ ...(signal ? { signal } : {}),
168
+ });
169
+ }
170
+ catch (e) {
171
+ throw new AithosSDKError("network", e.message);
172
+ }
173
+ if (!res.ok) {
174
+ throw new AithosSDKError("http", `HTTP ${res.status} ${res.statusText}`, { status: res.status });
175
+ }
176
+ const body = (await res.json());
177
+ if (body.error) {
178
+ throw new AithosSDKError(String(body.error.code), body.error.message, body.error.data ? { data: body.error.data } : undefined);
179
+ }
180
+ if (!body.result) {
181
+ throw new AithosSDKError("empty", "empty result from web extractor proxy");
182
+ }
183
+ return body.result;
184
+ }
185
+ }
186
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=agent-dispatch.test.d.ts.map