@matterfact/embed 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +122 -0
  2. package/dist/{chunk-6EM7T2JV.js → chunk-AANODHBV.js} +217 -15
  3. package/dist/chunk-AANODHBV.js.map +1 -0
  4. package/dist/{chunk-URGQBG4I.js → chunk-CTZEDOH7.js} +218 -17
  5. package/dist/chunk-CTZEDOH7.js.map +1 -0
  6. package/dist/{chunk-BKKXHSYU.js → chunk-JO3GWFMJ.js} +2 -2
  7. package/dist/chunk-SKJFF7RD.js +2 -0
  8. package/dist/chunk-Y7I25VHL.js +3 -0
  9. package/dist/chunk-Y7I25VHL.js.map +7 -0
  10. package/dist/context-FR7VFENN.js +3 -0
  11. package/dist/{context-ACFBWIFH.js.map → context-FR7VFENN.js.map} +1 -1
  12. package/dist/{context-U2HJJN2S.js → context-IK5MECUW.js} +4 -2
  13. package/dist/embed.js +1 -1
  14. package/dist/embed.js.map +3 -3
  15. package/dist/index.cjs +247 -13
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +88 -1
  18. package/dist/index.d.ts +88 -1
  19. package/dist/index.js +25 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/react.cjs +302 -15
  22. package/dist/react.cjs.map +1 -1
  23. package/dist/react.d.cts +174 -2
  24. package/dist/react.d.ts +174 -2
  25. package/dist/react.js +79 -3
  26. package/dist/react.js.map +1 -1
  27. package/dist/{snapshot-4GXT6PKZ.js → snapshot-2V5SDSH2.js} +2 -2
  28. package/dist/{snapshot-Y75SCGCM.js → snapshot-Y5BF2UJR.js} +3 -3
  29. package/dist/{snapshot-Y75SCGCM.js.map → snapshot-Y5BF2UJR.js.map} +1 -1
  30. package/examples/embed-demo/README.md +22 -0
  31. package/examples/embed-demo/src/App.tsx +105 -2
  32. package/examples/embed-demo/src/styles.css +29 -0
  33. package/package.json +1 -1
  34. package/dist/chunk-6EM7T2JV.js.map +0 -1
  35. package/dist/chunk-FEXG4LQJ.js +0 -3
  36. package/dist/chunk-FEXG4LQJ.js.map +0 -7
  37. package/dist/chunk-NWNMS34P.js +0 -2
  38. package/dist/chunk-URGQBG4I.js.map +0 -1
  39. package/dist/context-ACFBWIFH.js +0 -3
  40. /package/dist/{chunk-BKKXHSYU.js.map → chunk-JO3GWFMJ.js.map} +0 -0
  41. /package/dist/{chunk-NWNMS34P.js.map → chunk-SKJFF7RD.js.map} +0 -0
  42. /package/dist/{context-U2HJJN2S.js.map → context-IK5MECUW.js.map} +0 -0
  43. /package/dist/{snapshot-4GXT6PKZ.js.map → snapshot-2V5SDSH2.js.map} +0 -0
package/dist/react.d.cts CHANGED
@@ -61,6 +61,76 @@ interface SiteMapEntry {
61
61
  content?: ContentClass;
62
62
  current?: boolean;
63
63
  }
