@jini-ai/agentic 0.6.15-rc.2 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/core/a2ui/catalog.js +5 -2
  2. package/dist/core/a2ui/common-types.d.ts +1 -1
  3. package/dist/core/a2ui/renderer-to-agent.d.ts +8 -8
  4. package/dist/core/a2ui/renderer-to-agent.js +3 -1
  5. package/dist/core/a2ui/resolve.js +18 -13
  6. package/dist/core/a2ui/tree.js +18 -14
  7. package/dist/core/ag-ui.d.ts +0 -12
  8. package/dist/core/ag-ui.js +5 -3
  9. package/dist/core/annotated-actions-script.js +3 -1
  10. package/dist/core/capability.d.ts +12 -7
  11. package/dist/core/capability.js +10 -3
  12. package/dist/core/dom/dom-page-driver.d.ts +9 -1
  13. package/dist/core/dom/dom-page-driver.js +71 -25
  14. package/dist/core/dom/publication-filter.d.ts +26 -0
  15. package/dist/core/dom/publication-filter.js +129 -0
  16. package/dist/core/element-handles.d.ts +4 -0
  17. package/dist/core/gen-ui/encoder.js +2 -1
  18. package/dist/core/guards.js +11 -5
  19. package/dist/core/handle.js +2 -1
  20. package/dist/core/index.d.ts +3 -0
  21. package/dist/core/index.js +2 -0
  22. package/dist/core/list-handles.js +11 -1
  23. package/dist/core/messages.js +2 -1
  24. package/dist/core/omit-keys.d.ts +7 -0
  25. package/dist/core/omit-keys.js +12 -0
  26. package/dist/core/page-capabilities.js +4 -2
  27. package/dist/core/page-driver.d.ts +3 -1
  28. package/dist/core/page-executor.d.ts +3 -1
  29. package/dist/core/page-executor.js +93 -19
  30. package/dist/core/page-navigation.d.ts +59 -0
  31. package/dist/core/page-navigation.js +58 -0
  32. package/dist/core/page-publication.d.ts +22 -0
  33. package/dist/core/page-publication.js +5 -0
  34. package/dist/core/site-guide/site-guide.js +2 -3
  35. package/dist/core/site-guide/topic-schema.js +2 -2
  36. package/dist/skills/install/archive.js +7 -2
  37. package/dist/skills/install/github.js +11 -3
  38. package/dist/skills/install/install-service.js +4 -3
  39. package/dist/skills/install/layout.js +2 -1
  40. package/dist/skills/install/node-filesystem.js +9 -1
  41. package/dist/skills/install/unsafe-chars.d.ts +9 -0
  42. package/dist/skills/install/unsafe-chars.js +18 -0
  43. package/dist/skills/install/validation.js +4 -2
  44. package/dist/skills/install/yauzl-archive-reader.js +4 -1
  45. package/package.json +11 -8
  46. package/dist/core/a2ui/agent-to-renderer.d.ts.map +0 -1
  47. package/dist/core/a2ui/agent-to-renderer.js.map +0 -1
  48. package/dist/core/a2ui/catalog.d.ts.map +0 -1
  49. package/dist/core/a2ui/catalog.js.map +0 -1
  50. package/dist/core/a2ui/common-types.d.ts.map +0 -1
  51. package/dist/core/a2ui/common-types.js.map +0 -1
  52. package/dist/core/a2ui/index.d.ts.map +0 -1
  53. package/dist/core/a2ui/index.js.map +0 -1
  54. package/dist/core/a2ui/interpreter.d.ts.map +0 -1
  55. package/dist/core/a2ui/interpreter.js.map +0 -1
  56. package/dist/core/a2ui/json-pointer.d.ts.map +0 -1
  57. package/dist/core/a2ui/json-pointer.js.map +0 -1
  58. package/dist/core/a2ui/renderer-to-agent.d.ts.map +0 -1
  59. package/dist/core/a2ui/renderer-to-agent.js.map +0 -1
  60. package/dist/core/a2ui/resolve.d.ts.map +0 -1
  61. package/dist/core/a2ui/resolve.js.map +0 -1
  62. package/dist/core/a2ui/surface-properties.d.ts.map +0 -1
  63. package/dist/core/a2ui/surface-properties.js.map +0 -1
  64. package/dist/core/a2ui/tree.d.ts.map +0 -1
  65. package/dist/core/a2ui/tree.js.map +0 -1
  66. package/dist/core/ag-ui.d.ts.map +0 -1
  67. package/dist/core/ag-ui.js.map +0 -1
  68. package/dist/core/annotated-actions-script.d.ts.map +0 -1
  69. package/dist/core/annotated-actions-script.js.map +0 -1
  70. package/dist/core/capability.d.ts.map +0 -1
  71. package/dist/core/capability.js.map +0 -1
  72. package/dist/core/dom/dom-page-driver.d.ts.map +0 -1
  73. package/dist/core/dom/dom-page-driver.js.map +0 -1
  74. package/dist/core/dom/index.d.ts.map +0 -1
  75. package/dist/core/dom/index.js.map +0 -1
  76. package/dist/core/dom/model-context.d.ts.map +0 -1
  77. package/dist/core/dom/model-context.js.map +0 -1
  78. package/dist/core/dom/native-model-context.d.ts.map +0 -1
  79. package/dist/core/dom/native-model-context.js.map +0 -1
  80. package/dist/core/element-handles.d.ts.map +0 -1
  81. package/dist/core/element-handles.js.map +0 -1
  82. package/dist/core/gen-ui/encoder.d.ts.map +0 -1
  83. package/dist/core/gen-ui/encoder.js.map +0 -1
  84. package/dist/core/gen-ui/events.d.ts.map +0 -1
  85. package/dist/core/gen-ui/events.js.map +0 -1
  86. package/dist/core/gen-ui/index.d.ts.map +0 -1
  87. package/dist/core/gen-ui/index.js.map +0 -1
  88. package/dist/core/guards.d.ts.map +0 -1
  89. package/dist/core/guards.js.map +0 -1
  90. package/dist/core/handle.d.ts.map +0 -1
  91. package/dist/core/handle.js.map +0 -1
  92. package/dist/core/index.d.ts.map +0 -1
  93. package/dist/core/index.js.map +0 -1
  94. package/dist/core/list-handles.d.ts.map +0 -1
  95. package/dist/core/list-handles.js.map +0 -1
  96. package/dist/core/mcp-ui-apps.d.ts.map +0 -1
  97. package/dist/core/mcp-ui-apps.js.map +0 -1
  98. package/dist/core/messages.d.ts.map +0 -1
  99. package/dist/core/messages.js.map +0 -1
  100. package/dist/core/page-capabilities.d.ts.map +0 -1
  101. package/dist/core/page-capabilities.js.map +0 -1
  102. package/dist/core/page-driver.d.ts.map +0 -1
  103. package/dist/core/page-driver.js.map +0 -1
  104. package/dist/core/page-executor.d.ts.map +0 -1
  105. package/dist/core/page-executor.js.map +0 -1
  106. package/dist/core/site-guide/index.d.ts.map +0 -1
  107. package/dist/core/site-guide/index.js.map +0 -1
  108. package/dist/core/site-guide/site-guide.d.ts.map +0 -1
  109. package/dist/core/site-guide/site-guide.js.map +0 -1
  110. package/dist/core/site-guide/topic-schema.d.ts.map +0 -1
  111. package/dist/core/site-guide/topic-schema.js.map +0 -1
  112. package/dist/core/site-guide/types.d.ts.map +0 -1
  113. package/dist/core/site-guide/types.js.map +0 -1
  114. package/dist/core/webmcp.d.ts.map +0 -1
  115. package/dist/core/webmcp.js.map +0 -1
  116. package/dist/index.d.ts.map +0 -1
  117. package/dist/index.js.map +0 -1
  118. package/dist/skills/install/archive.d.ts.map +0 -1
  119. package/dist/skills/install/archive.js.map +0 -1
  120. package/dist/skills/install/fetch-adapter.d.ts.map +0 -1
  121. package/dist/skills/install/fetch-adapter.js.map +0 -1
  122. package/dist/skills/install/github.d.ts.map +0 -1
  123. package/dist/skills/install/github.js.map +0 -1
  124. package/dist/skills/install/index.d.ts.map +0 -1
  125. package/dist/skills/install/index.js.map +0 -1
  126. package/dist/skills/install/install-service.d.ts.map +0 -1
  127. package/dist/skills/install/install-service.js.map +0 -1
  128. package/dist/skills/install/layout.d.ts.map +0 -1
  129. package/dist/skills/install/layout.js.map +0 -1
  130. package/dist/skills/install/live-registration.d.ts.map +0 -1
  131. package/dist/skills/install/live-registration.js.map +0 -1
  132. package/dist/skills/install/node-filesystem.d.ts.map +0 -1
  133. package/dist/skills/install/node-filesystem.js.map +0 -1
  134. package/dist/skills/install/ports.d.ts.map +0 -1
  135. package/dist/skills/install/ports.js.map +0 -1
  136. package/dist/skills/install/state.d.ts.map +0 -1
  137. package/dist/skills/install/state.js.map +0 -1
  138. package/dist/skills/install/validation.d.ts.map +0 -1
  139. package/dist/skills/install/validation.js.map +0 -1
  140. package/dist/skills/install/yauzl-archive-reader.d.ts.map +0 -1
  141. package/dist/skills/install/yauzl-archive-reader.js.map +0 -1
