@tangle-network/agent-integrations 0.58.0 → 0.59.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.
Files changed (56) hide show
  1. package/README.md +31 -0
  2. package/dist/{adapters-ediOgE75.js → adapters-DQ0JJ-aG.js} +6 -283
  3. package/dist/{adapters-ediOgE75.js.map → adapters-DQ0JJ-aG.js.map} +1 -1
  4. package/dist/catalog-CFvKzwVj.js +201 -0
  5. package/dist/catalog-CFvKzwVj.js.map +1 -0
  6. package/dist/catalog.js +1 -1
  7. package/dist/connect/index.js +1 -1
  8. package/dist/connectors/adapters/index.js +1 -1
  9. package/dist/connectors/index.d.ts +2 -1
  10. package/dist/connectors/index.js +2 -1
  11. package/dist/connectors/oauth.d.ts +2 -0
  12. package/dist/connectors/oauth.js +282 -0
  13. package/dist/connectors/oauth.js.map +1 -0
  14. package/dist/{consumer-BC6pCUzI.d.ts → consumer-zHtoJixe.d.ts} +5 -37
  15. package/dist/{consumer-BC6pCUzI.d.ts.map → consumer-zHtoJixe.d.ts.map} +1 -1
  16. package/dist/consumer.d.ts +1 -1
  17. package/dist/hub-0Od2lgun.d.ts +38 -0
  18. package/dist/hub-0Od2lgun.d.ts.map +1 -0
  19. package/dist/hub-D13zPQGr.js +1004 -0
  20. package/dist/hub-D13zPQGr.js.map +1 -0
  21. package/dist/hub.d.ts +2 -0
  22. package/dist/hub.js +2 -0
  23. package/dist/index-BQXENE9p.d.ts +130 -0
  24. package/dist/index-BQXENE9p.d.ts.map +1 -0
  25. package/dist/index.d.ts +5 -3
  26. package/dist/index.js +15 -11
  27. package/dist/internal/bundled-manifest-runtime.js +1 -1
  28. package/dist/mcp.d.ts +1 -1
  29. package/dist/mcp.js +2 -1
  30. package/dist/mcp.js.map +1 -1
  31. package/dist/middleware/index.js +1 -1
  32. package/dist/oauth-C5pa5j3O.d.ts +136 -0
  33. package/dist/oauth-C5pa5j3O.d.ts.map +1 -0
  34. package/dist/registry-core-BZo69C37.js +186 -0
  35. package/dist/registry-core-BZo69C37.js.map +1 -0
  36. package/dist/registry.js +2 -2
  37. package/dist/runtime.d.ts +1 -1
  38. package/dist/runtime.js +1 -1
  39. package/dist/sandbox.d.ts +2 -0
  40. package/dist/sandbox.js +114 -0
  41. package/dist/sandbox.js.map +1 -0
  42. package/dist/{src-Bd4KMI-J.js → src-wcW1OCA7.js} +84922 -86026
  43. package/dist/src-wcW1OCA7.js.map +1 -0
  44. package/dist/stripe/index.js +1 -1
  45. package/dist/tangle-search/index.d.ts +37 -1
  46. package/dist/tangle-search/index.d.ts.map +1 -1
  47. package/dist/tangle-search/index.js +81 -1
  48. package/dist/tangle-search/index.js.map +1 -1
  49. package/dist/webhooks/index.js +1 -1
  50. package/docs/host-search-and-phone.md +28 -1
  51. package/package.json +17 -2
  52. package/dist/catalog-CBd0zbdD.js +0 -383
  53. package/dist/catalog-CBd0zbdD.js.map +0 -1
  54. package/dist/index-j2QziJA5.d.ts +0 -263
  55. package/dist/index-j2QziJA5.d.ts.map +0 -1
  56. package/dist/src-Bd4KMI-J.js.map +0 -1
@@ -1,6 +1,6 @@
1
- import { t as IntegrationRuntimeError } from "../errors-CvO_y8JO.js";
2
1
  import { FileSystemAtomicIdempotencyStore, InMemoryAtomicIdempotencyStore, resolveAtomicIdempotencyStore } from "../idempotency.js";
3
2
  import { o as assertNoProductFreeTrial, s as decideBillingAccess } from "../billing-access-policy-DJ63AxZ5.js";
3
+ import { t as IntegrationRuntimeError } from "../errors-CvO_y8JO.js";
4
4
  //#region src/stripe/errors.ts
