@matterfact/embed 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -0
- package/dist/{chunk-R2ZEJARX.js → chunk-4AE5WINC.js} +233 -17
- package/dist/chunk-4AE5WINC.js.map +1 -0
- package/dist/{chunk-PNSYFXXU.js → chunk-CFDPCZO3.js} +233 -15
- package/dist/chunk-CFDPCZO3.js.map +1 -0
- package/dist/{chunk-UD7CAQXV.js → chunk-DXLHTFB4.js} +2 -2
- package/dist/chunk-RL6VIGWK.js +2 -0
- package/dist/chunk-TWMHQF7O.js +3 -0
- package/dist/chunk-TWMHQF7O.js.map +7 -0
- package/dist/{context-MVGSYIMB.js → context-HOOW63MO.js} +6 -2
- package/dist/context-SSQ4HUP3.js +3 -0
- package/dist/{context-ARBB2XD6.js.map → context-SSQ4HUP3.js.map} +1 -1
- package/dist/embed.js +1 -1
- package/dist/embed.js.map +2 -2
- package/dist/index.cjs +243 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +92 -2
- package/dist/index.d.ts +92 -2
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +299 -16
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +172 -1
- package/dist/react.d.ts +172 -1
- package/dist/react.js +60 -4
- package/dist/react.js.map +1 -1
- package/dist/{snapshot-GL4YBMXD.js → snapshot-CRY2IBX6.js} +3 -3
- package/dist/{snapshot-GL4YBMXD.js.map → snapshot-CRY2IBX6.js.map} +1 -1
- package/dist/{snapshot-UGTXZVB6.js → snapshot-EYRXPLRC.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-PNSYFXXU.js.map +0 -1
- package/dist/chunk-R2ZEJARX.js.map +0 -1
- package/dist/chunk-UQCETVRF.js +0 -2
- package/dist/chunk-W52Q7G4J.js +0 -3
- package/dist/chunk-W52Q7G4J.js.map +0 -7
- package/dist/context-ARBB2XD6.js +0 -3
- /package/dist/{chunk-UD7CAQXV.js.map → chunk-DXLHTFB4.js.map} +0 -0
- /package/dist/{chunk-UQCETVRF.js.map → chunk-RL6VIGWK.js.map} +0 -0
- /package/dist/{context-MVGSYIMB.js.map → context-HOOW63MO.js.map} +0 -0
- /package/dist/{snapshot-UGTXZVB6.js.map → snapshot-EYRXPLRC.js.map} +0 -0
package/README.md
CHANGED
|
@@ -92,6 +92,132 @@ Script-tag / non-React hosts can use the iframe directly (the Share dialog shows
|
|
|
92
92
|
></iframe>
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
## Linking your app's matterfact content
|
|
96
|
+
|
|
97
|
+
A co-embedded `<MatterfactArtifact>` (above) is found automatically. But your app may
|
|
98
|
+
render matterfact content another way — a report/dossier you pulled from matterfact and
|
|
99
|
+
render yourself, or artifacts spread across many routes. Tell the agent where that content
|
|
100
|
+
lives with a **site map**: your routes, each tagged with what kind of matterfact content
|
|
101
|
+
it carries. The agent uses it to reason about — and navigate toward — content the user
|
|
102
|
+
isn't currently looking at.
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<MatterfactAgent
|
|
106
|
+
sitemap={[
|
|
107
|
+
{
|
|
108
|
+
path: '/company/:ticker/dossier',
|
|
109
|
+
label: 'Company Dossier',
|
|
110
|
+
content: { kind: 'mf-document', doctype: 'company_dossier' },
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
path: '/talent/exec-departures',
|
|
114
|
+
label: 'Exec Departures',
|
|
115
|
+
content: { kind: 'mf-artifact', slug: 'exec-departure-tracker' },
|
|
116
|
+
},
|
|
117
|
+
{ path: '/screener', label: 'Screener', content: { kind: 'host-data' } },
|
|
118
|
+
]}
|
|
119
|
+
/>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
// script tag — same shape
|
|
124
|
+
window.matterfact = {
|
|
125
|
+
sitemap: [
|
|
126
|
+
/* … */
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Each entry is **classification only** — a route pattern, a label, and a `content` tag:
|
|
132
|
+
|
|
133
|
+
- `{ kind: 'mf-artifact', slug }` — a matterfact artifact rendered on that route.
|
|
134
|
+
- `{ kind: 'mf-document', doctype }` — a matterfact document/report (dossier, etc.).
|
|
135
|
+
- `{ kind: 'host-data' }` — your own data; not matterfact content.
|
|
136
|
+
|
|
137
|
+
**Never put a capability token in the site map.** It is app-wide and rides on every turn;
|
|
138
|
+
the concrete artifact/document on the _current_ page arrives separately (the live artifact
|
|
139
|
+
iframe, or per-page context — see below), where the token belongs.
|
|
140
|
+
|
|
141
|
+
### Documents on the current page
|
|
142
|
+
|
|
143
|
+
For a matterfact **document** the user is looking at, hand the agent its id via page
|
|
144
|
+
context (the same `context.entities` you already declare — see _What the agent can see_):
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
<MatterfactAgent
|
|
148
|
+
getPageContext={() => ({
|
|
149
|
+
route: '/company/:ticker/dossier',
|
|
150
|
+
entities: [
|
|
151
|
+
{
|
|
152
|
+
kind: 'document',
|
|
153
|
+
id: currentDoc.mfDocId, // the matterfact MF_DOC_ID
|
|
154
|
+
label: currentDoc.title,
|
|
155
|
+
data: { href: location.href }, // optional: enables the panel's "open on page" deeplink
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
})}
|
|
159
|
+
/>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The agent can then cite the document inline as a chip; clicking it opens a read-only viewer
|
|
163
|
+
in the widget (authorized by the signed-in user's own matterfact access — no token), with an
|
|
164
|
+
optional **Open on page** deeplink back to your route. matterfact resolves the document from
|
|
165
|
+
its own systems by `MF_DOC_ID`; it never reads your database.
|
|
166
|
+
|
|
167
|
+
### Content the user _isn't_ looking at
|
|
168
|
+
|
|
169
|
+
The site map tells the agent a route _carries_ matterfact content; the two props below let it
|
|
170
|
+
actually **reach** that content from anywhere, without the user navigating there first.
|
|
171
|
+
|
|
172
|
+
**Boards on any route — `artifacts`.** Declare your matterfact artifacts once and the agent
|
|
173
|
+
can open any of them from any page, even routes where you don't embed the `<MatterfactArtifact>`
|
|
174
|
+
iframe:
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
<MatterfactAgent
|
|
178
|
+
artifacts={[
|
|
179
|
+
{ slug: 'exec-departure-tracker', owner: 'you@firm.com', token: '…', label: 'Talent Bank' },
|
|
180
|
+
{ slug: 'global-datacenter-buildout', owner: 'you@firm.com', token: '…', label: 'Data Centers' },
|
|
181
|
+
]}
|
|
182
|
+
/>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```js
|
|
186
|
+
// script tag — same shape
|
|
187
|
+
window.matterfact = { artifacts: [{ slug: '…', owner: '…', token: '…', label: '…' }] };
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
This replaces the old workaround of mounting hidden, zero-size `<MatterfactArtifact>` iframes
|
|
191
|
+
just to make a board's grant reachable. The `token` stays in this host-page config and in the
|
|
192
|
+
grant channel — it never enters the chat transcript or reaches the model.
|
|
193
|
+
|
|
194
|
+
**A resolver for content you look up per-request — `resolve`.** When your set of documents or
|
|
195
|
+
boards is per-user or per-ticker (too large to declare), give the agent a function that turns a
|
|
196
|
+
classification into a concrete reference. matterfact defines the tool contract (name, arguments,
|
|
197
|
+
result shape, and how the agent uses it — so it costs you no agent-instruction budget); you
|
|
198
|
+
supply the implementation:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<MatterfactAgent
|
|
202
|
+
resolve={{
|
|
203
|
+
// doctype + a key (e.g. a ticker) -> the matterfact document id, from YOUR lookup.
|
|
204
|
+
document: async ({ doctype, key }) => {
|
|
205
|
+
const row = await myHub.lookup(doctype, key);
|
|
206
|
+
return { id: row.MF_DOC_ID, label: `${key} ${doctype}`, href: hrefFor(doctype, key) };
|
|
207
|
+
},
|
|
208
|
+
// a slug -> a read grant, when you resolve boards dynamically instead of declaring them.
|
|
209
|
+
artifact: async ({ slug }) => myHub.artifactGrant(slug),
|
|
210
|
+
}}
|
|
211
|
+
/>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The agent calls `mf.resolveDocument` / `mf.resolveArtifact` when the site map shows the content
|
|
215
|
+
lives on another route, gets back an id (and, for a document, an optional `href` deeplink), and
|
|
216
|
+
cites it as a chip — exactly as for on-page content. **matterfact never queries your data**: your
|
|
217
|
+
function runs the lookup, matterfact only ever receives the id it returns. `resolve` is a host
|
|
218
|
+
tool (see [Host tools](#host-tools)), so your app's `host_tools` policy must allow `mf.*` — it
|
|
219
|
+
does by default.
|
|
220
|
+
|
|
95
221
|
## Signing users in
|
|
96
222
|
|
|
97
223
|
By default the widget runs its own sign-in in a popup (the only way an embedded frame can
|
|
@@ -310,6 +436,71 @@ Everything else about page observation still applies here: values are redacted b
|
|
|
310
436
|
leaves the page, row counts are capped, and `pageContext={false}` turns this off along with
|
|
311
437
|
everything else (see above).
|
|
312
438
|
|
|
439
|
+
## Host tools
|
|
440
|
+
|
|
441
|
+
`hoist.navigate` above is one instance of a general mechanism: **the host page can offer the
|
|
442
|
+
agent tools to call**. There are three kinds, distinguished by who owns the contract and who
|
|
443
|
+
runs the code:
|
|
444
|
+
|
|
445
|
+
| Prefix | Contract | Runs where | Examples |
|
|
446
|
+
| ---------- | ---------- | ------------------- | ------------------------------------------------ |
|
|
447
|
+
| `hoist.*` | matterfact | the widget's adapter | `hoist.navigate` (above) |
|
|
448
|
+
| `mf.*` | matterfact | **your** function | `mf.resolveDocument`, `mf.resolveArtifact` (above) |
|
|
449
|
+
| `app.*` | **you** | **your** function | anything you declare |
|
|
450
|
+
|
|
451
|
+
`hoist.*` and `mf.*` you get by turning on the features above. `app.*` is your own tools, which
|
|
452
|
+
you declare with the `tools` prop:
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
<MatterfactAgent
|
|
456
|
+
tools={[
|
|
457
|
+
{
|
|
458
|
+
name: 'exportBook', // advertised to the agent as `app.exportBook`
|
|
459
|
+
description: 'Export the current book to CSV.',
|
|
460
|
+
inputSchema: { type: 'object', properties: { scope: { type: 'string' } } },
|
|
461
|
+
confirm: 'required', // 'required' (default) shows an approval card; 'auto' runs without asking
|
|
462
|
+
handler: async ({ scope }) => myApp.exportBook(scope),
|
|
463
|
+
},
|
|
464
|
+
]}
|
|
465
|
+
/>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
```js
|
|
469
|
+
// script tag — same shape
|
|
470
|
+
window.matterfact = { tools: [{ name: '…', description: '…', handler: async () => {} }] };
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
Names are namespaced into `app.` automatically, so a host tool can never collide with a
|
|
474
|
+
matterfact one. A tool's `handler` result and any error it throws are **redacted before leaving
|
|
475
|
+
the page**; a handler that throws, rejects, or hangs (beyond 30s) becomes a normal "not
|
|
476
|
+
completed" result the agent reads and adapts to — never a crash. Like `actions`, changing the
|
|
477
|
+
_shape_ of your tools (a name, description, schema, or confirm policy) remounts the widget so the
|
|
478
|
+
new set is advertised; changing only a handler's body does not, so an inline `handler` closure
|
|
479
|
+
won't churn the chat session.
|
|
480
|
+
|
|
481
|
+
### Who decides which tools are exposed
|
|
482
|
+
|
|
483
|
+
Advertising a tool is necessary but not sufficient: **which tools an app may actually expose is
|
|
484
|
+
a server-side setting** on your embed app (`host_tools`: `off` / `allowlist` / `all`, plus an
|
|
485
|
+
allow-list of names). The default allows matterfact's own `hoist.*` and `mf.*` and denies
|
|
486
|
+
arbitrary `app.*` until an operator grants it — so `resolve` works out of the box, and turning on
|
|
487
|
+
your first `app.*` tool is a deliberate decision made in the admin console, not something a page
|
|
488
|
+
can do on its own. Ask your matterfact contact to enable the `app.*` names you need.
|
|
489
|
+
|
|
490
|
+
### Watching tool calls — `onToolEvent`
|
|
491
|
+
|
|
492
|
+
Pipe every advertise / call / result into your own telemetry:
|
|
493
|
+
|
|
494
|
+
```tsx
|
|
495
|
+
<MatterfactAgent
|
|
496
|
+
onToolEvent={(e) => myTelemetry.track('mf_tool', e)} // { phase, name, toolClass, args?, ok?, error?, ms? }
|
|
497
|
+
/>
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
It's fire-and-forget and fully isolated — a throw or a slow callback here never blocks or breaks
|
|
501
|
+
a tool call. matterfact also keeps its own durable audit of every call (visible to operators in
|
|
502
|
+
the admin console), independent of this hook.
|
|
503
|
+
|
|
313
504
|
## Microphone / dictation
|
|
314
505
|
|
|
315
506
|
The composer supports voice dictation; the loader grants the iframe `allow="microphone"`.
|
|
@@ -374,6 +374,148 @@ function executeHostTool(call, win = typeof window !== "undefined" ? window : vo
|
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
// src/adapters/registry.ts
|
|
378
|
+
var MAX_TOOLS = 16;
|
|
379
|
+
var HOST_HANDLER_TIMEOUT_MS = 3e4;
|
|
380
|
+
function mfGlobal(win) {
|
|
381
|
+
return win?.matterfact ?? {};
|
|
382
|
+
}
|
|
383
|
+
function toolClass(name) {
|
|
384
|
+
if (name.startsWith("hoist.")) return "hoist";
|
|
385
|
+
if (name.startsWith("mf.")) return "mf";
|
|
386
|
+
return "app";
|
|
387
|
+
}
|
|
388
|
+
function emitToolEvent(win, e) {
|
|
389
|
+
const cb = mfGlobal(win).onToolEvent;
|
|
390
|
+
if (typeof cb !== "function") return;
|
|
391
|
+
try {
|
|
392
|
+
cb(e);
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function mfTools(win) {
|
|
397
|
+
const { document: document2, artifact } = mfGlobal(win).resolve ?? {};
|
|
398
|
+
const out = [];
|
|
399
|
+
if (typeof document2 === "function") {
|
|
400
|
+
out.push({
|
|
401
|
+
name: "mf.resolveDocument",
|
|
402
|
+
description: 'Resolve a matterfact document (dossier, report, briefing) that is NOT on the current page to a concrete id. Call it with the document type and a key (e.g. a ticker) when the site map shows the document lives on another route. Returns {id, label, href}. Cite the result inline as <MFRef kind="document" id="<id>" label="<label>" /> \u2014 it renders as a chip that opens the document in the side panel; href is its "open on page" deeplink.',
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: {
|
|
406
|
+
doctype: { type: "string" },
|
|
407
|
+
key: { type: "string" }
|
|
408
|
+
},
|
|
409
|
+
required: ["doctype", "key"]
|
|
410
|
+
},
|
|
411
|
+
readOnly: true,
|
|
412
|
+
confirm: "auto"
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
if (typeof artifact === "function") {
|
|
416
|
+
out.push({
|
|
417
|
+
name: "mf.resolveArtifact",
|
|
418
|
+
description: 'Resolve a matterfact artifact (an interactive board) that is NOT co-embedded on the current page to a read grant. Call it with the artifact slug. Returns {id, owner, token, label}; the grant is applied for you. Cite the result inline as <MFRef kind="artifact" id="<id>" label="<label>" /> \u2014 it renders as a chip that opens the artifact in the side panel.',
|
|
419
|
+
inputSchema: {
|
|
420
|
+
type: "object",
|
|
421
|
+
properties: { slug: { type: "string" } },
|
|
422
|
+
required: ["slug"]
|
|
423
|
+
},
|
|
424
|
+
readOnly: true,
|
|
425
|
+
confirm: "auto"
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
return out;
|
|
429
|
+
}
|
|
430
|
+
var APP_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
431
|
+
function appName(name) {
|
|
432
|
+
return name.startsWith("app.") ? name : `app.${name}`;
|
|
433
|
+
}
|
|
434
|
+
function appTools(win) {
|
|
435
|
+
const defs = mfGlobal(win).tools;
|
|
436
|
+
if (!Array.isArray(defs)) return [];
|
|
437
|
+
const out = [];
|
|
438
|
+
for (const d of defs) {
|
|
439
|
+
const raw = (d?.name ?? "").trim();
|
|
440
|
+
if (!raw || typeof d.handler !== "function") continue;
|
|
441
|
+
const name = appName(raw);
|
|
442
|
+
if (!APP_NAME_RE.test(name)) continue;
|
|
443
|
+
out.push({
|
|
444
|
+
name,
|
|
445
|
+
description: String(d.description ?? `Run the host tool ${name}.`),
|
|
446
|
+
inputSchema: d.inputSchema ?? { type: "object", properties: {} },
|
|
447
|
+
readOnly: false,
|
|
448
|
+
confirm: d.confirm === "auto" ? "auto" : "required"
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
return out;
|
|
452
|
+
}
|
|
453
|
+
function advertise(win = typeof window !== "undefined" ? window : void 0) {
|
|
454
|
+
const merged = [];
|
|
455
|
+
const seen = /* @__PURE__ */ new Set();
|
|
456
|
+
const push = (tools) => {
|
|
457
|
+
for (const t of tools) {
|
|
458
|
+
if (seen.has(t.name)) continue;
|
|
459
|
+
seen.add(t.name);
|
|
460
|
+
merged.push(t);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
try {
|
|
464
|
+
push(advertiseTools(win));
|
|
465
|
+
} catch {
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
push(mfTools(win));
|
|
469
|
+
push(appTools(win));
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
return merged.slice(0, MAX_TOOLS);
|
|
473
|
+
}
|
|
474
|
+
function timeout(ms) {
|
|
475
|
+
return new Promise(
|
|
476
|
+
(_, reject) => setTimeout(() => reject(new Error("host tool timed out")), ms)
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
function redactResult(value) {
|
|
480
|
+
if (value === void 0 || value === null) return value;
|
|
481
|
+
if (typeof value === "string") return redact(value);
|
|
482
|
+
try {
|
|
483
|
+
return JSON.parse(redact(JSON.stringify(value)));
|
|
484
|
+
} catch {
|
|
485
|
+
return redact(String(value));
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async function execute(call, win = typeof window !== "undefined" ? window : void 0) {
|
|
489
|
+
const cls = toolClass(call.name);
|
|
490
|
+
try {
|
|
491
|
+
if (cls === "hoist") {
|
|
492
|
+
return executeHostTool(call, win);
|
|
493
|
+
}
|
|
494
|
+
const handler = resolveHandler(win, call.name);
|
|
495
|
+
if (!handler) return { ok: false, error: "unknown tool" };
|
|
496
|
+
const result = await Promise.race([
|
|
497
|
+
Promise.resolve(handler(call.args ?? {})),
|
|
498
|
+
timeout(HOST_HANDLER_TIMEOUT_MS)
|
|
499
|
+
]);
|
|
500
|
+
return { ok: true, result: redactResult(result) };
|
|
501
|
+
} catch (e) {
|
|
502
|
+
return {
|
|
503
|
+
ok: false,
|
|
504
|
+
error: redact(e instanceof Error ? e.message : "host tool failed")
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
function resolveHandler(win, name) {
|
|
509
|
+
const mf = mfGlobal(win);
|
|
510
|
+
if (name === "mf.resolveDocument") return mf.resolve?.document;
|
|
511
|
+
if (name === "mf.resolveArtifact") return mf.resolve?.artifact;
|
|
512
|
+
if (name.startsWith("app.")) {
|
|
513
|
+
const def = (mf.tools ?? []).find((d) => appName((d?.name ?? "").trim()) === name);
|
|
514
|
+
return def?.handler;
|
|
515
|
+
}
|
|
516
|
+
return void 0;
|
|
517
|
+
}
|
|
518
|
+
|
|
377
519
|
// src/context.ts
|
|
378
520
|
var widgetOrigin = "";
|
|
379
521
|
var MAX_ACTIVITY = 40;
|
|
@@ -479,6 +621,7 @@ async function publishContext() {
|
|
|
479
621
|
}
|
|
480
622
|
function provideContext() {
|
|
481
623
|
void publishContext();
|
|
624
|
+
publishSitemap();
|
|
482
625
|
}
|
|
483
626
|
function grantsFromIframeSrcs(srcs, origin) {
|
|
484
627
|
const out = [];
|
|
@@ -500,16 +643,56 @@ function grantsFromIframeSrcs(srcs, origin) {
|
|
|
500
643
|
}
|
|
501
644
|
return out;
|
|
502
645
|
}
|
|
646
|
+
function readDeclaredGrants() {
|
|
647
|
+
const declared = window.matterfact?.artifacts;
|
|
648
|
+
if (!Array.isArray(declared)) return [];
|
|
649
|
+
const out = [];
|
|
650
|
+
for (const a of declared) {
|
|
651
|
+
const id = (a?.slug || "").trim();
|
|
652
|
+
const owner = (a?.owner || "").trim();
|
|
653
|
+
const token = (a?.token || "").trim();
|
|
654
|
+
if (!id || !owner || !token) continue;
|
|
655
|
+
if (out.some((g) => g.id === id)) continue;
|
|
656
|
+
out.push({ id, owner, token });
|
|
657
|
+
}
|
|
658
|
+
return out;
|
|
659
|
+
}
|
|
503
660
|
function readArtifactGrants() {
|
|
504
661
|
if (!widgetOrigin) return [];
|
|
505
662
|
const srcs = Array.from(document.querySelectorAll("iframe")).map(
|
|
506
663
|
(f) => f.getAttribute("src") || ""
|
|
507
664
|
);
|
|
508
|
-
|
|
665
|
+
const scanned = grantsFromIframeSrcs(srcs, widgetOrigin);
|
|
666
|
+
const out = [...scanned];
|
|
667
|
+
for (const g of readDeclaredGrants()) {
|
|
668
|
+
if (!out.some((s) => s.id === g.id)) out.push(g);
|
|
669
|
+
}
|
|
670
|
+
return out;
|
|
509
671
|
}
|
|
510
672
|
function publishArtifactGrants() {
|
|
511
673
|
send?.({ type: "host.artifactGrants", grants: readArtifactGrants() });
|
|
512
674
|
}
|
|
675
|
+
function readSitemap2() {
|
|
676
|
+
const s = window.matterfact?.sitemap;
|
|
677
|
+
return Array.isArray(s) ? s : [];
|
|
678
|
+
}
|
|
679
|
+
function publishSitemap() {
|
|
680
|
+
if (!pageContextOn) return;
|
|
681
|
+
const sitemap = readSitemap2();
|
|
682
|
+
if (sitemap.length) send?.({ type: "host.sitemap", sitemap });
|
|
683
|
+
}
|
|
684
|
+
function navigateHost(href) {
|
|
685
|
+
if (typeof href !== "string" || !href) return;
|
|
686
|
+
let target;
|
|
687
|
+
try {
|
|
688
|
+
target = new URL(href, location.href);
|
|
689
|
+
} catch {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (target.protocol !== "http:" && target.protocol !== "https:") return;
|
|
693
|
+
if (target.origin !== location.origin) return;
|
|
694
|
+
location.assign(target.href);
|
|
695
|
+
}
|
|
513
696
|
function pushActivity(e) {
|
|
514
697
|
activity.push({ ...e, seq: ++activitySeq, ts: Date.now() });
|
|
515
698
|
if (activity.length > MAX_ACTIVITY) activity.shift();
|
|
@@ -574,10 +757,8 @@ function watchNavigation() {
|
|
|
574
757
|
pushActivity({ type: "nav", summary: `navigated to ${url}` });
|
|
575
758
|
void publishContext();
|
|
576
759
|
publishArtifactGrants();
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
tools: advertiseTools(typeof window !== "undefined" ? window : void 0)
|
|
580
|
-
});
|
|
760
|
+
publishSitemap();
|
|
761
|
+
publishTools();
|
|
581
762
|
};
|
|
582
763
|
navFire = fire;
|
|
583
764
|
for (const name of ["pushState", "replaceState"]) {
|
|
@@ -620,7 +801,7 @@ function describeControl(el) {
|
|
|
620
801
|
return describe(el);
|
|
621
802
|
}
|
|
622
803
|
async function loadSnapshotModule() {
|
|
623
|
-
const m = await import('./snapshot-
|
|
804
|
+
const m = await import('./snapshot-CRY2IBX6.js');
|
|
624
805
|
snapshotModule = m;
|
|
625
806
|
return m;
|
|
626
807
|
}
|
|
@@ -684,10 +865,24 @@ async function callTool(call, emit) {
|
|
|
684
865
|
});
|
|
685
866
|
return;
|
|
686
867
|
}
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
868
|
+
const win = typeof window !== "undefined" ? window : void 0;
|
|
869
|
+
const cls = toolClass(call.name);
|
|
870
|
+
const startedAt = Date.now();
|
|
871
|
+
emitToolEvent(win, {
|
|
872
|
+
phase: "call",
|
|
873
|
+
name: call.name,
|
|
874
|
+
toolClass: cls,
|
|
875
|
+
args: call.args
|
|
876
|
+
});
|
|
877
|
+
const r = await execute(call, win);
|
|
878
|
+
emitToolEvent(win, {
|
|
879
|
+
phase: "result",
|
|
880
|
+
name: call.name,
|
|
881
|
+
toolClass: cls,
|
|
882
|
+
ok: r.ok,
|
|
883
|
+
error: r.error,
|
|
884
|
+
ms: Date.now() - startedAt
|
|
885
|
+
});
|
|
691
886
|
emit({
|
|
692
887
|
type: "host.toolResult",
|
|
693
888
|
callId: call.callId,
|
|
@@ -736,6 +931,7 @@ function start(emit, origin, pageContext = true, provider) {
|
|
|
736
931
|
if (pageContextOn) {
|
|
737
932
|
void publishContext();
|
|
738
933
|
publishArtifactGrants();
|
|
934
|
+
publishSitemap();
|
|
739
935
|
watchNavigation();
|
|
740
936
|
document.addEventListener("click", onClick, {
|
|
741
937
|
capture: true,
|
|
@@ -764,13 +960,33 @@ function start(emit, origin, pageContext = true, provider) {
|
|
|
764
960
|
const theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
765
961
|
emit({ type: "host.theme", mode: theme });
|
|
766
962
|
if (pageContextOn) {
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
)
|
|
770
|
-
|
|
963
|
+
const w = typeof window !== "undefined" ? window : void 0;
|
|
964
|
+
const tools = advertise(w);
|
|
965
|
+
if (tools.length) {
|
|
966
|
+
emit({ type: "host.tools", tools });
|
|
967
|
+
for (const t of tools) {
|
|
968
|
+
emitToolEvent(w, {
|
|
969
|
+
phase: "advertise",
|
|
970
|
+
name: t.name,
|
|
971
|
+
toolClass: toolClass(t.name)
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
function publishTools() {
|
|
978
|
+
const win = typeof window !== "undefined" ? window : void 0;
|
|
979
|
+
const tools = advertise(win);
|
|
980
|
+
send?.({ type: "host.tools", tools });
|
|
981
|
+
for (const t of tools) {
|
|
982
|
+
emitToolEvent(win, {
|
|
983
|
+
phase: "advertise",
|
|
984
|
+
name: t.name,
|
|
985
|
+
toolClass: toolClass(t.name)
|
|
986
|
+
});
|
|
771
987
|
}
|
|
772
988
|
}
|
|
773
989
|
|
|
774
|
-
export { artifactIdFromPath, buildPageContext, callTool, grantsFromIframeSrcs, isPrivate, provideContext, readPageContext, redact, sendRegion, sendSnapshot, start, stop };
|
|
775
|
-
//# sourceMappingURL=chunk-
|
|
776
|
-
//# sourceMappingURL=chunk-
|
|
990
|
+
export { artifactIdFromPath, buildPageContext, callTool, grantsFromIframeSrcs, isPrivate, navigateHost, provideContext, readDeclaredGrants, readPageContext, redact, sendRegion, sendSnapshot, start, stop };
|
|
991
|
+
//# sourceMappingURL=chunk-4AE5WINC.js.map
|
|
992
|
+
//# sourceMappingURL=chunk-4AE5WINC.js.map
|