64
+ /**
65
+ * A DECLARED artifact grant — the `<MatterfactAgent artifacts>` prop (or
66
+ * `window.matterfact.artifacts`). Same wire shape as a grant discovered by scanning
67
+ * co-embedded `<MatterfactArtifact>` iframes, plus an optional `label`; it feeds the
68
+ * SAME `host.artifactGrants` channel. This is how a host makes a board reachable from
69
+ * a route where its iframe ISN'T mounted, without hidden zero-size iframes smuggling
70
+ * the token into the DOM. The token stays in the grant channel, never in message text.
71
+ */
72
+ interface DeclaredArtifact {
73
+ slug: string;
74
+ owner: string;
75
+ token: string;
76
+ label?: string;
77
+ }
78
+ /**
79
+ * One host-tool lifecycle event, surfaced to the host via `onToolEvent` (and the dev
80
+ * panel) — the host's own telemetry hook. Fired loader-side; a throwing or slow
81
+ * callback here NEVER blocks or breaks the call path.
82
+ *
83
+ * - `advertise` — the merged tool list changed (fired with the new set's names).
84
+ * - `call` — a tool call is about to run (`args` present).
85
+ * - `result` — the call settled (`ok`/`error`/`ms`).
86
+ */
87
+ interface ToolEvent {
88
+ phase: 'advertise' | 'call' | 'result';
89
+ name: string;
90
+ toolClass: 'hoist' | 'mf' | 'app';
91
+ args?: unknown;
92
+ ok?: boolean;
93
+ error?: string;
94
+ ms?: number;
95
+ }
96
+ /**
97
+ * One embed lifecycle event, surfaced to the host via `onEvent` — the host's single
98
+ * telemetry hook. Fired loader-side (lifecycle) and widget-side (chat); a throwing or
99
+ * slow callback NEVER blocks or breaks the mount/call/auth/run path.
100
+ *
101
+ * - `ready` — the widget connected (protocol handshake done).
102
+ * - `open`/`close` — the corner widget was opened/closed. (Inline has no open event —
103
+ * the host's own panel is the open/close control.)
104
+ * - `navigate` — the agent deeplinked the host to one of its own routes.
105
+ * - `auth` — host-auth passthrough: `required` (widget asked), `granted` (a
106
+ * token was handed over), `failed` (no provider result / the provider threw).
107
+ * - `error` — the widget failed to mount / load.
108
+ * - `chat` — a chat-turn moment. Content-free: phase + opaque chatId only.
109
+ * - `tool` — a host-tool lifecycle event (the `ToolEvent` shape, tagged). Also
110
+ * still delivered untagged to the legacy `onToolEvent` hook for back-compat.
111
+ */
112
+ type MatterfactEvent = {
113
+ type: 'ready';
114
+ } | {
115
+ type: 'open';
116
+ } | {
117
+ type: 'close';
118
+ } | {
119
+ type: 'navigate';
120
+ href: string;
121
+ } | {
122
+ type: 'auth';
123
+ phase: 'required' | 'granted' | 'failed';
124
+ } | {
125
+ type: 'error';
126
+ message: string;
127
+ } | {
128
+ type: 'chat';
129
+ phase: 'message' | 'response-start' | 'response-end';
130
+ chatId?: string;
131
+ } | ({
132
+ type: 'tool';
133
+ } & ToolEvent);
64
134
 