5
5
  /**
6
6
  * Stripe billing error taxonomy.
@@ -1,4 +1,40 @@
1
1
  import { n as ProviderProtocolError, t as JsonRequestOptions } from "../response-json-BSRmUvDu.js";
2
+ //#region src/tangle-search/reader.d.ts
3
+ interface TangleReadInput {
4
+ url: string;
5
+ maxBytes?: number;
6
+ }
7
+ interface TangleReadResult {
8
+ id: string;
9
+ object: 'web.read';
10
+ requestedUrl: string;
11
+ url: string;
12
+ contentType: string;
13
+ /** Untrusted original page text/HTML, not an extracted summary or instructions. */
14
+ content: string;
15
+ bytes: number;
16
+ truncated: boolean;
17
+ fetchedAt: string;
18
+ billedCost: number | null;
19
+ }
20
+ interface TangleReadClientOptions extends JsonRequestOptions {
21
+ /** Supplied by the trusted host, never by the agent's tool arguments. */
22
+ apiKey: string | (() => string | Promise<string>);
23
+ baseUrl?: string;
24
+ }
25
+ export declare function buildTangleReadRequest(input: TangleReadInput): {
26
+ url: string;
27
+ max_bytes: number;
28
+ };
29
+ export declare function parseTangleReadResult(raw: unknown, request: ReturnType<typeof buildTangleReadRequest>): TangleReadResult;
30
+ /** Router is the only destination; destination DNS/redirect checks are enforced by the Router. */
31
+ export declare class TangleReadClient {
32
+ private readonly endpoint;
33
+ private readonly options;
34
+ constructor(options: TangleReadClientOptions);
35
+ read(input: TangleReadInput, signal?: AbortSignal): Promise<TangleReadResult>;
36
+ }
37
+ //#endregion
2
38
  //#region src/tangle-search/index.d.ts
3
39
  /** Router protocol, not provider-specific APIs. New provider IDs require no client release. */
4
40
  export interface TangleSearchInput {
@@ -58,5 +94,5 @@ export declare class TangleSearchClient {
58
94
  search(input: TangleSearchInput, signal?: AbortSignal): Promise<TangleSearchResult>;
59
95
  }
60
96
  //#endregion
61
- export { ProviderProtocolError };
97
+ export { ProviderProtocolError, type TangleReadClientOptions, type TangleReadInput, type TangleReadResult };
62
98
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/tangle-search/index.ts"],"mappings":";;;iBAIiB;EACf;EACA;EACA;EACA;EACA;EACA;;iBAEe;EACf;EACA;EACA;EACA;EACA;EACA;;iBAEe;EACf;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;IAAS;IAA8B;;;iBAExB,kCAAkC;;EAEjD,iCAAiC;;EAEjC;;EAEA;;;wBAec,yBAAyB,OAAO,mBAAmB;;;;;;;;;wBAoBnD,wBAAwB,cAAc;EAAW;EAAe;IAAsB;qBAqBzF;mBACM;mBACA;EACjB,YAAY,SAAS;EASf,OAAO,OAAO,mBAAmB,SAAS,cAAc,QAAQ"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/tangle-search/reader.ts","../../src/tangle-search/index.ts"],"mappings":";;UAEiB;EACf;EACA;;UAEe;EACf;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;;UAEe,gCAAgC;;EAE/C,iCAAiC;EACjC;;wBAUc,uBAAuB,OAAO;EAMnC;EAAgB;;wBAEX,sBAAsB,cAAc,SAAS,kBAAkB,0BAA0B;;qBAiB5F;mBACM;mBACA;EACjB,YAAY,SAAS;EASf,KAAK,OAAO,iBAAiB,SAAS,cAAc,QAAQ;;;;;iBC/DnD;EACf;EACA;EACA;EACA;EACA;EACA;;iBAEe;EACf;EACA;EACA;EACA;EACA;EACA;;iBAEe;EACf;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;IAAS;IAA8B;;;iBAExB,kCAAkC;;EAEjD,iCAAiC;;EAEjC;;EAEA;;;wBAec,yBAAyB,OAAO,mBAAmB;;;;;;;;;wBAoBnD,wBAAwB,cAAc;EAAW;EAAe;IAAsB;qBAqBzF;mBACM;mBACA;EACjB,YAAY,SAAS;EASf,OAAO,OAAO,mBAAmB,SAAS,cAAc,QAAQ"}
@@ -1,4 +1,84 @@
1
1
  import { n as record, r as requestJson, t as ProviderProtocolError } from "../response-json-BnUINkTL.js";
