@happyvertical/smrt-web 0.51.38 → 0.52.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/AGENTS.md +1 -0
- package/dist/chunks/{intents-BUTyN7YQ.js → intents-DAjCIR6R.js} +6 -5
- package/dist/chunks/intents-DAjCIR6R.js.map +1 -0
- package/dist/chunks/{src-B2uQWayW.js → src-D6-Y3V-1.js} +12 -5
- package/dist/chunks/{src-B2uQWayW.js.map → src-D6-Y3V-1.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/intents.d.ts.map +1 -1
- package/dist/intents.js +1 -1
- package/dist/webmcp-page-tools.d.ts +123 -0
- package/dist/webmcp-page-tools.d.ts.map +1 -0
- package/dist/webmcp-page-tools.js +152 -0
- package/dist/webmcp-page-tools.js.map +1 -0
- package/dist/webmcp-tool-names.d.ts +11 -0
- package/dist/webmcp-tool-names.d.ts.map +1 -1
- package/dist/webmcp-tool-names.js +9 -1
- package/dist/webmcp-tool-names.js.map +1 -1
- package/dist/webmcp.d.ts +4 -1
- package/dist/webmcp.d.ts.map +1 -1
- package/dist/webmcp.js +1 -1
- package/package.json +7 -3
- package/dist/chunks/intents-BUTyN7YQ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as MAX_SMRT_WEB_DATA_SURFACE_ACTION_IDENTIFIER_LENGTH, B as MAX_SMRT_WEB_DATA_QUERY_OFFSET, C as getOutboxHandle, D as registerDurableResource, E as durableStoreNamespace, F as executeSmrtWebDataSurfaceAction, G as MAX_SMRT_WEB_DATA_QUERY_WARNINGS, H as MAX_SMRT_WEB_DATA_QUERY_RESULT_BYTES, I as normalizeSmrtWebDataSurfaceActionResult, J as runWrapMutation, K as executeSmrtWebDataQuery, L as MAX_SMRT_WEB_DATA_QUERY_CONTAINER_ITEMS, M as MAX_SMRT_WEB_DATA_SURFACE_ACTION_RESULT_BYTES, N as classifySmrtWebDataSurfaceActionResult, O as wipeDurableStore, P as dataSurfaceActionCommandTransport, R as MAX_SMRT_WEB_DATA_QUERY_FACETS, S as persistDataSurface, T as offlineOutbox, U as MAX_SMRT_WEB_DATA_QUERY_ROWS, V as MAX_SMRT_WEB_DATA_QUERY_PAGE_LIMIT, W as MAX_SMRT_WEB_DATA_QUERY_STRING_LENGTH, _ as createSmrtWebEventSubscriber, a as createSmrtWebClient, b as DEFAULT_PERSIST_DEBOUNCE_MS, c as newLocalId, d as unwrapListResult, f as validateSmrtWebClient, g as createUpdateState, h as registerWebMcpTools, i as createSmrtCollection, j as MAX_SMRT_WEB_DATA_SURFACE_ACTION_JSON_DEPTH, k as MAX_SMRT_WEB_DATA_SURFACE_ACTION_CONTAINER_ITEMS, l as throwIfSmrtWebError, m as registerWebMcpBespokeTool, n as buildListQuery, o as getEngineCollection, p as registerViewIntent, q as normalizeSmrtWebDataQueryResult, r as createDefinitionFetchers, s as invalidateSmrtWebCollections, t as SmrtWebRequestError, u as unwrapItemResult, v as liveInvalidation, w as offlineCommandQueue, x as persistCollection, y as createSmrtWebQuery, z as MAX_SMRT_WEB_DATA_QUERY_FACET_VALUES } from "./chunks/src-
|
|
2
|
-
import { n as compileViewIntentToolSpec, o as viewIntentToolName } from "./chunks/intents-
|
|
1
|
+
import { A as MAX_SMRT_WEB_DATA_SURFACE_ACTION_IDENTIFIER_LENGTH, B as MAX_SMRT_WEB_DATA_QUERY_OFFSET, C as getOutboxHandle, D as registerDurableResource, E as durableStoreNamespace, F as executeSmrtWebDataSurfaceAction, G as MAX_SMRT_WEB_DATA_QUERY_WARNINGS, H as MAX_SMRT_WEB_DATA_QUERY_RESULT_BYTES, I as normalizeSmrtWebDataSurfaceActionResult, J as runWrapMutation, K as executeSmrtWebDataQuery, L as MAX_SMRT_WEB_DATA_QUERY_CONTAINER_ITEMS, M as MAX_SMRT_WEB_DATA_SURFACE_ACTION_RESULT_BYTES, N as classifySmrtWebDataSurfaceActionResult, O as wipeDurableStore, P as dataSurfaceActionCommandTransport, R as MAX_SMRT_WEB_DATA_QUERY_FACETS, S as persistDataSurface, T as offlineOutbox, U as MAX_SMRT_WEB_DATA_QUERY_ROWS, V as MAX_SMRT_WEB_DATA_QUERY_PAGE_LIMIT, W as MAX_SMRT_WEB_DATA_QUERY_STRING_LENGTH, _ as createSmrtWebEventSubscriber, a as createSmrtWebClient, b as DEFAULT_PERSIST_DEBOUNCE_MS, c as newLocalId, d as unwrapListResult, f as validateSmrtWebClient, g as createUpdateState, h as registerWebMcpTools, i as createSmrtCollection, j as MAX_SMRT_WEB_DATA_SURFACE_ACTION_JSON_DEPTH, k as MAX_SMRT_WEB_DATA_SURFACE_ACTION_CONTAINER_ITEMS, l as throwIfSmrtWebError, m as registerWebMcpBespokeTool, n as buildListQuery, o as getEngineCollection, p as registerViewIntent, q as normalizeSmrtWebDataQueryResult, r as createDefinitionFetchers, s as invalidateSmrtWebCollections, t as SmrtWebRequestError, u as unwrapItemResult, v as liveInvalidation, w as offlineCommandQueue, x as persistCollection, y as createSmrtWebQuery, z as MAX_SMRT_WEB_DATA_QUERY_FACET_VALUES } from "./chunks/src-D6-Y3V-1.js";
|
|
2
|
+
import { n as compileViewIntentToolSpec, o as viewIntentToolName } from "./chunks/intents-DAjCIR6R.js";
|
|
3
3
|
import { WebMcpToolNameCollisionError, reserveWebMcpToolNames, webMcpToolNameOwner } from "./webmcp-tool-names.js";
|
|
4
4
|
export { DEFAULT_PERSIST_DEBOUNCE_MS, MAX_SMRT_WEB_DATA_QUERY_CONTAINER_ITEMS, MAX_SMRT_WEB_DATA_QUERY_FACETS, MAX_SMRT_WEB_DATA_QUERY_FACET_VALUES, MAX_SMRT_WEB_DATA_QUERY_OFFSET, MAX_SMRT_WEB_DATA_QUERY_PAGE_LIMIT, MAX_SMRT_WEB_DATA_QUERY_RESULT_BYTES, MAX_SMRT_WEB_DATA_QUERY_ROWS, MAX_SMRT_WEB_DATA_QUERY_STRING_LENGTH, MAX_SMRT_WEB_DATA_QUERY_WARNINGS, MAX_SMRT_WEB_DATA_SURFACE_ACTION_CONTAINER_ITEMS, MAX_SMRT_WEB_DATA_SURFACE_ACTION_IDENTIFIER_LENGTH, MAX_SMRT_WEB_DATA_SURFACE_ACTION_JSON_DEPTH, MAX_SMRT_WEB_DATA_SURFACE_ACTION_RESULT_BYTES, SmrtWebRequestError, WebMcpToolNameCollisionError, buildListQuery, classifySmrtWebDataSurfaceActionResult, compileViewIntentToolSpec, createDefinitionFetchers, createSmrtCollection, createSmrtWebClient, createSmrtWebEventSubscriber, createSmrtWebQuery, createUpdateState, dataSurfaceActionCommandTransport, durableStoreNamespace, executeSmrtWebDataQuery, executeSmrtWebDataSurfaceAction, getEngineCollection, getOutboxHandle, invalidateSmrtWebCollections, liveInvalidation, newLocalId, normalizeSmrtWebDataQueryResult, normalizeSmrtWebDataSurfaceActionResult, offlineCommandQueue, offlineOutbox, persistCollection, persistDataSurface, registerDurableResource, registerViewIntent, registerWebMcpBespokeTool, registerWebMcpTools, reserveWebMcpToolNames, runWrapMutation, throwIfSmrtWebError, unwrapItemResult, unwrapListResult, validateSmrtWebClient, viewIntentToolName, webMcpToolNameOwner, wipeDurableStore };
|
package/dist/intents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intents.d.ts","sourceRoot":"","sources":["../src/intents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,8BAA8B,EACnC,KAAK,2BAA2B,EACjC,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"intents.d.ts","sourceRoot":"","sources":["../src/intents.ts"],"names":[],"mappings":"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"}
|
package/dist/intents.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as resolveViewIntent, i as listViewIntents, n as compileViewIntentToolSpec, o as viewIntentToolName, r as defineIntent, t as clearViewIntentRegistry } from "./chunks/intents-
|
|
1
|
+
import { a as resolveViewIntent, i as listViewIntents, n as compileViewIntentToolSpec, o as viewIntentToolName, r as defineIntent, t as clearViewIntentRegistry } from "./chunks/intents-DAjCIR6R.js";
|
|
2
2
|
export { clearViewIntentRegistry, compileViewIntentToolSpec, defineIntent, listViewIntents, resolveViewIntent, viewIntentToolName };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page's own view of its WebMCP tools (#2908).
|
|
3
|
+
*
|
|
4
|
+
* Every browser tool source — generated model tools, the fixed `smrt_ui_*`
|
|
5
|
+
* UI tools, declared view intents, and bespoke `useWebMcpTool` tools — ends
|
|
6
|
+
* at one call: `document.modelContext.registerTool(tool, { signal })`. The
|
|
7
|
+
* browser's registry is write-only from the page's side (an outside agent
|
|
8
|
+
* reads it, page script cannot), so an IN-PAGE assistant has no way to offer
|
|
9
|
+
* the model the same tools an outside agent sees.
|
|
10
|
+
*
|
|
11
|
+
* {@link installWebMcpPageToolRegistry} closes that gap without touching any
|
|
12
|
+
* registrar: it puts a recording `modelContext` on the document. Each
|
|
13
|
+
* `registerTool` is kept in a page registry (and forwarded to the browser's
|
|
14
|
+
* native `modelContext` when there is one, so outside agents are unaffected);
|
|
15
|
+
* aborting the registration's signal removes it from both. The page registry
|
|
16
|
+
* then lists the live tools and runs one by name — the SAME `execute` the
|
|
17
|
+
* browser would call, so every registrar's effect gate, consent gate, and
|
|
18
|
+
* REST authorization stay exactly where they are.
|
|
19
|
+
*
|
|
20
|
+
* On a browser without WebMCP the recording context is the only one, which
|
|
21
|
+
* also makes the page's tools available to the in-page assistant everywhere.
|
|
22
|
+
*
|
|
23
|
+
* Install it before the first registration (early in the app's root layout,
|
|
24
|
+
* before the SMRT Provider mounts): a tool registered earlier went straight to
|
|
25
|
+
* the native context and is invisible here. Installing replaces the
|
|
26
|
+
* document's `modelContext`, which also resets the tool-name lock's table
|
|
27
|
+
* (#2613) — harmless at startup, when it is empty.
|
|
28
|
+
*
|
|
29
|
+
* Dependency-free (only the tool-name lock, itself dependency-free); ships as
|
|
30
|
+
* `@happyvertical/smrt-web/webmcp-page-tools`.
|
|
31
|
+
*/
|
|
32
|
+
import { type WebMcpToolNameOwner } from './webmcp-tool-names.js';
|
|
33
|
+
/** A tool's effect: read runs, write acts, destructive cannot be undone. */
|
|
34
|
+
export type WebMcpPageToolEffect = 'read' | 'write' | 'destructive';
|
|
35
|
+
/**
|
|
36
|
+
* The key a SMRT registrar stamps its RESOLVED effect under on the object it
|
|
37
|
+
* hands `registerTool`. The WebMCP annotations it sends the browser are
|
|
38
|
+
* deliberately coarser — every non-read tool is annotated
|
|
39
|
+
* `destructiveHint: true` for outside agents — so the page registry reads
|
|
40
|
+
* the resolved effect here and only falls back to the annotations for a
|
|
41
|
+
* registration that carries none. A browser ignores the extra symbol key.
|
|
42
|
+
*/
|
|
43
|
+
export declare const WEBMCP_TOOL_EFFECT: unique symbol;
|
|
44
|
+
/** The WebMCP annotation hints a registration may carry. */
|
|
45
|
+
export interface WebMcpPageToolAnnotations {
|
|
46
|
+
readOnlyHint?: boolean;
|
|
47
|
+
destructiveHint?: boolean;
|
|
48
|
+
idempotentHint?: boolean;
|
|
49
|
+
openWorldHint?: boolean;
|
|
50
|
+
untrustedContentHint?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** One live page tool, as the in-page assistant sees it. */
|
|
53
|
+
export interface WebMcpPageToolDescriptor {
|
|
54
|
+
name: string;
|
|
55
|
+
description: string;
|
|
56
|
+
inputSchema: Record<string, unknown>;
|
|
57
|
+
annotations: WebMcpPageToolAnnotations;
|
|
58
|
+
/**
|
|
59
|
+
* The registrar's resolved effect ({@link WEBMCP_TOOL_EFFECT}) when it
|
|
60
|
+
* stamped one. Otherwise from the annotations: `read` for
|
|
61
|
+
* `readOnlyHint: true`, `write` for `destructiveHint: false`, and
|
|
62
|
+
* `destructive` for `destructiveHint: true` or an undeclared effect —
|
|
63
|
+
* smrt-web's own fail-closed classification of a bespoke tool.
|
|
64
|
+
*/
|
|
65
|
+
effect: WebMcpPageToolEffect;
|
|
66
|
+
/**
|
|
67
|
+
* Which registration path owns the name (the tool-name lock's label), when
|
|
68
|
+
* known. DIAGNOSTIC ONLY: a bespoke caller can set this label, so it must
|
|
69
|
+
* never decide whether a write runs — read {@link proposal} for that.
|
|
70
|
+
*/
|
|
71
|
+
owner?: WebMcpToolNameOwner;
|
|
72
|
+
/**
|
|
73
|
+
* True only when the registered `execute` carries the module-private
|
|
74
|
+
* proposal brand (`markWebMcpProposalTool`): a compiled view intent or a
|
|
75
|
+
* fixed `smrt_ui_*` tool, which can only stage or dispatch through a
|
|
76
|
+
* consent-gated registry as `source: 'agent'`. Such a "write" is a proposal
|
|
77
|
+
* by construction; every other write acts.
|
|
78
|
+
*/
|
|
79
|
+
proposal: boolean;
|
|
80
|
+
}
|
|
81
|
+
/** What changed, for {@link WebMcpPageToolRegistry.subscribe}. */
|
|
82
|
+
export interface WebMcpPageToolEvent {
|
|
83
|
+
type: 'registered' | 'unregistered';
|
|
84
|
+
name: string;
|
|
85
|
+
}
|
|
86
|
+
/** The page registry an in-page assistant reads and runs tools through. */
|
|
87
|
+
export interface WebMcpPageToolRegistry {
|
|
88
|
+
/** The live tools, in registration order. */
|
|
89
|
+
list(): WebMcpPageToolDescriptor[];
|
|
90
|
+
get(name: string): WebMcpPageToolDescriptor | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Run a live tool by name through its own registered `execute`.
|
|
93
|
+
* Rejects with {@link WebMcpPageToolNotFoundError} for an unknown name.
|
|
94
|
+
* The caller decides WHETHER to run it (effect and confirmation policy);
|
|
95
|
+
* this only runs it.
|
|
96
|
+
*/
|
|
97
|
+
execute(name: string, args: Record<string, unknown>, options?: {
|
|
98
|
+
signal?: AbortSignal;
|
|
99
|
+
}): Promise<string>;
|
|
100
|
+
subscribe(listener: (event: WebMcpPageToolEvent) => void): () => void;
|
|
101
|
+
/** Restore the document's previous `modelContext` and forget every tool. */
|
|
102
|
+
uninstall(): void;
|
|
103
|
+
}
|
|
104
|
+
/** Thrown by {@link WebMcpPageToolRegistry.execute} for a tool that is not live. */
|
|
105
|
+
export declare class WebMcpPageToolNotFoundError extends Error {
|
|
106
|
+
readonly toolName: string;
|
|
107
|
+
constructor(toolName: string);
|
|
108
|
+
}
|
|
109
|
+
export interface InstallWebMcpPageToolRegistryOptions {
|
|
110
|
+
/** The document to install on. Defaults to `globalThis.document`. */
|
|
111
|
+
document?: object;
|
|
112
|
+
}
|
|
113
|
+
/** Fail-closed effect from WebMCP annotation hints. */
|
|
114
|
+
export declare function webMcpPageToolEffect(annotations: WebMcpPageToolAnnotations | undefined): WebMcpPageToolEffect;
|
|
115
|
+
/** The page registry already installed on `document`, if any. */
|
|
116
|
+
export declare function getWebMcpPageToolRegistry(options?: InstallWebMcpPageToolRegistryOptions): WebMcpPageToolRegistry | undefined;
|
|
117
|
+
/**
|
|
118
|
+
* Install (or return the already-installed) page tool registry on the
|
|
119
|
+
* document. Returns `undefined` off-DOM (SSR), where there is nothing to
|
|
120
|
+
* record. Idempotent: a second call returns the same registry.
|
|
121
|
+
*/
|
|
122
|
+
export declare function installWebMcpPageToolRegistry(options?: InstallWebMcpPageToolRegistryOptions): WebMcpPageToolRegistry | undefined;
|
|
123
|
+
//# sourceMappingURL=webmcp-page-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webmcp-page-tools.d.ts","sourceRoot":"","sources":["../src/webmcp-page-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,wBAAwB,CAAC;AAEhC,4EAA4E;AAC5E,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC;AAEpE;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,EAAE,OAAO,MAE9B,CAAC;AAEX,4DAA4D;AAC5D,MAAM,WAAW,yBAAyB;IACxC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,4DAA4D;AAC5D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,EAAE,yBAAyB,CAAC;IACvC;;;;;;OAMG;IACH,MAAM,EAAE,oBAAoB,CAAC;IAC7B;;;;OAIG;IACH,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B;;;;;;OAMG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAeD,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,YAAY,GAAG,cAAc,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,2EAA2E;AAC3E,MAAM,WAAW,sBAAsB;IACrC,6CAA6C;IAC7C,IAAI,IAAI,wBAAwB,EAAE,CAAC;IACnC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAAC;IACxD;;;;;OAKG;IACH,OAAO,CACL,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACtE,4EAA4E;IAC5E,SAAS,IAAI,IAAI,CAAC;CACnB;AAED,oFAAoF;AACpF,qBAAa,2BAA4B,SAAQ,KAAK;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;CAItC;AAED,MAAM,WAAW,oCAAoC;IACnD,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkBD,uDAAuD;AACvD,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,yBAAyB,GAAG,SAAS,GACjD,oBAAoB,CAQtB;AAWD,iEAAiE;AACjE,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,oCAAyC,GACjD,sBAAsB,GAAG,SAAS,CAMpC;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,oCAAyC,GACjD,sBAAsB,GAAG,SAAS,CAwJpC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { isWebMcpProposalTool, webMcpToolNameOwner } from "./webmcp-tool-names.js";
|
|
2
|
+
//#region src/webmcp-page-tools.ts
|
|
3
|
+
var WEBMCP_TOOL_EFFECT = /* @__PURE__ */ Symbol.for("@happyvertical/smrt-web:webmcp-tool-effect");
|
|
4
|
+
var WebMcpPageToolNotFoundError = class extends Error {
|
|
5
|
+
constructor(toolName) {
|
|
6
|
+
super(`No page tool named "${toolName}" is registered.`);
|
|
7
|
+
this.toolName = toolName;
|
|
8
|
+
this.name = "WebMcpPageToolNotFoundError";
|
|
9
|
+
}
|
|
10
|
+
toolName;
|
|
11
|
+
};
|
|
12
|
+
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("@happyvertical/smrt-web:webmcp-page-tool-registry");
|
|
13
|
+
function resolveDocument(documentLike) {
|
|
14
|
+
const doc = documentLike ?? globalThis.document;
|
|
15
|
+
return doc && typeof doc === "object" ? doc : void 0;
|
|
16
|
+
}
|
|
17
|
+
function webMcpPageToolEffect(annotations) {
|
|
18
|
+
if (annotations?.destructiveHint === true) return "destructive";
|
|
19
|
+
if (annotations?.readOnlyHint === true) return "read";
|
|
20
|
+
if (annotations?.destructiveHint === false) return "write";
|
|
21
|
+
return "destructive";
|
|
22
|
+
}
|
|
23
|
+
function stampedEffect(tool) {
|
|
24
|
+
const value = tool[WEBMCP_TOOL_EFFECT];
|
|
25
|
+
return value === "read" || value === "write" || value === "destructive" ? value : void 0;
|
|
26
|
+
}
|
|
27
|
+
function getWebMcpPageToolRegistry(options = {}) {
|
|
28
|
+
const doc = resolveDocument(options.document);
|
|
29
|
+
if (!doc) return void 0;
|
|
30
|
+
return doc[REGISTRY_KEY];
|
|
31
|
+
}
|
|
32
|
+
function installWebMcpPageToolRegistry(options = {}) {
|
|
33
|
+
const doc = resolveDocument(options.document);
|
|
34
|
+
if (!doc) return void 0;
|
|
35
|
+
const existing = getWebMcpPageToolRegistry({ document: doc });
|
|
36
|
+
if (existing) return existing;
|
|
37
|
+
const slot = doc;
|
|
38
|
+
const previousDescriptor = Object.getOwnPropertyDescriptor(doc, "modelContext");
|
|
39
|
+
const native = slot.modelContext;
|
|
40
|
+
const nativeContext = native && typeof native.registerTool === "function" ? native : void 0;
|
|
41
|
+
const tools = /* @__PURE__ */ new Map();
|
|
42
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
43
|
+
const notify = (event) => {
|
|
44
|
+
for (const listener of listeners) try {
|
|
45
|
+
listener(event);
|
|
46
|
+
} catch {}
|
|
47
|
+
};
|
|
48
|
+
const registerTool = (tool, registerOptions) => {
|
|
49
|
+
const signal = registerOptions?.signal;
|
|
50
|
+
if (signal?.aborted) return;
|
|
51
|
+
const annotations = { ...tool.annotations ?? {} };
|
|
52
|
+
const descriptor = {
|
|
53
|
+
name: tool.name,
|
|
54
|
+
description: tool.description,
|
|
55
|
+
inputSchema: tool.inputSchema,
|
|
56
|
+
annotations,
|
|
57
|
+
effect: stampedEffect(tool) ?? webMcpPageToolEffect(annotations),
|
|
58
|
+
owner: webMcpToolNameOwner(tool.name, { document: doc }),
|
|
59
|
+
proposal: isWebMcpProposalTool(tool.execute)
|
|
60
|
+
};
|
|
61
|
+
const token = {};
|
|
62
|
+
tools.set(tool.name, {
|
|
63
|
+
registration: tool,
|
|
64
|
+
descriptor,
|
|
65
|
+
token
|
|
66
|
+
});
|
|
67
|
+
notify({
|
|
68
|
+
type: "registered",
|
|
69
|
+
name: tool.name
|
|
70
|
+
});
|
|
71
|
+
signal?.addEventListener("abort", () => {
|
|
72
|
+
if (tools.get(tool.name)?.token === token) {
|
|
73
|
+
tools.delete(tool.name);
|
|
74
|
+
notify({
|
|
75
|
+
type: "unregistered",
|
|
76
|
+
name: tool.name
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}, { once: true });
|
|
80
|
+
if (!nativeContext) return;
|
|
81
|
+
try {
|
|
82
|
+
const forwarded = nativeContext.registerTool(tool, registerOptions);
|
|
83
|
+
if (forwarded && typeof forwarded.then === "function") return forwarded.catch((error) => {
|
|
84
|
+
if (tools.get(tool.name)?.token === token) {
|
|
85
|
+
tools.delete(tool.name);
|
|
86
|
+
notify({
|
|
87
|
+
type: "unregistered",
|
|
88
|
+
name: tool.name
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
});
|
|
93
|
+
return forwarded;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (tools.get(tool.name)?.token === token) {
|
|
96
|
+
tools.delete(tool.name);
|
|
97
|
+
notify({
|
|
98
|
+
type: "unregistered",
|
|
99
|
+
name: tool.name
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const context = nativeContext ? new Proxy(nativeContext, { get(target, property) {
|
|
106
|
+
if (property === "registerTool") return registerTool;
|
|
107
|
+
const value = Reflect.get(target, property, target);
|
|
108
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
109
|
+
} }) : { registerTool };
|
|
110
|
+
const registry = {
|
|
111
|
+
list: () => [...tools.values()].map((entry) => ({ ...entry.descriptor })),
|
|
112
|
+
get: (name) => {
|
|
113
|
+
const entry = tools.get(name);
|
|
114
|
+
return entry ? { ...entry.descriptor } : void 0;
|
|
115
|
+
},
|
|
116
|
+
async execute(name, args, executeOptions) {
|
|
117
|
+
const entry = tools.get(name);
|
|
118
|
+
if (!entry) throw new WebMcpPageToolNotFoundError(name);
|
|
119
|
+
return String(await entry.registration.execute(args ?? {}, executeOptions ?? {}));
|
|
120
|
+
},
|
|
121
|
+
subscribe(listener) {
|
|
122
|
+
listeners.add(listener);
|
|
123
|
+
return () => {
|
|
124
|
+
listeners.delete(listener);
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
uninstall() {
|
|
128
|
+
tools.clear();
|
|
129
|
+
listeners.clear();
|
|
130
|
+
if (previousDescriptor) Object.defineProperty(doc, "modelContext", previousDescriptor);
|
|
131
|
+
else delete slot.modelContext;
|
|
132
|
+
delete doc[REGISTRY_KEY];
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
Object.defineProperty(doc, "modelContext", {
|
|
136
|
+
value: context,
|
|
137
|
+
configurable: true,
|
|
138
|
+
enumerable: true,
|
|
139
|
+
writable: true
|
|
140
|
+
});
|
|
141
|
+
Object.defineProperty(doc, REGISTRY_KEY, {
|
|
142
|
+
value: registry,
|
|
143
|
+
configurable: true,
|
|
144
|
+
enumerable: false,
|
|
145
|
+
writable: true
|
|
146
|
+
});
|
|
147
|
+
return registry;
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { WEBMCP_TOOL_EFFECT, WebMcpPageToolNotFoundError, getWebMcpPageToolRegistry, installWebMcpPageToolRegistry, webMcpPageToolEffect };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=webmcp-page-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webmcp-page-tools.js","names":[],"sources":["../src/webmcp-page-tools.ts"],"sourcesContent":["/**\n * The page's own view of its WebMCP tools (#2908).\n *\n * Every browser tool source — generated model tools, the fixed `smrt_ui_*`\n * UI tools, declared view intents, and bespoke `useWebMcpTool` tools — ends\n * at one call: `document.modelContext.registerTool(tool, { signal })`. The\n * browser's registry is write-only from the page's side (an outside agent\n * reads it, page script cannot), so an IN-PAGE assistant has no way to offer\n * the model the same tools an outside agent sees.\n *\n * {@link installWebMcpPageToolRegistry} closes that gap without touching any\n * registrar: it puts a recording `modelContext` on the document. Each\n * `registerTool` is kept in a page registry (and forwarded to the browser's\n * native `modelContext` when there is one, so outside agents are unaffected);\n * aborting the registration's signal removes it from both. The page registry\n * then lists the live tools and runs one by name — the SAME `execute` the\n * browser would call, so every registrar's effect gate, consent gate, and\n * REST authorization stay exactly where they are.\n *\n * On a browser without WebMCP the recording context is the only one, which\n * also makes the page's tools available to the in-page assistant everywhere.\n *\n * Install it before the first registration (early in the app's root layout,\n * before the SMRT Provider mounts): a tool registered earlier went straight to\n * the native context and is invisible here. Installing replaces the\n * document's `modelContext`, which also resets the tool-name lock's table\n * (#2613) — harmless at startup, when it is empty.\n *\n * Dependency-free (only the tool-name lock, itself dependency-free); ships as\n * `@happyvertical/smrt-web/webmcp-page-tools`.\n */\n\nimport {\n isWebMcpProposalTool,\n type WebMcpToolNameOwner,\n webMcpToolNameOwner,\n} from './webmcp-tool-names.js';\n\n/** A tool's effect: read runs, write acts, destructive cannot be undone. */\nexport type WebMcpPageToolEffect = 'read' | 'write' | 'destructive';\n\n/**\n * The key a SMRT registrar stamps its RESOLVED effect under on the object it\n * hands `registerTool`. The WebMCP annotations it sends the browser are\n * deliberately coarser — every non-read tool is annotated\n * `destructiveHint: true` for outside agents — so the page registry reads\n * the resolved effect here and only falls back to the annotations for a\n * registration that carries none. A browser ignores the extra symbol key.\n */\nexport const WEBMCP_TOOL_EFFECT: unique symbol = Symbol.for(\n '@happyvertical/smrt-web:webmcp-tool-effect',\n) as never;\n\n/** The WebMCP annotation hints a registration may carry. */\nexport interface WebMcpPageToolAnnotations {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n untrustedContentHint?: boolean;\n}\n\n/** One live page tool, as the in-page assistant sees it. */\nexport interface WebMcpPageToolDescriptor {\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n annotations: WebMcpPageToolAnnotations;\n /**\n * The registrar's resolved effect ({@link WEBMCP_TOOL_EFFECT}) when it\n * stamped one. Otherwise from the annotations: `read` for\n * `readOnlyHint: true`, `write` for `destructiveHint: false`, and\n * `destructive` for `destructiveHint: true` or an undeclared effect —\n * smrt-web's own fail-closed classification of a bespoke tool.\n */\n effect: WebMcpPageToolEffect;\n /**\n * Which registration path owns the name (the tool-name lock's label), when\n * known. DIAGNOSTIC ONLY: a bespoke caller can set this label, so it must\n * never decide whether a write runs — read {@link proposal} for that.\n */\n owner?: WebMcpToolNameOwner;\n /**\n * True only when the registered `execute` carries the module-private\n * proposal brand (`markWebMcpProposalTool`): a compiled view intent or a\n * fixed `smrt_ui_*` tool, which can only stage or dispatch through a\n * consent-gated registry as `source: 'agent'`. Such a \"write\" is a proposal\n * by construction; every other write acts.\n */\n proposal: boolean;\n}\n\n/** A registration as the registrars hand it to `registerTool`. */\ninterface PageToolRegistration {\n [WEBMCP_TOOL_EFFECT]?: WebMcpPageToolEffect;\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n annotations?: WebMcpPageToolAnnotations;\n execute: (\n args: Record<string, unknown>,\n options?: { signal?: AbortSignal },\n ) => string | Promise<string>;\n}\n\n/** What changed, for {@link WebMcpPageToolRegistry.subscribe}. */\nexport interface WebMcpPageToolEvent {\n type: 'registered' | 'unregistered';\n name: string;\n}\n\n/** The page registry an in-page assistant reads and runs tools through. */\nexport interface WebMcpPageToolRegistry {\n /** The live tools, in registration order. */\n list(): WebMcpPageToolDescriptor[];\n get(name: string): WebMcpPageToolDescriptor | undefined;\n /**\n * Run a live tool by name through its own registered `execute`.\n * Rejects with {@link WebMcpPageToolNotFoundError} for an unknown name.\n * The caller decides WHETHER to run it (effect and confirmation policy);\n * this only runs it.\n */\n execute(\n name: string,\n args: Record<string, unknown>,\n options?: { signal?: AbortSignal },\n ): Promise<string>;\n subscribe(listener: (event: WebMcpPageToolEvent) => void): () => void;\n /** Restore the document's previous `modelContext` and forget every tool. */\n uninstall(): void;\n}\n\n/** Thrown by {@link WebMcpPageToolRegistry.execute} for a tool that is not live. */\nexport class WebMcpPageToolNotFoundError extends Error {\n constructor(readonly toolName: string) {\n super(`No page tool named \"${toolName}\" is registered.`);\n this.name = 'WebMcpPageToolNotFoundError';\n }\n}\n\nexport interface InstallWebMcpPageToolRegistryOptions {\n /** The document to install on. Defaults to `globalThis.document`. */\n document?: object;\n}\n\nconst REGISTRY_KEY = Symbol.for(\n '@happyvertical/smrt-web:webmcp-page-tool-registry',\n);\n\ninterface ModelContextLike {\n registerTool: (\n tool: PageToolRegistration,\n options?: { signal?: AbortSignal },\n ) => void | Promise<void>;\n}\n\nfunction resolveDocument(documentLike: unknown): object | undefined {\n const doc = documentLike ?? (globalThis as { document?: unknown }).document;\n return doc && typeof doc === 'object' ? (doc as object) : undefined;\n}\n\n/** Fail-closed effect from WebMCP annotation hints. */\nexport function webMcpPageToolEffect(\n annotations: WebMcpPageToolAnnotations | undefined,\n): WebMcpPageToolEffect {\n // The same rule smrt-web's own registrar applies to a bespoke tool: an\n // effect is `write` only when the tool says it is not destructive, and an\n // undeclared effect is destructive, like an undeclared custom action.\n if (annotations?.destructiveHint === true) return 'destructive';\n if (annotations?.readOnlyHint === true) return 'read';\n if (annotations?.destructiveHint === false) return 'write';\n return 'destructive';\n}\n\nfunction stampedEffect(\n tool: PageToolRegistration,\n): WebMcpPageToolEffect | undefined {\n const value = tool[WEBMCP_TOOL_EFFECT];\n return value === 'read' || value === 'write' || value === 'destructive'\n ? value\n : undefined;\n}\n\n/** The page registry already installed on `document`, if any. */\nexport function getWebMcpPageToolRegistry(\n options: InstallWebMcpPageToolRegistryOptions = {},\n): WebMcpPageToolRegistry | undefined {\n const doc = resolveDocument(options.document);\n if (!doc) return undefined;\n return (doc as Record<symbol, WebMcpPageToolRegistry | undefined>)[\n REGISTRY_KEY\n ];\n}\n\n/**\n * Install (or return the already-installed) page tool registry on the\n * document. Returns `undefined` off-DOM (SSR), where there is nothing to\n * record. Idempotent: a second call returns the same registry.\n */\nexport function installWebMcpPageToolRegistry(\n options: InstallWebMcpPageToolRegistryOptions = {},\n): WebMcpPageToolRegistry | undefined {\n const doc = resolveDocument(options.document);\n if (!doc) return undefined;\n const existing = getWebMcpPageToolRegistry({ document: doc });\n if (existing) return existing;\n\n const slot = doc as { modelContext?: unknown };\n const previousDescriptor = Object.getOwnPropertyDescriptor(\n doc,\n 'modelContext',\n );\n const native = slot.modelContext;\n const nativeContext =\n native && typeof (native as ModelContextLike).registerTool === 'function'\n ? (native as ModelContextLike)\n : undefined;\n\n const tools = new Map<\n string,\n {\n registration: PageToolRegistration;\n descriptor: WebMcpPageToolDescriptor;\n token: object;\n }\n >();\n const listeners = new Set<(event: WebMcpPageToolEvent) => void>();\n const notify = (event: WebMcpPageToolEvent) => {\n for (const listener of listeners) {\n try {\n listener(event);\n } catch {\n // A listener never breaks registration.\n }\n }\n };\n\n const registerTool = (\n tool: PageToolRegistration,\n registerOptions?: { signal?: AbortSignal },\n ): void | Promise<void> => {\n const signal = registerOptions?.signal;\n if (signal?.aborted) return;\n const annotations = { ...(tool.annotations ?? {}) };\n const descriptor: WebMcpPageToolDescriptor = {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations,\n effect: stampedEffect(tool) ?? webMcpPageToolEffect(annotations),\n // Every SMRT registrar reserves the name before calling registerTool,\n // so the lock already knows who owns it.\n owner: webMcpToolNameOwner(tool.name, { document: doc }),\n proposal: isWebMcpProposalTool(tool.execute),\n };\n const token = {};\n tools.set(tool.name, { registration: tool, descriptor, token });\n notify({ type: 'registered', name: tool.name });\n signal?.addEventListener(\n 'abort',\n () => {\n // Only drop the entry this registration made: a same-name\n // re-registration after the abort must survive it.\n if (tools.get(tool.name)?.token === token) {\n tools.delete(tool.name);\n notify({ type: 'unregistered', name: tool.name });\n }\n },\n { once: true },\n );\n if (!nativeContext) return;\n try {\n const forwarded = nativeContext.registerTool(tool, registerOptions);\n if (\n forwarded &&\n typeof (forwarded as Promise<void>).then === 'function'\n ) {\n return (forwarded as Promise<void>).catch((error: unknown) => {\n // The browser refused it: it is not live for outside agents either,\n // so it is not live here.\n if (tools.get(tool.name)?.token === token) {\n tools.delete(tool.name);\n notify({ type: 'unregistered', name: tool.name });\n }\n throw error;\n });\n }\n return forwarded;\n } catch (error) {\n if (tools.get(tool.name)?.token === token) {\n tools.delete(tool.name);\n notify({ type: 'unregistered', name: tool.name });\n }\n throw error;\n }\n };\n\n // Keep every other native member (and its `this`) reachable through the\n // installed context.\n const context: ModelContextLike = nativeContext\n ? (new Proxy(nativeContext as object, {\n get(target, property) {\n if (property === 'registerTool') return registerTool;\n const value = Reflect.get(target, property, target);\n return typeof value === 'function' ? value.bind(target) : value;\n },\n }) as ModelContextLike)\n : { registerTool };\n\n const registry: WebMcpPageToolRegistry = {\n list: () => [...tools.values()].map((entry) => ({ ...entry.descriptor })),\n get: (name) => {\n const entry = tools.get(name);\n return entry ? { ...entry.descriptor } : undefined;\n },\n async execute(name, args, executeOptions) {\n const entry = tools.get(name);\n if (!entry) throw new WebMcpPageToolNotFoundError(name);\n return String(\n await entry.registration.execute(args ?? {}, executeOptions ?? {}),\n );\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n uninstall() {\n tools.clear();\n listeners.clear();\n if (previousDescriptor) {\n Object.defineProperty(doc, 'modelContext', previousDescriptor);\n } else {\n delete slot.modelContext;\n }\n delete (doc as Record<symbol, unknown>)[REGISTRY_KEY];\n },\n };\n\n Object.defineProperty(doc, 'modelContext', {\n value: context,\n configurable: true,\n enumerable: true,\n writable: true,\n });\n Object.defineProperty(doc, REGISTRY_KEY, {\n value: registry,\n configurable: true,\n enumerable: false,\n writable: true,\n });\n return registry;\n}\n"],"mappings":"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"}
|
|
@@ -103,4 +103,15 @@ export declare function reserveWebMcpToolNames(names: readonly string[], owner:
|
|
|
103
103
|
* {@link reserveWebMcpToolNames}, which is atomic.
|
|
104
104
|
*/
|
|
105
105
|
export declare function webMcpToolNameOwner(name: string, options?: ReserveWebMcpToolNamesOptions): WebMcpToolNameOwner | undefined;
|
|
106
|
+
/**
|
|
107
|
+
* Brand `execute` as proposal-only. FRAMEWORK USE ONLY: call it solely on an
|
|
108
|
+
* `execute` your own code built so that it can only stage or dispatch through
|
|
109
|
+
* a consent-gated registry as `source: 'agent'` (smrt-svelte's
|
|
110
|
+
* `registerWebMcpUiTools` does; `compileViewIntentToolSpec` does internally).
|
|
111
|
+
* A branded write runs in the in-page assistant without a confirmation step,
|
|
112
|
+
* so never brand an `execute` that acts on its own. Returns `execute`.
|
|
113
|
+
*/
|
|
114
|
+
export declare function markWebMcpProposalTool<T extends (...args: never[]) => unknown>(execute: T): T;
|
|
115
|
+
/** Whether `execute` carries the proposal brand. */
|
|
116
|
+
export declare function isWebMcpProposalTool(execute: unknown): boolean;
|
|
106
117
|
//# sourceMappingURL=webmcp-tool-names.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webmcp-tool-names.d.ts","sourceRoot":"","sources":["../src/webmcp-tool-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,iEAAiE;AACjE,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5E;;;;GAIG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;IAEnD,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,mBAAmB;IACnC,sCAAsC;IACtC,QAAQ,CAAC,WAAW,EAAE,mBAAmB;gBAJhC,QAAQ,EAAE,MAAM;IACzB,oDAAoD;IAC3C,KAAK,EAAE,mBAAmB;IACnC,sCAAsC;IAC7B,WAAW,EAAE,mBAAmB;CAO5C;AAED,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,6BAA6B;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA2CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,6BAAkC,GAC1C,yBAAyB,CAqC3B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,6BAAkC,GAC1C,mBAAmB,GAAG,SAAS,CAIjC"}
|
|
1
|
+
{"version":3,"file":"webmcp-tool-names.d.ts","sourceRoot":"","sources":["../src/webmcp-tool-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,iEAAiE;AACjE,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE5E;;;;GAIG;AACH,qBAAa,4BAA6B,SAAQ,KAAK;IAEnD,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,mBAAmB;IACnC,sCAAsC;IACtC,QAAQ,CAAC,WAAW,EAAE,mBAAmB;gBAJhC,QAAQ,EAAE,MAAM;IACzB,oDAAoD;IAC3C,KAAK,EAAE,mBAAmB;IACnC,sCAAsC;IAC7B,WAAW,EAAE,mBAAmB;CAO5C;AAED,6DAA6D;AAC7D,MAAM,WAAW,yBAAyB;IACxC,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,6BAA6B;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA2CD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,6BAAkC,GAC1C,yBAAyB,CAqC3B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,6BAAkC,GAC1C,mBAAmB,GAAG,SAAS,CAIjC;AAmBD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,EAC5E,OAAO,EAAE,CAAC,GACT,CAAC,CAGH;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAI9D"}
|
|
@@ -67,7 +67,15 @@ function webMcpToolNameOwner(name, options = {}) {
|
|
|
67
67
|
if (!doc) return void 0;
|
|
68
68
|
return tableFor(doc).names.get(name)?.owner;
|
|
69
69
|
}
|
|
70
|
+
var proposalExecutes = /* @__PURE__ */ new WeakSet();
|
|
71
|
+
function markWebMcpProposalTool(execute) {
|
|
72
|
+
proposalExecutes.add(execute);
|
|
73
|
+
return execute;
|
|
74
|
+
}
|
|
75
|
+
function isWebMcpProposalTool(execute) {
|
|
76
|
+
return typeof execute === "function" && proposalExecutes.has(execute);
|
|
77
|
+
}
|
|
70
78
|
//#endregion
|
|
71
|
-
export { WebMcpToolNameCollisionError, reserveWebMcpToolNames, webMcpToolNameOwner };
|
|
79
|
+
export { WebMcpToolNameCollisionError, isWebMcpProposalTool, markWebMcpProposalTool, reserveWebMcpToolNames, webMcpToolNameOwner };
|
|
72
80
|
|
|
73
81
|
//# sourceMappingURL=webmcp-tool-names.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webmcp-tool-names.js","names":[],"sources":["../src/webmcp-tool-names.ts"],"sourcesContent":["/**\n * Document-global WebMCP tool-name lock (#2613).\n *\n * A browser tool name is derived independently on three paths that all end at\n * the same `document.modelContext`:\n *\n * 1. **generated** model tools — `<namespace>_<model>_<action>`, built by\n * `registerWebMcpTools` from the exposure policy;\n * 2. **ui** — the six fixed `smrt_ui_*` tools a UI layer registers under a\n * configurable prefix (`registerWebMcpUiTools` in `@happyvertical/smrt-svelte`);\n * 3. **intent** / **bespoke** — declared view intents (#2588) and hand-written\n * `useWebMcpTool` tools, whose names come from the declaration `id` or the\n * spec `name`.\n *\n * None of the three can see the others at declaration time: a namespace and a\n * UI prefix are runtime values a declaration never sees. `registerWebMcpTools`\n * rejects duplicates WITHIN its own prospective set, but nothing checked\n * ACROSS the paths, so a collision reached the host — which rejects the later\n * registration and leaves the tool silently absent.\n *\n * This module is the shared reservation table those paths coordinate through.\n * It is an AVAILABILITY guard, not a trust boundary: every path stays\n * fail-closed and browser-only regardless, and a collision loses a tool rather\n * than granting one. The authenticated REST surface remains the auth, tenant,\n * and field-write boundary.\n *\n * ## Why the table lives on the document, not in this module\n *\n * The lock must be shared by every registrar reachable from one page, which\n * spans package boundaries (`smrt-web` and a UI layer) and therefore possibly\n * more than one copy of this module — a second bundle chunk, a duplicated\n * dependency, or an HMR reload that replaces module state while the host\n * registry keeps every tool it already accepted. Module-level state loses the\n * reservations in all of those cases, and a lost reservation is worse than no\n * lock: it lets a duplicate through, or (on the release side) strands a name\n * forever. The table is therefore stored on the `document` object itself under\n * a cross-realm `Symbol.for` key, which every copy of this module resolves to\n * the same slot.\n *\n * The table is additionally stamped with the `modelContext` it was built for.\n * When a host installs a NEW model context on the same document — a fresh\n * origin-trial registry, or a test harness swapping the double — every tool\n * the old registry held is gone, so the table resets rather than stranding\n * those names.\n *\n * This module is dependency-free by design (see the package's dependency-DAG\n * guardrails) and ships as the `@happyvertical/smrt-web/webmcp-tool-names`\n * entry so a UI layer can reserve its fixed tool names without pulling in the\n * client-data engine.\n */\n\n/** Which registration path holds a reserved WebMCP tool name. */\nexport type WebMcpToolNameOwner = 'generated' | 'ui' | 'intent' | 'bespoke';\n\n/**\n * Thrown synchronously when a name is already held. Carries the colliding\n * name and the owner that holds it so a host can report both without parsing\n * the message.\n */\nexport class WebMcpToolNameCollisionError extends Error {\n constructor(\n readonly toolName: string,\n /** The path that already holds {@link toolName}. */\n readonly owner: WebMcpToolNameOwner,\n /** The path that tried to take it. */\n readonly requestedBy: WebMcpToolNameOwner,\n ) {\n super(\n `WebMCP tool name \"${toolName}\" is already registered by the ${owner} path; the ${requestedBy} path cannot register it. Rename the ${requestedBy} tool, or give the generated tools a webmcp namespace / the UI tools a different prefix.`,\n );\n this.name = 'WebMcpToolNameCollisionError';\n }\n}\n\n/** Releases every name one reservation holds. Idempotent. */\nexport interface WebMcpToolNameReservation {\n release(): void;\n}\n\nexport interface ReserveWebMcpToolNamesOptions {\n /**\n * The document whose `modelContext` the names are registered against.\n * Defaults to `globalThis.document`. Registrars that accept an injectable\n * document (tests, non-window hosts) must pass the SAME object they read\n * `modelContext` from, or their reservations land in a different table.\n */\n document?: unknown;\n}\n\n/** A held name plus the token identifying the reservation that holds it. */\ninterface HeldName {\n owner: WebMcpToolNameOwner;\n token: object;\n}\n\ninterface ToolNameTable {\n /** The model context these reservations were taken against. */\n modelContext: unknown;\n names: Map<string, HeldName>;\n}\n\nconst TABLE_KEY = Symbol.for('@happyvertical/smrt-web:webmcp-tool-names');\n\nconst INERT_RESERVATION: WebMcpToolNameReservation = { release: () => {} };\n\nfunction resolveDocument(documentLike: unknown): object | undefined {\n const doc =\n documentLike ??\n (globalThis as { document?: unknown }).document ??\n undefined;\n return doc && typeof doc === 'object' ? (doc as object) : undefined;\n}\n\nfunction tableFor(doc: object): ToolNameTable {\n const slot = doc as Record<symbol, ToolNameTable | undefined>;\n const modelContext = (doc as { modelContext?: unknown }).modelContext;\n const existing = slot[TABLE_KEY];\n // A replaced model context means the host registry that held the old\n // reservations no longer exists; start clean rather than stranding names.\n if (existing && existing.modelContext === modelContext) return existing;\n const table: ToolNameTable = { modelContext, names: new Map() };\n Object.defineProperty(doc, TABLE_KEY, {\n value: table,\n configurable: true,\n enumerable: false,\n writable: true,\n });\n return table;\n}\n\n/**\n * Reserve `names` for `owner` against the document's WebMCP registry.\n *\n * Reservation is ALL-OR-NOTHING: if any name is already held — by this path\n * or another — nothing is reserved and a {@link WebMcpToolNameCollisionError}\n * naming the colliding name and its current owner is thrown synchronously,\n * before any tool reaches `document.modelContext`. Duplicates within `names`\n * itself collide the same way.\n *\n * Off-WebMCP (no document) the call is a no-op returning an inert\n * reservation, matching every registrar's own no-op guarantee.\n *\n * A reservation releases only the names it took: `release()` never drops a\n * name a later reservation has since acquired, so a dispose that lands after\n * a same-name re-registration cannot revoke the new holder's claim.\n */\nexport function reserveWebMcpToolNames(\n names: readonly string[],\n owner: WebMcpToolNameOwner,\n options: ReserveWebMcpToolNamesOptions = {},\n): WebMcpToolNameReservation {\n const doc = resolveDocument(options.document);\n if (!doc) return INERT_RESERVATION;\n\n const table = tableFor(doc);\n const token = {};\n const taken: string[] = [];\n const drop = (): void => {\n for (const name of taken) {\n // Only ever drop a name this reservation still holds. Together with the\n // `released` latch below, a stale handle can neither release twice nor\n // revoke a claim a later reservation has since taken under the same name.\n if (table.names.get(name)?.token === token) table.names.delete(name);\n }\n };\n\n for (const name of names) {\n const held = table.names.get(name);\n if (held) {\n // All-or-nothing: give back everything taken so far, so a rejected\n // reservation leaves no name stranded and the caller can retry with a\n // corrected set.\n drop();\n throw new WebMcpToolNameCollisionError(name, held.owner, owner);\n }\n table.names.set(name, { owner, token });\n taken.push(name);\n }\n\n let released = false;\n return {\n release: () => {\n if (released) return;\n released = true;\n drop();\n },\n };\n}\n\n/**\n * The owner currently holding `name` on this document, or undefined. For\n * diagnostics and tests; registration paths use\n * {@link reserveWebMcpToolNames}, which is atomic.\n */\nexport function webMcpToolNameOwner(\n name: string,\n options: ReserveWebMcpToolNamesOptions = {},\n): WebMcpToolNameOwner | undefined {\n const doc = resolveDocument(options.document);\n if (!doc) return undefined;\n return tableFor(doc).names.get(name)?.owner;\n}\n"],"mappings":";AA2DO,IAAM,+BAAN,cAA2C,MAAM;CACtD,YACW,UAEA,OAEA,aACT;EACA,MACE,qBAAqB,SAAQ,iCAAkC,MAAK,aAAc,YAAW,uCAAwC,YAAW,yFAClJ;EARS,KAAA,WAAA;EAEA,KAAA,QAAA;EAEA,KAAA,cAAA;EAKT,KAAK,OAAO;CACd;CAVW;CAEA;CAEA;AAOb;AA6BA,IAAM,YAAY,uBAAO,IAAI,2CAA2C;AAExE,IAAM,oBAA+C,EAAE,eAAe,CAAC,EAAE;AAEzE,SAAS,gBAAgB,cAA2C;CAClE,MAAM,MACJ,gBACC,WAAsC,YACvC,KAAA;CACF,OAAO,OAAO,OAAO,QAAQ,WAAY,MAAiB,KAAA;AAC5D;AAEA,SAAS,SAAS,KAA4B;CAC5C,MAAM,OAAO;CACb,MAAM,eAAgB,IAAmC;CACzD,MAAM,WAAW,KAAK;CAGtB,IAAI,YAAY,SAAS,iBAAiB,cAAc,OAAO;CAC/D,MAAM,QAAuB;EAAE;EAAc,uBAAO,IAAI,IAAI;CAAE;CAC9D,OAAO,eAAe,KAAK,WAAW;EACpC,OAAO;EACP,cAAc;EACd,YAAY;EACZ,UAAU;CACZ,CAAC;CACD,OAAO;AACT;AAkBO,SAAS,uBACd,OACA,OACA,UAAyC,CAAC,GACf;CAC3B,MAAM,MAAM,gBAAgB,QAAQ,QAAQ;CAC5C,IAAI,CAAC,KAAK,OAAO;CAEjB,MAAM,QAAQ,SAAS,GAAG;CAC1B,MAAM,QAAQ,CAAC;CACf,MAAM,QAAkB,CAAC;CACzB,MAAM,aAAmB;EACvB,KAAA,MAAW,QAAQ,OAIjB,IAAI,MAAM,MAAM,IAAI,IAAI,CAAA,EAAG,UAAU,OAAO,MAAM,MAAM,OAAO,IAAI;CAEvE;CAEA,KAAA,MAAW,QAAQ,OAAO;EACxB,MAAM,OAAO,MAAM,MAAM,IAAI,IAAI;EACjC,IAAI,MAAM;GAIR,KAAK;GACL,MAAM,IAAI,6BAA6B,MAAM,KAAK,OAAO,KAAK;EAChE;EACA,MAAM,MAAM,IAAI,MAAM;GAAE;GAAO;EAAM,CAAC;EACtC,MAAM,KAAK,IAAI;CACjB;CAEA,IAAI,WAAW;CACf,OAAO,EACL,eAAe;EACb,IAAI,UAAU;EACd,WAAW;EACX,KAAK;CACP,EACF;AACF;AAOO,SAAS,oBACd,MACA,UAAyC,CAAC,GACT;CACjC,MAAM,MAAM,gBAAgB,QAAQ,QAAQ;CAC5C,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,OAAO,SAAS,GAAG,CAAA,CAAE,MAAM,IAAI,IAAI,CAAA,EAAG;AACxC"}
|
|
1
|
+
{"version":3,"file":"webmcp-tool-names.js","names":[],"sources":["../src/webmcp-tool-names.ts"],"sourcesContent":["/**\n * Document-global WebMCP tool-name lock (#2613).\n *\n * A browser tool name is derived independently on three paths that all end at\n * the same `document.modelContext`:\n *\n * 1. **generated** model tools — `<namespace>_<model>_<action>`, built by\n * `registerWebMcpTools` from the exposure policy;\n * 2. **ui** — the six fixed `smrt_ui_*` tools a UI layer registers under a\n * configurable prefix (`registerWebMcpUiTools` in `@happyvertical/smrt-svelte`);\n * 3. **intent** / **bespoke** — declared view intents (#2588) and hand-written\n * `useWebMcpTool` tools, whose names come from the declaration `id` or the\n * spec `name`.\n *\n * None of the three can see the others at declaration time: a namespace and a\n * UI prefix are runtime values a declaration never sees. `registerWebMcpTools`\n * rejects duplicates WITHIN its own prospective set, but nothing checked\n * ACROSS the paths, so a collision reached the host — which rejects the later\n * registration and leaves the tool silently absent.\n *\n * This module is the shared reservation table those paths coordinate through.\n * It is an AVAILABILITY guard, not a trust boundary: every path stays\n * fail-closed and browser-only regardless, and a collision loses a tool rather\n * than granting one. The authenticated REST surface remains the auth, tenant,\n * and field-write boundary.\n *\n * ## Why the table lives on the document, not in this module\n *\n * The lock must be shared by every registrar reachable from one page, which\n * spans package boundaries (`smrt-web` and a UI layer) and therefore possibly\n * more than one copy of this module — a second bundle chunk, a duplicated\n * dependency, or an HMR reload that replaces module state while the host\n * registry keeps every tool it already accepted. Module-level state loses the\n * reservations in all of those cases, and a lost reservation is worse than no\n * lock: it lets a duplicate through, or (on the release side) strands a name\n * forever. The table is therefore stored on the `document` object itself under\n * a cross-realm `Symbol.for` key, which every copy of this module resolves to\n * the same slot.\n *\n * The table is additionally stamped with the `modelContext` it was built for.\n * When a host installs a NEW model context on the same document — a fresh\n * origin-trial registry, or a test harness swapping the double — every tool\n * the old registry held is gone, so the table resets rather than stranding\n * those names.\n *\n * This module is dependency-free by design (see the package's dependency-DAG\n * guardrails) and ships as the `@happyvertical/smrt-web/webmcp-tool-names`\n * entry so a UI layer can reserve its fixed tool names without pulling in the\n * client-data engine.\n */\n\n/** Which registration path holds a reserved WebMCP tool name. */\nexport type WebMcpToolNameOwner = 'generated' | 'ui' | 'intent' | 'bespoke';\n\n/**\n * Thrown synchronously when a name is already held. Carries the colliding\n * name and the owner that holds it so a host can report both without parsing\n * the message.\n */\nexport class WebMcpToolNameCollisionError extends Error {\n constructor(\n readonly toolName: string,\n /** The path that already holds {@link toolName}. */\n readonly owner: WebMcpToolNameOwner,\n /** The path that tried to take it. */\n readonly requestedBy: WebMcpToolNameOwner,\n ) {\n super(\n `WebMCP tool name \"${toolName}\" is already registered by the ${owner} path; the ${requestedBy} path cannot register it. Rename the ${requestedBy} tool, or give the generated tools a webmcp namespace / the UI tools a different prefix.`,\n );\n this.name = 'WebMcpToolNameCollisionError';\n }\n}\n\n/** Releases every name one reservation holds. Idempotent. */\nexport interface WebMcpToolNameReservation {\n release(): void;\n}\n\nexport interface ReserveWebMcpToolNamesOptions {\n /**\n * The document whose `modelContext` the names are registered against.\n * Defaults to `globalThis.document`. Registrars that accept an injectable\n * document (tests, non-window hosts) must pass the SAME object they read\n * `modelContext` from, or their reservations land in a different table.\n */\n document?: unknown;\n}\n\n/** A held name plus the token identifying the reservation that holds it. */\ninterface HeldName {\n owner: WebMcpToolNameOwner;\n token: object;\n}\n\ninterface ToolNameTable {\n /** The model context these reservations were taken against. */\n modelContext: unknown;\n names: Map<string, HeldName>;\n}\n\nconst TABLE_KEY = Symbol.for('@happyvertical/smrt-web:webmcp-tool-names');\n\nconst INERT_RESERVATION: WebMcpToolNameReservation = { release: () => {} };\n\nfunction resolveDocument(documentLike: unknown): object | undefined {\n const doc =\n documentLike ??\n (globalThis as { document?: unknown }).document ??\n undefined;\n return doc && typeof doc === 'object' ? (doc as object) : undefined;\n}\n\nfunction tableFor(doc: object): ToolNameTable {\n const slot = doc as Record<symbol, ToolNameTable | undefined>;\n const modelContext = (doc as { modelContext?: unknown }).modelContext;\n const existing = slot[TABLE_KEY];\n // A replaced model context means the host registry that held the old\n // reservations no longer exists; start clean rather than stranding names.\n if (existing && existing.modelContext === modelContext) return existing;\n const table: ToolNameTable = { modelContext, names: new Map() };\n Object.defineProperty(doc, TABLE_KEY, {\n value: table,\n configurable: true,\n enumerable: false,\n writable: true,\n });\n return table;\n}\n\n/**\n * Reserve `names` for `owner` against the document's WebMCP registry.\n *\n * Reservation is ALL-OR-NOTHING: if any name is already held — by this path\n * or another — nothing is reserved and a {@link WebMcpToolNameCollisionError}\n * naming the colliding name and its current owner is thrown synchronously,\n * before any tool reaches `document.modelContext`. Duplicates within `names`\n * itself collide the same way.\n *\n * Off-WebMCP (no document) the call is a no-op returning an inert\n * reservation, matching every registrar's own no-op guarantee.\n *\n * A reservation releases only the names it took: `release()` never drops a\n * name a later reservation has since acquired, so a dispose that lands after\n * a same-name re-registration cannot revoke the new holder's claim.\n */\nexport function reserveWebMcpToolNames(\n names: readonly string[],\n owner: WebMcpToolNameOwner,\n options: ReserveWebMcpToolNamesOptions = {},\n): WebMcpToolNameReservation {\n const doc = resolveDocument(options.document);\n if (!doc) return INERT_RESERVATION;\n\n const table = tableFor(doc);\n const token = {};\n const taken: string[] = [];\n const drop = (): void => {\n for (const name of taken) {\n // Only ever drop a name this reservation still holds. Together with the\n // `released` latch below, a stale handle can neither release twice nor\n // revoke a claim a later reservation has since taken under the same name.\n if (table.names.get(name)?.token === token) table.names.delete(name);\n }\n };\n\n for (const name of names) {\n const held = table.names.get(name);\n if (held) {\n // All-or-nothing: give back everything taken so far, so a rejected\n // reservation leaves no name stranded and the caller can retry with a\n // corrected set.\n drop();\n throw new WebMcpToolNameCollisionError(name, held.owner, owner);\n }\n table.names.set(name, { owner, token });\n taken.push(name);\n }\n\n let released = false;\n return {\n release: () => {\n if (released) return;\n released = true;\n drop();\n },\n };\n}\n\n/**\n * The owner currently holding `name` on this document, or undefined. For\n * diagnostics and tests; registration paths use\n * {@link reserveWebMcpToolNames}, which is atomic.\n */\nexport function webMcpToolNameOwner(\n name: string,\n options: ReserveWebMcpToolNamesOptions = {},\n): WebMcpToolNameOwner | undefined {\n const doc = resolveDocument(options.document);\n if (!doc) return undefined;\n return tableFor(doc).names.get(name)?.owner;\n}\n\n/**\n * Proposal brand (#2908 hardening).\n *\n * An in-page assistant may run a `write` tool without asking only when that\n * tool's `execute` can do nothing but stage a value or dispatch a command as\n * `source: 'agent'` through a consent-gated registry — a compiled view intent\n * or one of the fixed `smrt_ui_*` tools. The tool-name lock's `owner` label\n * cannot carry that trust: it is a diagnostic any `registerWebMcpBespokeTool`\n * / `useWebMcpTool` caller can set.\n *\n * The brand is therefore held in this module-private `WeakSet`, keyed by the\n * `execute` function itself — not a property, and not a `Symbol.for` key a\n * caller could forge on a spec. A duplicated copy of this module holds its own\n * set, so a mismatched copy fails CLOSED (the tool asks) rather than open.\n */\nconst proposalExecutes = new WeakSet<object>();\n\n/**\n * Brand `execute` as proposal-only. FRAMEWORK USE ONLY: call it solely on an\n * `execute` your own code built so that it can only stage or dispatch through\n * a consent-gated registry as `source: 'agent'` (smrt-svelte's\n * `registerWebMcpUiTools` does; `compileViewIntentToolSpec` does internally).\n * A branded write runs in the in-page assistant without a confirmation step,\n * so never brand an `execute` that acts on its own. Returns `execute`.\n */\nexport function markWebMcpProposalTool<T extends (...args: never[]) => unknown>(\n execute: T,\n): T {\n proposalExecutes.add(execute);\n return execute;\n}\n\n/** Whether `execute` carries the proposal brand. */\nexport function isWebMcpProposalTool(execute: unknown): boolean {\n return (\n typeof execute === 'function' && proposalExecutes.has(execute as object)\n );\n}\n"],"mappings":";AA2DO,IAAM,+BAAN,cAA2C,MAAM;CACtD,YACW,UAEA,OAEA,aACT;EACA,MACE,qBAAqB,SAAQ,iCAAkC,MAAK,aAAc,YAAW,uCAAwC,YAAW,yFAClJ;EARS,KAAA,WAAA;EAEA,KAAA,QAAA;EAEA,KAAA,cAAA;EAKT,KAAK,OAAO;CACd;CAVW;CAEA;CAEA;AAOb;AA6BA,IAAM,YAAY,uBAAO,IAAI,2CAA2C;AAExE,IAAM,oBAA+C,EAAE,eAAe,CAAC,EAAE;AAEzE,SAAS,gBAAgB,cAA2C;CAClE,MAAM,MACJ,gBACC,WAAsC,YACvC,KAAA;CACF,OAAO,OAAO,OAAO,QAAQ,WAAY,MAAiB,KAAA;AAC5D;AAEA,SAAS,SAAS,KAA4B;CAC5C,MAAM,OAAO;CACb,MAAM,eAAgB,IAAmC;CACzD,MAAM,WAAW,KAAK;CAGtB,IAAI,YAAY,SAAS,iBAAiB,cAAc,OAAO;CAC/D,MAAM,QAAuB;EAAE;EAAc,uBAAO,IAAI,IAAI;CAAE;CAC9D,OAAO,eAAe,KAAK,WAAW;EACpC,OAAO;EACP,cAAc;EACd,YAAY;EACZ,UAAU;CACZ,CAAC;CACD,OAAO;AACT;AAkBO,SAAS,uBACd,OACA,OACA,UAAyC,CAAC,GACf;CAC3B,MAAM,MAAM,gBAAgB,QAAQ,QAAQ;CAC5C,IAAI,CAAC,KAAK,OAAO;CAEjB,MAAM,QAAQ,SAAS,GAAG;CAC1B,MAAM,QAAQ,CAAC;CACf,MAAM,QAAkB,CAAC;CACzB,MAAM,aAAmB;EACvB,KAAA,MAAW,QAAQ,OAIjB,IAAI,MAAM,MAAM,IAAI,IAAI,CAAA,EAAG,UAAU,OAAO,MAAM,MAAM,OAAO,IAAI;CAEvE;CAEA,KAAA,MAAW,QAAQ,OAAO;EACxB,MAAM,OAAO,MAAM,MAAM,IAAI,IAAI;EACjC,IAAI,MAAM;GAIR,KAAK;GACL,MAAM,IAAI,6BAA6B,MAAM,KAAK,OAAO,KAAK;EAChE;EACA,MAAM,MAAM,IAAI,MAAM;GAAE;GAAO;EAAM,CAAC;EACtC,MAAM,KAAK,IAAI;CACjB;CAEA,IAAI,WAAW;CACf,OAAO,EACL,eAAe;EACb,IAAI,UAAU;EACd,WAAW;EACX,KAAK;CACP,EACF;AACF;AAOO,SAAS,oBACd,MACA,UAAyC,CAAC,GACT;CACjC,MAAM,MAAM,gBAAgB,QAAQ,QAAQ;CAC5C,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,OAAO,SAAS,GAAG,CAAA,CAAE,MAAM,IAAI,IAAI,CAAA,EAAG;AACxC;AAiBA,IAAM,mCAAmB,IAAI,QAAgB;AAUtC,SAAS,uBACd,SACG;CACH,iBAAiB,IAAI,OAAO;CAC5B,OAAO;AACT;AAGO,SAAS,qBAAqB,SAA2B;CAC9D,OACE,OAAO,YAAY,cAAc,iBAAiB,IAAI,OAAiB;AAE3E"}
|
package/dist/webmcp.d.ts
CHANGED
|
@@ -93,7 +93,10 @@ export interface RegisterWebMcpBespokeToolOptions {
|
|
|
93
93
|
* blame a `useWebMcpTool` call that does not exist.
|
|
94
94
|
*
|
|
95
95
|
* Purely a diagnostic label: it grants no capability, narrows no policy,
|
|
96
|
-
* and changes nothing about how the tool registers or executes.
|
|
96
|
+
* and changes nothing about how the tool registers or executes. In
|
|
97
|
+
* particular it does NOT make a write proposal-only for the in-page
|
|
98
|
+
* assistant: only a compiled view intent's branded `execute` does (see
|
|
99
|
+
* `markWebMcpProposalTool`).
|
|
97
100
|
*/
|
|
98
101
|
owner?: Extract<WebMcpToolNameOwner, 'intent' | 'bespoke'>;
|
|
99
102
|
}
|
package/dist/webmcp.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webmcp.d.ts","sourceRoot":"","sources":["../src/webmcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAElB,KAAK,2BAA2B,EAMhC,KAAK,oBAAoB,EAE1B,MAAM,YAAY,CAAC;AACpB,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"webmcp.d.ts","sourceRoot":"","sources":["../src/webmcp.ts"],"names":[],"mappings":"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"}
|
package/dist/webmcp.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { h as registerWebMcpTools, m as registerWebMcpBespokeTool, p as registerViewIntent } from "./chunks/src-
|
|
1
|
+
import { h as registerWebMcpTools, m as registerWebMcpBespokeTool, p as registerViewIntent } from "./chunks/src-D6-Y3V-1.js";
|
|
2
2
|
import { WebMcpToolNameCollisionError, reserveWebMcpToolNames, webMcpToolNameOwner } from "./webmcp-tool-names.js";
|
|
3
3
|
export { WebMcpToolNameCollisionError, registerViewIntent, registerWebMcpBespokeTool, registerWebMcpTools, reserveWebMcpToolNames, webMcpToolNameOwner };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "SMRT browser client data runtime: typed collection factory wrapping the client-data engine over generated REST clients",
|
|
5
5
|
"author": "HappyVertical",
|
|
6
6
|
"type": "module",
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"./webmcp-tool-names": {
|
|
33
33
|
"types": "./dist/webmcp-tool-names.d.ts",
|
|
34
34
|
"import": "./dist/webmcp-tool-names.js"
|
|
35
|
+
},
|
|
36
|
+
"./webmcp-page-tools": {
|
|
37
|
+
"types": "./dist/webmcp-page-tools.d.ts",
|
|
38
|
+
"import": "./dist/webmcp-page-tools.js"
|
|
35
39
|
}
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
@@ -40,8 +44,8 @@
|
|
|
40
44
|
"@tanstack/query-db-collection": "^1.0.46"
|
|
41
45
|
},
|
|
42
46
|
"devDependencies": {
|
|
43
|
-
"@happyvertical/smrt-core": "0.
|
|
44
|
-
"@happyvertical/smrt-scanner": "0.
|
|
47
|
+
"@happyvertical/smrt-core": "0.52.0",
|
|
48
|
+
"@happyvertical/smrt-scanner": "0.52.0",
|
|
45
49
|
"@types/node": "24.13.6",
|
|
46
50
|
"fake-indexeddb": "^6.2.5",
|
|
47
51
|
"typescript": "6.0.3",
|