@waniwani/sdk 0.21.3 → 0.21.4

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.
@@ -1,2 +1,2 @@
1
- var a=class extends Error{constructor(e,o){super(e);this.status=o;this.name="WaniWaniError"}};var E="@waniwani/sdk",h="/api/mcp/modules/documents/extract";function g(t,n){try{let e=JSON.parse(t);if(e!==null&&typeof e=="object"){let o="message"in e&&typeof e.message=="string"?e.message:"",r="detail"in e&&typeof e.detail=="string"?e.detail:"";if(o&&r)return`${o}: ${r}`;if(o)return o;if(r)return r}}catch{}return t||`Documents API error: HTTP ${n}`}function A(t){return"documentId"in t&&typeof t.documentId=="string"}function x(t){return A(t)?{documentId:t.documentId}:{url:t.url,filename:t.filename}}function w(t){let{apiUrl:n,apiKey:e}=t;return{async extract(o){if(!e)throw new Error("WANIWANI_API_KEY is not set");let{toJSONSchema:r}=await import("zod"),s=await fetch(`${n.replace(/\/$/,"")}${h}`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json","X-WaniWani-SDK":E},body:JSON.stringify({...x(o),schema:r(o.schema),pages:o.pages,sessionId:o.sessionId,correlationId:o.correlationId})});if(!s.ok){let I=await s.text().catch(()=>"");throw new a(g(I,s.status),s.status)}let f=await s.json(),{fields:p,pageCount:l,pageConfidence:D,documentId:y}=f.data;return{fields:o.schema.parse(p),pageCount:l,pageConfidence:D??null,documentId:y}}}}var T=["documentId","document_id"],S=["filename","file_name","fileName","name"],k=["mediaType","media_type","mimeType","mime_type","contentType","content_type"],_="document",N="application/octet-stream";function c(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function d(t,n){for(let e of n){let o=t[e];if(typeof o=="string"&&o.length>0)return o}}function C(t){if(!c(t))return;let n=d(t,T);if(n)return{documentId:n,filename:d(t,S)??_,mediaType:d(t,k)??N}}function m(t){let n=[],e=new Set;for(let o of Array.isArray(t)?t:[t]){let r=C(o);r&&!e.has(r.documentId)&&(e.add(r.documentId),n.push(r))}return n}function M(t){if(!Array.isArray(t))return[];let n=[...t].reverse().find(e=>c(e)&&e.role==="user");return!c(n)||!Array.isArray(n.parts)?[]:m(n.parts.flatMap(e=>c(e)&&c(e.data)?[e,e.data]:[e]))}function R(t){let n=c(t)?t:void 0,e=m(n?n.documents:t);return e.length>0?e:M(n?n.messages:t)}export{w as createDocumentsClient,R as readAttachedDocuments};
1
+ var a=class extends Error{constructor(e,n){super(e);this.status=n;this.name="WaniWaniError"}};function d(t,o,e){try{let n=JSON.parse(t);if(n!==null&&typeof n=="object"){let r="message"in n&&typeof n.message=="string"?n.message:"",c="detail"in n&&typeof n.detail=="string"?n.detail:"";if(r&&c)return`${r}: ${c}`;if(r)return r;if(c)return c}}catch{}return t||`${e} error: HTTP ${o}`}var E="@waniwani/sdk",h="/api/mcp/modules/documents/extract";function A(t){return"documentId"in t&&typeof t.documentId=="string"}function x(t){return A(t)?{documentId:t.documentId}:{url:t.url,filename:t.filename}}function w(t){let{apiUrl:o,apiKey:e}=t;return{async extract(n){if(!e)throw new Error("WANIWANI_API_KEY is not set");let{toJSONSchema:r}=await import("zod"),c=await fetch(`${o.replace(/\/$/,"")}${h}`,{method:"POST",headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json","X-WaniWani-SDK":E},body:JSON.stringify({...x(n),schema:r(n.schema),pages:n.pages,sessionId:n.sessionId,correlationId:n.correlationId})});if(!c.ok){let g=await c.text().catch(()=>"");throw new a(d(g,c.status,"Documents API"),c.status)}let p=await c.json(),{fields:l,pageCount:D,pageConfidence:y,documentId:I}=p.data;return{fields:n.schema.parse(l),pageCount:D,pageConfidence:y??null,documentId:I}}}}var T=["documentId","document_id"],S=["filename","file_name","fileName","name"],k=["mediaType","media_type","mimeType","mime_type","contentType","content_type"],_="document",N="application/octet-stream";function s(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}function m(t,o){for(let e of o){let n=t[e];if(typeof n=="string"&&n.length>0)return n}}function C(t){if(!s(t))return;let o=m(t,T);if(o)return{documentId:o,filename:m(t,S)??_,mediaType:m(t,k)??N}}function f(t){let o=[],e=new Set;for(let n of Array.isArray(t)?t:[t]){let r=C(n);r&&!e.has(r.documentId)&&(e.add(r.documentId),o.push(r))}return o}function M(t){if(!Array.isArray(t))return[];let o=[...t].reverse().find(e=>s(e)&&e.role==="user");return!s(o)||!Array.isArray(o.parts)?[]:f(o.parts.flatMap(e=>s(e)&&s(e.data)?[e,e.data]:[e]))}function R(t){let o=s(t)?t:void 0,e=f(o?o.documents:t);return e.length>0?e:M(o?o.messages:t)}export{w as createDocumentsClient,R as readAttachedDocuments};
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/error.ts","../../src/documents/client.ts","../../src/documents/messages.ts"],"sourcesContent":["// Waniwani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","import { WaniWaniError } from \"../error.js\";\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tDocumentExtractInput,\n\tDocumentExtractResult,\n\tDocumentExtractStoredInput,\n\tDocumentSchema,\n\tDocumentsClient,\n} from \"./types.js\";\n\nconst SDK_NAME = \"@waniwani/sdk\";\nconst EXTRACT_PATH = \"/api/mcp/modules/documents/extract\";\n\n/** Method syntax makes the parameter bivariant, so a structural schema is accepted without an assertion. */\ninterface ZodModule {\n\ttoJSONSchema(schema: DocumentSchema<unknown>): Record<string, unknown>;\n}\n\ninterface ExtractWireResponse {\n\tfields: unknown;\n\tpageCount: number;\n\tpageConfidence: number | null;\n\tdocumentId: string;\n}\n\n/**\n * The platform puts the machine-readable code on `message` and the human reason\n * on `detail`, so a refusal reads as \"UNSUPPORTED_DOCUMENT_TYPE: photo.heic is\n * not one of ...\". The raw body is the fallback.\n */\nfunction refusalMessage(body: string, status: number): string {\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(body);\n\t\tif (parsed !== null && typeof parsed === \"object\") {\n\t\t\tconst code =\n\t\t\t\t\"message\" in parsed && typeof parsed.message === \"string\"\n\t\t\t\t\t? parsed.message\n\t\t\t\t\t: \"\";\n\t\t\tconst detail =\n\t\t\t\t\"detail\" in parsed && typeof parsed.detail === \"string\"\n\t\t\t\t\t? parsed.detail\n\t\t\t\t\t: \"\";\n\t\t\tif (code && detail) {\n\t\t\t\treturn `${code}: ${detail}`;\n\t\t\t}\n\t\t\tif (code) {\n\t\t\t\treturn code;\n\t\t\t}\n\t\t\tif (detail) {\n\t\t\t\treturn detail;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// A non-JSON body is surfaced as-is below.\n\t}\n\treturn body || `Documents API error: HTTP ${status}`;\n}\n\nfunction isStoredInput<T>(\n\tinput: DocumentExtractInput<T>,\n): input is DocumentExtractStoredInput<T> {\n\treturn \"documentId\" in input && typeof input.documentId === \"string\";\n}\n\n/** The platform's body is a union, so each branch's fields must travel alone. */\nfunction sourceFields<T>(\n\tinput: DocumentExtractInput<T>,\n): Record<string, string> {\n\tif (isStoredInput(input)) {\n\t\treturn { documentId: input.documentId };\n\t}\n\treturn { url: input.url, filename: input.filename };\n}\n\nexport function createDocumentsClient(\n\tconfig: Pick<InternalConfig, \"apiUrl\" | \"apiKey\">,\n): DocumentsClient {\n\tconst { apiUrl, apiKey } = config;\n\n\treturn {\n\t\tasync extract<T>(\n\t\t\tinput: DocumentExtractInput<T>,\n\t\t): Promise<DocumentExtractResult<T>> {\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t\t}\n\n\t\t\t// zod is an optional peer dependency and this module is reachable from the\n\t\t\t// package root, so a top-level import would break every consumer that only\n\t\t\t// uses tracking.\n\t\t\tconst { toJSONSchema }: ZodModule = await import(\"zod\");\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${apiUrl.replace(/\\/$/, \"\")}${EXTRACT_PATH}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t...sourceFields(input),\n\t\t\t\t\t\tschema: toJSONSchema(input.schema),\n\t\t\t\t\t\tpages: input.pages,\n\t\t\t\t\t\tsessionId: input.sessionId,\n\t\t\t\t\t\tcorrelationId: input.correlationId,\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\t\tthrow new WaniWaniError(\n\t\t\t\t\trefusalMessage(text, response.status),\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst json = (await response.json()) as { data: ExtractWireResponse };\n\t\t\tconst { fields, pageCount, pageConfidence, documentId } = json.data;\n\n\t\t\treturn {\n\t\t\t\tfields: input.schema.parse(fields),\n\t\t\t\tpageCount,\n\t\t\t\tpageConfidence: pageConfidence ?? null,\n\t\t\t\tdocumentId,\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { AttachedDocument } from \"./types.js\";\n\nconst DOCUMENT_ID_KEYS = [\"documentId\", \"document_id\"] as const;\nconst FILENAME_KEYS = [\"filename\", \"file_name\", \"fileName\", \"name\"] as const;\nconst MEDIA_TYPE_KEYS = [\n\t\"mediaType\",\n\t\"media_type\",\n\t\"mimeType\",\n\t\"mime_type\",\n\t\"contentType\",\n\t\"content_type\",\n] as const;\n\nconst UNNAMED_FILENAME = \"document\";\nconst UNKNOWN_MEDIA_TYPE = \"application/octet-stream\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction pickFirst(\n\tsource: Record<string, unknown>,\n\tkeys: readonly string[],\n): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = source[key];\n\t\tif (typeof value === \"string\" && value.length > 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/** Only the id is load-bearing: it is what `extract()` needs, so an entry missing the descriptive two is still usable. */\nfunction toAttachedDocument(entry: unknown): AttachedDocument | undefined {\n\tif (!isRecord(entry)) {\n\t\treturn undefined;\n\t}\n\n\tconst documentId = pickFirst(entry, DOCUMENT_ID_KEYS);\n\tif (!documentId) {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tdocumentId,\n\t\tfilename: pickFirst(entry, FILENAME_KEYS) ?? UNNAMED_FILENAME,\n\t\tmediaType: pickFirst(entry, MEDIA_TYPE_KEYS) ?? UNKNOWN_MEDIA_TYPE,\n\t};\n}\n\nfunction collect(candidate: unknown): AttachedDocument[] {\n\tconst documents: AttachedDocument[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const entry of Array.isArray(candidate) ? candidate : [candidate]) {\n\t\tconst document = toAttachedDocument(entry);\n\t\tif (document && !seen.has(document.documentId)) {\n\t\t\tseen.add(document.documentId);\n\t\t\tdocuments.push(document);\n\t\t}\n\t}\n\n\treturn documents;\n}\n\nfunction fromLatestUserTurn(messages: unknown): AttachedDocument[] {\n\tif (!Array.isArray(messages)) {\n\t\treturn [];\n\t}\n\n\tconst latest = [...messages]\n\t\t.reverse()\n\t\t.find((message) => isRecord(message) && message.role === \"user\");\n\tif (!isRecord(latest) || !Array.isArray(latest.parts)) {\n\t\treturn [];\n\t}\n\n\treturn collect(\n\t\tlatest.parts.flatMap((part: unknown) =>\n\t\t\tisRecord(part) && isRecord(part.data) ? [part, part.data] : [part],\n\t\t),\n\t);\n}\n\n/**\n * The documents a visitor attached on the latest user turn, for a chat route you\n * run yourself. Pass the parsed request body: the widget sends them in a\n * top-level `documents` field, outside `messages`, so the bytes stay out of the\n * transcript that is reposted on every later turn.\n *\n * ```ts\n * const body = await request.json();\n * for (const { documentId } of readAttachedDocuments(body)) {\n * const { fields } = await wani.documents.extract({ documentId, schema });\n * }\n * ```\n *\n * Also accepts the `documents` array alone, or a `messages` array whose latest\n * user message carries the ids in its parts. Unparseable input yields an empty\n * array; repeated ids collapse to one.\n */\nexport function readAttachedDocuments(input: unknown): AttachedDocument[] {\n\tconst body = isRecord(input) ? input : undefined;\n\n\tconst declared = collect(body ? body.documents : input);\n\tif (declared.length > 0) {\n\t\treturn declared;\n\t}\n\n\treturn fromLatestUserTurn(body ? body.messages : input);\n}\n"],"mappings":"AAEO,IAAMA,EAAN,cAA4B,KAAM,CACxC,YACCC,EACOC,EACN,CACD,MAAMD,CAAO,EAFN,YAAAC,EAGP,KAAK,KAAO,eACb,CACD,ECAA,IAAMC,EAAW,gBACXC,EAAe,qCAmBrB,SAASC,EAAeC,EAAcC,EAAwB,CAC7D,GAAI,CACH,IAAMC,EAAkB,KAAK,MAAMF,CAAI,EACvC,GAAIE,IAAW,MAAQ,OAAOA,GAAW,SAAU,CAClD,IAAMC,EACL,YAAaD,GAAU,OAAOA,EAAO,SAAY,SAC9CA,EAAO,QACP,GACEE,EACL,WAAYF,GAAU,OAAOA,EAAO,QAAW,SAC5CA,EAAO,OACP,GACJ,GAAIC,GAAQC,EACX,MAAO,GAAGD,CAAI,KAAKC,CAAM,GAE1B,GAAID,EACH,OAAOA,EAER,GAAIC,EACH,OAAOA,CAET,CACD,MAAQ,CAER,CACA,OAAOJ,GAAQ,6BAA6BC,CAAM,EACnD,CAEA,SAASI,EACRC,EACyC,CACzC,MAAO,eAAgBA,GAAS,OAAOA,EAAM,YAAe,QAC7D,CAGA,SAASC,EACRD,EACyB,CACzB,OAAID,EAAcC,CAAK,EACf,CAAE,WAAYA,EAAM,UAAW,EAEhC,CAAE,IAAKA,EAAM,IAAK,SAAUA,EAAM,QAAS,CACnD,CAEO,SAASE,EACfC,EACkB,CAClB,GAAM,CAAE,OAAAC,EAAQ,OAAAC,CAAO,EAAIF,EAE3B,MAAO,CACN,MAAM,QACLH,EACoC,CACpC,GAAI,CAACK,EACJ,MAAM,IAAI,MAAM,6BAA6B,EAM9C,GAAM,CAAE,aAAAC,CAAa,EAAe,KAAM,QAAO,KAAK,EAEhDC,EAAW,MAAM,MACtB,GAAGH,EAAO,QAAQ,MAAO,EAAE,CAAC,GAAGZ,CAAY,GAC3C,CACC,OAAQ,OACR,QAAS,CACR,cAAe,UAAUa,CAAM,GAC/B,eAAgB,mBAChB,iBAAkBd,CACnB,EACA,KAAM,KAAK,UAAU,CACpB,GAAGU,EAAaD,CAAK,EACrB,OAAQM,EAAaN,EAAM,MAAM,EACjC,MAAOA,EAAM,MACb,UAAWA,EAAM,UACjB,cAAeA,EAAM,aACtB,CAAC,CACF,CACD,EAEA,GAAI,CAACO,EAAS,GAAI,CACjB,IAAMC,EAAO,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,EAAE,EACjD,MAAM,IAAIE,EACThB,EAAee,EAAMD,EAAS,MAAM,EACpCA,EAAS,MACV,CACD,CAEA,IAAMG,EAAQ,MAAMH,EAAS,KAAK,EAC5B,CAAE,OAAAI,EAAQ,UAAAC,EAAW,eAAAC,EAAgB,WAAAC,CAAW,EAAIJ,EAAK,KAE/D,MAAO,CACN,OAAQV,EAAM,OAAO,MAAMW,CAAM,EACjC,UAAAC,EACA,eAAgBC,GAAkB,KAClC,WAAAC,CACD,CACD,CACD,CACD,CChIA,IAAMC,EAAmB,CAAC,aAAc,aAAa,EAC/CC,EAAgB,CAAC,WAAY,YAAa,WAAY,MAAM,EAC5DC,EAAkB,CACvB,YACA,aACA,WACA,YACA,cACA,cACD,EAEMC,EAAmB,WACnBC,EAAqB,2BAE3B,SAASC,EAASC,EAAkD,CACnE,OAAO,OAAOA,GAAU,UAAYA,IAAU,MAAQ,CAAC,MAAM,QAAQA,CAAK,CAC3E,CAEA,SAASC,EACRC,EACAC,EACqB,CACrB,QAAWC,KAAOD,EAAM,CACvB,IAAMH,EAAQE,EAAOE,CAAG,EACxB,GAAI,OAAOJ,GAAU,UAAYA,EAAM,OAAS,EAC/C,OAAOA,CAET,CAED,CAGA,SAASK,EAAmBC,EAA8C,CACzE,GAAI,CAACP,EAASO,CAAK,EAClB,OAGD,IAAMC,EAAaN,EAAUK,EAAOZ,CAAgB,EACpD,GAAKa,EAIL,MAAO,CACN,WAAAA,EACA,SAAUN,EAAUK,EAAOX,CAAa,GAAKE,EAC7C,UAAWI,EAAUK,EAAOV,CAAe,GAAKE,CACjD,CACD,CAEA,SAASU,EAAQC,EAAwC,CACxD,IAAMC,EAAgC,CAAC,EACjCC,EAAO,IAAI,IAEjB,QAAWL,KAAS,MAAM,QAAQG,CAAS,EAAIA,EAAY,CAACA,CAAS,EAAG,CACvE,IAAMG,EAAWP,EAAmBC,CAAK,EACrCM,GAAY,CAACD,EAAK,IAAIC,EAAS,UAAU,IAC5CD,EAAK,IAAIC,EAAS,UAAU,EAC5BF,EAAU,KAAKE,CAAQ,EAEzB,CAEA,OAAOF,CACR,CAEA,SAASG,EAAmBC,EAAuC,CAClE,GAAI,CAAC,MAAM,QAAQA,CAAQ,EAC1B,MAAO,CAAC,EAGT,IAAMC,EAAS,CAAC,GAAGD,CAAQ,EACzB,QAAQ,EACR,KAAME,GAAYjB,EAASiB,CAAO,GAAKA,EAAQ,OAAS,MAAM,EAChE,MAAI,CAACjB,EAASgB,CAAM,GAAK,CAAC,MAAM,QAAQA,EAAO,KAAK,EAC5C,CAAC,EAGFP,EACNO,EAAO,MAAM,QAASE,GACrBlB,EAASkB,CAAI,GAAKlB,EAASkB,EAAK,IAAI,EAAI,CAACA,EAAMA,EAAK,IAAI,EAAI,CAACA,CAAI,CAClE,CACD,CACD,CAmBO,SAASC,EAAsBC,EAAoC,CACzE,IAAMC,EAAOrB,EAASoB,CAAK,EAAIA,EAAQ,OAEjCE,EAAWb,EAAQY,EAAOA,EAAK,UAAYD,CAAK,EACtD,OAAIE,EAAS,OAAS,EACdA,EAGDR,EAAmBO,EAAOA,EAAK,SAAWD,CAAK,CACvD","names":["WaniWaniError","message","status","SDK_NAME","EXTRACT_PATH","refusalMessage","body","status","parsed","code","detail","isStoredInput","input","sourceFields","createDocumentsClient","config","apiUrl","apiKey","toJSONSchema","response","text","WaniWaniError","json","fields","pageCount","pageConfidence","documentId","DOCUMENT_ID_KEYS","FILENAME_KEYS","MEDIA_TYPE_KEYS","UNNAMED_FILENAME","UNKNOWN_MEDIA_TYPE","isRecord","value","pickFirst","source","keys","key","toAttachedDocument","entry","documentId","collect","candidate","documents","seen","document","fromLatestUserTurn","messages","latest","message","part","readAttachedDocuments","input","body","declared"]}
1
+ {"version":3,"sources":["../../src/error.ts","../../src/shared/refusal.ts","../../src/documents/client.ts","../../src/documents/messages.ts"],"sourcesContent":["// Waniwani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","/**\n * The platform puts the machine-readable code on `message` and the human reason\n * on `detail`, so a refusal reads as \"UNSUPPORTED_DOCUMENT_TYPE: photo.heic is\n * not one of ...\". The raw body is the fallback.\n */\nexport function refusalMessage(\n\tbody: string,\n\tstatus: number,\n\tlabel: string,\n): string {\n\ttry {\n\t\tconst parsed: unknown = JSON.parse(body);\n\t\tif (parsed !== null && typeof parsed === \"object\") {\n\t\t\tconst code =\n\t\t\t\t\"message\" in parsed && typeof parsed.message === \"string\"\n\t\t\t\t\t? parsed.message\n\t\t\t\t\t: \"\";\n\t\t\tconst detail =\n\t\t\t\t\"detail\" in parsed && typeof parsed.detail === \"string\"\n\t\t\t\t\t? parsed.detail\n\t\t\t\t\t: \"\";\n\t\t\tif (code && detail) {\n\t\t\t\treturn `${code}: ${detail}`;\n\t\t\t}\n\t\t\tif (code) {\n\t\t\t\treturn code;\n\t\t\t}\n\t\t\tif (detail) {\n\t\t\t\treturn detail;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\t// A non-JSON body is surfaced as-is below.\n\t}\n\treturn body || `${label} error: HTTP ${status}`;\n}\n","import { WaniWaniError } from \"../error.js\";\nimport { refusalMessage } from \"../shared/refusal.js\";\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tDocumentExtractInput,\n\tDocumentExtractResult,\n\tDocumentExtractStoredInput,\n\tDocumentSchema,\n\tDocumentsClient,\n} from \"./types.js\";\n\nconst SDK_NAME = \"@waniwani/sdk\";\nconst EXTRACT_PATH = \"/api/mcp/modules/documents/extract\";\n\n/** Method syntax makes the parameter bivariant, so a structural schema is accepted without an assertion. */\ninterface ZodModule {\n\ttoJSONSchema(schema: DocumentSchema<unknown>): Record<string, unknown>;\n}\n\ninterface ExtractWireResponse {\n\tfields: unknown;\n\tpageCount: number;\n\tpageConfidence: number | null;\n\tdocumentId: string;\n}\n\nfunction isStoredInput<T>(\n\tinput: DocumentExtractInput<T>,\n): input is DocumentExtractStoredInput<T> {\n\treturn \"documentId\" in input && typeof input.documentId === \"string\";\n}\n\n/** The platform's body is a union, so each branch's fields must travel alone. */\nfunction sourceFields<T>(\n\tinput: DocumentExtractInput<T>,\n): Record<string, string> {\n\tif (isStoredInput(input)) {\n\t\treturn { documentId: input.documentId };\n\t}\n\treturn { url: input.url, filename: input.filename };\n}\n\nexport function createDocumentsClient(\n\tconfig: Pick<InternalConfig, \"apiUrl\" | \"apiKey\">,\n): DocumentsClient {\n\tconst { apiUrl, apiKey } = config;\n\n\treturn {\n\t\tasync extract<T>(\n\t\t\tinput: DocumentExtractInput<T>,\n\t\t): Promise<DocumentExtractResult<T>> {\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t\t}\n\n\t\t\t// zod is an optional peer dependency and this module is reachable from the\n\t\t\t// package root, so a top-level import would break every consumer that only\n\t\t\t// uses tracking.\n\t\t\tconst { toJSONSchema }: ZodModule = await import(\"zod\");\n\n\t\t\tconst response = await fetch(\n\t\t\t\t`${apiUrl.replace(/\\/$/, \"\")}${EXTRACT_PATH}`,\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tAuthorization: `Bearer ${apiKey}`,\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t...sourceFields(input),\n\t\t\t\t\t\tschema: toJSONSchema(input.schema),\n\t\t\t\t\t\tpages: input.pages,\n\t\t\t\t\t\tsessionId: input.sessionId,\n\t\t\t\t\t\tcorrelationId: input.correlationId,\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\t\tthrow new WaniWaniError(\n\t\t\t\t\trefusalMessage(text, response.status, \"Documents API\"),\n\t\t\t\t\tresponse.status,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst json = (await response.json()) as { data: ExtractWireResponse };\n\t\t\tconst { fields, pageCount, pageConfidence, documentId } = json.data;\n\n\t\t\treturn {\n\t\t\t\tfields: input.schema.parse(fields),\n\t\t\t\tpageCount,\n\t\t\t\tpageConfidence: pageConfidence ?? null,\n\t\t\t\tdocumentId,\n\t\t\t};\n\t\t},\n\t};\n}\n","import type { AttachedDocument } from \"./types.js\";\n\nconst DOCUMENT_ID_KEYS = [\"documentId\", \"document_id\"] as const;\nconst FILENAME_KEYS = [\"filename\", \"file_name\", \"fileName\", \"name\"] as const;\nconst MEDIA_TYPE_KEYS = [\n\t\"mediaType\",\n\t\"media_type\",\n\t\"mimeType\",\n\t\"mime_type\",\n\t\"contentType\",\n\t\"content_type\",\n] as const;\n\nconst UNNAMED_FILENAME = \"document\";\nconst UNKNOWN_MEDIA_TYPE = \"application/octet-stream\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction pickFirst(\n\tsource: Record<string, unknown>,\n\tkeys: readonly string[],\n): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = source[key];\n\t\tif (typeof value === \"string\" && value.length > 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/** Only the id is load-bearing: it is what `extract()` needs, so an entry missing the descriptive two is still usable. */\nfunction toAttachedDocument(entry: unknown): AttachedDocument | undefined {\n\tif (!isRecord(entry)) {\n\t\treturn undefined;\n\t}\n\n\tconst documentId = pickFirst(entry, DOCUMENT_ID_KEYS);\n\tif (!documentId) {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tdocumentId,\n\t\tfilename: pickFirst(entry, FILENAME_KEYS) ?? UNNAMED_FILENAME,\n\t\tmediaType: pickFirst(entry, MEDIA_TYPE_KEYS) ?? UNKNOWN_MEDIA_TYPE,\n\t};\n}\n\nfunction collect(candidate: unknown): AttachedDocument[] {\n\tconst documents: AttachedDocument[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const entry of Array.isArray(candidate) ? candidate : [candidate]) {\n\t\tconst document = toAttachedDocument(entry);\n\t\tif (document && !seen.has(document.documentId)) {\n\t\t\tseen.add(document.documentId);\n\t\t\tdocuments.push(document);\n\t\t}\n\t}\n\n\treturn documents;\n}\n\nfunction fromLatestUserTurn(messages: unknown): AttachedDocument[] {\n\tif (!Array.isArray(messages)) {\n\t\treturn [];\n\t}\n\n\tconst latest = [...messages]\n\t\t.reverse()\n\t\t.find((message) => isRecord(message) && message.role === \"user\");\n\tif (!isRecord(latest) || !Array.isArray(latest.parts)) {\n\t\treturn [];\n\t}\n\n\treturn collect(\n\t\tlatest.parts.flatMap((part: unknown) =>\n\t\t\tisRecord(part) && isRecord(part.data) ? [part, part.data] : [part],\n\t\t),\n\t);\n}\n\n/**\n * The documents a visitor attached on the latest user turn, for a chat route you\n * run yourself. Pass the parsed request body: the widget sends them in a\n * top-level `documents` field, outside `messages`, so the bytes stay out of the\n * transcript that is reposted on every later turn.\n *\n * ```ts\n * const body = await request.json();\n * for (const { documentId } of readAttachedDocuments(body)) {\n * const { fields } = await wani.documents.extract({ documentId, schema });\n * }\n * ```\n *\n * Also accepts the `documents` array alone, or a `messages` array whose latest\n * user message carries the ids in its parts. Unparseable input yields an empty\n * array; repeated ids collapse to one.\n */\nexport function readAttachedDocuments(input: unknown): AttachedDocument[] {\n\tconst body = isRecord(input) ? input : undefined;\n\n\tconst declared = collect(body ? body.documents : input);\n\tif (declared.length > 0) {\n\t\treturn declared;\n\t}\n\n\treturn fromLatestUserTurn(body ? body.messages : input);\n}\n"],"mappings":"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","names":["WaniWaniError","message","status","refusalMessage","body","status","label","parsed","code","detail","SDK_NAME","EXTRACT_PATH","isStoredInput","input","sourceFields","createDocumentsClient","config","apiUrl","apiKey","toJSONSchema","response","text","WaniWaniError","refusalMessage","json","fields","pageCount","pageConfidence","documentId","DOCUMENT_ID_KEYS","FILENAME_KEYS","MEDIA_TYPE_KEYS","UNNAMED_FILENAME","UNKNOWN_MEDIA_TYPE","isRecord","value","pickFirst","source","keys","key","toAttachedDocument","entry","documentId","collect","candidate","documents","seen","document","fromLatestUserTurn","messages","latest","message","part","readAttachedDocuments","input","body","declared"]}
package/dist/index.d.ts CHANGED
@@ -74,6 +74,43 @@ interface DocumentsClient {
74
74
  */
75
75
  declare function readAttachedDocuments(input: unknown): AttachedDocument[];
76
76
 
77
+ interface EmailSendFields {
78
+ /** One recipient address */
79
+ to: string;
80
+ /** Copy recipients: one address or a list of up to 50 */
81
+ cc?: string | string[];
82
+ /** Blind copy recipients: one address or a list of up to 50 */
83
+ bcc?: string | string[];
84
+ /** Up to 998 characters */
85
+ subject: string;
86
+ /** Address replies go to instead of the sender */
87
+ replyTo?: string;
88
+ /** Conversation this email belongs to, recorded on its log row */
89
+ sessionId?: string;
90
+ }
91
+ interface EmailHtmlContent {
92
+ /** The finished email, for example `await render(<Recap />)` from React Email. Sent as is, up to 512 KB. */
93
+ html: string;
94
+ /** Plain-text part, up to 512 KB. Leave it out and one is built from `html`. */
95
+ text?: string;
96
+ }
97
+ interface EmailTextContent {
98
+ /** The finished email, for example `await render(<Recap />)` from React Email. Sent as is, up to 512 KB. */
99
+ html?: string;
100
+ /** Plain-text email, up to 512 KB, sent alone when `html` is left out. */
101
+ text: string;
102
+ }
103
+ /** Pass `html`, `text` or both. */
104
+ type EmailSendInput = EmailSendFields & (EmailHtmlContent | EmailTextContent);
105
+ interface EmailSendResult {
106
+ /** The log row for this send, listed under Modules > Email > Logs in the dashboard */
107
+ id: string;
108
+ }
109
+ interface EmailClient {
110
+ /** Send one email from the agent behind your API key, as `"Agent name" <agent-name@notifications.waniwani.run>`. */
111
+ send(input: EmailSendInput): Promise<EmailSendResult>;
112
+ }
113
+
77
114
  declare class WaniWaniError extends Error {
78
115
  status: number;
79
116
  constructor(message: string, status: number);
@@ -571,6 +608,8 @@ interface WaniWaniClient extends TrackingClient {
571
608
  readonly kb: KbClient;
572
609
  /** Documents client for reading a file into typed fields */
573
610
  readonly documents: DocumentsClient;
611
+ /** Email client for sending from the agent behind your API key */
612
+ readonly email: EmailClient;
574
613
  }
575
614
  interface InternalConfig {
576
615
  apiUrl: string;
@@ -641,4 +680,4 @@ interface V2BatchResponse {
641
680
  */
642
681
  declare function waniwani(config?: WaniWaniConfig | WaniWaniProjectConfig): WaniWaniClient;
643
682
 
644
- export { type AttachedDocument, type ComparedPriceOption, type ConvertedProperties, type DocumentExtractInput, type DocumentExtractResult, type DocumentExtractStoredInput, type DocumentExtractUrlInput, type DocumentSchema, type DocumentsClient, ERROR_CAUSES, EVENT_TYPES, type ErrorCauseType, type EventType, type FrontendClientOptions, type FrontendIdentity, type FrontendTrackingClient, type KbClient, type KbIngestFile, type KbIngestResult, type KbSearchOptions, type KbSource, type LeadQualifiedProperties, type LegacyTrackEvent, type LinkClickedProperties, type OptionSelectedProperties, type PriceShownProperties, type PricesComparedProperties, type RevenueConvertedInput, type RevenueLeadQualifiedInput, type RevenueOptionSelectedInput, type RevenuePriceShownInput, type RevenuePricesComparedInput, type RevenueTrackingApi, SESSION_ERROR_CODES, type SessionErrorCodeType, type SessionErrorProperties, type ToolCalledProperties, type TrackEvent, type TrackFn, type TrackInput, type TrackingConfig, type TrackingShutdownOptions, type TrackingShutdownResult, type V2BatchRejectedEvent, type V2BatchRequest, type V2BatchResponse, type V2CorrelationIds, type V2EnvelopeType, type V2EventEnvelope, type WaniWaniClient, type WaniWaniConfig, WaniWaniError, type WaniWaniProjectConfig, createFrontendClient, defineConfig, readAttachedDocuments, waniwani };
683
+ export { type AttachedDocument, type ComparedPriceOption, type ConvertedProperties, type DocumentExtractInput, type DocumentExtractResult, type DocumentExtractStoredInput, type DocumentExtractUrlInput, type DocumentSchema, type DocumentsClient, ERROR_CAUSES, EVENT_TYPES, type EmailClient, type EmailSendInput, type EmailSendResult, type ErrorCauseType, type EventType, type FrontendClientOptions, type FrontendIdentity, type FrontendTrackingClient, type KbClient, type KbIngestFile, type KbIngestResult, type KbSearchOptions, type KbSource, type LeadQualifiedProperties, type LegacyTrackEvent, type LinkClickedProperties, type OptionSelectedProperties, type PriceShownProperties, type PricesComparedProperties, type RevenueConvertedInput, type RevenueLeadQualifiedInput, type RevenueOptionSelectedInput, type RevenuePriceShownInput, type RevenuePricesComparedInput, type RevenueTrackingApi, SESSION_ERROR_CODES, type SessionErrorCodeType, type SessionErrorProperties, type ToolCalledProperties, type TrackEvent, type TrackFn, type TrackInput, type TrackingConfig, type TrackingShutdownOptions, type TrackingShutdownResult, type V2BatchRejectedEvent, type V2BatchRequest, type V2BatchResponse, type V2CorrelationIds, type V2EnvelopeType, type V2EventEnvelope, type WaniWaniClient, type WaniWaniConfig, WaniWaniError, type WaniWaniProjectConfig, createFrontendClient, defineConfig, readAttachedDocuments, waniwani };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var Z=["documentId","document_id"],ee=["filename","file_name","fileName","name"],te=["mediaType","media_type","mimeType","mime_type","contentType","content_type"],ne="document",re="application/octet-stream";function y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function R(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.length>0)return r}}function ie(e){if(!y(e))return;let t=R(e,Z);if(t)return{documentId:t,filename:R(e,ee)??ne,mediaType:R(e,te)??re}}function b(e){let t=[],n=new Set;for(let r of Array.isArray(e)?e:[e]){let i=ie(r);i&&!n.has(i.documentId)&&(n.add(i.documentId),t.push(i))}return t}function oe(e){if(!Array.isArray(e))return[];let t=[...e].reverse().find(n=>y(n)&&n.role==="user");return!y(t)||!Array.isArray(t.parts)?[]:b(t.parts.flatMap(n=>y(n)&&y(n.data)?[n,n.data]:[n]))}function P(e){let t=y(e)?e:void 0,n=b(t?t.documents:e);return n.length>0?n:oe(t?t.messages:e)}var m=class extends Error{constructor(n,r){super(n);this.status=r;this.name="WaniWaniError"}};import{existsSync as se,readFileSync as ae}from"fs";import{resolve as ce}from"path";var ue="waniwani.json",T;function A(){if(T!==void 0)return T;try{let e=ce(process.cwd(),ue);if(!se(e))return T=null,null;let t=ae(e,"utf-8");return T=JSON.parse(t),T}catch{return T=null,null}}var D="__waniwani_config__";function de(e){return globalThis[D]=e,e}function M(){return globalThis[D]}var O=["session.started","page.viewed","tool.called","link.clicked","widget_render","user.identified","price_shown","prices_compared","option_selected","lead_qualified","converted","session.error"],F=["agent_failed","tool_failed","upstream_failed"],U=["timeout","rate_limited","upstream_4xx","upstream_5xx","network","invalid_output","flow_dead_end","flow_unknown_node","flow_loop","unknown"];function v(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.length>0)return r}}var pe=["waniwani/sessionId","openai/sessionId","openai/session","sessionId","conversationId","mcp-session-id"],le=["waniwani/requestId","openai/requestId","requestId","mcp/requestId"],fe=["waniwani/traceId","openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"],ge=["waniwani/userId","openai/userId","externalUserId","userId","actorId"],he=["waniwani/visitorId","visitorId"],me=["correlationId","openai/requestId"];function B(e){return e?v(e,pe):void 0}function K(e){return e?v(e,le):void 0}function L(e){return e?v(e,fe):void 0}function W(e){return e?v(e,ge):void 0}function j(e){return e?v(e,he):void 0}function N(e){return e?v(e,me):void 0}var ye=[{key:"openai/sessionId",source:"chatgpt"},{key:"openai/session",source:"chatgpt"}],Te=[{needle:"claude",source:"claude"},{needle:"chatgpt",source:"chatgpt"},{needle:"openai",source:"chatgpt"},{needle:"gemini",source:"gemini"}];function V(e,t){if(e){let r=e["waniwani/source"];if(typeof r=="string"&&r.length>0)return r;for(let{key:i,source:o}of ye){let a=e[i];if(typeof a=="string"&&a.length>0)return o}}let n=t?.name;if(typeof n=="string"&&n.length>0){let r=n.toLowerCase();for(let{needle:i,source:o}of Te)if(r.includes(i))return o}}var ve="@waniwani/sdk";function I(e,t={}){let n=t.now??(()=>new Date),r=t.generateId??Y,i=ke(e),o=E(e.meta),a=E(e.metadata),s=Se(e,o),c=h(e.eventId)??r(),f=we(e.timestamp,n),d=h(e.source)??V(o)??t.source??ve,u=_(e)?{...e}:void 0,g={...a};return Object.keys(o).length>0&&(g.meta=o),u&&(g.rawLegacy=u),{id:c,type:"mcp.event",name:i,source:d,timestamp:f,correlation:s,properties:Ie(e,i),metadata:g,rawLegacy:u}}function Y(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function Ie(e,t){if(!_(e))return E(e.properties);let n=Ee(e,t),r=E(e.properties);return{...n,...r}}function Ee(e,t){switch(t){case"tool.called":{let n={};return h(e.toolName)&&(n.name=e.toolName),h(e.toolType)&&(n.type=e.toolType),n}case"link.clicked":{let n={};return h(e.linkUrl)&&(n.url=e.linkUrl),n}default:return{}}}function ke(e){return _(e)?e.eventType:e.event}function Se(e,t){let n=h(e.requestId)??K(t),r=h(e.sessionId)??B(t),i=h(e.traceId)??L(t),o=h(e.externalUserId)??W(t),a=h(e.visitorId)??j(t),s=h(e.correlationId)??N(t)??n,c={};return r&&(c.sessionId=r),i&&(c.traceId=i),n&&(c.requestId=n),s&&(c.correlationId=s),o&&(c.externalUserId=o),a&&(c.visitorId=a),c}function we(e,t){if(e instanceof Date)return e.toISOString();if(typeof e=="string"){let n=new Date(e);if(!Number.isNaN(n.getTime()))return n.toISOString()}return t().toISOString()}function E(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function h(e){if(typeof e=="string"&&e.trim().length!==0)return e}function _(e){return"eventType"in e}function k(e){return{priceShown:({amount:t,currency:n,itemId:r,label:i,...o})=>e({event:"price_shown",properties:{amount:t,currency:n,itemId:r,label:i},...o}),pricesCompared:({options:t,...n})=>e({event:"prices_compared",properties:{options:t},...n}),optionSelected:({id:t,amount:n,currency:r,...i})=>e({event:"option_selected",properties:{id:t,amount:n,currency:r},...i}),leadQualified:t=>{let{externalId:n,email:r,name:i,...o}=t??{};return e({event:"lead_qualified",properties:{externalId:n,email:r,name:i},...o})},converted:({amount:t,currency:n,occurredAt:r,...i})=>e({event:"converted",properties:{amount:t,currency:n,occurredAt:r},...i})}}var xe="/api/mcp/events/v2/batch";var q="@waniwani/sdk";var Re=new Set([401,403]),_e=new Set([408,425,429,500,502,503,504]),Ce=(e,t)=>globalThis.fetch(e,t);function S(e){return new C(e)}var C=class{endpointUrl;flushIntervalMs;maxBatchSize;maxBufferSize;maxRetries;retryBaseDelayMs;retryMaxDelayMs;shutdownTimeoutMs;sdkVersion;fetchFn;logger;now;sleep;apiKey;buffer=[];flushTimer;flushScheduled=!1;flushScheduledTimer;flushInFlight;inFlightCount=0;isStopped=!1;isShuttingDown=!1;constructor(t){this.endpointUrl=Ae(t.apiUrl,t.endpointPath??xe),this.flushIntervalMs=t.flushIntervalMs??1e3,this.maxBatchSize=t.maxBatchSize??20,this.maxBufferSize=t.maxBufferSize??1e3,this.maxRetries=t.maxRetries??3,this.retryBaseDelayMs=t.retryBaseDelayMs??200,this.retryMaxDelayMs=t.retryMaxDelayMs??2e3,this.shutdownTimeoutMs=t.shutdownTimeoutMs??2e3,this.fetchFn=t.fetchFn??Ce,this.logger=t.logger??console,this.now=t.now??(()=>new Date),this.sleep=t.sleep??(n=>new Promise(r=>setTimeout(r,n))),this.apiKey=t.apiKey,this.sdkVersion=t.sdkVersion,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>{this.flush()},this.flushIntervalMs)),this.registerBrowserTeardown()}enqueue(t){if(this.isStopped||this.isShuttingDown){this.logger.warn("[Waniwani] Tracking transport is stopped, dropping event %s",t.id);return}if(this.buffer.length>=this.maxBufferSize){let n=this.buffer.length-this.maxBufferSize+1;this.buffer.splice(0,n),this.logger.warn("[Waniwani] Tracking buffer overflow, dropped %d oldest event(s)",n)}if(this.buffer.push(t),this.buffer.length>=this.maxBatchSize){this.flush();return}this.scheduleMicroFlush()}pendingEvents(){return this.buffer.length+this.inFlightCount}async flush(){return this.flushInFlight?this.flushInFlight:(this.flushInFlight=this.flushLoop().finally(()=>{this.flushInFlight=void 0}),this.flushInFlight)}async shutdown(t){this.isShuttingDown=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),this.flushScheduledTimer&&(clearTimeout(this.flushScheduledTimer),this.flushScheduledTimer=void 0,this.flushScheduled=!1);let n=t?.timeoutMs??this.shutdownTimeoutMs,r=this.flush();if(!Number.isFinite(n)||n<=0)return await r,this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()};let i=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(n).then(()=>i)])===i?(this.isStopped=!0,{timedOut:!0,pendingEvents:this.pendingEvents()}):(this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()})}registerBrowserTeardown(){if(typeof document>"u"||typeof window>"u")return;let t=()=>{document.visibilityState==="hidden"&&this.keepaliveFlush()};document.addEventListener("visibilitychange",t),window.addEventListener("pagehide",()=>this.keepaliveFlush())}keepaliveFlush(){if(this.isStopped||this.buffer.length===0)return;let t=this.buffer.splice(0,this.buffer.length);this.sendKeepaliveChunk(t)}sendKeepaliveChunk(t){let n=JSON.stringify(this.makeBatchRequest(t));if(n.length>6e4&&t.length>1){let r=Math.ceil(t.length/2);this.sendKeepaliveChunk(t.slice(0,r)),this.sendKeepaliveChunk(t.slice(r));return}try{this.fetchFn(this.endpointUrl,{method:"POST",headers:this.requestHeaders(),body:n,keepalive:!0}).catch(()=>{})}catch{}}requestHeaders(){let t={"Content-Type":"application/json","X-WaniWani-SDK":q};return this.apiKey&&(t.Authorization=`Bearer ${this.apiKey}`),t}scheduleMicroFlush(){this.flushScheduled||(this.flushScheduled=!0,this.flushScheduledTimer=setTimeout(()=>{this.flushScheduledTimer=void 0,this.flushScheduled=!1,this.flush()},0))}async flushLoop(){for(;this.buffer.length>0&&!this.isStopped;){let t=this.buffer.splice(0,this.maxBatchSize);await this.sendBatchWithRetry(t)}}async sendBatchWithRetry(t){let n=0,r=t;for(;r.length>0&&!this.isStopped;){this.inFlightCount=r.length;let i=await this.sendBatchOnce(r);switch(this.inFlightCount=0,i.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(i.status,r.length);return;case"permanent":this.logger.error("[Waniwani] Dropping %d event(s) after permanent failure: %s",r.length,i.reason);return;case"retryable":if(n>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d event(s) after retry exhaustion: %s",r.length,i.reason);return}await this.sleep(this.backoffDelayMs(n)),n+=1;continue;case"partial":if(i.permanent.length>0&&this.logger.error("[Waniwani] Dropping %d event(s) rejected as permanent",i.permanent.length),i.retryable.length===0)return;if(n>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d retryable event(s) after retry exhaustion",i.retryable.length);return}r=i.retryable,await this.sleep(this.backoffDelayMs(n)),n+=1;continue}}}async sendBatchOnce(t){let n;try{n=await this.fetchFn(this.endpointUrl,{method:"POST",headers:this.requestHeaders(),body:JSON.stringify(this.makeBatchRequest(t))})}catch(o){return{kind:"retryable",reason:De(o)}}if(Re.has(n.status))return{kind:"auth",status:n.status};if(_e.has(n.status))return{kind:"retryable",reason:`HTTP ${n.status}`};if(!n.ok)return{kind:"permanent",reason:`HTTP ${n.status}`};let r=await Pe(n);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let i=this.classifyRejectedEvents(t,r.rejected);return i.retryable.length===0&&i.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:i.retryable,permanent:i.permanent}}makeBatchRequest(t){return{sentAt:this.now().toISOString(),source:{sdk:q,version:this.sdkVersion??"0.0.0"},events:t}}classifyRejectedEvents(t,n){let r=new Map(t.map(a=>[a.id,a])),i=[],o=[];for(let a of n){let s=r.get(a.eventId);if(s){if(be(a)){i.push(s);continue}o.push(s)}}return{retryable:i,permanent:o}}backoffDelayMs(t){let n=this.retryBaseDelayMs*2**t;return Math.min(n,this.retryMaxDelayMs)}stopTransportForAuthFailure(t,n){this.isStopped=!0;let r=this.buffer.length;this.buffer.splice(0,r),this.logger.error("[Waniwani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)",t,n+r)}};function be(e){if(e.retryable===!0)return!0;let t=e.code.toLowerCase();return t.includes("timeout")||t.includes("temporary")||t.includes("unavailable")||t.includes("rate_limit")||t.includes("transient")||t.includes("server")}async function Pe(e){let t=await e.text();if(t)try{return JSON.parse(t)}catch{return}}function Ae(e,t){let n=e.endsWith("/")?e:`${e}/`,r=t.startsWith("/")?t.slice(1):t;return`${n}${r}`}function De(e){return e instanceof Error?e.message:String(e)}function Me(e){let{apiUrl:t,endpointPath:n}=Oe(e.endpoint),r=S({apiUrl:t,apiKey:e.token,endpointPath:n,flushIntervalMs:e.flushIntervalMs}),i,o=s=>typeof s=="function"?s():s,a=async s=>{let c=e.identity?.()??{},f=o(e.channelId),d=f===void 0?s.properties:{channelId:f,...s.properties},u=I({sessionId:c.sessionId,visitorId:c.visitorId,externalUserId:c.externalUserId??i,traceId:c.traceId,...s,properties:d,metadata:{...e.metadata,...s.metadata}},{source:o(e.source)});return r.enqueue(u),{eventId:u.id}};return{track:Object.assign(a,k(a)),identify(s,c){return i=s,a({event:"user.identified",externalUserId:s,properties:c})},flush:()=>r.flush(),shutdown:s=>r.shutdown(s)}}function Oe(e){let t=new URL(e);return{apiUrl:t.origin,endpointPath:`${t.pathname}${t.search}`}}var Fe="@waniwani/sdk",Ue="/api/mcp/modules/documents/extract";function Be(e,t){try{let n=JSON.parse(e);if(n!==null&&typeof n=="object"){let r="message"in n&&typeof n.message=="string"?n.message:"",i="detail"in n&&typeof n.detail=="string"?n.detail:"";if(r&&i)return`${r}: ${i}`;if(r)return r;if(i)return i}}catch{}return e||`Documents API error: HTTP ${t}`}function Ke(e){return"documentId"in e&&typeof e.documentId=="string"}function Le(e){return Ke(e)?{documentId:e.documentId}:{url:e.url,filename:e.filename}}function z(e){let{apiUrl:t,apiKey:n}=e;return{async extract(r){if(!n)throw new Error("WANIWANI_API_KEY is not set");let{toJSONSchema:i}=await import("zod"),o=await fetch(`${t.replace(/\/$/,"")}${Ue}`,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json","X-WaniWani-SDK":Fe},body:JSON.stringify({...Le(r),schema:i(r.schema),pages:r.pages,sessionId:r.sessionId,correlationId:r.correlationId})});if(!o.ok){let u=await o.text().catch(()=>"");throw new m(Be(u,o.status),o.status)}let a=await o.json(),{fields:s,pageCount:c,pageConfidence:f,documentId:d}=a.data;return{fields:r.schema.parse(s),pageCount:c,pageConfidence:f??null,documentId:d}}}}import{AsyncLocalStorage as We}from"async_hooks";var $=globalThis,w=$.__waniwaniKbRetrievalStore;w||(w=new We,$.__waniwaniKbRetrievalStore=w);var je=w;function H(e){try{je.getStore()?.searches.push(e)}catch{}}var Ne="@waniwani/sdk";function X(e){let{apiUrl:t,apiKey:n}=e;function r(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}async function i(o,a,s){let c=r(),f=`${t.replace(/\/$/,"")}${a}`,d={Authorization:`Bearer ${c}`,"X-WaniWani-SDK":Ne},u={method:o,headers:d};s!==void 0&&(d["Content-Type"]="application/json",u.body=JSON.stringify(s));let g=await fetch(f,u);if(!g.ok){let J=await g.text().catch(()=>"");throw new m(J||`KB API error: HTTP ${g.status}`,g.status)}return(await g.json()).data}return{async ingest(o){return i("POST","/api/mcp/kb/ingest",{files:o})},async search(o,a){let s=await i("POST","/api/mcp/kb/search",{query:o,...a});return H({query:o,resultCount:s.length,results:s.map(c=>({source:c.source,heading:c.heading,score:c.score}))}),s},async sources(){return i("GET","/api/mcp/kb/sources")}}}function G(e){let{apiUrl:t,apiKey:n,tracking:r}=e;function i(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}let o=n?S({apiUrl:t,apiKey:n,endpointPath:r.endpointPath,flushIntervalMs:r.flushIntervalMs,maxBatchSize:r.maxBatchSize,maxBufferSize:r.maxBufferSize,maxRetries:r.maxRetries,retryBaseDelayMs:r.retryBaseDelayMs,retryMaxDelayMs:r.retryMaxDelayMs,shutdownTimeoutMs:r.shutdownTimeoutMs}):void 0;function a(d){i();let u=I(d);return!u.correlation.sessionId&&!u.correlation.externalUserId&&!u.correlation.visitorId&&console.warn(`[waniwani] event "${u.name}" has no sessionId, externalUserId, or visitorId; the ingest API requires one and will reject it.`),o?.enqueue(u),{eventId:u.id}}let s=async d=>a(d),c=Object.assign(s,k(s)),f={async identify(d,u,g){i();let x=I({event:"user.identified",externalUserId:d,properties:u,meta:g});return o?.enqueue(x),{eventId:x.id}},track:c,async flush(){i(),await o?.flush()},async shutdown(d){return i(),await o?.shutdown({timeoutMs:d?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return o&&Ve(f,r.shutdownTimeoutMs),f}function Ve(e,t){if(typeof process>"u"||typeof process.once!="function"||typeof process.on!="function")return;let n=()=>{e.shutdown({timeoutMs:t})};process.once("beforeExit",n),process.once("SIGINT",n),process.once("SIGTERM",n)}var Q="https://app.waniwani.ai";function Ye(e){let n=e??A()??M(),r=n?.apiUrl??process.env.WANIWANI_API_URL??Q,i=n?.apiKey??process.env.WANIWANI_API_KEY,o={endpointPath:n?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:n?.tracking?.flushIntervalMs??1e3,maxBatchSize:n?.tracking?.maxBatchSize??20,maxBufferSize:n?.tracking?.maxBufferSize??1e3,maxRetries:n?.tracking?.maxRetries??3,retryBaseDelayMs:n?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:n?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:n?.tracking?.shutdownTimeoutMs??2e3},a={apiUrl:r,apiKey:i,tracking:o},s=G(a),c=X(a),f=z(a);return{...s,kb:c,documents:f,_config:a}}export{U as ERROR_CAUSES,O as EVENT_TYPES,F as SESSION_ERROR_CODES,m as WaniWaniError,Me as createFrontendClient,de as defineConfig,P as readAttachedDocuments,Ye as waniwani};
1
+ var te=["documentId","document_id"],ne=["filename","file_name","fileName","name"],re=["mediaType","media_type","mimeType","mime_type","contentType","content_type"],ie="document",oe="application/octet-stream";function y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function C(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.length>0)return r}}function se(e){if(!y(e))return;let t=C(e,te);if(t)return{documentId:t,filename:C(e,ne)??ie,mediaType:C(e,re)??oe}}function P(e){let t=[],n=new Set;for(let r of Array.isArray(e)?e:[e]){let i=se(r);i&&!n.has(i.documentId)&&(n.add(i.documentId),t.push(i))}return t}function ae(e){if(!Array.isArray(e))return[];let t=[...e].reverse().find(n=>y(n)&&n.role==="user");return!y(t)||!Array.isArray(t.parts)?[]:P(t.parts.flatMap(n=>y(n)&&y(n.data)?[n,n.data]:[n]))}function A(e){let t=y(e)?e:void 0,n=P(t?t.documents:e);return n.length>0?n:ae(t?t.messages:e)}var h=class extends Error{constructor(n,r){super(n);this.status=r;this.name="WaniWaniError"}};import{existsSync as ce,readFileSync as ue}from"fs";import{resolve as de}from"path";var pe="waniwani.json",T;function D(){if(T!==void 0)return T;try{let e=de(process.cwd(),pe);if(!ce(e))return T=null,null;let t=ue(e,"utf-8");return T=JSON.parse(t),T}catch{return T=null,null}}var M="__waniwani_config__";function le(e){return globalThis[M]=e,e}function O(){return globalThis[M]}var F=["session.started","page.viewed","tool.called","link.clicked","widget_render","user.identified","price_shown","prices_compared","option_selected","lead_qualified","converted","session.error"],U=["agent_failed","tool_failed","upstream_failed"],B=["timeout","rate_limited","upstream_4xx","upstream_5xx","network","invalid_output","flow_dead_end","flow_unknown_node","flow_loop","unknown"];function v(e,t){for(let n of t){let r=e[n];if(typeof r=="string"&&r.length>0)return r}}var fe=["waniwani/sessionId","openai/sessionId","openai/session","sessionId","conversationId","mcp-session-id"],me=["waniwani/requestId","openai/requestId","requestId","mcp/requestId"],ge=["waniwani/traceId","openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"],he=["waniwani/userId","openai/userId","externalUserId","userId","actorId"],ye=["waniwani/visitorId","visitorId"],Te=["correlationId","openai/requestId"];function K(e){return e?v(e,fe):void 0}function W(e){return e?v(e,me):void 0}function j(e){return e?v(e,ge):void 0}function L(e){return e?v(e,he):void 0}function N(e){return e?v(e,ye):void 0}function V(e){return e?v(e,Te):void 0}var ve=[{key:"openai/sessionId",source:"chatgpt"},{key:"openai/session",source:"chatgpt"}],Ee=[{needle:"claude",source:"claude"},{needle:"chatgpt",source:"chatgpt"},{needle:"openai",source:"chatgpt"},{needle:"gemini",source:"gemini"}];function Y(e,t){if(e){let r=e["waniwani/source"];if(typeof r=="string"&&r.length>0)return r;for(let{key:i,source:o}of ve){let a=e[i];if(typeof a=="string"&&a.length>0)return o}}let n=t?.name;if(typeof n=="string"&&n.length>0){let r=n.toLowerCase();for(let{needle:i,source:o}of Ee)if(r.includes(i))return o}}var Ie="@waniwani/sdk";function E(e,t={}){let n=t.now??(()=>new Date),r=t.generateId??q,i=we(e),o=I(e.meta),a=I(e.metadata),s=xe(e,o),c=g(e.eventId)??r(),f=Re(e.timestamp,n),d=g(e.source)??Y(o)??t.source??Ie,u=_(e)?{...e}:void 0,m={...a};return Object.keys(o).length>0&&(m.meta=o),u&&(m.rawLegacy=u),{id:c,type:"mcp.event",name:i,source:d,timestamp:f,correlation:s,properties:Se(e,i),metadata:m,rawLegacy:u}}function q(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function Se(e,t){if(!_(e))return I(e.properties);let n=ke(e,t),r=I(e.properties);return{...n,...r}}function ke(e,t){switch(t){case"tool.called":{let n={};return g(e.toolName)&&(n.name=e.toolName),g(e.toolType)&&(n.type=e.toolType),n}case"link.clicked":{let n={};return g(e.linkUrl)&&(n.url=e.linkUrl),n}default:return{}}}function we(e){return _(e)?e.eventType:e.event}function xe(e,t){let n=g(e.requestId)??W(t),r=g(e.sessionId)??K(t),i=g(e.traceId)??j(t),o=g(e.externalUserId)??L(t),a=g(e.visitorId)??N(t),s=g(e.correlationId)??V(t)??n,c={};return r&&(c.sessionId=r),i&&(c.traceId=i),n&&(c.requestId=n),s&&(c.correlationId=s),o&&(c.externalUserId=o),a&&(c.visitorId=a),c}function Re(e,t){if(e instanceof Date)return e.toISOString();if(typeof e=="string"){let n=new Date(e);if(!Number.isNaN(n.getTime()))return n.toISOString()}return t().toISOString()}function I(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function g(e){if(typeof e=="string"&&e.trim().length!==0)return e}function _(e){return"eventType"in e}function S(e){return{priceShown:({amount:t,currency:n,itemId:r,label:i,...o})=>e({event:"price_shown",properties:{amount:t,currency:n,itemId:r,label:i},...o}),pricesCompared:({options:t,...n})=>e({event:"prices_compared",properties:{options:t},...n}),optionSelected:({id:t,amount:n,currency:r,...i})=>e({event:"option_selected",properties:{id:t,amount:n,currency:r},...i}),leadQualified:t=>{let{externalId:n,email:r,name:i,...o}=t??{};return e({event:"lead_qualified",properties:{externalId:n,email:r,name:i},...o})},converted:({amount:t,currency:n,occurredAt:r,...i})=>e({event:"converted",properties:{amount:t,currency:n,occurredAt:r},...i})}}var Ce="/api/mcp/events/v2/batch";var z="@waniwani/sdk";var _e=new Set([401,403]),be=new Set([408,425,429,500,502,503,504]),Pe=(e,t)=>globalThis.fetch(e,t);function k(e){return new b(e)}var b=class{endpointUrl;flushIntervalMs;maxBatchSize;maxBufferSize;maxRetries;retryBaseDelayMs;retryMaxDelayMs;shutdownTimeoutMs;sdkVersion;fetchFn;logger;now;sleep;apiKey;buffer=[];flushTimer;flushScheduled=!1;flushScheduledTimer;flushInFlight;inFlightCount=0;isStopped=!1;isShuttingDown=!1;constructor(t){this.endpointUrl=Me(t.apiUrl,t.endpointPath??Ce),this.flushIntervalMs=t.flushIntervalMs??1e3,this.maxBatchSize=t.maxBatchSize??20,this.maxBufferSize=t.maxBufferSize??1e3,this.maxRetries=t.maxRetries??3,this.retryBaseDelayMs=t.retryBaseDelayMs??200,this.retryMaxDelayMs=t.retryMaxDelayMs??2e3,this.shutdownTimeoutMs=t.shutdownTimeoutMs??2e3,this.fetchFn=t.fetchFn??Pe,this.logger=t.logger??console,this.now=t.now??(()=>new Date),this.sleep=t.sleep??(n=>new Promise(r=>setTimeout(r,n))),this.apiKey=t.apiKey,this.sdkVersion=t.sdkVersion,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>{this.flush()},this.flushIntervalMs)),this.registerBrowserTeardown()}enqueue(t){if(this.isStopped||this.isShuttingDown){this.logger.warn("[Waniwani] Tracking transport is stopped, dropping event %s",t.id);return}if(this.buffer.length>=this.maxBufferSize){let n=this.buffer.length-this.maxBufferSize+1;this.buffer.splice(0,n),this.logger.warn("[Waniwani] Tracking buffer overflow, dropped %d oldest event(s)",n)}if(this.buffer.push(t),this.buffer.length>=this.maxBatchSize){this.flush();return}this.scheduleMicroFlush()}pendingEvents(){return this.buffer.length+this.inFlightCount}async flush(){return this.flushInFlight?this.flushInFlight:(this.flushInFlight=this.flushLoop().finally(()=>{this.flushInFlight=void 0}),this.flushInFlight)}async shutdown(t){this.isShuttingDown=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),this.flushScheduledTimer&&(clearTimeout(this.flushScheduledTimer),this.flushScheduledTimer=void 0,this.flushScheduled=!1);let n=t?.timeoutMs??this.shutdownTimeoutMs,r=this.flush();if(!Number.isFinite(n)||n<=0)return await r,this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()};let i=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(n).then(()=>i)])===i?(this.isStopped=!0,{timedOut:!0,pendingEvents:this.pendingEvents()}):(this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()})}registerBrowserTeardown(){if(typeof document>"u"||typeof window>"u")return;let t=()=>{document.visibilityState==="hidden"&&this.keepaliveFlush()};document.addEventListener("visibilitychange",t),window.addEventListener("pagehide",()=>this.keepaliveFlush())}keepaliveFlush(){if(this.isStopped||this.buffer.length===0)return;let t=this.buffer.splice(0,this.buffer.length);this.sendKeepaliveChunk(t)}sendKeepaliveChunk(t){let n=JSON.stringify(this.makeBatchRequest(t));if(n.length>6e4&&t.length>1){let r=Math.ceil(t.length/2);this.sendKeepaliveChunk(t.slice(0,r)),this.sendKeepaliveChunk(t.slice(r));return}try{this.fetchFn(this.endpointUrl,{method:"POST",headers:this.requestHeaders(),body:n,keepalive:!0}).catch(()=>{})}catch{}}requestHeaders(){let t={"Content-Type":"application/json","X-WaniWani-SDK":z};return this.apiKey&&(t.Authorization=`Bearer ${this.apiKey}`),t}scheduleMicroFlush(){this.flushScheduled||(this.flushScheduled=!0,this.flushScheduledTimer=setTimeout(()=>{this.flushScheduledTimer=void 0,this.flushScheduled=!1,this.flush()},0))}async flushLoop(){for(;this.buffer.length>0&&!this.isStopped;){let t=this.buffer.splice(0,this.maxBatchSize);await this.sendBatchWithRetry(t)}}async sendBatchWithRetry(t){let n=0,r=t;for(;r.length>0&&!this.isStopped;){this.inFlightCount=r.length;let i=await this.sendBatchOnce(r);switch(this.inFlightCount=0,i.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(i.status,r.length);return;case"permanent":this.logger.error("[Waniwani] Dropping %d event(s) after permanent failure: %s",r.length,i.reason);return;case"retryable":if(n>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d event(s) after retry exhaustion: %s",r.length,i.reason);return}await this.sleep(this.backoffDelayMs(n)),n+=1;continue;case"partial":if(i.permanent.length>0&&this.logger.error("[Waniwani] Dropping %d event(s) rejected as permanent",i.permanent.length),i.retryable.length===0)return;if(n>=this.maxRetries){this.logger.error("[Waniwani] Dropping %d retryable event(s) after retry exhaustion",i.retryable.length);return}r=i.retryable,await this.sleep(this.backoffDelayMs(n)),n+=1;continue}}}async sendBatchOnce(t){let n;try{n=await this.fetchFn(this.endpointUrl,{method:"POST",headers:this.requestHeaders(),body:JSON.stringify(this.makeBatchRequest(t))})}catch(o){return{kind:"retryable",reason:Oe(o)}}if(_e.has(n.status))return{kind:"auth",status:n.status};if(be.has(n.status))return{kind:"retryable",reason:`HTTP ${n.status}`};if(!n.ok)return{kind:"permanent",reason:`HTTP ${n.status}`};let r=await De(n);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let i=this.classifyRejectedEvents(t,r.rejected);return i.retryable.length===0&&i.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:i.retryable,permanent:i.permanent}}makeBatchRequest(t){return{sentAt:this.now().toISOString(),source:{sdk:z,version:this.sdkVersion??"0.0.0"},events:t}}classifyRejectedEvents(t,n){let r=new Map(t.map(a=>[a.id,a])),i=[],o=[];for(let a of n){let s=r.get(a.eventId);if(s){if(Ae(a)){i.push(s);continue}o.push(s)}}return{retryable:i,permanent:o}}backoffDelayMs(t){let n=this.retryBaseDelayMs*2**t;return Math.min(n,this.retryMaxDelayMs)}stopTransportForAuthFailure(t,n){this.isStopped=!0;let r=this.buffer.length;this.buffer.splice(0,r),this.logger.error("[Waniwani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)",t,n+r)}};function Ae(e){if(e.retryable===!0)return!0;let t=e.code.toLowerCase();return t.includes("timeout")||t.includes("temporary")||t.includes("unavailable")||t.includes("rate_limit")||t.includes("transient")||t.includes("server")}async function De(e){let t=await e.text();if(t)try{return JSON.parse(t)}catch{return}}function Me(e,t){let n=e.endsWith("/")?e:`${e}/`,r=t.startsWith("/")?t.slice(1):t;return`${n}${r}`}function Oe(e){return e instanceof Error?e.message:String(e)}function Fe(e){let{apiUrl:t,endpointPath:n}=Ue(e.endpoint),r=k({apiUrl:t,apiKey:e.token,endpointPath:n,flushIntervalMs:e.flushIntervalMs}),i,o=s=>typeof s=="function"?s():s,a=async s=>{let c=e.identity?.()??{},f=o(e.channelId),d=f===void 0?s.properties:{channelId:f,...s.properties},u=E({sessionId:c.sessionId,visitorId:c.visitorId,externalUserId:c.externalUserId??i,traceId:c.traceId,...s,properties:d,metadata:{...e.metadata,...s.metadata}},{source:o(e.source)});return r.enqueue(u),{eventId:u.id}};return{track:Object.assign(a,S(a)),identify(s,c){return i=s,a({event:"user.identified",externalUserId:s,properties:c})},flush:()=>r.flush(),shutdown:s=>r.shutdown(s)}}function Ue(e){let t=new URL(e);return{apiUrl:t.origin,endpointPath:`${t.pathname}${t.search}`}}function w(e,t,n){try{let r=JSON.parse(e);if(r!==null&&typeof r=="object"){let i="message"in r&&typeof r.message=="string"?r.message:"",o="detail"in r&&typeof r.detail=="string"?r.detail:"";if(i&&o)return`${i}: ${o}`;if(i)return i;if(o)return o}}catch{}return e||`${n} error: HTTP ${t}`}var Be="@waniwani/sdk",Ke="/api/mcp/modules/documents/extract";function We(e){return"documentId"in e&&typeof e.documentId=="string"}function je(e){return We(e)?{documentId:e.documentId}:{url:e.url,filename:e.filename}}function $(e){let{apiUrl:t,apiKey:n}=e;return{async extract(r){if(!n)throw new Error("WANIWANI_API_KEY is not set");let{toJSONSchema:i}=await import("zod"),o=await fetch(`${t.replace(/\/$/,"")}${Ke}`,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json","X-WaniWani-SDK":Be},body:JSON.stringify({...je(r),schema:i(r.schema),pages:r.pages,sessionId:r.sessionId,correlationId:r.correlationId})});if(!o.ok){let u=await o.text().catch(()=>"");throw new h(w(u,o.status,"Documents API"),o.status)}let a=await o.json(),{fields:s,pageCount:c,pageConfidence:f,documentId:d}=a.data;return{fields:r.schema.parse(s),pageCount:c,pageConfidence:f??null,documentId:d}}}}var Le="@waniwani/sdk",Ne="/api/mcp/modules/email/send";function H(e){let{apiUrl:t,apiKey:n}=e;return{async send(r){if(!n)throw new Error("WANIWANI_API_KEY is not set");let i=await fetch(`${t.replace(/\/$/,"")}${Ne}`,{method:"POST",headers:{Authorization:`Bearer ${n}`,"Content-Type":"application/json","X-WaniWani-SDK":Le},body:JSON.stringify({to:r.to,cc:r.cc,bcc:r.bcc,subject:r.subject,html:r.html,text:r.text,replyTo:r.replyTo,sessionId:r.sessionId})});if(!i.ok){let a=await i.text().catch(()=>"");throw new h(w(a,i.status,"Email API"),i.status)}return{id:(await i.json()).data.id}}}}import{AsyncLocalStorage as Ve}from"async_hooks";var X=globalThis,x=X.__waniwaniKbRetrievalStore;x||(x=new Ve,X.__waniwaniKbRetrievalStore=x);var Ye=x;function G(e){try{Ye.getStore()?.searches.push(e)}catch{}}var qe="@waniwani/sdk";function Q(e){let{apiUrl:t,apiKey:n}=e;function r(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}async function i(o,a,s){let c=r(),f=`${t.replace(/\/$/,"")}${a}`,d={Authorization:`Bearer ${c}`,"X-WaniWani-SDK":qe},u={method:o,headers:d};s!==void 0&&(d["Content-Type"]="application/json",u.body=JSON.stringify(s));let m=await fetch(f,u);if(!m.ok){let ee=await m.text().catch(()=>"");throw new h(ee||`KB API error: HTTP ${m.status}`,m.status)}return(await m.json()).data}return{async ingest(o){return i("POST","/api/mcp/kb/ingest",{files:o})},async search(o,a){let s=await i("POST","/api/mcp/kb/search",{query:o,...a});return G({query:o,resultCount:s.length,results:s.map(c=>({source:c.source,heading:c.heading,score:c.score}))}),s},async sources(){return i("GET","/api/mcp/kb/sources")}}}function J(e){let{apiUrl:t,apiKey:n,tracking:r}=e;function i(){if(!n)throw new Error("WANIWANI_API_KEY is not set");return n}let o=n?k({apiUrl:t,apiKey:n,endpointPath:r.endpointPath,flushIntervalMs:r.flushIntervalMs,maxBatchSize:r.maxBatchSize,maxBufferSize:r.maxBufferSize,maxRetries:r.maxRetries,retryBaseDelayMs:r.retryBaseDelayMs,retryMaxDelayMs:r.retryMaxDelayMs,shutdownTimeoutMs:r.shutdownTimeoutMs}):void 0;function a(d){i();let u=E(d);return!u.correlation.sessionId&&!u.correlation.externalUserId&&!u.correlation.visitorId&&console.warn(`[waniwani] event "${u.name}" has no sessionId, externalUserId, or visitorId; the ingest API requires one and will reject it.`),o?.enqueue(u),{eventId:u.id}}let s=async d=>a(d),c=Object.assign(s,S(s)),f={async identify(d,u,m){i();let R=E({event:"user.identified",externalUserId:d,properties:u,meta:m});return o?.enqueue(R),{eventId:R.id}},track:c,async flush(){i(),await o?.flush()},async shutdown(d){return i(),await o?.shutdown({timeoutMs:d?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return o&&ze(f,r.shutdownTimeoutMs),f}function ze(e,t){if(typeof process>"u"||typeof process.once!="function"||typeof process.on!="function")return;let n=()=>{e.shutdown({timeoutMs:t})};process.once("beforeExit",n),process.once("SIGINT",n),process.once("SIGTERM",n)}var Z="https://app.waniwani.ai";function $e(e){let n=e??D()??O(),r=n?.apiUrl??process.env.WANIWANI_API_URL??Z,i=n?.apiKey??process.env.WANIWANI_API_KEY,o={endpointPath:n?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:n?.tracking?.flushIntervalMs??1e3,maxBatchSize:n?.tracking?.maxBatchSize??20,maxBufferSize:n?.tracking?.maxBufferSize??1e3,maxRetries:n?.tracking?.maxRetries??3,retryBaseDelayMs:n?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:n?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:n?.tracking?.shutdownTimeoutMs??2e3},a={apiUrl:r,apiKey:i,tracking:o},s=J(a),c=Q(a),f=$(a),d=H(a);return{...s,kb:c,documents:f,email:d,_config:a}}export{B as ERROR_CAUSES,F as EVENT_TYPES,U as SESSION_ERROR_CODES,h as WaniWaniError,Fe as createFrontendClient,le as defineConfig,A as readAttachedDocuments,$e as waniwani};
2
2
  //# sourceMappingURL=index.js.map