@touchcastllc/napster-companion-api-dev 1.0.0-alpha.73 → 1.0.0-alpha.75
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/lib/index.css +1 -1
- package/lib/index.d.ts +10 -0
- package/lib/index.esm.js +1 -1
- package/lib/index.js +1 -1
- package/lib/index.standalone.js +1 -1
- package/lib/services/edge-mcp/edge-mcp-bridge.d.ts +39 -0
- package/lib/services/edge-mcp/edge-mcp-discovery.d.ts +13 -6
- package/lib/services/edge-mcp/edge-mcp-types.d.ts +23 -0
- package/lib/services/edge-mcp/index.d.ts +1 -1
- package/lib/shell/mount-shell.d.ts +88 -0
- package/lib/types/index.d.ts +13 -0
- package/package.json +1 -1
|
@@ -20,17 +20,30 @@ export interface EdgeMcpBridgeOptions {
|
|
|
20
20
|
* false to manage the agent's function list yourself. Default: `true`.
|
|
21
21
|
*/
|
|
22
22
|
registerInlineFunctions?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Read tools from a same-origin child iframe's `document.modelContext` instead of
|
|
25
|
+
* this window's. Used by the persistent-agent shell: the avatar + bridge live in the
|
|
26
|
+
* top-level document while the SITE (and its tools) live in the wrapped iframe. The
|
|
27
|
+
* bridge re-discovers on each iframe `load` (navigation) and routes tool calls into
|
|
28
|
+
* it (`executeTool` runs in the iframe's realm, so the site operates itself).
|
|
29
|
+
*/
|
|
30
|
+
iframe?: HTMLIFrameElement;
|
|
23
31
|
}
|
|
24
32
|
export declare class EdgeMcpBridge {
|
|
25
33
|
private mc;
|
|
26
34
|
private tools;
|
|
27
35
|
private onToolChange;
|
|
28
36
|
private onResourceUpdated;
|
|
37
|
+
private onResourceListChanged;
|
|
38
|
+
private onIframeLoad;
|
|
39
|
+
private readonly seededUris;
|
|
29
40
|
private lastInlineSig;
|
|
30
41
|
private sessionReady;
|
|
31
42
|
private disposed;
|
|
32
43
|
private readonly options;
|
|
33
44
|
constructor(options: EdgeMcpBridgeOptions);
|
|
45
|
+
/** The document whose `modelContext` we read — the target iframe's, or this window's. */
|
|
46
|
+
private targetDoc;
|
|
34
47
|
/**
|
|
35
48
|
* Discover `document.modelContext` in the page, cache its tools, and wire up
|
|
36
49
|
* the `toolchange` / `resourceupdated` listeners. Returns the model context,
|
|
@@ -38,8 +51,34 @@ export declare class EdgeMcpBridge {
|
|
|
38
51
|
* failure is silent and non-fatal.
|
|
39
52
|
*/
|
|
40
53
|
attach(): Promise<EdgeMcpModelContext | null>;
|
|
54
|
+
/** Remove listeners from the current model context and drop the ref (no state reset). */
|
|
55
|
+
private unbind;
|
|
56
|
+
/**
|
|
57
|
+
* Re-discover and re-bind after the target iframe navigated to a NEW document (its old
|
|
58
|
+
* `document.modelContext` is dead). Keeps `sessionReady` (the avatar session in the parent
|
|
59
|
+
* survives), but resets the per-document dedupe so tools + resources re-register for the
|
|
60
|
+
* new page.
|
|
61
|
+
*/
|
|
62
|
+
private reattach;
|
|
41
63
|
/** Attach to a known model context: cache tools and subscribe to events. */
|
|
42
64
|
private bind;
|
|
65
|
+
/**
|
|
66
|
+
* Relay the current value of every registered resource to the agent once, as
|
|
67
|
+
* an initial state seed — batched into a SINGLE `send_message` rather than one
|
|
68
|
+
* per resource, so the agent gets the whole starting state in one context
|
|
69
|
+
* injection. Feature-detects the toolkit's resource extension (`getResources` /
|
|
70
|
+
* `readResource`) — a bare WebMCP surface has neither, so this is a no-op there.
|
|
71
|
+
* Already-seeded URIs are skipped so a `resourcelistchanged` re-seed only emits
|
|
72
|
+
* genuinely new resources. (Ongoing changes still relay per-resource via
|
|
73
|
+
* `relayResourceUpdate`, since they happen one at a time.)
|
|
74
|
+
*/
|
|
75
|
+
private seedResources;
|
|
76
|
+
/**
|
|
77
|
+
* Send the whole initial state set to the agent as ONE `role: 'system'`
|
|
78
|
+
* message (one line per resource). `trigger_response: false` so the avatar
|
|
79
|
+
* doesn't speak on the seed; `delay: true` to wait for any current speech.
|
|
80
|
+
*/
|
|
81
|
+
private relayInitialState;
|
|
43
82
|
/** Re-read the tool catalog from the model context. */
|
|
44
83
|
private refreshTools;
|
|
45
84
|
/** Map the current WebMCP tools to `set_settings.inline_functions` definitions. */
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import type { EdgeMcpModelContext } from "./edge-mcp-types";
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Read `modelContext` from a document, falling back to the deprecated navigator alias.
|
|
4
|
+
*
|
|
5
|
+
* `doc` defaults to the ambient `document`. Pass a same-origin child frame's
|
|
6
|
+
* `contentDocument` to read ITS surface instead (the shell targets the wrapped
|
|
7
|
+
* iframe this way). Cross-origin access throws — caught and returned as `null`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getModelContext(doc?: Document | null): EdgeMcpModelContext | null;
|
|
4
10
|
/**
|
|
5
11
|
* Discover `document.modelContext`, polling briefly if it is not present yet.
|
|
6
12
|
*
|
|
7
13
|
* Resolves with the model context if found, or `null` after `timeoutMs` if no
|
|
8
14
|
* WebMCP surface shows up. Never rejects.
|
|
9
15
|
*
|
|
10
|
-
* @param timeoutMs How long to wait for a late-installing polyfill. Default
|
|
11
|
-
*
|
|
12
|
-
*
|
|
16
|
+
* @param timeoutMs How long to wait for a late-installing polyfill. Default 1500ms.
|
|
17
|
+
* @param getDoc Optional getter for the document to read (re-evaluated each poll,
|
|
18
|
+
* since a targeted iframe's `contentDocument` changes as it navigates).
|
|
19
|
+
* Omit to use the ambient `document`.
|
|
13
20
|
*/
|
|
14
|
-
export declare function discoverModelContext(timeoutMs?: number): Promise<EdgeMcpModelContext | null>;
|
|
21
|
+
export declare function discoverModelContext(timeoutMs?: number, getDoc?: () => Document | null): Promise<EdgeMcpModelContext | null>;
|
|
@@ -12,6 +12,29 @@ export interface EdgeMcpToolInfo {
|
|
|
12
12
|
inputSchema?: Record<string, unknown> | string;
|
|
13
13
|
/** Optional human-readable label. */
|
|
14
14
|
title?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Standard WebMCP/MCP annotation hints, surfaced by the polyfill's
|
|
17
|
+
* `getTools()`. The bridge reads these to gate agent behavior — most
|
|
18
|
+
* importantly, `destructiveHint` ⇒ the agent must confirm with the user before
|
|
19
|
+
* calling. A bare WebMCP surface (or older polyfill) may omit this entirely, so
|
|
20
|
+
* every field is optional and the bridge treats a missing object as "no hints."
|
|
21
|
+
*/
|
|
22
|
+
annotations?: EdgeMcpToolAnnotations;
|
|
23
|
+
}
|
|
24
|
+
/** The standard MCP tool annotation hints the bridge understands. */
|
|
25
|
+
export interface EdgeMcpToolAnnotations {
|
|
26
|
+
/** Tool only reads state — safe to call freely, no confirmation. */
|
|
27
|
+
readOnlyHint?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Tool performs a destructive / final action. Bridge convention: treat this as
|
|
30
|
+
* "confirm with the user before calling" (covers additive-but-final actions
|
|
31
|
+
* like submit / send / place-order too).
|
|
32
|
+
*/
|
|
33
|
+
destructiveHint?: boolean;
|
|
34
|
+
/** Safe to retry with the same args (no extra effect). */
|
|
35
|
+
idempotentHint?: boolean;
|
|
36
|
+
/** Output may contain untrusted / third-party content (injection risk). */
|
|
37
|
+
untrustedContentHint?: boolean;
|
|
15
38
|
}
|
|
16
39
|
/**
|
|
17
40
|
* Payload of the toolkit's `resourceupdated` event (live-state extension).
|
|
@@ -2,4 +2,4 @@ export { EdgeMcpBridge } from "./edge-mcp-bridge";
|
|
|
2
2
|
export type { EdgeMcpBridgeOptions } from "./edge-mcp-bridge";
|
|
3
3
|
export { normalizeFunctionCallEvent } from "./normalizeFunctionCallEvent";
|
|
4
4
|
export { getModelContext, discoverModelContext } from "./edge-mcp-discovery";
|
|
5
|
-
export type { EdgeMcpModelContext, EdgeMcpToolInfo, EdgeMcpResourceUpdate, FunctionCallEvent, SendCommand, } from "./edge-mcp-types";
|
|
5
|
+
export type { EdgeMcpModelContext, EdgeMcpToolInfo, EdgeMcpToolAnnotations, EdgeMcpResourceUpdate, FunctionCallEvent, SendCommand, } from "./edge-mcp-types";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { NapsterCompanionApiConfig, NapsterCompanionApiInstance } from "../types";
|
|
2
|
+
export interface ShellOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Returns a fresh connection token. The site implements this (typically a POST to
|
|
5
|
+
* its own endpoint that mints a token server-side). Required.
|
|
6
|
+
*/
|
|
7
|
+
getToken: () => Promise<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Persist the session across page navigation by wrapping the current page in a
|
|
10
|
+
* same-origin iframe on connect (needed for multi-page apps). Default: `false` —
|
|
11
|
+
* the avatar mounts inline in the current page and a full page navigation ends the
|
|
12
|
+
* session (a normal in-page widget). Turn on only when you need cross-navigation
|
|
13
|
+
* persistence.
|
|
14
|
+
*/
|
|
15
|
+
persist?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* The ONLY URLs that break out of the iframe (open at the top window and end the session).
|
|
18
|
+
* A link is opened inside the iframe unless it matches this denylist — use it for URLs that
|
|
19
|
+
* can't be framed, e.g. an identity / SSO subdomain, or same-origin paths that redirect
|
|
20
|
+
* off-site. Everything not listed (including other cross-origin links) navigates in the iframe.
|
|
21
|
+
*/
|
|
22
|
+
persistConfig?: {
|
|
23
|
+
/**
|
|
24
|
+
* Hostnames to force out of the iframe. Matched as the exact host OR a parent domain —
|
|
25
|
+
* e.g. `"napster.com"` also matches `"identity.napster.com"`.
|
|
26
|
+
*/
|
|
27
|
+
domains?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* URLs to force out of the iframe: an absolute `"https://…"` (exact or prefix match), or a
|
|
30
|
+
* same-origin path prefix like `"/signout"`.
|
|
31
|
+
*/
|
|
32
|
+
urls?: string[];
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* SECOND persistence variant (no iframe). When a session is active and the user navigates
|
|
36
|
+
* (a normal full page load), automatically start a NEW session on the next page — so the
|
|
37
|
+
* avatar "follows" the user across pages. Each page is a fresh connection (the backend can
|
|
38
|
+
* resume the conversation by externalClientId). The intent is remembered in sessionStorage
|
|
39
|
+
* and cleared when the user ends the session. Ignored when `persist` (the iframe variant)
|
|
40
|
+
* is on. Default: `false`.
|
|
41
|
+
*/
|
|
42
|
+
reconnectOnNavigation?: boolean;
|
|
43
|
+
/** When `persist` is on, the URL to load in the iframe. Default: the current page (`location.href`). */
|
|
44
|
+
siteUrl?: string;
|
|
45
|
+
/** Element to build the launcher/iframe into. Default: `document.body`. */
|
|
46
|
+
mount?: HTMLElement;
|
|
47
|
+
/** Avatar config forwarded to `init()`. `mountContainer`/`layout` are managed by the shell. */
|
|
48
|
+
avatar?: Partial<NapsterCompanionApiConfig>;
|
|
49
|
+
/** The iframe `allow` attribute (Permissions Policy for the site). Default: `"autoplay; clipboard-write"`. */
|
|
50
|
+
iframeAllow?: string;
|
|
51
|
+
/** Launcher button label. Default: `"Talk to an agent"`. */
|
|
52
|
+
launcherLabel?: string;
|
|
53
|
+
/** Start a session immediately instead of showing a launcher. Default: `false`. */
|
|
54
|
+
autoConnect?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* While a session is active, mirror the iframe's path into the address bar (so inner
|
|
57
|
+
* pages stay shareable/bookmarkable) and follow parent back/forward into the iframe.
|
|
58
|
+
* Same-origin only. Default: `true`.
|
|
59
|
+
*/
|
|
60
|
+
syncHistory?: boolean;
|
|
61
|
+
/** Log shell lifecycle to the console. Default: `false`. */
|
|
62
|
+
debug?: boolean;
|
|
63
|
+
}
|
|
64
|
+
export interface ShellController {
|
|
65
|
+
/** `true` if this call built the launcher (top-level); `false` if it no-oped (framed content). */
|
|
66
|
+
isShell: boolean;
|
|
67
|
+
/** The live avatar instance once connected, else `null`. */
|
|
68
|
+
getInstance(): NapsterCompanionApiInstance | null;
|
|
69
|
+
/** The site iframe while a session is active, else `null`. */
|
|
70
|
+
getIframe(): HTMLIFrameElement | null;
|
|
71
|
+
/** Start a session: connect the avatar (and, with `persist`, wrap the current page in the iframe). */
|
|
72
|
+
connect(): Promise<void>;
|
|
73
|
+
/** End the session (and, if wrapped, unwrap back to the plain site). */
|
|
74
|
+
disconnect(): void;
|
|
75
|
+
/** Remove the launcher and any active session/iframe. */
|
|
76
|
+
destroy(): void;
|
|
77
|
+
}
|
|
78
|
+
type InitFn = (token: string, config?: Partial<NapsterCompanionApiConfig>) => Promise<NapsterCompanionApiInstance>;
|
|
79
|
+
/**
|
|
80
|
+
* Build the lazy persistent-agent shell. Returns a {@link ShellController}.
|
|
81
|
+
*
|
|
82
|
+
* A no-op (returns a controller with `isShell: false`) when run inside the shell
|
|
83
|
+
* iframe (framed content), so the same snippet is safe to include on every page.
|
|
84
|
+
*
|
|
85
|
+
* `init` is the SDK's `init` method, wired by the SDK.
|
|
86
|
+
*/
|
|
87
|
+
export declare function mountShell(init: InitFn, options: ShellOptions): ShellController;
|
|
88
|
+
export {};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -335,6 +335,13 @@ export interface NapsterCompanionApiConfig {
|
|
|
335
335
|
functions?: CompanionFunction[];
|
|
336
336
|
/** Enable debug logging throughout the SDK. When enabled, detailed logs will be output to the console. */
|
|
337
337
|
debug?: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* Read WebMCP tools from a same-origin child iframe's `document.modelContext` instead of
|
|
340
|
+
* this window's. Set by the persistent-agent shell (`mountShell` with `persist`) so the
|
|
341
|
+
* avatar in the top-level document operates the SITE running inside the wrapped iframe.
|
|
342
|
+
* Leave unset for a normal same-page integration.
|
|
343
|
+
*/
|
|
344
|
+
edgeMcpIframe?: HTMLIFrameElement;
|
|
338
345
|
/** Analytics configuration controls analytics delivery and tracking. */
|
|
339
346
|
analytics?: AnalyticsConfig;
|
|
340
347
|
/** Lifecycle callbacks. All are optional. */
|
|
@@ -456,6 +463,12 @@ export interface NapsterCompanionApiSDK {
|
|
|
456
463
|
* Returns a Promise that resolves to a controllable `NapsterCompanionApiInstance`.
|
|
457
464
|
*/
|
|
458
465
|
init(token: string, config?: Partial<NapsterCompanionApiConfig>): Promise<NapsterCompanionApiInstance>;
|
|
466
|
+
/**
|
|
467
|
+
* Mount a persistent-agent shell for multi-page apps: renders the site in a
|
|
468
|
+
* same-origin iframe with the avatar in the top-level shell, so the agent
|
|
469
|
+
* session survives navigation. No-op when run as the framed content.
|
|
470
|
+
*/
|
|
471
|
+
mountShell(options: import("../shell/mount-shell").ShellOptions): import("../shell/mount-shell").ShellController;
|
|
459
472
|
/** The SDK version string (useful for diagnostics). */
|
|
460
473
|
version: string;
|
|
461
474
|
}
|