2
+ //#region src/tangle-search/reader.ts
3
+ function invalid$1(message) {
4
+ throw new ProviderProtocolError(message, "invalid_read_request", 400, true);
5
+ }
6
+ function publicHttps(value) {
7
+ if (typeof value !== "string" || value.length > 2048) return false;
8
+ try {
9
+ const u = new URL(value);
10
+ return u.protocol === "https:" && !u.username && !u.password;
11
+ } catch {
12
+ return false;
13
+ }
14
+ }
15
+ function buildTangleReadRequest(input) {
16
+ if (!record(input) || Object.keys(input).some((key) => !["url", "maxBytes"].includes(key)) || !publicHttps(input.url)) invalid$1("A credential-free HTTPS page URL is required");
17
+ const max = input.maxBytes ?? 131072;
18
+ if (!Number.isSafeInteger(max) || max < 1024 || max > 131072) invalid$1("maxBytes must be between 1024 and 131072");
19
+ return {
20
+ url: input.url,
21
+ max_bytes: max
22
+ };
23
+ }
24
+ function parseTangleReadResult(raw, request) {
25
+ const bad = () => {
26
+ throw new ProviderProtocolError("Invalid or uncorrelated Router read response", "invalid_read_response");
27
+ };
28
+ if (!record(raw) || raw.object !== "web.read" || typeof raw.id !== "string" || !raw.id || raw.requested_url !== request.url || !publicHttps(raw.url) || typeof raw.content !== "string" || typeof raw.content_type !== "string" || typeof raw.fetched_at !== "string" || !Number.isFinite(Date.parse(raw.fetched_at)) || typeof raw.truncated !== "boolean" || typeof raw.bytes !== "number" || !Number.isSafeInteger(raw.bytes) || raw.bytes < 0 || raw.bytes > request.max_bytes || new TextEncoder().encode(raw.content).byteLength > raw.bytes * 3) return bad();
29
+ const billed = record(raw.usage) ? raw.usage.billed_cost : void 0;
30
+ return {
31
+ id: raw.id,
32
+ object: "web.read",
33
+ requestedUrl: request.url,
34
+ url: raw.url,
35
+ contentType: raw.content_type,
36
+ content: raw.content,
37
+ bytes: raw.bytes,
38
+ truncated: raw.truncated,
39
+ fetchedAt: raw.fetched_at,
40
+ billedCost: typeof billed === "number" && Number.isFinite(billed) && billed >= 0 ? billed : null
41
+ };
42
+ }
43
+ /** Router is the only destination; destination DNS/redirect checks are enforced by the Router. */
44
+ var TangleReadClient = class {
45
+ endpoint;
46
+ options;
47
+ constructor(options) {
48
+ const u = new URL(options.baseUrl ?? "https://router.tangle.tools");
49
+ if (u.username || u.password || u.search || u.hash || ![
50
+ "/",
51
+ "/v1",
52
+ "/v1/"
53
+ ].includes(u.pathname) || !(u.protocol === "https:" || u.protocol === "http:" && [
54
+ "localhost",
55
+ "127.0.0.1",
56
+ "[::1]"
57
+ ].includes(u.hostname))) invalid$1("Use an explicit trusted Router HTTPS origin, or HTTP loopback");
58
+ this.endpoint = `${u.origin}/v1/read`;
59
+ this.options = {
60
+ timeoutMs: 25e3,
61
+ maxResponseBytes: 1e6,
62
+ ...options
63
+ };
64
+ }
65
+ async read(input, signal) {
66
+ const body = buildTangleReadRequest(input);
67
+ signal?.throwIfAborted();
68
+ const key = typeof this.options.apiKey === "function" ? await this.options.apiKey() : this.options.apiKey;
69
+ if (!key || /[\r\n]/.test(key)) throw new ProviderProtocolError("A Router credential is required", "read_not_configured", 503, true);
70
+ return parseTangleReadResult(await requestJson(this.endpoint, {
71
+ method: "POST",
72
+ signal,
73
+ headers: {
74
+ Authorization: `Bearer ${key}`,
75
+ "Content-Type": "application/json"
76
+ },
77
+ body: JSON.stringify(body)
78
+ }, this.options), body);
79
+ }
80
+ };
81
+ //#endregion
2
82
  //#region src/tangle-search/index.ts
3
83
  function invalid(message) {
4
84
  throw new ProviderProtocolError(message, "invalid_search_request", 400, true);
@@ -115,6 +195,6 @@ var TangleSearchClient = class {
115
195
  }
116
196
  };
117
197
  //#endregion
118
- export { ProviderProtocolError, TangleSearchClient, buildTangleSearchRequest, parseTangleSearchResult };
198
+ export { ProviderProtocolError, TangleReadClient, TangleSearchClient, buildTangleReadRequest, buildTangleSearchRequest, parseTangleReadResult, parseTangleSearchResult };
119
199
 