65
135
  /**
66
136
  * The Hoist adapter — a pure transform from a `HoistRuntime` (already read out of
@@ -83,6 +153,64 @@ interface HoistActionsConfig {
83
153
  navigate: 'off' | 'confirm' | 'auto';
84
154
  }
85
155
 
156
+ /**
157
+ * The host-tool registry — the ONE place the three tool classes are merged into the
158
+ * single list the widget advertises, and the ONE execution path for calling any of
159
+ * them.
160
+ *
161
+ * Three classes, distinguished by who owns the contract and who runs the code:
162
+ *
163
+ * - `hoist.*` matterfact-defined, matterfact-implemented. The loader adapter runs
164
+ * them (today: `hoist.navigate`). Gated on `window.XH` + the host's
165
+ * `hoist.actions` policy. See `hoist-runtime.ts`.
166
+ * - `mf.*` matterfact-defined, HOST-implemented. WE fix the name, schema and
167
+ * agent semantics (the description is authored HERE, not per
168
+ * integration); the host supplies the function via `resolve.*`. Today:
169
+ * `mf.resolveDocument`, `mf.resolveArtifact`.
170
+ * - `app.*` host-defined, host-implemented. Anything the host advertises via
171
+ * `tools`. Normalised into the `app.` namespace so it can never shadow
172
+ * ours.
173
+ *
174
+ * Everything downstream (the widget's confirm gate, the backend dispatcher) is already
175
+ * generic over "a tool with a name/schema/confirm and a call round-trip" — this module
176
+ * is purely the loader-side composition + dispatch, so `hoist-runtime.ts` stays one
177
+ * adapter rather than the whole surface.
178
+ *
179
+ * SECURITY: `mf.*`/`app.*` handlers are HOST code. Their results and error strings pass
180
+ * through `redact()` before leaving the page, exactly like the hoist adapter's; a
181
+ * handler that throws or hangs becomes a normal `{ok:false}` result, never an exception
182
+ * into `context.ts`. And the SERVER decides which of these an app may actually expose
183
+ * (the `host_tools` policy) — advertising here is necessary but not sufficient.
184
+ */
185
+
186
+ /** A host-supplied resolver: matterfact fixes the contract, the host implements it. */
187
+ interface HostResolvers {
188
+ document?: (args: {
189
+ doctype: string;
190
+ key: string;
191
+ }) => Promise<{
192
+ id: string;
193
+ label: string;
194
+ href?: string;
195
+ }>;
196
+ artifact?: (args: {
197
+ slug: string;
198
+ }) => Promise<{
199
+ id: string;
200
+ owner: string;
201
+ token: string;
202
+ label: string;
203
+ }>;
204
+ }
205
+ /** A host-defined tool (the `tools` prop): a WebMCP-shaped descriptor plus its handler. */
206
+ interface HostToolDef {
207
+ name: string;
208
+ description: string;
209
+ inputSchema?: Record<string, unknown>;
210
+ confirm?: 'auto' | 'required';
211
+ handler: (args: Record<string, unknown>) => unknown | Promise<unknown>;
212
+ }
213
+
86
214
  interface MatterfactAgentProps {
87
215
  /** Publishable key (`pk_…`) identifying this embed app. Public, origin-scoped. */
88
216
  publishableKey: string;
@@ -156,8 +284,52 @@ interface MatterfactAgentProps {
156
284
  * on load and on navigation.
157
285
  */
158
286
  sitemap?: SiteMapEntry[];
287
+ /**
288
+ * Declared artifact grants — make a board reachable from ANY route, even ones where
289
+ * its `<MatterfactArtifact>` iframe isn't mounted. Replaces the pattern of mounting
290
+ * hidden zero-size iframes purely to smuggle a read token into the DOM. The token
291
+ * rides the grant channel, never message text. Sugar over `window.matterfact.artifacts`.
292
+ */
293
+ artifacts?: DeclaredArtifact[];
294
+ /**
295
+ * matterfact-defined resolver contracts the HOST implements. matterfact fixes the tool
296
+ * name, schema and agent semantics (so an integration spends no instruction budget on
297
+ * them); you supply the function.
298
+ *
299
+ * - `document({doctype, key})` → `{id, label, href?}` — resolve an off-page matterfact
300
+ * document (dossier/report) to its id, e.g. via your own content-location lookup.
301
+ * - `artifact({slug})` → `{id, owner, token, label}` — resolve an artifact to a read grant.
302
+ *
303
+ * The agent calls these when the site map shows the content lives on another route, and
304
+ * renders the result as an `<MFRef>` chip. Sugar over `window.matterfact.resolve`.
305
+ */
306
+ resolve?: HostResolvers;
307
+ /**
308
+ * Host-defined tools (`app.*`): your own functions, offered to the agent WebMCP-style.
309
+ * Each is `{name, description, inputSchema?, confirm?, handler}`; the name is normalised
310
+ * into the `app.` namespace. A tool is only actually exposed if this app's server-side
311
+ * policy allows it. Sugar over `window.matterfact.tools`.
312
+ */
313
+ tools?: HostToolDef[];
314
+ /**
315
+ * Host telemetry hook: fired on every host-tool advertise/call/result so you can pipe
316
+ * calls into your own observability. Never blocks or breaks the call path — a throw here
317
+ * is swallowed. Sugar over `window.matterfact.onToolEvent`.
318
+ *
319
+ * NOTE: this is now a SUBSET of `onEvent` (the tool events only), kept for back-compat.
320
+ * Prefer `onEvent` for new integrations — it delivers these same tool events (tagged
321
+ * `type:'tool'`) alongside the widget's lifecycle and chat events.
322
+ */
323
+ onToolEvent?: (e: ToolEvent) => void;
324
+ /**
325
+ * UNIFIED host telemetry hook: fired on every embed lifecycle moment — `ready`,
326
+ * `open`/`close`, `navigate`, `auth`, `error`, `chat`, and `tool` — so you can pipe the
327
+ * whole widget into your own observability from one place. Never blocks or breaks the
328
+ * widget; a throw here is swallowed. Sugar over `window.matterfact.onEvent`.
329
+ */
330
+ onEvent?: (e: MatterfactEvent) => void;
159
331
  }
160
- declare function MatterfactAgent({ publishableKey, widgetOrigin, surface, theme, getAuthToken, getPageContext, inline, className, style, pageContext, dev, actions, sitemap, }: MatterfactAgentProps): react_jsx_runtime.JSX.Element | null;
332
+ declare function MatterfactAgent({ publishableKey, widgetOrigin, surface, theme, getAuthToken, getPageContext, inline, className, style, pageContext, dev, actions, sitemap, artifacts, resolve, tools, onToolEvent, onEvent, }: MatterfactAgentProps): react_jsx_runtime.JSX.Element | null;
161
333
  interface MatterfactArtifactProps {
162
334
  /** The artifact's slug/name. */
163
335
  slug: string;
@@ -178,4 +350,4 @@ interface MatterfactArtifactProps {
178
350
  */
179
351
  declare function MatterfactArtifact({ slug, owner, token, widgetOrigin, theme, className, style, }: MatterfactArtifactProps): react_jsx_runtime.JSX.Element;
180
352
 
181
- export { MatterfactAgent, type MatterfactAgentProps, MatterfactArtifact, type MatterfactArtifactProps };
353
+ export { type HostResolvers, type HostToolDef, MatterfactAgent, type MatterfactAgentProps, MatterfactArtifact, type MatterfactArtifactProps };
package/dist/react.d.ts CHANGED
@@ -61,6 +61,76 @@ interface SiteMapEntry {
61
61
  content?: ContentClass;
62
62
  current?: boolean;
63
63
  }
64
+ /**
65
+ * A DECLARED artifact grant — the `<MatterfactAgent artifacts>` prop (or
66
+ * `window.matterfact.artifacts`). Same wire shape as a grant discovered by scanning
67
+ * co-embedded `<MatterfactArtifact>` iframes, plus an optional `label`; it feeds the
68
+ * SAME `host.artifactGrants` channel. This is how a host makes a board reachable from
69
+ * a route where its iframe ISN'T mounted, without hidden zero-size iframes smuggling
70
+ * the token into the DOM. The token stays in the grant channel, never in message text.
71
+ */
72
+ interface DeclaredArtifact {
73
+ slug: string;
74
+ owner: string;
75
+ token: string;
76
+ label?: string;
77
+ }
78
+ /**
79
+ * One host-tool lifecycle event, surfaced to the host via `onToolEvent` (and the dev
80
+ * panel) — the host's own telemetry hook. Fired loader-side; a throwing or slow
81
+ * callback here NEVER blocks or breaks the call path.
82
+ *
83
+ * - `advertise` — the merged tool list changed (fired with the new set's names).
84
+ * - `call` — a tool call is about to run (`args` present).
85
+ * - `result` — the call settled (`ok`/`error`/`ms`).
86
+ */
87
+ interface ToolEvent {
88
+ phase: 'advertise' | 'call' | 'result';
89
+ name: string;
90
+ toolClass: 'hoist' | 'mf' | 'app';
91
+ args?: unknown;
92
+ ok?: boolean;
93
+ error?: string;
94
+ ms?: number;
95
+ }
96
+ /**
97
+ * One embed lifecycle event, surfaced to the host via `onEvent` — the host's single
98
+ * telemetry hook. Fired loader-side (lifecycle) and widget-side (chat); a throwing or
99
+ * slow callback NEVER blocks or breaks the mount/call/auth/run path.
100
+ *
101
+ * - `ready` — the widget connected (protocol handshake done).
102
+ * - `open`/`close` — the corner widget was opened/closed. (Inline has no open event —
103
+ * the host's own panel is the open/close control.)
104
+ * - `navigate` — the agent deeplinked the host to one of its own routes.
105
+ * - `auth` — host-auth passthrough: `required` (widget asked), `granted` (a
106
+ * token was handed over), `failed` (no provider result / the provider threw).
107
+ * - `error` — the widget failed to mount / load.
108
+ * - `chat` — a chat-turn moment. Content-free: phase + opaque chatId only.
109
+ * - `tool` — a host-tool lifecycle event (the `ToolEvent` shape, tagged). Also
110
+ * still delivered untagged to the legacy `onToolEvent` hook for back-compat.
111
+ */
112
+ type MatterfactEvent = {
113
+ type: 'ready';
114
+ } | {
115
+ type: 'open';
116
+ } | {
117
+ type: 'close';
118
+ } | {
119
+ type: 'navigate';
120
+ href: string;
121
+ } | {
122
+ type: 'auth';
123
+ phase: 'required' | 'granted' | 'failed';
124
+ } | {
125
+ type: 'error';
126
+ message: string;
127
+ } | {
128
+ type: 'chat';
129
+ phase: 'message' | 'response-start' | 'response-end';
130
+ chatId?: string;
131
+ } | ({
132
+ type: 'tool';
133
+ } & ToolEvent);
64
134
 
65
135
  /**
66
136
  * The Hoist adapter — a pure transform from a `HoistRuntime` (already read out of
@@ -83,6 +153,64 @@ interface HoistActionsConfig {
83
153
  navigate: 'off' | 'confirm' | 'auto';
84
154
  }
85
155
 
156
+ /**
157
+ * The host-tool registry — the ONE place the three tool classes are merged into the
158
+ * single list the widget advertises, and the ONE execution path for calling any of
159
+ * them.
160
+ *
161
+ * Three classes, distinguished by who owns the contract and who runs the code:
162
+ *
163
+ * - `hoist.*` matterfact-defined, matterfact-implemented. The loader adapter runs
164
+ * them (today: `hoist.navigate`). Gated on `window.XH` + the host's
165
+ * `hoist.actions` policy. See `hoist-runtime.ts`.
166
+ * - `mf.*` matterfact-defined, HOST-implemented. WE fix the name, schema and
167
+ * agent semantics (the description is authored HERE, not per
168
+ * integration); the host supplies the function via `resolve.*`. Today:
169
+ * `mf.resolveDocument`, `mf.resolveArtifact`.
170
+ * - `app.*` host-defined, host-implemented. Anything the host advertises via
171
+ * `tools`. Normalised into the `app.` namespace so it can never shadow
172
+ * ours.
173
+ *
174
+ * Everything downstream (the widget's confirm gate, the backend dispatcher) is already
175
+ * generic over "a tool with a name/schema/confirm and a call round-trip" — this module
176
+ * is purely the loader-side composition + dispatch, so `hoist-runtime.ts` stays one
177
+ * adapter rather than the whole surface.
178
+ *
179
+ * SECURITY: `mf.*`/`app.*` handlers are HOST code. Their results and error strings pass
180
+ * through `redact()` before leaving the page, exactly like the hoist adapter's; a
181
+ * handler that throws or hangs becomes a normal `{ok:false}` result, never an exception
182
+ * into `context.ts`. And the SERVER decides which of these an app may actually expose
183
+ * (the `host_tools` policy) — advertising here is necessary but not sufficient.
184
+ */
185
+
186
+ /** A host-supplied resolver: matterfact fixes the contract, the host implements it. */
187
+ interface HostResolvers {
188
+ document?: (args: {
189
+ doctype: string;
190
+ key: string;
191
+ }) => Promise<{
192
+ id: string;
193
+ label: string;
194
+ href?: string;
195
+ }>;
196
+ artifact?: (args: {
197
+ slug: string;
198
+ }) => Promise<{
199
+ id: string;
200
+ owner: string;
201
+ token: string;
202
+ label: string;
203
+ }>;
204
+ }
205
+ /** A host-defined tool (the `tools` prop): a WebMCP-shaped descriptor plus its handler. */
206
+ interface HostToolDef {
207
+ name: string;
208
+ description: string;
209
+ inputSchema?: Record<string, unknown>;
210
+ confirm?: 'auto' | 'required';
211
+ handler: (args: Record<string, unknown>) => unknown | Promise<unknown>;
212
+ }
213
+
86
214
  interface MatterfactAgentProps {
87
215
  /** Publishable key (`pk_…`) identifying this embed app. Public, origin-scoped. */
88
216
  publishableKey: string;
@@ -156,8 +284,52 @@ interface MatterfactAgentProps {
156
284
  * on load and on navigation.
157
285
  */
158
286
  sitemap?: SiteMapEntry[];
287
+ /**
288
+ * Declared artifact grants — make a board reachable from ANY route, even ones where
289
+ * its `<MatterfactArtifact>` iframe isn't mounted. Replaces the pattern of mounting
290
+ * hidden zero-size iframes purely to smuggle a read token into the DOM. The token
291
+ * rides the grant channel, never message text. Sugar over `window.matterfact.artifacts`.
292
+ */
293
+ artifacts?: DeclaredArtifact[];
294
+ /**
295
+ * matterfact-defined resolver contracts the HOST implements. matterfact fixes the tool
296
+ * name, schema and agent semantics (so an integration spends no instruction budget on
297
+ * them); you supply the function.
298
+ *
299
+ * - `document({doctype, key})` → `{id, label, href?}` — resolve an off-page matterfact
300
+ * document (dossier/report) to its id, e.g. via your own content-location lookup.
301
+ * - `artifact({slug})` → `{id, owner, token, label}` — resolve an artifact to a read grant.
302
+ *
303
+ * The agent calls these when the site map shows the content lives on another route, and
304
+ * renders the result as an `<MFRef>` chip. Sugar over `window.matterfact.resolve`.
305
+ */
306
+ resolve?: HostResolvers;
307
+ /**
308
+ * Host-defined tools (`app.*`): your own functions, offered to the agent WebMCP-style.
309
+ * Each is `{name, description, inputSchema?, confirm?, handler}`; the name is normalised
310
+ * into the `app.` namespace. A tool is only actually exposed if this app's server-side
311
+ * policy allows it. Sugar over `window.matterfact.tools`.
312
+ */
313
+ tools?: HostToolDef[];
314
+ /**
315
+ * Host telemetry hook: fired on every host-tool advertise/call/result so you can pipe
316
+ * calls into your own observability. Never blocks or breaks the call path — a throw here
317
+ * is swallowed. Sugar over `window.matterfact.onToolEvent`.
318
+ *
319
+ * NOTE: this is now a SUBSET of `onEvent` (the tool events only), kept for back-compat.
320
+ * Prefer `onEvent` for new integrations — it delivers these same tool events (tagged
321
+ * `type:'tool'`) alongside the widget's lifecycle and chat events.
322
+ */
323
+ onToolEvent?: (e: ToolEvent) => void;
324
+ /**
325
+ * UNIFIED host telemetry hook: fired on every embed lifecycle moment — `ready`,
326
+ * `open`/`close`, `navigate`, `auth`, `error`, `chat`, and `tool` — so you can pipe the
327
+ * whole widget into your own observability from one place. Never blocks or breaks the
328
+ * widget; a throw here is swallowed. Sugar over `window.matterfact.onEvent`.
329
+ */
330
+ onEvent?: (e: MatterfactEvent) => void;
159
331
  }
160
- declare function MatterfactAgent({ publishableKey, widgetOrigin, surface, theme, getAuthToken, getPageContext, inline, className, style, pageContext, dev, actions, sitemap, }: MatterfactAgentProps): react_jsx_runtime.JSX.Element | null;
332
+ declare function MatterfactAgent({ publishableKey, widgetOrigin, surface, theme, getAuthToken, getPageContext, inline, className, style, pageContext, dev, actions, sitemap, artifacts, resolve, tools, onToolEvent, onEvent, }: MatterfactAgentProps): react_jsx_runtime.JSX.Element | null;
161
333
  interface MatterfactArtifactProps {
162
334
  /** The artifact's slug/name. */
163
335
  slug: string;
@@ -178,4 +350,4 @@ interface MatterfactArtifactProps {
178
350
  */
179
351
  declare function MatterfactArtifact({ slug, owner, token, widgetOrigin, theme, className, style, }: MatterfactArtifactProps): react_jsx_runtime.JSX.Element;
180
352
 
181
- export { MatterfactAgent, type MatterfactAgentProps, MatterfactArtifact, type MatterfactArtifactProps };
353
+ export { type HostResolvers, type HostToolDef, MatterfactAgent, type MatterfactAgentProps, MatterfactArtifact, type MatterfactArtifactProps };
package/dist/react.js CHANGED
@@ -371,6 +371,21 @@ var EmbedHost = class {
371
371
  __testHandle(msg) {
372
372
  this.handle(msg);
373
373
  }
374
+ /**
375
+ * The eager stub's own emit-and-swallow for the host's `onEvent` telemetry hook.
376
+ * Deliberately does NOT import the registry's `emitEvent`: pulling the lazy ./context
377
+ * chunk (where the registry lives) into the size-budgeted stub would blow it, so this
378
+ * ~5-line duplication across the bundle boundary is intentional. Host telemetry is
379
+ * host code — a throw here must never break the widget.
380
+ */
381
+ emit(e) {
382
+ const cb = globalThis.matterfact?.onEvent;
383
+ if (typeof cb !== "function") return;
384
+ try {
385
+ cb(e);
386
+ } catch {
387
+ }
388
+ }
374
389
  handle(msg) {
375
390
  switch (msg.type) {
376
391
  case "widget.ready":
@@ -383,8 +398,10 @@ var EmbedHost = class {
383
398
  this.send({ type: "host.theme", mode: this.themeMode() });
384
399
  this.flush();
385
400
  if (this.inline) void this.loadContext();
401
+ this.emit({ type: "ready" });
386
402
  break;
387
403
  case "widget.setOpen":
404
+ this.emit({ type: msg.open ? "open" : "close" });
388
405
  if (this.inline) break;
389
406
  this.open = msg.open;
390
407
  this.place();
@@ -573,11 +590,16 @@ var EmbedHost = class {
573
590
  if (this.hostEl) this.hostEl.style.display = "none";
574
591
  break;
575
592
  case "widget.needsAuth":
593
+ this.emit({ type: "auth", phase: "required" });
576
594
  void this.provideAuth();
577
595
  break;
578
596
  case "widget.navigate":
597
+ this.emit({ type: "navigate", href: msg.href });
579
598
  void this.loadContext().then((m) => m.navigateHost(msg.href));
580
599
  break;
600
+ case "widget.chat":
601
+ this.emit({ type: "chat", phase: msg.phase, chatId: msg.chatId });
602
+ break;
581
603
  }
582
604
  }
583
605
  /**
@@ -690,11 +712,13 @@ var EmbedHost = class {
690
712
  try {
691
713
  const token = await provider();
692
714
  if (token) this.send({ type: "host.auth", token, expiresAt: 0 });
715
+ this.emit({ type: "auth", phase: token ? "granted" : "failed" });
693
716
  } catch {
717
+ this.emit({ type: "auth", phase: "failed" });
694
718
  }
695
719
  }
696
720
  loadContext() {
697
- this.context ?? (this.context = import("./context-U2HJJN2S.js").then((m) => {
721
+ this.context ?? (this.context = import("./context-IK5MECUW.js").then((m) => {
698
722
  m.start(
699
723
  this.send,
700
724
  this.config.origin,
@@ -747,6 +771,37 @@ function writeSitemapGlobal(sitemap) {
747
771
  const w = window;
748
772
  (w.matterfact ?? (w.matterfact = {})).sitemap = sitemap;
749
773
  }
774
+ function writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent) {
775
+ if (typeof window === "undefined") return;
776
+ const mf = window.matterfact ?? (window.matterfact = {});
777
+ mf.artifacts = artifacts ?? [];
778
+ mf.resolve = resolve ?? {};
779
+ mf.tools = tools ?? [];
780
+ mf.onToolEvent = onToolEvent;
781
+ mf.onEvent = onEvent;
782
+ }
783
+ function toolDescriptorKey(artifacts, resolve, tools) {
784
+ return JSON.stringify({
785
+ // Artifact identity is data (slug/owner/label), not a function.
786
+ artifacts: (artifacts ?? []).map((a) => ({
787
+ slug: a.slug,
788
+ owner: a.owner,
789
+ label: a.label
790
+ })),
791
+ // For resolvers, only WHICH contracts are present changes the advertised set.
792
+ resolve: {
793
+ document: typeof resolve?.document === "function",
794
+ artifact: typeof resolve?.artifact === "function"
795
+ },
796
+ // For host tools, the advertised descriptor — never the handler.
797
+ tools: (tools ?? []).map((t) => ({
798
+ name: t.name,
799
+ description: t.description,
800
+ inputSchema: t.inputSchema,
801
+ confirm: t.confirm
802
+ }))
803
+ });
804
+ }
750
805
  function MatterfactAgent({
751
806
  publishableKey,
752
807
  widgetOrigin,
@@ -760,7 +815,12 @@ function MatterfactAgent({
760
815
  pageContext,
761
816
  dev,
762
817
  actions,
763
- sitemap
818
+ sitemap,
819
+ artifacts,
820
+ resolve,
821
+ tools,
822
+ onToolEvent,
823
+ onEvent
764
824
  }) {
765
825
  const authRef = useRef(getAuthToken);
766
826
  authRef.current = getAuthToken;
@@ -774,10 +834,15 @@ function MatterfactAgent({
774
834
  useEffect(() => {
775
835
  writeSitemapGlobal(sitemap);
776
836
  }, [sitemapKey]);
837
+ useEffect(() => {
838
+ writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);
839
+ });
840
+ const toolsKey = toolDescriptorKey(artifacts, resolve, tools);
777
841
  useEffect(() => {
778
842
  if (typeof window === "undefined") return;
779
843
  if (inline && !slot.current) return;
780
844
  writeActionsGlobal(actionsRef.current);
845
+ writeToolGlobals(artifacts, resolve, tools, onToolEvent, onEvent);
781
846
  const config = {
782
847
  publishableKey,
783
848
  origin: widgetOrigin || DEFAULT_ORIGIN,
@@ -798,6 +863,13 @@ function MatterfactAgent({
798
863
  host = mount(config);
799
864
  } catch (e) {
800
865
  console.error("[matterfact] failed to mount the embed widget", e);
866
+ try {
867
+ window.matterfact?.onEvent?.({
868
+ type: "error",
869
+ message: e instanceof Error ? e.message : String(e)
870
+ });
871
+ } catch {
872
+ }
801
873
  }
802
874
  return () => host?.destroy();
803
875
  }, [
@@ -808,7 +880,11 @@ function MatterfactAgent({
808
880
  inline,
809
881
  pageContext,
810
882
  dev,
811
- actionsKey
883
+ actionsKey,
884
+ // A change to the advertised tool SHAPE remounts so start() re-advertises under it;
885
+ // a change to only a handler body or callback identity does not (see toolDescriptorKey).
886
+ toolsKey
887
+ // eslint-disable-next-line react-hooks/exhaustive-deps
812
888
  ]);
813
889
  if (!inline) return null;
814
890
  return /* @__PURE__ */ jsx(