@@ -211,8 +211,11 @@ const SliderPropsSchema = checkableComponentProps({
211
211
  * mutually exclusive in practice, so exactly-one and at-least-one coincide.
212
212
  */
213
213
  const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
214
- const ISO_TIME = /^\d{2}:\d{2}(:\d{2}(\.\d+)?)?([Zz]|[+-]\d{2}:\d{2})?$/;
215
- const ISO_DATE_TIME = /^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}(:\d{2}(\.\d+)?)?([Zz]|[+-]\d{2}:\d{2})?$/;
214
+ // `hh:mm[:ss[.fff]]` and an optional `Z`/`±hh:mm` offset, shared by the time and date-time forms.
215
+ const CLOCK_TIME = /\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?/.source;
216
+ const UTC_OFFSET = /(?:[Zz]|[+-]\d{2}:\d{2})?/.source;
217
+ const ISO_TIME = new RegExp(`^${CLOCK_TIME}${UTC_OFFSET}$`);
218
+ const ISO_DATE_TIME = new RegExp(`^\\d{4}-\\d{2}-\\d{2}[Tt ]${CLOCK_TIME}${UTC_OFFSET}$`);
216
219
  const IsoDateTimeBoundSchema = DynamicStringSchema.superRefine((value, ctx) => {
217
220
  if (typeof value !== 'string')
218
221
  return;
@@ -226,7 +226,7 @@ export declare const ComponentCommonSchema: z.ZodObject<{
226
226
  export type ComponentCommon = z.infer<typeof ComponentCommonSchema>;
227
227
  /** `common_types.json#/$defs/Child` — "A reference to a single child component ID." */
228
228
  export declare const ChildSchema: z.ZodString;
229
- export type Child = ComponentId;
229
+ export type Child = z.infer<typeof ChildSchema>;
230
230
  /**
231
231
  * `common_types.json#/$defs/ChildList` — either a static array of `ComponentId`s, or a template
232
232
  * object (`{componentId, path}`) that generates one child per item in a data-model list.
@@ -78,15 +78,15 @@ export declare const ValidationFailedErrorSchema: z.ZodObject<{
78
78
  path: z.ZodString;
79
79
  message: z.ZodString;
80
80
  }, "strict", z.ZodTypeAny, {
81
+ path: string;
81
82
  surfaceId: string;
82
83
  code: "VALIDATION_FAILED";
83
84
  message: string;
84
- path: string;
85
85
  }, {
86
+ path: string;
86
87
  surfaceId: string;
87
88
  code: "VALIDATION_FAILED";
88
89
  message: string;
89
- path: string;
90
90
  }>;
91
91
  export type ValidationFailedError = z.infer<typeof ValidationFailedErrorSchema>;
92
92
  export declare const GenericErrorSchema: z.ZodEffects<z.ZodObject<{
@@ -122,15 +122,15 @@ export declare const ErrorPayloadSchema: z.ZodUnion<[z.ZodObject<{
122
122
  path: z.ZodString;
123
123
  message: z.ZodString;
124
124
  }, "strict", z.ZodTypeAny, {
125
+ path: string;
125
126
  surfaceId: string;
126
127
  code: "VALIDATION_FAILED";
127
128
  message: string;
128
- path: string;
129
129
  }, {
130
+ path: string;
130
131
  surfaceId: string;
131
132
  code: "VALIDATION_FAILED";
132
133
  message: string;
133
- path: string;
134
134
  }>, z.ZodEffects<z.ZodObject<{
135
135
  code: z.ZodEffects<z.ZodString, string, string>;
136
136
  message: z.ZodString;
@@ -257,15 +257,15 @@ export declare const ErrorMessageSchema: z.ZodObject<{
257
257
  path: z.ZodString;
258
258
  message: z.ZodString;
259
259
  }, "strict", z.ZodTypeAny, {
260
+ path: string;
260
261
  surfaceId: string;
261
262
  code: "VALIDATION_FAILED";
262
263
  message: string;
263
- path: string;
264
264
  }, {
265
+ path: string;
265
266
  surfaceId: string;
266
267
  code: "VALIDATION_FAILED";
267
268
  message: string;
268
- path: string;
269
269
  }>, z.ZodEffects<z.ZodObject<{
270
270
  code: z.ZodEffects<z.ZodString, string, string>;
271
271
  message: z.ZodString;
@@ -294,10 +294,10 @@ export declare const ErrorMessageSchema: z.ZodObject<{
294
294
  }, z.ZodTypeAny, "passthrough">>]>;
295
295
  }, "strict", z.ZodTypeAny, {
296
296
  error: {
297
+ path: string;
297
298
  surfaceId: string;
298
299
  code: "VALIDATION_FAILED";
299
300
  message: string;
300
- path: string;
301
301
  } | z.objectOutputType<{
302
302
  code: z.ZodEffects<z.ZodString, string, string>;
303
303
  message: z.ZodString;
@@ -307,10 +307,10 @@ export declare const ErrorMessageSchema: z.ZodObject<{
307
307
  version: "v1.0";
308
308
  }, {
309
309
  error: {
310
+ path: string;
310
311
  surfaceId: string;
311
312
  code: "VALIDATION_FAILED";
312
313
  message: string;
313
- path: string;
314
314
  } | z.objectInputType<{
315
315
  code: z.ZodEffects<z.ZodString, string, string>;
316
316
  message: z.ZodString;
@@ -85,6 +85,8 @@ export function buildGenericErrorMessage({ code, message, target }, _optional =
85
85
  return { version: PROTOCOL_VERSION, error: { code, message, ...target } };
86
86
  }
87
87
  const MESSAGE_KEYS = ['action', 'functionResponse', 'error'];
88
+ /** The schema each message key's envelope is validated against. */
89
+ const MESSAGE_SCHEMAS = { action: ActionMessageSchema, functionResponse: FunctionResponseMessageSchema, error: ErrorMessageSchema };
88
90
  /** Same "inspect the shape, validate the one matching branch" dispatcher as `parseAgentToRendererMessage` — see that function's doc for why this beats a plain `z.union`. Used by tests and by anything on the agent side of this fixture that needs to validate what the browser sent. */
89
91
  export function parseRendererToAgentMessage({ raw }, _optional = {}) {
90
92
  if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
@@ -99,7 +101,7 @@ export function parseRendererToAgentMessage({ raw }, _optional = {}) {
99
101
  return { ok: false, reason: `expected exactly one of ${MESSAGE_KEYS.join(', ')}, found ${presentKeys.length}` };
100
102
  }
101
103
  const key = presentKeys[0];
102
- const schema = key === 'action' ? ActionMessageSchema : key === 'functionResponse' ? FunctionResponseMessageSchema : ErrorMessageSchema;
104
+ const schema = MESSAGE_SCHEMAS[key];
103
105
  const result = schema.safeParse(obj);
104
106
  if (!result.success) {
105
107
  // Same zod guarantee as `parseAgentToRendererMessage` relies on: `success: false` implies a
@@ -27,26 +27,29 @@ import { callableFromOf, isFunctionRegistered } from './catalog.js';
27
27
  * `applyAgentMessage`, which this module's own doc says cannot happen.
28
28
  */
29
29
  function isDataBinding(value) {
30
- return typeof value === 'object'
31
- && value !== null
32
- && !Array.isArray(value)
33
- && 'path' in value
34
- && typeof value.path === 'string'
35
- && Object.keys(value).length === 1;
30
+ return hasStringField(value, 'path') && Object.keys(value).length === 1;
36
31
  }
37
32
  function isFunctionCall(value) {
38
- return typeof value === 'object' && value !== null && !Array.isArray(value) && 'call' in value && typeof value.call === 'string';
33
+ return hasStringField(value, 'call');
34
+ }
35
+ /** A plain (non-array) object whose `key` holds a string. */
36
+ function hasStringField(value, key) {
37
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
38
+ return false;
39
+ return key in value && typeof value[key] === 'string';
40
+ }
41
+ /** A relative path resolved under the current list item, or null outside any list-template item. */
42
+ function itemRelativePointer(path, ctx) {
43
+ if (!ctx.itemScope)
44
+ return null;
45
+ return joinPointer({ base: ctx.itemScope.basePath, tokens: [String(ctx.itemScope.index), ...parsePointerTokens({ pointer: `/${path}` })] });
39
46
  }
40
47
  function resolveBindingPath(path, ctx) {
41
48
  // A relative path is still RFC 6901 syntax, so its `~0`/`~1` escapes must be decoded into real
42
49
  // token text before `joinPointer` re-escapes them. Splitting the raw string on `/` instead would
43
50
  // hand `joinPointer` already-escaped text and double-escape it, making a relative `a~1b` resolve
44
51
  // the literal key `a~1b` while the equivalent absolute pointer correctly resolves `a/b`.
45
- const absolute = path.startsWith('/')
46
- ? path
47
- : ctx.itemScope
48
- ? joinPointer({ base: ctx.itemScope.basePath, tokens: [String(ctx.itemScope.index), ...parsePointerTokens({ pointer: `/${path}` })] })
49
- : null;
52
+ const absolute = path.startsWith('/') ? path : itemRelativePointer(path, ctx);
50
53
  if (absolute === null) {
51
54
  return { ok: false, reason: 'RELATIVE_PATH_OUTSIDE_LIST_CONTEXT', detail: `relative path "${path}" used outside a list-template item context` };
52
55
  }
@@ -69,7 +72,9 @@ function resolveIndexCall(call, ctx) {
69
72
  return { ok: true, value: ctx.itemScope.index + offset };
70
73
  }
71
74
  function isCallableFromSide(callableFrom, side) {
72
- return callableFrom === 'rendererOrAgent' || (callableFrom === 'rendererOnly' && side === 'renderer') || (callableFrom === 'agentOnly' && side === 'agent');
75
+ if (callableFrom === 'rendererOrAgent')
76
+ return true;
77
+ return (callableFrom === 'rendererOnly' && side === 'renderer') || (callableFrom === 'agentOnly' && side === 'agent');
73
78
  }
74
79
  /** The catalog-side refusals, in the order they apply: not registered, then the `callableFrom` boundary, then no implementation. Returns the implementation when all three pass. */
75
80
  function lookUpCallableImpl(call, ctx) {
@@ -11,6 +11,23 @@
11
11
  * is orders of magnitude smaller), so this is a fail-safe, not a design budget.
12
12
  */
13
13
  export const MAX_RENDER_NODES = 50_000;
14
+ /** Emits one node: a cycle back to an ancestor, a missing component, or a rendered node whose
15
+ * children are queued (after a `leave` frame that pops it off the ancestor path). */
16
+ function visitNode({ id, depth }, walk) {
17
+ if (walk.ancestors.has(id)) {
18
+ walk.output.push({ id, depth, status: 'cycle' });
19
+ return;
20
+ }
21
+ const component = walk.components.get(id);
22
+ if (!component) {
23
+ walk.output.push({ id, depth, status: 'missing' });
24
+ return;
25
+ }
26
+ walk.output.push({ id, depth, status: 'ok' });
27
+ walk.ancestors.add(id);
28
+ walk.stack.push({ leave: id });
29
+ pushChildren(walk.stack, walk.getChildIds({ component }), depth + 1);
30
+ }
14
31
  /** Pushes `childIds` onto the walk's stack at `depth`. */
15
32
  function pushChildren(stack, childIds, depth) {
16
33
  // Pushed in reverse so the first child is popped first, preserving pre-order.
@@ -57,20 +74,7 @@ export function flattenRenderTree({ components, rootId, getChildIds }, _optional
57
74
  output.push({ id: frame.id, depth: frame.depth, status: 'truncated' });
58
75
  return output;
59
76
  }
60
- const { id, depth } = frame;
61
- if (ancestors.has(id)) {
62
- output.push({ id, depth, status: 'cycle' });
63
- continue;
64
- }
65
- const component = components.get(id);
66
- if (!component) {
67
- output.push({ id, depth, status: 'missing' });
68
- continue;
69
- }
70
- output.push({ id, depth, status: 'ok' });
71
- ancestors.add(id);
72
- stack.push({ leave: id });
73
- pushChildren(stack, getChildIds({ component }), depth + 1);
77
+ visitNode(frame, { output, ancestors, stack, components, getChildIds });
74
78
  }
75
79
  return output;
76
80
  }
@@ -64,18 +64,6 @@ export declare function toAgUiTool({ capability }: {
64
64
  export declare function toAgUiTools({ capabilities }: {
65
65
  capabilities: readonly CapabilityDef[];
66
66
  }, _optional?: Record<string, never>): readonly AgUiTool[];
67
- /**
68
- * Builds the result message for a completed frontend tool call.
69
- *
70
- * AG-UI carries the result as a string, so structured output is JSON-encoded. Errors are
71
- * returned through this same channel rather than thrown — the agent needs to see the refusal
72
- * (a fill guard saying no, an unavailable capability) as a result it can reason about.
73
- *
74
- * @param messageId - Id for the result message itself.
75
- * @param toolCallId - The `toolCallId` from `TOOL_CALL_START`.
76
- * @param outcome - What happened.
77
- * @returns The `role: "tool"` message to send back.
78
- */
79
67
  export declare function createAgUiToolResult({ messageId, toolCallId, outcome }: {
80
68
  messageId: string;
81
69
  toolCallId: string;
@@ -76,10 +76,12 @@ function encodeToolContent(output) {
76
76
  * @param outcome - What happened.
77
77
  * @returns The `role: "tool"` message to send back.
78
78
  */
79
+ /** A string output verbatim; anything else through the tool-content encoder. */
80
+ function toolOutputText(output) {
81
+ return typeof output === 'string' ? output : encodeToolContent(output);
82
+ }
79
83
  export function createAgUiToolResult({ messageId, toolCallId, outcome }, _optional = {}) {
80
- const content = outcome.ok
81
- ? typeof outcome.output === 'string' ? outcome.output : encodeToolContent(outcome.output)
82
- : JSON.stringify({ error: outcome.error });
84
+ const content = outcome.ok ? toolOutputText(outcome.output) : JSON.stringify({ error: outcome.error });
83
85
  return { id: messageId, role: 'tool', content, toolCallId };
84
86
  }
85
87
  //# sourceMappingURL=ag-ui.js.map
@@ -63,6 +63,8 @@ export function createAnnotatedActionsScript({ preferenceKey, confirmationMessag
63
63
  return true;
64
64
  }
65
65
  function descriptor(element) {
66
+ // Visitor discovery/disclosure marks are not external WebMCP action declarations.
67
+ if (['read', 'menu', 'accordion'].includes(element.getAttribute('data-tool-action'))) return null;
66
68
  var name = element.getAttribute('data-toolname') || '';
67
69
  var description = element.getAttribute('data-tooldescription') || '';
68
70
  if (!/^[A-Za-z0-9_.-]{1,128}$/.test(name) || !description.trim() || !visible(element)) return null;
@@ -140,7 +142,7 @@ export function createAnnotatedActionsScript({ preferenceKey, confirmationMessag
140
142
  refresh();
141
143
  observer = new MutationObserver(refresh);
142
144
  observer.observe(doc.documentElement, { subtree: true, childList: true, attributes: true,
143
- attributeFilter: ['data-toolname', 'data-tooldescription', 'toolname', 'href', 'download', 'type', 'disabled', 'aria-disabled', 'hidden', 'inert', 'aria-hidden', 'style', 'class', 'open'] });
145
+ attributeFilter: ['data-tool-action', 'data-toolname', 'data-tooldescription', 'toolname', 'href', 'download', 'type', 'disabled', 'aria-disabled', 'hidden', 'inert', 'aria-hidden', 'style', 'class', 'open'] });
144
146
  })(` + config + ');';
145
147
  }
146
148
  //# sourceMappingURL=annotated-actions-script.js.map
@@ -24,13 +24,17 @@ export type CapabilityRisk = 'read' | 'write';
24
24
  * instead of waiting for a timeout that will never resolve.
25
25
  */
26
26
  export type CapabilitySurface = 'session' | 'server';
27
+ export interface CapabilityInputProperty {
28
+ readonly type: string;
29
+ readonly description?: string;
30
+ readonly enum?: readonly string[];
31
+ readonly properties?: Record<string, CapabilityInputProperty>;
32
+ readonly required?: readonly string[];
33
+ readonly additionalProperties?: boolean | CapabilityInputProperty;
34
+ }
27
35
  export interface CapabilityInputSchema {
28
36
  readonly type: 'object';
29
- readonly properties: Record<string, {
30
- type: string;
31
- description?: string;
32
- enum?: readonly string[];
33
- }>;
37
+ readonly properties: Record<string, CapabilityInputProperty>;
34
38
  readonly required?: readonly string[];
35
39
  readonly additionalProperties?: boolean;
36
40
  }
@@ -65,8 +69,9 @@ export declare function findCapability({ capabilities, id }: {
65
69
  * Manifests advertise `additionalProperties: false` and a `required` list, but nothing was
66
70
  * enforcing either — handlers read the fields they knew about and ignored the rest, so a
67
71
  * misnamed argument silently became a missing one. This closes that for any surface that calls
68
- * it. Intentionally a shallow check, not a JSON Schema engine: these schemas are flat by
69
- * construction, and a real validator is a dependency this package will not take.
72
+ * it. Intentionally a shallow check, not a JSON Schema engine. Nested schemas describe the public
73
+ * contract; the owning executor enforces their shape before any effect (for example navigation
74
+ * entity/query inputs). This check enforces top-level types, required and unknown arguments.
70
75
  *
71
76
  * @param capability - The capability being invoked.
72
77
  * @param input - Raw caller-supplied arguments.
@@ -26,8 +26,9 @@ export function findCapability({ capabilities, id }, _optional = {}) {
26
26
  * Manifests advertise `additionalProperties: false` and a `required` list, but nothing was
27
27
  * enforcing either — handlers read the fields they knew about and ignored the rest, so a
28
28
  * misnamed argument silently became a missing one. This closes that for any surface that calls
29
- * it. Intentionally a shallow check, not a JSON Schema engine: these schemas are flat by
30
- * construction, and a real validator is a dependency this package will not take.
29
+ * it. Intentionally a shallow check, not a JSON Schema engine. Nested schemas describe the public
30
+ * contract; the owning executor enforces their shape before any effect (for example navigation
31
+ * entity/query inputs). This check enforces top-level types, required and unknown arguments.
31
32
  *
32
33
  * @param capability - The capability being invoked.
33
34
  * @param input - Raw caller-supplied arguments.
@@ -80,8 +81,14 @@ function findPropertyInputError({ properties, input }, _optional = {}) {
80
81
  }
81
82
  return null;
82
83
  }
84
+ /** `typeof`, except `null` and arrays get their own names. */
85
+ function valueKind(value) {
86
+ if (value === null)
87
+ return 'null';
88
+ return Array.isArray(value) ? 'array' : typeof value;
89
+ }
83
90
  function propertyInputError({ name, schema, value }) {
84
- const actual = value === null ? 'null' : Array.isArray(value) ? 'array' : typeof value;
91
+ const actual = valueKind(value);
85
92
  if (!matchesPropertyType({ value, actual, expected: schema.type })) {
86
93
  const received = typeof value === 'number' && !Number.isFinite(value) ? String(value) : actual;
87
94
  return `"${name}" must be a ${schema.type}, received ${received}`;
@@ -1,10 +1,13 @@
1
+ import type { PagePublication } from '../page-publication.js';
2
+ import type { AgentNavigationScreen, PageNavigationInput, PageNavigationResult } from '../page-navigation.js';
1
3
  import type { PageDriver } from '../page-driver.js';
2
4
  /** One page a host allows navigation to: its human label, and how to actually get there. */
3
5
  export interface DomPageDriverPage {
4
6
  /** Shown to a caller by `page.find_elements`/`page.navigate`'s refusal message — see
5
7
  * `PageSummary.label` for why this exists at all. */
6
8
  readonly label: string;
7
- readonly navigate: (required: Record<string, never>) => void;
9
+ readonly navigation?: AgentNavigationScreen;
10
+ readonly navigate: (required: PageNavigationInput) => void | PageNavigationResult | Promise<void | PageNavigationResult>;
8
11
  }
9
12
  export interface DomPageDriverRequired {
10
13
  /** The subtree to expose. Never pass `document`. */
@@ -25,6 +28,11 @@ export interface DomPageDriverOptions {
25
28
  * view (it is bound to the connection, not the render).
26
29
  */
27
30
  readonly currentPage?: string | undefined;
31
+ readonly publication?: PagePublication;
32
+ /** Public hosts acknowledge terminal native submissions before executing the one saved effect. */
33
+ readonly deferSubmit?: (args: {
34
+ execute: () => Promise<void>;
35
+ }) => void;
28
36
  }
29
37
  /**
30
38
  * Builds a driver over `root`.
@@ -1,3 +1,4 @@
1
+ import { createPublicationFilter, publishedText } from './publication-filter.js';
1
2
  import { AGENT_ELEMENT_ATTRIBUTE, AGENT_LABEL_ATTRIBUTE, AGENT_PAGE_ATTRIBUTE, AGENT_PRIVATE_ATTRIBUTE, AGENT_ROLE_ATTRIBUTE, AGENT_ELEMENT_ROLES, resolveHandleSelector, } from '../element-handles.js';
2
3
  import { normalizeAgentLabel } from '../guards.js';
3
4
  /**
@@ -147,6 +148,7 @@ function dropdownFieldDescriptorOf({ dropdown }, _optional = {}) {
147
148
  return null;
148
149
  return {
149
150
  type: 'select',
151
+ autocomplete: dropdown.getAttribute('autocomplete')?.toLowerCase() || undefined,
150
152
  name: dropdown.name || undefined,
151
153
  id: dropdown.id || undefined,
152
154
  // Same reason `fieldDescriptorOf` carries these: a `<select aria-label="Card number"
@@ -160,6 +162,7 @@ function dropdownFieldDescriptorOf({ dropdown }, _optional = {}) {
160
162
  }
161
163
  function rawElementStateOf({ element, control, checkable, readable, value, dropdown }, _optional = {}) {
162
164
  const text = publicTextOf(element);
165
+ const expanded = expansionOf({ element, control });
163
166
  return {
164
167
  text,
165
168
  // A contenteditable region has no `.value`; its content IS its value, so `text` is the
@@ -172,6 +175,7 @@ function rawElementStateOf({ element, control, checkable, readable, value, dropd
172
175
  // driver deciding here which values are secret is a driver holding policy.
173
176
  ...(value !== undefined ? { value } : {}),
174
177
  ...(checkable ? { checked: control.checked } : {}),
178
+ ...(expanded !== undefined ? { expanded } : {}),
175
179
  ...(disabledOf(control) !== undefined ? { disabled: disabledOf(control) } : {}),
176
180
  ...(visibilityOf(element) !== undefined ? { visible: visibilityOf(element) } : {}),
177
181
  ...(readable !== null ? { field: readable } : {}),
@@ -184,6 +188,20 @@ function rawElementStateOf({ element, control, checkable, readable, value, dropd
184
188
  : {}),
185
189
  };
186
190
  }
191
+ /** Read disclosure semantics, not text changes: opening a panel often leaves its button unchanged.
192
+ * Native summaries have no aria-expanded; their owning details element carries the open state.
193
+ * @returns The platform's disclosure state, or undefined when this is not a disclosure control.
194
+ */
195
+ function expansionOf({ element, control }, _options = {}) {
196
+ const aria = control.getAttribute('aria-expanded') ?? element.getAttribute('aria-expanded');
197
+ if (aria === 'true' || aria === 'false')
198
+ return aria === 'true';
199
+ if (element instanceof HTMLDetailsElement)
200
+ return element.open;
201
+ if (control.matches('summary') && control.parentElement instanceof HTMLDetailsElement)
202
+ return control.parentElement.open;
203
+ return undefined;
204
+ }
187
205
  /**
188
206
  * Whether the element is actually rendered, or `undefined` when the platform cannot say.
189
207
  *
@@ -216,12 +234,9 @@ function visibilityOf(element) {
216
234
  * form submission entirely. `:disabled` is the platform's own answer and covers both cases.
217
235
  */
218
236
  function disabledOf(control) {
219
- return control instanceof HTMLInputElement
220
- || control instanceof HTMLTextAreaElement
221
- || control instanceof HTMLButtonElement
222
- || control instanceof HTMLSelectElement
223
- ? control.matches(':disabled')
224
- : undefined;
237
+ const formControl = control instanceof HTMLInputElement || control instanceof HTMLTextAreaElement
238
+ || control instanceof HTMLButtonElement || control instanceof HTMLSelectElement;
239
+ return formControl ? control.matches(':disabled') : undefined;
225
240
  }
226
241
  const PRIVATE_SELECTOR = `[${AGENT_PRIVATE_ATTRIBUTE}]`;
227
242
  /** Whether `node` is inside (or is) a `[data-agent-private]` subtree. @complexity O(depth). */
@@ -244,9 +259,9 @@ function publicTextOf(element) {
244
259
  /* c8 ignore next -- as above. */
245
260
  return copy.textContent ?? '';
246
261
  }
247
- function describe(element, page) {
262
+ function describe(element, page, publicText) {
248
263
  const role = element.getAttribute(AGENT_ROLE_ATTRIBUTE);
249
- const text = publicTextOf(element);
264
+ const text = publicText ?? publicTextOf(element);
250
265
  // Live text is a fine fallback label for an ordinary element — it is the page's own ontology,
251
266
  // rendered openly. For an editable region it is not: a contenteditable's content is the *user's
252
267
  // data*, so falling back to it published the contents of a rich-text field to every
@@ -325,8 +340,12 @@ export function createDomPageDriver({ root, pages }, options = {}) {
325
340
  * publishes the same handle twice makes `find`'s single-match resolution ambiguous — surfaced
326
341
  * here so both callers below can refuse rather than silently pick one.
327
342
  */
328
- const findAll = (handle) => Array.from(root.querySelectorAll(resolveHandleSelector({ handle })))
329
- .filter((element) => !isPrivate(element));
343
+ const findAll = (handle) => {
344
+ const matches = Array.from(root.querySelectorAll(resolveHandleSelector({ handle })));
345
+ if (options.publication && matches.length !== 1)
346
+ return [];
347
+ return matches.filter(element => !isPrivate(element) && (!publicationFilter || publicationFilter.allows({ element, action: 'read' })));
348
+ };
330
349
  /**
331
350
  * More than one element answers to the same handle — a page-authoring bug (duplicate
332
351
  * `data-agent-element`), not something a caller passing that handle can resolve on its own.
@@ -338,13 +357,16 @@ export function createDomPageDriver({ root, pages }, options = {}) {
338
357
  const ambiguousHandleError = (handle, count) => new Error(`"${handle}" is published on ${count} elements on this page, so which one it addresses is `
339
358
  + 'ambiguous — this is a page-authoring bug (duplicate data-agent-element), not something a '
340
359
  + 'caller can resolve; publish a unique handle per element');
341
- const find = (handle) => {
360
+ const find = (handle, action = 'read') => {
342
361
  const matches = findAll(handle);
343
362
  if (matches.length === 0)
344
363
  throw new Error(`no element published as "${handle}" on this page`);
345
364
  if (matches.length > 1)
346
365
  throw ambiguousHandleError(handle, matches.length);
347
- return matches[0];
366
+ const element = matches[0];
367
+ if (publicationFilter && !publicationFilter.allows({ element: controlOf(element), action }))
368
+ throw new Error('Published action unavailable');
369
+ return element;
348
370
  };
349
371
  /**
350
372
  * The control a handle addresses — for a wrapper like `<li><label><input>`, the input.
@@ -379,11 +401,13 @@ export function createDomPageDriver({ root, pages }, options = {}) {
379
401
  .find(owned);
380
402
  return candidate ?? element;
381
403
  };
404
+ const publicationFilter = options.publication ? createPublicationFilter({ root, publication: options.publication,
405
+ fieldOf: element => fieldDescriptorOf(controlOf(element)) ?? dropdownFieldDescriptorOf({ dropdown: element instanceof HTMLSelectElement ? element : undefined }) }) : undefined;
382
406
  return {
383
407
  async findElements(_required, filter = {}) {
384
408
  const found = Array.from(root.querySelectorAll(`[${AGENT_ELEMENT_ATTRIBUTE}]`))
385
- .filter((element) => !isPrivate(element))
386
- .map((element) => describe(element, pageOf(element)))
409
+ .filter(element => !isPrivate(element) && (!publicationFilter || findAll(element.getAttribute(AGENT_ELEMENT_ATTRIBUTE) ?? '').includes(element)))
410
+ .map((element) => describe(element, pageOf(element), publicationFilter ? publishedText({ element }) : undefined))
387
411
  .filter((element) => element.handle.length > 0);
388
412
  const query = filter.query?.toLowerCase();
389
413
  return found.filter((element) => {
@@ -399,7 +423,7 @@ export function createDomPageDriver({ root, pages }, options = {}) {
399
423
  // `Object.entries`, same own-enumerable-keys-only guarantee `Object.keys` gives `navigate`'s
400
424
  // own `hasOwnProperty` check above — no risk of a `pages.label`/`pages.navigate`-shaped
401
425
  // prototype entry leaking in as a fake page id.
402
- return Object.entries(pages).map(([id, page]) => ({ id, label: page.label }));
426
+ return Object.entries(pages).map(([id, page]) => ({ id, label: page.label, ...(page.navigation ? { navigation: page.navigation } : {}) }));
403
427
  },
404
428
  async describeField({ handle }, _optional = {}) {
405
429
  return fieldDescriptorOf(controlOf(find(handle)));
@@ -428,7 +452,8 @@ export function createDomPageDriver({ root, pages }, options = {}) {
428
452
  const value = field !== null
429
453
  ? control.value
430
454
  : dropdown?.value;
431
- return rawElementStateOf({ element, control, checkable, readable, value, dropdown });
455
+ const state = rawElementStateOf({ element, control, checkable, readable, value, dropdown });
456
+ return publicationState({ state, element, filtered: Boolean(publicationFilter) });
432
457
  },
433
458
  /**
434
459
  * Two animation frames — long enough for a framework to have re-rendered and the browser to
@@ -457,7 +482,7 @@ export function createDomPageDriver({ root, pages }, options = {}) {
457
482
  });
458
483
  },
459
484
  async highlight({ handle, durationMs }, _optional = {}) {
460
- const element = find(handle);
485
+ const element = find(handle, 'highlight');
461
486
  // Same refusal as `click()`, for the same reason: an SVG (or other non-HTMLElement) target
462
487
  // has no `.style` to draw an outline on, so silently returning here drew nothing while
463
488
  // `page-executor.ts` unconditionally reports the highlight as successful — a guaranteed
@@ -474,7 +499,9 @@ export function createDomPageDriver({ root, pages }, options = {}) {
474
499
  borderRadius: element.style.borderRadius,
475
500
  };
476
501
  Object.assign(element.style, HIGHLIGHT_STYLE);
477
- element.scrollIntoView({ behavior: 'smooth', block: 'center' });
502
+ // Embedded DOMs may omit scrolling; highlighting must still restore its styles.
503
+ if (typeof element.scrollIntoView === 'function')
504
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' });
478
505
  const timer = setTimeout(() => {
479
506
  // Restore what was there rather than clearing: the element may have had its own outline.
480
507
  Object.assign(element.style, restore);
@@ -483,16 +510,29 @@ export function createDomPageDriver({ root, pages }, options = {}) {
483
510
  highlights.set(handle, { timer, restore });
484
511
  },
485
512
  async scrollTo({ handle }, _optional = {}) {
486
- find(handle).scrollIntoView({ behavior: 'smooth', block: 'center' });
513
+ const element = find(handle, 'scrollTo');
514
+ // Revealing a clicked control is best-effort in DOMs without layout (including jsdom).
515
+ if (typeof element.scrollIntoView === 'function')
516
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' });
487
517
  },
488
518
  async click({ handle }, _optional = {}) {
489
- const control = controlOf(find(handle));
519
+ const control = controlOf(find(handle, 'click'));
490
520
  if (!(control instanceof HTMLElement))
491
521
  throw new Error(`"${handle}" is not clickable`);
492
- control.click();
522
+ if (options.deferSubmit && nativeSubmitter(control)) {
523
+ options.deferSubmit({ execute: async () => {
524
+ // A changed or replaced form/submitter cannot inherit the accepted plan.
525
+ const live = controlOf(find(handle, 'click'));
526
+ if (live !== control)
527
+ throw new Error('Submission target changed');
528
+ control.click();
529
+ } });
530
+ }
531
+ else
532
+ control.click();
493
533
  },
494
534
  async fill({ handle, text }, _optional = {}) {
495
- const control = controlOf(find(handle));
535
+ const control = controlOf(find(handle, 'fill'));
496
536
  if (isEditableRegion(control)) {
497
537
  // A contenteditable region has no `value`; its content *is* its children, and a real
498
538
  // editor owns that subtree. See `fillEditableRegion` for why this drives it through
@@ -518,7 +558,7 @@ export function createDomPageDriver({ root, pages }, options = {}) {
518
558
  control.dispatchEvent(new Event('change', { bubbles: true }));
519
559
  },
520
560
  async selectOption({ handle, option }, { selected = true } = {}) {
521
- const control = controlOf(find(handle));
561
+ const control = controlOf(find(handle, 'selectOption'));
522
562
  if (!(control instanceof HTMLSelectElement)) {
523
563
  throw new Error(`"${handle}" is not a dropdown`);
524
564
  }
@@ -563,7 +603,7 @@ export function createDomPageDriver({ root, pages }, options = {}) {
563
603
  control.dispatchEvent(new Event('input', { bubbles: true }));
564
604
  control.dispatchEvent(new Event('change', { bubbles: true }));
565
605
  },
566
- async navigate({ page }, _optional = {}) {
606
+ async navigate({ page, ...input }, _optional = {}) {
567
607
  // `hasOwnProperty`, not `pages[page]`: a plain object literal inherits everything
568
608
  // `Object.prototype` contributes, so a bare lookup answered `navigate("constructor")` with
569
609
  // the `Object` constructor — a function the host never published, which this then called,
@@ -579,7 +619,7 @@ export function createDomPageDriver({ root, pages }, options = {}) {
579
619
  // applies here too rather than assuming the only caller is the one that already checked.
580
620
  if (!published)
581
621
  throw new Error(`"${normalizeAgentLabel({ raw: page }).text}" is not a published page`);
582
- pages[page].navigate({});
622
+ return pages[page].navigate(input);
583
623
  },
584
624
  };
585
625
  }
@@ -634,4 +674,10 @@ export function currentAgentPage({ root }, _optional = {}) {
634
674
  const tagged = root.querySelector(`[${AGENT_PAGE_ATTRIBUTE}]`);
635
675
  return tagged?.getAttribute(AGENT_PAGE_ATTRIBUTE) ?? undefined;
636
676
  }
677
+ function publicationState({ state, element, filtered }) {
678
+ return filtered ? { ...state, text: publishedText({ element }) } : state;
679
+ }
680
+ function nativeSubmitter(control) {
681
+ return (control instanceof HTMLButtonElement || control instanceof HTMLInputElement) && control.type === 'submit' && control.form !== null;
682
+ }
637
683
  //# sourceMappingURL=dom-page-driver.js.map
@@ -0,0 +1,26 @@
1
+ import type { PagePublication } from '../page-publication.js';
2
+ import { type FieldDescriptor } from '../guards.js';
3
+ /** Rendering/private checks include ancestors, even where checkVisibility is unavailable. */
4
+ export declare function publicElement({ element }: {
5
+ element: Element;
6
+ }, _options?: {}): boolean;
7
+ /** Read only public text nodes. Never clone private/hidden text or form values into a label.
8
+ * O(n) over this already-published region, never the document. */
9
+ export declare function publishedText({ element }: {
10
+ element: Element;
11
+ }, _options?: {}): string;
12
+ /** Snapshot identities and targets once; a re-rendered control needs a fresh publication/binding.
13
+ * O(p × m) snapshot for p manifest entries and m scoped handles; O(depth) checks plus live duplicate detection per operation. */
14
+ export declare function createPublicationFilter({ root, publication, fieldOf }: {
15
+ root: ParentNode & {
16
+ querySelector: Element['querySelector'];
17
+ };
18
+ publication: PagePublication;
19
+ fieldOf: (element: Element) => FieldDescriptor | null;
20
+ }, _options?: {}): {
21
+ allows({ element, action }: {
22
+ element: Element;
23
+ action: string;
24
+ }): boolean;
25
+ };
26
+ //# sourceMappingURL=publication-filter.d.ts.map