120
200
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/tangle-search/index.ts"],"sourcesContent":["import { ProviderProtocolError, record, requestJson, type JsonRequestOptions } from '../http/response-json.js'\nexport { ProviderProtocolError } from '../http/response-json.js'\n\n/** Router protocol, not provider-specific APIs. New provider IDs require no client release. */\nexport interface TangleSearchInput {\n query: string\n provider?: string\n maxResults?: number\n searchRecency?: 'day' | 'week' | 'month' | 'year'\n includeDomains?: readonly string[]\n excludeDomains?: readonly string[]\n}\nexport interface TangleSearchHit {\n title: string\n url: string\n snippet?: string\n publishedAt?: string\n score?: number\n source?: string\n}\nexport interface TangleSearchResult {\n id: string\n object: 'search.result'\n provider: string\n query: string\n data: TangleSearchHit[]\n citations: string[]\n usage: { upstream_cost: number | null; billed_cost: number | null }\n}\nexport interface TangleSearchClientOptions extends JsonRequestOptions {\n /** Host-selected credential. Never infer one from a model URL or an agent argument. */\n apiKey: string | (() => string | Promise<string>)\n /** Explicit trusted base: HTTPS, or HTTP loopback for tests/self-hosting. */\n baseUrl?: string\n /** A deployment/evaluation pin wins over a per-call preference. */\n provider?: string\n}\n\nfunction invalid(message: string): never {\n throw new ProviderProtocolError(message, 'invalid_search_request', 400, true)\n}\nfunction domains(values: readonly string[] | undefined): string[] | undefined {\n if (values === undefined) return undefined\n if (!Array.isArray(values) || values.length > 50 || values.some(v => typeof v !== 'string' || v.length > 255 ||\n !/^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)*[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(v))) {\n invalid('Domain filters must be host names, not URLs')\n }\n return [...new Set(values.map(v => v.toLowerCase()))]\n}\n/** Useful for an existing bounded HTTP transport; never sends a request itself. */\nexport function buildTangleSearchRequest(input: TangleSearchInput, pinnedProvider?: string) {\n if (!record(input) || typeof input.query !== 'string' || !input.query.trim()) invalid('A search query is required')\n const allowed = new Set(['query', 'provider', 'maxResults', 'searchRecency', 'includeDomains', 'excludeDomains'])\n if (Object.keys(input).some(k => !allowed.has(k))) invalid('Unsupported search option; the Router has no page-offset parameter')\n const max = input.maxResults ?? 20\n if (!Number.isInteger(max) || max < 1 || max > 25) invalid('maxResults must be between 1 and 25')\n const provider = pinnedProvider || input.provider\n if (provider !== undefined && (typeof provider !== 'string' || !/^[a-z][a-z0-9_-]{0,63}$/.test(provider))) invalid('Invalid Router provider ID')\n if (input.searchRecency !== undefined && !['day', 'week', 'month', 'year'].includes(input.searchRecency)) invalid('Unsupported search recency')\n return { query: input.query.trim(), max_results: max,\n ...(provider ? { provider } : {}), ...(input.searchRecency ? { search_recency: input.searchRecency } : {}),\n ...(input.includeDomains ? { include_domains: domains(input.includeDomains) } : {}),\n ...(input.excludeDomains ? { exclude_domains: domains(input.excludeDomains) } : {}) }\n}\nfunction safeURL(value: unknown): value is string {\n if (typeof value !== 'string') return false\n try { const u = new URL(value); return ['http:', 'https:'].includes(u.protocol) && !u.username && !u.password }\n catch { return false }\n}\n/** Correlate a response without inventing pagination, images, stock or missing costs. */\nexport function parseTangleSearchResult(raw: unknown, request: { query: string; provider?: string }): TangleSearchResult {\n const bad = () => { throw new ProviderProtocolError('Invalid or uncorrelated Router search response', 'invalid_search_response') }\n if (!record(raw) || raw.object !== 'search.result' || typeof raw.id !== 'string' || !raw.id ||\n typeof raw.provider !== 'string' || !raw.provider || raw.query !== request.query || !Array.isArray(raw.data)) return bad()\n if (request.provider && raw.provider !== request.provider) {\n throw new ProviderProtocolError('Router served a different search provider than requested', 'search_provider_mismatch')\n }\n const hits: TangleSearchHit[] = raw.data.map(hit => {\n if (!record(hit) || !safeURL(hit.url) || typeof hit.title !== 'string' ||\n (hit.snippet !== undefined && typeof hit.snippet !== 'string')) return bad()\n return { url: hit.url, title: hit.title, ...(typeof hit.snippet === 'string' ? { snippet: hit.snippet } : {}),\n ...(typeof hit.publishedAt === 'string' ? { publishedAt: hit.publishedAt } : {}),\n ...(typeof hit.score === 'number' && Number.isFinite(hit.score) ? { score: hit.score } : {}),\n ...(typeof hit.source === 'string' ? { source: hit.source } : {}) }\n })\n const cost = (value: unknown) => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null\n const usage = record(raw.usage) ? raw.usage : {}\n return { id: raw.id, object: 'search.result', provider: raw.provider, query: request.query, data: hits,\n citations: Array.isArray(raw.citations) ? raw.citations.filter(safeURL) : [],\n usage: { upstream_cost: cost(usage.upstream_cost), billed_cost: cost(usage.billed_cost) } }\n}\nexport class TangleSearchClient {\n private readonly endpoint: string\n private readonly options: TangleSearchClientOptions\n constructor(options: TangleSearchClientOptions) {\n const u = new URL(options.baseUrl ?? 'https://router.tangle.tools')\n if (u.username || u.password || u.search || u.hash || !['/', '/v1', '/v1/'].includes(u.pathname) ||\n !(u.protocol === 'https:' || u.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(u.hostname))) {\n invalid('Use an explicit trusted Router HTTPS origin, or HTTP loopback')\n }\n this.endpoint = u.origin + '/v1/search'\n this.options = { ...options }\n }\n async search(input: TangleSearchInput, signal?: AbortSignal): Promise<TangleSearchResult> {\n const body = buildTangleSearchRequest(input, this.options.provider)\n signal?.throwIfAborted()\n const key = typeof this.options.apiKey === 'function' ? await this.options.apiKey() : this.options.apiKey\n if (!key || /[\\r\\n]/.test(key)) throw new ProviderProtocolError('A Router credential is required', 'search_not_configured', 503, true)\n const raw = await requestJson(this.endpoint, { method: 'POST', signal,\n headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' }, body: JSON.stringify(body) }, this.options)\n return parseTangleSearchResult(raw, body)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["invalid"],"sources":["../../src/tangle-search/reader.ts","../../src/tangle-search/index.ts"],"sourcesContent":["import { ProviderProtocolError, record, requestJson, type JsonRequestOptions } from '../http/response-json.js'\n\nexport interface TangleReadInput {\n url: string\n maxBytes?: number\n}\nexport interface TangleReadResult {\n id: string\n object: 'web.read'\n requestedUrl: string\n url: string\n contentType: string\n /** Untrusted original page text/HTML, not an extracted summary or instructions. */\n content: string\n bytes: number\n truncated: boolean\n fetchedAt: string\n billedCost: number | null\n}\nexport interface TangleReadClientOptions extends JsonRequestOptions {\n /** Supplied by the trusted host, never by the agent's tool arguments. */\n apiKey: string | (() => string | Promise<string>)\n baseUrl?: string\n}\nfunction invalid(message: string): never {\n throw new ProviderProtocolError(message, 'invalid_read_request', 400, true)\n}\nfunction publicHttps(value: unknown): value is string {\n if (typeof value !== 'string' || value.length > 2048) return false\n try { const u = new URL(value); return u.protocol === 'https:' && !u.username && !u.password }\n catch { return false }\n}\nexport function buildTangleReadRequest(input: TangleReadInput) {\n if (!record(input) || Object.keys(input).some(key => !['url', 'maxBytes'].includes(key)) || !publicHttps(input.url)) {\n invalid('A credential-free HTTPS page URL is required')\n }\n const max = input.maxBytes ?? 131072\n if (!Number.isSafeInteger(max) || max < 1024 || max > 131072) invalid('maxBytes must be between 1024 and 131072')\n return { url: input.url, max_bytes: max }\n}\nexport function parseTangleReadResult(raw: unknown, request: ReturnType<typeof buildTangleReadRequest>): TangleReadResult {\n // Router counts raw page bytes but decodes them as UTF-8; each undecodable\n // byte becomes U+FFFD (3 bytes), so decoded text can reach 3x raw bytes.\n const bad = (): never => { throw new ProviderProtocolError('Invalid or uncorrelated Router read response', 'invalid_read_response') }\n if (!record(raw) || raw.object !== 'web.read' || typeof raw.id !== 'string' || !raw.id ||\n raw.requested_url !== request.url || !publicHttps(raw.url) || typeof raw.content !== 'string' ||\n typeof raw.content_type !== 'string' || typeof raw.fetched_at !== 'string' ||\n !Number.isFinite(Date.parse(raw.fetched_at)) || typeof raw.truncated !== 'boolean' ||\n typeof raw.bytes !== 'number' || !Number.isSafeInteger(raw.bytes) || raw.bytes < 0 || raw.bytes > request.max_bytes ||\n new TextEncoder().encode(raw.content).byteLength > raw.bytes * 3) return bad()\n const billed = record(raw.usage) ? raw.usage.billed_cost : undefined\n return { id: raw.id, object: 'web.read', requestedUrl: request.url, url: raw.url,\n contentType: raw.content_type, content: raw.content, bytes: raw.bytes, truncated: raw.truncated,\n fetchedAt: raw.fetched_at,\n billedCost: typeof billed === 'number' && Number.isFinite(billed) && billed >= 0 ? billed : null }\n}\n/** Router is the only destination; destination DNS/redirect checks are enforced by the Router. */\nexport class TangleReadClient {\n private readonly endpoint: string\n private readonly options: TangleReadClientOptions\n constructor(options: TangleReadClientOptions) {\n const u = new URL(options.baseUrl ?? 'https://router.tangle.tools')\n if (u.username || u.password || u.search || u.hash || !['/', '/v1', '/v1/'].includes(u.pathname) ||\n !(u.protocol === 'https:' || u.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(u.hostname))) {\n invalid('Use an explicit trusted Router HTTPS origin, or HTTP loopback')\n }\n this.endpoint = `${u.origin}/v1/read`\n this.options = { timeoutMs: 25_000, maxResponseBytes: 1_000_000, ...options }\n }\n async read(input: TangleReadInput, signal?: AbortSignal): Promise<TangleReadResult> {\n const body = buildTangleReadRequest(input)\n signal?.throwIfAborted()\n const key = typeof this.options.apiKey === 'function' ? await this.options.apiKey() : this.options.apiKey\n if (!key || /[\\r\\n]/.test(key)) throw new ProviderProtocolError('A Router credential is required', 'read_not_configured', 503, true)\n const raw = await requestJson(this.endpoint, { method: 'POST', signal,\n headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' }, body: JSON.stringify(body) }, this.options)\n return parseTangleReadResult(raw, body)\n }\n}\n","import { ProviderProtocolError, record, requestJson, type JsonRequestOptions } from '../http/response-json.js'\nexport { ProviderProtocolError } from '../http/response-json.js'\nexport { TangleReadClient, buildTangleReadRequest, parseTangleReadResult,\n type TangleReadInput, type TangleReadResult, type TangleReadClientOptions } from './reader.js'\n\n/** Router protocol, not provider-specific APIs. New provider IDs require no client release. */\nexport interface TangleSearchInput {\n query: string\n provider?: string\n maxResults?: number\n searchRecency?: 'day' | 'week' | 'month' | 'year'\n includeDomains?: readonly string[]\n excludeDomains?: readonly string[]\n}\nexport interface TangleSearchHit {\n title: string\n url: string\n snippet?: string\n publishedAt?: string\n score?: number\n source?: string\n}\nexport interface TangleSearchResult {\n id: string\n object: 'search.result'\n provider: string\n query: string\n data: TangleSearchHit[]\n citations: string[]\n usage: { upstream_cost: number | null; billed_cost: number | null }\n}\nexport interface TangleSearchClientOptions extends JsonRequestOptions {\n /** Host-selected credential. Never infer one from a model URL or an agent argument. */\n apiKey: string | (() => string | Promise<string>)\n /** Explicit trusted base: HTTPS, or HTTP loopback for tests/self-hosting. */\n baseUrl?: string\n /** A deployment/evaluation pin wins over a per-call preference. */\n provider?: string\n}\n\nfunction invalid(message: string): never {\n throw new ProviderProtocolError(message, 'invalid_search_request', 400, true)\n}\nfunction domains(values: readonly string[] | undefined): string[] | undefined {\n if (values === undefined) return undefined\n if (!Array.isArray(values) || values.length > 50 || values.some(v => typeof v !== 'string' || v.length > 255 ||\n !/^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)*[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(v))) {\n invalid('Domain filters must be host names, not URLs')\n }\n return [...new Set(values.map(v => v.toLowerCase()))]\n}\n/** Useful for an existing bounded HTTP transport; never sends a request itself. */\nexport function buildTangleSearchRequest(input: TangleSearchInput, pinnedProvider?: string) {\n if (!record(input) || typeof input.query !== 'string' || !input.query.trim()) invalid('A search query is required')\n const allowed = new Set(['query', 'provider', 'maxResults', 'searchRecency', 'includeDomains', 'excludeDomains'])\n if (Object.keys(input).some(k => !allowed.has(k))) invalid('Unsupported search option; the Router has no page-offset parameter')\n const max = input.maxResults ?? 20\n if (!Number.isInteger(max) || max < 1 || max > 25) invalid('maxResults must be between 1 and 25')\n const provider = pinnedProvider || input.provider\n if (provider !== undefined && (typeof provider !== 'string' || !/^[a-z][a-z0-9_-]{0,63}$/.test(provider))) invalid('Invalid Router provider ID')\n if (input.searchRecency !== undefined && !['day', 'week', 'month', 'year'].includes(input.searchRecency)) invalid('Unsupported search recency')\n return { query: input.query.trim(), max_results: max,\n ...(provider ? { provider } : {}), ...(input.searchRecency ? { search_recency: input.searchRecency } : {}),\n ...(input.includeDomains ? { include_domains: domains(input.includeDomains) } : {}),\n ...(input.excludeDomains ? { exclude_domains: domains(input.excludeDomains) } : {}) }\n}\nfunction safeURL(value: unknown): value is string {\n if (typeof value !== 'string') return false\n try { const u = new URL(value); return ['http:', 'https:'].includes(u.protocol) && !u.username && !u.password }\n catch { return false }\n}\n/** Correlate a response without inventing pagination, images, stock or missing costs. */\nexport function parseTangleSearchResult(raw: unknown, request: { query: string; provider?: string }): TangleSearchResult {\n const bad = () => { throw new ProviderProtocolError('Invalid or uncorrelated Router search response', 'invalid_search_response') }\n if (!record(raw) || raw.object !== 'search.result' || typeof raw.id !== 'string' || !raw.id ||\n typeof raw.provider !== 'string' || !raw.provider || raw.query !== request.query || !Array.isArray(raw.data)) return bad()\n if (request.provider && raw.provider !== request.provider) {\n throw new ProviderProtocolError('Router served a different search provider than requested', 'search_provider_mismatch')\n }\n const hits: TangleSearchHit[] = raw.data.map(hit => {\n if (!record(hit) || !safeURL(hit.url) || typeof hit.title !== 'string' ||\n (hit.snippet !== undefined && typeof hit.snippet !== 'string')) return bad()\n return { url: hit.url, title: hit.title, ...(typeof hit.snippet === 'string' ? { snippet: hit.snippet } : {}),\n ...(typeof hit.publishedAt === 'string' ? { publishedAt: hit.publishedAt } : {}),\n ...(typeof hit.score === 'number' && Number.isFinite(hit.score) ? { score: hit.score } : {}),\n ...(typeof hit.source === 'string' ? { source: hit.source } : {}) }\n })\n const cost = (value: unknown) => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null\n const usage = record(raw.usage) ? raw.usage : {}\n return { id: raw.id, object: 'search.result', provider: raw.provider, query: request.query, data: hits,\n citations: Array.isArray(raw.citations) ? raw.citations.filter(safeURL) : [],\n usage: { upstream_cost: cost(usage.upstream_cost), billed_cost: cost(usage.billed_cost) } }\n}\nexport class TangleSearchClient {\n private readonly endpoint: string\n private readonly options: TangleSearchClientOptions\n constructor(options: TangleSearchClientOptions) {\n const u = new URL(options.baseUrl ?? 'https://router.tangle.tools')\n if (u.username || u.password || u.search || u.hash || !['/', '/v1', '/v1/'].includes(u.pathname) ||\n !(u.protocol === 'https:' || u.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(u.hostname))) {\n invalid('Use an explicit trusted Router HTTPS origin, or HTTP loopback')\n }\n this.endpoint = u.origin + '/v1/search'\n this.options = { ...options }\n }\n async search(input: TangleSearchInput, signal?: AbortSignal): Promise<TangleSearchResult> {\n const body = buildTangleSearchRequest(input, this.options.provider)\n signal?.throwIfAborted()\n const key = typeof this.options.apiKey === 'function' ? await this.options.apiKey() : this.options.apiKey\n if (!key || /[\\r\\n]/.test(key)) throw new ProviderProtocolError('A Router credential is required', 'search_not_configured', 503, true)\n const raw = await requestJson(this.endpoint, { method: 'POST', signal,\n headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' }, body: JSON.stringify(body) }, this.options)\n return parseTangleSearchResult(raw, body)\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { a as verifyHmacSignature, i as verifyEmailSignature, n as firstHeader, o as verifySlackSignature, s as verifyStripeSignature } from "../webhooks-FsjqRN_0.js";
2
1
  import { FileSystemAtomicIdempotencyStore, InMemoryAtomicIdempotencyStore, resolveAtomicIdempotencyStore } from "../idempotency.js";
2
+ import { a as verifyHmacSignature, i as verifyEmailSignature, n as firstHeader, o as verifySlackSignature, s as verifyStripeSignature } from "../webhooks-FsjqRN_0.js";
3
3
  import { authenticateTwilioForm } from "../twilio/index.js";
4
4
  import { createHash, createHmac, timingSafeEqual } from "node:crypto";
5
5
  //#region src/webhooks/router.ts
@@ -1,4 +1,4 @@
1
- # Router search and verified phone transports
1
+ # Router search, page reads, and verified phone transports
2
2
 
3
3
  These primitives were extracted from SUPER, but have no SUPER database, project,
4
4
  pricing policy, sender number, UI, or workflow dependency. Existing Hub invocation,
@@ -35,6 +35,33 @@ Protocol reference: `tangle-network/tangle-router` commit
35
35
  `8999a6a9a01d6c2872010e265327c207a73adbe9`, `app/v1/search/route.ts` and `lib/web-search.ts`.
36
36
  Requalify against the actual deployment; source compatibility is not live access.
37
37
 
38
+ ## Read a public page
39
+
40
+ Install the package and import the reader from the existing search entrypoint.
41
+
42
+ ```ts
43
+ import { TangleReadClient } from '@tangle-network/agent-integrations/tangle-search'
44
+
45
+ const reader = new TangleReadClient({ apiKey: () => secretStore.routerKey() })
46
+ const page = await reader.read({ url: 'https://example.com/', maxBytes: 32768 }, signal)
47
+ console.log(page.url, page.contentType, page.bytes, page.truncated)
48
+ ```
49
+
50
+ The client sends one POST to `/v1/read` without automatic retries.
51
+ Supply the Router credential through trusted host configuration.
52
+ The Router validates destinations and redirects without forwarding the Router credential to the page.
53
+
54
+ `maxBytes` accepts 1,024–131,072 raw page bytes and defaults to 131,072.
55
+ `bytes` reports raw bytes; decoded UTF-8 content can expand when invalid bytes become replacement characters.
56
+ `truncated` identifies a bounded partial result.
57
+ The response must correlate with the original requested URL and remain within its reported byte limit.
58
+ `billedCost` preserves the reported charge and stays null when the charge is absent or invalid.
59
+
60
+ `content` contains untrusted page text or HTML.
61
+ It is neither an extracted summary nor instructions for the agent.
62
+ Retain the request ID, returned URL, content type, and fetch time with downstream evidence.
63
+ For an existing bounded transport, use `buildTangleReadRequest` and `parseTangleReadResult` to share the same contract.
64
+
38
65
  ## Phone verification and SMS
39
66
 
40
67
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-integrations",
3
- "version": "0.58.0",
3
+ "version": "0.59.1",
4
4
  "description": "Vendor-neutral integration contracts and runtime helpers for sandbox and agent apps.",
5
5
  "homepage": "https://github.com/tangle-network/agent-integrations#readme",
6
6
  "repository": {
@@ -34,6 +34,21 @@
34
34
  "import": "./dist/connectors/index.js",
35
35
  "default": "./dist/connectors/index.js"
36
36
  },
37
+ "./connectors/oauth": {
38
+ "types": "./dist/connectors/oauth.d.ts",
39
+ "import": "./dist/connectors/oauth.js",
40
+ "default": "./dist/connectors/oauth.js"
41
+ },
42
+ "./hub": {
43
+ "types": "./dist/hub.d.ts",
44
+ "import": "./dist/hub.js",
45
+ "default": "./dist/hub.js"
46
+ },
47
+ "./sandbox": {
48
+ "types": "./dist/sandbox.d.ts",
49
+ "import": "./dist/sandbox.js",
50
+ "default": "./dist/sandbox.js"
51
+ },
37
52
  "./connectors/adapters": {
38
53
  "types": "./dist/connectors/adapters/index.d.ts",
39
54
  "import": "./dist/connectors/adapters/index.js",
@@ -146,7 +161,7 @@
146
161
  "license": "MIT",
147
162
  "dependencies": {
148
163
  "@duckdb/node-api": "1.5.5-r.5",
149
- "@ph0ny/sdk": "0.1.2",
164
+ "@ph0ny/sdk": "0.1.3",
150
165
  "amqplib": "2.1.0",
151
166
  "csv-parse": "7.0.3",
152
167
  "csv-stringify": "6.9.0",