@happyvertical/smrt-web 0.43.9 → 0.44.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/dist/webmcp.d.ts CHANGED
@@ -1,3 +1,48 @@
1
+ /**
2
+ * Register one declared view intent (#2588) against a mounted registry
3
+ * binding, for as long as the returned disposer is not called.
4
+ *
5
+ * This is the ONLY way an intent reaches the browser, and it goes through
6
+ * {@link registerWebMcpBespokeTool}, so an intent inherits the same
7
+ * fail-closed `effects` exposure policy as a generated model tool. The
8
+ * intent's classification was resolved at declaration time by the shared
9
+ * rule, and the hints `compileViewIntentToolSpec` emits round-trip through
10
+ * the bespoke registrar's own re-resolution unchanged.
11
+ *
12
+ * The `execute` registered here is constructed by
13
+ * `compileViewIntentToolSpec` from `intent.target`. No author-supplied code
14
+ * runs, and the only thing it can do is dispatch one browser registry
15
+ * command — the runtime half of the no-REST invariant.
16
+ */
17
+ export declare function registerViewIntent(intent: ViewIntent, binding: ViewIntentBinding, options?: RegisterWebMcpBespokeToolOptions): WebMcpRegistrationDisposer;
18
+
19
+ /**
20
+ * Register one hand-written browser tool through the same fail-closed effect
21
+ * classification and `effects` exposure policy as {@link registerWebMcpTools}
22
+ * (#2586). A tool with no `annotations`, or with annotations that leave its
23
+ * effect undeclared, classifies destructive/non-idempotent/open-world — the
24
+ * same default `actionSemantics` gives an undeclared custom model action —
25
+ * and is excluded unless the policy allows `destructive`. `namespace` and
26
+ * `maxTools` are out of scope for a bespoke tool; see
27
+ * {@link RegisterWebMcpBespokeToolOptions}.
28
+ *
29
+ * @returns a disposer that deregisters the tool this call registered (a
30
+ * no-op double-call). On a browser without WebMCP, or when policy excludes
31
+ * the tool's effect, the call is a no-op and the disposer is inert.
32
+ */
33
+ export declare function registerWebMcpBespokeTool(spec: WebMcpBespokeToolSpec, options?: RegisterWebMcpBespokeToolOptions): WebMcpRegistrationDisposer;
34
+
35
+ export declare interface RegisterWebMcpBespokeToolOptions {
36
+ /**
37
+ * Allowed effects. Omitted means read-only exposure — the same default as
38
+ * {@link registerWebMcpTools}. `namespace` and `maxTools` deliberately do
39
+ * not apply to a bespoke tool (#2586): a component author already chose a
40
+ * stable name, and counting one intent against a shared budget could make
41
+ * an unrelated generated tool set fail to register.
42
+ */
43
+ effects?: readonly WebMcpToolEffect[];
44
+ }
45
+
1
46
  /**
2
47
  * Register every collection's generated tool descriptors with WebMCP.
3
48
  *
@@ -170,6 +215,181 @@ declare interface SmrtWebToolRouteDescriptor {
170
215
  optionsBag?: boolean;
171
216
  }
172
217
 
218
+ /**
219
+ * A validated, frozen intent.
220
+ *
221
+ * `kind: 'intent'` and `id` are the exported identity a `smrt-playbooks`
222
+ * step reference (`{ kind: 'intent', id }`) names, and this object is
223
+ * directly assignable to that package's `PlaybookIntentRecord` seam —
224
+ * `{ id, classification, planes }` — so {@link resolveViewIntent} can be
225
+ * passed as its `intents` resolver unchanged.
226
+ */
227
+ declare interface ViewIntent {
228
+ readonly kind: 'intent';
229
+ readonly id: string;
230
+ readonly description: string;
231
+ readonly inputSchema: Record<string, unknown>;
232
+ /** Resolved through the shared fail-closed rule at declaration time. */
233
+ readonly classification: WebMcpCapabilityClassification;
234
+ readonly target: ViewIntentTarget;
235
+ /**
236
+ * An intent moves mounted browser state, so it is browser-valid only. A
237
+ * server-side agent reaches one through the #2446 command/ack bridge, which
238
+ * a playbook must declare explicitly.
239
+ */
240
+ readonly planes: readonly ['browser'];
241
+ }
242
+
243
+ /** The mounted identity a binding supplies for an intent's lifetime. */
244
+ declare type ViewIntentBinding = {
245
+ registry: 'control';
246
+ registryPort: ViewIntentControlRegistryPort;
247
+ identity: ViewIntentControlIdentity;
248
+ } | {
249
+ registry: 'dataSurface';
250
+ registryPort: ViewIntentDataSurfaceRegistryPort;
251
+ identity: ViewIntentDataSurfaceIdentity;
252
+ };
253
+
254
+ /**
255
+ * Control commands a view intent may dispatch. Structurally mirrors
256
+ * `ControlCommandAction` in `@happyvertical/smrt-ui/forms`; this package
257
+ * cannot import that one (see AGENTS.md "No inter-smrt dependencies").
258
+ */
259
+ declare type ViewIntentControlAction = 'focus' | 'reveal' | 'highlight' | 'explain' | 'validate' | 'stage' | 'apply' | 'discard' | 'clear' | 'undo';
260
+
261
+ /** A mounted control's full registry key, subject included. */
262
+ declare interface ViewIntentControlIdentity {
263
+ formId: string;
264
+ controlId: string;
265
+ subject?: ViewIntentSubject;
266
+ }
267
+
268
+ /**
269
+ * The `ControlInteractionRegistry` surface an intent uses. Declared
270
+ * structurally so this package takes no dependency on
271
+ * `@happyvertical/smrt-ui`; the real registry satisfies it.
272
+ */
273
+ declare interface ViewIntentControlRegistryPort {
274
+ execute(command: {
275
+ action: ViewIntentControlAction;
276
+ identity: ViewIntentControlIdentity;
277
+ value?: unknown;
278
+ durationMs?: number;
279
+ revision?: number;
280
+ }, context?: {
281
+ source: 'agent';
282
+ }): Promise<{
283
+ ok: boolean;
284
+ reason?: string;
285
+ }>;
286
+ }
287
+
288
+ /**
289
+ * A control-registry target: the intent dispatches exactly one
290
+ * `ControlInteractionRegistry` command against a mounted control.
291
+ *
292
+ * `formId`/`controlId` are the statically declared half of the identity. A
293
+ * binding supplies the mounted identity and must MATCH anything declared
294
+ * here — a declaration is authority over a binding, never the other way
295
+ * round.
296
+ */
297
+ declare interface ViewIntentControlTarget {
298
+ registry: 'control';
299
+ action: ViewIntentControlAction;
300
+ formId?: string;
301
+ controlId?: string;
302
+ }
303
+
304
+ /** A mounted data surface's full registry key, subject included. */
305
+ declare interface ViewIntentDataSurfaceIdentity {
306
+ surfaceId: string;
307
+ kind: ViewIntentDataSurfaceKind;
308
+ subject?: ViewIntentSubject;
309
+ }
310
+
311
+ /** Mirrors `DataSurfaceIdentity['kind']` in `@happyvertical/smrt-ui/data`. */
312
+ declare type ViewIntentDataSurfaceKind = 'table' | 'list' | 'report' | 'custom';
313
+
314
+ /** The `DataSurfaceRegistry` surface an intent uses. */
315
+ declare interface ViewIntentDataSurfaceRegistryPort {
316
+ inspect(identity: ViewIntentDataSurfaceIdentity): {
317
+ revision: number;
318
+ } | undefined;
319
+ execute(command: {
320
+ version: 1;
321
+ commandId: string;
322
+ identity: ViewIntentDataSurfaceIdentity;
323
+ expectedRevision: number;
324
+ controlId: string;
325
+ payload?: unknown;
326
+ }): Promise<{
327
+ ok: boolean;
328
+ revision?: number;
329
+ reason?: string;
330
+ }>;
331
+ }
332
+
333
+ /**
334
+ * A data-surface target: the intent dispatches one
335
+ * `DataSurfaceVisibleCommand` — a browser-visible state transition, never a
336
+ * server-side query or mutation.
337
+ */
338
+ declare interface ViewIntentDataSurfaceTarget {
339
+ registry: 'dataSurface';
340
+ /** The visible-command `controlId` the mounted surface implements. */
341
+ controlId: string;
342
+ surfaceId?: string;
343
+ kind?: ViewIntentDataSurfaceKind;
344
+ }
345
+
346
+ /**
347
+ * The optional record a registry identity is qualified by. Rich forms use it
348
+ * to tell apart controls that share a `formId`/`controlId` across records.
349
+ * Structurally mirrors `ControlIdentity['subject']` /
350
+ * `DataSurfaceIdentity['subject']` in `@happyvertical/smrt-ui`.
351
+ */
352
+ declare interface ViewIntentSubject {
353
+ type: string;
354
+ id: string;
355
+ label?: string;
356
+ }
357
+
358
+ declare type ViewIntentTarget = ViewIntentControlTarget | ViewIntentDataSurfaceTarget;
359
+
360
+ /**
361
+ * A hand-written browser tool from application code — not generated from a
362
+ * `@smrt()` model. Structurally identical to the WebMCP `registerTool` input;
363
+ * kept as a separate type so this framework-agnostic module never depends on
364
+ * a UI layer's tool-spec type.
365
+ */
366
+ export declare interface WebMcpBespokeToolSpec {
367
+ name: string;
368
+ description: string;
369
+ inputSchema: Record<string, unknown>;
370
+ annotations?: {
371
+ readOnlyHint?: boolean;
372
+ destructiveHint?: boolean;
373
+ idempotentHint?: boolean;
374
+ openWorldHint?: boolean;
375
+ untrustedContentHint?: boolean;
376
+ };
377
+ execute: (args: Record<string, unknown>) => string | Promise<string>;
378
+ }
379
+
380
+ /** A fully resolved classification. */
381
+ declare interface WebMcpCapabilityClassification {
382
+ effect: WebMcpToolEffect;
383
+ /**
384
+ * Derived, never declared: every non-read effect is annotated destructive
385
+ * to the browser, so a `write` capability cannot claim the MCP
386
+ * additive-only guarantee.
387
+ */
388
+ destructive: boolean;
389
+ idempotent: boolean;
390
+ openWorld: boolean;
391
+ }
392
+
173
393
  export declare interface WebMcpExposurePolicy {
174
394
  /** Allowed effects. Omitted means read-only exposure. */
175
395
  effects?: readonly WebMcpToolEffect[];
@@ -211,6 +431,30 @@ declare interface WebMcpToolDefinition extends WebToolDescriptor {
211
431
  relationships: SmrtWebRelationship[];
212
432
  }
213
433
 
434
+ /**
435
+ * The one capability classification rule this package applies, extracted so
436
+ * every declaration site in smrt-web shares a single implementation (#2587,
437
+ * #2588).
438
+ *
439
+ * Two sites consume it today: `webmcp.ts` (canonical definitions trusted
440
+ * through it directly, and its legacy CRUD switch's fail-closed default
441
+ * branch) and `intents.ts` (a declared view intent, which is never a CRUD
442
+ * verb and so resolves through the declaration rule alone). Keeping the rule
443
+ * here rather than private to `webmcp.ts` is what lets the intent path be a
444
+ * dependency-free module that never pulls the client-data engine.
445
+ *
446
+ * This module structurally mirrors `CapabilityEffect` / `CapabilityDeclaration`
447
+ * / `CapabilityClassification` in `@happyvertical/smrt-types` rather than
448
+ * importing them — this package's dependency-DAG guardrails keep it free of
449
+ * every `@happyvertical/*` dependency (see AGENTS.md "No inter-smrt
450
+ * dependencies"), the same reason `data-query.ts` mirrors that package's
451
+ * bounded query envelope structurally instead of importing it.
452
+ */
453
+ /**
454
+ * Browser/agent-visible effect classification for a capability. `'read'`
455
+ * never mutates; `'write'` mutates within the application; `'destructive'`
456
+ * may remove or irreversibly change data.
457
+ */
214
458
  export declare type WebMcpToolEffect = 'read' | 'write' | 'destructive';
215
459
 
216
460
  /**
package/dist/webmcp.js CHANGED
@@ -1,2 +1,2 @@
1
- import { p as registerWebMcpTools } from "./chunks/src-n14q6RHC.js";
2
- export { registerWebMcpTools };
1
+ import { h as registerWebMcpTools, m as registerWebMcpBespokeTool, p as registerViewIntent } from "./chunks/src-D1ZtD6Bt.js";
2
+ export { registerViewIntent, registerWebMcpBespokeTool, registerWebMcpTools };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-web",
3
- "version": "0.43.9",
3
+ "version": "0.44.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",
@@ -20,6 +20,10 @@
20
20
  "./webmcp": {
21
21
  "types": "./dist/webmcp.d.ts",
22
22
  "import": "./dist/webmcp.js"
23
+ },
24
+ "./intents": {
25
+ "types": "./dist/intents.d.ts",
26
+ "import": "./dist/intents.js"
23
27
  }
24
28
  },
25
29
  "dependencies": {
@@ -33,8 +37,8 @@
33
37
  "typescript": "5.9.3",
34
38
  "vite": "8.1.4",
35
39
  "vitest": "4.1.10",
36
- "@happyvertical/smrt-core": "0.43.9",
37
- "@happyvertical/smrt-scanner": "0.43.9"
40
+ "@happyvertical/smrt-scanner": "0.44.0",
41
+ "@happyvertical/smrt-core": "0.44.0"
38
42
  },
39
43
  "publishConfig": {
40
44
  "registry": "https://registry.npmjs.org",